/[thuban]/trunk/thuban/Thuban/UI/classifier.py
ViewVC logotype

Diff of /trunk/thuban/Thuban/UI/classifier.py

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

revision 630 by jonathan, Wed Apr 9 10:10:06 2003 UTC revision 2597 by bh, Thu Apr 7 19:46:29 2005 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2003 by Intevation GmbH  # Copyright (c) 2003-2005 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
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 Thuban.UI.layerproperties import LayerProperties
45    from messages import MAP_REPLACED
 # widget id's  
 ID_PROPERTY_SELECT = 4010  
 ID_CLASS_TABLE = 40011  
46    
47    
48  # table columns  # table columns
49  COL_SYMBOL = 0  COL_VISIBLE = 0
50  COL_VALUE  = 1  COL_SYMBOL  = 1
51  COL_LABEL  = 2  COL_VALUE   = 2
52    COL_LABEL   = 3
53    NUM_COLS    = 4
54    
55  # indices into the client data lists in Classifier.fields  # indices into the client data lists in Classifier.fields
56  FIELD_CLASS = 0  FIELD_CLASS = 0
# Line 62  class ClassGrid(wxGrid): Line 73  class ClassGrid(wxGrid):
73          clazz -- the working classification that this grid should          clazz -- the working classification that this grid should
74                   use for display.                   use for display.
75          """          """
76            wxGrid.__init__(self, parent, -1, style = 0)
         #wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))  
         wxGrid.__init__(self, parent, ID_CLASS_TABLE)  
         #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), True)  
77    
78          self.classifier = classifier          self.classifier = classifier
79    
# Line 77  class ClassGrid(wxGrid): Line 85  class ClassGrid(wxGrid):
85          EVT_GRID_COL_SIZE(self, self._OnCellResize)          EVT_GRID_COL_SIZE(self, self._OnCellResize)
86          EVT_GRID_ROW_SIZE(self, self._OnCellResize)          EVT_GRID_ROW_SIZE(self, self._OnCellResize)
87    
88      def CreateTable(self, clazz, shapeType, group = None):      #def GetCellAttr(self, row, col):
89            #print "GetCellAttr ", row, col
90            #wxGrid.GetCellAttr(self, row, col)
91    
92        def CreateTable(self, clazz, fieldType, shapeType, group = None):
93    
94          assert isinstance(clazz, Classification)          assert isinstance(clazz, Classification)
95    
96          table = self.GetTable()          table = self.GetTable()
97          if table is None:          if table is None:
98              w = self.GetDefaultColSize() * 3 + self.GetDefaultRowLabelSize()              w = self.GetDefaultColSize() * NUM_COLS \
99              h = self.GetDefaultRowSize() * 4 + self.GetDefaultColLabelSize()                  + self.GetDefaultRowLabelSize()
100                h = self.GetDefaultRowSize() * 4 \
101                    + self.GetDefaultColLabelSize()
102    
103              self.SetDimensions(-1, -1, w, h)              self.SetDimensions(-1, -1, w, h)
104              self.SetSizeHints(w, h, -1, -1)              self.SetSizeHints(w, h, -1, -1)
105              table = ClassTable(self)              table = ClassTable(self)
# Line 94  class ClassGrid(wxGrid): Line 109  class ClassGrid(wxGrid):
109          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(wxGrid.wxGridSelectRows)
110          self.ClearSelection()          self.ClearSelection()
111    
112          table.Reset(clazz, shapeType, group)          table.Reset(clazz, fieldType, shapeType, group)
113    
114      def GetCurrentSelection(self):      def GetCurrentSelection(self):
115          """Return the currently highlighted rows as an increasing list          """Return the currently highlighted rows as an increasing list
# Line 106  class ClassGrid(wxGrid): Line 121  class ClassGrid(wxGrid):
121      def GetSelectedRows(self):      def GetSelectedRows(self):
122          return self.GetCurrentSelection()          return self.GetCurrentSelection()
123    
124      def SetCellRenderer(self, row, col):      #def SetCellRenderer(self, row, col, renderer):
125          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          #raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
126    
127      #      #
128      # [Set|Get]Table is taken from http://wiki.wxpython.org      # [Set|Get]Table is taken from http://wiki.wxpython.org
# Line 143  class ClassGrid(wxGrid): Line 158  class ClassGrid(wxGrid):
158              group = self.GetTable().GetClassGroup(sel[0])              group = self.GetTable().GetClassGroup(sel[0])
159              if isinstance(group, ClassGroupDefault):              if isinstance(group, ClassGroupDefault):
160                  wxMessageDialog(self,                  wxMessageDialog(self,
161                                  "The Default group cannot be removed.",                                  _("The Default group cannot be removed."),
162                                  style = wxOK | wxICON_EXCLAMATION).ShowModal()                                  style = wxOK | wxICON_EXCLAMATION).ShowModal()
163                  return                  return
164                    
# Line 202  class ClassGrid(wxGrid): Line 217  class ClassGrid(wxGrid):
217  #                                  sel = False))  #                                  sel = False))
218    
219      def _OnCellDClick(self, event):      def _OnCellDClick(self, event):
220          """Handle a double on a cell."""          """Handle a double click on a cell."""
221    
222          r = event.GetRow()          r = event.GetRow()
223          c = event.GetCol()          c = event.GetCol()
         if c == COL_SYMBOL:  
             self.classifier.EditGroupProperties(r)  
224    
225            if c == COL_SYMBOL:
226                self.classifier.EditSymbol(r)
227            else:
228                event.Skip()
229    
230      #      #
231      # _OnSelectedRange() and _OnSelectedCell() were borrowed      # _OnSelectedRange() and _OnSelectedCell() were borrowed
# Line 237  class ClassGrid(wxGrid): Line 254  class ClassGrid(wxGrid):
254    
255      def _OnCellResize(self, event):      def _OnCellResize(self, event):
256          self.FitInside()          self.FitInside()
257            event.Skip()
258    
259  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
260      """Represents the underlying data structure for the grid."""      """Represents the underlying data structure for the grid."""
261    
262      NUM_COLS = 3      __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]
   
     __col_labels = [_("Symbol"), _("Value"), _("Label")]  
263    
264    
265      def __init__(self, view = None):      def __init__(self, view = None):
     #def __init__(self, clazz, shapeType, view = None):  
