[GRASS-SVN] r47615 - in grass/branches/develbranch_6/gui/wxpython: . gui_modules

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Aug 14 05:01:35 EDT 2011


Author: martinl
Date: 2011-08-14 02:01:34 -0700 (Sun, 14 Aug 2011)
New Revision: 47615

Modified:
   grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py
   grass/branches/develbranch_6/gui/wxpython/wxgui.py
Log:
wxGUI: sync colorrules.py with trunk


Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py	2011-08-14 08:45:36 UTC (rev 47614)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py	2011-08-14 09:01:34 UTC (rev 47615)
@@ -5,7 +5,11 @@
 vector rgb_column attributes.
 
 Classes:
+ - RulesPanel
  - ColorTable
+ - RasterColorTable
+ - VectorColorTable
+ - ThematicVectorTable
  - BuferedWindow
 
 (C) 2008, 2010-2011 by the GRASS Development Team
@@ -14,12 +18,14 @@
 
 @author Michael Barton (Arizona State University)
 @author Martin Landa <landa.martin gmail.com> (various updates)
- at author Anna Kratochvilova (load/save raster color tables)
+ at author Anna Kratochvilova <kratochanna gmail.com> (split to base and derived classes)
 """
 
 import os
 import sys
 import shutil
+import copy
+import tempfile
 
 import wx
 import wx.lib.colourselect as csel
@@ -33,155 +39,305 @@
 import gselect
 import render
 import utils
+import menuform
 from debug import Debug as Debug
 from preferences import globalSettings as UserSettings
+from nviz_mapdisp import wxUpdateProperties
 
+
+class RulesPanel:
+    def __init__(self, parent, mapType, columnType, properties, panelWidth = 180):
+        """!Create rules panel
+        
+        @param mapType raster/vector
+        @param columnType color/size for choosing widget type
+        @param properties properties of classes derived from ColorTabel
+        @param panelWidth width of scroll panel"""
+        
+        self.ruleslines = {}
+        self.mapType = mapType
+        self.columnType = columnType
+        self.properties = properties
+        self.parent = parent
+        
+        self.mainPanel = scrolled.ScrolledPanel(parent, id = wx.ID_ANY,
+                                          size = (panelWidth, 300),
+                                          style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
+        self.mainPanel.SetupScrolling(scroll_x = False)
+        self.mainSizer = wx.FlexGridSizer(cols = 3, vgap = 6, hgap = 4)
+        # put small border at the top of panel
+        for i in range(3):
+            self.mainSizer.Add(item = wx.Size(3, 3))
+        
+        self.mainPanel.SetSizer(self.mainSizer)
+        self.mainPanel.SetAutoLayout(True)
+        
+        if self.mapType == 'vector' and self.columnType == 'color':
+            self.label = wx.StaticText(parent, id = wx.ID_ANY, label =  _("Set color for attribute values:"))
+        elif self.mapType == 'vector' and self.columnType == 'size':
+            if self.parent.vectorType == 'points':
+                label = label = _("Set size for attribute values:")
+            else:
+                label = label = _("Set width for attribute values:")                
+            self.label = wx.StaticText(parent, id = wx.ID_ANY, label = label)
+        
+        #  determines how many rules should be added
+        self.numRules = wx.SpinCtrl(parent, id = wx.ID_ANY,
+                                    min = 1, max = 1e6)
+        # add rules
+        self.btnAdd = wx.Button(parent, id = wx.ID_ADD)
+        
+        self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAddRules)
+    
+    def Clear(self):
+        """!Clear and widgets and delete information"""
+        self.ruleslines.clear()
+        self.mainPanel.DestroyChildren()
+    
+    def OnAddRules(self, event):
+        """!Add rules button pressed"""
+        nrules = self.numRules.GetValue()
+        self.AddRules(nrules)
+        
+    def AddRules(self, nrules, start = False):
+        """!Add rules
+        
+        @param start set widgets (not append)"""
+        
+        snum = len(self.ruleslines.keys())
+        if start:
+            snum = 0
+        for num in range(snum, snum + nrules):
+            # enable
+            enable = wx.CheckBox(parent = self.mainPanel, id = num)
+            enable.SetValue(True)
+            enable.Bind(wx.EVT_CHECKBOX, self.OnRuleEnable)
+            # value
+            txt_ctrl = wx.TextCtrl(parent = self.mainPanel, id = 1000 + num,
+                                   size = (80, -1),
+                                   style = wx.TE_NOHIDESEL)
+            if self.mapType == 'vector':
+                txt_ctrl.SetToolTipString(_("Enter vector attribute values (e.g. 5) "
+                                            "or ranges (e.g. 5 to 10)"))
+            txt_ctrl.Bind(wx.EVT_TEXT, self.OnRuleValue)
+            if self.columnType == 'color':
+                # color
+                columnCtrl = csel.ColourSelect(self.mainPanel, id = 2000 + num,
+                                               size  =  globalvar.DIALOG_COLOR_SIZE)
+                columnCtrl.Bind(csel.EVT_COLOURSELECT, self.OnRuleColor)
+                if not start:
+                    self.ruleslines[enable.GetId()] = { 'value' : '',
+                                                        'color': "0:0:0" }
+            if self.columnType == 'size':
+                # size
+                columnCtrl = wx.SpinCtrl(self.mainPanel, id = 2000 + num,
+                                         size = (50, -1), min = 1, max = 1e3,
+                                         initial = 5)
+                columnCtrl.Bind(wx.EVT_SPINCTRL, self.OnRuleSize)
+                if not start:
+                    self.ruleslines[enable.GetId()] = { 'value' : '',
+                                                        'size': 5 }
+            
+            self.mainSizer.Add(item = enable, proportion = 0,
+                              flag = wx.ALIGN_CENTER_VERTICAL)
+            self.mainSizer.Add(item = txt_ctrl, proportion = 0,
+                              flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
+            self.mainSizer.Add(item = columnCtrl, proportion = 0,
+                              flag = wx.ALIGN_CENTER | wx.RIGHT, border = 10)
+        
+        self.mainPanel.Layout()
+        self.mainPanel.SetupScrolling(scroll_x = False)
+       
+    
+    def OnRuleEnable(self, event):
+        """!Rule enabled/disabled"""
+        id = event.GetId()
+        
+        if event.IsChecked():
+            self.mainPanel.FindWindowById(id + 1000).Enable()
+            self.mainPanel.FindWindowById(id + 2000).Enable()
+            if self.mapType == 'vector':
+                value = self.SQLConvert(self.mainPanel.FindWindowById(id + 1000).GetValue(), self.columnType)
+            else:
+                value = self.mainPanel.FindWindowById(id + 1000).GetValue()
+            color = self.mainPanel.FindWindowById(id + 2000).GetValue()
+            
+            if self.columnType == 'color':
+            # color
+                color_str = str(color[0]) + ':' \
+                          + str(color[1]) + ':' \
+                          + str(color[2])
+                self.ruleslines[id] = {'value' : value,
+                                       'color' : color_str }
+                
+            else:
+            # size
+                self.ruleslines[id] = {'value' : value,
+                                       'size'  : float(color) }
+        
+        else:
+            self.mainPanel.FindWindowById(id + 1000).Disable()
+            self.mainPanel.FindWindowById(id + 2000).Disable()
+            del self.ruleslines[id]
+        
+    def OnRuleColor(self, event):
+        """!Rule color changed"""
+        num = event.GetId()
+        
+        rgba_color = event.GetValue()
+        
+        rgb_string = str(rgba_color[0]) + ':' \
+                   + str(rgba_color[1]) + ':' \
+                   + str(rgba_color[2])
+        
+        self.ruleslines[num-2000]['color'] = rgb_string
+     
+    def OnRuleSize(self, event):
+        """!Rule size changed"""
+        num = event.GetId()
+        size = event.GetInt()
+        
+        self.ruleslines[num - 2000]['size'] = size
+        
+    def OnRuleValue(self, event):
+        """!Rule value changed"""
+        num = event.GetId()
+        vals = event.GetString().strip()
+        
+        if vals == '':
+            return
+        
+        if self.mapType == 'vector':
+            self.SetVectorRule(num, vals)
+        else:
+            self.SetRasterRule(num, vals)
+
+    def SetRasterRule(self, num, vals): 
+        """!Set raster rule"""       
+        self.ruleslines[num-1000]['value'] = vals
+
+    def SetVectorRule(self, num, vals):
+        """!Set vector rule"""
+        tc = self.mainPanel.FindWindowById(num)
+        if self.columnType == 'color':
+            source, target = 'source_rgb', 'rgb'
+        else:
+            source, target = 'source_size', 'size'
+        if self.properties[source] == '' or self.properties[target] == '':
+            tc.SetValue('')
+            gcmd.GMessage(parent = self,
+                          message = _("Please select attribute column "
+                                    "and RGB color column first"))
+        else:
+            try:
+                self.ruleslines[num-1000]['value'] = self.SQLConvert(vals, self.columnType)
+            except ValueError:
+                tc.SetValue('')
+                self.ruleslines[num-1000]['value'] = ''
+                
+                return
+            
+    def Enable(self, enable = True):
+        """!Enable/Disable all widgets"""
+        for child in self.mainPanel.GetChildren():
+            child.Enable(enable)
+        self.btnAdd.Enable(enable)
+        self.numRules.Enable(enable)
+        
+        
+    def LoadRules(self):
+        """!Fill rule widgets from ruleslines"""
+        message = ""
+        for item in range(len(self.ruleslines)):
+            self.mainPanel.FindWindowById(item + 1000).SetValue(self.ruleslines[item]['value'])
+            r, g, b = (0, 0, 0) # default
+            if self.ruleslines[item][self.columnType] == '':
+                if self.columnType == 'color':
+                    self.ruleslines[item][self.columnType] = '%d:%d:%d' % (r, g, b)
+                elif self.columnType == 'size':
+                    self.ruleslines[item][self.columnType] = 100
+                
+            if self.columnType == 'color':
+                try:
+                    r, g, b = map(int, self.ruleslines[item][self.columnType].split(':'))
+                except ValueError, e:
+                    message =  _("Bad color format. Use color format '0:0:0'")
+                self.mainPanel.FindWindowById(item + 2000).SetValue((r, g, b))
+            else:
+                value = float(self.ruleslines[item][self.columnType])
+                self.mainPanel.FindWindowById(item + 2000).SetValue(value)
+                
+        if message:
+            gcmd.GMessage(parent = self.parent, message = message)
+            return False
+        
+        return True    
+    
+    def SQLConvert(self, vals, type = 'color'):
+        """!Prepare value for SQL query"""
+        if type == 'color':
+            source = 'source_rgb'
+        elif type == 'size':
+            source = 'source_size'
+        valslist = []
+        valslist = vals.split('to')
+        if len(valslist) == 1:
+            sqlrule = '%s=%s' % (self.properties[source], valslist[0])
+        elif len(valslist) > 1:
+            sqlrule = '%s>=%s AND %s<=%s' % (self.properties[source], valslist[0],
+                                             self.properties[source], valslist[1])
+        else:
+            return None
+        
+        return sqlrule  
+
 class ColorTable(wx.Frame):
-    def __init__(self, parent, raster, id = wx.ID_ANY, title = _("Set color table"),
+    def __init__(self, parent, title, id = wx.ID_ANY,
                  style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
                  **kwargs):
         """!Dialog for interactively entering rules for map management
         commands
 
         @param raster True to raster otherwise vector
