Eliminate MultiComplete*

This commit is contained in:
Kovid Goyal 2012-07-08 23:08:54 +05:30
parent 4bed21a52f
commit ae1f768c1a
13 changed files with 458 additions and 86 deletions

View File

@ -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 <kovid@kovidgoyal.net>'
__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_()

View File

@ -190,7 +190,7 @@
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="MultiCompleteLineEdit" name="tags"> <widget class="EditWithComplete" name="tags">
<property name="toolTip"> <property name="toolTip">
<string>Tags categorize the book. This is particularly useful while searching. &lt;br&gt;&lt;br&gt;They can be any words or phrases, separated by commas.</string> <string>Tags categorize the book. This is particularly useful while searching. &lt;br&gt;&lt;br&gt;They can be any words or phrases, separated by commas.</string>
</property> </property>
@ -213,7 +213,7 @@
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="5" column="1">
<widget class="MultiCompleteComboBox" name="series"> <widget class="EditWithComplete" name="series">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>10</horstretch> <horstretch>10</horstretch>
@ -248,14 +248,14 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="MultiCompleteComboBox" name="publisher"> <widget class="EditWithComplete" name="publisher">
<property name="editable"> <property name="editable">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="MultiCompleteComboBox" name="author"> <widget class="EditWithComplete" name="author">
<property name="editable"> <property name="editable">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -277,14 +277,9 @@
<header>widgets.h</header> <header>widgets.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>MultiCompleteComboBox</class> <class>EditWithComplete</class>
<extends>QComboBox</extends> <extends>QComboBox</extends>
<header>calibre/gui2/complete.h</header> <header>calibre/gui2/complete2.h</header>
</customwidget>
<customwidget>
<class>MultiCompleteLineEdit</class>
<extends>QLineEdit</extends>
<header>calibre/gui2/complete.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>ImageView</class> <class>ImageView</class>

View File

