class HistoryManager(plugin.Plugin): def __init__(self): self.editor = None self.engine = None self._enabled = False self.undomanager = None def enable(self): if self._enabled is True: return self.editor = scripts.editor.getEditor() self.engine = self.editor.getEngine() self._show_action = Action(u"History manager", checkable=True) self._undo_action = Action(u"Undo", "gui/icons/undo.png") self._redo_action = Action(u"Redo", "gui/icons/redo.png") self._next_action = Action(u"Next branch", "gui/icons/next_branch.png") self._prev_action = Action(u"Previous branch", "gui/icons/previous_branch.png") self._show_action.helptext = u"Toggle HistoryManager" self._undo_action.helptext = u"Undo action (CTRL+Z)" self._redo_action.helptext = u"Redo action (CTRL+SHIFT+Z)" self._next_action.helptext = u"Next branch (CTRL+ALT+Z" self._prev_action.helptext = u"Previous branch (CTRL+ALT+SHIFT+Z)" scripts.gui.action.activated.connect(self.toggle, sender=self._show_action) scripts.gui.action.activated.connect(self._undo, sender=self._undo_action) scripts.gui.action.activated.connect(self._redo, sender=self._redo_action) scripts.gui.action.activated.connect(self._next, sender=self._next_action) scripts.gui.action.activated.connect(self._prev, sender=self._prev_action) self._undo_group = ActionGroup(name=u"UndoGroup") self._undo_group.addAction(self._undo_action) self._undo_group.addAction(self._redo_action) self._undo_group.addAction(self._next_action) self._undo_group.addAction(self._prev_action) self.editor._tools_menu.addAction(self._show_action) self.editor._edit_menu.insertAction(self._undo_group, 0) self.editor._edit_menu.insertSeparator(position=1) events.postMapShown.connect(self.update) undomanager.changed.connect(self.update) self.buildGui() def disable(self): if self._enabled is False: return self.gui.hide() self.removeAllChildren() events.postMapShown.disconnect(self.update) undomanager.changed.disconnect(self.update) scripts.gui.action.activated.connect(self.toggle, sender=self._show_action) scripts.gui.action.activated.disconnect(self._undo, sender=self._undo_action) scripts.gui.action.activated.disconnect(self._redo, sender=self._redo_action) scripts.gui.action.activated.disconnect(self._next, sender=self._next_action) scripts.gui.action.activated.disconnect(self._prev, sender=self._prev_action) self.editor._tools_menu.removeAction(self._show_action) self.editor._tools_menu.removeAction(self._undo_group) def isEnabled(self): return self._enabled; def getName(self): return "History manager" def buildGui(self): self.gui = Panel(title=u"History") self.scrollarea = widgets.ScrollArea(min_size=(200,300)) self.list = widgets.ListBox() self.list.capture(self._itemSelected) self.gui.addChild(self.scrollarea) self.scrollarea.addChild(self.list) self.gui.position_technique = "right:center" def _linearUndo(self, target): mapview = self.editor.getActiveMapView() if mapview is None: return undomanager = mapview.getController().getUndoManager() current_item = undomanager.current_item # Redo? item = current_item count = 0 while item is not None: if item == target: undomanager.redo(count) break count += 1 item = item.next else: # Undo? count = 0 item = current_item while item is not None: if item == target: undomanager.undo(count) break count += 1 item = item.previous else: print "HistoryManager: Didn't find target item!" # Select the current item, important to see if the undo/redo didn't work as expected self.update() def _itemSelected(self): mapview = self.editor.getActiveMapView() if mapview is None: return undomanager = mapview.getController().getUndoManager() stackitem = self.list.selected_item.item if stackitem == undomanager.current_item: return if undomanager.getBranchMode() is False: self._linearUndo(stackitem) return searchlist = [] searchlist2 = [] parent = stackitem branch = parent.next while parent is not None: if parent is undomanager.first_item or len(parent._branches) > 1: searchlist.append( (parent, branch) ) branch = parent parent = parent.parent current_item = undomanager.current_item parent = current_item branch = parent.next while parent is not None: if parent is undomanager.first_item or len(parent._branches) > 1: searchlist2.append( (parent, branch) ) branch = parent parent = parent.parent searchlist.reverse() searchlist2.reverse() # Remove duplicate entries, except the last duplicate, so we don't undo # more items than necessary sl = len(searchlist); if len(searchlist2) < sl: sl = len(searchlist2) for s in range(sl): if searchlist[s][0] != searchlist[s][0]: searchlist = searchlist[s-1:] s_item = searchlist[0][0] # Undo until we reach the first shared parent i = 0 item = current_item while item is not None: if item == s_item: undomanager.undo(i) current_item = item break i += 1 item = item.previous else: print "Nada (undo)" return # Switch branches for s_item in searchlist: if s_item[0].setBranch(s_item[1]) is False: print "Warning: HistoryManager: Switching branch failed for: ", s_item # Redo to stackitem item = current_item i = 0 while item is not None: if item == stackitem: undomanager.redo(i) break i += 1 item = item.next else: print "Nada (redo)" # Select the current item, important to see if the undo/redo didn't work as expected self.update() def recursiveUpdate(self, item, indention, parent=None, branchstr="-"): items = [] branchnr = 0 class _ListItem: def __init__(self, str, item, parent): self.str = str self.item = item self.parent = parent def __str__(self): return self.str.encode("utf-8") while item is not None: listitem = _ListItem(u" "*indention + branchstr + " " + item.object.name, item, parent) items.append(listitem) branchnr = -1 for branch in item.getBranches(): branchnr += 1 if branchnr == 0: continue items.extend(self.recursiveUpdate(branch, indention+2, listitem, str(branchnr))) if self.undomanager.getBranchMode(): if len(item._branches) > 0: item = item._branches[0] else: break else: item = item.next return items def update(self): mapview = self.editor.getActiveMapView() if mapview is None: self.list.items = [] return self.undomanager = undomanager = mapview.getController().getUndoManager() items = [] items = self.recursiveUpdate(undomanager.first_item, 0) self.list.items = items i = 0 for it in items: if it.item == undomanager.current_item: self.list.selected = i break i += 1 self.scrollarea.adaptLayout(False) def show(self): self.update() self.gui.show() self._show_action.setChecked(True) def hide(self): self.gui.setDocked(False) self.gui.hide() self._show_action.setChecked(False) def _undo(self): if self.undomanager: self.undomanager.undo() def _redo(self): if self.undomanager: self.undomanager.redo() def _next(self): if self.undomanager: self.undomanager.nextBranch() def _prev(self): if self.undomanager: self.undomanager.previousBranch() def toggle(self): if self.gui.isVisible() or self.gui.isDocked(): self.hide() else: self.show()
class MapFileHistory(plugin.Plugin): """ The B{MapFileHistory} uses the fife_settings module to store the last used mapfiles and provides the means to both display them and load them """ def __init__(self): super(MapFileHistory, self).__init__() # editor instance self._editor = scripts.editor.getEditor() # Plugin variables self._enabled = False self._action_show = None self.history = [] self.names = [] # gui vars self.container = None self.default_x, self.default_y = _POSITION self.default_settings = _PLUGIN_SETTINGS self.eds = self._editor._settings self.update_settings() def enable(self): """ enables the plugin and connects to the editor """ if self._enabled: return self._enabled = True # Fifedit plugin data self._action_show = Action(self.getName(), checkable=True) scripts.gui.action.activated.connect(self.toggle, sender=self._action_show) self._editor._tools_menu.addAction(self._action_show) self.load_map_history() self.create() onOpenMapFile.connect(self.update) if self.settings['docked']: self._editor.dockWidgetTo(self.container, self.settings['dockarea']) def disable(self): """ deactivates plugin """ if not self._enabled: return self._enabled = False onOpenMapFile.disconnect(self.update) self._editor._tools_menu.removeAction(self._action_show) def isEnabled(self): return self._enabled def getName(self): return u"Mapfile history" #--- These are not so important ---# def getAuthor(self): return "Zero-Projekt" def getDescription(self): return "" def getLicense(self): return "" def getVersion(self): return "0.1" def load_map(self, event=None, widget=None): """ load the the selected map """ index = widget.selected path = self.history[index] # make sure the mapfile exists, otherwise delete the entry if not self._editor.engine.getVFS().exists(path): self.delete_entry(path) self.update_gui() return self._editor.openFile(path) def load_map_history(self): """ loads the mapfile history from settings file """ for i in range(_HISTORY_LEN): entry = _ITEM_PREFIX+str(i) item = self.eds.get(_MODULE, entry) self.add_entry(item) def add_entry(self, path, save=False): """ adds an entry to the history @type path: str @param path: path to map file @type save: bool @param save: flag to either save entry to settings file or not """ if not path: return if path in self.history: return cur_len = len(self.history) parts = path.split(os.sep) name = parts[-1] if cur_len >= _HISTORY_LEN: self.history[0] = path self.names[0] = name elif cur_len < _HISTORY_LEN: self.history.append(path) self.names.append(name) if save: index = self.history.index(path) self.eds.set(_MODULE, _ITEM_PREFIX+str(index), path) def delete_entry(self, path): """ delete an (outdated) entry from the history @type path: str @param path: path to map file """ if path not in self.history: return index = self.history.index(path) del self.history[index] del self.names[index] self.eds.set(_MODULE, _ITEM_PREFIX+str(index), '') def create(self): """ creates the gui """ if self.container is not None: return self.container = Panel(title=self.getName()) self.container.position_technique = 'explicit' self.container.position = _POSITION self.container.min_size = _MIN_SIZE self.container.name = _MODULE self.lst_box = pychan.widgets.ListBox() self.lst_box.items = self.names self.lst_box.capture(self.load_map) self.container.addChild(self.lst_box) # overwrite Panel.afterUndock self.container.afterUndock = self.on_undock self.container.afterDock = self.on_dock def on_dock(self): """ callback for dock event of B{Panel} widget """ side = self.container.dockarea.side if not side: return module = self.default_settings['module'] self.eds.set(module, 'dockarea', side) self.eds.set(module, 'docked', True) def on_undock(self): """ callback for undock event of B{Panel} widget """ self.container.hide() self.toggle() module = self.default_settings['module'] self.eds.set(module, 'dockarea', '') self.eds.set(module, 'docked', False) def toggle(self): """ shows / hides the gui """ if self.container.isVisible(): self.last_dockarea = self.container.dockarea self.container.hide() self._action_show.setChecked(False) else: if not self.container.isDocked(): self.container.show() self.container.x = self.default_x self.container.y = self.default_y else: self.container.setDocked(True) self.dockWidgetTo(self.container, self.last_dockarea) self._action_show.setChecked(True) def update_gui(self): """ refresh the listbox and update the container layout """ self.lst_box.items = self.names self.container.adaptLayout() def update(self, path): """ updates the history with a new mapfile """ self.add_entry(path, save=True) self.update_gui()