/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/classifier.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/classifier.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 606 by jonathan, Fri Apr 4 12:16:39 2003 UTC revision 2386 by jan, Thu Oct 7 14:43:45 2004 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2003 by Intevation GmbH  # Copyright (c) 2003-2004 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jan-Oliver Wagner <[email protected]> (2003-2004)
4    # Martin Schulze <[email protected]> (2004)
5    # Frank Koormann <[email protected]> (2003)
6    # Bernhard Herzog <[email protected]> (2003)
7    # Jonathan Coles <[email protected]> (2003)
8  #  #
9  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
10  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 8  Line 12 
12  """Dialog for classifying how layers are displayed"""  """Dialog for classifying how layers are displayed"""
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    # $Source$
16    # $Id$
17    
18  import copy  import copy
19    
# Line 18  from wxPython.wx import * Line 24  from wxPython.wx import *
24  from wxPython.grid import *  from wxPython.grid import *
25    
26  from Thuban import _  from Thuban import _
27  from Thuban.common import *  from Thuban.UI.common import Color2wxColour, wxColour2Color
 from Thuban.UI.common import *  
28    
29  from Thuban.Model.classification import *  from Thuban.Model.messages import MAP_LAYERS_REMOVED, LAYER_SHAPESTORE_REPLACED
30    from Thuban.Model.range import Range
31    from Thuban.Model.classification import \
32        Classification, ClassGroupDefault, \
33        ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
34        ClassGroupProperties
35    
36  from Thuban.Model.color import Color  from Thuban.Model.color import Transparent
37    
38  from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.Model.layer import Layer, RasterLayer
39    from Thuban.Model.data import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
40    
41  from Thuban.UI.classgen import ClassGenDialog, ClassGenerator  from Thuban.UI.classgen import ClassGenDialog
42    from Thuban.UI.colordialog import ColorDialog
43    
44  from dialogs import NonModalDialog  from dialogs import NonModalNonParentDialog
45    from messages import MAP_REPLACED
46    
 # widget id's  
 ID_PROPERTY_SELECT = 4010  
47  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
48    
 ID_CLASSIFY_OK = 4001  
 ID_CLASSIFY_CANCEL = 4002  
 ID_CLASSIFY_ADD = 4003  
 ID_CLASSIFY_GENCLASS = 4004  
 ID_CLASSIFY_REMOVE = 4005  
 ID_CLASSIFY_MOVEUP = 4006  
 ID_CLASSIFY_MOVEDOWN = 4007  
 ID_CLASSIFY_APPLY = 4008  
 ID_CLASSIFY_EDITPROPS = 4009  
49    
50  # table columns  # table columns
51  COL_SYMBOL = 0  COL_VISIBLE = 0
52  COL_VALUE  = 1  COL_SYMBOL  = 1
53  COL_LABEL  = 2  COL_VALUE   = 2
54    COL_LABEL   = 3
55    NUM_COLS    = 4
56    
57  # indices into the client data lists in Classifier.fields  # indices into the client data lists in Classifier.fields
58  FIELD_CLASS = 0  FIELD_CLASS = 0
# Line 72  class ClassGrid(wxGrid): Line 76  class ClassGrid(wxGrid):
76                   use for display.                   use for display.
77          """          """
78    
79          #wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))          wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)
         wxGrid.__init__(self, parent, ID_CLASS_TABLE)  
         #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), True)  
80    
81          self.classifier = classifier          self.classifier = classifier
82    
# Line 86  class ClassGrid(wxGrid): Line 88  class ClassGrid(wxGrid):
88          EVT_GRID_COL_SIZE(self, self._OnCellResize)          EVT_GRID_COL_SIZE(self, self._OnCellResize)
89          EVT_GRID_ROW_SIZE(self, self._OnCellResize)          EVT_GRID_ROW_SIZE(self, self._OnCellResize)
90    
91          #print "123123123: ", ('Show' in dir(self))      #def GetCellAttr(self, row, col):
92            #print "GetCellAttr ", row, col
93            #wxGrid.GetCellAttr(self, row, col)
94    
95      #def Show(self):      def CreateTable(self, clazz, fieldType, shapeType, group = None):
         #print "SHOW!"  
   
     #def Refresh(self):  
         #self.Show()  
     #def Update(self):  
         #self.Show()  
   
     def CreateTable(self, clazz, shapeType, group = None):  
96    
97          assert isinstance(clazz, Classification)          assert isinstance(clazz, Classification)
98    
99          table = self.GetTable()          table = self.GetTable()
100          if table is None:          if table is None:
101              w = self.GetDefaultColSize() * 3 + self.GetDefaultRowLabelSize()              w = self.GetDefaultColSize() * NUM_COLS \
102              h = self.GetDefaultRowSize() * 4 + self.GetDefaultColLabelSize()                  + self.GetDefaultRowLabelSize()
103                h = self.GetDefaultRowSize() * 4 \
104                    + self.GetDefaultColLabelSize()
105    
106              self.SetDimensions(-1, -1, w, h)              self.SetDimensions(-1, -1, w, h)
107              self.SetSizeHints(w, h, -1, -1)              self.SetSizeHints(w, h, -1, -1)
108              table = ClassTable(self)              table = ClassTable(self)
# Line 113  class ClassGrid(wxGrid): Line 112  class ClassGrid(wxGrid):
112          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(wxGrid.wxGridSelectRows)
113          self.ClearSelection()          self.ClearSelection()
114    
115          #print "8------------------"          table.Reset(clazz, fieldType, shapeType, group)
         table.Reset(clazz, shapeType, group)  
         #print "9------------------"  
   
 #   def Show(self, show = True):  
 #       print "SHOW!"  
 #       wxGrid.Show(self, show)  
   
 #       sel = self.GetCurrentSelection()  
   
 #       print "( 1"  
 #       if len(sel) == 1:  
 #           print "( 2"  
 #           self.MakeCellVisible(sel[0], 0)  
116    
117      def GetCurrentSelection(self):      def GetCurrentSelection(self):
118          """Return the currently highlighted rows as an increasing list          """Return the currently highlighted rows as an increasing list
119             of row numbers."""             of row numbers."""
         #print "@@ ", self.currentSelection  
120          sel = copy.copy(self.currentSelection)          sel = copy.copy(self.currentSelection)
121          sel.sort()          sel.sort()
122          return sel          return sel
# Line 139  class ClassGrid(wxGrid): Line 124  class ClassGrid(wxGrid):
124      def GetSelectedRows(self):      def GetSelectedRows(self):
125          return self.GetCurrentSelection()          return self.GetCurrentSelection()
126    
127      def SetCellRenderer(self, row, col):      #def SetCellRenderer(self, row, col, renderer):
128          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          #raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
129    
130      #      #
131      # [Set|Get]Table is taken from http://wiki.wxpython.org      # [Set|Get]Table is taken from http://wiki.wxpython.org
# Line 215  class ClassGrid(wxGrid): Line 200  class ClassGrid(wxGrid):
200    
201          assert table is not None          assert table is not None
202    
   
         #print "-- ", group  
203          for i in range(table.GetNumberRows()):          for i in range(table.GetNumberRows()):
204              g = table.GetClassGroup(i)              g = table.GetClassGroup(i)
             #print "1", g  
205              if g is group:              if g is group:
                 #print "2"  
206                  self.SelectRow(i)                  self.SelectRow(i)
207                  if makeVisible:                  if makeVisible:
                     #print "3"  
208                      self.MakeCellVisible(i, 0)                      self.MakeCellVisible(i, 0)
209                  break                  break
210    
           
   
211  #  #
212  # XXX: This isn't working, and there is no way to deselect rows wxPython!  # XXX: This isn't working, and there is no way to deselect rows wxPython!
213  #  #
# Line 242  class ClassGrid(wxGrid): Line 220  class ClassGrid(wxGrid):
220  #                                  sel = False))  #                                  sel = False))
221    
222      def _OnCellDClick(self, event):      def _OnCellDClick(self, event):
223          """Handle a double on a cell."""          """Handle a double click on a cell."""
224    
225          r = event.GetRow()          r = event.GetRow()
226          c = event.GetCol()          c = event.GetCol()
         if c == COL_SYMBOL:  
             self.classifier.EditGroupProperties(r)  
