mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Wire up the search UI
This commit is contained in:
parent
134b4f5982
commit
d84fd0b5a4
@ -52,6 +52,7 @@ class Boss(QObject):
|
|||||||
fl.edit_file.connect(self.edit_file_requested)
|
fl.edit_file.connect(self.edit_file_requested)
|
||||||
self.gui.central.current_editor_changed.connect(self.apply_current_editor_state)
|
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.close_requested.connect(self.editor_close_requested)
|
||||||
|
self.gui.central.search_panel.search_triggered.connect(self.search)
|
||||||
|
|
||||||
def mkdtemp(self, prefix=''):
|
def mkdtemp(self, prefix=''):
|
||||||
self.container_count += 1
|
self.container_count += 1
|
||||||
@ -258,6 +259,50 @@ class Boss(QObject):
|
|||||||
self.update_global_history_actions()
|
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):
|
def save_book(self):
|
||||||
c = current_container()
|
c = current_container()
|
||||||
for name, ed in editors.iteritems():
|
for name, ed in editors.iteritems():
|
||||||
|
@ -338,6 +338,26 @@ class FileList(QTreeWidget):
|
|||||||
syntax = {'text':'html', 'styles':'css'}.get(category, None)
|
syntax = {'text':'html', 'styles':'css'}.get(category, None)
|
||||||
self.edit_file.emit(name, syntax, mime)
|
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):
|
class FileListWidget(QWidget):
|
||||||
|
|
||||||
delete_requested = pyqtSignal(object, object)
|
delete_requested = pyqtSignal(object, object)
|
||||||
@ -359,4 +379,7 @@ class FileListWidget(QWidget):
|
|||||||
def build(self, container, preserve_state=True):
|
def build(self, container, preserve_state=True):
|
||||||
self.file_list.build(container, preserve_state=preserve_state)
|
self.file_list.build(container, preserve_state=preserve_state)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def searchable_names(self):
|
||||||
|
return self.file_list.searchable_names
|
||||||
|
|
||||||
|
@ -21,11 +21,9 @@ REGEX_FLAGS = regex.VERSION1 | regex.WORD | regex.FULLCASE | regex.MULTILINE | r
|
|||||||
|
|
||||||
class PushButton(QPushButton):
|
class PushButton(QPushButton):
|
||||||
|
|
||||||
triggered = pyqtSignal(object)
|
|
||||||
|
|
||||||
def __init__(self, text, action, parent):
|
def __init__(self, text, action, parent):
|
||||||
QPushButton.__init__(self, text, 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):
|
class SearchWidget(QWidget):
|
||||||
|
|
||||||
@ -38,6 +36,8 @@ class SearchWidget(QWidget):
|
|||||||
'dot_all': False,
|
'dot_all': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
search_triggered = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
self.l = l = QGridLayout(self)
|
self.l = l = QGridLayout(self)
|
||||||
@ -168,7 +168,7 @@ class SearchWidget(QWidget):
|
|||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def where(self):
|
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):
|
def fget(self):
|
||||||
return wm[self.where_box.currentIndex()]
|
return wm[self.where_box.currentIndex()]
|
||||||
def fset(self, val):
|
def fset(self, val):
|
||||||
@ -223,8 +223,12 @@ class SearchWidget(QWidget):
|
|||||||
def save_state(self):
|
def save_state(self):
|
||||||
tprefs.set('find-widget-state', self.state)
|
tprefs.set('find-widget-state', self.state)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class SearchPanel(QWidget):
|
class SearchPanel(QWidget):
|
||||||
|
|
||||||
|
search_triggered = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
self.l = l = QHBoxLayout()
|
self.l = l = QHBoxLayout()
|
||||||
@ -241,6 +245,7 @@ class SearchPanel(QWidget):
|
|||||||
self.widget = SearchWidget(self)
|
self.widget = SearchWidget(self)
|
||||||
l.addWidget(self.widget)
|
l.addWidget(self.widget)
|
||||||
self.restore_state, self.save_state = self.widget.restore_state, self.widget.save_state
|
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):
|
def hide_panel(self):
|
||||||
self.setVisible(False)
|
self.setVisible(False)
|
||||||
@ -248,5 +253,11 @@ class SearchPanel(QWidget):
|
|||||||
def show_panel(self):
|
def show_panel(self):
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
self.widget.find_text.setFocus(Qt.OtherFocusReason)
|
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
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ class Main(MainWindow):
|
|||||||
group = _('Global Actions')
|
group = _('Global Actions')
|
||||||
|
|
||||||
def reg(icon, text, target, sid, keys, description):
|
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)
|
ac.setObjectName('action-' + sid)
|
||||||
if target is not None:
|
if target is not None:
|
||||||
ac.triggered.connect(target)
|
ac.triggered.connect(target)
|
||||||
@ -212,11 +212,27 @@ class Main(MainWindow):
|
|||||||
# Preview actions
|
# Preview actions
|
||||||
group = _('Preview')
|
group = _('Preview')
|
||||||
self.action_auto_reload_preview = reg('auto-reload.png', _('Auto reload preview'), None, 'auto-reload-preview', (), _('Auto reload 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
|
# Search actions
|
||||||
group = _('Search')
|
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):
|
def create_menubar(self):
|
||||||
b = self.menuBar()
|
b = self.menuBar()
|
||||||
@ -252,6 +268,20 @@ class Main(MainWindow):
|
|||||||
elif name.endswith('-bar'):
|
elif name.endswith('-bar'):
|
||||||
t.addAction(ac)
|
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_toolbars(self):
|
||||||
def create(text, name):
|
def create(text, name):
|
||||||
name += '-bar'
|
name += '-bar'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user