# 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 app.conf.const import LAYER_STATE, LAYER_COLOR from app 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 = config.preferences.layer_color): EditableCompound.__init__(self, []) self.name = name self.visible = visible self.printable = printable self.locked = locked self.outlined = outlined if type(outline_color) == TupleType: if len(outline_color)==3: outline_color=('RGB',outline_color[0],outline_color[1],outline_color[2]) self.outline_color = apply(color.ParseSKColor, 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