Index: gui/wxpython/gui_modules/goutput.py
===================================================================
--- gui/wxpython/gui_modules/goutput.py	(revision 39934)
+++ gui/wxpython/gui_modules/goutput.py	(working copy)
@@ -24,6 +24,8 @@
 import time
 import threading
 import Queue
+import shlex
+import keyword
 
 import wx
 import wx.stc
@@ -145,6 +147,10 @@
             self._notebook = self.parent.notebook
         self.lineWidth       = 80
         self.pageid          = pageid
+        
+        # dictionary of modules (description, keywords, ...)
+#        self.modules = self.parent.menudata.GetModules()
+                
         # remember position of line begining (used for '\r')
         self.linePos         = -1
         
@@ -269,7 +275,8 @@
         
         # p1 = self.cmd_output.GetCurrentPos()
         p1 = self.cmd_output.GetEndStyled()
-        self.cmd_output.GotoPos(p1)
+#        self.cmd_output.GotoPos(p1)
+        self.cmd_output.DocumentEnd()
         
         for line in text.splitlines():
             # fill space
@@ -756,12 +763,25 @@
     def __init__(self, parent, id, margin=False, wrap=None):
         wx.stc.StyledTextCtrl.__init__(self, parent, id)
         self.parent = parent
+        self.SetUndoCollection(True)
 
         #
         # styles
         #                
         self.SetStyle()
         
+        #
+        # create map lists for autocompletion
+        #
+        self.datatypes = []
+        self.maplists = {}
+        self.__getfiles()
+        
+        #
+        # command history buffer
+        #
+        self.cmdbuffer = []
+        self.cmdindex = 0
 
         #
         # line margins
@@ -789,7 +809,24 @@
         # bindins
         #
         self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
         
+    def __getfiles(self):            
+        self.datatypes = ['rast',
+                        'rast3d',
+                        'vect',
+                        'oldvect',
+                        'asciivect',
+                        'labels',
+                        'region',
+                        'region3d',
+                        'group',
+                        '3dview']
+            
+        for item in self.datatypes:
+            maps = grass.read_command("g.mlist", "m", type=item)
+            self.maplists[item] = maps.splitlines()
+                                        
     def SetStyle(self):
         """!Set styles for styled text output windows with type face 
         and point size selected by user (Courier New 10 is default)"""
@@ -830,8 +867,150 @@
         self.StyleSetSpec(self.StyleError,   self.StyleErrorSpec)
         self.StyleSetSpec(self.StyleWarning, self.StyleWarningSpec)
         self.StyleSetSpec(self.StyleMessage, self.StyleMessageSpec)
-        self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec)        
+        self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec)    
+        
+    def OnKeyPressed(self, event):
+        line = ''
+        
+        if event.GetKeyCode() == 32 and event.ControlDown():
+            #autocomplete and tooltip
+            pos = self.GetCurrentPos()
+            if self.GetCharAt(pos-1) != 61:
+                #must be immediately right of "=" to get map list
+                return
+            
+            #get all text left of cursor
+            self.HomeExtend()
+            entry = self.GetSelectedText()
+            self.SetCurrentPos(pos)
+            
+            #must be a command left of the cursor somewhere
+            if '.' in entry:
+                cmdtype, dot, rest = entry.partition('.')
+                cmdname = rest.split()[0]
+                cmd = cmdtype+dot+cmdname
+                cmd = cmd.strip() 
+            elif 'nviz' in entry.lower():
+                cmdtype = ''
+                cmdname = cmd = 'nviz'
+            else:
+                return
 
