[GRASS-SVN] r51565 - in grass/branches/develbranch_6/gui/wxpython: . gmodeler gui_core

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Apr 29 06:33:22 EDT 2012


Author: martinl
Date: 2012-04-29 03:33:22 -0700 (Sun, 29 Apr 2012)
New Revision: 51565

Modified:
   grass/branches/develbranch_6/gui/wxpython/gmodeler/frame.py
   grass/branches/develbranch_6/gui/wxpython/gmodeler/model.py
   grass/branches/develbranch_6/gui/wxpython/gui_core/goutput.py
   grass/branches/develbranch_6/gui/wxpython/wxpythonlib.dox
Log:
wxGUI modeler: implement python editor
      (merge r51564 from trunk)


Modified: grass/branches/develbranch_6/gui/wxpython/gmodeler/frame.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gmodeler/frame.py	2012-04-29 10:27:57 UTC (rev 51564)
+++ grass/branches/develbranch_6/gui/wxpython/gmodeler/frame.py	2012-04-29 10:33:22 UTC (rev 51565)
@@ -9,8 +9,9 @@
  - frame::ModelEvtHandler
  - frame::VariablePanel
  - frame::ItemPanel
+ - frame::PythonPanel
 
-(C) 2010-2011 by the GRASS Development Team
+(C) 2010-2012 by the GRASS Development Team
 
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -35,7 +36,7 @@
 import wx.lib.flatnotebook    as FN
 
 from gui_core.widgets     import GNotebook
-from gui_core.goutput     import GMConsole
+from gui_core.goutput     import GMConsole, PyStc
 from core.debug           import Debug
 from core.gcmd            import GMessage, GException, GWarning, GError, RunCommand
 from gui_core.dialogs     import GetImageHandlers
@@ -102,17 +103,21 @@
         
         self.itemPanel = ItemPanel(parent = self)
         
+        self.pythonPanel = PythonPanel(parent = self)
+        
         self.goutput = GMConsole(parent = self, notebook = self.notebook)
         
         self.notebook.AddPage(page = self.canvas, text=_('Model'), name = 'model')
         self.notebook.AddPage(page = self.itemPanel, text=_('Items'), name = 'items')
         self.notebook.AddPage(page = self.variablePanel, text=_('Variables'), name = 'variables')
+        self.notebook.AddPage(page = self.pythonPanel, text=_('Python script'), name = 'python')
         self.notebook.AddPage(page = self.goutput, text=_('Command output'), name = 'output')
         wx.CallAfter(self.notebook.SetSelectionByName, 'model')
         wx.CallAfter(self.ModelChanged, False)
 
         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
         self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
         
         self._layout()
         self.SetMinSize((475, 300))
@@ -162,7 +167,21 @@
                 self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.modelFile))
         else:
             self.SetTitle(self.baseTitle)
+
+    def OnPageChanged(self, event):
+        """!Page in notebook changed"""
+        page = event.GetSelection()
+        if page == self.notebook.GetPageIndexByName('python'):
+            if self.pythonPanel.IsEmpty():
+                self.pythonPanel.RefreshScript()
+            
+            if self.pythonPanel.IsModified():
+                self.SetStatusText(_('Python script contains local modifications'), 0)
+            else:
+                self.SetStatusText(_('Python script is up-to-date'), 0)
         
+        event.Skip()
+
     def OnVariables(self, event):
         """!Switch to variables page"""
         self.notebook.SetSelectionByName('variables')
@@ -553,45 +572,9 @@
         
         dlg.Destroy()
         
-    def OnExportPython(self, event):
+    def OnExportPython(self, event = None, text = None):
         """!Export model to Python script"""
