Merge from custcol trunk

This commit is contained in:
Charles Haley 2010-05-01 23:23:38 +01:00
commit 50aeaaaeff
10 changed files with 102 additions and 99 deletions

View File

@ -13,7 +13,6 @@ from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
ORG_NAME = 'KovidsBrain' ORG_NAME = 'KovidsBrain'
APP_UID = 'libprs500' APP_UID = 'libprs500'
from calibre import islinux, iswindows, isosx, isfreebsd from calibre import islinux, iswindows, isosx, isfreebsd
from calibre.constants import preferred_encoding
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
from calibre.utils.localization import set_qt_translator from calibre.utils.localization import set_qt_translator
from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats

View File

@ -7,6 +7,7 @@ from PyQt4.Qt import QDialog
from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog
class CommentsDialog(QDialog, Ui_CommentsDialog): class CommentsDialog(QDialog, Ui_CommentsDialog):
def __init__(self, parent, text): def __init__(self, parent, text):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
Ui_CommentsDialog.__init__(self) Ui_CommentsDialog.__init__(self)

View File

@ -29,7 +29,7 @@ from calibre.utils.date import dt_factory, qt_to_dt, isoformat
from calibre.utils.pyparsing import ParseException from calibre.utils.pyparsing import ParseException
from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.search_query_parser import SearchQueryParser
class LibraryDelegate(QStyledItemDelegate): class RatingDelegate(QStyledItemDelegate):
COLOR = QColor("blue") COLOR = QColor("blue")
SIZE = 16 SIZE = 16
PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
@ -162,17 +162,16 @@ class TagsDelegate(QStyledItemDelegate):
editor = TagsLineEdit(parent, self.db.all_tags()) editor = TagsLineEdit(parent, self.db.all_tags())
else: else:
editor = TagsLineEdit(parent, sorted(list(self.db.all_custom(label=col)))) editor = TagsLineEdit(parent, sorted(list(self.db.all_custom(label=col))))
return editor; return editor
else: else:
editor = EnLineEdit(parent) editor = EnLineEdit(parent)
return editor return editor
class CcTextDelegate(QStyledItemDelegate): class CcTextDelegate(QStyledItemDelegate):
def __init__(self, parent): '''
''' Delegate for text/int/float data.
Delegate for text/int/float data. '''
'''
QStyledItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
m = index.model() m = index.model()
col = m.column_map[index.column()] col = m.column_map[index.column()]
@ -191,12 +190,9 @@ class CcTextDelegate(QStyledItemDelegate):
return editor return editor
class CcCommentsDelegate(QStyledItemDelegate): class CcCommentsDelegate(QStyledItemDelegate):
def __init__(self, parent): '''
''' Delegate for comments data.
Delegate for comments data. '''
'''
QStyledItemDelegate.__init__(self, parent)
self.parent = parent
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
m = index.model() m = index.model()
@ -211,7 +207,7 @@ class CcCommentsDelegate(QStyledItemDelegate):
return None return None
def setModelData(self, editor, model, index): def setModelData(self, editor, model, index):
model.setData(index, QVariant(editor.textbox.text()), Qt.EditRole) model.setData(index, QVariant(editor.textbox.toPlainText()), Qt.EditRole)
class CcBoolDelegate(QStyledItemDelegate): class CcBoolDelegate(QStyledItemDelegate):
def __init__(self, parent): def __init__(self, parent):
@ -247,6 +243,7 @@ class BooksModel(QAbstractTableModel):
about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted') about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted')
sorting_done = pyqtSignal(object, name='sortingDone') sorting_done = pyqtSignal(object, name='sortingDone')
database_changed = pyqtSignal(object, name='databaseChanged')
orig_headers = { orig_headers = {
'title' : _("Title"), 'title' : _("Title"),
@ -304,6 +301,7 @@ class BooksModel(QAbstractTableModel):
self.db = db self.db = db
self.custom_columns = self.db.custom_column_label_map self.custom_columns = self.db.custom_column_label_map
self.read_config() self.read_config()
self.database_changed.emit(db)
def refresh_ids(self, ids, current_row=-1): def refresh_ids(self, ids, current_row=-1):
rows = self.db.refresh_ids(ids) rows = self.db.refresh_ids(ids)
@ -893,7 +891,7 @@ class BooksView(TableView):
def __init__(self, parent, modelcls=BooksModel): def __init__(self, parent, modelcls=BooksModel):
TableView.__init__(self, parent) TableView.__init__(self, parent)
self.rating_delegate = LibraryDelegate(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)
self.tags_delegate = TagsDelegate(self) self.tags_delegate = TagsDelegate(self)

View File

@ -301,17 +301,14 @@
<widget class="QWidget" name="library"> <widget class="QWidget" name="library">
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <widget class="QSplitter" name="horizontal_splitter">
<item> <property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="TagsView" name="tags_view"> <widget class="TagsView" name="tags_view">
<property name="maximumSize">
<size>
<width>256</width>
<height>16777215</height>
</size>
</property>
<property name="tabKeyNavigation"> <property name="tabKeyNavigation">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -354,12 +351,12 @@
</item> </item>
<item> <item>
<widget class="QPushButton" name="edit_categories"> <widget class="QPushButton" name="edit_categories">
<property name="text"> <property name="toolTip">
<string>Manage user categories</string> <string>Create, edit, and delete user categories</string>
</property>
<property name="text">
<string>Manage &amp;user categories</string>
</property> </property>
<property name="toolTip">
<string>Create, edit, and delete user categories</string>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -369,7 +366,10 @@
<item> <item>
<widget class="QLabel" name="restriction_label"> <widget class="QLabel" name="restriction_label">
<property name="text"> <property name="text">
<string>Restrict display to:</string> <string>&amp;Restrict to:</string>
</property>
<property name="buddy">
<cstring>search_restriction</cstring>
</property> </property>
</widget> </widget>
</item> </item>
@ -381,50 +381,48 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Books display will be restricted to those matching the selected saved search</string> <string>Books display will be restricted to those matching the selected saved search</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
</layout> </layout>
</item> </widget>
<item> <widget class="BooksView" name="library_view">
<widget class="BooksView" name="library_view"> <property name="sizePolicy">
<property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>100</horstretch>
<horstretch>100</horstretch> <verstretch>10</verstretch>
<verstretch>10</verstretch> </sizepolicy>
</sizepolicy> </property>
</property> <property name="acceptDrops">
<property name="acceptDrops"> <bool>true</bool>
<bool>true</bool> </property>
</property> <property name="dragEnabled">
<property name="dragEnabled"> <bool>true</bool>
<bool>true</bool> </property>
</property> <property name="dragDropOverwriteMode">
<property name="dragDropOverwriteMode"> <bool>false</bool>
<bool>false</bool> </property>
</property> <property name="dragDropMode">
<property name="dragDropMode"> <enum>QAbstractItemView::DragDrop</enum>
<enum>QAbstractItemView::DragDrop</enum> </property>
</property> <property name="alternatingRowColors">
<property name="alternatingRowColors"> <bool>true</bool>
<bool>true</bool> </property>
</property> <property name="selectionBehavior">
<property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum>
<enum>QAbstractItemView::SelectRows</enum> </property>
</property> <property name="showGrid">
<property name="showGrid"> <bool>false</bool>
<bool>false</bool> </property>
</property> <property name="wordWrap">
<property name="wordWrap"> <bool>false</bool>
<bool>false</bool> </property>
</property> </widget>
</widget> </widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@ -205,19 +205,6 @@ class CoverFlowButton(QToolButton):
self.setDisabled(True) self.setDisabled(True)
self.setToolTip(_('<p>Browsing books by their covers is disabled.<br>Import of pictureflow module failed:<br>')+reason) self.setToolTip(_('<p>Browsing books by their covers is disabled.<br>Import of pictureflow module failed:<br>')+reason)
class TagViewButton(QToolButton):
def __init__(self, parent=None):
QToolButton.__init__(self, parent)
self.setIconSize(QSize(80, 80))
self.setIcon(QIcon(I('tags.svg')))
self.setToolTip(_('Click to browse books by tags'))
self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding))
self.setCursor(Qt.PointingHandCursor)
self.setCheckable(True)
self.setChecked(False)
self.setAutoRaise(True)
class StatusBar(QStatusBar): class StatusBar(QStatusBar):
@ -227,9 +214,7 @@ class StatusBar(QStatusBar):
self.notifier = get_notifier(systray) self.notifier = get_notifier(systray)
self.movie_button = MovieButton(jobs_dialog) self.movie_button = MovieButton(jobs_dialog)
self.cover_flow_button = CoverFlowButton() self.cover_flow_button = CoverFlowButton()
self.tag_view_button = TagViewButton()
self.addPermanentWidget(self.cover_flow_button) self.addPermanentWidget(self.cover_flow_button)
self.addPermanentWidget(self.tag_view_button)
self.addPermanentWidget(self.movie_button) self.addPermanentWidget(self.movie_button)
self.book_info = BookInfoDisplay(self.clearMessage) self.book_info = BookInfoDisplay(self.clearMessage)
self.book_info.setAcceptDrops(True) self.book_info.setAcceptDrops(True)

