/[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 513 by jonathan, Tue Mar 11 16:42:38 2003 UTC revision 2362 by joey, Fri Oct 1 18:00:42 2004 UTC
# Line 18  from wxPython.wx import * Line 18  from wxPython.wx import *
18  from wxPython.grid import *  from wxPython.grid import *
19    
20  from Thuban import _  from Thuban import _
21  from Thuban.common import *  from Thuban.UI.common import Color2wxColour, wxColour2Color
 from Thuban.UI.common import *  
22    
23  from Thuban.Model.classification import *  from Thuban.Model.messages import MAP_LAYERS_REMOVED, LAYER_SHAPESTORE_REPLACED
24    from Thuban.Model.range import Range
25    from Thuban.Model.classification import \
26        Classification, ClassGroupDefault, \
27        ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
28        ClassGroupProperties
29    
30  from Thuban.Model.color import Color  from Thuban.Model.color import Transparent
31    
32  from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.Model.layer import Layer, RasterLayer
33    from Thuban.Model.data import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
34    
35  from dialogs import NonModalDialog  from Thuban.UI.classgen import ClassGenDialog
36    from Thuban.UI.colordialog import ColorDialog
37    
38    from dialogs import NonModalNonParentDialog
39    from messages import MAP_REPLACED
40    
 # widget id's  
 ID_PROPERTY_SELECT = 4010  
41  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
42    
 ID_CLASSIFY_OK = 4001  
 ID_CLASSIFY_CANCEL = 4002  
 ID_CLASSIFY_ADD = 4003  
 ID_CLASSIFY_GENRANGE = 4004  
 ID_CLASSIFY_REMOVE = 4005  
 ID_CLASSIFY_MOVEUP = 4006  
 ID_CLASSIFY_MOVEDOWN = 4007  
 ID_CLASSIFY_APPLY = 4008  
43    
44  # table columns  # table columns
45  COL_SYMBOL = 0  COL_VISIBLE = 0
46  COL_VALUE  = 1  COL_SYMBOL  = 1
47  COL_LABEL  = 2  COL_VALUE   = 2
48    COL_LABEL   = 3
49    NUM_COLS    = 4
50    
51  # indices into the client data lists in Classifier.fields  # indices into the client data lists in Classifier.fields
52  FIELD_CLASS = 0  FIELD_CLASS = 0
# Line 59  FIELD_NAME = 2 Line 60  FIELD_NAME = 2
60  import weakref  import weakref
61  class ClassGrid(wxGrid):  class ClassGrid(wxGrid):
62    
63      def __init__(self, parent):  
64        def __init__(self, parent, classifier):
65          """Constructor.          """Constructor.
66    
67          parent -- the parent window          parent -- the parent window
# Line 68  class ClassGrid(wxGrid): Line 70  class ClassGrid(wxGrid):
70                   use for display.                   use for display.
71          """          """
72    
73          #wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))          wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)
74          wxGrid.__init__(self, parent, ID_CLASS_TABLE)  
75          #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), True)          self.classifier = classifier
76    
77            self.currentSelection = []
78    
79          EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)          EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
80          EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)          EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
81          EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)          EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
82            EVT_GRID_COL_SIZE(self, self._OnCellResize)
83            EVT_GRID_ROW_SIZE(self, self._OnCellResize)
84    
85          self.currentSelection = []      #def GetCellAttr(self, row, col):
86            #print "GetCellAttr ", row, col
87            #wxGrid.GetCellAttr(self, row, col)
88    
89      def CreateTable(self, clazz, shapeType):      def CreateTable(self, clazz, fieldType, shapeType, group = None):
90    
91          assert(isinstance(clazz, Classification))          assert isinstance(clazz, Classification)
92    
         self.shapeType = shapeType  
93          table = self.GetTable()          table = self.GetTable()
94          if table is None:          if table is None:
95              w = self.GetDefaultColSize() * 3 + self.GetDefaultRowLabelSize()              w = self.GetDefaultColSize() * NUM_COLS \
96              h = self.GetDefaultRowSize() * 4 + self.GetDefaultColLabelSize()                  + self.GetDefaultRowLabelSize()
97                h = self.GetDefaultRowSize() * 4 \
98                    + self.GetDefaultColLabelSize()
99    
100              self.SetDimensions(-1, -1, w, h)              self.SetDimensions(-1, -1, w, h)
101              self.SetSizeHints(w, h, -1, -1)              self.SetSizeHints(w, h, -1, -1)
102              self.SetTable(ClassTable(clazz, self.shapeType, self), True)              table = ClassTable(self)
103          else:              self.SetTable(table, True)
104              table.Reset(clazz, self.shapeType)  
105    
106          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(wxGrid.wxGridSelectRows)
107          self.ClearSelection()          self.ClearSelection()
108    
109            table.Reset(clazz, fieldType, shapeType, group)
110    
111      def GetCurrentSelection(self):      def GetCurrentSelection(self):
112          """Return the currently highlighted rows as an increasing list          """Return the currently highlighted rows as an increasing list
113             of row numbers."""             of row numbers."""
# Line 103  class ClassGrid(wxGrid): Line 115  class ClassGrid(wxGrid):
115          sel.sort()          sel.sort()
116          return sel          return sel
117    
118      def SetCellRenderer(self, row, col):      def GetSelectedRows(self):
119          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          return self.GetCurrentSelection()
120    
121        #def SetCellRenderer(self, row, col, renderer):
122            #raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
123    
124      #      #
125      # [Set|Get]Table is taken from http://wiki.wxpython.org      # [Set|Get]Table is taken from http://wiki.wxpython.org
# Line 169  class ClassGrid(wxGrid): Line 184  class ClassGrid(wxGrid):
184                  r = self.GetNumberRows() - 1                  r = self.GetNumberRows() - 1
185              self.SelectRow(r)              self.SelectRow(r)
186                    
187    
188        def SelectGroup(self, group, makeVisible = True):
189            if group is None: return
190    
191            assert isinstance(group, ClassGroup)
192    
193            table = self.GetTable()
194    
195            assert table is not None
196    
197            for i in range(table.GetNumberRows()):
198                g = table.GetClassGroup(i)
199                if g is group:
200                    self.SelectRow(i)
201                    if makeVisible:
202                        self.MakeCellVisible(i, 0)
203                    break
204    
205  #  #
206  # 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!
207  #  #
# Line 181  class ClassGrid(wxGrid): Line 214  class ClassGrid(wxGrid):
214  #                                  sel = False))  #                                  sel = False))
215    
216      def _OnCellDClick(self, event):      def _OnCellDClick(self, event):
217          """Handle a double on a cell."""          """Handle a double click on a cell."""
218    
219          r = event.GetRow()          r = event.GetRow()
220          c = event.GetCol()          c = event.GetCol()
         if c == COL_SYMBOL:  
             prop = self.GetTable().GetValueAsCustom(r, c, None)  
             #prop = group.GetProperties()  
221    
222              # get a new ClassGroupProperties object and copy the          if c == COL_SYMBOL:
223              # values over to our current object              self.classifier.EditSymbol(r)
224              propDlg = SelectPropertiesDialog(NULL, prop, self.shapeType)          else:
225              if propDlg.ShowModal() == wxID_OK:              event.Skip()
                 new_prop = propDlg.GetClassGroupProperties()  
                 #prop.SetProperties(new_prop)  
                 self.GetTable().SetValueAsCustom(r, c, None, new_prop)  
             propDlg.Destroy()  
