/[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 1219 by bh, Mon Jun 16 17:42:54 2003 UTC revision 2817 by bernhard, Sun Jan 27 00:01:32 2008 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2003 by Intevation GmbH  # Copyright (c) 2003-2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jan-Oliver Wagner <[email protected]> (2003-2004)
4    # Martin Schulze <[email protected]> (2004)
5    # Frank Koormann <[email protected]> (2003, 2006)
6    # Bernhard Herzog <[email protected]> (2003)
7    # Jonathan Coles <[email protected]> (2003)
8  #  #
9  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
10  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 8  Line 12 
12  """Dialog for classifying how layers are displayed"""  """Dialog for classifying how layers are displayed"""
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    # $Source$
16    # $Id$
17    
18  import copy  import copy
19    import re
20    
21  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
22       FIELDTYPE_STRING       FIELDTYPE_STRING
23    
24  from wxPython.wx import *  import wx
25  from wxPython.grid import *  from wx import grid
26    
27  from Thuban import _  from Thuban import _
28    from Thuban.UI import internal_from_wxstring
29  from Thuban.UI.common import Color2wxColour, wxColour2Color  from Thuban.UI.common import Color2wxColour, wxColour2Color
30    
31  from Thuban.Model.messages import MAP_LAYERS_REMOVED, LAYER_SHAPESTORE_REPLACED  from Thuban.Model.messages import MAP_LAYERS_REMOVED, LAYER_SHAPESTORE_REPLACED
32  from Thuban.Model.range import Range  from Thuban.Model.range import Range
33  from Thuban.Model.classification import \  from Thuban.Model.classification import \
34      Classification, ClassGroupDefault, \      Classification, ClassGroupDefault, \
35      ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \      ClassGroupSingleton, ClassGroupPattern, ClassGroupRange, ClassGroupMap, \
36      ClassGroupProperties      ClassGroupProperties
37    
38  from Thuban.Model.color import Color  from Thuban.Model.color import Transparent
39    
40  from Thuban.Model.layer import Layer, RasterLayer, \  from Thuban.Model.layer import Layer
41      SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT  from Thuban.Model.data import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
42    
43  from Thuban.UI.classgen import ClassGenDialog  from Thuban.UI.classgen import ClassGenDialog
44    from Thuban.UI.colordialog import ColorDialog
45    
46  from dialogs import NonModalNonParentDialog  from Thuban.UI.layerproperties import LayerProperties
47    from messages import MAP_REPLACED
 ID_CLASS_TABLE = 40011  
48    
49    
50  # table columns  # table columns
# Line 56  FIELD_NAME = 2 Line 64  FIELD_NAME = 2
64  # passed into SetTable is the same that is returned by GetTable  # passed into SetTable is the same that is returned by GetTable
65  #  #
66  import weakref  import weakref
67  class ClassGrid(wxGrid):  class ClassGrid(grid.Grid):
68    
69    
70      def __init__(self, parent, classifier):      def __init__(self, parent, classifier):
# Line 67  class ClassGrid(wxGrid): Line 75  class ClassGrid(wxGrid):
75          clazz -- the working classification that this grid should          clazz -- the working classification that this grid should
76                   use for display.                   use for display.
77          """          """
78            grid.Grid.__init__(self, parent, -1, style = 0)
         wxGrid.__init__(self, parent, ID_CLASS_TABLE, style = 0)  
79    
80          self.classifier = classifier          self.classifier = classifier
81    
82          self.currentSelection = []          self.currentSelection = []
83    
84          EVT_GRID_CELL_LEFT_DCLICK(self, self._OnCellDClick)          self.Bind(grid.EVT_GRID_CELL_LEFT_DCLICK, self._OnCellDClick)
85          EVT_GRID_RANGE_SELECT(self, self._OnSelectedRange)          self.Bind(grid.EVT_GRID_RANGE_SELECT, self._OnSelectedRange)
86          EVT_GRID_SELECT_CELL(self, self._OnSelectedCell)          self.Bind(grid.EVT_GRID_SELECT_CELL, self._OnSelectedCell)
87          EVT_GRID_COL_SIZE(self, self._OnCellResize)          self.Bind(grid.EVT_GRID_COL_SIZE, self._OnCellResize)
88          EVT_GRID_ROW_SIZE(self, self._OnCellResize)          self.Bind(grid.EVT_GRID_ROW_SIZE, self._OnCellResize)
89            self.Bind(grid.EVT_GRID_LABEL_RIGHT_CLICK, self._OnLabelRightClicked)
90    
91    
92      #def GetCellAttr(self, row, col):      #def GetCellAttr(self, row, col):
93          #print "GetCellAttr ", row, col          #print "GetCellAttr ", row, col
94          #wxGrid.GetCellAttr(self, row, col)          #Grid.GetCellAttr(self, row, col)
95    
96      def CreateTable(self, clazz, shapeType, group = None):      def CreateTable(self, clazz, fieldType, shapeType, group = None):
97    
98          assert isinstance(clazz, Classification)          assert isinstance(clazz, Classification)
99    
100          table = self.GetTable()          table = self.GetTable()
101          if table is None:          if table is None:
102              w = self.GetDefaultColSize() * NUM_COLS \              w = self.GetDefaultColSize() * NUM_COLS \
103                  + self.GetDefaultRowLabelSize()                  + self.GetDefaultRowLabelSize()
104              h = self.GetDefaultRowSize() * 4 \              h = self.GetDefaultRowSize() * 4 \
105                  + self.GetDefaultColLabelSize()                  + self.GetDefaultColLabelSize()
106    
# Line 101  class ClassGrid(wxGrid): Line 110  class ClassGrid(wxGrid):
110              self.SetTable(table, True)              self.SetTable(table, True)
111    
112    
113          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(grid.Grid.wxGridSelectRows)
114          self.ClearSelection()          self.ClearSelection()
115    
116          table.Reset(clazz, shapeType, group)          table.Reset(clazz, fieldType, shapeType, group)
117    
118      def GetCurrentSelection(self):      def GetCurrentSelection(self):
119          """Return the currently highlighted rows as an increasing list          """Return the currently highlighted rows as an increasing list
# Line 127  class ClassGrid(wxGrid): Line 136  class ClassGrid(wxGrid):
136      #      #
137      def SetTable(self, object, *attributes):      def SetTable(self, object, *attributes):
138          self.tableRef = weakref.ref(object)          self.tableRef = weakref.ref(object)
139          return wxGrid.SetTable(self, object, *attributes)          return grid.Grid.SetTable(self, object, *attributes)
140    
141      def GetTable(self):      def GetTable(self):
142          try:          try:
# Line 137  class ClassGrid(wxGrid): Line 146  class ClassGrid(wxGrid):
146    
147      def DeleteSelectedRows(self):      def DeleteSelectedRows(self):
148          """Deletes all highlighted rows.          """Deletes all highlighted rows.
149      
150          If only one row is highlighted then after it is deleted the          If only one row is highlighted then after it is deleted the
151          row that was below the deleted row is highlighted."""          row that was below the deleted row is highlighted."""
152    
153          sel = self.GetCurrentSelection()          sel = self.GetCurrentSelection()
154    
155          # nothing to do          # nothing to do
156          if len(sel) == 0: return          if len(sel) == 0: return
157    
158          # if only one thing is selected check if it is the default          # if only one thing is selected check if it is the default
159          # data row, because we can't remove that          # data row, because we can't remove that
# Line 152  class ClassGrid(wxGrid): Line 161  class ClassGrid(wxGrid):
161              #group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)              #group = self.GetTable().GetValueAsCustom(sel[0], COL_SYMBOL, None)
162              group = self.GetTable().GetClassGroup(sel[0])              group = self.GetTable().GetClassGroup(sel[0])
163              if isinstance(group, ClassGroupDefault):              if isinstance(group, ClassGroupDefault):
164                  wxMessageDialog(self,                  wx.MessageDialog(self,
165                                  "The Default group cannot be removed.",                                  _("The Default group cannot be removed."),
166                                  style = wxOK | wxICON_EXCLAMATION).ShowModal()                                  style = wx.OK | wx.ICON_EXCLAMATION).ShowModal()
167                  return                  return
168            
169    
170          self.ClearSelection()          self.ClearSelection()
171    
# Line 181  class ClassGrid(wxGrid): Line 190  class ClassGrid(wxGrid):
190              if r > self.GetNumberRows() - 1:              if r > self.GetNumberRows() - 1:
191                  r = self.GetNumberRows() - 1                  r = self.GetNumberRows() - 1
192              self.SelectRow(r)              self.SelectRow(r)
193            
194    
195      def SelectGroup(self, group, makeVisible = True):      def SelectGroup(self, group, makeVisible = True):
196          if group is None: return          if group is None: return
197    
198          assert isinstance(group, ClassGroup)          assert isinstance(group, ClassGroup)
199    
200          table = self.GetTable()          table = self.GetTable()
201    
202          assert table is not None          assert table is not None
203    
204          for i in range(table.GetNumberRows()):          for i in range(table.GetNumberRows()):
205              g = table.GetClassGroup(i)              g = table.GetClassGroup(i)
# Line 205  class ClassGrid(wxGrid): Line 214  class ClassGrid(wxGrid):
214  #  #
215  #   def DeselectRow(self, row):  #   def DeselectRow(self, row):
216  #       self.ProcessEvent(  #       self.ProcessEvent(
217  #           wxGridRangeSelectEvent(-1,  #           GridRangeSelectEvent(-1,
218  #                                  wxEVT_GRID_RANGE_SELECT,  #                                  wxEVT_GRID_RANGE_SELECT,
219  #                                  self,  #                                  self,
220  #                                  (row, row), (row, row),  #                                  (row, row), (row, row),
# Line 227  class ClassGrid(wxGrid): Line 236  class ClassGrid(wxGrid):
236      # from http://wiki.wxpython.org to keep track of which      # from http://wiki.wxpython.org to keep track of which
237      # cells are currently highlighted      # cells are currently highlighted
238      #      #
239      def _OnSelectedRange(self, event):      def _OnSelectedRange(self, event):
240          """Internal update to the selection tracking list"""          """Internal update to the selection tracking list"""
241          if event.Selecting():          if event.Selecting():
242              for index in range( event.GetTopRow(), event.GetBottomRow()+1):              for index in range( event.GetTopRow(), event.GetBottomRow()+1):
243                  if index not in self.currentSelection:                  if index not in self.currentSelection:
244                      self.currentSelection.append( index )                      self.currentSelection.append( index )
245          else:          else:
246              for index in range( event.GetTopRow(), event.GetBottomRow()+1):              for index in range( event.GetTopRow(), event.GetBottomRow()+1):
247                  while index in self.currentSelection:                  while index in self.currentSelection:
248                      self.currentSelection.remove( index )                      self.currentSelection.remove( index )
249          #self.ConfigureForSelection()          #self.ConfigureForSelection()
250    
251          event.Skip()          event.Skip()
252    
253      def _OnSelectedCell( self, event ):      def _OnSelectedCell( self, event ):
254          """Internal update to the selection tracking list"""          """Internal update to the selection tracking list"""
255          self.currentSelection = [ event.GetRow() ]          self.currentSelection = [ event.GetRow() ]
256          #self.ConfigureForSelection()          #self.ConfigureForSelection()
257          event.Skip()          event.Skip()
258    
259      def _OnCellResize(self, event):      def _OnCellResize(self, event):
260          self.FitInside()          self.FitInside()
261          event.Skip()          event.Skip()
262    
263  class ClassTable(wxPyGridTableBase):      def _OnLabelRightClicked(self, event):
264            """Process right click on label, raise popup for row labels."""
265            row, col = event.GetRow(), event.GetCol()
266            if col == -1:
267                self.labelPopup(event, row)
268    
269        def labelPopup(self, event, row):
270            """Raise grid label popup."""
271            # check if row label is Pattern or Singleton
272            label = self.GetRowLabelValue(row)
273            if (label == _("Pattern") or label == _("Singleton")):
274                xe,ye = event.GetPosition()
275                x=self.GetRowSize(row)/2
276                menu = wx.Menu()
277                patternID = wx.NewId()
278                singletonID = wx.NewId()
279    
280                def _SetSingleton(event, self=self, row=row):
281                    table = self.GetTable()
282                    group = table.clazz.GetGroup(row - 1)
283                    if not isinstance(group, ClassGroupSingleton):
284                        ngroup = ClassGroupSingleton(
285                                    group.GetPattern(),
286                                    group.GetProperties(),
287                                    group.GetLabel()
288                                    )
289                        table.SetClassGroup(row, ngroup)
290    
291                def _SetPattern(event, self=self, row=row):
292                    table = self.GetTable()
293                    group = table.clazz.GetGroup(row - 1)
294                    if not isinstance(group, ClassGroupPattern):
295                        try:
296                            re.compile(group.GetValue())
297                        except:
298                            pass
299                        else:
300                            ngroup = ClassGroupPattern(
301                                    group.GetValue(),
302                                    group.GetProperties(),
303                                    group.GetLabel()
304                                    )
305                            table.SetClassGroup(row, ngroup)
306    
307                menu.Append(singletonID, _("Singleton"))
308                self.Bind(wx.EVT_MENU, _SetSingleton, id=singletonID)
309                if self.GetTable().fieldType == FIELDTYPE_STRING:
310                    menu.Append(patternID, _("Pattern"))
311                    self.Bind(wx.EVT_MENU, _SetPattern, id=patternID)
312                self.PopupMenu(menu, wx.Point(x,ye))
313                menu.Destroy()
314    
315    class ClassTable(grid.PyGridTableBase):
316      """Represents the underlying data structure for the grid."""      """Represents the underlying data structure for the grid."""
317    
318      __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]      __col_labels = [_("Visible"), _("Symbol"), _("Value"), _("Label")]
319    
320    
321      def __init__(self, view = None):      def __init__(self, view = None):
     #def __init__(self, clazz, shapeType, view = None):  
322          """Constructor.          """Constructor.
323    
324          shapeType -- the type of shape that the layer uses          shapeType -- the type of shape that the layer uses
325    
326          view -- a wxGrid object that uses this class for its table          view -- a Grid object that uses this class for its table
327          """          """
328    
329          wxPyGridTableBase.__init__(self)          grid.PyGridTableBase.__init__(self)
330    
331          assert len(ClassTable.__col_labels) == NUM_COLS          assert len(ClassTable.__col_labels) == NUM_COLS
332    
# Line 275  class ClassTable(wxPyGridTableBase): Line 335  class ClassTable(wxPyGridTableBase):
335    
336          self.SetView(view)          self.SetView(view)
337    
338      def Reset(self, clazz, shapeType, group = None):      def Reset(self, clazz, fieldType, shapeType, group = None):
339          """Reset the table with the given data.          """Reset the table with the given data.
340    
341          This is necessary because wxWindows does not allow a grid's          This is necessary because wxWindows does not allow a grid's
# Line 289  class ClassTable(wxPyGridTableBase): Line 349  class ClassTable(wxPyGridTableBase):
349          shapeType -- the type of shape that the layer uses          shapeType -- the type of shape that the layer uses
350          """          """
351    
352          assert isinstance(clazz, Classification)          assert isinstance(clazz, Classification)
353    
354          self.GetView().BeginBatch()          self.GetView().BeginBatch()
355    
356          self.fieldType = clazz.GetFieldType()          self.fieldType = fieldType
357          self.shapeType = shapeType          self.shapeType = shapeType
358    
359          self.SetClassification(clazz, group)          self.SetClassification(clazz, group)
# Line 301  class ClassTable(wxPyGridTableBase): Line 361  class ClassTable(wxPyGridTableBase):
361    
362          self.__colAttr = {}          self.__colAttr = {}
363    
364          attr = wxGridCellAttr()          attr = grid.GridCellAttr()
365          attr.SetEditor(wxGridCellBoolEditor())          attr.SetEditor(grid.GridCellBoolEditor())
366          attr.SetRenderer(wxGridCellBoolRenderer())          attr.SetRenderer(grid.GridCellBoolRenderer())
367          attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER)          attr.SetAlignment(wx.ALIGN_CENTER, wx.ALIGN_CENTER)
368          self.__colAttr[COL_VISIBLE] = attr          self.__colAttr[COL_VISIBLE] = attr
369    
370          attr = wxGridCellAttr()          attr = grid.GridCellAttr()
371          attr.SetRenderer(ClassRenderer(self.shapeType))          attr.SetRenderer(ClassRenderer(self.shapeType))
372          attr.SetReadOnly()          attr.SetReadOnly()
373          self.__colAttr[COL_SYMBOL] = attr          self.__colAttr[COL_SYMBOL] = attr
# Line 316  class ClassTable(wxPyGridTableBase): Line 376  class ClassTable(wxPyGridTableBase):
376          self.GetView().FitInside()          self.GetView().FitInside()
377    
378      def GetClassification(self):      def GetClassification(self):
379            """Return the current classification."""
380          return self.clazz          return self.clazz
381    
382      def SetClassification(self, clazz, group = None):      def SetClassification(self, clazz, group = None):
383            """Fill in the table with the given classification.
384            Select the given group if group is not None.
385            """
386    
387          self.GetView().BeginBatch()          self.GetView().BeginBatch()
388    
# Line 326  class ClassTable(wxPyGridTableBase): Line 390  class ClassTable(wxPyGridTableBase):
390    
391          row = -1          row = -1
392          self.clazz = clazz          self.clazz = clazz
393    
394          self.__NotifyRowChanges(old_len, self.GetNumberRows())          self.__NotifyRowChanges(old_len, self.GetNumberRows())
395    
396          #          #
# Line 339  class ClassTable(wxPyGridTableBase): Line 403  class ClassTable(wxPyGridTableBase):
403    
404          self.__Modified()          self.__Modified()
405    
   
406          self.GetView().EndBatch()          self.GetView().EndBatch()
407          self.GetView().FitInside()          self.GetView().FitInside()
408    
409      def __NotifyRowChanges(self, curRows, newRows):      def __NotifyRowChanges(self, curRows, newRows):
410            """Make sure table updates correctly if the number of
411            rows changes.
412            """
413          #          #
414          # silly message processing for updates to the number of          # silly message processing for updates to the number of
415          # rows and columns          # rows and columns
416          #          #
417          if newRows > curRows:          if newRows > curRows:
418              msg = wxGridTableMessage(self,              msg = grid.GridTableMessage(self,
419                          wxGRIDTABLE_NOTIFY_ROWS_APPENDED,                          grid.GRIDTABLE_NOTIFY_ROWS_APPENDED,
420                          newRows - curRows)    # how many                          newRows - curRows)    # how many
421              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
422              self.GetView().FitInside()              self.GetView().FitInside()
423          elif newRows < curRows:          elif newRows < curRows:
424              msg = wxGridTableMessage(self,              msg = grid.GridTableMessage(self,
425                          wxGRIDTABLE_NOTIFY_ROWS_DELETED,                          grid.GRIDTABLE_NOTIFY_ROWS_DELETED,
426                          curRows,              # position                          curRows,              # position
427                          curRows - newRows)    # how many                          curRows - newRows)    # how many
428              self.GetView().ProcessTableMessage(msg)              self.GetView().ProcessTableMessage(msg)
429              self.GetView().FitInside()              self.GetView().FitInside()
430    
   
431      def __SetRow(self, row, group):      def __SetRow(self, row, group):
432          """Set a row's data to that of the group.          """Set a row's data to that of the group.
433    
# Line 400  class ClassTable(wxPyGridTableBase): Line 465  class ClassTable(wxPyGridTableBase):
465              group = self.clazz.GetGroup(row - 1)              group = self.clazz.GetGroup(row - 1)
466              if isinstance(group, ClassGroupDefault):   return _("Default")              if isinstance(group, ClassGroupDefault):   return _("Default")
467              if isinstance(group, ClassGroupSingleton): return _("Singleton")              if isinstance(group, ClassGroupSingleton): return _("Singleton")
468                if isinstance(group, ClassGroupPattern):   return _("Pattern")
469              if isinstance(group, ClassGroupRange):     return _("Range")              if isinstance(group, ClassGroupRange):     return _("Range")
470              if isinstance(group, ClassGroupMap):       return _("Map")              if isinstance(group, ClassGroupMap):       return _("Map")
471    
# Line 433  class ClassTable(wxPyGridTableBase): Line 499  class ClassTable(wxPyGridTableBase):
499          """          """
500    
501          self.SetValueAsCustom(row, col, None, value)          self.SetValueAsCustom(row, col, None, value)
502          
503      def GetValueAsCustom(self, row, col, typeName):      def GetValueAsCustom(self, row, col, typeName):
504          """Return the object that is used to represent the given          """Return the object that is used to represent the given
505             cell coordinates. This may not be a string.             cell coordinates. This may not be a string.
506    
507          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
508          """          """
509    
# Line 457  class ClassTable(wxPyGridTableBase): Line 523  class ClassTable(wxPyGridTableBase):
523              return group.GetLabel()              return group.GetLabel()
524    
525          # col must be COL_VALUE          # col must be COL_VALUE
526          assert col == COL_VALUE          assert col == COL_VALUE
527    
528          if isinstance(group, ClassGroupDefault):          if isinstance(group, ClassGroupDefault):
529              return _("DEFAULT")              return _("DEFAULT")
530          elif isinstance(group, ClassGroupSingleton):          elif isinstance(group, ClassGroupSingleton):
531              return group.GetValue()              return group.GetValue()
532            elif isinstance(group, ClassGroupPattern):
533                return group.GetPattern()
534          elif isinstance(group, ClassGroupRange):          elif isinstance(group, ClassGroupRange):
535              return group.GetRange()              return group.GetRange()
536    
# Line 474  class ClassTable(wxPyGridTableBase): Line 542  class ClassTable(wxPyGridTableBase):
542             (string, number, or range)             (string, number, or range)
543    
544          Returns a tuple (type, data) where type is 0 if data is          Returns a tuple (type, data) where type is 0 if data is
545          a singleton value, or 1 if is a range          a singleton value, 1 if is a range or 2 if it is a pattern.
546          """          """
547    
548          type = self.fieldType          type = self.fieldType
549    
550          if type == FIELDTYPE_STRING:          if type == FIELDTYPE_STRING:
551              return (0, value)              # Approach: if we can compile the value as an expression,
552                # make it a pattern, else a singleton.
553                # This is quite crude, however I don't have a better idea:
554                # How to distinct the singleton "Thuban" from the pattern "Thuban"?
555                try:
556                    re.compile(value)
557                except:
558                    return (0, value)
559                else:
560                    return (2, value)
561          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):          elif type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
562              if type == FIELDTYPE_INT:              if type == FIELDTYPE_INT:
563                  # the float call allows the user to enter 1.0 for 1                  # the float call allows the user to enter 1.0 for 1
# Line 514  class ClassTable(wxPyGridTableBase): Line 591  class ClassTable(wxPyGridTableBase):
591          typeName -- unused, but needed to overload wxPyGridTableBase          typeName -- unused, but needed to overload wxPyGridTableBase
592          """          """
593    
594          assert 0 <= col < self.GetNumberCols()          assert 0 <= col < self.GetNumberCols()
595          assert 0 <= row < self.GetNumberRows()          assert 0 <= row < self.GetNumberRows()
596    
597          if row == 0:          if row == 0:
598              group = self.clazz.GetDefaultGroup()              group = self.clazz.GetDefaultGroup()
# Line 564  class ClassTable(wxPyGridTableBase): Line 641  class ClassTable(wxPyGridTableBase):
641                              ngroup = ClassGroupRange(props = props)                              ngroup = ClassGroupRange(props = props)
642                              changed = True                              changed = True
643                          ngroup.SetRange(dataInfo[1])                          ngroup.SetRange(dataInfo[1])
644                        elif dataInfo[0] == 2:
645                            if not isinstance(group, ClassGroupPattern):
646                                ngroup = ClassGroupPattern(props = props)
647                                changed = True
648                            ngroup.SetPattern(dataInfo[1])
649                      else:                      else:
650                          assert False                          assert False
651                          pass                          pass
# Line 582  class ClassTable(wxPyGridTableBase): Line 664  class ClassTable(wxPyGridTableBase):
664      def GetAttr(self, row, col, someExtraParameter):      def GetAttr(self, row, col, someExtraParameter):
665          """Returns the cell attributes"""          """Returns the cell attributes"""
666    
667          return self.__colAttr.get(col, wxGridCellAttr()).Clone()          return self.__colAttr.get(col, grid.GridCellAttr()).Clone()
668    
669      def GetClassGroup(self, row):      def GetClassGroup(self, row):
670          """Return the ClassGroup object representing row 'row'."""          """Return the ClassGroup object representing row 'row'."""
# Line 594  class ClassTable(wxPyGridTableBase): Line 676  class ClassTable(wxPyGridTableBase):
676              return self.clazz.GetGroup(row - 1)              return self.clazz.GetGroup(row - 1)
677    
678      def SetClassGroup(self, row, group):      def SetClassGroup(self, row, group):
679            """Set the given row to properties of group."""
680          self.__SetRow(row, group)          self.__SetRow(row, group)
681          self.GetView().Refresh()          self.GetView().Refresh()
682    
# Line 622  class ClassTable(wxPyGridTableBase): Line 705  class ClassTable(wxPyGridTableBase):
705          The table is considered modified if any rows are removed.          The table is considered modified if any rows are removed.
706          """          """
707    
708          assert pos >= 0          assert pos >= 0
709          old_len = self.GetNumberRows()          old_len = self.GetNumberRows()
710          for row in range(pos, pos - numRows, -1):          for row in range(pos, pos - numRows, -1):
711              group = self.GetClassGroup(row)              group = self.GetClassGroup(row)
712              if row != 0:              if row != 0:
713                  self.clazz.RemoveGroup(row - 1)                  self.clazz.RemoveGroup(row - 1)
714                  self.__Modified()                  self.__Modified()
715        
716          if self.IsModified():          if self.IsModified():
717              self.__NotifyRowChanges(old_len, self.GetNumberRows())              self.__NotifyRowChanges(old_len, self.GetNumberRows())
718    
# Line 671  EB_LAYER_TITLE = 0 Line 754  EB_LAYER_TITLE = 0
754  EB_SELECT_FIELD = 1  EB_SELECT_FIELD = 1
755  EB_GEN_CLASS = 2  EB_GEN_CLASS = 2
756    
757  class Classifier(NonModalNonParentDialog):  class Classifier(LayerProperties):
758    
759      type2string = {None:             _("None"),      type2string = {None:             _("None"),
760                     FIELDTYPE_STRING: _("Text"),                     FIELDTYPE_STRING: _("Text"),
761                     FIELDTYPE_INT:    _("Integer"),                     FIELDTYPE_INT:    _("Integer"),
762                     FIELDTYPE_DOUBLE: _("Decimal")}                     FIELDTYPE_DOUBLE: _("Decimal")}
763    
764      def __init__(self, parent, name, map, layer, group = None):      def __init__(self, parent, name, layer, group = None):
765          NonModalNonParentDialog.__init__(self, parent, name, "")          """Create a Properties/Classification dialog for a layer.
766            The layer is part of map. If group is not None, select that
767          self.__SetTitle(layer.Title())          group in the classification table.
768            """
769    
770          self.layer = layer          LayerProperties.__init__(self, parent, name, layer)
         self.map = map  
771    
         self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)  
772          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,          self.layer.Subscribe(LAYER_SHAPESTORE_REPLACED,
773                               self.layer_shapestore_replaced)                               self.layer_shapestore_replaced)
774    
775          self.genDlg = None          self.genDlg = None
776            self.group = group
777    
778          ############################          LayerProperties.dialog_layout(self)
         # Create the controls  
         #  
779    
780          panel = wxPanel(self, -1)      def dialog_layout(self, panel, panelBox):
781    
782            if self.layer.HasClassification():
783    
784          text_title = wxTextCtrl(panel, ID_PROPERTY_TITLE, layer.Title())              self.fieldTypeText = wx.StaticText(panel, -1, "")
         self.fieldTypeText = wxStaticText(panel, -1, "")  
785    
         if layer.HasClassification():  
786              self.originalClass = self.layer.GetClassification()              self.originalClass = self.layer.GetClassification()
787              field = self.originalClass.GetField()              self.originalClassField = self.layer.GetClassificationColumn()
788              fieldType = self.originalClass.GetFieldType()              field = self.originalClassField
789                fieldType = self.layer.GetFieldType(field)
790    
791              table = layer.ShapeStore().Table()              table = self.layer.ShapeStore().Table()
792              #              #
793              # make field choice box              # make field choice box
794              #              #
795              self.fields = wxChoice(panel, ID_PROPERTY_SELECT,)              self.fields = wx.Choice(panel, ID_PROPERTY_SELECT,)
796    
797              self.num_cols = table.NumColumns()              self.num_cols = table.NumColumns()
798              # just assume the first field in case one hasn't been              # just assume the first field in case one hasn't been
799              # specified in the file.              # specified in the file.
800              self.__cur_field = 0              self.__cur_field = 0
801    
802              self.fields.Append("<None>")              self.fields.Append("<None>")
803    
804              if self.originalClass.GetFieldType() is None:              if fieldType is None:
805                  self.fields.SetClientData(0, copy.deepcopy(self.originalClass))                  self.fields.SetClientData(0, copy.deepcopy(self.originalClass))
806              else:              else:
807                  self.fields.SetClientData(0, None)                  self.fields.SetClientData(0, None)
# Line 730  class Classifier(NonModalNonParentDialog Line 812  class Classifier(NonModalNonParentDialog
812    
813                  if name == field:                  if name == field:
814                      self.__cur_field = i + 1                      self.__cur_field = i + 1
815                      self.fields.SetClientData(i + 1,                      self.fields.SetClientData(i + 1,
816                                              copy.deepcopy(self.originalClass))                                                copy.deepcopy(self.originalClass))
817                  else:                  else:
818                      self.fields.SetClientData(i + 1, None)                      self.fields.SetClientData(i + 1, None)
819    
820              button_gen = wxButton(panel, ID_PROPERTY_GENCLASS,              button_gen = wx.Button(panel, ID_PROPERTY_GENCLASS,
821                  _("Generate Class"))                  _("Generate Class"))
822              button_add = wxButton(panel, ID_PROPERTY_ADD,              button_add = wx.Button(panel, ID_PROPERTY_ADD,
823                  _("Add"))                  _("Add"))
824              button_moveup = wxButton(panel, ID_PROPERTY_MOVEUP,              button_moveup = wx.Button(panel, ID_PROPERTY_MOVEUP,
825                  _("Move Up"))                  _("Move Up"))
826              button_movedown = wxButton(panel, ID_PROPERTY_MOVEDOWN,              button_movedown = wx.Button(panel, ID_PROPERTY_MOVEDOWN,
827                  _("Move Down"))                  _("Move Down"))
828              button_edit = wxButton(panel, ID_PROPERTY_EDITSYM,              button_edit = wx.Button(panel, ID_PROPERTY_EDITSYM,
829                  _("Edit Symbol"))                  _("Edit Symbol"))
830              button_remove = wxButton(panel, ID_PROPERTY_REMOVE,              button_remove = wx.Button(panel, ID_PROPERTY_REMOVE,
831                  _("Remove"))                  _("Remove"))
832    
833              self.classGrid = ClassGrid(panel, self)              self.classGrid = ClassGrid(panel, self)
# Line 753  class Classifier(NonModalNonParentDialog Line 835  class Classifier(NonModalNonParentDialog
835              # calling __SelectField after creating the classGrid fills in the              # calling __SelectField after creating the classGrid fills in the
836              # grid with the correct information              # grid with the correct information
837              self.fields.SetSelection(self.__cur_field)              self.fields.SetSelection(self.__cur_field)
838              self.__SelectField(self.__cur_field, group = group)              self.__SelectField(self.__cur_field, group = self.group)
   
         button_try = wxButton(self, ID_PROPERTY_TRY, _("Try"))  
         button_revert = wxButton(self, ID_PROPERTY_REVERT, _("Revert"))  
         button_ok = wxButton(self, wxID_OK, _("OK"))  
         button_ok.SetDefault()  
         button_close = wxButton(self, wxID_CANCEL, _("Close"))  
   
         ############################  
         # Layout the controls  
         #  
   
         topBox = wxBoxSizer(wxVERTICAL)  
         panelBox = wxBoxSizer(wxVERTICAL)  
   
         sizer = wxBoxSizer(wxHORIZONTAL)  
         sizer.Add(wxStaticText(panel, -1, _("Title: ")),  
             0, wxALIGN_LEFT | wxALL | wxALIGN_CENTER_VERTICAL, 4)  
         sizer.Add(text_title, 1, wxGROW, 0)  
   
         panelBox.Add(sizer, 0, wxGROW, 4)  
   
         if isinstance(layer, RasterLayer):  
             type = "Image"  
         else:  
             type = layer.ShapeType()  
839    
         panelBox.Add(wxStaticText(panel, -1, _("Type: %s") % type),  
             0, wxALIGN_LEFT | wxALL, 4)  
840    
841          if layer.HasClassification():              classBox = wx.StaticBoxSizer(
842                            wx.StaticBox(panel, -1, _("Classification")), wx.VERTICAL)
843    
             classBox = wxStaticBoxSizer(  
                         wxStaticBox(panel, -1, _("Classification")), wxVERTICAL)  
844    
845                sizer = wx.BoxSizer(wx.HORIZONTAL)
846                sizer.Add(wx.StaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),
847                    0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4)
848                sizer.Add(self.fields, 1, wx.GROW | wx.ALL, 4)
849    
850              sizer = wxBoxSizer(wxHORIZONTAL)              classBox.Add(sizer, 0, wx.GROW, 4)
             sizer.Add(wxStaticText(panel, ID_PROPERTY_FIELDTEXT, _("Field: ")),  
                 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)  
             sizer.Add(self.fields, 1, wxGROW | wxALL, 4)  
851    
852              classBox.Add(sizer, 0, wxGROW, 4)              classBox.Add(self.fieldTypeText, 0,
853                            wx.GROW | wx.ALIGN_LEFT | wx.ALL | wx.ADJUST_MINSIZE, 4)
854    
855              classBox.Add(self.fieldTypeText, 0,              controlBox = wx.BoxSizer(wx.HORIZONTAL)
856                          wxGROW | wxALIGN_LEFT | wxALL | wxADJUST_MINSIZE, 4)              controlButtonBox = wx.BoxSizer(wx.VERTICAL)
857    
858              controlBox = wxBoxSizer(wxHORIZONTAL)              controlButtonBox.Add(button_gen, 0, wx.GROW|wx.ALL, 4)
859              controlButtonBox = wxBoxSizer(wxVERTICAL)              controlButtonBox.Add(button_add, 0, wx.GROW|wx.ALL, 4)
860                controlButtonBox.Add(button_moveup, 0, wx.GROW|wx.ALL, 4)
861              controlButtonBox.Add(button_gen, 0, wxGROW|wxALL, 4)              controlButtonBox.Add(button_movedown, 0, wx.GROW|wx.ALL, 4)
862              controlButtonBox.Add(button_add, 0, wxGROW|wxALL, 4)              controlButtonBox.Add(button_edit, 0, wx.GROW|wx.ALL, 4)
863              controlButtonBox.Add(button_moveup, 0, wxGROW|wxALL, 4)              controlButtonBox.Add( (60, 20), 0, wx.GROW|wx.ALL|wx.ALIGN_BOTTOM, 4)
864              controlButtonBox.Add(button_movedown, 0, wxGROW|wxALL, 4)              controlButtonBox.Add(button_remove, 0,
865              controlButtonBox.Add(button_edit, 0, wxGROW|wxALL, 4)                                   wx.GROW|wx.ALL|wx.ALIGN_BOTTOM, 4)
             controlButtonBox.Add(60, 20, 0, wxGROW|wxALL|wxALIGN_BOTTOM, 4)  
             controlButtonBox.Add(button_remove, 0,  
                                  wxGROW|wxALL|wxALIGN_BOTTOM, 4)  
   
             controlBox.Add(self.classGrid, 1, wxGROW, 0)  
             controlBox.Add(controlButtonBox, 0, wxGROW, 10)  
   
             classBox.Add(controlBox, 1, wxGROW, 10)  
             panelBox.Add(classBox, 1, wxGROW, 0)  
   
   
         buttonBox = wxBoxSizer(wxHORIZONTAL)  
         buttonBox.Add(button_try, 0, wxRIGHT|wxEXPAND, 10)  
         buttonBox.Add(button_revert, 0, wxRIGHT|wxEXPAND, 10)  
         buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)  
         buttonBox.Add(button_close, 0, wxRIGHT|wxEXPAND, 10)  
   
         panel.SetAutoLayout(True)  
         panel.SetSizer(panelBox)  
         panelBox.Fit(panel)  
         panelBox.SetSizeHints(panel)  
   
         topBox.Add(panel, 1, wxGROW | wxALL, 4)  
         topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)  
   
         self.SetAutoLayout(True)  
         self.SetSizer(topBox)  
         topBox.Fit(self)  
         topBox.SetSizeHints(self)  
         self.Layout()  
866    
867          ###########              controlBox.Add(self.classGrid, 1, wx.GROW, 0)
868                controlBox.Add(controlButtonBox, 0, wx.GROW, 10)
869    
870          EVT_CHOICE(self, ID_PROPERTY_SELECT, self._OnFieldSelect)              classBox.Add(controlBox, 1, wx.GROW, 10)
871          EVT_TEXT(self, ID_PROPERTY_TITLE, self._OnTitleChanged)              panelBox.Add(classBox, 1, wx.GROW, 0)
         EVT_BUTTON(self, wxID_OK, self._OnOK)  
         EVT_BUTTON(self, ID_PROPERTY_TRY, self._OnTry)  
         EVT_BUTTON(self, wxID_CANCEL, self._OnCloseBtn)  
         EVT_BUTTON(self, ID_PROPERTY_REVERT, self._OnRevert)  
   
         EVT_BUTTON(self, ID_PROPERTY_ADD, self._OnAdd)  
         EVT_BUTTON(self, ID_PROPERTY_EDITSYM, self._OnEditSymbol)  
         EVT_BUTTON(self, ID_PROPERTY_REMOVE, self._OnRemove)  
         EVT_BUTTON(self, ID_PROPERTY_GENCLASS, self._OnGenClass)  
         EVT_BUTTON(self, ID_PROPERTY_MOVEUP, self._OnMoveUp)  
         EVT_BUTTON(self, ID_PROPERTY_MOVEDOWN, self._OnMoveDown)  
872    
         ######################  
873    
874          text_title.SetFocus()          self.Bind(wx.EVT_CHOICE, self._OnFieldSelect, id=ID_PROPERTY_SELECT)
875          self.haveApplied = False          self.Bind(wx.EVT_BUTTON, self._OnAdd, id=ID_PROPERTY_ADD)
876            self.Bind(wx.EVT_BUTTON, self._OnEditSymbol, id=ID_PROPERTY_EDITSYM)
877            self.Bind(wx.EVT_BUTTON, self._OnRemove, id=ID_PROPERTY_REMOVE)
878            self.Bind(wx.EVT_BUTTON, self._OnGenClass, id=ID_PROPERTY_GENCLASS)
879            self.Bind(wx.EVT_BUTTON, self._OnMoveUp, id=ID_PROPERTY_MOVEUP)
880            self.Bind(wx.EVT_BUTTON, self._OnMoveDown, id=ID_PROPERTY_MOVEDOWN)
881    
882      def unsubscribe_messages(self):      def unsubscribe_messages(self):
883          self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)          """Unsubscribe from all messages."""
884            LayerProperties.unsubscribe_messages(self)
885          self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,          self.layer.Unsubscribe(LAYER_SHAPESTORE_REPLACED,
886                                 self.layer_shapestore_replaced)                                 self.layer_shapestore_replaced)
887    
     def map_layers_removed(self, map):  
         if self.layer not in self.map.Layers():  
             self.Close()  
   
888      def layer_shapestore_replaced(self, *args):      def layer_shapestore_replaced(self, *args):
889            """Subscribed to the map's LAYER_SHAPESTORE_REPLACED message.
890            Close self.
891            """
892          self.Close()          self.Close()
893    
894      def EditSymbol(self, row):      def EditSymbol(self, row):
895            """Open up a dialog where the user can select the properties
896            for a group.
897            """
898          table = self.classGrid.GetTable()          table = self.classGrid.GetTable()
899          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)          prop = table.GetValueAsCustom(row, COL_SYMBOL, None)
900    
901          # get a new ClassGroupProperties object and copy the          # get a new ClassGroupProperties object and copy the
902          # values over to our current object          # values over to our current object
903          propDlg = SelectPropertiesDialog(NULL, prop, self.layer.ShapeType())          propDlg = SelectPropertiesDialog(self, prop, self.layer.ShapeType())
904    
905          self.Enable(False)          self.Enable(False)
906          if propDlg.ShowModal() == wxID_OK:          if propDlg.ShowModal() == wx.ID_OK:
907              new_prop = propDlg.GetClassGroupProperties()              new_prop = propDlg.GetClassGroupProperties()
908              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)              table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
909          self.Enable(True)          self.Enable(True)
910          propDlg.Destroy()          propDlg.Destroy()
911            
912      def _SetClassification(self, clazz):      def _SetClassification(self, clazz):
913                    """Called from the ClassGen dialog when a new classification has
914            been created and should be set in the table.
915            """
916            # FIXME: This could be implemented using a message
917    
918          self.fields.SetClientData(self.__cur_field, clazz)          self.fields.SetClientData(self.__cur_field, clazz)
919          self.classGrid.GetTable().SetClassification(clazz)          self.classGrid.GetTable().SetClassification(clazz)
920    
921      def __BuildClassification(self, fieldIndex, copyClass = False):      def __BuildClassification(self, fieldIndex, copyClass=False, force=False):
922            """Pack the classification setting into a Classification object.
923            Returns (Classification, fieldName) where fieldName is the selected
924            field in the table that the classification should be used with.
925            """
926    
927  #       numRows = self.classGrid.GetNumberRows()  #       numRows = self.classGrid.GetNumberRows()
928  #       assert numRows > 0  # there should always be a default row  #       assert numRows > 0  # there should always be a default row
929    
 #       clazz = Classification()  
930          if fieldIndex == 0:          if fieldIndex == 0:
931              fieldName = None              fieldName = None
932              fieldType = None              fieldType = None
933          else:          else:
934              fieldName = self.fields.GetString(fieldIndex)              fieldName = internal_from_wxstring(self.fields.GetString(fieldIndex))
935              fieldType = self.layer.GetFieldType(fieldName)              fieldType = self.layer.GetFieldType(fieldName)
936    
937          clazz = self.classGrid.GetTable().GetClassification()          clazz = self.fields.GetClientData(fieldIndex)
938            if clazz is None or self.classGrid.GetTable().IsModified() or force:
939          if copyClass:              clazz = self.classGrid.GetTable().GetClassification()
940              clazz = copy.deepcopy(clazz)              if copyClass:
941                    clazz = copy.deepcopy(clazz)
         clazz.SetField(fieldName)  
         clazz.SetFieldType(fieldType)  
   
   
 #       table = self.classGrid.GetTable()  
 #       clazz.SetDefaultGroup(table.GetClassGroup(0))  
   
 #       for i in range(1, numRows):  
 #           clazz.AppendGroup(table.GetClassGroup(i))  
942    
943          return clazz          return clazz, fieldName
944    
945      def __SetGridTable(self, fieldIndex, group = None):      def __SetGridTable(self, fieldIndex, group = None):
946            """Set the table with the classification associated with the
947            selected field at fieldIndex. Select the specified group
948            if group is not None.
949            """
950    
951          clazz = self.fields.GetClientData(fieldIndex)          clazz = self.fields.GetClientData(fieldIndex)
952    
# Line 932  class Classifier(NonModalNonParentDialog Line 957  class Classifier(NonModalNonParentDialog
957                      self.layer.GetClassification().                      self.layer.GetClassification().
958                                 GetDefaultGroup().GetProperties()))                                 GetDefaultGroup().GetProperties()))
959    
960              fieldName = self.fields.GetString(fieldIndex)          fieldName = internal_from_wxstring(self.fields.GetString(fieldIndex))
961              fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
962              clazz.SetFieldType(fieldType)  
963                            self.classGrid.CreateTable(clazz, fieldType,
964          self.classGrid.CreateTable(clazz, self.layer.ShapeType(), group)                                     self.layer.ShapeType(), group)
965    
966      def __SetFieldTypeText(self, fieldIndex):      def __SetFieldTypeText(self, fieldIndex):
967          fieldName = self.fields.GetString(fieldIndex)          """Set the field type string using the data type of the field
968            at fieldIndex.
969            """
970            fieldName = internal_from_wxstring(self.fields.GetString(fieldIndex))
971          fieldType = self.layer.GetFieldType(fieldName)          fieldType = self.layer.GetFieldType(fieldName)
972    
973          assert Classifier.type2string.has_key(fieldType)          assert Classifier.type2string.has_key(fieldType)
974    
975          text = Classifier.type2string[fieldType]          text = Classifier.type2string[fieldType]
976    
# Line 956  class Classifier(NonModalNonParentDialog Line 984  class Classifier(NonModalNonParentDialog
984          assert oldIndex >= -1          assert oldIndex >= -1
985    
986          if oldIndex != -1:          if oldIndex != -1:
987              clazz = self.__BuildClassification(oldIndex)              clazz, name = self.__BuildClassification(oldIndex, force = True)
988              self.fields.SetClientData(oldIndex, clazz)              self.fields.SetClientData(oldIndex, clazz)
989    
990          self.__SetGridTable(newIndex, group)          self.__SetGridTable(newIndex, group)
991    
992          self.__EnableButtons(EB_SELECT_FIELD, newIndex != 0)          self.__EnableButtons(EB_SELECT_FIELD)
993    
994          self.__SetFieldTypeText(newIndex)          self.__SetFieldTypeText(newIndex)
995    
996      def __SetTitle(self, title):      def __SetTitle(self, title):
997            """Set the title of the dialog."""
998          if title != "":          if title != "":
999              title = ": " + title              title = ": " + title
1000    
1001          self.SetTitle(_("Layer Properties") + title)          self.SetTitle(_("Layer Properties") + title)
1002    
1003      def _OnEditSymbol(self, event):      def _OnEditSymbol(self, event):
1004            """Open up a dialog for the user to select group properties."""
1005          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1006    
1007          if len(sel) == 1:          if len(sel) == 1:
1008              self.EditSymbol(sel[0])              self.EditSymbol(sel[0])
1009    
1010      def _OnFieldSelect(self, event):      def _OnFieldSelect(self, event):
1011          index = self.fields.GetSelection()          index = self.fields.GetSelection()
1012          self.__SelectField(index, self.__cur_field)          self.__SelectField(index, self.__cur_field)
1013          self.__cur_field = index          self.__cur_field = index
1014    
1015      def _OnTry(self, event):      def OnTry(self, event):
1016          """Put the data from the table into a new Classification and hand          """Put the data from the table into a new Classification and hand
1017             it to the layer.             it to the layer.
1018          """          """
# Line 995  class Classifier(NonModalNonParentDialog Line 1025  class Classifier(NonModalNonParentDialog
1025              # to begin with or it has been modified              # to begin with or it has been modified
1026              #              #
1027              self.classGrid.SaveEditControlValue()              self.classGrid.SaveEditControlValue()
1028              if clazz is None or self.classGrid.GetTable().IsModified():              clazz, name = self.__BuildClassification(self.__cur_field, True)
                 clazz = self.__BuildClassification(self.__cur_field, True)  
1029    
1030                self.layer.SetClassificationColumn(name)
1031              self.layer.SetClassification(clazz)              self.layer.SetClassification(clazz)
1032    
1033          self.haveApplied = True          self.haveApplied = True
1034    
1035      def _OnOK(self, event):      def OnOK(self, event):
1036          self._OnTry(event)          self.OnTry(event)
         self.Close()  
   
     def OnClose(self, event):  
         self.unsubscribe_messages()  
         NonModalNonParentDialog.OnClose(self, event)  
   
     def _OnCloseBtn(self, event):  
         """Close is similar to Cancel except that any changes that were  
         made and applied remain applied, but the currently displayed  
         classification is discarded.  
         """  
   
1037          self.Close()          self.Close()
1038    
1039      def _OnRevert(self, event):      def OnRevert(self, event):
1040          """The layer's current classification stays the same."""          """The layer's current classification stays the same."""
1041          if self.haveApplied:          if self.haveApplied and self.layer.HasClassification():
1042                self.layer.SetClassificationColumn(self.originalClassField)
1043              self.layer.SetClassification(self.originalClass)              self.layer.SetClassification(self.originalClass)
1044    
1045          #self.Close()          #self.Close()
1046    
1047      def _OnAdd(self, event):      def _OnAdd(self, event):
1048          self.classGrid.AppendRows()          self.classGrid.AppendRows()
1049    
1050      def _OnRemove(self, event):      def _OnRemove(self, event):
1051          self.classGrid.DeleteSelectedRows()          self.classGrid.DeleteSelectedRows()
1052    
1053      def _OnGenClass(self, event):      def _OnGenClass(self, event):
1054            """Open up a dialog for the user to generate classifications."""
1055    
1056          self.genDlg = ClassGenDialog(self, self.layer,          self.genDlg = ClassGenDialog(self, self.layer,
1057                            self.fields.GetString(self.__cur_field))                internal_from_wxstring(self.fields.GetString(self.__cur_field)))
1058    
1059          EVT_CLOSE(self.genDlg, self._OnGenDialogClose)          self.Bind(wx.EVT_CLOSE, self._OnGenDialogClose, self.genDlg)
1060    
1061          self.__EnableButtons(EB_GEN_CLASS, False)          self.__EnableButtons(EB_GEN_CLASS)
1062    
1063          self.genDlg.Show()          self.genDlg.Show()
1064    
1065      def _OnGenDialogClose(self, event):      def _OnGenDialogClose(self, event):
1066            """Reenable buttons after the generate classification
1067            dialog is closed.
1068            """
1069          self.genDlg.Destroy()          self.genDlg.Destroy()
1070          self.__EnableButtons(EB_GEN_CLASS, True)          self.genDlg = None
1071            self.__EnableButtons(EB_GEN_CLASS)
1072    
1073      def _OnMoveUp(self, event):      def _OnMoveUp(self, event):
1074            """When the user clicks MoveUp, try to move a group up one row."""
1075          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1076    
1077          if len(sel) == 1:          if len(sel) == 1:
# Line 1062  class Classifier(NonModalNonParentDialog Line 1087  class Classifier(NonModalNonParentDialog
1087                  self.classGrid.MakeCellVisible(i - 1, 0)                  self.classGrid.MakeCellVisible(i - 1, 0)
1088    
1089      def _OnMoveDown(self, event):      def _OnMoveDown(self, event):
1090            """When the user clicks MoveDown, try to move a group down one row."""
1091          sel = self.classGrid.GetCurrentSelection()          sel = self.classGrid.GetCurrentSelection()
1092    
1093          if len(sel) == 1:          if len(sel) == 1:
# Line 1077  class Classifier(NonModalNonParentDialog Line 1103  class Classifier(NonModalNonParentDialog
1103                  self.classGrid.MakeCellVisible(i + 1, 0)                  self.classGrid.MakeCellVisible(i + 1, 0)
1104    
1105      def _OnTitleChanged(self, event):      def _OnTitleChanged(self, event):
1106            """Update the dialog title when the user changed the layer name."""
1107          obj = event.GetEventObject()          obj = event.GetEventObject()
1108    
1109          self.layer.SetTitle(obj.GetValue())          self.layer.SetTitle(obj.GetValue())
1110          self.__SetTitle(self.layer.Title())          self.__SetTitle(self.layer.Title())
1111    
1112          self.__EnableButtons(EB_LAYER_TITLE, self.layer.Title() != "")          self.__EnableButtons(EB_LAYER_TITLE)
1113    
1114      def __EnableButtons(self, case, enable):      def __EnableButtons(self, case):
1115            """Helper method that enables/disables the appropriate buttons
1116            based on the case provided. Cases are constants beginning with EB_.
1117            """
1118    
1119          if case == EB_LAYER_TITLE:            list = {wx.ID_OK                 : True,
1120              list = (wxID_OK,                  wx.ID_CANCEL             : True,
1121                      wxID_CANCEL)                  ID_PROPERTY_ADD         : True,
1122                    ID_PROPERTY_MOVEUP      : True,
1123                    ID_PROPERTY_MOVEDOWN    : True,
1124                    ID_PROPERTY_REMOVE      : True,
1125                    ID_PROPERTY_SELECT      : True,
1126                    ID_PROPERTY_FIELDTEXT   : True,
1127                    ID_PROPERTY_GENCLASS    : True,
1128                    ID_PROPERTY_EDITSYM     : True}
1129    
1130            if case == EB_LAYER_TITLE:
1131                if self.layer.Title() == "":
1132                    list[wxID_OK] = False
1133                    list[wxID_CANCEL] = False
1134    
1135          elif case == EB_SELECT_FIELD:          elif case == EB_SELECT_FIELD:
1136              list = (ID_PROPERTY_GENCLASS,              if self.fields.GetSelection() == 0:
1137                      ID_PROPERTY_ADD,                  list[ID_PROPERTY_GENCLASS] = False
1138                      ID_PROPERTY_MOVEUP,                  list[ID_PROPERTY_ADD] = False
1139                      ID_PROPERTY_MOVEDOWN,                  list[ID_PROPERTY_MOVEUP] = False
1140                      ID_PROPERTY_EDITSYM,                  list[ID_PROPERTY_MOVEDOWN] = False
1141                      ID_PROPERTY_REMOVE)                  list[ID_PROPERTY_REMOVE] = False
1142    
1143          elif case == EB_GEN_CLASS:          elif case == EB_GEN_CLASS:
1144              list = (ID_PROPERTY_SELECT,              if self.genDlg is not None:
1145                      ID_PROPERTY_FIELDTEXT,                  list[ID_PROPERTY_SELECT] = False
1146                      ID_PROPERTY_GENCLASS,                  list[ID_PROPERTY_FIELDTEXT] = False
1147                      ID_PROPERTY_EDITSYM)                  list[ID_PROPERTY_GENCLASS] = False
1148    
1149            for id, enable in list.items():
1150                win = self.FindWindowById(id)
1151                if win:
1152                    win.Enable(enable)
1153    
1154          for id in list:  ID_SELPROP_SPINCTRL_LINEWIDTH = 4002
             self.FindWindowById(id).Enable(enable)  
   
 ID_SELPROP_SPINCTRL = 4002  
1155  ID_SELPROP_PREVIEW = 4003  ID_SELPROP_PREVIEW = 4003
1156  ID_SELPROP_STROKECLR = 4004  ID_SELPROP_STROKECLR = 4004
1157  ID_SELPROP_FILLCLR = 4005  ID_SELPROP_FILLCLR = 4005
1158  ID_SELPROP_STROKECLRTRANS = 4006  ID_SELPROP_STROKECLRTRANS = 4006
1159  ID_SELPROP_FILLCLRTRANS = 4007  ID_SELPROP_FILLCLRTRANS = 4007
1160    ID_SELPROP_SPINCTRL_SIZE = 4008
1161    
1162  class SelectPropertiesDialog(wxDialog):  class SelectPropertiesDialog(wx.Dialog):
1163        """Dialog that allows the user to select group properties."""
1164    
1165      def __init__(self, parent, prop, shapeType):      def __init__(self, parent, prop, shapeType):
1166          wxDialog.__init__(self, parent, -1, _("Select Properties"),          """Open the dialog with the initial prop properties and shapeType."""
1167                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)  
1168            wx.Dialog.__init__(self, parent, -1, _("Select Properties"),
1169                              style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
1170    
1171          self.prop = ClassGroupProperties(prop)          self.prop = ClassGroupProperties(prop)
1172    
1173          topBox = wxBoxSizer(wxVERTICAL)          topBox = wx.BoxSizer(wx.VERTICAL)
1174    
1175          itemBox = wxBoxSizer(wxHORIZONTAL)          itemBox = wx.BoxSizer(wx.HORIZONTAL)
1176    
1177          # preview box          # preview box
1178          previewBox = wxBoxSizer(wxVERTICAL)          previewBox = wx.BoxSizer(wx.VERTICAL)
1179          previewBox.Add(wxStaticText(self, -1, _("Preview:")),          previewBox.Add(wx.StaticText(self, -1, _("Preview:")),
1180              0, wxALIGN_LEFT | wxALL, 4)              0, wx.ALIGN_LEFT | wx.ALL, 4)
1181    
1182          self.previewWin = ClassGroupPropertiesCtrl(          self.previewWin = ClassGroupPropertiesCtrl(
1183              self, ID_SELPROP_PREVIEW, self.prop, shapeType,              self, ID_SELPROP_PREVIEW, self.prop, shapeType,
1184              (40, 40), wxSIMPLE_BORDER)              (40, 40), wx.SIMPLE_BORDER)
1185    
1186          self.previewWin.AllowEdit(False)          self.previewWin.AllowEdit(False)
1187    
1188          previewBox.Add(self.previewWin, 1, wxGROW | wxALL, 4)          previewBox.Add(self.previewWin, 1, wx.GROW | wx.ALL, 4)
1189    
1190          itemBox.Add(previewBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          itemBox.Add(previewBox, 1, wx.ALIGN_LEFT | wx.ALL | wx.GROW, 0)
1191    
1192          # control box          # control box
1193          ctrlBox = wxBoxSizer(wxVERTICAL)          ctrlBox = wx.BoxSizer(wx.VERTICAL)
1194    
1195          lineColorBox = wxBoxSizer(wxHORIZONTAL)          lineColorBox = wx.BoxSizer(wx.HORIZONTAL)
1196          button = wxButton(self, ID_SELPROP_STROKECLR, _("Change Line Color"))          button = wx.Button(self, ID_SELPROP_STROKECLR, _("Change Line Color"))
1197          button.SetFocus()          button.SetFocus()
1198          lineColorBox.Add(button, 1, wxALL | wxGROW, 4)          lineColorBox.Add(button, 1, wx.ALL | wx.GROW, 4)
1199          EVT_BUTTON(self, ID_SELPROP_STROKECLR, self._OnChangeLineColor)          self.Bind(wx.EVT_BUTTON, self._OnChangeLineColor, id=ID_SELPROP_STROKECLR)
1200    
1201          lineColorBox.Add(          lineColorBox.Add(
1202              wxButton(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),              wx.Button(self, ID_SELPROP_STROKECLRTRANS, _("Transparent")),
1203              1, wxALL | wxGROW, 4)              1, wx.ALL | wx.GROW, 4)
1204          EVT_BUTTON(self, ID_SELPROP_STROKECLRTRANS,          self.Bind(wx.EVT_BUTTON, self._OnChangeLineColorTrans, \
1205                     self._OnChangeLineColorTrans)                                     id=ID_SELPROP_STROKECLRTRANS)
1206    
1207          ctrlBox.Add(lineColorBox, 0,          ctrlBox.Add(lineColorBox, 0,
1208                      wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)                      wx.ALIGN_CENTER_HORIZONTAL | wx.ALL | wx.GROW, 4)
1209    
1210          if shapeType != SHAPETYPE_ARC:          if shapeType != SHAPETYPE_ARC:
1211              fillColorBox = wxBoxSizer(wxHORIZONTAL)              fillColorBox = wx.BoxSizer(wx.HORIZONTAL)
1212              fillColorBox.Add(              fillColorBox.Add(
1213                  wxButton(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),                  wx.Button(self, ID_SELPROP_FILLCLR, _("Change Fill Color")),
1214                  1, wxALL | wxGROW, 4)                  1, wx.ALL | wx.GROW, 4)
1215              EVT_BUTTON(self, ID_SELPROP_FILLCLR, self._OnChangeFillColor)              self.Bind(wx.EVT_BUTTON, self._OnChangeFillColor, id=ID_SELPROP_FILLCLR)
1216              fillColorBox.Add(              fillColorBox.Add(
1217                  wxButton(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),                  wx.Button(self, ID_SELPROP_FILLCLRTRANS, _("Transparent")),
1218                  1, wxALL | wxGROW, 4)                  1, wx.ALL | wx.GROW, 4)
1219              EVT_BUTTON(self, ID_SELPROP_FILLCLRTRANS,              self.Bind(wx.EVT_BUTTON, self._OnChangeFillColorTrans,\
1220                         self._OnChangeFillColorTrans)                                          id=ID_SELPROP_FILLCLRTRANS)
1221              ctrlBox.Add(fillColorBox, 0,              ctrlBox.Add(fillColorBox, 0,
1222                          wxALIGN_CENTER_HORIZONTAL | wxALL | wxGROW, 4)                          wx.ALIGN_CENTER_HORIZONTAL | wx.ALL | wx.GROW, 4)
1223    
1224          spinBox = wxBoxSizer(wxHORIZONTAL)          # Line width selection
1225          spinBox.Add(wxStaticText(self, -1, _("Line Width: ")),          spinBox = wx.BoxSizer(wx.HORIZONTAL)
1226                  0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4)          spinBox.Add(wx.StaticText(self, -1, _("Line Width: ")),
1227          self.spinCtrl = wxSpinCtrl(self, ID_SELPROP_SPINCTRL,                  0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4)
1228                                     min=1, max=10,          self.spinCtrl_linewidth = wx.SpinCtrl(self,
1229                                     value=str(prop.GetLineWidth()),                                               ID_SELPROP_SPINCTRL_LINEWIDTH,
1230                                     initial=prop.GetLineWidth())                                               min=1, max=10,
1231                                                 value=str(prop.GetLineWidth()),
1232          EVT_SPINCTRL(self, ID_SELPROP_SPINCTRL, self._OnSpin)                                               initial=prop.GetLineWidth())
1233    
1234          spinBox.Add(self.spinCtrl, 0, wxALIGN_LEFT | wxALL, 4)          self.Bind(wx.EVT_SPINCTRL, self._OnSpinLineWidth, \
1235                        id=ID_SELPROP_SPINCTRL_LINEWIDTH)
1236          ctrlBox.Add(spinBox, 0, wxALIGN_RIGHT | wxALL, 0)  
1237          itemBox.Add(ctrlBox, 0, wxALIGN_RIGHT | wxALL | wxGROW, 0)          spinBox.Add(self.spinCtrl_linewidth, 0, wx.ALIGN_LEFT | wx.ALL, 4)
1238          topBox.Add(itemBox, 1, wxALIGN_LEFT | wxALL | wxGROW, 0)          ctrlBox.Add(spinBox, 0, wx.ALIGN_RIGHT | wx.ALL, 0)
1239    
1240            # Size selection
1241            if shapeType == SHAPETYPE_POINT:
1242                spinBox = wx.BoxSizer(wx.HORIZONTAL)
1243                spinBox.Add(wx.StaticText(self, -1, _("Size: ")),
1244                            0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4)
1245                self.spinCtrl_size = wx.SpinCtrl(self, ID_SELPROP_SPINCTRL_SIZE,
1246                                                min=1, max=100,
1247                                                value=str(prop.GetSize()),
1248                                                initial=prop.GetSize())
1249    
1250                self.Bind(wx.EVT_SPINCTRL, self._OnSpinSize, id=ID_SELPROP_SPINCTRL_SIZE)
1251    
1252                spinBox.Add(self.spinCtrl_size, 0, wx.ALIGN_LEFT | wx.ALL, 4)
1253                ctrlBox.Add(spinBox, 0, wx.ALIGN_RIGHT | wx.ALL, 0)
1254    
1255    
1256            itemBox.Add(ctrlBox, 0, wx.ALIGN_RIGHT | wx.ALL | wx.GROW, 0)
1257            topBox.Add(itemBox, 1, wx.ALIGN_LEFT | wx.ALL | wx.GROW, 0)
1258    
1259          #          #
1260          # Control buttons:          # Control buttons:
1261          #          #
1262          buttonBox = wxBoxSizer(wxHORIZONTAL)          buttonBox = wx.BoxSizer(wx.HORIZONTAL)
1263          button_ok = wxButton(self, wxID_OK, _("OK"))          button_ok = wx.Button(self, wx.ID_OK, _("OK"))
1264            buttonBox.Add(button_ok, 0, wx.RIGHT|wx.EXPAND, 10)
1265            buttonBox.Add(wx.Button(self, wx.ID_CANCEL, _("Cancel")),
1266                          0, wx.RIGHT|wx.EXPAND, 10)
1267            topBox.Add(buttonBox, 0, wx.ALIGN_RIGHT|wx.BOTTOM|wx.TOP, 10)
1268    
1269          button_ok.SetDefault()          button_ok.SetDefault()
1270          buttonBox.Add(button_ok, 0, wxRIGHT|wxEXPAND, 10)  
         buttonBox.Add(wxButton(self, wxID_CANCEL, _("Cancel")),  
                       0, wxRIGHT|wxEXPAND, 10)  
         topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)  
                                                                                   
1271          #EVT_BUTTON(self, wxID_OK, self._OnOK)          #EVT_BUTTON(self, wxID_OK, self._OnOK)
1272          #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)          #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
1273                                                                                    
1274          self.SetAutoLayout(True)          self.SetAutoLayout(True)
1275          self.SetSizer(topBox)          self.SetSizer(topBox)
1276          topBox.Fit(self)          topBox.Fit(self)
1277          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
1278    
1279      def OnOK(self, event):      def OnOK(self, event):
1280          self.EndModal(wxID_OK)          self.EndModal(wx.ID_OK)
1281    
1282      def OnCancel(self, event):      def OnCancel(self, event):
1283          self.EndModal(wxID_CANCEL)          self.EndModal(wx.ID_CANCEL)
1284    
1285        def _OnSpinLineWidth(self, event):
1286            self.prop.SetLineWidth(self.spinCtrl_linewidth.GetValue())
1287            self.previewWin.Refresh()
1288    
1289      def _OnSpin(self, event):      def _OnSpinSize(self, event):
1290          self.prop.SetLineWidth(self.spinCtrl.GetValue())          self.prop.SetSize(self.spinCtrl_size.GetValue())
1291          self.previewWin.Refresh()          self.previewWin.Refresh()
1292    
1293      def __GetColor(self, cur):      def __GetColor(self, cur):
1294          dialog = wxColourDialog(self)          dialog = ColorDialog(self)
1295          if cur is not Color.Transparent:          dialog.SetColor(cur)
             dialog.GetColourData().SetColour(Color2wxColour(cur))  
1296    
1297          ret = None          ret = None
1298          if dialog.ShowModal() == wxID_OK:          if dialog.ShowModal() == wx.ID_OK:
1299              ret = wxColour2Color(dialog.GetColourData().GetColour())              ret = dialog.GetColor()
1300    
1301          dialog.Destroy()          dialog.Destroy()
1302    
1303          return ret          return ret
1304            
1305      def _OnChangeLineColor(self, event):      def _OnChangeLineColor(self, event):
1306          clr = self.__GetColor(self.prop.GetLineColor())          clr = self.__GetColor(self.prop.GetLineColor())
1307          if clr is not None:          if clr is not None:
# Line 1238  class SelectPropertiesDialog(wxDialog): Line 1309  class SelectPropertiesDialog(wxDialog):
1309          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1310    
1311      def _OnChangeLineColorTrans(self, event):      def _OnChangeLineColorTrans(self, event):
1312          self.prop.SetLineColor(Color.Transparent)          self.prop.SetLineColor(Transparent)
1313          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1314            
1315      def _OnChangeFillColor(self, event):      def _OnChangeFillColor(self, event):
1316          clr = self.__GetColor(self.prop.GetFill())          clr = self.__GetColor(self.prop.GetFill())
1317          if clr is not None:          if clr is not None:
# Line 1248  class SelectPropertiesDialog(wxDialog): Line 1319  class SelectPropertiesDialog(wxDialog):
1319          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1320    
1321      def _OnChangeFillColorTrans(self, event):      def _OnChangeFillColorTrans(self, event):
1322          self.prop.SetFill(Color.Transparent)          self.prop.SetFill(Transparent)
1323          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer          self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
1324    
1325      def GetClassGroupProperties(self):      def GetClassGroupProperties(self):
1326          return self.prop          return self.prop
1327    
1328    
1329  class ClassDataPreviewWindow(wxWindow):  class ClassDataPreviewWindow(wx.Window):
1330        """A custom window that draws group properties using the correct shape."""
1331    
1332      def __init__(self, rect, prop, shapeType,      def __init__(self, rect, prop, shapeType,
1333                         parent = None, id = -1, size = wxDefaultSize):                         parent = None, id = -1, size = wx.DefaultSize):
1334            """Draws the appropriate shape as specified with shapeType using
1335            prop properities.
1336            """
1337          if parent is not None:          if parent is not None:
1338              wxWindow.__init__(self, parent, id, (0, 0), size)              wx.Window.__init__(self, parent, id, (0, 0), size)
1339              EVT_PAINT(self, self._OnPaint)              self.Bind(wx.EVT_PAINT, self._OnPaint)
1340    
1341          self.rect = rect          self.rect = rect
1342    
# Line 1273  class ClassDataPreviewWindow(wxWindow): Line 1348  class ClassDataPreviewWindow(wxWindow):
1348          return self.prop          return self.prop
1349    
1350      def _OnPaint(self, event):      def _OnPaint(self, event):
1351          dc = wxPaintDC(self)          dc = wx.PaintDC(self)
1352    
1353          # XXX: this doesn't seem to be having an effect:          # XXX: this doesn't seem to be having an effect:
1354          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1355    
1356          if self.rect is None:          if self.rect is None:
1357              w, h = self.GetSize()              w, h = self.GetSize()
1358              rect = wxRect(0, 0, w, h)              rect = wx.Rect(0, 0, w, h)
1359          else:          else:
1360              rect = self.rect              rect = self.rect
1361    
1362          self.previewer.Draw(dc, rect, self.prop, self.shapeType)          self.previewer.Draw(dc, rect, self.prop, self.shapeType)
1363    
1364  class ClassDataPreviewer:  class ClassDataPreviewer:
1365        """Class that actually draws a group property preview."""
1366    
1367      def Draw(self, dc, rect, prop, shapeType):      def Draw(self, dc, rect, prop, shapeType):
1368            """Draw the property.
1369    
1370            returns: (w, h) as adapted extend if the drawing size
1371            exceeded the given rect. This can only be the case
1372            for point symbols. If the symbol fits the given rect,
1373            None is returned.
1374            """
1375    
1376          assert dc is not None          assert dc is not None
1377          assert isinstance(prop, ClassGroupProperties)          assert isinstance(prop, ClassGroupProperties)
# Line 1296  class ClassDataPreviewer: Line 1379  class ClassDataPreviewer:
1379          if rect is None:          if rect is None:
1380              x = 0              x = 0
1381              y = 0              y = 0
1382              w, h = dc.GetSize()              w = dc.GetSize().GetWidth()
1383                h = dc.GetSize().GetHeight()
1384          else:          else:
1385              x = rect.GetX()              x = rect.GetX()
1386              y = rect.GetY()              y = rect.GetY()
# Line 1304  class ClassDataPreviewer: Line 1388  class ClassDataPreviewer:
1388              h = rect.GetHeight()              h = rect.GetHeight()
1389    
1390          stroke = prop.GetLineColor()          stroke = prop.GetLineColor()
1391          if stroke is Color.Transparent:          if stroke is Transparent:
1392              pen = wxTRANSPARENT_PEN              pen = wx.TRANSPARENT_PEN
1393          else:          else:
1394              pen = wxPen(Color2wxColour(stroke),              pen = wx.Pen(Color2wxColour(stroke),
1395                          prop.GetLineWidth(),                          prop.GetLineWidth(),
1396                          wxSOLID)                          wx.SOLID)
1397    
1398          stroke = prop.GetFill()          stroke = prop.GetFill()
1399          if stroke is Color.Transparent:          if stroke is Transparent:
1400              brush = wxTRANSPARENT_BRUSH              brush = wx.TRANSPARENT_BRUSH
1401          else:          else:
1402              brush = wxBrush(Color2wxColour(stroke), wxSOLID)              brush = wx.Brush(Color2wxColour(stroke), wx.SOLID)
1403    
1404          dc.SetPen(pen)          dc.SetPen(pen)
1405          dc.SetBrush(brush)          dc.SetBrush(brush)
1406    
1407          if shapeType == SHAPETYPE_ARC:          if shapeType == SHAPETYPE_ARC:
1408              dc.DrawSpline([wxPoint(x, y + h),              dc.DrawSpline([wx.Point(x, y + h),
1409                             wxPoint(x + w/2, y + h/4),                             wx.Point(x + w/2, y + h/4),
1410                             wxPoint(x + w/2, y + h/4*3),                             wx.Point(x + w/2, y + h/4*3),
1411                             wxPoint(x + w, y)])                             wx.Point(x + w, y)])
1412    
1413          elif shapeType == SHAPETYPE_POINT:          elif shapeType == SHAPETYPE_POINT:
1414    
1415              dc.DrawCircle(x + w/2, y + h/2,              dc.DrawCircle(x + w/2, y + h/2, prop.GetSize())
1416                            (min(w, h) - prop.GetLineWidth())/2)              circle_size =  prop.GetSize() * 2 + prop.GetLineWidth() * 2
1417                new_h = h
1418                new_w = w
1419                if h < circle_size: new_h = circle_size
1420                if w < circle_size: new_w = circle_size
1421                if new_h > h or new_w > w:
1422                    return (new_w, new_h)
1423    
1424          elif shapeType == SHAPETYPE_POLYGON:          elif shapeType == SHAPETYPE_POLYGON:
1425              dc.DrawRectangle(x, y, w, h)              dc.DrawRectangle(x, y, w, h)
1426    
1427  class ClassRenderer(wxPyGridCellRenderer):          return None
1428    
1429    class ClassRenderer(grid.PyGridCellRenderer):
1430        """A wrapper class that can be used to draw group properties in a
1431        grid table.
1432        """
1433    
1434      def __init__(self, shapeType):      def __init__(self, shapeType):
1435          wxPyGridCellRenderer.__init__(self)          grid.PyGridCellRenderer.__init__(self)
1436          self.shapeType = shapeType          self.shapeType = shapeType
1437          self.previewer = ClassDataPreviewer()          self.previewer = ClassDataPreviewer()
1438    
1439      def Draw(self, grid, attr, dc, rect, row, col, isSelected):      def Draw(self, grid, attr, dc, rect, row, col, isSelected):
1440          data = grid.GetTable().GetClassGroup(row)          data = grid.GetTable().GetClassGroup(row)
1441    
1442          dc.SetClippingRegion(rect.GetX(), rect.GetY(),          dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1443                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
1444          dc.SetPen(wxPen(wxLIGHT_GREY))          dc.SetPen(wx.Pen(wx.LIGHT_GREY))
1445          dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))          dc.SetBrush(wx.Brush(wx.LIGHT_GREY, wx.SOLID))
1446          dc.DrawRectangle(rect.GetX(), rect.GetY(),          dc.DrawRectangle(rect.GetX(), rect.GetY(),
1447                           rect.GetWidth(), rect.GetHeight())                           rect.GetWidth(), rect.GetHeight())
1448    
1449          if not isinstance(data, ClassGroupMap):          if not isinstance(data, ClassGroupMap):
1450              self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)              new_size = self.previewer.Draw(dc, rect, data.GetProperties(),
1451                                               self.shapeType)
1452                if new_size is not None:
1453                    (new_w, new_h) = new_size
1454                    grid.SetRowSize(row, new_h)
1455                    grid.SetColSize(col, new_h)
1456                    grid.ForceRefresh()
1457    
1458                    # now that we know the height, redraw everything
1459                    rect.SetHeight(new_h)
1460                    rect.SetWidth(new_w)
1461                    dc.DestroyClippingRegion()
1462                    dc.SetClippingRegion(rect.GetX(), rect.GetY(),
1463                                         rect.GetWidth(), rect.GetHeight())
1464                    dc.SetPen(wx.Pen(wx.LIGHT_GREY))
1465                    dc.SetBrush(wx.Brush(wx.LIGHT_GREY, wx.SOLID))
1466                    dc.DrawRectangle(rect.GetX(), rect.GetY(),
1467                                     rect.GetWidth(), rect.GetHeight())
1468                    self.previewer.Draw(dc, rect, data.GetProperties(),
1469                                        self.shapeType)
1470    
1471          if isSelected:          if isSelected:
1472              dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))              dc.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID))
1473              dc.SetBrush(wxTRANSPARENT_BRUSH)              dc.SetBrush(wx.TRANSPARENT_BRUSH)
1474    
1475              dc.DrawRectangle(rect.GetX(), rect.GetY(),              dc.DrawRectangle(rect.GetX(), rect.GetY(),
1476                               rect.GetWidth(), rect.GetHeight())                               rect.GetWidth(), rect.GetHeight())
1477    
1478          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1479    
1480    
1481  class ClassGroupPropertiesCtrl(wxWindow, wxControl):  class ClassGroupPropertiesCtrl(wx.Control):
1482        """A custom window and control that draw a preview of group properties
1483      def __init__(self, parent, id, props, shapeType,      and can open a dialog to modify the properties if the user double-clicks
1484                   size = wxDefaultSize, style = 0):      it.
1485        """
1486    
1487        def __init__(self, parent, id, props, shapeType,
1488                     size = wx.DefaultSize, style = 0):
1489            wx.Control.__init__(self, parent, id, size = size, style = style)
1490    
1491          wxWindow.__init__(self, parent, id, size = size, style = style)          self.parent = parent
1492    
1493          self.SetProperties(props)          self.SetProperties(props)
1494          self.SetShapeType(shapeType)          self.SetShapeType(shapeType)
1495          self.AllowEdit(True)          self.AllowEdit(True)
1496    
1497          EVT_PAINT(self, self._OnPaint)          self.Bind(wx.EVT_PAINT, self._OnPaint)
1498          EVT_LEFT_DCLICK(self, self._OnLeftDClick)          self.Bind(wx.EVT_LEFT_DCLICK, self._OnLeftDClick)
1499    
1500          self.previewer = ClassDataPreviewer()          self.previewer = ClassDataPreviewer()
1501    
1502      def _OnPaint(self, event):      def _OnPaint(self, event):
1503          dc = wxPaintDC(self)          dc = wx.PaintDC(self)
1504    
1505          # XXX: this doesn't seem to be having an effect:          # XXX: this doesn't seem to be having an effect:
1506          dc.DestroyClippingRegion()          dc.DestroyClippingRegion()
1507    
1508          w, h = self.GetClientSize()          w, h = self.GetClientSize()
1509    
1510          self.previewer.Draw(dc,          self.previewer.Draw(dc,
1511                              wxRect(0, 0, w, h),                              wx.Rect(0, 0, w, h),
1512                              self.GetProperties(),                              self.GetProperties(),
1513                              self.GetShapeType())                              self.GetShapeType())
1514    
1515    
# Line 1409  class ClassGroupPropertiesCtrl(wxWindow, Line 1528  class ClassGroupPropertiesCtrl(wxWindow,
1528          self.Refresh()          self.Refresh()
1529    
1530      def AllowEdit(self, allow):      def AllowEdit(self, allow):
1531            """Allow/Disallow double-clicking on the control."""
1532          self.allowEdit = allow          self.allowEdit = allow
1533    
1534      def DoEdit(self):      def DoEdit(self):
1535            """Open the properties selector dialog."""
1536    
1537          if not self.allowEdit: return          if not self.allowEdit: return
1538    
1539          propDlg = SelectPropertiesDialog(NULL,          propDlg = SelectPropertiesDialog(self.parent,
1540                                           self.GetProperties(),                                           self.GetProperties(),
1541                                           self.GetShapeType())                                           self.GetShapeType())
1542    
1543          if propDlg.ShowModal() == wxID_OK:          if propDlg.ShowModal() == wx.ID_OK:
1544              new_prop = propDlg.GetClassGroupProperties()              new_prop = propDlg.GetClassGroupProperties()
1545              self.SetProperties(new_prop)              self.SetProperties(new_prop)
1546              self.Refresh()              self.Refresh()
# Line 1427  class ClassGroupPropertiesCtrl(wxWindow, Line 1549  class ClassGroupPropertiesCtrl(wxWindow,
1549    
1550      def _OnLeftDClick(self, event):      def _OnLeftDClick(self, event):
1551          self.DoEdit()          self.DoEdit()
1552    

Legend:
Removed from v.1219  
changed lines
  Added in v.2817

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26