227    
228            if c == COL_SYMBOL:
229                self.classifier.EditSymbol(r)
230            else:
231                event.Skip()
232    
233      #      #
234      # _OnSelectedRange() and _OnSelectedCell() were borrowed      # _OnSelectedRange() and _OnSelectedCell() were borrowed
# Line 260  class ClassGrid(wxGrid): Line 240  class ClassGrid(wxGrid):
240          if event.Selecting():          if event.Selecting():
241              for index in range( event.GetTopRow(), event.GetBottomRow()+1):              for index in range( event.GetTopRow(), event.GetBottomRow()+1):
242                  if index not in self.currentSelection:                  if index not in self.currentSelection:
                     #print "    ", index  
243                      self.currentSelection.append( index )                      self.currentSelection.append( index )
244          else:          else:
245              for index in range( event.GetTopRow(), event.GetBottomRow()+1):              for index in range( event.GetTopRow(), event.GetBottomRow()+1):
246                  while index in self.currentSelection:                  while index in self.currentSelection:
                     #print "    ", index  
247                      self.currentSelection.remove( index )                      self.currentSelection.remove( index )
248          #self.ConfigureForSelection()          #self.ConfigureForSelection()
249    
         #print self.GetCurrentSelection()  
250          event.Skip()          event.Skip()
251    
252      def _OnSelectedCell( self, event ):      def _OnSelectedCell( self, event ):
253          """Internal update to the selection tracking list"""          """Internal update to the selection tracking list"""
         #print "selecting cell: ", event.GetRow()  
254          self.currentSelection = [ event.GetRow() ]          self.currentSelection = [ event.GetRow() ]
255          #self.ConfigureForSelection()          #self.ConfigureForSelection()
256          event.Skip()          event.Skip()
257    
258      def _OnCellResize(self, event):      def _OnCellResize(self, event):
259          self.FitInside()          self.FitInside()
260            event.Skip()
261    
262  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
263      """Represents the underlying data structure for the grid."""      """Represents the underlying data structure for the grid."""
264    
265      NUM_COLS = 3      __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]
   
     __col_labels = [_("Symbol"), _("Value"), _("Label")]  
266    
267    
268      def __init__(self, view = None):      def __init__(self, view = None):
     #def __init__(self, clazz, shapeType, view = None):  
269          """Constructor.          """Constructor.
270    
271          shapeType -- the type of shape that the layer uses          shapeType -- the type of shape that the layer uses
# Line 301  class ClassTable(wxPyGridTableBase): Line 275  class ClassTable(wxPyGridTableBase):
275    
276          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
277    
278          self.SetView(view)          assert len(ClassTable.__col_labels) == NUM_COLS
         self.tdata = []  
279    
280          #self.Reset(clazz, shapeType)          self.clazz = None
281            self.__colAttr = {}
282    
283      def Reset(self, clazz, shapeType, group = None):          self.SetView(view)
284    
285        def Reset(self, clazz, fieldType, shapeType, group = None):
286          """Reset the table with the given data.          """Reset the table with the given data.
287    
288          This is necessary because wxWindows does not allow a grid's          This is necessary because wxWindows does not allow a grid's
# Line 324  class ClassTable(wxPyGridTableBase): Line 300  class ClassTable(wxPyGridTableBase):
300    
301          self.GetView().BeginBatch()          self.GetView().BeginBatch()
302    
303          self.fieldType = clazz.GetFieldType()          self.fieldType = fieldType
304          self.shapeType = shapeType          self.shapeType = shapeType
305    
306          self.SetClassification(clazz, group)          self.SetClassification(clazz, group)
307          self.__Modified(-1)          self.__Modified(-1)
308          #print "11------------------"  
309            self.__colAttr = {}
310    
311            attr = wxGridCellAttr()
312            attr.SetEditor(wxGridCellBoolEditor())
313            attr.SetRenderer(wxGridCellBoolRenderer())
314            attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER)
315            self.__colAttr[COL_VISIBLE] = attr
316    
317            attr = wxGridCellAttr()
318            attr.SetRenderer(ClassRenderer(self.shapeType))
319            attr.SetReadOnly()
320            self.__colAttr[COL_SYMBOL] = attr
321    
322          self.GetView().EndBatch()          self.GetView().EndBatch()
323          self.GetView().FitInside()          self.GetView().FitInside()
324    
325        def GetClassification(self):
326            """Return the current classification."""
327            return self.clazz
328    
329      def SetClassification(self, clazz, group = None):      def SetClassification(self, clazz, group = None):
330            """Fill in the table with the given classification.
331            Select the given group if group is not None.
332            """
333    
334          self.GetView().BeginBatch()          self.GetView().BeginBatch()
335    
336          old_len = self.GetNumberRows()          old_len = self.GetNumberRows()
         self.tdata = []  
337    
         #print "9------------------"  
         #  
         # copy the data out of the classification and into our  
         # array  
         #  
338          row = -1          row = -1
339          for g in clazz:          self.clazz = clazz
             ng = copy.deepcopy(g)  
             self.__SetRow(None, ng)  
             if g is group:  
                 row = self.GetNumberRows() - 1  
                 #print "selecting row..."  
340    
         #print "10------------------"  
   
341          self.__NotifyRowChanges(old_len, self.GetNumberRows())          self.__NotifyRowChanges(old_len, self.GetNumberRows())
342    
343            #
344            # XXX: this is dead code at the moment
345            #
346          if row > -1:          if row > -1:
347              self.GetView().ClearSelection()              self.GetView().ClearSelection()
348              self.GetView().SelectRow(row)              self.GetView().SelectRow(row)
349              self.GetView().MakeCellVisible(row, 0)              self.GetView().MakeCellVisible(row, 0)
350    
351            self.__Modified()
352    
353          self.GetView().EndBatch()          self.GetView().EndBatch()
354          self.GetView().FitInside()          self.GetView().FitInside()
355    
356      def __NotifyRowChanges(self, curRows, newRows):      def __NotifyRowChanges(self, curRows, newRows):
357            """Make sure table updates correctly if the number of
358            rows changes.
359            """
360          #          #
361          # silly message processing for updates to the number of          # silly message processing for updates to the number of
362          # rows and columns          # rows and columns
# Line 398  class ClassTable(wxPyGridTableBase): Line 388  class ClassTable(wxPyGridTableBase):
388    
389          # either append or replace          # either append or replace
390          if row is None or row >= self.GetNumberRows():          if row is None or row >= self.GetNumberRows():
391              self.tdata.append(group)              self.clazz.AppendGroup(group)
392          elif row < 0:          elif row < 0:
393              self.tdata.insert(0, group)              self.clazz.InsertGroup(0, group)
394          else:          else:
395              self.tdata[row] = group              if row == 0:
396                    self.clazz.SetDefaultGroup(group)
397                else:
398                    self.clazz.ReplaceGroup(row - 1, group)
399    
400          self.__Modified()          self.__Modified()
401    
# Line 413  class ClassTable(wxPyGridTableBase): Line 406  class ClassTable(wxPyGridTableBase):
406      def GetRowLabelValue(self, row):      def GetRowLabelValue(self, row):
407          """Return the label for the given row."""          """Return the label for the given row."""
408    
409          group = self.tdata[row]          if row == 0:
410          if isinstance(group, ClassGroupDefault):   return _("Default")              return _("Default")
411          if isinstance(group, ClassGroupSingleton): return _("Singleton")          else:
412          if isinstance(group, ClassGroupRange):     return _("Range")              group = self.clazz.GetGroup(row - 1)
413          if isinstance(group, ClassGroupMap):       return _("Map")              if isinstance(group, ClassGroupDefault):   return _("Default")
414                if isinstance(group, ClassGroupSingleton): return _("Singleton")
415                if isinstance(group, ClassGroupRange):     return _("Range")
416                if isinstance(group, ClassGroupMap):       return _("Map")
417    
418          assert False # shouldn't get here          assert False # shouldn't get here
419          return _("")          return ""
420    
421      def GetNumberRows(self):      def GetNumberRows(self):
422          """Return the number of rows."""          """Return the number of rows."""
423          return len(self.tdata)          if self.clazz is None:
424                return 0
425    
426            return self.clazz.GetNumGroups() + 1 # +1 for default group
427    
428      def GetNumberCols(self):      def GetNumberCols(self):
429          """Return the number of columns."""          """Return the number of columns."""
430          return self.NUM_COLS          return NUM_COLS
431    
432      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
433          """Determine if a cell is empty. This is always false."""          """Determine if a cell is empty. This is always false."""
# Line 446  class ClassTable(wxPyGridTableBase): Line 445  class ClassTable(wxPyGridTableBase):
445          """          """
446    
447          self.SetValueAsCustom(row, col, None, value)          self.SetValueAsCustom(row, col, None, value)
448          self.__Modified()  
         
