mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
More GUI Refactoring. Cover browser can now be dynamically resized like the other GUI areas
This commit is contained in:
parent
624dfff639
commit
e355672c70
@ -99,6 +99,8 @@ def _config():
|
|||||||
help=_('Limit max simultaneous jobs to number of CPUs'))
|
help=_('Limit max simultaneous jobs to number of CPUs'))
|
||||||
c.add_opt('tag_browser_hidden_categories', default=set(),
|
c.add_opt('tag_browser_hidden_categories', default=set(),
|
||||||
help=_('tag browser categories not to display'))
|
help=_('tag browser categories not to display'))
|
||||||
|
c.add_opt('gui_layout', choices=['wide', 'narrow'],
|
||||||
|
help=_('The layout of the user interface'), default='narrow')
|
||||||
return ConfigProxy(c)
|
return ConfigProxy(c)
|
||||||
|
|
||||||
config = _config()
|
config = _config()
|
||||||
@ -125,6 +127,11 @@ def available_width():
|
|||||||
desktop = QCoreApplication.instance().desktop()
|
desktop = QCoreApplication.instance().desktop()
|
||||||
return desktop.availableGeometry().width()
|
return desktop.availableGeometry().width()
|
||||||
|
|
||||||
|
try:
|
||||||
|
is_widescreen = float(available_width())/available_height() > 1.4
|
||||||
|
except:
|
||||||
|
is_widescreen = True
|
||||||
|
|
||||||
def extension(path):
|
def extension(path):
|
||||||
return os.path.splitext(path)[1][1:].lower()
|
return os.path.splitext(path)[1][1:].lower()
|
||||||
|
|
||||||
|
@ -10,10 +10,11 @@ Module to implement the Cover Flow feature
|
|||||||
import sys, os, time
|
import sys, os, time
|
||||||
|
|
||||||
from PyQt4.Qt import QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, \
|
from PyQt4.Qt import QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, \
|
||||||
QStackedLayout
|
QStackedLayout, QLabel
|
||||||
|
|
||||||
from calibre import plugins
|
from calibre import plugins
|
||||||
from calibre.gui2 import config, available_height, available_width
|
from calibre.gui2 import config, available_height, available_width
|
||||||
|
|
||||||
pictureflow, pictureflowerror = plugins['pictureflow']
|
pictureflow, pictureflowerror = plugins['pictureflow']
|
||||||
|
|
||||||
if pictureflow is not None:
|
if pictureflow is not None:
|
||||||
@ -78,12 +79,15 @@ if pictureflow is not None:
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
pictureflow.PictureFlow.__init__(self, parent,
|
pictureflow.PictureFlow.__init__(self, parent,
|
||||||
config['cover_flow_queue_length']+1)
|
config['cover_flow_queue_length']+1)
|
||||||
self.setMinimumSize(QSize(10, 10))
|
self.setMinimumSize(QSize(300, 150))
|
||||||
self.setFocusPolicy(Qt.WheelFocus)
|
self.setFocusPolicy(Qt.WheelFocus)
|
||||||
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
|
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
|
||||||
QSizePolicy.Expanding))
|
QSizePolicy.Expanding))
|
||||||
self.setZoomFactor(150)
|
self.setZoomFactor(150)
|
||||||
|
|
||||||
|
def sizeHint(self):
|
||||||
|
return self.minimumSize()
|
||||||
|
|
||||||
def wheelEvent(self, ev):
|
def wheelEvent(self, ev):
|
||||||
ev.accept()
|
ev.accept()
|
||||||
if ev.delta() < 0:
|
if ev.delta() < 0:
|
||||||
@ -107,56 +111,58 @@ class CoverFlowMixin(object):
|
|||||||
self.cover_flow_sync_timer.timeout.connect(self.cover_flow_do_sync)
|
self.cover_flow_sync_timer.timeout.connect(self.cover_flow_do_sync)
|
||||||
self.cover_flow_sync_flag = True
|
self.cover_flow_sync_flag = True
|
||||||
self.cover_flow = CoverFlow(parent=self)
|
self.cover_flow = CoverFlow(parent=self)
|
||||||
self.cover_flow.setVisible(False)
|
|
||||||
if not config['separate_cover_flow']:
|
|
||||||
self.cb_layout.addWidget(self.cover_flow)
|
|
||||||
self.cover_flow.currentChanged.connect(self.sync_listview_to_cf)
|
self.cover_flow.currentChanged.connect(self.sync_listview_to_cf)
|
||||||
self.library_view.selectionModel().currentRowChanged.connect(
|
self.library_view.selectionModel().currentRowChanged.connect(
|
||||||
self.sync_cf_to_listview)
|
self.sync_cf_to_listview)
|
||||||
self.db_images = DatabaseImages(self.library_view.model())
|
self.db_images = DatabaseImages(self.library_view.model())
|
||||||
self.cover_flow.setImages(self.db_images)
|
self.cover_flow.setImages(self.db_images)
|
||||||
ah, aw = available_height(), available_width()
|
else:
|
||||||
self._cb_layout_is_horizontal = float(aw)/ah >= 1.4
|
self.cover_flow = QLabel('<p>'+_('Cover browser could not be loaded')
|
||||||
self.cb_layout.setDirection(self.cb_layout.LeftToRight if
|
+'<br>'+pictureflowerror)
|
||||||
self._cb_layout_is_horizontal else
|
self.cover_flow.setWordWrap(True)
|
||||||
self.cb_layout.TopToBottom)
|
|
||||||
|
|
||||||
def toggle_cover_flow_visibility(self, show):
|
|
||||||
if config['separate_cover_flow']:
|
if config['separate_cover_flow']:
|
||||||
if show:
|
self.cb_splitter.button.clicked.connect(self.toggle_cover_browser)
|
||||||
d = QDialog(self)
|
if CoverFlow is not None:
|
||||||
ah, aw = available_height(), available_width()
|
self.cover_flow.stop.connect(self.hide_cover_browser)
|
||||||
d.resize(int(aw/1.5), ah-60)
|
|
||||||
d._layout = QStackedLayout()
|
|
||||||
d.setLayout(d._layout)
|
|
||||||
d.setWindowTitle(_('Browse by covers'))
|
|
||||||
d.layout().addWidget(self.cover_flow)
|
|
||||||
self.cover_flow.setVisible(True)
|
|
||||||
self.cover_flow.setFocus(Qt.OtherFocusReason)
|
|
||||||
d.show()
|
|
||||||
d.finished.connect(self.sidebar.external_cover_flow_finished)
|
|
||||||
self.cf_dialog = d
|
|
||||||
else:
|
|
||||||
cfd = getattr(self, 'cf_dialog', None)
|
|
||||||
if cfd is not None:
|
|
||||||
self.cover_flow.setVisible(False)
|
|
||||||
cfd.hide()
|
|
||||||
self.cf_dialog = None
|
|
||||||
else:
|
else:
|
||||||
if show:
|
self.cb_splitter.insertWidget(self.cb_splitter.side_index, self.cover_flow)
|
||||||
self.cover_flow.setVisible(True)
|
if CoverFlow is not None:
|
||||||
self.cover_flow.setFocus(Qt.OtherFocusReason)
|
self.cover_flow.stop.connect(self.cb_splitter.hide_side_pane)
|
||||||
else:
|
|
||||||
self.cover_flow.setVisible(False)
|
|
||||||
|
|
||||||
def toggle_cover_flow(self, show):
|
def toggle_cover_browser(self):
|
||||||
if show:
|
cbd = getattr(self, 'cb_dialog', None)
|
||||||
self.cover_flow.setCurrentSlide(self.library_view.currentIndex().row())
|
if cbd is not None:
|
||||||
self.library_view.setCurrentIndex(
|
self.hide_cover_browser()
|
||||||
self.library_view.currentIndex())
|
|
||||||
self.cover_flow_sync_timer.start(500)
|
|
||||||
self.library_view.scroll_to_row(self.library_view.currentIndex().row())
|
|
||||||
else:
|
else:
|
||||||
|
self.show_cover_browser()
|
||||||
|
|
||||||
|
def show_cover_browser(self):
|
||||||
|
if CoverFlow is not None:
|
||||||
|
self.cover_flow.setCurrentSlide(self.library_view.currentIndex().row())
|
||||||
|
self.cover_flow_sync_timer.start(500)
|
||||||
|
self.library_view.setCurrentIndex(
|
||||||
|
self.library_view.currentIndex())
|
||||||
|
self.library_view.scroll_to_row(self.library_view.currentIndex().row())
|
||||||
|
|
||||||
|
if config['separate_cover_flow']:
|
||||||
|
d = QDialog(self)
|
||||||
|
ah, aw = available_height(), available_width()
|
||||||
|
d.resize(int(aw/1.5), ah-60)
|
||||||
|
d._layout = QStackedLayout()
|
||||||
|
d.setLayout(d._layout)
|
||||||
|
d.setWindowTitle(_('Browse by covers'))
|
||||||
|
d.layout().addWidget(self.cover_flow)
|
||||||
|
self.cover_flow.setVisible(True)
|
||||||
|
self.cover_flow.setFocus(Qt.OtherFocusReason)
|
||||||
|
d.show()
|
||||||
|
self.cb_splitter.button.set_state_to_hide()
|
||||||
|
d.finished.connect(self.cb_splitter.button.set_state_to_show)
|
||||||
|
self.cb_dialog = d
|
||||||
|
else:
|
||||||
|
self.cover_flow.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
|
def hide_cover_browser(self):
|
||||||
|
if CoverFlow is not None:
|
||||||
self.cover_flow_sync_timer.stop()
|
self.cover_flow_sync_timer.stop()
|
||||||
idx = self.library_view.model().index(self.cover_flow.currentSlide(), 0)
|
idx = self.library_view.model().index(self.cover_flow.currentSlide(), 0)
|
||||||
if idx.isValid():
|
if idx.isValid():
|
||||||
@ -164,7 +170,12 @@ class CoverFlowMixin(object):
|
|||||||
sm.select(idx, sm.ClearAndSelect|sm.Rows)
|
sm.select(idx, sm.ClearAndSelect|sm.Rows)
|
||||||
self.library_view.setCurrentIndex(idx)
|
self.library_view.setCurrentIndex(idx)
|
||||||
self.library_view.scroll_to_row(idx.row())
|
self.library_view.scroll_to_row(idx.row())
|
||||||
self.toggle_cover_flow_visibility(show)
|
|
||||||
|
if config['separate_cover_flow']:
|
||||||
|
cbd = getattr(self, 'cb_dialog', None)
|
||||||
|
if cbd is not None:
|
||||||
|
cbd.accept()
|
||||||
|
self.cb_dialog = None
|
||||||
|
|
||||||
def sync_cf_to_listview(self, current, previous):
|
def sync_cf_to_listview(self, current, previous):
|
||||||
if self.cover_flow_sync_flag and self.cover_flow.isVisible() and \
|
if self.cover_flow_sync_flag and self.cover_flow.isVisible() and \
|
||||||
|
@ -7,12 +7,16 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from PyQt4.Qt import QMenu, Qt, pyqtSignal, QToolButton, QIcon
|
from PyQt4.Qt import QMenu, Qt, pyqtSignal, QToolButton, QIcon, QStackedWidget, \
|
||||||
|
QWidget, QHBoxLayout, QToolBar, QSize, QSizePolicy
|
||||||
|
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
from calibre.constants import isosx
|
from calibre.constants import isosx, __appname__
|
||||||
from calibre.gui2 import config
|
from calibre.gui2 import config, is_widescreen
|
||||||
|
from calibre.gui2.library.views import BooksView, DeviceBooksView
|
||||||
|
from calibre.gui2.widgets import Splitter
|
||||||
|
from calibre.gui2.tag_view import TagBrowserWidget
|
||||||
|
|
||||||
_keep_refs = []
|
_keep_refs = []
|
||||||
|
|
||||||
@ -279,3 +283,116 @@ class LibraryViewMixin(object): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class LibraryWidget(Splitter): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
orientation = Qt.Vertical if config['gui_layout'] == 'narrow' and \
|
||||||
|
not is_widescreen else Qt.Horizontal
|
||||||
|
#orientation = Qt.Vertical
|
||||||
|
idx = 0 if orientation == Qt.Vertical else 1
|
||||||
|
Splitter.__init__(self, 'cover_browser_splitter', _('Cover Browser'),
|
||||||
|
I('cover_flow.svg'),
|
||||||
|
orientation=orientation, parent=parent,
|
||||||
|
connect_button=not config['separate_cover_flow'],
|
||||||
|
side_index=idx, initial_side_size=400, initial_show=False)
|
||||||
|
parent.library_view = BooksView(parent)
|
||||||
|
parent.library_view.setObjectName('library_view')
|
||||||
|
self.addWidget(parent.library_view)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class Stack(QStackedWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QStackedWidget.__init__(self, parent)
|
||||||
|
|
||||||
|
parent.cb_splitter = LibraryWidget(parent)
|
||||||
|
self.tb_widget = TagBrowserWidget(parent)
|
||||||
|
parent.tb_splitter = Splitter('tag_browser_splitter',
|
||||||
|
_('Tag Browser'), I('tags.svg'),
|
||||||
|
parent=parent, side_index=0, initial_side_size=200)
|
||||||
|
parent.tb_splitter.addWidget(self.tb_widget)
|
||||||
|
parent.tb_splitter.addWidget(parent.cb_splitter)
|
||||||
|
parent.tb_splitter.setCollapsible(parent.tb_splitter.other_index, False)
|
||||||
|
|
||||||
|
self.addWidget(parent.tb_splitter)
|
||||||
|
for x in ('memory', 'card_a', 'card_b'):
|
||||||
|
name = x+'_view'
|
||||||
|
w = DeviceBooksView(parent)
|
||||||
|
setattr(parent, name, w)
|
||||||
|
self.addWidget(w)
|
||||||
|
w.setObjectName(name)
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class SideBar(QToolBar): # {{{
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, splitters, jobs_button, 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))
|
||||||
|
self.spacer = QWidget(self)
|
||||||
|
self.spacer.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||||
|
for s in splitters:
|
||||||
|
self.addWidget(s.button)
|
||||||
|
self.addWidget(self.spacer)
|
||||||
|
self.addWidget(jobs_button)
|
||||||
|
|
||||||
|
for ch in self.children():
|
||||||
|
if isinstance(ch, QToolButton):
|
||||||
|
ch.setCursor(Qt.PointingHandCursor)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class LayoutMixin(object): # {{{
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setWindowTitle(__appname__)
|
||||||
|
|
||||||
|
if config['gui_layout'] == 'narrow':
|
||||||
|
from calibre.gui2.status import StatusBar
|
||||||
|
self.status_bar = StatusBar(self)
|
||||||
|
self.stack = Stack(self)
|
||||||
|
self.bd_splitter = Splitter('book_details_splitter',
|
||||||
|
_('Book Details'), I('book.svg'),
|
||||||
|
orientation=Qt.Vertical, parent=self, side_index=1)
|
||||||
|
self._layout_mem = [QWidget(self), QHBoxLayout()]
|
||||||
|
self._layout_mem[0].setLayout(self._layout_mem[1])
|
||||||
|
l = self._layout_mem[1]
|
||||||
|
l.addWidget(self.stack)
|
||||||
|
self.sidebar = SideBar([getattr(self, x+'_splitter')
|
||||||
|
for x in ('bd', 'tb', 'cb')], self.jobs_button, parent=self)
|
||||||
|
l.addWidget(self.sidebar)
|
||||||
|
self.bd_splitter.addWidget(self._layout_mem[0])
|
||||||
|
self.bd_splitter.addWidget(self.status_bar)
|
||||||
|
self.bd_splitter.setCollapsible((self.bd_splitter.side_index+1)%2, False)
|
||||||
|
self.centralwidget.layout().addWidget(self.bd_splitter)
|
||||||
|
|
||||||
|
def finalize_layout(self):
|
||||||
|
m = self.library_view.model()
|
||||||
|
if m.rowCount(None) > 0:
|
||||||
|
self.library_view.set_current_row(0)
|
||||||
|
m.current_changed(self.library_view.currentIndex(),
|
||||||
|
self.library_view.currentIndex())
|
||||||
|
|
||||||
|
|
||||||
|
def save_layout_state(self):
|
||||||
|
for x in ('library', 'memory', 'card_a', 'card_b'):
|
||||||
|
getattr(self, x+'_view').save_state()
|
||||||
|
|
||||||
|
for x in ('cb', 'tb', 'bd'):
|
||||||
|
getattr(self, x+'_splitter').save_state()
|
||||||
|
|
||||||
|
def read_layout_settings(self):
|
||||||
|
# View states are restored automatically when set_database is called
|
||||||
|
|
||||||
|
for x in ('cb', 'tb', 'bd'):
|
||||||
|
getattr(self, x+'_splitter').restore_state()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
@ -26,6 +26,15 @@ class BooksView(QTableView): # {{{
|
|||||||
|
|
||||||
def __init__(self, parent, modelcls=BooksModel):
|
def __init__(self, parent, modelcls=BooksModel):
|
||||||
QTableView.__init__(self, parent)
|
QTableView.__init__(self, parent)
|
||||||
|
|
||||||
|
self.setDragEnabled(True)
|
||||||
|
self.setDragDropOverwriteMode(False)
|
||||||
|
self.setDragDropMode(self.DragDrop)
|
||||||
|
self.setAlternatingRowColors(True)
|
||||||
|
self.setSelectionBehavior(self.SelectRows)
|
||||||
|
self.setShowGrid(False)
|
||||||
|
self.setWordWrap(False)
|
||||||
|
|
||||||
self.rating_delegate = RatingDelegate(self)
|
self.rating_delegate = RatingDelegate(self)
|
||||||
self.timestamp_delegate = DateDelegate(self)
|
self.timestamp_delegate = DateDelegate(self)
|
||||||
self.pubdate_delegate = PubDateDelegate(self)
|
self.pubdate_delegate = PubDateDelegate(self)
|
||||||
@ -434,6 +443,18 @@ class BooksView(QTableView): # {{{
|
|||||||
self.scrollTo(self.model().index(row, i))
|
self.scrollTo(self.model().index(row, i))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def set_current_row(self, row, select=True):
|
||||||
|
if row > -1:
|
||||||
|
h = self.horizontalHeader()
|
||||||
|
for i in range(h.count()):
|
||||||
|
if not h.isSectionHidden(i):
|
||||||
|
index = self.model().index(row, i)
|
||||||
|
self.setCurrentIndex(index)
|
||||||
|
if select:
|
||||||
|
sm = self.selectionModel()
|
||||||
|
sm.select(index, sm.ClearAndSelect|sm.Rows)
|
||||||
|
break
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self._model.close()
|
self._model.close()
|
||||||
|
|
||||||
|
@ -304,270 +304,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="Splitter" name="vertical_splitter">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>100</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="layoutWidget">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QStackedWidget" name="stack">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>100</horstretch>
|
|
||||||
<verstretch>100</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="library">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
|
||||||
<item>
|
|
||||||
<widget class="Splitter" name="horizontal_splitter">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="layoutWidget">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="TagsView" name="tags_view">
|
|
||||||
<property name="tabKeyNavigation">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="animated">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="headerHidden">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="popularity">
|
|
||||||
<property name="text">
|
|
||||||
<string>Sort by &popularity</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="tag_match">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Match any</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Match all</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="edit_categories">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Create, edit, and delete user categories</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Manage &user categories</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="">
|
|
||||||
<layout class="QVBoxLayout" name="cb_layout">
|
|
||||||
<item>
|
|
||||||
<widget class="BooksView" name="library_view">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>100</horstretch>
|
|
||||||
<verstretch>10</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="acceptDrops">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropOverwriteMode">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode">
|
|
||||||
<enum>QAbstractItemView::DragDrop</enum>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior">
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showGrid">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="main_memory">
|
|
||||||
<layout class="QGridLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="DeviceBooksView" name="memory_view">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>100</horstretch>
|
|
||||||
<verstretch>10</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="acceptDrops">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropOverwriteMode">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode">
|
|
||||||
<enum>QAbstractItemView::DragDrop</enum>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior">
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showGrid">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="card_a_memory">
|
|
||||||
<layout class="QGridLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="DeviceBooksView" name="card_a_view">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
|
||||||
<horstretch>10</horstretch>
|
|
||||||
<verstretch>10</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="acceptDrops">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropOverwriteMode">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode">
|
|
||||||
<enum>QAbstractItemView::DragDrop</enum>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior">
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showGrid">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="card_b_memory">
|
|
||||||
<layout class="QGridLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="DeviceBooksView" name="card_b_view">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
|
||||||
<horstretch>10</horstretch>
|
|
||||||
<verstretch>10</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="acceptDrops">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropOverwriteMode">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode">
|
|
||||||
<enum>QAbstractItemView::DragDrop</enum>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior">
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showGrid">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</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>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>30</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="StatusBar" name="status_bar" native="true"/>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QToolBar" name="tool_bar">
|
<widget class="QToolBar" name="tool_bar">
|
||||||
@ -804,26 +540,11 @@
|
|||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
|
||||||
<class>BooksView</class>
|
|
||||||
<extends>QTableView</extends>
|
|
||||||
<header>calibre/gui2/library/views.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>LocationView</class>
|
<class>LocationView</class>
|
||||||
<extends>QListView</extends>
|
<extends>QListView</extends>
|
||||||
<header>widgets.h</header>
|
<header>widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>DeviceBooksView</class>
|
|
||||||
<extends>QTableView</extends>
|
|
||||||
<header>calibre/gui2/library/views.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>TagsView</class>
|
|
||||||
<extends>QTreeView</extends>
|
|
||||||
<header>calibre/gui2/tag_view.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>SearchBox2</class>
|
<class>SearchBox2</class>
|
||||||
<extends>QComboBox</extends>
|
<extends>QComboBox</extends>
|
||||||
@ -834,24 +555,6 @@
|
|||||||
<extends>QComboBox</extends>
|
<extends>QComboBox</extends>
|
||||||
<header>calibre.gui2.search_box</header>
|
<header>calibre.gui2.search_box</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>StatusBar</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>calibre/gui2/status.h</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>Splitter</class>
|
|
||||||
<extends>QSplitter</extends>
|
|
||||||
<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>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../resources/images.qrc"/>
|
<include location="../../../resources/images.qrc"/>
|
||||||
|
@ -713,8 +713,9 @@ void PictureFlowPrivate::render()
|
|||||||
QPainter painter;
|
QPainter painter;
|
||||||
painter.begin(&buffer);
|
painter.begin(&buffer);
|
||||||
|
|
||||||
QFont font("Arial", FONT_SIZE);
|
QFont font = QFont();
|
||||||
font.setBold(true);
|
font.setBold(true);
|
||||||
|
font.setPointSize(FONT_SIZE);
|
||||||
painter.setFont(font);
|
painter.setFont(font);
|
||||||
painter.setPen(Qt::white);
|
painter.setPen(Qt::white);
|
||||||
//painter.setPen(QColor(255,255,255,127));
|
//painter.setPen(QColor(255,255,255,127));
|
||||||
@ -763,8 +764,9 @@ void PictureFlowPrivate::render()
|
|||||||
QPainter painter;
|
QPainter painter;
|
||||||
painter.begin(&buffer);
|
painter.begin(&buffer);
|
||||||
|
|
||||||
QFont font("Arial", FONT_SIZE);
|
QFont font = QFont();
|
||||||
font.setBold(true);
|
font.setBold(true);
|
||||||
|
font.setPointSize(FONT_SIZE);
|
||||||
painter.setFont(font);
|
painter.setFont(font);
|
||||||
|
|
||||||
int leftTextIndex = (step>0) ? centerIndex : centerIndex-1;
|
int leftTextIndex = (step>0) ? centerIndex : centerIndex-1;
|
||||||
|
@ -1,147 +0,0 @@
|
|||||||
#!/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 functools import partial
|
|
||||||
|
|
||||||
from PyQt4.Qt import QToolBar, Qt, QIcon, QSizePolicy, QWidget, \
|
|
||||||
QSize, QToolButton
|
|
||||||
|
|
||||||
from calibre.gui2 import dynamic
|
|
||||||
|
|
||||||
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.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_button, cover_browser, toggle_cover_browser,
|
|
||||||
cover_browser_error, vertical_splitter, horizontal_splitter):
|
|
||||||
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)
|
|
||||||
tb_last_open_state = dynamic.get('tag_browser_last_open_state', None)
|
|
||||||
if tb_last_open_state is not None and \
|
|
||||||
not self.horizontal_splitter.is_side_index_hidden:
|
|
||||||
self.horizontal_splitter.restoreState(tb_last_open_state)
|
|
||||||
|
|
||||||
bi_state = dynamic.get('book_info_state', None)
|
|
||||||
if bi_state is not None:
|
|
||||||
self.vertical_splitter.restoreState(bi_state)
|
|
||||||
bi_last_open_state = dynamic.get('book_info_last_open_state', None)
|
|
||||||
if bi_last_open_state is not None and \
|
|
||||||
not self.vertical_splitter.is_side_index_hidden:
|
|
||||||
self.vertical_splitter.restoreState(bi_last_open_state)
|
|
||||||
|
|
||||||
self.horizontal_splitter.initialize(name='tag_browser')
|
|
||||||
self.vertical_splitter.initialize(name='book_info')
|
|
||||||
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)
|
|
||||||
self.addWidget(jobs_button)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
@ -10,9 +10,11 @@ Browsing book collection by tags.
|
|||||||
from itertools import izip
|
from itertools import izip
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, \
|
from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, QCheckBox, \
|
||||||
QFont, QSize, QIcon, QPoint, \
|
QFont, QSize, QIcon, QPoint, QVBoxLayout, QComboBox, \
|
||||||
QAbstractItemModel, QVariant, QModelIndex, QMenu
|
QAbstractItemModel, QVariant, QModelIndex, QMenu, \
|
||||||
|
QPushButton, QWidget
|
||||||
|
|
||||||
from calibre.gui2 import config, NONE
|
from calibre.gui2 import config, NONE
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.library.field_metadata import TagsIcons
|
from calibre.library.field_metadata import TagsIcons
|
||||||
@ -633,3 +635,28 @@ class TagBrowserMixin(object): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class TagBrowserWidget(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self._layout = QVBoxLayout()
|
||||||
|
self.setLayout(self._layout)
|
||||||
|
|
||||||
|
parent.tags_view = TagsView(parent)
|
||||||
|
self._layout.addWidget(parent.tags_view)
|
||||||
|
|
||||||
|
parent.popularity = QCheckBox(parent)
|
||||||
|
parent.popularity.setText(_('Sort by &popularity'))
|
||||||
|
self._layout.addWidget(parent.popularity)
|
||||||
|
|
||||||
|
parent.tag_match = QComboBox(parent)
|
||||||
|
for x in (_('Match any'), _('Match all')):
|
||||||
|
parent.tag_match.addItem(x)
|
||||||
|
parent.tag_match.setCurrentIndex(0)
|
||||||
|
self._layout.addWidget(parent.tag_match)
|
||||||
|
|
||||||
|
parent.edit_categories = QPushButton(_('Manage &user categories'), parent)
|
||||||
|
self._layout.addWidget(parent.edit_categories)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ from calibre.gui2 import warning_dialog, choose_files, error_dialog, \
|
|||||||
Dispatcher, gprefs, \
|
Dispatcher, gprefs, \
|
||||||
max_available_height, config, info_dialog, \
|
max_available_height, config, info_dialog, \
|
||||||
GetMetadata
|
GetMetadata
|
||||||
from calibre.gui2.cover_flow import pictureflowerror, CoverFlowMixin
|
from calibre.gui2.cover_flow import CoverFlowMixin
|
||||||
from calibre.gui2.widgets import ProgressIndicator, IMAGE_EXTENSIONS
|
from calibre.gui2.widgets import ProgressIndicator, IMAGE_EXTENSIONS
|
||||||
from calibre.gui2.wizard import move_library
|
from calibre.gui2.wizard import move_library
|
||||||
from calibre.gui2.dialogs.scheduler import Scheduler
|
from calibre.gui2.dialogs.scheduler import Scheduler
|
||||||
@ -61,7 +61,7 @@ from calibre.library.caches import CoverCache
|
|||||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||||
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
|
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
|
||||||
from calibre.gui2.tag_view import TagBrowserMixin
|
from calibre.gui2.tag_view import TagBrowserMixin
|
||||||
from calibre.gui2.init import ToolbarMixin, LibraryViewMixin
|
from calibre.gui2.init import ToolbarMixin, LibraryViewMixin, LayoutMixin
|
||||||
|
|
||||||
class Listener(Thread): # {{{
|
class Listener(Thread): # {{{
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
||||||
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin):
|
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, LayoutMixin):
|
||||||
'The main GUI'
|
'The main GUI'
|
||||||
|
|
||||||
def set_default_thumbnail(self, height):
|
def set_default_thumbnail(self, height):
|
||||||
@ -143,8 +143,15 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
|||||||
self.check_messages_timer.start(1000)
|
self.check_messages_timer.start(1000)
|
||||||
|
|
||||||
Ui_MainWindow.__init__(self)
|
Ui_MainWindow.__init__(self)
|
||||||
self.setupUi(self)
|
|
||||||
self.setWindowTitle(__appname__)
|
# Jobs Button {{{
|
||||||
|
self.job_manager = JobManager()
|
||||||
|
self.jobs_dialog = JobsDialog(self, self.job_manager)
|
||||||
|
self.jobs_button = JobsButton()
|
||||||
|
self.jobs_button.initialize(self.jobs_dialog, self.job_manager)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
LayoutMixin.__init__(self)
|
||||||
|
|
||||||
self.restriction_count_of_books_in_view = 0
|
self.restriction_count_of_books_in_view = 0
|
||||||
self.restriction_count_of_books_in_library = 0
|
self.restriction_count_of_books_in_library = 0
|
||||||
@ -167,11 +174,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
|||||||
self.progress_indicator = ProgressIndicator(self)
|
self.progress_indicator = ProgressIndicator(self)
|
||||||
self.verbose = opts.verbose
|
self.verbose = opts.verbose
|
||||||
self.get_metadata = GetMetadata()
|
self.get_metadata = GetMetadata()
|
||||||
self.read_settings()
|
|
||||||
self.job_manager = JobManager()
|
|
||||||
self.emailer = Emailer()
|
self.emailer = Emailer()
|
||||||
self.emailer.start()
|
self.emailer.start()
|
||||||
self.jobs_dialog = JobsDialog(self, self.job_manager)
|
|
||||||
self.upload_memory = {}
|
self.upload_memory = {}
|
||||||
self.delete_memory = {}
|
self.delete_memory = {}
|
||||||
self.conversion_jobs = {}
|
self.conversion_jobs = {}
|
||||||
@ -326,17 +330,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
|||||||
self.resize(self.width(), self._calculated_available_height)
|
self.resize(self.width(), self._calculated_available_height)
|
||||||
self.search.setMaximumWidth(self.width()-150)
|
self.search.setMaximumWidth(self.width()-150)
|
||||||
|
|
||||||
# Jobs Button {{{
|
|
||||||
self.jobs_button = JobsButton()
|
|
||||||
self.jobs_button.initialize(self.jobs_dialog, self.job_manager)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
####################### Side Bar ###############################
|
|
||||||
|
|
||||||
self.sidebar.initialize(self.jobs_button, self.cover_flow,
|
|
||||||
self.toggle_cover_flow, pictureflowerror,
|
|
||||||
self.vertical_splitter, self.horizontal_splitter)
|
|
||||||
|
|
||||||
|
|
||||||
if config['autolaunch_server']:
|
if config['autolaunch_server']:
|
||||||
from calibre.library.server.main import start_threaded_server
|
from calibre.library.server.main import start_threaded_server
|
||||||
@ -362,6 +355,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
|||||||
self._add_filesystem_book = Dispatcher(self.__add_filesystem_book)
|
self._add_filesystem_book = Dispatcher(self.__add_filesystem_book)
|
||||||
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
|
self.read_settings()
|
||||||
|
self.finalize_layout()
|
||||||
|
|
||||||
def do_saved_search_edit(self, search):
|
def do_saved_search_edit(self, search):
|
||||||
d = SavedSearchEditor(self, search)
|
d = SavedSearchEditor(self, search)
|
||||||
@ -1934,7 +1929,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
|||||||
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
|
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
|
||||||
self.stack.setCurrentIndex(page)
|
self.stack.setCurrentIndex(page)
|
||||||
self.status_bar.reset_info()
|
self.status_bar.reset_info()
|
||||||
self.sidebar.location_changed(location)
|
for x in ('tb', 'cb'):
|
||||||
|
splitter = getattr(self, x+'_splitter')
|
||||||
|
splitter.button.setEnabled(location == 'library')
|
||||||
if location == 'library':
|
if location == 'library':
|
||||||
self.action_edit.setEnabled(True)
|
self.action_edit.setEnabled(True)
|
||||||
self.action_merge.setEnabled(True)
|
self.action_merge.setEnabled(True)
|
||||||
@ -2037,14 +2034,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
|
|||||||
if geometry is not None:
|
if geometry is not None:
|
||||||
self.restoreGeometry(geometry)
|
self.restoreGeometry(geometry)
|
||||||
self.read_toolbar_settings()
|
self.read_toolbar_settings()
|
||||||
|
self.read_layout_settings()
|
||||||
|
|
||||||
def write_settings(self):
|
def write_settings(self):
|
||||||
config.set('main_window_geometry', self.saveGeometry())
|
config.set('main_window_geometry', self.saveGeometry())
|
||||||
dynamic.set('sort_history', self.library_view.model().sort_history)
|
dynamic.set('sort_history', self.library_view.model().sort_history)
|
||||||
self.sidebar.save_state()
|
self.save_layout_state()
|
||||||
for view in ('library_view', 'memory_view', 'card_a_view',
|
|
||||||
'card_b_view'):
|
|
||||||
getattr(self, view).save_state()
|
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
self.quit(restart=True)
|
self.quit(restart=True)
|
||||||
|
@ -4,16 +4,18 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
Miscellaneous widgets used in the GUI
|
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 QListView, QIcon, QFont, QLabel, QListWidget, \
|
||||||
QListWidgetItem, QTextCharFormat, QApplication, \
|
QListWidgetItem, QTextCharFormat, QApplication, \
|
||||||
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
||||||
QPixmap, QPalette, QSplitterHandle, \
|
QPixmap, QPalette, QSplitterHandle, QToolButton, \
|
||||||
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
|
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
|
||||||
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
|
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
|
||||||
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
||||||
QMenu, QStringListModel, QCompleter, QStringList
|
QMenu, QStringListModel, QCompleter, QStringList, \
|
||||||
|
QTimer
|
||||||
|
|
||||||
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, dynamic
|
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, human_readable
|
||||||
@ -927,6 +929,7 @@ class SplitterHandle(QSplitterHandle):
|
|||||||
self.double_clicked.connect(splitter.double_clicked,
|
self.double_clicked.connect(splitter.double_clicked,
|
||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
self.highlight = False
|
self.highlight = False
|
||||||
|
self.setToolTip(_('Drag to resize')+' '+splitter.label)
|
||||||
|
|
||||||
def splitter_moved(self, *args):
|
def splitter_moved(self, *args):
|
||||||
oh = self.highlight
|
oh = self.highlight
|
||||||
@ -944,20 +947,62 @@ class SplitterHandle(QSplitterHandle):
|
|||||||
def mouseDoubleClickEvent(self, ev):
|
def mouseDoubleClickEvent(self, ev):
|
||||||
self.double_clicked.emit(self)
|
self.double_clicked.emit(self)
|
||||||
|
|
||||||
|
class LayoutButton(QToolButton):
|
||||||
|
|
||||||
|
def __init__(self, icon, text, splitter, parent=None):
|
||||||
|
QToolButton.__init__(self, parent)
|
||||||
|
self.label = text
|
||||||
|
self.setIcon(QIcon(icon))
|
||||||
|
self.setCheckable(True)
|
||||||
|
|
||||||
|
self.splitter = splitter
|
||||||
|
splitter.state_changed.connect(self.update_state)
|
||||||
|
|
||||||
|
def set_state_to_show(self, *args):
|
||||||
|
self.setChecked(False)
|
||||||
|
label =_('Show')
|
||||||
|
self.setText(label + ' ' + self.label)
|
||||||
|
|
||||||
|
def set_state_to_hide(self, *args):
|
||||||
|
self.setChecked(True)
|
||||||
|
label = _('Hide')
|
||||||
|
self.setText(label + ' ' + self.label)
|
||||||
|
|
||||||
|
def update_state(self, *args):
|
||||||
|
if self.splitter.is_side_index_hidden:
|
||||||
|
self.set_state_to_show()
|
||||||
|
else:
|
||||||
|
self.set_state_to_hide()
|
||||||
|
|
||||||
class Splitter(QSplitter):
|
class Splitter(QSplitter):
|
||||||
|
|
||||||
state_changed = pyqtSignal(object)
|
state_changed = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, name, label, icon, initial_show=True,
|
||||||
QSplitter.__init__(self, *args)
|
initial_side_size=120, connect_button=True,
|
||||||
|
orientation=Qt.Horizontal, side_index=0, parent=None):
|
||||||
|
QSplitter.__init__(self, parent)
|
||||||
|
self.resize_timer = QTimer(self)
|
||||||
|
self.resize_timer.setSingleShot(True)
|
||||||
|
self.desired_side_size = initial_side_size
|
||||||
|
self.desired_show = initial_show
|
||||||
|
self.resize_timer.setInterval(5)
|
||||||
|
self.resize_timer.timeout.connect(self.do_resize)
|
||||||
|
self.setOrientation(orientation)
|
||||||
|
self.side_index = side_index
|
||||||
|
self._name = name
|
||||||
|
self.label = label
|
||||||
|
self.initial_side_size = initial_side_size
|
||||||
|
self.initial_show = initial_show
|
||||||
self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection)
|
self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection)
|
||||||
|
self.button = LayoutButton(icon, label, self)
|
||||||
|
if connect_button:
|
||||||
|
self.button.clicked.connect(self.double_clicked)
|
||||||
|
|
||||||
def createHandle(self):
|
def createHandle(self):
|
||||||
return SplitterHandle(self.orientation(), self)
|
return SplitterHandle(self.orientation(), self)
|
||||||
|
|
||||||
def initialize(self, name=None):
|
def initialize(self):
|
||||||
if name is not None:
|
|
||||||
self._name = name
|
|
||||||
for i in range(self.count()):
|
for i in range(self.count()):
|
||||||
h = self.handle(i)
|
h = self.handle(i)
|
||||||
if h is not None:
|
if h is not None:
|
||||||
@ -965,40 +1010,115 @@ class Splitter(QSplitter):
|
|||||||
self.state_changed.emit(not self.is_side_index_hidden)
|
self.state_changed.emit(not self.is_side_index_hidden)
|
||||||
|
|
||||||
def splitter_moved(self, *args):
|
def splitter_moved(self, *args):
|
||||||
|
self.desired_side_size = self.side_index_size
|
||||||
self.state_changed.emit(not self.is_side_index_hidden)
|
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
|
@property
|
||||||
def is_side_index_hidden(self):
|
def is_side_index_hidden(self):
|
||||||
sizes = list(self.sizes())
|
sizes = list(self.sizes())
|
||||||
return sizes[self.side_index] == 0
|
return sizes[self.side_index] == 0
|
||||||
|
|
||||||
def toggle_side_index(self):
|
@property
|
||||||
self.double_clicked(None)
|
def save_name(self):
|
||||||
|
ori = 'horizontal' if self.orientation() == Qt.Horizontal \
|
||||||
|
else 'vertical'
|
||||||
|
return self._name + '_' + ori
|
||||||
|
|
||||||
def double_clicked(self, handle):
|
def print_sizes(self):
|
||||||
visible = not self.is_side_index_hidden
|
if self.count() > 1:
|
||||||
sizes = list(self.sizes())
|
print self.save_name, 'side:', self.side_index_size, 'other:',
|
||||||
if 0 in sizes:
|
print list(self.sizes())[self.other_index]
|
||||||
idx = sizes.index(0)
|
|
||||||
sizes[idx] = 80
|
|
||||||
else:
|
|
||||||
sizes[self.side_index] = 0
|
|
||||||
|
|
||||||
if visible:
|
@dynamic_property
|
||||||
dynamic.set(self._name + '_last_open_state', str(self.saveState()))
|
def side_index_size(self):
|
||||||
|
def fget(self):
|
||||||
|
if self.count() < 2: return 0
|
||||||
|
return self.sizes()[self.side_index]
|
||||||
|
|
||||||
|
def fset(self, val):
|
||||||
|
if self.count() < 2: return
|
||||||
|
if val == 0 and not self.is_side_index_hidden:
|
||||||
|
self.save_state()
|
||||||
|
sizes = list(self.sizes())
|
||||||
|
for i in range(len(sizes)):
|
||||||
|
sizes[i] = val if i == self.side_index else 10
|
||||||
self.setSizes(sizes)
|
self.setSizes(sizes)
|
||||||
|
total = sum(self.sizes())
|
||||||
|
sizes = list(self.sizes())
|
||||||
|
for i in range(len(sizes)):
|
||||||
|
sizes[i] = val if i == self.side_index else total-val
|
||||||
|
self.setSizes(sizes)
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
|
def do_resize(self, *args):
|
||||||
|
orig = self.desired_side_size
|
||||||
|
QSplitter.resizeEvent(self, self._resize_ev)
|
||||||
|
if orig > 20 and self.desired_show:
|
||||||
|
c = 0
|
||||||
|
while abs(self.side_index_size - orig) > 10 and c < 5:
|
||||||
|
self.apply_state(self.get_state(), save_desired=False)
|
||||||
|
c += 1
|
||||||
|
|
||||||
|
def resizeEvent(self, ev):
|
||||||
|
if self.resize_timer.isActive():
|
||||||
|
self.resize_timer.stop()
|
||||||
|
self._resize_ev = ev
|
||||||
|
self.resize_timer.start()
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
if self.count() < 2: return (False, 200)
|
||||||
|
return (self.desired_show, self.desired_side_size)
|
||||||
|
|
||||||
|
def apply_state(self, state, save_desired=True):
|
||||||
|
if state[0]:
|
||||||
|
self.side_index_size = state[1]
|
||||||
|
if save_desired:
|
||||||
|
self.desired_side_size = self.side_index_size
|
||||||
else:
|
else:
|
||||||
state = dynamic.get(self._name+ '_last_open_state', None)
|
self.side_index_size = 0
|
||||||
if state is not None:
|
self.desired_show = state[0]
|
||||||
self.restoreState(state)
|
|
||||||
else:
|
|
||||||
self.setSizes(sizes)
|
|
||||||
self.initialize()
|
|
||||||
|
|
||||||
|
def default_state(self):
|
||||||
|
return (self.initial_show, self.initial_side_size)
|
||||||
|
|
||||||
|
# Public API {{{
|
||||||
|
|
||||||
|
def save_state(self):
|
||||||
|
if self.count() > 1:
|
||||||
|
gprefs[self.save_name+'_state'] = self.get_state()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def other_index(self):
|
||||||
|
return (self.side_index+1)%2
|
||||||
|
|
||||||
|
def restore_state(self):
|
||||||
|
if self.count() > 1:
|
||||||
|
state = gprefs.get(self.save_name+'_state',
|
||||||
|
self.default_state())
|
||||||
|
self.apply_state(state, save_desired=False)
|
||||||
|
self.desired_side_size = state[1]
|
||||||
|
|
||||||
|
def toggle_side_pane(self, hide=None):
|
||||||
|
if hide is None:
|
||||||
|
action = 'show' if self.is_side_index_hidden else 'hide'
|
||||||
|
else:
|
||||||
|
action = 'hide' if hide else 'show'
|
||||||
|
getattr(self, action+'_side_pane')()
|
||||||
|
|
||||||
|
def show_side_pane(self):
|
||||||
|
if self.count() < 2 or not self.is_side_index_hidden:
|
||||||
|
return
|
||||||
|
self.apply_state((True, self.desired_side_size))
|
||||||
|
|
||||||
|
def hide_side_pane(self):
|
||||||
|
if self.count() < 2 or self.is_side_index_hidden:
|
||||||
|
return
|
||||||
|
self.apply_state((False, self.desired_side_size))
|
||||||
|
|
||||||
|
def double_clicked(self, *args):
|
||||||
|
self.toggle_side_pane()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
@ -96,14 +96,14 @@ class Kobo(Device):
|
|||||||
class Booq(Device):
|
class Booq(Device):
|
||||||
name = 'Booq Reader'
|
name = 'Booq Reader'
|
||||||
manufacturer = 'Booq'
|
manufacturer = 'Booq'
|
||||||
output_profile = 'prs505'
|
output_profile = 'sony'
|
||||||
output_format = 'EPUB'
|
output_format = 'EPUB'
|
||||||
id = 'booq'
|
id = 'booq'
|
||||||
|
|
||||||
class TheBook(Device):
|
class TheBook(Device):
|
||||||
name = 'The Book'
|
name = 'The Book'
|
||||||
manufacturer = 'Augen'
|
manufacturer = 'Augen'
|
||||||
output_profile = 'prs505'
|
output_profile = 'sony'
|
||||||
output_format = 'EPUB'
|
output_format = 'EPUB'
|
||||||
id = 'thebook'
|
id = 'thebook'
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user