示例#1
0
文件: init.py 项目: Eksmo/calibre
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=_('Shift+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)
            button_order = ('tb', 'bd', 'cb')
        # }}}
        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)
            button_order = ('tb', 'cb', 'bd')
        # }}}

        self.status_bar = StatusBar(self)
        stylename = unicode(self.style().objectName())
        for x in button_order:
            button = getattr(self, x+'_splitter').button
            button.setIconSize(QSize(24, 24))
            if isosx and stylename != u'Calibre':
                button.setStyleSheet('''
                        QToolButton { background: none; border:none; padding: 0px; }
                        QToolButton:checked { background: rgba(0, 0, 0, 25%); }
                ''')
            self.status_bar.addPermanentWidget(button)
        self.status_bar.addPermanentWidget(self.jobs_button)
        self.setStatusBar(self.status_bar)
        self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)

    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.cover_changed.connect(self.bd_cover_changed,
                type=Qt.QueuedConnection)
        self.book_details.cover_removed.connect(self.bd_cover_removed,
                type=Qt.QueuedConnection)
        self.book_details.remote_file_dropped.connect(
                self.iactions['Add Books'].remote_file_dropped_on_book,
                type=Qt.QueuedConnection)
        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 bd_cover_changed(self, id_, cdata):
        self.library_view.model().db.set_cover(id_, cdata)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    def bd_cover_removed(self, id_):
        self.library_view.model().db.remove_cover(id_, commit=True,
                notify=False)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    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()
