Merge from custcol trunk

This commit is contained in:
Charles Haley 2010-05-04 11:01:02 +01:00
commit 6f9063705e
15 changed files with 647 additions and 483 deletions

View File

@ -18,7 +18,8 @@ class ANDROID(USBMS):
FORMATS = ['epub', 'pdf']
VENDOR_ID = {
0x0bb4 : { 0x0c02 : [0x100], 0x0c01 : [0x100]},
# HTC
0x0bb4 : { 0x0c02 : [0x100], 0x0c01 : [0x100], 0x0ff9 : [0x0100]},
# Motorola
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216]},

View File

@ -69,13 +69,15 @@ class PRS505(CLI, Device):
def write_cache(prefix):
try:
cachep = os.path.join(prefix, self.CACHE_XML)
cachep = os.path.join(prefix, *(self.CACHE_XML.split('/')))
if not os.path.exists(cachep):
dname = os.path.dirname(cachep)
if not os.path.exists(dname):
try:
os.makedirs(os.path.dirname(cachep), mode=0777)
os.makedirs(dname, mode=0777)
except:
time.sleep(5)
os.makedirs(os.path.dirname(cachep), mode=0777)
os.makedirs(dname, mode=0777)
with open(cachep, 'wb') as f:
f.write(u'''<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://www.kinoma.com/FskCache/1">

View File

@ -1190,6 +1190,7 @@ class Manifest(object):
if item in self.ids:
item = self.ids[item]
del self.ids[item.id]
if item.href in self.hrefs:
del self.hrefs[item.href]
self.items.remove(item)
if item in self.oeb.spine:

View File

@ -184,11 +184,14 @@ class MessageBox(QMessageBox):
def warning_dialog(parent, title, msg, det_msg='', show=False):
def warning_dialog(parent, title, msg, det_msg='', show=False,
show_copy_button=True):
d = MessageBox(QMessageBox.Warning, 'WARNING: '+title, msg, QMessageBox.Ok,
parent, det_msg)
d.setEscapeButton(QMessageBox.Ok)
d.setIconPixmap(QPixmap(I('dialog_warning.svg')))
if not show_copy_button:
d.cb.setVisible(False)
if show:
return d.exec_()
return d
@ -205,11 +208,14 @@ def error_dialog(parent, title, msg, det_msg='', show=False,
return d.exec_()
return d
def question_dialog(parent, title, msg, det_msg=''):
def question_dialog(parent, title, msg, det_msg='', show_copy_button=True):
d = MessageBox(QMessageBox.Question, title, msg, QMessageBox.Yes|QMessageBox.No,
parent, det_msg)
d.setIconPixmap(QPixmap(I('dialog_information.svg')))
d.setEscapeButton(QMessageBox.No)
if not show_copy_button:
d.cb.setVisible(False)
return d.exec_() == QMessageBox.Yes
def info_dialog(parent, title, msg, det_msg='', show=False):

View File

@ -656,7 +656,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
_('The selected column is not a custom column'), show=True)
if not question_dialog(self, _('Are you sure?'),
_('Do you really want to delete column %s and all its data?') %
self.custcols[col]['name']):
self.custcols[col]['name'], show_copy_button=False):
return
self.columns.item(idx).setCheckState(False)
self.columns.takeItem(idx)
@ -831,7 +831,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
warning_dialog(self, _('Must restart'),
_('The changes you made require that Calibre be '
'restarted. Please restart as soon as practical.'),
show=True)
show=True, show_copy_button=False)
self.parent.must_restart_before_config = True
QDialog.accept(self)

View File

@ -20,7 +20,7 @@
</sizepolicy>
</property>
<property name="windowTitle">
<string>Create a custom column</string>
<string>Create or edit custom columns</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@ -126,7 +126,7 @@
</font>
</property>
<property name="text">
<string>Create and edit custom columns</string>
<string>Create or edit custom columns</string>
</property>
</widget>
</item>

View File

@ -180,6 +180,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.formats_changed = True
def get_selected_format_metadata(self):
old = prefs['read_file_metadata']
if not old:
prefs['read_file_metadata'] = True
try:
row = self.formats.currentRow()
fmt = self.formats.item(row)
if fmt is None:
@ -201,6 +205,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
error_dialog(self, _('Could not read metadata'),
_('Could not read metadata from %s format')%ext).exec_()
return None, None
finally:
if old != prefs['read_file_metadata']:
prefs['read_file_metadata'] = old
def set_metadata_from_format(self):
mi, ext = self.get_selected_format_metadata()

View File

@ -150,7 +150,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<layout class="QHBoxLayout" name="hl234">
<property name="spacing">
<number>6</number>
</property>
@ -287,10 +287,12 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="Splitter" name="vertical_splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
@ -554,6 +556,18 @@
<widget class="StatusBar" name="status_bar" native="true"/>
</widget>
</item>
<item>
<widget class="SideBar" name="sidebar" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QToolBar" name="tool_bar">
@ -832,6 +846,12 @@
<header>calibre/gui2/widgets.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SideBar</class>
<extends>QWidget</extends>
<header>calibre/gui2/sidebar.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../resources/images.qrc"/>

235
src/calibre/gui2/sidebar.py Normal file
View File

@ -0,0 +1,235 @@
#!/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'
import re
from functools import partial
from PyQt4.Qt import QToolBar, Qt, QIcon, QSizePolicy, QWidget, \
QFrame, QVBoxLayout, QLabel, QSize, QCoreApplication, QToolButton
from calibre.gui2.progress_indicator import ProgressIndicator
from calibre.gui2 import dynamic
class JobsButton(QFrame):
def __init__(self, parent):
QFrame.__init__(self, parent)
self.setLayout(QVBoxLayout())
self.pi = ProgressIndicator(self)
self.layout().addWidget(self.pi)
self.jobs = QLabel('<b>'+_('Jobs:')+' 0')
self.jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom)
self.layout().addWidget(self.jobs)
self.layout().setAlignment(self.jobs, Qt.AlignHCenter)
self.jobs.setMargin(0)
self.layout().setMargin(0)
self.jobs.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
self.setCursor(Qt.PointingHandCursor)
self.setToolTip(_('Click to see list of active jobs.'))
def initialize(self, jobs_dialog):
self.jobs_dialog = jobs_dialog
self.jobs_dialog.jobs_view.restore_column_widths()
def mouseReleaseEvent(self, event):
if self.jobs_dialog.isVisible():
self.jobs_dialog.jobs_view.write_settings()
self.jobs_dialog.hide()
else:
self.jobs_dialog.jobs_view.read_settings()
self.jobs_dialog.show()
self.jobs_dialog.jobs_view.restore_column_widths()
@property
def is_running(self):
return self.pi.isAnimated()
def start(self):
self.pi.startAnimation()
def stop(self):
self.pi.stopAnimation()
class Jobs(ProgressIndicator):
def initialize(self, jobs_dialog):
self.jobs_dialog = jobs_dialog
def mouseClickEvent(self, event):
if self.jobs_dialog.isVisible():
self.jobs_dialog.jobs_view.write_settings()
self.jobs_dialog.hide()
else:
self.jobs_dialog.jobs_view.read_settings()
self.jobs_dialog.show()
self.jobs_dialog.jobs_view.restore_column_widths()
@property
def is_running(self):
return self.isAnimated()
def start(self):
self.startAnimation()
def stop(self):
self.stopAnimation()
class SideBar(QToolBar):
toggle_texts = {
'book_info' : (_('Show Book Details'), _('Hide Book Details')),
'tag_browser' : (_('Show Tag Browser'), _('Hide Tag Browser')),
'cover_browser': (_('Show Cover Browser'), _('Hide Cover Browser')),
}
toggle_icons = {
'book_info' : 'book.svg',
'tag_browser' : 'tags.svg',
'cover_browser': 'cover_flow.svg',
}
def __init__(self, parent=None):
QToolBar.__init__(self, _('Side bar'), parent)
self.setOrientation(Qt.Vertical)
self.setMovable(False)
self.setFloatable(False)
self.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.setIconSize(QSize(48, 48))
for ac in ('book_info', 'tag_browser', 'cover_browser'):
action = self.addAction(QIcon(I(self.toggle_icons[ac])),
self.toggle_texts[ac][1], getattr(self, '_toggle_'+ac))
setattr(self, 'action_toggle_'+ac, action)
w = self.widgetForAction(action)
w.setCheckable(True)
setattr(self, 'show_'+ac, partial(getattr(self, '_toggle_'+ac),
show=True))
setattr(self, 'hide_'+ac, partial(getattr(self, '_toggle_'+ac),
show=False))
self.spacer = QWidget(self)
self.spacer.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
self.addWidget(self.spacer)
self.jobs_button = JobsButton(self)
self.addWidget(self.jobs_button)
self.show_cover_browser = partial(self._toggle_cover_browser, show=True)
self.hide_cover_browser = partial(self._toggle_cover_browser,
show=False)
for ch in self.children():
if isinstance(ch, QToolButton):
ch.setCursor(Qt.PointingHandCursor)
def initialize(self, jobs_dialog, cover_browser, toggle_cover_browser,
cover_browser_error, vertical_splitter, horizontal_splitter):
self.jobs_button.initialize(jobs_dialog)
self.cover_browser, self.do_toggle_cover_browser = cover_browser, \
toggle_cover_browser
if self.cover_browser is None:
self.action_toggle_cover_browser.setEnabled(False)
self.action_toggle_cover_browser.setText(
_('Cover browser could not be loaded: ') + cover_browser_error)
else:
self.cover_browser.stop.connect(self.hide_cover_browser)
self._toggle_cover_browser(dynamic.get('cover_flow_visible', False))
self.horizontal_splitter = horizontal_splitter
self.vertical_splitter = vertical_splitter
tb_state = dynamic.get('tag_browser_state', None)
if tb_state is not None:
self.horizontal_splitter.restoreState(tb_state)
bi_state = dynamic.get('book_info_state', None)
if bi_state is not None:
self.vertical_splitter.restoreState(bi_state)
self.horizontal_splitter.initialize()
self.vertical_splitter.initialize()
self.view_status_changed('book_info', not
self.vertical_splitter.is_side_index_hidden)
self.view_status_changed('tag_browser', not
self.horizontal_splitter.is_side_index_hidden)
self.vertical_splitter.state_changed.connect(partial(self.view_status_changed,
'book_info'), type=Qt.QueuedConnection)
self.horizontal_splitter.state_changed.connect(partial(self.view_status_changed,
'tag_browser'), type=Qt.QueuedConnection)
def view_status_changed(self, name, visible):
action = getattr(self, 'action_toggle_'+name)
texts = self.toggle_texts[name]
action.setText(texts[int(visible)])
w = self.widgetForAction(action)
w.setCheckable(True)
w.setChecked(visible)
def location_changed(self, location):
is_lib = location == 'library'
for ac in ('cover_browser', 'tag_browser'):
ac = getattr(self, 'action_toggle_'+ac)
ac.setEnabled(is_lib)
self.widgetForAction(ac).setVisible(is_lib)
def save_state(self):
dynamic.set('cover_flow_visible', self.is_cover_browser_visible)
dynamic.set('tag_browser_state',
str(self.horizontal_splitter.saveState()))
dynamic.set('book_info_state',
str(self.vertical_splitter.saveState()))
@property
def is_cover_browser_visible(self):
return self.cover_browser is not None and self.cover_browser.isVisible()
def _toggle_cover_browser(self, show=None):
if show is None:
show = not self.is_cover_browser_visible
self.do_toggle_cover_browser(show)
self.view_status_changed('cover_browser', show)
def external_cover_flow_finished(self, *args):
self.view_status_changed('cover_browser', False)
def _toggle_tag_browser(self, show=None):
self.horizontal_splitter.toggle_side_index()
def _toggle_book_info(self, show=None):
self.vertical_splitter.toggle_side_index()
def jobs(self):
src = unicode(self.jobs_button.jobs.text())
return int(re.search(r'\d+', src).group())
def job_added(self, nnum):
jobs = self.jobs_button.jobs
src = unicode(jobs.text())
num = self.jobs()
text = src.replace(str(num), str(nnum))
jobs.setText(text)
self.jobs_button.start()
def job_done(self, nnum):
jobs = self.jobs_button.jobs
src = unicode(jobs.text())
num = self.jobs()
text = src.replace(str(num), str(nnum))
jobs.setText(text)
if nnum == 0:
self.no_more_jobs()
def no_more_jobs(self):
if self.jobs_button.is_running:
self.jobs_button.stop()
QCoreApplication.instance().alert(self, 5000)

View File

@ -1,15 +1,14 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, re, collections
import os, collections
from PyQt4.QtGui import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
QVBoxLayout, QSizePolicy, QToolButton, QIcon, QScrollArea, QFrame
from PyQt4.QtCore import Qt, QSize, SIGNAL, QCoreApplication, pyqtSignal
QSizePolicy, QScrollArea
from PyQt4.QtCore import Qt, QSize, pyqtSignal
from calibre import fit_image, preferred_encoding, isosx
from calibre.gui2 import config
from calibre.gui2.widgets import IMAGE_EXTENSIONS
from calibre.gui2.progress_indicator import ProgressIndicator
from calibre.gui2.notify import get_notifier
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.library.comments import comments_to_html
@ -17,6 +16,7 @@ from calibre.library.comments import comments_to_html
class BookInfoDisplay(QWidget):
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS
files_dropped = pyqtSignal(object, object)
@classmethod
def paths_from_event(cls, event):
@ -40,8 +40,7 @@ class BookInfoDisplay(QWidget):
def dropEvent(self, event):
paths = self.paths_from_event(event)
event.setDropAction(Qt.CopyAction)
self.emit(SIGNAL('files_dropped(PyQt_PyObject, PyQt_PyObject)'), event,
paths)
self.files_dropped.emit(event, paths)
def dragMoveEvent(self, event):
event.acceptProposedAction()
@ -87,6 +86,9 @@ class BookInfoDisplay(QWidget):
class BookDataDisplay(QLabel):
mr = pyqtSignal(int)
def __init__(self):
QLabel.__init__(self)
self.setText('')
@ -94,7 +96,7 @@ class BookInfoDisplay(QWidget):
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
def mouseReleaseEvent(self, ev):
self.emit(SIGNAL('mr(int)'), 1)
self.mr.emit(1)
WEIGHTS = collections.defaultdict(lambda : 100)
WEIGHTS[_('Path')] = 0
@ -103,6 +105,8 @@ class BookInfoDisplay(QWidget):
WEIGHTS[_('Series')] = 2
WEIGHTS[_('Tags')] = 3
show_book_info = pyqtSignal()
def __init__(self, clear_message):
QWidget.__init__(self)
self.setCursor(Qt.PointingHandCursor)
@ -113,14 +117,14 @@ class BookInfoDisplay(QWidget):
self.cover_display = BookInfoDisplay.BookCoverDisplay()
self._layout.addWidget(self.cover_display)
self.book_data = BookInfoDisplay.BookDataDisplay()
self.connect(self.book_data, SIGNAL('mr(int)'), self.mouseReleaseEvent)
self.book_data.mr.connect(self.mouseReleaseEvent)
self._layout.addWidget(self.book_data)
self.data = {}
self.setVisible(False)
self._layout.setAlignment(self.cover_display, Qt.AlignTop|Qt.AlignLeft)
def mouseReleaseEvent(self, ev):
self.emit(SIGNAL('show_book_info()'))
self.show_book_info.emit()
def show_data(self, data):
if data.has_key('cover'):
@ -128,7 +132,7 @@ class BookInfoDisplay(QWidget):
else:
self.cover_display.setPixmap(self.cover_display.default_pixmap)
rows = u''
rows, comments = [], ''
self.book_data.setText('')
self.data = data.copy()
keys = data.keys()
@ -142,97 +146,43 @@ class BookInfoDisplay(QWidget):
if isinstance(txt, str):
txt = txt.decode(preferred_encoding, 'replace')
if key == _('Comments'):
txt = comments_to_html(txt)
rows += u'<tr><td><b>%s:</b></td><td>%s</td></tr>'%(key, txt)
self.book_data.setText(u'<table>'+rows+u'</table>')
comments = comments_to_html(txt)
else:
rows.append((key, txt))
rows = '\n'.join([u'<tr><td valign="top"><b>%s:</b></td><td valign="top">%s</td></tr>'%(k,t) for
k, t in rows])
if comments:
comments = '<b>Comments:</b>'+comments
left_pane = u'<table>%s</table>'%rows
right_pane = u'<div>%s</div>'%comments
self.book_data.setText(u'<table><tr><td valign="top" '
'style="padding-right:2em">%s</td><td valign="top">%s</td></tr></table>'
% (left_pane, right_pane))
self.clear_message()
self.book_data.updateGeometry()
self.updateGeometry()
self.setVisible(True)
class MovieButton(QFrame):
def __init__(self, jobs_dialog):
QFrame.__init__(self)
self.setLayout(QVBoxLayout())
self.pi = ProgressIndicator(self)
self.layout().addWidget(self.pi)
self.jobs = QLabel('<b>'+_('Jobs:')+' 0')
self.jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom)
self.layout().addWidget(self.jobs)
self.layout().setAlignment(self.jobs, Qt.AlignHCenter)
self.jobs.setMargin(0)
self.layout().setMargin(0)
self.jobs.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
self.jobs_dialog = jobs_dialog
self.setCursor(Qt.PointingHandCursor)
self.setToolTip(_('Click to see list of active jobs.'))
self.jobs_dialog.jobs_view.restore_column_widths()
def mouseReleaseEvent(self, event):
if self.jobs_dialog.isVisible():
self.jobs_dialog.jobs_view.write_settings()
self.jobs_dialog.hide()
else:
self.jobs_dialog.jobs_view.read_settings()
self.jobs_dialog.show()
self.jobs_dialog.jobs_view.restore_column_widths()
@property
def is_running(self):
return self.pi.isAnimated()
def start(self):
self.pi.startAnimation()
def stop(self):
self.pi.stopAnimation()
class CoverFlowButton(QToolButton):
def __init__(self, parent=None):
QToolButton.__init__(self, parent)
self.setIconSize(QSize(80, 80))
self.setIcon(QIcon(I('cover_flow.svg')))
self.setCheckable(True)
self.setChecked(False)
self.setAutoRaise(True)
self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding))
self.connect(self, SIGNAL('toggled(bool)'), self.adjust_tooltip)
self.adjust_tooltip(False)
self.setCursor(Qt.PointingHandCursor)
def adjust_tooltip(self, on):
tt = _('Click to turn off Cover Browsing') if on else _('Click to browse books by their covers')
self.setToolTip(tt)
def disable(self, reason):
self.setDisabled(True)
self.setToolTip(_('<p>Browsing books by their covers is disabled.<br>Import of pictureflow module failed:<br>')+reason)
class StatusBar(QStatusBar):
resized = pyqtSignal(object)
files_dropped = pyqtSignal(object, object)
show_book_info = pyqtSignal()
def initialize(self, jobs_dialog, systray=None):
def initialize(self, systray=None):
self.systray = systray
self.notifier = get_notifier(systray)
self.movie_button = MovieButton(jobs_dialog)
self.cover_flow_button = CoverFlowButton()
self.addPermanentWidget(self.cover_flow_button)
self.addPermanentWidget(self.movie_button)
self.book_info = BookInfoDisplay(self.clearMessage)
self.book_info.setAcceptDrops(True)
self.scroll_area = QScrollArea()
self.scroll_area.setWidget(self.book_info)
self.scroll_area.setWidgetResizable(True)
self.connect(self.book_info, SIGNAL('show_book_info()'), self.show_book_info)
self.connect(self.book_info,
SIGNAL('files_dropped(PyQt_PyObject,PyQt_PyObject)'),
self.files_dropped, Qt.QueuedConnection)
self.book_info.show_book_info.connect(self.show_book_info.emit,
type=Qt.QueuedConnection)
self.book_info.files_dropped.connect(self.files_dropped.emit,
type=Qt.QueuedConnection)
self.addWidget(self.scroll_area, 100)
self.setMinimumHeight(120)
self.resized.connect(self.book_info.cover_display.relayout)
@ -241,10 +191,6 @@ class StatusBar(QStatusBar):
def resizeEvent(self, ev):
self.resized.emit(self.size())
def files_dropped(self, event, paths):
self.emit(SIGNAL('files_dropped(PyQt_PyObject, PyQt_PyObject)'), event,
paths)
def reset_info(self):
self.book_info.show_data({})
@ -259,33 +205,4 @@ class StatusBar(QStatusBar):
self.notifier(msg)
return ret
def jobs(self):
src = unicode(self.movie_button.jobs.text())
return int(re.search(r'\d+', src).group())
def show_book_info(self):
self.emit(SIGNAL('show_book_info()'))
def job_added(self, nnum):
jobs = self.movie_button.jobs
src = unicode(jobs.text())
num = self.jobs()
text = src.replace(str(num), str(nnum))
jobs.setText(text)
self.movie_button.start()
def job_done(self, nnum):
jobs = self.movie_button.jobs
src = unicode(jobs.text())
num = self.jobs()
text = src.replace(str(num), str(nnum))
jobs.setText(text)
if nnum == 0:
self.no_more_jobs()
def no_more_jobs(self):
if self.movie_button.is_running:
self.movie_button.stop()
QCoreApplication.instance().alert(self, 5000)

View File

@ -169,7 +169,7 @@ class TagTreeItem(object):
def category_data(self, role):
if role == Qt.DisplayRole:
return self.name
return QVariant(self.py_name + ' [%d]'%len(self.children))
if role == Qt.DecorationRole:
return self.icon
if role == Qt.FontRole:

View File

@ -262,16 +262,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
SIGNAL('update_found(PyQt_PyObject)'), self.update_found)
self.update_checker.start(2000)
####################### Status Bar #####################
self.status_bar.initialize(self.jobs_dialog, self.system_tray_icon)
#self.setStatusBar(self.status_bar)
QObject.connect(self.job_manager, SIGNAL('job_added(int)'),
self.status_bar.job_added, Qt.QueuedConnection)
QObject.connect(self.job_manager, SIGNAL('job_done(int)'),
self.status_bar.job_done, Qt.QueuedConnection)
QObject.connect(self.status_bar, SIGNAL('show_book_info()'),
self.show_book_info)
QObject.connect(self.status_bar, SIGNAL('files_dropped(PyQt_PyObject,PyQt_PyObject)'),
self.files_dropped_on_book)
self.status_bar.initialize(self.system_tray_icon)
self.status_bar.show_book_info.connect(self.show_book_info)
self.status_bar.files_dropped.connect(self.files_dropped_on_book)
####################### Setup Toolbar #####################
md = QMenu()
md.addAction(_('Edit metadata individually'))
@ -459,6 +453,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'),
self.do_advanced_search)
for ch in self.tool_bar.children():
if isinstance(ch, QToolButton):
ch.setCursor(Qt.PointingHandCursor)
####################### Library view ########################
similar_menu = QMenu(_('Similar books...'))
similar_menu.addAction(self.action_books_by_same_author)
@ -554,12 +552,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.cover_cache = CoverCache(self.library_path)
self.cover_cache.start()
self.library_view.model().cover_cache = self.cover_cache
self.tags_view.setVisible(False)
self.tag_match.setVisible(False)
self.popularity.setVisible(False)
self.restriction_label.setVisible(False)
self.edit_categories.setVisible(False)
self.search_restriction.setVisible(False)
self.connect(self.edit_categories, SIGNAL('clicked()'), self.do_edit_categories)
self.tags_view.set_database(db, self.tag_match, self.popularity, self.search_restriction)
self.connect(self.tags_view,
@ -626,22 +618,28 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
if not config['separate_cover_flow']:
self.library.layout().addWidget(self.cover_flow)
self.cover_flow.currentChanged.connect(self.sync_listview_to_cf)
self.connect(self.status_bar.cover_flow_button,
SIGNAL('toggled(bool)'), self.toggle_cover_flow)
self.connect(self.cover_flow, SIGNAL('stop()'),
self.status_bar.cover_flow_button.toggle)
self.library_view.selectionModel().currentRowChanged.connect(
self.sync_cf_to_listview)
self.db_images = DatabaseImages(self.library_view.model())
self.cover_flow.setImages(self.db_images)
else:
self.status_bar.cover_flow_button.disable(pictureflowerror)
self._calculated_available_height = min(max_available_height()-15,
self.height())
self.resize(self.width(), self._calculated_available_height)
self.search.setMaximumWidth(self.width()-150)
####################### Side Bar ###############################
self.sidebar.initialize(self.jobs_dialog, self.cover_flow,
self.toggle_cover_flow, pictureflowerror,
self.vertical_splitter, self.horizontal_splitter)
QObject.connect(self.job_manager, SIGNAL('job_added(int)'),
self.sidebar.job_added, Qt.QueuedConnection)
QObject.connect(self.job_manager, SIGNAL('job_done(int)'),
self.sidebar.job_done, Qt.QueuedConnection)
if config['autolaunch_server']:
from calibre.library.server import start_threaded_server
from calibre.library import server_config
@ -668,19 +666,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.location_view.setCurrentIndex(self.location_view.model().index(0))
if self.cover_flow is not None and dynamic.get('cover_flow_visible', False):
self.status_bar.cover_flow_button.toggle()
tb_state = dynamic.get('tag_browser_state', None)
if tb_state is not None:
self.horizontal_splitter.restoreState(tb_state)
self.toggle_tags_view(True)
bi_state = dynamic.get('book_info_state', None)
if bi_state is not None:
self.vertical_splitter.restoreState(bi_state)
self.horizontal_splitter.initialize()
self.vertical_splitter.initialize()
self._add_filesystem_book = Dispatcher(self.__add_filesystem_book)
v = self.library_view
@ -782,11 +767,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
if search:
self.search.set_search_string(join.join(search))
def uncheck_cover_button(self, *args):
self.status_bar.cover_flow_button.setChecked(False)
def toggle_cover_flow(self, show):
if config['separate_cover_flow']:
if show:
@ -802,8 +782,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.cover_flow.setFocus(Qt.OtherFocusReason)
self.library_view.scrollTo(self.library_view.currentIndex())
d.show()
self.connect(d, SIGNAL('finished(int)'),
self.uncheck_cover_button)
d.finished.connect(self.sidebar.external_cover_flow_finished)
self.cf_dialog = d
self.cover_flow_sync_timer.start(500)
else:
@ -825,8 +804,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.library_view.currentIndex())
self.cover_flow.setVisible(True)
self.cover_flow.setFocus(Qt.OtherFocusReason)
#self.status_bar.book_info.book_data.setMaximumHeight(100)
#self.status_bar.setMaximumHeight(120)
self.library_view.scrollTo(self.library_view.currentIndex())
self.cover_flow_sync_timer.start(500)
else:
@ -837,26 +814,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
sm = self.library_view.selectionModel()
sm.select(idx, sm.ClearAndSelect|sm.Rows)
self.library_view.setCurrentIndex(idx)
#self.status_bar.book_info.book_data.setMaximumHeight(1000)
#self.resize(self.width(), self._calculated_available_height)
#self.setMaximumHeight(available_height())
def toggle_tags_view(self, show):
if show:
self.tags_view.setVisible(True)
self.tag_match.setVisible(True)
self.popularity.setVisible(True)
self.restriction_label.setVisible(True)
self.edit_categories.setVisible(True)
self.search_restriction.setVisible(True)
self.tags_view.setFocus(Qt.OtherFocusReason)
else:
self.tags_view.setVisible(False)
self.tag_match.setVisible(False)
self.popularity.setVisible(False)
self.restriction_label.setVisible(False)
self.edit_categories.setVisible(False)
self.search_restriction.setVisible(False)
'''
Handling of the count of books in a restricted view requires that
@ -887,7 +846,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.restriction_count_of_books_in_view = self.current_view().row_count()
t = _("({0} of {1})").format(self.current_view().row_count(),
self.restriction_count_of_books_in_view)
self.search_count.setStyleSheet('QLabel { background-color: yellow; }')
self.search_count.setStyleSheet('QLabel { border-radius: 8px; background-color: yellow; }')
else: # No restriction
if all == 'yes':
t = _("(all books)")
@ -2330,6 +2289,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
view.resizeColumnsToContents()
view.resize_on_select = False
self.status_bar.reset_info()
self.sidebar.location_changed(location)
if location == 'library':
self.action_edit.setEnabled(True)
self.action_merge.setEnabled(True)
@ -2337,7 +2297,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.view_menu.actions()[1].setEnabled(True)
self.action_open_containing_folder.setEnabled(True)
self.action_sync.setEnabled(True)
self.status_bar.cover_flow_button.setEnabled(True)
for action in list(self.delete_menu.actions())[1:]:
action.setEnabled(True)
else:
@ -2347,7 +2306,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.view_menu.actions()[1].setEnabled(False)
self.action_open_containing_folder.setEnabled(False)
self.action_sync.setEnabled(False)
self.status_bar.cover_flow_button.setEnabled(False)
for action in list(self.delete_menu.actions())[1:]:
action.setEnabled(False)
@ -2463,11 +2421,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
def write_settings(self):
config.set('main_window_geometry', self.saveGeometry())
dynamic.set('sort_history', self.library_view.model().sort_history)
dynamic.set('cover_flow_visible', self.cover_flow.isVisible())
dynamic.set('tag_browser_state',
str(self.horizontal_splitter.saveState()))
dynamic.set('book_info_state',
str(self.vertical_splitter.saveState()))
self.sidebar.save_state()
self.library_view.write_settings()
if self.device_connected:
self.save_device_view_settings()

View File

@ -982,6 +982,12 @@ class SplitterHandle(QSplitterHandle):
class Splitter(QSplitter):
state_changed = pyqtSignal(object)
def __init__(self, *args):
QSplitter.__init__(self, *args)
self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection)
def createHandle(self):
return SplitterHandle(self.orientation(), self)
@ -990,6 +996,22 @@ class Splitter(QSplitter):
h = self.handle(i)
if h is not None:
h.splitter_moved()
self.state_changed.emit(not self.is_side_index_hidden)
def splitter_moved(self, *args):
self.state_changed.emit(not self.is_side_index_hidden)
@property
def side_index(self):
return 0 if self.orientation() == Qt.Horizontal else 1
@property
def is_side_index_hidden(self):
sizes = list(self.sizes())
return sizes[self.side_index] == 0
def toggle_side_index(self):
self.double_clicked(None)
def double_clicked(self, handle):
sizes = list(self.sizes())
@ -997,8 +1019,7 @@ class Splitter(QSplitter):
idx = sizes.index(0)
sizes[idx] = 80
else:
idx = 0 if self.orientation() == Qt.Horizontal else 1
sizes[idx] = 0
sizes[self.side_index] = 0
self.setSizes(sizes)
self.initialize()

