/[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 415 by jonathan, Wed Feb 19 16:52:51 2003 UTC revision 570 by jonathan, Fri Mar 28 17:06:26 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 by Intevation GmbH  # Copyright (c) 2001, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jonathan Coles <[email protected]>
4  #  #
# Line 11  __version__ = "$Revision$" Line 11  __version__ = "$Revision$"
11    
12  import copy  import copy
13    
14    from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
15         FIELDTYPE_STRING
16    
17  from wxPython.wx import *  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.common import *
22    from Thuban.UI.common import *
23    
24  from Thuban.Model.classification import * #Classification, ClassData  from Thuban.Model.classification import *
25    
26  from Thuban.Model.color import Color  from Thuban.Model.color import Color
27    
28  from Thuban.Model.layer import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
29    
30    from dialogs import NonModalDialog
31    
32    # widget id's
33  ID_PROPERTY_SELECT = 4010  ID_PROPERTY_SELECT = 4010
34  ID_CLASS_TABLE = 40011  ID_CLASS_TABLE = 40011
35    
# Line 30  ID_CLASSIFY_OK = 4001 Line 37  ID_CLASSIFY_OK = 4001
37  ID_CLASSIFY_CANCEL = 4002  ID_CLASSIFY_CANCEL = 4002
38  ID_CLASSIFY_ADD = 4003  ID_CLASSIFY_ADD = 4003
39  ID_CLASSIFY_GENRANGE = 4004  ID_CLASSIFY_GENRANGE = 4004
40    ID_CLASSIFY_REMOVE = 4005
41    ID_CLASSIFY_MOVEUP = 4006
42    ID_CLASSIFY_MOVEDOWN = 4007
43    ID_CLASSIFY_APPLY = 4008
44    
45  COL_VISUAL = 0  # table columns
46    COL_SYMBOL = 0
47  COL_VALUE  = 1  COL_VALUE  = 1
48  COL_LABEL  = 2  COL_LABEL  = 2
49    
50    # indices into the client data lists in Classifier.fields
51    FIELD_CLASS = 0
52    FIELD_TYPE = 1
53    FIELD_NAME = 2
54    
55  #  #
56  # this is a silly work around to ensure that the table that is  # this is a silly work around to ensure that the table that is
57  # passed into SetTable is the same that is returned by GetTable  # passed into SetTable is the same that is returned by GetTable
# Line 42  COL_LABEL  = 2 Line 59  COL_LABEL  = 2
59  import weakref  import weakref
60  class ClassGrid(wxGrid):  class ClassGrid(wxGrid):
61    
62      def __init__(self, parent, layer):  
63          wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (300, 150))      def __init__(self, parent):
64          self.SetTable(          """Constructor.
65              ClassTable(layer.GetClassification(), layer.ShapeType(), self),  
66              true)          parent -- the parent window
67    
68            clazz -- the working classification that this grid should
69                     use for display.
70            """
71    
72            #wxGrid.__init__(self, parent, ID_CLASS_TABLE, size = (340, 160))
73            wxGrid.__init__(self, parent, ID_CLASS_TABLE)
74            #self.SetTable(ClassTable(fieldData, layer.ShapeType(), self), True)
75    
76            self.currentSelection = []
77    
78            EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)
79            EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)
80            EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)
81    
82            print "123123123: ", ('Show' in dir(self))
83    
84        def Show(self):
85            print "SHOW!"
86    
87        def Refresh(self):
88            self.Show()
89        def Update(self):
90            self.Show()
91    
92        def CreateTable(self, clazz, shapeType, group = None):
93    
94            assert(isinstance(clazz, Classification))
95    
96            self.shapeType = shapeType
97            table = self.GetTable()
98            if table is None:
99                w = self.GetDefaultColSize() * 3 + self.GetDefaultRowLabelSize()
100                h = self.GetDefaultRowSize() * 4 + self.GetDefaultColLabelSize()
101                self.SetDimensions(-1, -1, w, h)
102                self.SetSizeHints(w, h, -1, -1)
103                table = ClassTable(self)
104                self.SetTable(table, True)
105    
106    
107            self.SetSelectionMode(wxGrid.wxGridSelectRows)
108            self.ClearSelection()
109    
110            print "8------------------"
111            table.Reset(clazz, self.shapeType, group)
112            print "9------------------"
113    
114    #   def Show(self, show = True):
115    #       print "SHOW!"
116    #       wxGrid.Show(self, show)
117    
118    #       sel = self.GetCurrentSelection()
119    
120    #       print "( 1"
121    #       if len(sel) == 1:
122    #           print "( 2"
123    #           self.MakeCellVisible(sel[0], 0)
124    
125        def GetCurrentSelection(self):
126            """Return the currently highlighted rows as an increasing list
127               of row numbers."""
128            print "@@ ", self.currentSelection
129            sel = copy.copy(self.currentSelection)
130            sel.sort()
131            return sel
132    
133        def GetSelectedRows(self):
134            return self.GetCurrentSelection()
135    
136      def SetCellRenderer(self, row, col):      def SetCellRenderer(self, row, col):
137          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))          raise ValueError(_("Must not allow setting of renderer in ClassGrid!"))
138    
139        #
140        # [Set|Get]Table is taken from http://wiki.wxpython.org
141        # they are needed as a work around to ensure that the table
142        # that is passed to SetTable is the one that is returned
143        # by GetTable.
144        #
145      def SetTable(self, object, *attributes):      def SetTable(self, object, *attributes):
146          self.tableRef = weakref.ref(object)          self.tableRef = weakref.ref(object)
147          return wxGrid.SetTable(self, object, *attributes)          return wxGrid.SetTable(self, object, *attributes)
148    
149      def GetTable(self):      def GetTable(self):
150          return self.tableRef()          try:
151                return self.tableRef()
152            except:
153                return None
154    
155        def DeleteSelectedRows(self):
156            """Deletes all highlighted rows.
157      
158            If only one row is highlighted then after it is deleted the
159            row that was below the deleted row is highlighted."""
160    
161            sel = self.GetCurrentSelection()
162    
163            # nothing to do
164            if len(sel) == 0: return
165    
166            # if only one thing is selected check if it is the default
167            # data row, because we can't remove that
168            if len(sel) == 1:
169                #group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)
170                group = self.GetTable().GetClassGroup(sel[0])
171                if isinstance(group, ClassGroupDefault):
172                    wxMessageDialog(self,
173                                    "The Default group cannot be removed.",
174                                    style = wxOK | wxICON_EXCLAMATION).ShowModal()
175                    return
176            
177    
178            self.ClearSelection()
179    
180            # we need to remove things from the bottom up so we don't
181            # change the indexes of rows that will be deleted next
182            sel.reverse()
183    
184            #
185            # actually remove the rows
186            #
187            table = self.GetTable()
188            for row in sel:
189                table.DeleteRows(row)
190    
191            #
192            # if there was only one row selected highlight the row
193            # that was directly below it, or move up one if the
194            # deleted row was the last row.
195            #
196            if len(sel) == 1:
197                r = sel[0]
198                if r > self.GetNumberRows() - 1:
199                    r = self.GetNumberRows() - 1
200                self.SelectRow(r)
201            
202    
203        def SelectGroup(self, group, makeVisible = True):
204            if group is None: return
205    
206            assert(isinstance(group, ClassGroup))
207    
208            table = self.GetTable()
209    
210            assert(table is not None)
211    
212    
213            print "-- ", group
214            for i in range(table.GetNumberRows()):
215                g = table.GetClassGroup(i)
216                print "1", g
217                if g is group:
218                    print "2"
219                    self.SelectRow(i)
220                    if makeVisible:
221                        print "3"
222                        self.MakeCellVisible(i, 0)
223                    break
224    
225            
226    
227    #
228    # XXX: This isn't working, and there is no way to deselect rows wxPython!
229    #
230    #   def DeselectRow(self, row):
231    #       self.ProcessEvent(
232    #           wxGridRangeSelectEvent(-1,
233    #                                  wxEVT_GRID_RANGE_SELECT,
234    #                                  self,
235    #                                  (row, row), (row, row),
236    #                                  sel = False))
237    
238        def _OnCellDClick(self, event):
239            """Handle a double on a cell."""
240    
241            r = event.GetRow()
242            c = event.GetCol()
243            if c == COL_SYMBOL:
244                prop = self.GetTable().GetValueAsCustom(r, c, None)
245                #prop = group.GetProperties()
246    
247                # get a new ClassGroupProperties object and copy the
248                # values over to our current object
249                propDlg = SelectPropertiesDialog(NULL, prop, self.shapeType)
250                if propDlg.ShowModal() == wxID_OK:
251                    new_prop = propDlg.GetClassGroupProperties()
252                    #prop.SetProperties(new_prop)
253                    self.GetTable().SetValueAsCustom(r, c, None, new_prop)
254                propDlg.Destroy()
255    
256        #
257        # _OnSelectedRange() and _OnSelectedCell() were borrowed
258        # from http://wiki.wxpython.org to keep track of which
259        # cells are currently highlighted
260        #
261        def _OnSelectedRange(self, event):
262            """Internal update to the selection tracking list"""
263            if event.Selecting():
264                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
265                    if index not in self.currentSelection:
266                        print "    ", index
267                        self.currentSelection.append( index )
268            else:
269                for index in range( event.GetTopRow(), event.GetBottomRow()+1):
270                    while index in self.currentSelection:
271                        print "    ", index
272                        self.currentSelection.remove( index )
273            #self.ConfigureForSelection()
274    
275            print self.GetCurrentSelection()
276            event.Skip()
277    
278        def _OnSelectedCell( self, event ):
279            """Internal update to the selection tracking list"""
280            print "selecting cell: ", event.GetRow()
281            self.currentSelection = [ event.GetRow() ]
282            #self.ConfigureForSelection()
283            event.Skip()
284    
285  class ClassTable(wxPyGridTableBase):  class ClassTable(wxPyGridTableBase):
286        """Represents the underlying data structure for the grid."""
287    
288      NUM_COLS = 3      NUM_COLS = 3
289    
290      __col_labels = [_("Visual"), _("Value"), _("Label")]      __col_labels = [_("Symbol"), _("Value"), _("Label")]
291    
292    
293        def __init__(self, view = None):
294        #def __init__(self, clazz, shapeType, view = None):
295            """Constructor.
296    
297      # this is tied to the values of classification.ClassData          shapeType -- the type of shape that the layer uses
298      __row_labels = [_("Default"), _("Point"), _("Range"), _("Map")]  
299            view -- a wxGrid object that uses this class for its table
300            """
301    
     def __init__(self, clazz, shapeType, view = None):  
