From ae1f768c1a15be76cbb697829f8feae33f303005 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 8 Jul 2012 23:08:54 +0530 Subject: [PATCH] Eliminate MultiComplete* --- src/calibre/gui2/complete2.py | 375 +++++++++++++++++++++ src/calibre/gui2/convert/metadata.ui | 17 +- src/calibre/gui2/custom_column_widgets.py | 16 +- src/calibre/gui2/dialogs/add_empty_book.py | 4 +- src/calibre/gui2/dialogs/metadata_bulk.ui | 19 +- src/calibre/gui2/dialogs/search.ui | 15 +- src/calibre/gui2/languages.py | 6 +- src/calibre/gui2/library/delegates.py | 12 +- src/calibre/gui2/metadata/basic_widgets.py | 18 +- src/calibre/gui2/preferences/__init__.py | 37 +- src/calibre/gui2/preferences/look_feel.ui | 10 +- src/calibre/gui2/preferences/search.py | 3 +- src/calibre/gui2/preferences/search.ui | 12 +- 13 files changed, 458 insertions(+), 86 deletions(-) create mode 100644 src/calibre/gui2/complete2.py diff --git a/src/calibre/gui2/complete2.py b/src/calibre/gui2/complete2.py new file mode 100644 index 0000000000..c3f9d624ba --- /dev/null +++ b/src/calibre/gui2/complete2.py @@ -0,0 +1,375 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2012, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import weakref + +from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt, pyqtSignal, QObject, + QApplication, QCompleter, QListView, QPoint) + +from calibre.utils.icu import sort_key, primary_startswith +from calibre.gui2 import NONE +from calibre.gui2.widgets import EnComboBox, LineEditECM + +class CompleteModel(QAbstractListModel): # {{{ + + def __init__(self, parent=None): + QAbstractListModel.__init__(self, parent) + self.items = [] + self.sorting = QCompleter.UnsortedModel + self.all_items = self.current_items = () + self.current_prefix = '' + + def set_items(self, items): + items = [unicode(x.strip()) for x in items] + items = [x for x in items if x] + items = tuple(sorted(items, key=sort_key)) + self.all_items = self.current_items = items + self.reset() + + def set_completion_prefix(self, prefix): + old_prefix = self.current_prefix + self.current_prefix = prefix + if prefix == old_prefix: + return + if not prefix: + self.current_items = self.all_items + self.reset() + return + subset = prefix.startswith(old_prefix) + universe = self.current_items if subset else self.all_items + self.current_items = tuple(x for x in universe if primary_startswith(x, + prefix)) + self.reset() + + def rowCount(self, *args): + return len(self.current_items) + + def data(self, index, role): + if role == Qt.DisplayRole: + try: + return self.current_items[index.row()] + except IndexError: + pass + return NONE +# }}} + +class Completer(QListView): # {{{ + + item_selected = pyqtSignal(object) + relayout_needed = pyqtSignal() + + def __init__(self, completer_widget, max_visible_items=7): + QListView.__init__(self) + self.completer_widget = weakref.ref(completer_widget) + self.setWindowFlags(Qt.Popup) + self.max_visible_items = max_visible_items + self.setEditTriggers(self.NoEditTriggers) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setSelectionBehavior(self.SelectRows) + self.setSelectionMode(self.SingleSelection) + self.setAlternatingRowColors(True) + self.setModel(CompleteModel(self)) + self.setMouseTracking(True) + self.entered.connect(self.item_entered) + self.activated.connect(self.item_chosen) + self.pressed.connect(self.item_chosen) + self.eat_focus_out = True + self.installEventFilter(self) + + def item_chosen(self, index): + if not self.isVisible(): return + self.hide() + text = self.model().data(index, Qt.DisplayRole) + self.item_selected.emit(unicode(text)) + + def set_items(self, items): + self.model().set_items(items) + if self.isVisible(): + self.relayout_needed.emit() + + def set_completion_prefix(self, prefix): + self.model().set_completion_prefix(prefix) + if self.isVisible(): + self.relayout_needed.emit() + + def item_entered(self, idx): + self.setCurrentIndex(idx) + + def next_match(self, previous=False): + c = self.currentIndex() + if c.isValid(): + r = c.row() + else: + r = 0 + r = r + (-1 if previous else 1) + index = self.model().index(r % self.model().rowCount()) + self.setCurrentIndex(index) + + def popup(self): + p = self + m = p.model() + widget = self.completer_widget() + if widget is None: + return + screen = QApplication.desktop().availableGeometry(widget) + h = (p.sizeHintForRow(0) * min(self.max_visible_items, m.rowCount()) + + 3) + 3 + hsb = p.horizontalScrollBar() + if hsb and hsb.isVisible(): + h += hsb.sizeHint().height() + + rh = widget.height() + pos = widget.mapToGlobal(QPoint(0, widget.height() - 2)) + w = min(widget.width(), screen.width()) + + if (pos.x() + w) > (screen.x() + screen.width()): + pos.setX(screen.x() + screen.width() - w) + if pos.x() < screen.x(): + pos.setX(screen.x()) + + top = pos.y() - rh - screen.top() + 2 + bottom = screen.bottom() - pos.y() + h = max(h, p.minimumHeight()) + if h > bottom: + h = min(max(top, bottom), h) + + if top > bottom: + pos.setY(pos.y() - h - rh + 2) + + p.setGeometry(pos.x(), pos.y(), w, h) + + if (not self.currentIndex().isValid() and self.model().rowCount() > 0): + self.setCurrentIndex(self.model().index(0)) + + if not p.isVisible(): + p.show() + + def eventFilter(self, obj, e): + 'Redirect key presses from the popup to the widget' + widget = self.completer_widget() + if widget is None: + return False + etype = e.type() + if self.eat_focus_out and widget is obj and etype == e.FocusOut: + if self.isVisible(): + return True + if obj is not self: + return QObject.eventFilter(self, obj, e) + + if etype == e.KeyPress: + key = e.key() + if key == Qt.Key_Escape: + self.hide() + e.accept() + return True + if key == Qt.Key_F4 and e.modifiers() & Qt.AltModifier: + self.hide() + e.accept() + return True + if key in (Qt.Key_End, Qt.Key_Home, Qt.Key_Up, Qt.Key_Down, + Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Enter, Qt.Key_Return): + # Let the list view handle these keys + return False + if key in (Qt.Key_Tab, Qt.Key_Backtab): + self.next_match(previous=key == Qt.Key_Backtab) + e.accept() + return True + # Send to widget + self.eat_focus_out = False + widget.keyPressEvent(e) + self.eat_focus_out = True + if not widget.hasFocus(): + # Widget lost focus hide the popup + self.hide() + if e.isAccepted(): + return True + elif etype == e.MouseButtonPress: + if not self.underMouse(): + self.hide() + e.accept() + return True + return False +# }}} + + +class LineEdit(QLineEdit, LineEditECM): + ''' + A line edit that completes on multiple items separated by a + separator. Use the :meth:`update_items_cache` to set the list of + all possible completions. Separator can be controlled with the + :meth:`set_separator` and :meth:`set_space_before_sep` methods. + + A call to self.set_separator(None) will allow this widget to be used + to complete non multiple fields as well. + ''' + + def __init__(self, parent=None, completer_widget=None): + QLineEdit.__init__(self, parent) + + self.sep = ',' + self.space_before_sep = False + self.add_separator = True + self.original_cursor_pos = None + completer_widget = (self if completer_widget is None else + completer_widget) + + self.mcompleter = Completer(completer_widget) + self.mcompleter.item_selected.connect(self.completion_selected, + type=Qt.QueuedConnection) + self.mcompleter.relayout_needed.connect(self.relayout) + self.mcompleter.setFocusProxy(completer_widget) + completer_widget.installEventFilter(self.mcompleter) + self.textEdited.connect(self.text_edited) + self.no_popup = False + + # Interface {{{ + def update_items_cache(self, complete_items): + self.all_items = complete_items + + def set_separator(self, sep): + self.sep = sep + + def set_space_before_sep(self, space_before): + self.space_before_sep = space_before + + def set_add_separator(self, what): + self.add_separator = bool(what) + + @dynamic_property + def all_items(self): + def fget(self): + return self.mcompleter.model().all_items + def fset(self, items): + self.mcompleter.model().set_items(items) + return property(fget=fget, fset=fset) + + # }}} + + def complete(self, show_all=False): + if show_all: + self.mcompleter.set_completion_prefix('') + if not self.mcompleter.model().current_items: + self.mcompleter.hide() + return + self.mcompleter.popup() + + def relayout(self): + self.mcompleter.popup() + + def text_edited(self, *args): + if self.no_popup: return + self.update_completions() + self.complete() + + def update_completions(self): + ' Update the list of completions ' + self.original_cursor_pos = cpos = self.cursorPosition() + text = unicode(self.text()) + prefix = text[:cpos] + complete_prefix = prefix.lstrip() + if self.sep: + complete_prefix = prefix.split(self.sep)[-1].lstrip() + self.mcompleter.set_completion_prefix(complete_prefix) + + def get_completed_text(self, text): + 'Get completed text in before and after parts' + if self.sep is None: + return text, '' + else: + cursor_pos = self.original_cursor_pos + if cursor_pos is None: + cursor_pos = self.cursorPosition() + self.original_cursor_pos = None + # Split text + curtext = unicode(self.text()) + before_text = curtext[:cursor_pos] + after_text = curtext[cursor_pos:].rstrip() + # Remove the completion prefix from the before text + before_text = self.sep.join(before_text.split(self.sep)[:-1]).rstrip() + if before_text: + # Add the separator to the end of before_text + if self.space_before_sep: + before_text += ' ' + before_text += self.sep + ' ' + if self.add_separator or after_text: + # Add separator to the end of completed text + if self.space_before_sep: + text = text.rstrip() + ' ' + completed_text = text + self.sep + ' ' + else: + completed_text = text + return before_text + completed_text, after_text + + def completion_selected(self, text): + before_text, after_text = self.get_completed_text(unicode(text)) + self.setText(before_text + after_text) + self.setCursorPosition(len(before_text)) + +class EditWithComplete(EnComboBox): + + def __init__(self, *args): + EnComboBox.__init__(self, *args) + self.setLineEdit(LineEdit(self, completer_widget=self)) + self.setCompleter(None) + + # Interface {{{ + def showPopup(self): + self.lineEdit().complete(show_all=True) + + def update_items_cache(self, complete_items): + self.lineEdit().update_items_cache(complete_items) + + def set_separator(self, sep): + self.lineEdit().set_separator(sep) + + def set_space_before_sep(self, space_before): + self.lineEdit().set_space_before_sep(space_before) + + def set_add_separator(self, what): + self.lineEdit().set_add_separator(what) + + def show_initial_value(self, what): + what = unicode(what) if what else u'' + le = self.lineEdit() + self.setEditText(what) + le.selectAll() + + @dynamic_property + def all_items(self): + def fget(self): return self.lineEdit().all_items + def fset(self, val): self.lineEdit().all_items = val + return property(fget=fget, fset=fset) + + + # }}} + + def text(self): + return unicode(self.lineEdit().text()) + + def setText(self, val): + le = self.lineEdit() + le.no_popup = True + le.setText(val) + le.no_popup = False + + def setCursorPosition(self, *args): + self.lineEdit().setCursorPosition(*args) + +if __name__ == '__main__': + from PyQt4.Qt import QDialog, QVBoxLayout + app = QApplication([]) + d = QDialog() + d.setLayout(QVBoxLayout()) + le = EditWithComplete(d) + d.layout().addWidget(le) + items = ['one', 'otwo', 'othree', 'ooone', 'ootwo', + 'oothree', 'a1', 'a2',u'Edgas', u'Èdgar', u'Édgaq', u'Edgar', u'Édgar'] + le.update_items_cache(items) + le.show_initial_value('') + d.exec_() diff --git a/src/calibre/gui2/convert/metadata.ui b/src/calibre/gui2/convert/metadata.ui index 478f65e870..df9273c431 100644 --- a/src/calibre/gui2/convert/metadata.ui +++ b/src/calibre/gui2/convert/metadata.ui @@ -190,7 +190,7 @@ - + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. @@ -213,7 +213,7 @@ - + 10 @@ -248,14 +248,14 @@ - + true - + true @@ -277,14 +277,9 @@
widgets.h
- MultiCompleteComboBox + EditWithComplete QComboBox -
calibre/gui2/complete.h
-
- - MultiCompleteLineEdit - QLineEdit -
calibre/gui2/complete.h
+
calibre/gui2/complete2.h
ImageView diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index c9c8255076..2b45769185 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -13,7 +13,7 @@ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateTimeEdit, QPushButton, QMessageBox, QToolButton from calibre.utils.date import qt_to_dt, now -from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox +from calibre.gui2.complete2 import EditWithComplete from calibre.gui2.comments_editor import Editor as CommentsEditor from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog from calibre.gui2.dialogs.tag_editor import TagEditor @@ -235,7 +235,7 @@ class MultipleWidget(QWidget): layout.setSpacing(5) layout.setContentsMargins(0, 0, 0, 0) - self.tags_box = MultiCompleteLineEdit(parent) + self.tags_box = EditWithComplete(parent) layout.addWidget(self.tags_box, stretch=1000) self.editor_button = QToolButton(self) self.editor_button.setToolTip(_('Open Item Editor')) @@ -293,7 +293,7 @@ class Text(Base): w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) w.get_editor_button().clicked.connect(self.edit) else: - w = MultiCompleteComboBox(parent) + w = EditWithComplete(parent) w.set_separator(None) w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) w.setMinimumContentsLength(25) @@ -363,7 +363,7 @@ class Text(Base): class Series(Base): def setup_ui(self, parent): - w = MultiCompleteComboBox(parent) + w = EditWithComplete(parent) w.set_separator(None) w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) w.setMinimumContentsLength(25) @@ -807,7 +807,7 @@ class BulkDateTime(BulkBase): class BulkSeries(BulkBase): def setup_ui(self, parent): - self.make_widgets(parent, MultiCompleteComboBox) + self.make_widgets(parent, EditWithComplete) values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon) @@ -934,7 +934,7 @@ class RemoveTags(QWidget): layout.setSpacing(5) layout.setContentsMargins(0, 0, 0, 0) - self.tags_box = MultiCompleteLineEdit(parent) + self.tags_box = EditWithComplete(parent) self.tags_box.update_items_cache(values) layout.addWidget(self.tags_box, stretch=3) self.checkbox = QCheckBox(_('Remove all tags'), parent) @@ -956,7 +956,7 @@ class BulkText(BulkBase): values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) if self.col_metadata['is_multiple']: - self.make_widgets(parent, MultiCompleteLineEdit, + self.make_widgets(parent, EditWithComplete, extra_label_text=_('tags to add')) self.main_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) self.adding_widget = self.main_widget @@ -976,7 +976,7 @@ class BulkText(BulkBase): self.main_widget.set_add_separator( tweaks['authors_completer_append_separator']) else: - self.make_widgets(parent, MultiCompleteComboBox) + self.make_widgets(parent, EditWithComplete) self.main_widget.set_separator(None) self.main_widget.setSizeAdjustPolicy( self.main_widget.AdjustToMinimumContentsLengthWithIcon) diff --git a/src/calibre/gui2/dialogs/add_empty_book.py b/src/calibre/gui2/dialogs/add_empty_book.py index 218bd90483..98992f85bb 100644 --- a/src/calibre/gui2/dialogs/add_empty_book.py +++ b/src/calibre/gui2/dialogs/add_empty_book.py @@ -7,7 +7,7 @@ __license__ = 'GPL v3' from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \ QApplication, QSpinBox, QToolButton, QIcon from calibre.ebooks.metadata import string_to_authors -from calibre.gui2.complete import MultiCompleteComboBox +from calibre.gui2.complete2 import EditWithComplete from calibre.utils.config import tweaks class AddEmptyBookDialog(QDialog): @@ -32,7 +32,7 @@ class AddEmptyBookDialog(QDialog): self.author_label = QLabel(_('Set the author of the new books to:')) self._layout.addWidget(self.author_label, 2, 0, 1, 2) - self.authors_combo = MultiCompleteComboBox(self) + self.authors_combo = EditWithComplete(self) self.authors_combo.setSizeAdjustPolicy( self.authors_combo.AdjustToMinimumContentsLengthWithIcon) self.authors_combo.setEditable(True) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index c9467ffaf0..82c114df0c 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -76,7 +76,7 @@
- + true @@ -175,7 +175,7 @@ - + true @@ -195,7 +195,7 @@ - + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. @@ -229,7 +229,7 @@ - + Comma separated list of tags to remove from the books. @@ -262,7 +262,7 @@ - + 0 @@ -1181,14 +1181,9 @@ not multiple and the destination field is multiple
widgets.h
- MultiCompleteComboBox + EditWithComplete QComboBox -
calibre/gui2/complete.h
-
- - MultiCompleteLineEdit - QLineEdit -
calibre/gui2/complete.h
+
calibre/gui2/complete2.h
HistoryLineEdit diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui index 0a536010ef..a5c2c0a256 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -237,21 +237,21 @@
- + Enter an author's name. Only one author can be used. - + Enter a series name, without an index. Only one series name can be used. - + Enter tags separated by spaces @@ -327,14 +327,9 @@
widgets.h
- MultiCompleteLineEdit - QLineEdit -
calibre/gui2/complete.h
-
- - MultiCompleteComboBox + EditWithComplete QComboBox -
calibre/gui2/complete.h
+
calibre/gui2/complete2.h
diff --git a/src/calibre/gui2/languages.py b/src/calibre/gui2/languages.py index f067027097..f49fc863cb 100644 --- a/src/calibre/gui2/languages.py +++ b/src/calibre/gui2/languages.py @@ -7,14 +7,14 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from calibre.gui2.complete import MultiCompleteComboBox +from calibre.gui2.complete2 import EditWithComplete from calibre.utils.localization import lang_map from calibre.utils.icu import sort_key, lower -class LanguagesEdit(MultiCompleteComboBox): +class LanguagesEdit(EditWithComplete): def __init__(self, parent=None, db=None): - MultiCompleteComboBox.__init__(self, parent) + EditWithComplete.__init__(self, parent) self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon) self.setMinimumContentsLength(20) diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index 77c3152842..0c2d050860 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -14,7 +14,7 @@ from PyQt4.Qt import (Qt, QApplication, QStyle, QIcon, QDoubleSpinBox, from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog, rating_font from calibre.constants import iswindows from calibre.gui2.widgets import EnLineEdit -from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox +from calibre.gui2.complete2 import EditWithComplete from calibre.utils.date import now, format_date, qt_to_dt from calibre.utils.config import tweaks from calibre.utils.formatter import validation_formatter @@ -121,7 +121,7 @@ class TextDelegate(QStyledItemDelegate): # {{{ def createEditor(self, parent, option, index): if self.auto_complete_function: - editor = MultiCompleteComboBox(parent) + editor = EditWithComplete(parent) editor.set_separator(None) complete_items = [i[1] for i in self.auto_complete_function()] editor.update_items_cache(complete_items) @@ -132,7 +132,7 @@ class TextDelegate(QStyledItemDelegate): # {{{ return editor def setModelData(self, editor, model, index): - if isinstance(editor, MultiCompleteComboBox): + if isinstance(editor, EditWithComplete): val = editor.lineEdit().text() model.setData(index, QVariant(val), Qt.EditRole) else: @@ -153,7 +153,7 @@ class CompleteDelegate(QStyledItemDelegate): # {{{ def createEditor(self, parent, option, index): if self.db and hasattr(self.db, self.items_func_name): col = index.model().column_map[index.column()] - editor = MultiCompleteComboBox(parent) + editor = EditWithComplete(parent) editor.set_separator(self.sep) editor.set_space_before_sep(self.space_before_sep) if self.sep == '&': @@ -171,7 +171,7 @@ class CompleteDelegate(QStyledItemDelegate): # {{{ return editor def setModelData(self, editor, model, index): - if isinstance(editor, MultiCompleteComboBox): + if isinstance(editor, EditWithComplete): val = editor.lineEdit().text() model.setData(index, QVariant(val), Qt.EditRole) else: @@ -244,7 +244,7 @@ class CcTextDelegate(QStyledItemDelegate): # {{{ def createEditor(self, parent, option, index): m = index.model() col = m.column_map[index.column()] - editor = MultiCompleteLineEdit(parent) + editor = EditWithComplete(parent) editor.set_separator(None) complete_items = sorted(list(m.db.all_custom(label=m.db.field_metadata.key_to_label(col))), key=sort_key) diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index 0d9f4e6aa8..6e764e90d5 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -15,7 +15,6 @@ from PyQt4.Qt import (Qt, QDateTimeEdit, pyqtSignal, QMessageBox, QPushButton, QSpinBox, QLineEdit, QSizePolicy, QDialogButtonBox, QAction) from calibre.gui2.widgets import EnLineEdit, FormatList as _FormatList, ImageView -from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox from calibre.utils.icu import sort_key from calibre.utils.config import tweaks, prefs from calibre.ebooks.metadata import (title_sort, authors_to_string, @@ -23,6 +22,7 @@ from calibre.ebooks.metadata import (title_sort, authors_to_string, from calibre.ebooks.metadata.meta import get_metadata from calibre.gui2 import (file_icon_provider, UNDEFINED_QDATETIME, choose_files, error_dialog, choose_images) +from calibre.gui2.complete2 import EditWithComplete from calibre.utils.date import (local_tz, qt_to_dt, as_local_time, UNDEFINED_DATE) from calibre import strftime @@ -204,7 +204,7 @@ class TitleSortEdit(TitleEdit): # }}} # Authors {{{ -class AuthorsEdit(MultiCompleteComboBox): +class AuthorsEdit(EditWithComplete): TOOLTIP = '' LABEL = _('&Author(s):') @@ -212,7 +212,7 @@ class AuthorsEdit(MultiCompleteComboBox): def __init__(self, parent, manage_authors): self.dialog = parent self.books_to_refresh = set([]) - MultiCompleteComboBox.__init__(self, parent) + EditWithComplete.__init__(self, parent) self.setToolTip(self.TOOLTIP) self.setWhatsThis(self.TOOLTIP) self.setEditable(True) @@ -443,13 +443,13 @@ class AuthorSortEdit(EnLineEdit): # }}} # Series {{{ -class SeriesEdit(MultiCompleteComboBox): +class SeriesEdit(EditWithComplete): TOOLTIP = _('List of known series. You can add new series.') LABEL = _('&Series:') def __init__(self, parent): - MultiCompleteComboBox.__init__(self, parent) + EditWithComplete.__init__(self, parent) self.set_separator(None) self.dialog = parent self.setSizeAdjustPolicy( @@ -1086,14 +1086,14 @@ class RatingEdit(QSpinBox): # {{{ # }}} -class TagsEdit(MultiCompleteLineEdit): # {{{ +class TagsEdit(EditWithComplete): # {{{ LABEL = _('Ta&gs:') TOOLTIP = '

'+_('Tags categorize the book. This is particularly ' 'useful while searching.

They can be any words ' 'or phrases, separated by commas.') def __init__(self, parent): - MultiCompleteLineEdit.__init__(self, parent) + EditWithComplete.__init__(self, parent) self.books_to_refresh = set([]) self.setToolTip(self.TOOLTIP) self.setWhatsThis(self.TOOLTIP) @@ -1327,11 +1327,11 @@ class ISBNDialog(QDialog) : # {{{ # }}} -class PublisherEdit(MultiCompleteComboBox): # {{{ +class PublisherEdit(EditWithComplete): # {{{ LABEL = _('&Publisher:') def __init__(self, parent): - MultiCompleteComboBox.__init__(self, parent) + EditWithComplete.__init__(self, parent) self.set_separator(None) self.setSizeAdjustPolicy( self.AdjustToMinimumContentsLengthWithIcon) diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index 5b0a05ba40..d724e49b46 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -13,6 +13,7 @@ from PyQt4.Qt import (QWidget, pyqtSignal, QCheckBox, QAbstractSpinBox, from calibre.customize.ui import preferences_plugins from calibre.utils.config import ConfigProxy +from calibre.gui2.complete2 import EditWithComplete class AbortCommit(Exception): pass @@ -133,11 +134,15 @@ class Setting(object): def initialize(self): self.gui_obj.blockSignals(True) if self.datatype == 'choice': - self.gui_obj.clear() - for x in self.choices: - if isinstance(x, basestring): - x = (x, x) - self.gui_obj.addItem(x[0], QVariant(x[1])) + choices = self.choices or [] + if isinstance(self.gui_obj, EditWithComplete): + self.gui_obj.all_items = choices + else: + self.gui_obj.clear() + for x in choices: + if isinstance(x, basestring): + x = (x, x) + self.gui_obj.addItem(x[0], QVariant(x[1])) self.set_gui_val(self.get_config_val(default=False)) self.gui_obj.blockSignals(False) self.initial_value = self.get_gui_val() @@ -171,11 +176,14 @@ class Setting(object): elif self.datatype == 'string': self.gui_obj.setText(val if val else '') elif self.datatype == 'choice': - idx = self.gui_obj.findData(QVariant(val), role=Qt.UserRole, - flags=self.CHOICES_SEARCH_FLAGS) - if idx == -1: - idx = 0 - self.gui_obj.setCurrentIndex(idx) + if isinstance(self.gui_obj, EditWithComplete): + self.gui_obj.setText(val) + else: + idx = self.gui_obj.findData(QVariant(val), role=Qt.UserRole, + flags=self.CHOICES_SEARCH_FLAGS) + if idx == -1: + idx = 0 + self.gui_obj.setCurrentIndex(idx) def get_gui_val(self): if self.datatype == 'bool': @@ -187,9 +195,12 @@ class Setting(object): if self.empty_string_is_None and not val: val = None elif self.datatype == 'choice': - idx = self.gui_obj.currentIndex() - if idx < 0: idx = 0 - val = unicode(self.gui_obj.itemData(idx).toString()) + if isinstance(self.gui_obj, EditWithComplete): + val = unicode(self.gui_obj.text()) + else: + idx = self.gui_obj.currentIndex() + if idx < 0: idx = 0 + val = unicode(self.gui_obj.itemData(idx).toString()) return val class CommaSeparatedList(Setting): diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index 43420a900e..bb60f3db2a 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -320,7 +320,7 @@ Manage Authors. You can use the values {author} and - + A comma-separated list of categories in which items containing periods are displayed in the tag browser trees. For example, if @@ -397,7 +397,7 @@ up into subcategories. If the partition method is set to disable, this value is - + A comma-separated list of categories that are not to be partitioned even if the number of items is larger than @@ -506,9 +506,9 @@ a few top-level elements. - MultiCompleteLineEdit - QLineEdit -

calibre/gui2/complete.h
+ EditWithComplete + QComboBox +
calibre/gui2/complete2.h
diff --git a/src/calibre/gui2/preferences/search.py b/src/calibre/gui2/preferences/search.py index 7542f4afdd..b5998022e7 100644 --- a/src/calibre/gui2/preferences/search.py +++ b/src/calibre/gui2/preferences/search.py @@ -98,7 +98,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): db.prefs.set('grouped_search_make_user_categories', []) r('grouped_search_make_user_categories', db.prefs, setting=CommaSeparatedList) self.muc_changed = False - self.opt_grouped_search_make_user_categories.editingFinished.connect( + self.opt_grouped_search_make_user_categories.lineEdit().editingFinished.connect( self.muc_box_changed) def set_similar_fields(self, initial=False): @@ -184,6 +184,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.opt_grouped_search_make_user_categories.update_items_cache(terms) self.gst_names.blockSignals(True) self.gst_names.clear() + print (1111, self.gst_names) self.gst_names.addItem('', '') for t in terms: self.gst_names.addItem(t, t) diff --git a/src/calibre/gui2/preferences/search.ui b/src/calibre/gui2/preferences/search.ui index 8e8ea35199..28735f09e0 100644 --- a/src/calibre/gui2/preferences/search.ui +++ b/src/calibre/gui2/preferences/search.ui @@ -69,7 +69,7 @@
- + @@ -134,7 +134,7 @@ a search term by changing the value box then pressing Save. - + @@ -173,7 +173,7 @@ of a search term by changing the value box then pressing Save. - + Enter the names of any grouped search terms you wish to be shown as user categories @@ -301,9 +301,9 @@ to be shown as user categories - MultiCompleteLineEdit - QLineEdit -
calibre/gui2/complete.h
+ EditWithComplete + QComboBox +
calibre/gui2/complete2.h