226    
227      #      #
228      # _OnSelectedRange() and _OnSelectedCell() were borrowed      # _OnSelectedRange() and _OnSelectedCell() were borrowed
# Line 223  class ClassGrid(wxGrid): Line 249  class ClassGrid(wxGrid):
249          #self.ConfigureForSelection()          #self.ConfigureForSelection()
250          event.Skip()          event.Skip()
251    
252        def _OnCellResize(self, event):
253            self.FitInside()
254            event.Skip()
255    
256  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
257      """Represents the underlying data structure for the grid."""      """Represents the underlying data structure for the grid."""
258    
259      NUM_COLS = 3      __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]
260    
     __col_labels = [_("Symbol"), _("Value"), _("Label")]  
261    
262      def __init__(self, clazz, shapeType, view = None):      def __init__(self, view = None):
263          """Constructor.          """Constructor.
264    
265          shapeType -- the type of shape that the layer uses          shapeType -- the type of shape that the layer uses
# Line 240  class ClassTable(wxPyGridTableBase): Line 269  class ClassTable(wxPyGridTableBase):
269    
270          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
271    
272          self.SetView(view)          assert len(ClassTable.__col_labels) == NUM_COLS
         self.tdata = []  
273    
274          self.Reset(clazz, shapeType)          self.clazz = None
275            self.__colAttr = {}
276    
277      def Reset(self, clazz, shapeType):          self.SetView(view)
278    
279        def Reset(self, clazz, fieldType, shapeType, group = None):
280          """Reset the table with the given data.          """Reset the table with the given data.
281    
282          This is necessary because wxWindows does not allow a grid's          This is necessary because wxWindows does not allow a grid's
# Line 259  class ClassTable(wxPyGridTableBase): Line 290  class ClassTable(wxPyGridTableBase):
290          shapeType -- the type of shape that the layer uses          shapeType -- the type of shape that the layer uses
291          """          """
292    
293          assert(isinstance(clazz, Classification))          assert isinstance(clazz, Classification)
294    
295          self.GetView().BeginBatch()          self.GetView().BeginBatch()
296    
297          self.fieldType = clazz.GetFieldType()          self.fieldType = fieldType
298          self.shapeType = shapeType          self.shapeType = shapeType
299    
300          old_len = len(self.tdata)          self.SetClassification(clazz, group)
301            self.__Modified(-1)
302    
303            self.__colAttr = {}
304    
305          self.tdata = []          attr = wxGridCellAttr()
306            attr.SetEditor(wxGridCellBoolEditor())
307            attr.SetRenderer(wxGridCellBoolRenderer())
308            attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER)
309            self.__colAttr[COL_VISIBLE] = attr
310    
311          #          attr = wxGridCellAttr()
312          # copy the data out of the classification and into our          attr.SetRenderer(ClassRenderer(self.shapeType))
313          # array          attr.SetReadOnly()
314          #          self.__colAttr[COL_SYMBOL] = attr
315          for p in clazz:  
316              np = copy.deepcopy(p)          self.GetView().EndBatch()
317              self.__SetRow(-1, np)          self.GetView().FitInside()
318    
319        def GetClassification(self):
320            """Return the current classification."""
321            return self.clazz
322    
323        def SetClassification(self, clazz, group = None):
324            """Fill in the table with the given classification.
325            Select the given group if group is not None.
326            """
327    
328            self.GetView().BeginBatch()
329    
330            old_len = self.GetNumberRows()
331    
332            row = -1
333            self.clazz = clazz
334    
335          self.__Modified(-1)          self.__NotifyRowChanges(old_len, self.GetNumberRows())
336    
337          self.__NotifyRowChanges(old_len, len(self.tdata))          #
338            # XXX: this is dead code at the moment
339            #
340            if row > -1:
341                self.GetView().ClearSelection()
342                self.GetView().SelectRow(row)
343                self.GetView().MakeCellVisible(row, 0)
344    
345            self.__Modified()
346    
               
347          self.GetView().EndBatch()          self.GetView().EndBatch()
348            self.GetView().FitInside()
349    
350      def __NotifyRowChanges(self, curRows, newRows):      def __NotifyRowChanges(self, curRows, newRows):
351            """Make sure table updates correctly if the number of
352            rows changes.
353            """
354          #          #
355          # silly message processing for updates to the number of          # silly message processing for updates to the number of
356          # rows and columns          # rows and columns
# Line 296  class ClassTable(wxPyGridTableBase): Line 360  class ClassTable(wxPyGridTableBase):
360                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
361                          newRows - curRows)    # how many                          newRows - curRows)    # how many
362              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
363              #self.GetView().FitInside() # XXX: only with wxWindows2.4              self.GetView().FitInside()
364          elif newRows < curRows:          elif newRows < curRows:
365              msg = wxGridTableMessage(self,              msg = wxGridTableMessage(self,
366                          wxGRIDTABLE_NOTIFY_ROWS_DELETED,                          wxGRIDTABLE_NOTIFY_ROWS_DELETED,
367                          curRows - newRows,    # position                          curRows,              # position
368                          curRows - newRows)    # how many                          curRows - newRows)    # how many
369              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
370              #self.GetView().FitInside() # XXX: only with wxWindows2.4              self.GetView().FitInside()
371    
372      def __SetRow(self, row, group):      def __SetRow(self, row, group):
373          """Set a row's data to that of the group.          """Set a row's data to that of the group.
374    
375          The table is considered modified after this operation.          The table is considered modified after this operation.
376    
377          row -- if row is -1 or greater than the current number of rows          row -- if row is < 0 'group' is inserted at the top of the table
378                 then group is appended to the end.                 if row is >= GetNumberRows() or None 'group' is append to
379                        the end of the table.
380                   otherwise 'group' replaces row 'row'
381          """          """
382    
383          # either append or replace          # either append or replace
384          if row == -1 or row >= self.GetNumberRows():          if row is None or row >= self.GetNumberRows():
385              self.tdata.append(group)              self.clazz.AppendGroup(group)
386            elif row < 0:
387                self.clazz.InsertGroup(0, group)
388          else:          else:
389              self.tdata[row] = group              if row == 0:
390                    self.clazz.SetDefaultGroup(group)
391                else:
392                    self.clazz.ReplaceGroup(row - 1, group)
393    
394          self.__Modified()          self.__Modified()
395    
# Line 329  class ClassTable(wxPyGridTableBase): Line 400  class ClassTable(wxPyGridTableBase):
400      def GetRowLabelValue(self, row):      def GetRowLabelValue(self, row):
401          """Return the label for the given row."""          """Return the label for the given row."""
402    
403          group = self.tdata[row]          if row == 0:
404          if isinstance(group, ClassGroupDefault):   return _("Default")              return _("Default")
405          if isinstance(group, ClassGroupSingleton): return _("Singleton")          else:
406          if isinstance(group, ClassGroupRange):     return _("Range")              group = self.clazz.GetGroup(row - 1)
407          if isinstance(group, ClassGroupMap):       return _("Map")              if isinstance(group, ClassGroupDefault):   return _("Default")
408                if isinstance(group, ClassGroupSingleton): return _("Singleton")
409                if isinstance(group, ClassGroupRange):     return _("Range")
410                if isinstance(group, ClassGroupMap):       return _("Map")
411    
412          assert(False) # shouldn't get here          assert False # shouldn't get here
413          return _("")          return ""
414    
415      def GetNumberRows(self):      def GetNumberRows(self):
416          """Return the number of rows."""          """Return the number of rows."""
417          return len(self.tdata)          if self.clazz is None:
418                return 0
419    
420            return self.clazz.GetNumGroups() + 1 # +1 for default group
421    
422      def GetNumberCols(self):      def GetNumberCols(self):
423          """Return the number of columns."""          """Return the number of columns."""
424          return self.NUM_COLS          return NUM_COLS
425    
426      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
427          """Determine if a cell is empty. This is always false."""          """Determine if a cell is empty. This is always false."""
# Line 362  class ClassTable(wxPyGridTableBase): Line 439  class ClassTable(wxPyGridTableBase):
439          """          """
440    
441          self.SetValueAsCustom(row, col, None, value)          self.SetValueAsCustom(row, col, None, value)
         self.__Modified()  
442                
443      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
444          """Return the object that is used to represent the given          """Return the object that is used to represent the given
# Line 371  class ClassTable(wxPyGridTableBase): Line 447  class ClassTable(wxPyGridTableBase):
447          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
448          """          """
449    
450          group = self.tdata[row]          if row == 0:
451                group = self.clazz.GetDefaultGroup()
452            else:
453                group = self.clazz.GetGroup(row - 1)
454    
455    
456            if col == COL_VISIBLE:
457                return group.IsVisible()
458    
459          if col == COL_SYMBOL:          if col == COL_SYMBOL:
460              return group.GetProperties()              return group.GetProperties()
# Line 380  class ClassTable(wxPyGridTableBase): Line 463  class ClassTable(wxPyGridTableBase):
463              return group.GetLabel()              return group.GetLabel()
464    
465          # col must be COL_VALUE          # col must be COL_VALUE
466          assert(col == COL_VALUE)          assert col == COL_VALUE
467    
468          if isinstance(group, ClassGroupDefault):          if isinstance(group, ClassGroupDefault):
469              return _("DEFAULT")              return _("DEFAULT")
470          elif isinstance(group, ClassGroupSingleton):          elif isinstance(group, ClassGroupSingleton):
471              return group.GetValue()              return group.GetValue()
472          elif isinstance(group, ClassGroupRange):          elif isinstance(group, ClassGroupRange):
473              return _("%s - %s") % (group.GetMin(), group.GetMax())              return group.GetRange()
474    
475          assert(False) # shouldn't get here          assert False # shouldn't get here
476          return None          return None
477    
478      def __ParseInput(self, value):      def __ParseInput(self, value):
479          """Try to determine what kind of input value is          """Try to determine what kind of input value is
480             (string, number, or range)             (string, number, or range)
481    
482          Returns a tuple of length one if there is a single          Returns a tuple (type, data) where type is 0 if data is
483          value, or of length two if it is a range.          a singleton value, or 1 if is a range
484          """          """
485    
486          type = self.fieldType          type = self.fieldType
487    
488          if type == FIELDTYPE_STRING:          if type == FIELDTYPE_STRING:
489              return (value,)              return (0, value)
490          elif type == FIELDTYPE_INT or type == FIELDTYPE_DOUBLE:          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
   
491              if type == FIELDTYPE_INT:              if type == FIELDTYPE_INT:
492                    # the float call allows the user to enter 1.0 for 1
493                  conv = lambda p: int(float(p))                  conv = lambda p: int(float(p))
494              else:              else:
495                  conv = lambda p: p                  conv = float
496    
497              #              #
498              # first try to take the input as a single number              # first try to take the input as a single number
499              # if there's an exception try to break it into              # if there's an exception try to break it into
500              # a range seperated by a '-'. take care to ignore              # a range. if there is an exception here, let it
501              # 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.  
502              #              #
503              try:              try:
504                  return (conv(Str2Num(value)),)                  return (0, conv(value))
505              except ValueError:              except ValueError:
506                  i = value.find('-')                  return (1, Range(value))
507                  if i == 0:  
508                      i = value.find('-', 1)          assert False  # shouldn't get here
509            return (0,None)
                 return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))  
   
         assert(False) # shouldn't get here  
         return (0,)  
               