266          """Constructor.          """Constructor.
267    
268          shapeType -- the type of shape that the layer uses          shapeType -- the type of shape that the layer uses
# Line 257  class ClassTable(wxPyGridTableBase): Line 272  class ClassTable(wxPyGridTableBase):
272    
273          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
274    
275          self.SetView(view)          assert len(ClassTable.__col_labels) == NUM_COLS
276    
277          self.clazz = None          self.clazz = None
278            self.__colAttr = {}
279    
280          #self.Reset(clazz, shapeType)          self.SetView(view)
281    
282      def Reset(self, clazz, shapeType, group = None):      def Reset(self, clazz, fieldType, shapeType, group = None):
283          """Reset the table with the given data.          """Reset the table with the given data.
284    
285          This is necessary because wxWindows does not allow a grid's          This is necessary because wxWindows does not allow a grid's
# Line 280  class ClassTable(wxPyGridTableBase): Line 297  class ClassTable(wxPyGridTableBase):
297    
298          self.GetView().BeginBatch()          self.GetView().BeginBatch()
299    
300          self.fieldType = clazz.GetFieldType()          self.fieldType = fieldType
301          self.shapeType = shapeType          self.shapeType = shapeType
302    
303          self.SetClassification(clazz, group)          self.SetClassification(clazz, group)
304          self.__Modified(-1)          self.__Modified(-1)
305    
306            self.__colAttr = {}
307    
308            attr = wxGridCellAttr()
309            attr.SetEditor(wxGridCellBoolEditor())
310            attr.SetRenderer(wxGridCellBoolRenderer())
311            attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER)
312            self.__colAttr[COL_VISIBLE] = attr
313    
314            attr = wxGridCellAttr()
315            attr.SetRenderer(ClassRenderer(self.shapeType))
316            attr.SetReadOnly()
317            self.__colAttr[COL_SYMBOL] = attr
318    
319          self.GetView().EndBatch()          self.GetView().EndBatch()
320          self.GetView().FitInside()          self.GetView().FitInside()
321    
322      def GetClassification(self):      def GetClassification(self):
323            """Return the current classification."""
324          return self.clazz          return self.clazz
325    
326      def SetClassification(self, clazz, group = None):      def SetClassification(self, clazz, group = None):
327            """Fill in the table with the given classification.
328            Select the given group if group is not None.
329            """
330    
331          self.GetView().BeginBatch()          self.GetView().BeginBatch()
332    
333          old_len = self.GetNumberRows()          old_len = self.GetNumberRows()
334    
         #  
         # copy the data out of the classification and into our  
         # array  
         #  
335          row = -1          row = -1
 #       for g in clazz:  
 #           ng = copy.deepcopy(g)  
 #           self.__SetRow(None, ng)  
 #           if g is group:  
 #               row = self.GetNumberRows() - 1  
 #               #print "selecting row..."  
   
   
         #self.clazz = copy.deepcopy(clazz)  
336          self.clazz = clazz          self.clazz = clazz
337    
338          self.__NotifyRowChanges(old_len, self.GetNumberRows())          self.__NotifyRowChanges(old_len, self.GetNumberRows())
339    
340            #
341            # XXX: this is dead code at the moment
342            #
343          if row > -1:          if row > -1:
344              self.GetView().ClearSelection()              self.GetView().ClearSelection()
345              self.GetView().SelectRow(row)              self.GetView().SelectRow(row)
# Line 327  class ClassTable(wxPyGridTableBase): Line 351  class ClassTable(wxPyGridTableBase):
351          self.GetView().FitInside()          self.GetView().FitInside()
352    
353      def __NotifyRowChanges(self, curRows, newRows):      def __NotifyRowChanges(self, curRows, newRows):
354            """Make sure table updates correctly if the number of
355            rows changes.
356            """
357          #          #
358          # silly message processing for updates to the number of          # silly message processing for updates to the number of
359          # rows and columns          # rows and columns
# Line 345  class ClassTable(wxPyGridTableBase): Line 372  class ClassTable(wxPyGridTableBase):
372              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
373              self.GetView().FitInside()              self.GetView().FitInside()
374    
   
375      def __SetRow(self, row, group):      def __SetRow(self, row, group):
376          """Set a row's data to that of the group.          """Set a row's data to that of the group.
377    
# Line 387  class ClassTable(wxPyGridTableBase): Line 413  class ClassTable(wxPyGridTableBase):
413              if isinstance(group, ClassGroupMap):       return _("Map")              if isinstance(group, ClassGroupMap):       return _("Map")
414    
415          assert False # shouldn't get here          assert False # shouldn't get here
416          return _("")          return ""
417    
418      def GetNumberRows(self):      def GetNumberRows(self):
419          """Return the number of rows."""          """Return the number of rows."""
# Line 398  class ClassTable(wxPyGridTableBase): Line 424  class ClassTable(wxPyGridTableBase):
424    
425      def GetNumberCols(self):      def GetNumberCols(self):
426          """Return the number of columns."""          """Return the number of columns."""
427          return self.NUM_COLS          return NUM_COLS
428    
429      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
430          """Determine if a cell is empty. This is always false."""          """Determine if a cell is empty. This is always false."""
# Line 416  class ClassTable(wxPyGridTableBase): Line 442  class ClassTable(wxPyGridTableBase):
442          """          """
443    
444          self.SetValueAsCustom(row, col, None, value)          self.SetValueAsCustom(row, col, None, value)
445          self.__Modified()  
         
446      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
447          """Return the object that is used to represent the given          """Return the object that is used to represent the given
448             cell coordinates. This may not be a string.             cell coordinates. This may not be a string.
449    
450          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
451          """          """
452    
# Line 431  class ClassTable(wxPyGridTableBase): Line 456  class ClassTable(wxPyGridTableBase):
456              group = self.clazz.GetGroup(row - 1)              group = self.clazz.GetGroup(row - 1)
457    
458    
459            if col == COL_VISIBLE:
460                return group.IsVisible()
461    
462          if col == COL_SYMBOL:          if col == COL_SYMBOL:
463              return group.GetProperties()              return group.GetProperties()
464    
# Line 445  class ClassTable(wxPyGridTableBase): Line 473  class ClassTable(wxPyGridTableBase):
473          elif isinstance(group, ClassGroupSingleton):          elif isinstance(group, ClassGroupSingleton):
474              return group.GetValue()              return group.GetValue()
475          elif isinstance(group, ClassGroupRange):          elif isinstance(group, ClassGroupRange):
476              return _("%s - %s") % (group.GetMin(), group.GetMax())              return group.GetRange()
477    
478          assert(False) # shouldn't get here          assert False # shouldn't get here
479          return None          return None
480    
481      def __ParseInput(self, value):      def __ParseInput(self, value):
482          """Try to determine what kind of input value is          """Try to determine what kind of input value is
483             (string, number, or range)             (string, number, or range)
484    
485          Returns a tuple of length one if there is a single          Returns a tuple (type, data) where type is 0 if data is
486          value, or of length two if it is a range.          a singleton value, or 1 if is a range
487          """          """
488    
489          type = self.fieldType          type = self.fieldType
490    
491          if type == FIELDTYPE_STRING:          if type == FIELDTYPE_STRING:
492              return (value,)              return (0, value)
493          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
   
494              if type == FIELDTYPE_INT:              if type == FIELDTYPE_INT:
495                    # the float call allows the user to enter 1.0 for 1
496                  conv = lambda p: int(float(p))                  conv = lambda p: int(float(p))
497              else:              else:
498                  conv = lambda p: p                  conv = float
499    
500              #              #
501              # first try to take the input as a single number              # first try to take the input as a single number
502              # if there's an exception try to break it into              # if there's an exception try to break it into
503              # a range seperated by a '-'. take care to ignore              # a range. if there is an exception here, let it
504              # 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.  
505              #              #
506              try:              try:
507                  return (conv(Str2Num(value)),)                  return (0, conv(value))
508              except ValueError:              except ValueError:
509                  i = value.find('-')                  return (1, Range(value))
                 if i == 0:  
                     i = value.find('-', 1)  
   
                 return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))  
