/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/view.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/view.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/thuban/Thuban/UI/view.py revision 1385 by jonathan, Thu Jul 10 14:52:39 2003 UTC branches/WIP-pyshapelib-bramz/Thuban/UI/view.py revision 2734 by bramz, Thu Mar 1 12:42:59 2007 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH  # opyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  # Frank Koormann <[email protected]>  # Frank Koormann <[email protected]>
# Line 10  Line 10 
10  Classes for display of a map and interaction with it  Classes for display of a map and interaction with it
11  """  """
12    
13  __version__ = "$Revision$"  from __future__ import generators
14    
15  from Thuban import _  __version__ = "$Revision$"
16    # $Source$
17    # $Id$
18    
 import sys  
19  import os.path  import os.path
20    import time
21    import traceback
22    
23  from math import hypot  import wx
   
 from wxPython.wx import wxWindow, \  
      wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\  
      EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \  
      wxBITMAP_TYPE_XPM, wxCursor, wxPlatform, \  
      wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \  
      wxOVERWRITE_PROMPT, wxID_OK  
24    
25  # Export related stuff  # Export related stuff
26  if wxPlatform == '__WXMSW__':  if wx.Platform == '__WXMSW__':
27      from wxPython.wx import wxMetaFileDC      from wx import MetaFileDC
   
 from wxPython import wx  
28    
29  from wxproj import point_in_polygon_shape, shape_centroid  from Thuban import _
30    
31  from Thuban.Model.messages import \  from Thuban.Model.messages import MAP_LAYERS_CHANGED, LAYER_CHANGED, \
32       MAP_PROJECTION_CHANGED, MAP_LAYERS_CHANGED, \       LAYER_VISIBILITY_CHANGED
      LAYER_PROJECTION_CHANGED, LAYER_CHANGED, LAYER_VISIBILITY_CHANGED  
33    
34  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
35    
36  import labeldialog  import labeldialog
37    
38  from viewport import ViewPort, PanTool  from viewport import ViewPort, PanTool, output_transform
39    
40  class CanvasPanTool(PanTool):  class CanvasPanTool(PanTool):
41    
# Line 55  class CanvasPanTool(PanTool): Line 48  class CanvasPanTool(PanTool):
48              x, y = self.current              x, y = self.current
49              width, height = self.view.GetSizeTuple()              width, height = self.view.GetSizeTuple()
50    
51              bitmapdc = wx.wxMemoryDC()              bitmapdc = wx.MemoryDC()
52              bitmapdc.SelectObject(self.view.bitmap)              bitmapdc.SelectObject(self.view.PreviewBitmap())
53    
54              dc = self.view.drag_dc              dc = self.view.drag_dc
55              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
56    
57  class MapPrintout(wx.wxPrintout):  class MapPrintout(wx.Printout):
58    
59      """      """
60      wxPrintout class for printing Thuban maps      wxPrintout class for printing Thuban maps
61      """      """
62    
63      def __init__(self, canvas, map, region, selected_layer, selected_shapes):      def __init__(self, canvas, map, region, selected_layer, selected_shapes):
64          wx.wxPrintout.__init__(self)          wx.Printout.__init__(self)
65          self.canvas = canvas          self.canvas = canvas
66          self.map = map          self.map = map
67          self.region = region          self.region = region
# Line 87  class MapPrintout(wx.wxPrintout): Line 80  class MapPrintout(wx.wxPrintout):
80    
81      def draw_on_dc(self, dc):      def draw_on_dc(self, dc):
82          width, height = self.GetPageSizePixels()          width, height = self.GetPageSizePixels()
83          scale, offset, mapregion = OutputTransform(self.canvas.scale,          scale, offset, mapregion = output_transform(self.canvas.scale,
84                                                     self.canvas.offset,                                                      self.canvas.offset,
85                                                     self.canvas.GetSizeTuple(),                                                      self.canvas.GetSizeTuple(),
86                                                     self.GetPageSizePixels())                                                      self.GetPageSizePixels())
87          resx, resy = self.GetPPIPrinter()          resx, resy = self.GetPPIPrinter()
         renderer = PrinterRenderer(dc, scale, offset, resolution = resy)  
         x, y, width, height = self.region  