510    
511      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
512          """Set the cell specified by 'row' and 'col' to 'value'.          """Set the cell specified by 'row' and 'col' to 'value'.
# Line 445  class ClassTable(wxPyGridTableBase): Line 520  class ClassTable(wxPyGridTableBase):
520          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
521          """          """
522    
523          assert(col >= 0 and col < self.GetNumberCols())          assert 0 <= col < self.GetNumberCols()
524          assert(row >= 0 and row < self.GetNumberRows())          assert 0 <= row < self.GetNumberRows()
525    
526          group = self.tdata[row]          if row == 0:
527                group = self.clazz.GetDefaultGroup()
528            else:
529                group = self.clazz.GetGroup(row - 1)
530    
531          mod = True # assume the data will change          mod = True # assume the data will change
532    
533          if col == COL_SYMBOL:          if col == COL_VISIBLE:
534                group.SetVisible(value)
535            elif col == COL_SYMBOL:
536              group.SetProperties(value)              group.SetProperties(value)
537          elif col == COL_LABEL:          elif col == COL_LABEL:
538              group.SetLabel(value)              group.SetLabel(value)
# Line 480  class ClassTable(wxPyGridTableBase): Line 560  class ClassTable(wxPyGridTableBase):
560                      # changing the underlying group type if the                      # changing the underlying group type if the
561                      # group was a singleton and a range was entered                      # group was a singleton and a range was entered
562                      #                      #
563                      if len(dataInfo) == 1:                      if dataInfo[0] == 0:
564                          if not isinstance(group, ClassGroupSingleton):                          if not isinstance(group, ClassGroupSingleton):
565                              ngroup = ClassGroupSingleton(prop = props)                              ngroup = ClassGroupSingleton(props = props)
566                              changed = True                              changed = True
567                          ngroup.SetValue(dataInfo[0])                          ngroup.SetValue(dataInfo[1])
568                      elif len(dataInfo) == 2:                      elif dataInfo[0] == 1:
569                          if not isinstance(group, ClassGroupRange):                          if not isinstance(group, ClassGroupRange):
570                              ngroup = ClassGroupRange(prop = props)                              ngroup = ClassGroupRange(props = props)
571                              changed = True                              changed = True
572                          ngroup.SetRange(dataInfo[0], dataInfo[1])                          ngroup.SetRange(dataInfo[1])
573                      else:                      else:
574                          assert(False)                          assert False
575                          pass                          pass
576    
577                      if changed:                      if changed:
578                          ngroup.SetLabel(group.GetLabel())                          ngroup.SetLabel(group.GetLabel())
579                          self.SetClassGroup(row, ngroup)                          self.SetClassGroup(row, ngroup)
580          else:          else:
581              assert(False) # shouldn't be here              assert False # shouldn't be here
582              pass              pass
583    
584          if mod:          if mod:
# Line 508  class ClassTable(wxPyGridTableBase): Line 588  class ClassTable(wxPyGridTableBase):
588      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
589          """Returns the cell attributes"""          """Returns the cell attributes"""
590    
591          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  
592    
593      def GetClassGroup(self, row):      def GetClassGroup(self, row):
594          """Return the ClassGroup object representing row 'row'."""          """Return the ClassGroup object representing row 'row'."""
595    
596          return self.tdata[row] # self.GetValueAsCustom(row, COL_SYMBOL, None)          #return self.GetValueAsCustom(row, COL_SYMBOL, None)
597            if row == 0:
598                return self.clazz.GetDefaultGroup()
599            else:
600                return self.clazz.GetGroup(row - 1)
601    
602      def SetClassGroup(self, row, group):      def SetClassGroup(self, row, group):
603            """Set the given row to properties of group."""
604          self.__SetRow(row, group)          self.__SetRow(row, group)
605          self.GetView().Refresh()          self.GetView().Refresh()
606    
# Line 553  class ClassTable(wxPyGridTableBase): Line 629  class ClassTable(wxPyGridTableBase):
629          The table is considered modified if any rows are removed.          The table is considered modified if any rows are removed.
630          """          """
631    
632          assert(pos >= 0)          assert pos >= 0
633          old_len = len(self.tdata)          old_len = self.GetNumberRows()
634          for row in range(pos, pos - numRows, -1):          for row in range(pos, pos - numRows, -1):
635              group = self.GetClassGroup(row)              group = self.GetClassGroup(row)
636              if not isinstance(group, ClassGroupDefault):              if row != 0:
637                  self.tdata.pop(row)                  self.clazz.RemoveGroup(row - 1)
638                  self.__Modified()                  self.__Modified()
639            
640          if self.IsModified():          if self.IsModified():
641              self.__NotifyRowChanges(old_len, len(self.tdata))              self.__NotifyRowChanges(old_len, self.GetNumberRows())
642    
643      def AppendRows(self, numRows = 1):      def AppendRows(self, numRows = 1):
644          """Append 'numRows' empty rows to the end of the table.          """Append 'numRows' empty rows to the end of the table.
# Line 570  class ClassTable(wxPyGridTableBase): Line 646  class ClassTable(wxPyGridTableBase):
646          The table is considered modified if any rows are appended.          The table is considered modified if any rows are appended.
647          """          """
648    
649          old_len = len(self.tdata)          old_len = self.GetNumberRows()
650          for i in range(numRows):          for i in range(numRows):
651              np = ClassGroupSingleton()              np = ClassGroupSingleton()
652              self.__SetRow(-1, np)              self.__SetRow(None, np)
653    
654          if self.IsModified():          if self.IsModified():
655              self.__NotifyRowChanges(old_len, len(self.tdata))              self.__NotifyRowChanges(old_len, self.GetNumberRows())
656    
657    
658  class Classifier(NonModalDialog):  ID_PROPERTY_REVERT = 4002
659        ID_PROPERTY_ADD = 4003
660      def __init__(self, parent, interactor, name, layer):  ID_PROPERTY_GENCLASS = 4004
661          NonModalDialog.__init__(self, parent, interactor, name,  ID_PROPERTY_REMOVE = 4005
662                                  _("Classifier: %s") % layer.Title())  ID_PROPERTY_MOVEUP = 4006
663    ID_PROPERTY_MOVEDOWN = 4007
664    ID_PROPERTY_TRY = 4008
665    ID_PROPERTY_EDITSYM = 4009
666    ID_PROPERTY_SELECT = 4011
667    ID_PROPERTY_TITLE = 4012
668    ID_PROPERTY_FIELDTEXT = 4013
669    
670    BTN_ADD = 0
671    BTN_EDIT = 1
672    BTN_GEN = 2
673    BTN_UP = 3
674    BTN_DOWN = 4
675    BTN_RM = 5
676    
677    EB_LAYER_TITLE = 0
678    EB_SELECT_FIELD = 1
679    EB_GEN_CLASS = 2
680    
681          panel = wxPanel(self, -1, size=(100, 100))  class Classifier(NonModalNonParentDialog):
682    
683          self.layer = layer      type2string = {None:             _("None"),
684                       FIELDTYPE_STRING: _("Text"),
685                       FIELDTYPE_INT:    _("Integer"),
686                       FIELDTYPE_DOUBLE: _("Decimal")}
687    
688          self.originalClass = self.layer.GetClassification()      def __init__(self, parent, name, layer, group = None):
689          field = self.originalClass.GetField()          """Create a Properties/Classification dialog for a layer.
690          fieldType = self.originalClass.GetFieldType()          The layer is part of map. If group is not None, select that
691            group in the classification table.
692            """
693    
694          topBox = wxBoxSizer(wxVERTICAL)          NonModalNonParentDialog.__init__(self, parent, name, "")
         panelBox = wxBoxSizer(wxVERTICAL)  
