Tag browser: Add options in Preferences->Look & feel->Tab browser to hide empty categories and als to have the Find in the Tb browser show all matches instead of jumping from match to match.

Tag browser: The Find function for searching for items in the Tag
browser can now do exact matching by using = as a prefix.

Manage authors dialog: Add a checkbox to show only authors from the
current Virtual library. Fixes #1878940 [[Enhancement] Add Show only available items in current Virtual library option in the Manage authors screen](https://bugs.launchpad.net/calibre/+bug/1878940)

Manage authors dialog: Show a modified icon next to entries that have
changed

Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
Kovid Goyal 2020-05-17 15:04:23 +05:30
commit db14695c6e
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
10 changed files with 261 additions and 129 deletions

View File

@ -150,6 +150,7 @@ def create_defs():
defs['ui_style'] = 'calibre' if iswindows or isosx else 'system'
defs['tag_browser_old_look'] = False
defs['tag_browser_hide_empty_categories'] = False
defs['tag_browser_always_autocollapse'] = False
defs['book_list_tooltips'] = True
defs['show_layout_buttons'] = False
defs['bd_show_cover'] = True

View File

@ -47,10 +47,11 @@ class EditColumnDelegate(QItemDelegate):
class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
def __init__(self, parent, db, id_to_select, select_sort, select_link):
def __init__(self, parent, db, id_to_select, select_sort, select_link, find_aut_func):
QDialog.__init__(self, parent)
Ui_EditAuthorsDialog.__init__(self)
self.setupUi(self)
# Remove help icon on title bar
icon = self.windowIcon()
self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint))
@ -68,49 +69,15 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
self.buttonBox.accepted.connect(self.accepted)
self.apply_vl_checkbox.stateChanged.connect(self.use_vl_changed)
# Set up the column headings
# Set up the heading for sorting
self.table.setSelectionMode(QAbstractItemView.SingleSelection)
self.table.setColumnCount(3)
self.down_arrow_icon = QIcon(I('arrow-down.png'))
self.up_arrow_icon = QIcon(I('arrow-up.png'))
self.blank_icon = QIcon(I('blank.png'))
self.auth_col = QTableWidgetItem(_('Author'))
self.table.setHorizontalHeaderItem(0, self.auth_col)
self.auth_col.setIcon(self.blank_icon)
self.aus_col = QTableWidgetItem(_('Author sort'))
self.table.setHorizontalHeaderItem(1, self.aus_col)
self.aus_col.setIcon(self.up_arrow_icon)
self.aul_col = QTableWidgetItem(_('Link'))
self.table.setHorizontalHeaderItem(2, self.aul_col)
self.aus_col.setIcon(self.blank_icon)
# Add the data
self.authors = {}
auts = db.get_authors_with_ids()
self.table.setRowCount(len(auts))
select_item = None
completion_data = []
for row, (_id, author, sort, link) in enumerate(auts):
author = author.replace('|', ',')
self.authors[_id] = (author, sort, link)
completion_data.append(author)
aut = tableItem(author)
aut.setData(Qt.UserRole, _id)
sort = tableItem(sort)
link = tableItem(link)
self.table.setItem(row, 0, aut)
self.table.setItem(row, 1, sort)
self.table.setItem(row, 2, link)
if id_to_select in (_id, author):
if select_sort:
select_item = sort
elif select_link:
select_item = link
else:
select_item = aut
self.table.setItemDelegate(EditColumnDelegate(completion_data))
self.find_aut_func = find_aut_func
self.table.resizeColumnsToContents()
if self.table.columnWidth(2) < 200:
self.table.setColumnWidth(2, 200)
@ -118,28 +85,14 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
# set up the cellChanged signal only after the table is filled
self.table.cellChanged.connect(self.cell_changed)
# set up sort buttons
self.sort_by_author.setCheckable(True)
self.sort_by_author.setChecked(False)
self.sort_by_author.clicked.connect(self.do_sort_by_author)
self.author_order = 1
self.sort_by_author_sort.clicked.connect(self.do_sort_by_author_sort)
self.sort_by_author_sort.setCheckable(True)
self.sort_by_author_sort.setChecked(True)
self.author_sort_order = 1
self.recalc_author_sort.clicked.connect(self.do_recalc_author_sort)
self.auth_sort_to_author.clicked.connect(self.do_auth_sort_to_author)
# Position on the desired item
if select_item is not None:
self.table.setCurrentItem(select_item)
self.table.editItem(select_item)
self.start_find_pos = select_item.row() * 2 + select_item.column()
else:
self.table.setCurrentCell(0, 0)
self.start_find_pos = -1
# Capture clicks on the horizontal header to sort the table columns
hh = self.table.horizontalHeader()
hh.setSectionsClickable(True)
hh.sectionClicked.connect(self.header_clicked)
hh.sectionResized.connect(self.table_column_resized)
# set up the search box
self.find_box.initialize('manage_authors_search')
@ -165,7 +118,101 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.table.setContextMenuPolicy(Qt.CustomContextMenu)
self.table.customContextMenuRequested.connect(self.show_context_menu)
# Fetch the data
self.authors = {}
self.original_authors = {}
auts = db.new_api.author_data()
self.completion_data = []
for id_, v in auts.items():
name = v['name']
name = name.replace('|', ',')
self.completion_data.append(name)
self.authors[id_] = {'name': name, 'sort': v['sort'], 'link': v['link']}
self.original_authors[id_] = {'name': name, 'sort': v['sort'],
'link': v['link']}
self.edited_icon = QIcon(I('edit_input.png'))
self.down_arrow_icon = QIcon(I('arrow-down.png'))
self.up_arrow_icon = QIcon(I('arrow-up.png'))
self.blank_icon = QIcon(I('blank.png'))
self.last_sorted_by = 'sort'
self.author_order = 1
self.author_sort_order = 0
self.link_order = 1
self.show_table(id_to_select, select_sort, select_link)
def use_vl_changed(self, x):
self.show_table(None, None, None)
def show_table(self, id_to_select, select_sort, select_link):
auts_to_show = [t[0] for t in
self.find_aut_func(use_virtual_library = self.apply_vl_checkbox.isChecked())]
self.table.blockSignals(True)
self.table.clear()
self.table.setColumnCount(3)
self.auth_col = QTableWidgetItem(_('Author'))
self.table.setHorizontalHeaderItem(0, self.auth_col)
self.aus_col = QTableWidgetItem(_('Author sort'))
self.table.setHorizontalHeaderItem(1, self.aus_col)
self.aul_col = QTableWidgetItem(_('Link'))
self.table.setHorizontalHeaderItem(2, self.aul_col)
self.table.setRowCount(len(auts_to_show))
select_item = None
row = 0
for id_, v in self.authors.items():
if id_ not in auts_to_show:
continue
name, sort, link = (v['name'], v['sort'], v['link'])
orig = self.original_authors[id_]
name = name.replace('|', ',')
name_item = tableItem(name)
name_item.setData(Qt.UserRole, id_)
if name != orig['name']:
name_item.setIcon(self.edited_icon)
sort_item = tableItem(sort)
if sort != orig['sort']:
sort_item.setIcon(self.edited_icon)
link_item = tableItem(link)
if link != orig['link']:
link_item.setIcon(self.edited_icon)
self.table.setItem(row, 0, name_item)
self.table.setItem(row, 1, sort_item)
self.table.setItem(row, 2, link_item)
if id_to_select and id_to_select in (id_, name):
print('id', id_to_select)
if select_sort:
select_item = sort_item
elif select_link:
select_item = link_item
else:
select_item = name_item
row += 1
self.table.setItemDelegate(EditColumnDelegate(self.completion_data))
if self.last_sorted_by == 'sort':
self.author_sort_order = 1 if self.author_sort_order == 0 else 0
self.do_sort_by_author_sort()
elif self.last_sorted_by == 'author':
self.author_order = 1 if self.author_order == 0 else 0
self.do_sort_by_author()
else:
self.link_order = 1 if self.link_order == 0 else 0
self.do_sort_by_link()
# Position on the desired item
if select_item is not None:
self.table.setCurrentItem(select_item)
self.table.editItem(select_item)
self.start_find_pos = select_item.row() * 2 + select_item.column()
else:
self.table.setCurrentCell(0, 0)
self.start_find_pos = -1
self.table.blockSignals(False)
def save_state(self):
self.table_column_widths = []
@ -174,6 +221,11 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
gprefs['manage_authors_table_widths'] = self.table_column_widths
gprefs['manage_authors_dialog_geometry'] = bytearray(self.saveGeometry())
def table_column_resized(self, col, old, new):
self.table_column_widths = []
for c in range(0, self.table.columnCount()):
self.table_column_widths.append(self.table.columnWidth(c))
def resizeEvent(self, *args):
QDialog.resizeEvent(self, *args)
if self.table_column_widths is not None:
@ -216,7 +268,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
ca.triggered.connect(self.copy_au_to_aus)
m.addSeparator()
ca = m.addAction(_("Show books by author in book list"))
ca.triggered.connect(self.search)
ca.triggered.connect(self.search_in_book_list)
else:
ca = m.addAction(_('Copy to author'))
ca.triggered.connect(self.copy_aus_to_au)
@ -224,7 +276,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
m.addMenu(case_menu)
m.exec_(self.table.mapToGlobal(point))
def search(self):
def search_in_book_list(self):
from calibre.gui2.ui import get_gui
row = self.context_item.row()
get_gui().search.set_search_string(self.table.item(row, 0).text())
@ -294,35 +346,48 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.not_found_label.setVisible(True)
self.not_found_label_timer.start(1500)
def header_clicked(self, idx):
if idx == 0:
self.do_sort_by_author()
elif idx == 1:
self.do_sort_by_author_sort()
else:
self.do_sort_by_link()
def do_sort_by_author(self):
self.last_sorted_by = 'author'
self.author_order = 1 if self.author_order == 0 else 0
self.table.sortByColumn(0, self.author_order)
self.sort_by_author.setChecked(True)
self.sort_by_author_sort.setChecked(False)
self.auth_col.setIcon(self.down_arrow_icon if self.author_order
else self.up_arrow_icon)
self.aus_col.setIcon(self.blank_icon)
self.aul_col.setIcon(self.blank_icon)
def do_sort_by_author_sort(self):
self.last_sorted_by = 'sort'
self.author_sort_order = 1 if self.author_sort_order == 0 else 0
self.table.sortByColumn(1, self.author_sort_order)
self.sort_by_author.setChecked(False)
self.sort_by_author_sort.setChecked(True)
self.aus_col.setIcon(self.down_arrow_icon if self.author_sort_order
else self.up_arrow_icon)
self.auth_col.setIcon(self.blank_icon)
self.aul_col.setIcon(self.blank_icon)
def do_sort_by_link(self):
self.last_sorted_by = 'link'
self.link_order = 1 if self.link_order == 0 else 0
self.table.sortByColumn(2, self.link_order)
self.aul_col.setIcon(self.down_arrow_icon if self.link_order
else self.up_arrow_icon)
self.auth_col.setIcon(self.blank_icon)
self.aus_col.setIcon(self.blank_icon)
def accepted(self):
self.save_state()
self.result = []
for row in range(0,self.table.rowCount()):
id = int(self.table.item(row, 0).data(Qt.UserRole))
aut = unicode_type(self.table.item(row, 0).text()).strip()
sort = unicode_type(self.table.item(row, 1).text()).strip()
link = unicode_type(self.table.item(row, 2).text()).strip()
orig_aut,orig_sort,orig_link = self.authors[id]
if orig_aut != aut or orig_sort != sort or orig_link != link:
self.result.append((id, orig_aut, aut, sort, link))
for id_, v in self.authors.items():
orig = self.original_authors[id_]
if orig != v:
self.result.append((id_, orig['name'], v['name'], v['sort'], v['link']))
def do_recalc_author_sort(self):
self.table.cellChanged.disconnect()
@ -347,8 +412,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.table.cellChanged.connect(self.cell_changed)
def cell_changed(self, row, col):
id_ = int(self.table.item(row, 0).data(Qt.UserRole))
if col == 0:
item = self.table.item(row, 0)
item.setIcon(self.edited_icon)
aut = unicode_type(item.text()).strip()
aut_list = string_to_authors(aut)
if len(aut_list) != 1:
@ -356,10 +423,18 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
_('You cannot change an author to multiple authors.')).exec_()
aut = ' % '.join(aut_list)
self.table.item(row, 0).setText(aut)
self.authors[id_]['name'] = aut
c = self.table.item(row, 1)
c.setText(author_to_author_sort(aut))
txt = author_to_author_sort(aut)
c.setText(txt)
self.authors[id_]['sort'] = txt
item = c
else:
item = self.table.item(row, col)
item.setIcon(self.edited_icon)
if col == 1:
self.authors[id_]['sort'] = item.text()
else:
self.authors[id_]['link'] = item.text()
self.table.setCurrentItem(item)
self.table.scrollToItem(item)

