[GRASS-SVN] r30850 - in grass/trunk/gui/wxpython: gui_modules icons vdigit

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Apr 3 08:22:37 EDT 2008


Author: martinl
Date: 2008-04-03 08:22:37 -0400 (Thu, 03 Apr 2008)
New Revision: 30850

Added:
   grass/trunk/gui/wxpython/vdigit/undo.cpp
Modified:
   grass/trunk/gui/wxpython/gui_modules/digit.py
   grass/trunk/gui/wxpython/gui_modules/mapdisp.py
   grass/trunk/gui/wxpython/gui_modules/toolbars.py
   grass/trunk/gui/wxpython/gui_modules/wxgui_utils.py
   grass/trunk/gui/wxpython/icons/icon.py
   grass/trunk/gui/wxpython/vdigit/digit.cpp
   grass/trunk/gui/wxpython/vdigit/digit.h
   grass/trunk/gui/wxpython/vdigit/line.cpp
Log:
wxGUI: very basic undo implementation for vdigit. TODO: add Vect_restore_line() to Vlib


Modified: grass/trunk/gui/wxpython/gui_modules/digit.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/digit.py	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/gui_modules/digit.py	2008-04-03 12:22:37 UTC (rev 30850)
@@ -80,7 +80,8 @@
         @param settings  initial settings of digitization tool
         """
         self.map       = None
-
+        self.mapWindow = mapwindow
+        
         Debug.msg (3, "AbstractDigit.__init__(): map=%s" % \
                    self.map)
 
@@ -739,6 +740,12 @@
 
         return [1,]
 
+    def Undo(self, level=-1):
+        """Undo not implemented here"""
+        wx.MessageBox(parent=self.mapWindow, message=_("Undondo is not implemented in vedit component. "
+                                                    "Use vdigit instead."),
+                      caption=_("Message"), style=wx.ID_OK | wx.ICON_INFORMATION | wx.CENTRE)
+
 class VDigit(AbstractDigit):
     """
     Prototype of digitization class based on v.digit reimplementation
@@ -758,6 +765,11 @@
         except (ImportError, NameError):
             self.digit = None
 
+        self.toolbar = mapwindow.parent.digittoolbar
+
+    def __del__(self):
+        del self.digit
+        
     def AddPoint (self, map, point, x, y, z=None):
         """Add new point/centroid
 
@@ -784,6 +796,8 @@
 
         if ret == -1:
             raise gcmd.DigitError, _("Adding new feature to vector map <%s> failed.") % map
+
+        self.toolbar.EnableUndo()
         
     def AddLine (self, map, line, coords):
         """Add line/boundary
@@ -816,7 +830,8 @@
         if ret == -1:
             raise gcmd.DigitError, _("Adding new feature to vector map <%s> failed.") % map
 
-
+        self.toolbar.EnableUndo()
+        
     def DeleteSelectedLines(self):
         """Delete selected features
 
@@ -824,6 +839,9 @@
         """
         nlines = self.digit.DeleteLines(UserSettings.Get(group='vdigit', key='delRecord', subkey='enabled'))
 
+        if nlines > 0:
+            self.toolbar.EnableUndo()
+            
         return nlines
 
     def MoveSelectedLines(self, move):
@@ -836,6 +854,9 @@
         nlines = self.digit.MoveLines(move[0], move[1], 0.0, # TODO 3D
                                       str(UserSettings.Get(group='vdigit', key="backgroundMap", subkey='value')), snap, thresh)
 
+        if nlines > 0:
+            self.toolbar.EnableUndo()
+            
         return nlines
 
     def MoveSelectedVertex(self, coords, move):
@@ -849,44 +870,76 @@
         """
         snap, thresh = self.__getSnapThreshold()
 
-        return self.digit.MoveVertex(coords[0], coords[1], 0.0, # TODO 3D
-                                     move[0], move[1], 0.0,
-                                     str(UserSettings.Get(group='vdigit', key="backgroundMap", subkey='value')), snap,
-                                     self.driver.GetThreshold(type='selectThresh'), thresh)
+        moved = self.digit.MoveVertex(coords[0], coords[1], 0.0, # TODO 3D
+                                      move[0], move[1], 0.0,
+                                      str(UserSettings.Get(group='vdigit', key="backgroundMap", subkey='value')), snap,
+                                      self.driver.GetThreshold(type='selectThresh'), thresh)
 
+        if moved:
+            self.toolbar.EnableUndo()
+
+        return moved
+
     def AddVertex(self, coords):
         """Add new vertex to the selected line/boundary on position 'coords'
 
         @param coords coordinates to add vertex