695    
696          #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)  
697    
698            self.parent.Subscribe(MAP_REPLACED, self.map_replaced)
699            self.layer = layer
700            self.map = parent.Map()
701    
702          #          self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
703          # make field combo box          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
704          #                               self.layer_shapestore_replaced)
         self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",  
                                      style = wxCB_READONLY)  
705    
706          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)  
707    
708            ############################
709            # Create the controls
710            #
711    
712          ###########          panel = wxPanel(self, -1)
713    
714            text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())
715          self.fieldTypeText = wxStaticText(panel, -1, "")          self.fieldTypeText = wxStaticText(panel, -1, "")
         panelBox.Add(self.fieldTypeText, 0, wxGROW | wxALIGN_LEFT | wxALL, 4)  
716    
717          propertyBox = wxBoxSizer(wxHORIZONTAL)          if layer.HasClassification():
718          propertyBox.Add(wxStaticText(panel, -1, _("Field: ")),              self.originalClass = self.layer.GetClassification()
719              0, wxALIGN_LEFT | wxALL, 4)              self.originalClassField = self.layer.GetClassificationColumn()
720          propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)              field = self.originalClassField
721          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)              fieldType = self.layer.GetFieldType(field)
722    
723          panelBox.Add(propertyBox, 0, wxGROW, 4)              table = layer.ShapeStore().Table()
724                #
725                # make field choice box
726                #
727                self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)
728    
729          ###########              self.num_cols = table.NumColumns()
730          #              # just assume the first field in case one hasn't been
731          # Classification data table              # specified in the file.
732          #              self.__cur_field = 0
733    
734          controlBox = wxBoxSizer(wxHORIZONTAL)              self.fields.Append("<None>")
735    
736          self.classGrid = ClassGrid(panel)              if fieldType is None:
737          self.__SetGridTable(self.__cur_field)                  self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
738                else:
739                    self.fields.SetClientData(0, None)
740    
741          controlBox.Add(self.classGrid, 1, wxGROW, 0)              for i in range(self.num_cols):
742                    name = table.Column(i).name
743                    self.fields.Append(name)
744    
745                    if name == field:
746                        self.__cur_field = i + 1
747                        self.fields.SetClientData(i + 1,
748                                                  copy.deepcopy(self.originalClass))
749                    else:
750                        self.fields.SetClientData(i + 1, None)
751    
752          ###########              button_gen = wxButton(panel, ID_PROPERTY_GENCLASS,
753          #                  _("Generate Class"))
754          # Control buttons:              button_add = wxButton(panel, ID_PROPERTY_ADD,
755                    _("Add"))
756                button_moveup = wxButton(panel, ID_PROPERTY_MOVEUP,
757                    _("Move Up"))
758                button_movedown = wxButton(panel, ID_PROPERTY_MOVEDOWN,
759                    _("Move Down"))
760                button_edit = wxButton(panel, ID_PROPERTY_EDITSYM,
761                    _("Edit Symbol"))
762                button_remove = wxButton(panel, ID_PROPERTY_REMOVE,
763                    _("Remove"))
764    
765                self.classGrid = ClassGrid(panel, self)
766    
767                # calling __SelectField after creating the classGrid fills in the
768                # grid with the correct information
769                self.fields.SetSelection(self.__cur_field)
770                self.__SelectField(self.__cur_field, group = group)
771    
772            button_try = wxButton(self, ID_PROPERTY_TRY, _("Try"))
773            button_revert = wxButton(self, ID_PROPERTY_REVERT, _("Revert"))
774            button_ok = wxButton(self, wxID_OK, _("OK"))
775            button_close = wxButton(self, wxID_CANCEL, _("Close"))
776            button_ok.SetDefault()
777    
778            ############################
779            # Layout the controls
780          #          #
         self.controlButtons = []  