View File

@ -62,6 +62,18 @@
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="apply_vl_checkbox">
<property name="toolTip">
<string>&lt;p&gt;Show authors only if they appear in the
current Virtual library. Edits already done may be hidden but will
not be forgotten.&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Show only available items in current Virtual library</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -79,20 +91,6 @@
</item>
<item>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="sort_by_author">
<property name="text">
<string>Sort by &amp;author</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="sort_by_author_sort">
<property name="text">
<string>Sort by author &amp;sort</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="recalc_author_sort">
<property name="toolTip">

View File

@ -281,7 +281,7 @@ class Quickview(QDialog, Ui_Quickview):
def item_doubleclicked(self, item):
tb = self.gui.stack.tb_widget
tb.set_focus_to_find_box()
tb.item_search.lineEdit().setText(self.current_key + ':' + item.text())
tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text())
tb.do_find()
def show_context_menu(self, point):

View File

@ -65,9 +65,9 @@
<item row="1" column="0" colspan="3">
<widget class="QCheckBox" name="apply_vl_checkbox">
<property name="toolTip">
<string>&lt;p&gt;Show items in the Available items box only if they appear in the
current Virtual library. Applied items not in the Virtual library will be marked
&quot;not on any book&quot;.&lt;/p&gt;</string>
<string>&lt;p&gt;Show items only if they appear in the
current Virtual library. Edits already done may be hidden but will
not be forgotten.&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Show only available items in current Virtual library</string>

