mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Nicer unified toolbar
This commit is contained in:
parent
93ece30686
commit
0ad6ab164f
@ -4,7 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import os, sys
|
import os, sys
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
|
||||||
from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSize, \
|
from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \
|
||||||
QByteArray, QTranslator, QCoreApplication, QThread, \
|
QByteArray, QTranslator, QCoreApplication, QThread, \
|
||||||
QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, \
|
QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, \
|
||||||
QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
|
QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
|
||||||
@ -33,10 +33,6 @@ def _config():
|
|||||||
help=_('Send file to storage card instead of main memory by default'))
|
help=_('Send file to storage card instead of main memory by default'))
|
||||||
c.add_opt('confirm_delete', default=False,
|
c.add_opt('confirm_delete', default=False,
|
||||||
help=_('Confirm before deleting'))
|
help=_('Confirm before deleting'))
|
||||||
c.add_opt('toolbar_icon_size', default=QSize(48, 48),
|
|
||||||
help=_('Toolbar icon size')) # value QVariant.toSize
|
|
||||||
c.add_opt('show_text_in_toolbar', default=True,
|
|
||||||
help=_('Show button labels in the toolbar'))
|
|
||||||
c.add_opt('main_window_geometry', default=None,
|
c.add_opt('main_window_geometry', default=None,
|
||||||
help=_('Main window geometry')) # value QVariant.toByteArray
|
help=_('Main window geometry')) # value QVariant.toByteArray
|
||||||
c.add_opt('new_version_notification', default=True,
|
c.add_opt('new_version_notification', default=True,
|
||||||
|
@ -638,7 +638,6 @@ class DeviceMixin(object): # {{{
|
|||||||
self.device_error_dialog = error_dialog(self, _('Error'),
|
self.device_error_dialog = error_dialog(self, _('Error'),
|
||||||
_('Error communicating with device'), ' ')
|
_('Error communicating with device'), ' ')
|
||||||
self.device_error_dialog.setModal(Qt.NonModal)
|
self.device_error_dialog.setModal(Qt.NonModal)
|
||||||
self.device_connected = None
|
|
||||||
self.emailer = Emailer()
|
self.emailer = Emailer()
|
||||||
self.emailer.start()
|
self.emailer.start()
|
||||||
self.device_manager = DeviceManager(Dispatcher(self.device_detected),
|
self.device_manager = DeviceManager(Dispatcher(self.device_detected),
|
||||||
@ -755,17 +754,14 @@ class DeviceMixin(object): # {{{
|
|||||||
self.device_manager.device.__class__.get_gui_name()+\
|
self.device_manager.device.__class__.get_gui_name()+\
|
||||||
_(' detected.'), 3000)
|
_(' detected.'), 3000)
|
||||||
self.device_connected = device_kind
|
self.device_connected = device_kind
|
||||||
self.location_view.model().device_connected(self.device_manager.device)
|
|
||||||
self.refresh_ondevice_info (device_connected = True, reset_only = True)
|
self.refresh_ondevice_info (device_connected = True, reset_only = True)
|
||||||
else:
|
else:
|
||||||
self.device_connected = None
|
self.device_connected = None
|
||||||
self.status_bar.device_disconnected()
|
self.status_bar.device_disconnected()
|
||||||
self.location_view.model().update_devices()
|
|
||||||
if self.current_view() != self.library_view:
|
if self.current_view() != self.library_view:
|
||||||
self.book_details.reset_info()
|
self.book_details.reset_info()
|
||||||
self.location_view.setCurrentIndex(self.location_view.model().index(0))
|
self.location_manager.update_devices()
|
||||||
self.refresh_ondevice_info(device_connected=False)
|
self.refresh_ondevice_info(device_connected=False)
|
||||||
self.tool_bar.device_status_changed(bool(connected))
|
|
||||||
|
|
||||||
def info_read(self, job):
|
def info_read(self, job):
|
||||||
'''
|
'''
|
||||||
@ -774,7 +770,8 @@ class DeviceMixin(object): # {{{
|
|||||||
if job.failed:
|
if job.failed:
|
||||||
return self.device_job_exception(job)
|
return self.device_job_exception(job)
|
||||||
info, cp, fs = job.result
|
info, cp, fs = job.result
|
||||||
self.location_view.model().update_devices(cp, fs)
|
self.location_manager.update_devices(cp, fs,
|
||||||
|
self.device_manager.device.icon)
|
||||||
self.status_bar.device_connected(info[0])
|
self.status_bar.device_connected(info[0])
|
||||||
self.device_manager.books(Dispatcher(self.metadata_downloaded))
|
self.device_manager.books(Dispatcher(self.metadata_downloaded))
|
||||||
|
|
||||||
@ -1076,9 +1073,9 @@ class DeviceMixin(object): # {{{
|
|||||||
dynamic.set('catalogs_to_be_synced', set([]))
|
dynamic.set('catalogs_to_be_synced', set([]))
|
||||||
if files:
|
if files:
|
||||||
remove = []
|
remove = []
|
||||||
space = { self.location_view.model().free[0] : None,
|
space = { self.location_manager.free[0] : None,
|
||||||
self.location_view.model().free[1] : 'carda',
|
self.location_manager.free[1] : 'carda',
|
||||||
self.location_view.model().free[2] : 'cardb' }
|
self.location_manager.free[2] : 'cardb' }
|
||||||
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
|
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
|
||||||
self.upload_books(files, names, metadata,
|
self.upload_books(files, names, metadata,
|
||||||
on_card=on_card,
|
on_card=on_card,
|
||||||
@ -1140,9 +1137,9 @@ class DeviceMixin(object): # {{{
|
|||||||
dynamic.set('news_to_be_synced', set([]))
|
dynamic.set('news_to_be_synced', set([]))
|
||||||
if config['upload_news_to_device'] and files:
|
if config['upload_news_to_device'] and files:
|
||||||
remove = ids if del_on_upload else []
|
remove = ids if del_on_upload else []
|
||||||
space = { self.location_view.model().free[0] : None,
|
space = { self.location_manager.free[0] : None,
|
||||||
self.location_view.model().free[1] : 'carda',
|
self.location_manager.free[1] : 'carda',
|
||||||
self.location_view.model().free[2] : 'cardb' }
|
self.location_manager.free[2] : 'cardb' }
|
||||||
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
|
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
|
||||||
self.upload_books(files, names, metadata,
|
self.upload_books(files, names, metadata,
|
||||||
on_card=on_card,
|
on_card=on_card,
|
||||||
@ -1263,7 +1260,8 @@ class DeviceMixin(object): # {{{
|
|||||||
self.device_job_exception(job)
|
self.device_job_exception(job)
|
||||||
return
|
return
|
||||||
cp, fs = job.result
|
cp, fs = job.result
|
||||||
self.location_view.model().update_devices(cp, fs)
|
self.location_manager.update_devices(cp, fs,
|
||||||
|
self.device_manager.device.icon)
|
||||||
# reset the views so that up-to-date info is shown. These need to be
|
# reset the views so that up-to-date info is shown. These need to be
|
||||||
# here because the sony driver updates collections in sync_booklists
|
# here because the sony driver updates collections in sync_booklists
|
||||||
self.memory_view.reset()
|
self.memory_view.reset()
|
||||||
|
@ -7,14 +7,13 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import functools, sys, os
|
import functools, sys, os
|
||||||
|
|
||||||
from PyQt4.Qt import QMenu, Qt, pyqtSignal, QIcon, QStackedWidget, \
|
from PyQt4.Qt import QMenu, Qt, QStackedWidget, \
|
||||||
QSize, QSizePolicy, QStatusBar, QUrl, QLabel, QFont
|
QSize, QSizePolicy, QStatusBar, QLabel, QFont
|
||||||
|
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
|
||||||
from calibre.constants import isosx, __appname__, preferred_encoding, \
|
from calibre.constants import isosx, __appname__, preferred_encoding, \
|
||||||
__version__
|
__version__
|
||||||
from calibre.gui2 import config, is_widescreen, open_url
|
from calibre.gui2 import config, is_widescreen
|
||||||
from calibre.gui2.library.views import BooksView, DeviceBooksView
|
from calibre.gui2.library.views import BooksView, DeviceBooksView
|
||||||
from calibre.gui2.widgets import Splitter
|
from calibre.gui2.widgets import Splitter
|
||||||
from calibre.gui2.tag_view import TagBrowserWidget
|
from calibre.gui2.tag_view import TagBrowserWidget
|
||||||
@ -28,157 +27,6 @@ def partial(*args, **kwargs):
|
|||||||
_keep_refs.append(ans)
|
_keep_refs.append(ans)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
class SaveMenu(QMenu): # {{{
|
|
||||||
|
|
||||||
save_fmt = pyqtSignal(object)
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
QMenu.__init__(self, _('Save single format to disk...'), parent)
|
|
||||||
for ext in sorted(BOOK_EXTENSIONS):
|
|
||||||
action = self.addAction(ext.upper())
|
|
||||||
setattr(self, 'do_'+ext, partial(self.do, ext))
|
|
||||||
action.triggered.connect(
|
|
||||||
getattr(self, 'do_'+ext))
|
|
||||||
|
|
||||||
def do(self, ext, *args):
|
|
||||||
self.save_fmt.emit(ext)
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
class ToolbarMixin(object): # {{{
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.action_help.triggered.connect(self.show_help)
|
|
||||||
md = QMenu()
|
|
||||||
md.addAction(_('Edit metadata individually'),
|
|
||||||
partial(self.edit_metadata, False, bulk=False))
|
|
||||||
md.addSeparator()
|
|
||||||
md.addAction(_('Edit metadata in bulk'),
|
|
||||||
partial(self.edit_metadata, False, bulk=True))
|
|
||||||
md.addSeparator()
|
|
||||||
md.addAction(_('Download metadata and covers'),
|
|
||||||
partial(self.download_metadata, False, covers=True),
|
|
||||||
Qt.ControlModifier+Qt.Key_D)
|
|
||||||
md.addAction(_('Download only metadata'),
|
|
||||||
partial(self.download_metadata, False, covers=False))
|
|
||||||
md.addAction(_('Download only covers'),
|
|
||||||
partial(self.download_metadata, False, covers=True,
|
|
||||||
set_metadata=False, set_social_metadata=False))
|
|
||||||
md.addAction(_('Download only social metadata'),
|
|
||||||
partial(self.download_metadata, False, covers=False,
|
|
||||||
set_metadata=False, set_social_metadata=True))
|
|
||||||
self.metadata_menu = md
|
|
||||||
|
|
||||||
mb = QMenu()
|
|
||||||
mb.addAction(_('Merge into first selected book - delete others'),
|
|
||||||
self.merge_books)
|
|
||||||
mb.addSeparator()
|
|
||||||
mb.addAction(_('Merge into first selected book - keep others'),
|
|
||||||
partial(self.merge_books, safe_merge=True))
|
|
||||||
self.merge_menu = mb
|
|
||||||
self.action_merge.setMenu(mb)
|
|
||||||
md.addSeparator()
|
|
||||||
md.addAction(self.action_merge)
|
|
||||||
|
|
||||||
self.add_menu = QMenu()
|
|
||||||
self.add_menu.addAction(_('Add books from a single directory'),
|
|
||||||
self.add_books)
|
|
||||||
self.add_menu.addAction(_('Add books from directories, including '
|
|
||||||
'sub-directories (One book per directory, assumes every ebook '
|
|
||||||
'file is the same book in a different format)'),
|
|
||||||
self.add_recursive_single)
|
|
||||||
self.add_menu.addAction(_('Add books from directories, including '
|
|
||||||
'sub directories (Multiple books per directory, assumes every '
|
|
||||||
'ebook file is a different book)'), self.add_recursive_multiple)
|
|
||||||
self.add_menu.addAction(_('Add Empty book. (Book entry with no '
|
|
||||||
'formats)'), self.add_empty)
|
|
||||||
self.action_add.setMenu(self.add_menu)
|
|
||||||
self.action_add.triggered.connect(self.add_books)
|
|
||||||
self.action_del.triggered.connect(self.delete_books)
|
|
||||||
self.action_edit.triggered.connect(self.edit_metadata)
|
|
||||||
self.action_merge.triggered.connect(self.merge_books)
|
|
||||||
|
|
||||||
self.action_save.triggered.connect(self.save_to_disk)
|
|
||||||
self.save_menu = QMenu()
|
|
||||||
self.save_menu.addAction(_('Save to disk'), partial(self.save_to_disk,
|
|
||||||
False))
|
|
||||||
self.save_menu.addAction(_('Save to disk in a single directory'),
|
|
||||||
partial(self.save_to_single_dir, False))
|
|
||||||
self.save_menu.addAction(_('Save only %s format to disk')%
|
|
||||||
prefs['output_format'].upper(),
|
|
||||||
partial(self.save_single_format_to_disk, False))
|
|
||||||
self.save_menu.addAction(
|
|
||||||
_('Save only %s format to disk in a single directory')%
|
|
||||||
prefs['output_format'].upper(),
|
|
||||||
partial(self.save_single_fmt_to_single_dir, False))
|
|
||||||
self.save_sub_menu = SaveMenu(self)
|
|
||||||
self.save_menu.addMenu(self.save_sub_menu)
|
|
||||||
self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk)
|
|
||||||
|
|
||||||
self.action_view.triggered.connect(self.view_book)
|
|
||||||
self.view_menu = QMenu()
|
|
||||||
self.view_menu.addAction(_('View'), partial(self.view_book, False))
|
|
||||||
ac = self.view_menu.addAction(_('View specific format'))
|
|
||||||
ac.setShortcut((Qt.ControlModifier if isosx else Qt.AltModifier)+Qt.Key_V)
|
|
||||||
self.action_view.setMenu(self.view_menu)
|
|
||||||
ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection)
|
|
||||||
|
|
||||||
self.delete_menu = QMenu()
|
|
||||||
self.delete_menu.addAction(_('Remove selected books'), self.delete_books)
|
|
||||||
self.delete_menu.addAction(
|
|
||||||
_('Remove files of a specific format from selected books..'),
|
|
||||||
self.delete_selected_formats)
|
|
||||||
self.delete_menu.addAction(
|
|
||||||
_('Remove all formats from selected books, except...'),
|
|
||||||
self.delete_all_but_selected_formats)
|
|
||||||
self.delete_menu.addAction(
|
|
||||||
_('Remove covers from selected books'), self.delete_covers)
|
|
||||||
self.delete_menu.addSeparator()
|
|
||||||
self.delete_menu.addAction(
|
|
||||||
_('Remove matching books from device'),
|
|
||||||
self.remove_matching_books_from_device)
|
|
||||||
self.action_del.setMenu(self.delete_menu)
|
|
||||||
|
|
||||||
self.action_open_containing_folder.setShortcut(Qt.Key_O)
|
|
||||||
self.addAction(self.action_open_containing_folder)
|
|
||||||
self.action_open_containing_folder.triggered.connect(self.view_folder)
|
|
||||||
self.action_sync.setShortcut(Qt.Key_D)
|
|
||||||
self.action_sync.setEnabled(True)
|
|
||||||
self.create_device_menu()
|
|
||||||
self.action_sync.triggered.connect(
|
|
||||||
self._sync_action_triggered)
|
|
||||||
|
|
||||||
self.action_edit.setMenu(md)
|
|
||||||
self.action_save.setMenu(self.save_menu)
|
|
||||||
|
|
||||||
cm = QMenu()
|
|
||||||
cm.addAction(_('Convert individually'), partial(self.convert_ebook,
|
|
||||||
False, bulk=False))
|
|
||||||
cm.addAction(_('Bulk convert'),
|
|
||||||
partial(self.convert_ebook, False, bulk=True))
|
|
||||||
cm.addSeparator()
|
|
||||||
ac = cm.addAction(
|
|
||||||
_('Create catalog of books in your calibre library'))
|
|
||||||
ac.triggered.connect(self.generate_catalog)
|
|
||||||
self.action_convert.setMenu(cm)
|
|
||||||
self.action_convert.triggered.connect(self.convert_ebook)
|
|
||||||
self.convert_menu = cm
|
|
||||||
|
|
||||||
pm = QMenu()
|
|
||||||
pm.addAction(QIcon(I('config.svg')), _('Preferences'), self.do_config)
|
|
||||||
pm.addAction(QIcon(I('wizard.svg')), _('Run welcome wizard'),
|
|
||||||
self.run_wizard)
|
|
||||||
self.action_preferences.setMenu(pm)
|
|
||||||
self.preferences_menu = pm
|
|
||||||
for x in (self.preferences_action, self.action_preferences):
|
|
||||||
x.triggered.connect(self.do_config)
|
|
||||||
|
|
||||||
def show_help(self, *args):
|
|
||||||
open_url(QUrl('http://calibre-ebook.com/user_manual'))
|
|
||||||
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
class LibraryViewMixin(object): # {{{
|
class LibraryViewMixin(object): # {{{
|
||||||
|
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
|
@ -6,108 +6,105 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \
|
from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, \
|
||||||
QAbstractListModel, QFont, QApplication, QPalette, pyqtSignal, QToolButton, \
|
pyqtSignal, QToolButton, \
|
||||||
QModelIndex, QListView, QAbstractButton, QPainter, QPixmap, QColor, \
|
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup, \
|
||||||
QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout
|
QMenu, QUrl
|
||||||
|
|
||||||
from calibre.constants import __appname__, filesystem_encoding
|
from calibre.constants import __appname__, isosx
|
||||||
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
|
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
|
||||||
from calibre.gui2.throbber import ThrobbingButton
|
from calibre.gui2.throbber import ThrobbingButton
|
||||||
from calibre.gui2 import NONE, config
|
from calibre.gui2 import config, open_url
|
||||||
from calibre.gui2.widgets import ComboBoxWithHelp
|
from calibre.gui2.widgets import ComboBoxWithHelp
|
||||||
from calibre import human_readable
|
from calibre import human_readable
|
||||||
|
from calibre.utils.config import prefs
|
||||||
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
|
||||||
ICON_SIZE = 48
|
ICON_SIZE = 48
|
||||||
|
|
||||||
# Location View {{{
|
class SaveMenu(QMenu): # {{{
|
||||||
|
|
||||||
class LocationModel(QAbstractListModel): # {{{
|
save_fmt = pyqtSignal(object)
|
||||||
|
|
||||||
devicesChanged = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QAbstractListModel.__init__(self, parent)
|
QMenu.__init__(self, _('Save single format to disk...'), parent)
|
||||||
self.icons = [QVariant(QIcon(I('library.png'))),
|
for ext in sorted(BOOK_EXTENSIONS):
|
||||||
QVariant(QIcon(I('reader.svg'))),
|
action = self.addAction(ext.upper())
|
||||||
QVariant(QIcon(I('sd.svg'))),
|
setattr(self, 'do_'+ext, partial(self.do, ext))
|
||||||
QVariant(QIcon(I('sd.svg')))]
|
action.triggered.connect(
|
||||||
self.text = [_('Library\n%d books'),
|
getattr(self, 'do_'+ext))
|
||||||
_('Reader\n%s'),
|
|
||||||
_('Card A\n%s'),
|
def do(self, ext, *args):
|
||||||
_('Card B\n%s')]
|
self.save_fmt.emit(ext)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class LocationManager(QObject): # {{{
|
||||||
|
|
||||||
|
locations_changed = pyqtSignal()
|
||||||
|
unmount_device = pyqtSignal()
|
||||||
|
location_selected = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QObject.__init__(self, parent)
|
||||||
self.free = [-1, -1, -1]
|
self.free = [-1, -1, -1]
|
||||||
self.count = 0
|
self.count = 0
|
||||||
self.highlight_row = 0
|
self.location_actions = QActionGroup(self)
|
||||||
self.library_tooltip = _('Click to see the books available on your computer')
|
self.location_actions.setExclusive(True)
|
||||||
self.tooltips = [
|
self.current_location = 'library'
|
||||||
self.library_tooltip,
|
self._mem = []
|
||||||
_('Click to see the books in the main memory of your reader'),
|
self.tooltips = {}
|
||||||
_('Click to see the books on storage card A in your reader'),
|
|
||||||
_('Click to see the books on storage card B in your reader')
|
|
||||||
]
|
|
||||||
|
|
||||||
def database_changed(self, db):
|
def ac(name, text, icon, tooltip):
|
||||||
lp = db.library_path
|
icon = QIcon(I(icon))
|
||||||
if not isinstance(lp, unicode):
|
ac = self.location_actions.addAction(icon, text)
|
||||||
lp = lp.decode(filesystem_encoding, 'replace')
|
setattr(self, 'location_'+name, ac)
|
||||||
self.tooltips[0] = self.library_tooltip + '\n\n' + \
|
ac.setAutoRepeat(False)
|
||||||
_('Books located at') + ' ' + lp
|
ac.setCheckable(True)
|
||||||
self.dataChanged.emit(self.index(0), self.index(0))
|
receiver = partial(self._location_selected, name)
|
||||||
|
ac.triggered.connect(receiver)
|
||||||
|
self.tooltips[name] = tooltip
|
||||||
|
if name != 'library':
|
||||||
|
m = QMenu(parent)
|
||||||
|
self._mem.append(m)
|
||||||
|
a = m.addAction(icon, tooltip)
|
||||||
|
a.triggered.connect(receiver)
|
||||||
|
self._mem.append(a)
|
||||||
|
a = m.addAction(QIcon(I('eject.svg')), _('Eject this device'))
|
||||||
|
a.triggered.connect(self._eject_requested)
|
||||||
|
ac.setMenu(m)
|
||||||
|
self._mem.append(a)
|
||||||
|
else:
|
||||||
|
ac.setToolTip(tooltip)
|
||||||
|
|
||||||
def rowCount(self, *args):
|
return ac
|
||||||
return 1 + len([i for i in self.free if i >= 0])
|
|
||||||
|
|
||||||
def get_device_row(self, row):
|
ac('library', _('Library'), 'lt.png',
|
||||||
if row == 2 and self.free[1] == -1 and self.free[2] > -1:
|
_('Show books in calibre library'))
|
||||||
row = 3
|
ac('main', _('Main'), 'reader.svg',
|
||||||
return row
|
_('Show books in the main memory of the device'))
|
||||||
|
ac('carda', _('Card A'), 'sd.svg',
|
||||||
|
_('Show books in storage card A'))
|
||||||
|
ac('cardb', _('Card B'), 'sd.svg',
|
||||||
|
_('Show books in storage card B'))
|
||||||
|
|
||||||
def get_tooltip(self, row, drow):
|
def _location_selected(self, location, *args):
|
||||||
ans = self.tooltips[row]
|
if location != self.current_location and hasattr(self,
|
||||||
if row > 0:
|
'location_'+location):
|
||||||
fs = self.free[drow-1]
|
self.current_location = location
|
||||||
if fs > -1:
|
self.location_selected.emit(location)
|
||||||
ans += '\n\n%s '%(human_readable(fs)) + _('free')
|
getattr(self, 'location_'+location).setChecked(True)
|
||||||
return ans
|
|
||||||
|
|
||||||
def data(self, index, role):
|
def _eject_requested(self, *args):
|
||||||
row = index.row()
|
self.unmount_device.emit()
|
||||||
drow = self.get_device_row(row)
|
|
||||||
data = NONE
|
|
||||||
if role == Qt.DisplayRole:
|
|
||||||
text = self.text[drow]%(human_readable(self.free[drow-1])) if row > 0 \
|
|
||||||
else self.text[drow]%self.count
|
|
||||||
data = QVariant(text)
|
|
||||||
elif role == Qt.DecorationRole:
|
|
||||||
data = self.icons[drow]
|
|
||||||
elif role in (Qt.ToolTipRole, Qt.StatusTipRole):
|
|
||||||
ans = self.get_tooltip(row, drow)
|
|
||||||
data = QVariant(ans)
|
|
||||||
elif role == Qt.SizeHintRole:
|
|
||||||
data = QVariant(QSize(155, 90))
|
|
||||||
elif role == Qt.FontRole:
|
|
||||||
font = QFont('monospace')
|
|
||||||
font.setBold(row == self.highlight_row)
|
|
||||||
data = QVariant(font)
|
|
||||||
elif role == Qt.ForegroundRole and row == self.highlight_row:
|
|
||||||
return QVariant(QApplication.palette().brush(
|
|
||||||
QPalette.HighlightedText))
|
|
||||||
elif role == Qt.BackgroundRole and row == self.highlight_row:
|
|
||||||
return QVariant(QApplication.palette().brush(
|
|
||||||
QPalette.Highlight))
|
|
||||||
|
|
||||||
return data
|
def update_devices(self, cp=(None, None), fs=[-1, -1, -1], icon=None):
|
||||||
|
if icon is None:
|
||||||
def device_connected(self, dev):
|
icon = I('reader.svg')
|
||||||
self.icons[1] = QIcon(dev.icon)
|
self.location_main.setIcon(QIcon(icon))
|
||||||
self.dataChanged.emit(self.index(1), self.index(1))
|
had_device = self.has_device
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
|
||||||
return NONE
|
|
||||||
|
|
||||||
def update_devices(self, cp=(None, None), fs=[-1, -1, -1]):
|
|
||||||
if cp is None:
|
if cp is None:
|
||||||
cp = (None, None)
|
cp = (None, None)
|
||||||
if isinstance(cp, (str, unicode)):
|
if isinstance(cp, (str, unicode)):
|
||||||
@ -120,137 +117,34 @@ class LocationModel(QAbstractListModel): # {{{
|
|||||||
cpa, cpb = cp
|
cpa, cpb = cp
|
||||||
self.free[1] = fs[1] if fs[1] is not None and cpa is not None else -1
|
self.free[1] = fs[1] if fs[1] is not None and cpa is not None else -1
|
||||||
self.free[2] = fs[2] if fs[2] is not None and cpb is not None else -1
|
self.free[2] = fs[2] if fs[2] is not None and cpb is not None else -1
|
||||||
self.reset()
|
self.update_tooltips()
|
||||||
self.devicesChanged.emit()
|
if self.has_device != had_device:
|
||||||
|
self.locations_changed.emit()
|
||||||
|
if not self.has_device:
|
||||||
|
self.location_library.trigger()
|
||||||
|
|
||||||
def location_changed(self, row):
|
def update_tooltips(self):
|
||||||
self.highlight_row = row
|
for i, loc in enumerate(('main', 'carda', 'cardb')):
|
||||||
self.dataChanged.emit(
|
t = self.tooltips[loc]
|
||||||
self.index(0), self.index(self.rowCount(QModelIndex())-1))
|
if self.free[i] > -1:
|
||||||
|
t += u'\n\n%s '%human_readable(self.free[i]) + _('available')
|
||||||
|
ac = getattr(self, 'location_'+loc)
|
||||||
|
ac.setToolTip(t)
|
||||||
|
ac.setWhatsThis(t)
|
||||||
|
ac.setStatusTip(t)
|
||||||
|
|
||||||
def location_for_row(self, row):
|
|
||||||
if row == 0: return 'library'
|
|
||||||
if row == 1: return 'main'
|
|
||||||
if row == 3: return 'cardb'
|
|
||||||
return 'carda' if self.free[1] > -1 else 'cardb'
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
class LocationView(QListView):
|
|
||||||
|
|
||||||
umount_device = pyqtSignal()
|
|
||||||
location_selected = pyqtSignal(object)
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
QListView.__init__(self, parent)
|
|
||||||
self.setModel(LocationModel(self))
|
|
||||||
self.reset()
|
|
||||||
self.currentChanged = self.current_changed
|
|
||||||
|
|
||||||
self.eject_button = EjectButton(self)
|
|
||||||
self.eject_button.hide()
|
|
||||||
|
|
||||||
self.entered.connect(self.item_entered)
|
|
||||||
self.viewportEntered.connect(self.viewport_entered)
|
|
||||||
self.eject_button.clicked.connect(self.eject_clicked)
|
|
||||||
self.model().devicesChanged.connect(self.eject_button.hide)
|
|
||||||
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
|
|
||||||
QSizePolicy.Expanding))
|
|
||||||
self.setMouseTracking(True)
|
|
||||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
||||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
||||||
self.setEditTriggers(self.NoEditTriggers)
|
|
||||||
self.setTabKeyNavigation(True)
|
|
||||||
self.setProperty("showDropIndicator", True)
|
|
||||||
self.setSelectionMode(self.SingleSelection)
|
|
||||||
self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
|
|
||||||
self.setMovement(self.Static)
|
|
||||||
self.setFlow(self.LeftToRight)
|
|
||||||
self.setGridSize(QSize(175, ICON_SIZE))
|
|
||||||
self.setViewMode(self.ListMode)
|
|
||||||
self.setWordWrap(True)
|
|
||||||
self.setObjectName("location_view")
|
|
||||||
self.setMaximumSize(QSize(600, ICON_SIZE+16))
|
|
||||||
self.setMinimumWidth(400)
|
|
||||||
|
|
||||||
def eject_clicked(self, *args):
|
|
||||||
self.umount_device.emit()
|
|
||||||
|
|
||||||
def count_changed(self, new_count):
|
|
||||||
self.model().count = new_count
|
|
||||||
self.model().reset()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def book_count(self):
|
def has_device(self):
|
||||||
return self.model().count
|
return max(self.free) > -1
|
||||||
|
|
||||||
def current_changed(self, current, previous):
|
|
||||||
if current.isValid():
|
|
||||||
i = current.row()
|
|
||||||
location = self.model().location_for_row(i)
|
|
||||||
self.location_selected.emit(location)
|
|
||||||
self.model().location_changed(i)
|
|
||||||
|
|
||||||
def location_changed(self, row):
|
|
||||||
if 0 <= row and row <= 3:
|
|
||||||
self.model().location_changed(row)
|
|
||||||
|
|
||||||
def leaveEvent(self, event):
|
|
||||||
self.unsetCursor()
|
|
||||||
self.eject_button.hide()
|
|
||||||
|
|
||||||
def item_entered(self, location):
|
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
|
||||||
self.eject_button.hide()
|
|
||||||
|
|
||||||
if location.row() == 1:
|
|
||||||
rect = self.visualRect(location)
|
|
||||||
|
|
||||||
self.eject_button.resize(rect.height()/2, rect.height()/2)
|
|
||||||
|
|
||||||
x, y = rect.left(), rect.top()
|
|
||||||
x = x + (rect.width() - self.eject_button.width() - 2)
|
|
||||||
y += 6
|
|
||||||
|
|
||||||
self.eject_button.move(x, y)
|
|
||||||
self.eject_button.show()
|
|
||||||
|
|
||||||
def viewport_entered(self):
|
|
||||||
self.unsetCursor()
|
|
||||||
self.eject_button.hide()
|
|
||||||
|
|
||||||
|
|
||||||
class EjectButton(QAbstractButton):
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
QAbstractButton.__init__(self, parent)
|
|
||||||
self.mouse_over = False
|
|
||||||
self.setMouseTracking(True)
|
|
||||||
|
|
||||||
def enterEvent(self, event):
|
|
||||||
self.mouse_over = True
|
|
||||||
QAbstractButton.enterEvent(self, event)
|
|
||||||
|
|
||||||
def leaveEvent(self, event):
|
|
||||||
self.mouse_over = False
|
|
||||||
QAbstractButton.leaveEvent(self, event)
|
|
||||||
|
|
||||||
def paintEvent(self, event):
|
|
||||||
painter = QPainter(self)
|
|
||||||
painter.setClipRect(event.rect())
|
|
||||||
image = QPixmap(I('eject')).scaledToHeight(event.rect().height(),
|
|
||||||
Qt.SmoothTransformation)
|
|
||||||
|
|
||||||
if not self.mouse_over:
|
|
||||||
alpha_mask = QPixmap(image.width(), image.height())
|
|
||||||
color = QColor(128, 128, 128)
|
|
||||||
alpha_mask.fill(color)
|
|
||||||
image.setAlphaChannel(alpha_mask)
|
|
||||||
|
|
||||||
painter.drawPixmap(0, 0, image)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available_actions(self):
|
||||||
|
ans = [self.location_library]
|
||||||
|
for i, loc in enumerate(('main', 'carda', 'cardb')):
|
||||||
|
if self.free[i] > -1:
|
||||||
|
ans.append(getattr(self, 'location_'+loc))
|
||||||
|
return ans
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -326,7 +220,7 @@ class SearchBar(QWidget): # {{{
|
|||||||
|
|
||||||
class ToolBar(QToolBar): # {{{
|
class ToolBar(QToolBar): # {{{
|
||||||
|
|
||||||
def __init__(self, actions, donate, location_view, parent=None):
|
def __init__(self, actions, donate, location_manager, parent=None):
|
||||||
QToolBar.__init__(self, parent)
|
QToolBar.__init__(self, parent)
|
||||||
self.setContextMenuPolicy(Qt.PreventContextMenu)
|
self.setContextMenuPolicy(Qt.PreventContextMenu)
|
||||||
self.setMovable(False)
|
self.setMovable(False)
|
||||||
@ -335,11 +229,12 @@ class ToolBar(QToolBar): # {{{
|
|||||||
self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
|
self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
|
||||||
self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
|
self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
|
||||||
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||||
|
self.setStyleSheet('QToolButton:checked { font-weight: bold }')
|
||||||
|
|
||||||
self.showing_device = False
|
|
||||||
self.all_actions = actions
|
self.all_actions = actions
|
||||||
self.donate = donate
|
self.donate = donate
|
||||||
self.location_view = location_view
|
self.location_manager = location_manager
|
||||||
|
self.location_manager.locations_changed.connect(self.build_bar)
|
||||||
self.d_widget = QWidget()
|
self.d_widget = QWidget()
|
||||||
self.d_widget.setLayout(QVBoxLayout())
|
self.d_widget.setLayout(QVBoxLayout())
|
||||||
self.d_widget.layout().addWidget(donate)
|
self.d_widget.layout().addWidget(donate)
|
||||||
@ -350,40 +245,45 @@ class ToolBar(QToolBar): # {{{
|
|||||||
def contextMenuEvent(self, *args):
|
def contextMenuEvent(self, *args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def device_status_changed(self, connected):
|
|
||||||
self.showing_device = connected
|
|
||||||
self.build_bar()
|
|
||||||
|
|
||||||
def build_bar(self):
|
def build_bar(self):
|
||||||
order_field = 'device' if self.showing_device else 'normal'
|
showing_device = self.location_manager.has_device
|
||||||
|
order_field = 'device' if showing_device else 'normal'
|
||||||
o = attrgetter(order_field+'_order')
|
o = attrgetter(order_field+'_order')
|
||||||
sepvals = [2] if self.showing_device else [1]
|
sepvals = [2] if showing_device else [1]
|
||||||
sepvals += [3]
|
sepvals += [3]
|
||||||
actions = [x for x in self.all_actions if o(x) > -1]
|
actions = [x for x in self.all_actions if o(x) > -1]
|
||||||
actions.sort(cmp=lambda x,y : cmp(o(x), o(y)))
|
actions.sort(cmp=lambda x,y : cmp(o(x), o(y)))
|
||||||
self.clear()
|
self.clear()
|
||||||
for x in actions:
|
|
||||||
self.addAction(x)
|
|
||||||
ch = self.widgetForAction(x)
|
def setup_tool_button(ac):
|
||||||
|
ch = self.widgetForAction(ac)
|
||||||
ch.setCursor(Qt.PointingHandCursor)
|
ch.setCursor(Qt.PointingHandCursor)
|
||||||
ch.setAutoRaise(True)
|
ch.setAutoRaise(True)
|
||||||
|
if ac.menu() is not None:
|
||||||
if x.action_name == 'choose_library':
|
|
||||||
self.location_action = self.addWidget(self.location_view)
|
|
||||||
self.choose_action = x
|
|
||||||
if config['show_donate_button']:
|
|
||||||
self.addWidget(self.d_widget)
|
|
||||||
if x.action_name not in ('choose_library', 'help'):
|
|
||||||
ch.setPopupMode(ch.MenuButtonPopup)
|
ch.setPopupMode(ch.MenuButtonPopup)
|
||||||
|
|
||||||
|
for x in actions:
|
||||||
|
self.addAction(x)
|
||||||
|
setup_tool_button(x)
|
||||||
|
|
||||||
|
if x.action_name == 'choose_library':
|
||||||
|
self.choose_action = x
|
||||||
|
if showing_device:
|
||||||
|
self.addSeparator()
|
||||||
|
for ac in self.location_manager.available_actions:
|
||||||
|
self.addAction(ac)
|
||||||
|
setup_tool_button(ac)
|
||||||
|
self.addSeparator()
|
||||||
|
self.location_manager.location_library.trigger()
|
||||||
|
elif config['show_donate_button']:
|
||||||
|
self.addWidget(self.d_widget)
|
||||||
|
|
||||||
for x in actions:
|
for x in actions:
|
||||||
if x.separator_before in sepvals:
|
if x.separator_before in sepvals:
|
||||||
self.insertSeparator(x)
|
self.insertSeparator(x)
|
||||||
|
|
||||||
|
self.choose_action.setVisible(not showing_device)
|
||||||
self.location_action.setVisible(self.showing_device)
|
|
||||||
self.choose_action.setVisible(not self.showing_device)
|
|
||||||
|
|
||||||
def count_changed(self, new_count):
|
def count_changed(self, new_count):
|
||||||
text = _('%d books')%new_count
|
text = _('%d books')%new_count
|
||||||
@ -397,6 +297,9 @@ class ToolBar(QToolBar): # {{{
|
|||||||
self.setToolButtonStyle(style)
|
self.setToolButtonStyle(style)
|
||||||
QToolBar.resizeEvent(self, ev)
|
QToolBar.resizeEvent(self, ev)
|
||||||
|
|
||||||
|
def database_changed(self, db):
|
||||||
|
pass
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class Action(QAction):
|
class Action(QAction):
|
||||||
@ -405,6 +308,7 @@ class Action(QAction):
|
|||||||
class MainWindowMixin(object):
|
class MainWindowMixin(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.device_connected = None
|
||||||
self.setObjectName('MainWindow')
|
self.setObjectName('MainWindow')
|
||||||
self.setWindowIcon(QIcon(I('library.png')))
|
self.setWindowIcon(QIcon(I('library.png')))
|
||||||
self.setWindowTitle(__appname__)
|
self.setWindowTitle(__appname__)
|
||||||
@ -417,9 +321,23 @@ class MainWindowMixin(object):
|
|||||||
self.resize(1012, 740)
|
self.resize(1012, 740)
|
||||||
self.donate_button = ThrobbingButton(self.centralwidget)
|
self.donate_button = ThrobbingButton(self.centralwidget)
|
||||||
self.donate_button.set_normal_icon_size(ICON_SIZE, ICON_SIZE)
|
self.donate_button.set_normal_icon_size(ICON_SIZE, ICON_SIZE)
|
||||||
|
self.location_manager = LocationManager(self)
|
||||||
|
|
||||||
# Actions {{{
|
all_actions = self.setup_actions()
|
||||||
|
|
||||||
|
self.search_bar = SearchBar(self)
|
||||||
|
self.tool_bar = ToolBar(all_actions, self.donate_button,
|
||||||
|
self.location_manager, self)
|
||||||
|
self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
|
||||||
|
|
||||||
|
l = self.centralwidget.layout()
|
||||||
|
l.addWidget(self.search_bar)
|
||||||
|
|
||||||
|
|
||||||
|
def read_toolbar_settings(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setup_actions(self): # {{{
|
||||||
all_actions = []
|
all_actions = []
|
||||||
|
|
||||||
def ac(normal_order, device_order, separator_before,
|
def ac(normal_order, device_order, separator_before,
|
||||||
@ -467,17 +385,135 @@ class MainWindowMixin(object):
|
|||||||
ac(-1, -1, 0, 'books_with_the_same_tags', _('Books with the same tags'),
|
ac(-1, -1, 0, 'books_with_the_same_tags', _('Books with the same tags'),
|
||||||
'tags.svg')
|
'tags.svg')
|
||||||
|
|
||||||
|
self.action_help.triggered.connect(self.show_help)
|
||||||
|
md = QMenu()
|
||||||
|
md.addAction(_('Edit metadata individually'),
|
||||||
|
partial(self.edit_metadata, False, bulk=False))
|
||||||
|
md.addSeparator()
|
||||||
|
md.addAction(_('Edit metadata in bulk'),
|
||||||
|
partial(self.edit_metadata, False, bulk=True))
|
||||||
|
md.addSeparator()
|
||||||
|
md.addAction(_('Download metadata and covers'),
|
||||||
|
partial(self.download_metadata, False, covers=True),
|
||||||
|
Qt.ControlModifier+Qt.Key_D)
|
||||||
|
md.addAction(_('Download only metadata'),
|
||||||
|
partial(self.download_metadata, False, covers=False))
|
||||||
|
md.addAction(_('Download only covers'),
|
||||||
|
partial(self.download_metadata, False, covers=True,
|
||||||
|
set_metadata=False, set_social_metadata=False))
|
||||||
|
md.addAction(_('Download only social metadata'),
|
||||||
|
partial(self.download_metadata, False, covers=False,
|
||||||
|
set_metadata=False, set_social_metadata=True))
|
||||||
|
self.metadata_menu = md
|
||||||
|
|
||||||
|
mb = QMenu()
|
||||||
|
mb.addAction(_('Merge into first selected book - delete others'),
|
||||||
|
self.merge_books)
|
||||||
|
mb.addSeparator()
|
||||||
|
mb.addAction(_('Merge into first selected book - keep others'),
|
||||||
|
partial(self.merge_books, safe_merge=True))
|
||||||
|
self.merge_menu = mb
|
||||||
|
self.action_merge.setMenu(mb)
|
||||||
|
md.addSeparator()
|
||||||
|
md.addAction(self.action_merge)
|
||||||
|
|
||||||
|
self.add_menu = QMenu()
|
||||||
|
self.add_menu.addAction(_('Add books from a single directory'),
|
||||||
|
self.add_books)
|
||||||
|
self.add_menu.addAction(_('Add books from directories, including '
|
||||||
|
'sub-directories (One book per directory, assumes every ebook '
|
||||||
|
'file is the same book in a different format)'),
|
||||||
|
self.add_recursive_single)
|
||||||
|
self.add_menu.addAction(_('Add books from directories, including '
|
||||||
|
'sub directories (Multiple books per directory, assumes every '
|
||||||
|
'ebook file is a different book)'), self.add_recursive_multiple)
|
||||||
|
self.add_menu.addAction(_('Add Empty book. (Book entry with no '
|
||||||
|
'formats)'), self.add_empty)
|
||||||
|
self.action_add.setMenu(self.add_menu)
|
||||||
|
self.action_add.triggered.connect(self.add_books)
|
||||||
|
self.action_del.triggered.connect(self.delete_books)
|
||||||
|
self.action_edit.triggered.connect(self.edit_metadata)
|
||||||
|
self.action_merge.triggered.connect(self.merge_books)
|
||||||
|
|
||||||
|
self.action_save.triggered.connect(self.save_to_disk)
|
||||||
|
self.save_menu = QMenu()
|
||||||
|
self.save_menu.addAction(_('Save to disk'), partial(self.save_to_disk,
|
||||||
|
False))
|
||||||
|
self.save_menu.addAction(_('Save to disk in a single directory'),
|
||||||
|
partial(self.save_to_single_dir, False))
|
||||||
|
self.save_menu.addAction(_('Save only %s format to disk')%
|
||||||
|
prefs['output_format'].upper(),
|
||||||
|
partial(self.save_single_format_to_disk, False))
|
||||||
|
self.save_menu.addAction(
|
||||||
|
_('Save only %s format to disk in a single directory')%
|
||||||
|
prefs['output_format'].upper(),
|
||||||
|
partial(self.save_single_fmt_to_single_dir, False))
|
||||||
|
self.save_sub_menu = SaveMenu(self)
|
||||||
|
self.save_menu.addMenu(self.save_sub_menu)
|
||||||
|
self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk)
|
||||||
|
|
||||||
|
self.action_view.triggered.connect(self.view_book)
|
||||||
|
self.view_menu = QMenu()
|
||||||
|
self.view_menu.addAction(_('View'), partial(self.view_book, False))
|
||||||
|
ac = self.view_menu.addAction(_('View specific format'))
|
||||||
|
ac.setShortcut((Qt.ControlModifier if isosx else Qt.AltModifier)+Qt.Key_V)
|
||||||
|
self.action_view.setMenu(self.view_menu)
|
||||||
|
ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
|
self.delete_menu = QMenu()
|
||||||
|
self.delete_menu.addAction(_('Remove selected books'), self.delete_books)
|
||||||
|
self.delete_menu.addAction(
|
||||||
|
_('Remove files of a specific format from selected books..'),
|
||||||
|
self.delete_selected_formats)
|
||||||
|
self.delete_menu.addAction(
|
||||||
|
_('Remove all formats from selected books, except...'),
|
||||||
|
self.delete_all_but_selected_formats)
|
||||||
|
self.delete_menu.addAction(
|
||||||
|
_('Remove covers from selected books'), self.delete_covers)
|
||||||
|
self.delete_menu.addSeparator()
|
||||||
|
self.delete_menu.addAction(
|
||||||
|
_('Remove matching books from device'),
|
||||||
|
self.remove_matching_books_from_device)
|
||||||
|
self.action_del.setMenu(self.delete_menu)
|
||||||
|
|
||||||
|
self.action_open_containing_folder.setShortcut(Qt.Key_O)
|
||||||
|
self.addAction(self.action_open_containing_folder)
|
||||||
|
self.action_open_containing_folder.triggered.connect(self.view_folder)
|
||||||
|
self.action_sync.setShortcut(Qt.Key_D)
|
||||||
|
self.action_sync.setEnabled(True)
|
||||||
|
self.create_device_menu()
|
||||||
|
self.action_sync.triggered.connect(
|
||||||
|
self._sync_action_triggered)
|
||||||
|
|
||||||
|
self.action_edit.setMenu(md)
|
||||||
|
self.action_save.setMenu(self.save_menu)
|
||||||
|
|
||||||
|
cm = QMenu()
|
||||||
|
cm.addAction(_('Convert individually'), partial(self.convert_ebook,
|
||||||
|
False, bulk=False))
|
||||||
|
cm.addAction(_('Bulk convert'),
|
||||||
|
partial(self.convert_ebook, False, bulk=True))
|
||||||
|
cm.addSeparator()
|
||||||
|
ac = cm.addAction(
|
||||||
|
_('Create catalog of books in your calibre library'))
|
||||||
|
ac.triggered.connect(self.generate_catalog)
|
||||||
|
self.action_convert.setMenu(cm)
|
||||||
|
self.action_convert.triggered.connect(self.convert_ebook)
|
||||||
|
self.convert_menu = cm
|
||||||
|
|
||||||
|
pm = QMenu()
|
||||||
|
pm.addAction(QIcon(I('config.svg')), _('Preferences'), self.do_config)
|
||||||
|
pm.addAction(QIcon(I('wizard.svg')), _('Run welcome wizard'),
|
||||||
|
self.run_wizard)
|
||||||
|
self.action_preferences.setMenu(pm)
|
||||||
|
self.preferences_menu = pm
|
||||||
|
for x in (self.preferences_action, self.action_preferences):
|
||||||
|
x.triggered.connect(self.do_config)
|
||||||
|
|
||||||
|
return all_actions
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
self.location_view = LocationView(self.centralwidget)
|
def show_help(self, *args):
|
||||||
self.search_bar = SearchBar(self)
|
open_url(QUrl('http://calibre-ebook.com/user_manual'))
|
||||||
self.tool_bar = ToolBar(all_actions, self.donate_button, self.location_view, self)
|
|
||||||
self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
|
|
||||||
|
|
||||||
l = self.centralwidget.layout()
|
|
||||||
l.addWidget(self.search_bar)
|
|
||||||
|
|
||||||
|
|
||||||
def read_toolbar_settings(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import collections, os, sys, textwrap, time
|
import collections, os, sys, textwrap, time
|
||||||
from Queue import Queue, Empty
|
from Queue import Queue, Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from PyQt4.Qt import Qt, SIGNAL, QObject, QTimer, \
|
from PyQt4.Qt import Qt, SIGNAL, QTimer, \
|
||||||
QPixmap, QMenu, QIcon, pyqtSignal, \
|
QPixmap, QMenu, QIcon, pyqtSignal, \
|
||||||
QDialog, \
|
QDialog, \
|
||||||
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
|
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
|
||||||
@ -38,7 +38,7 @@ from calibre.gui2.dialogs.config import ConfigDialog
|
|||||||
|
|
||||||
from calibre.gui2.dialogs.book_info import BookInfo
|
from calibre.gui2.dialogs.book_info import BookInfo
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
from calibre.library.database2 import LibraryDatabase2
|
||||||
from calibre.gui2.init import ToolbarMixin, LibraryViewMixin, LayoutMixin
|
from calibre.gui2.init import LibraryViewMixin, LayoutMixin
|
||||||
from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin
|
from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin
|
||||||
from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin
|
from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin
|
||||||
from calibre.gui2.tag_view import TagBrowserMixin
|
from calibre.gui2.tag_view import TagBrowserMixin
|
||||||
@ -91,7 +91,7 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
|
class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
||||||
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
||||||
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
|
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
|
||||||
AnnotationsAction, AddAction, DeleteAction,
|
AnnotationsAction, AddAction, DeleteAction,
|
||||||
@ -192,21 +192,14 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
|
|||||||
####################### Start spare job server ########################
|
####################### Start spare job server ########################
|
||||||
QTimer.singleShot(1000, self.add_spare_server)
|
QTimer.singleShot(1000, self.add_spare_server)
|
||||||
|
|
||||||
####################### Location View ########################
|
####################### Location Manager ########################
|
||||||
QObject.connect(self.location_view,
|
self.location_manager.location_selected.connect(self.location_selected)
|
||||||
SIGNAL('location_selected(PyQt_PyObject)'),
|
self.location_manager.unmount_device.connect(self.device_manager.umount_device)
|
||||||
self.location_selected)
|
|
||||||
QObject.connect(self.location_view,
|
|
||||||
SIGNAL('umount_device()'),
|
|
||||||
self.device_manager.umount_device)
|
|
||||||
self.eject_action.triggered.connect(self.device_manager.umount_device)
|
self.eject_action.triggered.connect(self.device_manager.umount_device)
|
||||||
|
|
||||||
#################### Update notification ###################
|
#################### Update notification ###################
|
||||||
UpdateMixin.__init__(self, opts)
|
UpdateMixin.__init__(self, opts)
|
||||||
|
|
||||||
####################### Setup Toolbar #####################
|
|
||||||
ToolbarMixin.__init__(self)
|
|
||||||
|
|
||||||
####################### Search boxes ########################
|
####################### Search boxes ########################
|
||||||
SavedSearchBoxMixin.__init__(self)
|
SavedSearchBoxMixin.__init__(self)
|
||||||
SearchBoxMixin.__init__(self)
|
SearchBoxMixin.__init__(self)
|
||||||
@ -218,7 +211,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
|
|||||||
|
|
||||||
if self.system_tray_icon.isVisible() and opts.start_in_tray:
|
if self.system_tray_icon.isVisible() and opts.start_in_tray:
|
||||||
self.hide_windows()
|
self.hide_windows()
|
||||||
for t in (self.location_view, self.tool_bar):
|
for t in (self.tool_bar, ):
|
||||||
self.library_view.model().count_changed_signal.connect \
|
self.library_view.model().count_changed_signal.connect \
|
||||||
(t.count_changed)
|
(t.count_changed)
|
||||||
if not gprefs.get('quick_start_guide_added', False):
|
if not gprefs.get('quick_start_guide_added', False):
|
||||||
@ -235,8 +228,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
|
|||||||
self.db_images.reset()
|
self.db_images.reset()
|
||||||
|
|
||||||
self.library_view.model().count_changed()
|
self.library_view.model().count_changed()
|
||||||
self.location_view.model().database_changed(self.library_view.model().db)
|
self.tool_bar.database_changed(self.library_view.model().db)
|
||||||
self.library_view.model().database_changed.connect(self.location_view.model().database_changed,
|
self.library_view.model().database_changed.connect(self.tool_bar.database_changed,
|
||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
|
|
||||||
########################### Tags Browser ##############################
|
########################### Tags Browser ##############################
|
||||||
|
Loading…
x
Reference in New Issue
Block a user