+
+        @return 1 vertex added
+        @return 0 nothing changed
+        @return -1 on failure
         """
-        return self.digit.ModifyLineVertex(1, coords[0], coords[1], 0.0, # TODO 3D
-                                           self.driver.GetThreshold(type='selectThresh'))
+        added = self.digit.ModifyLineVertex(1, coords[0], coords[1], 0.0, # TODO 3D
+                                            self.driver.GetThreshold(type='selectThresh'))
 
+        if added > 0:
+            self.toolbar.EnableUndo()
+
+        return added
+
     def RemoveVertex(self, coords):
         """Remove vertex from the selected line/boundary on position 'coords'
 
         @param coords coordinates to remove vertex
+
+        @return 1 vertex removed
+        @return 0 nothing changed
+        @return -1 on failure
         """
-        return self.digit.ModifyLineVertex(0, coords[0], coords[1], 0.0, # TODO 3D
-                                           self.driver.GetThreshold(type='selectThresh'))
+        deleted = self.digit.ModifyLineVertex(0, coords[0], coords[1], 0.0, # TODO 3D
+                                              self.driver.GetThreshold(type='selectThresh'))
 
+        if deleted > 0:
+            self.toolbar.EnableUndo()
+
+        return deleted
+
+
     def SplitLine(self, coords):
         """Split selected line/boundary on position 'coords'
 
         @param coords coordinates to split line
 
-        @return 1 line splitted
-        @return 0 no action
+        @return 1 line modified
+        @return 0 nothing changed
         @return -1 error
         """
-        return self.digit.SplitLine(coords[0], coords[1], 0.0, # TODO 3D
-                                    self.driver.GetThreshold('selectThresh'))
+        ret = self.digit.SplitLine(coords[0], coords[1], 0.0, # TODO 3D
+                                   self.driver.GetThreshold('selectThresh'))
 
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def EditLine(self, line, coords):
         """Edit existing line/boundary
 
         @param line id of line to be modified
         @param coords list of coordinates of modified line
+
+        @return feature id of new line
+        @return -1 on error
         """
         try:
             lineid = line[0]
@@ -900,49 +953,116 @@
 
         snap, thresh = self.__getSnapThreshold()
         
-        return self.digit.RewriteLine(lineid, listCoords,
-                                      str(UserSettings.Get(group='vdigit', key="backgroundMap", subkey='value')), snap, thresh)
+        ret = self.digit.RewriteLine(lineid, listCoords,
+                                     str(UserSettings.Get(group='vdigit', key="backgroundMap", subkey='value')), snap, thresh)
 
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def FlipLine(self):
-        """Flip selected lines/boundaries"""
-        return self.digit.FlipLines()
+        """Flip selected lines/boundaries
 
+        @return number of modified lines
+        @return -1 on error
+        """
+        ret = self.digit.FlipLines()
+
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def MergeLine(self):
-        """Merge selected lines/boundaries"""
-        return self.digit.MergeLines()
+        """Merge selected lines/boundaries
 
+        @return number of modified lines
+        @return -1 on error
+        """
+        ret = self.digit.MergeLines()
+
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def BreakLine(self):
-        """Break selected lines/boundaries"""
-        return self.digit.BreakLines()
+        """Break selected lines/boundaries
 
+        @return number of modified lines
+        @return -1 on error
+        """
+        ret = self.digit.BreakLines()
+
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def SnapLine(self):
-        """Snap selected lines/boundaries"""
+        """Snap selected lines/boundaries
+
+        @return on success
+        @return -1 on error
+        """
         snap, thresh = self.__getSnapThreshold()
-        return self.digit.SnapLines(thresh)
+        ret = self.digit.SnapLines(thresh)
+        
+        if ret == 0:
+            self.toolbar.EnableUndo()
 
+        return ret
+
     def ConnectLine(self):
-        """Connect selected lines/boundaries"""
+        """Connect selected lines/boundaries
+
+        @return 1 lines connected
+        @return 0 lines not connected
+        @return -1 on error
+        """
         snap, thresh = self.__getSnapThreshold()
-        return self.digit.ConnectLines(thresh)
+        ret = self.digit.ConnectLines(thresh)
 
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+        
     def CopyLine(self, ids=None):
         """Copy features from (background) vector map
 
         @param ids list of line ids to be copied
