document.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 » document.py
# Sketch - A Python-based interactive drawing program
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2003 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

#
# Classes:
#
# SketchDocument
# EditDocument(SketchDocument)
#
# The document class represents a complete Sketch drawing. Each drawing
# consists of one or more Layers, which in turn consist of zero of more
# graphics objects. Graphics objects can be primitives like rectangles
# or curves or composite objects like groups which consist of graphics
# objects themselves. Objects may be arbitrarily nested.
#
# The distinction between SketchDocument and EditDocument has only
# historical reasons...
#

from types import ListType,IntType,StringType,TupleType
from string import join

from Sketch.warn import pdebug,warn,warn_tb,USER,INTERNAL
from Sketch import SketchInternalError


from Sketch import config,_
from Sketch.connector import Issue,RemovePublisher,Connect,Disconnect,\
     QueueingPublisher, Connector
from Sketch.undodict import UndoDict

from Sketch import Rect,Point,UnionRects,InfinityRect,Trafo
from Sketch import UndoRedo,Undo,CreateListUndo,NullUndo,UndoAfter

import color, selinfo, pagelayout

from base import Protocols
from layer import Layer,GuideLayer,GridLayer
from group import Group
from bezier import CombineBeziers
from properties import EmptyProperties
from pattern import SolidPattern
import guide
from selection import SizeSelection,EditSelection,TrafoSelection

from Sketch.const import STYLE,SELECTION,EDITED,MODE,UNDO,REDRAW,LAYOUT
from Sketch.const import LAYER,LAYER_ORDER,LAYER_ACTIVE,GUIDE_LINES,GRID
from Sketch.const import SelectSet,SelectAdd,SelectSubtract,SelectSubobjects,\
     SelectDrag, SelectGuide, Button1Mask
from Sketch.const import SCRIPT_OBJECT,SCRIPT_OBJECTLIST,SCRIPT_GET

#
from text import CanCreatePathText,CreatePathText


# SketchDocument is derived from Protocols for the benefit of the loader
# classes

class SketchDocument(Protocols):

    can_be_empty = 1

    script_access = {}

    def __init__(self, create_layer = 0):
  self.snap_grid = GridLayer()
  self.snap_grid.SetDocument(self)
  self.guide_layer = GuideLayer(_("Guide Lines"))
  self.guide_layer.SetDocument(self)
  if create_layer:
      # a new empty document
      self.active_layer = Layer(_("Layer 1"))
      self.active_layer.SetDocument(self)
      self.layers = [self.active_layer, self.guide_layer,
         self.snap_grid]
  else:
      # we're being created by the load module
      self.active_layer = None
      self.layers = []

    def __del__(self):
  if __debug__:
      pdebug('__del__', '__del__', self.meta.filename)

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

    def AppendLayer(self, layer_name = None, *args, **kw_args):
  try:
      old_layers = self.layers[:]
      if layer_name is None:
    layer_name = _("Layer %d") % (len(self.layers) + 1)
      else:
    layer_name = str(layer_name)
      layer = apply(Layer, (layer_name,) + args, kw_args)
      layer.SetDocument(self)
      self.layers.append(layer)
      if not self.active_layer:
    self.active_layer = layer
      return layer
  except:
      self.layers[:] = old_layers
      raise
    script_access['AppendLayer'] = SCRIPT_OBJECT

    def BoundingRect(self, visible = 1, printable = 0):
  rects = []
  for layer in self.layers:
      if ((visible and layer.Visible())
    or (printable and layer.Printable())):
    rect = layer.bounding_rect
    if rect and rect != InfinityRect:
        rects.append(rect)
  if rects:
      return reduce(UnionRects, rects)
  return None
    script_access['BoundingRect'] = SCRIPT_GET

    def augment_sel_info(self, info, layeridx):
  if type(layeridx) != IntType:
      layeridx = self.layers.index(layeridx)
  return selinfo.prepend_idx(layeridx, info)

    def insert(self, object, at = None, layer = None):
  undo_info = None
  try:
      if layer is None:
    layer = self.active_layer
      elif type(layer) == IntType:
    layer = self.layers[layer]
      if layer is None or layer.Locked():
    raise SketchInternalError('Layer %s is locked' % layer)
      if type(object) == ListType:
    for obj in object:
        obj.SetDocument(self)
      else:
    object.SetDocument(self)
      sel_info, undo_info = layer.Insert(object, at)
      sel_info = self.augment_sel_info(sel_info, layer)
      return (sel_info, undo_info)
  except:
      if undo_info is not None:
    Undo(undo_info)
      raise

    def selection_from_point(self, p, hitrect, device, path = None):
  # iterate top down (i.e. backwards) through the list of layers
  if path:
      path_layer = path[0]
      path = path[1:]
  else:
      path_layer = -1
  for idx in range(len(self.layers) - 1, -1, -1):
      if idx == path_layer:
    info = self.layers[idx].SelectSubobject(p, hitrect, device,
              path)
      else:
    info = self.layers[idx].SelectSubobject(p, hitrect, device)
      if info:
    return self.augment_sel_info(info, idx)
  else:
      return None

    def selection_from_rect(self, rect):
  info = []
  for layer in self.layers:
      info = info + self.augment_sel_info(layer.SelectRect(rect), layer)
  return info

    def Draw(self, device, rect = None):
  for layer in self.layers:
      layer.Draw(device, rect)

    def Grid(self):
  return self.snap_grid

    def SnapToGrid(self, p):
  return self.snap_grid.Snap(p)

    def SnapToGuide(self, p, maxdist):
  return self.guide_layer.Snap(p) #, maxdist)

    def DocumentInfo(self):
  info = []
  info.append('%d layers' % len(self.layers))
  for idx in range(len(self.layers)):
      layer = self.layers[idx]
      info.append('%d: %s,\t%d objects' % (idx + 1, layer.name,
             len(layer.objects)))
  return join(info, '\n')

    def SaveToFile(self, file):
  file.BeginDocument()
  self.page_layout.SaveToFile(file)
  self.write_styles(file)
  for layer in self.layers:
      layer.SaveToFile(file)
  file.EndDocument()

    def load_AppendObject(self, layer):
  self.layers.append(layer)

    def load_Done(self):
  pass

    def load_Completed(self):
  if not self.layers:
      self.layers = [Layer(_("Layer 1"))]
  if self.active_layer is None:
      for layer in self.layers:
    if layer.CanSelect():
        self.active_layer = layer
        break
  add_guide_layer = add_grid_layer = 1
  for layer in self.layers:
      layer.SetDocument(self)
      if isinstance(layer, GuideLayer):
    self.guide_layer = layer
    add_guide_layer = 0
      if isinstance(layer, GridLayer):
    self.snap_grid = layer
    add_grid_layer = 0
  if add_guide_layer:
      self.layers.append(self.guide_layer)
  if add_grid_layer:
      self.layers.append(self.snap_grid)


#
#  Class MetaInfo
#
#  Each document has an instance of this class as the variable
#  meta. The application object uses this variable to store various
#  data about the document, such as the name of the file it was
#  read from, the file type, etc. See skapp.py
#
class MetaInfo:
    pass

class AbortTransactionError(SketchInternalError):
    pass

SelectionMode = 0
EditMode = 1

