home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_1342 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  28.0 KB  |  672 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 sys
  8. from threading import RLock
  9. from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QByteArray, QTranslator, QCoreApplication, QThread, QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, QFileDialog, QMessageBox, QPixmap, QFileIconProvider, QIcon, QApplication, QDialog, QPushButton, QUrl
  10. ORG_NAME = 'KovidsBrain'
  11. APP_UID = 'libprs500'
  12. from calibre.constants import islinux, iswindows, isosx, isfreebsd, isfrozen
  13. from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
  14. from calibre.utils.localization import set_qt_translator
  15. from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats
  16. from calibre.ebooks.metadata import MetaInformation
  17. from calibre.utils.date import UNDEFINED_DATE
  18. gprefs = JSONConfig('gui')
  19. NONE = QVariant()
  20. UNDEFINED_QDATE = QDate(UNDEFINED_DATE)
  21. ALL_COLUMNS = [
  22.     'title',
  23.     'ondevice',
  24.     'authors',
  25.     'size',
  26.     'timestamp',
  27.     'rating',
  28.     'publisher',
  29.     'tags',
  30.     'series',
  31.     'pubdate']
  32.  
  33. def _config():
  34.     c = Config('gui', 'preferences for the calibre GUI')
  35.     c.add_opt('send_to_storage_card_by_default', default = False, help = _('Send file to storage card instead of main memory by default'))
  36.     c.add_opt('confirm_delete', default = False, help = _('Confirm before deleting'))
  37.     c.add_opt('main_window_geometry', default = None, help = _('Main window geometry'))
  38.     c.add_opt('new_version_notification', default = True, help = _('Notify when a new version is available'))
  39.     c.add_opt('use_roman_numerals_for_series_number', default = True, help = _('Use Roman numerals for series number'))
  40.     c.add_opt('sort_tags_by', default = 'name', help = _('Sort tags list by name, popularity, or rating'))
  41.     c.add_opt('cover_flow_queue_length', default = 6, help = _('Number of covers to show in the cover browsing mode'))
  42.     c.add_opt('LRF_conversion_defaults', default = [], help = _('Defaults for conversion to LRF'))
  43.     c.add_opt('LRF_ebook_viewer_options', default = None, help = _('Options for the LRF ebook viewer'))
  44.     c.add_opt('internally_viewed_formats', default = [
  45.         'LRF',
  46.         'EPUB',
  47.         'LIT',
  48.         'MOBI',
  49.         'PRC',
  50.         'HTML',
  51.         'FB2',
  52.         'PDB',
  53.         'RB'], help = _('Formats that are viewed using the internal viewer'))
  54.     c.add_opt('column_map', default = ALL_COLUMNS, help = _('Columns to be displayed in the book list'))
  55.     c.add_opt('autolaunch_server', default = False, help = _('Automatically launch content server on application startup'))
  56.     c.add_opt('oldest_news', default = 60, help = _('Oldest news kept in database'))
  57.     c.add_opt('systray_icon', default = False, help = _('Show system tray icon'))
  58.     c.add_opt('upload_news_to_device', default = True, help = _('Upload downloaded news to device'))
  59.     c.add_opt('delete_news_from_library_on_upload', default = False, help = _('Delete books from library after uploading to device'))
  60.     c.add_opt('separate_cover_flow', default = False, help = _('Show the cover flow in a separate window instead of in the main calibre window'))
  61.     c.add_opt('disable_tray_notification', default = False, help = _('Disable notifications from the system tray icon'))
  62.     c.add_opt('default_send_to_device_action', default = None, help = _('Default action to perform when send to device button is clicked'))
  63.     c.add_opt('show_donate_button', default = True, help = 'Show donation button')
  64.     c.add_opt('asked_library_thing_password', default = False, help = 'Asked library thing password at least once.')
  65.     c.add_opt('search_as_you_type', default = True, help = 'Start searching as you type. If this is disabled then search will only take place when the Enter or Return key is pressed.')
  66.     c.add_opt('save_to_disk_template_history', default = [], help = 'Previously used Save to Disk templates')
  67.     c.add_opt('send_to_device_template_history', default = [], help = 'Previously used Send to Device templates')
  68.     c.add_opt('main_search_history', default = [], help = 'Search history for the main GUI')
  69.     c.add_opt('viewer_search_history', default = [], help = 'Search history for the ebook viewer')
  70.     c.add_opt('lrf_viewer_search_history', default = [], help = 'Search history for the LRF viewer')
  71.     c.add_opt('scheduler_search_history', default = [], help = 'Search history for the recipe scheduler')
  72.     c.add_opt('worker_limit', default = 6, help = _('Maximum number of waiting worker processes'))
  73.     c.add_opt('get_social_metadata', default = True, help = _('Download social metadata (tags/rating/etc.)'))
  74.     c.add_opt('overwrite_author_title_metadata', default = True, help = _('Overwrite author and title with new metadata'))
  75.     c.add_opt('enforce_cpu_limit', default = True, help = _('Limit max simultaneous jobs to number of CPUs'))
  76.     c.add_opt('tag_browser_hidden_categories', default = set(), help = _('tag browser categories not to display'))
  77.     c.add_opt('gui_layout', choices = [
  78.         'wide',
  79.         'narrow'], help = _('The layout of the user interface'), default = 'wide')
  80.     c.add_opt('show_avg_rating', default = True, help = _('Show the average rating per item indication in the tag browser'))
  81.     c.add_opt('disable_animations', default = False, help = _('Disable UI animations'))
  82.     return ConfigProxy(c)
  83.  
  84. config = _config()
  85. if iswindows:
  86.     import warnings
  87.     warnings.simplefilter('ignore', DeprecationWarning)
  88.  
  89.  
  90. def available_heights():
  91.     desktop = QCoreApplication.instance().desktop()
  92.     return map((lambda x: x.height()), map(desktop.availableGeometry, range(desktop.numScreens())))
  93.  
  94.  
  95. def available_height():
  96.     desktop = QCoreApplication.instance().desktop()
  97.     return desktop.availableGeometry().height()
  98.  
  99.  
  100. def max_available_height():
  101.     return max(available_heights())
  102.  
  103.  
  104. def min_available_height():
  105.     return min(available_heights())
  106.  
  107.  
  108. def available_width():
  109.     desktop = QCoreApplication.instance().desktop()
  110.     return desktop.availableGeometry().width()
  111.  
  112. _is_widescreen = None
  113.  
  114. def is_widescreen():
  115.     global _is_widescreen, _is_widescreen
  116.     if _is_widescreen is None:
  117.         
  118.         try:
  119.             _is_widescreen = float(available_width()) / available_height() > 1.4
  120.         _is_widescreen = False
  121.  
  122.     
  123.     return _is_widescreen
  124.  
  125.  
  126. def extension(path):
  127.     return os.path.splitext(path)[1][1:].lower()
  128.  
  129.  
  130. class CopyButton(QPushButton):
  131.     ACTION_KEYS = [
  132.         Qt.Key_Enter,
  133.         Qt.Key_Return,
  134.         Qt.Key_Space]
  135.     
  136.     def copied(self):
  137.         self.emit(SIGNAL('copy()'))
  138.         self.setDisabled(True)
  139.         self.setText(_('Copied'))
  140.  
  141.     
  142.     def keyPressEvent(self, ev):
  143.         
  144.         try:
  145.             if ev.key() in self.ACTION_KEYS:
  146.                 self.copied()
  147.                 return None
  148.         except:
  149.             pass
  150.  
  151.         QPushButton.keyPressEvent(self, ev)
  152.  
  153.     
  154.     def keyReleaseEvent(self, ev):
  155.         
  156.         try:
  157.             if ev.key() in self.ACTION_KEYS:
  158.                 return None
  159.         except:
  160.             pass
  161.  
  162.         QPushButton.keyReleaseEvent(self, ev)
  163.  
  164.     
  165.     def mouseReleaseEvent(self, ev):
  166.         ev.accept()
  167.         self.copied()
  168.  
  169.  
  170.  
  171. class MessageBox(QMessageBox):
  172.     
  173.     def __init__(self, type_, title, msg, buttons, parent, det_msg = ''):
  174.         QMessageBox.__init__(self, type_, title, msg, buttons, parent)
  175.         self.title = title
  176.         self.msg = msg
  177.         self.det_msg = det_msg
  178.         self.setDetailedText(det_msg)
  179.         self.cb = None(CopyButton if isosx else _('Copy to Clipboard'))
  180.         self.connect(self.cb, SIGNAL('copy()'), self.copy_to_clipboard)
  181.         self.addButton(self.cb, QMessageBox.ActionRole)
  182.         default_button = self.button(self.Ok)
  183.         if default_button is None:
  184.             default_button = self.button(self.Yes)
  185.         
  186.         if default_button is not None:
  187.             self.setDefaultButton(default_button)
  188.         
  189.  
  190.     
  191.     def copy_to_clipboard(self):
  192.         QApplication.clipboard().setText('%s: %s\n\n%s' % (self.title, self.msg, self.det_msg))
  193.  
  194.  
  195.  
  196. def warning_dialog(parent, title, msg, det_msg = '', show = False, show_copy_button = True):
  197.     d = MessageBox(QMessageBox.Warning, 'WARNING: ' + title, msg, QMessageBox.Ok, parent, det_msg)
  198.     d.setEscapeButton(QMessageBox.Ok)
  199.     d.setIconPixmap(QPixmap(I('dialog_warning.svg')))
  200.     if not show_copy_button:
  201.         d.cb.setVisible(False)
  202.     
  203.     if show:
  204.         return d.exec_()
  205.     return d
  206.  
  207.  
  208. def error_dialog(parent, title, msg, det_msg = '', show = False, show_copy_button = True):
  209.     d = MessageBox(QMessageBox.Critical, 'ERROR: ' + title, msg, QMessageBox.Ok, parent, det_msg)
  210.     d.setIconPixmap(QPixmap(I('dialog_error.svg')))
  211.     d.setEscapeButton(QMessageBox.Ok)
  212.     if not show_copy_button:
  213.         d.cb.setVisible(False)
  214.     
  215.     if show:
  216.         return d.exec_()
  217.     return d
  218.  
  219.  
  220. def question_dialog(parent, title, msg, det_msg = '', show_copy_button = True, buttons = QMessageBox.Yes | QMessageBox.No):
  221.     d = MessageBox(QMessageBox.Question, title, msg, buttons, parent, det_msg)
  222.     d.setIconPixmap(QPixmap(I('dialog_question.svg')))
  223.     d.setEscapeButton(QMessageBox.No)
  224.     if not show_copy_button:
  225.         d.cb.setVisible(False)
  226.     
  227.     return d.exec_() == QMessageBox.Yes
  228.  
  229.  
  230. def info_dialog(parent, title, msg, det_msg = '', show = False):
  231.     d = MessageBox(QMessageBox.Information, title, msg, QMessageBox.Ok, parent, det_msg)
  232.     d.setIconPixmap(QPixmap(I('dialog_information.svg')))
  233.     if show:
  234.         return d.exec_()
  235.     return d
  236.  
  237.  
  238. class Dispatcher(QObject):
  239.     dispatch_signal = pyqtSignal(object, object)
  240.     
  241.     def __init__(self, func):
  242.         QObject.__init__(self)
  243.         self.func = func
  244.         self.dispatch_signal.connect(self.dispatch, type = Qt.QueuedConnection)
  245.  
  246.     
  247.     def __call__(self, *args, **kwargs):
  248.         self.dispatch_signal.emit(args, kwargs)
  249.  
  250.     
  251.     def dispatch(self, args, kwargs):
  252.         self.func(*args, **kwargs)
  253.  
  254.  
  255.  
  256. class GetMetadata(QObject):
  257.     
  258.     def __init__(self):
  259.         QObject.__init__(self)
  260.         self.connect(self, SIGNAL('edispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'), self._get_metadata, Qt.QueuedConnection)
  261.         self.connect(self, SIGNAL('idispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'), self._from_formats, Qt.QueuedConnection)
  262.  
  263.     
  264.     def __call__(self, id, *args, **kwargs):
  265.         self.emit(SIGNAL('edispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'), id, args, kwargs)
  266.  
  267.     
  268.     def from_formats(self, id, *args, **kwargs):
  269.         self.emit(SIGNAL('idispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'), id, args, kwargs)
  270.  
  271.     
  272.     def _from_formats(self, id, args, kwargs):
  273.         
  274.         try:
  275.             mi = metadata_from_formats(*args, **kwargs)
  276.         except:
  277.             mi = MetaInformation('', [
  278.                 _('Unknown')])
  279.  
  280.         self.emit(SIGNAL('metadataf(PyQt_PyObject, PyQt_PyObject)'), id, mi)
  281.  
  282.     
  283.     def _get_metadata(self, id, args, kwargs):
  284.         
  285.         try:
  286.             mi = get_metadata(*args, **kwargs)
  287.         except:
  288.             mi = MetaInformation('', [
  289.                 _('Unknown')])
  290.  
  291.         self.emit(SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'), id, mi)
  292.  
  293.  
  294.  
  295. class FileIconProvider(QFileIconProvider):
  296.     ICONS = {
  297.         'default': 'unknown',
  298.         'dir': 'dir',
  299.         'zero': 'zero',
  300.         'jpeg': 'jpeg',
  301.         'jpg': 'jpeg',
  302.         'gif': 'gif',
  303.         'png': 'png',
  304.         'bmp': 'bmp',
  305.         'svg': 'svg',
  306.         'html': 'html',
  307.         'htm': 'html',
  308.         'xhtml': 'html',
  309.         'xhtm': 'html',
  310.         'lit': 'lit',
  311.         'lrf': 'lrf',
  312.         'lrx': 'lrx',
  313.         'pdf': 'pdf',
  314.         'pdr': 'zero',
  315.         'rar': 'rar',
  316.         'zip': 'zip',
  317.         'txt': 'txt',
  318.         'prc': 'mobi',
  319.         'azw': 'mobi',
  320.         'mobi': 'mobi',
  321.         'mbp': 'zero',
  322.         'azw1': 'mobi',
  323.         'tpz': 'mobi',
  324.         'tan': 'zero',
  325.         'epub': 'epub',
  326.         'fb2': 'fb2',
  327.         'rtf': 'rtf',
  328.         'odt': 'odt' }
  329.     
  330.     def __init__(self):
  331.         QFileIconProvider.__init__(self)
  332.         self.icons = { }
  333.         for key in self.__class__.ICONS.keys():
  334.             self.icons[key] = I('mimetypes/') + self.__class__.ICONS[key] + '.svg'
  335.         
  336.         for i in ('dir', 'default', 'zero'):
  337.             self.icons[i] = QIcon(self.icons[i])
  338.         
  339.  
  340.     
  341.     def key_from_ext(self, ext):
  342.         key = None if ext in self.icons.keys() else 'default'
  343.         if key == 'default' and ext.count('.') > 0:
  344.             ext = ext.rpartition('.')[2]
  345.             key = None if ext in self.icons.keys() else 'default'
  346.         
  347.         return key
  348.  
  349.     
  350.     def cached_icon(self, key):
  351.         candidate = self.icons[key]
  352.         if isinstance(candidate, QIcon):
  353.             return candidate
  354.         icon = QIcon(candidate)
  355.         self.icons[key] = icon
  356.         return icon
  357.  
  358.     
  359.     def icon_from_ext(self, ext):
  360.         key = None(self.key_from_ext if ext else '')
  361.         return self.cached_icon(key)
  362.  
  363.     
  364.     def load_icon(self, fileinfo):
  365.         key = 'default'
  366.         icons = self.icons
  367.         if fileinfo.isSymLink():
  368.             if not fileinfo.exists():
  369.                 return icons['zero']
  370.             fileinfo = QFileInfo(fileinfo.readLink())
  371.         
  372.         if fileinfo.isDir():
  373.             key = 'dir'
  374.         else:
  375.             ext = unicode(fileinfo.completeSuffix()).lower()
  376.             key = self.key_from_ext(ext)
  377.         return self.cached_icon(key)
  378.  
  379.     
  380.     def icon(self, arg):
  381.         if isinstance(arg, QFileInfo):
  382.             return self.load_icon(arg)
  383.         if arg == QFileIconProvider.Folder:
  384.             return self.icons['dir']
  385.         if arg == QFileIconProvider.File:
  386.             return self.icons['default']
  387.         return QFileIconProvider.icon(self, arg)
  388.  
  389.  
  390. _file_icon_provider = None
  391.  
  392. def initialize_file_icon_provider():
  393.     global _file_icon_provider
  394.     if _file_icon_provider is None:
  395.         _file_icon_provider = FileIconProvider()
  396.     
  397.  
  398.  
  399. def file_icon_provider():
  400.     initialize_file_icon_provider()
  401.     return _file_icon_provider
  402.  
  403.  
  404. class FileDialog(QObject):
  405.     
  406.     def __init__(self, title = _('Choose Files'), filters = [], add_all_files_filter = True, parent = None, modal = True, name = '', mode = QFileDialog.ExistingFiles, default_dir = '~'):
  407.         QObject.__init__(self)
  408.         ftext = ''
  409.         if filters:
  410.             for filter in filters:
  411.                 (text, extensions) = filter
  412.                 extensions = [ _[1] + '*' if i.startswith('.') else '.' + i for i in extensions ]
  413.                 ftext += '%s (%s);;' % (text, ' '.join(extensions))
  414.             
  415.         
  416.         if add_all_files_filter or not ftext:
  417.             ftext += 'All files (*)'
  418.         
  419.         if ftext.endswith(';;'):
  420.             ftext = ftext[:-2]
  421.         
  422.         self.dialog_name = None if name else 'dialog_' + title
  423.         self.selected_files = None
  424.         self.fd = None
  425.         initial_dir = dynamic.get(self.dialog_name, os.path.expanduser(default_dir))
  426.         if not isinstance(initial_dir, basestring):
  427.             initial_dir = os.path.expanduser(default_dir)
  428.         
  429.         self.selected_files = []
  430.         if mode == QFileDialog.AnyFile:
  431.             f = unicode(QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, ''))
  432.             if f and os.path.exists(f):
  433.                 self.selected_files.append(f)
  434.             
  435.         elif mode == QFileDialog.ExistingFile:
  436.             f = unicode(QFileDialog.getOpenFileName(parent, title, initial_dir, ftext, ''))
  437.             if f and os.path.exists(f):
  438.                 self.selected_files.append(f)
  439.             
  440.         elif mode == QFileDialog.ExistingFiles:
  441.             fs = QFileDialog.getOpenFileNames(parent, title, initial_dir, ftext, '')
  442.             for f in fs:
  443.                 f = unicode(f)
  444.                 if f and os.path.exists(f):
  445.                     self.selected_files.append(f)
  446.                     continue
  447.             
  448.         elif mode == QFileDialog.DirectoryOnly:
  449.             pass
  450.         
  451.         opts = QFileDialog.Option()
  452.         f = unicode(QFileDialog.getExistingDirectory(parent, title, initial_dir, opts))
  453.         if os.path.exists(f):
  454.             self.selected_files.append(f)
  455.         
  456.         if self.selected_files:
  457.             self.selected_files = [ unicode(q) for q in self.selected_files ]
  458.             saved_loc = self.selected_files[0]
  459.             dynamic[self.dialog_name] = saved_loc
  460.         
  461.         self.accepted = bool(self.selected_files)
  462.  
  463.     
  464.     def get_files(self):
  465.         if self.selected_files is None:
  466.             return tuple((lambda .0: for i in .0:
  467. os.path.abspath(unicode(i)))(self.fd.selectedFiles()))
  468.         return tuple(self.selected_files)
  469.  
  470.  
  471.  
  472. def choose_dir(window, name, title, default_dir = '~'):
  473.     fd = FileDialog(title = title, filters = [], add_all_files_filter = False, parent = window, name = name, mode = QFileDialog.DirectoryOnly, default_dir = default_dir)
  474.     dir = fd.get_files()
  475.     if dir:
  476.         return dir[0]
  477.  
  478.  
  479. def choose_files(window, name, title, filters = [], all_files = True, select_only_single_file = False):
  480.     mode = None if select_only_single_file else QFileDialog.ExistingFiles
  481.     fd = FileDialog(title = title, name = name, filters = filters, parent = window, add_all_files_filter = all_files, mode = mode)
  482.     if fd.accepted:
  483.         return fd.get_files()
  484.  
  485.  
  486. def choose_images(window, name, title, select_only_single_file = True):
  487.     mode = None if select_only_single_file else QFileDialog.ExistingFiles
  488.     fd = FileDialog(title = title, name = name, filters = [
  489.         ('Images', [
  490.             'png',
  491.             'gif',
  492.             'jpeg',
  493.             'jpg',
  494.             'svg'])], parent = window, add_all_files_filter = False, mode = mode)
  495.     if fd.accepted:
  496.         return fd.get_files()
  497.  
  498.  
  499. def pixmap_to_data(pixmap, format = 'JPEG'):
  500.     ba = QByteArray()
  501.     buf = QBuffer(ba)
  502.     buf.open(QBuffer.WriteOnly)
  503.     pixmap.save(buf, format)
  504.     return bytes(ba.data())
  505.  
  506.  
  507. class ResizableDialog(QDialog):
  508.     
  509.     def __init__(self, *args, **kwargs):
  510.         QDialog.__init__(self, *args)
  511.         self.setupUi(self)
  512.         nh = min_available_height() - 25
  513.         nw = available_width() - 10
  514.         if nh < 0:
  515.             nh = 800
  516.         
  517.         if nw < 0:
  518.             nw = 600
  519.         
  520.         nh = min(self.height(), nh)
  521.         nw = min(self.width(), nw)
  522.         self.resize(nw, nh)
  523.  
  524.  
  525. gui_thread = None
  526. qt_app = None
  527.  
  528. class Application(QApplication):
  529.     
  530.     def __init__(self, args):
  531.         global gui_thread, qt_app
  532.         qargs = [ _[1] if isinstance(i, unicode) else i for i in args ]
  533.         QApplication.__init__(self, qargs)
  534.         self.file_event_hook = None
  535.         gui_thread = QThread.currentThread()
  536.         self._translator = None
  537.         self.load_translations()
  538.         qt_app = self
  539.         self._file_open_paths = []
  540.         self._file_open_lock = RLock()
  541.         if islinux:
  542.             self.setStyleSheet('\n                    QToolTip {\n                        border: 2px solid black;\n                        padding: 5px;\n                        border-radius: 10px;\n                        opacity: 200;\n                        background-color: #e1e1ff;\n                        color: black;\n                    }\n            ')
  543.         
  544.  
  545.     
  546.     def _send_file_open_events(self):
  547.         self._file_open_lock.__enter__()
  548.         
  549.         try:
  550.             if self._file_open_paths:
  551.                 self.file_event_hook(self._file_open_paths)
  552.                 self._file_open_paths = []
  553.         finally:
  554.             pass
  555.  
  556.  
  557.     
  558.     def load_translations(self):
  559.         if self._translator is not None:
  560.             self.removeTranslator(self._translator)
  561.         
  562.         self._translator = QTranslator(self)
  563.         if set_qt_translator(self._translator):
  564.             self.installTranslator(self._translator)
  565.         
  566.  
  567.     
  568.     def event(self, e):
  569.         if callable(self.file_event_hook) and e.type() == QEvent.FileOpen:
  570.             path = unicode(e.file())
  571.             return True
  572.         return QApplication.event(self, e)
  573.  
  574.  
  575. _store_app = None
  576.  
  577. def open_url(qurl):
  578.     paths = os.environ.get('LD_LIBRARY_PATH', '').split(os.pathsep)
  579.     paths = _[1]
  580.     QDesktopServices.openUrl(qurl)
  581.  
  582.  
  583. def open_local_file(path):
  584.     if iswindows:
  585.         os.startfile(os.path.normpath(path))
  586.     else:
  587.         url = QUrl.fromLocalFile(path)
  588.         open_url(url)
  589.  
  590.  
  591. def is_ok_to_use_qt():
  592.     global _store_app, gui_thread
  593.     if (islinux or isfreebsd) and ':' not in os.environ.get('DISPLAY', ''):
  594.         return False
  595.     if _store_app is None and QApplication.instance() is None:
  596.         _store_app = QApplication([])
  597.     
  598.     if gui_thread is None:
  599.         gui_thread = QThread.currentThread()
  600.     
  601.     return gui_thread is QThread.currentThread()
  602.  
  603.  
  604. def is_gui_thread():
  605.     return gui_thread is QThread.currentThread()
  606.  
  607.  
  608. def find_forms(srcdir):
  609.     base = os.path.join(srcdir, 'calibre', 'gui2')
  610.     forms = []
  611.     for root, _, files in os.walk(base):
  612.         for name in files:
  613.             if name.endswith('.ui'):
  614.                 forms.append(os.path.abspath(os.path.join(root, name)))
  615.                 continue
  616.         
  617.     
  618.     return forms
  619.  
  620.  
  621. def form_to_compiled_form(form):
  622.     return form.rpartition('.')[0] + '_ui.py'
  623.  
  624.  
  625. def build_forms(srcdir, info = None):
  626.     import re
  627.     import cStringIO
  628.     compileUi = compileUi
  629.     import PyQt4.uic
  630.     forms = find_forms(srcdir)
  631.     if info is None:
  632.         prints = prints
  633.         import calibre
  634.         info = prints
  635.     
  636.     pat = re.compile('([\'"]):/images/([^\'"]+)\\1')
  637.     
  638.     def sub(match):
  639.         ans = 'I(%s%s%s)' % (match.group(1), match.group(2), match.group(1))
  640.         return ans
  641.  
  642.     for form in forms:
  643.         compiled_form = form_to_compiled_form(form)
  644.         if not os.path.exists(compiled_form) or os.stat(form).st_mtime > os.stat(compiled_form).st_mtime:
  645.             info('\tCompiling form', form)
  646.             buf = cStringIO.StringIO()
  647.             compileUi(form, buf)
  648.             dat = buf.getvalue()
  649.             dat = dat.replace('__appname__', 'calibre')
  650.             dat = dat.replace('import images_rc', '')
  651.             dat = dat.replace('from library import', 'from calibre.gui2.library import')
  652.             dat = dat.replace('from widgets import', 'from calibre.gui2.widgets import')
  653.             dat = dat.replace('from convert.xpath_wizard import', 'from calibre.gui2.convert.xpath_wizard import')
  654.             dat = re.compile('QtGui.QApplication.translate\\(.+?,\\s+"(.+?)(?<!\\\\)",.+?\\)', re.DOTALL).sub('_("\\1")', dat)
  655.             dat = dat.replace('_("MMM yyyy")', '"MMM yyyy"')
  656.             dat = pat.sub(sub, dat)
  657.             if form.endswith('viewer%smain.ui' % os.sep):
  658.                 info('\t\tPromoting WebView')
  659.                 dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
  660.                 dat = dat.replace('self.view = QWebView(', 'self.view = DocumentView(')
  661.                 dat = dat.replace('from QtWebKit.QWebView import QWebView', 'from PyQt4 import QtWebKit\nfrom PyQt4.QtWebKit import QWebView')
  662.                 dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
  663.             
  664.             open(compiled_form, 'wb').write(dat)
  665.             continue
  666.     
  667.  
  668. _df = os.environ.get('CALIBRE_DEVELOP_FROM', None)
  669. if _df and os.path.exists(_df):
  670.     build_forms(_df)
  671.  
  672.