home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.6) from __future__ import with_statement __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en' import collections import os import sys import textwrap import time from Queue import Queue, Empty from threading import Thread from PyQt4.Qt import Qt, SIGNAL, QTimer, QPixmap, QMenu, QIcon, pyqtSignal, QDialog, QSystemTrayIcon, QApplication, QKeySequence, QAction, QMessageBox, QHelpEvent from calibre import prints from calibre.constants import __appname__, isosx from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.config import prefs, dynamic from calibre.utils.ipc.server import Server from calibre.gui2 import error_dialog, GetMetadata, open_local_file, gprefs, max_available_height, config, info_dialog, Dispatcher from calibre.gui2.cover_flow import CoverFlowMixin from calibre.gui2.widgets import ProgressIndicator from calibre.gui2.update import UpdateMixin from calibre.gui2.main_window import MainWindow from calibre.gui2.layout import MainWindowMixin from calibre.gui2.device import DeviceMixin from calibre.gui2.jobs import JobManager, JobsDialog, JobsButton from calibre.gui2.dialogs.config import ConfigDialog from calibre.gui2.dialogs.book_info import BookInfo from calibre.library.database2 import LibraryDatabase2 from calibre.gui2.init import LibraryViewMixin, LayoutMixin from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin from calibre.gui2.tag_view import TagBrowserMixin from calibre.gui2.actions import AnnotationsAction, AddAction, DeleteAction, EditMetadataAction, SaveToDiskAction, GenerateCatalogAction, FetchNewsAction, ConvertAction, ViewAction class Listener(Thread): def __init__(self, listener): Thread.__init__(self) self.daemon = True self.listener = listener self.queue = Queue() self._run = True self.start() def run(self): while self._run: try: conn = self.listener.accept() msg = conn.recv() self.queue.put(msg) continue continue continue def close(self): self._run = False try: self.listener.close() except: pass class SystemTrayIcon(QSystemTrayIcon): tooltip_requested = pyqtSignal(object) def __init__(self, icon, parent): QSystemTrayIcon.__init__(self, icon, parent) def event(self, ev): if ev.type() == ev.ToolTip: evh = QHelpEvent(ev) self.tooltip_requested.emit((self, evh.globalPos())) return True return QSystemTrayIcon.event(self, ev) class Main(MainWindow, MainWindowMixin, DeviceMixin, TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin, SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin, AnnotationsAction, AddAction, DeleteAction, EditMetadataAction, SaveToDiskAction, GenerateCatalogAction, FetchNewsAction, ConvertAction, ViewAction): def __init__(self, opts, parent = None): MainWindow.__init__(self, opts, parent) self.opts = opts def initialize(self, library_path, db, listener, actions): opts = self.opts (self.preferences_action, self.quit_action) = actions self.library_path = library_path self.content_server = None self.spare_servers = [] self.must_restart_before_config = False fontconfig = fontconfig import calibre.utils.fonts self.fc = fontconfig self.listener = Listener(listener) self.check_messages_timer = QTimer() self.connect(self.check_messages_timer, SIGNAL('timeout()'), self.another_instance_wants_to_talk) self.check_messages_timer.start(1000) MainWindowMixin.__init__(self, db) self.job_manager = JobManager() self.jobs_dialog = JobsDialog(self, self.job_manager) self.jobs_button = JobsButton(horizontal = True) self.jobs_button.initialize(self.jobs_dialog, self.job_manager) LayoutMixin.__init__(self) DeviceMixin.__init__(self) self.restriction_count_of_books_in_view = 0 self.restriction_count_of_books_in_library = 0 self.restriction_in_effect = False self.progress_indicator = ProgressIndicator(self) self.progress_indicator.pos = (0, 20) self.verbose = opts.verbose self.get_metadata = GetMetadata() self.upload_memory = { } self.delete_memory = { } self.conversion_jobs = { } self.persistent_files = [] self.metadata_dialogs = [] self.default_thumbnail = None self.tb_wrapper = textwrap.TextWrapper(width = 40) self.viewers = collections.deque() self.system_tray_icon = SystemTrayIcon(QIcon(I('library.png')), self) self.system_tray_icon.setToolTip('calibre') self.system_tray_icon.tooltip_requested.connect(self.job_manager.show_tooltip) if not config['systray_icon']: self.system_tray_icon.hide() else: self.system_tray_icon.show() self.system_tray_menu = QMenu(self) self.restore_action = self.system_tray_menu.addAction(QIcon(I('page.svg')), _('&Restore')) self.donate_action = self.system_tray_menu.addAction(QIcon(I('donate.svg')), _('&Donate to support calibre')) self.donate_button.setDefaultAction(self.donate_action) self.donate_button.setStatusTip(self.donate_button.toolTip()) self.eject_action = self.system_tray_menu.addAction(QIcon(I('eject.svg')), _('&Eject connected device')) self.eject_action.setEnabled(False) self.addAction(self.quit_action) self.action_restart = QAction(_('&Restart'), self) self.addAction(self.action_restart) self.system_tray_menu.addAction(self.quit_action) self.quit_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q)) self.action_restart.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R)) self.action_show_book_details.setShortcut(QKeySequence(Qt.Key_I)) self.addAction(self.action_show_book_details) self.system_tray_icon.setContextMenu(self.system_tray_menu) self.connect(self.quit_action, SIGNAL('triggered(bool)'), self.quit) self.connect(self.donate_action, SIGNAL('triggered(bool)'), self.donate) self.connect(self.restore_action, SIGNAL('triggered()'), self.show_windows) self.connect(self.action_show_book_details, SIGNAL('triggered(bool)'), self.show_book_info) self.connect(self.action_restart, SIGNAL('triggered()'), self.restart) self.connect(self.system_tray_icon, SIGNAL('activated(QSystemTrayIcon::ActivationReason)'), self.system_tray_icon_activated) QTimer.singleShot(1000, self.add_spare_server) self.location_manager.location_selected.connect(self.location_selected) self.location_manager.unmount_device.connect(self.device_manager.umount_device) self.eject_action.triggered.connect(self.device_manager.umount_device) UpdateMixin.__init__(self, opts) SavedSearchBoxMixin.__init__(self, db) SearchBoxMixin.__init__(self) LibraryViewMixin.__init__(self, db) self.show() if self.system_tray_icon.isVisible() and opts.start_in_tray: self.hide_windows() for t in (self.tool_bar,): self.library_view.model().count_changed_signal.connect(t.count_changed) if not gprefs.get('quick_start_guide_added', False): MetaInformation = MetaInformation import calibre.ebooks.metadata mi = MetaInformation(_('Calibre Quick Start Guide'), [ 'John Schember']) mi.author_sort = 'Schember, John' mi.comments = 'A guide to get you up and running with calibre' mi.publisher = 'calibre' self.library_view.model().add_books([ P('quick_start.epub')], [ 'epub'], [ mi]) gprefs['quick_start_guide_added'] = True self.library_view.model().books_added(1) if hasattr(self, 'db_images'): self.db_images.reset() self.library_view.model().count_changed() self.tool_bar.database_changed(self.library_view.model().db) self.library_view.model().database_changed.connect(self.tool_bar.database_changed, type = Qt.QueuedConnection) TagBrowserMixin.__init__(self, db) SearchRestrictionMixin.__init__(self) CoverFlowMixin.__init__(self) self._calculated_available_height = min(max_available_height() - 15, self.height()) self.resize(self.width(), self._calculated_available_height) if config['autolaunch_server']: self.start_content_server() self.keyboard_interrupt.connect(self.quit, type = Qt.QueuedConnection) AddAction.__init__(self) self.read_settings() self.finalize_layout() self.donate_button.start_animation() self.scheduler.delete_old_news.connect(self.library_view.model().delete_books_by_id, type = Qt.QueuedConnection) def start_content_server(self): start_threaded_server = start_threaded_server import calibre.library.server.main server_config = server_config import calibre.library.server self.content_server = start_threaded_server(self.library_view.model().db, server_config().parse()) self.content_server.state_callback = Dispatcher(self.content_server_state_changed) self.content_server.state_callback(True) self.test_server_timer = QTimer.singleShot(10000, self.test_server) def resizeEvent(self, ev): MainWindow.resizeEvent(self, ev) self.search.setMaximumWidth(self.width() - 150) def add_spare_server(self, *args): self.spare_servers.append(Server(limit = int(config['worker_limit'] / 2))) def spare_server(self): if not hasattr(self, '__spare_server_property_limiter'): self._Main__spare_server_property_limiter = True return None try: QTimer.singleShot(1000, self.add_spare_server) return self.spare_servers.pop() except: hasattr(self, '__spare_server_property_limiter') spare_server = property(spare_server) def no_op(self, *args): pass def system_tray_icon_activated(self, r): if r == QSystemTrayIcon.Trigger: if self.isVisible(): self.hide_windows() else: self.show_windows() def hide_windows(self): for window in QApplication.topLevelWidgets(): if isinstance(window, (MainWindow, QDialog)) and window.isVisible(): window.hide() setattr(window, '__systray_minimized', True) continue def show_windows(self): for window in QApplication.topLevelWidgets(): if getattr(window, '__systray_minimized', False): window.show() setattr(window, '__systray_minimized', False) continue def test_server(self, *args): if self.content_server is not None and self.content_server.exception is not None: error_dialog(self, _('Failed to start content server'), unicode(self.content_server.exception)).exec_() def another_instance_wants_to_talk(self): try: msg = self.listener.queue.get_nowait() except Empty: return None if msg.startswith('launched:'): argv = eval(msg[len('launched:'):]) if len(argv) > 1: path = os.path.abspath(argv[1]) if os.access(path, os.R_OK): self.add_filesystem_book(path) self.setWindowState(self.windowState() & ~(Qt.WindowMinimized) | Qt.WindowActive) self.show_windows() self.raise_() self.activateWindow() elif msg.startswith('refreshdb:'): self.library_view.model().refresh() self.library_view.model().research() else: print msg def current_view(self): idx = self.stack.currentIndex() if idx == 0: return self.library_view if idx == 1: return self.memory_view if idx == 2: return self.card_a_view if idx == 3: return self.card_b_view def booklists(self): return (self.memory_view.model().db, self.card_a_view.model().db, self.card_b_view.model().db) def do_config(self, checked = False, initial_category = 'general'): if self.job_manager.has_jobs(): d = error_dialog(self, _('Cannot configure'), _('Cannot configure while there are running jobs.')) d.exec_() return None if self.must_restart_before_config: d = error_dialog(self, _('Cannot configure'), _('Cannot configure before calibre is restarted.')) d.exec_() return None d = ConfigDialog(self, self.library_view, server = self.content_server, initial_category = initial_category) d.exec_() self.content_server = d.server if d.result() == d.Accepted: self.read_toolbar_settings() self.search.search_as_you_type(config['search_as_you_type']) self.save_menu.actions()[2].setText(_('Save only %s format to disk') % prefs['output_format'].upper()) self.save_menu.actions()[3].setText(_('Save only %s format to disk in a single directory') % prefs['output_format'].upper()) self.tags_view.set_new_model() self.tags_view.recount() self.create_device_menu() self.set_device_menu_items_state(bool(self.device_connected)) self.tool_bar.apply_settings() def library_moved(self, newloc): if newloc is None: return None db = LibraryDatabase2(newloc) self.library_path = newloc self.book_on_device(None, reset = True) db.set_book_on_device_func(self.book_on_device) self.library_view.set_database(db) self.tags_view.set_database(db, self.tag_match, self.sort_by) self.library_view.model().set_book_on_device_func(self.book_on_device) self.status_bar.clear_message() self.search.clear_to_help() self.saved_search.clear_to_help() self.book_details.reset_info() self.library_view.model().count_changed() self.scheduler.database_changed(db) prefs['library_path'] = self.library_path def show_book_info(self, *args): if self.current_view() is not self.library_view: error_dialog(self, _('No detailed info available'), _('No detailed information is available for books on the device.')).exec_() return None index = self.library_view.currentIndex() if index.isValid(): BookInfo(self, self.library_view, index).show() def location_selected(self, location): if location == 'library': pass elif location == 'main': pass elif location == 'carda': pass page = 3 self.stack.setCurrentIndex(page) self.book_details.reset_info() for x in ('tb', 'cb'): splitter = getattr(self, x + '_splitter') splitter.button.setEnabled(location == 'library') if location == 'library': self.action_edit.setEnabled(True) self.action_merge.setEnabled(True) self.action_convert.setEnabled(True) self.view_menu.actions()[1].setEnabled(True) self.action_open_containing_folder.setEnabled(True) self.action_sync.setEnabled(True) self.search_restriction.setEnabled(True) for action in list(self.delete_menu.actions())[1:]: action.setEnabled(True) else: self.action_edit.setEnabled(False) self.action_merge.setEnabled(False) self.action_convert.setEnabled(False) self.view_menu.actions()[1].setEnabled(False) self.action_open_containing_folder.setEnabled(False) self.action_sync.setEnabled(False) self.search_restriction.setEnabled(False) for action in list(self.delete_menu.actions())[1:]: action.setEnabled(False) self.current_view().reset() self.set_number_of_books_shown() def job_exception(self, job): if not hasattr(self, '_modeless_dialogs'): self._modeless_dialogs = [] if self.isVisible(): for x in list(self._modeless_dialogs): if not x.isVisible(): self._modeless_dialogs.remove(x) continue try: if 'calibre.ebooks.DRMError' in job.details: d = error_dialog(self, _('Conversion Error'), _('<p>Could not convert: %s<p>It is a <a href="%s">DRM</a>ed book. You must first remove the DRM using third party tools.') % (job.description.split(':')[-1], 'http://bugs.calibre-ebook.com/wiki/DRM')) d.setModal(False) d.show() self._modeless_dialogs.append(d) return None if 'calibre.web.feeds.input.RecipeDisabled' in job.details: msg = job.details msg = msg[msg.find('calibre.web.feeds.input.RecipeDisabled:'):] msg = msg.partition(':')[-1] d = error_dialog(self, _('Recipe Disabled'), '<p>%s</p>' % msg) d.setModal(False) d.show() self._modeless_dialogs.append(d) return None except: pass if job.killed: return None try: prints(job.details, file = sys.stderr) except: job.killed d = error_dialog(self, _('Conversion Error'), _('<b>Failed</b>') + ': ' + unicode(job.description), det_msg = job.details) d.setModal(False) d.show() self._modeless_dialogs.append(d) def read_settings(self): geometry = config['main_window_geometry'] if geometry is not None: self.restoreGeometry(geometry) self.read_toolbar_settings() self.read_layout_settings() def write_settings(self): config.set('main_window_geometry', self.saveGeometry()) dynamic.set('sort_history', self.library_view.model().sort_history) self.save_layout_state() def restart(self): self.quit(restart = True) def quit(self, checked = True, restart = False): if not self.confirm_quit(): return None try: self.shutdown() except: self.confirm_quit() self.restart_after_quit = restart QApplication.instance().quit() def donate(self, *args): BUTTON = '\n <form action="https://www.paypal.com/cgi-bin/webscr" method="post">\n <input type="hidden" name="cmd" value="_s-xclick" />\n <input type="hidden" name="hosted_button_id" value="3029467" />\n <input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="Donate to support calibre development" />\n <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />\n </form>\n ' MSG = _('is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development. Your donation helps keep calibre development going.') HTML = u'\n <html>\n <head>\n <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />\n <title>Donate to support calibre</title>\n </head>\n <body style="background:white">\n <div><a href="http://calibre-ebook.com"><img style="border:0px"\n src="file://%s" alt="calibre" /></a></div>\n <p>Calibre %s</p>\n %s\n </body>\n </html>\n ' % (P('content_server/calibre_banner.png').replace(os.sep, '/'), MSG, BUTTON) pt = PersistentTemporaryFile('_donate.htm') pt.write(HTML.encode('utf-8')) pt.close() open_local_file(pt.name) def confirm_quit(self): if self.job_manager.has_jobs(): msg = _('There are active jobs. Are you sure you want to quit?') if self.job_manager.has_device_jobs(): msg = '<p>' + __appname__ + _(' is communicating with the device!<br>\n Quitting may cause corruption on the device.<br>\n Are you sure you want to quit?') + '</p>' d = QMessageBox(QMessageBox.Warning, _('WARNING: Active jobs'), msg, QMessageBox.Yes | QMessageBox.No, self) d.setIconPixmap(QPixmap(I('dialog_warning.svg'))) d.setDefaultButton(QMessageBox.No) if d.exec_() != QMessageBox.Yes: return False return True def shutdown(self, write_settings = True): if write_settings: self.write_settings() self.check_messages_timer.stop() self.update_checker.terminate() self.listener.close() self.job_manager.server.close() while self.spare_servers: self.spare_servers.pop().close() self.device_manager.keep_going = False cc = self.library_view.model().cover_cache if cc is not None: cc.stop() self.hide_windows() self.emailer.stop() try: try: if self.content_server is not None: s = self.content_server self.content_server = None s.exit() except: pass time.sleep(2) except KeyboardInterrupt: pass self.hide_windows() return True def run_wizard(self, *args): if self.confirm_quit(): self.run_wizard_b4_shutdown = True self.restart_after_quit = True try: self.shutdown(write_settings = False) except: pass QApplication.instance().quit() def closeEvent(self, e): self.write_settings() if self.system_tray_icon.isVisible(): if not dynamic['systray_msg'] and not isosx: info_dialog(self, 'calibre', 'calibre ' + _('will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray.')).exec_() dynamic['systray_msg'] = True self.hide_windows() e.ignore() elif self.confirm_quit(): try: self.shutdown(write_settings = False) except: pass e.accept() else: e.ignore()