510    
511          assert False  # shouldn't get here          assert False  # shouldn't get here
512          return (0,)          return (0,None)
               
513    
514      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
515          """Set the cell specified by 'row' and 'col' to 'value'.          """Set the cell specified by 'row' and 'col' to 'value'.
# Line 503  class ClassTable(wxPyGridTableBase): Line 523  class ClassTable(wxPyGridTableBase):
523          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
524          """          """
525    
526          assert col >= 0 and col < self.GetNumberCols()          assert 0 <= col < self.GetNumberCols()
527          assert row >= 0 and row < self.GetNumberRows()          assert 0 <= row < self.GetNumberRows()
528    
529          if row == 0:          if row == 0:
530              group = self.clazz.GetDefaultGroup()              group = self.clazz.GetDefaultGroup()
# Line 513  class ClassTable(wxPyGridTableBase): Line 533  class ClassTable(wxPyGridTableBase):
533    
534          mod = True # assume the data will change          mod = True # assume the data will change
535    
536          if col == COL_SYMBOL:          if col == COL_VISIBLE:
537                group.SetVisible(value)
538            elif col == COL_SYMBOL:
539              group.SetProperties(value)              group.SetProperties(value)
540          elif col == COL_LABEL:          elif col == COL_LABEL:
541              group.SetLabel(value)              group.SetLabel(value)
# Line 541  class ClassTable(wxPyGridTableBase): Line 563  class ClassTable(wxPyGridTableBase):
563                      # changing the underlying group type if the                      # changing the underlying group type if the
564                      # group was a singleton and a range was entered                      # group was a singleton and a range was entered
565                      #                      #
566                      if len(dataInfo) == 1:                      if dataInfo[0] == 0:
567                          if not isinstance(group, ClassGroupSingleton):                          if not isinstance(group, ClassGroupSingleton):
568                              ngroup = ClassGroupSingleton(prop = props)                              ngroup = ClassGroupSingleton(props = props)
569                              changed = True                              changed = True
570                          ngroup.SetValue(dataInfo[0])                          ngroup.SetValue(dataInfo[1])
571                      elif len(dataInfo) == 2:                      elif dataInfo[0] == 1:
572                          if not isinstance(group, ClassGroupRange):                          if not isinstance(group, ClassGroupRange):
573                              ngroup = ClassGroupRange(prop = props)                              ngroup = ClassGroupRange(props = props)
574                              changed = True                              changed = True
575                          ngroup.SetRange(dataInfo[0], dataInfo[1])                          ngroup.SetRange(dataInfo[1])
576                      else:                      else:
577                          assert False                          assert False
578                          pass                          pass
# Line 569  class ClassTable(wxPyGridTableBase): Line 591  class ClassTable(wxPyGridTableBase):
591      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
592          """Returns the cell attributes"""          """Returns the cell attributes"""
593    
594          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  
595    
596      def GetClassGroup(self, row):      def GetClassGroup(self, row):
597          """Return the ClassGroup object representing row 'row'."""          """Return the ClassGroup object representing row 'row'."""
# Line 590  class ClassTable(wxPyGridTableBase): Line 603  class ClassTable(wxPyGridTableBase):
603              return self.clazz.GetGroup(row - 1)              return self.clazz.GetGroup(row - 1)
604    
605      def SetClassGroup(self, row, group):      def SetClassGroup(self, row, group):
606            """Set the given row to properties of group."""
607          self.__SetRow(row, group)          self.__SetRow(row, group)
608          self.GetView().Refresh()          self.GetView().Refresh()
609    
# Line 644  class ClassTable(wxPyGridTableBase): Line 658  class ClassTable(wxPyGridTableBase):
658              self.__NotifyRowChanges(old_len, self.GetNumberRows())              self.__NotifyRowChanges(old_len, self.GetNumberRows())
659    
660    
661  ID_CLASSIFY_OK = 4001  ID_PROPERTY_REVERT = 4002
662  ID_CLASSIFY_CANCEL = 4002  ID_PROPERTY_ADD = 4003
663  ID_CLASSIFY_ADD = 4003  ID_PROPERTY_GENCLASS = 4004
664  ID_CLASSIFY_GENCLASS = 4004  ID_PROPERTY_REMOVE = 4005
665  ID_CLASSIFY_REMOVE = 4005  ID_PROPERTY_MOVEUP = 4006
666  ID_CLASSIFY_MOVEUP = 4006  ID_PROPERTY_MOVEDOWN = 4007
667  ID_CLASSIFY_MOVEDOWN = 4007  ID_PROPERTY_TRY = 4008
668  ID_CLASSIFY_APPLY = 4008  ID_PROPERTY_EDITSYM = 4009
669  ID_CLASSIFY_EDITPROPS = 4009  ID_PROPERTY_SELECT = 4011
670  ID_CLASSIFY_CLOSE = 4010  ID_PROPERTY_TITLE = 4012
671    ID_PROPERTY_FIELDTEXT = 4013
672    
673  BTN_ADD = 0  BTN_ADD = 0
674  BTN_EDIT = 1  BTN_EDIT = 1
# Line 662  BTN_UP = 3 Line 677  BTN_UP = 3
677  BTN_DOWN = 4  BTN_DOWN = 4
678  BTN_RM = 5  BTN_RM = 5
679    
680  class Classifier(NonModalDialog):  EB_LAYER_TITLE = 0
681    EB_SELECT_FIELD = 1
682    EB_GEN_CLASS = 2
683    
684    class Classifier(LayerProperties):
685    
686      type2string = {None:             _("None"),      type2string = {None:             _("None"),
687                     FIELDTYPE_STRING: _("Text"),                     FIELDTYPE_STRING: _("Text"),
# Line 670  class Classifier(NonModalDialog): Line 689  class Classifier(NonModalDialog):
689                     FIELDTYPE_DOUBLE: _("Decimal")}                     FIELDTYPE_DOUBLE: _("Decimal")}
690    
691      def __init__(self, parent, name, layer, group = None):      def __init__(self, parent, name, layer, group = None):
692          NonModalDialog.__init__(self, parent, name,          """Create a Properties/Classification dialog for a layer.
693                                  _("Classifier: %s") % layer.Title())          The layer is part of map. If group is not None, select that
694            group in the classification table.
695          panel = wxPanel(self, -1, size=(100, 100))          """
696    
697          self.layer = layer          LayerProperties.__init__(self, parent, name, layer)
698    
699          self.originalClass = self.layer.GetClassification()          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
700          field = self.originalClass.GetField()                               self.layer_shapestore_replaced)
         fieldType = self.originalClass.GetFieldType()  
701    
702          self.genDlg = None          self.genDlg = None
703            self.group = group
704    
705          topBox = wxBoxSizer(wxVERTICAL)          LayerProperties.dialog_layout(self)
         panelBox = wxBoxSizer(wxVERTICAL)  
   
         #panelBox.Add(wxStaticText(panel, -1, _("Layer: %s") % layer.Title()),  
             #0, wxALIGN_LEFT | wxALL, 4)  
         panelBox.Add(wxStaticText(panel, -1,  
                                 _("Layer Type: %s") % layer.ShapeType()),  
             0, wxALIGN_LEFT | wxALL, 4)  
   
   
         #  
         # make field combo box  
         #  
         self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",  
                                      style = wxCB_READONLY)  
   
         self.num_cols = layer.table.field_count()  
         # 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,  
                                           copy.deepcopy(self.originalClass))  
             else:  
                 self.fields.SetClientData(i + 1, None)  
   
   
         ###########  
   
         self.fieldTypeText = wxStaticText(panel, -1, "")  
         panelBox.Add(self.fieldTypeText, 0,  
                      wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)  
   
         propertyBox = wxBoxSizer(wxHORIZONTAL)  
         propertyBox.Add(wxStaticText(panel, -1, _("Field: ")),  
             0, wxALIGN_LEFT | wxALL, 4)  
         propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)  
         EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)  
   
         panelBox.Add(propertyBox, 0, wxGROW, 4)  
   
   
         #  
         # Control Box  
         #  
         controlBox = wxBoxSizer(wxHORIZONTAL)  
   
   
         ###########  
         #  
         # Control buttons:  
         #  
         self.controlButtons = []  
   
         controlButtonBox = wxBoxSizer(wxVERTICAL)  
   
         button = wxButton(panel, ID_CLASSIFY_ADD, _("Add"))  
         controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         self.controlButtons.append(button)  
   
         button = wxButton(panel, ID_CLASSIFY_EDITPROPS, _("Edit Properties"))  
         controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         self.controlButtons.append(button)  
   
         button = wxButton(panel, ID_CLASSIFY_GENCLASS, _("Generate Class"))  
         controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         self.controlButtons.append(button)  
   
         button = wxButton(panel, ID_CLASSIFY_MOVEUP, _("Move Up"))  
         controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         self.controlButtons.append(button)  
   
         button = wxButton(panel, ID_CLASSIFY_MOVEDOWN, _("Move Down"))  
         controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         self.controlButtons.append(button)  
   
         controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)  
   
         button = wxButton(panel, ID_CLASSIFY_REMOVE, _("Remove"))  
         controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)  
         self.controlButtons.append(button)  
   
706    
707          ###########      def dialog_layout(self, panel, panelBox):
         #  
         # Classification data table  
         #  
   
         self.classGrid = ClassGrid(panel, self)  
         #self.__SetGridTable(self.__cur_field, group)  
         #self.fields.SetSelection(self.__cur_field)  
   
         # calling __SelectField after creating the classGrid fills in the  
         # grid with the correct information  
         self.fields.SetSelection(self.__cur_field)  
         self.__SelectField(self.__cur_field, group = group)  
   
         #self.classGrid.SelectGroup(group)  
708    
709          controlBox.Add(self.classGrid, 1, wxGROW, 0)          if self.layer.HasClassification():
710    
711                self.fieldTypeText = wxStaticText(panel, -1, "")
712    
713                self.originalClass = self.layer.GetClassification()
714                self.originalClassField = self.layer.GetClassificationColumn()
715                field = self.originalClassField
716                fieldType = self.layer.GetFieldType(field)
717    
718          controlBox.Add(controlButtonBox, 0, wxGROW, 10)              table = self.layer.ShapeStore().Table()
719          panelBox.Add(controlBox, 1, wxGROW, 10)              #
720                # make field choice box
721                #
722                self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
723    
724          EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)              self.num_cols = table.NumColumns()
725          EVT_BUTTON(self, ID_CLASSIFY_EDITPROPS, self._OnEditGroupProperties)              # just assume the first field in case one hasn't been
726          EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)              # specified in the file.
727          EVT_BUTTON(self, ID_CLASSIFY_GENCLASS, self._OnGenClass)              self.__cur_field = 0
         EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)  
         EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)  
728    
729          ###########              self.fields.Append("<None>")
730    
731          buttonBox = wxBoxSizer(wxHORIZONTAL)              if fieldType is None:
732          buttonBox.Add(wxButton(panel, ID_CLASSIFY_OK, _("OK")),                  self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
733                        0, wxALL, 4)              else:
734          buttonBox.Add(60, 20, 0, wxALL, 4)                  self.fields.SetClientData(0, None)
         buttonBox.Add(wxButton(panel, ID_CLASSIFY_APPLY, _("Apply")),  
                       0, wxALL, 4)  
         buttonBox.Add(60, 20, 0, wxALL, 4)  
         buttonBox.Add(wxButton(panel, ID_CLASSIFY_CLOSE, _("Close")),  
                       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_CLOSE, self._OnCloseBtn)  
         EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self._OnCancel)  
   
         ###########  
   
   
         panel.SetAutoLayout(True)  
         panel.SetSizer(panelBox)  
         panelBox.SetSizeHints(panel)  
   
         topBox.Add(panel, 1, wxGROW, 0)  
         panelBox.SetSizeHints(self)  
         self.SetAutoLayout(True)  
         self.SetSizer(topBox)  
