#
# 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
#
#
# menu functions for creating entities in drawing
# like segments, circles, etc ...
#

from math import hypot, pi, atan2

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

import Generic.point
import Generic.segment
import Generic.segjoint
import Generic.color
import Generic.util

def make_tuple(text):
    try:
        _tpl = eval(text)
    except:
        raise StandardError, "Invalid point: " + text
    if not isinstance(_tpl, tuple):
        raise TypeError, "Invalid point: " + str(_tpl)
    if len(_tpl) != 2:
        raise ValueError, "Invalid point: " + str(_tpl)
    return _tpl

def create_entity(gtkimage, tool):
    _init_func = tool.getHandler("initialize")
    gtkimage.startAction()
    try:
        tool.create(gtkimage)
    finally:
        gtkimage.endAction()
    gtkimage.redraw()
    _init_func(gtkimage, tool)

#
# points
#

def point_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setPoint(_x, _y)
    create_entity(gtkimage, tool)

def point_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setPoint(_x, _y)
        create_entity(gtkimage, tool)

def point_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter the point.")
    tool.setHandler("initialize", point_mode_init)
    tool.setHandler("button_press", point_button_press_cb)
    tool.setHandler("entry_event", point_entry_event_cb)

#
# segments
#

def segment_motion_notify_cb(gtkimage, widget, event, tool):
    _segs = []
    _x1, _y1 = tool.getFirstPoint()
    _px1, _py1 = gtkimage.coordToPixTransform(_x1, _y1)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _segs.append((_px1, _py1, _xc, _yc))
    tool.setCurrentPoint(_x, _y)
    _segs.append((_px1, _py1, _x, _y))
    widget.window.draw_segments(_gc, _segs)


