home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_1282 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  33.5 KB  |  804 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 os
  7. import re
  8. import time
  9. import traceback
  10. import textwrap
  11. from PyQt4.Qt import SIGNAL, QObject, Qt, QTimer, QThread, QDate, QPixmap, QListWidgetItem, QDialog, pyqtSignal, QMessageBox
  12. from calibre.gui2 import error_dialog, file_icon_provider, dynamic, choose_files, choose_images, ResizableDialog, warning_dialog, question_dialog
  13. from calibre.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog
  14. from calibre.gui2.dialogs.fetch_metadata import FetchMetadata
  15. from calibre.gui2.dialogs.tag_editor import TagEditor
  16. from calibre.gui2.widgets import ProgressIndicator
  17. from calibre.ebooks import BOOK_EXTENSIONS
  18. from calibre.ebooks.metadata import string_to_authors, authors_to_string, check_isbn
  19. from calibre.ebooks.metadata.covers import download_cover
  20. from calibre.ebooks.metadata.meta import get_metadata
  21. from calibre.ebooks.metadata import MetaInformation
  22. from calibre.utils.config import prefs, tweaks
  23. from calibre.utils.date import qt_to_dt, local_tz, utcfromtimestamp
  24. from calibre.customize.ui import run_plugins_on_import, get_isbndb_key
  25. from calibre.gui2.preferences.social import SocialMetadata
  26. from calibre.gui2.custom_column_widgets import populate_metadata_page
  27. from calibre import strftime
  28.  
  29. class CoverFetcher(QThread):
  30.     
  31.     def __init__(self, username, password, isbn, timeout, title, author):
  32.         self.username = None if username else username
  33.         self.password = None if password else password
  34.         self.timeout = timeout
  35.         self.isbn = isbn
  36.         self.title = title
  37.         self.needs_isbn = False
  38.         self.author = author
  39.         QThread.__init__(self)
  40.         self.exception = None
  41.         self.traceback = None
  42.         self.cover_data = None
  43.  
  44.     
  45.     def run(self):
  46.         
  47.         try:
  48.             au = None if self.author else None
  49.             mi = MetaInformation(self.title, [
  50.                 au])
  51.             mi.isbn = self.isbn
  52.             (self.cover_data, self.errors) = download_cover(mi, timeout = self.timeout)
  53.         except Exception:
  54.             e = None
  55.             self.exception = e
  56.             self.traceback = traceback.format_exc()
  57.             print self.traceback
  58.  
  59.  
  60.  
  61.  
  62. class Format(QListWidgetItem):
  63.     
  64.     def __init__(self, parent, ext, size, path = None, timestamp = None):
  65.         self.path = path
  66.         self.ext = ext
  67.         self.size = float(size) / 1048576
  68.         text = '%s (%.2f MB)' % (self.ext.upper(), self.size)
  69.         QListWidgetItem.__init__(self, file_icon_provider().icon_from_ext(ext), text, parent, QListWidgetItem.UserType)
  70.         if timestamp is not None:
  71.             ts = timestamp.astimezone(local_tz)
  72.             t = strftime('%a, %d %b %Y [%H:%M:%S]', ts.timetuple())
  73.             text = _('Last modified: %s') % t
  74.             self.setToolTip(text)
  75.             self.setStatusTip(text)
  76.         
  77.  
  78.  
  79.  
  80. class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
  81.     COVER_FETCH_TIMEOUT = 240
  82.     view_format = pyqtSignal(object)
  83.     
  84.     def do_reset_cover(self, *args):
  85.         pix = QPixmap(I('default_cover.png'))
  86.         self.cover.setPixmap(pix)
  87.         self.cover_changed = True
  88.         self.cover_data = None
  89.  
  90.     
  91.     def select_cover(self, checked):
  92.         files = choose_images(self, 'change cover dialog', _('Choose cover for ') + unicode(self.title.text()))
  93.         if not files:
  94.             return None
  95.         _file = files[0]
  96.  
  97.     
  98.     def generate_cover(self, *args):
  99.         calibre_cover = calibre_cover
  100.         import calibre.ebooks
  101.         fmt_sidx = fmt_sidx
  102.         import calibre.ebooks.metadata
  103.         config = config
  104.         import calibre.gui2
  105.         title = unicode(self.title.text()).strip()
  106.         author = unicode(self.authors.text()).strip()
  107.         if not title or not author:
  108.             return error_dialog(self, _('Specify title and author'), _('You must specify a title and author before generating a cover'), show = True)
  109.         series = unicode(self.series.text()).strip()
  110.         series_string = None
  111.         if series:
  112.             series_string = _('Book %s of %s') % (fmt_sidx(self.series_index.value(), use_roman = config['use_roman_numerals_for_series_number']), series)
  113.         
  114.         self.cover_data = calibre_cover(title, author, series_string = series_string)
  115.         pix = QPixmap()
  116.         pix.loadFromData(self.cover_data)
  117.         self.cover.setPixmap(pix)
  118.         self.cover_changed = True
  119.         self.cpixmap = pix
  120.  
  121.     
  122.     def add_format(self, x):
  123.         files = choose_files(self, 'add formats dialog', _('Choose formats for ') + unicode(self.title.text()), [
  124.             (_('Books'), BOOK_EXTENSIONS)])
  125.         self._add_formats(files)
  126.  
  127.     
  128.     def _add_formats(self, paths):
  129.         added = False
  130.         if not paths:
  131.             return added
  132.         bad_perms = []
  133.         for _file in paths:
  134.             _file = os.path.abspath(_file)
  135.             if not os.access(_file, os.R_OK):
  136.                 bad_perms.append(_file)
  137.                 continue
  138.             
  139.             nfile = run_plugins_on_import(_file)
  140.             if nfile is not None:
  141.                 _file = nfile
  142.             
  143.             stat = os.stat(_file)
  144.             size = stat.st_size
  145.             ext = os.path.splitext(_file)[1].lower().replace('.', '')
  146.             timestamp = utcfromtimestamp(stat.st_mtime)
  147.             for row in range(self.formats.count()):
  148.                 fmt = self.formats.item(row)
  149.                 if fmt.ext.lower() == ext:
  150.                     self.formats.takeItem(row)
  151.                     break
  152.                     continue
  153.             
  154.             Format(self.formats, ext, size, path = _file, timestamp = timestamp)
  155.             self.formats_changed = True
  156.             added = True
  157.         
  158.         if bad_perms:
  159.             error_dialog(self, _('No permission'), _('You do not have permission to read the following files:'), det_msg = '\n'.join(bad_perms), show = True)
  160.         
  161.         return added
  162.  
  163.     
  164.     def formats_dropped(self, event, paths):
  165.         if self._add_formats(paths):
  166.             event.accept()
  167.         
  168.  
  169.     
  170.     def remove_format(self, *args):
  171.         rows = self.formats.selectionModel().selectedRows(0)
  172.         for row in rows:
  173.             self.formats.takeItem(row.row())
  174.             self.formats_changed = True
  175.         
  176.  
  177.     
  178.     def get_selected_format_metadata(self):
  179.         old = prefs['read_file_metadata']
  180.         if not old:
  181.             prefs['read_file_metadata'] = True
  182.         
  183.         
  184.         try:
  185.             row = self.formats.currentRow()
  186.             fmt = self.formats.item(row)
  187.             if fmt is None:
  188.                 if self.formats.count() == 1:
  189.                     fmt = self.formats.item(0)
  190.                 
  191.                 if fmt is None:
  192.                     error_dialog(self, _('No format selected'), _('No format selected')).exec_()
  193.                     return (None, None)
  194.             
  195.             ext = fmt.ext.lower()
  196.             if fmt.path is None:
  197.                 stream = self.db.format(self.row, ext, as_file = True)
  198.             else:
  199.                 stream = open(fmt.path, 'r+b')
  200.             
  201.             try:
  202.                 mi = get_metadata(stream, ext)
  203.                 return (mi, ext)
  204.             except:
  205.                 error_dialog(self, _('Could not read metadata'), _('Could not read metadata from %s format') % ext).exec_()
  206.  
  207.             return (None, None)
  208.         finally:
  209.             if old != prefs['read_file_metadata']:
  210.                 prefs['read_file_metadata'] = old
  211.             
  212.  
  213.  
  214.     
  215.     def set_metadata_from_format(self):
  216.         (mi, ext) = self.get_selected_format_metadata()
  217.         if mi is None:
  218.             return None
  219.         if mi.title:
  220.             self.title.setText(mi.title)
  221.         
  222.         if mi.authors:
  223.             self.authors.setEditText(authors_to_string(mi.authors))
  224.         
  225.         if mi.author_sort:
  226.             self.author_sort.setText(mi.author_sort)
  227.         
  228.         if mi.rating is not None:
  229.             
  230.             try:
  231.                 self.rating.setValue(mi.rating)
  232.  
  233.         
  234.         if mi.publisher:
  235.             self.publisher.setEditText(mi.publisher)
  236.         
  237.         if mi.tags:
  238.             self.tags.setText(', '.join(mi.tags))
  239.         
  240.         if mi.isbn:
  241.             self.isbn.setText(mi.isbn)
  242.         
  243.         if mi.pubdate:
  244.             self.pubdate.setDate(QDate(mi.pubdate.year, mi.pubdate.month, mi.pubdate.day))
  245.         
  246.         if mi.series and mi.series.strip():
  247.             self.series.setEditText(mi.series)
  248.             if mi.series_index is not None:
  249.                 self.series_index.setValue(float(mi.series_index))
  250.             
  251.         
  252.         if mi.comments and mi.comments.strip():
  253.             self.comments.setPlainText(mi.comments)
  254.         
  255.  
  256.     
  257.     def set_cover(self):
  258.         (mi, ext) = self.get_selected_format_metadata()
  259.         if mi is None:
  260.             return None
  261.         cdata = None
  262.         if mi.cover and os.access(mi.cover, os.R_OK):
  263.             cdata = open(mi.cover).read()
  264.         elif mi.cover_data[1] is not None:
  265.             cdata = mi.cover_data[1]
  266.         
  267.         if cdata is None:
  268.             error_dialog(self, _('Could not read cover'), _('Could not read cover from %s format') % ext).exec_()
  269.             return None
  270.         pix = QPixmap()
  271.         pix.loadFromData(cdata)
  272.         if pix.isNull():
  273.             error_dialog(self, _('Could not read cover'), _('The cover in the %s format is invalid') % ext).exec_()
  274.             return None
  275.         self.cover.setPixmap(pix)
  276.         self.cover_changed = True
  277.         self.cpixmap = pix
  278.         self.cover_data = cdata
  279.  
  280.     
  281.     def trim_cover(self, *args):
  282.         Image = Image
  283.         import calibre.utils.magick
  284.         cdata = self.cover_data
  285.         if not cdata:
  286.             return None
  287.         im = Image()
  288.         im.load(cdata)
  289.         im.trim(10)
  290.         cdata = im.export('png')
  291.         pix = QPixmap()
  292.         pix.loadFromData(cdata)
  293.         self.cover.setPixmap(pix)
  294.         self.cover_changed = True
  295.         self.cpixmap = pix
  296.         self.cover_data = cdata
  297.  
  298.     
  299.     def sync_formats(self):
  300.         old_extensions = set()
  301.         new_extensions = set()
  302.         paths = { }
  303.         for row in range(self.formats.count()):
  304.             fmt = self.formats.item(row)
  305.             ext = fmt.ext.lower()
  306.             path = fmt.path
  307.             if 'unknown' in ext.lower():
  308.                 ext = None
  309.             
  310.             if path:
  311.                 new_extensions.add(ext)
  312.                 paths[ext] = path
  313.                 continue
  314.             old_extensions.add(ext)
  315.         
  316.         for ext in new_extensions:
  317.             self.db.add_format(self.row, ext, open(paths[ext], 'rb'), notify = False)
  318.         
  319.         db_extensions = []([ f.lower() for f in self.db.formats(self.row).split(',') ])
  320.         extensions = new_extensions.union(old_extensions)
  321.         for ext in db_extensions:
  322.             if ext not in extensions:
  323.                 self.db.remove_format(self.row, ext, notify = False)
  324.                 continue
  325.             []
  326.         
  327.  
  328.     
  329.     def do_cancel_all(self):
  330.         self.cancel_all = True
  331.         self.reject()
  332.  
  333.     
  334.     def __init__(self, window, row, db, accepted_callback = None, cancel_all = False):
  335.         ResizableDialog.__init__(self, window)
  336.         self.bc_box.layout().setAlignment(self.cover, Qt.AlignCenter | Qt.AlignHCenter)
  337.         self.cancel_all = False
  338.         base = unicode(self.author_sort.toolTip())
  339.         self.ok_aus_tooltip = '<p>' + textwrap.fill(base + '<br><br>' + _(' The green color indicates that the current author sort matches the current author'))
  340.         self.bad_aus_tooltip = '<p>' + textwrap.fill(base + '<br><br>' + _(' The red color indicates that the current author sort does not match the current author'))
  341.         if cancel_all:
  342.             self._MetadataSingleDialog__abort_button = self.button_box.addButton(self.button_box.Abort)
  343.             self._MetadataSingleDialog__abort_button.setToolTip(_('Abort the editing of all remaining books'))
  344.             self.connect(self._MetadataSingleDialog__abort_button, SIGNAL('clicked()'), self.do_cancel_all)
  345.         
  346.         self.splitter.setStretchFactor(100, 1)
  347.         self.read_state()
  348.         self.db = db
  349.         self.pi = ProgressIndicator(self)
  350.         self.accepted_callback = accepted_callback
  351.         self.id = db.id(row)
  352.         self.row = row
  353.         self.cover_data = None
  354.         self.formats_changed = False
  355.         self.formats.setAcceptDrops(True)
  356.         self.cover_changed = False
  357.         self.cpixmap = None
  358.         self.pubdate.setMinimumDate(QDate(100, 1, 1))
  359.         pubdate_format = tweaks['gui_pubdate_display_format']
  360.         if pubdate_format is not None:
  361.             self.pubdate.setDisplayFormat(pubdate_format)
  362.         
  363.         self.date.setMinimumDate(QDate(100, 1, 1))
  364.         self.connect(self.cover, SIGNAL('cover_changed(PyQt_PyObject)'), self.cover_dropped)
  365.         QObject.connect(self.cover_button, SIGNAL('clicked(bool)'), self.select_cover)
  366.         QObject.connect(self.add_format_button, SIGNAL('clicked(bool)'), self.add_format)
  367.         self.connect(self.formats, SIGNAL('formats_dropped(PyQt_PyObject,PyQt_PyObject)'), self.formats_dropped)
  368.         QObject.connect(self.remove_format_button, SIGNAL('clicked(bool)'), self.remove_format)
  369.         QObject.connect(self.fetch_metadata_button, SIGNAL('clicked()'), self.fetch_metadata)
  370.         QObject.connect(self.fetch_cover_button, SIGNAL('clicked()'), self.fetch_cover)
  371.         QObject.connect(self.tag_editor_button, SIGNAL('clicked()'), self.edit_tags)
  372.         QObject.connect(self.remove_series_button, SIGNAL('clicked()'), self.remove_unused_series)
  373.         QObject.connect(self.auto_author_sort, SIGNAL('clicked()'), self.deduce_author_sort)
  374.         self.trim_cover_button.clicked.connect(self.trim_cover)
  375.         self.connect(self.author_sort, SIGNAL('textChanged(const QString&)'), self.author_sort_box_changed)
  376.         self.connect(self.authors, SIGNAL('editTextChanged(const QString&)'), self.authors_box_changed)
  377.         self.connect(self.formats, SIGNAL('itemDoubleClicked(QListWidgetItem*)'), self.show_format)
  378.         self.connect(self.formats, SIGNAL('delete_format()'), self.remove_format)
  379.         self.connect(self.button_set_cover, SIGNAL('clicked()'), self.set_cover)
  380.         self.connect(self.button_set_metadata, SIGNAL('clicked()'), self.set_metadata_from_format)
  381.         self.connect(self.reset_cover, SIGNAL('clicked()'), self.do_reset_cover)
  382.         self.connect(self.swap_button, SIGNAL('clicked()'), self.swap_title_author)
  383.         self.timeout = float(prefs['network_timeout'])
  384.         self.title.setText(db.title(row))
  385.         isbn = db.isbn(self.id, index_is_id = True)
  386.         if not isbn:
  387.             isbn = ''
  388.         
  389.         self.isbn.textChanged.connect(self.validate_isbn)
  390.         self.isbn.setText(isbn)
  391.         aus = self.db.author_sort(row)
  392.         None(self.author_sort.setText if aus else '')
  393.         tags = self.db.tags(row)
  394.         self.original_tags = None if tags else ''
  395.         self.tags.setText(self.original_tags)
  396.         self.tags.update_tags_cache(self.db.all_tags())
  397.         rating = self.db.rating(row)
  398.         if rating > 0:
  399.             self.rating.setValue(int(rating / 2))
  400.         
  401.         comments = self.db.comments(row)
  402.         None(self.comments.setPlainText if comments else '')
  403.         cover = self.db.cover(row)
  404.         pubdate = db.pubdate(self.id, index_is_id = True)
  405.         self.pubdate.setDate(QDate(pubdate.year, pubdate.month, pubdate.day))
  406.         timestamp = db.timestamp(self.id, index_is_id = True)
  407.         self.date.setDate(QDate(timestamp.year, timestamp.month, timestamp.day))
  408.         self.orig_date = qt_to_dt(self.date.date())
  409.         exts = self.db.formats(row)
  410.         if exts:
  411.             exts = exts.split(',')
  412.             for ext in exts:
  413.                 if not ext:
  414.                     ext = ''
  415.                 
  416.                 size = self.db.sizeof_format(row, ext)
  417.                 timestamp = self.db.format_last_modified(self.id, ext)
  418.                 if size is None:
  419.                     continue
  420.                 
  421.                 Format(self.formats, ext, size, timestamp = timestamp)
  422.             
  423.         
  424.         self.initialize_combos()
  425.         si = self.db.series_index(row)
  426.         if si is None:
  427.             si = 1
  428.         
  429.         
  430.         try:
  431.             self.series_index.setValue(float(si))
  432.         except:
  433.             self.series_index.setValue(1)
  434.  
  435.         QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.enable_series_index)
  436.         QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.enable_series_index)
  437.         self.series.lineEdit().editingFinished.connect(self.increment_series_index)
  438.         self.show()
  439.         pm = QPixmap()
  440.         if cover:
  441.             pm.loadFromData(cover)
  442.         
  443.         if pm.isNull():
  444.             pm = QPixmap(I('default_cover.png'))
  445.         else:
  446.             self.cover_data = cover
  447.         self.cover.setPixmap(pm)
  448.         self.original_series_name = unicode(self.series.text()).strip()
  449.         if len(db.custom_column_label_map) == 0:
  450.             self.central_widget.tabBar().setVisible(False)
  451.         else:
  452.             self.create_custom_column_editors()
  453.         self.generate_cover_button.clicked.connect(self.generate_cover)
  454.  
  455.     
  456.     def create_custom_column_editors(self):
  457.         w = self.central_widget.widget(1)
  458.         layout = w.layout()
  459.         (self.custom_column_widgets, self._MetadataSingleDialog__cc_spacers) = populate_metadata_page(layout, self.db, self.id, parent = w, bulk = False, two_column = True)
  460.         self._MetadataSingleDialog__custom_col_layouts = [
  461.             layout]
  462.         ans = self.custom_column_widgets
  463.         for i in range(len(ans) - 1):
  464.             if len(ans[i + 1].widgets) == 2:
  465.                 w.setTabOrder(ans[i].widgets[-1], ans[i + 1].widgets[1])
  466.             else:
  467.                 w.setTabOrder(ans[i].widgets[-1], ans[i + 1].widgets[0])
  468.             for c in range(2, len(ans[i].widgets), 2):
  469.                 w.setTabOrder(ans[i].widgets[c - 1], ans[i].widgets[c + 1])
  470.             
  471.         
  472.  
  473.     
  474.     def authors_box_changed(self, txt):
  475.         aus = unicode(txt)
  476.         aus = re.sub('\\s+et al\\.$', '', aus)
  477.         aus = self.db.author_sort_from_authors(string_to_authors(aus))
  478.         self.mark_author_sort(normal = unicode(self.author_sort.text()) == aus)
  479.  
  480.     
  481.     def author_sort_box_changed(self, txt):
  482.         au = unicode(self.authors.text())
  483.         au = re.sub('\\s+et al\\.$', '', au)
  484.         au = self.db.author_sort_from_authors(string_to_authors(au))
  485.         self.mark_author_sort(normal = au == txt)
  486.  
  487.     
  488.     def mark_author_sort(self, normal = True):
  489.         if normal:
  490.             col = 'rgb(0, 255, 0, 20%)'
  491.         else:
  492.             col = 'rgb(255, 0, 0, 20%)'
  493.         self.author_sort.setStyleSheet('QLineEdit { color: black; background-color: %s; }' % col)
  494.         tt = None if normal else self.bad_aus_tooltip
  495.         self.author_sort.setToolTip(tt)
  496.  
  497.     
  498.     def validate_isbn(self, isbn):
  499.         isbn = unicode(isbn).strip()
  500.         if not isbn:
  501.             self.isbn.setStyleSheet('QLineEdit { background-color: rgba(0,255,0,0%) }')
  502.             self.isbn.setToolTip(_('This ISBN number is valid'))
  503.             return None
  504.         if check_isbn(isbn):
  505.             self.isbn.setStyleSheet('QLineEdit { background-color: rgba(0,255,0,20%) }')
  506.             self.isbn.setToolTip(_('This ISBN number is valid'))
  507.         else:
  508.             self.isbn.setStyleSheet('QLineEdit { background-color: rgba(255,0,0,20%) }')
  509.             self.isbn.setToolTip(_('This ISBN number is invalid'))
  510.  
  511.     
  512.     def show_format(self, item, *args):
  513.         fmt = item.ext
  514.         self.view_format.emit(fmt)
  515.  
  516.     
  517.     def deduce_author_sort(self):
  518.         au = unicode(self.authors.text())
  519.         au = re.sub('\\s+et al\\.$', '', au)
  520.         authors = string_to_authors(au)
  521.         self.author_sort.setText(self.db.author_sort_from_authors(authors))
  522.  
  523.     
  524.     def swap_title_author(self):
  525.         title = self.title.text()
  526.         self.title.setText(self.authors.text())
  527.         self.authors.setText(title)
  528.         self.author_sort.setText('')
  529.  
  530.     
  531.     def cover_dropped(self, cover_data):
  532.         self.cover_changed = True
  533.         self.cover_data = cover_data
  534.  
  535.     
  536.     def initialize_combos(self):
  537.         self.initalize_authors()
  538.         self.initialize_series()
  539.         self.initialize_publisher()
  540.         self.layout().activate()
  541.  
  542.     
  543.     def initalize_authors(self):
  544.         all_authors = self.db.all_authors()
  545.         all_authors.sort(cmp = (lambda x, y: cmp(x[1], y[1])))
  546.         for i in all_authors:
  547.             (id, name) = i
  548.             name = [ name.strip().replace('|', ',') for n in name.split(',') ]
  549.             self.authors.addItem(authors_to_string(name))
  550.         
  551.         au = self.db.authors(self.row)
  552.         au = []([ a.strip().replace('|', ',') for a in au.split(',') ])
  553.         self.authors.setEditText(au)
  554.  
  555.     
  556.     def initialize_series(self):
  557.         self.series.setSizeAdjustPolicy(self.series.AdjustToContentsOnFirstShow)
  558.         all_series = self.db.all_series()
  559.         all_series.sort(cmp = (lambda x, y: cmp(x[1], y[1])))
  560.         series_id = self.db.series_id(self.row)
  561.         (idx, c) = (None, 0)
  562.         for i in all_series:
  563.             (id, name) = i
  564.             if id == series_id:
  565.                 idx = c
  566.             
  567.             self.series.addItem(name)
  568.             c += 1
  569.         
  570.         self.series.lineEdit().setText('')
  571.         if idx is not None:
  572.             self.series.setCurrentIndex(idx)
  573.             self.enable_series_index()
  574.         
  575.  
  576.     
  577.     def initialize_publisher(self):
  578.         all_publishers = self.db.all_publishers()
  579.         all_publishers.sort(cmp = (lambda x, y: cmp(x[1], y[1])))
  580.         publisher_id = self.db.publisher_id(self.row)
  581.         (idx, c) = (None, 0)
  582.         for i in all_publishers:
  583.             (id, name) = i
  584.             if id == publisher_id:
  585.                 idx = c
  586.             
  587.             self.publisher.addItem(name)
  588.             c += 1
  589.         
  590.         self.publisher.setEditText('')
  591.         if idx is not None:
  592.             self.publisher.setCurrentIndex(idx)
  593.         
  594.  
  595.     
  596.     def edit_tags(self):
  597.         if self.tags.text() != self.original_tags:
  598.             if question_dialog(self, _('Tags changed'), _('You have changed the tags. In order to use the tags editor, you must either discard or apply these changes'), show_copy_button = False, buttons = QMessageBox.Apply | QMessageBox.Discard, yes_button = QMessageBox.Apply):
  599.                 self.apply_tags(commit = True, notify = True)
  600.                 self.original_tags = unicode(self.tags.text())
  601.             else:
  602.                 self.tags.setText(self.original_tags)
  603.         
  604.         d = TagEditor(self, self.db, self.row)
  605.         d.exec_()
  606.         if d.result() == QDialog.Accepted:
  607.             tag_string = ', '.join(d.tags)
  608.             self.tags.setText(tag_string)
  609.             self.tags.update_tags_cache(self.db.all_tags())
  610.         
  611.  
  612.     
  613.     def fetch_cover(self):
  614.         isbn = re.sub('[^0-9a-zA-Z]', '', unicode(self.isbn.text())).strip()
  615.         self.fetch_cover_button.setEnabled(False)
  616.         self.setCursor(Qt.WaitCursor)
  617.         (title, author) = map(unicode, (self.title.text(), self.authors.text()))
  618.         self.cover_fetcher = CoverFetcher(None, None, isbn, self.timeout, title, author)
  619.         self.cover_fetcher.start()
  620.         self._hangcheck = QTimer(self)
  621.         self.connect(self._hangcheck, SIGNAL('timeout()'), self.hangcheck)
  622.         self.cf_start_time = time.time()
  623.         self.pi.start(_('Downloading cover...'))
  624.         self._hangcheck.start(100)
  625.  
  626.     
  627.     def hangcheck(self):
  628.         if not self.cover_fetcher.isFinished() and time.time() - self.cf_start_time < self.COVER_FETCH_TIMEOUT:
  629.             return None
  630.         self._hangcheck.stop()
  631.         
  632.         try:
  633.             if self.cover_fetcher.isRunning():
  634.                 self.cover_fetcher.terminate()
  635.                 error_dialog(self, _('Cannot fetch cover'), _('<b>Could not fetch cover.</b><br/>') + _('The download timed out.')).exec_()
  636.                 return None
  637.             if self.cover_fetcher.needs_isbn:
  638.                 error_dialog(self, _('Cannot fetch cover'), _('Could not find cover for this book. Try specifying the ISBN first.')).exec_()
  639.                 return None
  640.             if self.cover_fetcher.exception is not None:
  641.                 err = self.cover_fetcher.exception
  642.                 error_dialog(self, _('Cannot fetch cover'), _('<b>Could not fetch cover.</b><br/>') + unicode(err)).exec_()
  643.                 return None
  644.             if self.cover_fetcher.errors and self.cover_fetcher.cover_data is None:
  645.                 details = []([ e[-1] + ': ' + e[1] for e in self.cover_fetcher.errors ])
  646.                 error_dialog(self, _('Cannot fetch cover'), _('<b>Could not fetch cover.</b><br/>') + _('For the error message from each cover source, click Show details below.'), det_msg = details, show = True)
  647.                 return None
  648.             pix = QPixmap()
  649.             pix.loadFromData(self.cover_fetcher.cover_data)
  650.             if pix.isNull():
  651.                 error_dialog(self, _('Bad cover'), _('The cover is not a valid picture')).exec_()
  652.             else:
  653.                 self.cover.setPixmap(pix)
  654.                 self.cover_changed = True
  655.                 self.cpixmap = pix
  656.                 self.cover_data = self.cover_fetcher.cover_data
  657.         finally:
  658.             self.fetch_cover_button.setEnabled(True)
  659.             self.unsetCursor()
  660.             self.pi.stop()
  661.  
  662.  
  663.     
  664.     def fetch_metadata(self):
  665.         isbn = re.sub('[^0-9a-zA-Z]', '', unicode(self.isbn.text()))
  666.         title = unicode(self.title.text())
  667.         
  668.         try:
  669.             author = string_to_authors(unicode(self.authors.text()))[0]
  670.         except:
  671.             author = ''
  672.  
  673.         publisher = unicode(self.publisher.currentText())
  674.  
  675.     
  676.     def enable_series_index(self, *args):
  677.         self.series_index.setEnabled(True)
  678.  
  679.     
  680.     def increment_series_index(self):
  681.         if self.db is not None:
  682.             
  683.             try:
  684.                 series = unicode(self.series.text()).strip()
  685.                 if series and series != self.original_series_name:
  686.                     ns = 1
  687.                     if tweaks['series_index_auto_increment'] == 'next':
  688.                         ns = self.db.get_next_series_num_for(series)
  689.                     
  690.                     self.series_index.setValue(ns)
  691.                     self.original_series_name = series
  692.             traceback.print_exc()
  693.  
  694.         
  695.  
  696.     
  697.     def remove_unused_series(self):
  698.         self.db.remove_unused_series()
  699.         idx = unicode(self.series.currentText())
  700.         self.series.clear()
  701.         self.initialize_series()
  702.         if idx:
  703.             for i in range(self.series.count()):
  704.                 if unicode(self.series.itemText(i)) == idx:
  705.                     self.series.setCurrentIndex(i)
  706.                     break
  707.                     continue
  708.             
  709.         
  710.  
  711.     
  712.     def apply_tags(self, commit = False, notify = False):
  713.         []([], [ x.strip() for x in unicode(self.tags.text()).split(',') ], notify = notify, commit = commit)
  714.  
  715.     
  716.     def accept(self):
  717.         cf = getattr(self, 'cover_fetcher', None)
  718.         if cf is not None and hasattr(cf, 'terminate'):
  719.             cf.terminate()
  720.             cf.wait()
  721.         
  722.         
  723.         try:
  724.             if self.formats_changed:
  725.                 self.sync_formats()
  726.             
  727.             title = unicode(self.title.text()).strip()
  728.             self.db.set_title(self.id, title, notify = False)
  729.             au = unicode(self.authors.text()).strip()
  730.             if au:
  731.                 self.db.set_authors(self.id, string_to_authors(au), notify = False)
  732.             
  733.             aus = unicode(self.author_sort.text()).strip()
  734.             if aus:
  735.                 self.db.set_author_sort(self.id, aus, notify = False, commit = False)
  736.             
  737.             self.db.set_isbn(self.id, re.sub('[^0-9a-zA-Z]', '', unicode(self.isbn.text()).strip()), notify = False, commit = False)
  738.             self.db.set_rating(self.id, 2 * self.rating.value(), notify = False, commit = False)
  739.             self.apply_tags()
  740.             self.db.set_publisher(self.id, unicode(self.publisher.currentText()).strip(), notify = False, commit = False)
  741.             self.db.set_series(self.id, unicode(self.series.currentText()).strip(), notify = False, commit = False)
  742.             self.db.set_series_index(self.id, self.series_index.value(), notify = False, commit = False)
  743.             self.db.set_comment(self.id, unicode(self.comments.toPlainText()).strip(), notify = False, commit = False)
  744.             d = self.pubdate.date()
  745.             d = qt_to_dt(d)
  746.             self.db.set_pubdate(self.id, d, notify = False, commit = False)
  747.             d = self.date.date()
  748.             d = qt_to_dt(d)
  749.             if d != self.orig_date:
  750.                 self.db.set_timestamp(self.id, d, notify = False, commit = False)
  751.             
  752.             self.db.commit()
  753.             if self.cover_changed:
  754.                 if self.cover_data is not None:
  755.                     self.db.set_cover(self.id, self.cover_data)
  756.                 else:
  757.                     self.db.remove_cover(self.id)
  758.             
  759.             for w in getattr(self, 'custom_column_widgets', []):
  760.                 w.commit(self.id)
  761.             
  762.             self.db.commit()
  763.         except IOError:
  764.             err = None
  765.             if err.errno == 13:
  766.                 fname = None if err.filename else 'file'
  767.                 return error_dialog(self, _('Permission denied'), _('Could not open %s. Is it being used by another program?') % fname, det_msg = traceback.format_exc(), show = True)
  768.             raise 
  769.         except:
  770.             err.errno == 13
  771.  
  772.         self.save_state()
  773.         QDialog.accept(self)
  774.         if callable(self.accepted_callback):
  775.             self.accepted_callback(self.id)
  776.         
  777.  
  778.     
  779.     def reject(self, *args):
  780.         cf = getattr(self, 'cover_fetcher', None)
  781.         if cf is not None and hasattr(cf, 'terminate'):
  782.             cf.terminate()
  783.             cf.wait()
  784.         
  785.         QDialog.reject(self, *args)
  786.  
  787.     
  788.     def read_state(self):
  789.         wg = dynamic.get('metasingle_window_geometry', None)
  790.         ss = dynamic.get('metasingle_splitter_state', None)
  791.         if wg is not None:
  792.             self.restoreGeometry(wg)
  793.         
  794.         if ss is not None:
  795.             self.splitter.restoreState(ss)
  796.         
  797.  
  798.     
  799.     def save_state(self):
  800.         dynamic.set('metasingle_window_geometry', bytes(self.saveGeometry()))
  801.         dynamic.set('metasingle_splitter_state', bytes(self.splitter.saveState()))
  802.  
  803.  
  804.