+        @param nviz True if ColorTable is called from nviz thematic mapping
         """
         self.parent = parent # GMFrame
-        self.raster = raster
         
         wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
         
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         
+        # instance of render.Map to be associated with display
+        self.Map  = render.Map() 
+        
         # input map to change
         self.inmap = ''
-
-        if self.raster:
-            # raster properties
-            self.properties = {
-                # min cat in raster map
-                'min' : None,
-                # max cat in raster map
-                'max' : None,
-                }
-        else:
-            # vector properties
-            self.properties = {
-                # list of database layers for vector (minimum of 1)
-                'layers' : ['1'],
-                # list of database columns for vector
-                'columns' : [],
-                # vector layer for attribute table to use for setting color
-                'layer' : 1, 
-                # vector attribute table used for setting color         
-                'table' : '',
-                # vector attribute column for assigning colors
-                'column' : '', 
-                # vector attribute column to use for storing colors
-                'rgb' : '',
-                }
-
-        # rules for creating colortable
-        self.ruleslines = {}
-
-        # instance of render.Map to be associated with display
-        self.Map   = render.Map()  
-
         # reference to layer with preview
         self.layer = None          
+           
+        # layout
+        self._doLayout()
         
-        if self.raster:
-            self.SetTitle(_('Create new color table for raster map'))
-            crlabel = _('Enter raster category values or percents')
-        else:
-            self.SetTitle(_('Create new color table for vector map'))
-            crlabel = _('Enter vector attribute values or ranges (n or n1 to n2)')
-        
-        # top controls
-        if self.raster:
-            maplabel = _('Select raster map:')
-        else:
-            maplabel = _('Select vector map:')
-        inputBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
-                                label = " %s " % maplabel)
-        self.inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
-        if self.raster:
-            elem = 'cell'
-        else:
-            elem = 'vector'
-        self.selectionInput = gselect.Select(parent = self, id = wx.ID_ANY,
-                                             size = globalvar.DIALOG_GSELECT_SIZE,
-                                             type = elem)
-        
-        self.ovrwrtcheck = wx.CheckBox(parent = self, id = wx.ID_ANY,
-                                       label = _('replace existing color table'))
-        self.ovrwrtcheck.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
-        
-        if self.raster:
-            self.btnSave = wx.Button(parent = self, id = wx.ID_SAVE)
-            self.btnSave.SetToolTipString(_('Save color table to file'))
-        
-        if not self.raster:
-            self.cb_vl_label = wx.StaticText(parent = self, id = wx.ID_ANY,
-                                             label = _('Layer:'))
-            self.cb_vc_label = wx.StaticText(parent = self, id = wx.ID_ANY,
-                                             label = _('Attribute column:'))
-            self.cb_vrgb_label = wx.StaticText(parent = self, id = wx.ID_ANY,
-                                               label = _('RGB color column:'))
-            self.cb_vlayer = gselect.LayerSelect(self)
-            self.cb_vcol = gselect.ColumnSelect(self)
-            self.cb_vrgb = gselect.ColumnSelect(self)
-        
-        # color table and preview window
-        self.cr_label = wx.StaticText(parent = self, id = wx.ID_ANY,
-                                      label = crlabel)
-        self.cr_panel = self._colorRulesPanel()
-        # add two rules as default
-        self.AddRules(2)
-        
-        self.numRules = wx.SpinCtrl(parent = self, id = wx.ID_ANY,
-                                    min = 1, max = 1e6)
-        
-        # initialize preview display
-        self.InitDisplay()
-        self.preview = BufferedWindow(self, id = wx.ID_ANY, size = (400, 300),
-                                      Map = self.Map)
-        self.preview.EraseMap()
-        
-        self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
-        self.btnApply = wx.Button(parent = self, id = wx.ID_APPLY) 
-        self.btnOK = wx.Button(parent = self, id = wx.ID_OK)
-        self.btnOK.SetDefault()
-        self.btnOK.Enable(False)
-        self.btnApply.Enable(False)
-        
-        self.btnPreview = wx.Button(parent = self, id = wx.ID_ANY,
-                                    label = _("Preview"))
-        self.btnPreview.Enable(False)
-        self.btnAdd = wx.Button(parent = self, id = wx.ID_ADD)
-        self.helpbtn = wx.Button(parent = self, id = wx.ID_HELP)
-            
-        
         # bindings
-        self.Bind(wx.EVT_BUTTON, self.OnHelp, self.helpbtn)
+        self.Bind(wx.EVT_BUTTON, self.OnHelp, self.btnHelp)
         self.selectionInput.Bind(wx.EVT_TEXT, self.OnSelectionInput)
         self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
         self.Bind(wx.EVT_BUTTON, self.OnApply, self.btnApply)
         self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
+        self.Bind(wx.EVT_CLOSE,  self.OnCloseWindow)
+       
         self.Bind(wx.EVT_BUTTON, self.OnPreview, self.btnPreview)
-        self.Bind(wx.EVT_BUTTON, self.OnAddRules, self.btnAdd)
-        self.Bind(wx.EVT_CLOSE,  self.OnCloseWindow)
         
-        # additional bindings for raster/vector color management
-        if self.raster:
-            self.Bind(wx.EVT_BUTTON, self.OnSaveTable, self.btnSave)
-        else:
-            self.Bind(wx.EVT_COMBOBOX, self.OnLayerSelection, self.cb_vlayer)
-            self.Bind(wx.EVT_COMBOBOX, self.OnColumnSelection, self.cb_vcol)
-            self.Bind(wx.EVT_COMBOBOX, self.OnRGBColSelection, self.cb_vrgb)
-
-        # set map layer from layer tree
+    def _initLayer(self):
+        """!Set initial layer when opening dialog"""
+        # set map layer from layer tree, first selected,
+        # if not the right type, than select another
         try:
-            layer = self.parent.curr_page.maptree.layer_selected
+            sel = self.parent.curr_page.maptree.layer_selected
+            if sel and self.parent.curr_page.maptree.GetPyData(sel)[0]['type'] == self.type:
+                layer = sel
+            else:
+                layer = self.parent.curr_page.maptree.FindItemByData(key = 'type', value = self.type)
         except:
             layer = None
         if layer:
@@ -190,150 +346,88 @@
             type = mapLayer.GetType()
             self.selectionInput.SetValue(name)
             self.inmap = name
-            self.OnSelectionInput(None)
+            
+    def _createPreview(self, parent):
+        """!Create preview"""
+        # initialize preview display
+        self.InitDisplay()
+        self.preview = BufferedWindow(parent, id = wx.ID_ANY, size = (400, 300),
+                                      Map = self.Map)
+        self.preview.EraseMap()
         
-        # layout
-        self.__doLayout()
-        self.SetMinSize(self.GetSize())
+    def _createButtons(self):
+        """!Create buttons for leaving dialog"""
+        self.btnHelp = wx.Button(parent = self, id = wx.ID_HELP)
+        self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+        self.btnApply = wx.Button(parent = self, id = wx.ID_APPLY) 
+        self.btnOK = wx.Button(parent = self, id = wx.ID_OK)
         
-        self.CentreOnScreen()
-        self.Show()
+        self.btnOK.SetDefault()
+        self.btnOK.Enable(False)
+        self.btnApply.Enable(False)
         
-    def __doLayout(self):
-        sizer = wx.BoxSizer(wx.VERTICAL)
+        # layout
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(self.btnHelp,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        btnSizer.Add(self.btnCancel,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        btnSizer.Add(self.btnApply,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        btnSizer.Add(self.btnOK,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
         
-        #
-        # input
-        #
-        self.inputSizer.Add(item = self.selectionInput,
-                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
-        replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
-        replaceSizer.Add(item = self.ovrwrtcheck, proportion = 1,
-                         flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
-        if self.raster:
-            replaceSizer.Add(item = self.btnSave, proportion = 0,
-                            flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
-        
-        self.inputSizer.Add(item = replaceSizer, proportion = 1,
-                       flag = wx.ALL | wx.EXPAND, border = 0)
-
-        #
-        # body & preview
-        #
+        return btnSizer
+    
+    def _createBody(self, parent):
+        """!Create dialog body consisting of rules and preview"""
         bodySizer =  wx.GridBagSizer(hgap = 5, vgap = 5)
         row = 0
+        
+        # label with range
+        self.cr_label = wx.StaticText(parent, id = wx.ID_ANY, label = '')
         bodySizer.Add(item = self.cr_label, pos = (row, 0), span = (1, 3),
                       flag = wx.ALL, border = 5)
+        row += 1
         
-        if not self.raster:
-            vSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
-            vSizer.Add(self.cb_vl_label, pos = (0, 0),
-                       flag = wx.ALIGN_CENTER_VERTICAL)
-            vSizer.Add(self.cb_vlayer,  pos = (0, 1),
-                       flag = wx.ALIGN_CENTER_VERTICAL)
-            vSizer.Add(self.cb_vc_label, pos = (0, 2),
-                       flag = wx.ALIGN_CENTER_VERTICAL)
-            vSizer.Add(self.cb_vcol, pos = (0, 3),
-                       flag = wx.ALIGN_CENTER_VERTICAL)
-            vSizer.Add(self.cb_vrgb_label, pos = (1, 2),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-            vSizer.Add(self.cb_vrgb, pos = (1, 3),
-                       flag = wx.ALIGN_CENTER_VERTICAL)
+        # color table
+        self.colorRulesPanel = RulesPanel(parent = parent, mapType = self.type,
+                                          columnType = 'color', properties = self.properties)
+        if self.type == 'vector':
+            bodySizer.Add(item = self.colorRulesPanel.label, pos = (row, 0), span = (1, 2),
+                          flag = wx.ALL, border = 5)
             row += 1
-            bodySizer.Add(item = vSizer, pos = (row, 0), span = (1, 3))
         
-        row += 1
-        bodySizer.Add(item = self.cr_panel, pos = (row, 0), span = (1, 2))
+        bodySizer.Add(item = self.colorRulesPanel.mainPanel, pos = (row, 0), span = (1, 2),
+                      flag = wx.ALL, border = 5)
+        # add two rules as default
+        self.colorRulesPanel.AddRules(2)
         
+        # preview window
+        self._createPreview(parent = parent)
         bodySizer.Add(item = self.preview, pos = (row, 2),
                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10)
         bodySizer.AddGrowableRow(row)
         bodySizer.AddGrowableCol(2)
+        row += 1
         
-        row += 1
-        bodySizer.Add(item = self.numRules, pos = (row, 0),
+        # add rules button and spin to sizer
+        bodySizer.Add(item = self.colorRulesPanel.numRules, pos = (row, 0),
                       flag = wx.ALIGN_CENTER_VERTICAL)
+        bodySizer.Add(item = self.colorRulesPanel.btnAdd, pos = (row, 1))
         
-        bodySizer.Add(item = self.btnAdd, pos = (row, 1))
+        # preview button
+        self.btnPreview = wx.Button(parent, id = wx.ID_ANY,
+                                    label = _("Preview"))
         bodySizer.Add(item = self.btnPreview, pos = (row, 2),
                       flag = wx.ALIGN_RIGHT)
+        self.btnPreview.Enable(False)
+        self.btnPreview.SetToolTipString(_("Show preview of map "
+                                           "(current Map Display extent is used)."))
+                
+        return bodySizer
+    
         
-        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
-        btnSizer.Add(self.helpbtn,
-                     flag = wx.LEFT | wx.RIGHT, border = 5)
-        btnSizer.Add(self.btnCancel,
-                     flag = wx.LEFT | wx.RIGHT, border = 5)
-        btnSizer.Add(self.btnApply,
-                     flag = wx.LEFT | wx.RIGHT, border = 5)
-        btnSizer.Add(self.btnOK,
-                     flag = wx.LEFT | wx.RIGHT, border = 5)
-        
-        sizer.Add(item = self.inputSizer, proportion = 0,
-                  flag = wx.ALL | wx.EXPAND, border = 5)
-        
-        sizer.Add(item = bodySizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 5)
-        
-        sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
-                                     style = wx.LI_HORIZONTAL),
-                  proportion = 0,
-                  flag = wx.EXPAND | wx.ALL, border = 5) 
-        
-        sizer.Add(item = btnSizer, proportion = 0,
-                  flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
-        
-        self.SetSizer(sizer)
-        sizer.Fit(self)
-        self.Layout()
-        
-    def _colorRulesPanel(self):
-        """!Create rules panel"""
-        cr_panel = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY,
-                                          size = (180, 300),
-                                          style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
-        cr_panel.SetupScrolling(scroll_x = False)
-        self.cr_sizer = wx.GridBagSizer(vgap = 2, hgap = 4)
-        
-        cr_panel.SetSizer(self.cr_sizer)
-        cr_panel.SetAutoLayout(True)
-        
-        return cr_panel        
-
-    def OnAddRules(self, event):
-        """!Add rules button pressed"""
-        nrules = self.numRules.GetValue()
-        self.AddRules(nrules)
-        
-    def AddRules(self, nrules):
-        """!Add rules"""
-        snum = len(self.ruleslines.keys())
-        for num in range(snum, snum + nrules):
-            # enable
-            enable = wx.CheckBox(parent = self.cr_panel, id = num)
-            enable.SetValue(True)
-            self.Bind(wx.EVT_CHECKBOX, self.OnRuleEnable, enable)
-            # value
-            txt_ctrl = wx.TextCtrl(parent = self.cr_panel, id = 1000 + num,
-                                   size = (90, -1),
-                                   style = wx.TE_NOHIDESEL)
-            self.Bind(wx.EVT_TEXT, self.OnRuleValue, txt_ctrl)
-            # color
-            color_ctrl = csel.ColourSelect(self.cr_panel, id = 2000 + num,
-                                           size  =  globalvar.DIALOG_COLOR_SIZE)
-            self.Bind(csel.EVT_COLOURSELECT, self.OnRuleColor, color_ctrl)
-            self.ruleslines[enable.GetId()] = { 'value' : '',
-                                                'color': "0:0:0" }
-            
-            self.cr_sizer.Add(item = enable, pos = (num, 0),
-                              flag = wx.ALIGN_CENTER_VERTICAL)
-            self.cr_sizer.Add(item = txt_ctrl, pos = (num, 1),
-                              flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
-            self.cr_sizer.Add(item = color_ctrl, pos = (num, 2),
-                              flag = wx.ALIGN_CENTER | wx.RIGHT, border = 10)
-        
-        self.cr_panel.Layout()
-        self.cr_panel.SetupScrolling(scroll_x = False)
-        
     def InitDisplay(self):
         """!Initialize preview display, set dimensions and region
         """
@@ -341,29 +435,163 @@
         self.height = self.Map.height = 300
         self.Map.geom = self.width, self.height
 
-    def OnErase(self, event):
-        """!Erase the histogram display
-        """
-        self.PreviewWindow.Draw(self.HistWindow.pdc, pdctype = 'clear')
-
     def OnCloseWindow(self, event):
         """!Window closed
         Also remove associated rendered images
         """
         self.Map.Clean()
         self.Destroy()
+          
+    def OnApply(self, event):
+        """!Apply selected color table
+
+        @return True on success otherwise False
+        """
+        ret = self.CreateColorTable()
+        if ret:
+            display = self.parent.GetLayerTree().GetMapDisplay()
+            if display and display.IsAutoRendered():
+                display.GetWindow().UpdateMap(render = True)
+            
+        return ret
+
+    def OnOK(self, event):
+        """!Apply selected color table and close the dialog"""
+        if self.OnApply(event):
+            self.Destroy()
+    
+    def OnCancel(self, event):
+        """!Do not apply any changes and close the dialog"""
+        self.Destroy()
         
+    def DoPreview(self, ltype, cmdlist):
+        """!Update preview (based on computational region)"""
+        
+        if not self.layer:
+            self.layer = self.Map.AddLayer(type = ltype, name = 'preview', command = cmdlist,
+                                           l_active = True, l_hidden = False, l_opacity = 1.0,
+                                           l_render = False) 
+        else:
+            self.layer.SetCmd(cmdlist)
+        
+        # apply new color table and display preview
+        self.CreateColorTable(force = True)
+        self.preview.UpdatePreview()
+        
+    def RunHelp(self, cmd):
+        """!Show GRASS manual page"""
+        gcmd.RunCommand('g.manual',
+                        quiet = True,
+                        parent = self,
+                        entry = cmd)
+        
+    def _IsNumber(self, s):
+        """!Check if 's' is a number"""
+        try:
+            float(s)
+            return True
+        except ValueError:
+            return False
+        
+
+class RasterColorTable(ColorTable):
+    def __init__(self, parent, **kwargs):
+        """!Dialog for interactively entering color rules for raster maps"""
+
+        self.type = 'raster'
+        
+        # raster properties
+        self.properties = {
+            # min cat in raster map
+            'min' : None,
+            # max cat in raster map
+            'max' : None,
+            }        
+        
+        ColorTable.__init__(self, parent,
+                            title = _('Create new color table for raster map'), **kwargs)
+       
+       
+        # additional bindings for raster color management
+        self.Bind(wx.EVT_BUTTON, self.OnSaveTable, self.btnSave)
+        
+        self._initLayer()
+        
+        self.SetMinSize(self.GetSize()) 
+        self.CentreOnScreen()
+        self.Show()
+    
+    def _createMapSelection(self, parent):
+        """!Create map selection part of dialog"""
+        # top controls
+        maplabel = _('Select raster map:')
+        inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
+                                label = " %s " % maplabel)
+        inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
+
+        self.selectionInput = gselect.Select(parent, id = wx.ID_ANY,
+                                             size = globalvar.DIALOG_GSELECT_SIZE,
+                                             type = 'cell')
+        self.ovrwrtcheck = wx.CheckBox(parent, id = wx.ID_ANY,
+                                       label = _('replace existing color table'))
+        self.ovrwrtcheck.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
+        
+        self.btnSave = wx.Button(parent, id = wx.ID_SAVE)
+        self.btnSave.SetToolTipString(_('Save color table to file'))
+            
+        # layout
+        inputSizer.Add(item = self.selectionInput,
+                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
+        replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
+        replaceSizer.Add(item = self.ovrwrtcheck, proportion = 1,
+                         flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
+    
+        replaceSizer.Add(item = self.btnSave, proportion = 0,
+                        flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        
+        inputSizer.Add(item = replaceSizer, proportion = 1,
+                       flag = wx.ALL | wx.EXPAND, border = 0)
+    
+        return inputSizer
+    
+    def _doLayout(self):
+        """!Do main layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        #
+        # map selection
+        #
+        mapSelection = self._createMapSelection(parent = self)
+        sizer.Add(item = mapSelection, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # body & preview
+        #
+        bodySizer = self._createBody(parent = self)
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # buttons
+        #
+        btnSizer = self._createButtons()
+        sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
+                                       style = wx.LI_HORIZONTAL), proportion = 0,
+                                       flag = wx.EXPAND | wx.ALL, border = 5) 
+        
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        
+        self.SetSizer(sizer)
+        sizer.Layout()
+        sizer.Fit(self)
+        self.Layout()
+    
     def OnSelectionInput(self, event):
