home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_1280 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  25.0 KB  |  634 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __license__ = 'GPL v3'
  5. __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
  6. import re
  7. from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, pyqtSignal
  8. from PyQt4 import QtGui
  9. from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
  10. from calibre.gui2.dialogs.tag_editor import TagEditor
  11. from calibre.ebooks.metadata import string_to_authors, authors_to_string
  12. from calibre.gui2.custom_column_widgets import populate_metadata_page
  13. from calibre.gui2 import error_dialog
  14. from calibre.gui2.progress_indicator import ProgressIndicator
  15. from calibre.utils.config import dynamic
  16. from calibre.utils.titlecase import titlecase
  17.  
  18. class MyBlockingBusy(QDialog):
  19.     do_one_signal = pyqtSignal()
  20.     phases = [
  21.         '',
  22.         _('Title/Author'),
  23.         _('Standard metadata'),
  24.         _('Custom metadata'),
  25.         _('Search/Replace')]
  26.     
  27.     def __init__(self, msg, args, db, ids, cc_widgets, s_r_func, parent = None, window_title = _('Working')):
  28.         QDialog.__init__(self, parent)
  29.         self._layout = QVBoxLayout()
  30.         self.setLayout(self._layout)
  31.         self.msg_text = msg
  32.         self.msg = QLabel(msg + '        ')
  33.         self.font = QFont()
  34.         self.font.setPointSize(self.font.pointSize() + 8)
  35.         self.msg.setFont(self.font)
  36.         self.pi = ProgressIndicator(self)
  37.         self.pi.setDisplaySize(100)
  38.         self._layout.addWidget(self.pi, 0, Qt.AlignHCenter)
  39.         self._layout.addSpacing(15)
  40.         self._layout.addWidget(self.msg, 0, Qt.AlignHCenter)
  41.         self.setWindowTitle(window_title)
  42.         self.resize(self.sizeHint())
  43.         self.start()
  44.         self.args = args
  45.         self.series_start_value = None
  46.         self.db = db
  47.         self.ids = ids
  48.         self.error = None
  49.         self.cc_widgets = cc_widgets
  50.         self.s_r_func = s_r_func
  51.         self.do_one_signal.connect(self.do_one_safe, Qt.QueuedConnection)
  52.  
  53.     
  54.     def start(self):
  55.         self.pi.startAnimation()
  56.  
  57.     
  58.     def stop(self):
  59.         self.pi.stopAnimation()
  60.  
  61.     
  62.     def accept(self):
  63.         self.stop()
  64.         return QDialog.accept(self)
  65.  
  66.     
  67.     def exec_(self):
  68.         self.current_index = 0
  69.         self.current_phase = 1
  70.         self.do_one_signal.emit()
  71.         return QDialog.exec_(self)
  72.  
  73.     
  74.     def do_one_safe(self):
  75.         
  76.         try:
  77.             id = self.ids[self.current_index]
  78.             percent = int(self.current_index * 100 / float(len(self.ids)))
  79.             self.msg.setText(self.msg_text.format(self.phases[self.current_phase], percent))
  80.             self.do_one(id)
  81.         except Exception:
  82.             err = None
  83.             import traceback
  84.             
  85.             try:
  86.                 err = unicode(err)
  87.             except:
  88.                 err = repr(err)
  89.  
  90.             self.error = (err, traceback.format_exc())
  91.             return self.accept()
  92.  
  93.  
  94.     
  95.     def do_one(self, id):
  96.         (remove, add, au, aus, do_aus, rating, pub, do_series, do_autonumber, do_remove_format, remove_format, do_swap_ta, do_remove_conv, do_auto_author, series, do_series_restart, series_start_value, do_title_case, clear_series) = self.args
  97.         if self.current_phase == 1:
  98.             title_set = False
  99.             if do_swap_ta:
  100.                 title = self.db.title(id, index_is_id = True)
  101.                 aum = self.db.authors(id, index_is_id = True)
  102.                 if aum:
  103.                     aum = [ a.strip().replace('|', ',') for a in aum.split(',') ]
  104.                     new_title = authors_to_string(aum)
  105.                     self.db.set_title(id, new_title, notify = False)
  106.                     title_set = True
  107.                 
  108.                 if title:
  109.                     new_authors = string_to_authors(title)
  110.                     self.db.set_authors(id, new_authors, notify = False)
  111.                 
  112.             
  113.             if do_title_case and not title_set:
  114.                 title = self.db.title(id, index_is_id = True)
  115.                 self.db.set_title(id, titlecase(title), notify = False)
  116.             
  117.             if au:
  118.                 self.db.set_authors(id, string_to_authors(au), notify = False)
  119.             
  120.         elif self.current_phase == 2:
  121.             if do_auto_author:
  122.                 x = self.db.author_sort_from_book(id, index_is_id = True)
  123.                 if x:
  124.                     self.db.set_author_sort(id, x, notify = False, commit = False)
  125.                 
  126.             
  127.             if aus and do_aus:
  128.                 self.db.set_author_sort(id, aus, notify = False, commit = False)
  129.             
  130.             if rating != -1:
  131.                 self.db.set_rating(id, 2 * rating, notify = False, commit = False)
  132.             
  133.             if pub:
  134.                 self.db.set_publisher(id, pub, notify = False, commit = False)
  135.             
  136.             if clear_series:
  137.                 self.db.set_series(id, '', notify = False, commit = False)
  138.             
  139.             if do_series:
  140.                 if do_series_restart:
  141.                     if self.series_start_value is None:
  142.                         self.series_start_value = series_start_value
  143.                     
  144.                     next = self.series_start_value
  145.                     self.series_start_value += 1
  146.                 else:
  147.                     next = self.db.get_next_series_num_for(series)
  148.                 self.db.set_series(id, series, notify = False, commit = False)
  149.                 num = None if do_autonumber and series else 1
  150.                 self.db.set_series_index(id, num, notify = False, commit = False)
  151.             
  152.             if do_remove_format:
  153.                 self.db.remove_format(id, remove_format, index_is_id = True, notify = False, commit = False)
  154.             
  155.             if do_remove_conv:
  156.                 self.db.delete_conversion_options(id, 'PIPE', commit = False)
  157.             
  158.         elif self.current_phase == 3:
  159.             for w in self.cc_widgets:
  160.                 w.commit(self.ids)
  161.             
  162.             self.db.bulk_modify_tags(self.ids, add = add, remove = remove, notify = False)
  163.             self.current_index = len(self.ids)
  164.         elif self.current_phase == 4:
  165.             self.s_r_func(id)
  166.         
  167.         self.current_index += 1
  168.         self.do_one_signal.emit()
  169.  
  170.  
  171.  
  172. class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
  173.     s_r_functions = {
  174.         '': (lambda x: x),
  175.         _('Lower Case'): (lambda x: x.lower()),
  176.         _('Upper Case'): (lambda x: x.upper()),
  177.         _('Title Case'): (lambda x: titlecase(x)) }
  178.     s_r_match_modes = [
  179.         _('Character match'),
  180.         _('Regular Expression')]
  181.     s_r_replace_modes = [
  182.         _('Replace field'),
  183.         _('Prepend to field'),
  184.         _('Append to field')]
  185.     
  186.     def __init__(self, window, rows, model):
  187.         QDialog.__init__(self, window)
  188.         Ui_MetadataBulkDialog.__init__(self)
  189.         self.setupUi(self)
  190.         self.model = model
  191.         self.db = model.db
  192.         self.ids = [ self.db.id(r) for r in rows ]
  193.         self.box_title.setText('<p>' + _('Editing meta information for <b>%d books</b>') % len(rows))
  194.         self.write_series = False
  195.         self.changed = False
  196.         all_tags = self.db.all_tags()
  197.         self.tags.update_tags_cache(all_tags)
  198.         self.remove_tags.update_tags_cache(all_tags)
  199.         self.initialize_combos()
  200.         for f in self.db.all_formats():
  201.             self.remove_format.addItem(f)
  202.         
  203.         self.remove_format.setCurrentIndex(-1)
  204.         self.series.currentIndexChanged[int].connect(self.series_changed)
  205.         self.series.editTextChanged.connect(self.series_changed)
  206.         self.tag_editor_button.clicked.connect(self.tag_editor)
  207.         self.autonumber_series.stateChanged[int].connect(self.auto_number_changed)
  208.         self.prepare_search_and_replace()
  209.         self.exec_()
  210.  
  211.     
  212.     def prepare_search_and_replace(self):
  213.         self.search_for.initialize('bulk_edit_search_for')
  214.         self.replace_with.initialize('bulk_edit_replace_with')
  215.         self.test_text.initialize('bulk_edit_test_test')
  216.         self.all_fields = [
  217.             '']
  218.         self.writable_fields = [
  219.             '']
  220.         fm = self.db.field_metadata
  221.         for f in fm:
  222.             if (f in ('author_sort',) or fm[f]['datatype'] in ('text', 'series')) and fm[f].get('search_terms', None) and f not in ('formats', 'ondevice'):
  223.                 self.all_fields.append(f)
  224.                 self.writable_fields.append(f)
  225.             
  226.             if fm[f]['datatype'] == 'composite':
  227.                 self.all_fields.append(f)
  228.                 continue
  229.         
  230.         self.all_fields.sort()
  231.         self.writable_fields.sort()
  232.         self.search_field.setMaxVisibleItems(20)
  233.         self.destination_field.setMaxVisibleItems(20)
  234.         offset = 10
  235.         self.s_r_number_of_books = min(10, len(self.ids))
  236.         for i in range(1, self.s_r_number_of_books + 1):
  237.             w = QtGui.QLabel(self.tabWidgetPage3)
  238.             w.setText(_('Book %d:') % i)
  239.             self.testgrid.addWidget(w, i + offset, 0, 1, 1)
  240.             w = QtGui.QLineEdit(self.tabWidgetPage3)
  241.             w.setReadOnly(True)
  242.             name = 'book_%d_text' % i
  243.             setattr(self, name, w)
  244.             self.book_1_text.setObjectName(name)
  245.             self.testgrid.addWidget(w, i + offset, 1, 1, 1)
  246.             w = QtGui.QLineEdit(self.tabWidgetPage3)
  247.             w.setReadOnly(True)
  248.             name = 'book_%d_result' % i
  249.             setattr(self, name, w)
  250.             self.book_1_text.setObjectName(name)
  251.             self.testgrid.addWidget(w, i + offset, 2, 1, 1)
  252.         
  253.         self.main_heading = _('<b>You can destroy your library using this feature.</b> Changes are permanent. There is no undo function.  This feature is experimental, and there may be bugs. You are strongly encouraged to back up your library before proceeding.<p>Search and replace in text fields using character matching or regular expressions. ')
  254.         self.character_heading = _('In character mode, the field is searched for the entered search text. The text is replaced by the specified replacement text everywhere it is found in the specified field. After replacement is finished, the text can be changed to upper-case, lower-case, or title-case. If the case-sensitive check box is checked, the search text must match exactly. If it is unchecked, the search text will match both upper- and lower-case letters')
  255.         self.regexp_heading = _('In regular expression mode, the search text is an arbitrary python-compatible regular expression. The replacement text can contain backreferences to parenthesized expressions in the pattern. The search is not anchored, and can match and replace multiple times on the same string. The modification functions (lower-case etc) are applied to the matched text, not to the field as a whole. The destination box specifies the field where the result after matching and replacement is to be assigned. You can replace the text in the field, or prepend or append the matched text. See <a href="http://docs.python.org/library/re.html"> this reference</a> for more information on python\'s regular expressions, and in particular the \'sub\' function.')
  256.         self.search_mode.addItems(self.s_r_match_modes)
  257.         self.search_mode.setCurrentIndex(dynamic.get('s_r_search_mode', 0))
  258.         self.replace_mode.addItems(self.s_r_replace_modes)
  259.         self.replace_mode.setCurrentIndex(0)
  260.         self.s_r_search_mode = 0
  261.         self.s_r_error = None
  262.         self.s_r_obj = None
  263.         self.replace_func.addItems(sorted(self.s_r_functions.keys()))
  264.         self.search_mode.currentIndexChanged[int].connect(self.s_r_search_mode_changed)
  265.         self.search_field.currentIndexChanged[int].connect(self.s_r_search_field_changed)
  266.         self.destination_field.currentIndexChanged[str].connect(self.s_r_destination_field_changed)
  267.         self.replace_mode.currentIndexChanged[int].connect(self.s_r_paint_results)
  268.         self.replace_func.currentIndexChanged[str].connect(self.s_r_paint_results)
  269.         self.search_for.editTextChanged[str].connect(self.s_r_paint_results)
  270.         self.replace_with.editTextChanged[str].connect(self.s_r_paint_results)
  271.         self.test_text.editTextChanged[str].connect(self.s_r_paint_results)
  272.         self.comma_separated.stateChanged.connect(self.s_r_paint_results)
  273.         self.case_sensitive.stateChanged.connect(self.s_r_paint_results)
  274.         self.central_widget.setCurrentIndex(0)
  275.         self.search_for.completer().setCaseSensitivity(Qt.CaseSensitive)
  276.         self.replace_with.completer().setCaseSensitivity(Qt.CaseSensitive)
  277.         self.s_r_search_mode_changed(self.search_mode.currentIndex())
  278.  
  279.     
  280.     def s_r_get_field(self, mi, field):
  281.         if field:
  282.             fm = self.db.metadata_for_field(field)
  283.             val = mi.get(field, None)
  284.             if val is None:
  285.                 val = []
  286.             elif not fm['is_multiple']:
  287.                 val = [
  288.                     val]
  289.             elif field == 'authors':
  290.                 val = [ v.replace(',', '|') for v in val ]
  291.             
  292.         else:
  293.             val = []
  294.         return val
  295.  
  296.     
  297.     def s_r_search_field_changed(self, idx):
  298.         for i in range(0, self.s_r_number_of_books):
  299.             w = getattr(self, 'book_%d_text' % (i + 1))
  300.             mi = self.db.get_metadata(self.ids[i], index_is_id = True)
  301.             src = unicode(self.search_field.currentText())
  302.             t = self.s_r_get_field(mi, src)
  303.             w.setText(''.join(t[0:1]))
  304.         
  305.         if self.search_mode.currentIndex() == 0:
  306.             self.destination_field.setCurrentIndex(idx)
  307.         else:
  308.             self.s_r_paint_results(None)
  309.  
  310.     
  311.     def s_r_destination_field_changed(self, txt):
  312.         txt = unicode(txt)
  313.         self.comma_separated.setEnabled(True)
  314.         if txt:
  315.             fm = self.db.metadata_for_field(txt)
  316.             if fm['is_multiple']:
  317.                 self.comma_separated.setEnabled(False)
  318.                 self.comma_separated.setChecked(True)
  319.             
  320.         
  321.         self.s_r_paint_results(None)
  322.  
  323.     
  324.     def s_r_search_mode_changed(self, val):
  325.         self.search_field.clear()
  326.         self.destination_field.clear()
  327.         if val == 0:
  328.             self.search_field.addItems(self.writable_fields)
  329.             self.destination_field.addItems(self.writable_fields)
  330.             self.destination_field.setCurrentIndex(0)
  331.             self.destination_field.setVisible(False)
  332.             self.destination_field_label.setVisible(False)
  333.             self.replace_mode.setCurrentIndex(0)
  334.             self.replace_mode.setVisible(False)
  335.             self.replace_mode_label.setVisible(False)
  336.             self.comma_separated.setVisible(False)
  337.             self.s_r_heading.setText('<p>' + self.main_heading + self.character_heading)
  338.         else:
  339.             self.search_field.addItems(self.all_fields)
  340.             self.destination_field.addItems(self.writable_fields)
  341.             self.destination_field.setVisible(True)
  342.             self.destination_field_label.setVisible(True)
  343.             self.replace_mode.setVisible(True)
  344.             self.replace_mode_label.setVisible(True)
  345.             self.comma_separated.setVisible(True)
  346.             self.s_r_heading.setText('<p>' + self.main_heading + self.regexp_heading)
  347.         self.s_r_paint_results(None)
  348.  
  349.     
  350.     def s_r_set_colors(self):
  351.         if self.s_r_error is not None:
  352.             col = 'rgb(255, 0, 0, 20%)'
  353.             self.test_result.setText(self.s_r_error.message)
  354.         else:
  355.             col = 'rgb(0, 255, 0, 20%)'
  356.         self.test_result.setStyleSheet('QLineEdit { color: black; background-color: %s; }' % col)
  357.         for i in range(0, self.s_r_number_of_books):
  358.             getattr(self, 'book_%d_result' % (i + 1)).setText('')
  359.         
  360.  
  361.     
  362.     def s_r_func(self, match):
  363.         rfunc = self.s_r_functions[unicode(self.replace_func.currentText())]
  364.         rtext = unicode(self.replace_with.text())
  365.         rtext = match.expand(rtext)
  366.         return rfunc(rtext)
  367.  
  368.     
  369.     def s_r_do_regexp(self, mi):
  370.         src_field = unicode(self.search_field.currentText())
  371.         src = self.s_r_get_field(mi, src_field)
  372.         result = []
  373.         rfunc = self.s_r_functions[unicode(self.replace_func.currentText())]
  374.         for s in src:
  375.             t = self.s_r_obj.sub(self.s_r_func, s)
  376.             if self.search_mode.currentIndex() == 0:
  377.                 t = rfunc(t)
  378.             
  379.             result.append(t)
  380.         
  381.         return result
  382.  
  383.     
  384.     def s_r_do_destination(self, mi, val):
  385.         src = unicode(self.search_field.currentText())
  386.         if src == '':
  387.             return ''
  388.         dest = unicode(self.destination_field.currentText())
  389.         if dest == '':
  390.             if self.db.metadata_for_field(src)['datatype'] == 'composite':
  391.                 raise Exception(_('You must specify a destination when source is a composite field'))
  392.             self.db.metadata_for_field(src)['datatype'] == 'composite'
  393.             dest = src
  394.         
  395.         dest_mode = self.replace_mode.currentIndex()
  396.         if dest_mode != 0:
  397.             dest_val = mi.get(dest, '')
  398.             if dest_val is None:
  399.                 dest_val = []
  400.             elif isinstance(dest_val, list):
  401.                 if dest == 'authors':
  402.                     dest_val = [ v.replace(',', '|') for v in dest_val ]
  403.                 
  404.             else:
  405.                 dest_val = [
  406.                     dest_val]
  407.         else:
  408.             dest_val = []
  409.         if len(val) > 0:
  410.             if src == 'authors':
  411.                 val = [ v.replace(',', '|') for v in val ]
  412.             
  413.         
  414.         if dest_mode == 1:
  415.             val.extend(dest_val)
  416.         elif dest_mode == 2:
  417.             val[0:0] = dest_val
  418.         
  419.         return val
  420.  
  421.     
  422.     def s_r_replace_mode_separator(self):
  423.         if self.comma_separated.isChecked():
  424.             return ','
  425.         return ''
  426.  
  427.     
  428.     def s_r_paint_results(self, txt):
  429.         self.s_r_error = None
  430.         self.s_r_set_colors()
  431.         if self.case_sensitive.isChecked():
  432.             flags = 0
  433.         else:
  434.             flags = re.I
  435.         
  436.         try:
  437.             if self.search_mode.currentIndex() == 0:
  438.                 self.s_r_obj = re.compile(re.escape(unicode(self.search_for.text())), flags)
  439.             else:
  440.                 self.s_r_obj = re.compile(unicode(self.search_for.text()), flags)
  441.         except Exception:
  442.             e = None
  443.             self.s_r_obj = None
  444.             self.s_r_error = e
  445.             self.s_r_set_colors()
  446.             return None
  447.  
  448.         
  449.         try:
  450.             self.test_result.setText(self.s_r_obj.sub(self.s_r_func, unicode(self.test_text.text())))
  451.         except Exception:
  452.             e = None
  453.             self.s_r_error = e
  454.             self.s_r_set_colors()
  455.             return None
  456.  
  457.         for i in range(0, self.s_r_number_of_books):
  458.             mi = self.db.get_metadata(self.ids[i], index_is_id = True)
  459.             wr = getattr(self, 'book_%d_result' % (i + 1))
  460.             
  461.             try:
  462.                 result = self.s_r_do_regexp(mi)
  463.                 t = self.s_r_do_destination(mi, result[0:1])
  464.                 t = self.s_r_replace_mode_separator().join(t)
  465.                 wr.setText(t)
  466.             continue
  467.             except Exception:
  468.                 e = None
  469.                 self.s_r_error = e
  470.                 self.s_r_set_colors()
  471.                 break
  472.                 continue
  473.             
  474.  
  475.         
  476.  
  477.     
  478.     def do_search_replace(self, id):
  479.         source = unicode(self.search_field.currentText())
  480.         if not source or not (self.s_r_obj):
  481.             return None
  482.         dest = unicode(self.destination_field.currentText())
  483.         if not dest:
  484.             dest = source
  485.         
  486.         dfm = self.db.field_metadata[dest]
  487.         mi = self.db.get_metadata(id, index_is_id = True)
  488.         val = mi.get(source)
  489.         if val is None:
  490.             return None
  491.         val = self.s_r_do_regexp(mi)
  492.         val = self.s_r_do_destination(mi, val)
  493.         if dfm['is_custom']:
  494.             extra = self.db.get_custom_extra(id, label = dfm['label'], index_is_id = True)
  495.             self.db.set_custom(id, val, label = dfm['label'], extra = extra, commit = False)
  496.         elif dest == 'comments':
  497.             setter = self.db.set_comment
  498.         else:
  499.             setter = getattr(self.db, 'set_' + dest)
  500.         if dest in ('title', 'authors'):
  501.             setter(id, val, notify = False)
  502.         else:
  503.             setter(id, val, notify = False, commit = False)
  504.  
  505.     
  506.     def create_custom_column_editors(self):
  507.         w = self.central_widget.widget(1)
  508.         layout = QGridLayout()
  509.         (self.custom_column_widgets, self._MetadataBulkDialog__cc_spacers) = populate_metadata_page(layout, self.db, self.ids, parent = w, two_column = False, bulk = True)
  510.         w.setLayout(layout)
  511.         self._MetadataBulkDialog__custom_col_layouts = [
  512.             layout]
  513.         ans = self.custom_column_widgets
  514.         for i in range(len(ans) - 1):
  515.             w.setTabOrder(ans[i].widgets[-1], ans[i + 1].widgets[1])
  516.             for c in range(2, len(ans[i].widgets), 2):
  517.                 w.setTabOrder(ans[i].widgets[c - 1], ans[i].widgets[c + 1])
  518.             
  519.         
  520.  
  521.     
  522.     def initialize_combos(self):
  523.         self.initalize_authors()
  524.         self.initialize_series()
  525.         self.initialize_publisher()
  526.         for x in ('authors', 'publisher', 'series'):
  527.             x = getattr(self, x)
  528.             x.setSizeAdjustPolicy(x.AdjustToMinimumContentsLengthWithIcon)
  529.             x.setMinimumContentsLength(25)
  530.         
  531.  
  532.     
  533.     def initalize_authors(self):
  534.         all_authors = self.db.all_authors()
  535.         all_authors.sort(cmp = (lambda x, y: cmp(x[1].lower(), y[1].lower())))
  536.         for i in all_authors:
  537.             (id, name) = i
  538.             name = name.strip().replace('|', ',')
  539.             self.authors.addItem(name)
  540.         
  541.         self.authors.setEditText('')
  542.  
  543.     
  544.     def initialize_series(self):
  545.         all_series = self.db.all_series()
  546.         all_series.sort(cmp = (lambda x, y: cmp(x[1], y[1])))
  547.         for i in all_series:
  548.             (id, name) = i
  549.             self.series.addItem(name)
  550.         
  551.         self.series.setEditText('')
  552.  
  553.     
  554.     def initialize_publisher(self):
  555.         all_publishers = self.db.all_publishers()
  556.         all_publishers.sort(cmp = (lambda x, y: cmp(x[1], y[1])))
  557.         for i in all_publishers:
  558.             (id, name) = i
  559.             self.publisher.addItem(name)
  560.         
  561.         self.publisher.setEditText('')
  562.  
  563.     
  564.     def tag_editor(self, *args):
  565.         d = TagEditor(self, self.db, None)
  566.         d.exec_()
  567.         if d.result() == QDialog.Accepted:
  568.             tag_string = ', '.join(d.tags)
  569.             self.tags.setText(tag_string)
  570.             self.tags.update_tags_cache(self.db.all_tags())
  571.             self.remove_tags.update_tags_cache(self.db.all_tags())
  572.         
  573.  
  574.     
  575.     def auto_number_changed(self, state):
  576.         if state:
  577.             self.series_numbering_restarts.setEnabled(True)
  578.             self.series_start_number.setEnabled(True)
  579.         else:
  580.             self.series_numbering_restarts.setEnabled(False)
  581.             self.series_numbering_restarts.setChecked(False)
  582.             self.series_start_number.setEnabled(False)
  583.             self.series_start_number.setValue(1)
  584.  
  585.     
  586.     def accept(self):
  587.         if len(self.ids) < 1:
  588.             return QDialog.accept(self)
  589.         if self.s_r_error is not None:
  590.             error_dialog(self, _('Search/replace invalid'), _('Search pattern is invalid: %s') % self.s_r_error.message, show = True)
  591.             return False
  592.         self.changed = bool(self.ids)
  593.         for w in getattr(self, 'custom_column_widgets', []):
  594.             w.gui_val
  595.         
  596.         add = unicode(self.tags.text()).strip().split(',')
  597.         au = unicode(self.authors.text())
  598.         aus = unicode(self.author_sort.text())
  599.         do_aus = self.author_sort.isEnabled()
  600.         rating = self.rating.value()
  601.         pub = unicode(self.publisher.text())
  602.         do_series = self.write_series
  603.         clear_series = self.clear_series.isChecked()
  604.         series = unicode(self.series.currentText()).strip()
  605.         do_autonumber = self.autonumber_series.isChecked()
  606.         do_series_restart = self.series_numbering_restarts.isChecked()
  607.         series_start_value = self.series_start_number.value()
  608.         do_remove_format = self.remove_format.currentIndex() > -1
  609.         remove_format = unicode(self.remove_format.currentText())
  610.         do_swap_ta = self.swap_title_and_author.isChecked()
  611.         do_remove_conv = self.remove_conversion_settings.isChecked()
  612.         do_auto_author = self.auto_author_sort.isChecked()
  613.         do_title_case = self.change_title_to_title_case.isChecked()
  614.         args = (remove, add, au, aus, do_aus, rating, pub, do_series, do_autonumber, do_remove_format, remove_format, do_swap_ta, do_remove_conv, do_auto_author, series, do_series_restart, series_start_value, do_title_case, clear_series)
  615.         bb = MyBlockingBusy(_('Applying changes to %d books.\nPhase {0} {1}%%.') % len(self.ids), args, self.db, self.ids, getattr(self, 'custom_column_widgets', []), self.do_search_replace, parent = self)
  616.         self.model.stop_metadata_backup()
  617.         
  618.         try:
  619.             bb.exec_()
  620.         finally:
  621.             self.model.start_metadata_backup()
  622.  
  623.         if bb.error is not None:
  624.             return error_dialog(self, _('Failed'), bb.error[0], det_msg = bb.error[1], show = True)
  625.         dynamic['s_r_search_mode'] = self.search_mode.currentIndex()
  626.         self.db.clean()
  627.         return QDialog.accept(self)
  628.  
  629.     
  630.     def series_changed(self, *args):
  631.         self.write_series = True
  632.  
  633.  
  634.