# Sketch - A Python-based interactive drawing program
# Copyright (C) 1996, 1997, 1998, 1999 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
#
# The layer classes.
#
from types import TupleType
from Sketch.const import LAYER_STATE,LAYER_COLOR
from Sketch import _,NullUndo,EmptyRect,InfinityRect,Point,config,_sketch
import color
import selinfo
from compound import EditableCompound
class Layer(EditableCompound):
can_be_empty = 1
is_Layer = 1
is_SpecialLayer = 0
is_GridLayer = 0
is_GuideLayer = 0
def __init__(self, name = _("New Layer"),
visible = 1, printable = 1, locked = 0,
outlined = 0, outline_color = (0, 0, 0)):
EditableCompound.__init__(self, [])
self.name = name
self.visible = visible
self.printable = printable
self.locked = locked
self.outlined = outlined
if type(outline_color) == TupleType:
self.outline_color = apply(color.CreateRGBColor, outline_color)
else:
self.outline_color = outline_color
def Draw(self, device, rect = None):
# Draw all objects on the device device. RECT, if provided,
# gives the bounding rect of the region to be drawn allowing to
# optimize the redisplay by drawing only those objects that
# overlap with this rect.
if device.draw_visible and self.visible \
or device.draw_printable and self.printable:
outlined = self.outlined or device.IsOutlineActive()
if outlined:
device.StartOutlineMode(self.outline_color)
EditableCompound.DrawShape(self, device, rect)
if outlined:
device.EndOutlineMode()
def SelectSubobject(self, p, rect, device, path = (), *rest):
if not self.CanSelect():
return None
if self.outlined:
device.StartOutlineMode()
try:
result = EditableCompound.SelectSubobject(self, p, rect, device,
path)
finally:
if self.outlined:
device.EndOutlineMode()
return result
def SelectRect(self, rect):
if not self.CanSelect():
return []
test = rect.contains_rect
build_info = selinfo.build_info
selected = []
objects = self.objects
for idx in range(len(objects)):
obj = objects[idx]
if test(obj.bounding_rect):
selected.append(build_info(idx, obj))
return selected
def SelectAll(self):
if self.CanSelect():
return selinfo.select_all(self.objects)
else:
return []
def SelectionInfo(self, child, cache = None):
info = selinfo.build_info(_sketch.IdIndex(self.objects, child), child)
return selinfo.prepend_idx(self.document.LayerIndex(self), info)
def PickObject(self, p, rect, device):
if not self.visible:
return None
if self.outlined:
device.StartOutlineMode()
result = EditableCompound.PickObject(self, p, rect, device)
if self.outlined:
device.EndOutlineMode()
return result
def SetName(self, name):
undo = (self.SetName, self.name)
self.name = name
return undo
def Name(self):
return self.name
def NumObjects(self):
return len(self.objects)
def Visible(self):
return self.visible
def Printable(self):
return self.printable
def Locked(self):
return self.locked
def CanSelect(self):
return not self.locked and self.visible
def get_state(self):
return (self.visible, self.printable, self.locked, self.outlined)
def SetState(self, visible, printable, locked, outlined):
# set the layer state. Return undo info.
#
# Side Effect:
# Queue a LAYER message with parameter LAYER_STATE + tuple.
# The tuple has the form
#
# (layer, visible_changed, printable_changed, outline_changed)
#
# We assume here that the receiver (usually SketchCanvas or
# SketchView) uses this to determine whether to repaint parts of
# the screen. If the receiver shows only visible layers and
# allows outline, it should use the following expression to
# determine whether to redraw or not:
#
# layer.NumObjects() and (visible_changed or
# (outlined_changed and layer.Visible()))
#
# If you only show printable layers:
#
# layer.NumObjects() and printable_changed
#
# (in that case outline mode should be ignored as it is only
# meant for quicker or clearer display while editing)
#
# The bounding rect of the now invalid region is
# layer.bounding_rect
oldstate = self.get_state()
visible_changed = self.visible != visible
self.visible = visible
printable_changed = self.printable != printable
self.printable = printable
locked_changed = self.locked != locked
self.locked = locked
outlined_changed = self.outlined != outlined
self.outlined = outlined
if oldstate != self.get_state():
undo = (self.SetState,) + oldstate
visibility = (self, visible_changed, printable_changed,
outlined_changed)
if self.document is not None:
self.document.queue_layer(LAYER_STATE, visibility)
if locked_changed:
self.document.update_active_layer()
return undo
return NullUndo
def SetOutlineColor(self, color):
undo = (self.SetOutlineColor, self.outline_color)
self.outline_color = color
if self.document is not None:
self.document.queue_layer(LAYER_COLOR, self)
return undo
def OutlineColor(self):
return self.outline_color
def Outlined(self):
return self.outlined
def SaveToFile(self, file):
file.BeginLayer(self.name, self.visible, self.printable, self.locked,
self.outlined, self.outline_color)
for obj in self.objects:
obj.SaveToFile(file)
file.EndLayer()
class SpecialLayer(Layer):
is_SpecialLayer = 1
def __none(self, *args):
return None
SelectSubobject = __none
PickObject = __none
def SelectRect(self, *rect):
return []
SelectAll = SelectRect
class GuideLayer(SpecialLayer):
is_GuideLayer = 1
def __init__(self, name = _("Guides"), visible = 1, printable = 0,
locked = 0, outlined = 1, outline_color = None):
if outline_color is None:
outline_color = config.preferences.guide_color
SpecialLayer.__init__(self, name, visible, 0, locked, 1,
outline_color)
def SetState(self, visible, printable, locked, outlined):
return SpecialLayer.SetState(self, visible, 0, locked, outlined)
def Draw(self, device, rect = None):
if device.draw_visible and self.visible \
or device.draw_printable and self.printable:
device.StartOutlineMode(self.outline_color)
SpecialLayer.DrawShape(self, device)
device.EndOutlineMode()
def SelectSubobject(self, p, rect, device, path = (), *rest):
if not self.CanSelect():
return None
device.StartOutlineMode()
try:
objects = self.objects
for obj_idx in range(len(objects) - 1, -1, -1):
obj = objects[obj_idx]
if obj.Hit(p, rect, device):
result = obj.SelectSubobject(p, rect, device)
return selinfo.prepend_idx(obj_idx, result)
return None
finally:
device.EndOutlineMode()
def SelectRect(self, rect):
if not self.CanSelect():
return []
test = rect.contains_rect
build_info = selinfo.build_info
selected = []
objects = self.objects
for idx in range(len(objects)):
obj = objects[idx]
if not obj.is_GuideLine and test(obj.bounding_rect):
selected.append(build_info(idx, obj))
return selected
def SelectAll(self):
return self.SelectRect(InfinityRect)
def compute_rects(self):
if self.objects:
self.bounding_rect = self.coord_rect = InfinityRect
else:
self.bounding_rect = self.coord_rect = EmptyRect
def SaveToFile(self, file):
file.BeginGuideLayer(self.name, self.visible, self.printable,
self.locked, self.outlined, self.outline_color)
for obj in self.objects:
obj.SaveToFile(file)
file.EndGuideLayer()
def Snap(self, p):
default = (1e100, p)
horizontal = [default]
vertical = [default]
result = [default]
for obj in self.objects:
dist, snapped = obj.Snap(p)
if type(snapped) == TupleType:
if snapped[0] is None:
horizontal.append((dist, snapped))
else:
vertical.append((dist, snapped))
else:
result.append((dist, snapped))
return min(horizontal), min(vertical), min(result)
def GuideLines(self):
result = self.objects[:]
for idx in range(len(result) - 1, -1, -1):
if not result[idx].is_GuideLine:
del result[idx]
return result
def issue_changed(self):
Layer.issue_changed(self)
if self.document is not None:
self.document.GuideLayerChanged(self)
class GridLayer(SpecialLayer):
is_GridLayer = 1
geometry = (0, 0, 20, 20)
def __init__(self, geometry = None, visible = None, outline_color = None,
name = _("Grid")):
# The grid is locked, outlined and not printable
if geometry is None:
geometry = config.preferences.grid_geometry
if visible is None:
visible = config.preferences.grid_visible
if outline_color is None:
outline_color = config.preferences.grid_color
SpecialLayer.__init__(self, name, visible, 0, 1, 1, outline_color)
if len(geometry) == 2:
self.geometry = (0, 0) + geometry
elif len(geometry) == 4:
self.geometry = geometry
else:
raise ValueError, "grid tuple must have length 2 or 4"
def Draw(self, device, rect = None):
if device.draw_visible and self.visible \
or device.draw_printable and self.printable:
device.StartOutlineMode(self.outline_color)
xorg, yorg, xwidth, ywidth = self.geometry
device.DrawGrid(xorg, yorg, xwidth, ywidth, rect)
device.EndOutlineMode()
def update_rects(self):
self.bounding_rect = self.coord_rect = InfinityRect
def SaveToFile(self, file):
file.BeginGridLayer(self.geometry, self.visible, self.outline_color,
self.name)
file.EndGridLayer()
def Snap(self, p):
xorg, yorg, xwidth, ywidth = self.geometry
sx = round((p.x - xorg) / xwidth) * xwidth + xorg
sy = round((p.y - yorg) / ywidth) * ywidth + yorg
result = Point(sx, sy)
return (abs(result - p), result)
def SetState(self, visible, printable, locked, outlined):
return SpecialLayer.SetState(self, visible, 0, 1, 1)
def Geometry(self):
return self.geometry
def SetGeometry(self, geometry):
undo = (self.SetGeometry, self.geometry)
self.geometry = geometry
if self.document:
# a hack...
self.document.queue_layer(LAYER_COLOR, self)
return undo
|