def segment_second_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0, -1)
    _entry.delete_text(0, -1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setSecondPoint(_x, _y)
        create_entity(gtkimage, tool)

def segment_first_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setFirstPoint(_x, _y)
        tool.setHandler("button_press", segment_second_button_press_cb)
        tool.setHandler("motion_notify", segment_motion_notify_cb)
        tool.setHandler("entry_event", segment_second_entry_event_cb)
        gtkimage.setPrompt("Enter the second Point or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)

def segment_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setSecondPoint(_x, _y)
    create_entity(gtkimage, tool)

def segment_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setFirstPoint(_x, _y)
    tool.setHandler("button_press", segment_second_button_press_cb)
    tool.setHandler("entry_event", segment_second_entry_event_cb)
    tool.setHandler("motion_notify", segment_motion_notify_cb)
    gtkimage.setPrompt("Enter the second point or click in the drawing area")
    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def segment_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a point.")
    tool.setHandler("initialize", segment_mode_init)
    tool.setHandler("button_press", segment_first_button_press_cb)
    tool.setHandler("entry_event", segment_first_entry_event_cb)

#
# rectangles
#

def rectangle_motion_notify_cb(gtkimage, widget, event, tool):
    _x1, _y1 = tool.getFirstPoint()
    _px, _py = gtkimage.coordToPixTransform(_x1, _y1)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _xmin = min(_px, _xc)
        _ymin = min(_py, _yc)
        _width = abs(_px - _xc)
        _height = abs(_py - _yc)
        widget.window.draw_rectangle(_gc, gtk.FALSE, _xmin, _ymin, _width, _height)
    tool.setCurrentPoint(_x, _y)
    _xmin = min(_px, _x)
    _ymin = min(_py, _y)
    _width = abs(_px - _x)
    _height = abs(_py - _y)
    widget.window.draw_rectangle(_gc, gtk.FALSE, _xmin, _ymin, _width, _height)

def rectangle_second_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(text)
        tool.setSecondPointPoint(_x, _y)
        create_entity(gtkimage, tool)

def rectangle_first_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(text)
        tool.setFirstPoint(_x, _y)
        tool.setHandler("button_press", rectangle_second_button_press_cb)
        tool.setHandler("entry_event", rectangle_second_entry_event_cb)
        tool.setHandler("motion_notify", rectangle_motion_notify_cb)
        gtkimage.setPrompt("Enter the second Point or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)

def rectangle_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setSecondPoint(_x, _y)
    create_entity(gtkimage, tool)

def rectangle_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setFirstPoint(_x, _y)
    tool.setHandler("button_press", rectangle_second_button_press_cb)
    tool.setHandler("motion_notify", rectangle_motion_notify_cb)
    tool.setHandler("entry_event", rectangle_second_entry_event_cb)
    gtkimage.setPrompt("Enter the second point or click in the drawing area")
    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def rectangle_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a point.")
    tool.setHandler("initialize", rectangle_mode_init)
    tool.setHandler("button_press", rectangle_first_button_press_cb)
    tool.setHandler("entry_event", rectangle_first_entry_event_cb)

#
# circles
#

def circle_make_radius(text):
    try:
        _textrad = eval(text)
    except:
        raise ValueError, "Invalid radius: " + text
    _rad = _textrad
    if not isinstance(_rad, float):
        _rad = float(_textrad)
    if _rad < 1e-10:
        raise ValueError, "Invalid radius: %g" % _rad
    return _rad

def circle_center_motion_notify_cb(gtkimage, widget, event, tool):
    _cx, _cy = tool.getCenter()
    _pcx, _pcy = gtkimage.coordToPixTransform(_cx, _cy)
    _upp = gtkimage.getUnitsPerPixel()
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _radius = tool.getRadius()
    if _radius is not None:
        _pr = int(_radius/_upp)
        _xmin = _pcx - _pr
        _ymin = _pcy - _pr
        _cw = _ch = _pr * 2
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                               0, 360*64)
    _ix, _iy = gtkimage.getPoint()
    _radius = hypot((_cx - _ix),(_cy - _iy))
    tool.setRadius(_radius)
    _pr = int(_radius/_upp)
    _xmin = _pcx - _pr
    _ymin = _pcy - _pr
    _cw = _ch = _pr * 2
    widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                           0, 360*64)

def circle_radius_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _radius = circle_make_radius(_text)
        tool.setRadius(_radius)
        create_entity(gtkimage, tool)

def circle_point_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setCenter(_x, _y)
        tool.setHandler("button_press", circle_radius_button_press_cb)
        tool.setHandler("motion_notify", circle_center_motion_notify_cb)
        tool.setHandler("entry_event", circle_radius_entry_event_cb)
        gtkimage.setPrompt("Enter the radius or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)

def circle_radius_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    _cx, _cy = tool.getCenter()
    _radius = hypot((_cx - _x), (_cy - _y))
    tool.setRadius(_radius)
    create_entity(gtkimage, tool)

def circle_center_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setCenter(_x, _y)
    tool.setHandler("button_press", circle_radius_button_press_cb)
    tool.setHandler("motion_notify", circle_center_motion_notify_cb)
    tool.setHandler("entry_event", circle_radius_entry_event_cb)
    gtkimage.setPrompt("Enter the radius or click in the drawing area")
    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def circle_center_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a point.")
    tool.setHandler("button_press", circle_center_button_press_cb)
    tool.setHandler("entry_event", circle_point_entry_event_cb)
    tool.setHandler("initialize", circle_center_mode_init)

#
# circle from two points
#

def circle_tp_motion_notify_cb(gtkimage, widget, event, tool):
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _radius = tool.getRadius()
    _upp = gtkimage.getUnitsPerPixel()
    if _radius is not None:
        _cx, _cy = tool.getCenter()
        _pcx, _pcy = gtkimage.coordToPixTransform(_cx, _cy)
        _pr = int(_radius/_upp)
        _xmin = _pcx - _pr
        _ymin = _pcy - _pr
        _cw = _ch = _pr * 2
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                               0, 360*64)
    _ix, _iy = gtkimage.getPoint()
    tool.setSecondPoint(_ix, _iy)
    _cx, _cy = tool.getCenter()
    _pcx, _pcy = gtkimage.coordToPixTransform(_cx, _cy)
    _radius = tool.getRadius()
    _pr = int(_radius/_upp)
    _xmin = _pcx - _pr
    _ymin = _pcy - _pr
    _cw = _ch = _pr * 2
    widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                           0, 360*64)