View File

@ -412,6 +412,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('row_numbers_in_book_list', gprefs)
r('tag_browser_old_look', gprefs)
r('tag_browser_hide_empty_categories', gprefs)
r('tag_browser_always_autocollapse', gprefs)
r('tag_browser_show_tooltips', gprefs)
r('bd_show_cover', gprefs)
r('bd_overlay_cover_size', gprefs)

View File

@ -995,6 +995,18 @@ then the tags will be displayed each on their own line.</string>
</property>
</widget>
</item>
<item row="13" column="0" colspan="2">
<widget class="QCheckBox" name="opt_tag_browser_always_autocollapse">
<property name="toolTip">
<string>When checked, Find in the Tag browser will show all items
that match the search instead of the first one. If Hide empty categories is
also checked then only categories containing a matched item will be shown.</string>
</property>
<property name="text">
<string>Find &amp;shows all items that match in the Tag browser</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="opt_tags_browser_partition_method">
<property name="toolTip">

View File

@ -125,6 +125,8 @@ class CreateVirtualLibrary(QDialog): # {{{
self.vl_text.textChanged.connect(self.search_text_changed)
la2.setBuddy(self.vl_text)
gl.addWidget(self.vl_text, 1, 1)
# Trigger the textChanged signal to initialize the saved searches box
self.vl_text.setText(' ')
self.vl_text.setText(_build_full_search_string(self.gui))
self.sl = sl = QLabel('<p>'+_('Create a Virtual library based on: ')+
@ -213,10 +215,7 @@ class CreateVirtualLibrary(QDialog): # {{{
txt = ''
else:
txt = ''
if len(searches) > 1:
self.saved_searches_label.setPlainText('\n'.join(searches))
else:
self.saved_searches_label.setPlainText('')
def name_text_edited(self, new_name):
self.new_name = unicode_type(new_name)

View File

@ -994,9 +994,19 @@ class TagsModel(QAbstractItemModel): # {{{
self.restriction_error.emit()
if self.filter_categories_by:
if self.filter_categories_by.startswith('='):
use_exact_match = True
filter_by = self.filter_categories_by[1:]
else:
use_exact_match = False
filter_by = self.filter_categories_by
for category in data.keys():
if use_exact_match:
data[category] = [t for t in data[category]
if lower(t.name).find(self.filter_categories_by) >= 0]
if lower(t.name) == filter_by]
else:
data[category] = [t for t in data[category]
if lower(t.name).find(filter_by) >= 0]
# Build a dict of the keys that have data
tb_categories = self.db.field_metadata