302          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
303    
304          self.SetView(view)          self.SetView(view)
305          self.tdata = []          self.tdata = []
306    
307          self.Reset(clazz, shapeType)          #self.Reset(clazz, shapeType)
308    
309      def Reset(self, clazz, shapeType):      def Reset(self, clazz, shapeType, group = None):
310            """Reset the table with the given data.
311    
312            This is necessary because wxWindows does not allow a grid's
313            table to change once it has been intially set and so we
314            need a way of modifying the data.
315    
316            clazz -- the working classification that this table should
317                     use for display. This may be different from the
318                     classification in the layer.
319    
320            shapeType -- the type of shape that the layer uses
321            """
322    
323            assert(isinstance(clazz, Classification))
324    
325          self.GetView().BeginBatch()          self.GetView().BeginBatch()
326    
327            self.fieldType = clazz.GetFieldType()
328          self.shapeType = shapeType          self.shapeType = shapeType
         self.renderer = ClassRenderer(self.shapeType)  
329    
330          old_tdata = self.tdata          old_len = len(self.tdata)
331    
332          self.tdata = []          self.tdata = []
333    
334          if clazz is None:          print "9------------------"
335              clazz = Classification()          #
336            # copy the data out of the classification and into our
337            # array
338            #
339            row = -1
340            for g in clazz:
341                ng = copy.deepcopy(g)
342                self.__SetRow(-1, ng)
343                if g is group:
344                    row = self.GetNumberRows() - 1
345                    print "selecting row..."
346    
347          p = clazz.GetDefaultData()          print "10------------------"
348          np = ClassDataPoint(classData = p)  
349          self.tdata.append([np, 'DEFAULT', np.GetLabel()])          self.__Modified(-1)
   
         for p in clazz.points.values():  
             np = ClassDataPoint(p.GetValue(), classData = p)  
             self.tdata.append([np, np.GetValue(), np.GetLabel()])  
   
         for p in clazz.ranges:  
             np = ClassDataRange(p.GetMin(), p.GetMax(), classData = p)  
             self.tdata.append([np,  
                                '%s - %s' % (np.GetMin(), np.GetMax()),  
                                np.GetLabel()])  
350    
351          self.modified = 0          self.__NotifyRowChanges(old_len, len(self.tdata))
352            print "11------------------"
353    
354            if row > -1:
355                self.GetView().ClearSelection()
356                self.GetView().SelectRow(row)
357                self.GetView().MakeCellVisible(row, 0)
358                
359            self.GetView().EndBatch()
360    
361        def __NotifyRowChanges(self, curRows, newRows):
362          #          #
363          # silly message processing for updates to the number of          # silly message processing for updates to the number of
364          # rows and columns          # rows and columns
365          #          #
         curRows = len(old_tdata)  
         newRows = len(self.tdata)  