def circle_tp_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setSecondPoint(_x, _y)
    create_entity(gtkimage, tool)

def circle_tp_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setFirstPoint(_x, _y)
    tool.setHandler("button_press", circle_tp_second_button_press_cb)
    tool.setHandler("motion_notify", circle_tp_motion_notify_cb)
    tool.setHandler("entry_event", circle_tp_second_entry_event_cb)
    gtkimage.setPrompt("Enter the second point or click in the drawing area")
    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def circle_tp_second_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setSecondPoint(_x, _y)
        create_entity(gtkimage, tool)

def circle_tp_first_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setFirstPoint(_x, _y)
        tool.setHandler("motion_notify", circle_tp_motion_notify_cb)
        tool.setHandler("entry_event", circle_tp_second_entry_event_cb)
        gtkimage.setPrompt("Enter another point or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)

def circle_tp_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a point.")
    tool.setHandler("button_press", circle_tp_first_button_press_cb)
    tool.setHandler("entry_event", circle_tp_first_entry_event_cb)
    tool.setHandler("initialize", circle_tp_mode_init)

#
# arcs
#

def arc_make_angle(text):
    try:
        _textangle = eval(text)
    except:
        raise StandardError, "Invalid angle: " + text
    return Generic.util.make_c_angle(_textangle)

def arc_center_end_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    _cx, _cy = tool.getCenter()
    if _pt is not None:
        _x, _y = _pt.getCoords()
    _angle = (180.0/pi) * atan2((_y - _cy),(_x - _cx))
    if _angle < 0.0:
        _angle = _angle + 360.0
    tool.setEndAngle(_angle)
    create_entity(gtkimage, tool)

def arc_angle_motion_notify_cb(gtkimage, widget, event, tool):
    _ix, _iy = gtkimage.getPoint()
    _cx, _cy = tool.getCenter() # arc center
    _radius = tool.getRadius()
    _sa = tool.getStartAngle()
    _pcx, _pcy = gtkimage.coordToPixTransform(_cx, _cy)
    _upp = gtkimage.getUnitsPerPixel()
    _pr = int(_radius/_upp)
    _xmin = _pcx - _pr
    _ymin = _pcy - _pr
    _cw = _ch = _pr * 2
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _ea = tool.getEndAngle()
    _win = widget.window
    if _ea is not None:
        if _sa < _ea:
            _sweep = _ea - _sa
        else:
            _sweep = 360.0 - (_sa - _ea)
        _win.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                      int(_sa * 64), int(_sweep * 64))
    _ea = (180.0/pi) * atan2((_iy - _cy), (_ix - _cx))
    if _ea < 0.0:
        _ea = _ea + 360.0
    tool.setEndAngle(_ea)
    if _sa < _ea:
        _sweep = _ea - _sa
    else:
        _sweep = 360.0 - (_sa - _ea)
    _win.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                  int(_sa * 64), int(_sweep * 64))

def arc_start_angle_button_press_cb(gtkimage, widget, event, tool):
    _cx, _cy = tool.getCenter()
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    _angle = (180.0/pi) * atan2((_y - _cy), (_x - _cx))
    if _angle < 0.0:
        _angle = _angle + 360.0
    tool.setStartAngle(_angle)
    tool.setHandler("motion_notify", arc_angle_motion_notify_cb)
    tool.setHandler("entry_event", arc_center_ea_entry_event_cb)
    gtkimage.setPrompt("Enter the end angle of the arc.")

def arc_center_ea_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _angle = arc_make_angle(text)
        tool.setEndAngle(_angle)
        create_entity(gtkimage, tool)

def arc_center_sa_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _angle = arc_make_angle(text)
        tool.setStartAngle(_angle)
        tool.setHandler("motion_notify", arc_angle_motion_notify_cb)
        tool.setHandler("entry_event", arc_center_ea_entry_event_cb)
        gtkimage.setPrompt("Enter the end angle of the arc.")

def arc_center_radius_button_press_cb(gtkimage, widget, event, tool):
    _cx, _cy = tool.getCenter()
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    _radius = hypot((_x - _cx), (_y - _cy))
    tool.setRadius(_radius)
    _angle = (180.0/pi) * atan2((_y - _cy), (_x - _cx))
    if _angle < 0.0:
        _angle = _angle + 360.0
    tool.setStartAngle(_angle)
    tool.delHandler("entry_event")
    tool.setHandler("motion_notify", arc_angle_motion_notify_cb)
    gtkimage.refresh()
    gtkimage.getGC().set_function(gtk.gdk.INVERT)
    tool.setHandler("button_press", arc_center_end_button_press_cb)
    gtkimage.setPrompt("Click in the drawing area to finish the arc")

def arc_center_radius_entry_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _radius = circle_make_radius(_text)
        tool.setRadius(_radius)
        tool.setHandler("entry_event", arc_center_sa_entry_event_cb)
        tool.setHandler("button_press", arc_start_angle_button_press_cb)
        gtkimage.setPrompt("Enter the start angle of the arc.")

def arc_radius_motion_notify_cb(gtkimage, widget, event, tool):
    _cx, _cy = tool.getCenter()
    _pcx, _pcy = gtkimage.coordToPixTransform(_cx, _cy)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _radius = tool.getRadius()
    _upp = gtkimage.getUnitsPerPixel()
    if _radius is not None:
        _pr = int(_radius/_upp)
        _xmin = _pcx - _pr
        _ymin = _pcy - _pr
        _cw = _ch = _pr * 2
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                               0, 360*64)
    _ix, _iy = gtkimage.getPoint()
    _radius = hypot((_cx - _ix), (_cy - _iy))
    tool.setRadius(_radius)
    _pr = int(_radius/_upp)
    _xmin = _pcx - _pr
    _ymin = _pcy - _pr
    _cw = _ch = _pr * 2
    widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                           0, 360*64)

def arc_center_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setCenter(_x, _y)
    gtkimage.getGC().set_function(gtk.gdk.INVERT)
    tool.setHandler("motion_notify", arc_radius_motion_notify_cb)
    tool.setHandler("entry_event", arc_center_radius_entry_cb)
    tool.setHandler("button_press", arc_center_radius_button_press_cb)
    gtkimage.setPrompt("Enter the radius or click in the drawing area")

def arc_center_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setCenter(_x, _y)
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
        tool.setHandler("motion_notify", arc_radius_motion_notify_cb)
        tool.setHandler("button_press", arc_center_radius_button_press_cb)
        tool.setHandler("entry_event", arc_center_radius_entry_cb)
        gtkimage.setPrompt("Enter the arc radius or click in the drawing area")

def arc_center_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a point.")
    tool.setHandler("button_press", arc_center_button_press_cb)
    tool.setHandler("entry_event", arc_center_entry_event_cb)
    tool.setHandler("initialize", arc_center_mode_init)

#
# chamfers
#

def chamfer_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _new_pt = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None and not _new_pt:
        _active_layer = gtkimage.getActiveLayer()
        _urefs = _pt.getUsers()
        if len(_urefs) != 2:
            return
        _s1 = _s2 = None        
        for _uref in _urefs:
            _user = _uref()
            if _user is None:
                return
            if not isinstance(_user, Generic.segment.Segment):
                return
            if _s1 is None:
                _s1 = _user
            else:
                _s2 = _user
        _len = gtkimage.getOption('CHAMFER_LENGTH')
        _s = gtkimage.getOption('LINE_STYLE')
        _l = gtkimage.getOption('LINE_TYPE')
        _c = gtkimage.getOption('LINE_COLOR')
        _t = gtkimage.getOption('LINE_THICKNESS')
        #
        # the following is a work-around that needs to
        # eventually be replaced ...
        #
        _p1, _p2 = _s1.getEndpoints()
        _x1, _y1 = _p1.getCoords()
        _x2, _y2 = _p2.getCoords()
        _slen = _s1.length()
        _factor = 2.0
        _r = (_slen - (_len/_factor))/_slen
        _xn = _x1 + _r * (_x2 - _x1)
        _yn = _y1 + _r * (_y2 - _y1)
        _pc = _active_layer.find('point', _xn, _yn)
        while _pc is not None:
            _factor = _factor + 1.0
            if _factor > 1000:
                break
            _r = (_slen - (_len/_factor))/_slen
            _xn = _x1 + _r * (_x2 - _x1)
            _yn = _y1 + _r * (_y2 - _y1)
            _pc = _active_layer.find('point', _xn, _yn)
        if _pc is None:
            _pc = Generic.point.Point(_xn, _yn)
            _active_layer.addObject(_pc)
            if _pt is _p1:
                _s1.setP1(_pc)
            else:
                _s1.setP2(_pc)
            _active_layer.update()
            _ptx, _pty = _pt.getCoords()
            _pc.setx(_ptx)
            _pc.sety(_pty)
            _chamfer = Generic.segjoint.Chamfer(_s1, _s2, _len, _s, _l, _c, _t)
            _active_layer.addObject(_chamfer)
            gtkimage.redraw()

def chamfer_mode_init(tool):
    tool.initialize()
    tool.setHandler("button_press", chamfer_button_press_cb)

#
# fillet
#

def fillet_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _new_pt = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None and not _new_pt:
        _active_layer = gtkimage.getActiveLayer()
        _urefs = _pt.getUsers()
        if len(_urefs) != 2:
            return
        _s1 = _s2 = None        
        for _uref in _urefs:
            _user = _uref()
            if _user is None:
                return
            if not isinstance(_user, Generic.segment.Segment):
                return
            if _s1 is None:
                _s1 = _user
            else:
                _s2 = _user
        _rad = gtkimage.getOption('FILLET_RADIUS')
        _s = gtkimage.getOption('LINE_STYLE')
        _l = gtkimage.getOption('LINE_TYPE')
        _c = gtkimage.getOption('LINE_COLOR')
        _t = gtkimage.getOption('LINE_THICKNESS')
        #
        # the following is a work-around that needs to
        # eventually be replaced ...
        #
        _p1, _p2 = _s1.getEndpoints()
        _x1, _y1 = _p1.getCoords()
        _x2, _y2 = _p2.getCoords()
        _slen = _s1.length()
        _factor = 2.0
        _r = 1.0 - (_slen/_factor)
        _xn = _x1 + _r * (_x2 - _x1)
        _yn = _y1 + _r * (_y2 - _y1)
        _pc = _active_layer.find('point', _xn, _yn)
        while _pc is not None:
            _factor = _factor + 1.0
            if _factor > 1000:
                break
            _r = 1.0 - (_slen/_factor)
            _xn = _x1 + _r * (_x2 - _x1)
            _yn = _y1 + _r * (_y2 - _y1)
            _pc = _active_layer.find('point', _xn, _yn)
        if _pc is None:
            _pc = Generic.point.Point(_xn, _yn)
            _active_layer.addObject(_pc)
            if _pt is _p1:
                _s1.setP1(_pc)
            else:
                _s1.setP2(_pc)
            _active_layer.update()
            _ptx, _pty = _pt.getCoords()
            _pc.setx(_ptx)
            _pc.sety(_pty)
            _fillet = Generic.segjoint.Fillet(_s1, _s2, _rad, _s, _l, _c, _t)
            _active_layer.addObject(_fillet)
            gtkimage.redraw()

def fillet_mode_init(tool):
    tool.initialize()
    tool.setHandler("button_press", fillet_button_press_cb)

#
# leader lines
#

def leader_second_motion_notify_cb(gtkimage, widget, event, tool):
    _segs = []
    _x2, _y2 = tool.getMidPoint()
    _px2, _py2 = gtkimage.coordToPixTransform(_x2, _y2)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _segs.append((_px2, _py2, _xc, _yc))
    tool.setCurrentPoint(_x, _y)
    _segs.append((_px2, _py2, _x, _y))
    widget.window.draw_segments(_gc, _segs)

def leader_first_motion_notify_cb(gtkimage, widget, event, tool):
    _segs = []
    _x1, _y1 = tool.getFirstPoint()
    _px1, _py1 = gtkimage.coordToPixTransform(_x1, _y1)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _segs.append((_px1, _py1, _xc, _yc))
    tool.setCurrentPoint(_x, _y)
    _segs.append((_px1, _py1, _x, _y))
    widget.window.draw_segments(_gc, _segs)

def leader_final_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setFinalPoint(_x, _y)
    create_entity(gtkimage, tool)
    gtkimage.setPrompt("Click in the drawing area to place the initial point")

def leader_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setMidPoint(_x, _y)
    tool.clearCurrentPoint()
    tool.setHandler("motion_notify", leader_second_motion_notify_cb)
    tool.setHandler("button_press", leader_final_button_press_cb)
    gtkimage.setPrompt("Click in the drawing area to place the final point")

def leader_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setFirstPoint(_x, _y)
    tool.setHandler("motion_notify", leader_first_motion_notify_cb)
    tool.setHandler("button_press", leader_second_button_press_cb)
    gtkimage.getGC().set_function(gtk.gdk.INVERT)
    gtkimage.setPrompt("Click in the drawing area to place the second point")

def leader_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area to enter the first point")
    tool.setHandler("initialize", leader_mode_init)
    tool.setHandler("button_press", leader_first_button_press_cb)

#
# polylines
#

def polyline_motion_notify_cb(gtkimage, widget, event, tool):
    _segs = []
    _x1, _y1 = tool.getPoint(-1)
    _px1, _py1 = gtkimage.coordToPixTransform(_x1, _y1)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _segs.append((_px1, _py1, _xc, _yc))
    tool.setCurrentPoint(_x, _y)
    _segs.append((_px1, _py1, _x, _y))
    widget.window.draw_segments(_gc, _segs)

def polyline_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.clearCurrentPoint()
    tool.storePoint(_x, _y)
    _state = event.state
    if ((_state & gtk.gdk.SHIFT_MASK) == gtk.gdk.SHIFT_MASK):
        create_entity(gtkimage, tool)
    else:
        tool.setHandler("motion_notify", polyline_motion_notify_cb)
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
        gtkimage.setPrompt("Click to place the next point. Shift-click to finish polyline")

def polyline_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        if _text == 'end':
            create_entity(gtkimage, tool)
        else:
            _x, _y = make_tuple(_text)
            tool.setPoint(_x, _y)
            tool.setHandler("motion_notify", polyline_motion_notify_cb)
            gtkimage.getGC().set_function(gtk.gdk.INVERT)
            gtkimage.setPrompt("Click to place the next point. Shift-click to finish polyline.")

def polyline_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area to enter the first point")
    tool.setHandler("initialize", polyline_mode_init)
    tool.setHandler("entry_event", polyline_entry_event_cb)
    tool.setHandler("button_press", polyline_button_press_cb)

#
# polygons
#

def polygon_radius_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setLocation(_x, _y)
    create_entity(gtkimage, tool)

def polygon_radius_motion_notify_cb(gtkimage, widget, event, tool):
    _segs = []
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    _count = tool.getSideCount()
    if _cp is not None:
        _tx0, _ty0 = tool.getCoord(0)
        _px0, _py0 = gtkimage.coordToPixTransform(_tx0, _ty0)
        _px1 = _px0
        _py1 = _py0
        for _i in range(1, _count):
            _txi, _tyi = tool.getCoord(_i)
            _pxi, _pyi = gtkimage.coordToPixTransform(_txi, _tyi)
            _segs.append((_px1, _py1, _pxi, _pyi))
            _px1 = _pxi
            _py1 = _pyi
        _segs.append((_px1, _py1, _px0, _py0))
    tool.setCurrentPoint(_x, _y)
    _ix, _iy = gtkimage.getPoint()
    tool.setLocation(_ix, _iy)
    _tx0, _ty0 = tool.getCoord(0)
    _px0, _py0 = gtkimage.coordToPixTransform(_tx0, _ty0)
    _px1 = _px0
    _py1 = _py0
    for _i in range(1, _count):
        _txi, _tyi = tool.getCoord(_i)
        _pxi, _pyi = gtkimage.coordToPixTransform(_txi, _tyi)
        _segs.append((_px1, _py1, _pxi, _pyi))
        _px1 = _pxi
        _py1 = _pyi
    _segs.append((_px1, _py1, _px0, _py0))
    widget.window.draw_segments(_gc, _segs)

def polygon_center_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setCenter(_x, _y)
    tool.setHandler("motion_notify", polygon_radius_motion_notify_cb)
    tool.setHandler("button_press", polygon_radius_button_press_cb)
    gtkimage.getGC().set_function(gtk.gdk.INVERT)
    gtkimage.setPrompt("Click in the drawing area to place the second point")

def polygon_center_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = make_tuple(_text)
        tool.setCenter(_x, _y)
        tool.setHandler("motion_notify", polygon_radius_motion_notify_cb)
        gtkimage.getGC().set_function(gtk.gdk.INVERT)        
        tool.setHandler("button_press", polygon_radius_button_press_cb)
        tool.delHandler("entry_event") # ???
        gtkimage.setPrompt("Enter the click in the drawing area to draw the polygon")

def polygon_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area to define the center")
    tool.setHandler("initialize", polygon_mode_init)
    tool.setHandler("entry_event", polygon_center_entry_event_cb)
    tool.setHandler("button_press", polygon_center_button_press_cb)

#
# set the active style
#

def set_active_style(gtkimage):
    window = gtkimage.getWindow()
    dialog = gtk.Dialog("Set Active Style", window,
                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                        (gtk.STOCK_OK, gtk.RESPONSE_OK,
                        gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    hbox = gtk.HBox(gtk.FALSE, 2)
    hbox.set_border_width(2)
    dialog.vbox.pack_start(hbox, gtk.FALSE, gtk.FALSE, 0)
    label = gtk.Label("Style:")
    cur_style = gtkimage.getOption('LINE_STYLE')
    styles = gtkimage.getImageEntities("style")
    menu = gtk.Menu()
    idx = 0
    for i in range(len(styles)):
        s = styles[i]
        if s is cur_style:
            idx = i
        item = gtk.MenuItem(s.getName())
        menu.append(item)
    opt_menu = gtk.OptionMenu()
    opt_menu.set_menu(menu)
    opt_menu.set_history(idx)
    hbox.pack_start(label, gtk.FALSE, gtk.FALSE, 0)
    hbox.pack_start(opt_menu, gtk.TRUE, gtk.TRUE, 0)
    dialog.show_all()
    response = dialog.run()
    if response == gtk.RESPONSE_OK:
        s = styles[opt_menu.get_history()]
        gtkimage.setOption('LINE_STYLE', s)
    dialog.destroy()

#
# set the current color
#

def set_active_color(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.ColorSelectionDialog("Set Active Color")
    _dialog.set_transient_for(_window)
    _colorsel = _dialog.colorsel

    _prev_color = gtkimage.getOption('LINE_COLOR')
    if gtkimage.hasGTKColor(_prev_color):
        _gtk_color = gtkimage.getGTKColor(_prev_color)
    else:
        _da = gtkimage.getDA()
        _cstring = str(_prev_color)
        _gtk_color = _da.get_colormap().alloc_color(_cstring)
        gtkimage.saveGTKColor(_prev_color, _gtk_color)
    _colorsel.set_previous_color(_gtk_color)
    _colorsel.set_current_color(_gtk_color)
    _colorsel.set_has_palette(gtk.TRUE)
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _gtk_color = _colorsel.get_current_color()
        _r = int((_gtk_color.red/65535.0)*255)
        _g = int((_gtk_color.green/65535.0)*255)
        _b = int((_gtk_color.blue/65535.0)*255)
        _color = Generic.color.get_color(_r, _g, _b)
        gtkimage.setOption('LINE_COLOR', _color)
    _dialog.destroy()

#
# set the current linetype
#

def set_active_linetype(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Set Active Linetype", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(gtk.FALSE, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, gtk.FALSE, gtk.FALSE, 0)

    _label = gtk.Label("Linetype:")
    _hbox.pack_start(_label, gtk.FALSE, gtk.FALSE, 0)
    _cur_linetype = gtkimage.getOption('LINE_TYPE')
    _linetypes = gtkimage.getImageEntities("linetype")
    _menu = gtk.Menu()
    _idx = 0
    for _i in range(len(_linetypes)):
        _lt = _linetypes[_i]
        if _lt is _cur_linetype:
            _idx = _i
        _item = gtk.MenuItem(_lt.getName())
        _menu.append(_item)
    _opt_menu = gtk.OptionMenu()
    _opt_menu.set_menu(_menu)
    _opt_menu.set_history(_idx)
    _hbox.pack_start(_opt_menu, gtk.TRUE, gtk.TRUE, 0)
    _dialog.show_all()
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _lt = _linetypes[_opt_menu.get_history()]
        gtkimage.setOption('LINE_TYPE', _lt)
    _dialog.destroy()

#
# set the current line thickness
#

def move_cursor(entry):
    entry.set_position(-1)
    return gtk.FALSE

def thickness_activate(entry): # this probably isn't a good choice ...
    entry.stop_emission("activate")

def thickness_focus_out(entry, event):
    _text = entry.get_chars(0, -1)
    if _text == '-' or _text == '+':
        entry.delete_text(0, -1)
    return gtk.FALSE

def thickness_insert_text(entry, new_text, new_text_length, position):
    if (new_text.isdigit() or
        new_text == '.' or
        new_text == '+'):
        _string = entry.get_chars(0, -1) + new_text
        _hid = entry.get_data('handlerid')
        entry.handler_block(_hid)
        _pos = entry.get_position()
        _move = True
        if _string == '+':
            _pos = entry.insert_text(new_text, _pos)
        else:
            try:
                _val = float(_string)
                _pos = entry.insert_text(new_text, _pos)
            except StandardError, e:
                _move = False
        entry.handler_unblock(_hid)
        gtk.idle_add(move_cursor, entry)
    entry.stop_emission("insert-text")

def set_line_thickness(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Set Line Thickness", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK,
                          gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL,
                          gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(gtk.FALSE, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, gtk.FALSE, gtk.FALSE, 0)
    _label = gtk.Label("Thickness:")
    _hbox.pack_start(_label, gtk.FALSE, gtk.FALSE, 0)
    _thickness = "%g" % gtkimage.getOption('LINE_THICKNESS')
    _entry = gtk.Entry()
    _entry.set_text(_thickness)
    _entry.connect("focus_out_event", thickness_focus_out)
    _entry.connect("activate", thickness_activate)
    _handlerid = _entry.connect("insert-text", thickness_insert_text)
    _entry.set_data('handlerid', _handlerid)
    _hbox.pack_start(_entry, gtk.TRUE, gtk.TRUE, 0)
    _dialog.show_all()
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _text = _entry.get_chars(0, -1)
        _thickness = float(_text)
        gtkimage.setOption('LINE_THICKNESS', _thickness)
    _dialog.destroy()