-        filename = ''
-        dlg = wx.FileDialog(parent = self,
-                            message = _("Choose file to save"),
-                            defaultDir = os.getcwd(),
-                            wildcard=_("Python script (*.py)|*.py"),
-                            style=wx.FD_SAVE)
-        
-        if dlg.ShowModal() == wx.ID_OK:
-            filename = dlg.GetPath()
-        
-        if not filename:
-            return
-        
-        # check for extension
-        if filename[-3:] != ".py":
-            filename += ".py"
-        
-        if os.path.exists(filename):
-            dlg = wx.MessageDialog(self, message=_("File <%s> already exists. "
-                                                   "Do you want to overwrite this file?") % filename,
-                                   caption=_("Save file"),
-                                   style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
-            if dlg.ShowModal() == wx.ID_NO:
-                dlg.Destroy()
-                return
-            
-            dlg.Destroy()
-        
-        fd = open(filename, "w")
-        try:
-            WritePythonFile(fd, self.model)
-        finally:
-            fd.close()
-        
-        # executable file
-        os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR)
-        
+        filename = self.pythonPanel.SaveAs(force = True)
         self.SetStatusText(_("Model exported to <%s>") % filename)
 
     def OnDefineRelation(self, event):
@@ -1453,7 +1436,172 @@
     def Update(self):
         """!Reload list of variables"""
         self.list.OnReload(None)
+
+class PythonPanel(wx.Panel):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 **kwargs):
+        """!Model as python script
+        """
+        self.parent = parent
         
+        wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+
+        self.filename = None # temp file to run
+        
+        self.bodyBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                    label = " %s " % _("Python script"))
+        self.body = PyStc(parent = self)
+
+        self.btnRun = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Run"))
+        self.btnRun.SetToolTipString(_("Run python script"))
+        self.Bind(wx.EVT_BUTTON, self.OnRun, self.btnRun)
+        self.btnSaveAs = wx.Button(parent = self, id = wx.ID_SAVEAS)
+        self.btnSaveAs.SetToolTipString(_("Save python script to file"))
+        self.Bind(wx.EVT_BUTTON, self.OnSaveAs, self.btnSaveAs)
+        self.btnRefresh = wx.Button(parent = self, id = wx.ID_REFRESH)
+        self.btnRefresh.SetToolTipString(_("Refresh python script based on the model. It will discards local changes."))
+        self.Bind(wx.EVT_BUTTON, self.OnRefresh, self.btnRefresh)
+        
+        self._layout()
+        
+    def _layout(self):
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        bodySizer = wx.StaticBoxSizer(self.bodyBox, wx.HORIZONTAL)
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        bodySizer.Add(item = self.body, proportion = 1,
+                      flag = wx.EXPAND | wx.ALL, border = 3)
+        
+        btnSizer.Add(item = self.btnRefresh, proportion = 0,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        btnSizer.AddStretchSpacer()
+        btnSizer.Add(item = self.btnSaveAs, proportion = 0,
+                     flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
+        btnSizer.Add(item = self.btnRun, proportion = 0,
+                     flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
+        
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.EXPAND | wx.ALL, border = 3)
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALL, border = 3)
+        
+        sizer.Fit(self)
+        sizer.SetSizeHints(self)
+        self.SetSizer(sizer)
+
+    def OnRun(self, event):
+        """!Run Python script"""
+        self.filename = grass.tempfile()
+        try:
+            fd = open(self.filename, "w")
+            fd.write(self.body.GetText())
+        except IOError, e:
+            GError(_("Unable to launch Python script. %s") % e,
+                   parent = self)
+            return
+        finally:
+            fd.close()
+            mode = stat.S_IMODE(os.lstat(self.filename)[stat.ST_MODE])
+            os.chmod(self.filename, mode | stat.S_IXUSR)
+        
+        self.parent.goutput.RunCmd([fd.name], switchPage = True,
+                                   skipInterface = True, onDone = self.OnDone)
+        
+        event.Skip()
+
+    def OnDone(self, cmd, returncode):
+        """!Python script finished"""
+        grass.try_remove(self.filename)
+        self.filename = None
+        
+    def SaveAs(self, force = False):
+        """!Save python script to file
+
+        @return filename
+        """
+        filename = ''
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose file to save"),
+                            defaultDir = os.getcwd(),
+                            wildcard = _("Python script (*.py)|*.py"),
+                            style = wx.FD_SAVE)
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        
+        if not filename:
+            return ''
+        
+        # check for extension
+        if filename[-3:] != ".py":
+            filename += ".py"
+        
+        if os.path.exists(filename):
+            dlg = wx.MessageDialog(self, message=_("File <%s> already exists. "
+                                                   "Do you want to overwrite this file?") % filename,
+                                   caption=_("Save file"),
+                                   style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() == wx.ID_NO:
+                dlg.Destroy()
+                return ''
+            
+            dlg.Destroy()
+        
+        fd = open(filename, "w")
+        try:
+            if force:
+                WritePythonFile(fd, self.parent.GetModel())
+            else:
+                fd.write(self.body.GetText())
+        finally:
+            fd.close()
+        
+        # executable file
+        os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR)
+        
+        return filename
+    
+    def OnSaveAs(self, event):
+        """!Save python script to file"""
+        self.SaveAs(force = False)
+        event.Skip()
+        
+    def RefreshScript(self):
+        """!Refresh Python script"""
+        if self.body.modified:
+            dlg = wx.MessageDialog(self,
+                                   message = _("Python script is locally modificated. "
+                                               "Refresh will discard all changes. "
+                                               "Do you really want to continue?"),
+                                   caption=_("Update"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT |
+                                   wx.ICON_QUESTION | wx.CENTRE)
+            ret = dlg.ShowModal()
+            dlg.Destroy()
+            if ret == wx.ID_NO:
+                return
+        
+        fd = tempfile.TemporaryFile()
+        WritePythonFile(fd, self.parent.GetModel())
+        fd.seek(0)
+        self.body.SetText(fd.read())
+        fd.close()
+        
+        self.body.modified = False
+ 
+    def OnRefresh(self, event):
+        """!Refresh Python script"""
+        self.RefreshScript()
+        event.Skip()
+        
+    def IsModified(self):
+        """!Check if python script has been modified"""
+        return self.body.modified
+    
+    def IsEmpty(self):
+        """!Check if python script is empty"""
+        return len(self.body.GetText()) == 0
+        
 def main():
     import gettext
     gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)