@ -13,7 +13,7 @@ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateTimeEdit,
QPushButton, QMessageBox, QToolButton QPushButton, QMessageBox, QToolButton
from calibre.utils.date import qt_to_dt, now 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.comments_editor import Editor as CommentsEditor
from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog
from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.dialogs.tag_editor import TagEditor
@ -235,7 +235,7 @@ class MultipleWidget(QWidget):
layout.setSpacing(5) layout.setSpacing(5)
layout.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(0, 0, 0, 0)
self.tags_box = MultiCompleteLineEdit(parent) self.tags_box = EditWithComplete(parent)
layout.addWidget(self.tags_box, stretch=1000) layout.addWidget(self.tags_box, stretch=1000)
self.editor_button = QToolButton(self) self.editor_button = QToolButton(self)
self.editor_button.setToolTip(_('Open Item Editor')) self.editor_button.setToolTip(_('Open Item Editor'))
@ -293,7 +293,7 @@ class Text(Base):
w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
w.get_editor_button().clicked.connect(self.edit) w.get_editor_button().clicked.connect(self.edit)
else: else:
w = MultiCompleteComboBox(parent) w = EditWithComplete(parent)
w.set_separator(None) w.set_separator(None)
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
w.setMinimumContentsLength(25) w.setMinimumContentsLength(25)
@ -363,7 +363,7 @@ class Text(Base):
class Series(Base): class Series(Base):
def setup_ui(self, parent): def setup_ui(self, parent):
w = MultiCompleteComboBox(parent) w = EditWithComplete(parent)
w.set_separator(None) w.set_separator(None)
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
w.setMinimumContentsLength(25) w.setMinimumContentsLength(25)
@ -807,7 +807,7 @@ class BulkDateTime(BulkBase):
class BulkSeries(BulkBase): class BulkSeries(BulkBase):
def setup_ui(self, parent): 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 = self.all_values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key) values.sort(key=sort_key)
self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon) self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon)
@ -934,7 +934,7 @@ class RemoveTags(QWidget):
layout.setSpacing(5) layout.setSpacing(5)
layout.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(0, 0, 0, 0)
self.tags_box = MultiCompleteLineEdit(parent) self.tags_box = EditWithComplete(parent)
self.tags_box.update_items_cache(values) self.tags_box.update_items_cache(values)
layout.addWidget(self.tags_box, stretch=3) layout.addWidget(self.tags_box, stretch=3)
self.checkbox = QCheckBox(_('Remove all tags'), parent) 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 = self.all_values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key) values.sort(key=sort_key)
if self.col_metadata['is_multiple']: if self.col_metadata['is_multiple']:
self.make_widgets(parent, MultiCompleteLineEdit, self.make_widgets(parent, EditWithComplete,
extra_label_text=_('tags to add')) extra_label_text=_('tags to add'))
self.main_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) self.main_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
self.adding_widget = self.main_widget self.adding_widget = self.main_widget
@ -976,7 +976,7 @@ class BulkText(BulkBase):
self.main_widget.set_add_separator( self.main_widget.set_add_separator(
tweaks['authors_completer_append_separator']) tweaks['authors_completer_append_separator'])
else: else:
self.make_widgets(parent, MultiCompleteComboBox) self.make_widgets(parent, EditWithComplete)
self.main_widget.set_separator(None) self.main_widget.set_separator(None)
self.main_widget.setSizeAdjustPolicy( self.main_widget.setSizeAdjustPolicy(
self.main_widget.AdjustToMinimumContentsLengthWithIcon) self.main_widget.AdjustToMinimumContentsLengthWithIcon)

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \ from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \
QApplication, QSpinBox, QToolButton, QIcon QApplication, QSpinBox, QToolButton, QIcon
from calibre.ebooks.metadata import string_to_authors 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 from calibre.utils.config import tweaks
class AddEmptyBookDialog(QDialog): class AddEmptyBookDialog(QDialog):
@ -32,7 +32,7 @@ class AddEmptyBookDialog(QDialog):
self.author_label = QLabel(_('Set the author of the new books to:')) self.author_label = QLabel(_('Set the author of the new books to:'))
self._layout.addWidget(self.author_label, 2, 0, 1, 2) 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.setSizeAdjustPolicy(
self.authors_combo.AdjustToMinimumContentsLengthWithIcon) self.authors_combo.AdjustToMinimumContentsLengthWithIcon)
self.authors_combo.setEditable(True) self.authors_combo.setEditable(True)

View File

@ -76,7 +76,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="MultiCompleteComboBox" name="authors"> <widget class="EditWithComplete" name="authors">
<property name="editable"> <property name="editable">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -175,7 +175,7 @@
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="MultiCompleteComboBox" name="publisher"> <widget class="EditWithComplete" name="publisher">
<property name="editable"> <property name="editable">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -195,7 +195,7 @@
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="5" column="1">
<widget class="MultiCompleteLineEdit" name="tags"> <widget class="EditWithComplete" name="tags">
<property name="toolTip"> <property name="toolTip">
<string>Tags categorize the book. This is particularly useful while searching. &lt;br&gt;&lt;br&gt;They can be any words or phrases, separated by commas.</string> <string>Tags categorize the book. This is particularly useful while searching. &lt;br&gt;&lt;br&gt;They can be any words or phrases, separated by commas.</string>
</property> </property>
@ -229,7 +229,7 @@
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="6" column="1">
<widget class="MultiCompleteLineEdit" name="remove_tags"> <widget class="EditWithComplete" name="remove_tags">
<property name="toolTip"> <property name="toolTip">
<string>Comma separated list of tags to remove from the books. </string> <string>Comma separated list of tags to remove from the books. </string>
</property> </property>
@ -262,7 +262,7 @@
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="7" column="1">
<widget class="MultiCompleteComboBox" name="series"> <widget class="EditWithComplete" name="series">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -1181,14 +1181,9 @@ not multiple and the destination field is multiple</string>
<header>widgets.h</header> <header>widgets.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>MultiCompleteComboBox</class> <class>EditWithComplete</class>
<extends>QComboBox</extends> <extends>QComboBox</extends>
<header>calibre/gui2/complete.h</header> <header>calibre/gui2/complete2.h</header>
</customwidget>
<customwidget>
<class>MultiCompleteLineEdit</class>
<extends>QLineEdit</extends>
<header>calibre/gui2/complete.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>HistoryLineEdit</class> <class>HistoryLineEdit</class>