View File

@ -575,8 +575,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.connect(self.tags_view, self.connect(self.tags_view,
SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
self.saved_search.clear_to_help) self.saved_search.clear_to_help)
self.connect(self.status_bar.tag_view_button,
SIGNAL('toggled(bool)'), self.toggle_tags_view)
self.connect(self.search, self.connect(self.search,
SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'),
self.tags_view.model().reinit) self.tags_view.model().reinit)
@ -602,6 +600,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.db_images.reset() self.db_images.reset()
self.library_view.model().count_changed() self.library_view.model().count_changed()
self.location_view.model().database_changed(self.library_view.model().db)
self.library_view.model().database_changed.connect(self.location_view.model().database_changed,
type=Qt.QueuedConnection)
########################### Tags Browser ##############################
self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon)
self.search_restriction.setMinimumContentsLength(10)
########################### Cover Flow ################################ ########################### Cover Flow ################################
self.cover_flow = None self.cover_flow = None
@ -666,8 +672,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
if self.cover_flow is not None and dynamic.get('cover_flow_visible', False): if self.cover_flow is not None and dynamic.get('cover_flow_visible', False):
self.status_bar.cover_flow_button.toggle() self.status_bar.cover_flow_button.toggle()
if dynamic.get('tag_view_visible', False): tb_state = dynamic.get('tag_browser_state', None)
self.status_bar.tag_view_button.toggle() if tb_state is not None:
self.horizontal_splitter.restoreState(tb_state)
self.toggle_tags_view(True)
self._add_filesystem_book = Dispatcher(self.__add_filesystem_book) self._add_filesystem_book = Dispatcher(self.__add_filesystem_book)
v = self.library_view v = self.library_view
@ -2323,7 +2331,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.view_menu.actions()[1].setEnabled(True) self.view_menu.actions()[1].setEnabled(True)
self.action_open_containing_folder.setEnabled(True) self.action_open_containing_folder.setEnabled(True)
self.action_sync.setEnabled(True) self.action_sync.setEnabled(True)
self.status_bar.tag_view_button.setEnabled(True)
self.status_bar.cover_flow_button.setEnabled(True) self.status_bar.cover_flow_button.setEnabled(True)
for action in list(self.delete_menu.actions())[1:]: for action in list(self.delete_menu.actions())[1:]:
action.setEnabled(True) action.setEnabled(True)
@ -2334,7 +2341,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.view_menu.actions()[1].setEnabled(False) self.view_menu.actions()[1].setEnabled(False)
self.action_open_containing_folder.setEnabled(False) self.action_open_containing_folder.setEnabled(False)
self.action_sync.setEnabled(False) self.action_sync.setEnabled(False)
self.status_bar.tag_view_button.setEnabled(False)
self.status_bar.cover_flow_button.setEnabled(False) self.status_bar.cover_flow_button.setEnabled(False)
for action in list(self.delete_menu.actions())[1:]: for action in list(self.delete_menu.actions())[1:]:
action.setEnabled(False) action.setEnabled(False)
@ -2451,8 +2457,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
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)
dynamic.set('tag_view_visible', self.tags_view.isVisible())
dynamic.set('cover_flow_visible', self.cover_flow.isVisible()) dynamic.set('cover_flow_visible', self.cover_flow.isVisible())
dynamic.set('tag_browser_state',
str(self.horizontal_splitter.saveState()))
self.library_view.write_settings() self.library_view.write_settings()
if self.device_connected: if self.device_connected:
self.save_device_view_settings() self.save_device_view_settings()