88          canvas_scale = self.canvas.scale          canvas_scale = self.canvas.scale
89          renderer.RenderMap(self.map,          x, y, width, height = self.region
90                             (0,0,          renderer = PrinterRenderer(dc, self.map, scale, offset,
91                                  (width/canvas_scale)*scale,                                     region = (mapregion[0], mapregion[1],
92                                  (height/canvas_scale)*scale),                                               (width/canvas_scale)*scale,
93                                  mapregion,                                               (height/canvas_scale)*scale),
94                             self.selected_layer, self.selected_shapes)                                     resolution = resy,
95                                       destination_region = mapregion)
96            renderer.RenderMap(self.selected_layer, self.selected_shapes)
97          return True          return True
98    
99  class MapCanvas(wxWindow, ViewPort):  
100    class MapCanvas(wx.Window, ViewPort):
101    
102      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
103    
104      def __init__(self, parent, winid):      def __init__(self, parent, winid):
105          wxWindow.__init__(self, parent, winid)          wx.Window.__init__(self, parent, winid)
106          ViewPort.__init__(self)          ViewPort.__init__(self)
107    
108          self.SetBackgroundColour(wxColour(255, 255, 255))          self.SetBackgroundColour(wx.Colour(255, 255, 255))
109    
110          # the bitmap serving as backing store          # the bitmap serving as backing store
111          self.bitmap = None          self.bitmap = None
112            # the monochrome bitmap with the selection if any
113            self.selection_bitmap = None
114    
115          self.backgroundColor = wx.wxWHITE_BRUSH          self.backgroundColor = wx.WHITE_BRUSH
116    
117            # The rendering iterator object. Used when rendering
118            # incrementally
119            self.render_iter = None
120    
121          # subscribe the WX events we're interested in          # subscribe the WX events we're interested in
122          EVT_PAINT(self, self.OnPaint)          self.Bind(wx.EVT_PAINT, self.OnPaint)
123          EVT_LEFT_DOWN(self, self.OnLeftDown)          self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
124          EVT_LEFT_UP(self, self.OnLeftUp)          self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
125          EVT_MOTION(self, self.OnMotion)          self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
126          EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)          self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
127          wx.EVT_SIZE(self, self.OnSize)          self.Bind(wx.EVT_MOTION, self.OnMotion)
128          wx.EVT_IDLE(self, self.OnIdle)          self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
129            self.Bind(wx.EVT_SIZE, self.OnSize)
130            self.Bind(wx.EVT_IDLE, self.OnIdle)
131    
132      def __del__(self):      def __del__(self):
133          wxWindow.__del__(self)          wx.Window.__del__(self)
134          ViewPort.__del__(self)          ViewPort.__del__(self)
135    
136        def PreviewBitmap(self):
137            return self.bitmap
138    
139      def PanTool(self):      def PanTool(self):
140          """Start the canvas pan tool"""          """Start the canvas pan tool"""
141          self.SelectTool(CanvasPanTool(self))          self.SelectTool(CanvasPanTool(self))
142            
143      def SetMap(self, map):      def SetMap(self, map):
144          redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,          redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
145                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
# Line 154  class MapCanvas(wxWindow, ViewPort): Line 159  class MapCanvas(wxWindow, ViewPort):
159          self.full_redraw()          self.full_redraw()
160    
161      def OnPaint(self, event):      def OnPaint(self, event):
162          dc = wxPaintDC(self)          dc = wx.PaintDC(self)
   
163          if self.Map() is not None and self.Map().HasLayers():          if self.Map() is not None and self.Map().HasLayers():
164              if self.bitmap in (None, -1):              if self.bitmap is not None:
                 # set the flag that we should redraw the  
                 # bitmap in idle time  
                 self.bitmap = -1  
             else:  
                 # blit the bitmap to the screen  
