/[thuban]/trunk/thuban/Thuban/UI/classifier.py
ViewVC logotype

Annotation of /trunk/thuban/Thuban/UI/classifier.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 671 - (hide annotations)
Tue Apr 15 18:09:47 2003 UTC (21 years, 10 months ago) by bh
File MIME type: text/x-python
File size: 44544 byte(s)
* Thuban/__init__.py (_): Implement the translation function for
real using the python gettext module.

* Thuban/UI/classifier.py (ClassTable.GetRowLabelValue): Don't
translate empty strings.

* Thuban/UI/application.py (ThubanApplication.read_startup_files):
Add a missing space to a warning message

* po/README: New. Notes about the management of the translation
files.

* po/Makefile: New. Makefile to help manage the translation files.

* po/es.po: New. Spanish translation by Daniel Calvelo Aros

* MANIFEST.in: Include the *.mo files in Resources/Locale and the
translations and support files in po/

* setup.py (data_files): Add the *.mo files to the data_files too

* README: Add note about the translations when building from CVS

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

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26