From b060a20f71bcf24e90b7609b4d2845cab25267de Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 1 May 2010 11:16:01 -0600 Subject: [PATCH 1/6] Minor cleanups --- src/calibre/gui2/dialogs/comments_dialog.py | 1 + src/calibre/gui2/library.py | 22 +++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/calibre/gui2/dialogs/comments_dialog.py b/src/calibre/gui2/dialogs/comments_dialog.py index f9806b44d1..8b4df07fbc 100644 --- a/src/calibre/gui2/dialogs/comments_dialog.py +++ b/src/calibre/gui2/dialogs/comments_dialog.py @@ -7,6 +7,7 @@ from PyQt4.Qt import QDialog from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog class CommentsDialog(QDialog, Ui_CommentsDialog): + def __init__(self, parent, text): QDialog.__init__(self, parent) Ui_CommentsDialog.__init__(self) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index f499c95e14..545cc1f5e1 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -162,17 +162,16 @@ class TagsDelegate(QStyledItemDelegate): editor = TagsLineEdit(parent, self.db.all_tags()) else: editor = TagsLineEdit(parent, sorted(list(self.db.all_custom(label=col)))) - return editor; + return editor else: editor = EnLineEdit(parent) return editor class CcTextDelegate(QStyledItemDelegate): - def __init__(self, parent): - ''' - Delegate for text/int/float data. - ''' - QStyledItemDelegate.__init__(self, parent) + ''' + Delegate for text/int/float data. + ''' + def createEditor(self, parent, option, index): m = index.model() col = m.column_map[index.column()] @@ -191,12 +190,9 @@ class CcTextDelegate(QStyledItemDelegate): return editor class CcCommentsDelegate(QStyledItemDelegate): - def __init__(self, parent): - ''' - Delegate for comments data. - ''' - QStyledItemDelegate.__init__(self, parent) - self.parent = parent + ''' + Delegate for comments data. + ''' def createEditor(self, parent, option, index): m = index.model() @@ -211,7 +207,7 @@ class CcCommentsDelegate(QStyledItemDelegate): return None 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): def __init__(self, parent): From bb7edaa57b9aee6c9383dffa3bfae8431ef3b6cf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 1 May 2010 11:36:38 -0600 Subject: [PATCH 2/6] Make SearchQueryParser accept a dynamic list of locations --- src/calibre/library/caches.py | 4 +++- src/calibre/utils/search_query_parser.py | 13 ++++++++----- src/calibre/web/feeds/recipes/model.py | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index a31a8a846c..ee19f07644 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -155,7 +155,9 @@ class ResultCache(SearchQueryParser): self._map = self._map_filtered = self._data = [] self.first_sort = True 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() def build_relop_dict(self): diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py index 6768b66063..79324e6b8b 100644 --- a/src/calibre/utils/search_query_parser.py +++ b/src/calibre/utils/search_query_parser.py @@ -73,7 +73,7 @@ class SearchQueryParser(object): 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 - 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. 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] ''' - LOCATIONS = [ + DEFAULT_LOCATIONS = [ 'tag', 'title', 'author', @@ -116,10 +116,13 @@ class SearchQueryParser(object): failed.append(test[0]) 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 # 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() for l in standard_locations: location |= l @@ -228,7 +231,7 @@ class SearchQueryParser(object): ''' 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. ''' return set([]) diff --git a/src/calibre/web/feeds/recipes/model.py b/src/calibre/web/feeds/recipes/model.py index 55ff51d1e9..4eea0ce80c 100644 --- a/src/calibre/web/feeds/recipes/model.py +++ b/src/calibre/web/feeds/recipes/model.py @@ -121,7 +121,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser): def __init__(self, db, *args): QAbstractItemModel.__init__(self, *args) - SearchQueryParser.__init__(self) + SearchQueryParser.__init__(self, locations=['all']) self.db = db self.default_icon = QVariant(QIcon(I('news.svg'))) self.custom_icon = QVariant(QIcon(I('user_profile.svg'))) From 3f28c128ea0b0892609c0648128219832c92ad2a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 1 May 2010 11:49:46 -0600 Subject: [PATCH 3/6] Fix Tag Browser sizing issues with long searches --- src/calibre/gui2/main.ui | 27 ++++++++++++--------------- src/calibre/gui2/ui.py | 5 +++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index 73cee4a061..4c9351909e 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -306,12 +306,6 @@ - - - 256 - 16777215 - - true @@ -354,12 +348,12 @@ - - Manage user categories + + Create, edit, and delete user categories + + + Manage &user categories - - Create, edit, and delete user categories - @@ -369,7 +363,10 @@ - Restrict display to: + &Restrict display to: + + + search_restriction @@ -381,9 +378,9 @@ 0 - - Books display will be restricted to those matching the selected saved search - + + Books display will be restricted to those matching the selected saved search + diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index d5f50b92b8..12f620532e 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -603,6 +603,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.library_view.model().count_changed() + ########################### Tags Browser ############################## + self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon) + self.search_restriction.setMinimumContentsLength(10) + + ########################### Cover Flow ################################ self.cover_flow = None if CoverFlow is not None: From 6574f349030bfd06b871d3aa9806625b5e5b41c9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 1 May 2010 11:52:04 -0600 Subject: [PATCH 4/6] Rename LibraryDelegate --- src/calibre/gui2/library.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 545cc1f5e1..c7e2991010 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -29,7 +29,7 @@ from calibre.utils.date import dt_factory, qt_to_dt, isoformat from calibre.utils.pyparsing import ParseException from calibre.utils.search_query_parser import SearchQueryParser -class LibraryDelegate(QStyledItemDelegate): +class RatingDelegate(QStyledItemDelegate): COLOR = QColor("blue") SIZE = 16 PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) @@ -889,7 +889,7 @@ class BooksView(TableView): def __init__(self, parent, modelcls=BooksModel): TableView.__init__(self, parent) - self.rating_delegate = LibraryDelegate(self) + self.rating_delegate = RatingDelegate(self) self.timestamp_delegate = DateDelegate(self) self.pubdate_delegate = PubDateDelegate(self) self.tags_delegate = TagsDelegate(self) From ebb5c43abc83fd82dadcd5f97f54c0f4df1d7e38 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 1 May 2010 12:12:23 -0600 Subject: [PATCH 5/6] Library icon tooltip now shows the path to the currently displayed library --- src/calibre/gui2/library.py | 2 ++ src/calibre/gui2/ui.py | 3 +++ src/calibre/gui2/widgets.py | 12 +++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index c7e2991010..9f209a0066 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -243,6 +243,7 @@ class BooksModel(QAbstractTableModel): about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted') sorting_done = pyqtSignal(object, name='sortingDone') + database_changed = pyqtSignal(object, name='databaseChanged') orig_headers = { 'title' : _("Title"), @@ -300,6 +301,7 @@ class BooksModel(QAbstractTableModel): self.db = db self.custom_columns = self.db.custom_column_label_map self.read_config() + self.database_changed.emit(db) def refresh_ids(self, ids, current_row=-1): rows = self.db.refresh_ids(ids) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 12f620532e..f8bce11114 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -602,6 +602,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.db_images.reset() 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) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 586966d94f..c48ded1dc2 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -23,6 +23,7 @@ from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks.metadata.meta import metadata_from_filename from calibre.utils.config import prefs, XMLConfig from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator +from calibre.constants import filesystem_encoding history = XMLConfig('history') @@ -230,13 +231,22 @@ class LocationModel(QAbstractListModel): self.free = [-1, -1, -1] self.count = 0 self.highlight_row = 0 + self.library_tooltip = _('Click to see the books available on your computer') self.tooltips = [ - _('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 on storage card A in your reader'), _('Click to see the books on storage card B in your reader') ] + def database_changed(self, db): + lp = db.library_path + if not isinstance(lp, unicode): + lp = lp.decode(filesystem_encoding, 'replace') + self.tooltips[0] = self.library_tooltip + '\n\n' + \ + _('Books located at') + ' ' + lp + self.dataChanged.emit(self.index(0), self.index(0)) + def rowCount(self, *args): return 1 + len([i for i in self.free if i >= 0]) From 5a477915a981c29d000b22b70c486cc580c7d2b9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 1 May 2010 12:44:48 -0600 Subject: [PATCH 6/6] Make Tag Browser visible by default and freely resizable --- src/calibre/gui2/main.ui | 79 +++++++++++++++++++------------------- src/calibre/gui2/status.py | 15 -------- src/calibre/gui2/ui.py | 13 +++---- 3 files changed, 46 insertions(+), 61 deletions(-) diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index 4c9351909e..2021e1bc88 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -301,8 +301,11 @@ - - + + + Qt::Horizontal + + @@ -363,7 +366,7 @@ - &Restrict display to: + &Restrict to: search_restriction @@ -386,42 +389,40 @@ - - - - - - 100 - 10 - - - - true - - - true - - - false - - - QAbstractItemView::DragDrop - - - true - - - QAbstractItemView::SelectRows - - - false - - - false - - - - + + + + + 100 + 10 + + + + true + + + true + + + false + + + QAbstractItemView::DragDrop + + + true + + + QAbstractItemView::SelectRows + + + false + + + false + + + diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py index bdba768c5f..371efddb44 100644 --- a/src/calibre/gui2/status.py +++ b/src/calibre/gui2/status.py @@ -205,19 +205,6 @@ class CoverFlowButton(QToolButton): self.setDisabled(True) self.setToolTip(_('

Browsing books by their covers is disabled.
Import of pictureflow module failed:
')+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): @@ -227,9 +214,7 @@ class StatusBar(QStatusBar): self.notifier = get_notifier(systray) self.movie_button = MovieButton(jobs_dialog) self.cover_flow_button = CoverFlowButton() - self.tag_view_button = TagViewButton() self.addPermanentWidget(self.cover_flow_button) - self.addPermanentWidget(self.tag_view_button) self.addPermanentWidget(self.movie_button) self.book_info = BookInfoDisplay(self.clearMessage) self.book_info.setAcceptDrops(True) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index f8bce11114..6e0c5e333f 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -575,8 +575,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.connect(self.tags_view, SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), 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, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), self.tags_view.model().reinit) @@ -674,8 +672,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): if self.cover_flow is not None and dynamic.get('cover_flow_visible', False): self.status_bar.cover_flow_button.toggle() - if dynamic.get('tag_view_visible', False): - self.status_bar.tag_view_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) self._add_filesystem_book = Dispatcher(self.__add_filesystem_book) v = self.library_view @@ -2331,7 +2331,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.tag_view_button.setEnabled(True) self.status_bar.cover_flow_button.setEnabled(True) for action in list(self.delete_menu.actions())[1:]: action.setEnabled(True) @@ -2342,7 +2341,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.tag_view_button.setEnabled(False) self.status_bar.cover_flow_button.setEnabled(False) for action in list(self.delete_menu.actions())[1:]: action.setEnabled(False) @@ -2459,8 +2457,9 @@ 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('tag_view_visible', self.tags_view.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() if self.device_connected: self.save_device_view_settings()