From 554c937a76a8117dda7f8abd7c2502dd671403de Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 12 Apr 2013 11:34:58 +0200 Subject: [PATCH] 1) Add "Edit virtual library". This also solves the problem of no longer having tooltips because the user can "edit" to see the VL search 2) Remove remaining tooltips 3) Add "current search" as a VL 4) Limit VL and restriction names to 40 characters --- src/calibre/gui2/search_restriction_mixin.py | 161 +++++++++++++------ 1 file changed, 109 insertions(+), 52 deletions(-) diff --git a/src/calibre/gui2/search_restriction_mixin.py b/src/calibre/gui2/search_restriction_mixin.py index a09927be5d..702f67f69d 100644 --- a/src/calibre/gui2/search_restriction_mixin.py +++ b/src/calibre/gui2/search_restriction_mixin.py @@ -44,15 +44,46 @@ class SelectNames(QDialog): # {{{ yield unicode(item.data(Qt.DisplayRole).toString()) # }}} +MAX_VIRTUAL_LIBRARY_NAME_LENGTH = 40 + +def _build_full_search_string(gui): + search_templates = ( + '', + '{cl}', + '{cr}', + '(({cl}) and ({cr}))', + '{sb}', + '(({cl}) and ({sb}))', + '(({cr}) and ({sb}))', + '(({cl}) and ({cr}) and ({sb}))' + ) + + sb = gui.search.current_text + db = gui.current_db + cr = db.data.get_search_restriction() + cl = db.data.get_base_restriction() + dex = 0 + if sb: + dex += 4 + if cr: + dex += 2 + if cl: + dex += 1 + template = search_templates[dex] + return template.format(cl=cl, cr=cr, sb=sb).strip() + class CreateVirtualLibrary(QDialog): # {{{ - def __init__(self, gui, existing_names): + def __init__(self, gui, existing_names, editing=None): QDialog.__init__(self, gui) self.gui = gui self.existing_names = existing_names - self.setWindowTitle(_('Create virtual library')) + if editing: + self.setWindowTitle(_('Edit virtual library')) + else: + self.setWindowTitle(_('Create virtual library')) self.setWindowIcon(QIcon(I('lt.png'))) gl = QGridLayout() @@ -60,15 +91,19 @@ class CreateVirtualLibrary(QDialog): # {{{ self.la1 = la1 = QLabel(_('Virtual library &name:')) gl.addWidget(la1, 0, 0) self.vl_name = QLineEdit() + self.vl_name.setMaxLength(MAX_VIRTUAL_LIBRARY_NAME_LENGTH) la1.setBuddy(self.vl_name) gl.addWidget(self.vl_name, 0, 1) + self.editing = editing + if editing: + self.vl_name.setText(editing) self.la2 = la2 = QLabel(_('&Search expression:')) gl.addWidget(la2, 1, 0) self.vl_text = QLineEdit() la2.setBuddy(self.vl_text) gl.addWidget(self.vl_text, 1, 1) - self.vl_text.setText(self.build_full_search_string()) + self.vl_text.setText(_build_full_search_string(self.gui)) self.sl = sl = QLabel('