+
+        @return number of copied features
+        @return -1 on error
         """
-        return self.digit.CopyLines(ids, str(UserSettings.Get(group='vdigit', key='backgroundMap', subkey='value')))
+        ret = self.digit.CopyLines(ids, str(UserSettings.Get(group='vdigit', key='backgroundMap', subkey='value')))
 
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def CopyCats(self, cats, ids):
         """Copy given categories to objects with id listed in ids
 
         @param cats list of cats to be copied
         @param ids  ids of lines to be modified
+
+        @return number of modified features
+        @return -1 on error
         """
         if len(cats) == 0 or len(ids) == 0:
-            return False
+            return 0
 
-        return self.digit.CopyCats(cats, ids)
+        ret = self.digit.CopyCats(cats, ids)
 
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def SelectLinesByQuery(self, pos1, pos2):
         """Select features by query
 
@@ -983,9 +1103,17 @@
         @param layer layer number (-1 for first selected line)
         @param cats list of categories
         @param add if True to add, otherwise do delete categories
+
+        @return new feature id (feature need to be rewritten)
+        @return -1 on error
         """
-        return self.digit.SetLineCats(line, layer, cats, add)
+        ret = self.digit.SetLineCats(line, layer, cats, add)
 
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
     def GetLayers(self):
         """Get list of layers"""
         return self.digit.GetLayers()
@@ -1000,8 +1128,33 @@
         @return number of modified features
         @return -1 on error
         """
-        return self.digit.TypeConvLines()
+        ret = self.digit.TypeConvLines()
 
+        if ret > 0:
+            self.toolbar.EnableUndo()
+
+        return ret
+
+    def Undo(self, level=-1):
+        """Undo action
+
+        @param level levels to undo
+
+        @return id of current changeset
+        """
+        try:
+            ret = self.digit.Undo(level)
+        except SystemExit:
+            ret = -2
+
+        if ret == -2:
+            raise gcmd.DigitError, _("Undo failed, data corrupted.")
+
+        self.mapWindow.UpdateMap(render=False)
+        
+        if ret < 0: # disable undo tool
+            self.toolbar.EnableUndo(False)
+        
     def __getSnapThreshold(self):
         """Get snap mode and threshold value
 
@@ -1031,7 +1184,10 @@
         def __init__(self, mapwindow):
             VDigit.__init__(self, mapwindow)
             self.type = 'vdigit'
-
+            
+        def __del__(self):
+            VDigit.__del__(self)
+            
 class AbstractDisplayDriver:
     """Abstract classs for display driver"""
     def __init__(self, parent, mapwindow):

Modified: grass/trunk/gui/wxpython/gui_modules/mapdisp.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/mapdisp.py	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/gui_modules/mapdisp.py	2008-04-03 12:22:37 UTC (rev 30850)
@@ -2049,7 +2049,7 @@
         # We ONLY want to set extents here. Don't mess with resolution. Leave that
         # for user to set explicitly with g.region
         new = self.Map.AlignResolution()
-
+        
         cmdRegion = ["g.region", "--o",
                      "n=%f"    % new['n'],
                      "s=%f"    % new['s'],
@@ -2170,7 +2170,7 @@
     drawing window.
     """
 
