From 2791f02c3dca565d9bc24f33613ebdadd852f4e8 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 30 Nov 2010 16:37:55 +0000 Subject: [PATCH 1/4] Enhancement #7732: add an 'apply' button to bulk edit --- src/calibre/gui2/dialogs/metadata_bulk.py | 44 +++++++++++++++++------ src/calibre/gui2/dialogs/metadata_bulk.ui | 2 +- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index f8177b7680..f2a2394e9e 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -6,7 +6,7 @@ __copyright__ = '2008, Kovid Goyal ' import re from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \ - pyqtSignal + pyqtSignal, QDialogButtonBox from PyQt4 import QtGui from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog @@ -198,15 +198,30 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): ] def __init__(self, window, rows, model): + self.model = model + self.db = self.model.db + self.ids = [self.db.id(r) for r in rows] QDialog.__init__(self, window) Ui_MetadataBulkDialog.__init__(self) + self._initialize() + self.exec_() + + def _initialize(self): + # Remove all controls from the dialog box by deleting the top layout + if self.layout(): + import sip + sip.delete(self.layout()) + self.setupUi(self) - self.model = model - self.db = model.db - self.ids = [self.db.id(r) for r in rows] + self.button_box.clicked.connect(self.button_clicked) + self.button_box.button(QDialogButtonBox.Apply).setToolTip(_( + 'Immediately make all changes without closing the dialog. ' + 'This operation cannot be canceled or undone')) + self.box_title.setText('

' + _('Editing meta information for %d books') % - len(rows)) + len(self.ids)) + self.write_series = False self.changed = False @@ -232,7 +247,11 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.create_custom_column_editors() self.prepare_search_and_replace() - self.exec_() + + def button_clicked(self, which): + if which == self.button_box.button(QDialogButtonBox.Apply): + self._do_the_work() + self._initialize() def prepare_search_and_replace(self): self.search_for.initialize('bulk_edit_search_for') @@ -627,10 +646,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.series_start_number.setEnabled(False) self.series_start_number.setValue(1) - def accept(self): - if len(self.ids) < 1: - return QDialog.accept(self) - + def _do_the_work(self): if self.s_r_error is not None: error_dialog(self, _('Search/replace invalid'), _('Search pattern is invalid: %s')%self.s_r_error.message, @@ -690,8 +706,14 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): dynamic['s_r_search_mode'] = self.search_mode.currentIndex() self.db.clean() - return QDialog.accept(self) + return True + def accept(self): + if len(self.ids) < 1: + return QDialog.accept(self) + if not self._do_the_work(): + return False + return QDialog.accept(self) def series_changed(self, *args): self.write_series = True diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 44839bbacd..344bde0fa0 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -710,7 +710,7 @@ nothing should be put between the original text and the inserted text Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok From bfdc51a2dda552c99b5570c5a2becc61165d4dfe Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 30 Nov 2010 17:37:56 +0000 Subject: [PATCH 2/4] Make the 'clear' button respect the search emit flag. --- src/calibre/gui2/search_box.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 6624acf35f..065520b4c5 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -107,6 +107,7 @@ class SearchBox2(QComboBox): return self.currentText() def clear(self, emit_search=True): + print 'in clear', emit_search self.normalize_state() self.setEditText('') if emit_search: @@ -318,7 +319,7 @@ class SearchBoxMixin(object): self.search.cleared.connect(self.search_box_cleared) self.search.changed.connect(self.search_box_changed) self.search.focus_to_library.connect(self.focus_to_library) - self.clear_button.clicked.connect(self.search.clear) + self.clear_button.clicked.connect(lambda:self.search.clear(True)) self.advanced_search_button.clicked[bool].connect(self.do_advanced_search) self.search.clear() From ff978282c72f6fea76d79a5d90dab5af00298b95 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 30 Nov 2010 18:03:52 +0000 Subject: [PATCH 3/4] Remove widgets with removing the layout in bulk edit. --- src/calibre/gui2/dialogs/metadata_bulk.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index f2a2394e9e..e67a80e5d1 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -210,6 +210,11 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): # Remove all controls from the dialog box by deleting the top layout if self.layout(): import sip + while True: + child = self.layout().takeAt(0) + if not child: + break; + sip.delete(child) sip.delete(self.layout()) self.setupUi(self) From 566d56e71e7ca8fabb3ffa812a9b9e59ed790de0 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 30 Nov 2010 18:55:24 +0000 Subject: [PATCH 4/4] Another attempt at apply in bulk edit --- src/calibre/gui2/actions/edit_metadata.py | 9 +++- src/calibre/gui2/dialogs/metadata_bulk.py | 51 ++++++++--------------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index 9b3f2c5bb9..8b57b4b455 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -162,9 +162,14 @@ class EditMetadataAction(InterfaceAction): return # Prevent the TagView from updating due to signals from the database self.gui.tags_view.blockSignals(True) + changed = False try: - changed = MetadataBulkDialog(self.gui, rows, - self.gui.library_view.model()).changed + while True: + dialog = MetadataBulkDialog(self.gui, rows, self.gui.library_view.model()) + if dialog.changed: + changed = True + if not dialog.do_again: + break finally: self.gui.tags_view.blockSignals(False) if changed: diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index e67a80e5d1..4fd34e4c4c 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -198,35 +198,15 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): ] def __init__(self, window, rows, model): - self.model = model - self.db = self.model.db - self.ids = [self.db.id(r) for r in rows] QDialog.__init__(self, window) Ui_MetadataBulkDialog.__init__(self) - self._initialize() - self.exec_() - - def _initialize(self): - # Remove all controls from the dialog box by deleting the top layout - if self.layout(): - import sip - while True: - child = self.layout().takeAt(0) - if not child: - break; - sip.delete(child) - sip.delete(self.layout()) - self.setupUi(self) - self.button_box.clicked.connect(self.button_clicked) - self.button_box.button(QDialogButtonBox.Apply).setToolTip(_( - 'Immediately make all changes without closing the dialog. ' - 'This operation cannot be canceled or undone')) - + self.model = model + self.db = model.db + self.ids = [self.db.id(r) for r in rows] self.box_title.setText('

' + _('Editing meta information for %d books') % - len(self.ids)) - + len(rows)) self.write_series = False self.changed = False @@ -253,10 +233,17 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.prepare_search_and_replace() + self.button_box.clicked.connect(self.button_clicked) + self.button_box.button(QDialogButtonBox.Apply).setToolTip(_( + 'Immediately make all changes without closing the dialog. ' + 'This operation cannot be canceled or undone')) + self.do_again = False + self.exec_() + def button_clicked(self, which): if which == self.button_box.button(QDialogButtonBox.Apply): - self._do_the_work() - self._initialize() + self.do_again = True + self.accept() def prepare_search_and_replace(self): self.search_for.initialize('bulk_edit_search_for') @@ -651,7 +638,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.series_start_number.setEnabled(False) self.series_start_number.setValue(1) - def _do_the_work(self): + def accept(self): + if len(self.ids) < 1: + return QDialog.accept(self) + if self.s_r_error is not None: error_dialog(self, _('Search/replace invalid'), _('Search pattern is invalid: %s')%self.s_r_error.message, @@ -711,13 +701,6 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): dynamic['s_r_search_mode'] = self.search_mode.currentIndex() self.db.clean() - return True - - def accept(self): - if len(self.ids) < 1: - return QDialog.accept(self) - if not self._do_the_work(): - return False return QDialog.accept(self) def series_changed(self, *args):