示例#2
0
文件: init.py 项目: qykth-git/calibre
class LayoutMixin:  # {{{

    def __init__(self, *args, **kwargs):
        pass

    def init_layout_mixin(self):
        self.vl_tabs = VLTabs(self)
        self.centralwidget.layout().addWidget(self.vl_tabs)

        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.Orientation.Vertical, parent=self, side_index=1,
                    shortcut='Shift+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)
            button_order = ('sb', 'tb', 'bd', 'gv', 'cb', 'qv')
        # }}}
        else:  # wide {{{
            self.bd_splitter = Splitter('book_details_splitter',
                    _('Book details'), I('book.png'), initial_side_size=200,
                    orientation=Qt.Orientation.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.Policy.Expanding,
                QSizePolicy.Policy.Expanding))
            self.centralwidget.layout().addWidget(self.bd_splitter)
            button_order = ('sb', 'tb', 'cb', 'gv', 'qv', 'bd')
        # }}}

        # This must use the base method to find the plugin because it hasn't
        # been fully initialized yet
        self.qv = find_plugin('Quickview')
        if self.qv and self.qv.actual_plugin_:
            self.qv = self.qv.actual_plugin_

        self.status_bar = StatusBar(self)
        stylename = str(self.style().objectName())
        self.grid_view_button = GridViewButton(self)
        self.search_bar_button = SearchBarButton(self)
        self.grid_view_button.toggled.connect(self.toggle_grid_view)
        self.search_bar_button.toggled.connect(self.toggle_search_bar)

        self.layout_buttons = []
        for x in button_order:
            if hasattr(self, x + '_splitter'):
                button = getattr(self, x + '_splitter').button
            else:
                if x == 'gv':
                    button = self.grid_view_button
                elif x == 'qv':
                    if self.qv is None:
                        continue
                    button = self.qv.qv_button
                else:
                    button = self.search_bar_button
            self.layout_buttons.append(button)
            button.setVisible(False)
            if ismacos and stylename != 'Calibre':
                button.setStyleSheet('''
                        QToolButton { background: none; border:none; padding: 0px; }
                        QToolButton:checked { background: rgba(0, 0, 0, 25%); }
                ''')
            self.status_bar.addPermanentWidget(button)
        if gprefs['show_layout_buttons']:
            for b in self.layout_buttons:
                b.setVisible(True)
                self.status_bar.addPermanentWidget(b)
        else:
            self.layout_button = b = QToolButton(self)
            b.setAutoRaise(True), b.setCursor(Qt.CursorShape.PointingHandCursor)
            b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
            b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
            b.setText(_('Layout')), b.setIcon(QIcon(I('config.png')))
            b.setMenu(LayoutMenu(self))
            b.setToolTip(_(
                'Show and hide various parts of the calibre main window'))
            self.status_bar.addPermanentWidget(b)
        self.status_bar.addPermanentWidget(self.jobs_button)
        self.setStatusBar(self.status_bar)
        self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)

    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.cover_changed.connect(self.bd_cover_changed,
                type=Qt.ConnectionType.QueuedConnection)
        self.book_details.open_cover_with.connect(self.bd_open_cover_with,
                type=Qt.ConnectionType.QueuedConnection)
        self.book_details.open_fmt_with.connect(self.bd_open_fmt_with,
                type=Qt.ConnectionType.QueuedConnection)
        self.book_details.edit_book.connect(self.bd_edit_book,
                type=Qt.ConnectionType.QueuedConnection)
        self.book_details.cover_removed.connect(self.bd_cover_removed,
                type=Qt.ConnectionType.QueuedConnection)
        self.book_details.remote_file_dropped.connect(
                self.iactions['Add Books'].remote_file_dropped_on_book,
                type=Qt.ConnectionType.QueuedConnection)
        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)
        self.book_details.search_requested.connect(self.set_search_string_with_append)
        self.book_details.remove_specific_format.connect(
                self.iactions['Remove Books'].remove_format_by_id)
        self.book_details.remove_metadata_item.connect(
                self.iactions['Edit Metadata'].remove_metadata_item)
        self.book_details.save_specific_format.connect(
                self.iactions['Save To Disk'].save_library_format_by_ids)
        self.book_details.restore_specific_format.connect(
            self.iactions['Remove Books'].restore_format)
        self.book_details.set_cover_from_format.connect(
            self.iactions['Edit Metadata'].set_cover_from_format)
        self.book_details.copy_link.connect(self.bd_copy_link,
                type=Qt.ConnectionType.QueuedConnection)
        self.book_details.view_device_book.connect(
                self.iactions['View'].view_device_book)
        self.book_details.manage_category.connect(self.manage_category_triggerred)
        self.book_details.find_in_tag_browser.connect(self.find_in_tag_browser_triggered)
        self.book_details.edit_identifiers.connect(self.edit_identifiers_triggerred)
        self.book_details.compare_specific_format.connect(self.compare_format)

        m = self.library_view.model()
        if m.rowCount(None) > 0:
            QTimer.singleShot(0, self.library_view.set_current_row)
            m.current_changed(self.library_view.currentIndex(),
                    self.library_view.currentIndex())
        self.library_view.setFocus(Qt.FocusReason.OtherFocusReason)

    def set_search_string_with_append(self, expression, append=''):
        current = self.search.text().strip()
        if append:
            expr = f'{current} {append} {expression}' if current else expression
        else:
            expr = expression
        self.search.set_search_string(expr)

    def edit_identifiers_triggerred(self):
        book_id = self.library_view.current_book
        db = self.current_db.new_api
        identifiers = db.field_for('identifiers', book_id, default_value={})
        from calibre.gui2.metadata.basic_widgets import Identifiers
        d = Identifiers(identifiers, self)
        if d.exec() == QDialog.DialogCode.Accepted:
            identifiers = d.get_identifiers()
            db.set_field('identifiers', {book_id: identifiers})
            self.iactions['Edit Metadata'].refresh_books_after_metadata_edit({book_id})

    def manage_category_triggerred(self, field, value):
        if field and value:
            if field == 'authors':
                self.do_author_sort_edit(self, value, select_sort=False,
                                         select_link=False, lookup_author=True)
            elif field:
                self.do_tags_list_edit(value, field)

    def find_in_tag_browser_triggered(self, field, value):
        if field and value:
            tb = self.stack.tb_widget
            tb.set_focus_to_find_box()
            tb.item_search.lineEdit().setText(field + ':=' + value)
            tb.do_find()

    def toggle_grid_view(self, show):
        self.library_view.alternate_views.show_view('grid' if show else None)
        self.sort_sep.setVisible(show)
        self.sort_button.setVisible(show)

    def toggle_search_bar(self, show):
        self.search_bar.setVisible(show)
        if show:
            self.search.setFocus(Qt.FocusReason.OtherFocusReason)

    def bd_cover_changed(self, id_, cdata):
        self.library_view.model().db.set_cover(id_, cdata)
        self.refresh_cover_browser()

    def bd_open_cover_with(self, book_id, entry):
        cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__')
        if cpath:
            if entry is None:
                open_local_file(cpath)
                return
            from calibre.gui2.open_with import run_program
            run_program(entry, cpath, self)

    def bd_open_fmt_with(self, book_id, fmt, entry):
        path = self.current_db.new_api.format_abspath(book_id, fmt)
        if path:
            from calibre.gui2.open_with import run_program
            run_program(entry, path, self)
        else:
            fmt = fmt.upper()
            error_dialog(self, _('No %s format') % fmt, _(
                'The book {0} does not have the {1} format').format(
                    self.current_db.new_api.field_for('title', book_id, default_value=_('Unknown')),
                    fmt), show=True)

    def bd_edit_book(self, book_id, fmt):
        from calibre.gui2.device import BusyCursor
        with BusyCursor():
            self.iactions['Tweak ePub'].ebook_edit_format(book_id, fmt)

    def open_with_action_triggerred(self, fmt, entry, *args):
        book_id = self.library_view.current_book
        if book_id is not None:
            if fmt == 'cover_image':
                self.bd_open_cover_with(book_id, entry)
            else:
                self.bd_open_fmt_with(book_id, fmt, entry)

    def bd_cover_removed(self, id_):
        self.library_view.model().db.remove_cover(id_, commit=True,
                notify=False)
        self.refresh_cover_browser()

    def bd_copy_link(self, url):
        if url:
            QApplication.clipboard().setText(url)

    def compare_format(self, book_id, fmt):
        db = self.current_db.new_api
        ofmt = fmt
        if fmt.startswith('ORIGINAL_'):
            fmt = fmt.partition('_')[-1]
        else:
            ofmt = 'ORIGINAL_' + fmt
        path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(book_id, fmt)
        from calibre.gui2.tweak_book.diff.main import compare_books
        compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial(
            self.iactions['Remove Books'].restore_format, book_id, ofmt), names=(ofmt, fmt))

    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()
        self.grid_view_button.save_state()
        self.search_bar_button.save_state()
        if self.qv:
            self.qv.qv_button.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()
        self.grid_view_button.restore_state()
        self.search_bar_button.restore_state()
        # Can't do quickview here because the gui isn't totally set up. Do it in ui

    def update_status_bar(self, *args):
        v = self.current_view()
        selected = len(v.selectionModel().selectedRows())
        library_total, total, current = v.model().counts()
        self.status_bar.update_state(library_total, total, current, selected)
