From 6364ea1d39d50b2cafd2505b31661f09e8562c8a Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 8 Jun 2010 08:16:46 +0100 Subject: [PATCH] Fix bugs #5751 and #5752. Also distinguish between the delete warning dialogs for the library and the device --- .../dialogs/config/create_custom_column.py | 5 +- src/calibre/gui2/tag_view.py | 58 ++++++++++++------- src/calibre/gui2/ui.py | 4 +- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/calibre/gui2/dialogs/config/create_custom_column.py b/src/calibre/gui2/dialogs/config/create_custom_column.py index 3d5cb8ba53..a66b7b6642 100644 --- a/src/calibre/gui2/dialogs/config/create_custom_column.py +++ b/src/calibre/gui2/dialogs/config/create_custom_column.py @@ -3,6 +3,7 @@ __copyright__ = '2010, Kovid Goyal ' '''Dialog to create a new custom column''' +import re from functools import partial from PyQt4.QtCore import SIGNAL @@ -94,8 +95,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): col = unicode(self.column_name_box.text()).lower() if not col: return self.simple_error('', _('No lookup name was provided')) - if not col.isalnum() or not col[0].isalpha(): - return self.simple_error('', _('The label must contain only letters and digits, and start with a letter')) + if re.match('^\w*$', col) is None or not col[0].isalpha(): + return self.simple_error('', _('The label must contain only letters, digits and underscores, and start with a letter')) col_heading = unicode(self.column_heading_box.text()) col_type = self.column_types[self.column_type_box.currentIndex()]['datatype'] if col_type == '*text': diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 24e83376aa..95b83b407c 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -63,8 +63,7 @@ class TagsView(QTreeView): # {{{ def sort_changed(self, state): config.set('sort_by_popularity', state == Qt.Checked) - self.model().refresh() - # self.search_restriction_set() + self.recount() def set_search_restriction(self, s): if s: @@ -197,7 +196,9 @@ class TagsView(QTreeView): # {{{ ci = self.indexAt(QPoint(10, 10)) path = self.model().path_for_index(ci) if self.is_visible(ci) else None try: - self.model().refresh() + if not self.model().refresh(): # categories changed! + self.set_new_model() + path = None except: #Database connection could be closed if an integrity check is happening pass if path: @@ -210,10 +211,16 @@ class TagsView(QTreeView): # {{{ # gone, or if columns have been hidden or restored, we must rebuild the # model. Reason: it is much easier than reconstructing the browser tree. def set_new_model(self): - self._model = TagsModel(self.db, parent=self, - hidden_categories=self.hidden_categories, - search_restriction=self.search_restriction) - self.setModel(self._model) + try: + self._model = TagsModel(self.db, parent=self, + hidden_categories=self.hidden_categories, + search_restriction=self.search_restriction) + self.setModel(self._model) + except: + # The DB must be gone. Set the model to None and hope that someone + # will call set_database later. I don't know if this in fact works + self._model = None + self.setModel(None) # }}} class TagTreeItem(object): # {{{ @@ -323,18 +330,9 @@ class TagsModel(QAbstractItemModel): # {{{ self.tags_view = parent self.hidden_categories = hidden_categories self.search_restriction = search_restriction + self.row_map = [] - # Reconstruct the user categories, putting them into metadata - tb_cats = self.db.field_metadata - for k in tb_cats.keys(): - if tb_cats[k]['kind'] in ['user', 'search']: - del tb_cats[k] - for user_cat in sorted(prefs['user_categories'].keys()): - cat_name = user_cat+':' # add the ':' to avoid name collision - tb_cats.add_user_category(label=cat_name, name=user_cat) - if len(saved_searches.names()): - tb_cats.add_search_category(label='search', name=_('Searches')) - + # get_node_tree cannot return None here, because row_map is empty data = self.get_node_tree(config['sort_by_popularity']) self.root_item = TagTreeItem() for i, r in enumerate(self.row_map): @@ -355,9 +353,22 @@ class TagsModel(QAbstractItemModel): # {{{ self.search_restriction = s def get_node_tree(self, sort): + old_row_map = self.row_map[:] self.row_map = [] self.categories = [] + # Reconstruct the user categories, putting them into metadata + tb_cats = self.db.field_metadata + for k in tb_cats.keys(): + if tb_cats[k]['kind'] in ['user', 'search']: + del tb_cats[k] + for user_cat in sorted(prefs['user_categories'].keys()): + cat_name = user_cat+':' # add the ':' to avoid name collision + tb_cats.add_user_category(label=cat_name, name=user_cat) + if len(saved_searches.names()): + tb_cats.add_search_category(label='search', name=_('Searches')) + + # Now get the categories if self.search_restriction: data = self.db.get_categories(sort_on_count=sort, icon_map=self.category_icon_map, @@ -367,13 +378,19 @@ class TagsModel(QAbstractItemModel): # {{{ tb_categories = self.db.field_metadata for category in tb_categories: - if category in data: # They should always be there, but ... + if category in data: # The search category can come and go self.row_map.append(category) self.categories.append(tb_categories[category]['name']) + if len(old_row_map) != 0 and len(old_row_map) != len(self.row_map): + # A category has been added or removed. We must force a rebuild of + # the model + return None return data def refresh(self): data = self.get_node_tree(config['sort_by_popularity']) # get category data + if data is None: + return False row_index = -1 for i, r in enumerate(self.row_map): if self.hidden_categories and self.categories[i] in self.hidden_categories: @@ -395,6 +412,7 @@ class TagsModel(QAbstractItemModel): # {{{ tag.state = state_map.get(tag.name, 0) t = TagTreeItem(parent=category, data=tag, icon_map=self.icon_state_map) self.endInsertRows() + return True def columnCount(self, parent): return 1 @@ -439,7 +457,7 @@ class TagsModel(QAbstractItemModel): # {{{ label=self.db.field_metadata[key]['label']) self.tags_view.tag_item_renamed.emit() item.tag.name = val - self.refresh() + self.refresh() # Should work, because no categories can have disappeared return True def headerData(self, *args): diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 01f0cf6271..7d258608d0 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -854,7 +854,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): r = unicode(r) if r is not None and r != '': self.restriction_in_effect = True - restriction = "search:%s"%(r) + restriction = 'search:"%s"'%(r) else: self.restriction_in_effect = False restriction = '' @@ -1557,7 +1557,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): if not confirm('

'+_('The selected books will be ' 'permanently deleted ' 'from your device. Are you sure?') - +'

', 'library_delete_books', self): + +'

', 'device_delete_books', self): return if self.stack.currentIndex() == 1: view = self.memory_view