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

Legend:
Removed from v.392  
changed lines
  Added in v.782

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26