449      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
450          """Return the object that is used to represent the given          """Return the object that is used to represent the given
451             cell coordinates. This may not be a string.             cell coordinates. This may not be a string.
452    
453          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
454          """          """
455    
456          group = self.tdata[row]          if row == 0:
457                group = self.clazz.GetDefaultGroup()
458            else:
459                group = self.clazz.GetGroup(row - 1)
460    
461    
462            if col == COL_VISIBLE:
463                return group.IsVisible()
464    
465          if col == COL_SYMBOL:          if col == COL_SYMBOL:
466              return group.GetProperties()              return group.GetProperties()
# Line 471  class ClassTable(wxPyGridTableBase): Line 476  class ClassTable(wxPyGridTableBase):
476          elif isinstance(group, ClassGroupSingleton):          elif isinstance(group, ClassGroupSingleton):
477              return group.GetValue()              return group.GetValue()
478          elif isinstance(group, ClassGroupRange):          elif isinstance(group, ClassGroupRange):
479              return _("%s - %s") % (group.GetMin(), group.GetMax())              return group.GetRange()
480    
481          assert False  # shouldn't get here          assert False # shouldn't get here
482          return None          return None
483    
484      def __ParseInput(self, value):      def __ParseInput(self, value):
485          """Try to determine what kind of input value is          """Try to determine what kind of input value is
486             (string, number, or range)             (string, number, or range)
487    
488          Returns a tuple of length one if there is a single          Returns a tuple (type, data) where type is 0 if data is
489          value, or of length two if it is a range.          a singleton value, or 1 if is a range
490          """          """
491    
492          type = self.fieldType          type = self.fieldType
493    
494          if type == FIELDTYPE_STRING:          if type == FIELDTYPE_STRING:
495              return (value,)              return (0, value)
496          elif type == FIELDTYPE_INT or type == FIELDTYPE_DOUBLE:          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
   
497              if type == FIELDTYPE_INT:              if type == FIELDTYPE_INT:
498                    # the float call allows the user to enter 1.0 for 1
499                  conv = lambda p: int(float(p))                  conv = lambda p: int(float(p))
500              else:              else:
501                  conv = lambda p: p                  conv = float
502    
503              #              #
504              # first try to take the input as a single number              # first try to take the input as a single number
505              # if there's an exception try to break it into              # if there's an exception try to break it into
506              # a range seperated by a '-'. take care to ignore              # a range. if there is an exception here, let it
507              # a leading '-' as that could be for a negative number.              # pass up to the calling function.
             # then try to parse the individual parts. if there  
             # is an exception here, let it pass up to the calling  
             # function.  
508              #              #
509              try:              try:
510                  return (conv(Str2Num(value)),)                  return (0, conv(value))
511              except ValueError:              except ValueError:
512                  i = value.find('-')                  return (1, Range(value))
                 if i == 0:  
                     i = value.find('-', 1)  
   
                 return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))  
513    
514          assert False  # shouldn't get here          assert False  # shouldn't get here
515          return (0,)          return (0,None)
               
516    
517      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
518          """Set the cell specified by 'row' and 'col' to 'value'.          """Set the cell specified by 'row' and 'col' to 'value'.
# Line 529  class ClassTable(wxPyGridTableBase): Line 526  class ClassTable(wxPyGridTableBase):
526          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
527          """          """
528    
529          assert col >= 0 and col < self.GetNumberCols()          assert 0 <= col < self.GetNumberCols()
530          assert row >= 0 and row < self.GetNumberRows()          assert 0 <= row < self.GetNumberRows()
531    
532          group = self.tdata[row]          if row == 0:
533                group = self.clazz.GetDefaultGroup()
534            else:
535                group = self.clazz.GetGroup(row - 1)
536    
537          mod = True # assume the data will change          mod = True # assume the data will change
538    
539          if col == COL_SYMBOL:          if col == COL_VISIBLE:
540                group.SetVisible(value)
541            elif col == COL_SYMBOL:
542              group.SetProperties(value)              group.SetProperties(value)
543          elif col == COL_LABEL:          elif col == COL_LABEL:
544              group.SetLabel(value)              group.SetLabel(value)
# Line 564  class ClassTable(wxPyGridTableBase): Line 566  class ClassTable(wxPyGridTableBase):
566                      # changing the underlying group type if the                      # changing the underlying group type if the
567                      # group was a singleton and a range was entered                      # group was a singleton and a range was entered
568                      #                      #
569                      if len(dataInfo) == 1:                      if dataInfo[0] == 0:
570                          if not isinstance(group, ClassGroupSingleton):                          if not isinstance(group, ClassGroupSingleton):
571                              ngroup = ClassGroupSingleton(prop = props)                              ngroup = ClassGroupSingleton(props = props)
572                              changed = True                              changed = True
573                          ngroup.SetValue(dataInfo[0])                          ngroup.SetValue(dataInfo[1])
574                      elif len(dataInfo) == 2:                      elif dataInfo[0] == 1:
575                          if not isinstance(group, ClassGroupRange):                          if not isinstance(group, ClassGroupRange):
576                              ngroup = ClassGroupRange(prop = props)                              ngroup = ClassGroupRange(props = props)
577                              changed = True                              changed = True
578                          ngroup.SetRange(dataInfo[0], dataInfo[1])                          ngroup.SetRange(dataInfo[1])
579                      else:                      else:
580                          assert False                          assert False
581                          pass                          pass
# Line 592  class ClassTable(wxPyGridTableBase): Line 594  class ClassTable(wxPyGridTableBase):
594      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
595          """Returns the cell attributes"""          """Returns the cell attributes"""
596    
597          attr = wxGridCellAttr()          return self.__colAttr.get(col, wxGridCellAttr()).Clone()
         #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)  
   
         if col == COL_SYMBOL:  
             # we need to create a new renderer each time, because  
             # SetRenderer takes control of the parameter  
             attr.SetRenderer(ClassRenderer(self.shapeType))  
             attr.SetReadOnly()  
   
         return attr  
598    
599      def GetClassGroup(self, row):      def GetClassGroup(self, row):
600          """Return the ClassGroup object representing row 'row'."""          """Return the ClassGroup object representing row 'row'."""
601    
602          return self.tdata[row] # self.GetValueAsCustom(row, COL_SYMBOL, None)          #return self.GetValueAsCustom(row, COL_SYMBOL, None)
603            if row == 0:
604                return self.clazz.GetDefaultGroup()
605            else:
606                return self.clazz.GetGroup(row - 1)
607    
608      def SetClassGroup(self, row, group):      def SetClassGroup(self, row, group):
609            """Set the given row to properties of group."""
610          self.__SetRow(row, group)          self.__SetRow(row, group)
611          self.GetView().Refresh()          self.GetView().Refresh()
612    
# Line 638  class ClassTable(wxPyGridTableBase): Line 636  class ClassTable(wxPyGridTableBase):
636          """          """
637    
638          assert pos >= 0          assert pos >= 0
639          old_len = len(self.tdata)          old_len = self.GetNumberRows()
640          for row in range(pos, pos - numRows, -1):          for row in range(pos, pos - numRows, -1):
641              group = self.GetClassGroup(row)              group = self.GetClassGroup(row)
642              if not isinstance(group, ClassGroupDefault):              if row != 0:
643                  self.tdata.pop(row)                  self.clazz.RemoveGroup(row - 1)
644                  self.__Modified()                  self.__Modified()
645            
646          if self.IsModified():          if self.IsModified():
647              self.__NotifyRowChanges(old_len, len(self.tdata))              self.__NotifyRowChanges(old_len, self.GetNumberRows())
648    
649      def AppendRows(self, numRows = 1):      def AppendRows(self, numRows = 1):
650          """Append 'numRows' empty rows to the end of the table.          """Append 'numRows' empty rows to the end of the table.
# Line 654  class ClassTable(wxPyGridTableBase): Line 652  class ClassTable(wxPyGridTableBase):
652          The table is considered modified if any rows are appended.          The table is considered modified if any rows are appended.
653          """          """
654    
655          old_len = len(self.tdata)          old_len = self.GetNumberRows()
656          for i in range(numRows):          for i in range(numRows):
657              np = ClassGroupSingleton()              np = ClassGroupSingleton()
658              self.__SetRow(None, np)              self.__SetRow(None, np)
659    
660          if self.IsModified():          if self.IsModified():
661              self.__NotifyRowChanges(old_len, len(self.tdata))              self.__NotifyRowChanges(old_len, self.GetNumberRows())
   