-        """!Raster/vector map selected"""
+        """!Raster map selected"""
         if event:
             self.inmap = event.GetString()
 
         if self.inmap:
-            if self.raster:
-                mapType = 'cell'
-            else:
-                mapType = 'vector'
-            if not grass.find_file(name = self.inmap, element = mapType)['file']:
+            if not grass.find_file(name = self.inmap, element = 'cell')['file']:
                 self.inmap = None
         
         if not self.inmap:
@@ -373,134 +601,40 @@
             self.OnLoadTable(event)
             return
         
-        if self.raster:
-            info = grass.raster_info(map = self.inmap)
-            
-            if info:
-                self.properties['min'] = info['min']
-                self.properties['max'] = info['max']
-                self.OnLoadTable(event)
-            else:
-                self.inmap = ''
-                self.properties['min'] = self.properties['max'] = None
-                self.btnPreview.Enable(False)
-                self.btnOK.Enable(False)
-                self.btnApply.Enable(False)
-                self.preview.EraseMap()
-                self.cr_label.SetLabel(_('Enter raster category values or percents'))
-                return
-            
-            if info['datatype'] == 'CELL':
-                mapRange = _('range')
-            else:
-                mapRange = _('fp range')
-            self.cr_label.SetLabel(_('Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') %
-                                     { 'range' : mapRange,
-                                       'min' : self.properties['min'],
-                                       'max' : self.properties['max'] })
+        info = grass.raster_info(map = self.inmap)
         