-    def __init__(self, parent=None, id=wx.ID_ANY, title="GRASS GIS - Map display",
+    def __init__(self, parent=None, id=wx.ID_ANY, title=_("GRASS GIS - Map display"),
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_FRAME_STYLE, toolbars=["map"],
                  tree=None, notebook=None, gismgr=None, page=None,

Modified: grass/trunk/gui/wxpython/gui_modules/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/toolbars.py	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/gui_modules/toolbars.py	2008-04-03 12:22:37 UTC (rev 30850)
@@ -43,12 +43,9 @@
         @return list of ids (of added tools)
         """
 
-        ids = []
         for tool in toolData:
-            ids.append(self.CreateTool(parent, toolbar, *tool))
-
-        return ids
-
+            self.CreateTool(parent, toolbar, *tool)
+            
     def ToolbarData(self):
         """Toolbar data"""
 
@@ -63,16 +60,15 @@
 
         bmpDisabled=wx.NullBitmap
 
-        id = wx.NewId()
         if label:
-            tool = toolbar.AddLabelTool(id, label, bitmap,
+            toolWin = toolbar.AddLabelTool(tool, label, bitmap,
                                         bmpDisabled, kind,
                                         shortHelp, longHelp)
-            parent.Bind(wx.EVT_TOOL, handler, tool)
+            parent.Bind(wx.EVT_TOOL, handler, toolWin)
         else: # add separator
             toolbar.AddSeparator()
 
-        return id
+        return tool
     
 class MapToolbar(AbstractToolbar):
     """
@@ -86,7 +82,7 @@
 	self.toolbar = wx.ToolBar(parent=self.mapdisplay, id=wx.ID_ANY)
         self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
 
-        self.pointerId = self.InitToolbar(self.mapdisplay, self.toolbar, self.ToolbarData())[4]
+        self.InitToolbar(self.mapdisplay, self.toolbar, self.ToolbarData())
         
         # optional tools
         self.combo = wx.ComboBox(parent=self.toolbar, id=wx.ID_ANY, value='Tools',
@@ -101,9 +97,20 @@
     def ToolbarData(self):
         """Toolbar data"""
 
-        self.displaymap = self.rendermap = self.erase = \
-        self.pointer = self.query = self.pan = self.zoomin = self.zoomout = \
-        self.zoomback = self.zoommenu = self.analyze = self.dec = self.savefile = self.printmap = None
+        self.displaymap = wx.NewId()
+        self.rendermap = wx.NewId()
+        self.erase = wx.NewId()
+        self.pointer = wx.NewId()
+        self.query = wx.NewId()
+        self.pan = wx.NewId()
+        self.zoomin = wx.NewId()
+        self.zoomout = wx.NewId()
+        self.zoomback = wx.NewId()
+        self.zoommenu = wx.NewId()
+        self.analyze = wx.NewId()
+        self.dec = wx.NewId()
+        self.savefile = wx.NewId()
+        self.printmap = wx.NewId()
 
         # tool, label, bitmap, kind, shortHelp, longHelp, handler
         return (
@@ -187,9 +194,15 @@
     def ToolbarData(self):
         """Toolbar data"""
 
-        self.displaymap = self.rendermap = self.erase = \
-        self.gcpset = self.query = self.pan = self.zoomin = self.zoomout = \
-        self.zoomback = self.zoommenu = self.analyze = self.dec = self.savefile = self.printmap =None
+        self.displaymap = wx.NewId()
+        self.rendermap = wx.NewId()
+        self.erase = wx.NewId()
+        self.gcpset = wx.NewId()
+        self.pan = wx.NewId()
+        self.zoomin = wx.NewId()
+        self.zoomout = wx.NewId()
+        self.zoomback = wx.NewId()
+        self.zoommenu = wx.NewId()
 
         # tool, label, bitmap, kind, shortHelp, longHelp, handler
         return (
@@ -257,13 +270,12 @@
         # only one dialog can be open
         self.settingsDialog   = None
 
-        # create toolbars (two rows)
+        # create toolbars (two rows optionaly)
         self.toolbar = []
         self.numOfRows = 1 # number of rows for toolbar
         for row in range(0, self.numOfRows):
             self.toolbar.append(wx.ToolBar(parent=self.parent, id=wx.ID_ANY))
 	    self.toolbar[row].SetToolBitmapSize(globalvar.toolbarSize)
-            self.toolbar[row].SetToolBitmapSize(wx.Size(24,24))
             self.toolbar[row].Bind(wx.EVT_TOOL, self.OnTool)
             
             # create toolbar
@@ -280,6 +292,9 @@
         for row in range(0, self.numOfRows):
             self.toolbar[row].Realize()
 
+        # disable undo/redo
+        self.toolbar[0].EnableTool(self.undo, False)
+        
         # toogle to pointer by default
         self.OnTool(None)
         
@@ -289,11 +304,21 @@
         """
         data = []
         if row is None or row == 0:
-            self.addPoint = self.addLine = self.addBoundary = self.addCentroid = None
-            self.moveVertex = self.addVertex = self.removeVertex = None
-            self.splitLine = self.editLine = self.moveLine = self.deleteLine = None
-            self.additionalTools = None
-            self.displayCats = self.displayAttr = self.copyCats = None
+            self.addPoint = wx.NewId()
+            self.addLine = wx.NewId()
+            self.addBoundary = wx.NewId()
+            self.addCentroid = wx.NewId()
+            self.moveVertex = wx.NewId()
+            self.addVertex = wx.NewId()
+            self.removeVertex = wx.NewId()
+            self.splitLine = wx.NewId()
+            self.editLine = wx.NewId()
+            self.moveLine = wx.NewId()
+            self.deleteLine = wx.NewId()
+            self.additionalTools = wx.NewId()
+            self.displayCats = wx.NewId()
+            self.displayAttr = wx.NewId()
+            self.copyCats = wx.NewId()
 
             data = [("", "", "", "", "", "", ""),
                     (self.addPoint, "digAddPoint", Icons["digAddPoint"].GetBitmap(),
@@ -344,12 +369,17 @@
                      self.OnAdditionalToolMenu)]
 
         if row is None or row == 1:
-            self.undo = self.settings = self.exit = None
+            self.undo = wx.NewId()
+            self.settings = wx.NewId()
+            self.exit = wx.NewId()
 
             data.append(("", "", "", "", "", "", ""))
             data.append((self.undo, "digUndo", Icons["digUndo"].GetBitmap(),
                          wx.ITEM_NORMAL, Icons["digUndo"].GetLabel(), Icons["digUndo"].GetDesc(),
                          self.OnUndo))
+            # data.append((self.undo, "digRedo", Icons["digRedo"].GetBitmap(),
+            #             wx.ITEM_NORMAL, Icons["digRedo"].GetLabel(), Icons["digRedo"].GetDesc(),
+            #             self.OnRedo))
             data.append((self.settings, "digSettings", Icons["digSettings"].GetBitmap(),
                          wx.ITEM_NORMAL, Icons["digSettings"].GetLabel(), Icons["digSettings"].GetDesc(),
                          self.OnSettings))
@@ -361,7 +391,7 @@
 
     def OnTool(self, event):
         """Tool selected -> toggle tool to pointer"""
-        id = self.parent.maptoolbar.pointerId
+        id = self.parent.maptoolbar.pointer
         self.parent.maptoolbar.toolbar.ToggleTool(id, True)
         self.parent.maptoolbar.mapdisplay.OnPointer(event)
         if event:
@@ -481,14 +511,23 @@
         self.parent.MapWindow.mouse['box'] = 'point'
 
     def OnUndo(self, event):
-        """Undo changes
+        """Undo previous changes"""
+        self.parent.digit.Undo()
 
-        @todo
-        """
-        wx.MessageBox(parent=self.parent, message=_("Undo is not implemented yet."),
-                      caption=_("Message"), style=wx.ID_OK | wx.ICON_INFORMATION)
         event.Skip()
 
+    def EnableUndo(self, enable=True):
+        """Enable 'Undo' in toolbar
+
+        @param enable False for disable
+        """
+        if enable:
+            if self.toolbar[0].GetToolEnabled(self.undo) is False:
+                self.toolbar[0].EnableTool(self.undo, True)
+        else:
+            if self.toolbar[0].GetToolEnabled(self.undo) is True:
+                self.toolbar[0].EnableTool(self.undo, False)
+        
     def OnSettings(self, event):
         """Show settings dialog"""
 
@@ -702,7 +741,6 @@
         if not self.parent.MapWindow.resize:
             self.parent.MapWindow.UpdateMap(render=True)
 
-
         return True
 
     def StopEditing (self, layerSelected):
@@ -730,6 +768,7 @@
             self.parent.MapWindow.pdcVector = None
             self.parent.digit.driver.SetDevice(None)
 
+            self.parent.digit.__del__() # FIXME: destructor is not called here (del)
             self.parent.digit = None
 
             return True

Modified: grass/trunk/gui/wxpython/gui_modules/wxgui_utils.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/wxgui_utils.py	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/gui_modules/wxgui_utils.py	2008-04-03 12:22:37 UTC (rev 30850)
@@ -89,7 +89,7 @@
 
         # init associated map display
         self.mapdisplay = mapdisp.MapFrame(self,
-                                           id=wx.ID_ANY, pos=wx.DefaultPosition, size=(640,480),
+                                           id=wx.ID_ANY, pos=wx.DefaultPosition, size=(680, 520),
                                            style=wx.DEFAULT_FRAME_STYLE,
                                            tree=self, notebook=self.notebook,
                                            gismgr=self.gismgr, page=self.treepg,

Modified: grass/trunk/gui/wxpython/icons/icon.py
===================================================================
--- grass/trunk/gui/wxpython/icons/icon.py	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/icons/icon.py	2008-04-03 12:22:37 UTC (rev 30850)
@@ -302,8 +302,8 @@
     "digAdditionalTools" : MetaIcon (img=icons_img["digAdditionalTools"], label="Additional tools " \
                                          "(copy, flip, connect, etc.)",
                                      desc="Left: Select; Middle: Unselect; Right: Confirm"),
-    "digUndo" : MetaIcon (img=icons_img["digUndo"], label="Undo", 	 	 
-                          desc="Undo previous changes (not implemented yet)"),
+    "digUndo" : MetaIcon (img=icons_img["digUndo"], label="Undo",
+                          desc="Undo previous changes"),
     # analyze raster
     "analyze"    : MetaIcon (img=icons_img["analyze"], label="Analyze map"),
     "measure"    : MetaIcon (img=icons_img["measure"], label="Measure distance"),

Modified: grass/trunk/gui/wxpython/vdigit/digit.cpp
===================================================================
--- grass/trunk/gui/wxpython/vdigit/digit.cpp	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/vdigit/digit.cpp	2008-04-03 12:22:37 UTC (rev 30850)
@@ -30,6 +30,21 @@
 	InitCats();
     }
 
+    changesetCurrent = -2; // initial value for undo/redo
+    changesetDead = -2;
+
     // avoid GUI crash
     // Vect_set_fatal_error(GV_FATAL_PRINT);
 }
+
+/**
+   Digit class destructor
+
+   Frees changeset structure
+*/
+Digit::~Digit()
+{
+    for(int changeset = 0; changeset < (int) changesets.size(); changeset++) {
+	FreeChangeset(changeset);
+    }
+}

Modified: grass/trunk/gui/wxpython/vdigit/digit.h
===================================================================
--- grass/trunk/gui/wxpython/vdigit/digit.h	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/vdigit/digit.h	2008-04-03 12:22:37 UTC (rev 30850)
@@ -3,6 +3,7 @@
 
 #define GSQL_MAX 4000
 
+#include <vector>
 #include <map>
 
 class Digit
@@ -16,8 +17,27 @@
     int SetCategory(int, int);
     struct Map_info** OpenBackgroundVectorMap(const char *);
 
+    /* undo/redo */
+    enum action_type { ADD, DELETE };
+    struct action_meta {
+	action_type type;
+	int line;
+	/* TODO: replace by new Vect_restore_line() */
+	int ltype; // line type
+	struct line_pnts *Points;
+	struct line_cats *Cats;
+    };
+    std::map<int, std::vector<action_meta> > changesets;
+    int changesetCurrent;  /* first changeset to apply */
+    int changesetDead;     /* first dead changeset */
+
+    int AddActionToChangeset(int, action_type, int);
+    int ApplyChangeset(int, bool);
+    void FreeChangeset(int);
+
 public:
     Digit(DisplayDriver *);
+    ~Digit();
 
     int InitCats();
 
@@ -57,6 +77,8 @@
     std::map<int, std::vector<int> > GetLineCats(int);
     int SetLineCats(int, int, std::vector<int>, bool);
     std::vector<int> GetLayers();
+
+    int Undo(int);
 };
 
 #endif /* __DIGIT_H__ */

Modified: grass/trunk/gui/wxpython/vdigit/line.cpp
===================================================================
--- grass/trunk/gui/wxpython/vdigit/line.cpp	2008-04-03 08:14:54 UTC (rev 30849)
+++ grass/trunk/gui/wxpython/vdigit/line.cpp	2008-04-03 12:22:37 UTC (rev 30850)
@@ -41,6 +41,8 @@
     size_t i;
     size_t npoints;
 
+    int newline;
+
     struct line_pnts *Points;
     struct line_cats *Cats;
 
@@ -57,7 +59,7 @@
     }
 
     G_debug(2, "wxDigit.AddLine(): npoints=%d, layer=%d, cat=%d, snap=%d",
-	    npoints, layer, cat, snap);
+	    (int) npoints, layer, cat, snap);
 
     /* TODO: 3D */
     if (!(type & GV_POINTS) && !(type & GV_LINES)) {
@@ -119,10 +121,14 @@
 			threshold, (snap == SNAP) ? 0 : 1); 
     }
 
-    if (Vect_write_line(display->mapInfo, type, Points, Cats) < 0) {
+    newline = Vect_write_line(display->mapInfo, type, Points, Cats);
+    if (newline < 0) {
 	return -1;
     }
 
+    /* register changeset */
+    AddActionToChangeset(changesets.size(), ADD, newline);
+
     Vect_destroy_line_struct(Points);
     Vect_destroy_cats_struct(Cats);
 
@@ -265,6 +271,7 @@
 {
     int ret;
     int n_dblinks;
+    int changeset;
     struct line_cats *Cats, *Cats_del;
     // struct ilist *List;
 
@@ -310,6 +317,12 @@
 	Vect_destroy_cats_struct(Cats);
     }
 
+    /* register changeset */
+    changeset = changesets.size();
+    for (int i = 0; i < display->selected->n_values; i++) {
+	AddActionToChangeset(changeset, DELETE, display->selected->value[i]);
+    }
+
     ret = Vedit_delete_lines(display->mapInfo, display->selected);
 
     if (ret > 0 && delete_records) {

Added: grass/trunk/gui/wxpython/vdigit/undo.cpp
===================================================================
--- grass/trunk/gui/wxpython/vdigit/undo.cpp	                        (rev 0)
+++ grass/trunk/gui/wxpython/vdigit/undo.cpp	2008-04-03 12:22:37 UTC (rev 30850)
@@ -0,0 +1,191 @@
+/**
+   \file undo.cpp
+
+   \brief Undo/Redo functionality
+
+   \todo Implement Vect_restore_line() in Vlib
+
+   This program is free software under the GNU General Public
+   License (>=v2). Read the file COPYING that comes with GRASS
+   for details.
+
+   (C) 2008 by The GRASS development team
+
+   \author Martin Landa <landa.martin gmail.com>
+
+   \date 2008 
+*/
+
+#include "driver.h"
+#include "digit.h"
+
+/**
+   \brief Undo/Redo operations
+
+   \param level level for undo/redo
+
+   \return id of current chanset
+   \return -2 on error
+*/
+int Digit::Undo(int level)
+{
+    int changesetLast;
+
+    changesetLast = (int) changesets.size() - 1;
+
+    if (changesetLast < 0)
+	return changesetLast;
+
+    if (changesetCurrent == -2) { /* value uninitialized */
+	changesetCurrent = changesetLast;
+    }
+
+    if (level > 0 && changesetCurrent < 0) {
+	changesetCurrent = 0;
+    }
+
+    G_debug(2, "Digit.Undo(): changeset_last=%d changeset_dead=%d, changeset_current=%d, level=%d",
+	    changesetLast, changesetDead, changesetCurrent, level);
+    
+    if (level < 0) { /* undo */
+	if (changesetCurrent + level < -1)
+	    return changesetCurrent;
+	for (int changeset = changesetCurrent; changeset > changesetCurrent + level; --changeset) {
+	    ApplyChangeset(changeset, true);
+	}
+    }
+    else if (level > 0) { /* redo */
+	if (changesetCurrent + level > (int) changesets.size())
+	    return changesetCurrent;
+	for (int changeset = changesetCurrent; changeset < changesetCurrent + level; ++changeset) {
+	    ApplyChangeset(changeset, false);
+	}
+    }
+
+    changesetCurrent += level;
+
+    G_debug(2, "Digit.Undo(): changeset_dead=%d, changeset_current=%d",
+	    changesetDead, changesetCurrent);
+
+    return (changesetDead >= changesetCurrent) ? -1 : changesetCurrent;
+}
+
+/**
+   \brief Apply changeset (undo/redo changeset)
+   
+   \param changeset changeset id
+   \param undo if true -> undo otherwise redo
+
+   \return 1 changeset applied
+   \return 0 changeset not applied
+   \return -1 on error
+*/
+int Digit::ApplyChangeset(int changeset, bool undo)
+{ 
+    int ret;
+
+    if (changeset < 0 || changeset > (int) changesets.size())
+	return -1;
+
+    ret = 0;
+    std::vector<action_meta> action = changesets[changeset];
+    for (std::vector<action_meta>::const_iterator i = action.begin(), e = action.end();
+	 i != e; ++i) {
+	if ((undo && (*i).type == ADD) ||
+	    (!undo && (*i).type == DELETE)) {
+	    if (Vect_line_alive(display->mapInfo, (*i).line)) {
+		G_debug(3, "Digit.ApplyChangeset(): changeset=%d, action=add, line=%d -> deleted",
+			changeset, (*i).line);
+		Vect_delete_line(display->mapInfo, (*i).line);
+		if (!ret)
+		    ret = 1;
+	    }
+	    else {
+		G_debug(3, "Digit.ApplyChangeset(): changeset=%d, action=add, line=%d dead",
+			changeset, (*i).line);
+	    }
+	}
+	else { /* DELETE */
+	    if (!Vect_line_alive(display->mapInfo, (*i).line)) {
+		G_debug(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d -> added",
+			changeset, (*i).line);
+		if (Vect_write_line(display->mapInfo, (*i).ltype, (*i).Points, (*i).Cats) < 0)
+		    return -1;
+		if (!ret)
+		    ret = 1;
+	    }
+	    else {
+		G_debug(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d alive",
+			changeset, (*i).line);
+	    }
+	}
+    }
+    
+    if (changeset < (int) changesets.size() - 1)
+	changesetDead = changeset;
+
+    return ret;
+}
+
+/**
+   \brief Add action to changeset
+
+   \todo Use Vect_restore_line() (TODO) instead!
+
+   \param type action type (ADD, DELETE)
+
+   \return 0 on success
+   \return -1 on error
+*/
+int Digit::AddActionToChangeset(int changeset, Digit::action_type type, int line)
+{
+    int ltype;
+    struct line_pnts *Points;
+    struct line_cats *Cats;
+
+    if (!display->mapInfo) {
+	return -1;
+    }
+
+    Points = Vect_new_line_struct();
+    Cats = Vect_new_cats_struct(); 
+
+    /* do copy */
+    if (!Vect_line_alive(display->mapInfo, line))
+	return -1;
+
+    ltype = Vect_read_line(display->mapInfo, Points, Cats, line);
+
+    action_meta data = { type, line, ltype, Points, Cats };
+    if (changesets.find(changeset) == changesets.end()) {
+	changesets[changeset] = std::vector<action_meta>();
+	changesetCurrent = changeset;
+    }
+    changesets[changeset].push_back(data);
+    G_debug (3, "Digit.AddActionToChangeset(): changeset=%d, type=%d, line=%d",
+	     changeset, type, line);
+
+    return 0;
+}
+
+/**
+   \brief Free changeset structures
+
+   \param changeset changeset id
+*/
+void Digit::FreeChangeset(int changeset)
+{
+    if (changesets.find(changeset) == changesets.end())
+	return;
+
+    std::vector<action_meta> action = changesets[changeset];
+    for (std::vector<action_meta>::iterator i = action.begin(), e = action.end();
+	 i != e; ++i) {
+	Vect_destroy_line_struct((*i).Points);
+	Vect_destroy_cats_struct((*i).Cats);
+	(*i).Points = NULL;
+	(*i).Cats = NULL;
+    }
+
+    return;
+}



More information about the grass-commit mailing list