/[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 398 by jonathan, Tue Feb 11 14:23:45 2003 UTC revision 671 by bh, Tue Apr 15 18:09:47 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.classification import Classification  from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
29    
30  from Thuban.Model.layer import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.UI.classgen import ClassGenDialog, ClassGenerator
31    
32    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)
264    
265          self.tdata = []          assert len(ClassTable.__col_labels) == NUM_COLS
266    
267            self.clazz = None
268            self.__colAttr = {}
269    
270          self.tdata.append([clinfo.DefaultData, 'DEFAULT'])          self.SetView(view)
271    
272          for value, data in clinfo.points.items():      def Reset(self, clazz, shapeType, group = None):
273              self.tdata.append([data, value])          """Reset the table with the given data.
274    
275          for range in self.clinfo.ranges:          This is necessary because wxWindows does not allow a grid's
276              self.tdata.append([range[2], '%s-%s' % range[0], range[1]])          table to change once it has been intially set and so we
277            need a way of modifying the data.
278    
279          self.SetColLabelValue(1, _("Data Values"))          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        def GetClassification(self):
313            return self.clazz
314    
315        def SetClassification(self, clazz, group = None):
316    
317            self.GetView().BeginBatch()
318    
319            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                    conv = lambda p: int(float(p))
482                else:
483                    conv = lambda p: p
484    
485                #
486                # first try to take the input as a single number
487                # if there's an exception try to break it into
488                # a range seperated by a '-'. take care to ignore
489                # a leading '-' as that could be for a negative number.
490                # then try to parse the individual parts. if there
491                # is an exception here, let it pass up to the calling
492                # function.
493                #
494                try:
495                    return (conv(Str2Num(value)),)
496                except ValueError:
497                    i = value.find('-')
498                    if i == 0:
499                        i = value.find('-', 1)
500    
501                    return (conv(Str2Num(value[:i])), conv(Str2Num(value[i+1:])))
502    
503            assert False  # shouldn't get here
504            return (0,)
505                
506    
507        def SetValueAsCustom(self, row, col, typeName, value):
508            """Set the cell specified by 'row' and 'col' to 'value'.
509    
510            If column represents the value column, the input is parsed
511            to determine if a string, number, or range was entered.
512            A new ClassGroup may be created if the type of data changes.
513    
514            The table is considered modified after this operation.
515    
516            typeName -- unused, but needed to overload wxPyGridTableBase
517            """
518    
519            assert 0 <= col < self.GetNumberCols()
520            assert 0 <= row < self.GetNumberRows()
521    
522            if row == 0:
523                group = self.clazz.GetDefaultGroup()
524            else:
525                group = self.clazz.GetGroup(row - 1)
526    
527            mod = True # assume the data will change
528    
529            if col == COL_VISIBLE:
530                group.SetVisible(value)
531            elif col == COL_SYMBOL:
532                group.SetProperties(value)
533            elif col == COL_LABEL:
534                group.SetLabel(value)
535            elif col == COL_VALUE:
536                if isinstance(group, ClassGroupDefault):
537                    # not allowed to modify the default value
538                    pass
539                elif isinstance(group, ClassGroupMap):
540                    # something special
541                    pass
542                else: # SINGLETON, RANGE
543                    try:
544                        dataInfo = self.__ParseInput(value)
545                    except ValueError:
546                        # bad input, ignore the request
547                        mod = False
548                    else:
549    
550                        changed = False
551                        ngroup = group
552                        props = group.GetProperties()
553    
554                        #
555                        # try to update the values, which may include
556                        # changing the underlying group type if the
557                        # group was a singleton and a range was entered
558                        #
559                        if len(dataInfo) == 1:
560                            if not isinstance(group, ClassGroupSingleton):
561                                ngroup = ClassGroupSingleton(prop = props)
562                                changed = True
563                            ngroup.SetValue(dataInfo[0])
564                        elif len(dataInfo) == 2:
565                            if not isinstance(group, ClassGroupRange):
566                                ngroup = ClassGroupRange(prop = props)
567                                changed = True
568                            ngroup.SetRange(dataInfo[0], dataInfo[1])
569                        else:
570                            assert False
571                            pass
572    
573                        if changed:
574                            ngroup.SetLabel(group.GetLabel())
575                            self.SetClassGroup(row, ngroup)
576            else:
577                assert False # shouldn't be here
578                pass
579    
580            if mod:
581                self.__Modified()
582                self.GetView().Refresh()
583    
584        def GetAttr(self, row, col, someExtraParameter):
585            """Returns the cell attributes"""
586    
587            return self.__colAttr.get(col, wxGridCellAttr()).Clone()
588    
589        def GetClassGroup(self, row):
590            """Return the ClassGroup object representing row 'row'."""
591    
592            #return self.GetValueAsCustom(row, COL_SYMBOL, None)
593            if row == 0:
594                return self.clazz.GetDefaultGroup()
595            else:
596                return self.clazz.GetGroup(row - 1)
597    
598  class Classifier(wxDialog):      def SetClassGroup(self, row, group):
599            self.__SetRow(row, group)
600            self.GetView().Refresh()
601    
602        def __Modified(self, mod = True):
603            """Adjust the modified flag.
604    
605            mod -- if -1 set the modified flag to False, otherwise perform
606                   an 'or' operation with the current value of the flag and
607                   'mod'
608            """
609    
610            if mod == -1:
611                self.modified = False
612            else:
613                self.modified = mod or self.modified
614    
615        def IsModified(self):
616            """True if this table is considered modified."""
617            return self.modified
618    
619        def DeleteRows(self, pos, numRows = 1):
620            """Deletes 'numRows' beginning at row 'pos'.
621    
622            The row representing the default group is not removed.
623    
624            The table is considered modified if any rows are removed.
625            """
626    
627            assert pos >= 0
628            old_len = self.GetNumberRows()
629            for row in range(pos, pos - numRows, -1):
630                group = self.GetClassGroup(row)
631                if row != 0:
632                    self.clazz.RemoveGroup(row - 1)
633                    self.__Modified()
634            
635      def __init__(self, parent, layer):          if self.IsModified():
636          wxDialog.__init__(self, parent, -1, _("Classify"),              self.__NotifyRowChanges(old_len, self.GetNumberRows())
637                            style = wxRESIZE_BORDER)  
638        def AppendRows(self, numRows = 1):
639            """Append 'numRows' empty rows to the end of the table.
640    
641            The table is considered modified if any rows are appended.
642            """
643    
644            old_len = self.GetNumberRows()
645            for i in range(numRows):
646                np = ClassGroupSingleton()
647                self.__SetRow(None, np)
648    
649            if self.IsModified():
650                self.__NotifyRowChanges(old_len, self.GetNumberRows())
651    
652    
653    ID_PROPERTY_OK = 4001
654    ID_PROPERTY_REVERT = 4002
655    ID_PROPERTY_ADD = 4003
656    ID_PROPERTY_GENCLASS = 4004
657    ID_PROPERTY_REMOVE = 4005
658    ID_PROPERTY_MOVEUP = 4006
659    ID_PROPERTY_MOVEDOWN = 4007
660    ID_PROPERTY_TRY = 4008
661    ID_PROPERTY_EDITSYM = 4009
662    ID_PROPERTY_CLOSE = 4010
663    ID_PROPERTY_SELECT = 4011
664    ID_PROPERTY_TITLE = 4012
665    ID_PROPERTY_FIELDTEXT = 4013
666    
667    BTN_ADD = 0
668    BTN_EDIT = 1
669    BTN_GEN = 2
670    BTN_UP = 3
671    BTN_DOWN = 4
672    BTN_RM = 5
673    
674    EB_LAYER_TITLE = 0
675    EB_SELECT_FIELD = 1
676    EB_GEN_CLASS = 2
677    
678    class Classifier(NonModalDialog):
679    
680        type2string = {None:             _("None"),
681                       FIELDTYPE_STRING: _("Text"),
682                       FIELDTYPE_INT:    _("Integer"),
683                       FIELDTYPE_DOUBLE: _("Decimal")}
684    
685        def __init__(self, parent, name, layer, group = None):
686            NonModalDialog.__init__(self, parent, name, "")
687    
688            self.__SetTitle(layer.Title())
689    
690            self.layer = layer
691    
692            self.originalClass = self.layer.GetClassification()
693            field = self.originalClass.GetField()
694            fieldType = self.originalClass.GetFieldType()
695    
696            self.genDlg = None
697    
698          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
699    
700          propertyBox = wxBoxSizer(wxHORIZONTAL)          panel = wxPanel(self, -1, size=(100, 100))
701          propertyBox.Add(wxStaticText(self, -1, _("Property")),  
702              0, wxALIGN_CENTER | wxALL, 4)          panelBox = wxBoxSizer(wxVERTICAL)
703    
704          self.properties = wxComboBox(self, ID_PROPERTY_SELECT, "",          sizer = wxBoxSizer(wxHORIZONTAL)
705            sizer.Add(wxStaticText(panel, -1, _("Title: ")),
706                0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
707            sizer.Add(wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title()),
708                      1, wxGROW | wxALL, 4)
709            EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
710    
711            panelBox.Add(sizer, 0, wxGROW, 4)
712    
713            panelBox.Add(wxStaticText(panel, -1,
714                                    _("Type: %s") % layer.ShapeType()),
715                0, wxALIGN_LEFT | wxALL, 4)
716    
717    
718            #####################
719    
720            #panelBox = wxBoxSizer(wxVERTICAL)
721            classBox = wxStaticBoxSizer(
722                        wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
723    
724    
725            #
726            # make field combo box
727            #
728            self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",
729                                       style = wxCB_READONLY)                                       style = wxCB_READONLY)
730    
731          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
732          cur_hilight = 0          # just assume the first field in case one hasn't been
733            # specified in the file.
734            self.__cur_field = 0
735    
736            self.fields.Append("<None>")
737    
738            if self.originalClass.GetFieldType() is None:
739                self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
740            else:
741                self.fields.SetClientData(0, None)
742    
743          for i in range(self.num_cols):          for i in range(self.num_cols):
744              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
745              if name == layer.classification.field:              self.fields.Append(name)
746                  cur_hilight = i  
747              self.properties.Append(name)              if name == field:
748                    self.__cur_field = i + 1
749          self.properties.SetSelection(cur_hilight)                  self.fields.SetClientData(i + 1,
750          propertyBox.Add(self.properties, 1, wxGROW, 4)                                            copy.deepcopy(self.originalClass))
751          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnPropertySelect)              else:
752                    self.fields.SetClientData(i + 1, None)
753    
754    
755            ###########
756    
757    
758            sizer = wxBoxSizer(wxHORIZONTAL)
759            sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
760                0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
761            sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
762            EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
763    
764            classBox.Add(sizer, 0, wxGROW, 4)
765    
766            self.fieldTypeText = wxStaticText(panel, -1, "")
767            classBox.Add(self.fieldTypeText, 0,
768                         wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
769    
770    
771            #
772            # Control Box
773            #
774            controlBox = wxBoxSizer(wxHORIZONTAL)
775    
         topBox.Add(propertyBox, 0, wxGROW, 4)  
776    
777            ###########
778            #
779            # Control buttons:
780            #
781            controlButtonBox = wxBoxSizer(wxVERTICAL)
782    
783            button = wxButton(panel, ID_PROPERTY_GENCLASS, _("Generate Class"))
784            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
785    
786            button = wxButton(panel, ID_PROPERTY_ADD, _("Add"))
787            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
788    
789            button = wxButton(panel, ID_PROPERTY_MOVEUP, _("Move Up"))
790            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
791    
792            button = wxButton(panel, ID_PROPERTY_MOVEDOWN, _("Move Down"))
793            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
794    
795            button = wxButton(panel, ID_PROPERTY_EDITSYM, _("Edit Symbol"))
796            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
797    
798            controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
799    
800            button = wxButton(panel, ID_PROPERTY_REMOVE, _("Remove"))
801            controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
802    
803    
804            ###########
805          #          #
806          # Classification data table          # Classification data table
807          #          #
808    
809          self.classTable = wxGrid(self, ID_CLASS_TABLE, size=(300, 150))          self.classGrid = ClassGrid(panel, self)
810    
811            # calling __SelectField after creating the classGrid fills in the
812            # grid with the correct information
813            self.fields.SetSelection(self.__cur_field)
814            self.__SelectField(self.__cur_field, group = group)
815    
816            controlBox.Add(self.classGrid, 1, wxGROW, 0)
817            controlBox.Add(controlButtonBox, 0, wxGROW, 10)
818    
819            classBox.Add(controlBox, 1, wxGROW, 10)
820            panelBox.Add(classBox, 1, wxGROW, 0)
821    
822            EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
823            EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
824            EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
825            EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
826            EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
827            EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
828    
829            ###########
830    
         table = ClassTable(layer.classification)  
         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)  
831    
832          topBox.Add(self.classTable, 1, wxGROW, 0)          panel.SetAutoLayout(True)
833            panel.SetSizer(panelBox)
834            panelBox.SetSizeHints(panel)
835    
836            topBox.Add(panel, 1, wxGROW | wxALL, 4)
837    
838            ###########
839    
840            buttonBox = wxBoxSizer(wxHORIZONTAL)
841            buttonBox.Add(wxButton(self, ID_PROPERTY_TRY, _("Try")),
842                          0, wxALL, 4)
843            buttonBox.Add(60, 20, 0, wxALL, 4)
844            buttonBox.Add(wxButton(self, ID_PROPERTY_REVERT, _("Revert")),
845                          0, wxALL, 4)
846            buttonBox.Add(60, 20, 0, wxALL, 4)
847            buttonBox.Add(wxButton(self, ID_PROPERTY_OK, _("OK")),
848                          0, wxALL, 4)
849            buttonBox.Add(60, 20, 0, wxALL, 4)
850            buttonBox.Add(wxButton(self, ID_PROPERTY_CLOSE, _("Close")),
851                          0, wxALL, 4)
852            topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
853    
854            EVT_BUTTON(self, ID_PROPERTY_OK, self._OnOK)
855            EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
856            EVT_BUTTON(self, ID_PROPERTY_CLOSE, self._OnCloseBtn)
857            EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
858    
859            ###########
860    
861            topBox.SetSizeHints(self)
862            self.SetAutoLayout(True)
863            self.SetSizer(topBox)
864    
865            #self.Fit()
866            ######################
867    
868            self.haveApplied = False
869    
870        def EditSymbol(self, row):
871            table = self.classGrid.GetTable()
872            prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
873    
874            # get a new ClassGroupProperties object and copy the
875            # values over to our current object
876            propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
877    
878            self.Enable(False)
879            if propDlg.ShowModal() == wxID_OK:
880                new_prop = propDlg.GetClassGroupProperties()
881                table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
882            self.Enable(True)
883            propDlg.Destroy()
884            
885        def _SetClassification(self, clazz):
886            
887            self.fields.SetClientData(self.__cur_field, clazz)
888            self.classGrid.GetTable().SetClassification(clazz)
889    
890        def __BuildClassification(self, fieldIndex, copyClass = False):
891    
892    #       numRows = self.classGrid.GetNumberRows()
893    #       assert numRows > 0  # there should always be a default row
894    
895    #       clazz = Classification()
896            if fieldIndex == 0:
897                fieldName = None
898                fieldType = None
899            else:
900                fieldName = self.fields.GetString(fieldIndex)
901                fieldType = self.layer.GetFieldType(fieldName)
902    
903            clazz = self.classGrid.GetTable().GetClassification()
904    
905            if copyClass:
906                clazz = copy.deepcopy(clazz)
907    
908            clazz.SetField(fieldName)
909            clazz.SetFieldType(fieldType)
910    
911    
912    #       table = self.classGrid.GetTable()
913    #       clazz.SetDefaultGroup(table.GetClassGroup(0))
914    
915    #       for i in range(1, numRows):
916    #           clazz.AppendGroup(table.GetClassGroup(i))
917    
918            return clazz
919    
920        def __SetGridTable(self, fieldIndex, group = None):
921    
922            clazz = self.fields.GetClientData(fieldIndex)
923    
924            if clazz is None:
925                clazz = Classification()
926                clazz.SetDefaultGroup(
927                    ClassGroupDefault(
928                        self.layer.GetClassification().
929                                   GetDefaultGroup().GetProperties()))
930    
931                fieldName = self.fields.GetString(fieldIndex)
932                fieldType = self.layer.GetFieldType(fieldName)
933                clazz.SetFieldType(fieldType)
934                    
935            self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
936    
937        def __SetFieldTypeText(self, fieldIndex):
938            fieldName = self.fields.GetString(fieldIndex)
939            fieldType = self.layer.GetFieldType(fieldName)
940    
941            assert Classifier.type2string.has_key(fieldType)
942    
943            text = Classifier.type2string[fieldType]
944    
945            self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
946    
947        def __SelectField(self, newIndex, oldIndex = -1, group = None):
948            """This method assumes that the current selection for the
949            combo has already been set by a call to SetSelection().
950            """
951    
952            assert oldIndex >= -1
953    
954            if oldIndex != -1:
955                clazz = self.__BuildClassification(oldIndex)
956                self.fields.SetClientData(oldIndex, clazz)
957    
958            self.__SetGridTable(newIndex, group)
959    
960            self.__EnableButtons(EB_SELECT_FIELD, newIndex != 0)
961    
962            self.__SetFieldTypeText(newIndex)
963    
964        def __SetTitle(self, title):
965            if title != "":
966                title = ": " + title
967    
968            self.SetTitle(_("Layer Properties") + title)
969    
970        def _OnEditSymbol(self, event):
971            sel = self.classGrid.GetCurrentSelection()
972    
973            if len(sel) == 1:
974                self.EditSymbol(sel[0])
975    
976        def _OnFieldSelect(self, event):
977            index = self.fields.GetSelection()
978            self.__SelectField(index, self.__cur_field)
979            self.__cur_field = index
980    
981        def _OnTry(self, event):
982            """Put the data from the table into a new Classification and hand
983               it to the layer.
984            """
985    
986            clazz = self.fields.GetClientData(self.__cur_field)
987    
988            #
989            # only build the classification if there wasn't one to
990            # to begin with or it has been modified
991            #
992            if clazz is None or self.classGrid.GetTable().IsModified():
993                clazz = self.__BuildClassification(self.__cur_field, True)
994    
995            self.layer.SetClassification(clazz)
996    
997            self.haveApplied = True
998    
999        def _OnOK(self, event):
1000            self._OnTry(event)
1001            self.Close()
1002    
1003        def _OnCloseBtn(self, event):
1004            """Close is similar to Cancel except that any changes that were
1005            made and applied remain applied, but the currently displayed
1006            classification is discarded.
1007            """
1008    
1009            self.Close()
1010    
1011        def _OnRevert(self, event):
1012            """The layer's current classification stays the same."""
1013            if self.haveApplied:
1014                self.layer.SetClassification(self.originalClass)
1015    
1016            #self.Close()
1017    
1018        def _OnAdd(self, event):
1019            self.classGrid.AppendRows()
1020    
1021        def _OnRemove(self, event):
1022            self.classGrid.DeleteSelectedRows()
1023    
1024        def _OnGenClass(self, event):
1025    
1026            self.genDlg = ClassGenDialog(self, self.layer,
1027                              self.fields.GetString(self.__cur_field))
1028    
1029            EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1030    
1031            self.__EnableButtons(EB_GEN_CLASS, False)
1032    
1033            self.genDlg.Show()
1034    
1035        def _OnGenDialogClose(self, event):
1036            self.genDlg.Destroy()
1037            self.__EnableButtons(EB_GEN_CLASS, True)
1038    
1039        def _OnMoveUp(self, event):
1040            sel = self.classGrid.GetCurrentSelection()
1041    
1042            if len(sel) == 1:
1043                i = sel[0]
1044                if i > 1:
1045                    table = self.classGrid.GetTable()
1046                    x = table.GetClassGroup(i - 1)
1047                    y = table.GetClassGroup(i)
1048                    table.SetClassGroup(i - 1, y)
1049                    table.SetClassGroup(i, x)
1050                    self.classGrid.ClearSelection()
1051                    self.classGrid.SelectRow(i - 1)
1052                    self.classGrid.MakeCellVisible(i - 1, 0)
1053    
1054        def _OnMoveDown(self, event):
1055            sel = self.classGrid.GetCurrentSelection()
1056    
1057            if len(sel) == 1:
1058                i = sel[0]
1059                table = self.classGrid.GetTable()
1060                if 0 < i < table.GetNumberRows() - 1:
1061                    x = table.GetClassGroup(i)
1062                    y = table.GetClassGroup(i + 1)
1063                    table.SetClassGroup(i, y)
1064                    table.SetClassGroup(i + 1, x)
1065                    self.classGrid.ClearSelection()
1066                    self.classGrid.SelectRow(i + 1)
1067                    self.classGrid.MakeCellVisible(i + 1, 0)
1068    
1069        def _OnTitleChanged(self, event):
1070            obj = event.GetEventObject()
1071    
1072            self.layer.SetTitle(obj.GetValue())
1073            self.__SetTitle(self.layer.Title())
1074    
1075            self.__EnableButtons(EB_LAYER_TITLE, self.layer.Title() != "")
1076    
1077        def __EnableButtons(self, case, enable):
1078    
1079            if case == EB_LAYER_TITLE:  
1080                list = (ID_PROPERTY_OK,
1081                        ID_PROPERTY_CLOSE)
1082    
1083            elif case == EB_SELECT_FIELD:
1084                list = (ID_PROPERTY_GENCLASS,
1085                        ID_PROPERTY_ADD,
1086                        ID_PROPERTY_MOVEUP,
1087                        ID_PROPERTY_MOVEDOWN,
1088                        ID_PROPERTY_EDITSYM,
1089                        ID_PROPERTY_REMOVE)
1090    
1091            elif case == EB_GEN_CLASS:
1092                list = (ID_PROPERTY_SELECT,
1093                        ID_PROPERTY_FIELDTEXT,
1094                        ID_PROPERTY_GENCLASS,
1095                        ID_PROPERTY_EDITSYM)
1096    
1097            for id in list:
1098                self.FindWindowById(id).Enable(enable)
1099    
1100    ID_SELPROP_OK = 4001
1101    ID_SELPROP_CANCEL = 4002
1102    ID_SELPROP_SPINCTRL = 4002
1103    ID_SELPROP_PREVIEW = 4003
1104    ID_SELPROP_STROKECLR = 4004
1105    ID_SELPROP_FILLCLR = 4005
1106    ID_SELPROP_STROKECLRTRANS = 4006
1107    ID_SELPROP_FILLCLRTRANS = 4007
1108    
1109    class SelectPropertiesDialog(wxDialog):
1110    
1111        def __init__(self, parent, prop, shapeType):
1112            wxDialog.__init__(self, parent, -1, _("Select Properties"),
1113                              style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1114    
1115            self.prop = ClassGroupProperties(prop)
1116    
1117            topBox = wxBoxSizer(wxVERTICAL)
1118    
1119            itemBox = wxBoxSizer(wxHORIZONTAL)
1120    
1121            # preview box
1122            previewBox = wxBoxSizer(wxVERTICAL)
1123            previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1124                0, wxALIGN_LEFT | wxALL, 4)
1125    
1126            self.previewWin = ClassGroupPropertiesCtrl(
1127                self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1128                (40, 40), wxSIMPLE_BORDER)
1129    
1130            self.previewWin.AllowEdit(False)
1131    
1132            previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1133    
1134            itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1135    
1136            # control box
1137            ctrlBox = wxBoxSizer(wxVERTICAL)
1138    
1139            lineColorBox = wxBoxSizer(wxHORIZONTAL)
1140            lineColorBox.Add(
1141                wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),
1142                1, wxALL | wxGROW, 4)
1143            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1144    
1145            lineColorBox.Add(
1146                wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1147                1, wxALL | wxGROW, 4)
1148            EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1149                       self._OnChangeLineColorTrans)
1150    
1151            ctrlBox.Add(lineColorBox, 0,
1152                        wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1153    
1154            if shapeType != SHAPETYPE_ARC:
1155                fillColorBox = wxBoxSizer(wxHORIZONTAL)
1156                fillColorBox.Add(
1157                    wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1158                    1, wxALL | wxGROW, 4)
1159                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1160                fillColorBox.Add(
1161                    wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1162                    1, wxALL | wxGROW, 4)
1163                EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1164                           self._OnChangeFillColorTrans)
1165                ctrlBox.Add(fillColorBox, 0,
1166                            wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1167    
1168            spinBox = wxBoxSizer(wxHORIZONTAL)
1169            spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1170                    0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1171            self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1172                                       min=1, max=10,
1173                                       value=str(prop.GetLineWidth()),
1174                                       initial=prop.GetLineWidth())
1175    
1176            EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1177    
1178            spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1179    
1180            ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1181            itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1182            topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1183    
1184          #          #
1185          # Control buttons:          # Control buttons:
1186          #          #
1187          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1188          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
1189                        0, wxALL, 4)                        0, wxALL, 4)
1190          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
1191                        0, wxALL, 4)                        0, wxALL, 4)
1192          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1193                                                                                    
1194          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
1195          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1196                                                                                    
1197          self.SetAutoLayout(true)          self.SetAutoLayout(True)
1198          self.SetSizer(topBox)          self.SetSizer(topBox)
1199          topBox.Fit(self)          topBox.Fit(self)
1200          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1201    
1202      def OnPropertySelect(self, event): pass      def _OnOK(self, event):
   
     def OnOK(self, event):  