662    
 class Classifier(NonModalDialog):  
663    
664      def __init__(self, parent, name, layer, group = None):  ID_PROPERTY_REVERT = 4002
665          NonModalDialog.__init__(self, parent, name,  ID_PROPERTY_ADD = 4003
666                                  _("Classifier: %s") % layer.Title())  ID_PROPERTY_GENCLASS = 4004
667    ID_PROPERTY_REMOVE = 4005
668    ID_PROPERTY_MOVEUP = 4006
669    ID_PROPERTY_MOVEDOWN = 4007
670    ID_PROPERTY_TRY = 4008
671    ID_PROPERTY_EDITSYM = 4009
672    ID_PROPERTY_SELECT = 4011
673    ID_PROPERTY_TITLE = 4012
674    ID_PROPERTY_FIELDTEXT = 4013
675    
676    BTN_ADD = 0
677    BTN_EDIT = 1
678    BTN_GEN = 2
679    BTN_UP = 3
680    BTN_DOWN = 4
681    BTN_RM = 5
682    
683    EB_LAYER_TITLE = 0
684    EB_SELECT_FIELD = 1
685    EB_GEN_CLASS = 2
686    
687          panel = wxPanel(self, -1, size=(100, 100))  class Classifier(NonModalNonParentDialog):
688    
689          self.layer = layer      type2string = {None:             _("None"),
690                       FIELDTYPE_STRING: _("Text"),
691                       FIELDTYPE_INT:    _("Integer"),
692                       FIELDTYPE_DOUBLE: _("Decimal")}
693    
694          self.originalClass = self.layer.GetClassification()      def __init__(self, parent, name, layer, group = None):
695          field = self.originalClass.GetField()          """Create a Properties/Classification dialog for a layer.
696          fieldType = self.originalClass.GetFieldType()          The layer is part of map. If group is not None, select that
697            group in the classification table.
698            """
699    
700          topBox = wxBoxSizer(wxVERTICAL)          NonModalNonParentDialog.__init__(self, parent, name, "")
         panelBox = wxBoxSizer(wxVERTICAL)  
701    
702          #panelBox.Add(wxStaticText(panel, -1, _("Layer: %s") % layer.Title()),          self.__SetTitle(layer.Title())
             #0, wxALIGN_LEFT | wxALL, 4)  
         panelBox.Add(wxStaticText(panel, -1,  
                                 _("Layer Type: %s") % layer.ShapeType()),  
             0, wxALIGN_LEFT | wxALL, 4)  
703    
704            self.parent.Subscribe(MAP_REPLACED, self.map_replaced)
705            self.layer = layer
706            self.map = parent.Map()
707    
708          #          self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
709          # make field combo box          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
710          #                               self.layer_shapestore_replaced)
         self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",  
                                      style = wxCB_READONLY)  
711    
712          self.num_cols = layer.table.field_count()          self.genDlg = None
         # just assume the first field in case one hasn't been  
         # specified in the file.  
         self.__cur_field = 0  
   
         self.fields.Append("<None>")  
         self.fields.SetClientData(0, None)  
   
         for i in range(self.num_cols):  
             type, name, len, decc = layer.table.field_info(i)  
             self.fields.Append(name)  
   
             if name == field:  
                 self.__cur_field = i + 1  
                 self.fields.SetClientData(i + 1, self.originalClass)  
             else:  
                 self.fields.SetClientData(i + 1, None)  
713    
714            ############################
715            # Create the controls
716            #
717    
718          ###########          panel = wxPanel(self, -1)
719    
720            text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())
721          self.fieldTypeText = wxStaticText(panel, -1, "")          self.fieldTypeText = wxStaticText(panel, -1, "")
         panelBox.Add(self.fieldTypeText, 0,  
                      wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)  
722    
723          propertyBox = wxBoxSizer(wxHORIZONTAL)          if layer.HasClassification():
724          propertyBox.Add(wxStaticText(panel, -1, _("Field: ")),              self.originalClass = self.layer.GetClassification()
725              0, wxALIGN_LEFT | wxALL, 4)              self.originalClassField = self.layer.GetClassificationColumn()
726          propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)              field = self.originalClassField
727          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)              fieldType = self.layer.GetFieldType(field)
728    
729          panelBox.Add(propertyBox, 0, wxGROW, 4)              table = layer.ShapeStore().Table()
730                #
731                # make field choice box
732                #
733                self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
734    
735                self.num_cols = table.NumColumns()
736                # just assume the first field in case one hasn't been
737                # specified in the file.
738                self.__cur_field = 0
739    
740          #              self.fields.Append("<None>")
         # Control Box  
         #  
         controlBox = wxBoxSizer(wxHORIZONTAL)  
741    
742                if fieldType is None:
743                    self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
744                else:
745                    self.fields.SetClientData(0, None)
746    
747          ###########              for i in range(self.num_cols):
748          #                  name = table.Column(i).name
749          # Control buttons:                  self.fields.Append(name)
750          #  
751          self.controlButtons = []                  if name == field:
752                        self.__cur_field = i + 1
753          controlButtonBox = wxBoxSizer(wxVERTICAL)                      self.fields.SetClientData(i + 1,
754                                                  copy.deepcopy(self.originalClass))
755                    else:
756                        self.fields.SetClientData(i + 1, None)
757    
758          button = wxButton(panel, ID_CLASSIFY_ADD, _("Add"))              button_gen = wxButton(panel, ID_PROPERTY_GENCLASS,
759          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)                  _("Generate Class"))
760          self.controlButtons.append(button)              button_add = wxButton(panel, ID_PROPERTY_ADD,
761                    _("Add"))
762                button_moveup = wxButton(panel, ID_PROPERTY_MOVEUP,
763                    _("Move Up"))
764                button_movedown = wxButton(panel, ID_PROPERTY_MOVEDOWN,
765                    _("Move Down"))
766                button_edit = wxButton(panel, ID_PROPERTY_EDITSYM,
767                    _("Edit Symbol"))
768                button_remove = wxButton(panel, ID_PROPERTY_REMOVE,
769                    _("Remove"))
770    
771                self.classGrid = ClassGrid(panel, self)
772    
773                # calling __SelectField after creating the classGrid fills in the
774                # grid with the correct information
775                self.fields.SetSelection(self.__cur_field)
776                self.__SelectField(self.__cur_field, group = group)
777    
778            button_try = wxButton(self, ID_PROPERTY_TRY, _("Try"))
779            button_revert = wxButton(self, ID_PROPERTY_REVERT, _("Revert"))
780            button_ok = wxButton(self, wxID_OK, _("OK"))
781            button_close = wxButton(self, wxID_CANCEL, _("Close"))
782            button_ok.SetDefault()
783    
784          button = wxButton(panel, ID_CLASSIFY_EDITPROPS, _("Edit Properties"))          ############################
785          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)          # Layout the controls
786          self.controlButtons.append(button)          #
787    
788          button = wxButton(panel, ID_CLASSIFY_GENCLASS, _("Generate Class"))          topBox = wxBoxSizer(wxVERTICAL)
789          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)          panelBox = wxBoxSizer(wxVERTICAL)
         self.controlButtons.append(button)  
790    
791          button = wxButton(panel, ID_CLASSIFY_MOVEUP, _("Move Up"))          sizer = wxBoxSizer(wxHORIZONTAL)
792          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)          sizer.Add(wxStaticText(panel, -1, _("Title: ")),
793          self.controlButtons.append(button)              0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
794            sizer.Add(text_title, 1, wxGROW, 0)
795    
796          button = wxButton(panel, ID_CLASSIFY_MOVEDOWN, _("Move Down"))          panelBox.Add(sizer, 0, wxGROW, 4)
         controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         self.controlButtons.append(button)  
797    
798          controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)          if isinstance(layer, RasterLayer):
799                type = "Image"
800            else:
801                type = layer.ShapeType()
802    
803          button = wxButton(panel, ID_CLASSIFY_REMOVE, _("Remove"))          panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),
804          controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)              0, wxALIGN_LEFT | wxALL, 4)
         self.controlButtons.append(button)  
805    
806            if layer.HasClassification():
807    
808          ###########              classBox = wxStaticBoxSizer(
809          #                          wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
         # Classification data table  
         #  
