#
# Copyright (c) 2002, 2003, 2004 Art Haas
#
# This file is part of PythonCAD.
# 
# PythonCAD is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# PythonCAD 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 General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with PythonCAD; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# gtk dimension code
#

import pygtk
pygtk.require('2.0')
import gtk
import pango

import Generic.dimension as dimension

#
# format the dimesion text string
#

def format_dim_layout(layout, ds):
    _attlist = pango.AttrList()
    _family = pango.AttrFamily(ds.getFamily())
    _family.start_index = 0
    _family.end_index = -1
    _attlist.insert(_family)
    _sty = pango.AttrStyle(ds.getStyle())
    _sty.start_index = 0
    _sty.end_index = -1
    _attlist.insert(_sty)
    _wt = pango.AttrWeight(ds.getWeight())
    _wt.start_index = 0
    _wt.end_index = -1
    _attlist.insert(_wt)
    _r, _g, _b = ds.getColor().getColors()
    _rs = int(65535 * (_r/255.0))
    _gs = int(65535 * (_g/255.0))
    _bs = int(65535 * (_b/255.0))
    _fg = pango.AttrForeground(_rs, _gs, _bs)
    _fg.start_index = 0
    _fg.end_index = -1
    _attlist.insert(_fg)
    try:
        _sz = pango.AttrSize(pango.SCALE * ds.getSize()) # need to scale
    except:
        _sz = pango.AttrSize(1024 * ds.getSize()) # need to scale
    _sz.start_index = 0
    _sz.end_index = -1
    _attlist.insert(_sz)
    layout.set_attributes(_attlist)

#
# linear dimension motion-notify handler
#

def dim_pts_motion_notify_cb(gtkimage, widget, event, tool):
    _tx, _ty = tool.getLocation()
    _px, _py = gtkimage.coordToPixTransform(_tx, _ty)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    _segs = []
    if _cp is not None:
        _xc, _yc = _cp
        _segs.append((_px, _py, _xc, _yc))
    tool.setCurrentPoint(_x, _y)
    _segs.append((_px, _py, _x, _y))
    widget.window.draw_segments(_gc, _segs)

def dim_txt_motion_notify_cb(gtkimage, widget, event, tool):
    _ix, _iy = gtkimage.getPoint()
    _gc = gtkimage.getGC()
    _ex = int(event.x)
    _ey = int(event.y)
    _cp = tool.getCurrentPoint()
    _dim = tool.getDimension()
    _bar1, _bar2 = _dim.getDimBars()
    _crossbar = _dim.getDimCrossbar()
    _segs = []
    if _cp is not None:
        _ep1, _ep2 = _bar1.getEndpoints()
        _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_px1, _py1, _px2, _py2))
        _ep1, _ep2 = _bar2.getEndpoints()
        _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_px1, _py1, _px2, _py2))
        _ep1, _ep2 = _crossbar.getEndpoints()
        _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_px1, _py1, _px2, _py2))
    tool.setCurrentPoint(_ex, _ey)
    _dim.setLocation(_ix, _iy)
    _dim.calcDimValues(False)
    _ep1, _ep2 = _bar1.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    _ep1, _ep2 = _bar2.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    _ep1, _ep2 = _crossbar.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    widget.window.draw_segments(_gc, _segs)

#
# draw linear, horizontal, and vertical dimensions
#