781    
782          controlButtonBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
783            panelBox = wxBoxSizer(wxVERTICAL)
784    
785          button = wxButton(panel, ID_CLASSIFY_ADD, _("Add"))          sizer = wxBoxSizer(wxHORIZONTAL)
786          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)          sizer.Add(wxStaticText(panel, -1, _("Title: ")),
787          self.controlButtons.append(button)              0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
788            sizer.Add(text_title, 1, wxGROW, 0)
789    
790          #button = wxButton(panel, ID_CLASSIFY_GENRANGE, _("Generate Ranges"))          panelBox.Add(sizer, 0, wxGROW, 4)
         #controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)  
         #self.controlButtons.append(button)  
791    
792          button = wxButton(panel, ID_CLASSIFY_MOVEUP, _("Move Up"))          if isinstance(layer, RasterLayer):
793          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)              type = "Image"
794          self.controlButtons.append(button)          else:
795                type = layer.ShapeType()
796    
797          button = wxButton(panel, ID_CLASSIFY_MOVEDOWN, _("Move Down"))          panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),
798          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)              0, wxALIGN_LEFT | wxALL, 4)
         self.controlButtons.append(button)  
799    
800          controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)          if layer.HasClassification():
801    
802          button = wxButton(panel, ID_CLASSIFY_REMOVE, _("Remove"))              classBox = wxStaticBoxSizer(
803          controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)                          wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
         self.controlButtons.append(button)  
804    
         controlBox.Add(controlButtonBox, 0, wxGROW, 10)  
         panelBox.Add(controlBox, 1, wxGROW, 10)  
805    
806          EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)              sizer = wxBoxSizer(wxHORIZONTAL)
807          EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)              sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
808          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self._OnGenRange)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
809          EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)              sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
         EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)  
810    
811          ###########              classBox.Add(sizer, 0, wxGROW, 4)
812    
813          buttonBox = wxBoxSizer(wxHORIZONTAL)              classBox.Add(self.fieldTypeText, 0,
814          buttonBox.Add(wxButton(panel, ID_CLASSIFY_OK, _("OK")),                          wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
                       0, wxALL, 4)  
         buttonBox.Add(60, 20, 0, wxALL, 4)  
         buttonBox.Add(wxButton(panel, ID_CLASSIFY_APPLY, _("Apply")),  
                       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)  
815    
816          ###########              controlBox = wxBoxSizer(wxHORIZONTAL)
817                controlButtonBox = wxBoxSizer(wxVERTICAL)
818    
819                controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)
820                controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)
821                controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)
822                controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)
823                controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)
824                controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)
825                controlButtonBox.Add(button_remove, 0,
826                                     wxGROW|wxALL|wxALIGN_BOTTOM, 4)
827    
828                controlBox.Add(self.classGrid, 1, wxGROW, 0)
829                controlBox.Add(controlButtonBox, 0, wxGROW, 10)
830    
831          self.fields.SetSelection(self.__cur_field)              classBox.Add(controlBox, 1, wxGROW, 10)
832          self.__SelectField(self.__cur_field)              panelBox.Add(classBox, 1, wxGROW, 0)
833    
834    
835            buttonBox = wxBoxSizer(wxHORIZONTAL)
836            buttonBox.Add(button_try, 0, wxRIGHT|wxEXPAND, 10)
837            buttonBox.Add(button_revert, 0, wxRIGHT|wxEXPAND, 10)
838            buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
839            buttonBox.Add(button_close, 0, wxRIGHT|wxEXPAND, 10)
840    
841          panel.SetAutoLayout(True)          panel.SetAutoLayout(True)
842          panel.SetSizer(panelBox)          panel.SetSizer(panelBox)
843            panelBox.Fit(panel)
844          panelBox.SetSizeHints(panel)          panelBox.SetSizeHints(panel)
845    
846          topBox.Add(panel, 1, wxGROW, 0)          topBox.Add(panel, 1, wxGROW | wxALL, 4)
847          panelBox.SetSizeHints(self)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
848    
849          self.SetAutoLayout(True)          self.SetAutoLayout(True)
850          self.SetSizer(topBox)          self.SetSizer(topBox)
851            topBox.Fit(self)
852            topBox.SetSizeHints(self)
853            self.Layout()
854    
855      def __BuildClassification(self, fieldIndex):          ###########
856    
857            EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
858            EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
859            EVT_BUTTON(self, wxID_OK, self._OnOK)
860            EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
861            EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)
862            EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
863    
864            EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
865            EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
866            EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
867            EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
868            EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
869            EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
870    
871            ######################
872    
873            text_title.SetFocus()
874            self.haveApplied = False
875    
876        def unsubscribe_messages(self):
877            """Unsubscribe from all messages."""
878            self.parent.Unsubscribe(MAP_REPLACED, self.map_replaced)
879            self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
880            self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
881                                   self.layer_shapestore_replaced)
882    
883        def map_layers_removed(self, map):
884            """Subscribed to MAP_LAYERS_REMOVED. If this layer was removed,
885            Close self.
886            """
887            if self.layer not in self.map.Layers():
888                self.Close()
889    
890          numRows = self.classGrid.GetNumberRows()      def layer_shapestore_replaced(self, *args):
891          assert(numRows > 0) # there should always be a default row          """Subscribed to the map's LAYER_SHAPESTORE_REPLACED message.
892    
893            Close self.
894            """
895            self.Close()
896    
897        def map_replaced(self, *args):
898            """Subscribed to the mainwindow's MAP_REPLACED message. Close self."""
899            self.Close()
900    
901        def EditSymbol(self, row):
902            """Open up a dialog where the user can select the properties
903            for a group.
904            """
905            table = self.classGrid.GetTable()
906            prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
907    
908            # get a new ClassGroupProperties object and copy the
909            # values over to our current object
910            propDlg = SelectPropertiesDialog(self, prop, self.layer.ShapeType())
911    
912            self.Enable(False)
913            if propDlg.ShowModal() == wxID_OK:
914                new_prop = propDlg.GetClassGroupProperties()
915                table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
916            self.Enable(True)
917            propDlg.Destroy()
918            
919        def _SetClassification(self, clazz):
920            """Called from the ClassGen dialog when a new classification has
921            been created and should be set in the table.
922            """
923            # FIXME: This could be implemented using a message
924            
925            self.fields.SetClientData(self.__cur_field, clazz)
926            self.classGrid.GetTable().SetClassification(clazz)
927    
928        def __BuildClassification(self, fieldIndex, copyClass=False, force=False):
929            """Pack the classification setting into a Classification object.
930            Returns (Classification, fieldName) where fieldName is the selected
931            field in the table that the classification should be used with.
932            """
933    
934    #       numRows = self.classGrid.GetNumberRows()
935    #       assert numRows > 0  # there should always be a default row
936    
         clazz = Classification()  
937          if fieldIndex == 0:          if fieldIndex == 0:
938              fieldName = None              fieldName = None
939              fieldType = None              fieldType = None
# Line 736  class Classifier(NonModalDialog): Line 941  class Classifier(NonModalDialog):
941              fieldName = self.fields.GetString(fieldIndex)              fieldName = self.fields.GetString(fieldIndex)
942              fieldType = self.layer.GetFieldType(fieldName)              fieldType = self.layer.GetFieldType(fieldName)
943    
944          clazz.SetField(fieldName)          clazz = self.fields.GetClientData(fieldIndex)
945          clazz.SetFieldType(fieldType)          if clazz is None or self.classGrid.GetTable().IsModified() or force:
946                clazz = self.classGrid.GetTable().GetClassification()
947                if copyClass:
948          table = self.classGrid.GetTable()                  clazz = copy.deepcopy(clazz)
949          clazz.SetDefaultGroup(table.GetClassGroup(0))  
950            return clazz, fieldName
951          for i in range(1, numRows):  
952              clazz.AddGroup(table.GetClassGroup(i))      def __SetGridTable(self, fieldIndex, group = None):
953            """Set the table with the classification associated with the
954          return clazz          selected field at fieldIndex. Select the specified group
955            if group is not None.
956      def __SetGridTable(self, fieldIndex):          """
957    
958          clazz = self.fields.GetClientData(fieldIndex)          clazz = self.fields.GetClientData(fieldIndex)
959    
# Line 759  class Classifier(NonModalDialog): Line 964  class Classifier(NonModalDialog):
964                      self.layer.GetClassification().                      self.layer.GetClassification().
965                                 GetDefaultGroup().GetProperties()))                                 GetDefaultGroup().GetProperties()))
966    
967              fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
968              fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
             clazz.SetFieldType(fieldType)  