+            cmdargs = entry.strip('=')
+            arg = cmdargs.rsplit(' ',1)[1]
+            
+            drastcmd = ['d.rast',
+                        'd.rgb',
+                        'd.his',
+                        'd.rast.arrow',
+                        'd.rast.num']
+                        
+            dvectcmd = ['d.vect',
+                        'd.vect.chart'
+                        'd.thematic.area',
+                        'd.vect.thematic']
+            
+            rastargs = ['map',
+                        'input',
+                        'elevation',
+                        'color',
+                        'rast',
+                        'raster',
+                        'red',
+                        'green',
+                        'blue',
+                        'h_map',
+                        'i_map',
+                        's_map',
+                        'hue_input',
+                        'intensity_input',
+                        'saturation_input',
+                        'red_input',
+                        'green_input',
+                        'blue_input']
+
+            # Tips
+            if event.ShiftDown():
+                self.CallTipSetBackground("yellow")
+                self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
+                                 'show some suff, maybe parameters..\n\n'
+                                 'fubar(param1, param2)')
+            # map/data entry completion
+            else:
+                #what kind of map/data type is desired?
+                maptype = ''
+                maplist = []
+                if (((cmdtype=='r' or cmdtype=='i' or cmd in drastcmd) and arg in rastargs) or
+                  ((cmd=='nviz' or cmdtype=='r3') and (arg=='elevation' or arg=='color')) or
+                  arg=='rast' or art=='raster'):
+                    maptype = 'rast'
+                elif (((cmdtype=='v' or cmd in dvectcmd) and (arg=='map' or arg=='input')) or
+                  (cmdtype=='r3' and arg=='input') or
+                  arg=='vect' or arg=='vector' or arg=='points'):
+                    maptype = 'vect'
+                elif ((cmdtype=='r3' and (arg=='map' or arg=='input')) or
+                  (cmdtype=='nviz' and arg=='volume') or arg=='rast3d'):
+                    maptype = 'rast3d'
+                elif arg=='labels':
+                    maptype ='labels'
+                elif arg=='region':
+                    maptype ='region'
+                elif arg=='region3d':
+                    maptype ='region3d'
+                elif arg=='group':
+                    maptype ='group'
+                elif arg=='3dview':
+                    maptype ='3dview'
+                
+                maplist = self.maplists[maptype]
+
+                self.AutoCompShow(0, " ".join(maplist))
+        elif event.GetKeyCode() in [315,317] and event.ControlDown():
+            #recall command history
+            self.DocumentEnd()
+            
+            if event.GetKeyCode() == 315:
+                self.cmdindex = self.cmdindex - 1
+            if event.GetKeyCode() == 317:
+                self.cmdindex = self.cmdindex + 1
+            if self.cmdindex < 0:
+                self.cmdindex = 0
+            if self.cmdindex > len(self.cmdbuffer) - 1:
+                self.cmdindex = len(self.cmdbuffer) - 1
+            
+            txt = self.cmdbuffer[self.cmdindex]
+            self.DelLineLeft()
+            self.DelLineRight()
+            pos = self.GetCurrentPos()            
+            self.InsertText(pos,txt)
+            
+        elif event.GetKeyCode() == 13 and self.AutoCompActive() == False:
+            #run commands
+            line = str(self.GetCurLine()[0]).strip()
+            if line == '' or line == None or \
+                ('.' not in line and 'nviz' not in line.lower()): 
+                # must have some text and basic test for GRASS command
+                self.DocumentEnd()
+                return            
+                        
+            cmd = shlex.split(str(line))
+            if len(cmd) > 1:
+                self.parent.RunCmd(cmd, switchPage = True)
+            else:
+                self.parent.RunCmd(cmd, switchPage = False)
+            if self.GetColumn(self.GetCurrentPos()) < len(line):
+                self.DeleteBack()
+                self.Undo()
+                self.DocumentEnd()
+                
+            #add command to buffer    
+            self.cmdbuffer.append(line)
+            self.cmdindex = len(self.cmdbuffer) - 1
+            #TODO set focus back to terminal after command
+
+        else:
+            event.Skip()
+
     def OnDestroy(self, evt):
         """!The clipboard contents can be preserved after
         the app has exited"""
         