+        if info:
+            self.properties['min'] = info['min']
+            self.properties['max'] = info['max']
+            self.OnLoadTable(event)
         else:
-            # initialize layer selection combobox
-            self.cb_vlayer.InsertLayers(self.inmap)
-            # initialize attribute table for layer=1
-            layer = int(self.properties['layer'])
-            self.properties['table'] = gselect.VectorDBInfo(self.inmap).layers[layer]['table']
-            # initialize column selection comboboxes 
-            self.cb_vcol.InsertColumns(vector = self.inmap, layer = layer)
-            self.cb_vrgb.InsertColumns(vector = self.inmap, layer = layer)
-            self.Update()
-    
-        self.btnPreview.Enable(True)
-        self.btnOK.Enable(True)
-        self.btnApply.Enable(True)
-        
-    def OnLayerSelection(self, event):
-        # reset choices in column selection comboboxes if layer changes
-        self.vlayer = int(event.GetString())
-        self.vtable = gselect.VectorDBInfo(self.inmap).layers[str(self.vlayer)]
-        self.cb_vcol.InsertColumns(vector = self.inmap, layer = self.vlayer)
-        self.cb_vrgb.InsertColumns(vector = self.inmap, layer = self.vlayer)
-        self.Update()
-        
-    def OnColumnSelection(self, event):
-        self.properties['column'] = event.GetString()
-    
-    def OnRGBColSelection(self, event):
-        self.properties['rgb'] = event.GetString()
-        
-    def OnRuleEnable(self, event):
-        """!Rule enabled/disabled"""
-        id = event.GetId()
-        
-        if event.IsChecked():
-            value = self.FindWindowById(id+1000).GetValue()
-            color = self.FindWindowById(id+2000).GetValue()
-            color_str = str(color[0]) + ':' \
-                + str(color[1]) + ':' + \
-                str(color[2])
-            
-            self.ruleslines[id] = {
-                'value' : value,
-                'color' : color_str }
-        else:
-            del self.ruleslines[id]
-        
-    def OnRuleValue(self, event):
-        """!Rule value changed"""
-        num = event.GetId()
-        vals = event.GetString().strip()
-        
-        if vals == '':
+            self.inmap = ''
+            self.properties['min'] = self.properties['max'] = None
+            self.btnPreview.Enable(False)
+            self.btnOK.Enable(False)
+            self.btnApply.Enable(False)
+            self.preview.EraseMap()
+            self.cr_label.SetLabel(_('Enter raster category values or percents'))
             return
-
-        tc = self.FindWindowById(num)
         
-        if self.raster:
-            self.ruleslines[num-1000]['value'] = vals
-        
+        if info['datatype'] == 'CELL':
+            mapRange = _('range')
         else:
-            if self.properties['column'] == '' or self.properties['rgb'] == '':
-                tc.SetValue('')
-                gcmd.GMessage(parent = self,
-                              message = _("Please select attribute column "
-                                        "and RGB color column first"))
-            else:
-                try:
-                    self.ruleslines[num-1000]['value'] = self.SQLConvert(vals)
-                except ValueError:
-                    tc.SetValue('')
-                    self.ruleslines[num-1000]['value'] = ''
-                    return
+            mapRange = _('fp range')
+        self.cr_label.SetLabel(_('Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') %
+                                 { 'range' : mapRange,
+                                   'min' : self.properties['min'],
+                                   'max' : self.properties['max'] })                       
+            
+        self.btnPreview.Enable()
+        self.btnOK.Enable()
+        self.btnApply.Enable()
+    
+    def OnLoadTable(self, event):
+        """!Load current color table (using `r.colors.out`)"""
         
-    def OnRuleColor(self, event):
-        """!Rule color changed"""
-        num = event.GetId()
+        self.colorRulesPanel.Clear()
         
