# Sketch - A Python-based interactive drawing program
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2004 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
#
# SketchCanvas
#
# Displays the contents of a document and accepts user input in the form
# of mouse and keyboard events.
#
import string
from types import TupleType
from Sketch.Lib.util import Empty,format
from Sketch import _,InvertingDevice,HitTestDevice,StandardColors,Point
from Sketch.Graphics.selection import SelectionRectangle
from Sketch import const,SketchInternalError,SelectionMode,EditMode
from Sketch.config import preferences
from Sketch.warn import pdebug,warn,INTERNAL
from Sketch.const import STATE,MODE,SELECTION,VIEW,POSITION,EDITED,\
LAYER_STATE, LAYER_ACTIVE, CURRENTINFO, CHANGED, \
SelectSet, SelectAdd, SelectSubtract, \
SelectSubobjects, SelectDrag, SelectGuide
import Sketch
import command
import skpixmaps
pixmaps = skpixmaps.PixmapTk
from tkext import MakeCommand,UpdatedMenu
from cursorstack import CursorStack
from view import SketchView
from modes import MajorMode,TemporaryMode,WidgetWithModes,noop
from converters import converters
constraint_keysyms = {'Control_L' : const.ControlMask,
'Control_R' : const.ControlMask,
'Shift_L' : const.ShiftMask,
'Shift_R' : const.ShiftMask}
command_list = []
def AddCmd(name, menu_name, method_name = None, subscribe_to = STATE, **kw):
# use name both as name and method name
kw['menu_name'] = menu_name
kw['subscribe_to'] = subscribe_to
if not method_name:
method_name = name
cmd = apply(command.CommandClass, (name, method_name), kw)
command_list.append(cmd)
def AddSelCmd(*args, **kw):
if not kw.has_key('sensitive_cb'):
kw['sensitive_cb'] = ('document', 'HasSelection')
if not kw.has_key('subscribe_to'):
kw['subscribe_to'] = SELECTION
apply(AddCmd, args, kw)
def AddModeCmd(*args, **kw):
if not kw.has_key('value_cb'):
kw['value_cb'] = 'ModeName'
if not kw.has_key('subscribe_to'):
kw['subscribe_to'] = MODE
if not kw.has_key('is_check'):
kw['is_check'] = 1
apply(AddCmd, args, kw)
class CanvasError(SketchInternalError):
pass
# stop codes
stop_continue = 0
stop_regular = 1
def selection_type(state):
state = state & const.AllowedModifierMask
if state == const.AddSelectionMask:
type = SelectAdd
elif state == const.SubtractSelectionMask:
type = SelectSubtract
elif state == const.SubobjectSelectionMask:
type = SelectSubobjects
else:
type = SelectSet
return type
class SketchCanvas(SketchView, CursorStack, WidgetWithModes):
document = None
commands = None
context_menu_items = ['CreateFillStyleDialog',
'FillSolid',
'FillNone',
None,
'CreateLineStyleDialog',
'LineNone',
None,
'CreateFontDialog',
None,
'CreateStyleFromSelection',
None,
'FitSelectedToWindow',
'FitPageToWindow']
def __init__(self, master=None, toplevel = None, main_window = None,
document = None, **kw):
self.init_handles()
self.init_modes()
self.init_cross_hairs()
WidgetWithModes.__init__(self)
kw['show_visible'] = 1
kw['show_printable'] = 0
self.snap_to_object = 0
self.snap_to_guide = 0
self.snap_correction_rect = 1
self.snap_move_relative = 0 #1
apply(SketchView.__init__, (self, master, toplevel, document), kw)
CursorStack.__init__(self, const.CurStd, self.set_handle_cursor)
self.main_window = main_window
self.current_pos = Point(0, 0)
self.current_info_text = None
self.expect_release_event = 0
self.ignore_key_press_events = 0
self.start_drag = 0
self.start_pos = None
self.dragging = 0 # true if dragging with left button down
self.current = None # currently dragged/edited object
self.correction = Point(0, 0)
self.create_creator = None # Class of object to create
# None while in select/edit mode
self.create_type = ''
self.snap_to_grid = 0
self.snap_points = ()
self.init_bindings()
self.SelectionMode()
self.create_commands()
self.context_commands = None
preferences.Subscribe(CHANGED, self.preference_changed)
def init_modes(self):
self.modes = m = Empty()
m.selection_mode = MajorMode(self.selection_enter,
self.selection_down, self.drag_mouse,
self.selection_up, noop,
self.selection_cancel,
start_drag = self.selection_start_drag,
name = 'Select',
text = _("Select"))
m.edit_mode = MajorMode(self.edit_enter,
self.edit_down, self.drag_mouse, self.edit_up,
noop, self.edit_cancel,
start_drag = self.edit_start_drag,
name = 'Edit',
text = _("Edit"))
m.creation_mode = MajorMode(self.creation_enter,
self.creation_begin, self.drag_to,
self.creation_end, self.creation_exit,
self.creation_cancel,
name = 'Create',
text = _("Create"))
m.zoom_mode = TemporaryMode(self.zoom_enter, self.zoom_begin,
self.drag_to, self.zoom_end,
noop, self.zoom_cancel,
name = 'Zoom',
text = _("Zoom Area"))
m.pick_mode = TemporaryMode(self.pick_enter, self.pick_begin, noop,
self.pick_end, noop, self.pick_cancel,
name = 'Pick',
text = _("Pick Object"))
m.place_mode = TemporaryMode(self.place_enter, self.place_begin,
self.place_drag, self.place_end,
noop, self.place_cancel,
name = 'Place',
text = _("Place Object"))
def init_bindings(self):
self.bind('<ButtonPress>', self.ButtonPressEvent)
self.bind('<Motion>', self.PointerMotionEvent)
self.bind('<ButtonRelease>', self.ButtonReleaseEvent)
self.bind('<KeyPress>', self.KeyPressEvent)
self.bind('<KeyRelease>', self.KeyReleaseEvent)
self.bind('<Leave>', self.LeaveEvent)
self.bind('<Enter>', self.EnterEvent)
self.last_event = None
scroll_timer = None
def start_auto_scroll(self):
intervall = preferences.autoscroll_interval
if intervall:
self.scroll_timer = self.after(intervall, self.auto_scroll)
def cancel_auto_scroll(self):
if self.scroll_timer is not None:
self.after_cancel(self.scroll_timer)
def auto_scroll(self):
x, y, state = self.tkwin.QueryPointer()[4:7]
if state & const.Button1Mask:
amount = preferences.autoscroll_amount
self.begin_transaction()
if x < 0:
self.ScrollXUnits(-amount)
elif x > self.tkwin.width:
self.ScrollXUnits(amount)
if y < 0:
self.ScrollYUnits(-amount)
elif y > self.tkwin.height:
self.ScrollYUnits(amount)
self.start_auto_scroll()
self.handle_update_pending = 0
self.end_transaction()
def LeaveEvent(self, event):
if event.state & const.Button1Mask:
self.start_auto_scroll()
self.hide_crosshairs()
self.crosshairs_visible = 0
def EnterEvent(self, event):
self.cancel_auto_scroll()
self.crosshairs_visible = 1
self.show_crosshairs()
def create_obj_cmds(self, commands):
cmds = command.Commands()
keymap = command.Keymap()
for cmd_class in commands:
cmd = cmd_class.InstantiateFor(self)
setattr(cmds, cmd.name, cmd)
keymap.AddCommand(cmd)
return cmds, keymap
def create_commands(self):
cmds = command.Commands()
keymap = command.Keymap()
for cmd_class in command_list:
cmd = cmd_class.InstantiateFor(self)
setattr(cmds, cmd.name, cmd)
keymap.AddCommand(cmd)
self.keymap = keymap
class_maps = {}
classes = Sketch.command_classes
for aclass in classes:
name = aclass.__name__
if aclass.is_Editor:
#name = aclass.EditedClass.__name__
commands = aclass.commands + aclass.EditedClass.commands
else:
commands = aclass.commands
ocmds, keymap = self.create_obj_cmds(commands)
setattr(cmds, name, ocmds)
class_maps[name] = keymap
if self.context_menu_items[-1] != None:
# insert a separator if necessary
self.context_menu_items.append(None)
self.context_menu_items = self.context_menu_items \
+ list(aclass.context_commands)
self.commands = cmds
self.object_keymap = None
self.class_maps = class_maps
def find_map(self, classes):
maps = self.class_maps
result = None
for c in classes:
map = maps.get(c.__name__)
if map is not None:
result = map
break
map = self.find_map(c.__bases__)
if map is not None:
result = map
break
return result
def update_object_keymap(self):
self.object_keymap = None
obj = self.document.CurrentObject()
if obj is not None:
if self.document.selection.is_EditSelection:
# XXX very ugly hack
cls = self.document.selection.editor.editor.__class__
else:
cls = obj.__class__
self.object_keymap = self.find_map((cls,))
def UpdateCommands(self):
# currently needed to ensure that all buttons have correct
# sensitivity at startup
self.commands.Update()
def init_gcs(self):
# XXX: integrate with SketchView
self.gc.init_gc(self.tkwin, graphics_exposures = 1)
self.invgc = InvertingDevice()
self.invgc.init_gc(self.tkwin)
self.hitgc = HitTestDevice()
self.hitgc.init_gc(self.tkwin)
#self.tk.call(self._w, 'motionhints')
self.draw_handle_funcs = [self.invgc.DrawRectHandle,
self.invgc.DrawRectHandle,
self.invgc.DrawSmallRectHandle,
self.invgc.DrawSmallRectHandle,
self.invgc.DrawCircleHandle,
self.invgc.DrawCircleHandle,
self.invgc.DrawSmallCircleHandle,
self.invgc.DrawSmallCircleHandle,
self.invgc.DrawSmallRectHandleList,
self.invgc.DrawHandleLine,
self.invgc.DrawPixmapHandle,
self.invgc.DrawCaretHandle]
self.gcs_initialized = 1
self.FitPageToWindow(save_viewport = 0)
self.set_gc_transforms()
def set_gc_transforms(self):
SketchView.set_gc_transforms(self)
if self.gcs_initialized:
self.invgc.SetViewportTransform(self.scale, self.doc_to_win,
self.win_to_doc)
self.hitgc.SetViewportTransform(self.scale, self.doc_to_win,
self.win_to_doc)
#
# Channels
#
def issue_selection(self):
self.queue_message(SELECTION)
def issue_mode(self):
self.queue_message(MODE)
def queue_update_handles(self):
self.handle_update_pending = 1
def init_transactions(self):
SketchView.init_transactions(self)
self.handle_update_pending = 0
def end_transaction(self):
SketchView.end_transaction(self)
if self.transaction == 0:
if self.handle_update_pending:
self.update_handles()
self.handle_update_pending = 0
def check_transaction(self):
# for debugging, check whether a transaction is active
if not self.transaction:
pdebug(None, 'Warning: no transaction')
def call_document_method(self, method, *args, **kwargs):
method = getattr(self.document, method)
apply(method, args, kwargs)
#
# receivers for channels
#
def preference_changed(self, attr, value):
if attr == 'default_unit':
self.selection_info_text = None
self.issue(CURRENTINFO)
def selection_changed(self):
self.selection_info_text = None
self.issue_selection()
self.update_handles()
self.update_object_keymap()
if self.gcs_initialized:
x, y = self.tkwin.QueryPointer()[4:6]
self.set_handle_cursor(x, y)
def doc_was_edited(self, *rest):
self.selection_info_text = None
self.update_snap_points()
self.update_handles()
self.queue_message(EDITED)
# extend the method of SketchView. SketchView subscribes this.
def layer_changed(self, *args):
if args:
recompute = 0
if args[0] == LAYER_STATE:
layer, visible_changed, printable_changed, outlined_changed \
= args[1]
if layer.NumObjects():
if ((self.show_printable and printable_changed)
or (self.show_visible and visible_changed)):
recompute = 1
elif args[0] == LAYER_ACTIVE:
if (self.mode is self.modes.creation_mode
and self.document.ActiveLayer() is None):
self.mode.cancel()
else:
self.issue_mode()
if recompute:
self.update_snap_points()
apply(SketchView.layer_changed, (self,) + args)
#
# Widget Methods (Redraw, ... )
#
def RedrawMethod(self, region = None):
#self.hide_handles()
#print 'RedrawMethod', region
if hasattr(preferences, 'profile_redraw'):
import profile
warn(INTERNAL, 'profiling...')
prof = profile.Profile()
prof.runctx('region = SketchView.RedrawMethod(self, region)',
globals(), locals())
prof.dump_stats('/tmp/redraw.prof')
warn(INTERNAL, 'profiling... (done)')
del preferences.profile_redraw
else:
region = SketchView.RedrawMethod(self, region)
# draw the handles
self.invgc.InitClip()
if region:
self.invgc.PushClip()
self.invgc.ClipRegion(region)
if self.current is not None:
self.current.DrawDragged(self.invgc, 0)
else:
self.show_handles(1)
self.show_crosshairs(1)
if region:
self.invgc.PopClip()
#self.show_handles()
# The sync helps to avoid the scroll bug. Clicking once into the
# scroll bar to scroll by a page could scroll twice for a
# complex drawing.
self.tkwin.Sync()
#
# Event handler
#
def ButtonPressEvent(self, event):
# handle button press event
self.begin_transaction()
self.expect_release_event = 1
self.last_event = event
try:
self.ignore_key_press_events = 1
button = event.num
if button == const.Button1:
p = self.WinToDoc(event.x, event.y)
self.mode.button_down(p, button, event.state)
elif button == const.ContextButton:
self.popup_context_menu(event.x_root, event.y_root,
button, event.state)
self.ignore_key_press_events = 0
elif button == const.Button4:
self.ScrollYUnits(-3)
elif button == const.Button5:
self.ScrollYUnits(3)
finally:
self.end_transaction()
def PointerMotionEvent(self, event):
# handle Motion events
#event.button = event.num
#event.x, event.y = self.tkwin.QueryPointer()[4:6]
self.last_event = event
p = self.WinToDoc(event.x, event.y)
self.set_current_pos(p, snap = 1)
if self.start_drag or self.dragging:
self.mode.mouse_move(p, event.state)
self.update_current_info_text()
else:
if self.cursor_function:
self.cursor_function(event.x, event.y)
self.hide_crosshairs()
self.show_crosshairs()
def ButtonReleaseEvent(self, event):
self.grab_release()
self.cancel_auto_scroll()
if not self.expect_release_event:
return
self.last_event = event
self.begin_transaction()
try:
p = self.WinToDoc(event.x, event.y)
event.button = event.num
if event.button == const.Button1:
self.mode.button_up(p, event.button, event.state)
self.dragging = 0
elif event.button == const.Button2:
self.mode.button_up(p, event.button, event.state,
force_stop = stop_regular)
self.dragging = 0
finally:
if self.mode.isTemporaryMode:
self.exit_temporary_mode()
self.ignore_key_press_events = 0
self.expect_release_event = 0
self.end_transaction()
def MapKeystroke(self, stroke):
if self.object_keymap:
cmd = self.object_keymap.MapKeystroke(stroke)
if cmd:
return cmd
cmd = self.keymap.MapKeystroke(stroke)
if cmd:
return cmd
return self.main_window.MapKeystroke(stroke)
def KeyPressEvent(self, event):
self.begin_transaction()
try:
state = event.state
sym = event.keysym
char = event.char
try:
char = self.tk.utf8_to_latin1(char)
except AttributeError:
# we're using Python's own _tkinter. FIXME: we should
# handle this case properly, bit it only happens with
# python <= 1.5.1, so it shouldn't be much of a problem
pass
# key events should probably be also handled by the modes
if sym == 'Escape':
self.cancel_current_mode()
return
mask = constraint_keysyms.get(sym, 0)
if mask:
# generate a `fake' motion event if a modifier changed. This is
# done to reflect changes in the constraints during a drag
# immediately.
# modify the state, because it reflects the state before the
# key press
event.state = event.state ^ mask
self.PointerMotionEvent(event)
if self.ignore_key_press_events:
return
if char and ord(char) < 32:
char = ''
# build the modifier part of the key for the keymap
modm = state & const.MetaMask and 'M-' or ''
modc = state & const.ControlMask and 'C-' or ''
mods = state & const.ShiftMask and 'S-' or ''
if char:
mod = modm + modc
else:
mod = modm + modc + mods
cmd = None
if char:
# the key produced a normal character. Try the binding
# for this first.
stroke = mod + char
cmd = self.MapKeystroke(stroke)
if not cmd and char != sym:
# there was no character or no entry in the keymap. Try
# the keysym next.
#
# XXX: The current implementation allows bindings for
# the Shift- and Control-keys, etc.
stroke = mod + sym
cmd = self.MapKeystroke(stroke)
if cmd:
if cmd.invoke_with_keystroke:
cmd.Invoke(stroke)
else:
cmd.Invoke()
finally:
self.end_transaction()
def KeyReleaseEvent(self, event):
# generate a `fake' motion event if a modifier changed. This is
# done to reflect changes in the constraints during a drag
# immediately.
mask = constraint_keysyms.get(event.keysym, 0)
if mask:
# modify the state, because it reflects the state before the
# key release
event.state = event.state ^ mask
self.PointerMotionEvent(event)
#
#
def build_context_commands(self):
items = []
for entry in self.context_menu_items:
if entry:
cmd = self.commands.Get(entry)
if not cmd:
cmd = self.main_window.commands.Get(entry)
if cmd:
items.append(cmd)
else:
# XXX: if the context menu is configurable by the
# user, this should be visible to the user.
warn(INTERNAL, 'unknown command', entry)
else:
items.append(entry)
self.context_commands = items
def popup_context_menu(self, x, y, button, state):
if state & const.AllButtonsMask == 0:
if __debug__:
pdebug('context_menu', 'popup_context_menu',
x, y, button, state)
if self.context_commands is None:
self.build_context_commands()
items = []
last = None
for cmd in self.context_commands:
if cmd and not cmd.InContext():
cmd = None
if cmd != last:
items.append(cmd)
last = cmd
if items and items[0] is None:
del items[0]
if items and items[-1] is None:
del items[-1]
if items:
context_menu = UpdatedMenu(self, map(MakeCommand, items))
context_menu.Popup(x, y)
context_menu.clean_up()
#
# Report the current pointer position in doc coords.
# (might be also interesting for the view)
#
def set_current_pos(self, p, snap = 0):
if snap and preferences.snap_current_pos:
p = self.snap_point(p)
self.current_pos = p
self.issue(POSITION)
def GetCurrentPos(self):
return self.current_pos
#
# Report the current state of the dragged object.
#
def update_current_info_text(self):
if self.current is not None:
self.current_info_text = self.current.CurrentInfoText()
self.issue(CURRENTINFO)
def CurrentInfoText(self):
if self.current is not None and self.current_info_text:
if type(self.current_info_text) == TupleType:
template, dict = self.current_info_text
self.current_info_text = format(template, converters, dict)
return self.current_info_text
else:
if self.selection_info_text is None:
text = self.document.SelectionInfoText()
if type(text) == TupleType:
template, dict = text
text = format(template, converters, dict)
self.selection_info_text = text
return self.selection_info_text
#
# The mode specific functions
#
# Extend some inherited methods to issue the appropriate messages
def push_mode(self, mode):
WidgetWithModes.push_mode(self, mode)
self.issue_mode()
def pop_mode(self):
WidgetWithModes.pop_mode(self)
self.issue_mode()
def enter_mode(self, mode, *args):
apply(WidgetWithModes.enter_mode, (self, mode) + args)
self.issue_mode()
#
def cancel_current_mode(self):
if self.mode.isTemporaryMode:
self.cancel_temporary_mode()
else:
self.mode.cancel()
#
# Creation mode
#
def can_create(self):
return self.document.ActiveLayer() is not None
# enter creation mode for a gfx_name objects
AddModeCmd('CreateRectangle', _("Draw Rectangle"), 'Create',
args = 'RectangleCreator', bitmap = pixmaps.CreateRect,
value_on = 'Create:RectangleCreator',
sensitive_cb = 'can_create')
AddModeCmd('CreateEllipse', _("Draw Ellipse"), 'Create',
args = 'EllipseCreator', bitmap = pixmaps.CreateEllipse,
value_on = 'Create:EllipseCreator',
sensitive_cb = 'can_create')
AddModeCmd('CreatePolyBezier', _("Draw Curve"), 'Create',
args = 'PolyBezierCreator', bitmap = pixmaps.CreateCurve,
value_on = 'Create:PolyBezierCreator',
sensitive_cb = 'can_create')
AddModeCmd('CreatePolyLine', _("Draw Poly-Line"), 'Create',
args = 'PolyLineCreator', bitmap = pixmaps.CreatePoly,
value_on = 'Create:PolyLineCreator',
sensitive_cb = 'can_create')
AddModeCmd('CreateSimpleText', _("Draw Text"), 'Create',
args = 'SimpleTextCreator', bitmap = pixmaps.Text,
value_on = 'Create:SimpleTextCreator',
sensitive_cb = 'can_create')
def Create(self, gfx_name = None):
self.begin_transaction()
if gfx_name is None:
gfx_name = self.create_type
self.enter_mode(self.modes.creation_mode, gfx_name)
self.end_transaction()
def creation_enter(self, gfx_name):
self.create_creator = getattr(Sketch, gfx_name)
self.create_type = gfx_name
self.push_static_cursor(const.CurCreate)
self.mode.text = self.create_creator.creation_text
def creation_begin(self, p, button, state):
# Begin creating an instance of self.create_creator.
if __debug__:
self.check_transaction()
if self.create_creator is None:
raise CanvasError('no create_creator in creation mode')
self.begin_creator(self.create_creator, p, button, state)
def begin_creator(self, creator, p, button, state):
# Begin creating an instance of creator
# also used by zoom_mode
self.hide_handles()
if self.current is not None:
self.current.Hide(self.invgc)
snapped = self.snap_point(p)
self.current = creator(snapped)
self.set_correction(self.current.ButtonDown(snapped, button, state))
self.current.Show(self.invgc)
self.start_drag = 1
self.dragging = 0
def creation_end(self, p, button, state, force_stop = stop_continue):
if __debug__:
self.check_transaction()
if self.current is None:
# current might be none if the user ends creation before
# she's begun creating something (i.e. clicking button-2
# immediately after selecting CreateCurve)
self.creation_cancel()
return
self.current.ButtonUp(self.correct_and_snap(p), button, state)
self.do_end_creation(force_stop, set_mode = 1)
def do_end_creation(self, force_stop = stop_regular, set_mode = 0):
if __debug__:
self.check_transaction()
self.pop_cursor_state()
obj = self.current
obj.Hide(self.invgc)
self.current = None
was_ok = 1
if force_stop:
self.create_creator = None
was_ok = obj.EndCreation()
else:
self.create_creator = obj.ContinueCreation()
if self.create_creator is None:
self.start_drag = 0
self.dragging = 0
if preferences.creation_is_temporary:
new_mode = self.SelectionMode
else:
new_mode = self.Create
if was_ok:
self.document.Insert(obj.CreatedObject())
# switch to edit mode if the new object is a text object.
# There should be a better way to achieve this
if obj.is_Text:
new_mode = self.EditMode
else:
self.document.SelectNone()
if set_mode:
new_mode()
else:
self.push_static_cursor(const.CurCreate)
self.current = obj
obj.Show(self.invgc)
def creation_exit(self, set_mode = 0):
if self.current is not None:
self.do_end_creation(set_mode = set_mode)
def creation_cancel(self):
self.dragging = 0
self.start_drag = 0
if self.current is not None:
self.current.Hide(self.invgc)
self.current = None
self.create_creator = None
self.SelectionMode()
#
# Selection and Edit mode
#
# two methods shared by the selection and edit modes
def begin_edit_object(self, p, button, state, handle = None):
# begin to edit OBJ
self.current = self.document
if handle is not None:
self.current.SelectHandle(handle, SelectDrag)
else:
self.current.SelectPointPart(p, self.hitgc, SelectDrag)
self.hide_handles()
self.set_correction(self.current.ButtonDown(p, button, state))
self.current.Show(self.invgc, 1)
def stop_edit_object(self, p, button, state, force_stop = 0):
if __debug__:
self.check_transaction()
if self.current is None:
return None
obj = self.current
p = self.correct_and_snap(p)
self.current = None
self.start_drag = 0
self.dragging = 0
obj.Hide(self.invgc, 1)
obj.ButtonUp(p, button, state)
return obj
def drag_mouse(self, p, state):
if self.start_drag:
# XXX it sometimes happens that self.start_pos has not been
# set yet. Why?
if abs(self.start_pos[0] - p) * self.scale > 3:
apply(self.mode.start_drag, self.start_pos)
self.start_drag = 0
else:
return
self.dragging = 1
if self.current is not None and (state & self.current.drag_mask):
# if something is being edited, hide it and show it at new
# position
self.current.Hide(self.invgc, 1)
self.current.MouseMove(self.correct_and_snap(p), state)
self.current.Show(self.invgc, 1)
def selection_rect(self, p, button, state):
self.current = SelectionRectangle(p)
self.set_correction(self.current.ButtonDown(p, button, state))
self.current.Show(self.invgc)
#
# Selection
#
# enter selection mode
AddModeCmd('SelectionMode', _("Selection Mode"), value_on = 'Select',
bitmap = pixmaps.SelectionMode)
def SelectionMode(self):
self.begin_transaction()
self.document.SetMode(SelectionMode)
self.end_transaction()
def selection_mode(self):
self.enter_mode(self.modes.selection_mode)
def selection_enter(self):
self.create_creator = None
self.set_active_cursor(self.set_handle_cursor, const.CurStd)
def selection_down(self, p, button, state):
self.start_pos = (p, button, state)
self.start_drag = 1
self.dragging = 0
self.hide_handles()
def selection_start_drag(self, p, button, state):
if selection_type(state) == SelectSet:
handle = self.handle_hit(p)
if handle is not None or self.document.SelectionHit(p, self.hitgc):
# something is already selected and the user pressed the
# button somewhere on the selection. Edit the current
# object(s)
self.begin_edit_object(p, button, state, handle)
return
# there's no selection or the user pressed the button somewhere
# outside the current selection.
# test for a guide line
guide = self.document.SelectPoint(p, self.hitgc, SelectGuide)
if guide is not None:
self.current = guide
self.set_correction(self.current.ButtonDown(p, button, state))
self.current.Show(self.invgc)
else:
# Make a new selection
self.selection_rect(p, button, state)
def selection_click(self, p, button, state):
type = selection_type(state)
if type == SelectSet and self.document.SelectionHit(p, self.hitgc):
self.document.ToggleSelectionBehaviour()
else:
self.document.SelectPoint(p, self.hitgc, type)
def selection_up(self, p, button, state, force_stop = 0):
if not self.dragging:
self.selection_click(p, button, state)
else:
obj = self.stop_edit_object(p, button, state, force_stop)
if obj is not None:
if isinstance(obj, SelectionRectangle):
self.document.SelectRect(obj.bounding_rect,
selection_type(state))
elif obj.is_GuideLine:
# a guideline
x = self.last_event.x
y = self.last_event.y
if (0 <= x < self.winfo_width()
and 0 <= y <= self.winfo_height()):
self.document.MoveGuideLine(obj, obj.drag_cur)
else:
self.document.RemoveGuideLine(obj)
self.show_handles()
self.current = None
self.start_drag = self.dragging = 0
def selection_cancel(self):
if self.dragging:
if self.current is not None:
self.current.Hide(self.invgc)
self.current = None
self.dragging = 0
else:
self.document.SelectNone()
self.start_drag = 0
#
# Edit mode
#
AddModeCmd('EditMode', _("Edit Mode"), bitmap = pixmaps.EditMode,
value_on = 'Edit')
def EditMode(self):
self.begin_transaction()
self.document.SetMode(EditMode)
self.end_transaction()
def edit_mode(self):
self.enter_mode(self.modes.edit_mode)
def edit_enter(self):
self.create_creator = None
self.set_active_cursor(self.set_handle_cursor, const.CurEdit)
def edit_down(self, p, button, state):
self.start_pos = (p, button, state)
self.start_drag = 1
self.dragging = 0
def edit_start_drag(self, p, button, state):
handle = self.handle_hit(p)
if handle is None:
self.hitgc.StartOutlineMode()
is_hit = self.document.SelectionHit(p, self.hitgc, test_all = 0)
self.hitgc.EndOutlineMode()
else:
is_hit = 1
if is_hit:
# something is already selected and the user pressed the
# button somewhere on the selection. Edit the current object
self.begin_edit_object(p, button, state, handle)
else:
# there's no selection or the user pressed the button
# somewhere outside the current selection.
# test for a guide line
guide = self.document.SelectPoint(p, self.hitgc, SelectGuide)
if guide is not None:
# edit the guide line
self.current = guide
self.set_correction(self.current.ButtonDown(p, button, state))
self.current.Show(self.invgc)
else:
# Make a new selection
self.selection_rect(p, button, state)
def edit_click(self, p, button, state):
handle = self.handle_hit(p)
if handle is not None:
self.document.SelectHandle(handle, selection_type(state))
else:
self.hitgc.StartOutlineMode()
is_hit = self.document.SelectionHit(p, self.hitgc, test_all = 0)
self.hitgc.EndOutlineMode()
if is_hit:
self.document.SelectPointPart(p, self.hitgc)
else:
self.document.SelectPoint(p, self.hitgc)
def edit_up(self, p, button, state, force_stop = stop_continue):
if not self.dragging:
self.edit_click(p, button, state)
else:
obj = self.stop_edit_object(p, button, state, force_stop)
if obj.__class__ == SelectionRectangle:
self.document.SelectRectPart(obj.bounding_rect,
selection_type(state))
elif obj.is_GuideLine:
# a guideline
x = self.last_event.x
y = self.last_event.y
if 0 <= x < self.winfo_width() and 0<= y <=self.winfo_height():
self.document.MoveGuideLine(obj, obj.drag_cur)
else:
self.document.RemoveGuideLine(obj)
self.current = None
self.show_handles()
self.start_drag = 0
self.dragging = 0
def edit_cancel(self):
if self.dragging:
self.current.Hide(self.invgc)
self.current = None
self.dragging = 0
self.show_handles()
self.start_drag = 0
#
# the standard mouse_move method for the modes
#
def drag_to(self, p, state):
self.dragging = 1
if self.current is not None and (state & self.current.drag_mask):
# if something is being created/edited, hide it and show it
# at new position
self.current.Hide(self.invgc, 1)
self.current.MouseMove(self.correct_and_snap(p), state)
self.current.Show(self.invgc, 1)
#
# Temporary modes
#
#
# Place object mode
#
def PlaceObject(self, object, text = None):
self.begin_transaction()
if self.IsCreationMode():
self.creation_exit(set_mode = 1)
self.enter_mode(self.modes.place_mode, object, text)
self.end_transaction()
def place_enter(self, object, text):
#self.create_creator = None
self.push_static_cursor(const.CurPlace)
self.place_object = object
self.place_text = text
self.start_drag = 1
def place_begin(self, p, button, state):
if self.start_drag:
self.place_start_drag(p, state)
def place_start_drag(self, p, state):
if self.place_object.is_GuideLine:
self.place_object.SetPoint(p)
self.current = self.place_object
else:
self.place_object.SetLowerLeftCorner(p)
self.current = SelectionRectangle(self.place_object.coord_rect,
anchor = self.place_object.LayoutPoint())
self.current.Select()
# selection rect doesn't use button and state:
self.set_correction(self.current.ButtonDown(p, 0, state))
self.current.Show(self.invgc)
self.dragging = 1
self.start_drag = 0
def place_drag(self, p, state):
if self.start_drag:
self.place_start_drag(p, state)
self.dragging = 1
if self.current is not None:
# if something is being placed, hide it and show it at new
# position
self.current.Hide(self.invgc, 1)
state = state | const.Button1Mask
self.current.MouseMove(self.correct_and_snap(p), state)
self.current.Show(self.invgc, 1)
def place_cancel(self):
self.pop_cursor_state()
if self.current is not None:
self.current.Hide(self.invgc)
self.current = None
self.place_object = None
self.place_text = None
self.dragging = 0
self.start_drag = 0
def place_end(self, p, button, state, force_stop = stop_continue):
self.pop_cursor_state()
if self.current is None:
return
rect = self.current
rect.Hide(self.invgc)
p = self.correct_and_snap(p)
rect.ButtonUp(p, button, state)
self.current = None
obj = self.place_object
self.place_object = None
if obj.is_GuideLine:
x = self.last_event.x
y = self.last_event.y
if (0 <= x < self.winfo_width() and 0 <= y <= self.winfo_height()):
obj.SetPoint(p)
else:
# guide line is outside of the canvas window.
obj = None
else:
obj.SetLowerLeftCorner(rect.start)
if obj:
if self.place_text:
self.document.Insert(obj, self.place_text)
else:
self.document.Insert(obj)
self.place_text = None
#
# Pick object mode
#
def PickObject(self, callback, args = ()):
self.begin_transaction()
self.enter_mode(self.modes.pick_mode, callback, args)
self.end_transaction()
def pick_enter(self, callback, args):
self.create_creator = None
self.push_static_cursor(const.CurPick)
self.pick_object_cb = callback, args
def pick_begin(self, p, button, state):
pass
def pick_cancel(self):
self.pop_cursor_state()
self.pick_object_cb = None
def pick_end(self, p, button, state, force_stop = stop_continue):
self.pop_cursor_state()
object = self.document.PickObject(self.hitgc, p)
cb, args = self.pick_object_cb
self.pick_object_cb = None
args = (object,) + args
apply(cb, args)
#
# Zoom mode
#
AddModeCmd('ZoomMode', _("Zoom Area"), bitmap = pixmaps.Zoom,
value_on = 'Zoom')
def ZoomMode(self):
self.begin_transaction()
self.enter_mode(self.modes.zoom_mode)
self.end_transaction()
def zoom_enter(self):
self.push_static_cursor(const.CurZoom)
def zoom_begin(self, p, button, state):
self.begin_creator(SelectionRectangle, p, button, state)
def zoom_cancel(self):
self.pop_cursor_state()
if self.current is not None:
self.current.Hide(self.invgc)
self.current = None
self.dragging = 0
self.start_drag = 0
def zoom_end(self, p, button, state, force_stop = stop_continue):
self.pop_cursor_state()
obj = None
if self.current is not None:
obj = self.current
obj.Hide(self.invgc)
obj.ButtonUp(self.correct_and_snap(p), button, state)
self.current = None
zoom_out = state & const.ControlMask
self.save_viewport()
if self.dragging and obj is not None:
epsilon = 1e-10
rect = obj.bounding_rect
rw = rect.right - rect.left
rh = rect.top - rect.bottom
width = self.tkwin.width
height = self.tkwin.height
if abs(rw) < epsilon or abs(rh) < epsilon:
return
scalex = width / rw
scaley = height / rh
if zoom_out:
scale = (self.scale ** 2) / max(scalex, scaley)
x, y = self.DocToWin(rect.center())
center = rect.center() + Point(width / 2 - x,
-height / 2 + y) / scale
else:
scale = min(scalex, scaley, self.max_scale)
center = rect.center()
else:
if zoom_out:
scale = 0.5 * self.scale
else:
scale = 2 * self.scale
center = p
self.SetScale(scale / self.pixel_per_point, do_center = 0)
self.SetCenter(center, move_contents = 0)
self.start_drag = 0
self.dragging = 0
#
# Other mode related methods
#
AddCmd('ToggleMode', _("Toggle Mode"), key_stroke = (' ', 'C-space'),
subscribe_to = None)
def ToggleMode(self):
self.begin_transaction()
try:
if self.IsCreationMode():
self.SelectionMode()
elif self.IsSelectionMode():
self.EditMode()
else:
# go to selection mode as default
self.SelectionMode()
finally:
self.end_transaction()
def set_mode_from_doc(self, *args, **kwargs):
mode = self.document.Mode()
if mode == SelectionMode:
self.selection_mode()
else:
self.edit_mode()
self.update_object_keymap()
def IsSelectionMode(self):
return self.mode == self.modes.selection_mode
def IsEditMode(self):
return self.mode == self.modes.edit_mode
def IsCreationMode(self):
return self.mode == self.modes.creation_mode
def ModeInfoText(self):
return self.mode.text
def ModeName(self):
name = self.mode.name
if name == 'Create':
name = name + ':' + self.create_type
return name
#
# Selection related methods
#
def SelectionInfoText(self):
# Return a string describing the currently selected objects
return self.document.SelectionInfoText()
#
# correction, snapping
#
def set_correction(self, correct):
if type(correct) == TupleType:
correct, rect = correct
else:
rect = None
if correct is None:
correct = Point(0, 0)
self.correction = correct
self.correction_rect = rect
snap_update_installed = 0
def update_snap_points(self):
if self.snap_to_object and not self.snap_update_installed:
self.after_idle(self.idle_update_snap_points)
self.snap_update_installed = 1
def extract_snap_points(self, obj):
self.snap_points[0:0] = obj.GetSnapPoints()
def idle_update_snap_points(self):
self.snap_update_installed = 0
self.snap_points = []
self.document.WalkHierarchy(self.extract_snap_points, visible = 1,
printable = 0)
self.snap_points.sort()
def correct_and_snap(self, point):
point = point - self.correction
points = [point]
if self.correction_rect and self.snap_correction_rect:
l, b, r, t = self.correction_rect.translated(point)
points = points + [Point(l,b), Point(l,t), Point(r,b), Point(r,t)]
if self.dragging and self.start_pos \
and self.snap_move_relative:
start_pos = point - self.start_pos[0] + self.correction
x, y = self.document.GridGeometry()[:2]
points.append(Point(start_pos.x - x, start_pos.y - y))
mindist = 1e100
result = point
#print points
for p in points:
dist, snapped = self.snap_point_dist(p)
if dist < mindist:
mindist = dist
result = point - p + snapped
self.set_current_pos(result)
return result
def snap_point(self, p):
dist, p = self.snap_point_dist(p)
return p
def snap_point_dist(self, p):
maxdist = preferences.max_snap_distance / self.scale
pgrid = pobj = pguide = pmax = (maxdist, p)
if self.snap_to_grid:
pgrid = self.document.SnapToGrid(p)
if self.snap_to_guide:
pgh, pgv, pguide = self.document.SnapToGuide(p, maxdist)
if (self.current is None
or isinstance(self.current, SelectionRectangle)
or not self.current.is_GuideLine):
# We're not dragging a guideline, so snap to guide-lines
# or to guide-objects. Otherwise, only snap to guide
# objects
pguide = min(pgh, pgv, pguide)
if pguide == pgh or pguide == pgv:
dist, (x, y) = pguide
if x is None:
if pgv[0] < maxdist:
x = pgv[-1][0]
dist = dist + pgv[0] - maxdist
else:
x = p.x
else:
if pgh[0] < maxdist:
y = pgh[-1][1]
dist = dist + pgh[0] - maxdist
else:
y = p.y
pguide = dist, Point(x, y)
if self.snap_to_object and self.snap_points:
x, y = p
mindist = 1e100;
xlow = x - maxdist; ylow = y - maxdist
xhigh = x + maxdist; yhigh = y + maxdist
points = self.snap_points
ilow = 0; ihigh = len(points)
while ilow < ihigh:
imid = (ilow + ihigh) / 2
if points[imid].x > xlow:
ihigh = imid
else:
ilow = imid + 1
for idx in range(ilow, len(points)):
pmin = points[idx]
if pmin.x > xhigh:
break
if ylow < pmin.y < yhigh:
dist = max(abs(pmin.x - x), abs(pmin.y - y))
if dist < mindist:
pobj = pmin
mindist = dist
if type(pobj) != TupleType:
pobj = (abs(pobj - p), pobj)
result = min(pgrid, pguide, pobj, pmax)
return result
AddCmd('ToggleSnapToGrid', _("Snap to Grid"), bitmap = pixmaps.GridOn,
value = 0, value_cb = 'IsSnappingToGrid', is_check = 1)
def ToggleSnapToGrid(self):
self.begin_transaction()
try:
self.snap_to_grid = not self.snap_to_grid
self.issue_state()
finally:
self.end_transaction()
def IsSnappingToGrid(self):
return self.snap_to_grid
AddCmd('ToggleSnapToObjects', _("Snap to Objects"),
value = 0, value_cb = 'IsSnappingToObjects', is_check = 1)
def ToggleSnapToObjects(self):
self.begin_transaction()
try:
self.snap_to_object = not self.snap_to_object
self.snap_points = ()
self.update_snap_points()
self.issue_state()
finally:
self.end_transaction()
def IsSnappingToObjects(self):
return self.snap_to_object
AddCmd('ToggleSnapToGuides', _("Snap to Guides"),
value = 0, value_cb = 'IsSnappingToGuides', is_check = 1)
def ToggleSnapToGuides(self):
self.begin_transaction()
try:
self.snap_to_guide = not self.snap_to_guide
self.issue_state()
finally:
self.end_transaction()
def IsSnappingToGuides(self):
return self.snap_to_guide
AddCmd('ToggleSnapBoundingRect', _("Snap Bounding Rect"),
value = 0, value_cb = 'IsSnappingBoundingRect', is_check = 1)
def ToggleSnapBoundingRect(self):
self.begin_transaction()
try:
self.snap_correction_rect = not self.snap_correction_rect
self.issue_state()
finally:
self.end_transaction()
def IsSnappingBoundingRect(self):
return self.snap_correction_rect
AddCmd('ToggleSnapMoveRelative', _("Snap Move Relative"),
value = 0, value_cb = 'IsSnappingRelative', is_check = 1)
def ToggleSnapMoveRelative(self):
self.begin_transaction()
try:
self.snap_move_relative = not self.snap_move_relative
self.issue_state()
finally:
self.end_transaction()
def IsSnappingRelative(self):
return self.snap_move_relative
def SnapInfoText(self):
# NLS might make problems here
snap = []
if self.snap_to_grid:
snap.append(_("Grid"))
if self.snap_to_object:
snap.append(_("Objects"))
if self.snap_to_guide:
snap.append(_("Guide"))
if snap:
return _("Snap: ") + string.join(snap, '/')
return _("No Snap")
#
# Crosshairs
#
def init_cross_hairs(self):
self.crosshairs = 0
self.crosshairs_visible = 0
self.crosshairs_pos = ()
self.crosshairs_drawn = 0
def draw_crosshairs(self, pos):
x, y = self.DocToWin(pos)
width = self.tkwin.width
height = self.tkwin.height
self.invgc.gc.DrawLine(0, y, width, y)
self.invgc.gc.DrawLine(x, 0, x, height)
self.crosshairs_pos = pos
def show_crosshairs(self, force = 0):
if self.crosshairs and self.crosshairs_visible:
if not self.crosshairs_drawn or force:
self.draw_crosshairs(self.current_pos)
self.crosshairs_drawn = 1
def hide_crosshairs(self):
if self.crosshairs_drawn:
self.draw_crosshairs(self.crosshairs_pos)
self.crosshairs_drawn = 0
AddCmd('ToggleCrosshairs', _("Crosshairs"), key_stroke = 'F2',
value = 0, value_cb = 'IsShowingCrosshairs', is_check = 1)
def ToggleCrosshairs(self):
self.begin_transaction()
try:
self.hide_crosshairs()
self.crosshairs = not self.crosshairs
self.show_crosshairs()
self.issue_state()
finally:
self.end_transaction()
def IsShowingCrosshairs(self):
return self.crosshairs
#
# Handles
#
def init_handles(self):
self.handle_points = []
self.handle_funcs = []
self.handles_drawn = 0
handle_update_installed = 0
def update_handles(self):
self.hide_handles()
if not self.handle_update_installed:
self.after_idle(self.idle_update_handles)
self.handle_update_installed = 1
def idle_update_handles(self):
self.handle_update_installed = 0
self.set_handles(self.document.GetSelectionHandles())
def set_handles(self, handles):
from Sketch.const import HandleLine,Handle_Pixmap,Handle_Caret,\
Handle_SmallOpenRectList
points = []
funcs = []
used = {}
if self.gcs_initialized:
factor = self.gc.LengthToDoc(8)
else:
factor = 1
for handle in handles:
handle_type = handle.type
p = handle.p
cursor = handle.cursor
if handle.offset is not None:
offset = handle.offset
p = p + Point(offset[0] * factor, offset[1] * factor)
if handle_type == HandleLine:
funcs.append((self.invgc.DrawHandleLine, (p, handle.p2)))
elif handle_type == Handle_Pixmap:
if cursor:
points.append(self.DocToWin(p) + (cursor, handle))
funcs.append((self.draw_handle_funcs[handle_type],
(p, handle.pixmap)))
elif handle_type == Handle_Caret:
funcs.append((self.invgc.DrawCaretHandle, (p, handle.p2)))
elif handle_type == Handle_SmallOpenRectList:
p = handle.list
if p:
pts = map(self.DocToWin, p)
last = pts[-1]
for idx in range(len(p) - 2, -1, -1):
if pts[idx] == last:
del p[idx]
else:
last = pts[idx]
funcs.append((self.draw_handle_funcs[handle_type], (p, 0)))
else:
win = self.DocToWin(p)
if cursor:
points.append(win + (cursor, handle))
if not used.has_key(win):
funcs.append((self.draw_handle_funcs[handle_type],
(p, handle_type & 1)))
used[win] = 0
self.hide_handles()
self.handle_points = points
self.handle_funcs = funcs
self.show_handles()
def show_handles(self, force = 0):
if __debug__:
pdebug('handles',
'show_handles: drawn = %d, force = %d, update = %d',
self.handles_drawn, force, self.handle_update_installed)
if not self.handle_update_installed\
and (not self.handles_drawn or force):
self.draw_handles()
self.handles_drawn = 1
def hide_handles(self):
if __debug__:
pdebug('handles', 'hide_handles: drawn = %d', self.handles_drawn)
if self.handles_drawn:
self.draw_handles()
self.handles_drawn = 0
def draw_handles(self):
for f, args in self.handle_funcs:
apply(f, args)
handle_hit_radius = 4
def set_handle_cursor(self, x, y):
dist = self.handle_hit_radius
for xp, yp, cursor, handle in self.handle_points:
if abs(xp - x) < dist and abs(yp - y) < dist:
self.set_window_cursor(cursor)
break
else:
cursor = self.cursor_shape
if preferences.active_cursor:
object = self.document.PickActiveObject(self.hitgc,
self.win_to_doc(x, y))
if object is not None:
if object.is_GuideLine:
if object.horizontal:
cursor = const.CurHGuide
else:
cursor = const.CurVGuide
elif self.IsSelectionMode():
cursor = const.CurMove
self.set_window_cursor(cursor)
def handle_hit(self, p):
x, y = self.DocToWin(p)
dist = self.handle_hit_radius
handle_points = self.handle_points
for idx in range(len(handle_points)):
xp, yp, cursor, handle = handle_points[idx]
if abs(xp - x) < dist and abs(yp - y) < dist:
handle.index = idx
return handle
return None
#
# Viewport- and related methods
#
# extend some Viewport methods to handle handles and to issue VIEW
# whenever the displayed area changes
#
AddCmd('ForceRedraw', _("Redraw"), key_stroke = 'M-r', subscribe_to = None)
def set_origin(self, xorg, yorg, move_contents = 1):
self.begin_transaction()
try:
#self.hide_handles()
SketchView.set_origin(self, xorg, yorg,
move_contents = move_contents)
self.queue_update_handles()
finally:
self.end_transaction()
def SetScale(self, scale, do_center = 1):
# Set current scale
self.begin_transaction()
try:
self.hide_handles() # XXX: really necessary ?
SketchView.SetScale(self, scale, do_center = do_center)
self.queue_update_handles()
finally:
self.end_transaction()
def ZoomFactor(self, factor):
self.SetScale(self.scale * factor)
AddCmd('ZoomIn', _("Zoom In"), 'ZoomFactor', args = 2.0,
key_stroke = '>')
AddCmd('ZoomOut', _("Zoom Out"), 'ZoomFactor', args = 0.5,
key_stroke = '<')
# add commands for inherited methods
AddCmd('ScrollYPages', 'Page Up', args = -1, key_stroke = 'Prior',
subscribe_to = None)
AddCmd('ScrollYPages', 'Page Down', args = +1, key_stroke = 'Next',
subscribe_to = None)
# scrolling with the cursor keys
AddCmd('ScrollYPages', '', args = -1, key_stroke = 'C-Up',
subscribe_to = None)
AddCmd('ScrollYPages', '', args = +1, key_stroke = 'C-Down',
subscribe_to = None)
AddCmd('ScrollXPages', '', args = -1, key_stroke = 'C-Left',
subscribe_to = None)
AddCmd('ScrollXPages', '', args = +1, key_stroke = 'C-Right',
subscribe_to = None)
# other view related methods
AddCmd('FitToWindow', _("Fit to Window"), key_stroke = 'S-F4',
subscribe_to = None)
AddSelCmd('FitSelectedToWindow', _("Fit Selected to Window"),
'FitToWindow', args = 1, key_stroke = 'C-F4')
def FitToWindow(self, selected_only = 0, save_viewport = 1):
self.begin_transaction()
try:
self.hide_handles() # XXX: really necessary ?
SketchView.FitToWindow(self, selected_only = selected_only,
save_viewport = save_viewport)
finally:
self.end_transaction()
AddCmd('FitPageToWindow', _("Fit Page to Window"), key_stroke = 'F4',
subscribe_to = None)
def FitPageToWindow(self, save_viewport = 1):
self.begin_transaction()
try:
self.hide_handles() # XXX: really necessary ?
SketchView.FitPageToWindow(self, save_viewport = save_viewport)
finally:
self.end_transaction()
def CanRestoreViewport(self):
return len(self.viewport_ring)
AddCmd('RestoreViewport', _("Restore Previous View"), key_stroke = 'F3',
subscribe_to = VIEW, sensitive_cb = 'CanRestoreViewport')
def RestoreViewport(self):
if self.viewport_ring:
self.begin_transaction()
try:
self.restore_viewport()
finally:
self.end_transaction()
AddCmd('ToggleOutlineMode', _("Outline"), key_stroke = 'S-F9',
value = 0, value_cb = 'IsOutlineMode', subscribe_to = VIEW,
is_check = 1)
AddCmd('TogglePageOutlineMode', _("Draw Page Outline"),
value = 0, value_cb = 'IsPageOutlineMode', subscribe_to = VIEW,
is_check = 1)
#
#
#
def unsubscribe_doc(self):
if self.document is not None:
self.document.Unsubscribe(SELECTION,self.selection_changed)
self.document.Unsubscribe(EDITED,self.doc_was_edited)
self.document.Unsubscribe(MODE, self.set_mode_from_doc)
SketchView.unsubscribe_doc(self)
def subscribe_doc(self):
self.document.Subscribe(SELECTION, self.selection_changed)
self.document.Subscribe(EDITED,self.doc_was_edited)
self.document.Subscribe(MODE, self.set_mode_from_doc)
SketchView.subscribe_doc(self)
def SetDocument(self, doc):
self.begin_transaction()
try:
SketchView.SetDocument(self, doc)
self.SelectionMode()
self.hide_handles()
self.issue_selection()
self.issue_mode()
self.update_handles()
self.update_snap_points()
finally:
self.end_transaction()
self.init_viewport_ring()
#
# Fill
#
AddSelCmd('FillSolid', _("Set Fill Color..."))
def FillSolid(self, col = None):
# Set the fill style of the currently selected objects to a
# solid fill of color COL. If COL is None let the user
# interactively select a color.
if col is None:
import colordlg
current_color = self.document.CurrentFillColor()
if current_color is None:
current_color = StandardColors.white
col = colordlg.GetColor(self, current_color)
if not col:
return
from Sketch import SolidPattern
import styledlg
styledlg.set_properties(self.master, self.document,
_("Set Fill Color"), 'fill',
{'fill_pattern' : SolidPattern(col)})
#
# LineStyle
#
def LineColor(self, color):
# Set the line color of the currently selected objects to COLOR
# XXX: should be removed; only needed by skapp to connect
# palette with canvas. (Dec 97)
if self.document.CountSelected() > 1:
self.call_document_method('SetLineColor', color)
else:
from Sketch import SolidPattern
import styledlg
styledlg.set_properties(self.master, self.document,
_("Set Line Color"), 'line',
{'line_pattern' : SolidPattern(color)})
|