165                  dc.BeginDrawing()                  dc.BeginDrawing()
166                  dc.DrawBitmap(self.bitmap, 0, 0)                  dc.DrawBitmap(self.bitmap, 0, 0)
167                    if self.selection_bitmap is not None:
168                        dc.DrawBitmap(self.selection_bitmap, 0, 0, True)
169                  dc.EndDrawing()                  dc.EndDrawing()
170          else:          else:
171              # If we've got no map or if the map is empty, simply clear              # If we've got no map or if the map is empty, simply clear
# Line 180  class MapCanvas(wxWindow, ViewPort): Line 181  class MapCanvas(wxWindow, ViewPort):
181              dc.EndDrawing()              dc.EndDrawing()
182    
183      def OnIdle(self, event):      def OnIdle(self, event):
184          # render the screen if necessary          """Idle handler. Redraw the bitmap if necessary"""
185            if (self.Map() is not None
186                and (self.bitmap is None
187                     or self.render_iter is not None
188                     or (self.HasSelectedShapes()
189                         and self.selection_bitmap is None))):
190                event.RequestMore(self._do_redraw())
191    
192        def _do_redraw(self):
193            """Redraw a bit and return whether this method has to be called again.
194    
195            Called by OnIdle to handle the actual redraw. Redraw is
196            incremental for both the bitmap with the normal layers and the
197            bitmap with the selection.
198            """
199            finished = False
200            if self.render_iter is not None:
201                try:
202                    if self.render_iter.next():
203                        # Redraw if the last preview redraw was some time
204                        # ago and the user is not currently dragging the
205                        # mouse because redrawing would interfere with what
206                        # the current tool is drawing on the window.
207                        if not self.dragging \
208                               and time.time() - self.render_last_preview > 0.5:
209                            client_dc = wx.ClientDC(self)
210                            client_dc.BeginDrawing()
211                            client_dc.DrawBitmap(self.bitmap, 0, 0)
212                            client_dc.EndDrawing()
213                            self.render_last_preview = time.time()
214                    else:
215                        self.render_iter = None
216                        # Redraw if not dragging because redrawing would
217                        # interfere with what the current tool is drawing on
218                        # the window.
219                        if not self.dragging:
220                            self.redraw()
221                        finished = True
222                except StopIteration:
223                    finished = True
224                    self.render_iter = None
225                except:
226                    finished = True
227                    self.render_iter = None
228                    traceback.print_exc()
229            else:
230                self.render_iter = self._render_iterator()
231                self.render_last_preview = time.time()
232            return not finished
233    
234          if self.bitmap != -1:      def _render_iterator(self):
235              return          width, height = self.GetSizeTuple()
236            dc = wx.MemoryDC()
237    
238          wxBeginBusyCursor()          render_start = time.time()
         try:  
             width, height = self.GetSizeTuple()  
239    
240              bitmap = wx.wxEmptyBitmap(width, height)          if self.bitmap is None:
241              dc = wx.wxMemoryDC()              self.bitmap = wx.EmptyBitmap(width, height)
242              dc.SelectObject(bitmap)              dc.SelectObject(self.bitmap)
243              dc.BeginDrawing()              dc.BeginDrawing()
244    
245              dc.SetBackground(self.backgroundColor)              dc.SetBackground(self.backgroundColor)
246              dc.Clear()              dc.Clear()
247    
             selected_layer = self.selection.SelectedLayer()  
             selected_shapes = self.selection.SelectedShapes()  
   
248              # draw the map into the bitmap              # draw the map into the bitmap
249              renderer = ScreenRenderer(dc, self.scale, self.offset)              renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
250                                          (0, 0, width, height))
251                for cont in renderer.RenderMapIncrementally():
252                    yield True
253    
254              # Pass the entire bitmap as update region to the renderer.              dc.EndDrawing()
255              # We're redrawing the whole bitmap, after all.              dc.SelectObject(wx.NullBitmap)
256              renderer.RenderMap(self.Map(), (0, 0, width, height),  
257                                 selected_layer, selected_shapes)          if self.HasSelectedShapes() and self.selection_bitmap is None:
258                bitmap = wx.EmptyBitmap(width, height)
259                dc.SelectObject(bitmap)
260                dc.BeginDrawing()
261                dc.SetBackground(wx.WHITE_BRUSH)
262                dc.Clear()
263    
264                renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
265                                          (0, 0, width, height))
266                layer = self.SelectedLayer()
267                shapes = self.selection.SelectedShapes()
268                for cont in renderer.draw_selection_incrementally(layer, shapes):
269                    yield True
270    
271              dc.EndDrawing()              dc.EndDrawing()
272              dc.SelectObject(wx.wxNullBitmap)              dc.SelectObject(wx.NullBitmap)
273    
274              self.bitmap = bitmap              bitmap.SetMask(wx.Mask(bitmap, wx.WHITE))
275          finally:              self.selection_bitmap = bitmap
             wxEndBusyCursor()  
             pass  
276    
277          # This causes a paint event that then draws the bitmap          yield False
         self.redraw()  