-        rgba_color = event.GetValue()
-        
-        rgb_string = str(rgba_color[0]) + ':' \
-            + str(rgba_color[1]) + ':' + \
-            str(rgba_color[2])
-        
-        self.ruleslines[num-2000]['color'] = rgb_string
-        
-    def SQLConvert(self, vals):
-        valslist = []
-        valslist = vals.split('to')
-        if len(valslist) == 1:
-            sqlrule = '%s=%s' % (self.properties['column'], valslist[0])
-        elif len(valslist) > 1:
-            sqlrule = '%s>=%s AND %s<=%s' % (self.properties['column'], valslist[0],
-                                             self.properties['column'], valslist[1])
-        else:
-            return None
-        
-        return sqlrule
-        
-    def OnLoadTable(self, event):
-        """!Load current color table (using `r.colors.out`)"""
-        self.ruleslines.clear()
-        self.cr_panel.DestroyChildren()
         if self.inmap:
             ctable = gcmd.RunCommand('r.colors.out',
                                      parent = self,
@@ -512,22 +646,64 @@
             return
         
         rulesNumber = len(ctable.splitlines())
-        self.AddRules(rulesNumber)
+        self.colorRulesPanel.AddRules(rulesNumber)
         
         count = 0
         for line in ctable.splitlines():
             value, color = map(lambda x: x.strip(), line.split(' '))
-            self.ruleslines[count]['value'] = value
-            self.ruleslines[count]['color'] = color
-            self.FindWindowById(count + 1000).SetValue(value)
+            self.colorRulesPanel.ruleslines[count]['value'] = value
+            self.colorRulesPanel.ruleslines[count]['color'] = color
+            self.colorRulesPanel.mainPanel.FindWindowById(count + 1000).SetValue(value)
             rgb = list()
             for c in color.split(':'):
                 rgb.append(int(c))
-            self.FindWindowById(count + 2000).SetColour(rgb)
+            self.colorRulesPanel.mainPanel.FindWindowById(count + 2000).SetColour(rgb)
             count += 1
         
         self.OnPreview(tmp = False)
+            
+          
+    def OnPreview(self, event = None, tmp = True):
+        """!Update preview (based on computational region)"""
+        if not self.inmap:
+            self.preview.EraseMap()
+            return
         
+        cmdlist = ['d.rast',
+                   'map=%s' % self.inmap]
+        ltype = 'raster'
+        
+        # find existing color table and copy to temp file
+        try:
+            name, mapset = self.inmap.split('@')
+        except ValueError:
+            name = self.inmap
+            mapset = grass.find_file(self.inmap, element = 'cell')['mapset']
+            if not mapset:
+                return
+        old_colrtable = None
+        if mapset == grass.gisenv()['MAPSET']:
+            old_colrtable = grass.find_file(name = name, element = 'colr')['file']
+        else:
+            old_colrtable = grass.find_file(name = name, element = 'colr2/' + mapset)['file']
+        
+        if old_colrtable:
+            colrtemp = utils.GetTempfile()
+            shutil.copyfile(old_colrtable, colrtemp)
+            
+        ColorTable.DoPreview(self, ltype, cmdlist)  
+        
+        # restore previous color table
+        if tmp:
+            if old_colrtable:
+                shutil.copyfile(colrtemp, old_colrtable)
+                os.remove(colrtemp)
+            else:
+                gcmd.RunCommand('r.colors',
+                                parent = self,
+                                flags = 'r',
+                                map = self.inmap)
+    
     def OnSaveTable(self, event):
         """!Save color table to file"""
         rulestxt = ''   
@@ -549,107 +725,494 @@
             fd.write(rulestxt)
             fd.close()            
         dlg.Destroy()
-          
-    def OnApply(self, event):
-        """!Apply selected color table
+        
+    def OnHelp(self, event):
+        """!Show GRASS manual page"""
+        cmd = 'r.colors'
+        ColorTable.RunHelp(self, cmd = cmd)
+    
+    def CreateColorTable(self, force = False):
+        """!Creates color table
 
-        @return True on success otherwise False
+        @return True on success
+        @return False on failure
         """
-        ret = self.CreateColorTable()
-        display = self.parent.GetLayerTree().GetMapDisplay()
-        if display and display.IsAutoRendered():
-            display.GetWindow().UpdateMap(render = True)
+        rulestxt = ''
         
-        return ret
+        for rule in self.colorRulesPanel.ruleslines.itervalues():
+            if not rule['value']: # skip empty rules
+                continue
+            
+            if rule['value'] not in ('nv', 'default') and \
+                    rule['value'][-1] != '%' and \
+                    not self._IsNumber(rule['value']):
+                gcmd.GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
+                            parent = self)
+                return False
+            
+            rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
+           
+        if not rulestxt:
+            gcmd.GMessage(parent = self, message = _("No color rules given."))
+            return False
+        
+        gtemp = utils.GetTempfile()
+        output = open(gtemp, "w")
+        try:
+            output.write(rulestxt)
+        finally:
+            output.close()
+        
+        if not force and not self.ovrwrtcheck.IsChecked():
+            flags = 'w'
+        else:
+            flags = ''
+        
+        ret = gcmd.RunCommand('r.colors',
+                              flags = flags,
+                              map = self.inmap,
+                              rules = gtemp)
+        if ret != 0:
+            gcmd.GMessage(_("Color table already exists. "
+                            "Check out 'replace existing color table' to "
+                            "overwrite it."),
+                          parent = self)
+            return False
+        
+        return True
+                      
+class VectorColorTable(ColorTable):
+    def __init__(self, parent, **kwargs):
+        """!Dialog for interactively entering color rules for vector maps"""
+        
+        self.type = 'vector'
+        # vector properties
+        self.properties = {
+            # list of database layers for vector (minimum of 1)
+            'layers' : [],
+            # list of database columns for vector
+            'columns' : [],
+            # vector layer for attribute table to use for setting color
+            'layer' : 1, 
+            # vector attribute table used for setting color         
+            'table' : '',
+            # vector attribute column for assigning colors
+            'source_rgb' : '', 
+            # vector attribute column to use for storing colors
+            'rgb' : '',       
+            # vector attribute column for assigning size
+            'source_size' : '', 
+            # vector attribute column to use for storing size
+            'size' : '',
+            }         
+        
+        ColorTable.__init__(self, parent = parent,
+                            title = _('Create new color table for vector map'), **kwargs)   
+        
+        # additional bindings for vector color management
+        self.Bind(wx.EVT_COMBOBOX, self.OnLayerSelection, self.cb_vlayer)
+        self.Bind(wx.EVT_COMBOBOX, self.OnColumnSelection, self.cb_color_att)
+        self.Bind(wx.EVT_COMBOBOX, self.OnRGBColSelection, self.cb_rgb_col)
+        self.Bind(wx.EVT_BUTTON, self.OnAddColumn, self.btn_add_RGB)    
+        
+        self._initLayer()
+        self.cr_label.SetLabel(_("Enter vector attribute values (e.g. 5) "
+                                            "or ranges (e.g. 5 to 10)"))
+        self.SetMinSize(self.GetSize()) 
+        self.CentreOnScreen()
+        self.Show()
 
-    def OnOK(self, event):
-        """!Apply selected color table and close the dialog"""
-        if self.OnApply(event):
-            self.Destroy()
+    def _createMapSelection(self, parent):
+        """!Create map selection part of dialog"""
+        # top controls
+        maplabel = _('Select vector map:')
+        inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
+                                label = " %s " % maplabel)
+        inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
+        
+        self.selectionInput = gselect.Select(parent, id = wx.ID_ANY,
+                                             size = globalvar.DIALOG_GSELECT_SIZE,
+                                             type = 'vector')
+            
+        # layout
+        inputSizer.Add(item = self.selectionInput,
+                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
+        replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        inputSizer.Add(item = replaceSizer, proportion = 1,
+                       flag = wx.ALL | wx.EXPAND, border = 0)
     
-    def OnCancel(self, event):
-        """!Do not apply any changes and close the dialog"""
-        self.Destroy()
+        return inputSizer
+    
+    def _createVectorAttrb(self, parent):
+        """!Create part of dialog with layer/column selection"""
+        inputBox = wx.StaticBox(parent = parent, id = wx.ID_ANY,
+                                label = " %s " % _("Select vector columns"))
+        cb_vl_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                             label = _('Layer:'))
+        cb_vc_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                         label = _('Attribute column:'))
+        cb_vrgb_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                           label = _('RGB color column:'))
+        self.rgb_range_label = wx.StaticText(parent, id = wx.ID_ANY)
+        self.cb_vlayer = gselect.LayerSelect(parent)
+        self.cb_color_att = gselect.ColumnSelect(parent)
+        self.cb_rgb_col = gselect.ColumnSelect(parent)
+        self.btn_add_RGB = wx.Button(parent, id = wx.ID_ANY,
+                                             label = _('Add column'))
+        self.btn_add_RGB.SetToolTipString(_("Add GRASSRGB column to current attribute table."))
         
-    def OnPreview(self, event = None, tmp = True):
-        """!Update preview (based on computational region)"""
+        # layout
+        inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
+        vSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        vSizer.Add(cb_vl_label, pos = (0, 0),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.cb_vlayer,  pos = (0, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(cb_vc_label, pos = (1, 0),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.cb_color_att, pos = (1, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.rgb_range_label, pos = (1, 2),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(cb_vrgb_label, pos = (2, 0),
+                  flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.cb_rgb_col, pos = (2, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.btn_add_RGB, pos = (2, 2),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        inputSizer.Add(item = vSizer,
+                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
+                
+        return inputSizer 
+       
+    def _doLayout(self):
+        """!Do main layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        #
+        # map selection
+        #
+        mapSelection = self._createMapSelection(parent = self)
+        sizer.Add(item = mapSelection, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # set vector attributes
+        #
+        vectorAttrb = self._createVectorAttrb(parent = self)
+        sizer.Add(item = vectorAttrb, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # body & preview
+        #
+        bodySizer = self._createBody(parent = self)
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # buttons
+        #
+        btnSizer = self._createButtons()
+        
+        sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
+                                       style = wx.LI_HORIZONTAL), proportion = 0,
+                                       flag = wx.EXPAND | wx.ALL, border = 5) 
+        
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        
+        self.SetSizer(sizer)
+        sizer.Layout()
+        sizer.Fit(self)
+        self.Layout()
+                 
+    def SetInfoString(self):
+        """!Show information about vector map column type/range"""
+        driver, db = self.dbInfo.GetDbSettings(int(self.properties['layer']))
+        nrows = grass.db_describe(table = self.properties['table'], driver = driver, database = db)['nrows']
+        self.properties['min'] = self.properties['max'] = ''
+        type = self.dbInfo.GetTableDesc(self.properties['table'])\
+                                                    [self.properties['source_rgb']]['type']
+        ctype = self.dbInfo.GetTableDesc(self.properties['table'])\
+                                                    [self.properties['source_rgb']]['ctype']
+        if ctype == int or ctype == float:  
+            if nrows < 500: # not too large
+                ret = gcmd.RunCommand('v.db.select',
+                                      quiet = True,
+                                      read  = True,
+                                      flags = 'c',
+                                      map = self.inmap,
+                                      layer = self.properties['layer'],
+                                      columns = self.properties['source_rgb']).strip('\n')
+                records = ret.split('\n')
+                try:
+                    self.properties['min'] = min(map(float, records))
+                    self.properties['max'] = max(map(float, records))
+                except ValueError:
+                    self.properties['min'] = self.properties['max'] = ''
+                    
+        if self.properties['min'] or self.properties['max']:
+            if ctype == int:
+                self.cr_label.SetLabel(_("Enter vector attribute values or ranges (type: %s, range: %d - %d)")
+                            % (type, self.properties['min'], self.properties['max']))
+            elif ctype == float:
+                self.cr_label.SetLabel(_("Enter vector attribute values or ranges (type: %s, range: %.1f - %.1f )")
+                            % (type, self.properties['min'], self.properties['max']))
+        else:
+            self.cr_label.SetLabel(_("Enter vector attribute values or ranges (type: %s)") % type)
+               
+    
+    def CheckMapset(self):
+        """!Check if current vector is in current mapset"""
+        if grass.find_file(name = self.inmap,
+                           element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
+            return True
+        else:
+            return False 
+         
+    def NoConnection(self, vectorName):
+        dlg = wx.MessageDialog(parent = self,
+                                message = _("Database connection for vector map <%s> "
+                                            "is not defined in DB file.  Do you want to create and "
+                                            "connect new attribute table?") % vectorName,
+                                caption = _("No database connection defined"),
+                                style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
+        if dlg.ShowModal() == wx.ID_YES:
+            dlg.Destroy()
+            menuform.GUI(parent = self).ParseCommand(['v.db.addtable', 'map=' + self.inmap], 
+                                                      completed = (self.CreateTable, self.inmap, ''))
+        else:
+            dlg.Destroy()
+            
+    def OnSelectionInput(self, event):
+        """!Vector map selected"""
+        if event:
+            self.inmap = event.GetString()
+
+        if self.inmap:
+            if not grass.find_file(name = self.inmap, element = 'vector')['file']:
+                self.inmap = None
+        
+        self.UpdateDialog()
+        
+    def UpdateDialog(self):
+        """!Update dialog after map selection"""    
         if not self.inmap:
+            self.colorRulesPanel.ruleslines.Clear()
+            self.btnPreview.Enable(False)
+            self.btnOK.Enable(False)
+            self.btnApply.Enable(False)
             self.preview.EraseMap()
             return
-        
-        # raster
-        if self.raster:
-            cmdlist = ['d.rast',
-                       'map=%s' % self.inmap]
-            ltype = 'raster'
+                               
+        # check for db connection
+        self.dbInfo = gselect.VectorDBInfo(self.inmap)
+        if not len(self.dbInfo.layers):
+            wx.CallAfter(self.NoConnection, self.inmap)
+            for combo in (self.cb_vlayer, self.cb_color_att, self.cb_rgb_col):
+                combo.SetValue("")
+                combo.Disable()
+                combo.Clear()
+            enable = False   
+        else:
+        # initialize layer selection combobox
+            for combo in (self.cb_vlayer, self.cb_color_att, self.cb_rgb_col):
+                combo.Enable()
+            self.cb_vlayer.InsertLayers(self.inmap)
+            # initialize attribute table for layer=1
+            self.properties['layer'] = self.cb_vlayer.GetString(0)
+            self.cb_vlayer.SetStringSelection(self.properties['layer'])
+            layer = int(self.properties['layer'])
+            self.properties['table'] = self.dbInfo.layers[layer]['table']
             
-            # find existing color table and copy to temp file
-            try:
-                name, mapset = self.inmap.split('@')
-            except ValueError:
-                name = self.inmap
-                mapset = grass.find_file(self.inmap, element = 'cell')['mapset']
-                if not mapset:
-                    return
-            old_colrtable = None
-            if mapset == grass.gisenv()['MAPSET']:
-                old_colrtable = grass.find_file(name = name, element = 'colr')['file']
+            # initialize column selection comboboxes 
+            self.OnLayerSelection(event = None)
+            
+            if self.CheckMapset():
+                enable = True
+                self.btn_add_RGB.Enable(True)
             else:
-                old_colrtable = grass.find_file(name = name, element = 'colr2/' + mapset)['file']
+                enable = False
+                wx.CallAfter(gcmd.GMessage, parent = self, 
+                             message = _("Selected map <%s> is not in current mapset <%s>. "
+                                        "Attribute table cannot be edited. "
+                                        "Color rules will not be saved.") % 
+                                        (self.inmap, grass.gisenv()['MAPSET']))
+                              
+                self.btn_add_RGB.Enable(False)
             
-            if old_colrtable:
-                colrtemp = utils.GetTempfile()
-                shutil.copyfile(old_colrtable, colrtemp)
-        # vector
+        self.btnPreview.Enable(enable)
+        self.btnOK.Enable(enable)
+        self.btnApply.Enable(enable)   
+        
+    def OnLayerSelection(self, event):
+        # reset choices in column selection comboboxes if layer changes
+        vlayer = int(self.cb_vlayer.GetStringSelection())
+        self.cb_color_att.InsertColumns(vector = self.inmap, layer = vlayer, dbInfo = self.dbInfo)
+        self.cb_color_att.SetSelection(0)
+        self.properties['source_rgb'] = self.cb_color_att.GetString(0)
+        self.cb_rgb_col.InsertColumns(vector = self.inmap, layer = vlayer, type = ["character"], dbInfo = self.dbInfo)
+#        self.cb_rgb_col.Delete(self.cb_rgb_col.FindString(self.properties['source_rgb']))
+        found = self.cb_rgb_col.FindString('GRASSRGB')
+        if found != wx.NOT_FOUND:
+            self.cb_rgb_col.SetSelection(found)
+            self.properties['rgb'] = self.cb_rgb_col.GetString(found)
         else:
-            cmdlist = ['d.vect',
-                        '-a',
-                       'map=%s' % self.inmap,
-                       'rgb_column=%s' % self.properties["rgb"],
-                       'type=point,line,boundary,area']
-            ltype = 'vector'
+            self.properties['rgb'] = ''
+##        self.SetInfoString()
         
-        if not self.layer:
-            self.layer = self.Map.AddLayer(type = ltype, name = 'preview', command = cmdlist,
-                                           l_active = True, l_hidden = False, l_opacity = 1.0,
-                                           l_render = False) 
+        self.LoadTable(attColumn = self.properties['source_rgb'],
+                       rgbColumn = self.properties['rgb'], rulesPanel = self.colorRulesPanel)
+        self.Update()
+        
+    def OnColumnSelection(self, event):
+        self.properties['source_rgb'] = event.GetString()
+        
+        self.LoadTable(attColumn = self.properties['source_rgb'],
+                       rgbColumn = self.properties['rgb'], rulesPanel = self.colorRulesPanel)
+    
+    def OnAddColumn(self, event):
+        """!Add GRASSRGB column if it doesn't exist"""
+        if 'GRASSRGB' not in self.cb_rgb_col.GetItems():
+            ret = gcmd.RunCommand('v.db.addcolumn',
+                                   map = self.inmap,
+                                  layer = self.properties['layer'],
+                                  columns = 'GRASSRGB varchar(20)')
+            self.cb_rgb_col.InsertColumns(self.inmap, self.properties['layer'], type = ["character"])
+            self.cb_rgb_col.SetStringSelection('GRASSRGB')
+            self.properties['rgb'] = self.cb_rgb_col.GetStringSelection()
+            
+            self.LoadTable(attColumn = self.properties['source_rgb'], rgbColumn = self.properties['rgb'],
+                       rulesPanel = self.colorRulesPanel, type = 'color')
         else:
-            self.layer.SetCmd(cmdlist)
+            gcmd.GMessage(parent = self,
+                          message = _("GRASSRGB column already exists."))
+                        
+    def CreateTable(self, dcmd, layer, params, propwin):
+        """!Create attribute table"""
+        if dcmd:
+            cmd = utils.CmdToTuple(dcmd)
+            ret = gcmd.RunCommand(cmd[0], **cmd[1])
+            if ret == 0:
+                self.OnSelectionInput(None)
+                return True
+            
+        for combo in (self.cb_vlayer, self.cb_color_att, self.cb_rgb_col):
+            combo.SetValue("")
+            combo.Disable()
+        return False    
+    
+    def LoadTable(self, attColumn, rgbColumn, rulesPanel, type = 'color'):
+        """!Load current column (GRASSRGB, size column)"""
         
-        # apply new color table and display preview
-        self.CreateColorTable(force = True)
-        self.preview.UpdatePreview()
+        rulesPanel.Clear()
+        if not attColumn or not rgbColumn:
+            self.preview.EraseMap()
+            return
         
-        # restore previous color table
-        if self.raster and tmp:
-            if old_colrtable:
-                shutil.copyfile(colrtemp, old_colrtable)
-                os.remove(colrtemp)
+        busy = wx.BusyInfo(message = _("Please wait, loading data from attribute table..."),
+                           parent = self)
+        wx.Yield()
+        if self.inmap:
+            outFile = tempfile.NamedTemporaryFile(mode='w+b')
+            sep = '|'
+            ret = gcmd.RunCommand('v.db.select',
+                                      quiet = True,
+                                      flags = 'c',
+                                      map = self.inmap,
+                                      layer = self.properties['layer'],
+                                      columns = attColumn + ',' + rgbColumn,
+                                      fs = sep,
+                                      stdout = outFile)
+            
+        else:
+            self.OnPreview(event)
+            busy.Destroy()
+            return
+        if type == 'color':
+            ctype = self.dbInfo.GetTableDesc(self.properties['table'])\
+                                            [self.properties['source_rgb']]['ctype']
+        elif type == 'size':
+            ctype = self.dbInfo.GetTableDesc(self.properties['table'])\
+                                            [self.properties['source_size']]['ctype']
+        outFile.seek(0)
+        i = 0
+        minim = maxim = 0.0
+        while True:
+            # os.linesep doesn't work here (MSYS)
+            record = outFile.readline().replace('\n', '')
+            
+            if not record:
+                break
+            rulesPanel.ruleslines[i] = {}
+            
+            value = record.split(sep)[0]
+            if ctype not in (int, float):
+                value = "'" + value + "'"
             else:
-                gcmd.RunCommand('r.colors',
-                                parent = self,
-                                flags = 'r',
-                                map = self.inmap)
+                if float(value) < minim:
+                    minim = float(value)
+                if float(value) > maxim:
+                    maxim = float(value)
+            rulesPanel.ruleslines[i]['value'] = value
+            rulesPanel.ruleslines[i][type] = record.split(sep)[1]
+            i += 1
         
+        rulesPanel.AddRules(i, start = True)
+        ret = rulesPanel.LoadRules()
+        self.SetRangeLabel(type, ctype, minim, maxim)
+        
+        if ret:
+            self.OnPreview()    
+        else:
+            rulesPanel.Clear()
+    
+        busy.Destroy()
+        
+    def SetRangeLabel(self, type, ctype, minim, maxim):
+        """!Set labels with info about attribute column range"""
+        if type == 'color':
+            if minim or maxim:
+                if ctype == int:
+                    self.rgb_range_label.SetLabel(_("range: %.1f to %.1f") % (minim, maxim))
+                elif ctype == float:
+                    self.rgb_range_label.SetLabel(_("range: %d to %d") % (minim, maxim))
+            else:
+                self.rgb_range_label.SetLabel('')
+        elif type == 'size':
+            if minim or maxim:
+                if ctype == int:
+                    self.size_range_label.SetLabel(_("range: %.1f to %.1f") % (minim, maxim))
+                elif ctype == float:
+                    self.size_range_label.SetLabel(_("range: %d to %d") % (minim, maxim))
+            else:
+                self.size_range_label.SetLabel('')
+                
+                
+    def OnRGBColSelection(self, event):
+        self.properties['rgb'] = event.GetString()
+        
+        self.LoadTable(attColumn = self.properties['source_rgb'],
+                       rgbColumn = self.properties['rgb'], rulesPanel = self.colorRulesPanel)
+            
+    def OnPreview(self, event = None):
+        """!Update preview (based on computational region)"""
+        if not self.inmap or not self.properties["rgb"]:
+            self.preview.EraseMap()
+            return
+        
+        cmdlist = ['d.vect',
+                    '-a',
+                   'map=%s' % self.inmap,
+                   'rgb_column=%s' % self.properties["rgb"],
+                   'type=point,line,boundary,area']
+        ltype = 'vector'
+        
+        ColorTable.DoPreview(self, ltype, cmdlist)
+        
     def OnHelp(self, event):
         """!Show GRASS manual page"""
-        if self.raster:
-            cmd = 'r.colors'
-        else:
-            cmd = 'vcolors'
-        gcmd.RunCommand('g.manual',
-                        quiet = True,
-                        parent = self,
-                        entry = cmd)
+        cmd = 'v.colors'
+        ColorTable.RunHelp(self, cmd = cmd)
         
-    def _IsNumber(self, s):
-        """!Check if 's' is a number"""
-        try:
-            float(s)
-            return True
-        except ValueError:
-            return False
-        
     def CreateColorTable(self, force = False):
         """!Creates color table
 
@@ -658,24 +1221,14 @@
         """
         rulestxt = ''
         
-        for rule in self.ruleslines.itervalues():
+        for rule in self.colorRulesPanel.ruleslines.itervalues():
             if not rule['value']: # skip empty rules
                 continue
             
-            if self.raster:
-                if rule['value'] not in ('nv', 'default') and \
-                        rule['value'][-1] != '%' and \
-                        not self._IsNumber(rule['value']):
-                    gcmd.GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
-                                parent = self)
-                    return False
-                
-                rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
-            else:
-                rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
-                                                                    self.properties['rgb'],
-                                                                    rule['color'],
-                                                                    rule['value'])
+            rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
+                                                                self.properties['rgb'],
+                                                                rule['color'],
+                                                                rule['value'])
         if not rulestxt:
             return False
         