View File

@ -237,21 +237,21 @@
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="MultiCompleteComboBox" name="authors_box"> <widget class="EditWithComplete" name="authors_box">
<property name="toolTip"> <property name="toolTip">
<string>Enter an author's name. Only one author can be used.</string> <string>Enter an author's name. Only one author can be used.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="MultiCompleteComboBox" name="series_box"> <widget class="EditWithComplete" name="series_box">
<property name="toolTip"> <property name="toolTip">
<string>Enter a series name, without an index. Only one series name can be used.</string> <string>Enter a series name, without an index. Only one series name can be used.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="MultiCompleteLineEdit" name="tags_box"> <widget class="EditWithComplete" name="tags_box">
<property name="toolTip"> <property name="toolTip">
<string>Enter tags separated by spaces</string> <string>Enter tags separated by spaces</string>
</property> </property>
@ -327,14 +327,9 @@
<header>widgets.h</header> <header>widgets.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>MultiCompleteLineEdit</class> <class>EditWithComplete</class>
<extends>QLineEdit</extends>
<header>calibre/gui2/complete.h</header>
</customwidget>
<customwidget>
<class>MultiCompleteComboBox</class>
<extends>QComboBox</extends> <extends>QComboBox</extends>
<header>calibre/gui2/complete.h</header> <header>calibre/gui2/complete2.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>

View File

@ -7,14 +7,14 @@ __license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __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.localization import lang_map
from calibre.utils.icu import sort_key, lower from calibre.utils.icu import sort_key, lower
class LanguagesEdit(MultiCompleteComboBox): class LanguagesEdit(EditWithComplete):
def __init__(self, parent=None, db=None): def __init__(self, parent=None, db=None):
MultiCompleteComboBox.__init__(self, parent) EditWithComplete.__init__(self, parent)
self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon) self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon)
self.setMinimumContentsLength(20) self.setMinimumContentsLength(20)

View File

@ -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.gui2 import UNDEFINED_QDATETIME, error_dialog, rating_font
from calibre.constants import iswindows from calibre.constants import iswindows
from calibre.gui2.widgets import EnLineEdit 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.date import now, format_date, qt_to_dt
from calibre.utils.config import tweaks from calibre.utils.config import tweaks
from calibre.utils.formatter import validation_formatter from calibre.utils.formatter import validation_formatter
@ -121,7 +121,7 @@ class TextDelegate(QStyledItemDelegate): # {{{
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
if self.auto_complete_function: if self.auto_complete_function:
editor = MultiCompleteComboBox(parent) editor = EditWithComplete(parent)
editor.set_separator(None) editor.set_separator(None)
complete_items = [i[1] for i in self.auto_complete_function()] complete_items = [i[1] for i in self.auto_complete_function()]
editor.update_items_cache(complete_items) editor.update_items_cache(complete_items)
@ -132,7 +132,7 @@ class TextDelegate(QStyledItemDelegate): # {{{
return editor return editor
def setModelData(self, editor, model, index): def setModelData(self, editor, model, index):
if isinstance(editor, MultiCompleteComboBox): if isinstance(editor, EditWithComplete):
val = editor.lineEdit().text() val = editor.lineEdit().text()
model.setData(index, QVariant(val), Qt.EditRole) model.setData(index, QVariant(val), Qt.EditRole)
else: else:
@ -153,7 +153,7 @@ class CompleteDelegate(QStyledItemDelegate): # {{{
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
if self.db and hasattr(self.db, self.items_func_name): if self.db and hasattr(self.db, self.items_func_name):
col = index.model().column_map[index.column()] col = index.model().column_map[index.column()]
editor = MultiCompleteComboBox(parent) editor = EditWithComplete(parent)
editor.set_separator(self.sep) editor.set_separator(self.sep)
editor.set_space_before_sep(self.space_before_sep) editor.set_space_before_sep(self.space_before_sep)
if self.sep == '&': if self.sep == '&':
@ -171,7 +171,7 @@ class CompleteDelegate(QStyledItemDelegate): # {{{
return editor return editor
def setModelData(self, editor, model, index): def setModelData(self, editor, model, index):
if isinstance(editor, MultiCompleteComboBox): if isinstance(editor, EditWithComplete):
val = editor.lineEdit().text() val = editor.lineEdit().text()
model.setData(index, QVariant(val), Qt.EditRole) model.setData(index, QVariant(val), Qt.EditRole)
else: else:
@ -244,7 +244,7 @@ class CcTextDelegate(QStyledItemDelegate): # {{{
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()]
editor = MultiCompleteLineEdit(parent) editor = EditWithComplete(parent)
editor.set_separator(None) editor.set_separator(None)
complete_items = sorted(list(m.db.all_custom(label=m.db.field_metadata.key_to_label(col))), complete_items = sorted(list(m.db.all_custom(label=m.db.field_metadata.key_to_label(col))),
key=sort_key) key=sort_key)