class EditDocument(SketchDocument, QueueingPublisher):

    drag_mask = Button1Mask # canvas sometimes has the doc as current
          # object
    script_access = SketchDocument.script_access.copy()

    def __init__(self, create_layer = 0):
  SketchDocument.__init__(self, create_layer)
  QueueingPublisher.__init__(self)
  self.selection = SizeSelection()
  self.__init_undo()
  self.was_dragged = 0
  self.meta = MetaInfo()
  self.hit_cache = None
  self.connector = Connector()
  self.init_transaction()
  self.init_clear()
  self.init_styles()
  self.init_after_handler()
  self.init_layout()

    def Destroy(self):
  self.undo = None
  self.destroy_styles()
  RemovePublisher(self)
  for layer in self.layers:
      layer.Destroy()
  self.layers = []
  self.active_layer = None
  self.guide_layer = None
  self.snap_grid = None
  # make self.connector empty connector to remove circular refs
  # and to allow object to call document.connector.RemovePublisher
  # in their __del__ methods
  self.connector = Connector()
  self.selection = None
  self.transaction_undo = []
  self.transaction_sel = []

    def queue_layer(self, *args):
  if self.transaction:
      apply(self.queue_message, (LAYER,) + args)
      return (self.queue_layer, args)
  else:
      apply(self.issue, (LAYER,) + args)

    def queue_selection(self):
  self.queue_message(SELECTION)

    def queue_edited(self):
  # An EDITED message should probably indicate the type of edit,
  # i.e. whether properties changed, the geometry of objects
  # changed, etc.; hence the additional string argument which may
  # hold this information in the future
  self.queue_message(EDITED, '')
  return (self.queue_edited,)

    def Subscribe(self, channel, func, *args):
  Connect(self, channel, func, args)

    def Unsubscribe(self, channel, func, *args):
  Disconnect(self, channel, func, args)

    def init_after_handler(self):
  self.after_handlers = []

    def AddAfterHandler(self, handler, args = (), depth = 0):
  handler = (depth, handler, args)
  try:
      self.after_handlers.remove(handler)
  except ValueError:
      pass
  self.after_handlers.append(handler)

    def call_after_handlers(self):
  if not self.after_handlers:
      return 0

  while self.after_handlers:
            handlers = self.after_handlers

      handlers.sort()
      handlers.reverse()
      depth = handlers[0][0]

      count = 0
      for d, handler, args in handlers:
    if d == depth:
        count = count + 1
    else:
        break
      self.after_handlers = handlers[count:]
      handlers = handlers[:count]

      for d, handler, args in handlers:
    try:
        apply(handler, args)
    except:
        warn_tb(INTERNAL, "In after handler `%s'%s", handler, args)

  return 1

    def init_clear(self):
  self.clear_rects = []
  self.clear_all = 0

    reset_clear = init_clear

    def AddClearRect(self, rect):
  self.clear_rects.append(rect)
  return (self.AddClearRect, rect)

    def view_redraw_all(self):
  self.clear_all = 1
  return (self.view_redraw_all,)

    def issue_redraw(self):
  try:
      if self.clear_all:
    Issue(self, REDRAW, 1)
      else:
    Issue(self, REDRAW, 0, self.clear_rects)
  finally:
      self.clear_rects = []
      self.clear_all = 0

    def init_transaction(self):
  self.reset_transaction()

    def reset_transaction(self):
  self.transaction = 0
  self.transaction_name = ''
  self.transaction_sel = []
  self.transaction_undo = []
  self.transaction_sel_ignore = 0
  self.transaction_clear = None
  self.transaction_aborted = 0
  self.transaction_cleanup = []

    def cleanup_transaction(self):
  for handler, args in self.transaction_cleanup:
      try:
    apply(handler, args)
      except:
    warn_tb(INTERNAL, "in cleanup handler %s%s", handler, `args`)
  self.transaction_cleanup = []

    def add_cleanup_handler(self, handler, *args):
  handler = (handler, args)
  try:
      self.transaction_cleanup.remove(handler)
  except ValueError:
      pass
  self.transaction_cleanup.append(handler)

    def begin_transaction(self, name = '', no_selection = 0,
        clear_selection_rect = 1):
  if self.transaction_aborted:
      raise AbortTransactionError
  if self.transaction == 0:
      if not no_selection:
    selinfo = self.selection.GetInfo()[:]
    if selinfo != self.transaction_sel:
        self.transaction_sel = selinfo
    self.transaction_sel_mode = self.selection.__class__
      self.transaction_sel_ignore = no_selection
      self.transaction_name = name
      self.transaction_undo = []
      if clear_selection_rect:
    if self.selection:
        self.transaction_clear = self.selection.bounding_rect
      else:
    self.transaction_clear = None
  elif not self.transaction_name:
      self.transaction_name = name
  self.transaction = self.transaction + 1

    def end_transaction(self, issue = (), queue_edited = 0):
  self.transaction = self.transaction - 1
  if self.transaction_aborted:
      # end an aborted transaction
      if self.transaction == 0:
    # undo the changes already done...
    undo = self.transaction_undo
    undo.reverse()
    map(Undo, undo)
    self.cleanup_transaction()
    self.reset_transaction()
    self.reset_clear()
  else:
      # a normal transaction
      if type(issue) == StringType:
    self.queue_message(issue)
      else:
    for channel in issue:
        self.queue_message(channel)
      if self.transaction == 0:
    # the outermost end_transaction
    # increase transaction flag temporarily because some
    # after handlers might call public methods that are
    # themselves transactions...
    self.transaction = 1
    if self.call_after_handlers():
        self.selection.ResetRectangle()
    self.transaction = 0
    undo = CreateListUndo(self.transaction_undo)
    if undo is not NullUndo:
        undo = [undo]
        if self.transaction_clear is not None:
      undo.append(self.AddClearRect(self.transaction_clear))
      if self.selection:
          self.selection.ResetRectangle()
          rect = self.selection.bounding_rect
          undo.append(self.AddClearRect(rect))
        if queue_edited:
      undo.append(self.queue_edited())
        undo = CreateListUndo(undo)
        if self.transaction_sel_ignore:
      self.__real_add_undo(self.transaction_name, undo)
        else:
      self.__real_add_undo(self.transaction_name, undo,
               self.transaction_sel,
               self.transaction_sel_mode)
    self.flush_message_queue()
    self.issue_redraw()
    self.cleanup_transaction()
    self.reset_transaction()
    self.reset_clear()
      elif self.transaction < 0:
    raise SketchInternalError('transaction < 0')

    def abort_transaction(self):
  self.transaction_aborted = 1
  warn_tb(INTERNAL, "in transaction `%s'" % self.transaction_name)
  raise AbortTransactionError

    # public versions of the transaction methods
    BeginTransaction = begin_transaction
    AbortTransaction = abort_transaction

    def EndTransaction(self):
  self.end_transaction(queue_edited = 1)

    def Insert(self, object, undo_text = _("Create Object")):
  if isinstance(object, guide.GuideLine):
      self.add_guide_line(object)
  else:
      self.begin_transaction(undo_text, clear_selection_rect = 0)
      try:
    try:
        object.SetDocument(self)
        selected, undo = self.insert(object)
        self.add_undo(undo)
        self.add_undo(self.AddClearRect(object.bounding_rect))
        self.__set_selection(selected, SelectSet)
    except:
        self.abort_transaction()
      finally:
    self.end_transaction(queue_edited = 1)

    def SelectPoint(self, p, device, type = SelectSet):
  # find object at point, and modify the current selection
  # according to type
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    if type == SelectSubobjects:
        path = self.selection.GetPath()
    else:
        path = ()
    rect = device.HitRectAroundPoint(p)
    if self.hit_cache:
        cp, cdevice, hit = self.hit_cache
        self.hit_cache = None
        if p is cp and device is cdevice:
      selected = hit
        else:
      selected = self.selection_from_point(p, rect, device,
                   path)
    else:
        selected = self.selection_from_point(p, rect, device, path)
    if type == SelectGuide:
                    if selected and selected[-1].is_GuideLine:
                        return selected[-1]
        return None
    elif selected:
        path, object = selected
        if self.layers[path[0]] is self.guide_layer:
      if object.is_GuideLine:
          # guide lines cannot be selected in the
          # ordinary way, but other objects on the
          # guide layer can.
          selected = None
    self.__set_selection(selected, type)

                if self.IsEditMode():
                    object = self.CurrentObject()
                    if object is not None and object.is_Text:
                        self.SelectPointPart(p, device, SelectSet)

      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
  return selected

    def SelectRect(self, rect, mode = SelectSet):
  # Find all objects contained in rect and modify the current
  # selection according to mode
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    self.hit_cache = None
    selected = self.selection_from_rect(rect)
    self.__set_selection(selected, mode)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
  return selected

    def SelectRectPart(self, rect, mode = SelectSet):
  # Select the part of the CSO that lies in rect. Currently this
  # works only in edit mode. For a PolyBezier this means that all
  # nodes within rect are selected.
  if not self.IsEditMode():
      raise SketchInternalError('SelectRectPart requires edit mode')
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    self.hit_cache = None
    self.selection.SelectRect(rect, mode)
    self.queue_selection()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def SelectPointPart(self, p, device, mode = SelectSet):
  # Select the part of the current object under the point p.
  # Like SelectRectPart this only works in edit mode.
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
                self.hit_cache = None
                rect = device.HitRectAroundPoint(p)
                self.selection.SelectPoint(p, rect, device, mode)
    if mode != SelectDrag:
        self.queue_selection()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def SelectHandle(self, handle, mode = SelectSet):
  # Select the handle indicated by handle. This only works in edit
  # mode.
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    self.hit_cache = None
    self.selection.SelectHandle(handle, mode)
    if mode != SelectDrag:
        self.queue_selection()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def SelectAll(self):
  # Select all objects that can currently be selected.
  # XXX should the objects in the guide layer also be selected by
  # this method? (currently they are)
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    sel_info = []
    for layer_idx in range(len(self.layers)):
        sel = self.layers[layer_idx].SelectAll()
        if sel:
      sel = self.augment_sel_info(sel, layer_idx)
      sel_info = sel_info + sel
    self.__set_selection(sel_info, SelectSet)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
    script_access['SelectAll'] = SCRIPT_GET

    def SelectNone(self):
  # Deselect all objects.
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    self.__set_selection(None, SelectSet)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
    script_access['SelectNone'] = SCRIPT_GET

    def SelectObject(self, objects, mode = SelectSet):
  # Select the objects defined by OBJECTS. OBJECTS may be a single
  # GraphicsObject or a list of such objects. Modify the current
  # selection according to MODE.
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    if type(objects) != ListType:
        objects = [objects]
    selinfo = []
    for object in objects:
        selinfo.append(object.SelectionInfo())
                if selinfo:
                    self.__set_selection(selinfo, mode)
                else:
                    self.__set_selection(None, SelectSet)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
    #script_access['SelectObject'] = SCRIPT_GET


    def select_first_in_layer(self, idx = 0):
  for layer in self.layers[idx:]:
      if layer.CanSelect() and not layer.is_SpecialLayer:
    object = layer.SelectFirstChild()
    if object is not None:
        return object

    def SelectNextObject(self):
  # If exactly one object is selected select its next higher
  # sibling. If there is no next sibling and its parent is a
  # layer, select the first object in the next higher layer that
  # allows selections.
  #
  # If more than one object is currently selected, deselect all
  # but the the highest of them.
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    info = self.selection.GetInfo()
    if len(info) > 1:
        self.__set_selection(info[-1], SelectSet)
    elif info:
        path, object = info[0]
        parent = object.parent
        object = parent.SelectNextChild(object, path[-1])
        if object is None and parent.is_Layer:
      idx = self.layers.index(parent)
      object = self.select_first_in_layer(idx + 1)
        if object is not None:
      self.SelectObject(object)
    else:
        object = self.select_first_in_layer()
        if object is not None:
      self.SelectObject(object)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
    script_access['SelectNextObject'] = SCRIPT_GET

    def select_last_in_layer(self, idx):
  if idx < 0:
      return
  layers = self.layers[:idx + 1]
  layers.reverse()
  for layer in layers:
      if layer.CanSelect() and not layer.is_SpecialLayer:
    object = layer.SelectLastChild()
    if object is not None:
        return object

    def SelectPreviousObject(self):
  # If exactly one object is selected select its next lower
  # sibling. If there is no lower sibling and its parent is a
  # layer, select the last object in the next lower layer that
  # allows selections.
  #
  # If more than one object is currently selected, deselect all
  # but the the lowest of them.
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    info = self.selection.GetInfo()
    if len(info) > 1:
        self.__set_selection(info[0], SelectSet)
    elif info:
        path, object = info[0]
        parent = object.parent
        object = parent.SelectPreviousChild(object, path[-1])
        if object is None and parent.is_Layer:
      idx = self.layers.index(parent)
      object = self.select_last_in_layer(idx - 1)
        if object is not None:
      self.SelectObject(object)
    else:
        object = self.select_last_in_layer(len(self.layers))
        if object is not None:
      self.SelectObject(object)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
    script_access['SelectPreviousObject'] = SCRIPT_GET

    def SelectFirstChild(self):
  # If exactly one object is selected and this object is a
  # compound object, select its first (lowest) child. The first
  # child is the object returned by the compound object's method
  # SelectFirstChild. If that method returns none, do nothing.
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    objects = self.selection.GetObjects()
    if len(objects) == 1:
        object = objects[0]
        if object.is_Compound:
      object = object.SelectFirstChild()
      if object is not None:
          self.SelectObject(object)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
    script_access['SelectFirstChild'] = SCRIPT_GET

    def SelectParent(self):
  # Select the parent of the currently selected object(s).
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    if len(self.selection) > 1:
        path = selinfo.common_prefix(self.selection.GetInfo())
        if len(path) > 1:
      object = self[path]
      self.SelectObject(object)
    elif len(self.selection) == 1:
        object = self.selection.GetObjects()[0].parent
        if not object.is_Layer:
      self.SelectObject(object)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
    script_access['SelectParent'] = SCRIPT_GET

    def DeselectObject(self, object):
  # Deselect the object OBJECT.
  # XXX: for large selections this can be very slow.
  selected = self.selection.GetObjects()
  try:
      index = selected.index(object)
  except ValueError:
      return
  info = self.selection.GetInfo()
  del info[index]
  self.__set_selection(info, SelectSet)

    def __set_selection(self, selected, type):
  # Modify the current selection. SELECTED is a list of selection
  # info describing the new selection, TYPE indicates how the
  # current selection is modified:
  #
  # type      Meaning
  # SelectSet    Replace the old selection by the new one
  # SelectSubtract  Subtract the new selection from the old one
  # SelectAdd    Add the new selection to the old one.
  # SelectSubobjects  like SelectSet here
  changed = 0
  if type == SelectAdd:
      if selected:
    changed = self.selection.Add(selected)
  elif type == SelectSubtract:
      if selected:
    changed = self.selection.Subtract(selected)
  elif type == SelectGuide:
      if selected:
    pass
  else:
      # type is SelectSet or SelectSubobjects
      # set the selection. make a size selection if necessary
      if self.selection.__class__ == TrafoSelection:
    self.selection = SizeSelection()
    changed = 1
      changed = self.selection.SetSelection(selected) or changed
  if changed:
      self.queue_selection()

    def SetMode(self, mode):
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    if mode == SelectionMode:
        self.selection = SizeSelection(self.selection)
    else:
        self.selection = EditSelection(self.selection)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction(issue = (SELECTION, MODE))

    def Mode(self):
  if self.selection.__class__ == EditSelection:
      return EditMode
  return SelectionMode
    script_access['Mode'] = SCRIPT_GET

    def IsSelectionMode(self):
  return self.Mode() == SelectionMode
    script_access['IsSelectionMode'] = SCRIPT_GET

    def IsEditMode(self):
  return self.Mode() == EditMode
    script_access['IsEditMode'] = SCRIPT_GET


    def SelectionHit(self, p, device, test_all = 1):
  # Return true, if the point P hits the currently selected
  # objects.
  #
  # If test_all is true (the default), find the object that would
  # be selected by SelectPoint and return true if it or one of its
  # ancestors is contained in the current selection and false
  # otherwise.
  #
  # If test_all is false, just test the currently selected objects.
  rect = device.HitRectAroundPoint(p)
  if len(self.selection) < 10 or not test_all:
      selection_hit = self.selection.Hit(p, rect, device)
      if not test_all or not selection_hit:
    return selection_hit
  if test_all:
      path = self.selection.GetPath()
      if len(path) > 2:
    path = path[:-1]
      else:
    path = ()
      hit = self.selection_from_point(p, rect, device, path)
      self.hit_cache = (p, device, hit)
      while hit:
    if hit in self.selection.GetInfo():
        return 1
    hit = selinfo.get_parent(hit)
      #self.hit_cache = None
      return 0

    def GetSelectionHandles(self):
  if self.selection:
      return self.selection.GetHandles()
  else:
      return []

    #
    #  Get information about the selected objects
    #

    def HasSelection(self):
  # Return true, if one or more objects are selected
  return len(self.selection)
    script_access['HasSelection'] = SCRIPT_GET

    def CountSelected(self):
  # Return the number of currently selected objects
  return len(self.selection)
    script_access['CountSelected'] = SCRIPT_GET

    def SelectionInfoText(self):
  # Return a string describing the selected object(s)
  return self.selection.InfoText()
    script_access['SelectionInfoText'] = SCRIPT_GET

    def CurrentInfoText(self):
        return self.selection.CurrentInfoText()

    def SelectionBoundingRect(self):
  # Return the bounding rect of the current selection
  return self.selection.bounding_rect
    script_access['SelectionBoundingRect'] = SCRIPT_GET

    def CurrentObject(self):
  # If exactly one object is selected return that, None instead.
  if len(self.selection) == 1:
      return self.selection.GetObjects()[0]
  return None
    script_access['CurrentObject'] = SCRIPT_OBJECT

    def SelectedObjects(self):
  # Return the selected objects as a list. They are listed in the
  # order in which they are drawn.
  return self.selection.GetObjects()
    script_access['SelectedObjects'] = SCRIPT_OBJECTLIST

    def CurrentProperties(self):
  # Return the properties of the current object if exactly one
  # object is selected. Return EmptyProperties otherwise.
  if self.selection:
      if len(self.selection) > 1:
    return EmptyProperties
      return self.selection.GetInfo()[0][-1].Properties()
  return EmptyProperties
    script_access['CurrentProperties'] = SCRIPT_OBJECT


    def CurrentFillColor(self):
  # Return the fill color of the current object if exactly one
  # object is selected and that object has a solid fill. Return
  # None otherwise.
  if len(self.selection) == 1:
      properties = self.selection.GetInfo()[0][-1].Properties()
      try:
    return  properties.fill_pattern.Color()
      except AttributeError:
    pass
  return None
    script_access['CurrentFillColor'] = SCRIPT_GET


    def PickObject(self, device, point, selectable = 0):
  # Return the object that is hit by a click at POINT. The object
  # is not selected and should not be modified by the caller.
        #
  # If selectable is false, this function descends into compound
  # objects that are normally selected as a whole when one of
  # their children is hit. If selectable is true, the search is
  # done as for a normal selection.
        #
        # This method is intended to be used to
  # let the user click on the drawing and extract properties from
  # the indicated object. The fill and line dialogs use this
  # indirectly (through the canvas object's PickObject) for their
  # 'Update From...' button.
  #
  # XXX should this be implemented by calling WalkHierarchy
  # instead of requiring a special PickObject method in each
  # compound? Unlike the normal hit-test, this method is not that
  # time critical and WalkHierarchy is sufficiently fast for most
  # purposes (see extract_snap_points in the canvas).
  # WalkHierarchy would have to be able to traverse the hierarchy
  # top down and not just bottom up.
        object = None
  rect = device.HitRectAroundPoint(point)
        if not selectable:
            layers = self.layers[:]
            layers.reverse()
            for layer in layers:
                object = layer.PickObject(point, rect, device)
                if object is not None:
                    break
        else:
            selected = self.selection_from_point(point, rect, device)
            if selected:
                object = selected[-1]
  return object

    def PickActiveObject(self, device, p):
        # return the object under point if it's selected or a guide
        # line. None otherwise.
  rect = device.HitRectAroundPoint(p)
        path = self.selection.GetPath()
        if len(path) > 2:
            path = path[:-1]
        else:
            path = ()
        hit = self.selection_from_point(p, rect, device, path)
        #self.hit_cache = (p, device, hit)
        if hit:
            if not hit[-1].is_GuideLine:
                while hit:
                    if hit in self.selection.GetInfo():
                        hit = hit[-1]
                        break
                    hit = selinfo.get_parent(hit)
            else:
                hit = hit[-1]
        return hit
    
    #
    #
    #

    def WalkHierarchy(self, func, printable = 1, visible = 1, all = 0):
  # XXX make the selection of layers more versatile
  for layer in self.layers:
      if (all
                or printable and layer.Printable()
                or visible and layer.Visible()):
    layer.WalkHierarchy(func)

    #
    #
    #
    def ButtonDown(self, p, button, state):
  self.was_dragged = 0
  self.old_change_rect = self.selection.ChangeRect()
  result = self.selection.ButtonDown(p, button, state)
  return result

    def MouseMove(self, p, state):
  self.was_dragged = 1
  self.selection.MouseMove(p, state)

    def ButtonUp(self, p, button, state):
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    if self.was_dragged:
        undo_text, undo_edit \
             = self.selection.ButtonUp(p, button, state)
        if undo_edit is not None and undo_edit != NullUndo:
      self.add_undo(undo_text, undo_edit)
      uc1 = self.AddClearRect(self.old_change_rect)
      uc2 = self.AddClearRect(self.selection.ChangeRect())
      self.add_undo(uc1, uc2)
      self.add_undo(self.queue_edited())
        else:
      # the user probably just moved the rotation
      # center point. The canvas has to update the
      # handles
      self.queue_selection()
    else:
        self.selection.ButtonUp(p, button, state, forget_trafo = 1)
        self.ToggleSelectionBehaviour()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def ToggleSelectionBehaviour(self):
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    if self.selection.__class__ == SizeSelection:
        self.selection = TrafoSelection(self.selection)
    elif self.selection.__class__ == TrafoSelection:
        self.selection = SizeSelection(self.selection)
    self.queue_selection()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def DrawDragged(self, device, partially = 0):
  self.selection.DrawDragged(device, partially)

    def Hide(self, device, partially = 0):
  self.selection.Hide(device, partially)

    def Show(self, device, partially = 0):
  self.selection.Show(device, partially)

    def ChangeRect(self):
  return self.selection.ChangeRect()

    #
    #  The undo mechanism
    #

    def __init_undo(self):
  self.undo = UndoRedo()

    def CanUndo(self):
  return self.undo.CanUndo()
    script_access['CanUndo'] = SCRIPT_GET

    def CanRedo(self):
  return self.undo.CanRedo()
    script_access['CanRedo'] = SCRIPT_GET

    def Undo(self):
  if self.undo.CanUndo():
      self.begin_transaction(clear_selection_rect = 0)
      try:
    try:
        self.undo.Undo()
    except:
        self.abort_transaction()
      finally:
    self.end_transaction(issue = UNDO)
    script_access['Undo'] = SCRIPT_GET

    def add_undo(self, *infos):
  # Add undoinfo for the current transaction. should not be called
  # when not in a transaction.
  if infos:
      if type(infos[0]) == StringType:
    if not self.transaction_name:
        self.transaction_name = infos[0]
    infos = infos[1:]
    if not infos:
        return
      for info in infos:
    if type(info) == ListType:
        info = CreateListUndo(info)
    else:
        if type(info[0]) == StringType:
      if __debug__:
          pdebug(None, 'add_undo: info contains text')
      info = info[1:]
    self.transaction_undo.append(info)

    # public version of add_undo. to be called between calls to
    # BeginTransaction and EndTransaction/AbortTransaction
    AddUndo = add_undo

    def __undo_set_sel(self, selclass, selinfo, redo_class, redo_info):
  old_class = self.selection.__class__
  if old_class != selclass:
      self.selection = selclass(selinfo)
      self.queue_message(MODE)
  else:
      # keep the same selection object to avoid creating a new
      # editor object in EditMode
      self.selection.SetSelection(selinfo)
  self.queue_selection()
  return (self.__undo_set_sel, redo_class, redo_info, selclass, selinfo)

    def __real_add_undo(self, text, undo, selinfo = None, selclass = None):
  if undo is not NullUndo:
      if selinfo is not None:
    new_class = self.selection.__class__
    new_info = self.selection.GetInfo()[:]
    if new_info == selinfo:
        # make both lists identical
        new_info = selinfo
    undo_sel = (self.__undo_set_sel, selclass, selinfo,
          new_class, new_info)
    info = (text, UndoAfter, undo_sel, undo)
      else:
    info = (text, undo)
      self.undo.AddUndo(info)
      self.queue_message(UNDO)


    def Redo(self):
  if self.undo.CanRedo():
      self.begin_transaction(clear_selection_rect = 0)
      try:
    try:
        self.undo.Redo()
    except:
        self.abort_transaction()
      finally:
    self.end_transaction(issue = UNDO)
    script_access['Redo'] = SCRIPT_GET

    def ResetUndo(self):
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    self.undo.Reset()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction(issue = UNDO)
    script_access['ResetUndo'] = SCRIPT_GET

    def UndoMenuText(self):
  return self.undo.UndoText()
    script_access['UndoMenuText'] = SCRIPT_GET

    def RedoMenuText(self):
  return self.undo.RedoText()
    script_access['RedoMenuText'] = SCRIPT_GET

    def SetUndoLimit(self, limit):
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    self.undo.SetUndoLimit(limit)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction(issue = UNDO)
    script_access['SetUndoLimit'] = SCRIPT_GET

    def WasEdited(self):
  # return true if document has changed since last save
  return self.undo.UndoCount()
    script_access['WasEdited'] = SCRIPT_GET

    def ClearEdited(self):
  self.undo.ResetUndoCount()
  self.issue(UNDO)

    #
    #

    def apply_to_selected(self, undo_text, func):
  if self.selection:
      self.begin_transaction(undo_text)
      try:
    try:
        self.add_undo(self.selection.ForAllUndo(func))
        self.queue_selection()
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def AddStyle(self, style):
  if type(style) == StringType:
      style = self.GetDynamicStyle(style)
  self.apply_to_selected(_("Add Style"),
             lambda o, style = style: o.AddStyle(style))

    def SetLineColor(self, color):
  # Set the line color of the currently selected objects.
  # XXX this method shpuld be removed in favour of the more
  # generic SetProperties.
  self.SetProperties(line_pattern = SolidPattern(color),
         if_type_present = 1)

    def SetProperties(self, **kw):
  self.apply_to_selected(_("Set Properties"),
             lambda o, kw=kw: apply(o.SetProperties, (), kw))

    def SetStyle(self, style):
  if type(style) == StringType:
      style = self.get_dynamic_style(style)
      self.AddStyle(style)

    #
    #  Deleting and rearranging objects...
    #

    def remove_objects(self, infolist):
  split = selinfo.list_to_tree(infolist)
  undo = []
  try:
      for layer, infolist in split:
    undo.append(self.layers[layer].RemoveObjects(infolist))
      return CreateListUndo(undo)
  except:
      Undo(CreateListUndo(undo))
      raise

    def remove_selected(self):
  return self.remove_objects(self.selection.GetInfo())

    def RemoveSelected(self):
  # Remove all selected objects. After successful completion, the
  # selection will be empty.
  if self.selection:
      self.begin_transaction(_("Delete"))
      try:
    try:
        self.add_undo(self.remove_selected())
        self.__set_selection(None, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def __call_layer_method_sel(self, undotext, methodname, *args):
  if not self.selection:
      return
  self.begin_transaction(undotext)
  try:
      try:
    split = selinfo.list_to_tree(self.selection.GetInfo())
    edited = 0
    selection = []
    for layer, infolist in split:
        method = getattr(self.layers[layer], methodname)
        sel, undo = apply(method, (infolist,) + args)
        if undo is not NullUndo:
      self.add_undo(undo)
      edited = 1
        selection = selection + self.augment_sel_info(sel, layer)
    self.__set_selection(selection, SelectSet)
    if edited:
        self.add_undo(self.queue_edited())
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def MoveSelectedToTop(self):
  self.__call_layer_method_sel(_("Move To Top"), 'MoveObjectsToTop')

    def MoveSelectedToBottom(self):
  self.__call_layer_method_sel(_("Move To Bottom"),'MoveObjectsToBottom')

    def MoveSelectionDown(self):
  self.__call_layer_method_sel(_("Lower"), 'MoveObjectsDown')

    def MoveSelectionUp(self):
  self.__call_layer_method_sel(_("Raise"), 'MoveObjectsUp')

    def MoveSelectionToLayer(self, layer):
  if self.selection:
      self.begin_transaction(_("Move Selection to `%s'")
           % self.layers[layer].Name())
      try:
    try:
        # remove the objects from the document...
        self.add_undo(self.remove_selected())
        # ... and insert them a the end of the layer
        objects = self.selection.GetObjects()
        select, undo_insert = self.insert(objects, layer = layer)

        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    #
    #  Cut/Copy
    #

    def copy_objects(self, objects):
  copies = []
  for obj in objects:
      copies.append(obj.Duplicate())

  if len(copies) > 1:
      copies = Group(copies)
  else:
      copies = copies[0]
            # This is ugly: Special case for internal path text objects.
            # If the internal path text object is the only selected
            # object, turn the copy into a normal simple text object.
            # Thsi avoids some of the problems when you "Copy" an
            # internal path text.
            import text
            if copies.is_PathTextText:
                properties = copies.Properties().Duplicate()
                copies = text.SimpleText(text = copies.Text(),
                                         properties = properties)

  copies.UntieFromDocument()
  copies.SetDocument(None)
  return copies

    def CopyForClipboard(self):
  if self.selection:
      return self.copy_objects(self.selection.GetObjects())

    def CutForClipboard(self):
  result = None
  if self.selection:
      self.begin_transaction(_("Cut"))
      try:
    try:
        objects = self.selection.GetObjects()
        result = self.copy_objects(objects)
        self.add_undo(self.remove_selected())
        self.__set_selection(None, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        result = None
        self.abort_transaction()
      finally:
    self.end_transaction()
  return result

    #
    #  Duplicate
    #

    def DuplicateSelected(self, offset = None):
  if offset is None:
      offset = Point(config.preferences.duplicate_offset)
  self.__call_layer_method_sel(_("Duplicate"), 'DuplicateObjects',
             offset)

    #
    #  Group
    #

    def group_selected(self, title, creator):
        self.begin_transaction(title)
        try:
            try:
                self.add_undo(self.remove_selected())
                objects = self.selection.GetObjects()
                group = creator(objects)
                parent = selinfo.common_prefix(self.selection.GetInfo())
                if parent:
                    layer = parent[0]
                    at = parent[1:]
                else:
                    layer = None
                    at = None
                select, undo_insert = self.insert(group, at = at, layer =layer)
                self.add_undo(undo_insert)
                self.__set_selection(select, SelectSet)
            except:
                self.abort_transaction()
        finally:
            self.end_transaction()

    def CanGroup(self):
  return len(self.selection) > 1

    def GroupSelected(self):
  if self.CanGroup():
            self.group_selected(_("Create Group"), Group)

    def CanUngroup(self):
  infos = self.selection.GetInfo()
  return len(infos) == 1 and infos[0][-1].is_Group

    def UngroupSelected(self):
  if self.CanUngroup():
      self.begin_transaction(_("Ungroup"))
      try:
    try:
        self.add_undo(self.remove_selected())
        info, group = self.selection.GetInfo()[0]
        objects = group.Ungroup()
        select, undo_insert = self.insert(objects, at = info[1:],
                  layer = info[0])
        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def CanCreateMaskGroup(self):
  infos = self.selection.GetInfo()
  return len(infos) > 1 and infos[-1][-1].is_clip

    def CreateMaskGroup(self):
  if self.CanCreateMaskGroup():
      self.begin_transaction(_("Create Mask Group"))
      try:
    try:
        import maskgroup
        self.add_undo(self.remove_selected())
        objects = self.selection.GetObjects()
        if config.preferences.topmost_is_mask:
      mask = objects[-1]
      del objects[-1]
      objects.insert(0, mask)
        group = maskgroup.MaskGroup(objects)
        parent = selinfo.common_prefix(self.selection.GetInfo())
        if parent:
      layer = parent[0]
      at = parent[1:]
        else:
      layer = None
      at = None
        select, undo_insert = self.insert(group, at = at,
                  layer = layer)
        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()


    #
    #  Transform, Translate, ...
    #

    def TransformSelected(self, trafo, undo_text = _("Transform")):
  self.apply_to_selected(undo_text, lambda o, t = trafo: o.Transform(t))

    def TranslateSelected(self, offset, undo_text = _("Translate")):
  self.apply_to_selected(undo_text, lambda o, v = offset: o.Translate(v))

    def RemoveTransformation(self):
  self.apply_to_selected(_("Remove Transformation"),
             lambda o: o.RemoveTransformation())

    #
    #  Align, Flip, ...
    #
    # XXX These functions could be implemented outside of the document.
    # (Maybe by command plugins or scripts?)
    #

    def AlignSelection(self, x, y, reference = 'selection'):
        if self.selection and (x or y):
      self.begin_transaction(_("Align Objects"))
      try:
    try:
                    add_undo = self.add_undo
                    objects = self.selection.GetObjects()
        if reference == 'page':
      br = self.PageRect()
                    elif reference == 'lowermost':
                        br = objects[0].coord_rect
        else:
      br = self.selection.coord_rect
        for obj in objects:
      r = obj.coord_rect
      xoff = yoff = 0
      if x == 1:
          xoff = br.left - r.left
      elif x == 3:
          xoff = br.right - r.right
      elif x == 2:
          xoff = (br.left + br.right - r.left - r.right) / 2

      if y == 1:
          yoff = br.top - r.top
      elif y == 3:
          yoff = br.bottom - r.bottom
      elif y == 2:
          yoff = (br.top + br.bottom - r.top - r.bottom) / 2

      add_undo(obj.Translate(Point(xoff, yoff)))

        add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def AbutHorizontal(self):
  if len(self.selection) > 1:
      self.begin_transaction(_("Abut Horizontal"))
      try:
    try:
        pos = []
        for obj in self.selection.GetObjects():
      rect = obj.coord_rect
      pos.append((rect.left, rect.top,
            rect.right - rect.left, obj))
        pos.sort()
        undo = []
        start, top, width, ob = pos[0]
        next = start + width
        for left, top, width, obj in pos[1:]:
      off = Point(next - left, 0)
      self.add_undo(obj.Translate(off))
      next = next + width

        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def AbutVertical(self):
  if len(self.selection) > 1:
      self.begin_transaction(_("Abut Vertical"))
      try:
    try:
        pos = []
        for obj in self.selection.GetObjects():
      rect = obj.coord_rect
      pos.append((rect.top, -rect.left,
            rect.top - rect.bottom, obj))
        pos.sort()
        pos.reverse()
        undo = []
        start, left, height, ob = pos[0]
        next = start - height
        for top, left, height, obj in pos[1:]:
      off = Point(0, next - top)
      self.add_undo(obj.Translate(off))
      next = next - height

        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def FlipSelected(self, horizontal = 0, vertical = 0):
  if self.selection and (horizontal or vertical):
      self.begin_transaction()
      try:
    try:
        rect = self.selection.coord_rect
        if horizontal:
      xoff = rect.left + rect.right
      factx = -1
      text = _("Flip Horizontal")
        else:
      xoff = 0
      factx = 1
        if vertical:
      yoff = rect.top + rect.bottom
      facty = -1
      text = _("Flip Vertical")
        else:
      yoff = 0
      facty = 1
        if horizontal and vertical:
      text = _("Flip Both")
        trafo = Trafo(factx, 0, 0, facty, xoff, yoff)
        self.TransformSelected(trafo, text)
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    #
    #
    #

    def CallObjectMethod(self, aclass, description, methodname, *args):
  self.begin_transaction(description)
  try:
      try:
    undo = self.selection.CallObjectMethod(aclass, methodname,
                   args)
    if undo != NullUndo:
        self.add_undo(undo)
        self.add_undo(self.queue_edited())
        # force recomputation of selections rects:
        self.selection.ResetRectangle()
    else:
        # in case the handles have to be updated
        self.queue_selection()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def GetObjectMethod(self, aclass, method):
  return self.selection.GetObjectMethod(aclass, method)

    def CurrentObjectCompatible(self, aclass):
  obj = self.CurrentObject()
  if obj is not None:
      if aclass.is_Editor:
    return isinstance(obj, aclass.EditedClass)
      else:
    return isinstance(obj, aclass)
  return 0

    # XXX the following methods for blend groups, path text, clones and
    # bezier objects should perhaps be implemented in their respective
    # modules (and then somehow grafted onto the document class?)

    def CanBlend(self):
  info = self.selection.GetInfo()
  if len(info) == 2:
      path1, obj1 = info[0]
      path2, obj2 = info[1]
      if len(path1) == len(path2) + 1:
    return obj1.parent.is_Blend and 2
      if len(path1) + 1 == len(path2):
    return obj2.parent.is_Blend and 2
      return len(path1) == len(path2)
  return 0

    def Blend(self, steps):
  info = self.selection.GetInfo()
  path1, obj1 = info[0]
  path2, obj2 = info[1]
  if len(path1) == len(path2) + 1:
      if obj1.parent.is_Blend:
    del info[0]
      else:
    return
  elif len(path1) + 1 == len(path2):
      if obj2.parent.is_Blend:
    del info[1]
      else:
    return
  elif len(path1) != len(path2):
      return
  if steps >= 2:
      import blendgroup, blend
      self.begin_transaction(_("Blend"))
      try:
    try:
        self.add_undo(self.remove_objects(info))
        try:
      blendgrp, undo = blendgroup.CreateBlendGroup(obj1,obj2,
                     steps)
      self.add_undo(undo)
        except blend.MismatchError:
      warn(USER, _("I can't blend the selected objects"))
      # XXX: is there some other solution?:
      raise

        if len(info) == 2:
      select, undo_insert = self.insert(blendgrp,
                at = path1[1:],
                layer = path1[0])
      self.add_undo(undo_insert)
      self.__set_selection(select, SelectSet)
        else:
      self.SelectObject(blendgrp)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def CanCancelBlend(self):
  info = self.selection.GetInfo()
  return len(info) == 1 and info[0][-1].is_Blend

    def CancelBlend(self):
  if self.CanCancelBlend():
      self.begin_transaction(_("Cancel Blend"))
      try:
    try:
        info = self.selection.GetInfo()[0]
        self.add_undo(self.remove_selected())
        group = info[-1]
        objs = group.CancelEffect()
        info = info[0]
        layer = info[0]
        at = info[1:]
        select, undo_insert = self.insert(objs, at = at,
                  layer = layer)
        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    #
    #

    def CanCreatePathText(self):
  return CanCreatePathText(self.selection.GetObjects())

    def CreatePathText(self):
  if self.CanCreatePathText():
      self.begin_transaction(_("Create Path Text"))
      try:
    try:
        self.add_undo(self.remove_selected())
        object = CreatePathText(self.selection.GetObjects())

        select, undo_insert = self.insert(object)
        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    #
    #  Clone (under construction...)
    #

    def CanCreateClone(self):
  if len(self.selection) == 1:
      obj = self.selection.GetObjects()[0]
      return not obj.is_Compound
  return 0

    def CreateClone(self):
  if self.CanCreateClone():
      self.begin_transaction(_("Create Clone"))
      try:
    try:
        from clone import CreateClone
        object = self.selection.GetObjects()[0]
        clone, undo_clone = CreateClone(object)
        self.add_undo(undo_clone)
        select, undo_insert = self.insert(clone)
        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()


    #
    #  Bezier Curves
    #

    def CanCombineBeziers(self):
  if len(self.selection) > 1:
      can = 1
      for obj in self.selection.GetObjects():
    can = can and obj.is_Bezier
      return can
  return 0

    def CombineBeziers(self):
  if self.CanCombineBeziers():
      self.begin_transaction(_("Combine Beziers"))
      try:
    try:
        self.add_undo(self.remove_selected())
        objects = self.selection.GetObjects()
        combined = CombineBeziers(objects)
        select, undo_insert = self.insert(combined)
        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def CanSplitBeziers(self):
  return len(self.selection) == 1 \
         and self.selection.GetObjects()[0].is_Bezier

    def SplitBeziers(self):
  if self.CanSplitBeziers():
      self.begin_transaction(_("Split Beziers"))
      try:
    try:
        self.add_undo(self.remove_selected())
        info, bezier = self.selection.GetInfo()[0]
        objects = bezier.PathsAsObjects()
        select, undo_insert = self.insert(objects, at = info[1:],
                  layer =info[0])
        self.add_undo(undo_insert)
        self.__set_selection(select, SelectSet)
        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def CanConvertToCurve(self):
  return len(self.selection) == 1 \
         and self.selection.GetObjects()[0].is_curve

    def ConvertToCurve(self):
  if self.CanConvertToCurve():
      self.begin_transaction(_("Convert To Curve"))
      try:
    try:
                    selection = []
                    edited = 0
                    for info, object in self.selection.GetInfo():
                        if object.is_Bezier:
                            selection.append((info, object))
                        else:
                            bezier = object.AsBezier()
                            parent = object.parent
                            self.add_undo(parent.ReplaceChild(object, bezier))
                            selection.append((info, bezier))
                            edited = 1
                    self.__set_selection(selection, SelectSet)
                    if edited:
                        self.add_undo(self.queue_edited())
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    #
    #
    #

    def Layers(self):
  return self.layers[:]

    def NumLayers(self):
  return len(self.layers)

    def ActiveLayer(self):
  return self.active_layer

    def ActiveLayerIdx(self):
  if self.active_layer is None:
      return None
  return self.layers.index(self.active_layer)

    def SetActiveLayer(self, idx):
  if type(idx) == IntType:
      layer = self.layers[idx]
  else:
      layer = idx
  if not layer.Locked():
      self.active_layer = layer
  self.queue_layer(LAYER_ACTIVE)

    def LayerIndex(self, layer):
  return self.layers.index(layer)

    def update_active_layer(self):
  if self.active_layer is not None and self.active_layer.CanSelect():
      return
  self.find_active_layer()

    def find_active_layer(self, idx = None):
  if idx is not None:
      layer = self.layers[idx]
      if layer.CanSelect():
    self.SetActiveLayer(idx)
    return
  for layer in self.layers:
      if layer.CanSelect():
    self.SetActiveLayer(layer)
    return
  self.active_layer = None
  self.queue_layer(LAYER_ACTIVE)

    def deselect_layer(self, layer_idx):
  # Deselect all objects in layer given by layer_idx
  # Called when a layer is deleted or becomes locked
  sel = selinfo.list_to_tree(self.selection.GetInfo())
  for idx, info in sel:
      if idx == layer_idx:
    self.__set_selection(selinfo.tree_to_list([(idx, info)]),
             SelectSubtract)

    def SelectLayer(self, layer_idx, mode = SelectSet):
  # Select all children of the layer given by layer_idx
  self.begin_transaction(_("Select Layer"), clear_selection_rect = 0)
  try:
      try:
    layer = self.layers[layer_idx]
    info = self.augment_sel_info(layer.SelectAll(), layer_idx)
    self.__set_selection(info, mode)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def SetLayerState(self, layer_idx, visible, printable, locked, outlined):
  self.begin_transaction(_("Change Layer State"),
             clear_selection_rect = 0)
  try:
      try:
    layer = self.layers[layer_idx]
    self.add_undo(layer.SetState(visible, printable, locked,
               outlined))
    if not layer.CanSelect():
        # XXX: this depends on whether we're drawing visible or
        # printable layers
        self.deselect_layer(layer_idx)
        self.update_active_layer()
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def SetLayerColor(self, layer_idx, color):
  self.begin_transaction(_("Set Layer Outline Color"),
             clear_selection_rect = 0)
  try:
      try:
    layer = self.layers[layer_idx]
    self.add_undo(layer.SetOutlineColor(color))
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def SetLayerName(self, idx, name):
  self.begin_transaction(_("Rename Layer"), clear_selection_rect = 0)
  try:
      try:
    layer = self.layers[idx]
    self.add_undo(layer.SetName(name))
    self.add_undo(self.queue_layer())
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def AppendLayer(self, *args, **kw_args):
  self.begin_transaction(_("Append Layer"),clear_selection_rect = 0)
  try:
      try:
    layer = apply(SketchDocument.AppendLayer, (self,) + args,
            kw_args)
    self.add_undo((self._remove_layer, len(self.layers) - 1))
    self.queue_layer(LAYER_ORDER, layer)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()
  return layer

    def NewLayer(self):
  self.begin_transaction(_("New Layer"), clear_selection_rect = 0)
  try:
      try:
    self.AppendLayer()
    self.active_layer = self.layers[-1]
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def _move_layer_up(self, idx):
  # XXX: exception handling
  if idx < len(self.layers) - 1:
      # move the layer...
      layer = self.layers[idx]
      del self.layers[idx]
      self.layers.insert(idx + 1, layer)
      other = self.layers[idx]
      # ... and adjust the selection
      sel = self.selection.GetInfoTree()
      newsel = []
      for i, info in sel:
    if i == idx:
        i = idx + 1
    elif i == idx + 1:
        i = idx
    newsel.append((i, info))
      self.__set_selection(selinfo.tree_to_list(newsel), SelectSet)
      self.queue_layer(LAYER_ORDER, layer, other)
      return (self._move_layer_down, idx + 1)
  return None

    def _move_layer_down(self, idx):
  # XXX: exception handling
  if idx > 0:
      # move the layer...
      layer = self.layers[idx]
      del self.layers[idx]
      self.layers.insert(idx - 1, layer)
      other = self.layers[idx]
      # ...and adjust the selection
      sel = self.selection.GetInfoTree()
      newsel = []
      for i, info in sel:
    if i == idx:
        i = idx - 1
    elif i == idx - 1:
        i = idx
    newsel.append((i, info))
      self.__set_selection(selinfo.tree_to_list(newsel), SelectSet)
      self.queue_layer(LAYER_ORDER, layer, other)
      return (self._move_layer_up, idx - 1)
  return NullUndo

    def MoveLayerUp(self, idx):
  if idx < len(self.layers) - 1:
      self.begin_transaction(_("Move Layer Up"), clear_selection_rect=0)
      try:
    try:
        self.add_undo(self._move_layer_up(idx))
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def MoveLayerDown(self, idx):
  if idx > 0:
      self.begin_transaction(_("Move Layer Down"),clear_selection_rect=0)
      try:
    try:
        self.add_undo(self._move_layer_down(idx))
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def _remove_layer(self, idx):
  layer = self.layers[idx]
  del self.layers[idx]
  if layer is self.active_layer:
      if idx < len(self.layers):
    self.find_active_layer(idx)
      else:
    self.find_active_layer()
  sel = self.selection.GetInfoTree()
  newsel = []
  for i, info in sel:
      if i == idx:
    continue
      elif i > idx:
    i = i - 1
      newsel.append((i, info))
  self.__set_selection(selinfo.tree_to_list(newsel), SelectSet)

  self.queue_layer(LAYER_ORDER, layer)
  return (self._insert_layer, idx, layer)

    def _insert_layer(self, idx, layer):
  self.layers.insert(idx, layer)
  layer.SetDocument(self)
  self.queue_layer(LAYER_ORDER, layer)
  return (self._remove_layer, idx)

    def CanDeleteLayer(self, idx):
  return (len(self.layers) > 3 and not self.layers[idx].is_SpecialLayer)

    def DeleteLayer(self, idx):
  if self.CanDeleteLayer(idx):
      self.begin_transaction(_("Delete Layer"), clear_selection_rect = 0)
      try:
    try:
        self.add_undo(self._remove_layer(idx))
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()



    #
    #  Style management
    #

    def queue_style(self):
  self.queue_message(STYLE)
  return (self.queue_style,)

    def init_styles(self):
  self.styles = UndoDict()
  self.auto_assign_styles = 1
  self.asked_about = {}

    def destroy_styles(self):
  for style in self.styles.values():
      style.Destroy()
  self.styles = None

    def get_dynamic_style(self, name):
  return self.styles[name]

    def GetDynamicStyle(self, name):
  try:
      return self.styles[name]
  except KeyError:
      return None

    def Styles(self):
  return self.styles.values()

    def write_styles(self, file):
  for style in self.styles.values():
      style.SaveToFile(file)

    def load_AddStyle(self, style):
  self.styles.SetItem(style.Name(), style)

    def add_dynamic_style(self, name, style):
  if style:
      style = style.AsDynamicStyle()
      self.add_undo(self.styles.SetItem(name, style))
      self.add_undo(self.queue_style())
      return style

    def update_style_dependencies(self, style):
  def update(obj, style = style):
      obj.ObjectChanged(style)
  self.WalkHierarchy(update)
  return (self.update_style_dependencies, style)

    def UpdateDynamicStyleSel(self):
        if len(self.selection) == 1:
            self.begin_transaction(_("Update Style"), clear_selection_rect = 0)
            try:
                try:
                    properties = self.CurrentProperties()
                    # XXX hack
                    for style in properties.stack:
                        if style.is_dynamic:
                            break
                    else:
                        return
                    undo = []
                    # we used to use dir(style) to get at the list of
                    # instance variables of style. In Python 2.2 dir
                    # returns class attributes as well. So we use
                    # __dict__.keys() now.
                    for attr in style.__dict__.keys():
                        if attr not in ('name', 'is_dynamic'):
                            undo.append(style.SetProperty(attr,
                                                          getattr(properties,
                                                                  attr)))
                    undo.append(properties.AddStyle(style))
                    undo = (UndoAfter, CreateListUndo(undo),
                            self.update_style_dependencies(style))
                    self.add_undo(undo)
                except:
                    self.abort_transaction()
            finally:
                self.end_transaction()

    def CanCreateStyle(self):
  if len(self.selection) == 1:
      obj = self.selection.GetObjects()[0]
      return obj.has_fill or obj.has_line
  return 0

    def CreateStyleFromSelection(self, name, which_properties):
  if self.CanCreateStyle():
      properties = self.CurrentProperties()
      style = properties.CreateStyle(which_properties)
      self.begin_transaction(_("Create Style %s") % name,
           clear_selection_rect = 0)
      try:
    try:
        style = self.add_dynamic_style(name, style)
        self.AddStyle(style)
    except:
        self.abort_transaction()
      finally:
    self.end_transaction()

    def RemoveDynamicStyle(self, name):
  style = self.GetDynamicStyle(name)
  if not style:
      # style does not exist. XXX: raise an exception ?
      return
  self.begin_transaction(_("Remove Style %s") % name,
             clear_selection_rect = 0)
  try:
      try:
    def remove(obj, style = style, add_undo = self.add_undo):
        add_undo(obj.ObjectRemoved(style))
    self.WalkHierarchy(remove)
    self.add_undo(self.styles.DelItem(name))
    self.add_undo(self.queue_style())
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def GetStyleNames(self):
  names = self.styles.keys()
  names.sort()
  return names

    #
    #  Layout
    #

    def queue_layout(self):
  self.queue_message(LAYOUT)
  return (self.queue_layout,)

    def init_layout(self):
  self.page_layout = pagelayout.PageLayout()

    def Layout(self):
  return self.page_layout

    def PageSize(self):
  return (self.page_layout.Width(), self.page_layout.Height())

    def PageRect(self):
  w, h = self.page_layout.Size()
  return Rect(0, 0, w, h)

    def load_SetLayout(self, layout):
  self.page_layout = layout

    def __set_page_layout(self, layout):
  undo = (self.__set_page_layout, self.page_layout)
  self.page_layout = layout
  self.queue_layout()
  return undo

    def SetLayout(self, layout):
  self.begin_transaction(clear_selection_rect = 0)
  try:
      try:
    undo = self.__set_page_layout(layout)
    self.add_undo(_("Change Page Layout"), undo)
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    #
    #  Grid Settings
    #

    def queue_grid(self):
  self.queue_message(GRID)
  return (self.queue_grid,)

    def SetGridGeometry(self, geometry):
  self.begin_transaction(_("Set Grid Geometry"))
  try:
      try:
    self.add_undo(self.snap_grid.SetGeometry(geometry))
    self.add_undo(self.queue_grid())
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def GridGeometry(self):
        return self.snap_grid.Geometry()

    def GridLayerChanged(self):
  return self.queue_grid()


    #
    #  Guide Lines
    #

    def add_guide_line(self, line):
  self.begin_transaction(_("Add Guide Line"), clear_selection_rect = 0)
  try:
      try:
    sel, undo = self.guide_layer.Insert(line, 0)
    self.add_undo(undo)
    self.add_undo(self.AddClearRect(line.get_clear_rect()))
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def AddGuideLine(self, point, horizontal):
  self.add_guide_line(guide.GuideLine(point, horizontal))

    def RemoveGuideLine(self, line):
  if not line.parent is self.guide_layer or not line.is_GuideLine:
      return
  self.begin_transaction(_("Delete Guide Line"),
             clear_selection_rect = 0)
  try:
      try:
    self.add_undo(self.remove_objects([line.SelectionInfo()]))
    self.add_undo(self.AddClearRect(line.get_clear_rect()))
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def MoveGuideLine(self, line, point):
  if not line.parent is self.guide_layer or not line.is_GuideLine:
      return
  self.begin_transaction(_("Move Guide Line"), clear_selection_rect = 0)
  try:
      try:
    self.add_undo(self.AddClearRect(line.get_clear_rect()))
    self.add_undo(line.SetPoint(point))
    self.add_undo(self.AddClearRect(line.get_clear_rect()))
    self.add_undo(self.GuideLayerChanged(line.parent))
      except:
    self.abort_transaction()
  finally:
      self.end_transaction()

    def GuideLayerChanged(self, layer):
  self.queue_message(GUIDE_LINES, layer)
  return (self.GuideLayerChanged, layer)

    def GuideLines(self):
  return self.guide_layer.GuideLines()


    #
    #
    def as_group(self):
  for name in self.GetStyleNames():
      self.RemoveDynamicStyle(name)
  layers = self.layers
  self.layers = []
  groups = []
  for layer in layers:
      if not layer.is_SpecialLayer:
    layer.UntieFromDocument()
    objects = layer.GetObjects()
    layer.objects = []
    if objects:
        groups.append(Group(objects))
      else:
    layer.Destroy()
  if groups:
      return Group(groups)
  else:
      return None

www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.