'+_('Create a virtual library based on: ')+ ('{0}, ' @@ -102,6 +137,11 @@ class CreateVirtualLibrary(QDialog): # {{{ bb.rejected.connect(self.reject) gl.addWidget(bb, 4, 0, 1, 0) + if editing: + db = self.gui.current_db + virt_libs = db.prefs.get('virtual_libraries', {}) + self.vl_text.setText(virt_libs.get(editing, '')) + self.resize(self.sizeHint()+QSize(150, 25)) def link_activated(self, url): @@ -116,48 +156,28 @@ class CreateVirtualLibrary(QDialog): # {{{ self.vl_name.setText(d.names.next()) self.vl_text.setText(' or '.join(search)) - def build_full_search_string(self): - search_templates = ( - '', - '{cl}', - '{cr}', - '(({cl}) and ({cr}))', - '{sb}', - '(({cl}) and ({sb}))', - '(({cr}) and ({sb}))', - '(({cl}) and ({cr}) and ({sb}))' - ) - - sb = self.gui.search.current_text - db = self.gui.current_db - cr = db.data.get_search_restriction() - cl = db.data.get_base_restriction() - dex = 0 - if sb: - dex += 4 - if cr: - dex += 2 - if cl: - dex += 1 - template = search_templates[dex] - return template.format(cl=cl, cr=cr, sb=sb) - def accept(self): - n = unicode(self.vl_name.text()) + n = unicode(self.vl_name.text()).strip() if not n: error_dialog(self.gui, _('No name'), _('You must provide a name for the new virtual library'), show=True) return - if n in self.existing_names: + if n.startswith('*'): + error_dialog(self.gui, _('Invalid name'), + _('A virtual library name cannot begin with "*"'), + show=True) + return + + if n in self.existing_names and n != self.editing: if question_dialog(self.gui, _('Name already in use'), _('That name is already in use. Do you want to replace it ' 'with the new search?'), default_yes=False) == self.Rejected: return - v = unicode(self.vl_text.text()) + v = unicode(self.vl_text.text()).strip() if not v: error_dialog(self.gui, _('No search string'), _('You must provide a search to define the new virtual library'), @@ -192,6 +212,8 @@ class SearchRestrictionMixin(object): def __init__(self): self.checked = QIcon(I('ok.png')) self.empty = QIcon() + self.search_based_vl_name = None + self.search_based_vl = None self.virtual_library_menu = QMenu() @@ -211,32 +233,31 @@ class SearchRestrictionMixin(object): virt_libs[name] = search db.prefs.set('virtual_libraries', virt_libs) - def do_create(self): + def do_create_edit(self, editing=None): db = self.library_view.model().db virt_libs = db.prefs.get('virtual_libraries', {}) - cd = CreateVirtualLibrary(self, virt_libs.keys()) + cd = CreateVirtualLibrary(self, virt_libs.keys(), editing=editing) if cd.exec_() == cd.Accepted: + if editing: + self._remove_vl(editing, reapply=False) self.add_virtual_library(db, cd.library_name, cd.library_search) self.apply_virtual_library(cd.library_name) - def do_remove(self): - db = self.library_view.model().db - db.data.set_base_restriction("") - db.data.set_base_restriction_name("") - self._apply_search_restriction(db.data.get_search_restriction(), - db.data.get_search_restriction_name()) - def virtual_library_clicked(self): m = self.virtual_library_menu m.clear() a = m.addAction(_('Create Virtual Library')) - a.triggered.connect(self.do_create) - a.setToolTip(_('Create a new virtual library from the results of a search')) + a.triggered.connect(partial(self.do_create_edit, editing=None)) + + self.edit_menu = a = QMenu() + a.setTitle(_('Edit Virtual Library')) + a.aboutToShow.connect(partial(self.build_virtual_library_list, remove=False)) + m.addMenu(a) self.rm_menu = a = QMenu() a.setTitle(_('Remove Virtual Library')) - a.aboutToShow.connect(self.build_virtual_library_list) + a.aboutToShow.connect(partial(self.build_virtual_library_list, remove=True)) m.addMenu(a) m.addSeparator() @@ -259,10 +280,20 @@ class SearchRestrictionMixin(object): a = m.addAction(self.empty, self.no_restriction) a.triggered.connect(partial(self.apply_virtual_library, library='')) + a = m.addAction(self.empty, _('*current search')) + a.triggered.connect(partial(self.apply_virtual_library, library='*')) + + if self.search_based_vl_name: + a = m.addAction( + self.checked if db.data.get_base_restriction_name().startswith('*') + else self.empty, + self.search_based_vl_name) + a.triggered.connect(partial(self.apply_virtual_library, + library=self.search_based_vl_name)) + virt_libs = db.prefs.get('virtual_libraries', {}) for vl in sorted(virt_libs.keys(), key=sort_key): a = m.addAction(self.checked if vl == current_lib else self.empty, vl) - a.setToolTip(virt_libs[vl]) a.triggered.connect(partial(self.apply_virtual_library, library=vl)) p = QPoint(0, self.virtual_library.height()) @@ -274,22 +305,41 @@ class SearchRestrictionMixin(object): if not library: db.data.set_base_restriction('') db.data.set_base_restriction_name('') + elif library == '*': + if not _build_full_search_string(self): + error_dialog(self, _('No search'), + _('There is no current search to use'), show=True) + return + + self.search_based_vl = _build_full_search_string(self) + db.data.set_base_restriction(self.search_based_vl) + self.search_based_vl_name = self._trim_restriction_name( + '*' + self.search_based_vl) + db.data.set_base_restriction_name(self.search_based_vl_name) + elif library == self.search_based_vl_name: + db.data.set_base_restriction(self.search_based_vl) + db.data.set_base_restriction_name(self.search_based_vl_name) elif library in virt_libs: db.data.set_base_restriction(virt_libs[library]) db.data.set_base_restriction_name(library) self._apply_search_restriction(db.data.get_search_restriction(), db.data.get_search_restriction_name()) - def build_virtual_library_list(self): + def build_virtual_library_list(self, remove=False): db = self.library_view.model().db virt_libs = db.prefs.get('virtual_libraries', {}) - m = self.rm_menu + if remove: + m = self.rm_menu + else: + m = self.edit_menu m.clear() def add_action(name, search): a = m.addAction(name) - a.setToolTip(search) - a.triggered.connect(partial(self.remove_vl_triggered, name=name)) + if remove: + a.triggered.connect(partial(self.remove_vl_triggered, name=name)) + else: + a.triggered.connect(partial(self.do_create_edit, editing=name)) for n in sorted(virt_libs.keys(), key=sort_key): add_action(n, virt_libs[n]) @@ -300,13 +350,19 @@ class SearchRestrictionMixin(object): 'the virtual library {0}').format(name), default_yes=False): return + self._remove_vl(name, reapply=True) + + def _remove_vl(self, name, reapply=True): db = self.library_view.model().db virt_libs = db.prefs.get('virtual_libraries', {}) virt_libs.pop(name, None) db.prefs.set('virtual_libraries', virt_libs) - if db.data.get_base_restriction_name() == name: + if reapply and db.data.get_base_restriction_name() == name: self.apply_virtual_library('') + def _trim_restriction_name(self, name): + return name[0:MAX_VIRTUAL_LIBRARY_NAME_LENGTH].strip() + def build_search_restriction_list(self): m = self.ar_menu m.clear() @@ -324,6 +380,7 @@ class SearchRestrictionMixin(object): def add_action(txt, index): self.search_restriction.addItem(txt) + txt = self._trim_restriction_name(txt) if txt == current_restriction: a = m.addAction(self.checked, txt if txt else self.no_restriction) else: @@ -332,7 +389,7 @@ class SearchRestrictionMixin(object): action=a, index=index)) add_action('', 0) - add_action('*current search', 1) + add_action(_('*current search'), 1) dex = 2 if current_restriction_text: add_action(current_restriction_text, 2) @@ -372,7 +429,7 @@ class SearchRestrictionMixin(object): else: self.search_restriction.insertItem(2, s) self.search_restriction.setCurrentIndex(2) - self._apply_search_restriction(search, s) + self._apply_search_restriction(search, self._trim_restriction_name(s)) def apply_search_restriction(self, i): if i == 1: