'
+__docformat__ = 'restructuredtext en'
+
+
+from PyQt4.Qt import QLineEdit, QListView, QAbstractListModel, Qt, QTimer, \
+ QApplication, QPoint, QItemDelegate, QStyleOptionViewItem, \
+ QStyle, QEvent, pyqtSignal
+
+from calibre.utils.icu import sort_key, lower
+from calibre.gui2 import NONE
+from calibre.gui2.widgets import EnComboBox
+
+class CompleterItemDelegate(QItemDelegate): # {{{
+
+ ''' Renders the current item as thought it were selected '''
+
+ def __init__(self, view):
+ self.view = view
+ QItemDelegate.__init__(self, view)
+
+ def paint(self, p, opt, idx):
+ opt = QStyleOptionViewItem(opt)
+ opt.showDecorationSelected = True
+ if self.view.currentIndex() == idx:
+ opt.state |= QStyle.State_HasFocus
+ QItemDelegate.paint(self, p, opt, idx)
+
+# }}}
+
+class CompleteWindow(QListView): # {{{
+
+ '''
+ The completion popup. For keyboard and mouse handling see
+ :meth:`eventFilter`.
+ '''
+
+ #: This signal is emitted when the user selects one of the listed
+ #: completions, by mouse or keyboard
+ completion_selected = pyqtSignal(object)
+
+ def __init__(self, widget, model):
+ self.widget = widget
+ QListView.__init__(self)
+ self.setVisible(False)
+ self.setParent(None, Qt.Popup)
+ self.setAlternatingRowColors(True)
+ self.setFocusPolicy(Qt.NoFocus)
+ self._d = CompleterItemDelegate(self)
+ self.setItemDelegate(self._d)
+ self.setModel(model)
+ self.setFocusProxy(widget)
+ self.installEventFilter(self)
+ self.clicked.connect(self.do_selected)
+ self.entered.connect(self.do_entered)
+ self.setMouseTracking(True)
+
+ def do_entered(self, idx):
+ if idx.isValid():
+ self.setCurrentIndex(idx)
+
+ def do_selected(self, idx=None):
+ idx = self.currentIndex() if idx is None else idx
+ if not idx.isValid() and self.model().rowCount() > 0:
+ idx = self.model().index(0)
+ if idx.isValid():
+ data = unicode(self.model().data(idx, Qt.DisplayRole))
+ self.completion_selected.emit(data)
+ self.hide()
+
+ def eventFilter(self, o, e):
+ if o is not self:
+ return False
+ if e.type() == e.KeyPress:
+ key = e.key()
+ if key in (Qt.Key_Escape, Qt.Key_Backtab) or \
+ (key == Qt.Key_F4 and (e.modifiers() & Qt.AltModifier)):
+ self.hide()
+ return True
+ elif key in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab):
+ self.do_selected()
+ return True
+ elif key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp,
+ Qt.Key_PageDown):
+ return False
+ # Send key event to associated line edit
+ self.widget.eat_focus_out = False
+ try:
+ self.widget.event(e)
+ finally:
+ self.widget.eat_focus_out = True
+ if not self.widget.hasFocus():
+ # Line edit lost focus
+ self.hide()
+ if e.isAccepted():
+ # Line edit consumed event
+ return True
+ elif e.type() == e.MouseButtonPress:
+ # Hide popup if user clicks outside it, otherwise
+ # pass event to popup
+ if not self.underMouse():
+ self.hide()
+ return True
+ elif e.type() in (e.InputMethod, e.ShortcutOverride):
+ QApplication.sendEvent(self.widget, e)
+
+ return False # Do not filter this event
+
+# }}}
+
+class CompleteModel(QAbstractListModel):
+
+ def __init__(self, parent=None):
+ QAbstractListModel.__init__(self, parent)
+ self.sep = ','
+ self.space_before_sep = False
+ self.items = []
+ self.lowered_items = []
+ self.matches = []
+
+ def set_items(self, items):
+ items = [unicode(x.strip()) for x in items]
+ self.items = list(sorted(items, key=lambda x: sort_key(x)))
+ self.lowered_items = [lower(x) for x in self.items]
+ self.matches = []
+ self.reset()
+
+ def rowCount(self, *args):
+ return len(self.matches)
+
+ def data(self, index, role):
+ if role == Qt.DisplayRole:
+ r = index.row()
+ try:
+ return self.matches[r]
+ except IndexError:
+ pass
+ return NONE
+
+ def get_matches(self, prefix):
+ '''
+ Return all matches that (case insensitively) start with prefix
+ '''
+ prefix = lower(prefix)
+ ans = []
+ if prefix:
+ for i, test in enumerate(self.lowered_items):
+ if test.startswith(prefix):
+ ans.append(self.items[i])
+ return ans
+
+ def update_matches(self, matches):
+ self.matches = matches
+ self.reset()
+
+class MultiCompleteLineEdit(QLineEdit):
+ '''
+ 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.
+ '''
+
+ def __init__(self, parent=None):
+ self.eat_focus_out = True
+ self.max_visible_items = 7
+ self.current_prefix = None
+ QLineEdit.__init__(self, parent)
+
+ self._model = CompleteModel(parent=self)
+ self.complete_window = CompleteWindow(self, self._model)
+ self.textChanged.connect(self.text_changed)
+ self.cursorPositionChanged.connect(self.cursor_position_changed)
+ self.complete_window.completion_selected.connect(self.completion_selected)
+
+ # 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 eventFilter(self, o, e):
+ if self.eat_focus_out and o is self and e.type() == QEvent.FocusOut:
+ if self.complete_window.isVisible():
+ return True # Filter this event since the cw is visible
+ return QLineEdit.eventFilter(self, o, e)
+
+
+ def text_changed(self, *args):
+ self.update_completions()
+
+ def cursor_position_changed(self, *args):
+ self.update_completions()
+
+ def update_completions(self):
+ ' Update the list of completions '
+ cpos = self.cursorPosition()
+ text = unicode(self.text())
+ prefix = text[:cpos]
+ self.current_prefix = prefix
+ complete_prefix = prefix.lstrip()
+ if self.sep:
+ complete_prefix = prefix = prefix.split(self.sep)[-1].lstrip()
+
+ matches = self._model.get_matches(complete_prefix)
+ self.update_complete_window(matches)
+
+ def get_completed_text(self, text):
+ '''
+ Get completed text from current cursor position and the completion
+ text
+ '''
+ if self.sep is None:
+ return text
+ else:
+ cursor_pos = self.cursorPosition()
+ before_text = unicode(self.text())[:cursor_pos]
+ after_text = unicode(self.text())[cursor_pos:]
+ after_parts = after_text.split(self.sep)
+ if len(after_parts) < 3 and not after_parts[-1].strip():
+ after_text = u''
+ prefix_len = len(before_text.split(self.sep)[-1].lstrip())
+ if self.space_before_sep:
+ complete_text_pat = '%s%s %s %s'
+ len_extra = 3
+ else:
+ complete_text_pat = '%s%s%s %s'
+ len_extra = 2
+ return prefix_len, len_extra, complete_text_pat % (
+ before_text[:cursor_pos - prefix_len], text, self.sep, after_text)
+
+ def completion_selected(self, text):
+ prefix_len, len_extra, ctext = self.get_completed_text(text)
+ if self.sep is None:
+ self.setText(ctext)
+ self.setCursorPosition(len(ctext))
+ else:
+ cursor_pos = self.cursorPosition()
+ self.setText(ctext)
+ self.setCursorPosition(cursor_pos - prefix_len + len(text) + len_extra)
+
+ def update_complete_window(self, matches):
+ self._model.update_matches(matches)
+ if matches:
+ self.show_complete_window()
+ else:
+ self.complete_window.hide()
+
+
+ def position_complete_window(self):
+ popup = self.complete_window
+ screen = QApplication.desktop().availableGeometry(self)
+ h = (popup.sizeHintForRow(0) * min(self.max_visible_items,
+ popup.model().rowCount()) + 3) + 3
+ hsb = popup.horizontalScrollBar()
+ if hsb and hsb.isVisible():
+ h += hsb.sizeHint().height()
+
+ rh = self.height()
+ pos = self.mapToGlobal(QPoint(0, self.height() - 2))
+ w = self.width()
+
+ if w > screen.width():
+ w = 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, popup.minimumHeight())
+ if h > bottom:
+ h = min(max(top, bottom), h)
+ if top > bottom:
+ pos.setY(pos.y() - h - rh + 2)
+
+ popup.setGeometry(pos.x(), pos.y(), w, h)
+
+
+ def show_complete_window(self):
+ self.position_complete_window()
+ self.complete_window.show()
+
+ def moveEvent(self, ev):
+ ret = QLineEdit.moveEvent(self, ev)
+ QTimer.singleShot(0, self.position_complete_window)
+ return ret
+
+ def resizeEvent(self, ev):
+ ret = QLineEdit.resizeEvent(self, ev)
+ QTimer.singleShot(0, self.position_complete_window)
+ return ret
+
+
+ @dynamic_property
+ def all_items(self):
+ def fget(self):
+ return self._model.items
+ def fset(self, items):
+ self._model.set_items(items)
+ return property(fget=fget, fset=fset)
+
+ @dynamic_property
+ def sep(self):
+ def fget(self):
+ return self._model.sep
+ def fset(self, val):
+ self._model.sep = val
+ return property(fget=fget, fset=fset)
+
+ @dynamic_property
+ def space_before_sep(self):
+ def fget(self):
+ return self._model.space_before_sep
+ def fset(self, val):
+ self._model.space_before_sep = val
+ return property(fget=fget, fset=fset)
+
+class MultiCompleteComboBox(EnComboBox):
+
+ def __init__(self, *args):
+ EnComboBox.__init__(self, *args)
+ self.setLineEdit(MultiCompleteLineEdit(self))
+
+ 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)
+
+
+
+if __name__ == '__main__':
+ from PyQt4.Qt import QDialog, QVBoxLayout
+ app = QApplication([])
+ d = QDialog()
+ d.setLayout(QVBoxLayout())
+ le = MultiCompleteLineEdit(d)
+ d.layout().addWidget(le)
+ le.all_items = ['one', 'otwo', 'othree', 'ooone', 'ootwo', 'oothree']
+ d.exec_()
diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py
index d3fa5958ab..8ec037278e 100644
--- a/src/calibre/gui2/metadata/basic_widgets.py
+++ b/src/calibre/gui2/metadata/basic_widgets.py
@@ -12,8 +12,8 @@ from PyQt4.Qt import Qt, QDateEdit, QDate, \
QDoubleSpinBox, QListWidgetItem, QSize, QPixmap, \
QPushButton, QSpinBox, QLineEdit
-from calibre.gui2.widgets import EnLineEdit, CompleteComboBox, \
- EnComboBox, FormatList, ImageView, CompleteLineEdit
+from calibre.gui2.widgets import EnLineEdit, EnComboBox, 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, \
@@ -149,14 +149,14 @@ class TitleSortEdit(TitleEdit):
# }}}
# Authors {{{
-class AuthorsEdit(CompleteComboBox):
+class AuthorsEdit(MultiCompleteComboBox):
TOOLTIP = ''
LABEL = _('&Author(s):')
def __init__(self, parent):
self.dialog = parent
- CompleteComboBox.__init__(self, parent)
+ MultiCompleteComboBox.__init__(self, parent)
self.setToolTip(self.TOOLTIP)
self.setWhatsThis(self.TOOLTIP)
self.setEditable(True)
@@ -814,14 +814,14 @@ class RatingEdit(QSpinBox): # {{{
# }}}
-class TagsEdit(CompleteLineEdit): # {{{
+class TagsEdit(MultiCompleteLineEdit): # {{{
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):
- CompleteLineEdit.__init__(self, parent)
+ MultiCompleteLineEdit.__init__(self, parent)
self.setToolTip(self.TOOLTIP)
self.setWhatsThis(self.TOOLTIP)
@@ -839,7 +839,7 @@ class TagsEdit(CompleteLineEdit): # {{{
tags = db.tags(id_, index_is_id=True)
tags = tags.split(',') if tags else []
self.current_val = tags
- self.update_items_cache(db.all_tags())
+ self.all_items = db.all_tags()
self.original_val = self.current_val
@property
@@ -860,7 +860,7 @@ class TagsEdit(CompleteLineEdit): # {{{
d = TagEditor(self, db, id_)
if d.exec_() == TagEditor.Accepted:
self.current_val = d.tags
- self.update_items_cache(db.all_tags())
+ self.all_items = db.all_tags()
def commit(self, db, id_):
From 7852275d9325b3d8b32a97ef37838bcd1a5831dd Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 00:19:21 -0700
Subject: [PATCH 41/55] ...
---
src/calibre/gui2/complete.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/calibre/gui2/complete.py b/src/calibre/gui2/complete.py
index 5c5a836d98..ce8609fc99 100644
--- a/src/calibre/gui2/complete.py
+++ b/src/calibre/gui2/complete.py
@@ -162,6 +162,9 @@ class MultiCompleteLineEdit(QLineEdit):
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):
From b22e640b509d811d22705e2ad997a535ec053f34 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 2 Feb 2011 09:03:06 +0000
Subject: [PATCH 42/55] Fix template program regression triggered by
recursively calling the processor
---
src/calibre/utils/formatter_functions.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py
index 2e5852df89..518f2ed140 100644
--- a/src/calibre/utils/formatter_functions.py
+++ b/src/calibre/utils/formatter_functions.py
@@ -186,7 +186,7 @@ class BuiltinTemplate(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals, template):
template = template.replace('[[', '{').replace(']]', '}')
- return formatter.safe_format(template, kwargs, 'TEMPLATE', mi)
+ return formatter.__class__().safe_format(template, kwargs, 'TEMPLATE', mi)
class BuiltinEval(BuiltinFormatterFunction):
name = 'eval'
From dece4f236fd8e149089619f455e2522cff8f545b Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 09:21:21 -0700
Subject: [PATCH 43/55] Connect/share menu: Re-organize to make it a little
less easy to select email and delete instead of just email by mistake
---
src/calibre/gui2/actions/device.py | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/calibre/gui2/actions/device.py b/src/calibre/gui2/actions/device.py
index fb3e627789..b32568f8fd 100644
--- a/src/calibre/gui2/actions/device.py
+++ b/src/calibre/gui2/actions/device.py
@@ -74,23 +74,29 @@ class ShareConnMenu(QMenu): # {{{
opts = email_config().parse()
if opts.accounts:
self.email_to_menu = QMenu(_('Email to')+'...', self)
+ ac = self.addMenu(self.email_to_menu)
+ self.email_actions.append(ac)
+ self.email_to_and_delete_menu = QMenu(
+ _('Email to and delete from library')+'...', self)
keys = sorted(opts.accounts.keys())
for account in keys:
formats, auto, default = opts.accounts[account]
dest = 'mail:'+account+';'+formats
action1 = DeviceAction(dest, False, False, I('mail.png'),
- _('Email to')+' '+account)
+ account)
action2 = DeviceAction(dest, True, False, I('mail.png'),
- _('Email to')+' '+account+ _(' and delete from library'))
- map(self.email_to_menu.addAction, (action1, action2))
+ account + ' ' + _('(delete from library)'))
+ self.email_to_menu.addAction(action1)
+ self.email_to_and_delete_menu.addAction(action2)
map(self.memory.append, (action1, action2))
if default:
- map(self.addAction, (action1, action2))
- map(self.email_actions.append, (action1, action2))
- self.email_to_menu.addSeparator()
+ ac = DeviceAction(dest, False, False,
+ I('mail.png'), _('Email to') + ' ' +account)
+ self.addAction(ac)
+ self.email_actions.append(ac)
action1.a_s.connect(sync_menu.action_triggered)
action2.a_s.connect(sync_menu.action_triggered)
- ac = self.addMenu(self.email_to_menu)
+ ac = self.addMenu(self.email_to_and_delete_menu)
self.email_actions.append(ac)
else:
ac = self.addAction(_('Setup email based sharing of books'))
From 92b313ec7e5f4ee21a1ecd0b43503db85f2502ee Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 09:47:18 -0700
Subject: [PATCH 44/55] ...
---
resources/recipes/le_temps.recipe | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/resources/recipes/le_temps.recipe b/resources/recipes/le_temps.recipe
index c33d9a51d2..7e320fe710 100644
--- a/resources/recipes/le_temps.recipe
+++ b/resources/recipes/le_temps.recipe
@@ -15,12 +15,26 @@ class LeTemps(BasicNewsRecipe):
oldest_article = 7
max_articles_per_feed = 100
__author__ = 'Sujata Raman'
+ description = 'French news. Needs a subscription from http://www.letemps.ch'
no_stylesheets = True
remove_javascript = True
recursions = 1
encoding = 'UTF-8'
match_regexps = [r'http://www.letemps.ch/Page/Uuid/[-0-9a-f]+\|[1-9]']
language = 'fr'
+ needs_subscription = True
+
+ def get_browser(self):
+ br = BasicNewsRecipe.get_browser(self)
+ br.open('http://www.letemps.ch/login')
+ br['username'] = self.username
+ br['password'] = self.password
+ raw = br.submit().read()
+ if '>Login' in raw:
+ raise ValueError('Failed to login to letemp.ch. Check '
+ 'your username and password')
+ return br
+
keep_only_tags = [dict(name='div', attrs={'id':'content'}),
dict(name='div', attrs={'class':'story'})
From 1de9e0b94b1398c577cab36b87b849569f773b0d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 10:44:18 -0700
Subject: [PATCH 45/55] use the new completer for publisher and series boxes as
well
---
src/calibre/gui2/complete.py | 18 +++++++++++++-----
src/calibre/gui2/metadata/basic_widgets.py | 14 +++++++++-----
2 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/src/calibre/gui2/complete.py b/src/calibre/gui2/complete.py
index ce8609fc99..7fbfae901a 100644
--- a/src/calibre/gui2/complete.py
+++ b/src/calibre/gui2/complete.py
@@ -64,8 +64,8 @@ class CompleteWindow(QListView): # {{{
def do_selected(self, idx=None):
idx = self.currentIndex() if idx is None else idx
- if not idx.isValid() and self.model().rowCount() > 0:
- idx = self.model().index(0)
+ #if not idx.isValid() and self.model().rowCount() > 0:
+ # idx = self.model().index(0)
if idx.isValid():
data = unicode(self.model().data(idx, Qt.DisplayRole))
self.completion_selected.emit(data)
@@ -175,9 +175,10 @@ class MultiCompleteLineEdit(QLineEdit):
self._model = CompleteModel(parent=self)
self.complete_window = CompleteWindow(self, self._model)
- self.textChanged.connect(self.text_changed)
+ self.textEdited.connect(self.text_edited)
self.cursorPositionChanged.connect(self.cursor_position_changed)
self.complete_window.completion_selected.connect(self.completion_selected)
+ self.installEventFilter(self)
# Interface {{{
def update_items_cache(self, complete_items):
@@ -198,7 +199,7 @@ class MultiCompleteLineEdit(QLineEdit):
return QLineEdit.eventFilter(self, o, e)
- def text_changed(self, *args):
+ def text_edited(self, *args):
self.update_completions()
def cursor_position_changed(self, *args):
@@ -206,6 +207,8 @@ class MultiCompleteLineEdit(QLineEdit):
def update_completions(self):
' Update the list of completions '
+ if not self.complete_window.isVisible() and not self.hasFocus():
+ return
cpos = self.cursorPosition()
text = unicode(self.text())
prefix = text[:cpos]
@@ -223,7 +226,7 @@ class MultiCompleteLineEdit(QLineEdit):
text
'''
if self.sep is None:
- return text
+ return -1, -1, text
else:
cursor_pos = self.cursorPosition()
before_text = unicode(self.text())[:cursor_pos]
@@ -334,6 +337,11 @@ class MultiCompleteComboBox(EnComboBox):
def __init__(self, *args):
EnComboBox.__init__(self, *args)
self.setLineEdit(MultiCompleteLineEdit(self))
+ # Needed to allow changing the case of an existing item
+ # otherwise on focus out, the text is changed to the
+ # item that matches case insensitively
+ c = self.lineEdit().completer()
+ c.setCaseSensitivity(Qt.CaseSensitive)
def update_items_cache(self, complete_items):
self.lineEdit().update_items_cache(complete_items)
diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py
index 8ec037278e..6b89e306e6 100644
--- a/src/calibre/gui2/metadata/basic_widgets.py
+++ b/src/calibre/gui2/metadata/basic_widgets.py
@@ -12,7 +12,7 @@ from PyQt4.Qt import Qt, QDateEdit, QDate, \
QDoubleSpinBox, QListWidgetItem, QSize, QPixmap, \
QPushButton, QSpinBox, QLineEdit
-from calibre.gui2.widgets import EnLineEdit, EnComboBox, FormatList, ImageView
+from calibre.gui2.widgets import EnLineEdit, FormatList, ImageView
from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox
from calibre.utils.icu import sort_key
from calibre.utils.config import tweaks, prefs
@@ -283,13 +283,14 @@ class AuthorSortEdit(EnLineEdit):
# }}}
# Series {{{
-class SeriesEdit(EnComboBox):
+class SeriesEdit(MultiCompleteComboBox):
TOOLTIP = _('List of known series. You can add new series.')
LABEL = _('&Series:')
def __init__(self, parent):
- EnComboBox.__init__(self, parent)
+ MultiCompleteComboBox.__init__(self, parent)
+ self.set_separator(None)
self.dialog = parent
self.setSizeAdjustPolicy(
self.AdjustToMinimumContentsLengthWithIcon)
@@ -314,6 +315,7 @@ class SeriesEdit(EnComboBox):
def initialize(self, db, id_):
all_series = db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
+ self.update_items_cache([x[1] for x in all_series])
series_id = db.series_id(id_, index_is_id=True)
idx, c = None, 0
for i in all_series:
@@ -910,11 +912,12 @@ class ISBNEdit(QLineEdit): # {{{
# }}}
-class PublisherEdit(EnComboBox): # {{{
+class PublisherEdit(MultiCompleteComboBox): # {{{
LABEL = _('&Publisher:')
def __init__(self, parent):
- EnComboBox.__init__(self, parent)
+ MultiCompleteComboBox.__init__(self, parent)
+ self.set_separator(None)
self.setSizeAdjustPolicy(
self.AdjustToMinimumContentsLengthWithIcon)
@@ -935,6 +938,7 @@ class PublisherEdit(EnComboBox): # {{{
def initialize(self, db, id_):
all_publishers = db.all_publishers()
all_publishers.sort(key=lambda x : sort_key(x[1]))
+ self.update_items_cache([x[1] for x in all_publishers])
publisher_id = db.publisher_id(id_, index_is_id=True)
idx, c = None, 0
for i in all_publishers:
From 99fa9659c9f8618eaa89494599e782ab277ddaee Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 11:03:03 -0700
Subject: [PATCH 46/55] dont popup completer on cursor position changing as
that can be confusing for noobs
---
src/calibre/gui2/complete.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/calibre/gui2/complete.py b/src/calibre/gui2/complete.py
index 7fbfae901a..39ecd847ec 100644
--- a/src/calibre/gui2/complete.py
+++ b/src/calibre/gui2/complete.py
@@ -176,7 +176,6 @@ class MultiCompleteLineEdit(QLineEdit):
self._model = CompleteModel(parent=self)
self.complete_window = CompleteWindow(self, self._model)
self.textEdited.connect(self.text_edited)
- self.cursorPositionChanged.connect(self.cursor_position_changed)
self.complete_window.completion_selected.connect(self.completion_selected)
self.installEventFilter(self)
@@ -202,9 +201,6 @@ class MultiCompleteLineEdit(QLineEdit):
def text_edited(self, *args):
self.update_completions()
- def cursor_position_changed(self, *args):
- self.update_completions()
-
def update_completions(self):
' Update the list of completions '
if not self.complete_window.isVisible() and not self.hasFocus():
From b2055e106ee9d7d637292a08960a89a396269d83 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 11:07:16 -0700
Subject: [PATCH 47/55] Don't insert separator after completion, again
confusing for noobs
---
src/calibre/gui2/complete.py | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/src/calibre/gui2/complete.py b/src/calibre/gui2/complete.py
index 39ecd847ec..0ad8fb13d4 100644
--- a/src/calibre/gui2/complete.py
+++ b/src/calibre/gui2/complete.py
@@ -222,7 +222,7 @@ class MultiCompleteLineEdit(QLineEdit):
text
'''
if self.sep is None:
- return -1, -1, text
+ return -1, text
else:
cursor_pos = self.cursorPosition()
before_text = unicode(self.text())[:cursor_pos]
@@ -231,24 +231,18 @@ class MultiCompleteLineEdit(QLineEdit):
if len(after_parts) < 3 and not after_parts[-1].strip():
after_text = u''
prefix_len = len(before_text.split(self.sep)[-1].lstrip())
- if self.space_before_sep:
- complete_text_pat = '%s%s %s %s'
- len_extra = 3
- else:
- complete_text_pat = '%s%s%s %s'
- len_extra = 2
- return prefix_len, len_extra, complete_text_pat % (
- before_text[:cursor_pos - prefix_len], text, self.sep, after_text)
+ return prefix_len, \
+ before_text[:cursor_pos - prefix_len] + text + after_text
def completion_selected(self, text):
- prefix_len, len_extra, ctext = self.get_completed_text(text)
+ prefix_len, ctext = self.get_completed_text(text)
if self.sep is None:
self.setText(ctext)
self.setCursorPosition(len(ctext))
else:
cursor_pos = self.cursorPosition()
self.setText(ctext)
- self.setCursorPosition(cursor_pos - prefix_len + len(text) + len_extra)
+ self.setCursorPosition(cursor_pos - prefix_len + len(text))
def update_complete_window(self, matches):
self._model.update_matches(matches)
From fd3ed9b8f6f20519fdfd337538d16c24e0e5dfaa Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 11:40:37 -0700
Subject: [PATCH 48/55] Use the new completer in all delegates
---
src/calibre/gui2/library/delegates.py | 57 ++++++++-------------------
1 file changed, 17 insertions(+), 40 deletions(-)
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py
index ae9d6e2f71..fed2e42470 100644
--- a/src/calibre/gui2/library/delegates.py
+++ b/src/calibre/gui2/library/delegates.py
@@ -12,11 +12,11 @@ from PyQt4.Qt import QColor, Qt, QModelIndex, QSize, \
QPainterPath, QLinearGradient, QBrush, \
QPen, QStyle, QPainter, QStyleOptionViewItemV4, \
QIcon, QDoubleSpinBox, QVariant, QSpinBox, \
- QStyledItemDelegate, QCompleter, \
- QComboBox, QTextDocument
+ QStyledItemDelegate, QComboBox, QTextDocument
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
-from calibre.gui2.widgets import EnLineEdit, CompleteLineEdit
+from calibre.gui2.widgets import EnLineEdit
+from calibre.gui2.complete import MultiCompleteLineEdit
from calibre.utils.date import now, format_date
from calibre.utils.config import tweaks
from calibre.utils.formatter import validation_formatter
@@ -151,38 +151,15 @@ class TextDelegate(QStyledItemDelegate): # {{{
self.auto_complete_function = f
def createEditor(self, parent, option, index):
- editor = EnLineEdit(parent)
if self.auto_complete_function:
+ editor = MultiCompleteLineEdit(parent)
+ editor.set_separator(None)
complete_items = [i[1] for i in self.auto_complete_function()]
- completer = QCompleter(complete_items, self)
- completer.setCaseSensitivity(Qt.CaseInsensitive)
- completer.setCompletionMode(QCompleter.PopupCompletion)
- editor.setCompleter(completer)
- return editor
-#}}}
-
-class TagsDelegate(QStyledItemDelegate): # {{{
- def __init__(self, parent):
- QStyledItemDelegate.__init__(self, parent)
- self.db = None
-
- def set_database(self, db):
- self.db = db
-
- def createEditor(self, parent, option, index):
- if self.db:
- col = index.model().column_map[index.column()]
- if not index.model().is_custom_column(col):
- editor = CompleteLineEdit(parent, self.db.all_tags())
- else:
- editor = CompleteLineEdit(parent,
- sorted(list(self.db.all_custom(label=self.db.field_metadata.key_to_label(col))),
- key=sort_key))
- return editor
+ editor.update_items_cache(complete_items)
else:
editor = EnLineEdit(parent)
return editor
-# }}}
+#}}}
class CompleteDelegate(QStyledItemDelegate): # {{{
def __init__(self, parent, sep, items_func_name, space_before_sep=False):
@@ -197,13 +174,15 @@ 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 = MultiCompleteLineEdit(parent)
+ editor.set_separator(self.sep)
+ editor.set_space_before_sep(self.space_before_sep)
if not index.model().is_custom_column(col):
- editor = CompleteLineEdit(parent, getattr(self.db, self.items_func_name)(),
- self.sep, self.space_before_sep)
+ all_items = getattr(self.db, self.items_func_name)()
else:
- editor = CompleteLineEdit(parent,
- sorted(list(self.db.all_custom(label=self.db.field_metadata.key_to_label(col))),
- key=sort_key), self.sep, self.space_before_sep)
+ all_items = list(self.db.all_custom(
+ label=self.db.field_metadata.key_to_label(col)))
+ editor.update_items_cache(all_items)
else:
editor = EnLineEdit(parent)
return editor
@@ -273,13 +252,11 @@ class CcTextDelegate(QStyledItemDelegate): # {{{
editor.setRange(-100., float(sys.maxint))
editor.setDecimals(2)
else:
- editor = EnLineEdit(parent)
+ editor = MultiCompleteLineEdit(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)
- completer = QCompleter(complete_items, self)
- completer.setCaseSensitivity(Qt.CaseInsensitive)
- completer.setCompletionMode(QCompleter.PopupCompletion)
- editor.setCompleter(completer)
+ editor.update_items_cache(complete_items)
return editor
# }}}
From c6260c678ac073b63e65c8f6900fecdc169c5f10 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 12:12:45 -0700
Subject: [PATCH 49/55] Refactoring to use new completer
---
src/calibre/gui2/convert/metadata.ui | 12 +++++-----
src/calibre/gui2/custom_column_widgets.py | 28 +++++++++++++----------
2 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/src/calibre/gui2/convert/metadata.ui b/src/calibre/gui2/convert/metadata.ui
index 61c27594c4..47d983d870 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.
@@ -255,7 +255,7 @@
-
-
+
true
@@ -282,14 +282,14 @@
- CompleteComboBox
+ MultiCompleteComboBox
QComboBox
-
+
- CompleteLineEdit
+ MultiCompleteLineEdit
QLineEdit
-
+
ImageView
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index 360a5bcd0a..0555d42b4f 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -14,7 +14,7 @@ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \
QPushButton
from calibre.utils.date import qt_to_dt, now
-from calibre.gui2.widgets import CompleteLineEdit, EnComboBox
+from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox
from calibre.gui2.comments_editor import Editor as CommentsEditor
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.utils.config import tweaks
@@ -228,10 +228,11 @@ class Text(Base):
values = self.all_values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
if self.col_metadata['is_multiple']:
- w = CompleteLineEdit(parent, values)
+ w = MultiCompleteLineEdit(parent)
+ w.update_items_cache(values)
w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
else:
- w = EnComboBox(parent)
+ w = MultiCompleteComboBox(parent)
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
w.setMinimumContentsLength(25)
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w]
@@ -240,9 +241,10 @@ class Text(Base):
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
self.initial_val = val
val = self.normalize_db_val(val)
+ self.widgets[1].update_items_cache(self.all_values)
+
if self.col_metadata['is_multiple']:
self.setter(val)
- self.widgets[1].update_items_cache(self.all_values)
else:
idx = None
for i, c in enumerate(self.all_values):
@@ -276,7 +278,7 @@ class Series(Base):
def setup_ui(self, parent):
values = self.all_values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
- w = EnComboBox(parent)
+ w = MultiCompleteComboBox(parent)
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
w.setMinimumContentsLength(25)
self.name_widget = w
@@ -305,6 +307,7 @@ class Series(Base):
if c == val:
idx = i
self.name_widget.addItem(c)
+ self.name_widget.update_items_cache(self.all_values)
self.name_widget.setEditText('')
if idx is not None:
self.widgets[1].setCurrentIndex(idx)
@@ -670,7 +673,7 @@ class BulkDateTime(BulkBase):
class BulkSeries(BulkBase):
def setup_ui(self, parent):
- self.make_widgets(parent, EnComboBox)
+ self.make_widgets(parent, MultiCompleteComboBox)
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)
@@ -705,6 +708,7 @@ class BulkSeries(BulkBase):
def initialize(self, book_id):
self.idx_widget.setChecked(False)
+ self.main_widget.update_items_cache(self.all_values)
for c in self.all_values:
self.main_widget.addItem(c)
self.main_widget.setEditText('')
@@ -795,7 +799,8 @@ class RemoveTags(QWidget):
layout.setSpacing(5)
layout.setContentsMargins(0, 0, 0, 0)
- self.tags_box = CompleteLineEdit(parent, values)
+ self.tags_box = MultiCompleteLineEdit(parent)
+ self.tags_box.update_items_cache(values)
layout.addWidget(self.tags_box, stretch=3)
self.checkbox = QCheckBox(_('Remove all tags'), parent)
layout.addWidget(self.checkbox)
@@ -816,7 +821,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, CompleteLineEdit,
+ self.make_widgets(parent, MultiCompleteLineEdit,
extra_label_text=_('tags to add'))
self.main_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
self.adding_widget = self.main_widget
@@ -829,16 +834,15 @@ class BulkText(BulkBase):
w.tags_box.textChanged.connect(self.a_c_checkbox_changed)
w.checkbox.stateChanged.connect(self.a_c_checkbox_changed)
else:
- self.make_widgets(parent, EnComboBox)
+ self.make_widgets(parent, MultiCompleteComboBox)
self.main_widget.setSizeAdjustPolicy(
self.main_widget.AdjustToMinimumContentsLengthWithIcon)
self.main_widget.setMinimumContentsLength(25)
self.ignore_change_signals = False
def initialize(self, book_ids):
- if self.col_metadata['is_multiple']:
- self.main_widget.update_items_cache(self.all_values)
- else:
+ self.main_widget.update_items_cache(self.all_values)
+ if not self.col_metadata['is_multiple']:
val = self.get_initial_value(book_ids)
self.initial_val = val = self.normalize_db_val(val)
idx = None
From fc3123e4c171f504f5e8fa9cd14f6c444b1e575a Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 12:20:07 -0700
Subject: [PATCH 50/55] Refactor advanced search dialog to use new completer
---
src/calibre/gui2/dialogs/search.py | 7 ++-----
src/calibre/gui2/dialogs/search.ui | 14 +++++++-------
2 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py
index ab3fd3ec4e..0fbe188aa5 100644
--- a/src/calibre/gui2/dialogs/search.py
+++ b/src/calibre/gui2/dialogs/search.py
@@ -3,7 +3,7 @@ __copyright__ = '2008, Kovid Goyal '
import re, copy
-from PyQt4.Qt import QDialog, QDialogButtonBox, QCompleter, Qt
+from PyQt4.Qt import QDialog, QDialogButtonBox
from calibre.gui2.dialogs.search_ui import Ui_Dialog
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
@@ -29,20 +29,17 @@ class SearchDialog(QDialog, Ui_Dialog):
name = name.strip().replace('|', ',')
self.authors_box.addItem(name)
self.authors_box.setEditText('')
- self.authors_box.completer().setCompletionMode(QCompleter.PopupCompletion)
- self.authors_box.setAutoCompletionCaseSensitivity(Qt.CaseInsensitive)
self.authors_box.set_separator('&')
self.authors_box.set_space_before_sep(True)
self.authors_box.update_items_cache(db.all_author_names())
all_series = db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
+ self.series_box.update_items_cache([x[1] for x in all_series])
for i in all_series:
id, name = i
self.series_box.addItem(name)
self.series_box.setEditText('')
- self.series_box.completer().setCompletionMode(QCompleter.PopupCompletion)
- self.series_box.setAutoCompletionCaseSensitivity(Qt.CaseInsensitive)
all_tags = db.all_tags()
self.tags_box.update_items_cache(all_tags)
diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui
index 1d013a1e9f..842787a2da 100644
--- a/src/calibre/gui2/dialogs/search.ui
+++ b/src/calibre/gui2/dialogs/search.ui
@@ -265,21 +265,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
@@ -360,14 +360,14 @@
- CompleteLineEdit
+ MultiCompleteLineEdit
QLineEdit
-
+
- CompleteComboBox
+ MultiCompleteComboBox
QComboBox
-
+
From c7c3027c8d2602687e1f6cb198486b19b1edd671 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 12:29:54 -0700
Subject: [PATCH 51/55] Refactor bulk metadata edit dialog to use new completer
(apart from S&R widgets)
---
src/calibre/gui2/dialogs/metadata_bulk.py | 2 ++
src/calibre/gui2/dialogs/metadata_bulk.ui | 23 +++++++++--------------
2 files changed, 11 insertions(+), 14 deletions(-)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py
index 533a344de5..9239a0e136 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.py
+++ b/src/calibre/gui2/dialogs/metadata_bulk.py
@@ -764,6 +764,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
def initialize_series(self):
all_series = self.db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
+ self.series.update_items_cache([x[1] for x in all_series])
for i in all_series:
id, name = i
@@ -773,6 +774,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
def initialize_publisher(self):
all_publishers = self.db.all_publishers()
all_publishers.sort(key=lambda x : sort_key(x[1]))
+ self.publisher.update_items_cache([x[1] for x in all_publishers])
for i in all_publishers:
id, name = i
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui
index b0f2c144fc..ecdb396662 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
@@ -1072,19 +1072,14 @@ not multiple and the destination field is multiple
- EnComboBox
+ MultiCompleteComboBox
QComboBox
-
+
- CompleteComboBox
- QComboBox
-
-
-
- CompleteLineEdit
+ MultiCompleteLineEdit
QLineEdit
-
+
HistoryLineEdit
From b02186802f8320c1bcb4f1bf11c09624c8bec29d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 12:43:19 -0700
Subject: [PATCH 52/55] ...
---
src/calibre/gui2/convert/metadata.py | 10 +++++++---
src/calibre/gui2/convert/metadata.ui | 9 ++-------
src/calibre/gui2/custom_column_widgets.py | 3 +++
src/calibre/gui2/dialogs/metadata_bulk.py | 2 ++
src/calibre/gui2/dialogs/search.py | 1 +
5 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/calibre/gui2/convert/metadata.py b/src/calibre/gui2/convert/metadata.py
index 23cac74cf8..81274f25a8 100644
--- a/src/calibre/gui2/convert/metadata.py
+++ b/src/calibre/gui2/convert/metadata.py
@@ -70,9 +70,6 @@ class MetadataWidget(Widget, Ui_Form):
def initialize_metadata_options(self):
self.initialize_combos()
self.author.editTextChanged.connect(self.deduce_author_sort)
- self.author.set_separator('&')
- self.author.set_space_before_sep(True)
- self.author.update_items_cache(self.db.all_author_names())
mi = self.db.get_metadata(self.book_id, index_is_id=True)
self.title.setText(mi.title)
@@ -109,6 +106,9 @@ class MetadataWidget(Widget, Ui_Form):
def initalize_authors(self):
all_authors = self.db.all_authors()
all_authors.sort(key=lambda x : sort_key(x[1]))
+ self.author.set_separator('&')
+ self.author.set_space_before_sep(True)
+ self.author.update_items_cache(self.db.all_author_names())
for i in all_authors:
id, name = i
@@ -124,6 +124,8 @@ class MetadataWidget(Widget, Ui_Form):
def initialize_series(self):
all_series = self.db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
+ self.series.set_separator(None)
+ self.series.update_items_cache([x[1] for x in all_series])
for i in all_series:
id, name = i
@@ -133,6 +135,8 @@ class MetadataWidget(Widget, Ui_Form):
def initialize_publisher(self):
all_publishers = self.db.all_publishers()
all_publishers.sort(key=lambda x : sort_key(x[1]))
+ self.publisher.set_separator(None)
+ self.publisher.update_items_cache([x[1] for x in all_publishers])
for i in all_publishers:
id, name = i
diff --git a/src/calibre/gui2/convert/metadata.ui b/src/calibre/gui2/convert/metadata.ui
index 47d983d870..95ccac6890 100644
--- a/src/calibre/gui2/convert/metadata.ui
+++ b/src/calibre/gui2/convert/metadata.ui
@@ -213,7 +213,7 @@
-
-
+
10
@@ -248,7 +248,7 @@
-
-
+
true
@@ -276,11 +276,6 @@
QLineEdit
-
- EnComboBox
- QComboBox
-
-
MultiCompleteComboBox
QComboBox
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index 0555d42b4f..5180999379 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -233,6 +233,7 @@ class Text(Base):
w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
else:
w = MultiCompleteComboBox(parent)
+ w.set_separator(None)
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
w.setMinimumContentsLength(25)
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w]
@@ -708,6 +709,7 @@ class BulkSeries(BulkBase):
def initialize(self, book_id):
self.idx_widget.setChecked(False)
+ self.main_widget.set_separator(None)
self.main_widget.update_items_cache(self.all_values)
for c in self.all_values:
self.main_widget.addItem(c)
@@ -835,6 +837,7 @@ class BulkText(BulkBase):
w.checkbox.stateChanged.connect(self.a_c_checkbox_changed)
else:
self.make_widgets(parent, MultiCompleteComboBox)
+ self.main_widget.set_separator(None)
self.main_widget.setSizeAdjustPolicy(
self.main_widget.AdjustToMinimumContentsLengthWithIcon)
self.main_widget.setMinimumContentsLength(25)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py
index 9239a0e136..12f49baaca 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.py
+++ b/src/calibre/gui2/dialogs/metadata_bulk.py
@@ -764,6 +764,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
def initialize_series(self):
all_series = self.db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
+ self.series.set_separator(None)
self.series.update_items_cache([x[1] for x in all_series])
for i in all_series:
@@ -774,6 +775,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
def initialize_publisher(self):
all_publishers = self.db.all_publishers()
all_publishers.sort(key=lambda x : sort_key(x[1]))
+ self.publisher.set_separator(None)
self.publisher.update_items_cache([x[1] for x in all_publishers])
for i in all_publishers:
diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py
index 0fbe188aa5..9c91446f3c 100644
--- a/src/calibre/gui2/dialogs/search.py
+++ b/src/calibre/gui2/dialogs/search.py
@@ -35,6 +35,7 @@ class SearchDialog(QDialog, Ui_Dialog):
all_series = db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
+ self.series_box.set_separator(None)
self.series_box.update_items_cache([x[1] for x in all_series])
for i in all_series:
id, name = i
From f76a8e5f4f493eaaf2329db418cb9d84d7c17fdc Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 12:50:31 -0700
Subject: [PATCH 53/55] Refactor old edit metadata dialog to use new completer
---
src/calibre/gui2/dialogs/metadata_single.py | 4 ++++
src/calibre/gui2/dialogs/metadata_single.ui | 21 ++++++++-------------
2 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py
index fa20658c12..f36fd3019d 100644
--- a/src/calibre/gui2/dialogs/metadata_single.py
+++ b/src/calibre/gui2/dialogs/metadata_single.py
@@ -739,6 +739,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.series.setSizeAdjustPolicy(self.series.AdjustToContentsOnFirstShow)
all_series = self.db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
+ self.series.set_separator(None)
+ self.series.update_items_cache([x[1] for x in all_series])
series_id = self.db.series_id(self.row)
idx, c = None, 0
for i in all_series:
@@ -756,6 +758,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
def initialize_publisher(self):
all_publishers = self.db.all_publishers()
all_publishers.sort(key=lambda x : sort_key(x[1]))
+ self.publisher.set_separator(None)
+ self.publisher.update_items_cache([x[1] for x in all_publishers])
publisher_id = self.db.publisher_id(self.row)
idx, c = None, 0
for i in all_publishers:
diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui
index 23efc45399..5bcf268aaa 100644
--- a/src/calibre/gui2/dialogs/metadata_single.ui
+++ b/src/calibre/gui2/dialogs/metadata_single.ui
@@ -240,7 +240,7 @@ Using this button to create author sort will change author sort from red to gree
-
-
+
true
@@ -313,7 +313,7 @@ If the box is colored green, then text matches the individual author's sort stri
-
-
+
true
@@ -335,7 +335,7 @@ If the box is colored green, then text matches the individual author's sort stri
-
-
-
+
Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.
@@ -379,7 +379,7 @@ If the box is colored green, then text matches the individual author's sort stri
5
-
-
+
List of known series. You can add new series.
@@ -837,19 +837,14 @@ If the box is colored green, then text matches the individual author's sort stri
- EnComboBox
- QComboBox
-
-
-
- CompleteLineEdit
+ MultiCompleteLineEdit
QLineEdit
-
+
- CompleteComboBox
+ MultiCompleteComboBox
QComboBox
-
+
FormatList
From ef88d0b5ccc5f30cde2df90fc364d509c397576b Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 12:54:10 -0700
Subject: [PATCH 54/55] ...
---
src/calibre/gui2/dialogs/add_empty_book.py | 4 ++--
src/calibre/gui2/dialogs/search.ui | 5 -----
2 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/src/calibre/gui2/dialogs/add_empty_book.py b/src/calibre/gui2/dialogs/add_empty_book.py
index b8339f95f5..9e5fb07308 100644
--- a/src/calibre/gui2/dialogs/add_empty_book.py
+++ b/src/calibre/gui2/dialogs/add_empty_book.py
@@ -7,8 +7,8 @@ __license__ = 'GPL v3'
from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \
QApplication, QSpinBox, QToolButton, QIcon
from calibre.ebooks.metadata import authors_to_string, string_to_authors
-from calibre.gui2.widgets import CompleteComboBox
from calibre.utils.icu import sort_key
+from calibre.gui2.complete import MultiCompleteComboBox
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 = CompleteComboBox(self)
+ self.authors_combo = MultiCompleteComboBox(self)
self.authors_combo.setSizeAdjustPolicy(
self.authors_combo.AdjustToMinimumContentsLengthWithIcon)
self.authors_combo.setEditable(True)
diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui
index 842787a2da..eb6fffdb60 100644
--- a/src/calibre/gui2/dialogs/search.ui
+++ b/src/calibre/gui2/dialogs/search.ui
@@ -354,11 +354,6 @@
QLineEdit
-
- EnComboBox
- QComboBox
-
-
MultiCompleteLineEdit
QLineEdit
From f52ba0aae37d0534503619bd730ac5ffaa89636c Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 2 Feb 2011 14:13:24 -0700
Subject: [PATCH 55/55] ...
---
src/calibre/gui2/complete.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/complete.py b/src/calibre/gui2/complete.py
index 0ad8fb13d4..bdfbaaf0da 100644
--- a/src/calibre/gui2/complete.py
+++ b/src/calibre/gui2/complete.py
@@ -64,8 +64,6 @@ class CompleteWindow(QListView): # {{{
def do_selected(self, idx=None):
idx = self.currentIndex() if idx is None else idx
- #if not idx.isValid() and self.model().rowCount() > 0:
- # idx = self.model().index(0)
if idx.isValid():
data = unicode(self.model().data(idx, Qt.DisplayRole))
self.completion_selected.emit(data)
@@ -81,6 +79,9 @@ class CompleteWindow(QListView): # {{{
self.hide()
return True
elif key in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab):
+ if key == Qt.Key_Tab and not self.currentIndex().isValid():
+ if self.model().rowCount() > 0:
+ self.setCurrentIndex(self.model().index(0))
self.do_selected()
return True
elif key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp,