# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2001 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from types import TupleType from app import _, SketchError, CreateListUndo, Undo, \ Trafo, Translation, Scale, Identity, TrafoType from compound import Compound class PluginCompound(Compound): class_name = '' # has to be provided by derived classes is_Plugin = 1 def SetParameters(self, kw = None): # XXX could be extended to handle keyword arguments. undo = {} for key, value in kw.items(): undo[key] = getattr(self, key) setattr(self, key, value) return self.SetParameters, undo def SaveToFile(self, file, *args, **kw): if self.class_name: apply(file.BeginPluginCompound, (self.class_name,) + args, kw) for obj in self.objects: obj.SaveToFile(file) file.EndPluginCompound() else: raise SketchError("Plugin %s doesn't define a class name" % self.__class__) class TrafoPlugin(PluginCompound): def __init__(self, trafo = None, duplicate = None, loading = 0): PluginCompound.__init__(self, duplicate = duplicate) if duplicate is not None: self.trafo = duplicate.trafo else: if trafo is None: trafo = Identity elif type(trafo) == TupleType: trafo = apply(Trafo, trafo) elif isinstance(trafo, TrafoType): # trafo is already a trafo object pass else: # assume a number and interpret it as a scaling transformation trafo = Scale(trafo) self.trafo = trafo def recompute(self): # Implement this in the derived class to update the children. pass def SetParameters(self, kw): undo = PluginCompound.SetParameters(self, kw) try: self.recompute() except: Undo(undo) raise return undo def set_transformation(self, trafo): undo = (self.set_transformation, self.trafo) self.trafo = trafo self.recompute() return undo def Transform(self, trafo): undo = [self.begin_change_children()] try: undo.append(PluginCompound.Transform(self, trafo)) undo.append(self.set_transformation(trafo(self.trafo))) undo.append(self.end_change_children()) except: undo.reverse() map(Undo, undo) raise return CreateListUndo(undo) def Translate(self, offset): return self.Transform(Translation(offset)) def RemoveTransformation(self): return self.set_transformation(Translation(self.trafo.offset())) def LayoutPoint(self): return self.trafo.offset() def Trafo(self): return self.trafo class UnknownPlugin(PluginCompound): is_Group = 1 changed = 0 def __init__(self, class_name = '', *args, **kw): if kw.has_key('loading'): del kw['loading'] duplicate = kw.get('duplicate') if duplicate is not None: self.class_name = duplicate.class_name self.args = duplicate.args self.kw = duplicate.kw else: self.class_name = class_name self.args = args self.kw = kw PluginCompound.__init__(self, duplicate = duplicate) self.disguise() def disguise(self): # If self has only one child, try to behave like it: if len(self.objects) == 1: object = self.objects[0] if object.is_curve: self.is_curve = object.is_curve self.AsBezier = object.AsBezier self.Paths = object.Paths def _changed(self): PluginCompound._changed(self) self.changed = 1 def load_Done(self): PluginCompound.load_Done(self) self.disguise() def SaveToFile(self, file): if not self.changed: apply(PluginCompound.SaveToFile, (self, file) + self.args, self.kw) else: # XXX an alternative approach for a changed UnknownPlugin # might be to store explicitly as an 'UnknownPlugin' so that # always is represented by this class. if len(self.objects) == 1: self.objects[0].SaveToFile(file) else: # Save as ordinary group. This might not be a good idea, # since the UnknownPlugin may (in the future) have some # special behaviour, that the group doesn't have. file.BeginGroup() for obj in self.objects: obj.SaveToFile(file) file.EndGroup() def Info(self): return _("Unknown Plugin Object `%s'") % self.class_name Ungroup = PluginCompound.GetObjects # XXX this implementation of the UnknownPlugin my fail in some # situations. # # The following example is now fixed, but similar situations are # conceivable: # # If the plugin defines is_curve and AsBezier(), for instance, it can be # used as a control object in a BlendGroup with, say, a PolyBezier # object as the other control object. The UnknownPlugin does not provide # that interface, so that loading a document containing such a # BlendGroup would fail, because the UnknownPlugin instance cannot be # converted to a PolyBezier object when the interpolation is recomputed. # # A solution would be to make the UnknownPlugin behave different if it # only has one child. Or to have it examine its children and try to # support the interfaces they have in common. I.e. if all children have # is_curve == 1, it could also set its instance variable is_curve to 1, # and implement the AsBezier meghod by returning a PolyBezier object # that is the combination (`Combine Beziers') of its converted children. # This would not always be appropriate, however. # # Are there other cases where UnknownPlugin has shortcomings?