示例#3
0
class LayoutMixin(object):  # {{{
    def __init__(self, *args, **kwargs):
        pass

    def init_layout_mixin(self):
        self.vl_tabs = VLTabs(self)
        self.centralwidget.layout().addWidget(self.vl_tabs)

        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='Shift+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)
            button_order = ('tb', 'bd', 'gv', 'cb')
        # }}}
        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)
            button_order = ('tb', 'cb', 'gv', 'bd')
        # }}}

        self.status_bar = StatusBar(self)
        stylename = unicode(self.style().objectName())
        self.grid_view_button = GridViewButton(self)
        self.grid_view_button.toggled.connect(self.toggle_grid_view)

        for x in button_order:
            button = self.grid_view_button if x == 'gv' else getattr(
                self, x + '_splitter').button
            button.setIconSize(QSize(24, 24))
            if isosx and stylename != u'Calibre':
                button.setStyleSheet('''
                        QToolButton { background: none; border:none; padding: 0px; }
                        QToolButton:checked { background: rgba(0, 0, 0, 25%); }
                ''')
            self.status_bar.addPermanentWidget(button)
        self.status_bar.addPermanentWidget(self.jobs_button)
        self.setStatusBar(self.status_bar)
        self.status_bar.update_label.linkActivated.connect(
            self.update_link_clicked)

    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.cover_changed.connect(self.bd_cover_changed,
                                                type=Qt.QueuedConnection)
        self.book_details.open_cover_with.connect(self.bd_open_cover_with,
                                                  type=Qt.QueuedConnection)
        self.book_details.open_fmt_with.connect(self.bd_open_fmt_with,
                                                type=Qt.QueuedConnection)
        self.book_details.cover_removed.connect(self.bd_cover_removed,
                                                type=Qt.QueuedConnection)
        self.book_details.remote_file_dropped.connect(
            self.iactions['Add Books'].remote_file_dropped_on_book,
            type=Qt.QueuedConnection)
        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)
        self.book_details.search_requested.connect(
            self.search.set_search_string)
        self.book_details.remove_specific_format.connect(
            self.iactions['Remove Books'].remove_format_by_id)
        self.book_details.save_specific_format.connect(
            self.iactions['Save To Disk'].save_library_format_by_ids)
        self.book_details.restore_specific_format.connect(
            self.iactions['Remove Books'].restore_format)
        self.book_details.copy_link.connect(self.bd_copy_link,
                                            type=Qt.QueuedConnection)
        self.book_details.view_device_book.connect(
            self.iactions['View'].view_device_book)
        self.book_details.manage_author.connect(
            lambda author: self.do_author_sort_edit(
                self, author, select_sort=False, select_link=False))
        self.book_details.compare_specific_format.connect(self.compare_format)

        m = self.library_view.model()
        if m.rowCount(None) > 0:
            QTimer.singleShot(0, self.library_view.set_current_row)
            m.current_changed(self.library_view.currentIndex(),
                              self.library_view.currentIndex())
        self.library_view.setFocus(Qt.OtherFocusReason)

    def toggle_grid_view(self, show):
        self.library_view.alternate_views.show_view('grid' if show else None)

    def bd_cover_changed(self, id_, cdata):
        self.library_view.model().db.set_cover(id_, cdata)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    def bd_open_cover_with(self, book_id, entry):
        cpath = self.current_db.new_api.format_abspath(book_id,
                                                       '__COVER_INTERNAL__')
        if cpath:
            from calibre.gui2.open_with import run_program
            run_program(entry, cpath, self)

    def bd_open_fmt_with(self, book_id, fmt, entry):
        path = self.current_db.new_api.format_abspath(book_id, fmt)
        if path:
            from calibre.gui2.open_with import run_program
            run_program(entry, path, self)
        else:
            fmt = fmt.upper()
            error_dialog(self,
                         _('No %s format') % fmt,
                         _('The book {0} does not have the {1} format').format(
                             self.current_db.new_api.field_for(
                                 'title', book_id, default_value=_('Unknown')),
                             fmt),
                         show=True)

    def open_with_action_triggerred(self, fmt, entry, *args):
        book_id = self.library_view.current_book
        if book_id is not None:
            if fmt == 'cover_image':
                self.bd_open_cover_with(book_id, entry)
            else:
                self.bd_open_fmt_with(book_id, fmt, entry)

    def bd_cover_removed(self, id_):
        self.library_view.model().db.remove_cover(id_,
                                                  commit=True,
                                                  notify=False)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    def bd_copy_link(self, url):
        if url:
            QApplication.clipboard().setText(url)

    def compare_format(self, book_id, fmt):
        db = self.current_db.new_api
        ofmt = fmt
        if fmt.startswith('ORIGINAL_'):
            fmt = fmt.partition('_')[-1]
        else:
            ofmt = 'ORIGINAL_' + fmt
        path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(
            book_id, fmt)
        from calibre.gui2.tweak_book.diff.main import compare_books
        compare_books(path1,
                      path2,
                      parent=self,
                      revert_msg=_('Restore %s') % ofmt,
                      revert_callback=partial(
                          self.iactions['Remove Books'].restore_format,
                          book_id, ofmt),
                      names=(ofmt, fmt))

    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()
        self.grid_view_button.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()
        self.grid_view_button.restore_state()

    def update_status_bar(self, *args):
        v = self.current_view()
        selected = len(v.selectionModel().selectedRows())
        library_total, total, current = v.model().counts()
        self.status_bar.update_state(library_total, total, current, selected)