View File

@ -15,7 +15,6 @@ from PyQt4.Qt import (Qt, QDateTimeEdit, pyqtSignal, QMessageBox,
QPushButton, QSpinBox, QLineEdit, QSizePolicy, QDialogButtonBox, QAction) QPushButton, QSpinBox, QLineEdit, QSizePolicy, QDialogButtonBox, QAction)
from calibre.gui2.widgets import EnLineEdit, FormatList as _FormatList, ImageView 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.icu import sort_key
from calibre.utils.config import tweaks, prefs from calibre.utils.config import tweaks, prefs
from calibre.ebooks.metadata import (title_sort, authors_to_string, 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.ebooks.metadata.meta import get_metadata
from calibre.gui2 import (file_icon_provider, UNDEFINED_QDATETIME, from calibre.gui2 import (file_icon_provider, UNDEFINED_QDATETIME,
choose_files, error_dialog, choose_images) 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, from calibre.utils.date import (local_tz, qt_to_dt, as_local_time,
UNDEFINED_DATE) UNDEFINED_DATE)
from calibre import strftime from calibre import strftime
@ -204,7 +204,7 @@ class TitleSortEdit(TitleEdit):
# }}} # }}}
# Authors {{{ # Authors {{{
class AuthorsEdit(MultiCompleteComboBox): class AuthorsEdit(EditWithComplete):
TOOLTIP = '' TOOLTIP = ''
LABEL = _('&Author(s):') LABEL = _('&Author(s):')
@ -212,7 +212,7 @@ class AuthorsEdit(MultiCompleteComboBox):
def __init__(self, parent, manage_authors): def __init__(self, parent, manage_authors):
self.dialog = parent self.dialog = parent
self.books_to_refresh = set([]) self.books_to_refresh = set([])
MultiCompleteComboBox.__init__(self, parent) EditWithComplete.__init__(self, parent)
self.setToolTip(self.TOOLTIP) self.setToolTip(self.TOOLTIP)
self.setWhatsThis(self.TOOLTIP) self.setWhatsThis(self.TOOLTIP)
self.setEditable(True) self.setEditable(True)
@ -443,13 +443,13 @@ class AuthorSortEdit(EnLineEdit):
# }}} # }}}
# Series {{{ # Series {{{
class SeriesEdit(MultiCompleteComboBox): class SeriesEdit(EditWithComplete):
TOOLTIP = _('List of known series. You can add new series.') TOOLTIP = _('List of known series. You can add new series.')
LABEL = _('&Series:') LABEL = _('&Series:')
def __init__(self, parent): def __init__(self, parent):
MultiCompleteComboBox.__init__(self, parent) EditWithComplete.__init__(self, parent)
self.set_separator(None) self.set_separator(None)
self.dialog = parent self.dialog = parent
self.setSizeAdjustPolicy( self.setSizeAdjustPolicy(
@ -1086,14 +1086,14 @@ class RatingEdit(QSpinBox): # {{{
# }}} # }}}
class TagsEdit(MultiCompleteLineEdit): # {{{ class TagsEdit(EditWithComplete): # {{{
LABEL = _('Ta&gs:') LABEL = _('Ta&gs:')
TOOLTIP = '<p>'+_('Tags categorize the book. This is particularly ' TOOLTIP = '<p>'+_('Tags categorize the book. This is particularly '
'useful while searching. <br><br>They can be any words ' 'useful while searching. <br><br>They can be any words '
'or phrases, separated by commas.') 'or phrases, separated by commas.')
def __init__(self, parent): def __init__(self, parent):
MultiCompleteLineEdit.__init__(self, parent) EditWithComplete.__init__(self, parent)
self.books_to_refresh = set([]) self.books_to_refresh = set([])
self.setToolTip(self.TOOLTIP) self.setToolTip(self.TOOLTIP)
self.setWhatsThis(self.TOOLTIP) self.setWhatsThis(self.TOOLTIP)
@ -1327,11 +1327,11 @@ class ISBNDialog(QDialog) : # {{{
# }}} # }}}
class PublisherEdit(MultiCompleteComboBox): # {{{ class PublisherEdit(EditWithComplete): # {{{
LABEL = _('&Publisher:') LABEL = _('&Publisher:')
def __init__(self, parent): def __init__(self, parent):
MultiCompleteComboBox.__init__(self, parent) EditWithComplete.__init__(self, parent)
self.set_separator(None) self.set_separator(None)
self.setSizeAdjustPolicy( self.setSizeAdjustPolicy(
self.AdjustToMinimumContentsLengthWithIcon) self.AdjustToMinimumContentsLengthWithIcon)

View File

@ -13,6 +13,7 @@ from PyQt4.Qt import (QWidget, pyqtSignal, QCheckBox, QAbstractSpinBox,
from calibre.customize.ui import preferences_plugins from calibre.customize.ui import preferences_plugins
from calibre.utils.config import ConfigProxy from calibre.utils.config import ConfigProxy
from calibre.gui2.complete2 import EditWithComplete
class AbortCommit(Exception): class AbortCommit(Exception):
pass pass
@ -133,11 +134,15 @@ class Setting(object):
def initialize(self): def initialize(self):
self.gui_obj.blockSignals(True) self.gui_obj.blockSignals(True)
if self.datatype == 'choice': if self.datatype == 'choice':
self.gui_obj.clear() choices = self.choices or []
for x in self.choices: if isinstance(self.gui_obj, EditWithComplete):
if isinstance(x, basestring): self.gui_obj.all_items = choices
x = (x, x) else:
self.gui_obj.addItem(x[0], QVariant(x[1])) 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.set_gui_val(self.get_config_val(default=False))
self.gui_obj.blockSignals(False) self.gui_obj.blockSignals(False)
self.initial_value = self.get_gui_val() self.initial_value = self.get_gui_val()
@ -171,11 +176,14 @@ class Setting(object):
elif self.datatype == 'string': elif self.datatype == 'string':
self.gui_obj.setText(val if val else '') self.gui_obj.setText(val if val else '')
elif self.datatype == 'choice': elif self.datatype == 'choice':
idx = self.gui_obj.findData(QVariant(val), role=Qt.UserRole, if isinstance(self.gui_obj, EditWithComplete):
flags=self.CHOICES_SEARCH_FLAGS) self.gui_obj.setText(val)
if idx == -1: else:
idx = 0 idx = self.gui_obj.findData(QVariant(val), role=Qt.UserRole,
self.gui_obj.setCurrentIndex(idx) flags=self.CHOICES_SEARCH_FLAGS)
if idx == -1:
idx = 0
self.gui_obj.setCurrentIndex(idx)
def get_gui_val(self): def get_gui_val(self):
if self.datatype == 'bool': if self.datatype == 'bool':
@ -187,9 +195,12 @@ class Setting(object):
if self.empty_string_is_None and not val: if self.empty_string_is_None and not val:
val = None val = None
elif self.datatype == 'choice': elif self.datatype == 'choice':
idx = self.gui_obj.currentIndex() if isinstance(self.gui_obj, EditWithComplete):
if idx < 0: idx = 0 val = unicode(self.gui_obj.text())
val = unicode(self.gui_obj.itemData(idx).toString()) else:
idx = self.gui_obj.currentIndex()
if idx < 0: idx = 0
val = unicode(self.gui_obj.itemData(idx).toString())
return val return val
class CommaSeparatedList(Setting): class CommaSeparatedList(Setting):

View File

@ -320,7 +320,7 @@ Manage Authors. You can use the values {author} and
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_10"> <layout class="QGridLayout" name="gridLayout_10">
<item row="3" column="2" colspan="3"> <item row="3" column="2" colspan="3">
<widget class="MultiCompleteLineEdit" name="opt_categories_using_hierarchy"> <widget class="EditWithComplete" name="opt_categories_using_hierarchy">
<property name="toolTip"> <property name="toolTip">
<string>A comma-separated list of categories in which items containing <string>A comma-separated list of categories in which items containing
periods are displayed in the tag browser trees. For example, if 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
</widget> </widget>
</item> </item>
<item row="1" column="3" colspan="2"> <item row="1" column="3" colspan="2">
<widget class="MultiCompleteLineEdit" name="opt_tag_browser_dont_collapse"> <widget class="EditWithComplete" name="opt_tag_browser_dont_collapse">
<property name="toolTip"> <property name="toolTip">
<string>A comma-separated list of categories that are not to <string>A comma-separated list of categories that are not to
be partitioned even if the number of items is larger than be partitioned even if the number of items is larger than
@ -506,9 +506,9 @@ a few top-level elements.</string>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>MultiCompleteLineEdit</class> <class>EditWithComplete</class>
<extends>QLineEdit</extends> <extends>QComboBox</extends>
<header>calibre/gui2/complete.h</header> <header>calibre/gui2/complete2.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources>

View File

@ -98,7 +98,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
db.prefs.set('grouped_search_make_user_categories', []) db.prefs.set('grouped_search_make_user_categories', [])
r('grouped_search_make_user_categories', db.prefs, setting=CommaSeparatedList) r('grouped_search_make_user_categories', db.prefs, setting=CommaSeparatedList)
self.muc_changed = False 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) self.muc_box_changed)
def set_similar_fields(self, initial=False): 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.opt_grouped_search_make_user_categories.update_items_cache(terms)
self.gst_names.blockSignals(True) self.gst_names.blockSignals(True)
self.gst_names.clear() self.gst_names.clear()
print (1111, self.gst_names)
self.gst_names.addItem('', '') self.gst_names.addItem('', '')
for t in terms: for t in terms:
self.gst_names.addItem(t, t) self.gst_names.addItem(t, t)

View File

@ -69,7 +69,7 @@
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="MultiCompleteLineEdit" name="opt_limit_search_columns_to"/> <widget class="EditWithComplete" name="opt_limit_search_columns_to"/>
</item> </item>
<item row="5" column="0" colspan="2"> <item row="5" column="0" colspan="2">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
@ -134,7 +134,7 @@ a search term by changing the value box then pressing Save.</string>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="MultiCompleteLineEdit" name="gst_value"/> <widget class="EditWithComplete" name="gst_value"/>
</item> </item>
<item> <item>
<widget class="QToolButton" name="gst_save_button"> <widget class="QToolButton" name="gst_save_button">
@ -173,7 +173,7 @@ of a search term by changing the value box then pressing Save.</string>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="MultiCompleteLineEdit" name="opt_grouped_search_make_user_categories"> <widget class="EditWithComplete" name="opt_grouped_search_make_user_categories">
<property name="toolTip"> <property name="toolTip">
<string>Enter the names of any grouped search terms you wish <string>Enter the names of any grouped search terms you wish
to be shown as user categories</string> to be shown as user categories</string>
@ -301,9 +301,9 @@ to be shown as user categories</string>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>MultiCompleteLineEdit</class> <class>EditWithComplete</class>
<extends>QLineEdit</extends> <extends>QComboBox</extends>
<header>calibre/gui2/complete.h</header> <header>calibre/gui2/complete2.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources>