diff options
Diffstat (limited to 'plugins/externaltools/tools/outputpanel.py')
-rwxr-xr-x | plugins/externaltools/tools/outputpanel.py | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/plugins/externaltools/tools/outputpanel.py b/plugins/externaltools/tools/outputpanel.py new file mode 100755 index 00000000..a30aad72 --- /dev/null +++ b/plugins/externaltools/tools/outputpanel.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# Gedit External Tools plugin +# Copyright (C) 2005-2006 Steve Frécinaux <[email protected]> +# Copyright (C) 2010 Per Arneng <[email protected]> +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +__all__ = ('OutputPanel', 'UniqueById') + +import gtk, gedit +import pango +import gobject +import os +from weakref import WeakKeyDictionary +from capture import * +from gtk import gdk +import re +import gio +import linkparsing +import filelookup + +class UniqueById: + __shared_state = WeakKeyDictionary() + + def __init__(self, i): + if i in self.__class__.__shared_state: + self.__dict__ = self.__class__.__shared_state[i] + return True + else: + self.__class__.__shared_state[i] = self.__dict__ + return False + + def states(self): + return self.__class__.__shared_state + +class OutputPanel(UniqueById): + def __init__(self, datadir, window): + if UniqueById.__init__(self, window): + return + + callbacks = { + 'on_stop_clicked' : self.on_stop_clicked, + 'on_view_visibility_notify_event': self.on_view_visibility_notify_event, + 'on_view_motion_notify_event': self.on_view_motion_notify_event, + 'on_view_button_press_event': self.on_view_button_press_event + } + + self.window = window + self.ui = gtk.Builder() + self.ui.add_from_file(os.path.join(datadir, 'ui', 'outputpanel.ui')) + self.ui.connect_signals(callbacks) + + self.panel = self["output-panel"] + self['view'].modify_font(pango.FontDescription('Monospace')) + + buffer = self['view'].get_buffer() + + self.normal_tag = buffer.create_tag('normal') + + self.error_tag = buffer.create_tag('error') + self.error_tag.set_property('foreground', 'red') + + self.italic_tag = buffer.create_tag('italic') + self.italic_tag.set_property('style', pango.STYLE_OBLIQUE) + + self.bold_tag = buffer.create_tag('bold') + self.bold_tag.set_property('weight', pango.WEIGHT_BOLD) + + self.invalid_link_tag = buffer.create_tag('invalid_link') + + self.link_tag = buffer.create_tag('link') + self.link_tag.set_property('underline', pango.UNDERLINE_SINGLE) + + self.link_cursor = gdk.Cursor(gdk.HAND2) + self.normal_cursor = gdk.Cursor(gdk.XTERM) + + self.process = None + + self.links = [] + + self.link_parser = linkparsing.LinkParser() + self.file_lookup = filelookup.FileLookup() + + def set_process(self, process): + self.process = process + + def __getitem__(self, key): + # Convenience function to get an object from its name + return self.ui.get_object(key) + + def on_stop_clicked(self, widget, *args): + if self.process is not None: + self.write("\n" + _('Stopped.') + "\n", + self.italic_tag) + self.process.stop(-1) + + def scroll_to_end(self): + iter = self['view'].get_buffer().get_end_iter() + self['view'].scroll_to_iter(iter, 0.0) + return False # don't requeue this handler + + def clear(self): + self['view'].get_buffer().set_text("") + self.links = [] + + def visible(self): + panel = self.window.get_bottom_panel() + return panel.props.visible and panel.item_is_active(self.panel) + + def write(self, text, tag = None): + buffer = self['view'].get_buffer() + + end_iter = buffer.get_end_iter() + insert = buffer.create_mark(None, end_iter, True) + + if tag is None: + buffer.insert(end_iter, text) + else: + buffer.insert_with_tags(end_iter, text, tag) + + # find all links and apply the appropriate tag for them + links = self.link_parser.parse(text) + for lnk in links: + + insert_iter = buffer.get_iter_at_mark(insert) + lnk.start = insert_iter.get_offset() + lnk.start + lnk.end = insert_iter.get_offset() + lnk.end + + start_iter = buffer.get_iter_at_offset(lnk.start) + end_iter = buffer.get_iter_at_offset(lnk.end) + + tag = None + + # if the link points to an existing file then it is a valid link + if self.file_lookup.lookup(lnk.path) is not None: + self.links.append(lnk) + tag = self.link_tag + else: + tag = self.invalid_link_tag + + buffer.apply_tag(tag, start_iter, end_iter) + + buffer.delete_mark(insert) + gobject.idle_add(self.scroll_to_end) + + def show(self): + panel = self.window.get_bottom_panel() + panel.show() + panel.activate_item(self.panel) + + def update_cursor_style(self, view, x, y): + if self.get_link_at_location(view, x, y) is not None: + cursor = self.link_cursor + else: + cursor = self.normal_cursor + + view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(cursor) + + def on_view_motion_notify_event(self, view, event): + if event.window == view.get_window(gtk.TEXT_WINDOW_TEXT): + self.update_cursor_style(view, int(event.x), int(event.y)) + + return False + + def on_view_visibility_notify_event(self, view, event): + if event.window == view.get_window(gtk.TEXT_WINDOW_TEXT): + x, y, m = event.window.get_pointer() + self.update_cursor_style(view, x, y) + + return False + + def idle_grab_focus(self): + self.window.get_active_view().grab_focus() + return False + + def get_link_at_location(self, view, x, y): + """ + Get the link under a specified x,y coordinate. If no link exists then + None is returned. + """ + + # get the offset within the buffer from the x,y coordinates + buff_x, buff_y = view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, + x, y) + iter_at_xy = view.get_iter_at_location(buff_x, buff_y) + offset = iter_at_xy.get_offset() + + # find the first link that contains the offset + for lnk in self.links: + if offset >= lnk.start and offset <= lnk.end: + return lnk + + # no link was found at x,y + return None + + def on_view_button_press_event(self, view, event): + if event.button != 1 or event.type != gdk.BUTTON_PRESS or \ + event.window != view.get_window(gtk.TEXT_WINDOW_TEXT): + return False + + link = self.get_link_at_location(view, int(event.x), int(event.y)) + if link is None: + return False + + gfile = self.file_lookup.lookup(link.path) + + if gfile: + gedit.commands.load_uri(self.window, gfile.get_uri(), None, + link.line_nr) + gobject.idle_add(self.idle_grab_focus) + +# ex:ts=4:et: |