810    
         self.classGrid = ClassGrid(panel, self)  
         #self.__SetGridTable(self.__cur_field, group)  
         #self.fields.SetSelection(self.__cur_field)  
811    
812          # calling __SelectField after creating the classGrid fills in the              sizer = wxBoxSizer(wxHORIZONTAL)
813          # grid with the correct information              sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
814          #print "2------------------"                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
815          self.__SelectField(self.__cur_field, group = group)              sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
816    
817          #self.classGrid.SelectGroup(group)              classBox.Add(sizer, 0, wxGROW, 4)
818    
819          controlBox.Add(self.classGrid, 1, wxGROW, 0)              classBox.Add(self.fieldTypeText, 0,
820                            wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
821    
822                controlBox = wxBoxSizer(wxHORIZONTAL)
823                controlButtonBox = wxBoxSizer(wxVERTICAL)
824    
825                controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)
826                controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)
827                controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
828                controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
829                controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
830                controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
831                controlButtonBox.Add(button_remove, 0,
832                                     wxGROW|wxALL|wxALIGN_BOTTOM, 4)
833    
834          controlBox.Add(controlButtonBox, 0, wxGROW, 10)              controlBox.Add(self.classGrid, 1, wxGROW, 0)
835          panelBox.Add(controlBox, 1, wxGROW, 10)              controlBox.Add(controlButtonBox, 0, wxGROW, 10)
836    
837          EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)              classBox.Add(controlBox, 1, wxGROW, 10)
838          EVT_BUTTON(self, ID_CLASSIFY_EDITPROPS, self._OnEditGroupProperties)              panelBox.Add(classBox, 1, wxGROW, 0)
         EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)  
         EVT_BUTTON(self, ID_CLASSIFY_GENCLASS, self._OnGenClass)  
         EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)  
         EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)  
839    
         ###########  
840    
841          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
842          buttonBox.Add(wxButton(panel, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(button_try, 0, wxRIGHT|wxEXPAND, 10)
843                        0, wxALL, 4)          buttonBox.Add(button_revert, 0, wxRIGHT|wxEXPAND, 10)
844          buttonBox.Add(60, 20, 0, wxALL, 4)          buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
845          buttonBox.Add(wxButton(panel, ID_CLASSIFY_APPLY, _("Apply")),          buttonBox.Add(button_close, 0, wxRIGHT|wxEXPAND, 10)
                       0, wxALL, 4)  
         buttonBox.Add(60, 20, 0, wxALL, 4)  
         buttonBox.Add(wxButton(panel, ID_CLASSIFY_CANCEL, _("Cancel")),  
                       0, wxALL, 4)  
         panelBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)  
   
         EVT_BUTTON(self, ID_CLASSIFY_OK, self._OnOK)  
         EVT_BUTTON(self, ID_CLASSIFY_APPLY, self._OnApply)  
         EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self._OnCancel)  
   
         ###########  
   
846    
847          panel.SetAutoLayout(True)          panel.SetAutoLayout(True)
848          panel.SetSizer(panelBox)          panel.SetSizer(panelBox)
849            panelBox.Fit(panel)
850          panelBox.SetSizeHints(panel)          panelBox.SetSizeHints(panel)
851    
852          topBox.Add(panel, 1, wxGROW, 0)          topBox.Add(panel, 1, wxGROW | wxALL, 4)
853          panelBox.SetSizeHints(self)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
854    
855          self.SetAutoLayout(True)          self.SetAutoLayout(True)
856          self.SetSizer(topBox)          self.SetSizer(topBox)
857            topBox.Fit(self)
858            topBox.SetSizeHints(self)
859            self.Layout()
860    
861            ###########
862    
863            EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
864            EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
865            EVT_BUTTON(self, wxID_OK, self._OnOK)
866            EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
867            EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)
868            EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
869    
870            EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
871            EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
872            EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
873            EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
874            EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
875            EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
876    
         #print "1------------------"  
         #self.Fit()  
877          ######################          ######################
878    
879            text_title.SetFocus()
880          self.haveApplied = False          self.haveApplied = False
881    
882      def EditGroupProperties(self, row):      def unsubscribe_messages(self):
883            """Unsubscribe from all messages."""
884            self.parent.Unsubscribe(MAP_REPLACED, self.map_replaced)
885            self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
886            self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
887                                   self.layer_shapestore_replaced)
888    
889        def map_layers_removed(self, map):
890            """Subscribed to MAP_LAYERS_REMOVED. If this layer was removed,
891            Close self.
892            """
893            if self.layer not in self.map.Layers():
894                self.Close()
895    
896        def layer_shapestore_replaced(self, *args):
897            """Subscribed to the map's LAYER_SHAPESTORE_REPLACED message.
898    
899            Close self.
900            """
901            self.Close()
902    
903        def map_replaced(self, *args):
904            """Subscribed to the mainwindow's MAP_REPLACED message. Close self."""
905            self.Close()
906    
907        def EditSymbol(self, row):
908            """Open up a dialog where the user can select the properties
909            for a group.
910            """
911          table = self.classGrid.GetTable()          table = self.classGrid.GetTable()
912          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
913    
914          # get a new ClassGroupProperties object and copy the          # get a new ClassGroupProperties object and copy the
915          # values over to our current object          # values over to our current object
916          propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())          propDlg = SelectPropertiesDialog(self, prop, self.layer.ShapeType())
917    
918            self.Enable(False)
919          if propDlg.ShowModal() == wxID_OK:          if propDlg.ShowModal() == wxID_OK:
920              new_prop = propDlg.GetClassGroupProperties()              new_prop = propDlg.GetClassGroupProperties()
921              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
922            self.Enable(True)
923          propDlg.Destroy()          propDlg.Destroy()
           