@@ -686,31 +1239,408 @@
         finally:
             output.close()
         
-        if self.raster:
-            if not force and \
-                    not self.ovrwrtcheck.IsChecked():
-                flags = 'w'
+        gcmd.RunCommand('db.execute',
+                        parent = self,
+                        input = gtemp)
+        return True
+    
+    
+##    def ColorFromString(self, rgb):
+##        """!Convert color string '255:255:255' to tuple"""
+##        try:
+##            r, g, b = rgb.split(':')
+##            return (r, g, b)
+##        
+##        except ValueError:
+##            return False
+        
+class ThematicVectorTable(VectorColorTable):
+    def __init__(self, parent, vectorType, **kwargs):
+        """!Dialog for interactively entering color/size rules
+            for vector maps for thematic mapping in nviz"""
+        self.vectorType = vectorType
+        VectorColorTable.__init__(self, parent = parent, **kwargs)
+        
+        # additional bingings
+        self.Bind(wx.EVT_COMBOBOX, self.OnSizeSourceSelection, self.cb_size_att)
+        self.Bind(wx.EVT_COMBOBOX, self.OnSizeSelection, self.cb_size_col)
+        self.Bind(wx.EVT_BUTTON, self.OnAddSizeColumn, self.btn_add_size)
+        self.Bind(wx.EVT_CHECKBOX, self.OnColorChecked,  self.rgb_check)
+        self.Bind(wx.EVT_CHECKBOX, self.OnSizeChecked,  self.size_check)
+        
+        self.SetTitle(_("Thematic mapping for vector map in 3D view"))
+        
+        
+    def UpdateDialog(self):
+        """!Update dialog according to selected map"""
+        VectorColorTable.UpdateDialog(self)
+                               
+        if not len(self.dbInfo.layers):
+            for combo in (self.cb_size_att, self.cb_size_col):
+                combo.SetValue("")
+                combo.Disable()
+                combo.Clear()
+            enable = False   
+        else:
+        # initialize layer selection combobox
+            for combo in (self.cb_vlayer, self.cb_size_att, self.cb_size_col):
+                combo.Enable()
+            
+            if self.CheckMapset():
+                self.btn_add_size.Enable(True)
             else:
