import gtk import gedit from math import pi #todo: add menus to turn the feature on/off #todo: these should be real options #todo: options to only draw leading/trailing stuff #maybe: hl newlines and other chars? #issues: it doesn't work with hl current line DRAW_TABS = True DRAW_SPACES = True def draw_tab_at_iter(view, cr, iter): if not DRAW_TABS: return rect = view.get_iter_location(iter) x, y = view.buffer_to_window_coords (gtk.TEXT_WINDOW_TEXT, rect.x, rect.y + rect.height / 2) cr.save() cr.move_to(x + 4, y) cr.rel_line_to(rect.width - 8, 0) cr.rel_line_to(-5,-5) cr.rel_move_to(+5,+5) cr.rel_line_to(-5,+5) cr.restore() def draw_space_at_iter(view, cr, iter): if not DRAW_SPACES: return rect = view.get_iter_location(iter) x, y = view.buffer_to_window_coords (gtk.TEXT_WINDOW_TEXT, rect.x + rect.width / 2, rect.y + rect.height / 2) cr.save() cr.move_to(x, y) cr.arc(x, y, 1, 0, 2 * pi) cr.restore() def draw_tabs_and_spaces(view, event, start, end): i = start.copy(); cr = event.window.cairo_create() # we use the insensitive color? no idea if it's a good guess in general... cr.set_source_color (view.style.text[gtk.STATE_INSENSITIVE]) while i.compare(end) < 0: if i.get_char() == '\t': draw_tab_at_iter (view, cr, i) elif i.get_char() == ' ': draw_space_at_iter (view, cr, i) if not i.forward_char(): break cr.stroke() class DrawSpacesHelper(object): def expose_cb(self, view, event): if event.window == view.get_window(gtk.TEXT_WINDOW_TEXT): y = view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, event.area.x, event.area.y)[1] s = view.get_line_at_y(y)[0] e = view.get_line_at_y(y + event.area.height)[0] draw_tabs_and_spaces(view, event, s, e) def attach(self, view): self._view = view self.expose_id = view.connect('expose-event', self.expose_cb) def detach(self): self._view.disconnect(self.expose_id) class DrawSpacesPlugin(gedit.Plugin): def __init__(self): gedit.Plugin.__init__(self) def activate(self, window): for view in window.get_views(): self._create_view_helper(view) tab_added_id = window.connect("tab_added", lambda w, t: self._create_view_helper(t.get_view())) window.set_data("DrawSpacesPluginHandlerId", tab_added_id) def deactivate(self, window): tab_added_id = window.get_data("DrawSpacesPluginHandlerId") window.disconnect(tab_added_id) window.set_data("DrawSpacesPluginHandlerId", None) for view in window.get_views(): self._destroy_view_helper(view) def update_ui(self, window): pass def _create_view_helper(self, view): helper = DrawSpacesHelper() helper.attach(view) view.set_data("DrawSpacesHelper", helper) def _destroy_view_helper(self, view): helper = view.get_data("DrawSpacesHelper") helper.detach() view.set_data("DrawSpacesHelper", None)