diff options
-rw-r--r-- | GstDebugViewer/Plugins/FindBar.py | 219 | ||||
-rw-r--r-- | data/gst-debug-viewer.glade | 41 |
2 files changed, 246 insertions, 14 deletions
diff --git a/GstDebugViewer/Plugins/FindBar.py b/GstDebugViewer/Plugins/FindBar.py new file mode 100644 index 0000000..21cfe5a --- /dev/null +++ b/GstDebugViewer/Plugins/FindBar.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8; mode: python; -*- +# +# GStreamer Debug Viewer - View and analyze GStreamer debug log files +# +# Copyright (C) 2007 René Stadler <mail@renestadler.de> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 3 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see <http://www.gnu.org/licenses/>. + +"""GStreamer Debug Viewer timeline widget plugin.""" + +import logging + +from GstDebugViewer import Common, Data, GUI +from GstDebugViewer.Plugins import * + +import gtk + +class SearchOperation (object): + + def __init__ (self, model, search_string, search_forward = True, start_position = None): + + self.model = model + self.search_string = search_string + self.search_forward = search_forward + self.start_position = start_position + +class SearchSentinel (object): + + def __init__ (self): + + self.dispatcher = Common.Data.GSourceDispatcher () + + def run_for (self, operation): + + self.dispatcher.cancel () + self.dispatcher (self.__process (operation)) + + def abort (self): + + self.dispatcher.cancel () + + def __process (self, operation): + + model = operation.model + + if operation.start_position is not None: + start_iter = model.iter_nth_child (None, operation.start_position) + else: + start_iter = model.iter_nth_child (None, 0) + + if not operation.search_forward: + # FIXME: + raise NotImplementedError ("backward search not supported yet") + + search_string = operation.search_string + col_id = model.COL_MESSAGE + model_get = model.get_value + iter_next = model.iter_next + + YIELD_LIMIT = 1000 + i = YIELD_LIMIT + tree_iter = start_iter + while tree_iter: + i -= 1 + if i == 0: + yield True + i = YIELD_LIMIT + msg = model_get (tree_iter, col_id) + if search_string in msg: + self.handle_match_found (model, tree_iter) + tree_iter = iter_next (tree_iter) + + self.handle_search_complete () + yield False + + def handle_match_found (self, model, tree_iter): + + pass + + def handle_search_complete (self): + + pass + +class FindBarWidget (gtk.HBox): + + def __init__ (self): + + gtk.HBox.__init__ (self) + + label = gtk.Label (_("Find:")) + self.pack_start (label, False, False, 0) + + self.entry = gtk.Entry () + self.pack_start (self.entry) + + self.show_all () + +class FindBarFeature (FeatureBase): + + def __init__ (self): + + FeatureBase.__init__ (self) + + self.matches = [] + + self.logger = logging.getLogger ("ui.findbar") + + self.action_group = gtk.ActionGroup ("FindBarActions") + self.action_group.add_toggle_actions ([("show-find-bar", + None, + _("Find Bar"), + "<Ctrl>F")]) + + self.bar = None + self.operation = None + + self.sentinel = SearchSentinel () + self.sentinel.handle_match_found = self.handle_match_found + self.sentinel.handle_search_complete = self.handle_search_complete + + def scroll_view_to_line (self, line_index): + + view = self.log_view + + path = (line_index,) + + start_path, end_path = view.get_visible_range () + + if path >= start_path and path <= end_path: + self.logger.debug ("line index %i already visible, not scrolling", line_index) + return + + self.logger.debug ("scrolling to line_index %i", line_index) + view.scroll_to_cell (path, use_align = True, row_align = .5) + + def handle_attach_window (self, window): + + ui = window.ui_manager + + ui.insert_action_group (self.action_group, 0) + + self.log_view = window.log_view + + self.merge_id = ui.new_merge_id () + ui.add_ui (self.merge_id, "/menubar/ViewMenu/ViewMenuAdditions", + "ViewFindBar", "show-find-bar", + gtk.UI_MANAGER_MENUITEM, False) + + box = window.widgets.vbox_view + self.bar = FindBarWidget () + box.pack_end (self.bar, False, False, 0) + self.bar.hide () + + action = self.action_group.get_action ("show-find-bar") + handler = self.handle_show_find_bar_action_activate + action.connect ("toggled", handler) + + self.bar.entry.connect ("changed", self.handle_entry_changed) + + def handle_detach_window (self, window): + + window.ui_manager.remove_ui (self.merge_id) + self.merge_id = None + + def handle_show_find_bar_action_activate (self, action): + + if action.props.active: + self.bar.show () + self.bar.entry.grab_focus () + else: + self.bar.hide () + + def handle_entry_changed (self, entry): + + # FIXME: If the new search operation is stricter than the previous one + # (find as you type!), re-use the previous results for a nice + # performance gain (by only searching in previous results again) + del self.matches[:] + + model = self.log_view.props.model + search_string = entry.props.text + if search_string == "": + self.logger.debug ("search string set to '', aborting search") + self.sentinel.abort () + # FIXME: Set start position + self.logger.debug ("starting search for %r", search_string) + self.operation = SearchOperation (model, search_string) + self.sentinel.run_for (self.operation) + + def handle_match_found (self, model, tree_iter): + + line_index = model.get_path (tree_iter)[0] + self.matches.append (line_index) + + if len (self.matches) == 1: + self.scroll_view_to_line (line_index) + elif len (self.matches) > 10000: + self.sentinel.abort () + + def handle_search_complete (self): + + self.logger.debug ("search for %r complete, got %i results", + self.operation.search_string, + len (self.matches)) + +class Plugin (PluginBase): + + features = [FindBarFeature] diff --git a/data/gst-debug-viewer.glade b/data/gst-debug-viewer.glade index edc13c8..9fb9bad 100644 --- a/data/gst-debug-viewer.glade +++ b/data/gst-debug-viewer.glade @@ -36,26 +36,39 @@ <property name="spacing">0</property> <child> - <widget class="GtkScrolledWindow" id="log_view_scrolled_window"> + <widget class="GtkVBox" id="vbox_view"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> - <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> - <property name="shadow_type">GTK_SHADOW_IN</property> - <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> <child> - <widget class="GtkTreeView" id="log_view"> + <widget class="GtkScrolledWindow" id="log_view_scrolled_window"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="headers_visible">True</property> - <property name="rules_hint">True</property> - <property name="reorderable">True</property> - <property name="enable_search">True</property> - <property name="fixed_height_mode">False</property> - <property name="hover_selection">False</property> - <property name="hover_expand">False</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="log_view"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">True</property> + <property name="reorderable">True</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> </child> </widget> <packing> |