-                flags = ''
+                self.btn_add_RGB.Enable(False)
+                
+    def OnLayerSelection(self, event):
+        VectorColorTable.OnLayerSelection(self, event)
+        # reset choices in column selection comboboxes if layer changes
+        vlayer = int(self.cb_vlayer.GetStringSelection())
+        self.cb_size_att.InsertColumns(vector = self.inmap, layer = vlayer, dbInfo = self.dbInfo)
+        self.cb_size_att.SetSelection(0)
+        self.properties['source_size'] = self.cb_size_att.GetString(0)
+        self.cb_size_col.InsertColumns(vector = self.inmap, layer = vlayer,
+                                       type = ["integer", "double precision"], dbInfo = self.dbInfo)
+        for item in self.cb_size_col.GetItems():
+            if item.lower().find('size') >= 0:
+                self.cb_size_col.SetStringSelection(item)
+                self.properties['size'] = item
+            else:
+                self.properties['size'] = ''
+##        self.SetInfoString()
+        self.LoadTable(attColumn = self.properties['source_size'], rgbColumn = self.properties['size'],
+                       rulesPanel = self.sizeRulesPanel, type = 'size')
+        self.Update()  
+        
+    def OnSizeSelection(self, event):
+        self.properties['size'] = event.GetString()
+        
+        self.LoadTable(attColumn = self.properties['source_size'], rgbColumn = self.properties['size'],
+                       rulesPanel = self.sizeRulesPanel, type = 'size')
+    
+    def OnSizeSourceSelection(self, event):
+        self.properties['source_size'] = event.GetString()
+        
+##        self.SetInfoString()
+        
+        self.LoadTable(attColumn = self.properties['source_size'], rgbColumn = self.properties['size'],
+                       rulesPanel = self.sizeRulesPanel, type = 'size') 
+                    
+    def _initLayer(self):
+        """!Set initial layer when opening dialog"""
+        self.inmap = self.parent.GetLayerData(nvizType = 'vector', nameOnly = True)
+        self.selectionInput.SetValue(self.inmap)
+        self.selectionInput.Disable()
+        
+    def _doLayout(self):
+        """!Do main layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        #
+        # map selection
+        #
+        mapSelection = self._createMapSelection(parent = self)
+        sizer.Add(item = mapSelection, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # set vector attributes
+        #
+        vectorAttrb = self._createVectorAttrb(parent = self)
+        sizer.Add(item = vectorAttrb, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # body & preview
+        #
+        bodySizer = self._createBody(parent = self)
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # buttons
+        #
+        btnSizer = self._createButtons()
+        
+        sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
+                                       style = wx.LI_HORIZONTAL), proportion = 0,
+                                       flag = wx.EXPAND | wx.ALL, border = 5) 
+        
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        
+        self.SetSizer(sizer)
+        sizer.Layout()
+        sizer.Fit(self)
+        self.Layout()
+    
+    def _createBody(self, parent):
+        """!Create dialog body consisting of rules and preview"""
+        bodySizer =  wx.GridBagSizer(hgap = 5, vgap = 5)
+        row = 0
+        
+        # label with instructions - don't want it now
+        self.cr_label = wx.StaticText(parent, id = wx.ID_ANY)
+        self.cr_label.Hide()
+
+        # color table
+        self.colorRulesPanel = RulesPanel(parent = parent, mapType = self.type,
+                                          columnType = 'color', properties = self.properties,
+                                          panelWidth = 200)
+        # size table        
+        self.sizeRulesPanel = RulesPanel(parent = parent, mapType = self.type,
+                                         columnType = 'size', properties = self.properties,
+                                         panelWidth = 200)
+                                        
+        bodySizer.Add(item = self.colorRulesPanel.label, pos = (row, 0), span = (1, 2))
+        bodySizer.Add(item = self.sizeRulesPanel.label, pos = (row, 2), span = (1, 2))
+        row += 1
+        
+        bodySizer.Add(item = self.colorRulesPanel.mainPanel, pos = (row, 0), span = (1, 2))
+        # add two rules as default
+        self.colorRulesPanel.AddRules(2)
+        bodySizer.Add(item = self.sizeRulesPanel.mainPanel, pos = (row, 2), span = (1, 2))
+        # add two rules as default
+        self.sizeRulesPanel.AddRules(2)
+        
+        # preview window
+        self._createPreview(parent = parent)
+        bodySizer.Add(item = self.preview, pos = (row, 4),
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10)
+        bodySizer.AddGrowableRow(row)
+        bodySizer.AddGrowableCol(4)
+        row += 1
+        
+        # add rules button and spin to sizer
+        bodySizer.Add(item = self.colorRulesPanel.numRules, pos = (row, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        bodySizer.Add(item = self.colorRulesPanel.btnAdd, pos = (row, 1))
+        bodySizer.Add(item = self.sizeRulesPanel.numRules, pos = (row, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        bodySizer.Add(item = self.sizeRulesPanel.btnAdd, pos = (row, 3), 
+                        flag = wx.ALIGN_LEFT)
+
+        # preview button
+        self.btnPreview = wx.Button(parent, id = wx.ID_ANY,
+                                    label = _("Preview"))
+        bodySizer.Add(item = self.btnPreview, pos = (row, 4),
+                      flag = wx.ALIGN_RIGHT)
+        self.btnPreview.Enable(False)
+        self.btnPreview.SetToolTipString(_("Show preview of map "
+                                           "(current Map Display extent is used)."))
+                
+        return bodySizer
+    
+    def _createVectorAttrb(self, parent):
+        """!Create part of dialog with layer/column selection"""
+        inputBox = wx.StaticBox(parent = parent, id = wx.ID_ANY,
+                                label = " %s " % _("Select vector columns"))
+        layer_label = wx.StaticText(parent, id = wx.ID_ANY, label = _('Layer:'))
+        self.rgb_check = wx.CheckBox(parent, id = wx.ID_ANY, label = _('Use color for thematic mapping:'))
+        if self.vectorType == 'points':
+            label = _('Use symbol size for thematic mapping:')
+        else:
+            label = _('Use line width for thematic mapping:')
+        self.size_check = wx.CheckBox(parent, id = wx.ID_ANY, label = label)
+        
+        self.rgb_check.SetValue(True)
+        self.size_check.SetValue(True)
+                                            
+        color_att_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                         label = _('Attribute column:'))
+        size_att_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                         label = _('Attribute column:'))
+        rgb_col_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                           label = _('RGB color column:'))
+        if self.vectorType == 'points':
+            label = _('Symbol size column:')
+        else:
+            label = _('Line with column:')
+        size_col_label = wx.StaticText(parent, id = wx.ID_ANY, label = label)
+        
+        self.rgb_range_label = wx.StaticText(parent, id = wx.ID_ANY)                                        
+        self.size_range_label = wx.StaticText(parent, id = wx.ID_ANY)
+        cb_size = (150, -1)
+        self.cb_vlayer = gselect.LayerSelect(parent, size = cb_size)
+        self.cb_color_att = gselect.ColumnSelect(parent, size = cb_size)
+        self.cb_size_att = gselect.ColumnSelect(parent, size = cb_size)
+        self.cb_rgb_col = gselect.ColumnSelect(parent, size = cb_size)
+        self.cb_size_col = gselect.ColumnSelect(parent, size = cb_size)
+        self.btn_add_RGB = wx.Button(parent, id = wx.ID_ANY,
+                                             label = _('Add column'))
+        self.btn_add_size = wx.Button(parent, id = wx.ID_ANY,
+                                             label = _('Add column'))
+        self.btn_add_RGB.SetToolTipString(_("Add GRASSRGB column to current attribute table."))
+        
+        if self.vectorType == 'points':
+            label = _("Add size column to current attribute table.")
+        else:
+            label = _("Add width column to current attribute table.")
+        self.btn_add_size.SetToolTipString(label)
+        
+        # layout
+        inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
+        vSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        vSizer.AddGrowableCol(2)
+        
+        vSizer.Add(layer_label, pos = (0, 0),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.cb_vlayer,  pos = (0, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+                
+        vSizer.Add(self.rgb_check,  pos = (1, 0), span = (1, 3),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.size_check,  pos = (1, 3), span = (1, 3),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+                
+        vSizer.Add(color_att_label, pos = (2, 0),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(size_att_label, pos = (2, 3),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+                
+        vSizer.Add(rgb_col_label, pos = (4, 0),
+                  flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(size_col_label, pos = (4, 3),
+                  flag = wx.ALIGN_CENTER_VERTICAL)
+                
+        vSizer.Add(self.cb_color_att, pos = (2, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.cb_size_att, pos = (2, 4),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+                
+        vSizer.Add(self.rgb_range_label, pos = (3, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.size_range_label, pos = (3, 4),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+                
+        vSizer.Add(self.cb_rgb_col, pos = (4, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.cb_size_col, pos = (4, 4),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+                
+        vSizer.Add(self.btn_add_RGB, pos = (4, 2),
+                  flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.btn_add_size, pos = (4, 5),
+                  flag = wx.ALIGN_CENTER_VERTICAL)
+        inputSizer.Add(item = vSizer,
+                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
+                
+        return inputSizer 
+    
+    def OnAddSizeColumn(self, event):
+        """!Add size column if it doesn't exist"""
+        if self.vectorType == 'points':
+            name = 'GRASSSIZE'
+        else:
+            name = 'GRASSWIDTH'
+        
+        ret = gcmd.RunCommand('v.db.addcolumn',
+                               map = self.inmap,
+                              layer = self.properties['layer'],
+                              columns = '%s integer' % name)
+        self.cb_size_col.InsertColumns(self.inmap, self.properties['layer'], type = ["integer"])
+        self.cb_size_col.SetStringSelection(name)
+        self.properties['size'] = name
+        
+        self.LoadTable(attColumn = self.properties['source_size'], rgbColumn = self.properties['size'],
+                       rulesPanel = self.sizeRulesPanel, type = 'size')
+
+    def OnColorChecked(self, event):
+        """!Use color for thematic mapping"""
+        if self.rgb_check.IsChecked():
+            self.colorRulesPanel.Enable(True)
+        else:
+            self.colorRulesPanel.Enable(False)
+        
+    def OnSizeChecked(self, event):
+        """!Use size for thematic mapping"""
+        if self.size_check.IsChecked():
+            self.sizeRulesPanel.Enable(True)
+        else:
+            self.sizeRulesPanel.Enable(False)
+        
+    def OnPreview(self, event = None):
+        """!Update preview (based on computational region)"""
+        if not self.inmap:
+            self.preview.EraseMap()
+            return
+        
+        cmdlist = ['d.vect',
+                    '-a',
+                   'map=%s' % self.inmap,
+                   'type=point,line,boundary,area']
+                
+        if self.size_check.IsChecked() and self.properties["size"]:
+            if self.vectorType == 'points':
+                cmdlist.append('size_column=%s' % self.properties["size"])
+            else:
+                cmdlist.append('width_column=%s' % self.properties["size"])
             