969                                    
970          self.classGrid.CreateTable(clazz, self.layer.ShapeType())          self.classGrid.CreateTable(clazz, fieldType,
971                                       self.layer.ShapeType(), group)
   
   
     type2string = {None:             _("None"),  
                    FIELDTYPE_STRING: _("Text"),  
                    FIELDTYPE_INT:    _("Integer"),  
                    FIELDTYPE_DOUBLE: _("Decimal")}  
972    
973      def __SetFieldTypeText(self, fieldIndex):      def __SetFieldTypeText(self, fieldIndex):
974            """Set the field type string using the data type of the field
975            at fieldIndex.
976            """
977          fieldName = self.fields.GetString(fieldIndex)          fieldName = self.fields.GetString(fieldIndex)
978          fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
979    
980          assert(Classifier.type2string.has_key(fieldType))          assert Classifier.type2string.has_key(fieldType)
981    
982          text = Classifier.type2string[fieldType]          text = Classifier.type2string[fieldType]
983    
984          self.fieldTypeText.SetLabel(_("Field Type: %s") % text)          self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
985    
986      def __SelectField(self, newIndex, oldIndex = -1):      def __SelectField(self, newIndex, oldIndex = -1, group = None):
987            """This method assumes that the current selection for the
988            combo has already been set by a call to SetSelection().
989            """
990    
991          assert(oldIndex >= -1)          assert oldIndex >= -1
992    
993          if oldIndex != -1:          if oldIndex != -1:
994              clazz = self.__BuildClassification(oldIndex)              clazz, name = self.__BuildClassification(oldIndex, force = True)
995              self.fields.SetClientData(oldIndex, clazz)              self.fields.SetClientData(oldIndex, clazz)
996    
997          self.__SetGridTable(newIndex)          self.__SetGridTable(newIndex, group)
   
         enabled = newIndex != 0  
998    
999          for b in self.controlButtons:          self.__EnableButtons(EB_SELECT_FIELD)
             b.Enable(enabled)  
1000    
1001          self.__SetFieldTypeText(newIndex)          self.__SetFieldTypeText(newIndex)
1002    
1003        def __SetTitle(self, title):
1004            """Set the title of the dialog."""
1005            if title != "":
1006                title = ": " + title
1007    
1008            self.SetTitle(_("Layer Properties") + title)
1009    
1010        def _OnEditSymbol(self, event):
1011            """Open up a dialog for the user to select group properties."""
1012            sel = self.classGrid.GetCurrentSelection()
1013    
1014            if len(sel) == 1:
1015                self.EditSymbol(sel[0])
1016    
1017      def _OnFieldSelect(self, event):      def _OnFieldSelect(self, event):
1018          index = self.fields.GetSelection()          index = self.fields.GetSelection()
1019          self.__SelectField(index, self.__cur_field)          self.__SelectField(index, self.__cur_field)
1020          self.__cur_field = index          self.__cur_field = index
1021    
1022      def _OnApply(self, event):      def _OnTry(self, event):
1023          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
1024             it to the layer.             it to the layer.
1025          """          """
1026    
1027          clazz = self.fields.GetClientData(self.__cur_field)          if self.layer.HasClassification():
1028                clazz = self.fields.GetClientData(self.__cur_field)
1029    
1030          #              #
1031          # only build the classification if there wasn't one to              # only build the classification if there wasn't one to
1032          # to begin with or it has been modified              # to begin with or it has been modified
1033          #              #
1034          if clazz is None or self.classGrid.GetTable().IsModified():              self.classGrid.SaveEditControlValue()
1035              clazz = self.__BuildClassification(self.__cur_field)              clazz, name = self.__BuildClassification(self.__cur_field, True)
1036    
1037                self.layer.SetClassificationColumn(name)
1038                self.layer.SetClassification(clazz)
1039    
1040          self.layer.SetClassification(clazz)          self.haveApplied = True
1041    
1042      def _OnOK(self, event):      def _OnOK(self, event):
1043          self._OnApply(event)          self._OnTry(event)
1044          self.OnClose(event)          self.Close()
1045    
1046        def OnClose(self, event):
1047            self.unsubscribe_messages()
1048            NonModalNonParentDialog.OnClose(self, event)
1049    
1050        def _OnCloseBtn(self, event):
1051            """Close is similar to Cancel except that any changes that were
1052            made and applied remain applied, but the currently displayed
1053            classification is discarded.
1054            """
1055    
1056            self.Close()
1057    
1058      def _OnCancel(self, event):      def _OnRevert(self, event):
1059          """The layer's current classification stays the same."""          """The layer's current classification stays the same."""
1060          self.layer.SetClassification(self.originalClass)          if self.haveApplied:
1061          self.OnClose(event)              self.layer.SetClassificationColumn(self.originalClassField)
1062                self.layer.SetClassification(self.originalClass)
1063    
1064            #self.Close()
1065    
1066      def _OnAdd(self, event):      def _OnAdd(self, event):
1067          self.classGrid.AppendRows()          self.classGrid.AppendRows()
# Line 836  class Classifier(NonModalDialog): Line 1069  class Classifier(NonModalDialog):
1069      def _OnRemove(self, event):      def _OnRemove(self, event):
1070          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
1071    
1072      def _OnGenRange(self, event):      def _OnGenClass(self, event):
1073          print "Classifier._OnGenRange()"          """Open up a dialog for the user to generate classifications."""
1074    
1075            self.genDlg = ClassGenDialog(self, self.layer,
1076                              self.fields.GetString(self.__cur_field))
1077    
1078            EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1079    
1080            self.__EnableButtons(EB_GEN_CLASS)
1081    
1082            self.genDlg.Show()
1083    
1084        def _OnGenDialogClose(self, event):
1085            """Reenable buttons after the generate classification
1086            dialog is closed.
1087            """
1088            self.genDlg.Destroy()
1089            self.genDlg = None
1090            self.__EnableButtons(EB_GEN_CLASS)
1091    
1092      def _OnMoveUp(self, event):      def _OnMoveUp(self, event):
1093            """When the user clicks MoveUp, try to move a group up one row."""
1094          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1095    
1096          if len(sel) == 1:          if len(sel) == 1:
# Line 852  class Classifier(NonModalDialog): Line 1103  class Classifier(NonModalDialog):
1103                  table.SetClassGroup(i, x)                  table.SetClassGroup(i, x)
1104                  self.classGrid.ClearSelection()                  self.classGrid.ClearSelection()
1105                  self.classGrid.SelectRow(i - 1)                  self.classGrid.SelectRow(i - 1)
1106                    self.classGrid.MakeCellVisible(i - 1, 0)
1107    
1108      def _OnMoveDown(self, event):      def _OnMoveDown(self, event):
1109            """When the user clicks MoveDown, try to move a group down one row."""
1110          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1111    
1112          if len(sel) == 1:          if len(sel) == 1:
# Line 866  class Classifier(NonModalDialog): Line 1119  class Classifier(NonModalDialog):
1119                  table.SetClassGroup(i + 1, x)                  table.SetClassGroup(i + 1, x)
1120                  self.classGrid.ClearSelection()                  self.classGrid.ClearSelection()
1121                  self.classGrid.SelectRow(i + 1)                  self.classGrid.SelectRow(i + 1)
1122                    self.classGrid.MakeCellVisible(i + 1, 0)
1123    
1124        def _OnTitleChanged(self, event):
1125            """Update the dialog title when the user changed the layer name."""
1126            obj = event.GetEventObject()
1127    
1128            self.layer.SetTitle(obj.GetValue())
1129            self.__SetTitle(self.layer.Title())
1130    
1131            self.__EnableButtons(EB_LAYER_TITLE)
1132    
1133        def __EnableButtons(self, case):
1134            """Helper method that enables/disables the appropriate buttons
1135            based on the case provided. Cases are constants beginning with EB_.
1136            """
1137    
1138            list = {wxID_OK                 : True,
1139                    wxID_CANCEL             : True,
1140                    ID_PROPERTY_ADD         : True,
1141                    ID_PROPERTY_MOVEUP      : True,
1142                    ID_PROPERTY_MOVEDOWN    : True,
1143                    ID_PROPERTY_REMOVE      : True,
1144                    ID_PROPERTY_SELECT      : True,
1145                    ID_PROPERTY_FIELDTEXT   : True,
1146                    ID_PROPERTY_GENCLASS    : True,
1147                    ID_PROPERTY_EDITSYM     : True}
1148    
1149            if case == EB_LAYER_TITLE:  
1150                if self.layer.Title() == "":
1151                    list[wxID_OK] = False
1152                    list[wxID_CANCEL] = False
1153    
1154            elif case == EB_SELECT_FIELD:
1155                if self.fields.GetSelection() == 0:
1156                    list[ID_PROPERTY_GENCLASS] = False
1157                    list[ID_PROPERTY_ADD] = False
1158                    list[ID_PROPERTY_MOVEUP] = False
1159                    list[ID_PROPERTY_MOVEDOWN] = False
1160                    list[ID_PROPERTY_REMOVE] = False
1161    
1162            elif case == EB_GEN_CLASS:
1163                if self.genDlg is not None:
1164                    list[ID_PROPERTY_SELECT] = False
1165                    list[ID_PROPERTY_FIELDTEXT] = False
1166                    list[ID_PROPERTY_GENCLASS] = False
1167    
1168            for id, enable in list.items():
1169                win = self.FindWindowById(id)
1170                if win:
1171                    win.Enable(enable)
1172    
 ID_SELPROP_OK = 4001  
 ID_SELPROP_CANCEL = 4002  
