Merge from trunk

This commit is contained in:
Charles Haley 2010-07-10 13:58:34 +01:00
commit 4782888466
6 changed files with 490 additions and 773 deletions

View File

@ -5,7 +5,7 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, posixpath, urllib, sys
import os, posixpath, urllib, sys, re
from lxml import etree
@ -160,8 +160,26 @@ class Container(object):
mt = mimetype.lower()
if mt.endswith('+xml'):
parser = etree.XMLParser(no_network=True, huge_tree=not iswindows)
return etree.fromstring(xml_to_unicode(raw,
strip_encoding_pats=True, assume_utf8=True)[0], parser=parser)
raw = xml_to_unicode(raw,
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
def write(self, path):

View File

@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
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
from calibre.utils.config import prefs
@ -173,20 +173,6 @@ class ToolbarMixin(object): # {{{
for x in (self.preferences_action, self.action_preferences):
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):
open_url(QUrl('http://calibre-ebook.com/user_manual'))
@ -435,8 +421,6 @@ class StatusBar(QStatusBar): # {{{
class LayoutMixin(object): # {{{
def __init__(self):
self.setupUi(self)
self.setWindowTitle(__appname__)
if config['gui_layout'] == 'narrow': # narrow {{{
self.book_details = BookDetails(False, self)

460
src/calibre/gui2/layout.py Normal file
View 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)

View File

@ -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>&amp;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>&lt;p&gt;Search the list of books by title, author, publisher, tags, comments, etc.&lt;br&gt;&lt;br&gt;Words separated by spaces are ANDed</string>
</property>
<property name="whatsThis">
<string>&lt;p&gt;Search the list of books by title, author, publisher, tags, comments, etc.&lt;br&gt;&lt;br&gt;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>

View File

@ -31,7 +31,7 @@ from calibre.gui2.wizard import move_library
from calibre.gui2.dialogs.scheduler import Scheduler
from calibre.gui2.update import UpdateMixin
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.jobs import JobManager, JobsDialog, JobsButton
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,
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
AnnotationsAction, AddAction, DeleteAction,
@ -120,7 +120,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
self.another_instance_wants_to_talk)
self.check_messages_timer.start(1000)
Ui_MainWindow.__init__(self)
MainWindowMixin.__init__(self)
# Jobs Button {{{
self.job_manager = JobManager()
@ -281,7 +281,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
self.read_settings()
self.finalize_layout()
self.donate_button.set_normal_icon_size(64, 64)
self.donate_button.start_animation()
def resizeEvent(self, ev):

View File

@ -5,26 +5,25 @@ Miscellaneous widgets used in the GUI
'''
import re, os, traceback
from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
from PyQt4.Qt import QIcon, QFont, QLabel, QListWidget, \
QListWidgetItem, QTextCharFormat, QApplication, \
QSyntaxHighlighter, QCursor, QColor, QWidget, \
QPixmap, QPalette, QSplitterHandle, QToolButton, \
QPixmap, QSplitterHandle, QToolButton, \
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
QAbstractButton, QPainter, QLineEdit, QComboBox, \
QRegExp, QSettings, QSize, QSplitter, \
QPainter, QLineEdit, QComboBox, \
QMenu, QStringListModel, QCompleter, QStringList, \
QTimer, QRect
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
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.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.metadata.meta import metadata_from_filename
from calibre.utils.config import prefs, XMLConfig
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
from calibre.constants import filesystem_encoding
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):