def draw_linear_dimension(gtkimage, dim, active):
    _gc = gtkimage.getGC()
    _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT,
                            gtk.gdk.JOIN_MITER)
    _gc.set_function(gtk.gdk.COPY)
    _da = gtkimage.getDA()
    _window = _da.window
    _ctx = _da.get_pango_context()
    _width, _height = gtkimage.getSize()
    _pixmap = gtkimage.getPixmap()
    # dim.update()
    if dim.isModified():
        dim.calcDimValues()
        dim.reset()
    _bar1, _bar2 = dim.getDimBars()
    _crossbar = dim.getDimCrossbar()
    if active:
        _dim_color = dim.getColor()
    else:
        _dim_color = gtkimage.getOption('INACTIVE_LAYER_COLOR')
    if gtkimage.hasGTKColor(_dim_color):
        _color = gtkimage.getGTKColor(_dim_color)
    else:
        _cstring = str(_dim_color)
        _color = _da.get_colormap().alloc_color(_cstring)
        gtkimage.saveGTKColor(_dim_color, _color)
    _gc.set_foreground(_color)
    #
    # draw dimension lines
    #
    _segs = []
    _ep1, _ep2 = _bar1.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    _ep1, _ep2 = _bar2.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    _ep1, _ep2 = _crossbar.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    #
    # draw endpoints
    #
    _etype = dim.getEndpointType()
    _mpts = _crossbar.getCrossbarPoints()
    _mp = _mpts[0]
    _mp1x, _mp1y = gtkimage.coordToPixTransform(_mp[0], _mp[1])
    _mp = _mpts[1]
    _mp2x, _mp2y = gtkimage.coordToPixTransform(_mp[0], _mp[1])
    if (_etype == dimension.Dimension.ARROW or
        _etype == dimension.Dimension.FILLED_ARROW or
        _etype == dimension.Dimension.SLASH):
        _epts = _crossbar.getMarkerPoints()
        assert len(_epts) == 4, "Bad marker point count: %d" % len(_epts)
        if _etype == dimension.Dimension.ARROW:
            _ep = _epts[0]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_px1, _py1, _mp1x, _mp1y))
            _ep = _epts[1]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_px1, _py1, _mp1x, _mp1y))
            _ep = _epts[2]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_px1, _py1, _mp2x, _mp2y))
            _ep = _epts[3]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_px1, _py1, _mp2x, _mp2y))
        elif _etype == dimension.Dimension.FILLED_ARROW:
            _tuples = []
            _ep = _epts[0]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_px1, _py1))
            _ep = _epts[1]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_px1, _py1))
            _tuples.append((_mp1x, _mp1y))
            _pixmap.draw_polygon(_gc, gtk.TRUE, _tuples)
            del _tuples[:]
            _ep = _epts[2]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_px1, _py1))
            _ep = _epts[3]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_px1, _py1))
            _tuples.append((_mp2x, _mp2y))
            _pixmap.draw_polygon(_gc, gtk.TRUE, _tuples)
        elif _etype == dimension.Dimension.SLASH:
            _ep = _epts[0]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _ep = _epts[1]
            _px2, _py2 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_px1, _py1, _px2, _py2))
            _ep = _epts[2]
            _px1, _py1 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _ep = _epts[3]
            _px2, _py2 = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_px1, _py1, _px2, _py2))
    elif _etype == dimension.Dimension.CIRCLE:
        _upp = gtkimage.getUnitsPerPixel()
        _size = dim.getEndpointSize()/2.0
        _width = int(_size/_upp)
        _cw = _ch = _width * 2
        _xmin = _mp1x - _width
        _ymin = _mp1y - _width
        _pixmap.draw_arc(_gc, gtk.TRUE, _xmin, _ymin, _cw, _ch,
                         0, (360 * 64))
        _xmin = _mp2x - _width
        _ymin = _mp2y - _width
        _pixmap.draw_arc(_gc, gtk.TRUE, _xmin, _ymin, _cw, _ch,
                         0, (360 * 64))
    else:
        pass # catchall for now 
    #
    # draw the dimension bars, crossbars, and endpoints
    #
    _pixmap.draw_segments(_gc, _segs)
    #
    # draw dimension text
    #
    _dx, _dy = dim.getLocation()
    _pdx, _pdy = gtkimage.coordToPixTransform(_dx, _dy)    
    _dlen = dim.calculate()
    _hlen = gtkimage.scaleLength(_dlen)
    _dims = dim.getDimensions(_hlen)
    _dlines = []
    _pri_layout = pango.Layout(_ctx)
    try:
        _pri_layout.set_text(_dims[0])
    except:
        _pri_layout.set_text(_dims[0], len(_dims[0]))
    _ds = dim.getPrimaryDimstring()
    format_dim_layout(_pri_layout, _ds)
    _pri_w, _pri_h = _pri_layout.get_pixel_size()
    _layout_width = _pri_w + 2
    _layout_height = _pri_h + 2
    _dual_mode = dim.getDualDimMode()
    if _dual_mode:
        _sec_layout = pango.Layout(_ctx)
        try:
            _sec_layout.set_text(_dims[1])
        except:
            _sec_layout.set_text(_dims[1], len(_dims[1]))
        _sds = dim.getSecondaryDimstring()
        format_dim_layout(_sec_layout, _sds)
        _sec_w, _sec_h = _sec_layout.get_pixel_size()
        _layout_width = max(_pri_w, _sec_w) + 2 # extra for padding space
        _layout_height = _pri_h + _sec_h + 2 # same
    _pos = dim.getPosition() # fixme
    _hw = _layout_width//2
    _hh = _layout_height//2
    #
    # fill over the area where the text will be drawn
    #
    _box_gc = _da.get_style().fg_gc[gtk.STATE_NORMAL]
    _pixmap.draw_rectangle(_box_gc, gtk.TRUE, (_pdx - _hw), (_pdy - _hh),
                           _layout_width, _layout_height)
    #
    # draw the dimension text
    #
    _pgc = _da.get_style().black_gc
    if _dual_mode:
        _pixmap.draw_layout(_pgc, (_pdx - (_pri_w//2)), (_pdy - _pri_h - 1),
                            _pri_layout)
        _pixmap.draw_layout(_pgc, (_pdx - (_sec_w//2)), (_pdy + 1),
                            _sec_layout)
        _pixmap.draw_line(_gc, (_pdx - _hw + 1), _pdy, (_pdx + _hw - 1), _pdy)
    else:
        _pixmap.draw_layout(_pgc, (_pdx - (_pri_w//2)), (_pdy - (_pri_h//2)),
                            _pri_layout)

def add_dimension(gtkimage, tool):
    _init_func = tool.getHandler("initialize")
    tool.create(gtkimage)
    gtkimage.redraw()
    _init_func(tool)

def linear_text_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _ldim = tool.getDimension()
    _ldim.setLocation(_x, _y)
    _ldim.calcDimValues()
    _ldim.reset()
    add_dimension(gtkimage, tool)
    gtkimage.setPrompt("Click on the first point for the dimension.")
    
def linear_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _layers = [gtkimage.getTopLayer()]
    while len(_layers):
        _layer = _layers.pop()
        if _layer.isVisible():
            _pt = _layer.find('point', _x, _y, _tol)
            if _pt is not None:
                _x, _y = _pt.getCoords()
                tool.setSecondPoint(_layer, _pt)
                tool.setDimPosition(_x, _y)
                tool.clearCurrentPoint()
                tool.makeDimension(gtkimage)
                tool.setHandler("button_press", linear_text_button_press_cb)
                tool.setHandler("motion_notify", dim_txt_motion_notify_cb)
                gtkimage.setPrompt("Click where the dimension text should go.")
                break
        _layers.extend(_layer.getSublayers())

def linear_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _layers = [gtkimage.getTopLayer()]
    while len(_layers):
        _layer = _layers.pop()
        if _layer.isVisible():
            _pt = _layer.find('point', _x, _y, _tol)
            if _pt is not None:
                _x, _y = _pt.getCoords()
                tool.setLocation(_x, _y)
                tool.setFirstPoint(_layer, _pt)
                tool.setHandler("button_press", linear_second_button_press_cb)
                tool.setHandler("motion_notify", dim_pts_motion_notify_cb)
                gtkimage.setPrompt("Click on the second point for the dimension.")
                gtkimage.getGC().set_function(gtk.gdk.INVERT)
                break
        _layers.extend(_layer.getSublayers())

#
# linear dimensions
#

def linear_mode_init(tool):
    tool.setHandler("button_press", linear_first_button_press_cb)
    tool.setHandler("initialize", linear_mode_init)

#
# horizontal dimensions
#

def horizontal_mode_init(tool):
    tool.setHandler("button_press", linear_first_button_press_cb)
    tool.setHandler("initialize", horizontal_mode_init)

#
# vertical dimensions
#


def vertical_mode_init(tool):
    tool.setHandler("button_press", linear_first_button_press_cb)
    tool.setHandler("initialize", vertical_mode_init)

#
# radial dimensions
#

def draw_radial_dimension(gtkimage, rdim, active):
    _gc = gtkimage.getGC()
    _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT,
                            gtk.gdk.JOIN_MITER)
    _gc.set_function(gtk.gdk.COPY)
    _da = gtkimage.getDA()
    _window = _da.window
    _ctx = _da.get_pango_context()
    _width, _height = gtkimage.getSize()
    _pixmap = gtkimage.getPixmap()
    if rdim.isModified():
        rdim.calcDimValues()
        rdim.reset()
    _crossbar = rdim.getDimCrossbar()
    _ep1, _ep2 = _crossbar.getEndpoints()
    _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs = []
    _segs.append((_p1x, _p1y, _p2x, _p2y))
    _dx, _dy = rdim.getLocation()
    _pdx, _pdy = gtkimage.coordToPixTransform(_dx, _dy)
    if active:
        _dim_color = rdim.getColor()
    else:
        _dim_color = gtkimage.getOption('INACTIVE_LAYER_COLOR')
    if gtkimage.hasGTKColor(_dim_color):
        _color = gtkimage.getGTKColor(_dim_color)
    else:
        _cstring = str(_dim_color)
        _color = _da.get_colormap().alloc_color(_cstring)
        gtkimage.saveGTKColor(_dim_color, _color)
    _gc.set_foreground(_color)
    #
    # draw marker points
    #
    _mp1, _mp2 = _crossbar.getCrossbarPoints()
    _mp1x, _mp1y = gtkimage.coordToPixTransform(_mp1[0], _mp1[1])
    _etype = rdim.getEndpointType()
    _dia_mode = rdim.getDiaMode()
    if (_etype == dimension.Dimension.ARROW or
        _etype == dimension.Dimension.FILLED_ARROW or
        _etype == dimension.Dimension.SLASH):
        _epts = _crossbar.getMarkerPoints()
        assert len(_epts) == 4, "Unexpect endpoint length: %d" % len(_epts)
        if _etype == dimension.Dimension.ARROW:
            if _dia_mode:
                _ex, _ey = _epts[0]
                _epx1, _epy1 = gtkimage.coordToPixTransform(_ex, _ey)
                _segs.append((_epx1, _epy1, _mp1x, _mp1y))
                _ex, _ey = _epts[1]
                _epx2, _epy2 = gtkimage.coordToPixTransform(_ex, _ey)
                _segs.append((_epx2, _epy2, _mp1x, _mp1y))
            _mp2x, _mp2y = gtkimage.coordToPixTransform(_mp2[0], _mp2[1])
            _ex, _ey = _epts[2]
            _epx1, _epy1 = gtkimage.coordToPixTransform(_ex, _ey)
            _segs.append((_epx1, _epy1, _mp2x, _mp2y))
            _ex, _ey = _epts[3]
            _epx2, _epy2 = gtkimage.coordToPixTransform(_ex, _ey)
            _segs.append((_epx2, _epy2, _mp2x, _mp2y))
        elif _etype == dimension.Dimension.FILLED_ARROW:
            _tuples = []
            if _dia_mode:            
                _ex, _ey = _epts[0]
                _epx1, _epy1 =  gtkimage.coordToPixTransform(_ex, _ey)
                _tuples.append((_epx1, _epy1))
                _ex, _ey = _epts[1]
                _epx2, _epy2 =  gtkimage.coordToPixTransform(_ex, _ey)
                _tuples.append((_epx2, _epy2))
                _tuples.append((_mp1x, _mp1y))
                _pixmap.draw_polygon(_gc, gtk.TRUE, _tuples)
                del _tuples[:]
            _mp2x, _mp2y = gtkimage.coordToPixTransform(_mp2[0], _mp2[1])
            _ex, _ey = _epts[2]
            _epx1, _epy1 =  gtkimage.coordToPixTransform(_ex, _ey)
            _tuples.append((_epx1, _epy1))
            _ex, _ey = _epts[3]
            _epx2, _epy2 =  gtkimage.coordToPixTransform(_ex, _ey)
            _tuples.append((_epx2, _epy2))
            _tuples.append((_mp2x, _mp2y))
            _pixmap.draw_polygon(_gc, gtk.TRUE, _tuples)
        elif _etype == dimension.Dimension.SLASH:
            if _dia_mode:
                _ex, _ey = _epts[0]
                _epx1, _epy1 =  gtkimage.coordToPixTransform(_ex, _ey)
                _ex, _ey = _epts[1]
                _epx2, _epy2 =  gtkimage.coordToPixTransform(_ex, _ey)
                _segs.append((_epx1, _epy1, _epx2, _epy2))
            _ex, _ey = _epts[2]
            _epx1, _epy1 =  gtkimage.coordToPixTransform(_ex, _ey)
            _ex, _ey = _epts[3]
            _epx2, _epy2 =  gtkimage.coordToPixTransform(_ex, _ey)
            _segs.append((_epx1, _epy1, _epx2, _epy2))
    elif _etype == dimension.Dimension.CIRCLE:
        _upp = gtkimage.getUnitsPerPixel()
        _size = rdim.getEndpointSize()/2.0
        _width = int(_size/_upp)
        _cw = _ch = _width * 2
        if _dia_mode:        
            _xmin = _mp1x - _width
            _ymin = _mp1y - _width
            _pixmap.draw_arc(_gc, gtk.TRUE, _xmin, _ymin, _cw, _ch,
                             0, (360 * 64))
        _mp2x, _mp2y = gtkimage.coordToPixTransform(_mp2[0], _mp2[1])
        _xmin = _mp2x - _width
        _ymin = _mp2y - _width
        _pixmap.draw_arc(_gc, gtk.TRUE, _xmin, _ymin, _cw, _ch,
                         0, (360 * 64))
    else:
        pass # catchall, for now ...
    #
    # draw dimension bar and any markers
    #
    _pixmap.draw_segments(_gc, _segs)
    #
    # draw dimension text
    #
    _rlen = gtkimage.scaleLength(rdim.calculate())
    _dims = rdim.getDimensions(_rlen)
    _pri_layout = pango.Layout(_ctx)
    try:
        _pri_layout.set_text(_dims[0])
    except:
        _pri_layout.set_text(_dims[0], len(_dims[0]))
    _pds = rdim.getPrimaryDimstring()
    format_dim_layout(_pri_layout, _pds)
    _pri_w, _pri_h = _pri_layout.get_pixel_size()
    _layout_width = _pri_w + 2
    _layout_height = _pri_h + 2
    _dual_mode = rdim.getDualDimMode()
    if _dual_mode:
        _sec_layout = pango.Layout(_ctx)
        try:
            _sec_layout.set_text(_dims[1])
        except:
            _sec_layout.set_text(_dims[1], len(_dims[1]))
        _sds = rdim.getSecondaryDimstring()
        format_dim_layout(_sec_layout, _sds)
        _sec_w, _sec_h = _sec_layout.get_pixel_size()
        _layout_width = max(_pri_w, _sec_w) + 2 # extra for padding space
        _layout_height = _pri_h + _sec_h + 2 # same
    _pos = rdim.getPosition() # fixme
    _hw = _layout_width//2
    _hh = _layout_height//2
    #
    # fill over the area where the text will be drawn
    #
    _box_gc = _da.get_style().fg_gc[gtk.STATE_NORMAL]
    _pixmap.draw_rectangle(_box_gc, gtk.TRUE, (_pdx - _hw), (_pdy - _hh),
                           _layout_width, _layout_height)
    #
    # draw the dimension text
    #
    _pgc = _da.get_style().black_gc
    if _dual_mode:
        _pixmap.draw_layout(_pgc, (_pdx - (_pri_w//2)),
                            (_pdy - _pri_h - 1), _pri_layout)
        _pixmap.draw_layout(_pgc, (_pdx - (_sec_w//2)),
                            (_pdy + 1), _sec_layout)
        _pixmap.draw_line(_gc, (_pdx - _hw), _pdy,
                          (_pdx + _hw), _pdy)
    else:
        _pixmap.draw_layout(_pgc, (_pdx - (_pri_w//2)),
                            (_pdy - (_pri_h//2)), _pri_layout)

def radial_txt_motion_notify_cb(gtkimage, widget, event, tool):
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _rdim = tool.getDimension()
    _crossbar = _rdim.getDimCrossbar()
    _cp = tool.getCurrentPoint()
    _segs = []
    if _cp is not None:
        _p1, _p2 = _crossbar.getEndpoints()
        _p0x, _p0y = gtkimage.coordToPixTransform(_p1[0], _p1[1])
        _p1x, _p1y = gtkimage.coordToPixTransform(_p2[0], _p2[1])
        _segs.append((_p0x, _p0y, _p1x, _p1y))
    tool.setCurrentPoint(_x, _y)
    _ix, _iy = gtkimage.getPoint()
    _rdim.setLocation(_ix, _iy)
    _rdim.calcDimValues(False)
    _p1, _p2 = _crossbar.getEndpoints()
    _p0x, _p0y = gtkimage.coordToPixTransform(_p1[0], _p1[1])
    _p1x, _p1y = gtkimage.coordToPixTransform(_p2[0], _p2[1])
    _segs.append((_p0x, _p0y, _p1x, _p1y))
    widget.window.draw_segments(_gc, _segs)

def radial_text_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _rdim = tool.getDimension()
    _rdim.setLocation(_x, _y)
    _rdim.calcDimValues()
    _rdim.reset()
    add_dimension(gtkimage, tool)
    gtkimage.setPrompt("Click on the circle or arc to be dimensioned.")

def radial_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _dc = None
    _dl = None
    _layers = [gtkimage.getTopLayer()]
    while(len(_layers)):
        _layer = _layers.pop()
        if _layer.isVisible():
            _cobjs = (_layer.getLayerEntities("circle") +
                      _layer.getLayerEntities("arc"))
            for _cobj in _cobjs:
                _mp = _cobj.mapCoords(_x, _y, _tol)
                if _mp is not None:
                    _dc = _cobj
                    _dl = _layer
                    break
        _layers.extend(_layer.getSublayers())
    if _dc is not None:
        _x, _y = _mp
        tool.setDimObject(_dl, _dc)
        tool.setDimPosition(_x, _y)
        tool.makeDimension(gtkimage)
        tool.setHandler("motion_notify", radial_txt_motion_notify_cb)
        tool.setHandler("button_press", radial_text_button_press_cb)
        gtkimage.setPrompt("Click where the dimension text should be placed.")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)

def radial_mode_init(tool):
    tool.setHandler("initialize", radial_mode_init)
    tool.setHandler("button_press", radial_button_press_cb)

#
# angular dimensions
#

def adim_txt_motion_notify_cb(gtkimage, widget, event, tool):
    _ix, _iy = gtkimage.getPoint()
    _gc = gtkimage.getGC()
    _ex = int(event.x)
    _ey = int(event.y)
    _cp = tool.getCurrentPoint()
    _adim = tool.getDimension()
    _vx, _vy = _adim.getVertexPoint().getCoords()
    _px, _py = gtkimage.coordToPixTransform(_vx, _vy)
    _win = widget.window
    _bar1, _bar2 = _adim.getDimBars()
    _crossarc = _adim.getDimCrossarc()
    _segs = []
    if _cp is not None:
        #
        # draw bars
        #
        _ep1, _ep2 = _bar1.getEndpoints()        
        _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_p1x, _p1y, _p2x, _p2y))
        _ep1, _ep2 = _bar2.getEndpoints()
        _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_p1x, _p1y, _p2x, _p2y))
        _win.draw_segments(_gc, _segs)
        del _segs[:]
        #
        # draw arc
        #
        _sa = _crossarc.getStartAngle()
        _ea = _crossarc.getEndAngle()
        _rad = int(_crossarc.getRadius()/gtkimage.getUnitsPerPixel())
        _pxmin = _px - _rad
        _pymin = _py - _rad
        _cw = _ch = _rad * 2
        if _sa < _ea:
            _sweep = _ea - _sa
        else:
            _sweep = 360.0 - (_sa - _ea)
        _win.draw_arc(_gc, gtk.FALSE, _pxmin, _pymin, _cw, _ch,
                      int(_sa * 64), int(_sweep * 64))
    tool.setCurrentPoint(_ex, _ey)
    _adim.setLocation(_ix, _iy)
    _adim.calcDimValues(False)
    #
    # draw bars
    #
    _ep1, _ep2 = _bar1.getEndpoints()        
    _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_p1x, _p1y, _p2x, _p2y))
    _ep1, _ep2 = _bar2.getEndpoints()
    _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_p1x, _p1y, _p2x, _p2y))
    _win.draw_segments(_gc, _segs)
    #
    # draw arc
    #
    _sa = _crossarc.getStartAngle()
    _ea = _crossarc.getEndAngle()
    _rad = int(_crossarc.getRadius()/gtkimage.getUnitsPerPixel())
    _pxmin = _px - _rad
    _pymin = _py - _rad
    _cw = _ch = _rad * 2
    if _sa < _ea:
        _sweep = _ea - _sa
    else:
        _sweep = 360.0 - (_sa - _ea)
    _win.draw_arc(_gc, gtk.FALSE, _pxmin, _pymin, _cw, _ch,
                  int(_sa * 64), int(_sweep * 64))

def draw_angular_dimension(gtkimage, adim, active):
    _gc = gtkimage.getGC()
    _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT,
                            gtk.gdk.JOIN_MITER)
    _gc.set_function(gtk.gdk.COPY)
    _da = gtkimage.getDA()
    _window = _da.window
    _ctx = _da.get_pango_context()
    _width, _height = gtkimage.getSize()
    _pixmap = gtkimage.getPixmap()
    # adim.update()
    if adim.isModified():
        adim.calcDimValues()
        adim.reset()
    _bar1, _bar2 = adim.getDimBars()
    _crossarc = adim.getDimCrossarc()
    if active:
        _dim_color = adim.getColor()
    else:
        _dim_color = gtkimage.getOption('INACTIVE_LAYER_COLOR')
    if gtkimage.hasGTKColor(_dim_color):
        _color = gtkimage.getGTKColor(_dim_color)
    else:
        _cstring = str(_dim_color)
        _color = _da.get_colormap().alloc_color(_cstring)
        gtkimage.saveGTKColor(_dim_color, _color)
    _gc.set_foreground(_color)
    #
    # draw dimension lines
    #
    _segs = []
    _ep1, _ep2 = _bar1.getEndpoints()        
    _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_p1x, _p1y, _p2x, _p2y))
    _ep1, _ep2 = _bar2.getEndpoints()
    _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_p1x, _p1y, _p2x, _p2y))
    #
    # draw the dimension crossbar/arc
    #
    _vx, _vy = adim.getVertexPoint().getCoords()
    _px, _py = gtkimage.coordToPixTransform(_vx, _vy)
    _dx, _dy = adim.getLocation()
    _pdx, _pdy = gtkimage.coordToPixTransform(_dx, _dy)
    _rad = int(_crossarc.getRadius()/gtkimage.getUnitsPerPixel())
    _pxmin = _px - _rad
    _pymin = _py - _rad
    _cw = _ch = _rad * 2
    _sa = _crossarc.getStartAngle()
    _ea = _crossarc.getEndAngle()
    if _sa < _ea:
        _sweep = _ea - _sa
    else:
        _sweep = 360.0 - (_sa - _ea)
    _pixmap.draw_arc(_gc, gtk.FALSE, _pxmin, _pymin, _cw, _ch,
                     int(_sa * 64), int(_sweep * 64))
    #
    # draw endpont markers
    #
    _etype = adim.getEndpointType()
    _mp1, _mp2 = _crossarc.getCrossbarPoints()
    _mp1x, _mp1y = gtkimage.coordToPixTransform(_mp1[0], _mp1[1])
    _mp2x, _mp2y = gtkimage.coordToPixTransform(_mp2[0], _mp2[1])
    if (_etype == dimension.Dimension.ARROW or
        _etype == dimension.Dimension.FILLED_ARROW or
        _etype == dimension.Dimension.SLASH):
        _epts = _crossarc.getMarkerPoints()
        assert len(_epts) == 4, "Unexpected endpoint array length: %d" % len(_epts)        
        if _etype == dimension.Dimension.ARROW:
            _ep = _epts[0]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_p1x, _p1y, _mp1x, _mp1y))
            _ep = _epts[1]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_p1x, _p1y, _mp1x, _mp1y))
            _ep = _epts[2]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_p1x, _p1y, _mp2x, _mp2y))
            _ep = _epts[3]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_p1x, _p1y, _mp2x, _mp2y))
        elif _etype == dimension.Dimension.FILLED_ARROW:
            _tuples = []
            _ep = _epts[0]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_p1x, _p1y))
            _ep = _epts[1]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_p1x, _p1y))
            _tuples.append((_mp1x, _mp1y))
            _pixmap.draw_polygon(_gc, gtk.TRUE, _tuples)
            del _tuples[:]
            _ep = _epts[2]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_p1x, _p1y))
            _ep = _epts[3]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _tuples.append((_p1x, _p1y))
            _tuples.append((_mp2x, _mp2y))
            _pixmap.draw_polygon(_gc, gtk.TRUE, _tuples)
        elif _etype == dimension.Dimension.SLASH:
            _ep = _epts[0]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _ep = _epts[1]
            _p2x, _p2y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_p1x, _p1y, _p2x, _p2y))
            _ep = _epts[2]
            _p1x, _p1y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _ep = _epts[3]
            _p2x, _p2y = gtkimage.coordToPixTransform(_ep[0], _ep[1])
            _segs.append((_p1x, _p1y, _p2x, _p2y))
    elif _etype == dimension.Dimension.CIRCLE:
        _upp = gtkimage.getUnitsPerPixel()
        _size = adim.getEndpointSize()/2.0
        _width = int(_size/_upp)
        _cw = _ch = _width * 2
        _xmin = _mp1x - _width
        _ymin = _mp1y - _width
        _pixmap.draw_arc(_gc, gtk.TRUE, _xmin, _ymin, _cw, _ch,
                         0, (360 * 64))
        _xmin = _mp2x - _width
        _ymin = _mp2y - _width
        _pixmap.draw_arc(_gc, gtk.TRUE, _xmin, _ymin, _cw, _ch,
                         0, (360 * 64))
    else:
        pass # catchall for now ...
    #
    # draw the dimension bars and any dimension markers
    #
    _pixmap.draw_segments(_gc, _segs)    
    #
    # draw dimension text
    #
    _dlen = adim.calculate()
    _dims = adim.getDimensions(_dlen)
    _dlines = []
    _pri_layout = pango.Layout(_ctx)
    try:
        _pri_layout.set_text(_dims[0])
    except:
        _pri_layout.set_text(_dims[0], len(_dims[0]))
    _ds = adim.getPrimaryDimstring()
    format_dim_layout(_pri_layout, _ds)
    _pri_w, _pri_h = _pri_layout.get_pixel_size()
    _layout_width = _pri_w + 2
    _layout_height = _pri_h + 2
    _dual_mode = adim.getDualDimMode()
    if _dual_mode:
        _sec_layout = pango.Layout(_ctx)
        try:
            _sec_layout.set_text(_dims[1])
        except:
            _sec_layout.set_text(_dims[1], len(_dims[1]))
        _sds = adim.getSecondaryDimstring()
        format_dim_layout(_sec_layout, _sds)
        _sec_w, _sec_h = _sec_layout.get_pixel_size()
        _layout_width = max(_pri_w, _sec_w) + 2 # extra for padding space
        _layout_height = _pri_h + _sec_h + 2 # same
    _pos = adim.getPosition() # fixme
    _hw = _layout_width//2
    _hh = _layout_height//2
    #
    # fill over the area where the text will be drawn
    #
    _box_gc = _da.get_style().fg_gc[gtk.STATE_NORMAL]
    _pixmap.draw_rectangle(_box_gc, gtk.TRUE, (_pdx - _hw), (_pdy - _hh),
                           _layout_width, _layout_height)
    #
    # draw the dimension text
    #
    _pgc = _da.get_style().black_gc
    if _dual_mode:
        _pixmap.draw_layout(_pgc, (_pdx - (_pri_w//2)), (_pdy - _pri_h - 1), _pri_layout)
        _pixmap.draw_layout(_pgc, (_pdx - (_sec_w//2)), (_pdy + 1), _sec_layout)
        _pixmap.draw_line(_gc, (_pdx - _hw), _pdy, (_pdx + _hw), _pdy)
    else:
        _pixmap.draw_layout(_pgc, (_pdx - (_pri_w//2)), (_pdy - (_pri_h//2)), _pri_layout)

def _test_layer_arcs(gtkimage, lyr, x, y, tol):
    _adim = None
    for _arc in lyr.getLayerEntities("arc"):
        _arc_pt = _arc.mapCoords(x, y, tol)
        if _arc_pt is not None:
            _x, _y = _arc_pt.getCoords()
            _cp = _arc.getCenter()
            _ep1, _ep2 = _arc.getEndpoints()
            _ex, _ey = _ep1
            _p1 = lyr.find('point', _ex, _ey)
            assert _p1 is not None, "Missing arc endpoint"
            _ex, _ey = _ep2
            _p2 = lyr.find('point', _ex, _ey)
            assert _p2 is not None, "Missing arc endpoint"
            _ds = gtkimage.getOption("DIM_STYLE")
            _adim = dimension.AngularDimension(lyr, _cp, lyr, _p1, lyr, _p2,
                                               _x, _y, _ds)
            break
    return _adim

def angular_pts_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _pt_layer = _active_layer
    _pt = _active_layer.find('point',_x, _y, _tol)
    if _pt is None:
        _layers = [gtkimage.getTopLayer()]
        while(len(_layers)):
            _layer = _layers.pop()
            if _layer is not _active_layer and _layer.isVisible():
                _pt = _layer.find('point', _x, _y, _tol)
                if _pt is not None:
                    _pt_layer = _layer
                    break
            _layers.extend(_layer.getSublayers())
    if _pt is not None:
        _x, _y = _pt.getCoords()
        tool.storeCoords(_x, _y)
        if len(tool) == 2:
            tool.pushObject(_pt_layer)
            tool.pushObject(_pt)
        else:
            _p1 = tool.popObject()
            _l1 = tool.popObject()
            _vp = tool.popObject()
            _vl = tool.popObject()
            _ds = gtkimage.getOption("DIM_STYLE")
            _adim = dimension.AngularDimension(_vl, _vp, _l1, _p1,
                                               _pt_layer, _pt,
                                               _x, _y, _ds)
            tool.pushObject(_adim)

def angular_text_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _adim = tool.getDimension()
    _adim.setLocation(_x, _y)
    _adim.calcDimValues()
    _adim.reset()
    add_dimension(gtkimage, tool)
    gtkimage.setPrompt("Click on the angle vertex point or an arc.")

def angular_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _layers = [gtkimage.getTopLayer()]
    while len(_layers):
        _layer = _layers.pop()
        if _layer.isVisible():
            _pt = _layer.find('point', _x, _y, _tol)
            if _pt is not None:
                _x, _y = _pt.getCoords()
                tool.setLocation(_x, _y)
                tool.setSecondPoint(_layer, _pt)
                tool.setDimPosition(_x, _y)
                tool.makeDimension(gtkimage)
                tool.setHandler("button_press", angular_text_button_press_cb)
                tool.setHandler("motion_notify", adim_txt_motion_notify_cb)
                gtkimage.getGC().set_function(gtk.gdk.INVERT)            
                gtkimage.setPrompt("Click where the dimension text should be located.")
                break
        _layers.extend(_layer.getSublayers())
    
def angular_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _layers = [gtkimage.getTopLayer()]
    while len(_layers):
        _layer = _layers.pop()
        if _layer.isVisible():
            _pt = _layer.find('point', _x, _y, _tol)
            if _pt is not None:
                _x, _y = _pt.getCoords()
                tool.setLocation(_x, _y)
                tool.setFirstPoint(_layer, _pt)
                tool.setHandler("button_press", angular_second_button_press_cb)
                gtkimage.setPrompt("Click on the second point for the dimension.")
                break
        _layers.extend(_layer.getSublayers())

def _test_layer(layer, x, y, tol):
    _arc = None
    _pt = layer.find('point', x, y)
    if _pt is None:
        _pt = layer.find('point', x, y, tol)
        if _pt is None:
            _arc_pt = None
            for _arc in layer.getLayerEntities("arc"):
                _arc_pt = _arc.mapCoords(x, y, tol)
                if _arc_pt is not None:
                    break
            if _arc_pt is None:
                _arc = None # no hits on any arcs ...
    return _pt, _arc

def angular_initial_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _layer = _active_layer
    _pt, _arc = _test_layer(_active_layer, _x, _y, _tol)
    if _pt is None and _arc is None:
        _layers = [gtkimage.getTopLayer()]
        while len(_layers):
            _layer = _layers.pop()
            if _layer is not _active_layer and _layer.isVisible():
                _pt, _arc = _test_layer(_layer, _x, _y, _tol)
                if _pt is not None or _arc is not None:
                    break
            _layers.extend(_layer.getSublayers())
    if _pt is not None:
        tool.setVertexPoint(_layer, _pt)
        tool.setHandler("button_press", angular_first_button_press_cb)
        gtkimage.setPrompt("Click on the first endpoint for the dimension.")
    elif _arc is not None:
        _cp = _arc.getCenter()
        tool.setVertexPoint(_layer, _cp)
        _ep1, _ep2 = _arc.getEndpoints()
        _ex, _ey = _ep1
        _p1 = _layer.find('point', _ex, _ey)
        assert _p1 is not None, "Missing arc endpoint"
        tool.setFirstPoint(_layer, _p1)
        _ex, _ey = _ep2
        _p2 = _layer.find('point', _ex, _ey)
        assert _p2 is not None, "Missing arc endpoint"
        tool.setSecondPoint(_layer, _p2)
        tool.setDimPosition(_x, _y)
        tool.makeDimension(gtkimage)
        tool.setHandler("button_press", angular_text_button_press_cb)
        tool.setHandler("motion_notify", adim_txt_motion_notify_cb)
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
        gtkimage.setPrompt("Click where the dimension text should be located.")        
def angular_mode_init(tool):
    tool.initialize()
    tool.setHandler("button_press", angular_initial_button_press_cb)
    tool.setHandler("initialize", angular_mode_init)