-            ret = gcmd.RunCommand('r.colors',
-                                  flags = flags,
-                                  map = self.inmap,
-                                  rules = gtemp)
-            if ret != 0:
-                gcmd.GMessage(_("Color table already exists. "
-                                "Check out 'replace existing color table' to "
-                                "overwrite it."),
-                              parent = self)
-                return False
+        if self.rgb_check.IsChecked() and self.properties["rgb"]:
+            cmdlist.append('rgb_column=%s' % self.properties["rgb"])
+        ltype = 'vector'
+        ColorTable.DoPreview(self, ltype, cmdlist)
+    
+    def OnApply(self, event):
+        """!Apply selected color table
+
+        @return True on success otherwise False
+        """
+        ret = VectorColorTable.CreateColorTable(self)
+        if not ret:
+            gcmd.GMessage(parent = self, message = _("No color rules given."))
+        ret = self.CreateSizeTable()
+        if not ret:
+            gcmd.GMessage(parent = self, message = _("No size rules given."))
         
+        data = self.parent.GetLayerData(nvizType = 'vector')
+        data['vector']['points']['thematic']['layer'] = int(self.properties['layer'])
+        
+        if self.size_check.IsChecked() and self.properties['size']:
+            data['vector'][self.vectorType]['thematic']['sizecolumn'] = self.properties['size']
         else:
-            gcmd.RunCommand('db.execute',
-                            parent = self,
-                            input = gtemp)
+            data['vector'][self.vectorType]['thematic']['sizecolumn'] = None
+            
+        if self.rgb_check.IsChecked() and self.properties['rgb']:
+            data['vector'][self.vectorType]['thematic']['rgbcolumn'] = self.properties['rgb']
+        else:
+            data['vector'][self.vectorType]['thematic']['rgbcolumn'] = None
         
+        data['vector'][self.vectorType]['thematic']['update'] = None
+        
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.parent.mapWindow, event)
+        self.parent.mapWindow.Refresh(False)
+        
+        return ret
+    
+    def CreateSizeTable(self, force = False):
+        """!Creates size table
+
+        @return True on success
+        @return False on failure
+        """
+        VectorColorTable.CreateColorTable(self)
+        rulestxt = ''
+        
+        for rule in self.sizeRulesPanel.ruleslines.itervalues():
+            if not rule['value']: # skip empty rules
+                continue
+            
+            rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
+                                                                self.properties['size'],
+                                                                rule['size'],
+                                                                rule['value'])
+        if not rulestxt:
+            return False
+        
+        gtemp = utils.GetTempfile()
+        output = open(gtemp, "w")
+        try:
+            output.write(rulestxt)
+        finally:
+            output.close()
+        
+        gcmd.RunCommand('db.execute',
+                        parent = self,
+                        input = gtemp)
         return True
-    
+        
 class BufferedWindow(wx.Window):
     """!A Buffered window class"""
     def __init__(self, parent, id,
@@ -843,10 +1773,11 @@
         oldencoding = ""
         
         if self.render:
-            # make sure that extents are updated
-            self.Map.region = self.Map.GetRegion()
-            self.Map.SetRegion()
-            
+            # extent is taken from current map display
+            try:
+                self.Map.region = copy.deepcopy(self.parent.parent.curr_page.maptree.Map.region)
+            except AttributeError:
+                self.Map.region = self.Map.GetRegion()
             # render new map images
             self.mapfile = self.Map.Render(force = self.render)
             self.img = self.GetImage()

Modified: grass/branches/develbranch_6/gui/wxpython/wxgui.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/wxgui.py	2011-08-14 08:45:36 UTC (rev 47614)
+++ grass/branches/develbranch_6/gui/wxpython/wxgui.py	2011-08-14 09:01:34 UTC (rev 47615)
@@ -1010,9 +1010,9 @@
         cmd = self.GetMenuCmd(event)
         
         if cmd[0] == 'r.colors':
-            ctable = colorrules.ColorTable(self, raster = True)
+            ctable = colorrules.RasterColorTable(self)
         else:
-            ctable = colorrules.ColorTable(self, raster = False)
+            ctable = colorrules.VectorColorTable(self)
         ctable.CentreOnScreen()
         ctable.Show()
         



More information about the grass-commit mailing list