366          if newRows > curRows:          if newRows > curRows:
367              msg = wxGridTableMessage(self,              msg = wxGridTableMessage(self,
368                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
369                          newRows - curRows)    # how many                          newRows - curRows)    # how many
370              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
371                self.GetView().FitInside()
372          elif newRows < curRows:          elif newRows < curRows:
373              msg = wxGridTableMessage(self,              msg = wxGridTableMessage(self,
374                          wxGRIDTABLE_NOTIFY_ROWS_DELETED,                          wxGRIDTABLE_NOTIFY_ROWS_DELETED,
375                          curRows - newRows,    # position                          curRows - newRows,    # position
376                          curRows - newRows)    # how many                          curRows - newRows)    # how many
377              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
378                self.GetView().FitInside()
379    
380          self.GetView().EndBatch()      def __SetRow(self, row, group):
381            """Set a row's data to that of the group.
382    
383            The table is considered modified after this operation.
384    
385            row -- if row is -1 or greater than the current number of rows
386                   then group is appended to the end.
387            """
388    
389            # either append or replace
390            if row == -1 or row >= self.GetNumberRows():
391                self.tdata.append(group)
392            else:
393                self.tdata[row] = group
394    
395            self.__Modified()
396    
397      def GetColLabelValue(self, col):      def GetColLabelValue(self, col):
398            """Return the label for the given column."""
399          return self.__col_labels[col]          return self.__col_labels[col]
400    
401      def GetRowLabelValue(self, row):      def GetRowLabelValue(self, row):
402          data = self.tdata[row][COL_VISUAL]          """Return the label for the given row."""
403          type = data.GetType()  
404          return self.__row_labels[type]          group = self.tdata[row]
405            if isinstance(group, ClassGroupDefault):   return _("Default")
406            if isinstance(group, ClassGroupSingleton): return _("Singleton")
407            if isinstance(group, ClassGroupRange):     return _("Range")
408            if isinstance(group, ClassGroupMap):       return _("Map")
409    
410            assert(False) # shouldn't get here
411            return _("")
412    
413      def GetNumberRows(self):      def GetNumberRows(self):
414            """Return the number of rows."""
415          return len(self.tdata)          return len(self.tdata)
416    
417      def GetNumberCols(self):      def GetNumberCols(self):
418            """Return the number of columns."""
419          return self.NUM_COLS          return self.NUM_COLS
420    
421      def IsEmptyCell(self, row, col):      def IsEmptyCell(self, row, col):
422          return 0          """Determine if a cell is empty. This is always false."""
423            return False
424    
425      def GetValue(self, row, col):      def GetValue(self, row, col):
426          return self.GetValueAsCustom(row, col, "")          """Return the object that is used to represent the given
427               cell coordinates. This may not be a string."""
428            return self.GetValueAsCustom(row, col, None)
429    
430      def SetValue(self, row, col, value):      def SetValue(self, row, col, value):
431          self.SetValueAsCustom(row, col, "", value)          """Assign 'value' to the cell specified by 'row' and 'col'.
432    
433            The table is considered modified after this operation.
434            """
435    
436            self.SetValueAsCustom(row, col, None, value)
437          self.__Modified()          self.__Modified()
438                
439      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
440          return self.tdata[row][col]          """Return the object that is used to represent the given
441               cell coordinates. This may not be a string.
442    
443            typeName -- unused, but needed to overload wxPyGridTableBase
444            """
445    
446            group = self.tdata[row]
447    
448            if col == COL_SYMBOL:
449                return group.GetProperties()
450    
451            if col == COL_LABEL:
452                return group.GetLabel()
453    
454            # col must be COL_VALUE
455            assert(col == COL_VALUE)
456    
457            if isinstance(group, ClassGroupDefault):
458                return _("DEFAULT")
459            elif isinstance(group, ClassGroupSingleton):
460                return group.GetValue()
461            elif isinstance(group, ClassGroupRange):
462                return _("%s - %s") % (group.GetMin(), group.GetMax())
463    
464            assert(False) # shouldn't get here
465            return None
466    
467      def __ParseInput(self, value):      def __ParseInput(self, value):
468          """Try to determine what kind of input value is          """Try to determine what kind of input value is
469             (a single number or a range)             (string, number, or range)
470    
471            Returns a tuple of length one if there is a single
472            value, or of length two if it is a range.
473          """          """
474    
475          #          type = self.fieldType
476          # first try to take the input as a single number  
477          # if there's an exception try to break it into          if type == FIELDTYPE_STRING:
478          # a range seperated by a '-'. take care to ignore              return (value,)
479          # a leading '-' as that could be for a negative number.          elif type == FIELDTYPE_INT or type == FIELDTYPE_DOUBLE:
480          # then try to parse the individual parts. if there  
481          # is an exception here, let it pass up to the calling              if type == FIELDTYPE_INT:
482          # function.                  conv = lambda p: int(float(p))
483          #              else:
484          try:                  conv = lambda p: p
485              return (ClassData.POINT, Str2Num(value))  
486          except:              #
487              i = value.find('-')              # first try to take the input as a single number
488              if i == 0:              # if there's an exception try to break it into
489                  i = value.find('-', 1)              # a range seperated by a '-'. take care to ignore
490                # a leading '-' as that could be for a negative number.
491              return (ClassData.RANGE,              # then try to parse the individual parts. if there
492                      Str2Num(value[:i]),              # is an exception here, let it pass up to the calling
493                      Str2Num(value[i+1:]))              # function.
494                #
495                try:
496                    return (conv(Str2Num(value)),)
497                except ValueError:
498                    i = value.find('-')
499                    if i == 0:
500                        i = value.find('-', 1)
501    
502                    return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))
503    
504            assert(False) # shouldn't get here
505            return (0,)
506                            
507    
508      def SetValueAsCustom(self, row, col, typeName, value):      def SetValueAsCustom(self, row, col, typeName, value):
509          data = self.tdata[row][COL_VISUAL]          """Set the cell specified by 'row' and 'col' to 'value'.
510    
511          if col == COL_VISUAL:          If column represents the value column, the input is parsed
512              self.tdata[row][COL_VISUAL] = value          to determine if a string, number, or range was entered.
513          elif col == COL_VALUE:          A new ClassGroup may be created if the type of data changes.
             if row != 0: # DefaultData row  
                 type = data.GetType()  
514    
515                  if type == ClassData.MAP:          The table is considered modified after this operation.
516                      # something special  
517                      pass          typeName -- unused, but needed to overload wxPyGridTableBase
518                  else: # POINT, RANGE          """
519                      try:  
520                          dataInfo = self.__ParseInput(value)          assert(col >= 0 and col < self.GetNumberCols())
521                      except: pass          assert(row >= 0 and row < self.GetNumberRows())
                         # bad input, ignore the request  
                     else:  