924    
925      def __BuildClassification(self, fieldIndex):      def _SetClassification(self, clazz):
926            """Called from the ClassGen dialog when a new classification has
927            been created and should be set in the table.
928            """
929            # FIXME: This could be implemented using a message
930    
931            self.fields.SetClientData(self.__cur_field, clazz)
932            self.classGrid.GetTable().SetClassification(clazz)
933    
934          numRows = self.classGrid.GetNumberRows()      def __BuildClassification(self, fieldIndex, copyClass=False, force=False):
935          assert numRows > 0  # there should always be a default row          """Pack the classification setting into a Classification object.
936            Returns (Classification, fieldName) where fieldName is the selected
937            field in the table that the classification should be used with.
938            """
939    
940    #       numRows = self.classGrid.GetNumberRows()
941    #       assert numRows > 0  # there should always be a default row
942    
         clazz = Classification()  
943          if fieldIndex == 0:          if fieldIndex == 0:
944              fieldName = None              fieldName = None
945              fieldType = None              fieldType = None
# Line 859  class Classifier(NonModalDialog): Line 947  class Classifier(NonModalDialog):
947              fieldName = self.fields.GetString(fieldIndex)              fieldName = self.fields.GetString(fieldIndex)
948              fieldType = self.layer.GetFieldType(fieldName)              fieldType = self.layer.GetFieldType(fieldName)
949    
950          clazz.SetField(fieldName)          clazz = self.fields.GetClientData(fieldIndex)
951          clazz.SetFieldType(fieldType)          if clazz is None or self.classGrid.GetTable().IsModified() or force:
952                clazz = self.classGrid.GetTable().GetClassification()
953                if copyClass:
954          table = self.classGrid.GetTable()                  clazz = copy.deepcopy(clazz)
         clazz.SetDefaultGroup(table.GetClassGroup(0))  
   
         for i in range(1, numRows):  
             clazz.AddGroup(table.GetClassGroup(i))  
955    
956          return clazz          return clazz, fieldName
957    
958      def __SetGridTable(self, fieldIndex, group = None):      def __SetGridTable(self, fieldIndex, group = None):
959            """Set the table with the classification associated with the
960            selected field at fieldIndex. Select the specified group
961            if group is not None.
962            """
963    
964          clazz = self.fields.GetClientData(fieldIndex)          clazz = self.fields.GetClientData(fieldIndex)
965    
# Line 882  class Classifier(NonModalDialog): Line 970  class Classifier(NonModalDialog):
970                      self.layer.GetClassification().                      self.layer.GetClassification().
971                                 GetDefaultGroup().GetProperties()))                                 GetDefaultGroup().GetProperties()))
972    
973              fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
974              fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
             clazz.SetFieldType(fieldType)  
975                                    
976          #print "6------------------"          self.classGrid.CreateTable(clazz, fieldType,
977          self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)                                     self.layer.ShapeType(), group)
         #print "7------------------"  
   
   
   
     type2string = {None:             _("None"),  
                    FIELDTYPE_STRING: _("Text"),  
                    FIELDTYPE_INT:    _("Integer"),  
                    FIELDTYPE_DOUBLE: _("Decimal")}  
978    
979      def __SetFieldTypeText(self, fieldIndex):      def __SetFieldTypeText(self, fieldIndex):
980            """Set the field type string using the data type of the field
981            at fieldIndex.
982            """
983          fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
984          fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
985    
# Line 905  class Classifier(NonModalDialog): Line 987  class Classifier(NonModalDialog):
987    
988          text = Classifier.type2string[fieldType]          text = Classifier.type2string[fieldType]
989    
990          self.fieldTypeText.SetLabel(_("Field Type: %s") % text)          self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
991    
992      def __SelectField(self, newIndex, oldIndex = -1, group = None):      def __SelectField(self, newIndex, oldIndex = -1, group = None):
993            """This method assumes that the current selection for the
994          #print "3------------------"          combo has already been set by a call to SetSelection().
995            """
996    
997          assert oldIndex >= -1          assert oldIndex >= -1
998    
         self.fields.SetSelection(newIndex)  
   
999          if oldIndex != -1:          if oldIndex != -1:
1000              clazz = self.__BuildClassification(oldIndex)              clazz, name = self.__BuildClassification(oldIndex, force = True)
1001              self.fields.SetClientData(oldIndex, clazz)              self.fields.SetClientData(oldIndex, clazz)
1002    
         #print "4------------------"  
1003          self.__SetGridTable(newIndex, group)          self.__SetGridTable(newIndex, group)
         #print "5------------------"  
   
         enabled = newIndex != 0  
1004    
1005          for b in self.controlButtons:          self.__EnableButtons(EB_SELECT_FIELD)
             b.Enable(enabled)  
1006    
1007          self.__SetFieldTypeText(newIndex)          self.__SetFieldTypeText(newIndex)
1008    
1009        def __SetTitle(self, title):
1010            """Set the title of the dialog."""
1011            if title != "":
1012                title = ": " + title
1013    
1014            self.SetTitle(_("Layer Properties") + title)
1015    
1016      def _OnEditGroupProperties(self, event):      def _OnEditSymbol(self, event):
1017            """Open up a dialog for the user to select group properties."""
1018          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1019    
1020          if len(sel) == 1:          if len(sel) == 1:
1021              self.EditGroupProperties(sel[0])              self.EditSymbol(sel[0])
1022    
1023      def _OnFieldSelect(self, event):      def _OnFieldSelect(self, event):
1024          index = self.fields.GetSelection()          index = self.fields.GetSelection()
1025          self.__SelectField(index, self.__cur_field)          self.__SelectField(index, self.__cur_field)
1026          self.__cur_field = index          self.__cur_field = index
1027    
1028      def _OnApply(self, event):      def _OnTry(self, event):
1029          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
1030             it to the layer.             it to the layer.
1031          """          """
1032    
1033          clazz = self.fields.GetClientData(self.__cur_field)          if self.layer.HasClassification():
1034                clazz = self.fields.GetClientData(self.__cur_field)
1035    
1036          #              #
1037          # only build the classification if there wasn't one to              # only build the classification if there wasn't one to
1038          # to begin with or it has been modified              # to begin with or it has been modified
1039          #              #
1040          if clazz is None or self.classGrid.GetTable().IsModified():              self.classGrid.SaveEditControlValue()
1041              clazz = self.__BuildClassification(self.__cur_field)              clazz, name = self.__BuildClassification(self.__cur_field, True)
1042    
1043          self.layer.SetClassification(clazz)              self.layer.SetClassificationColumn(name)
1044                self.layer.SetClassification(clazz)
1045    
1046          self.haveApplied = True          self.haveApplied = True
1047    
1048      def _OnOK(self, event):      def _OnOK(self, event):
1049          self._OnApply(event)          self._OnTry(event)
1050          self.OnClose(event)          self.Close()
1051    
1052        def OnClose(self, event):
1053            self.unsubscribe_messages()
1054            NonModalNonParentDialog.OnClose(self, event)
1055    
1056        def _OnCloseBtn(self, event):
1057            """Close is similar to Cancel except that any changes that were
1058            made and applied remain applied, but the currently displayed
1059            classification is discarded.
1060            """
1061    
1062      def _OnCancel(self, event):          self.Close()
1063    
1064        def _OnRevert(self, event):
1065          """The layer's current classification stays the same."""          """The layer's current classification stays the same."""
1066          if self.haveApplied:          if self.haveApplied:
1067                self.layer.SetClassificationColumn(self.originalClassField)
1068              self.layer.SetClassification(self.originalClass)              self.layer.SetClassification(self.originalClass)
1069    
1070          self.OnClose(event)          #self.Close()
1071    
1072      def _OnAdd(self, event):      def _OnAdd(self, event):
1073          self.classGrid.AppendRows()          self.classGrid.AppendRows()
# Line 978  class Classifier(NonModalDialog): Line 1076  class Classifier(NonModalDialog):
1076          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
1077    
1078      def _OnGenClass(self, event):      def _OnGenClass(self, event):
1079            """Open up a dialog for the user to generate classifications."""
1080    
1081            self.genDlg = ClassGenDialog(self, self.layer,
1082                              self.fields.GetString(self.__cur_field))
1083    
1084            EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1085    
1086          genDlg = ClassGenDialog(self,          self.__EnableButtons(EB_GEN_CLASS)
1087                                  self.layer.table,  
1088                                  self.fields.GetString(self.__cur_field))          self.genDlg.Show()
1089    
1090          if genDlg.ShowModal() == wxID_OK:      def _OnGenDialogClose(self, event):
1091              clazz = genDlg.GetClassification()          """Reenable buttons after the generate classification
1092              self.fields.SetClientData(self.__cur_field, clazz)          dialog is closed.
1093              self.classGrid.GetTable().SetClassification(clazz)          """
1094          genDlg.Destroy()          self.genDlg.Destroy()
1095            self.genDlg = None
1096            self.__EnableButtons(EB_GEN_CLASS)
1097    
1098      def _OnMoveUp(self, event):      def _OnMoveUp(self, event):
1099            """When the user clicks MoveUp, try to move a group up one row."""
1100          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1101    
         #print "sel: ", sel  
   
1102          if len(sel) == 1:          if len(sel) == 1:
1103              i = sel[0]              i = sel[0]
1104              if i > 1:              if i > 1:
# Line 1007  class Classifier(NonModalDialog): Line 1112  class Classifier(NonModalDialog):
1112                  self.classGrid.MakeCellVisible(i - 1, 0)                  self.classGrid.MakeCellVisible(i - 1, 0)
1113    
1114      def _OnMoveDown(self, event):      def _OnMoveDown(self, event):
1115            """When the user clicks MoveDown, try to move a group down one row."""
1116          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1117    
1118          if len(sel) == 1:          if len(sel) == 1:
# Line 1021  class Classifier(NonModalDialog): Line 1127  class Classifier(NonModalDialog):
1127                  self.classGrid.SelectRow(i + 1)                  self.classGrid.SelectRow(i + 1)
1128                  self.classGrid.MakeCellVisible(i + 1, 0)                  self.classGrid.MakeCellVisible(i + 1, 0)
1129    
1130        def _OnTitleChanged(self, event):
1131            """Update the dialog title when the user changed the layer name."""
1132            obj = event.GetEventObject()
1133    
1134            self.layer.SetTitle(obj.GetValue())
1135            self.__SetTitle(self.layer.Title())
1136    
1137            self.__EnableButtons(EB_LAYER_TITLE)
1138    
1139        def __EnableButtons(self, case):
1140            """Helper method that enables/disables the appropriate buttons
1141            based on the case provided. Cases are constants beginning with EB_.
1142            """
1143    
1144            list = {wxID_OK                 : True,
1145                    wxID_CANCEL             : True,
1146                    ID_PROPERTY_ADD         : True,
1147                    ID_PROPERTY_MOVEUP      : True,
1148                    ID_PROPERTY_MOVEDOWN    : True,
1149                    ID_PROPERTY_REMOVE      : True,
1150                    ID_PROPERTY_SELECT      : True,
1151                    ID_PROPERTY_FIELDTEXT   : True,
1152                    ID_PROPERTY_GENCLASS    : True,
1153                    ID_PROPERTY_EDITSYM     : True}
1154    
1155            if case == EB_LAYER_TITLE:  
1156                if self.layer.Title() == "":
1157                    list[wxID_OK] = False
1158                    list[wxID_CANCEL] = False
1159    
1160            elif case == EB_SELECT_FIELD:
1161                if self.fields.GetSelection() == 0:
1162                    list[ID_PROPERTY_GENCLASS] = False
1163                    list[ID_PROPERTY_ADD] = False
1164                    list[ID_PROPERTY_MOVEUP] = False
1165                    list[ID_PROPERTY_MOVEDOWN] = False
1166                    list[ID_PROPERTY_REMOVE] = False
1167    
1168            elif case == EB_GEN_CLASS:
1169                if self.genDlg is not None:
1170                    list[ID_PROPERTY_SELECT] = False
1171                    list[ID_PROPERTY_FIELDTEXT] = False
1172                    list[ID_PROPERTY_GENCLASS] = False
1173    
1174            for id, enable in list.items():
1175                win = self.FindWindowById(id)
1176                if win:
1177                    win.Enable(enable)
1178    
1179  ID_SELPROP_OK = 4001  ID_SELPROP_SPINCTRL_LINEWIDTH = 4002
 ID_SELPROP_CANCEL = 4002  
 ID_SELPROP_SPINCTRL = 4002  
1180  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1181  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
1182  ID_SELPROP_FILLCLR = 4005  ID_SELPROP_FILLCLR = 4005
1183  ID_SELPROP_STROKECLRTRANS = 4006  ID_SELPROP_STROKECLRTRANS = 4006
1184  ID_SELPROP_FILLCLRTRANS = 4007  ID_SELPROP_FILLCLRTRANS = 4007
1185    ID_SELPROP_SPINCTRL_SIZE = 4008
1186    
1187  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
1188        """Dialog that allows the user to select group properties."""
1189    
1190      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1191            """Open the dialog with the initial prop properties and shapeType."""
1192    
1193          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
1194                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1195    
# Line 1047  class SelectPropertiesDialog(wxDialog): Line 1203  class SelectPropertiesDialog(wxDialog):
1203          previewBox = wxBoxSizer(wxVERTICAL)          previewBox = wxBoxSizer(wxVERTICAL)
1204          previewBox.Add(wxStaticText(self, -1, _("Preview:")),          previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1205              0, wxALIGN_LEFT | wxALL, 4)              0, wxALIGN_LEFT | wxALL, 4)
1206          self.previewWin = ClassDataPreviewWindow(None, self.prop, shapeType,  
1207                                              self, ID_SELPROP_PREVIEW, (40, 40))          self.previewWin = ClassGroupPropertiesCtrl(
1208          previewBox.Add(self.previewWin, 1, wxGROW, 15)              self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1209                (40, 40), wxSIMPLE_BORDER)
1210    
1211            self.previewWin.AllowEdit(False)
1212    
1213            previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1214    
1215          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1216    
# Line 1057  class SelectPropertiesDialog(wxDialog): Line 1218  class SelectPropertiesDialog(wxDialog):
1218          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wxBoxSizer(wxVERTICAL)
1219    
1220          lineColorBox = wxBoxSizer(wxHORIZONTAL)          lineColorBox = wxBoxSizer(wxHORIZONTAL)
1221          lineColorBox.Add(          button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1222              wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),          button.SetFocus()
1223              1, wxALL | wxGROW, 4)          lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1224          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1225    
1226          lineColorBox.Add(          lineColorBox.Add(
# Line 1085  class SelectPropertiesDialog(wxDialog): Line 1246  class SelectPropertiesDialog(wxDialog):
1246              ctrlBox.Add(fillColorBox, 0,              ctrlBox.Add(fillColorBox, 0,
1247                          wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)                          wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1248    
1249            # Line width selection
1250          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
1251          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1252                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1253          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl_linewidth = wxSpinCtrl(self,
1254                                     min=1, max=10,                                               ID_SELPROP_SPINCTRL_LINEWIDTH,
1255                                     value=str(prop.GetLineWidth()),                                               min=1, max=10,
1256                                     initial=prop.GetLineWidth())                                               value=str(prop.GetLineWidth()),
1257                                                 initial=prop.GetLineWidth())
         EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)  
1258    
1259          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL_LINEWIDTH,
1260                         self._OnSpinLineWidth)
1261    
1262            spinBox.Add(self.spinCtrl_linewidth, 0, wxALIGN_LEFT | wxALL, 4)
1263          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1264    
1265            # Size selection
1266            if shapeType == SHAPETYPE_POINT:
1267                spinBox = wxBoxSizer(wxHORIZONTAL)
1268                spinBox.Add(wxStaticText(self, -1, _("Size: ")),
1269                            0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1270                self.spinCtrl_size = wxSpinCtrl(self, ID_SELPROP_SPINCTRL_SIZE,
1271                                                min=1, max=100,
1272                                                value=str(prop.GetSize()),
1273                                                initial=prop.GetSize())
1274    
1275                EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL_SIZE, self._OnSpinSize)
1276    
1277                spinBox.Add(self.spinCtrl_size, 0, wxALIGN_LEFT | wxALL, 4)
1278                ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1279    
1280    
1281          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1282          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1283    
# Line 1105  class SelectPropertiesDialog(wxDialog): Line 1285  class SelectPropertiesDialog(wxDialog):
1285          # Control buttons:          # Control buttons:
1286          #          #
1287          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1288          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),          button_ok = wxButton(self, wxID_OK, _("OK"))
1289                        0, wxALL, 4)          buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1290          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1291                        0, wxALL, 4)                        0, wxRIGHT|wxEXPAND, 10)
1292          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1293                                                                                    
1294          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)          button_ok.SetDefault()
1295          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)  
1296                                                                                            #EVT_BUTTON(self, wxID_OK, self._OnOK)
1297            #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1298    
1299          self.SetAutoLayout(True)          self.SetAutoLayout(True)
1300          self.SetSizer(topBox)          self.SetSizer(topBox)
1301          topBox.Fit(self)          topBox.Fit(self)
1302          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1303    
1304      def _OnOK(self, event):      def OnOK(self, event):
1305          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1306    
1307      def _OnCancel(self, event):      def OnCancel(self, event):
1308          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1309    
1310      def _OnSpin(self, event):      def _OnSpinLineWidth(self, event):
1311          self.prop.SetLineWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl_linewidth.GetValue())
1312            self.previewWin.Refresh()
1313    
1314        def _OnSpinSize(self, event):
1315            self.prop.SetSize(self.spinCtrl_size.GetValue())
1316          self.previewWin.Refresh()          self.previewWin.Refresh()
1317    
1318      def __GetColor(self, cur):      def __GetColor(self, cur):
1319          dialog = wxColourDialog(self)          dialog = ColorDialog(self)
1320          if cur is not Color.None:          dialog.SetColor(cur)
             dialog.GetColourData().SetColour(Color2wxColour(cur))  
1321    
1322          ret = None          ret = None
1323          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wxID_OK:
1324              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = dialog.GetColor()
1325    
1326          dialog.Destroy()          dialog.Destroy()
1327    
1328          return ret          return ret
1329            
1330      def _OnChangeLineColor(self, event):      def _OnChangeLineColor(self, event):
1331          clr = self.__GetColor(self.prop.GetLineColor())          clr = self.__GetColor(self.prop.GetLineColor())
1332          if clr is not None:          if clr is not None:
# Line 1149  class SelectPropertiesDialog(wxDialog): Line 1334  class SelectPropertiesDialog(wxDialog):
1334          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1335    
1336      def _OnChangeLineColorTrans(self, event):      def _OnChangeLineColorTrans(self, event):
1337          self.prop.SetLineColor(Color.None)          self.prop.SetLineColor(Transparent)
1338          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1339            
1340      def _OnChangeFillColor(self, event):      def _OnChangeFillColor(self, event):
1341          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1342          if clr is not None:          if clr is not None:
# Line 1159  class SelectPropertiesDialog(wxDialog): Line 1344  class SelectPropertiesDialog(wxDialog):
1344          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1345    
1346      def _OnChangeFillColorTrans(self, event):      def _OnChangeFillColorTrans(self, event):
1347          self.prop.SetFill(Color.None)          self.prop.SetFill(Transparent)
1348          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1349    
1350      def GetClassGroupProperties(self):      def GetClassGroupProperties(self):
# Line 1167  class SelectPropertiesDialog(wxDialog): Line 1352  class SelectPropertiesDialog(wxDialog):
1352    
1353    
1354  class ClassDataPreviewWindow(wxWindow):  class ClassDataPreviewWindow(wxWindow):
1355        """A custom window that draws group properties using the correct shape."""
1356    
1357      def __init__(self, rect, prop, shapeType,      def __init__(self, rect, prop, shapeType,
1358                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
1359            """Draws the appropriate shape as specified with shapeType using
1360            prop properities.
1361            """
1362          if parent is not None:          if parent is not None:
1363              wxWindow.__init__(self, parent, id, (0, 0), size)              wxWindow.__init__(self, parent, id, (0, 0), size)
1364              EVT_PAINT(self, self._OnPaint)              EVT_PAINT(self, self._OnPaint)
# Line 1180  class ClassDataPreviewWindow(wxWindow): Line 1369  class ClassDataPreviewWindow(wxWindow):
1369          self.shapeType = shapeType          self.shapeType = shapeType
1370          self.previewer = ClassDataPreviewer()          self.previewer = ClassDataPreviewer()
1371    
1372        def GetProperties():
1373            return self.prop
1374    
1375      def _OnPaint(self, event):      def _OnPaint(self, event):
1376          dc = wxPaintDC(self)          dc = wxPaintDC(self)
1377    
# Line 1195  class ClassDataPreviewWindow(wxWindow): Line 1387  class ClassDataPreviewWindow(wxWindow):
1387          self.previewer.Draw(dc, rect, self.prop, self.shapeType)          self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1388    
1389  class ClassDataPreviewer:  class ClassDataPreviewer:
1390        """Class that actually draws a group property preview."""
1391    
1392      def Draw(self, dc, rect, prop, shapeType):      def Draw(self, dc, rect, prop, shapeType):
1393            """Draw the property.
1394    
1395            returns: (w, h) as adapted extend if the drawing size
1396            exceeded the given rect. This can only be the case
1397            for point symbols. If the symbol fits the given rect,
1398            None is returned.
1399            """
1400    
1401          assert dc is not None          assert dc is not None
1402          assert isinstance(prop, ClassGroupProperties)          assert isinstance(prop, ClassGroupProperties)
# Line 1212  class ClassDataPreviewer: Line 1412  class ClassDataPreviewer:
1412              h = rect.GetHeight()              h = rect.GetHeight()
1413    
1414          stroke = prop.GetLineColor()          stroke = prop.GetLineColor()
1415          if stroke is Color.None:          if stroke is Transparent:
1416              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1417          else:          else:
1418              pen = wxPen(Color2wxColour(stroke),              pen = wxPen(Color2wxColour(stroke),
# Line 1220  class ClassDataPreviewer: Line 1420  class ClassDataPreviewer:
1420                          wxSOLID)                          wxSOLID)
1421    
1422          stroke = prop.GetFill()          stroke = prop.GetFill()
1423          if stroke is Color.None:          if stroke is Transparent:
1424              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1425          else:          else:
1426              brush = wxBrush(Color2wxColour(stroke), wxSOLID)              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
# Line 1236  class ClassDataPreviewer: Line 1436  class ClassDataPreviewer:
1436    
1437          elif shapeType == SHAPETYPE_POINT:          elif shapeType == SHAPETYPE_POINT:
1438    
1439              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2, prop.GetSize())
1440                            (min(w, h) - prop.GetLineWidth())/2)              circle_size =  prop.GetSize() * 2 + prop.GetLineWidth() * 2
1441                new_h = h
1442                new_w = w
1443                if h < circle_size: new_h = circle_size
1444                if w < circle_size: new_w = circle_size
1445                if new_h > h or new_w > w:
1446                    return (new_w, new_h)
1447    
1448          elif shapeType == SHAPETYPE_POLYGON:          elif shapeType == SHAPETYPE_POLYGON:
1449              dc.DrawRectangle(x, y, w, h)              dc.DrawRectangle(x, y, w, h)
1450    
1451            return None
1452    
1453  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1454        """A wrapper class that can be used to draw group properties in a
1455        grid table.
1456        """
1457    
1458      def __init__(self, shapeType):      def __init__(self, shapeType):
1459          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
# Line 1260  class ClassRenderer(wxPyGridCellRenderer Line 1471  class ClassRenderer(wxPyGridCellRenderer
1471                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1472    
1473          if not isinstance(data, ClassGroupMap):          if not isinstance(data, ClassGroupMap):
1474              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)              new_size = self.previewer.Draw(dc, rect, data.GetProperties(),
1475                                               self.shapeType)
1476                if new_size is not None:
1477                    (new_w, new_h) = new_size
1478                    grid.SetRowSize(row, new_h)
1479                    grid.SetColSize(col, new_h)
1480                    grid.ForceRefresh()
1481    
1482                    # now that we know the height, redraw everything
1483                    rect.SetHeight(new_h)
1484                    rect.SetWidth(new_w)
1485                    dc.DestroyClippingRegion()
1486                    dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1487                                         rect.GetWidth(), rect.GetHeight())
1488                    dc.SetPen(wxPen(wxLIGHT_GREY))
1489                    dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1490                    dc.DrawRectangle(rect.GetX(), rect.GetY(),
1491                                     rect.GetWidth(), rect.GetHeight())
1492                    self.previewer.Draw(dc, rect, data.GetProperties(),
1493                                        self.shapeType)
1494    
1495          if isSelected:          if isSelected:
1496              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
                       4, wxSOLID))  
1497              dc.SetBrush(wxTRANSPARENT_BRUSH)              dc.SetBrush(wxTRANSPARENT_BRUSH)
1498    
1499              dc.DrawRectangle(rect.GetX(), rect.GetY(),              dc.DrawRectangle(rect.GetX(), rect.GetY(),
1500                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
1501    
1502          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1503    
1504    
1505    class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1506        """A custom window and control that draw a preview of group properties
1507        and can open a dialog to modify the properties if the user double-clicks
1508        it.
1509        """
1510    
1511        def __init__(self, parent, id, props, shapeType,
1512                     size = wxDefaultSize, style = 0):
1513    
1514            wxWindow.__init__(self, parent, id, size = size, style = style)
1515    
1516            self.parent = parent
1517    
1518            self.SetProperties(props)
1519            self.SetShapeType(shapeType)
1520            self.AllowEdit(True)
1521    
1522            EVT_PAINT(self, self._OnPaint)
1523            EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1524    
1525            self.previewer = ClassDataPreviewer()
1526    
1527        def _OnPaint(self, event):
1528            dc = wxPaintDC(self)
1529    
1530            # XXX: this doesn't seem to be having an effect:
1531            dc.DestroyClippingRegion()
1532    
1533            w, h = self.GetClientSize()
1534    
1535            self.previewer.Draw(dc,
1536                                wxRect(0, 0, w, h),
1537                                self.GetProperties(),
1538                                self.GetShapeType())
1539    
1540    
1541        def GetProperties(self):
1542            return self.props
1543    
1544        def SetProperties(self, props):
1545            self.props = props
1546            self.Refresh()
1547    
1548        def GetShapeType(self):
1549            return self.shapeType
1550    
1551        def SetShapeType(self, shapeType):
1552            self.shapeType = shapeType
1553            self.Refresh()
1554    
1555        def AllowEdit(self, allow):
1556            """Allow/Disallow double-clicking on the control."""
1557            self.allowEdit = allow
1558    
1559        def DoEdit(self):
1560            """Open the properties selector dialog."""
1561    
1562            if not self.allowEdit: return
1563    
1564            propDlg = SelectPropertiesDialog(self.parent,
1565                                             self.GetProperties(),
1566                                             self.GetShapeType())
1567    
1568            if propDlg.ShowModal() == wxID_OK:
1569                new_prop = propDlg.GetClassGroupProperties()
1570                self.SetProperties(new_prop)
1571                self.Refresh()
1572    
1573            propDlg.Destroy()
1574    
1575        def _OnLeftDClick(self, event):
1576            self.DoEdit()
1577    
1578    from Thuban.UI.mainwindow import layer_properties_dialogs
1579    layer_properties_dialogs.add(Layer, Classifier)
1580    layer_properties_dialogs.add(RasterLayer, Classifier)

Legend:
Removed from v.606  
changed lines
  Added in v.2386

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26