/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/classification.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/classification.py

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

revision 643 by jonathan, Thu Apr 10 17:55:57 2003 UTC revision 1909 by bh, Fri Oct 31 18:16:34 2003 UTC
# Line 20  See the description of FindGroup() for m Line 20  See the description of FindGroup() for m
20  on the mapping algorithm.  on the mapping algorithm.
21  """  """
22        
23  # fix for people using python2.1  import copy, operator, types
 from __future__ import nested_scopes  
   
 import copy  
24    
25  from Thuban import _  from Thuban import _
26    
 from types import *  
   
27  from messages import \  from messages import \
28      LAYER_PROJECTION_CHANGED, \      LAYER_PROJECTION_CHANGED, \
29      LAYER_LEGEND_CHANGED, \      LAYER_LEGEND_CHANGED, \
30      LAYER_VISIBILITY_CHANGED      LAYER_VISIBILITY_CHANGED,\
31        CLASS_CHANGED
32    
33  from Thuban.Model.color import Color  from Thuban.Model.color import Color, Transparent, Black
34    from Thuban.Model.range import Range
35    
36  import Thuban.Model.layer  import Thuban.Model.layer
37    
38  class Classification:  from Thuban.Lib.connector import Publisher
39    
40    class Classification(Publisher):
41      """Encapsulates the classification of layer.      """Encapsulates the classification of layer.
42            
43      The Classification divides some kind of data into Groups which      The Classification divides some kind of data into Groups which
# Line 46  class Classification: Line 45  class Classification:
45      retrieved by matching data values to the appropriate group.      retrieved by matching data values to the appropriate group.
46      """      """
47    
48      def __init__(self, layer = None, field = None):      def __init__(self):
49          """Initialize a classification.          """Initialize a classification."""
   
         layer -- the Layer object who owns this classification  
   
         field -- the name of the data table field that  
                  is to be used to classify layer properties  
         """  
50    
         self.layer = None  
         self.field = None  
         self.fieldType = None  
51          self.__groups = []          self.__groups = []
52    
         self.__setLayerLock = False  
   
53          self.SetDefaultGroup(ClassGroupDefault())          self.SetDefaultGroup(ClassGroupDefault())
54    
         self.SetLayer(layer)  
         self.SetField(field)  
   
55      def __iter__(self):      def __iter__(self):
56          return ClassIterator(self.__groups)          return ClassIterator(self.__groups)
57    
58      def __deepcopy__(self, memo):      def __deepcopy__(self, memo):
59          clazz = Classification()          clazz = Classification()
60    
         # note: the only thing that isn't copied is the layer reference  
         clazz.field = self.field  
         clazz.fieldType = self.fieldType  
61          clazz.__groups[0] = copy.deepcopy(self.__groups[0])          clazz.__groups[0] = copy.deepcopy(self.__groups[0])
62    
63          for i in range(1, len(self.__groups)):          for i in range(1, len(self.__groups)):
# Line 85  class Classification: Line 67  class Classification:
67    
68      def __SendNotification(self):      def __SendNotification(self):
69          """Notify the layer that this class has changed."""          """Notify the layer that this class has changed."""
70          if self.layer is not None:          self.issue(CLASS_CHANGED)
             self.layer.ClassChanged()  