522    
523                          if dataInfo[0] == ClassData.POINT:          group = self.tdata[row]
                             if type != ClassData.POINT:  
                                 data = ClassDataPoint(classData = data)  
                             data.SetValue(dataInfo[1])  
                             self.tdata[row][COL_VALUE] = data.GetValue()  
                         elif dataInfo[0] == ClassData.RANGE:  
                             if type != ClassData.RANGE:  
                                 data = ClassDataRange(classData = data)  
                             data.SetRange(dataInfo[1], dataInfo[2])  
                             self.tdata[row][COL_VALUE] = \  
                                 "%s - %s" % (data.GetMin(), data.GetMax())  
   
                         self.tdata[row][COL_VISUAL] = data  
   
                         # i just want it to redraw!  
                         self.GetView().BeginBatch()  
                         self.GetView().EndBatch()  
524    
525            mod = True # assume the data will change
526    
527            if col == COL_SYMBOL:
528                group.SetProperties(value)
529          elif col == COL_LABEL:          elif col == COL_LABEL:
530              data.SetLabel(value)              group.SetLabel(value)
531              self.tdata[row][COL_LABEL] = data.GetLabel()          elif col == COL_VALUE:
532                if isinstance(group, ClassGroupDefault):
533                    # not allowed to modify the default value
534                    pass
535                elif isinstance(group, ClassGroupMap):
536                    # something special
537                    pass
538                else: # SINGLETON, RANGE
539                    try:
540                        dataInfo = self.__ParseInput(value)
541                    except ValueError:
542                        # bad input, ignore the request
543                        mod = False
544                    else:
545    
546                        changed = False
547                        ngroup = group
548                        props = group.GetProperties()
549    
550                        #
551                        # try to update the values, which may include
552                        # changing the underlying group type if the
553                        # group was a singleton and a range was entered
554                        #
555                        if len(dataInfo) == 1:
556                            if not isinstance(group, ClassGroupSingleton):
557                                ngroup = ClassGroupSingleton(prop = props)
558                                changed = True
559                            ngroup.SetValue(dataInfo[0])
560                        elif len(dataInfo) == 2:
561                            if not isinstance(group, ClassGroupRange):
562                                ngroup = ClassGroupRange(prop = props)
563                                changed = True
564                            ngroup.SetRange(dataInfo[0], dataInfo[1])
565                        else:
566                            assert(False)
567                            pass
568    
569                        if changed:
570                            ngroup.SetLabel(group.GetLabel())
571                            self.SetClassGroup(row, ngroup)
572          else:          else:
573              raise ValueError(_("Invalid column request"))              assert(False) # shouldn't be here
574                pass
575    
576          self.__Modified()          if mod:
577                self.__Modified()
578                self.GetView().Refresh()
579    
580      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
581            """Returns the cell attributes"""
582    
583          attr = wxGridCellAttr()          attr = wxGridCellAttr()
584          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)          #attr = wxPyGridTableBase.GetAttr(self, row, col, someExtraParameter)
585    
586          if col == COL_VISUAL:          if col == COL_SYMBOL:
587                # we need to create a new renderer each time, because
588                # SetRenderer takes control of the parameter
589              attr.SetRenderer(ClassRenderer(self.shapeType))              attr.SetRenderer(ClassRenderer(self.shapeType))
590              attr.SetReadOnly()              attr.SetReadOnly()
591    
592          return attr          return attr
593    
594      def GetClassData(self, row):      def GetClassGroup(self, row):
595          return self.tdata[row][COL_VISUAL]          """Return the ClassGroup object representing row 'row'."""
596    
597            return self.tdata[row] # self.GetValueAsCustom(row, COL_SYMBOL, None)
598    
599        def SetClassGroup(self, row, group):
600            self.__SetRow(row, group)
601            self.GetView().Refresh()
602    
603        def __Modified(self, mod = True):
604            """Adjust the modified flag.
605    
606            mod -- if -1 set the modified flag to False, otherwise perform
607                   an 'or' operation with the current value of the flag and
608                   'mod'
609            """
610    
611      def __Modified(self):          if mod == -1:
612          self.modified = 1              self.modified = False
613            else:
614                self.modified = mod or self.modified
615    
616      def IsModified(self):      def IsModified(self):
617            """True if this table is considered modified."""
618          return self.modified          return self.modified
619    
620      def AddNewDataRow(self):      def DeleteRows(self, pos, numRows = 1):
621          np = ClassDataPoint()          """Deletes 'numRows' beginning at row 'pos'.
622          self.tdata.append([np, np.GetValue(), np.GetLabel()])  
623          msg = wxGridTableMessage(self, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1)          The row representing the default group is not removed.
624          self.GetView().ProcessTableMessage(msg)  
625          self.GetView().BeginBatch()          The table is considered modified if any rows are removed.
626          self.GetView().EndBatch()          """
627    
628  class Classifier(wxDialog):          assert(pos >= 0)
629            old_len = len(self.tdata)
630            for row in range(pos, pos - numRows, -1):
631                group = self.GetClassGroup(row)
632                if not isinstance(group, ClassGroupDefault):
633                    self.tdata.pop(row)
634                    self.__Modified()
635            
636      def __init__(self, parent, layer):          if self.IsModified():
637          wxDialog.__init__(self, parent, -1, _("Classify"),              self.__NotifyRowChanges(old_len, len(self.tdata))
638                            style = wxRESIZE_BORDER)  
639        def AppendRows(self, numRows = 1):
640            """Append 'numRows' empty rows to the end of the table.
641    
642            The table is considered modified if any rows are appended.
643            """
644    
645            old_len = len(self.tdata)
646            for i in range(numRows):
647                np = ClassGroupSingleton()
648                self.__SetRow(-1, np)
649    
650            if self.IsModified():
651                self.__NotifyRowChanges(old_len, len(self.tdata))
652    
653    
654    class Classifier(NonModalDialog):
655    
656        def __init__(self, parent, name, layer, group = None):
657            NonModalDialog.__init__(self, parent, name,
658                                    _("Classifier: %s") % layer.Title())
659    
660            panel = wxPanel(self, -1, size=(100, 100))
661    
662          self.layer = layer          self.layer = layer
663    
664            self.originalClass = self.layer.GetClassification()
665            field = self.originalClass.GetField()
666            fieldType = self.originalClass.GetFieldType()
667    
668          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
669            panelBox = wxBoxSizer(wxVERTICAL)
670    
671          topBox.Add(wxStaticText(self, -1, _("Layer: %s") % layer.Title()),          #panelBox.Add(wxStaticText(panel, -1, _("Layer: %s") % layer.Title()),
672              0, wxALIGN_LEFT | wxBOTTOM, 4)              #0, wxALIGN_LEFT | wxALL, 4)
673          topBox.Add(wxStaticText(self, -1, _("Type: %s") % layer.ShapeType()),          panelBox.Add(wxStaticText(panel, -1,
674              0, wxALIGN_LEFT | wxBOTTOM, 4)                                  _("Layer Type: %s") % layer.ShapeType()),
675                0, wxALIGN_LEFT | wxALL, 4)
676    
         propertyBox = wxBoxSizer(wxHORIZONTAL)  
         propertyBox.Add(wxStaticText(self, -1, _("Property: ")),  
             0, wxALIGN_CENTER | wxALL, 4)  