示例#4
0
文件: init.py 项目: AEliu/calibre
class LayoutMixin(object):  # {{{

    def __init__(self, *args, **kwargs):
        pass

    def init_layout_mixin(self):
        self.vl_tabs = VLTabs(self)
        self.centralwidget.layout().addWidget(self.vl_tabs)

        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='Shift+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)
            button_order = ('tb', 'bd', 'gv', 'cb')
        # }}}
        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)
            button_order = ('tb', 'cb', 'gv', 'bd')
        # }}}

        self.status_bar = StatusBar(self)
        stylename = unicode(self.style().objectName())
        self.grid_view_button = GridViewButton(self)
        self.grid_view_button.toggled.connect(self.toggle_grid_view)

        for x in button_order:
            button = self.grid_view_button if x == 'gv' else getattr(self, x+'_splitter').button
            button.setIconSize(QSize(24, 24))
            if isosx and stylename != u'Calibre':
                button.setStyleSheet('''
                        QToolButton { background: none; border:none; padding: 0px; }
                        QToolButton:checked { background: rgba(0, 0, 0, 25%); }
                ''')
            self.status_bar.addPermanentWidget(button)
        self.status_bar.addPermanentWidget(self.jobs_button)
        self.setStatusBar(self.status_bar)
        self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)

    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.cover_changed.connect(self.bd_cover_changed,
                type=Qt.QueuedConnection)
        self.book_details.open_cover_with.connect(self.bd_open_cover_with,
                type=Qt.QueuedConnection)
        self.book_details.open_fmt_with.connect(self.bd_open_fmt_with,
                type=Qt.QueuedConnection)
        self.book_details.cover_removed.connect(self.bd_cover_removed,
                type=Qt.QueuedConnection)
        self.book_details.remote_file_dropped.connect(
                self.iactions['Add Books'].remote_file_dropped_on_book,
                type=Qt.QueuedConnection)
        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)
        self.book_details.search_requested.connect(self.search.set_search_string)
        self.book_details.remove_specific_format.connect(
                self.iactions['Remove Books'].remove_format_by_id)
        self.book_details.remove_metadata_item.connect(
                self.iactions['Edit Metadata'].remove_metadata_item)
        self.book_details.save_specific_format.connect(
                self.iactions['Save To Disk'].save_library_format_by_ids)
        self.book_details.restore_specific_format.connect(
            self.iactions['Remove Books'].restore_format)
        self.book_details.set_cover_from_format.connect(
            self.iactions['Edit Metadata'].set_cover_from_format)
        self.book_details.copy_link.connect(self.bd_copy_link,
                type=Qt.QueuedConnection)
        self.book_details.view_device_book.connect(
                self.iactions['View'].view_device_book)
        self.book_details.manage_author.connect(lambda author:self.do_author_sort_edit(self, author, select_sort=False, select_link=False))
        self.book_details.compare_specific_format.connect(self.compare_format)

        m = self.library_view.model()
        if m.rowCount(None) > 0:
            QTimer.singleShot(0, self.library_view.set_current_row)
            m.current_changed(self.library_view.currentIndex(),
                    self.library_view.currentIndex())
        self.library_view.setFocus(Qt.OtherFocusReason)

    def toggle_grid_view(self, show):
        self.library_view.alternate_views.show_view('grid' if show else None)

    def bd_cover_changed(self, id_, cdata):
        self.library_view.model().db.set_cover(id_, cdata)
        self.refresh_cover_browser()

    def bd_open_cover_with(self, book_id, entry):
        cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__')
        if cpath:
            from calibre.gui2.open_with import run_program
            run_program(entry, cpath, self)

    def bd_open_fmt_with(self, book_id, fmt, entry):
        path = self.current_db.new_api.format_abspath(book_id, fmt)
        if path:
            from calibre.gui2.open_with import run_program
            run_program(entry, path, self)
        else:
            fmt = fmt.upper()
            error_dialog(self, _('No %s format') % fmt, _(
                'The book {0} does not have the {1} format').format(
                    self.current_db.new_api.field_for('title', book_id, default_value=_('Unknown')),
                    fmt), show=True)

    def open_with_action_triggerred(self, fmt, entry, *args):
        book_id = self.library_view.current_book
        if book_id is not None:
            if fmt == 'cover_image':
                self.bd_open_cover_with(book_id, entry)
            else:
                self.bd_open_fmt_with(book_id, fmt, entry)

    def bd_cover_removed(self, id_):
        self.library_view.model().db.remove_cover(id_, commit=True,
                notify=False)
        self.refresh_cover_browser()

    def bd_copy_link(self, url):
        if url:
            QApplication.clipboard().setText(url)

    def compare_format(self, book_id, fmt):
        db = self.current_db.new_api
        ofmt = fmt
        if fmt.startswith('ORIGINAL_'):
            fmt = fmt.partition('_')[-1]
        else:
            ofmt = 'ORIGINAL_' + fmt
        path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(book_id, fmt)
        from calibre.gui2.tweak_book.diff.main import compare_books
        compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial(
            self.iactions['Remove Books'].restore_format, book_id, ofmt), names=(ofmt, fmt))

    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()
        self.grid_view_button.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()
        self.grid_view_button.restore_state()

    def update_status_bar(self, *args):
        v = self.current_view()
        selected = len(v.selectionModel().selectedRows())
        library_total, total, current = v.model().counts()
        self.status_bar.update_state(library_total, total, current, selected)