278    
279      def Export(self):      def Export(self):
280    
# Line 225  class MapCanvas(wxWindow, ViewPort): Line 282  class MapCanvas(wxWindow, ViewPort):
282              export_path = self.export_path              export_path = self.export_path
283          else:          else:
284              export_path="."              export_path="."
285          dlg = wxFileDialog(self, _("Export Map"), export_path, "",          dlg = wx.FileDialog(self, _("Export Map"), export_path, "",
286                             "Enhanced Metafile (*.wmf)|*.wmf",                             "Enhanced Metafile (*.wmf)|*.wmf",
287                             wxSAVE|wxOVERWRITE_PROMPT)                             wx.SAVE|wx.OVERWRITE_PROMPT)
288          if dlg.ShowModal() == wxID_OK:          if dlg.ShowModal() == wx.ID_OK:
289              self.export_path = os.path.dirname(dlg.GetPath())              self.export_path = os.path.dirname(dlg.GetPath())
290              dc = wxMetaFileDC(dlg.GetPath())              dc = wx.MetaFileDC(dlg.GetPath())
291        
292              scale, offset, mapregion = OutputTransform(self.scale,              scale, offset, mapregion = output_transform(self.scale,
293                                                         self.offset,                                                          self.offset,
294                                                         self.GetSizeTuple(),                                                          self.GetSizeTuple(),
295                                                         dc.GetSizeTuple())                                                          dc.GetSizeTuple())
296    
297              selected_layer = self.selection.SelectedLayer()              selected_layer = self.selection.SelectedLayer()
298              selected_shapes = self.selection.SelectedShapes()              selected_shapes = self.selection.SelectedShapes()
299    
             renderer = ExportRenderer(dc, scale, offset)  
   
             # Pass the entire bitmap as update region to the renderer.  
             # We're redrawing the whole bitmap, after all.  
300              width, height = self.GetSizeTuple()              width, height = self.GetSizeTuple()
301              renderer.RenderMap(self.Map(),              renderer = ExportRenderer(dc, self.Map(), scale, offset,
302                                  (0,0,                                        region = (0, 0,
303                                      (width/self.scale)*scale,                                                  (width/self.scale)*scale,
304                                      (height/self.scale)*scale),                                                  (height/self.scale)*scale),
305                                  mapregion,                                        destination_region = mapregion)
306                                  selected_layer, selected_shapes)              renderer.RenderMap(selected_layer, selected_shapes)
307    
308              dc.EndDrawing()              dc.EndDrawing()
309              dc.Close()              dc.Close()
310          dlg.Destroy()          dlg.Destroy()
311            
312      def Print(self):      def Print(self):
313          printer = wx.wxPrinter()          printer = wx.Printer()
314          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
315          selected_layer = self.selection.SelectedLayer()          selected_layer = self.selection.SelectedLayer()
316          selected_shapes = self.selection.SelectedShapes()          selected_shapes = self.selection.SelectedShapes()
317            
318          printout = MapPrintout(self, self.Map(), (0, 0, width, height),          printout = MapPrintout(self, self.Map(), (0, 0, width, height),
319                                 selected_layer, selected_shapes)                                 selected_layer, selected_shapes)
320          printer.Print(self, printout, True)          printer.Print(self, printout, True)
321          printout.Destroy()          printout.Destroy()
# Line 271  class MapCanvas(wxWindow, ViewPort): Line 325  class MapCanvas(wxWindow, ViewPort):
325    
326      def full_redraw(self, *args):      def full_redraw(self, *args):
327          self.bitmap = None          self.bitmap = None
328            self.selection_bitmap = None
329            self.render_iter = None
330            self.redraw()
331    
332        def redraw_selection(self, *args):
333            self.selection_bitmap = None
334            self.render_iter = None
335          self.redraw()          self.redraw()
336    
337      def map_projection_changed(self, map, old_proj):      def map_projection_changed(self, map, old_proj):
# Line 288  class MapCanvas(wxWindow, ViewPort): Line 349  class MapCanvas(wxWindow, ViewPort):
349      def GetPortSizeTuple(self):      def GetPortSizeTuple(self):
350          return self.GetSizeTuple()          return self.GetSizeTuple()
351    
352        def OnMiddleDown(self, event):
353            self.remembertool = self.tool
354            if self.Map() is not None and self.Map().HasLayers():
355                self.PanTool()
356                self.OnLeftDown(event)
357    
358        def OnMiddleUp(self, event):
359            self.OnLeftUp(event)
360            if self.remembertool:
361                self.SelectTool(self.remembertool)
362    
363      def OnLeftDown(self, event):      def OnLeftDown(self, event):
364          self.MouseLeftDown(event)          self.MouseLeftDown(event)
365          if self.tool is not None:          if self.tool is not None:
366              self.drag_dc = wxClientDC(self)              self.drag_dc = wx.ClientDC(self)
367              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wx.INVERT)
368              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)              self.drag_dc.SetBrush(wx.TRANSPARENT_BRUSH)
369              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
370                self.CaptureMouse()
371              self.dragging = 1              self.dragging = 1
372    
373      def OnLeftUp(self, event):      def OnLeftUp(self, event):
374          self.MouseLeftUp(event)          """Handle EVT_LEFT_UP
375    
376            Release the mouse if it was captured, if a tool is active call
377            its Hide method and call self.MouseLeftUp.
378            """
379            # It's important that ReleaseMouse is called before MouseLeftUp.
380            # MouseLeftUp may pop up modal dialogs which leads to an
381            # effectively frozen X session because the user can only
382            # interact with the dialog but the mouse is still grabbed by the
383            # canvas.
384          if self.dragging:          if self.dragging:
385              self.ReleaseMouse()              if self.HasCapture():
386                    self.ReleaseMouse()
387              try:              try:
388                  self.tool.Hide(self.drag_dc)                  self.tool.Hide(self.drag_dc)
389              finally:              finally:
390                  self.drag_dc = None                  self.drag_dc = None
391                  self.dragging = 0                  self.dragging = 0
392            self.MouseLeftUp(event)
393    
394      def OnMotion(self, event):      def OnMotion(self, event):
395          if self.dragging:          if self.dragging:
# Line 332  class MapCanvas(wxWindow, ViewPort): Line 416  class MapCanvas(wxWindow, ViewPort):
416          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
417          # The selection object takes care that it only issues          # The selection object takes care that it only issues
418          # SHAPES_SELECTED messages when the set of selected shapes has          # SHAPES_SELECTED messages when the set of selected shapes has
419          # actually changed, so we can do a full redraw unconditionally.          # actually changed, so we can do a full redraw of the
420          # FIXME: We should perhaps try to limit the redraw to the are          # selection_bitmap unconditionally.
         # actually covered by the shapes before and after the selection  
         # change.  
421          ViewPort.shape_selected(self, layer, shape)          ViewPort.shape_selected(self, layer, shape)
422          self.full_redraw()          self.redraw_selection()
423    
424      def GetTextExtent(self, text):      def GetTextExtent(self, text):
425          dc = wxClientDC(self)          dc = wx.ClientDC(self)
426          font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)          font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
427          dc.SetFont(font)          dc.SetFont(font)
428          return dc.GetTextExtent(text)          return dc.GetTextExtent(text.decode('iso-8859-1'))
429    
430      def LabelShapeAt(self, x, y, text=None):      def LabelShapeAt(self, x, y, text=None):
431          """Add or remove a label at window position x, y.          """Add or remove a label at window position x, y.
432                                                                                    
433          If there's a label at the given position, remove it. Otherwise          If there's a label at the given position, remove it. Otherwise
434          determine the shape at the position, run the label dialog and          determine the shape at the position, run the label dialog and
435          unless the user cancels the dialog, add a label.          unless the user cancels the dialog, add a label.
436          """          """
         label_layer = self.map.LabelLayer()  
437          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
438          if layer is None and shape_index is not None:          if layer is None and shape_index is not None:
439              ViewPort.LabelShapeAt(self, x, y)              ViewPort.LabelShapeAt(self, x, y)
# Line 361  class MapCanvas(wxWindow, ViewPort): Line 442  class MapCanvas(wxWindow, ViewPort):
442                                                  layer.ShapeStore().Table(),                                                  layer.ShapeStore().Table(),
443                                                  shape_index)                                                  shape_index)
444              ViewPort.LabelShapeAt(self, x, y, text)              ViewPort.LabelShapeAt(self, x, y, text)
           
 def OutputTransform(canvas_scale, canvas_offset, canvas_size, device_extend):  
     """Calculate dimensions to transform canvas content to output device."""  
     width, height = device_extend  
   
     # Only 80 % of the with are available for the map  
     width = width * 0.8  
   
     # Define the distance of the map from DC border  
     distance = 20  
   
     if height < width:  
         # landscape  
         map_height = height - 2*distance  
         map_width = map_height  
     else:  
         # portrait, recalibrate width (usually the legend width is too  
         # small  
         width = width * 0.9  
         map_height = width - 2*distance  
         map_width = map_height  
       
     mapregion = (distance, distance,  
                  distance+map_width, distance+map_height)  
   
     canvas_width, canvas_height = canvas_size  
       
     scalex = map_width / (canvas_width/canvas_scale)  
     scaley = map_height / (canvas_height/canvas_scale)  
     scale = min(scalex, scaley)  
     canvas_offx, canvas_offy = canvas_offset  
     offx = scale*canvas_offx/canvas_scale  
     offy = scale*canvas_offy/canvas_scale  
445    
446      return scale, (offx, offy), mapregion  

Legend:
Removed from v.1385  
changed lines
  Added in v.2734

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26