677    
678          self.properties = wxComboBox(self, ID_PROPERTY_SELECT, "",          #
679            # make field combo box
680            #
681            self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",
682                                       style = wxCB_READONLY)                                       style = wxCB_READONLY)
683    
684          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
685          self.__cur_prop = -1          # just assume the first field in case one hasn't been
686          field = layer.GetClassification().GetField()          # specified in the file.
687            self.__cur_field = 0
688    
689            self.fields.Append("<None>")
690            self.fields.SetClientData(0, None)
691    
692          for i in range(self.num_cols):          for i in range(self.num_cols):
693              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
694                self.fields.Append(name)
695    
696              if name == field:              if name == field:
697                  self.__cur_prop = i                  self.__cur_field = i + 1
698              self.properties.Append(name)                  self.fields.SetClientData(i + 1, self.originalClass)
699              self.properties.SetClientData(i, None)              else:
700                    self.fields.SetClientData(i + 1, None)
701          self.properties.SetSelection(self.__cur_prop)  
702          propertyBox.Add(self.properties, 1, wxGROW|wxALL, 0)  
703          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnPropertySelect)          ###########
704    
705            self.fieldTypeText = wxStaticText(panel, -1, "")
706            panelBox.Add(self.fieldTypeText, 0,
707                         wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
708    
709            propertyBox = wxBoxSizer(wxHORIZONTAL)
710            propertyBox.Add(wxStaticText(panel, -1, _("Field: ")),
711                0, wxALIGN_LEFT | wxALL, 4)
712            propertyBox.Add(self.fields, 1, wxGROW|wxALL, 4)
713            EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
714    
715            panelBox.Add(propertyBox, 0, wxGROW, 4)
716    
         topBox.Add(propertyBox, 0, wxGROW, 4)  
717    
718          #          #
719          # Classification data table          # Control Box
720          #          #
   
721          controlBox = wxBoxSizer(wxHORIZONTAL)          controlBox = wxBoxSizer(wxHORIZONTAL)
         self.classGrid = ClassGrid(self, layer)  
722    
723          controlBox.Add(self.classGrid, 1, wxGROW, 0)  
724            ###########
725            #
726            # Control buttons:
727            #
728            self.controlButtons = []
729    
730          controlButtonBox = wxBoxSizer(wxVERTICAL)          controlButtonBox = wxBoxSizer(wxVERTICAL)
         controlButtonBox.Add(wxButton(self, ID_CLASSIFY_ADD,  
             _("Add")), 0, wxGROW | wxALL, 4)  
         controlButtonBox.Add(wxButton(self, ID_CLASSIFY_GENRANGE,  
             _("Generate Ranges")), 0, wxGROW | wxALL, 4)  
731    
732          controlBox.Add(controlButtonBox, 0, wxGROW, 10)          button = wxButton(panel, ID_CLASSIFY_ADD, _("Add"))
733          topBox.Add(controlBox, 1, wxGROW, 10)          controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
734            self.controlButtons.append(button)
735    
736            button = wxButton(panel, ID_CLASSIFY_GENRANGE, _("Generate Ranges"))
737            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
738            self.controlButtons.append(button)
739    
740            button = wxButton(panel, ID_CLASSIFY_MOVEUP, _("Move Up"))
741            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
742            self.controlButtons.append(button)
743    
744            button = wxButton(panel, ID_CLASSIFY_MOVEDOWN, _("Move Down"))
745            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
746            self.controlButtons.append(button)
747    
748            controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
749    
750          EVT_BUTTON(self, ID_CLASSIFY_ADD, self.OnAdd)          button = wxButton(panel, ID_CLASSIFY_REMOVE, _("Remove"))
751          EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self.OnGenRange)          controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
752          EVT_GRID_CELL_LEFT_DCLICK(self.classGrid, self.OnCellDClick)          self.controlButtons.append(button)
753    
754    
755            ###########
756          #          #
757          # Control buttons:          # Classification data table
758          #          #
759    
760            self.classGrid = ClassGrid(panel)
761            #self.__SetGridTable(self.__cur_field, group)
762            #self.fields.SetSelection(self.__cur_field)
763    
764            # calling __SelectField after creating the classGrid fills in the
765            # grid with the correct information
766            print "2------------------"
767            self.__SelectField(self.__cur_field, group = group)
768    
769            #self.classGrid.SelectGroup(group)
770    
771            controlBox.Add(self.classGrid, 1, wxGROW, 0)
772    
773    
774    
775            controlBox.Add(controlButtonBox, 0, wxGROW, 10)
776            panelBox.Add(controlBox, 1, wxGROW, 10)
777    
778            EVT_BUTTON(self, ID_CLASSIFY_ADD, self._OnAdd)
779            EVT_BUTTON(self, ID_CLASSIFY_REMOVE, self._OnRemove)
780            EVT_BUTTON(self, ID_CLASSIFY_GENRANGE, self._OnGenRange)
781            EVT_BUTTON(self, ID_CLASSIFY_MOVEUP, self._OnMoveUp)
782            EVT_BUTTON(self, ID_CLASSIFY_MOVEDOWN, self._OnMoveDown)
783    
784            ###########
785    
786          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
787          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(panel, ID_CLASSIFY_OK, _("OK")),
788                        0, wxALL, 4)                        0, wxALL, 4)
789          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(60, 20, 0, wxALL, 4)
790            buttonBox.Add(wxButton(panel, ID_CLASSIFY_APPLY, _("Apply")),
791                        0, wxALL, 4)                        0, wxALL, 4)
792          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          buttonBox.Add(60, 20, 0, wxALL, 4)
793            buttonBox.Add(wxButton(panel, ID_CLASSIFY_CANCEL, _("Cancel")),
794                          0, wxALL, 4)
795            panelBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
796    
797            EVT_BUTTON(self, ID_CLASSIFY_OK, self._OnOK)
798            EVT_BUTTON(self, ID_CLASSIFY_APPLY, self._OnApply)
799            EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self._OnCancel)
800    
801            ###########
802    
         EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)  
         EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)  
803    
804          self.SetAutoLayout(true)          panel.SetAutoLayout(True)
805            panel.SetSizer(panelBox)
806            panelBox.SetSizeHints(panel)
807    
808            topBox.Add(panel, 1, wxGROW, 0)
809            panelBox.SetSizeHints(self)
810            self.SetAutoLayout(True)
811          self.SetSizer(topBox)          self.SetSizer(topBox)
         topBox.Fit(self)  
         topBox.SetSizeHints(self)  
812    
813      def __BuildClassification(self, prop):          print "1------------------"
814            #self.Fit()
815            ######################
816    
817          clazz = Classification()          self.haveApplied = False
818          clazz.SetField(self.properties.GetStringSelection())  
819        def __BuildClassification(self, fieldIndex):
820    
821          numRows = self.classGrid.GetNumberRows()          numRows = self.classGrid.GetNumberRows()
822            assert(numRows > 0) # there should always be a default row
823    
824          if numRows > 0:          clazz = Classification()
825              table = self.classGrid.GetTable()          if fieldIndex == 0:
826              clazz.SetDefaultData(table.GetClassData(0))              fieldName = None
827                fieldType = None
828            else:
829                fieldName = self.fields.GetString(fieldIndex)
830                fieldType = self.layer.GetFieldType(fieldName)
831    
832            clazz.SetField(fieldName)
833            clazz.SetFieldType(fieldType)
834    
835    
836            table = self.classGrid.GetTable()
837            clazz.SetDefaultGroup(table.GetClassGroup(0))
838    
839              for i in range(1, numRows):          for i in range(1, numRows):
840                  clazz.AddClassData(table.GetClassData(i))              clazz.AddGroup(table.GetClassGroup(i))
841    
842          return clazz          return clazz
843    
844      def OnPropertySelect(self, event):      def __SetGridTable(self, fieldIndex, group = None):
         self.properties.SetClientData(  
             self.__cur_prop, self.__BuildClassification(self.__cur_prop))  
845    
846          self.__cur_prop = self.properties.GetSelection()          clazz = self.fields.GetClientData(fieldIndex)
847          clazz = self.properties.GetClientData(self.__cur_prop)  
848          table = self.classGrid.GetTable()          if clazz is None:
849                clazz = Classification()
850                clazz.SetDefaultGroup(
851                    ClassGroupDefault(
852                        self.layer.GetClassification().
853                                   GetDefaultGroup().GetProperties()))
854    
855                fieldName = self.fields.GetString(fieldIndex)
856                fieldType = self.layer.GetFieldType(fieldName)
857                clazz.SetFieldType(fieldType)
858                    
859            print "6------------------"
860            self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
861            print "7------------------"
862    
863    
864    
865        type2string = {None:             _("None"),
866                       FIELDTYPE_STRING: _("Text"),
867                       FIELDTYPE_INT:    _("Integer"),
868                       FIELDTYPE_DOUBLE: _("Decimal")}
869    
870        def __SetFieldTypeText(self, fieldIndex):
871            fieldName = self.fields.GetString(fieldIndex)
872            fieldType = self.layer.GetFieldType(fieldName)
873    
874            assert(Classifier.type2string.has_key(fieldType))
875    
876            text = Classifier.type2string[fieldType]
877    
878            self.fieldTypeText.SetLabel(_("Field Type: %s") % text)
879    
880        def __SelectField(self, newIndex, oldIndex = -1, group = None):
881    
882          table.Reset(clazz, self.layer.ShapeType())          print "3------------------"
883    
884      def OnOK(self, event):          assert(oldIndex >= -1)
885    
886            self.fields.SetSelection(newIndex)
887    
888            if oldIndex != -1:
889                clazz = self.__BuildClassification(oldIndex)
890                self.fields.SetClientData(oldIndex, clazz)
891    
892            print "4------------------"
893            self.__SetGridTable(newIndex, group)
894            print "5------------------"
895    
896            enabled = newIndex != 0
897    
898            for b in self.controlButtons:
899                b.Enable(enabled)
900    
901            self.__SetFieldTypeText(newIndex)
902    
903    
904        def _OnFieldSelect(self, event):
905            index = self.fields.GetSelection()
906            self.__SelectField(index, self.__cur_field)
907            self.__cur_field = index
908    
909        def _OnApply(self, event):
910          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
911             it to the layer.             it to the layer.
912          """          """
913    
914          clazz = self.properties.GetClientData(self.__cur_prop)          clazz = self.fields.GetClientData(self.__cur_field)
915    
916          #          #
917          # only build the classification if there wasn't one to          # only build the classification if there wasn't one to
918          # to begin with or it has been modified          # to begin with or it has been modified
919          #          #
920          if clazz is None or self.classGrid.GetTable().IsModified():          if clazz is None or self.classGrid.GetTable().IsModified():
921              clazz = self.__BuildClassification(self.__cur_prop)              clazz = self.__BuildClassification(self.__cur_field)
   
         clazz.SetLayer(self.layer)  
922    
923          self.layer.SetClassification(clazz)          self.layer.SetClassification(clazz)
924    
925          self.EndModal(wxID_OK)          self.haveApplied = True
   
     def OnCancel(self, event):  
         """Do nothing. The layer's current classification stays the same."""  
         self.EndModal(wxID_CANCEL)  
   
926    
927      def OnAdd(self, event):      def _OnOK(self, event):
928          self.classGrid.GetTable().AddNewDataRow()          self._OnApply(event)
929          print "Classifier.OnAdd()"          self.OnClose(event)
930    
931        def _OnCancel(self, event):
932            """The layer's current classification stays the same."""
933            if self.haveApplied:
934                self.layer.SetClassification(self.originalClass)
935    
936            self.OnClose(event)
937    
938        def _OnAdd(self, event):
939            self.classGrid.AppendRows()
940    
941        def _OnRemove(self, event):
942            self.classGrid.DeleteSelectedRows()
943    
944        def _OnGenRange(self, event):
945            print "Classifier._OnGenRange()"
946    
947        def _OnMoveUp(self, event):
948            sel = self.classGrid.GetCurrentSelection()
949    
950            print "sel: ", sel
951    
952            if len(sel) == 1:
953                i = sel[0]
954                if i > 1:
955                    table = self.classGrid.GetTable()
956                    x = table.GetClassGroup(i - 1)
957                    y = table.GetClassGroup(i)
958                    table.SetClassGroup(i - 1, y)
959                    table.SetClassGroup(i, x)
960                    self.classGrid.ClearSelection()
961                    self.classGrid.SelectRow(i - 1)
962                    self.classGrid.MakeCellVisible(i - 1, 0)
963    
964      def OnGenRange(self, event):      def _OnMoveDown(self, event):
965          print "Classifier.OnGenRange()"          sel = self.classGrid.GetCurrentSelection()
966    
967      def OnCellDClick(self, event):          if len(sel) == 1:
968          r = event.GetRow()              i = sel[0]
969          c = event.GetCol()              table = self.classGrid.GetTable()
970          if c == COL_VISUAL:              if 0 < i < table.GetNumberRows() - 1:
971              prop = self.classGrid.GetTable().GetValueAsCustom(r, c, None)                  x = table.GetClassGroup(i)
972              propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())                  y = table.GetClassGroup(i + 1)
973              if propDlg.ShowModal() == wxID_OK:                  table.SetClassGroup(i, y)
974                  new_prop = propDlg.GetClassData()                  table.SetClassGroup(i + 1, x)
975                  prop.SetStroke(new_prop.GetStroke())                  self.classGrid.ClearSelection()
976                  prop.SetStrokeWidth(new_prop.GetStrokeWidth())                  self.classGrid.SelectRow(i + 1)
977                  prop.SetFill(new_prop.GetFill())                  self.classGrid.MakeCellVisible(i + 1, 0)
                 self.classGrid.BeginBatch()  
                 self.classGrid.EndBatch()  
             propDlg.Destroy()  