Modified: grass/branches/develbranch_6/gui/wxpython/gmodeler/model.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gmodeler/model.py	2012-04-29 10:27:57 UTC (rev 51564)
+++ grass/branches/develbranch_6/gui/wxpython/gmodeler/model.py	2012-04-29 10:33:22 UTC (rev 51565)
@@ -17,7 +17,7 @@
  - model::WritePythonFile
  - model::ModelParamDialog
 
-(C) 2010-2011 by the GRASS Development Team
+(C) 2010-2012 by the GRASS Development Team
 
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -1994,7 +1994,7 @@
 #
 # MODULE:       %s
 #
-# AUTHOR(S):	%s
+# AUTHOR(S):    %s
 #               
 # PURPOSE:      %s
 #

Modified: grass/branches/develbranch_6/gui/wxpython/gui_core/goutput.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_core/goutput.py	2012-04-29 10:27:57 UTC (rev 51564)
+++ grass/branches/develbranch_6/gui/wxpython/gui_core/goutput.py	2012-04-29 10:33:22 UTC (rev 51565)
@@ -9,8 +9,9 @@
  - goutput::GMStdout
  - goutput::GMStderr
  - goutput::GMStc
+ - goutput::PyStc
 
-(C) 2007-2011 by the GRASS Development Team
+(C) 2007-2012 by the GRASS Development Team
 
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -28,9 +29,10 @@
 import Queue
 import codecs
 import locale
+import keyword
 
 import wx
-import wx.stc
+from   wx import stc
 from wx.lib.newevent import NewEvent
 
 import grass.script as grass
