# -*- coding: utf-8 -*- # smartspaces.py # # Copyright (C) 2006 - Steve Frécinaux # # 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., 59 Temple Place, Suite 330, # Boston, MA 02111-1307, USA. import gedit import gtk class SmartSpacesViewHelper(object): def __init__(self, view): self._view = view self._handlers = [ None, view.connect('notify::editable', self.on_notify), view.connect('notify::insert-spaces-instead-of-tabs', self.on_notify) ] self.update_active() def deactivate(self): for handler in self._handlers: if handler is not None: self._view.disconnect(handler) def update_active(self): # Don't activate the feature if the buffer isn't editable or if # we're using tabs active = self._view.get_editable() and \ self._view.get_insert_spaces_instead_of_tabs() if active and self._handlers[0] is None: self._handlers[0] = self._view.connect('key-press-event', self.on_key_press_event) elif not active and self._handlers[0] is not None: self._view.disconnect(self._handlers[0]) self._handlers[0] = None def on_notify(self, view, pspec): self.update_active() def on_key_press_event(self, view, event): # Only take care of backspace if event.keyval != gtk.keysyms.BackSpace: return False doc = view.get_buffer() if doc.get_has_selection(): return False cur = doc.get_iter_at_mark(doc.get_insert()) offset = cur.get_line_offset() if offset == 0: # We're at the begining of the line, so we can't obviously # unindent in this case return False start = cur.copy() start.backward_char() # If the previous char is a tab, we should just remove it if start.get_char() == '\t': doc.begin_user_action() doc.delete(start, cur) doc.end_user_action() return True # Otherwise, check how many spaces we're able to remove max_move = (offset - 1) % view.get_tab_width() + 1 moved = 0 while moved < max_move and start.get_char() == ' ': start.backward_char() moved += 1 start.forward_char() if moved == 0: # The iterator hasn't moved, so there is nothing to remove return False # Actually delete the spaces doc.begin_user_action() doc.delete(start, cur) doc.end_user_action() return True class SmartSpacesPlugin(gedit.Plugin): WINDOW_DATA_KEY = "SmartSpacesPluginWindowData" VIEW_DATA_KEY = "SmartSpacesPluginViewData" def __init__(self): gedit.Plugin.__init__(self) def add_helper(self, view): helper = SmartSpacesViewHelper(view) view.set_data(self.VIEW_DATA_KEY, helper) def remove_helper(self, view): view.get_data(self.VIEW_DATA_KEY).deactivate() view.set_data(self.VIEW_DATA_KEY, None) def activate(self, window): for view in window.get_views(): self.add_helper(view) handler_id = window.connect("tab-added", lambda w, t: self.add_helper(t.get_view())) window.set_data(self.WINDOW_DATA_KEY, handler_id) def deactivate(self, window): handler_id = window.get_data(self.WINDOW_DATA_KEY) window.disconnect(handler_id) window.set_data(self.WINDOW_DATA_KEY, None) for view in window.get_views(): self.remove_helper(view) def update_ui(self, window): pass # ex:ts=4:et: