#
# 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

from PythonCAD.Generic.text import TextStyle
from PythonCAD.Generic.dimension import Dimension
from PythonCAD.Interface.Gtk import gtktext

#
# format the dimesion text string
#

def _make_dimstring_layout(gtkimage, ds):
    _text = ds.getText()
    _family = ds.getFamily()
    _style = ds.getStyle()
    _weight = ds.getWeight()
    _color = ds.getColor()
    if ds.getBounds() is None:
        gtktext.set_textblock_bounds(gtkimage, ds)
    _scale = ds.getFontScale()        
    _layout = gtktext.make_pango_layout(gtkimage, _text, _family, _style,
                                        _weight, _color, _scale)
    _align = ds.getAlignment()
    if _align == TextStyle.ALIGN_LEFT:
        _layout.set_alignment(pango.ALIGN_LEFT)
    elif _align == TextStyle.ALIGN_CENTER:
        _layout.set_alignment(pango.ALIGN_CENTER)
    elif _align == TextStyle.ALIGN_RIGHT:
        _layout.set_alignment(pango.ALIGN_RIGHT)
    return _layout

#
# 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_function(gtk.gdk.COPY)
    _da = gtkimage.getDA()
    _window = _da.window
    _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)
    _upp = gtkimage.getUnitsPerPixel()
    _thickness = int(dim.getThickness()/_upp)
    if _thickness == 0:
        _thickness = 1
    _gc.set_line_attributes(_thickness, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT,
                            gtk.gdk.JOIN_MITER)
    #
    # 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.DIM_ENDPT_ARROW or
        _etype == Dimension.DIM_ENDPT_FILLED_ARROW or
        _etype == Dimension.DIM_ENDPT_SLASH):
        _epts = _crossbar.getMarkerPoints()
        assert len(_epts) == 4, "Bad marker point count: %d" % len(_epts)
        if _etype == Dimension.DIM_ENDPT_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.DIM_ENDPT_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.DIM_ENDPT_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.DIM_ENDPT_CIRCLE:
        _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
    #
    draw_dimtext(gtkimage, dim, active)

def _calc_dimstring_offset(ds):
    _bounds = ds.getBounds()
    if _bounds is None:
        raise ValueError, "DimString bounds not defined: " + `ds`
    _w, _h = _bounds
    _align = ds.getAlignment()
    if _align == TextStyle.ALIGN_LEFT:
        _dx = 0
    elif _align == TextStyle.ALIGN_CENTER:
        _dx = _w/2.0
    elif _align == TextStyle.ALIGN_RIGHT:
        _dx = _w
    else:
        raise ValueError, "Unexpected alignment: %d" % _align
    _dy = _h/2.0
    return _dx, _dy
        
def draw_dimtext(gtkimage, dim, active):
    _da = gtkimage.getDA()
    _slen = gtkimage.scaleLength(dim.calculate())
    _dims = dim.getDimensions(_slen)
    _ds1, _ds2 = dim.getDimstrings()
    _x, _y = dim.getLocation()
    _upp = gtkimage.getUnitsPerPixel()    
    #
    # format primary DimString as if it is the only visible DimString
    #
    _l1 = _make_dimstring_layout(gtkimage, _ds1)
    _dx, _dy = _calc_dimstring_offset(_ds1)
    _ds1.setLocation((_x - _dx), (_y + _dy))
    _l2 = None
    if dim.getDualDimMode():
        #
        # format secondary DimString, and move primary DimString
        #
        _l2 = _make_dimstring_layout(gtkimage, _ds2)
        _dx, _dy = _calc_dimstring_offset(_ds2)
        _ddm = dim.getDualModeOffset()
        _ds2.setLocation((_x - _dx), (_y - _ddm))
        _w, _h = _ds1.getBounds()
        _dx, _dy = _ds1.getLocation()
        _ds1.setLocation(_dx, (_y + _ddm + _h))
    #
    # time to draw ...
    #
    # blank out area where text will go
    #
    _x1, _y1 = _ds1.getLocation()
    _w1, _h1 = _ds1.getBounds()
    _ymax = _y1
    _xmin = _x1
    _ymin = _y1 - _h1
    _xmax = _x1 + _w1
    if _l2 is not None:
        _x2, _y2 = _ds2.getLocation()
        _w2, _h2 = _ds2.getBounds()
        if _xmin > _x2:
            _xmin = _x2
        if _xmax < (_x2 + _w2):
            _xmax = _x2 + _w2
        _ymin = _y2 - _h2
    _px, _py = gtkimage.coordToPixTransform(_xmin, _ymax)
    _bw = int((_xmax - _xmin)/_upp) + 1 # round up ...
    _bh = int((_ymax - _ymin)/_upp) + 1 # here too ...
    _box_gc = _da.get_style().fg_gc[gtk.STATE_NORMAL]
    _pixmap = gtkimage.getPixmap()
    _pixmap.draw_rectangle(_box_gc, gtk.TRUE, (_px - 2), (_py - 2),
                           (_bw + 4), (_bh + 4))
    #
    # draw the DimStrings
    #
    _w, _h = _l1.get_pixel_size()
    _dx, _dy = _ds1.getLocation()
    _px, _py = gtkimage.coordToPixTransform(_dx, _dy)
    _pgc = _da.get_style().black_gc
    _pixmap.draw_layout(_pgc, _px, _py, _l1)
    del _l1
    if _l2 is not None:
        _w, _h = _l2.get_pixel_size()
        _dx, _dy = _ds2.getLocation()
        _px, _py = gtkimage.coordToPixTransform(_dx, _dy)
        _pixmap.draw_layout(_pgc, _px, _py, _l2)
        # draw the bar between dimension text
        _px1, _py1 = gtkimage.coordToPixTransform(_xmin, _y)
        _px2, _py2 = gtkimage.coordToPixTransform(_xmax, _y)
        _gc = gtkimage.getGC()        
        _pixmap.draw_line(_gc, _px1, _py1, _px2, _py2)
        del _l2

def add_dimension(gtkimage, tool):
    _init_func = tool.getHandler("initialize")
    gtkimage.startAction()
    try:
        tool.create(gtkimage)
    finally:
        gtkimage.endAction()
    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_function(gtk.gdk.COPY)
    _da = gtkimage.getDA()
    _window = _da.window
    _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)
    _upp = gtkimage.getUnitsPerPixel()
    _thickness = int(rdim.getThickness()/_upp)
    if _thickness == 0:
        _thickness = 1
    _gc.set_line_attributes(_thickness, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT,
                            gtk.gdk.JOIN_MITER)
    #
    # 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.DIM_ENDPT_ARROW or
        _etype == Dimension.DIM_ENDPT_FILLED_ARROW or
        _etype == Dimension.DIM_ENDPT_SLASH):
        _epts = _crossbar.getMarkerPoints()
        assert len(_epts) == 4, "Unexpect endpoint length: %d" % len(_epts)
        if _etype == Dimension.DIM_ENDPT_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.DIM_ENDPT_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.DIM_ENDPT_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.DIM_ENDPT_CIRCLE:
        _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
    #
    draw_dimtext(gtkimage, rdim, active)

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_function(gtk.gdk.COPY)
    _da = gtkimage.getDA()
    _window = _da.window
    _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)
    _upp = gtkimage.getUnitsPerPixel()
    _thickness = int(adim.getThickness()/_upp)
    if _thickness == 0:
        _thickness = 1
    _gc.set_line_attributes(_thickness, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT,
                            gtk.gdk.JOIN_MITER)
    #
    # 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.DIM_ENDPT_ARROW or
        _etype == Dimension.DIM_ENDPT_FILLED_ARROW or
        _etype == Dimension.DIM_ENDPT_SLASH):
        _epts = _crossarc.getMarkerPoints()
        assert len(_epts) == 4, "Unexpected endpoint array length: %d" % len(_epts)        
        if _etype == Dimension.DIM_ENDPT_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.DIM_ENDPT_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.DIM_ENDPT_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.DIM_ENDPT_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
    #
    #
    # draw dimension text
    #
    draw_dimtext(gtkimage, adim, active)

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:
            from PythonCAD.Generic.dimension import AngularDimension
            _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 = 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:
            from PythonCAD.Generic.dimension import AngularDimension
            _p1 = tool.popObject()
            _l1 = tool.popObject()
            _vp = tool.popObject()
            _vl = tool.popObject()
            _ds = gtkimage.getOption("DIM_STYLE")
            _adim = 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)
