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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26