1173  ID_SELPROP_SPINCTRL = 4002  ID_SELPROP_SPINCTRL = 4002
1174  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1175  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
# Line 878  ID_SELPROP_STROKECLRTRANS = 4006 Line 1178  ID_SELPROP_STROKECLRTRANS = 4006
1178  ID_SELPROP_FILLCLRTRANS = 4007  ID_SELPROP_FILLCLRTRANS = 4007
1179    
1180  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
1181        """Dialog that allows the user to select group properties."""
1182    
1183      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1184            """Open the dialog with the initial prop properties and shapeType."""
1185    
1186          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
1187                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1188    
# Line 893  class SelectPropertiesDialog(wxDialog): Line 1196  class SelectPropertiesDialog(wxDialog):
1196          previewBox = wxBoxSizer(wxVERTICAL)          previewBox = wxBoxSizer(wxVERTICAL)
1197          previewBox.Add(wxStaticText(self, -1, _("Preview:")),          previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1198              0, wxALIGN_LEFT | wxALL, 4)              0, wxALIGN_LEFT | wxALL, 4)
1199          self.previewer = ClassDataPreviewer(None, self.prop, shapeType,  
1200                                              self, ID_SELPROP_PREVIEW, (40, 40))          self.previewWin = ClassGroupPropertiesCtrl(
1201          previewBox.Add(self.previewer, 1, wxGROW, 15)              self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1202                (40, 40), wxSIMPLE_BORDER)
1203    
1204            self.previewWin.AllowEdit(False)
1205    
1206            previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1207    
1208          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1209    
# Line 903  class SelectPropertiesDialog(wxDialog): Line 1211  class SelectPropertiesDialog(wxDialog):
1211          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wxBoxSizer(wxVERTICAL)
1212    
1213          lineColorBox = wxBoxSizer(wxHORIZONTAL)          lineColorBox = wxBoxSizer(wxHORIZONTAL)
1214          lineColorBox.Add(          button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1215              wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),          button.SetFocus()
1216              1, wxALL | wxGROW, 4)          lineColorBox.Add(button, 1, wxALL | wxGROW, 4)
1217          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1218    
1219          lineColorBox.Add(          lineColorBox.Add(
# Line 951  class SelectPropertiesDialog(wxDialog): Line 1259  class SelectPropertiesDialog(wxDialog):
1259          # Control buttons:          # Control buttons:
1260          #          #
1261          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1262          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),          button_ok = wxButton(self, wxID_OK, _("OK"))
1263                        0, wxALL, 4)          buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)
1264          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),
1265                        0, wxALL, 4)                        0, wxRIGHT|wxEXPAND, 10)
1266          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
1267    
1268            button_ok.SetDefault()
1269                                                                                                                                                                    
1270          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)          #EVT_BUTTON(self, wxID_OK, self._OnOK)
1271          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)          #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1272                                                                                                                                                                    
1273          self.SetAutoLayout(True)          self.SetAutoLayout(True)
1274          self.SetSizer(topBox)          self.SetSizer(topBox)
1275          topBox.Fit(self)          topBox.Fit(self)
1276          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1277    
1278      def _OnOK(self, event):      def OnOK(self, event):
1279          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1280    
1281      def _OnCancel(self, event):      def OnCancel(self, event):
1282          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1283    
1284      def _OnSpin(self, event):      def _OnSpin(self, event):
1285          self.prop.SetLineWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl.GetValue())
1286          self.previewer.Refresh()          self.previewWin.Refresh()
1287    
1288      def __GetColor(self, cur):      def __GetColor(self, cur):
1289          dialog = wxColourDialog(self)          dialog = ColorDialog(self)
1290          dialog.GetColourData().SetColour(Color2wxColour(cur))          dialog.SetColor(cur)
1291    
1292          ret = None          ret = None
1293          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wxID_OK:
1294              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = dialog.GetColor()
1295    
1296          dialog.Destroy()          dialog.Destroy()
1297    
# Line 990  class SelectPropertiesDialog(wxDialog): Line 1301  class SelectPropertiesDialog(wxDialog):
1301          clr = self.__GetColor(self.prop.GetLineColor())          clr = self.__GetColor(self.prop.GetLineColor())
1302          if clr is not None:          if clr is not None:
1303              self.prop.SetLineColor(clr)              self.prop.SetLineColor(clr)
1304          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1305    
1306      def _OnChangeLineColorTrans(self, event):      def _OnChangeLineColorTrans(self, event):
1307          self.prop.SetLineColor(Color.None)          self.prop.SetLineColor(Transparent)
1308          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1309                    
1310      def _OnChangeFillColor(self, event):      def _OnChangeFillColor(self, event):
1311          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1312          if clr is not None:          if clr is not None:
1313              self.prop.SetFill(clr)              self.prop.SetFill(clr)
1314          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1315    
1316      def _OnChangeFillColorTrans(self, event):      def _OnChangeFillColorTrans(self, event):
1317          self.prop.SetFill(Color.None)          self.prop.SetFill(Transparent)
1318          self.previewer.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1319    
1320      def GetClassGroupProperties(self):      def GetClassGroupProperties(self):
1321          return self.prop          return self.prop
1322    
1323    
1324  class ClassDataPreviewer(wxWindow):  class ClassDataPreviewWindow(wxWindow):
1325        """A custom window that draws group properties using the correct shape."""
1326    
1327      def __init__(self, rect, prop, shapeType,      def __init__(self, rect, prop, shapeType,
1328                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wxDefaultSize):
1329            """Draws the appropriate shape as specified with shapeType using
1330            prop properities.
1331            """
1332          if parent is not None:          if parent is not None:
1333              wxWindow.__init__(self, parent, id, size=size)              wxWindow.__init__(self, parent, id, (0, 0), size)
1334              EVT_PAINT(self, self._OnPaint)              EVT_PAINT(self, self._OnPaint)
1335    
1336          self.rect = rect          self.rect = rect
1337    
1338          self.prop = prop          self.prop = prop
1339          self.shapeType = shapeType          self.shapeType = shapeType
1340            self.previewer = ClassDataPreviewer()
1341    
1342        def GetProperties():
1343            return self.prop
1344    
1345      def _OnPaint(self, event):      def _OnPaint(self, event):
1346          dc = wxPaintDC(self)          dc = wxPaintDC(self)
# Line 1028  class ClassDataPreviewer(wxWindow): Line 1348  class ClassDataPreviewer(wxWindow):
1348          # XXX: this doesn't seem to be having an effect:          # XXX: this doesn't seem to be having an effect:
1349          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1350    
1351          self.Draw(dc, None)          if self.rect is None:
1352                w, h = self.GetSize()
1353                rect = wxRect(0, 0, w, h)
1354            else:
1355                rect = self.rect
1356    
1357            self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1358    
1359      def Draw(self, dc, rect, prop = None, shapeType = None):  class ClassDataPreviewer:
1360        """Class that actually draws a group property preview."""
1361    
1362          if prop is None: prop = self.prop      def Draw(self, dc, rect, prop, shapeType):
1363          if shapeType is None: shapeType = self.shapeType  
1364            assert dc is not None
1365            assert isinstance(prop, ClassGroupProperties)
1366    
1367          if rect is None:          if rect is None:
1368              x = y = 0              x = 0
1369              w, h = self.GetClientSizeTuple()              y = 0
1370                w, h = dc.GetSize()
1371          else:          else:
1372              x = rect.GetX()              x = rect.GetX()
1373              y = rect.GetY()              y = rect.GetY()
# Line 1045  class ClassDataPreviewer(wxWindow): Line 1375  class ClassDataPreviewer(wxWindow):
1375              h = rect.GetHeight()              h = rect.GetHeight()
1376    
1377          stroke = prop.GetLineColor()          stroke = prop.GetLineColor()
1378          if stroke is Color.None:          if stroke is Transparent:
1379              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1380          else:          else:
1381              pen = wxPen(Color2wxColour(stroke),              pen = wxPen(Color2wxColour(stroke),
# Line 1053  class ClassDataPreviewer(wxWindow): Line 1383  class ClassDataPreviewer(wxWindow):
1383                          wxSOLID)                          wxSOLID)
1384    
1385          stroke = prop.GetFill()          stroke = prop.GetFill()
1386          if stroke is Color.None:          if stroke is Transparent:
1387              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1388          else:          else:
1389              brush = wxBrush(Color2wxColour(stroke), wxSOLID)              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
# Line 1067  class ClassDataPreviewer(wxWindow): Line 1397  class ClassDataPreviewer(wxWindow):
1397                             wxPoint(x + w/2, y + h/4*3),                             wxPoint(x + w/2, y + h/4*3),
1398                             wxPoint(x + w, y)])                             wxPoint(x + w, y)])
1399    
1400          elif shapeType == SHAPETYPE_POINT or \          elif shapeType == SHAPETYPE_POINT:
              shapeType == SHAPETYPE_POLYGON:  