1203          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1204    
1205      def OnCancel(self, event):      def _OnCancel(self, event):
1206          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1207    
1208        def _OnSpin(self, event):
1209            self.prop.SetLineWidth(self.spinCtrl.GetValue())
1210            self.previewWin.Refresh()
1211    
1212        def __GetColor(self, cur):
1213            dialog = wxColourDialog(self)
1214            if cur is not Color.Transparent:
1215                dialog.GetColourData().SetColour(Color2wxColour(cur))
1216    
1217            ret = None
1218            if dialog.ShowModal() == wxID_OK:
1219                ret = wxColour2Color(dialog.GetColourData().GetColour())
1220    
1221            dialog.Destroy()
1222    
1223            return ret
1224            
1225        def _OnChangeLineColor(self, event):
1226            clr = self.__GetColor(self.prop.GetLineColor())
1227            if clr is not None:
1228                self.prop.SetLineColor(clr)
1229            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1230    
1231        def _OnChangeLineColorTrans(self, event):
1232            self.prop.SetLineColor(Color.Transparent)
1233            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1234            
1235        def _OnChangeFillColor(self, event):
1236            clr = self.__GetColor(self.prop.GetFill())
1237            if clr is not None:
1238                self.prop.SetFill(clr)
1239            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1240    
1241        def _OnChangeFillColorTrans(self, event):
1242            self.prop.SetFill(Color.Transparent)
1243            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1244    
1245        def GetClassGroupProperties(self):
1246            return self.prop
1247    
1248    
1249    class ClassDataPreviewWindow(wxWindow):
1250    
1251        def __init__(self, rect, prop, shapeType,
1252                           parent = None, id = -1, size = wxDefaultSize):
1253            if parent is not None:
1254                wxWindow.__init__(self, parent, id, (0, 0), size)
1255                EVT_PAINT(self, self._OnPaint)
1256    
1257  class ClassRenderer(wxPyGridCellRenderer):          self.rect = rect
1258    
1259      def __init__(self, shapeType):          self.prop = prop
         wxPyGridCellRenderer.__init__(self)  
