#
# 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
#
#
# the event handling bits for construction lines
# and circles
#

import math

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

import Generic.point
import Generic.segment
import Generic.circle
import Generic.arc
import Generic.hcline
import Generic.vcline
import Generic.acline
import Generic.cline
import Generic.ccircle
import Generic.util
import Generic.color

import gtkentities

#
# horizontal construction lines
#

def hcline_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:
        _x, _y = _pt.getCoords()
    tool.setPoint(_x, _y)
    gtkentities.create_entity(gtkimage, tool)

def hcline_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _textval = eval(_text)
        _val = _textval
        if not isinstance(_val, float):
            _val = float(_textval)
        tool.setPoint(0.0, _val)
        gtkentities.create_entity(gtkimage, tool)
            
def hcline_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a point.")
    tool.setHandler("initialize", hcline_mode_init)
    tool.setHandler("button_press", hcline_button_press_cb)
    tool.setHandler("entry_event", hcline_entry_event_cb)

#
# vertical construction lines
#

def vcline_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:
        _x, _y = _pt.getCoords()
    tool.setPoint(_x, _y)
    gtkentities.create_entity(gtkimage, tool)

def vcline_entry_event_cb(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _textval = eval(_text)
        _val = _textval
        if not isinstance(_val, float):
            _val = float(_textval)
        tool.setPoint(_val, 0.0)
        gtkentities.create_entity(gtkimage, tool)

def vcline_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter x coordinate")
    tool.setHandler("initialize", vcline_mode_init)
    tool.setHandler("button_press", vcline_button_press_cb)
    tool.setHandler("entry_event", vcline_entry_event_cb)

#
# common functions for for acline, cline, and ccircle objects
#

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

def acline_motion_notify_cb(gtkimage, widget, event, tool):
    _segs = []
    _ax, _ay = tool.getPoint()
    _pax, _pay = gtkimage.coordToPixTransform(_ax, _ay)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _segs.append((_pax, _pay, _xc, _yc))
    _xl, _yl = gtkimage.getPoint()
    tool.setLocation(_xl, _yl)
    tool.setCurrentPoint(_x, _y)
    _segs.append((_pax, _pay, _x, _y))
    widget.window.draw_segments(_gc, _segs)
        
def acline_entry_make_angle(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _angle = Generic.util.make_angle(_text)
        tool.setAngle(_angle)
        gtkentities.create_entity(gtkimage, tool)

def acline_entry_make_pt(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)
        tool.setHandler("button_press", acline_second_button_press_cb)
        tool.setHandler("entry_event", acline_entry_make_angle)
        tool.setHandler("motion_notify", acline_motion_notify_cb)
        gtkimage.setPrompt("Enter the angle or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)

def acline_second_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:
        _x, _y = _pt.getCoords()
    tool.setLocation(_x, _y)
    gtkentities.create_entity(gtkimage, tool)

def acline_first_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:
        _x, _y = _pt.getCoords()
    tool.setPoint(_x, _y)
    tool.setHandler("button_press", acline_second_button_press_cb)
    tool.setHandler("entry_event", acline_entry_make_angle)
    tool.setHandler("motion_notify", acline_motion_notify_cb)
    gtkimage.setPrompt("Enter the angle or click in the drawing area")
    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def acline_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a Point")
    tool.setHandler("initialize", acline_mode_init)
    tool.setHandler("button_press", acline_first_button_press_cb)
    tool.setHandler("entry_event", acline_entry_make_pt)

#
# two point construction line
#

def cline_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))
    _ix, _iy = gtkimage.getPoint()
    tool.setSecondPoint(_ix, _iy)
    tool.setCurrentPoint(_x, _y)
    _segs.append((_px1, _py1, _x, _y))
    widget.window.draw_segments(_gc, _segs)

def cline_second_entry_make_pt(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)
        gtkentities.create_entity(gtkimage, tool)

def cline_first_entry_make_pt(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", cline_second_button_press_cb)
        tool.setHandler("motion_notify", cline_motion_notify_cb)
        tool.setHandler("entry_event", cline_second_entry_make_pt)
        gtkimage.setPrompt("Enter the second Point or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)

def cline_second_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:
        _x, _y = _pt.getCoords()
    tool.setSecondPoint(_x, _y)
    gtkentities.create_entity(gtkimage, tool)

def cline_first_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:
        _x, _y = _pt.getCoords()
    tool.setFirstPoint(_x, _y)
    tool.setHandler("button_press", cline_second_button_press_cb)
    tool.setHandler("entry_event", cline_second_entry_make_pt)
    tool.setHandler("motion_notify", cline_motion_notify_cb)
    gtkimage.setPrompt("Enter the second point or click in the drawing area")
    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def cline_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click in the drawing area or enter a Point")
    tool.setHandler("initialize", cline_mode_init)
    tool.setHandler("button_press", cline_first_button_press_cb)
    tool.setHandler("entry_event", cline_first_entry_make_pt)

#
# construction circles
#

def ccircle_make_point(gtkimage, text, addflag=True):
    _tpl = make_tuple(text)
    _x, _y = _tpl
    _pt = gtkimage.findPoint(_x, _y)
    if _pt is None:
        _pt = Generic.point.Point(_x, _y)
    return _pt

def ccircle_make_radius(text):
    _text_rad = eval(text)
    _rad = _text_rad
    if not isinstance(_rad, float):
        _rad = float(_text_rad)
    if _rad < 1e-10:
        raise ValueError, "Invalid radius: %g" % _rad
    return _rad

def ccircle_motion_notify_cb(gtkimage, widget, event, tool):
    _tx, _ty = tool.getLocation()
    _px, _py = gtkimage.coordToPixTransform(_tx, _ty)
    _width, _height = gtkimage.getSize()
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _radius = math.hypot((_px - _xc),(_py - _yc))
        _xmin = _px - _radius
        _ymin = _py - _radius
        _cw = _ch = _radius * 2
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                               0, 360*64)
    tool.setCurrentPoint(_x, _y)
    _radius = math.hypot((_px - _x),(_py - _y))
    _xmin = _px - _radius
    _ymin = _py - _radius
    _cw = _ch = _radius * 2
    widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch, 0, 360*64)

def ccircle_entry_make_radius(gtkimage, widget, tool):
    entry = gtkimage.getEntry()
    text = entry.get_chars(0,-1)
    entry.delete_text(0,-1)
    if len(text):
        try:
            radius =  ccircle_make_radius(text)
        except:
            print "Invalid radius: " + `text` # need better error handling
            return
        tool.pushObject(radius)
        ccircle_create(gtkimage, tool)

def ccircle_create(gtkimage, tool):
    assert len(tool) == 2, "CCircle tool should hold two objects."
    _obj = tool.popObject() # radius or a Point
    _cen = tool.popObject() # center Point
    if isinstance(_obj, float):
        _radius = _obj
    elif isinstance(_obj, Generic.point._Point):
        _radius = _obj - _cen
    else:
        raise "Unexpected object type: " + `_obj`
    _active_layer = gtkimage.getActiveLayer()
    gtkimage.startAction()
    try:
        if _active_layer.findObject(_cen) is None:
            _active_layer.addObject(_cen)
        gtkimage.addObject(Generic.ccircle.CCircle(_cen, _radius))
    finally:
        gtkimage.endAction()
    gtkimage.redraw()
    gtkimage.setPrompt("Click in drawing area or enter a Point")
    init_func = tool.getHandler("initialize")
    init_func(tool)

def ccircle_entry_make_pt(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        try:
            _p = ccircle_make_point(gtkimage, _text)
        except:
            print "Invalid point: " + `_text` # need better error handling
            return
        tool.pushObject(_p)
        if len(tool) == 1:
            _x, _y = _p.getCoords()
            tool.storeCoords(_x, _y)
            tool.setLocation(_x, _y)
            tool.setHandler("motion_notify", ccircle_motion_notify_cb)
            tool.setHandler("entry_event", ccircle_entry_make_radius)
            gtkimage.setPrompt("Enter the radius or click in the drawing area")
            gtkimage.getGC().set_function(gtk.gdk.INVERT)
        else:
            ccircle_create(gtkimage, tool)

def ccircle_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 None:
        _pt = Generic.point.Point(_x, _y)
    tool.pushObject(_pt)
    if len(tool) == 1:
        _x, _y = _pt.getCoords()
        tool.storeCoords(_x, _y)
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", ccircle_motion_notify_cb)
        tool.setHandler("entry_event", ccircle_entry_make_radius)
        gtkimage.setPrompt("Enter the radius or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
    else:
        ccircle_create(gtkimage, tool)

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

#
# two-point construction circle
#

def ccircle_tp_create(gtkimage, tool):
    assert len(tool) == 2, "CCircle TP tool should hold two objects."
    _p2 = tool.popObject()
    _p1 = tool.popObject()
    _xc = (_p1.x + _p2.x)/2.0
    _yc = (_p1.y + _p2.y)/2.0
    _rad = (_p2 - _p1)/2.0
    _pt, _new_pt = gtkimage.findPoint(_xc, _yc)
    gtkimage.startAction()
    try:
        if _pt is None:
            _pt = Generic.point.Point(_xc, _yc)
            gtkimage.addObject(_pt)
        gtkimage.addObject(Generic.ccircle.CCircle(_pt, _rad))
    finally:
        gtkimage.endAction()
    gtkimage.redraw()
    gtkimage.setPrompt("Click in drawing area or enter a Point")
    init_func = tool.getHandler("initialize")
    init_func(tool)


def ccircle_tp_motion_notify_cb(gtkimage, widget, event, tool):
    _tx, _ty = tool.getCoordPair(0)
    _px, _py = gtkimage.coordToPixTransform(_tx, _ty)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _xcen = _px + (_xc - _px)//2
        _ycen = _py + (_yc - _py)//2
        _radius = math.hypot((_xc - _xcen), (_yc - _ycen))
        _xmin = _xcen - _radius
        _ymin = _ycen - _radius
        _cw = _ch = _radius * 2
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch,
                               0, 360*64)
    tool.setCurrentPoint(_x, _y)
    _xcen = _px + (_x - _px)//2
    _ycen = _py + (_y - _py)//2
    _radius = math.hypot((_x - _xcen), (_y - _ycen))
    _xmin = _xcen - _radius
    _ymin = _ycen - _radius
    _cw = _ch = _radius * 2
    widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _cw, _ch, 0, 360*64)

def ccircle_tp_entry_make_pt(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        try:
            _p = ccircle_make_point(gtkimage, _text, False)
        except:
            print "Invalid point: " + `_text` # need better error handling
            return
        tool.pushObject(_p)
        if len(tool) == 1:
            _x, _y = _p.getCoords()
            tool.storeCoords(_x, _y)
            tool.setLocation(_x, _y)
            tool.setHandler("motion_notify", ccircle_tp_motion_notify_cb)
            gtkimage.setPrompt("Enter the second point or click in the drawing area")
            gtkimage.getGC().set_function(gtk.gdk.INVERT)
        else:
            ccircle_tp_create(gtkimage, tool)

def ccircle_tp_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 None:
        _pt = Generic.point.Point(_x, _y)
    tool.pushObject(_pt)
    if len(tool) == 1:
        _x, _y = _pt.getCoords()
        tool.storeCoords(_x, _y)
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", ccircle_tp_motion_notify_cb)
        gtkimage.setPrompt("Enter the second point or click in the drawing area")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
    else:
        ccircle_tp_create(gtkimage, tool)

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

#
# perpendicular construction line creation
#

def perp_cline_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _hits = _active_layer.mapPoint((_x, _y), _tol, 1)
    if len(_hits):
        _obj, _pt = _hits[0]
        _lp = _active_layer.findObject(_pt)
        _add_pt = False
        _pcl = None
        if _lp is None:
            _lp = _pt
            _add_pt = True
        if isinstance(_obj, Generic.segment.Segment):
            _p1, _p2 = _obj.getEndpoints()
            _p1x, _p1y = _p1.getCoords()
            _p2x, _p2y = _p2.getCoords()
            if abs(_p1x - _p2x) < 1e-10: # vertical
                _pcl = Generic.hcline.HCLine(_lp)
            elif abs(_p1y - _p2y) < 1e-10: # horizontal
                _pcl = Generic.vcline.VCLine(_lp)
            else:
                _slope = (180.0/math.pi) * math.atan2((_p2y - _p1y),
                                                      (_p2x - _p1x)) + 90.0
                _pcl = Generic.acline.ACLine(_lp, _slope)
        elif isinstance(_obj, (Generic.circle.Circle,
                               Generic.arc.Arc,
                               Generic.ccircle.CCircle)):
            _cp = _obj.getCenter()
            _pcl = Generic.cline.CLine(_cp, _lp)
        elif isinstance(_obj, Generic.hcline.HCLine):
            _pcl = Generic.vcline.VCLine(_lp)
        elif isinstance(_obj, Generic.vcline.VCLine):
            _pcl = Generic.hcline.HCLine(_lp)
        elif isinstance(_obj, Generic.acline.ACLine):
            _angle = _obj.getAngle()
            if abs(_angle) < 1e-10: # horizontal
                _pcl = Generic.vcline.VCLine(_lp)
            elif abs(abs(_angle) - 90.0) < 1e-10: # vertical
                _pcl = Generic.hcline.HCLine(_lp)
            else:
                _slope = _angle + 90.0
                _pcl = Generic.acline.ACLine(_lp, _slope)
        elif isinstance(_obj, Generic.cline.CLine):
            _p1, _p2 = _obj.getKeypoints()
            _p1x, _p1y = _p1.getCoords()
            _p2x, _p2y = _p2.getCoords()
            if abs(_p1x - _p2x) < 1e-10: # vertical
                _pcl = Generic.hcline.HCLine(_lp)
            elif abs(_p1y - _p2y) < 1e-10: # horizontal
                _pcl = Generic.vcline.VCLine(_lp)
            else:
                _slope = (180.0/math.pi) * math.atan2((_p2y - _p1y),
                                                      (_p2x - _p1x)) + 90.0
                _pcl = Generic.acline.ACLine(_lp, _slope)
        else:
            pass
        gtkimage.startAction()
        try:
            if _add_pt:
                _active_layer.addObject(_lp)
            if _pcl is not None:
                _active_layer.addObject(_pcl)
        finally:
            gtkimage.endAction()
        if _add_pt or _pcl is not None:
            gtkimage.redraw()

def perpendicular_cline_mode_init(tool):
    tool.initialize()
    tool.setHandler("button_press", perp_cline_button_press_cb)

#
# tangent cline creation
#

def tangent_cline_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:
        _x, _y = _pt.getCoords()
        _layer = gtkimage.getActiveLayer()
        _rtd = 180.0/math.pi
        _cobj = None
        _angle = None
        _circles = (_layer.getLayerEntities("circle") +
                    _layer.getLayerEntities("ccircle"))
        for _circle in _circles:
            _cx, _cy = _circle.getCenter().getCoords()
            _rad = _circle.getRadius()
            if abs(math.hypot((_x - _cx), (_y - _cy)) - _rad) < 1e-10:
                _cobj = _circle
                _angle = _rtd * math.atan2((_y - _cy), (_x - _cx))
                if _angle < 0.0:
                    _angle = _angle + 360.0
                break
        if _cobj is None:
            for _arc in _layer.getLayerEntities("arc"):
                _cx, _cy = _arc.getCenter().getCoords()
                _rad = _arc.getRadius()
                if abs(math.hypot((_cx - _x), (_cy - _y)) - _rad) < 1e-10:
                    _angle = _rtd * math.atan2((_y - _cy), (_x - _cx))
                    if _angle < 0.0:
                        _angle = _angle + 360.0
                    if _arc.throughAngle(_angle):
                        _cobj = _arc
                        break
        if _cobj is not None:
            gtkimage.startAction()
            try:
                if _new_pt:
                    _layer.addObject(_pt)
                if (abs(_angle) < 1e-6 or
                    abs(_angle - 180.0) < 1e-6 or
                    abs(_angle - 360.0) < 1e-6):
                    _tcl = Generic.vcline.VCLine(_pt)
                elif (abs(_angle - 90.0) < 1e-6 or
                      abs(_angle - 270.0) < 1e-6):
                    _tcl = Generic.hcline.HCLine(_pt)
                else:
                    _slope = _angle + 90.0
                    _tcl = Generic.acline.ACLine(_pt, _slope)
                _layer.addObject(_tcl)
            finally:
                gtkimage.endAction()
            gtkimage.redraw()

def tangent_cline_mode_init(tool):
    tool.initialize()
    tool.setHandler("button_press", tangent_cline_button_press_cb)

#
# parallel offset mode
#

def parallel_refpt_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    tool.setReferencePoint(_x, _y)
    _init_func = tool.getHandler("initialize")
    tool.create(gtkimage)
    gtkimage.redraw()
    _init_func(gtkimage, tool)

def parallel_conline_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, 1)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            for _obj, _pt in _objdict[_active_layer]:
                if isinstance(_obj, (Generic.hcline.HCLine,
                                     Generic.vcline.VCLine,
                                     Generic.acline.ACLine,
                                     Generic.cline.CLine)):
                    tool.setConstructionLine(_obj)
                    tool.setHandler("button_press", parallel_refpt_button_press_cb)
                    gtkimage.setPrompt("Click on the side to place the new construction line.")

def parallel_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()
    _x1, _y1 = tool.getLocation()
    _offset = math.hypot((_x - _x1), (_y - _y1))
    tool.setOffset(_offset)
    tool.setHandler("button_press", parallel_conline_button_press_cb)
    gtkimage.setPrompt("Click on the reference construction line.")

def parallel_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.setLocation(_x, _y)
    tool.setHandler("button_press", parallel_second_button_press_cb)
    tool.delHandler("entry_event")
    gtkimage.setPrompt("Click another point to define the offset distance.")

def parallel_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    if len(_text):
        _textdist = eval(_text)
        _dist = _textdist
        if not isinstance(_dist, float):
            _dist = float(_textdist)
        tool.setOffset(_dist)
        tool.delHandler("entry_event")
        tool.setHandler("button_press", parallel_conline_button_press_cb)
        gtkimage.setPrompt("Click on the reference construction line.")

def parallel_offset_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Enter the distance or click in the drawing area.")
    tool.setHandler("initialize", parallel_offset_mode_init)
    tool.setHandler("button_press", parallel_first_button_press_cb)
    tool.setHandler("entry_event", parallel_entry_event)

#
# construction circle tangent to a construction line
#

def ccircle_single_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.setLocation(_x, _y)
    gtkentities.create_entity(gtkimage, tool)

def ccircle_single_motion_notify_cb(gtkimage, widget, event, tool):
    _gc = gtkimage.getGC()
    _upp = gtkimage.getUnitsPerPixel()
    _rect = tool.getPixelRect()
    if _rect is not None:
        _xmin, _ymin, _width, _height = _rect
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _width, _height,
                               0, 360*64)
    _ix, _iy = gtkimage.getPoint()
    tool.setLocation(_ix, _iy)
    _cx, _cy = tool.getCenter()
    _radius = tool.getRadius()
    _pcx, _pcy = gtkimage.coordToPixTransform(_cx, _cy)
    _pr = int(_radius/_upp)
    _xmin = _pcx - _pr
    _ymin = _pcy - _pr
    _width = _height = _pr * 2
    tool.setPixelRect(_xmin, _ymin, _width, _height)
    widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _width, _height,
                           0, 360*64)