示例#5
0
文件: init.py 项目: banksps/calibre
class LayoutMixin(object):  # {{{

    def __init__(self):
        self.vl_tabs = VLTabs(self)
        self.centralwidget.layout().addWidget(self.vl_tabs)

        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=_('Shift+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)
            button_order = ('tb', 'bd', 'cb', 'gv')
        # }}}
        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)
            button_order = ('tb', 'cb', 'bd', 'gv')
        # }}}

        self.status_bar = StatusBar(self)
        stylename = unicode(self.style().objectName())
        self.grid_view_button = GridViewButton(self)
        self.grid_view_button.toggled.connect(self.toggle_grid_view)

        for x in button_order:
            button = self.grid_view_button if x == 'gv' else getattr(self, x+'_splitter').button
            button.setIconSize(QSize(24, 24))
            if isosx and stylename != u'Calibre':
                button.setStyleSheet('''
                        QToolButton { background: none; border:none; padding: 0px; }
                        QToolButton:checked { background: rgba(0, 0, 0, 25%); }
                ''')
            self.status_bar.addPermanentWidget(button)
        self.status_bar.addPermanentWidget(self.jobs_button)
        self.setStatusBar(self.status_bar)
        self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)

    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.cover_changed.connect(self.bd_cover_changed,
                type=Qt.QueuedConnection)
        self.book_details.cover_removed.connect(self.bd_cover_removed,
                type=Qt.QueuedConnection)
        self.book_details.remote_file_dropped.connect(
                self.iactions['Add Books'].remote_file_dropped_on_book,
                type=Qt.QueuedConnection)
        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)
        self.book_details.remove_specific_format.connect(
                self.iactions['Remove Books'].remove_format_by_id)
        self.book_details.save_specific_format.connect(
                self.iactions['Save To Disk'].save_library_format_by_ids)
        self.book_details.restore_specific_format.connect(
            self.iactions['Remove Books'].restore_format)
        self.book_details.copy_link.connect(self.bd_copy_link,
                type=Qt.QueuedConnection)
        self.book_details.view_device_book.connect(
                self.iactions['View'].view_device_book)
        self.book_details.manage_author.connect(lambda author:self.do_author_sort_edit(self, author, select_sort=False, select_link=False))

        m = self.library_view.model()
        if m.rowCount(None) > 0:
            QTimer.singleShot(0, self.library_view.set_current_row)
            m.current_changed(self.library_view.currentIndex(),
                    self.library_view.currentIndex())
        self.library_view.setFocus(Qt.OtherFocusReason)

    def toggle_grid_view(self, show):
        self.library_view.alternate_views.show_view('grid' if show else None)

    def bd_cover_changed(self, id_, cdata):
        self.library_view.model().db.set_cover(id_, cdata)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    def bd_cover_removed(self, id_):
        self.library_view.model().db.remove_cover(id_, commit=True,
                notify=False)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    def bd_copy_link(self, url):
        if url:
            QApplication.clipboard().setText(url)

    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()
        self.grid_view_button.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()
        self.grid_view_button.restore_state()

    def update_status_bar(self, *args):
        v = self.current_view()
        selected = len(v.selectionModel().selectedRows())
        library_total, total, current = v.model().counts()
        self.status_bar.update_state(library_total, total, current, selected)