735    
736          #self.Fit()              for i in range(self.num_cols):
737          ######################                  name = table.Column(i).name
738                    self.fields.Append(name)
739    
740                    if name == field:
741                        self.__cur_field = i + 1
742                        self.fields.SetClientData(i + 1,
743                                                  copy.deepcopy(self.originalClass))
744                    else:
745                        self.fields.SetClientData(i + 1, None)
746    
747          self.haveApplied = False              button_gen = wxButton(panel, ID_PROPERTY_GENCLASS,
748                    _("Generate Class"))
749                button_add = wxButton(panel, ID_PROPERTY_ADD,
750                    _("Add"))
751                button_moveup = wxButton(panel, ID_PROPERTY_MOVEUP,
752                    _("Move Up"))
753                button_movedown = wxButton(panel, ID_PROPERTY_MOVEDOWN,
754                    _("Move Down"))
755                button_edit = wxButton(panel, ID_PROPERTY_EDITSYM,
756                    _("Edit Symbol"))
757                button_remove = wxButton(panel, ID_PROPERTY_REMOVE,
758                    _("Remove"))
759    
760                self.classGrid = ClassGrid(panel, self)
761    
762                # calling __SelectField after creating the classGrid fills in the
763                # grid with the correct information
764                self.fields.SetSelection(self.__cur_field)
765                self.__SelectField(self.__cur_field, group = self.group)
766    
767    
768                classBox = wxStaticBoxSizer(
769                            wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
770    
771    
772                sizer = wxBoxSizer(wxHORIZONTAL)
773                sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
774                    0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
775                sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
776    
777                classBox.Add(sizer, 0, wxGROW, 4)
778    
779                classBox.Add(self.fieldTypeText, 0,
780                            wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
781    
782                controlBox = wxBoxSizer(wxHORIZONTAL)
783                controlButtonBox = wxBoxSizer(wxVERTICAL)
784    
785                controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)
786                controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)
787                controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
788                controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
789                controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
790                controlButtonBox.Add( (60, 20), 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
791                controlButtonBox.Add(button_remove, 0,
792                                     wxGROW|wxALL|wxALIGN_BOTTOM, 4)
793    
794                controlBox.Add(self.classGrid, 1, wxGROW, 0)
795                controlBox.Add(controlButtonBox, 0, wxGROW, 10)
796    
797                classBox.Add(controlBox, 1, wxGROW, 10)
798                panelBox.Add(classBox, 1, wxGROW, 0)
799    
800    
801            EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
802            EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
803            EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
804            EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
805            EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
806            EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
807            EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
808    
809        def unsubscribe_messages(self):
810            """Unsubscribe from all messages."""
811            LayerProperties.unsubscribe_messages(self)
812            self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
813                                   self.layer_shapestore_replaced)
814    
815        def layer_shapestore_replaced(self, *args):
816            """Subscribed to the map's LAYER_SHAPESTORE_REPLACED message.
817            Close self.
818            """
819            self.Close()
820    
821      def EditGroupProperties(self, row):      def EditSymbol(self, row):
822            """Open up a dialog where the user can select the properties
823            for a group.
824            """
825          table = self.classGrid.GetTable()          table = self.classGrid.GetTable()
826          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
827    
828          # get a new ClassGroupProperties object and copy the          # get a new ClassGroupProperties object and copy the
829          # values over to our current object          # values over to our current object
830          propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())          propDlg = SelectPropertiesDialog(self, prop, self.layer.ShapeType())
831    
832          self.Enable(False)          self.Enable(False)
833          if propDlg.ShowModal() == wxID_OK:          if propDlg.ShowModal() == wxID_OK:
# Line 857  class Classifier(NonModalDialog): Line 835  class Classifier(NonModalDialog):
835              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
836          self.Enable(True)          self.Enable(True)
837          propDlg.Destroy()          propDlg.Destroy()
838            
839      def _SetClassification(self, clazz):      def _SetClassification(self, clazz):
840                    """Called from the ClassGen dialog when a new classification has
841            been created and should be set in the table.
842            """
843            # FIXME: This could be implemented using a message
844    
845          self.fields.SetClientData(self.__cur_field, clazz)          self.fields.SetClientData(self.__cur_field, clazz)
846          self.classGrid.GetTable().SetClassification(clazz)          self.classGrid.GetTable().SetClassification(clazz)
847    
848      def __BuildClassification(self, fieldIndex, copyClass = False):      def __BuildClassification(self, fieldIndex, copyClass=False, force=False):
849            """Pack the classification setting into a Classification object.
850            Returns (Classification, fieldName) where fieldName is the selected
851            field in the table that the classification should be used with.
852            """
853    
854  #       numRows = self.classGrid.GetNumberRows()  #       numRows = self.classGrid.GetNumberRows()
855  #       assert numRows > 0  # there should always be a default row  #       assert numRows > 0  # there should always be a default row
856    
 #       clazz = Classification()  
857          if fieldIndex == 0:          if fieldIndex == 0:
858              fieldName = None              fieldName = None
859              fieldType = None              fieldType = None
# Line 876  class Classifier(NonModalDialog): Line 861  class Classifier(NonModalDialog):
861              fieldName = self.fields.GetString(fieldIndex)              fieldName = self.fields.GetString(fieldIndex)
862              fieldType = self.layer.GetFieldType(fieldName)              fieldType = self.layer.GetFieldType(fieldName)
863    
864          clazz = self.classGrid.GetTable().GetClassification()          clazz = self.fields.GetClientData(fieldIndex)
865            if clazz is None or self.classGrid.GetTable().IsModified() or force:
866          if copyClass:              clazz = self.classGrid.GetTable().GetClassification()
867              clazz = copy.deepcopy(clazz)              if copyClass:
868                    clazz = copy.deepcopy(clazz)
         clazz.SetField(fieldName)  
         clazz.SetFieldType(fieldType)  
   
   
 #       table = self.classGrid.GetTable()  
 #       clazz.SetDefaultGroup(table.GetClassGroup(0))  
   
 #       for i in range(1, numRows):  
 #           clazz.AppendGroup(table.GetClassGroup(i))  
869    
870          return clazz          return clazz, fieldName
871    
872      def __SetGridTable(self, fieldIndex, group = None):      def __SetGridTable(self, fieldIndex, group = None):
873            """Set the table with the classification associated with the
874            selected field at fieldIndex. Select the specified group
875            if group is not None.
876            """
877    
878          clazz = self.fields.GetClientData(fieldIndex)          clazz = self.fields.GetClientData(fieldIndex)
879    
# Line 904  class Classifier(NonModalDialog): Line 884  class Classifier(NonModalDialog):
884                      self.layer.GetClassification().                      self.layer.GetClassification().
885                                 GetDefaultGroup().GetProperties()))                                 GetDefaultGroup().GetProperties()))
886    
887              fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
888              fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
             clazz.SetFieldType(fieldType)  
889                                    
890          self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)          self.classGrid.CreateTable(clazz, fieldType,
891                                       self.layer.ShapeType(), group)
892    
893      def __SetFieldTypeText(self, fieldIndex):      def __SetFieldTypeText(self, fieldIndex):
894            """Set the field type string using the data type of the field
895            at fieldIndex.
896            """
897          fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
898          fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
899    
# Line 918  class Classifier(NonModalDialog): Line 901  class Classifier(NonModalDialog):
901    
902          text = Classifier.type2string[fieldType]          text = Classifier.type2string[fieldType]
903    
904          self.fieldTypeText.SetLabel(_("Field Type: %s") % text)          self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
905    
906      def __SelectField(self, newIndex, oldIndex = -1, group = None):      def __SelectField(self, newIndex, oldIndex = -1, group = None):
907          """This method assumes that the current selection for the          """This method assumes that the current selection for the
# Line 928  class Classifier(NonModalDialog): Line 911  class Classifier(NonModalDialog):
911          assert oldIndex >= -1          assert oldIndex >= -1
912    
913          if oldIndex != -1:          if oldIndex != -1:
914              clazz = self.__BuildClassification(oldIndex)              clazz, name = self.__BuildClassification(oldIndex, force = True)
915              self.fields.SetClientData(oldIndex, clazz)              self.fields.SetClientData(oldIndex, clazz)
916    
917          self.__SetGridTable(newIndex, group)          self.__SetGridTable(newIndex, group)
918    
919          enabled = newIndex != 0          self.__EnableButtons(EB_SELECT_FIELD)
   
         for b in self.controlButtons:  
             b.Enable(enabled)  