View File

@ -11,7 +11,7 @@ from functools import partial
from PyQt5.Qt import (
Qt, QIcon, QWidget, QHBoxLayout, QVBoxLayout, QToolButton, QLabel, QFrame,
QTimer, QMenu, QActionGroup, QAction, QSizePolicy)
QTimer, QMenu, QActionGroup, QAction, QSizePolicy, pyqtSignal)
from calibre.gui2 import error_dialog, question_dialog, gprefs
from calibre.gui2.widgets import HistoryLineEdit
@ -231,15 +231,7 @@ class TagBrowserMixin(object): # {{{
self.tags_view.recount()
self.user_categories_edited()
def do_tags_list_edit(self, tag, category):
'''
Open the 'manage_X' dialog where X == category. If tag is not None, the
dialog will position the editor on that item.
'''
db = self.current_db
def get_book_ids(use_virtual_library):
def get_book_ids(self, use_virtual_library, db, category):
book_ids = None if not use_virtual_library else self.tags_view.model().get_book_ids_to_use()
data = db.new_api.get_categories(book_ids=book_ids)
if category in data:
@ -248,13 +240,22 @@ class TagBrowserMixin(object): # {{{
result = None
return result
def do_tags_list_edit(self, tag, category):
'''
Open the 'manage_X' dialog where X == category. If tag is not None, the
dialog will position the editor on that item.
'''
db = self.current_db
if category == 'series':
key = lambda x:sort_key(title_sort(x))
else:
key = sort_key
d = TagListEditor(self, cat_name=db.field_metadata[category]['name'],
tag_to_match=tag, get_book_ids=get_book_ids, sorter=key)
tag_to_match=tag,
get_book_ids=partial(self.get_book_ids, db=db, category=category),
sorter=key)
d.exec_()
if d.result() == d.Accepted:
to_rename = d.to_rename # dict of old id to new name
@ -361,7 +362,8 @@ class TagBrowserMixin(object): # {{{
'''
db = self.library_view.model().db
editor = EditAuthorsDialog(parent, db, id_, select_sort, select_link)
editor = EditAuthorsDialog(parent, db, id_, select_sort, select_link,
partial(self.get_book_ids, db=db, category='authors'))
if editor.exec_() == editor.Accepted:
# Save and restore the current selections. Note that some changes
# will cause sort orders to change, so don't bother with attempting
@ -404,6 +406,8 @@ class FindBox(HistoryLineEdit): # {{{
class TagBrowserBar(QWidget): # {{{
clear_find = pyqtSignal()
def __init__(self, parent):
QWidget.__init__(self, parent)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Preferred)
@ -429,13 +433,16 @@ class TagBrowserBar(QWidget): # {{{
self.item_search.setSizeAdjustPolicy(self.item_search.AdjustToMinimumContentsLengthWithIcon)
self.item_search.initialize('tag_browser_search')
self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive)
self.item_search.setToolTip(_(
'Search for items. This is a "contains" search; items containing the\n'
'text anywhere in the name will be found. You can limit the search\n'
'to particular categories using syntax similar to search. For example,\n'
'tags:foo will find foo in any tag, but not in authors etc. Entering\n'
'*foo will filter all categories at once, showing only those items\n'
'containing the text "foo"'))
self.item_search.setToolTip(
'<p>' +_(
'Search for items. If the text begins with equals (=) the search is '
'exact match, otherwise it is "contains" finding items containing '
'the text anywhere in the item name. Both exact and contains '
'searches ignore case. You can limit the search to particular '
'categories using syntax similar to search. For example, '
'tags:foo will find foo in any tag, but not in authors etc. Entering '
'*foo will collapse all categories then showing only those categories '
'with items containing the text "foo"') + '</p')
ac = QAction(parent)
parent.addAction(ac)
parent.keyboard.register_shortcut('tag browser find box',
@ -472,6 +479,7 @@ class TagBrowserBar(QWidget): # {{{
self.item_search.setCurrentIndex(0)
self.item_search.setCurrentText('')
self.toggle_search_button.click()
self.clear_find.emit()
def set_focus_to_find_box(self):
self.toggle_search_button.setChecked(True)
@ -517,6 +525,7 @@ class TagBrowserWidget(QFrame): # {{{
# Set up the find box & button
self.tb_bar = tbb = TagBrowserBar(self)
tbb.clear_find.connect(self.reset_find)
self.alter_tb, self.item_search, self.search_button = tbb.alter_tb, tbb.item_search, tbb.search_button
self.toggle_search_button = tbb.toggle_search_button
self._layout.addWidget(tbb)
@ -636,11 +645,42 @@ class TagBrowserWidget(QFrame): # {{{
def find_text(self):
return unicode_type(self.item_search.currentText()).strip()
def reset_find(self):
model = self.tags_view.model()
if model.get_categories_filter():
model.set_categories_filter(None)
self.tags_view.recount()
self.current_find_position = None
def find(self):
model = self.tags_view.model()
model.clear_boxed()
txt = self.find_text
# When a key is specified don't use the auto-collapsing search.
# A colon separates the lookup key from the search string.
# A leading colon says not to use autocollapsing search but search all keys
txt = self.find_text
colon = txt.find(':')
if colon >= 0:
key = self._parent.library_view.model().db.\
field_metadata.search_term_to_field_key(txt[:colon])
if key in self._parent.library_view.model().db.field_metadata:
txt = txt[colon+1:]
else:
key = ''
txt = txt[1:] if colon == 0 else txt
else:
key = None
# key is None indicates that no colon was found.
# key == '' means either a leading : was found or the key is invalid
# At this point the txt might have a leading =, in which case do an
# exact match search
if (gprefs.get('tag_browser_always_autocollapse', False) and
key is None and not txt.startswith('*')):
txt = '*' + txt
if txt.startswith('*'):
self.tags_view.collapseAll()
model.set_categories_filter(txt[1:])
@ -659,18 +699,14 @@ class TagBrowserWidget(QFrame): # {{{
self.search_button.setFocus(True)
self.item_search.lineEdit().blockSignals(False)
key = None
colon = txt.find(':') if len(txt) > 2 else 0
if colon > 0:
key = self._parent.library_view.model().db.\
field_metadata.search_term_to_field_key(txt[:colon])
if key in self._parent.library_view.model().db.field_metadata:
txt = txt[colon+1:]
if txt.startswith('='):
equals_match = True
txt = txt[1:]
else:
key = None
equals_match = False
self.current_find_position = \
model.find_item_node(key, txt, self.current_find_position)
model.find_item_node(key, txt, self.current_find_position,
equals_match=equals_match)
if self.current_find_position:
self.tags_view.show_item_at_path(self.current_find_position, box=True)