示例#6
0
文件: init.py 项目: bjhemens/calibre
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=_('Shift+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)
            button_order = ('tb', 'bd', 'cb')
        # }}}
        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)
            button_order = ('tb', 'cb', 'bd')
        # }}}

        self.status_bar = StatusBar(self)
        stylename = unicode(self.style().objectName())
        for x in button_order:
            button = getattr(self, x + '_splitter').button
            button.setIconSize(QSize(24, 24))
            if isosx and stylename != u'Calibre':
                button.setStyleSheet('''
                        QToolButton { background: none; border:none; padding: 0px; }
                        QToolButton:checked { background: rgba(0, 0, 0, 25%); }
                ''')
            self.status_bar.addPermanentWidget(button)
        self.status_bar.addPermanentWidget(self.jobs_button)
        self.setStatusBar(self.status_bar)
        self.status_bar.update_label.linkActivated.connect(
            self.update_link_clicked)

    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.cover_changed.connect(self.bd_cover_changed,
                                                type=Qt.QueuedConnection)
        self.book_details.cover_removed.connect(self.bd_cover_removed,
                                                type=Qt.QueuedConnection)
        self.book_details.remote_file_dropped.connect(
            self.iactions['Add Books'].remote_file_dropped_on_book,
            type=Qt.QueuedConnection)
        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)
        self.book_details.remove_specific_format.connect(
            self.iactions['Remove Books'].remove_format_by_id)
        self.book_details.save_specific_format.connect(
            self.iactions['Save To Disk'].save_library_format_by_ids)
        self.book_details.restore_specific_format.connect(
            self.iactions['Remove Books'].restore_format)
        self.book_details.copy_link.connect(self.bd_copy_link,
                                            type=Qt.QueuedConnection)
        self.book_details.view_device_book.connect(
            self.iactions['View'].view_device_book)

        m = self.library_view.model()
        if m.rowCount(None) > 0:
            QTimer.singleShot(0, self.library_view.set_current_row)
            m.current_changed(self.library_view.currentIndex(),
                              self.library_view.currentIndex())
        self.library_view.setFocus(Qt.OtherFocusReason)

    def bd_cover_changed(self, id_, cdata):
        self.library_view.model().db.set_cover(id_, cdata)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    def bd_cover_removed(self, id_):
        self.library_view.model().db.remove_cover(id_,
                                                  commit=True,
                                                  notify=False)
        if self.cover_flow:
            self.cover_flow.dataChanged()

    def bd_copy_link(self, url):
        if url:
            QApplication.clipboard().setText(url)

    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()

    def update_status_bar(self, *args):
        v = self.current_view()
        selected = len(v.selectionModel().selectedRows())
        library_total, total, current = v.model().counts()
        self.status_bar.update_state(library_total, total, current, selected)
