/[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 376 by jonathan, Tue Jan 28 12:01:38 2003 UTC revision 650 by jonathan, Fri Apr 11 14:27:41 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 by Intevation GmbH  # Copyright (c) 2001, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jonathan Coles <[email protected]>
4  #  #
# Line 11  __version__ = "$Revision$" Line 11  __version__ = "$Revision$"
11    
12  import copy  import copy
13    
14    from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
15         FIELDTYPE_STRING
16    
17  from wxPython.wx import *  from wxPython.wx import *
18  from wxPython.grid import *  from wxPython.grid import *
19    
20  from Thuban import _  from Thuban import _
21    from Thuban.common import *
22    from Thuban.UI.common import *
23    
24    from Thuban.Model.classification import *
25    
26    from Thuban.Model.color import Color
27    
28    from Thuban.Model.layer import Layer, SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
29    
30    from Thuban.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)
         self.clinfo = copy.deepcopy(clinfo)  
264    
265          self.tdata = []          assert len(ClassTable.__col_labels) == NUM_COLS
266    
267            self.clazz = None
268            self.__colAttr = {}
269    
270            self.SetView(view)
271    
272        def Reset(self, clazz, shapeType, group = None):
273            """Reset the table with the given data.
274    
275            This is necessary because wxWindows does not allow a grid's
276            table to change once it has been intially set and so we
277            need a way of modifying the data.
278    
279          for value, data in self.clinfo.points.items():          clazz -- the working classification that this table should
280              self.tdata.append([data, value])                   use for display. This may be different from the
281                     classification in the layer.
282    
283          for range in self.clinfo.ranges:          shapeType -- the type of shape that the layer uses
284              self.tdata.append([range[2], '%s-%s' % range[0], range[1]])          """
285    
286          self.SetColLabelValue(1, "Data Values")          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          pass          """Assign 'value' to the cell specified by 'row' and 'col'.
       
425    
426            The table is considered modified after this operation.
427            """
428    
429  class Classifier(wxDialog):          self.SetValueAsCustom(row, col, None, value)
430          
431        def GetValueAsCustom(self, row, col, typeName):
432            """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        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)          sizer = wxBoxSizer(wxHORIZONTAL)
701          propertyBox.Add(wxStaticText(self, -1, _("Property")),          sizer.Add(wxStaticText(self, -1, _("Title: ")),
702              0, wxALIGN_CENTER | wxALL, 4)              0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)
703            sizer.Add(wxTextCtrl(self, ID_PROPERTY_TITLE, layer.Title()),
704                      1, wxGROW | wxALL, 4)
705            EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)
706    
707            topBox.Add(sizer, 0, wxGROW, 4)
708    
709            topBox.Add(wxStaticText(self, -1,
710                                    _("Type: %s") % layer.ShapeType()),
711                0, wxALIGN_LEFT | wxALL, 4)
712    
713            panel = wxPanel(self, -1, size=(100, 100))
714    
715            #####################
716    
717            #panelBox = wxBoxSizer(wxVERTICAL)
718            panelBox = wxStaticBoxSizer(
719                        wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)
720    
721    
722          self.properties = wxComboBox(self, ID_PROPERTY_SELECT, "",          #
723            # make field combo box
724            #
725            self.fields = wxComboBox(panel, ID_PROPERTY_SELECT, "",
726                                       style = wxCB_READONLY)                                       style = wxCB_READONLY)
727    
728          self.num_cols = layer.table.field_count()          self.num_cols = layer.table.field_count()
729          cur_hilight = 0          # just assume the first field in case one hasn't been
730            # specified in the file.
731            self.__cur_field = 0
732    
733            self.fields.Append("<None>")
734    
735            if self.originalClass.GetFieldType() is None:
736                self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
737            else:
738                self.fields.SetClientData(0, None)
739    
740          for i in range(self.num_cols):          for i in range(self.num_cols):
741              type, name, len, decc = layer.table.field_info(i)              type, name, len, decc = layer.table.field_info(i)
742              if name == layer.classification.field:              self.fields.Append(name)
743                  cur_hilight = i  
744              self.properties.Append(name)              if name == field:
745                    self.__cur_field = i + 1
746          self.properties.SetSelection(cur_hilight)                  self.fields.SetClientData(i + 1,
747          propertyBox.Add(self.properties, 0, wxGROW, 4)                                            copy.deepcopy(self.originalClass))
748          EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self.OnPropertySelect)              else:
749                    self.fields.SetClientData(i + 1, None)
750    
751    
752            ###########
753    
754    
755            sizer = wxBoxSizer(wxHORIZONTAL)
756            sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
757                0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
758            sizer.Add(self.fields, 1, wxGROW | wxALL, 4)
759            EVT_COMBOBOX(self, ID_PROPERTY_SELECT, self._OnFieldSelect)
760    
761            panelBox.Add(sizer, 0, wxGROW, 4)
762    
763            self.fieldTypeText = wxStaticText(panel, -1, "")
764            panelBox.Add(self.fieldTypeText, 0,
765                         wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)
766    
         topBox.Add(propertyBox, 0, wxGROW, 4)  
767    
768          #          #
769            # Control Box
770            #
771            controlBox = wxBoxSizer(wxHORIZONTAL)
772    
773    
774            ###########
775            #
776            # Control buttons:
777            #
778            controlButtonBox = wxBoxSizer(wxVERTICAL)
779    
780            button = wxButton(panel, ID_PROPERTY_GENCLASS, _("Generate Class"))
781            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
782    
783            button = wxButton(panel, ID_PROPERTY_ADD, _("Add"))
784            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
785    
786            button = wxButton(panel, ID_PROPERTY_MOVEUP, _("Move Up"))
787            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
788    
789            button = wxButton(panel, ID_PROPERTY_MOVEDOWN, _("Move Down"))
790            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
791    
792            button = wxButton(panel, ID_PROPERTY_EDITSYM, _("Edit Symbol"))
793            controlButtonBox.Add(button, 0, wxGROW | wxALL, 4)
794    
795            controlButtonBox.Add(60, 20, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
796    
797            button = wxButton(panel, ID_PROPERTY_REMOVE, _("Remove"))
798            controlButtonBox.Add(button, 0, wxGROW | wxALL | wxALIGN_BOTTOM, 4)
799    
800    
801            ###########
802            #
803          # Classification data table          # Classification data table
804          #          #
805    
806          self.classTable = wxGrid(self, ID_CLASS_TABLE, size=(300, 150))          self.classGrid = ClassGrid(panel, self)
807    
808            # calling __SelectField after creating the classGrid fills in the
809            # grid with the correct information
810            self.fields.SetSelection(self.__cur_field)
811            self.__SelectField(self.__cur_field, group = group)
812    
813            controlBox.Add(self.classGrid, 1, wxGROW, 0)
814    
815            controlBox.Add(controlButtonBox, 0, wxGROW, 10)
816            panelBox.Add(controlBox, 1, wxGROW, 10)
817    
818            EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)
819            EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)
820            EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)
821            EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)
822            EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)
823            EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)
824    
825            ###########
826    
827          table = ClassTable(layer.classification)          panel.SetAutoLayout(True)
828          self.classTable.SetTable(table, true)          panel.SetSizer(panelBox)
829  <<<<<<< classifier.py          panelBox.SetSizeHints(panel)
   
         topBox.Add(self.classTable, 0, wxGROW, 0)  
 =======  
         #table.SetNumberRows(10)  
         #table.SetNumberCols(2)  
         table.SetColLabelValue(0, _("Class"))  
         table.SetColLabelValue(1, _("Value"))  
         #self.classTable.SetColLabelValue(0, _("Class"))  
         #self.classTable.SetColLabelValue(1, _("Value"))  
         #self.classTable.SetCellValue(1, 1, _("Value"))  
830    
831          tableBox.Add(self.classTable, 0, wxALL, 4)          topBox.Add(panel, 1, wxGROW | wxALL, 4)
832    
833            ###########
834    
835            buttonBox = wxBoxSizer(wxHORIZONTAL)
836            buttonBox.Add(wxButton(self, ID_PROPERTY_TRY, _("Try")),
837                          0, wxALL, 4)
838            buttonBox.Add(60, 20, 0, wxALL, 4)
839            buttonBox.Add(wxButton(self, ID_PROPERTY_REVERT, _("Revert")),
840                          0, wxALL, 4)
841            buttonBox.Add(60, 20, 0, wxALL, 4)
842            buttonBox.Add(wxButton(self, ID_PROPERTY_OK, _("OK")),
843                          0, wxALL, 4)
844            buttonBox.Add(60, 20, 0, wxALL, 4)
845            buttonBox.Add(wxButton(self, ID_PROPERTY_CLOSE, _("Close")),
846                          0, wxALL, 4)
847            topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 0)
848    
849            EVT_BUTTON(self, ID_PROPERTY_OK, self._OnOK)
850            EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)
851            EVT_BUTTON(self, ID_PROPERTY_CLOSE, self._OnCloseBtn)
852            EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)
853    
854            ###########
855    
856            topBox.SetSizeHints(self)
857            self.SetAutoLayout(True)
858            self.SetSizer(topBox)
859    
860            #self.Fit()
861            ######################
862    
863            self.haveApplied = False
864    
865        def EditSymbol(self, row):
866            table = self.classGrid.GetTable()
867            prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
868    
869            # get a new ClassGroupProperties object and copy the
870            # values over to our current object
871            propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())
872    
873            self.Enable(False)
874            if propDlg.ShowModal() == wxID_OK:
875                new_prop = propDlg.GetClassGroupProperties()
876                table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
877            self.Enable(True)
878            propDlg.Destroy()
879            
880        def _SetClassification(self, clazz):
881                    
882          topBox.Add(self.classTable, 0, 0)          self.fields.SetClientData(self.__cur_field, clazz)
883  >>>>>>> 1.2          self.classGrid.GetTable().SetClassification(clazz)
884    
885        def __BuildClassification(self, fieldIndex, copyClass = False):
886    
887    #       numRows = self.classGrid.GetNumberRows()
888    #       assert numRows > 0  # there should always be a default row
889    
890    #       clazz = Classification()
891            if fieldIndex == 0:
892                fieldName = None
893                fieldType = None
894            else:
895                fieldName = self.fields.GetString(fieldIndex)
896                fieldType = self.layer.GetFieldType(fieldName)
897    
898            clazz = self.classGrid.GetTable().GetClassification()
899    
900            if copyClass:
901                clazz = copy.deepcopy(clazz)
902    
903            clazz.SetField(fieldName)
904            clazz.SetFieldType(fieldType)
905    
906    
907    #       table = self.classGrid.GetTable()
908    #       clazz.SetDefaultGroup(table.GetClassGroup(0))
909    
910    #       for i in range(1, numRows):
911    #           clazz.AppendGroup(table.GetClassGroup(i))
912    
913            return clazz
914    
915        def __SetGridTable(self, fieldIndex, group = None):
916    
917            clazz = self.fields.GetClientData(fieldIndex)
918    
919            if clazz is None:
920                clazz = Classification()
921                clazz.SetDefaultGroup(
922                    ClassGroupDefault(
923                        self.layer.GetClassification().
924                                   GetDefaultGroup().GetProperties()))
925    
926                fieldName = self.fields.GetString(fieldIndex)
927                fieldType = self.layer.GetFieldType(fieldName)
928                clazz.SetFieldType(fieldType)
929                    
930            self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)
931    
932        def __SetFieldTypeText(self, fieldIndex):
933            fieldName = self.fields.GetString(fieldIndex)
934            fieldType = self.layer.GetFieldType(fieldName)
935    
936            assert Classifier.type2string.has_key(fieldType)
937    
938            text = Classifier.type2string[fieldType]
939    
940            self.fieldTypeText.SetLabel(_("Data Type: %s") % text)
941    
942        def __SelectField(self, newIndex, oldIndex = -1, group = None):
943            """This method assumes that the current selection for the
944            combo has already been set by a call to SetSelection().
945            """
946    
947            assert oldIndex >= -1
948    
949            if oldIndex != -1:
950                clazz = self.__BuildClassification(oldIndex)
951                self.fields.SetClientData(oldIndex, clazz)
952    
953            self.__SetGridTable(newIndex, group)
954    
955            self.__EnableButtons(EB_SELECT_FIELD, newIndex != 0)
956    
957            self.__SetFieldTypeText(newIndex)
958    
959        def __SetTitle(self, title):
960            if title != "":
961                title = ": " + title
962    
963            self.SetTitle(_("Layer Properties") + title)
964    
965        def _OnEditSymbol(self, event):
966            sel = self.classGrid.GetCurrentSelection()
967    
968            if len(sel) == 1:
969                self.EditSymbol(sel[0])
970    
971        def _OnFieldSelect(self, event):
972            index = self.fields.GetSelection()
973            self.__SelectField(index, self.__cur_field)
974            self.__cur_field = index
975    
976        def _OnTry(self, event):
977            """Put the data from the table into a new Classification and hand
978               it to the layer.
979            """
980    
981            clazz = self.fields.GetClientData(self.__cur_field)
982    
983            #
984            # only build the classification if there wasn't one to
985            # to begin with or it has been modified
986            #
987            if clazz is None or self.classGrid.GetTable().IsModified():
988                clazz = self.__BuildClassification(self.__cur_field, True)
989    
990            self.layer.SetClassification(clazz)
991    
992            self.haveApplied = True
993    
994        def _OnOK(self, event):
995            self._OnTry(event)
996            self.Close()
997    
998        def _OnCloseBtn(self, event):
999            """Close is similar to Cancel except that any changes that were
1000            made and applied remain applied, but the currently displayed
1001            classification is discarded.
1002            """
1003    
1004            self.Close()
1005    
1006        def _OnRevert(self, event):
1007            """The layer's current classification stays the same."""
1008            if self.haveApplied:
1009                self.layer.SetClassification(self.originalClass)
1010    
1011            #self.Close()
1012    
1013        def _OnAdd(self, event):
1014            self.classGrid.AppendRows()
1015    
1016        def _OnRemove(self, event):
1017            self.classGrid.DeleteSelectedRows()
1018    
1019        def _OnGenClass(self, event):
1020    
1021            self.genDlg = ClassGenDialog(self, self.layer,
1022                              self.fields.GetString(self.__cur_field))
1023    
1024            EVT_CLOSE(self.genDlg, self._OnGenDialogClose)
1025    
1026            self.__EnableButtons(EB_GEN_CLASS, False)
1027    
1028            self.genDlg.Show()
1029    
1030        def _OnGenDialogClose(self, event):
1031            self.genDlg.Destroy()
1032            self.__EnableButtons(EB_GEN_CLASS, True)
1033    
1034        def _OnMoveUp(self, event):
1035            sel = self.classGrid.GetCurrentSelection()
1036    
1037            if len(sel) == 1:
1038                i = sel[0]
1039                if i > 1:
1040                    table = self.classGrid.GetTable()
1041                    x = table.GetClassGroup(i - 1)
1042                    y = table.GetClassGroup(i)
1043                    table.SetClassGroup(i - 1, y)
1044                    table.SetClassGroup(i, x)
1045                    self.classGrid.ClearSelection()
1046                    self.classGrid.SelectRow(i - 1)
1047                    self.classGrid.MakeCellVisible(i - 1, 0)
1048    
1049        def _OnMoveDown(self, event):
1050            sel = self.classGrid.GetCurrentSelection()
1051    
1052            if len(sel) == 1:
1053                i = sel[0]
1054                table = self.classGrid.GetTable()
1055                if 0 < i < table.GetNumberRows() - 1:
1056                    x = table.GetClassGroup(i)
1057                    y = table.GetClassGroup(i + 1)
1058                    table.SetClassGroup(i, y)
1059                    table.SetClassGroup(i + 1, x)
1060                    self.classGrid.ClearSelection()
1061                    self.classGrid.SelectRow(i + 1)
1062                    self.classGrid.MakeCellVisible(i + 1, 0)
1063    
1064        def _OnTitleChanged(self, event):
1065            obj = event.GetEventObject()
1066    
1067            self.layer.SetTitle(obj.GetValue())
1068            self.__SetTitle(self.layer.Title())
1069    
1070            self.__EnableButtons(EB_LAYER_TITLE, self.layer.Title() != "")
1071    
1072        def __EnableButtons(self, case, enable):
1073    
1074            if case == EB_LAYER_TITLE:  
1075                list = (ID_PROPERTY_OK,
1076                        ID_PROPERTY_CLOSE)
1077    
1078            elif case == EB_SELECT_FIELD:
1079                list = (ID_PROPERTY_GENCLASS,
1080                        ID_PROPERTY_ADD,
1081                        ID_PROPERTY_MOVEUP,
1082                        ID_PROPERTY_MOVEDOWN,
1083                        ID_PROPERTY_EDITSYM,
1084                        ID_PROPERTY_REMOVE)
1085    
1086            elif case == EB_GEN_CLASS:
1087                list = (ID_PROPERTY_SELECT,
1088                        ID_PROPERTY_FIELDTEXT,
1089                        ID_PROPERTY_GENCLASS,
1090                        ID_PROPERTY_EDITSYM)
1091    
1092            for id in list:
1093                self.FindWindowById(id).Enable(enable)
1094    
1095    ID_SELPROP_OK = 4001
1096    ID_SELPROP_CANCEL = 4002
1097    ID_SELPROP_SPINCTRL = 4002
1098    ID_SELPROP_PREVIEW = 4003
1099    ID_SELPROP_STROKECLR = 4004
1100    ID_SELPROP_FILLCLR = 4005
1101    ID_SELPROP_STROKECLRTRANS = 4006
1102    ID_SELPROP_FILLCLRTRANS = 4007
1103    
1104    class SelectPropertiesDialog(wxDialog):
1105    
1106        def __init__(self, parent, prop, shapeType):
1107            wxDialog.__init__(self, parent, -1, _("Select Properties"),
1108                              style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1109    
1110            self.prop = ClassGroupProperties(prop)
1111    
1112            topBox = wxBoxSizer(wxVERTICAL)
1113    
1114            itemBox = wxBoxSizer(wxHORIZONTAL)
1115    
1116            # preview box
1117            previewBox = wxBoxSizer(wxVERTICAL)
1118            previewBox.Add(wxStaticText(self, -1, _("Preview:")),
1119                0, wxALIGN_LEFT | wxALL, 4)
1120    
1121            self.previewWin = ClassGroupPropertiesCtrl(
1122                self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1123                (40, 40), wxSIMPLE_BORDER)
1124    
1125            self.previewWin.AllowEdit(False)
1126    
1127            previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)
1128    
1129            itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1130    
1131            # control box
1132            ctrlBox = wxBoxSizer(wxVERTICAL)
1133    
1134            lineColorBox = wxBoxSizer(wxHORIZONTAL)
1135            lineColorBox.Add(
1136                wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color")),
1137                1, wxALL | wxGROW, 4)
1138            EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)
1139    
1140            lineColorBox.Add(
1141                wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1142                1, wxALL | wxGROW, 4)
1143            EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,
1144                       self._OnChangeLineColorTrans)
1145    
1146            ctrlBox.Add(lineColorBox, 0,
1147                        wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1148    
1149            if shapeType != SHAPETYPE_ARC:
1150                fillColorBox = wxBoxSizer(wxHORIZONTAL)
1151                fillColorBox.Add(
1152                    wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1153                    1, wxALL | wxGROW, 4)
1154                EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)
1155                fillColorBox.Add(
1156                    wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1157                    1, wxALL | wxGROW, 4)
1158                EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,
1159                           self._OnChangeFillColorTrans)
1160                ctrlBox.Add(fillColorBox, 0,
1161                            wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)
1162    
1163            spinBox = wxBoxSizer(wxHORIZONTAL)
1164            spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),
1165                    0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)
1166            self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,
1167                                       min=1, max=10,
1168                                       value=str(prop.GetLineWidth()),
1169                                       initial=prop.GetLineWidth())
1170    
1171            EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)
1172    
1173            spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)
1174    
1175            ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)
1176            itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)
1177            topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)
1178    
1179          #          #
1180          # Control buttons:          # Control buttons:
1181          #          #
1182          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wxBoxSizer(wxHORIZONTAL)
1183          buttonBox.Add(wxButton(self, ID_CLASSIFY_OK, _("OK")),          buttonBox.Add(wxButton(self, ID_SELPROP_OK, _("OK")),
1184                        0, wxALL, 4)                        0, wxALL, 4)
1185          buttonBox.Add(wxButton(self, ID_CLASSIFY_CANCEL, _("Cancel")),          buttonBox.Add(wxButton(self, ID_SELPROP_CANCEL, _("Cancel")),
1186                        0, wxALL, 4)                        0, wxALL, 4)
1187          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)          topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 10)
1188                                                                                    
1189          EVT_BUTTON(self, ID_CLASSIFY_OK, self.OnOK)          EVT_BUTTON(self, ID_SELPROP_OK, self._OnOK)
1190          EVT_BUTTON(self, ID_CLASSIFY_CANCEL, self.OnCancel)          EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1191                                                                                    
1192          self.SetAutoLayout(true)          self.SetAutoLayout(True)
1193          self.SetSizer(topBox)          self.SetSizer(topBox)
1194          topBox.Fit(self)          topBox.Fit(self)
1195          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1196    
1197      def OnPropertySelect(self, event): pass      def _OnOK(self, event):
   
     def OnOK(self, event):  
1198          self.EndModal(wxID_OK)          self.EndModal(wxID_OK)
1199    
1200      def OnCancel(self, event):      def _OnCancel(self, event):
1201          self.EndModal(wxID_CANCEL)          self.EndModal(wxID_CANCEL)
1202    
1203        def _OnSpin(self, event):
1204            self.prop.SetLineWidth(self.spinCtrl.GetValue())
1205            self.previewWin.Refresh()
1206    
1207        def __GetColor(self, cur):
1208            dialog = wxColourDialog(self)
1209            if cur is not Color.Transparent:
1210                dialog.GetColourData().SetColour(Color2wxColour(cur))
1211    
1212            ret = None
1213            if dialog.ShowModal() == wxID_OK:
1214                ret = wxColour2Color(dialog.GetColourData().GetColour())
1215    
1216            dialog.Destroy()
1217    
1218            return ret
1219            
1220        def _OnChangeLineColor(self, event):
1221            clr = self.__GetColor(self.prop.GetLineColor())
1222            if clr is not None:
1223                self.prop.SetLineColor(clr)
1224            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1225    
1226        def _OnChangeLineColorTrans(self, event):
1227            self.prop.SetLineColor(Color.Transparent)
1228            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1229            
1230        def _OnChangeFillColor(self, event):
1231            clr = self.__GetColor(self.prop.GetFill())
1232            if clr is not None:
1233                self.prop.SetFill(clr)
1234            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1235    
1236        def _OnChangeFillColorTrans(self, event):
1237            self.prop.SetFill(Color.Transparent)
1238            self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1239    
1240        def GetClassGroupProperties(self):
1241            return self.prop
1242    
1243    
1244    class ClassDataPreviewWindow(wxWindow):
1245    
1246        def __init__(self, rect, prop, shapeType,
1247                           parent = None, id = -1, size = wxDefaultSize):
1248            if parent is not None:
1249                wxWindow.__init__(self, parent, id, (0, 0), size)
1250                EVT_PAINT(self, self._OnPaint)
1251    
1252            self.rect = rect
1253    
1254            self.prop = prop
1255            self.shapeType = shapeType
1256            self.previewer = ClassDataPreviewer()
1257    
1258        def GetProperties():
1259            return self.prop
1260    
1261        def _OnPaint(self, event):
1262            dc = wxPaintDC(self)
1263    
1264            # XXX: this doesn't seem to be having an effect:
1265            dc.DestroyClippingRegion()
1266    
1267            if self.rect is None:
1268                w, h = self.GetSize()
1269                rect = wxRect(0, 0, w, h)
1270            else:
1271                rect = self.rect
1272    
1273            self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1274    
1275    class ClassDataPreviewer:
1276    
1277        def Draw(self, dc, rect, prop, shapeType):
1278    
1279            assert dc is not None
1280            assert isinstance(prop, ClassGroupProperties)
1281    
1282            if rect is None:
1283                x = 0
1284                y = 0
1285                w, h = dc.GetSize()
1286            else:
1287                x = rect.GetX()
1288                y = rect.GetY()
1289                w = rect.GetWidth()
1290                h = rect.GetHeight()
1291    
1292            stroke = prop.GetLineColor()
1293            if stroke is Color.Transparent:
1294                pen = wxTRANSPARENT_PEN
1295            else:
1296                pen = wxPen(Color2wxColour(stroke),
1297                            prop.GetLineWidth(),
1298                            wxSOLID)
1299    
1300            stroke = prop.GetFill()
1301            if stroke is Color.Transparent:
1302                brush = wxTRANSPARENT_BRUSH
1303            else:
1304                brush = wxBrush(Color2wxColour(stroke), wxSOLID)
1305    
1306            dc.SetPen(pen)
1307            dc.SetBrush(brush)
1308    
1309            if shapeType == SHAPETYPE_ARC:
1310                dc.DrawSpline([wxPoint(x, y + h),
1311                               wxPoint(x + w/2, y + h/4),
1312                               wxPoint(x + w/2, y + h/4*3),
1313                               wxPoint(x + w, y)])
1314    
1315            elif shapeType == SHAPETYPE_POINT:
1316    
1317                dc.DrawCircle(x + w/2, y + h/2,
1318                              (min(w, h) - prop.GetLineWidth())/2)
1319    
1320            elif shapeType == SHAPETYPE_POLYGON:
1321                dc.DrawRectangle(x, y, w, h)
1322    
1323    class ClassRenderer(wxPyGridCellRenderer):
1324    
1325        def __init__(self, shapeType):
1326            wxPyGridCellRenderer.__init__(self)
1327            self.shapeType = shapeType
1328            self.previewer = ClassDataPreviewer()
1329    
1330        def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1331            data = grid.GetTable().GetClassGroup(row)
1332    
1333            dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1334                                 rect.GetWidth(), rect.GetHeight())
1335            dc.SetPen(wxPen(wxLIGHT_GREY))
1336            dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
1337            dc.DrawRectangle(rect.GetX(), rect.GetY(),
1338                             rect.GetWidth(), rect.GetHeight())
1339    
1340            if not isinstance(data, ClassGroupMap):
1341                self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
1342    
1343            if isSelected:
1344                dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
1345                dc.SetBrush(wxTRANSPARENT_BRUSH)
1346    
1347                dc.DrawRectangle(rect.GetX(), rect.GetY(),
1348                                 rect.GetWidth(), rect.GetHeight())
1349    
1350            dc.DestroyClippingRegion()
1351    
1352    
1353    class ClassGroupPropertiesCtrl(wxWindow, wxControl):
1354    
1355        def __init__(self, parent, id, props, shapeType,
1356                     size = wxDefaultSize, style = 0):
1357    
1358            wxWindow.__init__(self, parent, id, size = size, style = style)
1359    
1360            self.SetProperties(props)
1361            self.SetShapeType(shapeType)
1362            self.AllowEdit(True)
1363    
1364            EVT_PAINT(self, self._OnPaint)
1365            EVT_LEFT_DCLICK(self, self._OnLeftDClick)
1366    
1367            self.previewer = ClassDataPreviewer()
1368    
1369        def _OnPaint(self, event):
1370            dc = wxPaintDC(self)
1371    
1372            # XXX: this doesn't seem to be having an effect:
1373            dc.DestroyClippingRegion()
1374    
1375            w, h = self.GetClientSize()
1376    
1377            self.previewer.Draw(dc,
1378                                wxRect(0, 0, w, h),
1379                                self.GetProperties(),
1380                                self.GetShapeType())
1381    
1382    
1383        def GetProperties(self):
1384            return self.props
1385    
1386        def SetProperties(self, props):
1387            self.props = props
1388            self.Refresh()
1389    
1390        def GetShapeType(self):
1391            return self.shapeType
1392    
1393        def SetShapeType(self, shapeType):
1394            self.shapeType = shapeType
1395            self.Refresh()
1396    
1397        def AllowEdit(self, allow):
1398            self.allowEdit = allow
1399    
1400        def DoEdit(self):
1401            if not self.allowEdit: return
1402    
1403            propDlg = SelectPropertiesDialog(NULL,
1404                                             self.GetProperties(),
1405                                             self.GetShapeType())
1406    
1407            if propDlg.ShowModal() == wxID_OK:
1408                new_prop = propDlg.GetClassGroupProperties()
1409                self.SetProperties(new_prop)
1410                self.Refresh()
1411    
1412            propDlg.Destroy()
1413    
1414        def _OnLeftDClick(self, event):
1415            self.DoEdit()

Legend:
Removed from v.376  
changed lines
  Added in v.650

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26