def ccircle_single_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, 1)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            for _obj, _pt in _objdict[_active_layer]:
                if isinstance(_obj, (Generic.hcline.HCLine,
                                     Generic.vcline.VCLine,
                                     Generic.acline.ACLine,
                                     Generic.cline.CLine,
                                     Generic.ccircle.CCircle)):
                    tool.setConstructionLine(_obj)
                    tool.setHandler("button_press", ccircle_single_second_button_press_cb)
                    tool.setHandler("motion_notify", ccircle_single_motion_notify_cb)
                    gtkimage.setPrompt("Click where the circle should be drawn.")
                    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def tangent_ccircle_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click on the construction object used for tangency.")
    tool.setHandler("initialize", tangent_ccircle_mode_init)
    tool.setHandler("button_press", ccircle_single_first_button_press_cb)

#
# construction circle between two construction lines
#

def two_cline_set_circle_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)
    gtkentities.create_entity(gtkimage, tool)

def two_cline_motion_notify_cb(gtkimage, widget, event, tool):
    _gc = gtkimage.getGC()
    _upp = gtkimage.getUnitsPerPixel()
    _rect = tool.getPixelRect()
    if _rect is not None:
        _xmin, _ymin, _width, _height = _rect
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _width, _height,
                               0, 360*64)
    _ix, _iy = gtkimage.getPoint()
    tool.setLocation(_ix, _iy)
    _radius = tool.getRadius()
    if _radius > 0.0:
        _cx, _cy = tool.getCenter()
        _pcx, _pcy = gtkimage.coordToPixTransform(_cx, _cy)
        _pr = int(_radius/_upp)
        _xmin = _pcx - _pr
        _ymin = _pcy - _pr
        _width = _height = _pr * 2
        tool.setPixelRect(_xmin, _ymin, _width, _height)
        widget.window.draw_arc(_gc, gtk.FALSE, _xmin, _ymin, _width, _height,
                               0, 360*64)
    
def two_cline_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, 1)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            _first_conobj = tool.getFirstConObject()
            for _obj, _pt in _objdict[_active_layer]:
                if _obj is _first_conobj:
                    continue
                if isinstance(_obj, (Generic.hcline.HCLine,
                                     Generic.vcline.VCLine,
                                     Generic.acline.ACLine,
                                     Generic.cline.CLine)):
                    tool.setHandler("button_press", two_cline_set_circle_cb)
                    tool.setHandler("motion_notify", two_cline_motion_notify_cb)
                    tool.setSecondConObject(_obj)
                    gtkimage.setPrompt("Click where you want the tangent circle to be.")
                    gtkimage.getGC().set_function(gtk.gdk.INVERT)

def two_cline_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, 1)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            for _obj, _pt in _objdict[_active_layer]:
                if isinstance(_obj, (Generic.hcline.HCLine,
                                     Generic.vcline.VCLine,
                                     Generic.acline.ACLine,
                                     Generic.cline.CLine,
                                     Generic.ccircle.CCircle)):
                    tool.setHandler("button_press", two_cline_second_button_press_cb)
                    tool.setFirstConObject(_obj)
                    gtkimage.setPrompt("Click on the second construction line for tangency.")

def two_cline_tancc_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click on the first construction line for tangency.")
    tool.setHandler("initialize", two_cline_tancc_mode_init)
    tool.setHandler("button_press", two_cline_first_button_press_cb)