View File

@ -145,6 +145,8 @@ class CustomColumns(object):
if v['normalized']:
tn = 'custom_column_{0}'.format(i)
self.tag_browser_categories[tn] = [v['label'], 'value']
if v['datatype'] == 'rating':
self.tag_browser_formatters[tn] = lambda x:u'\u2605'*int(round(x/2.))
def get_custom(self, idx, label=None, num=None, index_is_id=False):

View File

@ -127,6 +127,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
'authors' : ['author', 'name'],
'news' : ['news', 'name'],
}
self.tag_browser_formatters = {}
self.connect()
self.is_case_sensitive = not iswindows and not isosx and \
@ -632,11 +633,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
else:
icon = icon_map['*custom']
tooltip = self.custom_column_label_map[category]['name']
if ids is None: # no filtering
categories[category] = [Tag(r[1], count=r[2], id=r[0], icon=icon, tooltip = tooltip)
for r in data if r[2] > 0]
else: # filter out zero-count tags
categories[category] = [Tag(r[1], count=r[2], id=r[0], icon=icon, tooltip = tooltip)
formatter = self.tag_browser_formatters.get(tn, lambda x: x)
categories[category] = [Tag(formatter(r[1]), count=r[2], id=r[0], icon=icon, tooltip = tooltip)
for r in data if r[2] > 0]
categories['format'] = []
for fmt in self.conn.get('SELECT DISTINCT format FROM data'):