compound.py :  » GUI » Sketch » skencil-0.6.17 » Sketch » Graphics » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » Sketch 
Sketch » skencil 0.6.17 » Sketch » Graphics » compound.py
# Sketch - A Python-based interactive drawing program
# Copyright (C) 1997, 1998, 1999, 2002 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

#
# Class Compound
#
# a baseclass for objects that contain other graphics objects
# (primitives or other compounds)
#

from types import ListType,TupleType,IntType,InstanceType
import operator

from Sketch import _,SketchError,UnionRects,EmptyRect,_sketch
from Sketch import NullUndo,CreateListUndo,Undo

from base import GraphicsObject,Bounded,CHANGED
from blend import Blend,MismatchError
from properties import EmptyProperties

from selinfo import prepend_idx,select_range,build_info,list_to_tree2,\
     list_to_tree_sliced



class Compound(GraphicsObject):

    has_edit_mode = 0
    is_Group    = 0
    is_Compound    = 1
    can_be_empty  = 0

    allow_traversal = 0

    def __init__(self, objects = None, duplicate = None):
  GraphicsObject.__init__(self, duplicate = duplicate)
  if duplicate is not None:
      objects = []
      for obj in duplicate.objects:
    objects.append(obj.Duplicate())
      self.objects = objects
  elif objects:
      self.objects = objects
  else:
      self.objects = []
  self.changing_children = 0
  self.set_parent()

    def Destroy(self):
  self.destroy_objects()
  GraphicsObject.Destroy(self)

    def destroy_objects(self):
  for obj in self.objects:
      obj.Destroy()
  self.objects = []

    def SetParent(self, parent):
  if parent is self.parent:
      return
  GraphicsObject.SetParent(self, parent)
  if parent is not None:
      self.set_parent()
  else:
      self.unset_parent()

    def set_parent(self):
  for child in self.objects:
      child.SetParent(self)

    def unset_parent(self):
  for child in self.objects:
      child.SetParent(None)

    def SelectionInfo(self, child = None):
  info = GraphicsObject.SelectionInfo(self)
  if info and child is not None:
      path = info[0]
      return (path + (_sketch.IdIndex(self.objects, child),),
                    child)
  return info

    def ChildChanged(self, child):
  self.del_lazy_attrs()
  if self.changing_children:
      return
  self.issue_changed()

    def SetDocument(self, doc):
  for obj in self.objects:
      obj.SetDocument(doc)
  GraphicsObject.SetDocument(self, doc)
  self.set_parent()

    def disconnect_objects(self):
  for obj in self.objects:
      obj.Disconnect()

    def Disconnect(self):
  self.disconnect_objects()
  self.unset_parent()

    def connect_objects(self):
  for obj in self.objects:
      obj.Connect()

    def Connect(self):
  self.set_parent()
  self.connect_objects()

    def set_objects(self, new_objs):
        if self.document is not None:
            self.document.AddClearRect(self.bounding_rect)
        self.disconnect_objects()
        self.destroy_objects()
        self.objects = new_objs
        for obj in self.objects:
            if self.document is not None:
                obj.SetDocument(self.document)
            obj.SetParent(self)
        # XXX no connect here ?
        self.del_lazy_attrs()
        if self.document is not None:
            self.document.AddClearRect(self.bounding_rect)

    def UntieFromDocument(self):
  for obj in self.objects:
      obj.UntieFromDocument()

    def TieToDocument(self):
  for obj in self.objects:
      obj.TieToDocument()

    def load_AppendObject(self, object):
  self.objects.append(object)

    def load_Done(self):
  pass

    def __getitem__(self, idx):
  if type(idx) == IntType:
      return self.objects[idx]
  elif type(idx) == TupleType:
      if len(idx) > 1:
    return self.objects[idx[0]][idx[1:]]
      elif len(idx) == 1:
    return self.objects[idx[0]]
  raise ValueError, 'invalid index %s' % `idx`

    def GetObjects(self):
  # XXX should this return a copy of self.objects?
  return self.objects

    def del_lazy_attrs(self):
  Bounded.del_lazy_attrs(self)
  return (self.del_lazy_attrs,)

    def update_rects(self):
  # XXX: should we raise an exception here if self.objects is empty?
  boxes = map(lambda o: o.coord_rect, self.objects)
  if boxes:
      self.coord_rect = reduce(UnionRects, boxes, boxes[0])
  else:
      self.coord_rect = EmptyRect

  boxes = map(lambda o: o.bounding_rect, self.objects)
  if boxes:
      self.bounding_rect = reduce(UnionRects, boxes, boxes[0])
  else:
      self.bounding_rect = EmptyRect

    def SelectSubobject(self, p, rect, device, path = None, *rest):
  return self

    def Insert(self, obj, at):
  raise SketchError('Cannot insert in compound')

    def Remove(self, *args, **kw):
  raise SketchError('Cannot remove from compound')

    RemoveSlice = Remove
    RemoveObjects = Remove

    ReplaceChild = Remove

    def MoveObjectsToTop(self, infolist):
  raise SketchError('Cannot rearrange objects in compound')

    MoveObjectsToBottom = MoveObjectsToTop
    MoveObjectsDown = MoveObjectsToTop
    MoveObjectsUp = MoveObjectsToTop
    def move_objects_to_top(self, infolist, to_bottom = 0):
  return infolist, NullUndo

    def DuplicateObjects(self, infolist, offset):
  raise SketchError('Cannot duplicate objects in compound')

    def ForAll(self, func):
  self.changing_children = 1
  try:
      return map(func, self.objects)
  finally:
      self.changing_children = 0

    def WalkHierarchy(self, func):
  for obj in self.objects:
      if obj.is_Compound:
    obj.WalkHierarchy(func)
      else:
    func(obj)

    def begin_change_children(self):
  self.changing_children = self.changing_children + 1
  return (self.end_change_children,)

    def end_change_children(self):
  self.changing_children = self.changing_children - 1
  if not self.changing_children:
      self._changed()
  return (self.begin_change_children,)

    def ForAllUndo(self, func):
  if self.objects:
      undo = [self.begin_change_children()]
      undo = undo + map(func, self.objects)
      undo.append(self.end_change_children())
      return CreateListUndo(undo)
  else:
      return NullUndo

    def FilterAll(self, func):
  return filter(func, self.GetObjects())

    def NumObjects(self):
  return len(self.objects)

    def Info(self):
  return _("Compound with %d objects") % len(self.objects)

    def AddStyle(self, style):
  undo = self.ForAllUndo(lambda o, style = style: o.AddStyle(style))
  return undo

    def Properties(self):
  return EmptyProperties

    def SetProperties(self, **kw):
  self.del_lazy_attrs()
  func = lambda o, kw = kw: apply(o.SetProperties, (), kw)
  undo = self.ForAllUndo(func)
  return undo

    def Hit(self, p, rect, device):
  test = rect.overlaps
  objects = self.objects
  for obj_idx in range(len(objects) - 1, -1, -1):
      obj = objects[obj_idx]
      if test(obj.bounding_rect):
    if obj.Hit(p, rect, device):
        return obj_idx + 1
  return 0

    def Transform(self, trafo):
  self.del_lazy_attrs()
  undo = self.ForAllUndo(lambda o, t = trafo: o.Transform(t))
  return undo

    def Translate(self, offset):
  undo = self.ForAllUndo(lambda o, p = offset: o.Translate(p))
  return undo

    def DrawShape(self, device, rect = None):
  if rect:
      test = rect.overlaps
      for o in self.objects:
    if test(o.bounding_rect):
        o.DrawShape(device, rect)
  else:
      for obj in self.objects:
    obj.DrawShape(device)

    def PickObject(self, point, rect, device):
  objects = self.objects[:]
  objects.reverse()
  test = rect.overlaps
  for obj in objects:
      if test(obj.bounding_rect):
    if obj.is_Compound:
        result = obj.PickObject(point, rect, device)
        if result:
      break
    elif obj.Hit(point, rect, device):
        result =  obj
        break
  else:
      result = None

  return result

    def Blend(self, other, frac1, frac2):
  try:
      objs = self.objects
      oobjs = other.objects
      blended = []
      for i in range(min(len(objs), len(oobjs))):
    blended.append(Blend(objs[i], oobjs[i], frac1, frac2))
      return Compound(blended)
  except:
      raise MismatchError

    def GetObjectHandle(self, multiple):
  return self.ForAll(lambda o: o.GetObjectHandle(1))


    def SelectFirstChild(self):
  if self.allow_traversal and self.objects:
      return self.objects[0]

    def SelectLastChild(self):
  if self.allow_traversal and self.objects:
      return self.objects[-1]

    def SelectNextChild(self, child, idx):
  if self.allow_traversal and len(self.objects) > idx + 1:
      return self.objects[idx + 1]

    def SelectPreviousChild(self, child, idx):
  if self.allow_traversal and len(self.objects) > idx - 1 and idx > 0:
      return self.objects[idx - 1]


class EditableCompound(Compound):

    allow_traversal = 1

    def SelectSubobject(self, p, rect, device, path = None, *rest):
  test = rect.overlaps
  if path is None:
      return self
  if path:
      path_idx = path[0]
      path = path[1:]
  else:
      path_idx = -1
      path = None
  objects = self.objects
  for obj_idx in range(len(objects) - 1, -1, -1):
      obj = objects[obj_idx]
      if test(obj.bounding_rect) and obj.Hit(p, rect, device):
    if obj_idx == path_idx:
        result = obj.SelectSubobject(p, rect, device, path)
    else:
        result = obj.SelectSubobject(p, rect, device)
    return prepend_idx(obj_idx, result)
  return None

    def Insert(self, obj, at):
  # Insert OBJ into the object hierarchy at the position described
  # by AT. AT should be either an integer or a tuple of integers.
  # OBJ can be a graphics object of a list of such objects.
  #
  # If AT is a tuple of 2 or more ints, self's child at AT[0] has
  # to be a compound object and its Insert method is called with
  # OBJ and AT[1:] as arguments.
  #
  # If AT is an int or a singleton of one int, insert OBJ at that
  # position in self's children. If OBJ is a graphics object, this
  # works just like a list objects insert method (insert(AT,
  # OBJ)). If its a list of graphics objects this method
  # effectively assigns that list to the slice AT:AT.
  #
  # As a side effect, this method calls the following methods of
  # the inserted objects:
  #
  #      obj.SetDocument(self.document)
  #      obj.SetParent(self)
  #      obj.Connect()
  #
  # Return a tuple (SELINFO, UNDO), where SELINFO is selection
  # info for the inserted objects at their new positions, and UNDO
  # is the appropriate undo info.
  #
  # If self is modified directly, issue a CHANGED message.
  undo_info = None
  try:
      if type(at) == TupleType and at:
    if len(at) == 1:
        at = at[0]
    else:
        child = at[0]
        at = at[1:]
        sel_info, undo_info = self.objects[child].Insert(obj, at)
        sel_info = prepend_idx(child, sel_info)
        return (sel_info, undo_info)
      if type(at) != IntType or at > len(self.objects):
    at = len(self.objects)
      if type(obj) == InstanceType:
    self.objects.insert(at, obj)
    obj.SetDocument(self.document)
    obj.SetParent(self)
    obj.Connect()
    sel_info = build_info(at, obj)
    undo_info = (self.Remove, obj, at)
      else:
    self.objects[at:at] = obj
    for o in obj:
        # XXX: should we have undo info for these:
        o.SetDocument(self.document)
        o.SetParent(self)
        o.Connect()
    sel_info = select_range(at, obj)
    undo_info = (self.RemoveSlice, at, at + len(obj))
      self._changed()
      return (sel_info, undo_info)
  except:
      if undo_info is not None:
    Undo(undo_info)
      raise

    def _insert_with_undo(self, obj, at):
  # The same as the Insert method but return only the undo info.
  return self.Insert(obj, at)[1]

    def do_remove_child(self, idx):
  obj = self.objects[idx]
  del self.objects[idx]
  obj.Disconnect()
  obj.SetParent(None)
  self._changed()
  return (self._insert_with_undo, obj, idx)

    def Remove(self, obj, idx = None):
  if type(idx) == TupleType:
      if len(idx) == 1:
    idx = idx[0]
      else:
    return self.objects[idx[0]].Remove(obj, idx[1:])
  if idx is None:
      idx = self.objects.index(obj)
  elif self.objects[idx] is not obj:
      raise ValueError, 'Compound.Remove(): invalid index'
  return self.do_remove_child(idx)

    def RemoveSlice(self, min, max):
  objs = self.objects[min:max]
  self.objects[min:max] = []
  for obj in objs:
      obj.Disconnect()
      obj.SetParent(None)
  self._changed()
  return (self._insert_with_undo, objs, min)

    def RemoveObjects(self, infolist):
  if not infolist:
      return NullUndo
  sliced = list_to_tree_sliced(infolist)
  sliced.reverse() # important!
  undo = [self.begin_change_children()]
  try:
      for start, end in sliced:
    if type(end) == IntType:
        undo.append(self.RemoveSlice(start, end))
    elif type(end) == ListType:
        undo.append(self.objects[start].RemoveObjects(end))
    else:
        undo.append(self.Remove(end, start))
      undo.append(self.end_change_children())
      return CreateListUndo(undo)
  except:
      Undo(CreateListUndo(undo))
      raise

    def ReplaceChild(self, child, object):
  # replace self's child child with object. Return undo info
  idx = self.objects.index(child)
  self.objects[idx] = object
  object.SetParent(self)
  object.SetDocument(self.document)
  child.SetParent(None)
  self._changed()
  return (self.ReplaceChild, object, child)

    def permute_objects(self, permutation):
  # permutation must be a list of ints and len(permutation) must be
  # equal to len(self.objects). permutation[i] is the index of the
  # object that is moved to index i in the result.
  objects = self.objects
  length = len(objects)
  identity = range(length)
  if permutation == identity:
      return NullUndo
  if len(objects) != len(permutation):
      raise ValueError, 'len(permutation) != len(self.objects)'

  result = map(operator.getitem, [objects] * length, permutation)
  inverse = [0] * length
  map(operator.setitem, [inverse] * length, permutation, identity)
  self.objects = result
  self._changed()
  return (self.permute_objects, inverse)

    def move_objects_to_top(self, infolist, to_bottom = 0):
  # Implement the public methods MoveToTop (if to_bottom is false)
  # and MoveToBottom (if to_bottom is true).
  sliced = list_to_tree_sliced(infolist)
  sliced.reverse()

  undo = [self.begin_change_children()]
  selection = []
  idxs = []
  permutation = range(len(self.objects))
  try:
      for start, end in sliced:
    if type(end) == IntType:
        # a contiguous range of self's children (start:end)
        idxs[:0] = permutation[start:end]
        del permutation[start:end]
    elif type(end) == ListType:
        # children of self.objects[start]
        child = self.objects[start]
        sel, undo_info = child.move_objects_to_top(end, to_bottom)
        if undo_info is not NullUndo:
      undo.append(undo_info)
        selection = selection + prepend_idx(start, sel)
    else:
        # a single object (self.object[start])
        idxs.insert(0, start)
        del permutation[start]

      if idxs:
    # direct children of self are involved: apply the
    # permutation
    if to_bottom:
        permutation = idxs + permutation
    else:
        permutation = permutation + idxs
    undo_info = self.permute_objects(permutation)
    if undo_info is not NullUndo:
        undo.append(undo_info)
      # finished:
      undo.append(self.end_change_children())
      if len(undo) <= 2:
    # We haven't really done anything (undo has length 2),
    # so we just pass the selection info back unchanged
    selection = infolist
    undo = NullUndo
      else:
    # We have done something, so figure out the new
    # selection info
    undo = CreateListUndo(undo)

    if to_bottom:
        selection = selection \
        + select_range(0, self.objects[:len(idxs)])
    else:
        min = len(self.objects) - len(idxs)
        selection = selection \
        + select_range(min, self.objects[min:])
      return (selection, undo)
  except:
      # Ooops, something's gone wrong. Undo everything we've done
      # so far... (hmm, this currently fails to undo everything if
      # undo.append(undo_info) fails... (the undo_info involved
      # would be lost))
      Undo(CreateListUndo(undo))
      raise

    def MoveObjectsToTop(self, infolist):
  return self.move_objects_to_top(infolist)

    def MoveObjectsToBottom(self, infolist):
  return self.move_objects_to_top(infolist, to_bottom = 1)

    def MoveObjectsDown(self, infolist):
  sliced = list_to_tree_sliced(infolist)
  undo = [self.begin_change_children()]
  selection = []
  permutation = range(len(self.objects))
  objects = self.objects
  try:
      for start, end in sliced:
    if type(end) == IntType:
        if start > 0:
      temp = permutation[start:end]
      del permutation[start:end]
      permutation[start - 1:start - 1] = temp
      selection = selection +select_range(start - 1,
                  objects[start:end])
        else:
      selection = selection +select_range(start,
                  objects[start:end])

    elif type(end) == ListType:
        sel, undo_info = objects[start].MoveObjectsDown(end)
        if undo_info is not NullUndo:
      undo.append(undo_info)
        selection = selection + prepend_idx(start, sel)
    else:
        if start > 0:
      del permutation[start]
      permutation.insert(start - 1, start)
      selection.append(build_info(start - 1, objects[start]))
        else:
      selection.append(build_info(start, objects[start]))

      undo_info = self.permute_objects(permutation)
      if undo_info is not NullUndo:
    undo.append(undo_info)
      undo.append(self.end_change_children())
      if len(undo) <= 2:
    undo = NullUndo
    selection = infolist
      else:
    undo = CreateListUndo(undo)
      return (selection, undo)
  except:
      Undo(CreateListUndo(undo))
      raise

    def MoveObjectsUp(self, infolist):
  sliced = list_to_tree_sliced(infolist)
  sliced.reverse()
  undo = [self.begin_change_children()]
  selection = []
  permutation = range(len(self.objects))
  objects = self.objects
  max = len(objects)
  try:
      for start, end in sliced:
    if type(end) == IntType:
        if end < max:
      temp = permutation[start:end]
      del permutation[start:end]
      permutation[start + 1:start + 1] = temp
      selection = selection + select_range(start + 1,
                 objects[start:end])
        else:
      selection = selection + select_range(start,
                 objects[start:end])

    elif type(end) == ListType:
        sel, undo_info = objects[start].MoveObjectsUp(end)
        if undo_info is not NullUndo:
      undo.append(undo_info)
        selection = selection + prepend_idx(start, sel)
    else:
        if start < max - 1:
      del permutation[start]
      permutation.insert(start + 1, start)
      selection.append(build_info(start + 1, objects[start]))
        else:
      selection.append(build_info(start, objects[start]))

      undo_info = self.permute_objects(permutation)
      if undo_info is not NullUndo:
    undo.append(undo_info)
      undo.append(self.end_change_children())
      if len(undo) <= 2:
    undo = NullUndo
    selection = infolist
      else:
    undo = CreateListUndo(undo)
      return (selection, undo)
  except:
      Undo(CreateListUndo(undo))
      raise

    def DuplicateObjects(self, infolist, offset):
  infolist = list_to_tree2(infolist)

  objects = self.objects
  undo = [self.begin_change_children()]
  selection = []
  added = 0
  try:
      for idx, obj in infolist:
    idx = idx + added
    if type(obj) == ListType:
        # duplicate in subobj
        sel, undoinfo = objects[idx].DuplicateObjects(obj, offset)
        undo.append(undoinfo)
        selection = selection + prepend_idx(idx, sel)
    else:
        obj = obj.Duplicate()
        obj.Translate(offset)
        sel, undoinfo = self.Insert(obj, idx + 1)
        undo.append(undoinfo)
        selection.append(sel)
        added = added + 1
      undo.append(self.end_change_children())
      return (selection, CreateListUndo(undo))
  except:
      Undo(CreateListUndo(undo))
      raise
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.