#
# tangent lines around two circles
#

def two_ccircle_tangent_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    tool.setLocation(_x, _y)
    gtkentities.create_entity(gtkimage, tool)    

def _draw_two_circle_tangents(gtkimage, tool):
    _tanpts = tool.getTangentPoints()
    assert len(_tanpts), "No tangent points defined!"
    _gc = gtkimage.getGC()
    _da = gtkimage.getDA()
    #
    # adjust the GC to draw the temporary segments in
    # a distinctive manner
    #
    _gc.set_line_attributes(1, gtk.gdk.LINE_DOUBLE_DASH,
                            gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
    _gc.set_dashes(0, [3, 3])
    _cstring = '#ffff99' # yellowish color
    _tempcolor = Generic.color.Color(_cstring)
    if gtkimage.hasGTKColor(_tempcolor):
        _color = gtkimage.getGTKColor(_tempcolor)
    else:
        _color = _da.get_colormap().alloc_color(_cstring)
        gtkimage.saveGTKColor(_tempcolor, _color)
    _gc.set_foreground(_color)
    _gc.set_function(gtk.gdk.COPY)
    #
    _segs = []
    for _set in _tanpts:
        _x1, _y1, _x2, _y2 = _set
        # print "x1: %g; y1: %g" % (_x1, _y1)
        # print "x2: %g; y2: %g" % (_x2, _y2)
        _px1, _py1 = gtkimage.coordToPixTransform(_x1, _y1)
        _px2, _py2 = gtkimage.coordToPixTransform(_x2, _y2)
        _segs.append((_px1, _py1, _px2, _py2))
    _da.window.draw_segments(_gc, _segs)

def two_circle_tangent_second_button_press_cb(gtkimage, widget, event, tool):
    # print "in second_button_press_cb() ..."
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, 1)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            for _obj, _pt in _objdict[_active_layer]:
                if isinstance(_obj, Generic.ccircle.CCircle):
                    tool.setHandler("button_press", two_circle_tangent_second_button_press_cb)
                    tool.setSecondCCircle(_obj)
                    if tool.hasTangentPoints():
                        _draw_two_circle_tangents(gtkimage, tool)
                        gtkimage.setPrompt("Click on the segment to keep.")
                        tool.setHandler("button_press", two_ccircle_tangent_cb)
                    else:
                        tool.reset()
                        two_circle_tangent_line_mode_init(gtkimage, tool)

def two_circle_tangent_first_button_press_cb(gtkimage, widget, event, tool):
    # print "in first_button_press_cb() ..."
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, 1)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            for _obj, _pt in _objdict[_active_layer]:
                if isinstance(_obj, Generic.ccircle.CCircle):
                    tool.setHandler("button_press", two_circle_tangent_second_button_press_cb)
                    tool.setFirstCCircle(_obj)
                    gtkimage.setPrompt("Click on the second construction circle.")

def two_circle_tangent_line_mode_init(gtkimage, tool):
    gtkimage.setPrompt("Click on the first construction circle.")
    tool.setHandler("initialize", two_circle_tangent_line_mode_init)
    tool.setHandler("button_press", two_circle_tangent_first_button_press_cb)
