Wire up the search UI

This commit is contained in:
Kovid Goyal 2013-11-11 15:09:58 +05:30
parent 134b4f5982
commit d84fd0b5a4
4 changed files with 117 additions and 8 deletions

View File

@ -52,6 +52,7 @@ class Boss(QObject):
fl.edit_file.connect(self.edit_file_requested)
self.gui.central.current_editor_changed.connect(self.apply_current_editor_state)
self.gui.central.close_requested.connect(self.editor_close_requested)
self.gui.central.search_panel.search_triggered.connect(self.search)
def mkdtemp(self, prefix=''):
self.container_count += 1
@ -258,6 +259,50 @@ class Boss(QObject):
self.update_global_history_actions()
# }}}
def search(self, action, overrides=None):
' Run a search/replace '
sp = self.gui.central.search_panel
# Ensure the search panel is visible
sp.setVisible(True)
ed = self.gui.central.current_editor
name = None
for n, x in editors.iteritems():
if x is ed:
name = n
break
state = sp.state
if overrides:
state.update(overrides)
searchable_names = self.gui.file_list.searchable_names
where = state['where']
err = None
if name is None and where in {'current', 'selected-text'}:
err = _('No file is being edited.')
elif where == 'selected' and not searchable_names['selected']:
err = _('No files are selected in the Files Browser')
if not err and not state['find']:
err = _('No search query specified')
if err:
return error_dialog(self.gui, _('Cannot search'), err, show=True)
del err
if where == 'current':
files = [name]
editor = ed
elif where in {'styles', 'text', 'selected'}:
files = searchable_names[where]
if name in files:
editor = ed
else:
common = set(editors).intersection(set(files))
if common:
name = next(x for x in files if x in common)
editor = editors[name]
self.gui.central.show_editor(editor)
else:
pass # TODO: Find the first name with a match and open its editor
else:
pass # selected text TODO: Implement this
def save_book(self):
c = current_container()
for name, ed in editors.iteritems():

View File

@ -338,6 +338,26 @@ class FileList(QTreeWidget):
syntax = {'text':'html', 'styles':'css'}.get(category, None)
self.edit_file.emit(name, syntax, mime)
@property
def all_files(self):
return (category.child(i) for category in self.categories.itervalues() for i in xrange(category.childCount()))
@property
def searchable_names(self):
ans = {'text':[], 'styles':[], 'selected':[]}
for item in self.all_files:
category = unicode(item.data(0, CATEGORY_ROLE).toString())
mime = unicode(item.data(0, MIME_ROLE).toString())
name = unicode(item.data(0, NAME_ROLE).toString())
ok = category in {'text', 'styles'}
if ok:
ans[category].append(name)
if not ok and category == 'misc':
ok = mime in {guess_type('a.'+x) for x in ('opf', 'ncx', 'txt', 'xml')}
if ok and item.isSelected():
ans['selected'].append(name)
return ans
class FileListWidget(QWidget):
delete_requested = pyqtSignal(object, object)
@ -359,4 +379,7 @@ class FileListWidget(QWidget):
def build(self, container, preserve_state=True):
self.file_list.build(container, preserve_state=preserve_state)
@property
def searchable_names(self):
return self.file_list.searchable_names

View File

@ -21,11 +21,9 @@ REGEX_FLAGS = regex.VERSION1 | regex.WORD | regex.FULLCASE | regex.MULTILINE | r
class PushButton(QPushButton):
triggered = pyqtSignal(object)
def __init__(self, text, action, parent):
QPushButton.__init__(self, text, parent)
self.clicked.connect(lambda : self.triggered.emit(action))
self.clicked.connect(lambda : parent.search_triggered.emit(action))
class SearchWidget(QWidget):
@ -38,6 +36,8 @@ class SearchWidget(QWidget):
'dot_all': False,
}
search_triggered = pyqtSignal(object)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QGridLayout(self)
@ -168,7 +168,7 @@ class SearchWidget(QWidget):
@dynamic_property
def where(self):
wm = {0:'current', 1:'text', 2:'style', 3:'selected-files', 4:'selected-text'}
wm = {0:'current', 1:'text', 2:'styles', 3:'selected', 4:'selected-text'}
def fget(self):
return wm[self.where_box.currentIndex()]
def fset(self, val):
@ -223,8 +223,12 @@ class SearchWidget(QWidget):
def save_state(self):
tprefs.set('find-widget-state', self.state)
# }}}
class SearchPanel(QWidget):
search_triggered = pyqtSignal(object)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QHBoxLayout()
@ -241,6 +245,7 @@ class SearchPanel(QWidget):
self.widget = SearchWidget(self)
l.addWidget(self.widget)
self.restore_state, self.save_state = self.widget.restore_state, self.widget.save_state
self.widget.search_triggered.connect(self.search_triggered)
def hide_panel(self):
self.setVisible(False)
@ -248,5 +253,11 @@ class SearchPanel(QWidget):
def show_panel(self):
self.setVisible(True)
self.widget.find_text.setFocus(Qt.OtherFocusReason)
# }}}
@property
def state(self):
ans = self.widget.state
ans['find'] = self.widget.find
ans['replace'] = self.widget.replace
return ans

View File

@ -154,7 +154,7 @@ class Main(MainWindow):
group = _('Global Actions')
def reg(icon, text, target, sid, keys, description):
ac = actions[sid] = QAction(QIcon(I(icon)), text, self)
ac = actions[sid] = QAction(QIcon(I(icon)), text, self) if icon else QAction(text, self)
ac.setObjectName('action-' + sid)
if target is not None:
ac.triggered.connect(target)
@ -212,11 +212,27 @@ class Main(MainWindow):
# Preview actions
group = _('Preview')
self.action_auto_reload_preview = reg('auto-reload.png', _('Auto reload preview'), None, 'auto-reload-preview', (), _('Auto reload preview'))
self.action_reload_preview = reg('view-refresh.png', _('Refresh preview'), None, 'reload-preview', ('F5', 'Ctrl+R'), _('Refresh preview'))
self.action_reload_preview = reg('view-refresh.png', _('Refresh preview'), None, 'reload-preview', ('F5',), _('Refresh preview'))
# Search actions
group = _('Search')
self.action_find = reg('search.png', _('&Find/Replace'), self.central.show_find, 'find-replace', ('Ctrl+F',), _('Find/Replace'))
self.action_find = reg('search.png', _('&Find/Replace'), self.central.show_find, 'find-replace', ('Ctrl+F',), _('Show the Find/Replace panel'))
def sreg(name, text, action, overrides={}, keys=(), description=None, icon=None):
return reg(icon, text, partial(self.boss.search, action, overrides), name, keys, description or text.replace('&', ''))
self.action_find_next = sreg('find-next', _('Find &Next'),
'find', {'direction':'down'}, ('F3', 'Ctrl+G'), _('Find next match'))
self.action_find_previous = sreg('find-previous', _('Find &Previous'),
'find', {'direction':'up'}, ('Shift+F3', 'Shift+Ctrl+G'), _('Find previous match'))
self.action_replace = sreg('replace', _('Replace'),
'replace', keys=('Ctrl+R'), description=_('Replace current match'))
self.action_replace_next = sreg('replace-next', _('&Replace and find next'),
'replace-find', {'direction':'down'}, ('Ctrl+]'), _('Replace current match and find next'))
self.action_replace_previous = sreg('replace-previous', _('R&eplace and find previous'),
'replace-find', {'direction':'up'}, ('Ctrl+['), _('Replace current match and find previous'))
self.action_replace_all = sreg('replace-all', _('Replace &all'),
'replace-all', keys=('Ctrl+A'), description=_('Replace all matches'))
self.action_count = sreg('count-matches', _('&Count all'),
'count', keys=('Ctrl+N'), description=_('Count number of matches'))
def create_menubar(self):
b = self.menuBar()
@ -252,6 +268,20 @@ class Main(MainWindow):
elif name.endswith('-bar'):
t.addAction(ac)
e = b.addMenu(_('&Search'))
a = e.addAction
a(self.action_find)
e.addSeparator()
a(self.action_find_next)
a(self.action_find_previous)
e.addSeparator()
a(self.action_replace)
a(self.action_replace_next)
a(self.action_replace_previous)
a(self.action_replace_all)
e.addSeparator()
a(self.action_count)
def create_toolbars(self):
def create(text, name):
name += '-bar'