920    
921          self.__SetFieldTypeText(newIndex)          self.__SetFieldTypeText(newIndex)
922    
923        def __SetTitle(self, title):
924            """Set the title of the dialog."""
925            if title != "":
926                title = ": " + title
927    
928            self.SetTitle(_("Layer Properties") + title)
929    
930      def _OnEditGroupProperties(self, event):      def _OnEditSymbol(self, event):
931            """Open up a dialog for the user to select group properties."""
932          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
933    
934          if len(sel) == 1:          if len(sel) == 1:
935              self.EditGroupProperties(sel[0])              self.EditSymbol(sel[0])
936    
937      def _OnFieldSelect(self, event):      def _OnFieldSelect(self, event):
938          index = self.fields.GetSelection()          index = self.fields.GetSelection()
939          self.__SelectField(index, self.__cur_field)          self.__SelectField(index, self.__cur_field)
940          self.__cur_field = index          self.__cur_field = index
941    
942      def _OnApply(self, event):      def OnTry(self, event):
943          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
944             it to the layer.             it to the layer.
945          """          """
946    
947          clazz = self.fields.GetClientData(self.__cur_field)          if self.layer.HasClassification():
948                clazz = self.fields.GetClientData(self.__cur_field)
949    
950          #              #
951          # only build the classification if there wasn't one to              # only build the classification if there wasn't one to
952          # to begin with or it has been modified              # to begin with or it has been modified
953          #              #
954          if clazz is None or self.classGrid.GetTable().IsModified():              self.classGrid.SaveEditControlValue()
955              clazz = self.__BuildClassification(self.__cur_field, True)              clazz, name = self.__BuildClassification(self.__cur_field, True)
956    
957          self.layer.SetClassification(clazz)              self.layer.SetClassificationColumn(name)
958                self.layer.SetClassification(clazz)
959    
960          self.haveApplied = True          self.haveApplied = True
961    
962      def _OnOK(self, event):      def OnOK(self, event):
963          self._OnApply(event)          self.OnTry(event)
         self.Close()  
   
     def _OnCloseBtn(self, event):  
         """Close is similar to Cancel except that any changes that were  
         made and applied remain applied, but the currently displayed  
         classification is discarded.  
         """  
   
964          self.Close()          self.Close()
965    
966      def _OnCancel(self, event):      def OnRevert(self, event):
967          """The layer's current classification stays the same."""          """The layer's current classification stays the same."""
968          if self.haveApplied:          if self.haveApplied and self.layer.HasClassification():
969                self.layer.SetClassificationColumn(self.originalClassField)
970              self.layer.SetClassification(self.originalClass)              self.layer.SetClassification(self.originalClass)
971    
972          self.Close()          #self.Close()
973    
974      def _OnAdd(self, event):      def _OnAdd(self, event):
975          self.classGrid.AppendRows()          self.classGrid.AppendRows()
# Line 996  class Classifier(NonModalDialog): Line 978  class Classifier(NonModalDialog):
978          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
979    
980      def _OnGenClass(self, event):      def _OnGenClass(self, event):
981            """Open up a dialog for the user to generate classifications."""
982    
         #if self.genDlg is None:  
983          self.genDlg = ClassGenDialog(self, self.layer,          self.genDlg = ClassGenDialog(self, self.layer,
984                            self.fields.GetString(self.__cur_field))                            self.fields.GetString(self.__cur_field))
985    
986          EVT_CLOSE(self.genDlg, self._OnGenDialogClose)          EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
987    
988          self.fields.Enable(False)          self.__EnableButtons(EB_GEN_CLASS)
         self.controlButtons[BTN_EDIT].Enable(False)  
         self.controlButtons[BTN_GEN].Enable(False)  
989    
990          self.genDlg.Show()          self.genDlg.Show()
         #if self.genDlg.ShowModal() == wxID_OK:  
         #    clazz = self.genDlg.GetClassification()  
         #    self.fields.SetClientData(self.__cur_field, clazz)  
         #    self.classGrid.GetTable().SetClassification(clazz)  
         #self.Enable(True)  
         #self.genDlg.Destroy()  
991    
992      def _OnGenDialogClose(self, event):      def _OnGenDialogClose(self, event):
993            """Reenable buttons after the generate classification
994            dialog is closed.
995            """
996          self.genDlg.Destroy()          self.genDlg.Destroy()
997            self.genDlg = None
998          self.fields.Enable(True)          self.__EnableButtons(EB_GEN_CLASS)
         self.controlButtons[BTN_EDIT].Enable(True)  
         self.controlButtons[BTN_GEN].Enable(True)  
999    
1000      def _OnMoveUp(self, event):      def _OnMoveUp(self, event):
1001            """When the user clicks MoveUp, try to move a group up one row."""
1002          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1003    
1004          if len(sel) == 1:          if len(sel) == 1:
# Line 1038  class Classifier(NonModalDialog): Line 1014  class Classifier(NonModalDialog):
1014                  self.classGrid.MakeCellVisible(i - 1, 0)                  self.classGrid.MakeCellVisible(i - 1, 0)
1015    
1016      def _OnMoveDown(self, event):      def _OnMoveDown(self, event):
1017            """When the user clicks MoveDown, try to move a group down one row."""
1018          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1019    
1020          if len(sel) == 1:          if len(sel) == 1:
# Line 1052  class Classifier(NonModalDialog): Line 1029  class Classifier(NonModalDialog):
1029                  self.classGrid.SelectRow(i + 1)                  self.classGrid.SelectRow(i + 1)
1030                  self.classGrid.MakeCellVisible(i + 1, 0)                  self.classGrid.MakeCellVisible(i + 1, 0)
1031    
1032        def _OnTitleChanged(self, event):
1033            """Update the dialog title when the user changed the layer name."""
1034            obj = event.GetEventObject()
1035    
1036            self.layer.SetTitle(obj.GetValue())
1037            self.__SetTitle(self.layer.Title())
1038    
1039            self.__EnableButtons(EB_LAYER_TITLE)
1040    
1041        def __EnableButtons(self, case):
1042            """Helper method that enables/disables the appropriate buttons
1043            based on the case provided. Cases are constants beginning with EB_.
1044            """
1045    
1046            list = {wxID_OK                 : True,
1047                    wxID_CANCEL             : True,
1048                    ID_PROPERTY_ADD         : True,
1049                    ID_PROPERTY_MOVEUP      : True,
1050                    ID_PROPERTY_MOVEDOWN    : True,
1051                    ID_PROPERTY_REMOVE      : True,
1052                    ID_PROPERTY_SELECT      : True,
1053                    ID_PROPERTY_FIELDTEXT   : True,
1054                    ID_PROPERTY_GENCLASS    : True,
1055                    ID_PROPERTY_EDITSYM     : True}
1056    
1057            if case == EB_LAYER_TITLE:  
1058                if self.layer.Title() == "":
1059                    list[wxID_OK] = False
1060                    list[wxID_CANCEL] = False
1061    
1062            elif case == EB_SELECT_FIELD:
1063                if self.fields.GetSelection() == 0:
1064                    list[ID_PROPERTY_GENCLASS] = False
1065                    list[ID_PROPERTY_ADD] = False
1066                    list[ID_PROPERTY_MOVEUP] = False
1067                    list[ID_PROPERTY_MOVEDOWN] = False
1068                    list[ID_PROPERTY_REMOVE] = False
1069    
1070            elif case == EB_GEN_CLASS:
1071                if self.genDlg is not None:
1072                    list[ID_PROPERTY_SELECT] = False
1073                    list[ID_PROPERTY_FIELDTEXT] = False
1074                    list[ID_PROPERTY_GENCLASS] = False
1075    
1076            for id, enable in list.items():
1077                win = self.FindWindowById(id)
1078                if win:
1079                    win.Enable(enable)
1080    
1081  ID_SELPROP_OK = 4001  ID_SELPROP_SPINCTRL_LINEWIDTH = 4002
 ID_SELPROP_CANCEL = 4002  
 ID_SELPROP_SPINCTRL = 4002  