示例#7
0
文件: init.py 项目: JimmXinu/calibre
class LayoutMixin(object):  # {{{

    def __init__(self, *args, **kwargs):
        pass

    def init_layout_mixin(self):
        self.vl_tabs = VLTabs(self)
        self.centralwidget.layout().addWidget(self.vl_tabs)

        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='Shift+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)
            button_order = ('sb', 'tb', 'bd', 'gv', 'cb', 'qv')
        # }}}
        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)
            button_order = ('sb', 'tb', 'cb', 'gv', 'qv', 'bd')
        # }}}

        # This must use the base method to find the plugin because it hasn't
        # been fully initialized yet
        self.qv = find_plugin('Quickview')
        if self.qv and self.qv.actual_plugin_:
            self.qv = self.qv.actual_plugin_

        self.status_bar = StatusBar(self)
        stylename = unicode_type(self.style().objectName())
        self.grid_view_button = GridViewButton(self)
        self.search_bar_button = SearchBarButton(self)
        self.grid_view_button.toggled.connect(self.toggle_grid_view)
        self.search_bar_button.toggled.connect(self.toggle_search_bar)

        self.layout_buttons = []
        for x in button_order:
            if hasattr(self, x + '_splitter'):
                button = getattr(self, x + '_splitter').button
            else:
                if x == 'gv':
                    button = self.grid_view_button
                elif x == 'qv':
                    if self.qv is None:
                        continue
                    button = self.qv.qv_button
                else:
                    button = self.search_bar_button
            self.layout_buttons.append(button)
            button.setVisible(False)
            if isosx and stylename != u'Calibre':
                button.setStyleSheet('''
                        QToolButton { background: none; border:none; padding: 0px; }
                        QToolButton:checked { background: rgba(0, 0, 0, 25%); }
                ''')
            self.status_bar.addPermanentWidget(button)
        if gprefs['show_layout_buttons']:
            for b in self.layout_buttons:
                b.setVisible(True)
                self.status_bar.addPermanentWidget(b)
        else:
            self.layout_button = b = QToolButton(self)
            b.setAutoRaise(True), b.setCursor(Qt.PointingHandCursor)
            b.setPopupMode(b.InstantPopup)
            b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            b.setText(_('Layout')), b.setIcon(QIcon(I('config.png')))
            b.setMenu(LayoutMenu(self))
            b.setToolTip(_(
                'Show and hide various parts of the calibre main window'))
            self.status_bar.addPermanentWidget(b)
        self.status_bar.addPermanentWidget(self.jobs_button)
        self.setStatusBar(self.status_bar)
        self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)

    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.cover_changed.connect(self.bd_cover_changed,
                type=Qt.QueuedConnection)
        self.book_details.open_cover_with.connect(self.bd_open_cover_with,
                type=Qt.QueuedConnection)
        self.book_details.open_fmt_with.connect(self.bd_open_fmt_with,
                type=Qt.QueuedConnection)
        self.book_details.edit_book.connect(self.bd_edit_book,
                type=Qt.QueuedConnection)
        self.book_details.cover_removed.connect(self.bd_cover_removed,
                type=Qt.QueuedConnection)
        self.book_details.remote_file_dropped.connect(
                self.iactions['Add Books'].remote_file_dropped_on_book,
                type=Qt.QueuedConnection)
        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)
        self.book_details.search_requested.connect(self.search.set_search_string)
        self.book_details.remove_specific_format.connect(
                self.iactions['Remove Books'].remove_format_by_id)
        self.book_details.remove_metadata_item.connect(
                self.iactions['Edit Metadata'].remove_metadata_item)
        self.book_details.save_specific_format.connect(
                self.iactions['Save To Disk'].save_library_format_by_ids)
        self.book_details.restore_specific_format.connect(
            self.iactions['Remove Books'].restore_format)
        self.book_details.set_cover_from_format.connect(
            self.iactions['Edit Metadata'].set_cover_from_format)
        self.book_details.copy_link.connect(self.bd_copy_link,
                type=Qt.QueuedConnection)
        self.book_details.view_device_book.connect(
                self.iactions['View'].view_device_book)
        self.book_details.manage_category.connect(self.manage_category_triggerred)
        self.book_details.edit_identifiers.connect(self.edit_identifiers_triggerred)
        self.book_details.compare_specific_format.connect(self.compare_format)

        m = self.library_view.model()
        if m.rowCount(None) > 0:
            QTimer.singleShot(0, self.library_view.set_current_row)
            m.current_changed(self.library_view.currentIndex(),
                    self.library_view.currentIndex())
        self.library_view.setFocus(Qt.OtherFocusReason)

    def edit_identifiers_triggerred(self):
        book_id = self.library_view.current_book
        db = self.current_db.new_api
        identifiers = db.field_for('identifiers', book_id, default_value={})
        from calibre.gui2.metadata.basic_widgets import Identifiers
        d = Identifiers(identifiers, self)
        if d.exec_() == d.Accepted:
            identifiers = d.get_identifiers()
            db.set_field('identifiers', {book_id: identifiers})
            self.iactions['Edit Metadata'].refresh_books_after_metadata_edit({book_id})

    def manage_category_triggerred(self, field, value):
        if field and value:
            if field == 'authors':
                self.do_author_sort_edit(self, value, select_sort=False, select_link=False)
            elif field:
                self.do_tags_list_edit(value, field)

    def toggle_grid_view(self, show):
        self.library_view.alternate_views.show_view('grid' if show else None)
        self.sort_sep.setVisible(show)
        self.sort_button.setVisible(show)

    def toggle_search_bar(self, show):
        self.search_bar.setVisible(show)
        if show:
            self.search.setFocus(Qt.OtherFocusReason)

    def bd_cover_changed(self, id_, cdata):
        self.library_view.model().db.set_cover(id_, cdata)
        self.refresh_cover_browser()

    def bd_open_cover_with(self, book_id, entry):
        cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__')
        if cpath:
            from calibre.gui2.open_with import run_program
            run_program(entry, cpath, self)

    def bd_open_fmt_with(self, book_id, fmt, entry):
        path = self.current_db.new_api.format_abspath(book_id, fmt)
        if path:
            from calibre.gui2.open_with import run_program
            run_program(entry, path, self)
        else:
            fmt = fmt.upper()
            error_dialog(self, _('No %s format') % fmt, _(
                'The book {0} does not have the {1} format').format(
                    self.current_db.new_api.field_for('title', book_id, default_value=_('Unknown')),
                    fmt), show=True)

    def bd_edit_book(self, book_id, fmt):
        from calibre.gui2.device import BusyCursor
        with BusyCursor():
            self.iactions['Tweak ePub'].ebook_edit_format(book_id, fmt)

    def open_with_action_triggerred(self, fmt, entry, *args):
        book_id = self.library_view.current_book
        if book_id is not None:
            if fmt == 'cover_image':
                self.bd_open_cover_with(book_id, entry)
            else:
                self.bd_open_fmt_with(book_id, fmt, entry)

    def bd_cover_removed(self, id_):
        self.library_view.model().db.remove_cover(id_, commit=True,
                notify=False)
        self.refresh_cover_browser()

    def bd_copy_link(self, url):
        if url:
            QApplication.clipboard().setText(url)

    def compare_format(self, book_id, fmt):
        db = self.current_db.new_api
        ofmt = fmt
        if fmt.startswith('ORIGINAL_'):
            fmt = fmt.partition('_')[-1]
        else:
            ofmt = 'ORIGINAL_' + fmt
        path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(book_id, fmt)
        from calibre.gui2.tweak_book.diff.main import compare_books
        compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial(
            self.iactions['Remove Books'].restore_format, book_id, ofmt), names=(ofmt, fmt))

    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()
        self.grid_view_button.save_state()
        self.search_bar_button.save_state()
        if self.qv:
            self.qv.qv_button.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()
        self.grid_view_button.restore_state()
        self.search_bar_button.restore_state()
        # Can't do quickview here because the gui isn't totally set up. Do it in ui

    def update_status_bar(self, *args):
        v = self.current_view()
        selected = len(v.selectionModel().selectedRows())
        library_total, total, current = v.model().counts()
        self.status_bar.update_state(library_total, total, current, selected)