@@ -436,7 +438,7 @@
         """!Write message in error style"""
         self.WriteLog(line, style = self.cmdOutput.StyleError, switchPage = True)
 
-    def RunCmd(self, command, compReg = True, switchPage = False,
+    def RunCmd(self, command, compReg = True, switchPage = False, skipInterface = False,
                onDone = None, onPrepare = None, userData = None):
         """!Run command typed into console command prompt (GPrompt).
         
@@ -448,6 +450,8 @@
         @param command command given as a list (produced e.g. by utils.split())
         @param compReg True use computation region
         @param switchPage switch to output page
+        @param skipInterface True to do not launch GRASS interface
+        parser when command has no arguments given
         @param onDone function to be called when command is finished
         @param onPrepare function to be called before command is launched
         @param userData data defined for the command
@@ -585,7 +589,7 @@
         else:
             # Send any other command to the shell. Send output to
             # console output window
-            if len(command) == 1:
+            if len(command) == 1 and not skipInterface:
                 try:
                     task = gtask.parse_interface(command[0])
                 except:
@@ -659,11 +663,11 @@
         @param copy True for enable, False for disable
         """
         if copy:
-            self.cmdPrompt.Bind(wx.stc.EVT_STC_PAINTED, self.cmdPrompt.OnTextSelectionChanged)
-            self.cmdOutput.Bind(wx.stc.EVT_STC_PAINTED, self.cmdOutput.OnTextSelectionChanged)
+            self.cmdPrompt.Bind(stc.EVT_STC_PAINTED, self.cmdPrompt.OnTextSelectionChanged)
+            self.cmdOutput.Bind(stc.EVT_STC_PAINTED, self.cmdOutput.OnTextSelectionChanged)
         else:
-            self.cmdPrompt.Unbind(wx.stc.EVT_STC_PAINTED)
-            self.cmdOutput.Unbind(wx.stc.EVT_STC_PAINTED)
+            self.cmdPrompt.Unbind(stc.EVT_STC_PAINTED)
+            self.cmdOutput.Unbind(stc.EVT_STC_PAINTED)
         
     def OnUpdateStatusBar(self, event):
         """!Update statusbar text"""
@@ -1003,7 +1007,7 @@
             evt = wxCmdProgress(value = progressValue)
             wx.PostEvent(self.parent.progressbar, evt)
             
-class GMStc(wx.stc.StyledTextCtrl):
+class GMStc(stc.StyledTextCtrl):
     """!Styled GMConsole
 
     Based on FrameOutErr.py
@@ -1015,7 +1019,7 @@
     Licence:   GPL
     """    
     def __init__(self, parent, id, margin = False, wrap = None):
-        wx.stc.StyledTextCtrl.__init__(self, parent, id)
+        stc.StyledTextCtrl.__init__(self, parent, id)
         self.parent = parent
         self.SetUndoCollection(True)
         self.SetReadOnly(True)
@@ -1032,7 +1036,7 @@
         self.SetMarginWidth(1, 0)
         self.SetMarginWidth(2, 0)
         if margin:
-            self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER)
+            self.SetMarginType(0, stc.STC_MARGIN_NUMBER)
             self.SetMarginWidth(0, 30)
         else:
             self.SetMarginWidth(0, 0)
@@ -1094,7 +1098,7 @@
         self.StyleUnknownSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize)
         
         # default and clear => init
-        self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec)
+        self.StyleSetSpec(stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec)
         self.StyleClearAll()
         self.StyleSetSpec(self.StyleCommand, self.StyleCommandSpec)
         self.StyleSetSpec(self.StyleOutput,  self.StyleOutputSpec)
@@ -1150,3 +1154,233 @@
         
         # reset output window to read only
         self.SetReadOnly(True)