1082  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1083  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
1084  ID_SELPROP_FILLCLR = 4005  ID_SELPROP_FILLCLR = 4005
1085  ID_SELPROP_STROKECLRTRANS = 4006  ID_SELPROP_STROKECLRTRANS = 4006
1086  ID_SELPROP_FILLCLRTRANS = 4007  ID_SELPROP_FILLCLRTRANS = 4007
1087    ID_SELPROP_SPINCTRL_SIZE = 4008
1088    
1089  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
1090        """Dialog that allows the user to select group properties."""
1091    
1092      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1093            """Open the dialog with the initial prop properties and shapeType."""
1094    
1095          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
1096                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1097    
# Line 1093  class SelectPropertiesDialog(wxDialog): Line 1120  class SelectPropertiesDialog(wxDialog):
1120          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wxBoxSizer(wxVERTICAL)
1121    
1122          lineColorBox = wxBoxSizer(wxHORIZONTAL)          lineColorBox = wxBoxSizer(wxHORIZONTAL)
1123          lineColorBox.Add(          button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1124              wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),          button.SetFocus()
1125              1, wxALL | wxGROW, 4)          lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1126          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1127    
1128          lineColorBox.Add(          lineColorBox.Add(
# Line 1121  class SelectPropertiesDialog(wxDialog): Line 1148  class SelectPropertiesDialog(wxDialog):
1148              ctrlBox.Add(fillColorBox, 0,              ctrlBox.Add(fillColorBox, 0,
1149                          wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)                          wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1150    
1151            # Line width selection
1152          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
1153          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1154                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1155          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl_linewidth = wxSpinCtrl(self,
1156                                     min=1, max=10,                                               ID_SELPROP_SPINCTRL_LINEWIDTH,
1157                                     value=str(prop.GetLineWidth()),                                               min=1, max=10,
1158                                     initial=prop.GetLineWidth())                                               value=str(prop.GetLineWidth()),
1159                                                 initial=prop.GetLineWidth())
1160    
1161          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL_LINEWIDTH,
1162                         self._OnSpinLineWidth)
         spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)  
1163    
1164            spinBox.Add(self.spinCtrl_linewidth, 0, wxALIGN_LEFT | wxALL, 4)
1165          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1166    
1167            # Size selection
1168            if shapeType == SHAPETYPE_POINT:
1169                spinBox = wxBoxSizer(wxHORIZONTAL)
1170                spinBox.Add(wxStaticText(self, -1, _("Size: ")),
1171                            0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1172                self.spinCtrl_size = wxSpinCtrl(self, ID_SELPROP_SPINCTRL_SIZE,
1173                                                min=1, max=100,
1174                                                value=str(prop.GetSize()),
1175                                                initial=prop.GetSize())
1176    
1177                EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL_SIZE, self._OnSpinSize)
1178    
1179                spinBox.Add(self.spinCtrl_size, 0, wxALIGN_LEFT | wxALL, 4)
1180                ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1181    
1182    
1183          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1184          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1185    
# Line 1141  class SelectPropertiesDialog(wxDialog): Line 1187  class SelectPropertiesDialog(wxDialog):
1187          # Control buttons:          # Control buttons:
1188          #          #
1189          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1190          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),          button_ok = wxButton(self, wxID_OK, _("OK"))
1191                        0, wxALL, 4)          buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1192          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1193                        0, wxALL, 4)                        0, wxRIGHT|wxEXPAND, 10)
1194          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1195                                                                                    
1196          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)          button_ok.SetDefault()
1197          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)  
1198                                                                                            #EVT_BUTTON(self, wxID_OK, self._OnOK)
1199            #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1200    
1201          self.SetAutoLayout(True)          self.SetAutoLayout(True)
1202          self.SetSizer(topBox)          self.SetSizer(topBox)
1203          topBox.Fit(self)          topBox.Fit(self)
1204          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1205    
1206      def _OnOK(self, event):      def OnOK(self, event):
1207          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1208    
1209      def _OnCancel(self, event):      def OnCancel(self, event):
1210          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1211    
1212      def _OnSpin(self, event):      def _OnSpinLineWidth(self, event):
1213          self.prop.SetLineWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl_linewidth.GetValue())
1214            self.previewWin.Refresh()
1215    
1216        def _OnSpinSize(self, event):
1217            self.prop.SetSize(self.spinCtrl_size.GetValue())
1218          self.previewWin.Refresh()          self.previewWin.Refresh()
1219    
1220      def __GetColor(self, cur):      def __GetColor(self, cur):
1221          dialog = wxColourDialog(self)          dialog = ColorDialog(self)
1222          if cur is not Color.Transparent:          dialog.SetColor(cur)
             dialog.GetColourData().SetColour(Color2wxColour(cur))  