978    
979    
980  ID_SELPROP_OK = 4001  ID_SELPROP_OK = 4001
981  ID_SELPROP_CANCEL = 4002  ID_SELPROP_CANCEL = 4002
982  ID_SELPROP_SPINCTRL = 4002  ID_SELPROP_SPINCTRL = 4002
983    ID_SELPROP_PREVIEW = 4003
984    ID_SELPROP_STROKECLR = 4004
985    ID_SELPROP_FILLCLR = 4005
986    ID_SELPROP_STROKECLRTRANS = 4006
987    ID_SELPROP_FILLCLRTRANS = 4007
988    
989  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wxDialog):
990    
991      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
992          wxDialog.__init__(self, parent, -1, _("Select Properties"),          wxDialog.__init__(self, parent, -1, _("Select Properties"),
993                            style = wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
994    
995            self.prop = ClassGroupProperties(prop)
996    
997          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
   
         self.prop = ClassData(classData = prop)  
998    
999          topBox.Add(wxStaticText(self, -1, _("Stroke Color: ")),          itemBox = wxBoxSizer(wxHORIZONTAL)
1000              0, wxALIGN_LEFT | wxALL, 4)  
1001            # preview box
1002            previewBox = wxBoxSizer(wxVERTICAL)
1003            previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1004                0, wxALIGN_LEFT | wxALL, 4)
1005            self.previewWin = ClassDataPreviewWindow(None, self.prop, shapeType,
1006                                                self, ID_SELPROP_PREVIEW, (40, 40))
1007            previewBox.Add(self.previewWin, 1, wxGROW, 15)
1008    
1009            itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1010    
1011            # control box
1012            ctrlBox = wxBoxSizer(wxVERTICAL)
1013    
1014            lineColorBox = wxBoxSizer(wxHORIZONTAL)
1015            lineColorBox.Add(
1016                wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),
1017                1, wxALL | wxGROW, 4)
1018            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1019    
1020            lineColorBox.Add(
1021                wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1022                1, wxALL | wxGROW, 4)
1023            EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1024                       self._OnChangeLineColorTrans)
1025    
1026            ctrlBox.Add(lineColorBox, 0,
1027                        wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1028    
1029            if shapeType != SHAPETYPE_ARC:
1030                fillColorBox = wxBoxSizer(wxHORIZONTAL)
1031                fillColorBox.Add(
1032                    wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1033                    1, wxALL | wxGROW, 4)
1034                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1035                fillColorBox.Add(
1036                    wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1037                    1, wxALL | wxGROW, 4)
1038                EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1039                           self._OnChangeFillColorTrans)
1040                ctrlBox.Add(fillColorBox, 0,
1041                            wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1042    
1043          spinBox = wxBoxSizer(wxHORIZONTAL)          spinBox = wxBoxSizer(wxHORIZONTAL)
1044          spinBox.Add(wxStaticText(self, -1, _("Stroke Width: ")),          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1045                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxBOTTOM, 4)                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1046          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1047                                     min=1, max=10,                                     min=1, max=10,
1048                                     value=str(prop.GetStrokeWidth()),                                     value=str(prop.GetLineWidth()),
1049                                     initial=prop.GetStrokeWidth())                                     initial=prop.GetLineWidth())
1050    
1051          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self.OnSpin)          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1052    
1053          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1054    
1055          topBox.Add(spinBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1056            itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1057          if shapeType != SHAPETYPE_ARC:          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
             topBox.Add(wxStaticText(self, -1, _("Fill Color: ")),  
                 0, wxALIGN_LEFT | wxBOTTOM, 4)  
1058    
1059          #          #
1060          # Control buttons:          # Control buttons:
1061          #          #
1062          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1063          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
1064                        0, wxALL, 4)                        0, wxALL, 4)
1065          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
1066                        0, wxALL, 4)                        0, wxALL, 4)
1067          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1068                                                                                                                                                                    
1069          EVT_BUTTON(self, ID_SELPROP_OK, self.OnOK)          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
1070          EVT_BUTTON(self, ID_SELPROP_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1071                                                                                                                                                                    
1072          self.SetAutoLayout(true)          self.SetAutoLayout(True)
1073          self.SetSizer(topBox)          self.SetSizer(topBox)
1074          topBox.Fit(self)          topBox.Fit(self)
1075          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1076    
1077      def OnOK(self, event):      def _OnOK(self, event):
1078          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1079    
1080      def OnCancel(self, event):      def _OnCancel(self, event):
1081          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1082    
1083      def OnSpin(self, event):      def _OnSpin(self, event):
1084          self.prop.SetStrokeWidth(self.spinCtrl.GetValue())          self.prop.SetLineWidth(self.spinCtrl.GetValue())
1085            self.previewWin.Refresh()
1086    
1087        def __GetColor(self, cur):
1088            dialog = wxColourDialog(self)
1089            dialog.GetColourData().SetColour(Color2wxColour(cur))
1090            ret = None
1091            if dialog.ShowModal() == wxID_OK:
1092                ret = wxColour2Color(dialog.GetColourData().GetColour())
1093    
1094            dialog.Destroy()
1095    
1096            return ret
1097            
1098        def _OnChangeLineColor(self, event):
1099            clr = self.__GetColor(self.prop.GetLineColor())
1100            if clr is not None:
1101                self.prop.SetLineColor(clr)
1102            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1103    
1104        def _OnChangeLineColorTrans(self, event):
1105            self.prop.SetLineColor(Color.None)
1106            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1107            
1108        def _OnChangeFillColor(self, event):
1109            clr = self.__GetColor(self.prop.GetFill())
1110            if clr is not None:
1111                self.prop.SetFill(clr)
1112            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1113    
1114        def _OnChangeFillColorTrans(self, event):
1115            self.prop.SetFill(Color.None)
1116            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1117    
1118      def GetClassData(self):      def GetClassGroupProperties(self):
1119          return self.prop          return self.prop
1120    
1121    
1122    class ClassDataPreviewWindow(wxWindow):
1123    
1124        def __init__(self, rect, prop, shapeType,
1125                           parent = None, id = -1, size = wxDefaultSize):
1126            if parent is not None:
1127                wxWindow.__init__(self, parent, id, (0, 0), size)
1128                EVT_PAINT(self, self._OnPaint)
1129    
1130            self.rect = rect
1131    
1132            self.prop = prop
1133            self.shapeType = shapeType
1134            self.previewer = ClassDataPreviewer()
1135    
1136        def _OnPaint(self, event):
1137            dc = wxPaintDC(self)
1138    
1139            # XXX: this doesn't seem to be having an effect:
1140            dc.DestroyClippingRegion()
1141    
1142            if self.rect is None:
1143                w, h = self.GetSize()
1144                rect = wxRect(0, 0, w, h)
1145            else:
1146                rect = self.rect
1147    
1148            self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1149    
1150  class ClassDataPreviewer:  class ClassDataPreviewer:
1151    
1152      def Draw(self, dc, rect, data, shapeType):      def Draw(self, dc, rect, prop, shapeType):
1153    
1154            assert(dc is not None)
1155            assert(isinstance(prop, ClassGroupProperties))
1156    
1157          stroke = data.GetStroke()          if rect is None:
1158                x = 0
1159                y = 0
1160                w, h = dc.GetSize()
1161            else:
1162                x = rect.GetX()
1163                y = rect.GetY()
1164                w = rect.GetWidth()
1165                h = rect.GetHeight()
1166    
1167            stroke = prop.GetLineColor()
1168          if stroke is Color.None:          if stroke is Color.None:
1169              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1170          else:          else:
1171              pen = wxPen(wxColour(stroke.red * 255,              pen = wxPen(Color2wxColour(stroke),
1172                                   stroke.green * 255,                          prop.GetLineWidth(),
                                  stroke.blue * 255),  
                         data.GetStrokeWidth(),  
1173                          wxSOLID)                          wxSOLID)
1174    
1175          stroke = data.GetFill()          stroke = prop.GetFill()
1176          if stroke is Color.None:          if stroke is Color.None:
1177              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1178          else:          else:
1179              brush = wxBrush(wxColour(stroke.red * 255,              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
                                      stroke.green * 255,  
                                      stroke.blue * 255), wxSOLID)  
1180    
1181          dc.SetPen(pen)          dc.SetPen(pen)
1182          dc.SetBrush(brush)          dc.SetBrush(brush)
1183    
1184          if shapeType == SHAPETYPE_ARC:          if shapeType == SHAPETYPE_ARC:
1185              dc.DrawSpline([wxPoint(rect.GetX(), rect.GetY() + rect.GetHeight()),              dc.DrawSpline([wxPoint(x, y + h),
1186                             wxPoint(rect.GetX() + rect.GetWidth()/2,                             wxPoint(x + w/2, y + h/4),
1187                                     rect.GetY() + rect.GetHeight()/4),                             wxPoint(x + w/2, y + h/4*3),
1188                             wxPoint(rect.GetX() + rect.GetWidth()/2,                             wxPoint(x + w, y)])
                                    rect.GetY() + rect.GetHeight()/4*3),  
                            wxPoint(rect.GetX() + rect.GetWidth(), rect.GetY())])  
1189    
1190          elif shapeType == SHAPETYPE_POINT or \          elif shapeType == SHAPETYPE_POINT or \
1191               shapeType == SHAPETYPE_POLYGON:               shapeType == SHAPETYPE_POLYGON:
1192    
1193              dc.DrawCircle(rect.GetX() + rect.GetWidth()/2,              dc.DrawCircle(x + w/2, y + h/2,
1194                            rect.GetY() + rect.GetHeight()/2,                            (min(w, h) - prop.GetLineWidth())/2)
                           (min(rect.GetWidth(), rect.GetHeight())  
                            - data.GetStrokeWidth())/2)  
1195    
1196  class ClassRenderer(wxPyGridCellRenderer):  class ClassRenderer(wxPyGridCellRenderer):
1197    
1198      def __init__(self, shapeType):      def __init__(self, shapeType):
1199          wxPyGridCellRenderer.__init__(self)          wxPyGridCellRenderer.__init__(self)
1200          self.shapeType = shapeType          self.shapeType = shapeType
1201            self.previewer = ClassDataPreviewer()
1202    
1203      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1204          data = grid.GetTable().GetValueAsCustom(row, col, "")          data = grid.GetTable().GetClassGroup(row)
   
1205    
1206          dc.SetClippingRegion(rect.GetX(), rect.GetY(),          dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1207                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
# Line 526  class ClassRenderer(wxPyGridCellRenderer Line 1210  class ClassRenderer(wxPyGridCellRenderer
1210          dc.DrawRectangle(rect.GetX(), rect.GetY(),          dc.DrawRectangle(rect.GetX(), rect.GetY(),
1211                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1212    
1213          ClassDataPreviewer().Draw(dc, rect, data, self.shapeType)          if not isinstance(data, ClassGroupMap):
1214                self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1215    
1216          if isSelected:          if isSelected:
1217              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),              dc.SetPen(wxPen(wxColour(0 * 255, 0 * 255, 0 * 255),

Legend:
Removed from v.415  
changed lines
  Added in v.570

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26