mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
4782888466
@ -5,7 +5,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, posixpath, urllib, sys
|
import os, posixpath, urllib, sys, re
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
@ -160,8 +160,26 @@ class Container(object):
|
|||||||
mt = mimetype.lower()
|
mt = mimetype.lower()
|
||||||
if mt.endswith('+xml'):
|
if mt.endswith('+xml'):
|
||||||
parser = etree.XMLParser(no_network=True, huge_tree=not iswindows)
|
parser = etree.XMLParser(no_network=True, huge_tree=not iswindows)
|
||||||
return etree.fromstring(xml_to_unicode(raw,
|
raw = xml_to_unicode(raw,
|
||||||
strip_encoding_pats=True, assume_utf8=True)[0], parser=parser)
|
strip_encoding_pats=True, assume_utf8=True,
|
||||||
|
resolve_entities=True)[0].strip()
|
||||||
|
idx = raw.find('<html')
|
||||||
|
if idx == -1:
|
||||||
|
idx = raw.find('<HTML')
|
||||||
|
if idx > -1:
|
||||||
|
pre = raw[:idx]
|
||||||
|
raw = raw[idx:]
|
||||||
|
if '<!DOCTYPE' in pre:
|
||||||
|
user_entities = {}
|
||||||
|
for match in re.finditer(r'<!ENTITY\s+(\S+)\s+([^>]+)', pre):
|
||||||
|
val = match.group(2)
|
||||||
|
if val.startswith('"') and val.endswith('"'):
|
||||||
|
val = val[1:-1]
|
||||||
|
user_entities[match.group(1)] = val
|
||||||
|
if user_entities:
|
||||||
|
pat = re.compile(r'&(%s);'%('|'.join(user_entities.keys())))
|
||||||
|
raw = pat.sub(lambda m:user_entities[m.group(1)], raw)
|
||||||
|
return etree.fromstring(raw, parser=parser)
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
def write(self, path):
|
def write(self, path):
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import functools, sys, os
|
import functools, sys, os
|
||||||
|
|
||||||
from PyQt4.Qt import QMenu, Qt, pyqtSignal, QToolButton, QIcon, QStackedWidget, \
|
from PyQt4.Qt import QMenu, Qt, pyqtSignal, QIcon, QStackedWidget, \
|
||||||
QSize, QSizePolicy, QStatusBar, QUrl, QLabel, QFont
|
QSize, QSizePolicy, QStatusBar, QUrl, QLabel, QFont
|
||||||
|
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
@ -173,20 +173,6 @@ class ToolbarMixin(object): # {{{
|
|||||||
for x in (self.preferences_action, self.action_preferences):
|
for x in (self.preferences_action, self.action_preferences):
|
||||||
x.triggered.connect(self.do_config)
|
x.triggered.connect(self.do_config)
|
||||||
|
|
||||||
for x in ('news', 'edit', 'sync', 'convert', 'save', 'add', 'view',
|
|
||||||
'del', 'preferences'):
|
|
||||||
w = self.tool_bar.widgetForAction(getattr(self, 'action_'+x))
|
|
||||||
w.setPopupMode(w.MenuButtonPopup)
|
|
||||||
|
|
||||||
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
|
|
||||||
|
|
||||||
for ch in self.tool_bar.children():
|
|
||||||
if isinstance(ch, QToolButton):
|
|
||||||
ch.setCursor(Qt.PointingHandCursor)
|
|
||||||
ch.setStatusTip(ch.toolTip())
|
|
||||||
|
|
||||||
self.tool_bar.contextMenuEvent = self.no_op
|
|
||||||
|
|
||||||
def show_help(self, *args):
|
def show_help(self, *args):
|
||||||
open_url(QUrl('http://calibre-ebook.com/user_manual'))
|
open_url(QUrl('http://calibre-ebook.com/user_manual'))
|
||||||
|
|
||||||
@ -435,8 +421,6 @@ class StatusBar(QStatusBar): # {{{
|
|||||||
class LayoutMixin(object): # {{{
|
class LayoutMixin(object): # {{{
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.setupUi(self)
|
|
||||||
self.setWindowTitle(__appname__)
|
|
||||||
|
|
||||||
if config['gui_layout'] == 'narrow': # narrow {{{
|
if config['gui_layout'] == 'narrow': # narrow {{{
|
||||||
self.book_details = BookDetails(False, self)
|
self.book_details = BookDetails(False, self)
|
||||||
|
460
src/calibre/gui2/layout.py
Normal file
460
src/calibre/gui2/layout.py
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \
|
||||||
|
QAbstractListModel, QFont, QApplication, QPalette, pyqtSignal, QToolButton, \
|
||||||
|
QModelIndex, QListView, QAbstractButton, QPainter, QPixmap, QColor, \
|
||||||
|
QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QComboBox
|
||||||
|
|
||||||
|
from calibre.constants import __appname__, filesystem_encoding
|
||||||
|
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
|
||||||
|
from calibre.gui2.throbber import ThrobbingButton
|
||||||
|
from calibre.gui2 import NONE
|
||||||
|
from calibre import human_readable
|
||||||
|
|
||||||
|
class ToolBar(QToolBar): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QToolBar.__init__(self, parent)
|
||||||
|
self.setContextMenuPolicy(Qt.PreventContextMenu)
|
||||||
|
self.setMovable(False)
|
||||||
|
self.setFloatable(False)
|
||||||
|
self.setOrientation(Qt.Horizontal)
|
||||||
|
self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
|
||||||
|
self.setIconSize(QSize(48, 48))
|
||||||
|
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||||
|
|
||||||
|
def add_actions(self, *args):
|
||||||
|
self.left_space = QWidget(self)
|
||||||
|
self.left_space.setSizePolicy(QSizePolicy.Expanding,
|
||||||
|
QSizePolicy.Minimum)
|
||||||
|
self.addWidget(self.left_space)
|
||||||
|
for action in args:
|
||||||
|
if action is None:
|
||||||
|
self.addSeparator()
|
||||||
|
else:
|
||||||
|
self.addAction(action)
|
||||||
|
self.right_space = QWidget(self)
|
||||||
|
self.right_space.setSizePolicy(QSizePolicy.Expanding,
|
||||||
|
QSizePolicy.Minimum)
|
||||||
|
self.addWidget(self.right_space)
|
||||||
|
|
||||||
|
def contextMenuEvent(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Location View {{{
|
||||||
|
|
||||||
|
class LocationModel(QAbstractListModel): # {{{
|
||||||
|
|
||||||
|
devicesChanged = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QAbstractListModel.__init__(self, parent)
|
||||||
|
self.icons = [QVariant(QIcon(I('library.png'))),
|
||||||
|
QVariant(QIcon(I('reader.svg'))),
|
||||||
|
QVariant(QIcon(I('sd.svg'))),
|
||||||
|
QVariant(QIcon(I('sd.svg')))]
|
||||||
|
self.text = [_('Library\n%d books'),
|
||||||
|
_('Reader\n%s'),
|
||||||
|
_('Card A\n%s'),
|
||||||
|
_('Card B\n%s')]
|
||||||
|
self.free = [-1, -1, -1]
|
||||||
|
self.count = 0
|
||||||
|
self.highlight_row = 0
|
||||||
|
self.library_tooltip = _('Click to see the books available on your computer')
|
||||||
|
self.tooltips = [
|
||||||
|
self.library_tooltip,
|
||||||
|
_('Click to see the books in the main memory of your reader'),
|
||||||
|
_('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):
|
||||||
|
lp = db.library_path
|
||||||
|
if not isinstance(lp, unicode):
|
||||||
|
lp = lp.decode(filesystem_encoding, 'replace')
|
||||||
|
self.tooltips[0] = self.library_tooltip + '\n\n' + \
|
||||||
|
_('Books located at') + ' ' + lp
|
||||||
|
self.dataChanged.emit(self.index(0), self.index(0))
|
||||||
|
|
||||||
|
def rowCount(self, *args):
|
||||||
|
return 1 + len([i for i in self.free if i >= 0])
|
||||||
|
|
||||||
|
def get_device_row(self, row):
|
||||||
|
if row == 2 and self.free[1] == -1 and self.free[2] > -1:
|
||||||
|
row = 3
|
||||||
|
return row
|
||||||
|
|
||||||
|
def get_tooltip(self, row, drow):
|
||||||
|
ans = self.tooltips[row]
|
||||||
|
if row > 0:
|
||||||
|
fs = self.free[drow-1]
|
||||||
|
if fs > -1:
|
||||||
|
ans += '\n\n%s '%(human_readable(fs)) + _('free')
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
row = index.row()
|
||||||
|
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 device_connected(self, dev):
|
||||||
|
self.icons[1] = QIcon(dev.icon)
|
||||||
|
self.dataChanged.emit(self.index(1), self.index(1))
|
||||||
|
|
||||||
|
def headerData(self, section, orientation, role):
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def update_devices(self, cp=(None, None), fs=[-1, -1, -1]):
|
||||||
|
if cp is None:
|
||||||
|
cp = (None, None)
|
||||||
|
if isinstance(cp, (str, unicode)):
|
||||||
|
cp = (cp, None)
|
||||||
|
if len(fs) < 3:
|
||||||
|
fs = list(fs) + [0]
|
||||||
|
self.free[0] = fs[0]
|
||||||
|
self.free[1] = fs[1]
|
||||||
|
self.free[2] = fs[2]
|
||||||
|
cpa, cpb = cp
|
||||||
|
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.reset()
|
||||||
|
self.devicesChanged.emit()
|
||||||
|
|
||||||
|
def location_changed(self, row):
|
||||||
|
self.highlight_row = row
|
||||||
|
self.dataChanged.emit(
|
||||||
|
self.index(0), self.index(self.rowCount(QModelIndex())-1))
|
||||||
|
|
||||||
|
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):
|
||||||
|
|
||||||
|
unmount_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(40, 40))
|
||||||
|
self.setMovement(self.Static)
|
||||||
|
self.setFlow(self.LeftToRight)
|
||||||
|
self.setGridSize(QSize(175, 90))
|
||||||
|
self.setViewMode(self.ListMode)
|
||||||
|
self.setWordWrap(True)
|
||||||
|
self.setObjectName("location_view")
|
||||||
|
self.setMaximumHeight(74)
|
||||||
|
|
||||||
|
def eject_clicked(self, *args):
|
||||||
|
self.unmount_device.emit()
|
||||||
|
|
||||||
|
def count_changed(self, new_count):
|
||||||
|
self.model().count = new_count
|
||||||
|
self.model().reset()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def enterEvent(self, event):
|
||||||
|
self.mouse_over = True
|
||||||
|
|
||||||
|
def leaveEvent(self, event):
|
||||||
|
self.mouse_over = False
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class SearchBar(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self._layout = l = QHBoxLayout()
|
||||||
|
self.setLayout(self._layout)
|
||||||
|
|
||||||
|
self.restriction_label = QLabel(_("&Restrict to:"))
|
||||||
|
l.addWidget(self.restriction_label)
|
||||||
|
self.restriction_label.setSizePolicy(QSizePolicy.Minimum,
|
||||||
|
QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
x = QComboBox(self)
|
||||||
|
x.setMaximumSize(QSize(150, 16777215))
|
||||||
|
x.setObjectName("search_restriction")
|
||||||
|
x.setToolTip(_("Books display will be restricted to those matching the selected saved search"))
|
||||||
|
l.addWidget(x)
|
||||||
|
parent.search_restriction = x
|
||||||
|
|
||||||
|
x = QLabel(self)
|
||||||
|
x.setObjectName("search_count")
|
||||||
|
l.addWidget(x)
|
||||||
|
parent.search_count = x
|
||||||
|
x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
parent.advanced_search_button = x = QToolButton(self)
|
||||||
|
x.setIcon(QIcon(I('search.svg')))
|
||||||
|
l.addWidget(x)
|
||||||
|
x.setToolTip(_("Advanced search"))
|
||||||
|
|
||||||
|
self.label = x = QLabel('&Search:')
|
||||||
|
l.addWidget(self.label)
|
||||||
|
x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
x = parent.search = SearchBox2(self)
|
||||||
|
x.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||||
|
x.setObjectName("search")
|
||||||
|
x.setToolTip(_("<p>Search the list of books by title, author, publisher, tags, comments, etc.<br><br>Words separated by spaces are ANDed"))
|
||||||
|
l.addWidget(x)
|
||||||
|
|
||||||
|
x = parent.clear_button = QToolButton(self)
|
||||||
|
x.setIcon(QIcon(I('clear_left.svg')))
|
||||||
|
x.setObjectName("clear_button")
|
||||||
|
l.addWidget(x)
|
||||||
|
x.setToolTip(_("Reset Quick Search"))
|
||||||
|
|
||||||
|
x = parent.saved_search = SavedSearchBox(self)
|
||||||
|
x.setMaximumSize(QSize(150, 16777215))
|
||||||
|
x.setMinimumContentsLength(15)
|
||||||
|
x.setObjectName("saved_search")
|
||||||
|
l.addWidget(x)
|
||||||
|
|
||||||
|
x = parent.copy_search_button = QToolButton(self)
|
||||||
|
x.setIcon(QIcon(I("search_copy_saved.svg")))
|
||||||
|
x.setObjectName("copy_search_button")
|
||||||
|
l.addWidget(x)
|
||||||
|
x.setToolTip(_("Copy current search text (instead of search name)"))
|
||||||
|
|
||||||
|
x = parent.save_search_button = QToolButton(self)
|
||||||
|
x.setIcon(QIcon(I("search_add_saved.svg")))
|
||||||
|
x.setObjectName("save_search_button")
|
||||||
|
l.addWidget(x)
|
||||||
|
x.setToolTip(_("Save current search under the name shown in the box"))
|
||||||
|
|
||||||
|
x = parent.delete_search_button = QToolButton(self)
|
||||||
|
x.setIcon(QIcon(I("search_delete_saved.svg")))
|
||||||
|
x.setObjectName("delete_search_button")
|
||||||
|
l.addWidget(x)
|
||||||
|
x.setToolTip(_("Delete current saved search"))
|
||||||
|
|
||||||
|
self.label.setBuddy(parent.search)
|
||||||
|
self.restriction_label.setBuddy(parent.search_restriction)
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class LocationBar(ToolBar): # {{{
|
||||||
|
|
||||||
|
def __init__(self, actions, donate, location_view, parent=None):
|
||||||
|
ToolBar.__init__(self, parent)
|
||||||
|
|
||||||
|
for ac in actions:
|
||||||
|
self.addAction(ac)
|
||||||
|
|
||||||
|
self.addWidget(location_view)
|
||||||
|
self.w = QWidget()
|
||||||
|
self.w.setLayout(QVBoxLayout())
|
||||||
|
self.w.layout().addWidget(donate)
|
||||||
|
donate.setAutoRaise(True)
|
||||||
|
donate.setCursor(Qt.PointingHandCursor)
|
||||||
|
self.addWidget(self.w)
|
||||||
|
self.setIconSize(QSize(50, 50))
|
||||||
|
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||||
|
|
||||||
|
def button_for_action(self, ac):
|
||||||
|
b = QToolButton(self)
|
||||||
|
b.setDefaultAction(ac)
|
||||||
|
for x in ('ToolTip', 'StatusTip', 'WhatsThis'):
|
||||||
|
getattr(b, 'set'+x)(b.text())
|
||||||
|
|
||||||
|
return b
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class MainWindowMixin(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.setObjectName('MainWindow')
|
||||||
|
self.setWindowIcon(QIcon(I('library.png')))
|
||||||
|
self.setWindowTitle(__appname__)
|
||||||
|
|
||||||
|
self.setContextMenuPolicy(Qt.NoContextMenu)
|
||||||
|
self.centralwidget = QWidget(self)
|
||||||
|
self.setCentralWidget(self.centralwidget)
|
||||||
|
self._central_widget_layout = QVBoxLayout()
|
||||||
|
self.centralwidget.setLayout(self._central_widget_layout)
|
||||||
|
self.resize(1012, 740)
|
||||||
|
self.donate_button = ThrobbingButton(self.centralwidget)
|
||||||
|
self.donate_button.set_normal_icon_size(64, 64)
|
||||||
|
|
||||||
|
# Actions {{{
|
||||||
|
|
||||||
|
def ac(name, text, icon, shortcut=None, tooltip=None):
|
||||||
|
action = QAction(QIcon(I(icon)), text, self)
|
||||||
|
text = tooltip if tooltip else text
|
||||||
|
action.setToolTip(text)
|
||||||
|
action.setStatusTip(text)
|
||||||
|
action.setWhatsThis(text)
|
||||||
|
action.setAutoRepeat(False)
|
||||||
|
action.setObjectName('action_'+name)
|
||||||
|
if shortcut:
|
||||||
|
action.setShortcut(shortcut)
|
||||||
|
setattr(self, 'action_'+name, action)
|
||||||
|
|
||||||
|
ac('add', _('Add books'), 'add_book.svg', _('A'))
|
||||||
|
ac('del', _('Remove books'), 'trash.svg', _('Del'))
|
||||||
|
ac('edit', _('Edit meta info'), 'edit_input.svg', _('E'))
|
||||||
|
ac('merge', _('Merge book records'), 'merge_books.svg', _('M'))
|
||||||
|
ac('sync', _('Send to device'), 'sync.svg')
|
||||||
|
ac('save', _('Save to disk'), 'save.svg', _('S'))
|
||||||
|
ac('news', _('Fetch news'), 'news.svg', _('F'))
|
||||||
|
ac('convert', _('Convert books'), 'convert.svg', _('C'))
|
||||||
|
ac('view', _('View'), 'view.svg', _('V'))
|
||||||
|
ac('open_containing_folder', _('Open containing folder'),
|
||||||
|
'document_open.svg')
|
||||||
|
ac('show_book_details', _('Show book details'),
|
||||||
|
'dialog_information.svg')
|
||||||
|
ac('books_by_same_author', _('Books by same author'),
|
||||||
|
'user_profile.svg')
|
||||||
|
ac('books_in_this_series', _('Books in this series'),
|
||||||
|
'books_in_series.svg')
|
||||||
|
ac('books_by_this_publisher', _('Books by this publisher'),
|
||||||
|
'publisher.png')
|
||||||
|
ac('books_with_the_same_tags', _('Books with the same tags'),
|
||||||
|
'tags.svg')
|
||||||
|
ac('preferences', _('Preferences'), 'config.svg', _('Ctrl+P'))
|
||||||
|
ac('help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual"))
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
self.tool_bar = ToolBar(self)
|
||||||
|
self.addToolBar(Qt.BottomToolBarArea, self.tool_bar)
|
||||||
|
self.tool_bar.add_actions(self.action_convert, self.action_view,
|
||||||
|
None, self.action_edit, None,
|
||||||
|
self.action_save, self.action_del,
|
||||||
|
None,
|
||||||
|
self.action_help, None, self.action_preferences)
|
||||||
|
|
||||||
|
self.location_view = LocationView(self.centralwidget)
|
||||||
|
self.search_bar = SearchBar(self)
|
||||||
|
self.location_bar = LocationBar([self.action_add, self.action_sync,
|
||||||
|
self.action_news], self.donate_button, self.location_view, self)
|
||||||
|
self.addToolBar(Qt.TopToolBarArea, self.location_bar)
|
||||||
|
|
||||||
|
l = self.centralwidget.layout()
|
||||||
|
l.addWidget(self.search_bar)
|
||||||
|
|
||||||
|
for ch in list(self.tool_bar.children()) + list(self.location_bar.children()):
|
||||||
|
if isinstance(ch, QToolButton):
|
||||||
|
ch.setCursor(Qt.PointingHandCursor)
|
||||||
|
ch.setAutoRaise(True)
|
||||||
|
if ch is not self.donate_button:
|
||||||
|
ch.setPopupMode(ch.MenuButtonPopup)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,551 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<author>Kovid Goyal</author>
|
|
||||||
<class>MainWindow</class>
|
|
||||||
<widget class="QMainWindow" name="MainWindow">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>1012</width>
|
|
||||||
<height>822</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="contextMenuPolicy">
|
|
||||||
<enum>Qt::NoContextMenu</enum>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>__appname__</string>
|
|
||||||
</property>
|
|
||||||
<property name="windowIcon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/library.png</normaloff>:/images/library.png</iconset>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralwidget">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
|
||||||
<item>
|
|
||||||
<widget class="LocationView" name="location_view">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>75</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="mouseTracking">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
|
||||||
</property>
|
|
||||||
<property name="horizontalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
|
||||||
</property>
|
|
||||||
<property name="editTriggers">
|
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
|
||||||
</property>
|
|
||||||
<property name="tabKeyNavigation">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="showDropIndicator" stdset="0">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionMode">
|
|
||||||
<enum>QAbstractItemView::NoSelection</enum>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior">
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="movement">
|
|
||||||
<enum>QListView::Static</enum>
|
|
||||||
</property>
|
|
||||||
<property name="flow">
|
|
||||||
<enum>QListView::LeftToRight</enum>
|
|
||||||
</property>
|
|
||||||
<property name="gridSize">
|
|
||||||
<size>
|
|
||||||
<width>175</width>
|
|
||||||
<height>90</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="viewMode">
|
|
||||||
<enum>QListView::ListMode</enum>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="ThrobbingButton" name="donate_button">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/donate.svg</normaloff>:/images/donate.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="autoRaise">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hl234">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="margin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="ComboBoxWithHelp" name="search_restriction">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>150</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Books display will be restricted to those matching the selected saved search</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="search_count">
|
|
||||||
<property name="text">
|
|
||||||
<string>set in ui.py</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="advanced_search_button">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Advanced search</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/search.svg</normaloff>:/images/search.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>Alt+S</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Search:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>search</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="SearchBox2" name="search">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>700</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><p>Search the list of books by title, author, publisher, tags, comments, etc.<br><br>Words separated by spaces are ANDed</string>
|
|
||||||
</property>
|
|
||||||
<property name="whatsThis">
|
|
||||||
<string><p>Search the list of books by title, author, publisher, tags, comments, etc.<br><br>Words separated by spaces are ANDed</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="clear_button">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Reset Quick Search</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/clear_left.svg</normaloff>:/images/clear_left.svg</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="SavedSearchBox" name="saved_search">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>150</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="minimumContentsLength">
|
|
||||||
<number>15</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="copy_search_button">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Copy current search text (instead of search name)</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/search_copy_saved.svg</normaloff>:/images/search_copy_saved.svg</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="save_search_button">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Save current search under the name shown in the box</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/search_add_saved.svg</normaloff>:/images/search_add_saved.svg</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="delete_search_button">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Delete current saved search</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/search_delete_saved.svg</normaloff>:/images/search_delete_saved.svg</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QToolBar" name="tool_bar">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="contextMenuPolicy">
|
|
||||||
<enum>Qt::PreventContextMenu</enum>
|
|
||||||
</property>
|
|
||||||
<property name="movable">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>48</width>
|
|
||||||
<height>48</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
|
||||||
</property>
|
|
||||||
<attribute name="toolBarArea">
|
|
||||||
<enum>TopToolBarArea</enum>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="toolBarBreak">
|
|
||||||
<bool>false</bool>
|
|
||||||
</attribute>
|
|
||||||
<addaction name="action_add"/>
|
|
||||||
<addaction name="action_edit"/>
|
|
||||||
<addaction name="action_convert"/>
|
|
||||||
<addaction name="action_view"/>
|
|
||||||
<addaction name="action_news"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_sync"/>
|
|
||||||
<addaction name="action_save"/>
|
|
||||||
<addaction name="action_del"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_help"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_preferences"/>
|
|
||||||
</widget>
|
|
||||||
<action name="action_add">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/add_book.svg</normaloff>:/images/add_book.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Add books</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>A</string>
|
|
||||||
</property>
|
|
||||||
<property name="autoRepeat">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_del">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/trash.svg</normaloff>:/images/trash.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove books</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Remove books</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>Del</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_edit">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/edit_input.svg</normaloff>:/images/edit_input.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Edit meta information</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>E</string>
|
|
||||||
</property>
|
|
||||||
<property name="autoRepeat">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_merge">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/merge_books.svg</normaloff>:/images/merge_books.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Merge book records</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>M</string>
|
|
||||||
</property>
|
|
||||||
<property name="autoRepeat">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_sync">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/sync.svg</normaloff>:/images/sync.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Send to device</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_save">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/save.svg</normaloff>:/images/save.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Save to disk</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>S</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_news">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/news.svg</normaloff>:/images/news.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Fetch news</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>F</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_convert">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/convert.svg</normaloff>:/images/convert.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Convert E-books</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>C</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_view">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/view.svg</normaloff>:/images/view.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>View</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>V</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_open_containing_folder">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Open containing folder</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_show_book_details">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/dialog_information.svg</normaloff>:/images/dialog_information.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Show book details</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_books_by_same_author">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/user_profile.svg</normaloff>:/images/user_profile.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Books by same author</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_books_in_this_series">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/books_in_series.svg</normaloff>:/images/books_in_series.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Books in this series</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_books_by_this_publisher">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/publisher.png</normaloff>:/images/publisher.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Books by this publisher</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_books_with_the_same_tags">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/tags.svg</normaloff>:/images/tags.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Books with the same tags</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_preferences">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Preferences</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Configure calibre</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>Ctrl+P</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_help">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/help.svg</normaloff>:/images/help.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Help</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Browse the calibre User Manual</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>F1</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>LocationView</class>
|
|
||||||
<extends>QListView</extends>
|
|
||||||
<header>widgets.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>SearchBox2</class>
|
|
||||||
<extends>QComboBox</extends>
|
|
||||||
<header>calibre.gui2.search_box</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>SavedSearchBox</class>
|
|
||||||
<extends>QComboBox</extends>
|
|
||||||
<header>calibre.gui2.search_box</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>ComboBoxWithHelp</class>
|
|
||||||
<extends>QComboBox</extends>
|
|
||||||
<header>calibre.gui2.widgets</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>ThrobbingButton</class>
|
|
||||||
<extends>QToolButton</extends>
|
|
||||||
<header>calibre/gui2/throbber.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources>
|
|
||||||
<include location="../../../resources/images.qrc"/>
|
|
||||||
</resources>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -31,7 +31,7 @@ from calibre.gui2.wizard import move_library
|
|||||||
from calibre.gui2.dialogs.scheduler import Scheduler
|
from calibre.gui2.dialogs.scheduler import Scheduler
|
||||||
from calibre.gui2.update import UpdateMixin
|
from calibre.gui2.update import UpdateMixin
|
||||||
from calibre.gui2.main_window import MainWindow
|
from calibre.gui2.main_window import MainWindow
|
||||||
from calibre.gui2.main_ui import Ui_MainWindow
|
from calibre.gui2.layout import MainWindowMixin
|
||||||
from calibre.gui2.device import DeviceMixin
|
from calibre.gui2.device import DeviceMixin
|
||||||
from calibre.gui2.jobs import JobManager, JobsDialog, JobsButton
|
from calibre.gui2.jobs import JobManager, JobsDialog, JobsButton
|
||||||
from calibre.gui2.dialogs.config import ConfigDialog
|
from calibre.gui2.dialogs.config import ConfigDialog
|
||||||
@ -91,7 +91,7 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
|
class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
|
||||||
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
||||||
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
|
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
|
||||||
AnnotationsAction, AddAction, DeleteAction,
|
AnnotationsAction, AddAction, DeleteAction,
|
||||||
@ -120,7 +120,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
|
|||||||
self.another_instance_wants_to_talk)
|
self.another_instance_wants_to_talk)
|
||||||
self.check_messages_timer.start(1000)
|
self.check_messages_timer.start(1000)
|
||||||
|
|
||||||
Ui_MainWindow.__init__(self)
|
MainWindowMixin.__init__(self)
|
||||||
|
|
||||||
# Jobs Button {{{
|
# Jobs Button {{{
|
||||||
self.job_manager = JobManager()
|
self.job_manager = JobManager()
|
||||||
@ -281,7 +281,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
|
|||||||
|
|
||||||
self.read_settings()
|
self.read_settings()
|
||||||
self.finalize_layout()
|
self.finalize_layout()
|
||||||
self.donate_button.set_normal_icon_size(64, 64)
|
|
||||||
self.donate_button.start_animation()
|
self.donate_button.start_animation()
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
|
@ -5,26 +5,25 @@ Miscellaneous widgets used in the GUI
|
|||||||
'''
|
'''
|
||||||
import re, os, traceback
|
import re, os, traceback
|
||||||
|
|
||||||
from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
|
from PyQt4.Qt import QIcon, QFont, QLabel, QListWidget, \
|
||||||
QListWidgetItem, QTextCharFormat, QApplication, \
|
QListWidgetItem, QTextCharFormat, QApplication, \
|
||||||
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
||||||
QPixmap, QPalette, QSplitterHandle, QToolButton, \
|
QPixmap, QSplitterHandle, QToolButton, \
|
||||||
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
|
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
|
||||||
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
|
QRegExp, QSettings, QSize, QSplitter, \
|
||||||
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
QPainter, QLineEdit, QComboBox, \
|
||||||
QMenu, QStringListModel, QCompleter, QStringList, \
|
QMenu, QStringListModel, QCompleter, QStringList, \
|
||||||
QTimer, QRect
|
QTimer, QRect
|
||||||
|
|
||||||
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
|
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
|
||||||
|
|
||||||
from calibre.gui2.filename_pattern_ui import Ui_Form
|
from calibre.gui2.filename_pattern_ui import Ui_Form
|
||||||
from calibre import fit_image, human_readable
|
from calibre import fit_image
|
||||||
from calibre.utils.fonts import fontconfig
|
from calibre.utils.fonts import fontconfig
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
from calibre.ebooks.metadata.meta import metadata_from_filename
|
from calibre.ebooks.metadata.meta import metadata_from_filename
|
||||||
from calibre.utils.config import prefs, XMLConfig
|
from calibre.utils.config import prefs, XMLConfig
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
|
||||||
from calibre.constants import filesystem_encoding
|
|
||||||
|
|
||||||
history = XMLConfig('history')
|
history = XMLConfig('history')
|
||||||
|
|
||||||
@ -259,198 +258,6 @@ class ImageView(QWidget):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class LocationModel(QAbstractListModel):
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
QAbstractListModel.__init__(self, parent)
|
|
||||||
self.icons = [QVariant(QIcon(I('library.png'))),
|
|
||||||
QVariant(QIcon(I('reader.svg'))),
|
|
||||||
QVariant(QIcon(I('sd.svg'))),
|
|
||||||
QVariant(QIcon(I('sd.svg')))]
|
|
||||||
self.text = [_('Library\n%d books'),
|
|
||||||
_('Reader\n%s'),
|
|
||||||
_('Card A\n%s'),
|
|
||||||
_('Card B\n%s')]
|
|
||||||
self.free = [-1, -1, -1]
|
|
||||||
self.count = 0
|
|
||||||
self.highlight_row = 0
|
|
||||||
self.library_tooltip = _('Click to see the books available on your computer')
|
|
||||||
self.tooltips = [
|
|
||||||
self.library_tooltip,
|
|
||||||
_('Click to see the books in the main memory of your reader'),
|
|
||||||
_('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):
|
|
||||||
lp = db.library_path
|
|
||||||
if not isinstance(lp, unicode):
|
|
||||||
lp = lp.decode(filesystem_encoding, 'replace')
|
|
||||||
self.tooltips[0] = self.library_tooltip + '\n\n' + \
|
|
||||||
_('Books located at') + ' ' + lp
|
|
||||||
self.dataChanged.emit(self.index(0), self.index(0))
|
|
||||||
|
|
||||||
def rowCount(self, *args):
|
|
||||||
return 1 + len([i for i in self.free if i >= 0])
|
|
||||||
|
|
||||||
def get_device_row(self, row):
|
|
||||||
if row == 2 and self.free[1] == -1 and self.free[2] > -1:
|
|
||||||
row = 3
|
|
||||||
return row
|
|
||||||
|
|
||||||
def get_tooltip(self, row, drow):
|
|
||||||
ans = self.tooltips[row]
|
|
||||||
if row > 0:
|
|
||||||
fs = self.free[drow-1]
|
|
||||||
if fs > -1:
|
|
||||||
ans += '\n\n%s '%(human_readable(fs)) + _('free')
|
|
||||||
return ans
|
|
||||||
|
|
||||||
def data(self, index, role):
|
|
||||||
row = index.row()
|
|
||||||
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 device_connected(self, dev):
|
|
||||||
self.icons[1] = QIcon(dev.icon)
|
|
||||||
self.dataChanged.emit(self.index(1), self.index(1))
|
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
|
||||||
return NONE
|
|
||||||
|
|
||||||
def update_devices(self, cp=(None, None), fs=[-1, -1, -1]):
|
|
||||||
if cp is None:
|
|
||||||
cp = (None, None)
|
|
||||||
if isinstance(cp, (str, unicode)):
|
|
||||||
cp = (cp, None)
|
|
||||||
if len(fs) < 3:
|
|
||||||
fs = list(fs) + [0]
|
|
||||||
self.free[0] = fs[0]
|
|
||||||
self.free[1] = fs[1]
|
|
||||||
self.free[2] = fs[2]
|
|
||||||
cpa, cpb = cp
|
|
||||||
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.reset()
|
|
||||||
self.emit(SIGNAL('devicesChanged()'))
|
|
||||||
|
|
||||||
def location_changed(self, row):
|
|
||||||
self.highlight_row = row
|
|
||||||
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'),
|
|
||||||
self.index(0), self.index(self.rowCount(QModelIndex())-1))
|
|
||||||
|
|
||||||
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):
|
|
||||||
|
|
||||||
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.connect(self, SIGNAL('entered(QModelIndex)'), self.item_entered)
|
|
||||||
self.connect(self, SIGNAL('viewportEntered()'), self.viewport_entered)
|
|
||||||
self.connect(self.eject_button, SIGNAL('clicked()'), lambda: self.emit(SIGNAL('umount_device()')))
|
|
||||||
self.connect(self.model(), SIGNAL('devicesChanged()'), self.eject_button.hide)
|
|
||||||
|
|
||||||
def count_changed(self, new_count):
|
|
||||||
self.model().count = new_count
|
|
||||||
self.model().reset()
|
|
||||||
|
|
||||||
def current_changed(self, current, previous):
|
|
||||||
if current.isValid():
|
|
||||||
i = current.row()
|
|
||||||
location = self.model().location_for_row(i)
|
|
||||||
self.emit(SIGNAL('location_selected(PyQt_PyObject)'), 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
|
|
||||||
|
|
||||||
def enterEvent(self, event):
|
|
||||||
self.mouse_over = True
|
|
||||||
|
|
||||||
def leaveEvent(self, event):
|
|
||||||
self.mouse_over = False
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FontFamilyModel(QAbstractListModel):
|
class FontFamilyModel(QAbstractListModel):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user