+    
+class PyStc(stc.StyledTextCtrl):
+    """!Styled Python output (see gmodeler::frame::PythonPanel for
+    usage)
+
+    Based on StyledTextCtrl_2 from wxPython demo
+    """    
+    def __init__(self, parent, id = wx.ID_ANY):
+        stc.StyledTextCtrl.__init__(self, parent, id)
+        
+        self.parent = parent
+        
+        self.modified = False # content modified ?
+        
+        self.faces = { 'times': 'Times New Roman',
+                       'mono' : 'Courier New',
+                       'helv' : 'Arial',
+                       'other': 'Comic Sans MS',
+                       'size' : 10,
+                       'size2': 8,
+                       }
+        
+        self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
+        self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
+        
+        self.SetLexer(stc.STC_LEX_PYTHON)
+        self.SetKeyWords(0, " ".join(keyword.kwlist))
+        
+        self.SetProperty("fold", "1")
+        self.SetProperty("tab.timmy.whinge.level", "1")
+        self.SetMargins(0, 0)
+        self.SetTabWidth(4)
+        self.SetUseTabs(False)
+        
+        self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
+        self.SetEdgeColumn(78)
+        
+        # setup a margin to hold fold markers
+        self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
+        self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
+        self.SetMarginSensitive(2, True)
+        self.SetMarginWidth(2, 12)
+        
+        # like a flattened tree control using square headers
+        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_BOXMINUS,          "white", "#808080")
+        self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_BOXPLUS,           "white", "#808080")
+        self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,             "white", "#808080")
+        self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNER,           "white", "#808080")
+        self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_BOXPLUSCONNECTED,  "white", "#808080")
+        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
+        self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,           "white", "#808080")
+        
+        self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
+        self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
+        
+        # Make some styles, the lexer defines what each style is used
+        # for, we just have to define what each style looks like.
+        # This set is adapted from Scintilla sample property files.
+        
+        # global default styles for all languages
+        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "face:%(helv)s,size:%(size)d" % self.faces)
+        self.StyleClearAll()  # reset all to be like the default
+        
+        # global default styles for all languages
+        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "face:%(helv)s,size:%(size)d" % self.faces)
+        self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,  "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % self.faces)
+        self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % self.faces)
+        self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,  "fore:#FFFFFF,back:#0000FF,bold")
+        self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,    "fore:#000000,back:#FF0000,bold")
+        
+        # Python styles
+        # Default 
+        self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % self.faces)
+        # Comments
+        self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % self.faces)
+        # Number
+        self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % self.faces)
+        # String
+        self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % self.faces)
+        # Single quoted string
+        self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % self.faces)
+        # Keyword
+        self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % self.faces)
+        # Triple quotes
+        self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % self.faces)
+        # Triple double quotes
+        self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % self.faces)
+        # Class name definition
+        self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % self.faces)
+        # Function or method name definition
+        self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % self.faces)
+        # Operators
+        self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % self.faces)
+        # Identifiers
+        self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % self.faces)
+        # Comment-blocks
+        self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % self.faces)
+        # End of line where string is not closed
+        self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % self.faces)
+        
+        self.SetCaretForeground("BLUE")
+        
+    def OnKeyPressed(self, event):
+        """!Key pressed
+        
+        @todo implement code completion (see wxPython demo)
+        """
+        if not self.modified:
+            self.modified = True
+        
+        event.Skip()
+        
+    def OnUpdateUI(self, evt):
+        # check for matching braces
+        braceAtCaret = -1
+        braceOpposite = -1
+        charBefore = None
+        caretPos = self.GetCurrentPos()
+        
+        if caretPos > 0:
+            charBefore  = self.GetCharAt(caretPos - 1)
+            styleBefore = self.GetStyleAt(caretPos - 1)
+        
+        # check before
+        if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
+            braceAtCaret = caretPos - 1
+        
+        # check after
+        if braceAtCaret < 0:
+            charAfter = self.GetCharAt(caretPos)
+            styleAfter = self.GetStyleAt(caretPos)
+            
+            if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
+                braceAtCaret = caretPos
+        
+        if braceAtCaret >= 0:
+            braceOpposite = self.BraceMatch(braceAtCaret)
+        
+        if braceAtCaret != -1  and braceOpposite == -1:
+            self.BraceBadLight(braceAtCaret)
+        else:
+            self.BraceHighlight(braceAtCaret, braceOpposite)
+        
+    def OnMarginClick(self, evt):
+        # fold and unfold as needed
+        if evt.GetMargin() == 2:
+            if evt.GetShift() and evt.GetControl():
+                self.FoldAll()
+            else:
+                lineClicked = self.LineFromPosition(evt.GetPosition())
+                
+                if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
+                    if evt.GetShift():
+                        self.SetFoldExpanded(lineClicked, True)
+                        self.Expand(lineClicked, True, True, 1)
+                    elif evt.GetControl():
+                        if self.GetFoldExpanded(lineClicked):
+                            self.SetFoldExpanded(lineClicked, False)
+                            self.Expand(lineClicked, False, True, 0)
+                        else:
+                            self.SetFoldExpanded(lineClicked, True)
+                            self.Expand(lineClicked, True, True, 100)
+                    else:
+                        self.ToggleFold(lineClicked)
+        
+    def FoldAll(self):
+        lineCount = self.GetLineCount()
+        expanding = True
+        
+        # find out if we are folding or unfolding
+        for lineNum in range(lineCount):
+            if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
+                expanding = not self.GetFoldExpanded(lineNum)
+                break
+        
+        lineNum = 0
+        while lineNum < lineCount:
+            level = self.GetFoldLevel(lineNum)
+            if level & stc.STC_FOLDLEVELHEADERFLAG and \
+               (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
+                
+                if expanding:
+                    self.SetFoldExpanded(lineNum, True)
+                    lineNum = self.Expand(lineNum, True)
+                    lineNum = lineNum - 1
+                else:
+                    lastChild = self.GetLastChild(lineNum, -1)
+                    self.SetFoldExpanded(lineNum, False)
+                    
+                    if lastChild > lineNum:
+                        self.HideLines(lineNum+1, lastChild)
+            
+            lineNum = lineNum + 1
+        
+    def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
+        lastChild = self.GetLastChild(line, level)
+        line = line + 1
+        
+        while line <= lastChild:
+            if force:
+                if visLevels > 0:
+                    self.ShowLines(line, line)
+                else:
+                    self.HideLines(line, line)
+            else:
+                if doExpand:
+                    self.ShowLines(line, line)
+            
+            if level == -1:
+                level = self.GetFoldLevel(line)
+            
+            if level & stc.STC_FOLDLEVELHEADERFLAG:
+                if force:
+                    if visLevels > 1:
+                        self.SetFoldExpanded(line, True)
+                    else:
+                        self.SetFoldExpanded(line, False)
+                    
+                    line = self.Expand(line, doExpand, force, visLevels-1)
+                else:
+                    if doExpand and self.GetFoldExpanded(line):
+                        line = self.Expand(line, True, force, visLevels-1)
+                    else:
+                        line = self.Expand(line, False, force, visLevels-1)
+            else:
+                line = line + 1
+        
+        return line
+

Modified: grass/branches/develbranch_6/gui/wxpython/wxpythonlib.dox
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/wxpythonlib.dox	2012-04-29 10:27:57 UTC (rev 51564)
+++ grass/branches/develbranch_6/gui/wxpython/wxpythonlib.dox	2012-04-29 10:33:22 UTC (rev 51565)
@@ -122,6 +122,7 @@
  - goutput::GMStc
  - goutput::GMStdout
  - goutput::GMStderr
+ - goutput::PyStc
 - gui_core::gselect
  - gselect::Select
  - gselect::VectorSelect
@@ -286,6 +287,7 @@
  - frame::ModelEvtHandler
  - frame::VariablePanel
  - frame::ItemPanel
+ - frame::PythonPanel
 - gmodeler::menudata
  - menudata::ModelerData
 - gmodeler::model



More information about the grass-commit mailing list