# Sketch - A Python-based interactive drawing program
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 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
# This file contains the classes that act as a more or less abstract
# graphics device. For the purposes of this file, a graphics device
# provides methods for drawing primitives like rectangles, curves or
# images and for setting the colors and pattern with which they are
# drawn. A graphics device is abstract in the sense that all coordinates
# are given in document coordinates and code using a graphics device
# need not know whether output goes to a window or a printer, etc.
#
import sys
from types import TupleType
import operator, string
from math import pi
import PIL.Image
import X
from pax import PaxRegionType,IntersectMasks,CreateRegion
from Sketch.warn import pdebug,warn,INTERNAL,USER
from Sketch import _,SketchError,const,config
from Sketch.Lib.util import Empty
from Sketch.const import ArcPieSlice,ArcChord
from Sketch import _sketch
from Sketch import Point,Polar,Rect,UnitRect,Identity,SingularMatrix,\
Trafo, Rotation, Scale, Translation, Undo, TransformRectangle
from color import StandardColors
import color
from properties import SolidLine,PropertyStack
from pattern import SolidPattern,EmptyPattern
# approximate a unit circle
circle_path = _sketch.approx_arc(0, 2 * pi)
# Flip y coordinates
FlipY = Trafo(1, 0, 0, -1, 0, 0)
# Define a device dependent color (as opposed to color.RGBColor, which
# is a bit device independent). Mainly interesting when drawing on a
# Bitmap or inverting device where it is important which bits are
# involved and not which color is actually used. The colors are
# currently represented as python integers.
def ScreenColor(color):
return int(color)
color_0 = ScreenColor(0)
color_1 = ScreenColor(1)
# some default properties
black_line = PropertyStack()
black_line.AddStyle(SolidLine(StandardColors.black))
blue_line = PropertyStack()
blue_line.AddStyle(SolidLine(StandardColors.blue))
defaultLineStyle = black_line
default_outline_style = black_line
default_guide_style = blue_line
default_grid_style = blue_line
class GCDevice:
# The base class for devices dealing with X graphics contexts
# (GCs). Implements the methods for converting between window and
# doc coordinates, clipping and outline mode
outline_style = default_outline_style
line = defaultLineStyle
def __init__(self):
self.orig_x = self.orig_y = 0.0
self.scale = 1.0
self.win_to_doc = self.doc_to_win = Identity
self.init_trafo_stack()
self.gc = None
self.outline_mode = 0
self.font = None
self.clip_region = None
self.clip_stack = None
self.proc_fill = 0
def init_gc(self, widget, **gcargs):
self.gc = widget.CreateGC(gcargs)
self.widget = widget
self.visual = color.skvisual
self.InitClip()
#
# Clipping
#
# Ideally, the clipping mechanism works as follows:
#
# Before drawing anything, the canvas initializes the clipping
# mechanism with InitClip(). After this, no clipping is active
# (apart from clipping to the window which is always done by X)
#
# Subsequently, whenever clipping is needed, you are expected to
# call PushClip to save the current clipping region on a stack and
# use one (or more) of the methods ClipRegion, ClipRect,
# ClipPolygon or ClipBitmap to establish a new clipping region.
# After this, only those parts of the device that lie within the
# clipping region are modified.
#
# If there already is an active clipping region, the new clipping
# region will be the intersection of the old clipping region and
# the region specified in the method invocation.
#
# To restore the old clipping region, finally call PopClip. There
# must be a call to PopClip for every call to PushClip, etc.
#
# This mechanism allows arbitrarily nested clipping operations.
#
# The clipping regions are given in device coordinates
#
def InitClip(self):
# reset all clipping...
self.gc.SetClipMask(None)
self.clip_region = None
self.clip_stack = ()
def IsClipping(self):
return self.clip_stack != ()
def PushClip(self):
self.clip_stack = (self.clip_region, self.clip_stack)
def PopClip(self):
# Pop the old clip region from the clip stack and make it the
# active clip region.
self.clip_region, self.clip_stack = self.clip_stack
self.gc.SetClipMask(self.clip_region)
def ClipRegion(self, region):
# Itersect the current clip region and REGION and make the
# result the new clip region. REGION may be a region object or a
# pixmap object of depth 1
self.clip_region = IntersectMasks(self.clip_region, region)
self.gc.SetClipMask(self.clip_region)
def ClipRect(self, recttuple):
region = self.widget.CreateRegion()
apply(region.UnionRectWithRegion, recttuple)
self.ClipRegion(region)
def ClipPolygon(self, pts):
self.ClipRegion(self.widget.PolygonRegion(pts))
ClipBitmap = ClipRegion
def create_clip_bitmap(self):
width = self.widget.width
height = self.widget.height
bitmap = self.widget.CreatePixmap(width, height, 1)
bitmap_gc = bitmap.CreateGC(foreground = 0)
bitmap_gc.FillRectangle(0, 0, width, height)
bitmap_gc.foreground = 1
return (bitmap, bitmap_gc)
#
# Convert document coordinates to window coordinates and vice versa.
#
def DocToWin(self, *args):
# Return the point in window coords as a tuple of ints
return apply(self.doc_to_win.DocToWin, args)
def DocToWinPoint(self, p):
# Return the point in window coords as a point object
return self.doc_to_win(p)
def LengthToWin(self, len):
return int(len * self.scale + 0.5)
def LengthToWinFloat(self, len):
return len * self.scale
def LengthToDoc(self, len):
return len / self.scale
def WinToDoc(self, *args):
return apply(self.win_to_doc, args)
def init_trafo_stack(self):
self.trafo_stack = ()
self.default_trafo = (self.win_to_doc, self.doc_to_win)
def SetViewportTransform(self, scale, doc_to_win, win_to_doc):
self.scale = scale
self.doc_to_win = doc_to_win
self.win_to_doc = win_to_doc
self.init_trafo_stack()
def InitTrafo():
self.win_to_doc, self.doc_to_win = self.default_trafo
self.trafo_stack = ()
def PushTrafo(self):
self.trafo_stack = (self.win_to_doc, self.doc_to_win, self.trafo_stack)
def Concat(self, trafo):
self.doc_to_win = self.doc_to_win(trafo)
try:
self.win_to_doc = trafo.inverse()(self.win_to_doc)
except SingularMatrix:
pass
def Translate(self, *args):
self.Concat(apply(Translation, args))
def Scale(self, factor):
self.Concat(Scale(factor))
def Rotate(self, angle):
self.Concat(Rotation(angle))
def PopTrafo(self):
self.win_to_doc, self.doc_to_win, self.trafo_stack = self.trafo_stack
def WindowResized(self, width, height):
pass
#
# Double Buffering
#
def StartDblBuffer(self):
self.buffer_pixmap = self.widget.CreatePixmap()
self.gc.SetDrawable(self.buffer_pixmap)
def EndDblBuffer(self):
self.gc.SetDrawable(self.widget)
self.buffer_pixmap.CopyArea(self.widget, self.gc, 0, 0,
self.widget.width, self.widget.height,
0, 0)
self.buffer_pixmap = None
class SimpleGC(GCDevice):
#
# Functions for drawing X-primitives (rectangles, lines, ...)
# with solid colors
#
# These functions are used internally and by the Patterns
#
def __init__(self):
GCDevice.__init__(self)
self.current_color = StandardColors.black
def SetFillColor(self, color):
self.current_color = color
self.gc.SetForegroundAndFill(self.visual.get_pixel(color))
def SetLineColor(self, color):
self.current_color = color
self.gc.SetForegroundAndFill(self.visual.get_pixel(color))
def SetLineAttributes(self, width, cap = X.CapButt, join = X.JoinMiter,
dashes = None):
if dashes:
line = X.LineOnOffDash
else:
line = X.LineSolid
self.gc.SetLineAttributes(int(round(width * self.scale)), line, cap,
join)
if dashes:
if width < 1.0:
scale = self.scale
else:
scale = width * self.scale
dashes = map(operator.mul, dashes, [scale] * len(dashes))
dashes = map(int, map(round, dashes))
for idx in range(len(dashes)):
length = dashes[idx]
if length <= 0:
dashes[idx] = 1
elif length > 255:
dashes[idx] = 255
self.gc.SetDashes(dashes)
def SetLineSolid(self):
self.gc.line_style = X.LineSolid
def DrawLine(self, start, end):
startx, starty = self.DocToWin(start)
endx, endy = self.DocToWin(end)
self.gc.DrawLine(startx, starty, endx, endy)
def DrawLineXY(self, x1, y1, x2, y2):
startx, starty = self.DocToWin(x1, y1)
endx, endy = self.DocToWin(x2, y2)
self.gc.DrawLine(startx, starty, endx, endy)
def DrawLines(self, pts):
pts = map(self.DocToWin, pts)
self.gc.DrawLines(pts, X.CoordModeOrigin)
def FillPolygon(self, pts):
pts = map(self.DocToWin, pts)
self.gc.FillPolygon(pts, X.Complex, X.CoordModeOrigin)
def DrawRectangle(self, start, end):
# draw the outline of the rectangle whose opposite corners are
# start and end.
pts = TransformRectangle(self.doc_to_win, Rect(start, end))
if type(pts) == TupleType:
apply(self.gc.DrawRectangle, pts)
else:
self.gc.DrawLines(pts, X.CoordModeOrigin)
def FillRectangle(self, left, top, right, bottom):
pts = TransformRectangle(self.doc_to_win,
Rect(left, top, right, bottom))
if type(pts) == TupleType:
apply(self.gc.FillRectangle, pts)
else:
self.gc.FillPolygon(pts, X.Convex, X.CoordModeOrigin)
def DrawEllipse(self, start, end):
pts = TransformRectangle(self.doc_to_win, Rect(start, end))
if type(pts) == TupleType:
apply(self.gc.DrawArc, pts + (0, 360 * 64))
else:
d = end - start
self.PushTrafo()
self.Concat(Trafo(d.x, 0, 0, d.y, start.x, start.y))
self.DrawBezierPath(circle_path)
self.PopTrafo()
def DrawCircle(self, center, radius):
rect = Rect(center.x - radius, center.y - radius,
center.x + radius, center.y + radius)
pts = TransformRectangle(self.doc_to_win, rect)
if type(pts) == TupleType:
apply(self.gc.DrawArc, pts + (0, 360 * 64))
else:
self.PushTrafo()
self.Concat(Trafo(radius, 0, 0, radius, center.x, center.y))
self.DrawBezierPath(circle_path)
self.PopTrafo()
def FillCircle(self, center, radius):
rect = Rect(center.x - radius, center.y - radius,
center.x + radius, center.y + radius)
pts = TransformRectangle(self.doc_to_win, rect)
if type(pts) == TupleType:
apply(self.gc.FillArc, pts + (0, 360 * 64))
else:
self.PushTrafo()
self.Concat(Trafo(radius, 0, 0, radius, center.x, center.y))
self.FillBezierPath(circle_path)
self.PopTrafo()
def DrawBezierPath(self, path, rect = None):
path.draw_transformed(self.gc, self.doc_to_win, 1, 0, rect)
def FillBezierPath(self, path, rect = None):
path.draw_transformed(self.gc, self.doc_to_win, 0, 1, rect)
class CommonDevice:
# some methods common to GraphicsDevice and PostScriptDevice
def draw_arrow(self, arrow, width, pos, dir, rect = None):
self.PushTrafo()
self.Translate(pos.x, pos.y)
self.Rotate(dir.polar()[1])
if width >= 1.0:
self.Scale(width)
self.SetLineSolid()
arrow.Draw(self) #, rect)
self.PopTrafo()
def draw_arrows(self, paths, rect = None):
if self.line:
arrow1 = self.properties.line_arrow1
arrow2 = self.properties.line_arrow2
if arrow1 or arrow2:
width = self.properties.line_width
for path in paths:
if not path.closed and path.len > 1:
if arrow1:
type, controls, p3, cont = path.Segment(1)
p = path.Node(0)
if type == _sketch.Bezier:
p1, p2 = controls
dir = p - p1
if not abs(dir):
dir = p - p2
else:
dir = p - p3
self.draw_arrow(arrow1, width, p, dir, rect)
if arrow2:
type, controls, p, cont = path.Segment(-1)
p3 = path.Node(-2)
if type == _sketch.Bezier:
p1, p2 = controls
dir = p - p2
if not abs(dir):
dir = p - p1
else:
dir = p - p3
self.draw_arrow(arrow2, width, p, dir, rect)
def draw_ellipse_arrows(self, trafo, start_angle, end_angle, arc_type,
rect = None):
if arc_type == const.ArcArc and self.line and start_angle != end_angle:
pi2 = pi / 2
width = self.properties.line_width
arrow1 = self.properties.line_arrow1
if arrow1 is not None:
pos = trafo(Polar(1, start_angle))
dir = trafo.DTransform(Polar(1, start_angle - pi2))
self.draw_arrow(arrow1, width, pos, dir, rect)
arrow2 = self.properties.line_arrow2
if arrow2 is not None:
pos = trafo(Polar(1, end_angle))
dir = trafo.DTransform(Polar(1, end_angle + pi2))
self.draw_arrow(arrow2, width, pos, dir, rect)
use_shm_images = 0
shm_images_supported = 0
class GraphicsDevice(SimpleGC, CommonDevice):
#
# Graphics device that allows complex fill and line properties
# XXX This class should have a different name
#
ximage = None
font_cache = None
old_font_cache = None
grid_style = default_grid_style
guide_style = default_guide_style
# the following flags may be used by the document and layer objects
# to determine which parts to draw, depending on whether the user
# marks them as visible or printable.
draw_visible = 1
draw_printable = 0
def __init__(self):
SimpleGC.__init__(self)
self.line = 0
self.fill = 0
self.properties = PropertyStack()
self.gradient_steps = config.preferences.gradient_steps_editor
self.images_drawn = 0
self.unknown_fonts = {}
self.failed_fonts = {}
def InitClip(self):
SimpleGC.InitClip(self)
self.images_drawn = 0
proc_fill = proc_line = 0
fill_rect = None
def set_properties(self, properties):
self.properties = properties
if properties.HasFill():
self.fill = 1
self.proc_fill = properties.IsAlgorithmicFill()
else:
self.fill = 0
self.proc_fill = 0
if properties.HasLine():
self.line = 1
self.proc_line = properties.IsAlgorithmicLine()
else:
self.line = 0
self.proc_line = 0
def SetProperties(self, properties, rect = None):
if not self.outline_mode:
self.fill_rect = rect
self.set_properties(properties)
else:
# if outline, set only font properties
self.properties.SetProperty(font = properties.font,
font_size = properties.font_size)
#
def activate_line(self):
self.properties.ExecuteLine(self)
def activate_fill(self):
self.properties.ExecuteFill(self, self.fill_rect)
#
# Patterns
#
def get_pattern_image(self):
width = self.widget.width
height = self.widget.height
winrect = self.doc_to_win(self.fill_rect)
left, top, right, bottom = map(int, map(round, winrect))
l = max(left, 0); r = min(right, width);
t = max(top, 0); b = min(bottom, height);
if type(self.clip_region) == PaxRegionType:
cx, cy, cw, ch = self.clip_region.ClipBox()
l = max(l, cx); r = min(r, cx + cw)
t = max(t, cy); b = min(b, cy + ch)
if l >= r or t >= b:
return None, None, None
image = PIL.Image.new('RGB', (r - l, b - t), (255, 255, 255))
trafo = Translation(-l, -t)(self.doc_to_win)
return image, trafo, (l, t)
def draw_pattern_image(self, image, pos):
if use_shm_images and self.images_drawn:
# force a shmimage to be drawn if ShmPutImage requests might
# be in the queue
self.widget.Sync()
self.create_ximage()
ximage = self.ximage
x, y = pos
w, h = image.size
_sketch.copy_image_to_ximage(self.visual, image.im, ximage, x, y, w, h)
if use_shm_images:
self.gc.ShmPutImage(ximage, x, y, x, y, w, h, 0)
self.images_drawn = 1
else:
self.gc.PutImage(ximage, x, y, x, y, w, h)
has_axial_gradient = 1
def AxialGradient(self, gradient, p0, p1):
# p0 and p1 may be PointSpecs
image, trafo, pos = self.get_pattern_image()
if image is None:
return
x0, y0 = trafo(p0)
x1, y1 = trafo(p1)
#import time
#_t = time.clock()
_sketch.fill_axial_gradient(image.im, gradient.Colors(), x0,y0, x1,y1)
#_t = time.clock() - _t
#print 'axial:', _t
self.draw_pattern_image(image, pos)
has_radial_gradient = 1
def RadialGradient(self, gradient, p, r0, r1):
# p may be PointSpec
image, trafo, pos = self.get_pattern_image()
if image is None:
return
x, y = trafo.DocToWin(p)
r0 = int(round(abs(trafo.DTransform(r0, 0))))
r1 = int(round(abs(trafo.DTransform(r1, 0))))
#import time
#_t = time.clock()
_sketch.fill_radial_gradient(image.im, gradient.Colors(), x, y, r0, r1)
#_t = time.clock() - _t
#print 'radial:', _t
self.draw_pattern_image(image, pos)
has_conical_gradient = 1
def ConicalGradient(self, gradient, p, angle):
# p may be PointSpec
image, trafo, pos = self.get_pattern_image()
if image is None:
return
cx, cy = trafo.DocToWin(p)
#import time
#_t = time.clock()
_sketch.fill_conical_gradient(image.im, gradient.Colors(), cx, cy,
-angle)
#_t = time.clock() - _t
#print 'conical:', _t
self.draw_pattern_image(image, pos)
use_pixmap_tile = 1
def TileImage(self, tile, trafo):
# XXX this could be faster with some caching.
width, height = self.doc_to_win(trafo).DTransform(tile.size)
width = int(round(width))
height = int(round(height))
if self.use_pixmap_tile and trafo.m12 == 0 and trafo.m21 == 0 \
and width * height < self.widget.width * self.widget.height:
# the image is only scaled. Use a tile pixmap
#
# there are other cases where this could be done:
# horizontal/vertical shearing, rotation by 90 degrees,...
# This should also be done like in DrawImage, i.e. use the
# integer coordintes of the transformed tile to determine if
# pixmaps can be used. This would be a little less precise,
# though.
# degenerate cases
if width == 0: width = 1
if height == 0: height = 1
ximage = self.create_sized_ximage(abs(width), abs(height))
pixmap = self.widget.CreatePixmap(abs(width), abs(height),
ximage.depth)
_sketch.copy_image_to_ximage(self.visual, tile.im, ximage,
0, 0, width, height)
gc = pixmap.CreateGC()
gc.PutImage(ximage, 0, 0, 0, 0, abs(width), abs(height))
self.gc.SetForegroundAndFill(pixmap)
x, y = trafo.DocToWin(0, 0)
self.gc.SetTSOrigin(x, y)
self.gc.FillRectangle(0, 0, self.widget.width, self.widget.height)
else:
image, temp_trafo, pos = self.get_pattern_image()
if image is None:
return
trafo = temp_trafo(trafo)
try:
_sketch.fill_transformed_tile(image.im, tile.im,
trafo.inverse())
self.draw_pattern_image(image, pos)
except SingularMatrix:
pass
#
# Outline Mode
#
# StartOutlineMode(COLOR) starts drawing everything unfilled with
# a solid line of color COLOR.
#
# EndOutlineMode() restores the previous mode.
#
# As long as there are always matching calls to StartOutlineMode
# and EndOutlineMode outline modes can be arbitrarily nested. This
# allows activating the outline mode globally in the canvas widget
# and overriding the color in indiviual layers or drawing only
# some layers in outline mode in different colors.
allow_outline = 1
def StartOutlineMode(self, color = None):
if self.allow_outline:
self.outline_mode = (self.properties, self.outline_mode)
if color is None:
properties = self.outline_style
else:
properties = PropertyStack()
properties.SetProperty(fill_pattern = EmptyPattern)
properties.AddStyle(SolidLine(color))
self.set_properties(properties)
def EndOutlineMode(self):
if self.allow_outline and self.outline_mode:
properties, self.outline_mode = self.outline_mode
self.set_properties(properties)
def IsOutlineActive(self):
return not not self.outline_mode
#
# Primitives
#
def Rectangle(self, trafo, clip = 0):
self.PushTrafo()
self.Concat(trafo)
pts = TransformRectangle(self.doc_to_win, UnitRect)
self.PopTrafo()
if type(pts) == TupleType:
if self.proc_fill:
if not clip:
self.PushClip()
self.ClipRect(pts)
self.properties.ExecuteFill(self, self.fill_rect)
if not clip:
self.PopClip()
clip = 0
elif self.fill:
self.properties.ExecuteFill(self, self.fill_rect)
apply(self.gc.FillRectangle, pts)
if self.line:
self.properties.ExecuteLine(self)
apply(self.gc.DrawRectangle, pts)
else:
if self.proc_fill:
if not clip:
self.PushClip()
self.ClipPolygon(pts)
self.properties.ExecuteFill(self, self.fill_rect)
if not clip:
self.PopClip()
clip = 0
elif self.fill:
self.properties.ExecuteFill(self, self.fill_rect)
self.gc.FillPolygon(pts, X.Convex, X.CoordModeOrigin)
if self.line:
self.properties.ExecuteLine(self)
self.gc.DrawLines(pts, X.CoordModeOrigin)
if clip:
if type(pts) == TupleType:
self.ClipRect(pts)
else:
self.ClipPolygon(pts)
def RoundedRectangle(self, trafo, radius1, radius2, clip = 0):
path = _sketch.RoundedRectanglePath(trafo, radius1, radius2)
self.MultiBezier((path,), None, clip)
def SimpleEllipse(self, trafo, start_angle, end_angle, arc_type,
rect = None, clip = 0):
trafo2 = self.doc_to_win(trafo)
if trafo2.m12 == 0.0 and trafo2.m21 == 0.0 and not self.proc_fill \
and start_angle == end_angle and not clip:
x1, y1 = trafo2.DocToWin(1, 1)
x2, y2 = trafo2.DocToWin(-1, -1)
if x1 > x2:
t = x1; x1 = x2; x2 = t
if y1 > y2:
t = y1; y1 = y2; y2 = t
w = x2 - x1
h = y2 - y1
if self.fill:
self.properties.ExecuteFill(self, self.fill_rect)
self.gc.FillArc(x1, y1, w, h, 0, 23040) # 360 * 64
if self.line:
self.properties.ExecuteLine(self)
self.gc.DrawArc(x1, y1, w, h, 0, 23040)
else:
if self.line:
line = self.activate_line
else:
line = None
if self.fill:
fill = self.activate_fill
else:
fill = None
if start_angle != end_angle:
arc = _sketch.approx_arc(start_angle, end_angle, arc_type)
else:
arc = circle_path
# pass rect as None, because trafo2 is not really the
# viewport transformation
_sketch.draw_multipath(self.gc, trafo2, line, fill,
self.PushClip, self.PopClip,
self.ClipRegion, None, (arc,),
CreateRegion(), self.proc_fill, clip)
self.draw_ellipse_arrows(trafo, start_angle, end_angle, arc_type,
rect)
def MultiBezier(self, paths, rect = None, clip = 0):
if self.line:
line = self.activate_line
else:
line = None
if self.fill:
fill = self.activate_fill
else:
fill = None
_sketch.draw_multipath(self.gc, self.doc_to_win, line, fill,
self.PushClip, self.PopClip, self.ClipRegion,
rect, paths, CreateRegion(), self.proc_fill,
clip)
if self.line:
self.draw_arrows(paths, rect)
def draw_text_on_gc(self, gc, text, trafo, font, font_size, cache = None):
self.PushTrafo()
try:
self.Concat(trafo)
self.Scale(font_size)
up = self.doc_to_win.DTransform(0, 1)
if abs(up) >= config.preferences.greek_threshold:
ptrafo = FlipY(self.doc_to_win)
xlfd = font.GetXLFD(ptrafo)
if (ptrafo and (ptrafo.m12 != 0 or ptrafo.m21 != 0
or ptrafo.m11 > 40 or ptrafo.m11 < 0
or ptrafo.m22 > 40 or ptrafo.m22 < 0)):
xlfd = '%s[%s]' % (xlfd, _sketch.xlfd_char_range(text))
try:
xfont = self.load_font(xlfd, cache)
except RuntimeError, val:
# We must be careful here when reporting this to the
# user. If warn pops up a message box and the user
# clicks OK, the window gets another expose event
# and sketch tries to draw the text again which will
# also fail. To avoid infinite loops we try to
# report unknown fonts only once for a given font.
if not self.unknown_fonts.has_key(font.PostScriptName()):
warn(USER, _("Cannot load %(font)s:\n%(text)s"),
font = `xlfd`, text = val)
self.unknown_fonts[font.PostScriptName()] = 1
# Use a font that will hopefully be always available.
# XXX Is there a better way to handle this situation?
# We might try times roman with the same size and trafo
xfont = self.load_font('fixed', None)
gc.SetFont(xfont)
pos = font.TypesetText(text)
pos = map(self.DocToWin, pos)
for i in range(len(text)):
x, y = pos[i]
gc.DrawString(x, y, text[i])
else:
# 'greek'. XXX is this really necessary. It avoids
# rendering fonts that are too small to read.
pos = font.TypesetText(text)
pos = map(self.DocToWin, pos)
ux, uy = up
lx = ux / 2; ly = uy / 2
uppercase = string.uppercase
draw = gc.DrawLine # we should draw rectangles...
for i in range(len(text)):
x, y = pos[i]
if text[i] in uppercase:
draw(x, y, int(round(x + ux)), int(round(y + uy)))
else:
draw(x, y, int(round(x + lx)), int(round(y + ly)))
finally:
self.PopTrafo()
def DrawText(self, text, trafo = None, clip = 0, cache = None):
if text and self.properties.font:
if self.fill or clip:
if self.proc_fill or clip:
bitmap, bitmapgc = self.create_clip_bitmap()
self.draw_text_on_gc(bitmapgc, text, trafo,
self.properties.font,
self.properties.font_size,
cache)
if not clip:
self.PushClip()
self.ClipBitmap(bitmap)
self.properties.ExecuteFill(self, self.fill_rect)
if not self.proc_fill:
w, h = bitmap.GetGeometry()[3:5]
bitmap.CopyPlane(self.widget, self.gc, 0, 0, w, h,
0, 0, 1)
if not clip:
self.PopClip()
else:
self.properties.ExecuteFill(self)
self.draw_text_on_gc(self.gc, text, trafo,
self.properties.font,
self.properties.font_size,
cache)
elif self.IsOutlineActive():
# in outline mode, draw text filled with the current
# outline color, because we can't draw outlined text at
# the moment. We could draw a rectangle, though. (?)
self.properties.ExecuteLine(self)
self.draw_text_on_gc(self.gc, text, trafo,
self.properties.font,
self.properties.font_size,
cache)
def ResetFontCache(self):
self.font_cache = {}
def load_font(self, xlfd, cache):
font_cache = self.font_cache
complex_text = self.complex_text
if self.failed_fonts.has_key(xlfd):
# the same xlfd failed before. use fixed as fallback
# immediately to avoid delays. some servers apparantly take
# very long to decide that they can't load a font.
xlfd = 'fixed'
if cache and cache.has_key(id(self)):
old_xlfd, old_font = cache[id(self)]
if old_xlfd == xlfd:
font_cache[xlfd] = old_font
return old_font
if complex_text is not None:
cache = complex_text.cache
key = id(self), complex_text.idx
if cache.has_key(key):
old_xlfd, old_font = cache[key]
if old_xlfd == xlfd:
font_cache[xlfd] = old_font
return old_font
cache = None
if font_cache is not None and font_cache.has_key(xlfd):
font = font_cache[xlfd]
else:
#print 'load font', xlfd
try:
font = self.widget.LoadQueryFont(xlfd)
except RuntimeError:
self.failed_fonts[xlfd] = 1
raise
if font_cache is not None:
font_cache[xlfd] = font
if cache is not None:
cache[id(self)] = (xlfd, font)
elif complex_text is not None:
complex_text.cache[(id(self), complex_text.idx)] = (xlfd, font)
return font
complex_text = None
def BeginComplexText(self, clip = 0, cache = None):
if self.fill or clip or self.IsOutlineActive():
if cache is None:
cache = {}
if self.proc_fill or clip:
bitmap, gc = self.create_clip_bitmap()
self.complex_text = Empty(bitmap = bitmap, gc = gc,
clip = clip, cache = cache, idx = 0)
else:
if self.fill:
self.properties.ExecuteFill(self)
else:
# outline mode
self.properties.ExecuteLine(self)
self.complex_text = Empty(gc = self.gc, clip = 0,
cache = cache, idx = 0)
else:
self.complex_text = None
def DrawComplexText(self, text, trafo, font, font_size):
if self.complex_text is not None:
self.draw_text_on_gc(self.complex_text.gc, text, trafo, font,
font_size)
self.complex_text.idx = self.complex_text.idx + 1
def EndComplexText(self):
if self.complex_text is not None:
if self.complex_text.clip or self.proc_fill:
bitmap = self.complex_text.bitmap
self.PushClip()
self.ClipBitmap(bitmap)
self.properties.ExecuteFill(self, self.fill_rect)
if not self.proc_fill:
w, h = bitmap.GetGeometry()[3:5]
bitmap.CopyPlane(self.widget, self.gc, 0, 0, w, h,
0, 0, 1)
if not self.complex_text.clip:
self.PopClip()
self.complex_text = None
def create_ximage(self):
global use_shm_images
if not self.ximage:
w = self.widget
if use_shm_images and not shm_images_supported:
warn(INTERNAL,
'tried to use unsupported shared memory images\n')
use_shm_images = 0
if use_shm_images:
try:
self.ximage = w.ShmCreateImage(w.depth, X.ZPixmap,
None, w.width, w.height, 1)
except:
# Creating a shared memory image failed. Print a
# message and don't use shmimages again. A likely
# reason for this is that the test for shmimages in
# pax succeeded but the ShmCreateImage here fails
# because the limit for shm segments is too low, as
# it is by default on Solaris.
warn(INTERNAL, _("Can't create shared memory image: %s"),
sys.exc_info()[1])
use_shm_images = 0
if not self.ximage:
self.ximage = self.create_sized_ximage(w.width, w.height)
def create_sized_ximage(self, width, height):
w = self.widget
depth = w.depth
if depth > 16:
bpl = 4 * width
elif depth > 8:
bpl = ((2 * width + 3) / 4) * 4
elif depth == 8:
bpl = ((width + 3) / 4) * 4
else:
raise SketchError('unsupported depth for images')
return w.CreateImage(w.depth, X.ZPixmap, 0, None, width, height,
32, bpl)
def DrawImage(self, image, trafo, clip = 0):
w, h = image.size
if self.IsOutlineActive():
self.PushTrafo()
self.Concat(trafo)
self.DrawRectangle(Point(0, 0), Point(w, h))
self.PopTrafo()
return
self.create_ximage()
ximage = self.ximage
if use_shm_images and self.IsClipping() and self.images_drawn:
# force a shmimage to be drawn if complex clipping is done
# or ShmPutImage requests might be in the queue.
self.widget.Sync()
self.images_drawn = 0
llx, lly = self.DocToWin(trafo.offset())
lrx, lry = self.DocToWin(trafo(w, 0))
ulx, uly = self.DocToWin(trafo(0, h))
urx, ury = self.DocToWin(trafo(w, h))
if llx == ulx and lly == lry:
if llx < lrx:
sx = llx; w = lrx - llx + 1
else:
sx = lrx; w = lrx - llx - 1
if uly < lly:
sy = uly; h = lly - uly + 1
else:
sy = lly; h = lly - uly - 1
_sketch.copy_image_to_ximage(self.visual, image.im, ximage,
sx, sy, w, h)
if w < 0: w = -w
if h < 0: h = -h
if not clip:
self.PushClip()
self.ClipRect((sx, sy, w, h))
else:
self.PushTrafo()
self.Concat(trafo)
self.Concat(Trafo(1, 0, 0, -1, 0, h))
inverse = self.win_to_doc
dtw = self.DocToWin
ulx, uly = dtw(0, 0)
urx, ury = dtw(0, h)
llx, lly = dtw(w, 0)
lrx, lry = dtw(w, h)
self.PopTrafo()
sx = min(ulx, llx, urx, lrx)
ex = max(ulx, llx, urx, lrx)
sy = min(uly, lly, ury, lry)
ey = max(uly, lly, ury, lry)
if type(self.clip_region) == PaxRegionType:
cx, cy, cw, ch = self.clip_region.ClipBox()
cex = cx + cw; cey = cy + ch
if cx >= ex or cex <= sx or cy >= ey or cey <= sy:
return
if cx > sx and cx < ex: sx = cx
if cex < ex and cex > sx: ex = cex
if cy > sy and cy < ey: sy = cy
if cey < ey and cey > sy: ey = cey
w = ex - sx
h = ey - sy
region = self.widget.CreateRegion()
_sketch.transform_to_ximage(self.visual, inverse,
image.im, ximage, sx, sy, w, h, region)
if not clip:
self.PushClip()
self.ClipRegion(region)
if sx+w <= 0 or sx >= ximage.width or sy+h <= 0 or sy >= ximage.height:
if not clip:
self.PopClip()
return
if sx < 0:
w = w + sx
sx = 0
if sx + w > ximage.width:
w = ximage.width - sx
if sy < 0:
h = h + sy
sy = 0
if sy + h > ximage.height:
h = ximage.height - sy
if use_shm_images:
self.gc.ShmPutImage(ximage, sx, sy, sx, sy, w, h, 0)
self.images_drawn = 1
else:
self.gc.PutImage(ximage, sx, sy, sx, sy, w, h)
if not clip:
self.PopClip()
def DrawEps(self, data, trafo):
if not data.image or self.IsOutlineActive():
w, h = data.Size()
self.PushTrafo()
self.Concat(trafo)
self.DrawRectangle(Point(0, 0), Point(w, h))
self.PopTrafo()
else:
resolution = config.preferences.eps_preview_resolution
self.DrawImage(data.image, trafo(Scale(72.0 / resolution)))
#
#
def WindowResized(self, width, height):
self.ximage = None
SimpleGC.WindowResized(self, width, height)
#
#
def DrawGrid(self, orig_x, orig_y, xwidth, ywidth, rect):
# Draw a grid with a horitontal width XWIDTH and vertical width
# YWIDTH whose origin is at (ORIG_X, ORIG_Y). RECT gives the
# region of the document for which the grid has to be drawn.
# RECT is usually the parameter of the same name of the
# Draw/DrawShape methods of the various graphics objects and the
# document/layer Note: This functions assumes that doc_to_win is
# in its initial state
self.SetProperties(self.grid_style)
xwinwidth = self.LengthToWinFloat(xwidth)
if not xwinwidth:
if __debug__:
pdebug(None, 'GraphicsDevice.DrawGrid: zero winwidth')
return
ywinwidth = self.LengthToWinFloat(ywidth)
if not ywinwidth:
if __debug__:
pdebug(None, 'GraphicsDevice.DrawGrid: zero winwidth')
return
# make the minimum distance between drawn points at least 5
# pixels XXX: should be configurable
if xwinwidth < 5:
xwinwidth = (int(5.0 / xwinwidth) + 1) * xwinwidth
xwidth = self.LengthToDoc(xwinwidth)
if ywinwidth < 5:
ywinwidth = (int(5.0 / ywinwidth) + 1) * ywinwidth
ywidth = self.LengthToDoc(ywinwidth)
startx = int((rect.left - orig_x) / xwidth) * xwidth + orig_x
starty = int((rect.top - orig_y) / ywidth) * ywidth + orig_y
winx, winy = self.DocToWinPoint((startx, starty))
nx = int((rect.right - rect.left) / xwidth) + 2
ny = int((rect.top - rect.bottom) / ywidth) + 2
if self.line:
self.properties.ExecuteLine(self)
_sketch.DrawGrid(self.gc, winx, winy, xwinwidth, ywinwidth, nx, ny)
def DrawGuideLine(self, point, horizontal):
if self.line:
self.properties.ExecuteLine(self)
self.gc.line_style = X.LineOnOffDash
self.gc.dashes = 5
x, y = self.DocToWin(point)
if horizontal:
self.gc.DrawLine(0, y, self.widget.width, y)
else:
self.gc.DrawLine(x, 0, x, self.widget.height)
self.gc.line_style = X.LineSolid
def DrawPageOutline(self, width, height):
# Draw the outline of the page whose size is given by width and
# height. The page's lower left corner is at (0,0) and its upper
# right corner at (width, height) in doc coords. The outline is
# drawn as a rectangle with a thin shadow.
self.gc.line_width = 0
self.gc.line_style = X.LineSolid
left, bottom = self.DocToWin(0, 0)
right, top = self.DocToWin(width, height)
sw = 5 # shadow width XXX: should be configurable ?
w = right - left
h = bottom - top
self.SetFillColor(StandardColors.gray)
self.gc.FillRectangles([(left + sw, bottom, w + 1, sw + 1),
(right, top + sw, sw + 1, h + 1)])
self.SetFillColor(StandardColors.black)
self.gc.DrawRectangle(left, top, w, h)
#
# Class InvertingDevice
#
# Draws objects always in outline mode, regardless of the object's
# properties. Also draws with function = GXxor.
#
# This class defines a few additional drawing methods that are used by
# the primitives Rectangle and PolyBezier during interactive creation
# and dragging
#
# DummyLineStyle is needed for the InvertingDevice and the HitTestDevice
class DummyAttr(PropertyStack):
def ExecuteLine(self, gc):
pass
dummyLineStyle = DummyAttr(SolidLine(color_0))
class InvertingDevice(GraphicsDevice):
normal_line_style = X.LineSolid
handle_line_style = X.LineOnOffDash
caret_line_style = X.LineSolid
caret_line_width = 2
def __init__(self):
GraphicsDevice.__init__(self)
self.handle_size = 3
self.small_handle_size = 2
self.properties = dummyLineStyle
self.fill = 0
self.line = 1
self.gc = None
self.font_cache = {}
def init_gc(self, widget, **gcargs):
self.visual = color.skvisual
line_width = config.preferences.editor_line_width
self.gc = widget.CreateGC(foreground = ~0,
function = X.GXxor,
background = 0,
line_width = line_width,
line_style = self.normal_line_style)
self.widget = widget
# make sure that the properties are not changed
def SetProperties(self, properties, rect = None):
pass
def IsOutlineActive(self):
return 1
# Bezier and Line are currently only needed for the bezier objects
# during a drag
def Bezier(self, p1, p2, p3, p4):
dtw = self.DocToWin
pts = dtw(p1) + dtw(p2) + dtw(p3) + dtw(p4)
apply(_sketch.DrawBezier, (self.gc,) + pts)
def Line(self, p1, p2):
if self.line:
startx, starty = self.DocToWin(p1)
endx, endy = self.DocToWin(p2)
self.gc.DrawLine(startx, starty, endx, endy)
# draw a rectangular 'handle' at P. The size of the handle is given
# by self.handle_size and is always in window coordinates (i.e. is
# independent of scaling)
def DrawRectHandle(self, p, filled = 1):
x, y = self.DocToWin(p)
size = self.handle_size
x = x - size
y = y - size
if filled:
self.gc.FillRectangle(x, y, 2 * size + 1, 2 * size + 1)
else:
self.gc.DrawRectangle(x, y, 2 * size, 2 * size)
def DrawSmallRectHandle(self, p, filled = 1):
x, y = self.DocToWin(p)
size = self.small_handle_size
x = x - size
y = y - size
if filled:
self.gc.FillRectangle(x, y, 2 * size + 1, 2 * size + 1)
else:
self.gc.DrawRectangle(x, y, 2 * size, 2 * size)
def DrawCircleHandle(self, p, filled = 1):
x, y = self.DocToWin(p)
size = self.handle_size
x = x - size
y = y - size
if filled:
# 23040 = 360 * 64
self.gc.FillArc(x, y, 2 * size + 1, 2 * size + 1, 0, 23040)
else:
self.gc.DrawArc(x, y, 2 * size, 2 * size, 0, 23040)
def DrawSmallCircleHandle(self, p, filled = 1):
x, y = self.DocToWin(p)
size = self.small_handle_size
x = x - size
y = y - size
if filled:
# 23040 = 360 * 64
self.gc.FillArc(x, y, 2 * size + 1, 2 * size + 1, 0, 23040)
else:
self.gc.DrawArc(x, y, 2 * size, 2 * size, 0, 23040)
def DrawSmallRectHandleList(self, pts, filled = 1):
pts = map(self.doc_to_win.DocToWin, pts)
size = self.small_handle_size
size2 = 2 * size
if filled:
size = size + 1
rects = []
pts.sort()
lx = ly = None
for x, y in pts:
if y != ly or x != lx:
rects.append((x - size, y - size, size2, size2))
lx = x
ly = y
if rects:
if filled:
self.gc.FillRectangles(rects)
else:
self.gc.DrawRectangles(rects)
def DrawHandleLine(self, start, end):
self.gc.line_style = self.handle_line_style
self.DrawLine(start, end)
self.gc.line_style = self.normal_line_style
def DrawRubberRect(self, start, end):
self.gc.line_style = self.handle_line_style
self.DrawRectangle(start, end)
self.gc.line_style = self.normal_line_style
def DrawPixmapHandle(self, p, pixmap):
x, y = self.DocToWin(p)
width, height = pixmap.GetGeometry()[3:5]
x = x - width / 2
y = y - height / 2
pixmap.CopyPlane(self.widget, self.gc, 0, 0, width, height, x, y, 1)
def DrawCaretHandle(self, p, up):
line_width = self.gc.line_width
self.gc.line_width = self.caret_line_width
self.gc.line_style = self.caret_line_style
self.DrawLine(p, p + up)
self.gc.line_style = self.normal_line_style
self.gc.line_width = line_width
#
# Class HitTestDevice
#
pixmap_width_2 = 4
pixmap_width = 2 * pixmap_width_2 + 1
hit_properties = PropertyStack()
hit_properties.SetProperty(fill_pattern = SolidPattern(color_1))
hit_properties.AddStyle(SolidLine(color_1, width = 3))
class HitTestDevice(GraphicsDevice):
outline_style = hit_properties
def __init__(self):
GraphicsDevice.__init__(self)
self.properties = hit_properties
self.fill = 1
self.line = 1
self.gc = None
def init_gc(self, widget, **gcargs):
self.pixmap = widget.CreatePixmap(pixmap_width, pixmap_width, 1)
self.gc = self.pixmap.CreateGC(foreground = 1, line_width = 3)
self.visual = color.skvisual
# make sure that the properties are not changed
def SetProperties(self, properties, rect = None):
if properties.HasLine():
self.line_width = properties.line_width
else:
self.line_width = 0
#
#
def SetViewportTransform(self, scale, doc_to_win, win_to_doc):
GraphicsDevice.SetViewportTransform(self, scale, doc_to_win,
win_to_doc)
self.hit_radius_doc = self.LengthToDoc(self.hit_radius)
hit_properties.SetProperty(line_width = self.hit_radius_doc * 2)
#
# Detect various `hits'
#
hit_radius = 2
def SetHitRadius(self, radius):
self.hit_radius = radius
def HitRadiusDoc(self):
return self.LengthToDoc(self.hit_radius)
def HitRectAroundPoint(self, p):
rad = self.HitRadiusDoc()
return Rect(p.x - rad, p.y - rad, p.x + rad, p.y + rad)
def LineHit(self, start, end, p, line_width = 0):
radius = self.hit_radius / self.scale
if line_width:
w = line_width / 2
if radius < w:
radius = w
# check bounding box
if p.x < min(start.x, end.x) - radius:
return 0
if p.x > max(start.x, end.x) + radius:
return 0
if p.y < min(start.y, end.y) - radius:
return 0
if p.y > max(start.y, end.y) + radius:
return 0
# check if line is hit
try:
d = end - start
len = abs(d)
if len < 1:
return abs(start.x - p.x) <= radius \
and abs(start.y - p.y) <= radius
off = p - start
dist = abs((float(off.x) * d.y - float(off.y) * d.x) / len)
linepos = (off * d) / len
return dist <= radius and linepos > 0 and linepos < len
except OverflowError:
warn(INTERNAL, 'HitTestDevice.LineHit: start = %s end = %s p = %s',
start, end, p)
return 0
def ParallelogramHit(self, p, trafo, maxx, maxy, filled, properties=None,
ignore_outline_mode = 0):
filled = filled and (ignore_outline_mode or not self.outline_mode)
if filled:
try:
inverse = trafo.inverse()
x, y = inverse(p)
return 0 <= x <= maxx and 0 <= y <= maxy
except SingularMatrix:
if properties is not None and properties.HasLine():
properties = defaultLineStyle
return self.ParallelogramHit(p, trafo, maxx, maxy,
0, properties)
if self.outline_mode or (properties is not None
and properties.HasLine()):
p1 = trafo.offset()
p2 = trafo(0, maxy)
p3 = trafo(maxx, 0)
p4 = trafo(maxx, maxy)
if self.outline_mode:
line_width = 0
else:
line_width = properties.line_width
return self.LineHit(p1, p2, p, line_width)\
or self.LineHit(p1, p3, p, line_width) \
or self.LineHit(p2, p4, p, line_width) \
or self.LineHit(p3, p4, p, line_width)
def SimpleEllipseHit(self, p, trafo, start_angle, end_angle, arc_type,
properties, filled, ignore_outline_mode = 0):
# Hmm, the ellipse is not that simple anymore, maybe we should
# change the name?
filled = filled and (ignore_outline_mode or not self.outline_mode)
try:
inverse = trafo.inverse()
p2 = inverse(p)
dist, phi = p2.polar()
has_line = properties.HasLine() or self.outline_mode
if has_line:
# Check whether p is on the outline of the complete
# ellipse. This check is not entirely correct, but it
# works well enough for now.
if not self.outline_mode:
line_width = properties.line_width
else:
line_width = 0
d = max(line_width / 2, self.HitRadiusDoc())
d = abs(inverse.DTransform(Point(d, 0)))
border_hit = abs(1.0 - dist) < d
else:
border_hit = 0
if start_angle == end_angle:
# The most common case: a complete ellipse
if filled and dist <= 1.0:
# p is inside of the ellipse -> Hit!
return 1
# Either ellipse is not filled or dist > 1.0. NOw it
# only depends on the outline.
return border_hit
else:
# The ellipse is not complete. Now it depends on the
# arc_type.
if phi < 0:
phi = phi + pi + pi # map phi into the range 0 - 2*PI
if start_angle < end_angle:
between = start_angle <= phi <= end_angle
else:
between = start_angle <= phi or phi <= end_angle
center = trafo.offset()
start = Polar(start_angle)
end = Polar(end_angle)
if arc_type == ArcPieSlice:
if between:
# p is somewhere in the painted sector. Just
# like for a full ellipse:
if filled and dist <= 1:
return 1
return border_hit
else:
# p is outside of the painted sector. It might
# still be on the lines:
if has_line:
if (self.LineHit(center, trafo(start), p,
line_width)
or self.LineHit(center, trafo(end), p,
line_width)):
return 1
# no line was hit
return 0
else:
# ArcArc or ArcChord.
if filled:
# this is identical for both arc_types.
# Strategy: p is inside of the ellipse if it is
# in the intersection of the full ellipse the
# half plane defined by the line through start
# and end (the plane to the left of the vector
# (start - end)).
v = start - end
d = p2 - end
in_plane = (v.x * d.y - v.y * d.x) > 0
if dist <= 1 and in_plane:
return 1
#
if between and border_hit:
return 1
if has_line and arc_type == ArcChord:
return self.LineHit(trafo(start), trafo(end), p,
line_width)
return 0
except SingularMatrix:
# ellipse degenerates into a line
# XXX we should use the eigenvectors. The following code is
# incorrect.
start = trafo.offset()
right = Point(trafo.m11, trafo.m21)
up = Point(trafo.m12, trafo.m22)
if abs(up) > abs(right):
dir = up
else:
dir = right
return self.LineHit(start - dir, start + dir, p,
properties.line_width)
def MultiBezierHit(self, paths, p, properties, filled,
ignore_outline_mode = 0):
x, y = self.DocToWin(p)
filled = filled and (ignore_outline_mode or not self.outline_mode)
result = _sketch.test_transformed(paths, self.doc_to_win, x, y,
filled)
if properties.HasLine():
line_width = properties.line_width
else:
line_width = 0
if result or self.outline_mode or self.LengthToWin(line_width) <= 1:
return result
odtw = self.doc_to_win
self.doc_to_win = Trafo(odtw.m11, odtw.m21, odtw.m12, odtw.m22,
-x + pixmap_width_2 + odtw.v1,
-y + pixmap_width_2 + odtw.v2)
top_left = self.WinToDoc(x - pixmap_width_2 - 1,
y - pixmap_width_2 - 1)
bot_right = self.WinToDoc(x + pixmap_width_2 + 1,
y + pixmap_width_2 + 1)
rect = Rect(top_left, bot_right)
self.gc.function = X.GXclear
self.gc.FillRectangle(0, 0, pixmap_width + 1, pixmap_width + 1)
self.gc.function = X.GXcopy
self.fill = 0
line_width = max(line_width, hit_properties.line_width)
undo = self.properties.SetProperty(line_width = line_width,
line_pattern = SolidPattern(color_1))
self.MultiBezier(paths, rect)
self.doc_to_win = odtw
Undo(undo)
self.fill = 1
return _sketch.GetPixel(self.gc, pixmap_width_2, pixmap_width_2)
#
# Initialization that can only be done after widgets were realized
#
_init_from_widget_done = 0
def check_for_shm_images(widget):
global shm_images_supported
shm_images_supported = 0
try:
img = widget.ShmCheckExtension()
except RuntimeError, exc:
print "Exception in ShmCheckExtension:", exc
img = None
if img is not None:
if img.depth not in (15, 16, 24, 32, 8):
# XXX: warn
print 'depth =', img.depth
return
if img.format != X.ZPixmap:
# XXX: warn
print 'format =', img.format
return
shm_images_supported = 1
def InitFromWidget(widget):
global _init_from_widget_done, use_shm_images
if not _init_from_widget_done:
color.InitFromWidget(widget) # make certain that color is initialized
check_for_shm_images(widget)
if shm_images_supported:
warn(INTERNAL, 'shared memory images supported')
use_shm_images = 1
else:
warn(INTERNAL, 'shared memory images not supported')
use_shm_images = 0
_init_from_widget_done = 1
|