#!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' import functools, sys, os from PyQt4.Qt import Qt, QStackedWidget, QMenu, \ QSize, QSizePolicy, QStatusBar, QLabel, QFont from calibre.utils.config import prefs from calibre.constants import isosx, __appname__, preferred_encoding, \ __version__ from calibre.gui2 import config, is_widescreen, gprefs from calibre.gui2.library.views import BooksView, DeviceBooksView from calibre.gui2.widgets import Splitter from calibre.gui2.tag_view import TagBrowserWidget from calibre.gui2.book_details import BookDetails from calibre.gui2.notify import get_notifier _keep_refs = [] def partial(*args, **kwargs): ans = functools.partial(*args, **kwargs) _keep_refs.append(ans) return ans class LibraryViewMixin(object): # {{{ def __init__(self, db): self.library_view.files_dropped.connect(self.iactions['Add Books'].files_dropped, type=Qt.QueuedConnection) for func, args in [ ('connect_to_search_box', (self.search, self.search_done)), ('connect_to_book_display', (self.book_details.show_data,)), ]: for view in (self.library_view, self.memory_view, self.card_a_view, self.card_b_view): getattr(view, func)(*args) self.memory_view.connect_dirtied_signal(self.upload_booklists) self.memory_view.connect_upload_collections_signal( func=self.upload_collections, oncard=None) self.card_a_view.connect_dirtied_signal(self.upload_booklists) self.card_a_view.connect_upload_collections_signal( func=self.upload_collections, oncard='carda') self.card_b_view.connect_dirtied_signal(self.upload_booklists) self.card_b_view.connect_upload_collections_signal( func=self.upload_collections, oncard='cardb') self.book_on_device(None, reset=True) db.set_book_on_device_func(self.book_on_device) self.library_view.set_database(db) self.library_view.model().set_book_on_device_func(self.book_on_device) prefs['library_path'] = self.library_path for view in ('library', 'memory', 'card_a', 'card_b'): view = getattr(self, view+'_view') view.verticalHeader().sectionDoubleClicked.connect(self.iactions['View'].view_specific_book) self.build_context_menus() def build_context_menus(self): lm = QMenu(self) def populate_menu(m, items): for what in items: if what is None: m.addSeparator() elif what in self.iactions: m.addAction(self.iactions[what].qaction) populate_menu(lm, gprefs['action-layout-context-menu']) dm = QMenu(self) populate_menu(dm, gprefs['action-layout-context-menu-device']) ec = self.iactions['Edit Collections'].qaction self.library_view.set_context_menu(lm, ec) for v in (self.memory_view, self.card_a_view, self.card_b_view): v.set_context_menu(dm, ec) def search_done(self, view, ok): if view is self.current_view(): self.search.search_done(ok) self.set_number_of_books_shown() # }}} class LibraryWidget(Splitter): # {{{ def __init__(self, parent): orientation = Qt.Vertical if config['gui_layout'] == 'narrow': orientation = Qt.Horizontal if is_widescreen() else Qt.Vertical idx = 0 if orientation == Qt.Vertical else 1 size = 300 if orientation == Qt.Vertical else 550 Splitter.__init__(self, 'cover_browser_splitter', _('Cover Browser'), I('cover_flow.png'), orientation=orientation, parent=parent, connect_button=not config['separate_cover_flow'], side_index=idx, initial_side_size=size, initial_show=False, shortcut=_('Shift+Alt+B')) parent.library_view = BooksView(parent) parent.library_view.setObjectName('library_view') self.addWidget(parent.library_view) # }}} class Stack(QStackedWidget): # {{{ def __init__(self, parent): QStackedWidget.__init__(self, parent) parent.cb_splitter = LibraryWidget(parent) self.tb_widget = TagBrowserWidget(parent) parent.tb_splitter = Splitter('tag_browser_splitter', _('Tag Browser'), I('tags.png'), parent=parent, side_index=0, initial_side_size=200, shortcut=_('Shift+Alt+T')) parent.tb_splitter.addWidget(self.tb_widget) parent.tb_splitter.addWidget(parent.cb_splitter) parent.tb_splitter.setCollapsible(parent.tb_splitter.other_index, False) self.addWidget(parent.tb_splitter) for x in ('memory', 'card_a', 'card_b'): name = x+'_view' w = DeviceBooksView(parent) setattr(parent, name, w) self.addWidget(w) w.setObjectName(name) # }}} class StatusBar(QStatusBar): # {{{ def __init__(self, parent=None): QStatusBar.__init__(self, parent) self.default_message = __appname__ + ' ' + _('version') + ' ' + \ self.get_version() + ' ' + _('created by Kovid Goyal') self.device_string = '' self.update_label = QLabel('') self.update_label.setOpenExternalLinks(True) self.addPermanentWidget(self.update_label) self.update_label.setVisible(False) self._font = QFont() self._font.setBold(True) self.setFont(self._font) def initialize(self, systray=None): self.systray = systray self.notifier = get_notifier(systray) self.messageChanged.connect(self.message_changed, type=Qt.QueuedConnection) self.message_changed('') def device_connected(self, devname): self.device_string = _('Connected ') + devname self.clearMessage() def device_disconnected(self): self.device_string = '' self.clearMessage() def new_version_available(self, ver, url): msg = (u'%s: %s') % ( _('Update found'), url, ver) self.update_label.setText(msg) self.update_label.setCursor(Qt.PointingHandCursor) self.update_label.setVisible(True) def get_version(self): dv = os.environ.get('CALIBRE_DEVELOP_FROM', None) v = __version__ if getattr(sys, 'frozen', False) and dv and os.path.abspath(dv) in sys.path: v += '*' return v def show_message(self, msg, timeout=0): self.showMessage(msg, timeout) if self.notifier is not None and not config['disable_tray_notification']: if isosx and isinstance(msg, unicode): try: msg = msg.encode(preferred_encoding) except UnicodeEncodeError: msg = msg.encode('utf-8') self.notifier(msg) def clear_message(self): self.clearMessage() def message_changed(self, msg): if not msg or msg.isEmpty() or msg.isNull() or \ not unicode(msg).strip(): extra = '' if self.device_string: extra = ' ..::.. ' + self.device_string self.showMessage(self.default_message + extra) # }}} class LayoutMixin(object): # {{{ def __init__(self): if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), orientation=Qt.Vertical, parent=self, side_index=1, shortcut=_('Alt+D')) self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1, shortcut=_('Shift+Alt+D')) self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) # }}} self.status_bar = StatusBar(self) for x in ('cb', 'tb', 'bd'): button = getattr(self, x+'_splitter').button button.setIconSize(QSize(24, 24)) self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book) self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id) m = self.library_view.model() if m.rowCount(None) > 0: self.library_view.set_current_row(0) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x+'_splitter') s.update_desired_state() s.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x+'_splitter').restore_state() # }}}