1260          self.shapeType = shapeType          self.shapeType = shapeType
1261            self.previewer = ClassDataPreviewer()
1262    
1263      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def GetProperties():
1264          value = grid.GetTable().GetValueAsCustom(row, col, "")          return self.prop
1265          # XXX: check if value is a dictionary  
1266          stroke = value.GetStroke()      def _OnPaint(self, event):
1267          if stroke is None:          dc = wxPaintDC(self)
1268    
1269            # XXX: this doesn't seem to be having an effect:
1270            dc.DestroyClippingRegion()
1271    
1272            if self.rect is None:
1273                w, h = self.GetSize()
1274                rect = wxRect(0, 0, w, h)
1275            else:
1276                rect = self.rect
1277    
1278            self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1279    
1280    class ClassDataPreviewer:
1281    
1282        def Draw(self, dc, rect, prop, shapeType):
1283    
1284            assert dc is not None
1285            assert isinstance(prop, ClassGroupProperties)
1286    
1287            if rect is None:
1288                x = 0
1289                y = 0
1290                w, h = dc.GetSize()
1291            else:
1292                x = rect.GetX()
1293                y = rect.GetY()
1294                w = rect.GetWidth()
1295                h = rect.GetHeight()
1296    
1297            stroke = prop.GetLineColor()
1298            if stroke is Color.Transparent:
1299              pen = wxTRANSPARENT_PEN              pen = wxTRANSPARENT_PEN
1300          else:          else:
1301              pen = wxPen(wxColour(stroke.red * 255,              pen = wxPen(Color2wxColour(stroke),
1302                                   stroke.green * 255,                          prop.GetLineWidth(),
                                  stroke.blue * 255),  
                         value.GetStrokeWidth(),  
1303                          wxSOLID)                          wxSOLID)
1304    
1305          stroke = value.GetFill()          stroke = prop.GetFill()
1306          if stroke is None:          if stroke is Color.Transparent:
1307              brush = wxTRANSPARENT_BRUSH              brush = wxTRANSPARENT_BRUSH
1308          else:          else:
1309              brush = wxBrush(wxColour(stroke.red * 255,              brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1310                                       stroke.green * 255,  
1311                                       stroke.blue * 255), wxSOLID)          dc.SetPen(pen)
1312            dc.SetBrush(brush)
1313    
1314            if shapeType == SHAPETYPE_ARC:
1315                dc.DrawSpline([wxPoint(x, y + h),
1316                               wxPoint(x + w/2, y + h/4),
1317                               wxPoint(x + w/2, y + h/4*3),
1318                               wxPoint(x + w, y)])
1319    
1320            elif shapeType == SHAPETYPE_POINT:
1321    
1322                dc.DrawCircle(x + w/2, y + h/2,
1323                              (min(w, h) - prop.GetLineWidth())/2)
1324    
1325            elif shapeType == SHAPETYPE_POLYGON:
1326                dc.DrawRectangle(x, y, w, h)
1327    
1328    class ClassRenderer(wxPyGridCellRenderer):
1329    
1330        def __init__(self, shapeType):
1331            wxPyGridCellRenderer.__init__(self)
1332            self.shapeType = shapeType
1333            self.previewer = ClassDataPreviewer()
1334    
1335        def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1336            data = grid.GetTable().GetClassGroup(row)
1337    
1338          dc.SetClippingRegion(rect.GetX(), rect.GetY(),          dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1339                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
# Line 167  class ClassRenderer(wxPyGridCellRenderer Line 1342  class ClassRenderer(wxPyGridCellRenderer
1342          dc.DrawRectangle(rect.GetX(), rect.GetY(),          dc.DrawRectangle(rect.GetX(), rect.GetY(),
1343                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1344    
1345          dc.SetPen(pen)          if not isinstance(data, ClassGroupMap):
1346          dc.SetBrush(brush)              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1347    
1348            if isSelected:
1349                dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1350                dc.SetBrush(wxTRANSPARENT_BRUSH)
1351    
1352          if self.shapeType == SHAPETYPE_ARC:              dc.DrawRectangle(rect.GetX(), rect.GetY(),
1353              dc.DrawSpline([wxPoint(rect.GetX(), rect.GetY() + rect.GetHeight()),                               rect.GetWidth(), rect.GetHeight())
                            wxPoint(rect.GetX() + rect.GetWidth()/2,  
                                    rect.GetY() + rect.GetHeight()/4),  
                            wxPoint(rect.GetX() + rect.GetWidth()/2,  
                                    rect.GetY() + rect.GetHeight()/4*3),  
                            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)  
1354    
1355          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1356    
1357    
1358    class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1359    
1360        def __init__(self, parent, id, props, shapeType,
1361                     size = wxDefaultSize, style = 0):
1362    
1363            wxWindow.__init__(self, parent, id, size = size, style = style)
1364    
1365            self.SetProperties(props)
1366            self.SetShapeType(shapeType)
1367            self.AllowEdit(True)
1368    
1369            EVT_PAINT(self, self._OnPaint)
1370            EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1371    
1372            self.previewer = ClassDataPreviewer()
1373    
1374        def _OnPaint(self, event):
1375            dc = wxPaintDC(self)
1376    
1377            # XXX: this doesn't seem to be having an effect:
1378            dc.DestroyClippingRegion()
1379    
1380            w, h = self.GetClientSize()
1381    
1382            self.previewer.Draw(dc,
1383                                wxRect(0, 0, w, h),
1384                                self.GetProperties(),
1385                                self.GetShapeType())
1386    
1387    
1388        def GetProperties(self):
1389            return self.props
1390    
1391        def SetProperties(self, props):
1392            self.props = props
1393            self.Refresh()
1394    
1395        def GetShapeType(self):
1396            return self.shapeType
1397    
1398        def SetShapeType(self, shapeType):
1399            self.shapeType = shapeType
1400            self.Refresh()
1401    
1402        def AllowEdit(self, allow):
1403            self.allowEdit = allow
1404    
1405        def DoEdit(self):
1406            if not self.allowEdit: return
1407    
1408            propDlg = SelectPropertiesDialog(NULL,
1409                                             self.GetProperties(),
1410                                             self.GetShapeType())
1411    
1412            if propDlg.ShowModal() == wxID_OK:
1413                new_prop = propDlg.GetClassGroupProperties()
1414                self.SetProperties(new_prop)
1415                self.Refresh()
1416    
1417            propDlg.Destroy()
1418    
1419        def _OnLeftDClick(self, event):
1420            self.DoEdit()

Legend:
Removed from v.398  
changed lines
  Added in v.671

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26