1223    
1224          ret = None          ret = None
1225          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wxID_OK:
1226              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = dialog.GetColor()
1227    
1228          dialog.Destroy()          dialog.Destroy()
1229    
1230          return ret          return ret
1231            
1232      def _OnChangeLineColor(self, event):      def _OnChangeLineColor(self, event):
1233          clr = self.__GetColor(self.prop.GetLineColor())          clr = self.__GetColor(self.prop.GetLineColor())
1234          if clr is not None:          if clr is not None:
# Line 1185  class SelectPropertiesDialog(wxDialog): Line 1236  class SelectPropertiesDialog(wxDialog):
1236          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1237    
1238      def _OnChangeLineColorTrans(self, event):      def _OnChangeLineColorTrans(self, event):
1239          self.prop.SetLineColor(Color.Transparent)          self.prop.SetLineColor(Transparent)
1240          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1241            
1242      def _OnChangeFillColor(self, event):      def _OnChangeFillColor(self, event):
1243          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1244          if clr is not None:          if clr is not None:
# Line 1195  class SelectPropertiesDialog(wxDialog): Line 1246  class SelectPropertiesDialog(wxDialog):
1246          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1247    
1248      def _OnChangeFillColorTrans(self, event):      def _OnChangeFillColorTrans(self, event):
1249          self.prop.SetFill(Color.Transparent)          self.prop.SetFill(Transparent)
1250          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1251    
1252      def GetClassGroupProperties(self):      def GetClassGroupProperties(self):
# Line 1203  class SelectPropertiesDialog(wxDialog): Line 1254  class SelectPropertiesDialog(wxDialog):
1254    
1255    
1256  class ClassDataPreviewWindow(wxWindow):  class ClassDataPreviewWindow(wxWindow):
1257        """A custom window that draws group properties using the correct shape."""
1258    
1259      def __init__(self, rect, prop, shapeType,      def __init__(self, rect, prop, shapeType,
1260                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
1261            """Draws the appropriate shape as specified with shapeType using
1262            prop properities.
1263            """
1264          if parent is not None:          if parent is not None:
1265              wxWindow.__init__(self, parent, id, (0, 0), size)              wxWindow.__init__(self, parent, id, (0, 0), size)
1266              EVT_PAINT(self, self._OnPaint)              EVT_PAINT(self, self._OnPaint)
# Line 1234  class ClassDataPreviewWindow(wxWindow): Line 1289  class ClassDataPreviewWindow(wxWindow):
1289          self.previewer.Draw(dc, rect, self.prop, self.shapeType)          self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1290    
1291  class ClassDataPreviewer:  class ClassDataPreviewer:
1292        """Class that actually draws a group property preview."""
1293    
1294      def Draw(self, dc, rect, prop, shapeType):      def Draw(self, dc, rect, prop, shapeType):
1295            """Draw the property.
1296    
1297            returns: (w, h) as adapted extend if the drawing size
1298            exceeded the given rect. This can only be the case
1299            for point symbols. If the symbol fits the given rect,
1300            None is returned.
1301            """
1302    
1303          assert dc is not None          assert dc is not None
1304          assert isinstance(prop, ClassGroupProperties)          assert isinstance(prop, ClassGroupProperties)
# Line 1251  class ClassDataPreviewer: Line 1314  class ClassDataPreviewer:
1314              h = rect.GetHeight()              h = rect.GetHeight()
1315    
1316          stroke = prop.GetLineColor()          stroke = prop.GetLineColor()
1317          if stroke is Color.Transparent:          if stroke is Transparent:
1318              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1319          else:          else:
1320              pen = wxPen(Color2wxColour(stroke),              pen = wxPen(Color2wxColour(stroke),
# Line 1259  class ClassDataPreviewer: Line 1322  class ClassDataPreviewer:
1322                          wxSOLID)                          wxSOLID)
1323    
1324          stroke = prop.GetFill()          stroke = prop.GetFill()
1325          if stroke is Color.Transparent:          if stroke is Transparent:
1326              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1327          else:          else:
1328              brush = wxBrush(Color2wxColour(stroke), wxSOLID)              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
# Line 1275  class ClassDataPreviewer: Line 1338  class ClassDataPreviewer:
1338    
1339          elif shapeType == SHAPETYPE_POINT:          elif shapeType == SHAPETYPE_POINT:
1340    
1341              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2, prop.GetSize())
1342                            (min(w, h) - prop.GetLineWidth())/2)              circle_size =  prop.GetSize() * 2 + prop.GetLineWidth() * 2
1343                new_h = h
1344                new_w = w
1345                if h < circle_size: new_h = circle_size
1346                if w < circle_size: new_w = circle_size
1347                if new_h > h or new_w > w:
1348                    return (new_w, new_h)
1349    
1350          elif shapeType == SHAPETYPE_POLYGON:          elif shapeType == SHAPETYPE_POLYGON:
1351              dc.DrawRectangle(x, y, w, h)              dc.DrawRectangle(x, y, w, h)
1352    
1353            return None
1354    
1355  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1356        """A wrapper class that can be used to draw group properties in a
1357        grid table.
1358        """
1359    
1360      def __init__(self, shapeType):      def __init__(self, shapeType):
1361          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
# Line 1299  class ClassRenderer(wxPyGridCellRenderer Line 1373  class ClassRenderer(wxPyGridCellRenderer
1373                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1374    
1375          if not isinstance(data, ClassGroupMap):          if not isinstance(data, ClassGroupMap):
1376              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)              new_size = self.previewer.Draw(dc, rect, data.GetProperties(),
1377                                               self.shapeType)
1378                if new_size is not None:
1379                    (new_w, new_h) = new_size
1380                    grid.SetRowSize(row, new_h)
1381                    grid.SetColSize(col, new_h)
1382                    grid.ForceRefresh()
1383    
1384                    # now that we know the height, redraw everything
1385                    rect.SetHeight(new_h)
1386                    rect.SetWidth(new_w)
1387                    dc.DestroyClippingRegion()
1388                    dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1389                                         rect.GetWidth(), rect.GetHeight())
1390                    dc.SetPen(wxPen(wxLIGHT_GREY))
1391                    dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1392                    dc.DrawRectangle(rect.GetX(), rect.GetY(),
1393                                     rect.GetWidth(), rect.GetHeight())
1394                    self.previewer.Draw(dc, rect, data.GetProperties(),
1395                                        self.shapeType)
1396    
1397          if isSelected:          if isSelected:
1398              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
# Line 1311  class ClassRenderer(wxPyGridCellRenderer Line 1404  class ClassRenderer(wxPyGridCellRenderer
1404          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1405    
1406    
1407  class ClassGroupPropertiesCtrl(wxWindow, wxControl):  class ClassGroupPropertiesCtrl(wxControl):
1408        """A custom window and control that draw a preview of group properties
1409        and can open a dialog to modify the properties if the user double-clicks
1410        it.
1411        """
1412    
1413      def __init__(self, parent, id, props, shapeType,      def __init__(self, parent, id, props, shapeType,
1414                   size = wxDefaultSize, style = 0):                   size = wxDefaultSize, style = 0):
1415            wxControl.__init__(self, parent, id, size = size, style = style)
1416    
1417          wxWindow.__init__(self, parent, id, size = size, style = style)          self.parent = parent
1418    
1419          self.SetProperties(props)          self.SetProperties(props)
1420          self.SetShapeType(shapeType)          self.SetShapeType(shapeType)
# Line 1356  class ClassGroupPropertiesCtrl(wxWindow, Line 1454  class ClassGroupPropertiesCtrl(wxWindow,
1454          self.Refresh()          self.Refresh()
1455    
1456      def AllowEdit(self, allow):      def AllowEdit(self, allow):
1457            """Allow/Disallow double-clicking on the control."""
1458          self.allowEdit = allow          self.allowEdit = allow
1459    
1460      def DoEdit(self):      def DoEdit(self):
1461            """Open the properties selector dialog."""
1462    
1463          if not self.allowEdit: return          if not self.allowEdit: return
1464    
1465          propDlg = SelectPropertiesDialog(NULL,          propDlg = SelectPropertiesDialog(self.parent,
1466                                           self.GetProperties(),                                           self.GetProperties(),
1467                                           self.GetShapeType())                                           self.GetShapeType())
1468    
# Line 1374  class ClassGroupPropertiesCtrl(wxWindow, Line 1475  class ClassGroupPropertiesCtrl(wxWindow,
1475    
1476      def _OnLeftDClick(self, event):      def _OnLeftDClick(self, event):
1477          self.DoEdit()          self.DoEdit()
1478    

Legend:
Removed from v.630  
changed lines
  Added in v.2597

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26