/[thuban]/branches/greater-ms3/thuban/Thuban/UI/legend.py
ViewVC logotype

Contents of /branches/greater-ms3/thuban/Thuban/UI/legend.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1217 - (show annotations)
Mon Jun 16 16:37:19 2003 UTC (21 years, 8 months ago) by frank
File MIME type: text/x-python
File size: 21123 byte(s)
(LegendTree.__init__): Instance variable
	raiseProperties initialized to prevent endless loops
(LegendTree._OnItemActivated): Depending on self.raiseProperties
	simply raise the properties or open the dialog and issue a second
	event.

1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Jonathan Coles <[email protected]>
4 # Frank Koormann <[email protected]>
5 #
6 # This program is free software under the GPL (>=v2)
7 # Read the file COPYING coming with Thuban for details.
8
9 __version__ = "$Revision$"
10
11 from Thuban import _
12
13 import resource
14
15 from wxPython.wx import *
16
17 from Thuban.Model.layer import BaseLayer
18 from Thuban.Model.map import Map
19 from Thuban.Model.classification import ClassGroup
20
21 from Thuban.Model.messages import \
22 MAP_STACKING_CHANGED, MAP_LAYERS_ADDED, MAP_LAYERS_REMOVED, LAYER_CHANGED,\
23 LAYER_VISIBILITY_CHANGED, TITLE_CHANGED
24
25 from Thuban.UI.messages import SCALE_CHANGED
26
27 from Thuban.UI.classifier import ClassDataPreviewer
28 from Thuban.UI.dock import DockPanel
29 from Thuban.UI.scalebar import ScaleBar
30
31 from Thuban.Lib.connector import ConnectorError
32
33 ID_LEGEND_TOP = 4001
34 ID_LEGEND_RAISE = 4002
35 ID_LEGEND_LOWER = 4003
36 ID_LEGEND_BOTTOM = 4004
37 ID_LEGEND_TREE = 4005
38 ID_LEGEND_PROPS = 4006
39 ID_LEGEND_SHOWLAYER = 4007
40 ID_LEGEND_HIDELAYER = 4008
41
42 BMP_SIZE_W = 15
43 BMP_SIZE_H = 15
44
45 TOP_BMP = "top_layer"
46 RAISE_BMP = "raise_layer"
47 LOWER_BMP = "lower_layer"
48 BOTTOM_BMP = "bottom_layer"
49 SHOW_BMP = "show_layer"
50 HIDE_BMP = "hide_layer"
51 PROPS_BMP = "layer_properties"
52
53
54 class LegendPanel(DockPanel):
55
56 def __init__(self, parent, map, mainWindow):
57 DockPanel.__init__(self, parent, -1)
58
59 self.mainWindow = mainWindow
60 self.parent = parent
61
62 self.buttons = []
63
64 panelBox = wxBoxSizer(wxVERTICAL)
65
66 self.toolBar = wxToolBar(self, -1)
67 self.toolBar.SetToolBitmapSize(wxSize(24, 24))
68
69 bmp = resource.GetBitmapResource(TOP_BMP, wxBITMAP_TYPE_XPM)
70 self.toolBar.AddTool(ID_LEGEND_TOP, bmp,
71 shortHelpString=_("Top Layer"))
72
73 bmp = resource.GetBitmapResource(RAISE_BMP, wxBITMAP_TYPE_XPM)
74 self.toolBar.AddTool(ID_LEGEND_RAISE, bmp,
75 shortHelpString=_("Raise Layer"))
76
77 bmp = resource.GetBitmapResource(LOWER_BMP, wxBITMAP_TYPE_XPM)
78 self.toolBar.AddTool(ID_LEGEND_LOWER, bmp,
79 shortHelpString=_("Lower Layer"))
80
81 bmp = resource.GetBitmapResource(BOTTOM_BMP, wxBITMAP_TYPE_XPM)
82 self.toolBar.AddTool(ID_LEGEND_BOTTOM, bmp,
83 shortHelpString=_("Bottom Layer"))
84
85 bmp = resource.GetBitmapResource(SHOW_BMP, wxBITMAP_TYPE_XPM)
86 self.toolBar.AddTool(ID_LEGEND_SHOWLAYER, bmp,
87 shortHelpString=_("Show Layer"))
88
89 bmp = resource.GetBitmapResource(HIDE_BMP, wxBITMAP_TYPE_XPM)
90 self.toolBar.AddTool(ID_LEGEND_HIDELAYER, bmp,
91 shortHelpString=_("Hide Layer"))
92
93 bmp = resource.GetBitmapResource(PROPS_BMP, wxBITMAP_TYPE_XPM)
94 self.toolBar.AddTool(ID_LEGEND_PROPS, bmp,
95 shortHelpString=_("Edit Layer Properties"))
96
97 self.toolBar.Realize()
98 panelBox.Add(self.toolBar, 0, wxGROW, 0)
99
100 EVT_TOOL(self, ID_LEGEND_TOP, self._OnMoveTop)
101 EVT_TOOL(self, ID_LEGEND_RAISE, self._OnMoveUp)
102 EVT_TOOL(self, ID_LEGEND_LOWER, self._OnMoveDown)
103 EVT_TOOL(self, ID_LEGEND_BOTTOM, self._OnMoveBottom)
104 EVT_TOOL(self, ID_LEGEND_PROPS, self._OnProperties)
105 EVT_TOOL(self, ID_LEGEND_SHOWLAYER, self._OnShowLayer)
106 EVT_TOOL(self, ID_LEGEND_HIDELAYER, self._OnHideLayer)
107
108 self.tree = LegendTree(self, ID_LEGEND_TREE, map, mainWindow)
109
110 panelBox.Add(self.tree, 1, wxGROW, 0)
111
112 self.scalebarbitmap = ScaleBarBitmap(self, map, mainWindow)
113 panelBox.Add(self.scalebarbitmap, 0, wxGROW, 0)
114
115 self.SetAutoLayout(True)
116 self.SetSizer(panelBox)
117 panelBox.SetSizeHints(self)
118
119
120 self.panelBox = panelBox
121
122 self.__EnableButtons(False)
123
124 self.Create()
125
126 EVT_CLOSE(self, self._OnClose)
127
128
129 def GetMap(self):
130 return self.tree.GetMap()
131
132 def SetMap(self, map):
133 self.tree.SetMap(map)
134 self.scalebarbitmap.SetCanvas(self.mainWindow.canvas)
135
136 def DoOnSelChanged(self, layer, group):
137
138 ok = isinstance(layer, BaseLayer)
139 self.__EnableButtons(ok)
140
141 self.mainWindow.SelectLayer(layer)
142
143 def DoOnProperties(self):
144 list = self.tree.GetSelectedHierarchy()
145
146 ok = isinstance(list[0], BaseLayer)
147 if ok:
148 self.mainWindow.OpenLayerProperties(list[0], list[1])
149
150 def Destroy(self):
151 self.__Close()
152
153 def _OnProperties(self, event):
154 self.DoOnProperties()
155
156 def _OnMoveTop(self, event):
157 self.tree.MoveCurrentItemTop()
158
159 def _OnMoveUp(self, event):
160 self.tree.MoveCurrentItemUp()
161
162 def _OnMoveDown(self, event):
163 self.tree.MoveCurrentItemDown()
164
165 def _OnMoveBottom(self, event):
166 self.tree.MoveCurrentItemBottom()
167
168 def _OnShowLayer(self, event):
169 self.tree.DoOnShowLayer()
170 pass
171
172 #def Close(self, force = False):
173 #DockPanel.Close(self, force)
174
175 def _OnClose(self, event):
176 self.__Close()
177
178 def _OnHideLayer(self, event):
179 self.tree.DoOnHideLayer()
180 pass
181
182 def __EnableButtons(self, on):
183 self.toolBar.EnableTool(ID_LEGEND_TOP, on)
184 self.toolBar.EnableTool(ID_LEGEND_RAISE, on)
185 self.toolBar.EnableTool(ID_LEGEND_LOWER, on)
186 self.toolBar.EnableTool(ID_LEGEND_BOTTOM, on)
187 self.toolBar.EnableTool(ID_LEGEND_SHOWLAYER, on)
188 self.toolBar.EnableTool(ID_LEGEND_HIDELAYER, on)
189 self.toolBar.EnableTool(ID_LEGEND_PROPS, on)
190
191 def __Close(self):
192 self.tree.Close()
193
194 class LegendTree(wxTreeCtrl):
195
196 def __init__(self, parent, id, map, mainWindow):
197 wxTreeCtrl.__init__(self, parent, id,
198 style = wxTR_DEFAULT_STYLE | wxTR_HIDE_ROOT,
199 size = (200, 200))
200
201 self.mainWindow = mainWindow
202 self.map = None
203 self.parent = parent
204 self.changing_selection = 0
205
206 #
207 # The image list used by the wxTreeCtrl causes problems when
208 # we remove layers and/or change a classification because it
209 # changes the image indices if you remove images from the list.
210 # Rather than removing unused images we use this list to keep
211 # track of which indices are available in the image list
212 # (because of a previous removal) and then replace those indices
213 # with new images rather than appending to the end of the image
214 # list (assuming there are any that are available).
215 #
216 self.availImgListIndices = []
217
218 self.image_list = None
219 self.emptyImageIndex = 0
220
221 self.previewer = ClassDataPreviewer()
222
223 self.preventExpandCollapse = False
224 self.raiseProperties = False
225
226 EVT_TREE_ITEM_ACTIVATED(self, ID_LEGEND_TREE, self._OnItemActivated)
227 EVT_TREE_SEL_CHANGED(self, ID_LEGEND_TREE, self._OnSelChanged)
228 EVT_TREE_ITEM_EXPANDING(self, ID_LEGEND_TREE, self.OnItemExpandCollapse)
229 EVT_TREE_ITEM_COLLAPSING(self, ID_LEGEND_TREE, self.OnItemExpandCollapse)
230
231 EVT_CLOSE(self, self._OnClose)
232
233 self.SetMap(map)
234
235 def find_layer(self, layer):
236 """Return the tree item for the layer"""
237 root = self.GetRootItem()
238 id, cookie = self.GetFirstChild(root, 0)
239 while id.IsOk():
240 if self.GetPyData(id) is layer:
241 return id
242 id, cookie = self.GetNextChild(root, cookie)
243 return None
244
245 def _OnClose(self, event):
246 self.SetMap(None)
247
248 def GetMap(self):
249 return self.map
250
251 def SetMap(self, map):
252
253 sub_list = [(MAP_STACKING_CHANGED, self._OnMsgMapStackingChanged),
254 (MAP_LAYERS_ADDED, self._OnMsgMapLayersAdded),
255 (MAP_LAYERS_REMOVED, self._OnMsgMapLayersRemoved)]
256
257 if self.map is not None:
258 for msg, func in sub_list: self.map.Unsubscribe(msg, func)
259 #self.mainWindow.application.Unsubscribe(SESSION_REPLACED,
260 #self._OnMsgMapsChanged)
261 #try:
262 #self.mainWindow.application.session.Unsubscribe(MAPS_CHANGED,
263 #self._OnMsgMapsChanged)
264 #except ConnectorError:
265 #pass
266 self.DeleteAllItems()
267
268 self.map = map
269
270 if self.map is not None:
271 for msg, func in sub_list: self.map.Subscribe(msg, func)
272 #self.mainWindow.application.session.Subscribe(MAPS_CHANGED,
273 #self._OnMsgMapsChanged)
274 #self.mainWindow.application.Subscribe(SESSION_REPLACED,
275 #self._OnMsgMapsChanged)
276 self.__FillTree(self.map)
277
278 def MoveCurrentItemTop(self):
279 layer, group = self.GetSelectedHierarchy()
280
281 if layer is not None:
282 self.map.MoveLayerToTop(layer)
283 else:
284 assert False, "Shouldn't be allowed."
285 pass
286
287 def MoveCurrentItemUp(self):
288 layer, group = self.GetSelectedHierarchy()
289
290 if layer is not None:
291 self.map.RaiseLayer(layer)
292 else:
293 assert False, "Shouldn't be allowed."
294 pass
295
296 def MoveCurrentItemDown(self):
297 layer, group = self.GetSelectedHierarchy()
298
299 if layer is not None:
300 self.map.LowerLayer(layer)
301 else:
302 assert False, "Shouldn't be allowed."
303 pass
304
305 def MoveCurrentItemBottom(self):
306 layer, group = self.GetSelectedHierarchy()
307
308 if layer is not None:
309 self.map.MoveLayerToBottom(layer)
310 else:
311 assert False, "Shouldn't be allowed."
312 pass
313
314 def OnCompareItems(self, item1, item2):
315
316 data1 = self.GetPyData(item1)
317 data2 = self.GetPyData(item2)
318
319 if isinstance(data1, BaseLayer):
320 layers = self.map.Layers()
321 return layers.index(data2) - layers.index(data1)
322 else:
323 return wxTreeCtrl.OnCompareItems(self, item1, item2)
324
325 def DoOnShowLayer(self):
326 layer, group = self.GetSelectedHierarchy()
327 layer.SetVisible(True)
328
329 def DoOnHideLayer(self):
330 layer, group = self.GetSelectedHierarchy()
331 layer.SetVisible(False)
332
333 def Sort(self):
334 self.SortChildren(self.GetRootItem())
335
336 def GetSelectedHierarchy(self):
337 id = self.GetSelection()
338
339 if not id.IsOk():
340 return (None, None)
341
342 layer = self.GetPyData(id)
343 group = None
344
345 if isinstance(layer, ClassGroup):
346 id = self.GetItemParent(id)
347 assert id.IsOk()
348 group = layer
349 layer = self.GetPyData(id)
350
351 return (layer, group)
352
353 def _OnMsgMapsChanged(self):
354 #print self.map is self.mainWindow.Map()
355 self.SetMap(self.mainWindow.Map())
356
357 def _OnSelChanged(self, event):
358 # If we change the selection from normalize_selection do nothing.
359 if self.changing_selection:
360 return
361
362 self.normalize_selection()
363 self.__UpdateSelection()
364
365 def normalize_selection(self):
366 """Select the layer containing currently selected item"""
367 # This is really a workaround for a bug in wx where deleting a
368 # subtree with DeleteChildren does not update the selection
369 # properly and can lead to segfaults later because the return
370 # value of GetSelection points to invalid data.
371 item = self.GetSelection()
372 while item.IsOk():
373 object = self.GetPyData(item)
374 if isinstance(object, BaseLayer):
375 break
376 item = self.GetItemParent(item)
377 else:
378 # No layer was found in the chain of parents, so there's
379 # nothing we can do.
380 return
381
382 self.changing_selection = 1
383 try:
384 self.SelectItem(item)
385 finally:
386 self.changing_selection = 0
387
388
389 def OnItemExpandCollapse(self, event):
390 if self.preventExpandCollapse:
391 event.Veto()
392 self.preventExpandCollapse = False
393
394 def _OnItemActivated(self, event):
395 # The following looks strange but is need under Windows to
396 # raise the Properties on double-click: The tree control
397 # always gets an Expanded / Collapsed event after the ItemActivated
398 # on double click, which raises the main window again. We add a second
399 # ItemActivated event to the queue, which simply raises the already
400 # displayed window.
401 if self.raiseProperties:
402 self.parent.DoOnProperties()
403 self.raiseProperties = False
404 else:
405 self.raiseProperties = True
406 self.preventExpandCollapse = True
407 self.parent.DoOnProperties()
408 self.AddPendingEvent(event)
409
410 def _OnMsgLayerChanged(self, layer):
411 assert isinstance(layer, BaseLayer)
412
413 id = self.find_layer(layer)
414 assert id is not None
415
416 self.__FillTreeLayer(id)
417 self.__UpdateSelection()
418
419 def _OnMsgMapStackingChanged(self, *args):
420 self.Sort()
421 id = self.GetSelection()
422
423 if id.IsOk():
424 self.EnsureVisible(id)
425 self.__UpdateSelection()
426
427 def _OnMsgMapLayersAdded(self, map):
428 assert map is self.map
429
430 # Build a dict with all layers known by the the tree as keys
431 layers = {}
432 root = self.GetRootItem()
433 id, cookie = self.GetFirstChild(root, 0)
434 while id.IsOk():
435 layers[self.GetPyData(id)] = 1
436 id, cookie = self.GetNextChild(root, cookie)
437
438 # Add layers in the map but not in the dict
439 i = 0
440 for l in map.Layers():
441 if not l in layers:
442 self.__AddLayer(i, l)
443
444 self.__UpdateSelection()
445
446 def _OnMsgMapLayersRemoved(self, map):
447 assert map is self.map
448
449 layers = map.Layers()
450
451 root = self.GetRootItem()
452 id, cookie = self.GetFirstChild(root, 0)
453 while id.IsOk():
454 if self.GetPyData(id) not in layers:
455 self.__RemoveLayer(id)
456 id, cookie = self.GetNextChild(root, cookie)
457
458
459 self.__UpdateSelection()
460
461 def _OnMsgLayerVisibilityChanged(self, layer):
462 assert isinstance(layer, BaseLayer)
463
464 self.__ShowHideLayer(layer)
465 self.__UpdateSelection()
466
467 def _OnMsgLayerTitleChanged(self, layer):
468
469 id = self.find_layer(layer)
470 if id.IsOk():
471 self.SetItemText(id, layer.Title())
472 self.__UpdateSelection()
473
474 def __UpdateSelection(self):
475 layer, group = self.GetSelectedHierarchy()
476 self.parent.DoOnSelChanged(layer, group)
477
478 def __FillTree(self, map):
479
480 self.Freeze()
481
482 self.DeleteAllItems()
483
484 if map.HasLayers():
485 root = self.GetRootItem()
486 for l in map.Layers():
487 self.__AddLayer(0, l)
488
489 self.Thaw()
490
491 def __FillTreeLayer(self, pid):
492 layer = self.GetPyData(pid)
493
494 self.Freeze()
495
496 self.DeleteChildren(pid)
497
498 if layer.HasClassification():
499
500 clazz = layer.GetClassification()
501
502 shapeType = layer.ShapeType()
503
504 show = layer.Visible()
505 for g in clazz:
506 if g.IsVisible():
507 id = self.AppendItem(pid, g.GetDisplayText())
508 self.SetPyData(id, g)
509 self.__SetVisibilityStyle(show, id)
510
511 bmp = self.__BuildGroupImage(g, shapeType)
512
513 if bmp is None:
514 self.SetItemImage(id, -1)
515 self.SetItemSelectedImage(id, -1)
516 else:
517 if self.availImgListIndices:
518 i = self.availImgListIndices.pop(0)
519 self.image_list.Replace(i, bmp)
520 else:
521 i = self.image_list.Add(bmp)
522
523 self.SetItemImage(id, i)
524 self.SetItemSelectedImage(id, i)
525
526 self.Thaw()
527
528 def __BuildGroupImage(self, group, shapeType):
529
530 bmp = wxEmptyBitmap(BMP_SIZE_W, BMP_SIZE_H)
531 #brush = wxBrush(Color2wxColour(item[1]), wxSOLID)
532 dc = wxMemoryDC()
533 dc.SelectObject(bmp)
534 dc.Clear()
535
536 self.previewer.Draw(dc, None, group.GetProperties(), shapeType)
537
538 return bmp
539
540 def DeleteAllItems(self):
541
542 pid = self.GetRootItem()
543
544 id, cookie = self.GetFirstChild(pid, 123)
545 while id.IsOk():
546 self.__RemoveLayer(id)
547 id, cookie = self.GetNextChild(pid, cookie)
548
549 wxTreeCtrl.DeleteAllItems(self)
550
551 def __AddLayer(self, before, l):
552 root = self.GetRootItem()
553 id = self.InsertItemBefore(root, before,
554 l.Title(),
555 self.mapImageIndex,
556 self.mapImageIndex)
557
558 self.SetPyData(id, l)
559 self.__SetVisibilityStyle(l.Visible(), id)
560
561 self.__FillTreeLayer(id)
562 self.Expand(id)
563
564 l.Subscribe(LAYER_CHANGED, self._OnMsgLayerChanged)
565 l.Subscribe(LAYER_VISIBILITY_CHANGED,
566 self._OnMsgLayerVisibilityChanged)
567 l.Subscribe(TITLE_CHANGED, self._OnMsgLayerTitleChanged)
568
569 def __RemoveLayer(self, id):
570 self.DeleteChildren(id)
571
572 layer = self.GetPyData(id)
573 layer.Unsubscribe(LAYER_CHANGED,
574 self._OnMsgLayerChanged)
575 layer.Unsubscribe(LAYER_VISIBILITY_CHANGED,
576 self._OnMsgLayerVisibilityChanged)
577 layer.Unsubscribe(TITLE_CHANGED, self._OnMsgLayerTitleChanged)
578
579 self.Delete(id)
580
581 def DeleteChildren(self, pid):
582 id, cookie = self.GetFirstChild(pid, 123)
583 while id.IsOk():
584 self.availImgListIndices.append(self.GetItemImage(id))
585 id, cookie = self.GetNextChild(pid, cookie)
586 wxTreeCtrl.DeleteChildren(self, pid)
587
588 def GetRootItem(self):
589 root = wxTreeCtrl.GetRootItem(self)
590
591 if not root.IsOk():
592 self.image_list = wxImageList(BMP_SIZE_W, BMP_SIZE_H, False, 0)
593
594 bmp = wxEmptyBitmap(BMP_SIZE_W, BMP_SIZE_H)
595 dc = wxMemoryDC()
596 dc.SelectObject(bmp)
597 dc.SetBrush(wxBLACK_BRUSH)
598 dc.Clear()
599 dc.SelectObject(wxNullBitmap)
600
601 self.emptyImageIndex = \
602 self.image_list.AddWithColourMask(bmp, wxColour(0, 0, 0))
603
604 bmp = resource.GetBitmapResource("legend_icon_layer",
605 wxBITMAP_TYPE_XPM)
606 self.mapImageIndex = \
607 self.image_list.Add(bmp)
608
609 self.AssignImageList(self.image_list)
610 self.availImgListIndices = []
611
612 root = self.AddRoot("")
613
614 return root
615
616 def __SetVisibilityStyle(self, visible, id):
617 font = self.GetItemFont(id)
618
619 if visible:
620 font.SetStyle(wxNORMAL)
621 color = wxBLACK
622 else:
623 #font.SetStyle(wxITALIC)
624 font.SetStyle(wxNORMAL)
625 color = wxLIGHT_GREY
626
627 self.SetItemTextColour(id, color)
628 self.SetItemFont(id, font)
629
630 def __ShowHideLayer(self, layer):
631 parent = self.find_layer(layer)
632 assert parent.IsOk()
633
634 visible = layer.Visible()
635
636 self.__SetVisibilityStyle(visible, parent)
637
638 id, cookie = self.GetFirstChild(parent, 123)
639
640 while id.IsOk():
641 self.__SetVisibilityStyle(visible, id)
642 id, cookie = self.GetNextChild(parent, cookie)
643
644 class ScaleBarBitmap(wxBoxSizer):
645
646 def __init__(self, parent, map, mainWindow):
647 # While the width is fixed, get the height _now_.
648 dc = wxMemoryDC()
649 textwidth, textheight = dc.GetTextExtent("%d"%0)
650 self.width = 210
651 self.height = textheight + 3*2 + 8
652
653 wxBoxSizer.__init__(self, wxVERTICAL)
654 bmp=wxEmptyBitmap(self.width, self.height)
655 self.scalebarBitmap = wxStaticBitmap(parent, -1, bmp)
656 self.Add(self.scalebarBitmap, 0, wxALIGN_CENTER|wxLEFT|wxTOP|wxRIGHT, 1)
657
658 self.mainWindow = mainWindow
659 self.parent = parent
660 self.canvas = None
661 self.SetCanvas(self.mainWindow.canvas)
662
663 def SetCanvas(self, canvas):
664 sub_list = [(SCALE_CHANGED, self._OnMsgScaleChanged)]
665
666 if self.canvas is not None:
667 for msg, func in sub_list: self.canvas.Unsubscribe(msg, func)
668
669 self.canvas = canvas
670 self.scalebar = ScaleBar(canvas.map)
671
672 if self.canvas is not None:
673 for msg, func in sub_list: self.canvas.Subscribe(msg, func)
674 self.__SetScale(self.canvas.scale)
675
676 def _OnMsgScaleChanged(self, scale):
677 self.__SetScale(scale)
678
679 def __SetScale(self, scale):
680 bmp = wxEmptyBitmap(self.width, self.height)
681 dc = wxMemoryDC()
682 dc.SelectObject(bmp)
683 dc.Clear()
684
685 if self.canvas.map.projection is not None:
686 self.scalebar.DrawScaleBar(scale, dc, (0,0), dc.GetSizeTuple())
687
688 self.scalebarBitmap.SetBitmap(bmp)
689

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26