1401    
1402              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2,
1403                            (min(w, h) - prop.GetLineWidth())/2)                            (min(w, h) - prop.GetLineWidth())/2)
1404    
1405            elif shapeType == SHAPETYPE_POLYGON:
1406                dc.DrawRectangle(x, y, w, h)
1407    
1408  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1409        """A wrapper class that can be used to draw group properties in a
1410        grid table.
1411        """
1412    
1413      def __init__(self, shapeType):      def __init__(self, shapeType):
1414          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
1415          self.previewer = ClassDataPreviewer(None, None, shapeType)          self.shapeType = shapeType
1416            self.previewer = ClassDataPreviewer()
1417    
1418      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1419          data = grid.GetTable().GetClassGroup(row)          data = grid.GetTable().GetClassGroup(row)
# Line 1090  class ClassRenderer(wxPyGridCellRenderer Line 1426  class ClassRenderer(wxPyGridCellRenderer
1426                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1427    
1428          if not isinstance(data, ClassGroupMap):          if not isinstance(data, ClassGroupMap):
1429              self.previewer.Draw(dc, rect, data.GetProperties())              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1430    
1431          if isSelected:          if isSelected:
1432              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
                       4, wxSOLID))  
1433              dc.SetBrush(wxTRANSPARENT_BRUSH)              dc.SetBrush(wxTRANSPARENT_BRUSH)
1434    
1435              dc.DrawRectangle(rect.GetX(), rect.GetY(),              dc.DrawRectangle(rect.GetX(), rect.GetY(),
1436                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
1437    
1438          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1439    
1440    
1441    class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1442        """A custom window and control that draw a preview of group properties
1443        and can open a dialog to modify the properties if the user double-clicks
1444        it.
1445        """
1446    
1447        def __init__(self, parent, id, props, shapeType,
1448                     size = wxDefaultSize, style = 0):
1449    
1450            wxWindow.__init__(self, parent, id, size = size, style = style)
1451    
1452            self.parent = parent
1453    
1454            self.SetProperties(props)
1455            self.SetShapeType(shapeType)
1456            self.AllowEdit(True)
1457    
1458            EVT_PAINT(self, self._OnPaint)
1459            EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1460    
1461            self.previewer = ClassDataPreviewer()
1462    
1463        def _OnPaint(self, event):
1464            dc = wxPaintDC(self)
1465    
1466            # XXX: this doesn't seem to be having an effect:
1467            dc.DestroyClippingRegion()
1468    
1469            w, h = self.GetClientSize()
1470    
1471            self.previewer.Draw(dc,
1472                                wxRect(0, 0, w, h),
1473                                self.GetProperties(),
1474                                self.GetShapeType())
1475    
1476    
1477        def GetProperties(self):
1478            return self.props
1479    
1480        def SetProperties(self, props):
1481            self.props = props
1482            self.Refresh()
1483    
1484        def GetShapeType(self):
1485            return self.shapeType
1486    
1487        def SetShapeType(self, shapeType):
1488            self.shapeType = shapeType
1489            self.Refresh()
1490    
1491        def AllowEdit(self, allow):
1492            """Allow/Disallow double-clicking on the control."""
1493            self.allowEdit = allow
1494    
1495        def DoEdit(self):
1496            """Open the properties selector dialog."""
1497    
1498            if not self.allowEdit: return
1499    
1500            propDlg = SelectPropertiesDialog(self.parent,
1501                                             self.GetProperties(),
1502                                             self.GetShapeType())
1503    
1504            if propDlg.ShowModal() == wxID_OK:
1505                new_prop = propDlg.GetClassGroupProperties()
1506                self.SetProperties(new_prop)
1507                self.Refresh()
1508    
1509            propDlg.Destroy()
1510    
1511        def _OnLeftDClick(self, event):
1512            self.DoEdit()

Legend:
Removed from v.513  
changed lines
  Added in v.2362

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26