71            
     def SetField(self, field):  
         """Set the name of the data table field to use.  
           
         If there is no layer then the field type is set to None,  
         otherwise the layer is queried to find the type of the  
         field data  
   
         field -- if None then all values map to the default data  
         """  
   
         if field == "":  
             field = None  
   
   
         if field is None:  
             if self.layer is not None:  
                 self.fieldType = None  
         else:  
             if self.layer is not None:  
                 fieldType = self.layer.GetFieldType(field)  
                 if fieldType is None:  
                     raise ValueError("'%s' was not found in the layer's table."  
                                      % self.field)  
   
                 #  
                 # unfortunately we cannot call SetFieldType() because it  
                 # requires the layer to be None  
                 #  
                 self.fieldType = fieldType  
                 #self.SetFieldType(fieldType)  
   
         self.field = field  
   
         self.__SendNotification()  
   
     def GetField(self):  
         """Return the name of the field."""  
         return self.field  
   
     def GetFieldType(self):  
         """Return the field type."""  
         return self.fieldType  
   
     def SetFieldType(self, type):  
         """Set the type of the field used by this classification.  
   
         A ValueError is raised if the owning layer is not None and  
         'type' is different from the current field type.  
         """  
   
         if type != self.fieldType:  
             if self.layer is not None:  
                 raise ValueError()  
             else:  
                 self.fieldType = type  
                 self.__SendNotification()  
   
     def SetLayer(self, layer):  
         """Set the owning Layer of this classification.  
   
         A ValueError exception will be thrown either the field or  
         field type mismatch the information in the layer's table.  
         """  
   
         # prevent infinite recursion when calling SetClassification()  
         if self.__setLayerLock: return  
   
         self.__setLayerLock = True  
   
         if layer is None:  
             if self.layer is not None:  
                 l = self.layer  
                 self.layer = None  
                 l.SetClassification(None)  
         else:  
             assert isinstance(layer, Thuban.Model.layer.Layer)  
   
             old_layer = self.layer  
   
             self.layer = layer  
   
             try:  
                 self.SetField(self.GetField()) # this sync's the fieldType  
             except ValueError:  
                 self.layer = old_layer  
                 self.__setLayerLock = False  
                 raise ValueError  
             else:  
                 self.layer.SetClassification(self)  
   
         self.__setLayerLock = False  
   
     def GetLayer(self):  
         """Return the parent layer."""  
         return self.layer  
   
   
72      #      #
73      # these SetDefault* methods are really only provided for      # these SetDefault* methods are really only provided for
74      # some backward compatibility. they should be considered      # some backward compatibility. they should be considered
# Line 196  class Classification: Line 80  class Classification:
80    
81          fill -- a Color object.          fill -- a Color object.
82          """          """
         assert isinstance(fill, Color)  
83          self.GetDefaultGroup().GetProperties().SetFill(fill)          self.GetDefaultGroup().GetProperties().SetFill(fill)
84          self.__SendNotification()          self.__SendNotification()
85                    
# Line 209  class Classification: Line 92  class Classification:
92    
93          color -- a Color object.          color -- a Color object.
94          """          """
         assert isinstance(color, Color)  
95          self.GetDefaultGroup().GetProperties().SetLineColor(color)          self.GetDefaultGroup().GetProperties().SetLineColor(color)
96          self.__SendNotification()          self.__SendNotification()
97                    
# Line 222  class Classification: Line 104  class Classification:
104    
105          lineWidth -- an integer > 0.          lineWidth -- an integer > 0.
106          """          """
107          assert isinstance(lineWidth, IntType)          assert isinstance(lineWidth, types.IntType)
108          self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)          self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)
109          self.__SendNotification()          self.__SendNotification()
110                    
# Line 244  class Classification: Line 126  class Classification:
126    
127          group -- group that the value maps to.          group -- group that the value maps to.
128          """          """
   
129          assert isinstance(group, ClassGroupDefault)          assert isinstance(group, ClassGroupDefault)
130          if len(self.__groups) > 0:          if len(self.__groups) > 0:
131              self.__groups[0] = group              self.__groups[0] = group
132          else:          else:
133              self.__groups.append(group)              self.__groups.append(group)
134            self.__SendNotification()
135    
136      def GetDefaultGroup(self):      def GetDefaultGroup(self):
137          """Return the default group."""          """Return the default group."""
# Line 264  class Classification: Line 146  class Classification:
146          self.InsertGroup(self.GetNumGroups(), item)          self.InsertGroup(self.GetNumGroups(), item)
147    
148      def InsertGroup(self, index, group):      def InsertGroup(self, index, group):
           
149          assert isinstance(group, ClassGroup)          assert isinstance(group, ClassGroup)
   
150          self.__groups.insert(index + 1, group)          self.__groups.insert(index + 1, group)
   
151          self.__SendNotification()          self.__SendNotification()
152    
153      def RemoveGroup(self, index):      def RemoveGroup(self, index):
154          return self.__groups.pop(index + 1)          """Remove the classification group with the given index"""
155            self.__groups.pop(index + 1)
156            self.__SendNotification()
157    
158      def ReplaceGroup(self, index, group):      def ReplaceGroup(self, index, group):
159          assert isinstance(group, ClassGroup)          assert isinstance(group, ClassGroup)
   
160          self.__groups[index + 1] = group          self.__groups[index + 1] = group
   
161          self.__SendNotification()          self.__SendNotification()
162    
163      def GetGroup(self, index):      def GetGroup(self, index):
# Line 288  class Classification: Line 167  class Classification:
167          """Return the number of non-default groups in the classification."""          """Return the number of non-default groups in the classification."""
168          return len(self.__groups) - 1          return len(self.__groups) - 1
169    
   
170      def FindGroup(self, value):      def FindGroup(self, value):
171          """Return the associated group, or the default group.          """Return the associated group, or the default group.
172    
# Line 300  class Classification: Line 178  class Classification:
178                   return the default properties                   return the default properties
179          """          """
180    
181          if self.GetField() is not None and value is not None:          if value is not None:
   
182              for i in range(1, len(self.__groups)):              for i in range(1, len(self.__groups)):
183                  group = self.__groups[i]                  group = self.__groups[i]
184                  if group.Matches(value):                  if group.Matches(value):
# Line 327  class Classification: Line 204  class Classification:
204          items = []          items = []
205    
206          def build_color_item(text, color):          def build_color_item(text, color):
207              if color is Color.Transparent:              if color is Transparent:
208                  return ("%s: %s" % (text, _("None")), None)                  return ("%s: %s" % (text, _("None")), None)
209    
210              return ("%s: (%.3f, %.3f, %.3f)" %              return ("%s: (%.3f, %.3f, %.3f)" %
# Line 354  class Classification: Line 231  class Classification:
231          for p in self:          for p in self:
232              items.append(build_item(p, p.GetDisplayText()))              items.append(build_item(p, p.GetDisplayText()))
233    
 #           if isinstance(p, ClassGroupDefault):  
 #               items.append(build_item(self.GetDefaultGroup(), _("'DEFAULT'")))  
 #           elif isinstance(p, ClassGroupSingleton):  
 #               items.append(build_item(p, str(p.GetValue())))  
 #           elif isinstance(p, ClassGroupRange):  
 #               items.append(build_item(p, "%s - %s" %  
 #                                          (p.GetMin(), p.GetMax())))  
   
234          return (_("Classification"), items)          return (_("Classification"), items)
235    
236  class ClassIterator:  class ClassIterator:
# Line 383  class ClassIterator: Line 252  class ClassIterator:
252          maps -- a list of map groups          maps -- a list of map groups
253          """          """
254    
255          self.data = data #[default, points, ranges, maps]          self.data = data
256          self.data_index = 0          self.data_index = 0
         #self.data_iter = iter(self.data)  
         #self.iter = None  
257    
258      def __iter__(self):      def __iter__(self):
259          return self          return self
# Line 401  class ClassIterator: Line 268  class ClassIterator:
268              self.data_index += 1              self.data_index += 1
269              return d              return d
270                    
 #       if self.iter is None:  
 #           try:  
 #               self.data_item = self.data_iter.next()  
 #               self.iter = iter(self.data_item)  
 #           except TypeError:  
 #               return self.data_item  
   
 #       try:  
 #           return self.iter.next()  
 #       except StopIteration:  
 #           self.iter = None  
 #           return self.next()  
         
271  class ClassGroupProperties:  class ClassGroupProperties:
272      """Represents the properties of a single Classification Group.      """Represents the properties of a single Classification Group.
273        
# Line 424  class ClassGroupProperties: Line 278  class ClassGroupProperties:
278    
279          props -- a ClassGroupProperties object. The class is copied if          props -- a ClassGroupProperties object. The class is copied if
280                   prop is not None. Otherwise, a default set of properties                   prop is not None. Otherwise, a default set of properties
281                   is created such that: line color = Color.Black, line width = 1,                   is created such that: line color = Black, line width = 1,
282                   and fill color = Color.Transparent                   and fill color = Transparent
283          """          """
284    
         #self.stroke = None  
         #self.strokeWidth = 0  
         #self.fill = None  
   
285          if props is not None:          if props is not None:
286              self.SetProperties(props)              self.SetProperties(props)
287          else:          else:
288              self.SetLineColor(Color.Black)              self.SetLineColor(Black)
289              self.SetLineWidth(1)              self.SetLineWidth(1)
290              self.SetFill(Color.Transparent)              self.SetFill(Transparent)
291    
292      def SetProperties(self, props):      def SetProperties(self, props):
293          """Set this class's properties to those in class props."""          """Set this class's properties to those in class props."""
# Line 449  class ClassGroupProperties: Line 299  class ClassGroupProperties:
299                    
300      def GetLineColor(self):      def GetLineColor(self):
301          """Return the line color as a Color object."""          """Return the line color as a Color object."""
302          return self.stroke          return self.__stroke
303    
304      def SetLineColor(self, color):      def SetLineColor(self, color):
305          """Set the line color.          """Set the line color.
# Line 457  class ClassGroupProperties: Line 307  class ClassGroupProperties:
307          color -- the color of the line. This must be a Color object.          color -- the color of the line. This must be a Color object.
308          """          """
309    
310          assert isinstance(color, Color)          self.__stroke = color
         self.stroke = color  
311    
312      def GetLineWidth(self):      def GetLineWidth(self):
313          """Return the line width."""          """Return the line width."""
314          return self.strokeWidth          return self.__strokeWidth
315    
316      def SetLineWidth(self, lineWidth):      def SetLineWidth(self, lineWidth):
317          """Set the line width.          """Set the line width.
318    
319          lineWidth -- the new line width. This must be > 0.          lineWidth -- the new line width. This must be > 0.
320          """          """
321          assert isinstance(lineWidth, IntType)          assert isinstance(lineWidth, types.IntType)
322          if (lineWidth < 1):          if (lineWidth < 1):
323              raise ValueError(_("lineWidth < 1"))              raise ValueError(_("lineWidth < 1"))
324    
325          self.strokeWidth = lineWidth          self.__strokeWidth = lineWidth
326    
327      def GetFill(self):      def GetFill(self):
328          """Return the fill color as a Color object."""          """Return the fill color as a Color object."""
329          return self.fill          return self.__fill
330    
331      def SetFill(self, fill):      def SetFill(self, fill):
332          """Set the fill color.          """Set the fill color.
# Line 485  class ClassGroupProperties: Line 334  class ClassGroupProperties:
334          fill -- the color of the fill. This must be a Color object.          fill -- the color of the fill. This must be a Color object.
335          """          """
336    
337          assert isinstance(fill, Color)          self.__fill = fill
         self.fill = fill  
338    
339      def __eq__(self, other):      def __eq__(self, other):
340          """Return true if 'props' has the same attributes as this class"""          """Return true if 'props' has the same attributes as this class"""
341    
342            #
343            # using 'is' over '==' results in a huge performance gain
344            # in the renderer
345            #
346          return isinstance(other, ClassGroupProperties)   \          return isinstance(other, ClassGroupProperties)   \
347              and self.stroke      == other.GetLineColor() \              and (self.__stroke is other.__stroke or      \
348              and self.strokeWidth == other.GetLineWidth() \                   self.__stroke == other.__stroke)        \
349              and self.fill        == other.GetFill()              and (self.__fill is other.__fill or          \
350                     self.__fill == other.__fill)            \
351                and self.__strokeWidth == other.__strokeWidth
352    
353      def __ne__(self, other):      def __ne__(self, other):
354          return not self.__eq__(other)          return not self.__eq__(other)
# Line 505  class ClassGroupProperties: Line 359  class ClassGroupProperties:
359      def __deepcopy__(self):      def __deepcopy__(self):
360          return ClassGroupProperties(self)          return ClassGroupProperties(self)
361    
362        def __repr__(self):
363            return repr((self.__stroke, self.__strokeWidth, self.__fill))
364    
365  class ClassGroup:  class ClassGroup:
366      """A base class for all Groups within a Classification"""      """A base class for all Groups within a Classification"""
367    
# Line 533  class ClassGroup: Line 390  class ClassGroup:
390          label -- a string representing the Group's label. This must          label -- a string representing the Group's label. This must
391                   not be None.                   not be None.
392          """          """
393          assert isinstance(label, StringType)          assert isinstance(label, types.StringTypes)
394          self.label = label          self.label = label
395    
396      def GetDisplayText(self):      def GetDisplayText(self):
# Line 572  class ClassGroup: Line 429  class ClassGroup:
429    
430      def __eq__(self, other):      def __eq__(self, other):
431          return isinstance(other, ClassGroup) \          return isinstance(other, ClassGroup) \
432                and self.label == other.label \
433              and self.GetProperties() == other.GetProperties()              and self.GetProperties() == other.GetProperties()
434    
435      def __ne__(self, other):      def __ne__(self, other):
436          return not self.__eq__(other)          return not self.__eq__(other)
437    
438        def __repr__(self):
439            return repr(self.label) + ", " + repr(self.GetProperties())
440            
441  class ClassGroupSingleton(ClassGroup):  class ClassGroupSingleton(ClassGroup):
442      """A Group that is associated with a single value."""      """A Group that is associated with a single value."""
# Line 605  class ClassGroupSingleton(ClassGroup): Line 465  class ClassGroupSingleton(ClassGroup):
465    
466      def GetValue(self):      def GetValue(self):
467          """Return the associated value."""          """Return the associated value."""
468          return self.value          return self.__value
469    
470      def SetValue(self, value):      def SetValue(self, value):
471          """Associate this Group with the given value."""          """Associate this Group with the given value."""
472          self.value = value          self.__value = value
473    
474      def Matches(self, value):      def Matches(self, value):
475          """Determine if the given value matches the associated Group value."""          """Determine if the given value matches the associated Group value."""
476    
477          """Returns True if the value matches, False otherwise."""          """Returns True if the value matches, False otherwise."""
478    
479          return self.value == value          return self.__value == value
480    
481      def GetDisplayText(self):      def GetDisplayText(self):
482          label = self.GetLabel()          label = self.GetLabel()
# Line 628  class ClassGroupSingleton(ClassGroup): Line 488  class ClassGroupSingleton(ClassGroup):
488      def __eq__(self, other):      def __eq__(self, other):
489          return ClassGroup.__eq__(self, other) \          return ClassGroup.__eq__(self, other) \
490              and isinstance(other, ClassGroupSingleton) \              and isinstance(other, ClassGroupSingleton) \
491              and self.GetValue() == other.GetValue()              and self.__value == other.__value
492    
493        def __repr__(self):
494            return "(" + repr(self.__value) + ", " + ClassGroup.__repr__(self) + ")"
495    
496  class ClassGroupDefault(ClassGroup):  class ClassGroupDefault(ClassGroup):
497      """The default Group. When values do not match any other      """The default Group. When values do not match any other
# Line 667  class ClassGroupDefault(ClassGroup): Line 530  class ClassGroupDefault(ClassGroup):
530              and isinstance(other, ClassGroupDefault) \              and isinstance(other, ClassGroupDefault) \
531              and self.GetProperties() == other.GetProperties()              and self.GetProperties() == other.GetProperties()
532    
533        def __repr__(self):
534            return "(" + ClassGroup.__repr__(self) + ")"
535    
536  class ClassGroupRange(ClassGroup):  class ClassGroupRange(ClassGroup):
537      """A Group that represents a range of values that map to the same      """A Group that represents a range of values that map to the same
538         set of properties."""         set of properties."""
539    
540      def __init__(self, min = 0, max = 1, props = None, label = "", group=None):      def __init__(self, _range = (0,1), props = None, label = "", group=None):
541          """Constructor.          """Constructor.
542    
543          The minumum value must be strictly less than the maximum.          The minumum value must be strictly less than the maximum.
544    
545          min -- the minimum range value          _range -- either a tuple (min, max) where min < max or
546                      a Range object
         max -- the maximum range value  
547    
548          prop -- a ClassGroupProperites object. If prop is None a default          prop -- a ClassGroupProperites object. If prop is None a default
549                   set of properties is created.                   set of properties is created.
# Line 687  class ClassGroupRange(ClassGroup): Line 552  class ClassGroupRange(ClassGroup):
552          """          """
553    
554          ClassGroup.__init__(self, label, props, group)          ClassGroup.__init__(self, label, props, group)
555            self.SetRange(_range)
         self.min = self.max = 0  
   
         self.SetRange(min, max)  
556    
557      def __copy__(self):      def __copy__(self):
558          return ClassGroupRange(self.GetMin(),          return ClassGroupRange(self.__range,
559                                 self.GetMax(),                                 props = self.GetProperties(),
560                                 self.GetProperties(),                                 label = self.GetLabel())
                                self.GetLabel())  
561    
562      def __deepcopy__(self, memo):      def __deepcopy__(self, memo):
563          return ClassGroupRange(copy.copy(self.GetMin()),          return ClassGroupRange(copy.copy(self.__range),
                                copy.copy(self.GetMax()),  
564                                 group = self)                                 group = self)
565    
566      def GetMin(self):      def GetMin(self):
567          """Return the range's minimum value."""          """Return the range's minimum value."""
568          return self.min          return self.__range.GetRange()[1]
569    
570      def SetMin(self, min):      def SetMin(self, min):
571          """Set the range's minimum value.          """Set the range's minimum value.
# Line 714  class ClassGroupRange(ClassGroup): Line 574  class ClassGroupRange(ClassGroup):
574                 maximum value. Use SetRange() to change both min and max values.                 maximum value. Use SetRange() to change both min and max values.
575          """          """
576            
577          self.SetRange(min, self.max)          self.SetRange((min, self.__range.GetRange()[2]))
578    
579      def GetMax(self):      def GetMax(self):
580          """Return the range's maximum value."""          """Return the range's maximum value."""
581          return self.max          return self.__range.GetRange()[2]
582    
583      def SetMax(self, max):      def SetMax(self, max):
584          """Set the range's maximum value.          """Set the range's maximum value.
# Line 726  class ClassGroupRange(ClassGroup): Line 586  class ClassGroupRange(ClassGroup):
586          max -- the new maximum. Note that this must be greater than the current          max -- the new maximum. Note that this must be greater than the current
587                 minimum value. Use SetRange() to change both min and max values.                 minimum value. Use SetRange() to change both min and max values.
588          """          """
589          self.SetRange(self.min, max)          self.SetRange((self.__range.GetRange()[1], max))
590    
591      def SetRange(self, min, max):      def SetRange(self, _range):
592          """Set a new range.          """Set a new range.
593    
594          Note that min must be strictly less than max.          _range -- Either a tuple (min, max) where min < max or
595                      a Range object.
596    
597          min -- the new minimum value          Raises ValueError on error.
         min -- the new maximum value  
598          """          """
599    
600          if min >= max:          if isinstance(_range, Range):
601              raise ValueError(_("ClassGroupRange: %i(min) >= %i(max)!") %              self.__range = _range
602                               (min, max))          elif isinstance(_range, types.TupleType) and len(_range) == 2:
603          self.min = min              self.__range = Range(("[", _range[0], _range[1], "["))
604          self.max = max          else:
605                raise ValueError()
606    
607      def GetRange(self):      def GetRange(self):
608          """Return the range as a tuple (min, max)"""          """Return the range as a string"""
609          return (self.min, self.max)          return self.__range.string(self.__range.GetRange())
610    
611      def Matches(self, value):      def Matches(self, value):
612          """Determine if the given value lies with the current range.          """Determine if the given value lies with the current range.
# Line 753  class ClassGroupRange(ClassGroup): Line 614  class ClassGroupRange(ClassGroup):
614          The following check is used: min <= value < max.          The following check is used: min <= value < max.
615          """          """
616    
617          return self.min <= value < self.max          return operator.contains(self.__range, value)
618    
619      def GetDisplayText(self):      def GetDisplayText(self):
620          label = self.GetLabel()          label = self.GetLabel()
621    
622          if label != "": return label          if label != "": return label
623    
624          return _("%s - %s") % (self.GetMin(), self.GetMax())          return self.__range.string(self.__range.GetRange())
625    
626      def __eq__(self, other):      def __eq__(self, other):
627          return ClassGroup.__eq__(self, other) \          return ClassGroup.__eq__(self, other) \
628              and isinstance(other, ClassGroupRange) \              and isinstance(other, ClassGroupRange) \
629              and self.GetRange() == other.GetRange()              and self.__range == other.__range
630    
631        def __repr__(self):
632            return "(" + str(self.__range) + ClassGroup.__repr__(self) + ")"
633    
634  class ClassGroupMap(ClassGroup):  class ClassGroupMap(ClassGroup):
635      """Currently, this class is not used."""      """Currently, this class is not used."""

Legend:
Removed from v.643  
changed lines
  Added in v.1909

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26