View File

@ -23,6 +23,7 @@ from calibre.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.metadata.meta import metadata_from_filename from calibre.ebooks.metadata.meta import metadata_from_filename
from calibre.utils.config import prefs, XMLConfig from calibre.utils.config import prefs, XMLConfig
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
from calibre.constants import filesystem_encoding
history = XMLConfig('history') history = XMLConfig('history')
@ -230,13 +231,22 @@ class LocationModel(QAbstractListModel):
self.free = [-1, -1, -1] self.free = [-1, -1, -1]
self.count = 0 self.count = 0
self.highlight_row = 0 self.highlight_row = 0
self.library_tooltip = _('Click to see the books available on your computer')
self.tooltips = [ self.tooltips = [
_('Click to see the books available on your computer'), self.library_tooltip,
_('Click to see the books in the main memory of your reader'), _('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 A in your reader'),
_('Click to see the books on storage card B 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): def rowCount(self, *args):
return 1 + len([i for i in self.free if i >= 0]) return 1 + len([i for i in self.free if i >= 0])

View File

@ -155,7 +155,9 @@ class ResultCache(SearchQueryParser):
self._map = self._map_filtered = self._data = [] self._map = self._map_filtered = self._data = []
self.first_sort = True self.first_sort = True
self.search_restriction = '' self.search_restriction = ''
SearchQueryParser.__init__(self, [c for c in cc_label_map]) SearchQueryParser.__init__(self,
locations=SearchQueryParser.DEFAULT_LOCATIONS +
[c for c in cc_label_map])
self.build_relop_dict() self.build_relop_dict()
def build_relop_dict(self): def build_relop_dict(self):

View File

@ -73,7 +73,7 @@ class SearchQueryParser(object):
When no operator is specified between two tokens, `and` is assumed. When no operator is specified between two tokens, `and` is assumed.
Each token is a string of the form `location:query`. `location` is a string Each token is a string of the form `location:query`. `location` is a string
from :member:`LOCATIONS`. It is optional. If it is omitted, it is assumed to from :member:`DEFAULT_LOCATIONS`. It is optional. If it is omitted, it is assumed to
be `all`. `query` is an arbitrary string that must not contain parentheses. be `all`. `query` is an arbitrary string that must not contain parentheses.
If it contains whitespace, it should be quoted by enclosing it in `"` marks. If it contains whitespace, it should be quoted by enclosing it in `"` marks.
@ -86,7 +86,7 @@ class SearchQueryParser(object):
* `(author:Asimov or author:Hardy) and not tag:read` [search for unread books by Asimov or Hardy] * `(author:Asimov or author:Hardy) and not tag:read` [search for unread books by Asimov or Hardy]
''' '''
LOCATIONS = [ DEFAULT_LOCATIONS = [
'tag', 'tag',
'title', 'title',
'author', 'author',
@ -116,10 +116,13 @@ class SearchQueryParser(object):
failed.append(test[0]) failed.append(test[0])
return failed return failed
def __init__(self, custcols=[], test=False): def __init__(self, locations=None, test=False):
if locations is None:
locations = self.DEFAULT_LOCATIONS
self._tests_failed = False self._tests_failed = False
# Define a token # Define a token
standard_locations = map(lambda x : CaselessLiteral(x)+Suppress(':'), self.LOCATIONS+custcols) standard_locations = map(lambda x : CaselessLiteral(x)+Suppress(':'),
locations)
location = NoMatch() location = NoMatch()
for l in standard_locations: for l in standard_locations:
location |= l location |= l
@ -228,7 +231,7 @@ class SearchQueryParser(object):
''' '''
Should return the set of matches for :param:'location` and :param:`query`. Should return the set of matches for :param:'location` and :param:`query`.
:param:`location` is one of the items in :member:`SearchQueryParser.LOCATIONS`. :param:`location` is one of the items in :member:`SearchQueryParser.DEFAULT_LOCATIONS`.
:param:`query` is a string literal. :param:`query` is a string literal.
''' '''
return set([]) return set([])

View File

@ -121,7 +121,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
def __init__(self, db, *args): def __init__(self, db, *args):
QAbstractItemModel.__init__(self, *args) QAbstractItemModel.__init__(self, *args)
SearchQueryParser.__init__(self) SearchQueryParser.__init__(self, locations=['all'])
self.db = db self.db = db
self.default_icon = QVariant(QIcon(I('news.svg'))) self.default_icon = QVariant(QIcon(I('news.svg')))
self.custom_icon = QVariant(QIcon(I('user_profile.svg'))) self.custom_icon = QVariant(QIcon(I('user_profile.svg')))