#
# 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
#
#
# functions for doing modifications on drawing
# entities
#

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

import Generic.dimension
import Generic.color
import Generic.graphicobject
import Generic.segment
import Generic.circle
import Generic.arc
import Generic.polyline

import Generic.move
import Generic.transfer
import Generic.split
import Generic.delete

#
# common code
#

def select_motion_notify(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()
    if _cp is not None:
        _xc, _yc = _cp
        _xmin = min(_xc, _px)
        _ymin = min(_yc, _py)
        _rw = abs(_xc - _px)
        _rh = abs(_yc - _py)
        widget.window.draw_rectangle(_gc, gtk.FALSE, _xmin, _ymin, _rw, _rh)
    tool.setCurrentPoint(_x, _y)
    _xmin = min(_x, _px)
    _ymin = min(_y, _py)
    _rw = abs(_x - _px)
    _rh = abs(_y - _py)
    widget.window.draw_rectangle(_gc, gtk.FALSE, _xmin, _ymin, _rw, _rh)

def entry_get_distance(text):
    try:
        _text_dist = eval(text)
    except:
        raise ValueError, "Invalid text: " + str(text)
    _dist = _text_dist
    if not isinstance(_dist, float):
        _dist = float(_text_dist)
    return _dist
            
#
# move objects
#

def move_objects(gtkimage, objlist, tool):
    _init_func = tool.getHandler("initialize")
    _active_layer = gtkimage.getActiveLayer()
    _dx, _dy = tool.getDistance()
    gtkimage.startAction()
    try:
        Generic.move.move_objects(_active_layer, objlist, _dx, _dy)
    finally:
        gtkimage.endAction()
    gtkimage.redraw()
    gtkimage.setPrompt("Click in the drawing area or enter a distance.")
    tool.reset()
    _init_func(tool)
    
def move_button_press(gtkimage, tool):
    _x, _y = gtkimage.getPoint()
    #
    # need to find if the point is an intersection of drawing objects ...
    #
    tool.pushObject(_x)
    tool.pushObject(_y)

def move_end_button_press_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getPoint()
    _x1, _y1 = tool.getLocation()
    _xmin = min(_x1, _x2)
    _xmax = max(_x1, _x2)
    _ymin = min(_y1, _y2)
    _ymax = max(_y1, _y2)
    _active_layer = gtkimage.getActiveLayer()
    _objlist = _active_layer.objsInRegion(_xmin, _ymin, _xmax, _ymax)
    move_objects(gtkimage, _objlist, tool)

def move_elem_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, None)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            _objs = []
            for _obj, _pt in _objdict[_active_layer]:
                _objs.append(_obj)
            _dx, _dy = tool.getDistance()
            move_objects(gtkimage, _objs, tool)
    else:
        _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
        if _pt is not None:
            _x, _y = _pt.getCoords()
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", select_motion_notify)
        tool.setHandler("button_press", move_end_button_press_cb)
        _gc = gtkimage.getGC()
        _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
                                gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
        _gc.set_function(gtk.gdk.INVERT)
    
#
# move horizontal
#

def move_horizontal_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    _dist = entry_get_distance(_text)
    tool.setDistance(_dist, 0.0)
    gtkimage.setPrompt("Click on the objects to move.")
    tool.setHandler("button_press", move_elem_button_press_cb)
    tool.delHandler("entry_event")

def move_horizontal_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()
    tool.setDistance((_x - _x1), 0.0)
    tool.clearLocation()
    gtkimage.setPrompt("Select the objects to move.")
    tool.setHandler("button_press", move_elem_button_press_cb)
    tool.delHandler("entry_event")

def move_horizontal_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)
    gtkimage.setPrompt("Click another point to define the distance")
    tool.setHandler("button_press", move_horizontal_second_button_press_cb)
    
def move_horizontal_init(tool):
    tool.setHandler("button_press", move_horizontal_first_button_press_cb)
    tool.setHandler("entry_event", move_horizontal_entry_event)
    tool.setHandler("initialize", move_horizontal_init)

#
# move vertical
#

def move_vertical_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_chars(0,-1)
    _entry.delete_text(0,-1)
    _dist = entry_get_distance(_text)
    tool.setDistance(0.0, _dist)
    gtkimage.setPrompt("Click on the objects to move.")
    tool.setHandler("button_press", move_elem_button_press_cb)
    tool.delHandler("entry_event")
    
def move_vertical_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()
    tool.setDistance(0.0, (_y - _y1))
    tool.clearLocation()
    gtkimage.setPrompt("Select the objects to move.")
    tool.setHandler("button_press", move_elem_button_press_cb)
    tool.delHandler("entry_event")

def move_vertical_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)
    gtkimage.setPrompt("Click another point to define the distance")
    tool.setHandler("button_press", move_vertical_second_button_press_cb)

def move_vertical_init(tool):
    tool.setHandler("button_press", move_vertical_first_button_press_cb)
    tool.setHandler("entry_event", move_vertical_entry_event)
    tool.setHandler("initialize", move_vertical_init)

#
# move based on two mouse clicks
#
# it would be good to add an entry event handler here ...
#

def move_twopoint_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()
    tool.setDistance((_x - _x1), (_y - _y1))
    tool.clearLocation()
    gtkimage.setPrompt("Select the objects to move.")
    tool.setHandler("button_press", move_elem_button_press_cb)
    tool.delHandler("entry_event")

def move_twopoint_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)
    gtkimage.setPrompt("Click another point to define the distance")
    tool.setHandler("button_press", move_twopoint_second_button_press_cb)

def move_twopoint_init(tool):
    tool.setHandler("button_press", move_twopoint_first_button_press_cb)
    tool.setHandler("initialize", move_twopoint_init)

#
# delete objects
#

def delete_region_end_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getPoint()
    _x1, _y1 = tool.getLocation()
    _xmin = min(_x1, _x2)
    _xmax = max(_x1, _x2)
    _ymin = min(_y1, _y2)
    _ymax = max(_y1, _y2)
    tool.delHandler("motion_notify")
    tool.setHandler("button_press", delete_button_press_cb)
    _active_layer = gtkimage.getActiveLayer()
    _objs = _active_layer.objsInRegion(_xmin, _ymin, _xmax, _ymax)
    gtkimage.refresh()
    if len(_objs):
        gtkimage.startAction()
        try:
            Generic.delete.delete_objects(_active_layer, _objs)
        finally:
            gtkimage.endAction()
        tool.reset()
        gtkimage.redraw()
    delete_mode_init(tool)
    
def delete_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _active_layer = gtkimage.getActiveLayer()
    _tol = gtkimage.getTolerance()
    _objs = _active_layer.mapPoint((_x, _y), _tol)
    if len(_objs):
        _dims = []
        gtkimage.startAction()
        try:
            for _obj in _objs:
                if isinstance(_obj, Generic.dimension.Dimension):
                    _dims.append(_obj)
                elif isinstance(_obj, tuple):
                    _entity, _pt = _obj
                    _active_layer.delObject(_entity)
                else:
                    raise TypeError, "Unhandled object: " + `_obj`
            for _dim in _dims:
                if _dim in _active_layer: # it may have been removed ...
                    _active_layer.delObject(_dim)
        finally:
            gtkimage.endAction()
        gtkimage.redraw()
    else:
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", select_motion_notify)
        tool.setHandler("button_press", delete_region_end_cb)
        _gc = gtkimage.getGC()
        _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
                                gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
        _gc.set_function(gtk.gdk.INVERT)
        
def delete_mode_init(tool):
    tool.setHandler("button_press", delete_button_press_cb)
    tool.setHandler("initialize", delete_mode_init)

#
# stretch operations
#

def stretch_end_button_press_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getPoint()
    _y1 = tool.popObject()
    _x1 = tool.popObject()
    _xmin = min(_x1, _x2)
    _xmax = max(_x1, _x2)
    _ymin = min(_y1, _y2)
    _ymax = max(_y1, _y2)
    tool.delHandler("motion_notify")
    _active_layer = gtkimage.getActiveLayer()
    _dx, _dy = tool.getDistance()
    for _pt in _active_layer.getLayerEntities("point"):
        if _pt.x > _xmax:
            break
        if _pt.inRegion(_xmin, _ymin, _xmax, _ymax):
            _pt.move(_dx, _dy)
    gtkimage.redraw()
    tool.clearLocation()
    tool.clearCurrentPoint()
    tool.setHandler("button_press", stretch_elem_button_press_cb)

def stretch_elem_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _pt = _active_layer.find('point', _x, _y, _tol)
    if _pt is not None:
        _dx, _dy = tool.getDistance()
        _pt.move(_dx, _dy)
        gtkimage.redraw()
    else:
        tool.pushObject(_x)
        tool.pushObject(_y)
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", select_motion_notify)
        tool.setHandler("button_press", stretch_end_button_press_cb)
        _gc = gtkimage.getGC()
        _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
                                gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
        _gc.set_function(gtk.gdk.INVERT)

#
# stretch horizontal
#

def stretch_horiz_button_press_cb(gtkimage, widget, event, tool):
    if not len(tool):
        move_button_press(gtkimage, tool)
        gtkimage.setPrompt("Click a second point to indicate the horizontal distance")
    else:
        _x, _y = gtkimage.getPoint()
        #
        # see if the point is at an intersection of drawing objects ...
        #
        _y1 = tool.popObject()
        _x1 = tool.popObject()
        tool.setDistance((_x - _x1), 0.0)
        gtkimage.setPrompt("Select the points to move.")
        # tool.delHandler("entry_event")
        tool.setHandler("button_press", stretch_elem_button_press_cb)

def stretch_horizontal_init(tool):
    tool.initialize()
    tool.setHandler("button_press", stretch_horiz_button_press_cb)
    # tool.setHandler("entry_event", stretch_horiz_entry_event)

#
# stretch vertical
#

def stretch_vert_button_press_cb(gtkimage, widget, event, tool):
    if not len(tool):
        move_button_press(gtkimage, tool)
        gtkimage.setPrompt("Click a second point to indicate the vertical distance")
    else:
        _x, _y = gtkimage.getPoint()
        #
        # see if the point is at an intersection of drawing objects ...
        #
        _y1 = tool.popObject()
        _x1 = tool.popObject()
        tool.setDistance(0.0, (_y - _y1))
        gtkimage.setPrompt("Select the points to move.")
        # tool.delHandler("entry_event")
        tool.setHandler("button_press", stretch_elem_button_press_cb)

def stretch_vertical_init(tool):
    tool.initialize()
    tool.setHandler("button_press", stretch_vert_button_press_cb)
    # tool.setHandler("entry_event", stretch_horiz_entry_event)

#
# split objects into two pieces or at intersection points
#

def split_end_button_press_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getPoint()
    _y1 = tool.popObject()
    _x1 = tool.popObject()
    _xmin = min(_x1, _x2)
    _xmax = max(_x1, _x2)
    _ymin = min(_y1, _y2)
    _ymax = max(_y1, _y2)
    tool.delHandler("motion_notify")
    _active_layer = gtkimage.getActiveLayer()
    _objs = _active_layer.objsInRegion(_xmin, _ymin, _xmax, _ymax, True)
    gtkimage.refresh()
    if len(_objs):
        _splitable = []
        for _obj in _objs:
            if isinstance(_obj, (Generic.segment.Segment,
                                 Generic.circle.Circle,
                                 Generic.arc.Arc,
                                 Generic.polyline.Polyline)):
                _splitable.append(_obj)
        if len(_splitable):
            gtkimage.startAction()
            try:
                Generic.split.split_objects(_active_layer, _splitable)
            finally:
                gtkimage.endAction()
        gtkimage.redraw()
    gtkimage.setPrompt("Click on the objects you want to split.")
    tool.clearLocation()
    tool.clearCurrentPoint()
    tool.setHandler("button_press", split_object_button_press_cb)
    
def split_object_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _objlist = _active_layer.mapPoint((_x, _y), _tol, None)
    if len(_objlist):
        _redraw = False
        for _obj, _pt in _objlist:
            if isinstance(_obj, Generic.segment.Segment):
                _redraw = True
                _p1, _p2 = _obj.getEndpoints()
                _lpt = _active_layer.find('point', _pt.x, _pt.y)
                if _lpt is None:
                    _active_layer.addObject(_pt)
                    _lpt = _pt
                _s1, _s2 = Generic.split.split_segment(_obj, _pt)
                gtkimage.startAction()
                try:
                    _active_layer.addObject(_s1)
                    _active_layer.addObject(_s2)
                    _active_layer.delObject(_obj)
                finally:
                    gtkimage.endAction()
            elif isinstance(_obj, Generic.arc.Arc):
                _redraw = True
                _arc1, _arc2 = Generic.split.split_arc(_obj, _pt)
                gtkimage.startAction()
                try:
                    _active_layer.addObject(_arc1)
                    _active_layer.addObject(_arc2)
                    _active_layer.delObject(_obj)
                finally:
                    gtkimage.endAction()
            elif isinstance(_obj, Generic.circle.Circle):
                _redraw = True
                _arc = Generic.split.split_circle(_obj, _pt)
                gtkimage.startAction()
                try:
                    _active_layer.addObject(_arc)
                    _active_layer.delObject(_obj)
                finally:
                    gtkimage.endAction()
            elif isinstance(_obj, Generic.polyline.Polyline):
                _redraw = True
                _lpt = _active_layer.find('point', _pt.x, _pt.y)
                gtkimage.startAction()
                try:
                    if _lpt is None:
                        _active_layer.addObject(_pt)
                        _lpt = _pt
                    Generic.split.split_polyline(_obj, _lpt)
                finally:
                    gtkimage.endAction()
            else:
                pass
        if _redraw:
            gtkimage.redraw()
    else:
        tool.pushObject(_x)
        tool.pushObject(_y)
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", select_motion_notify)
        tool.setHandler("button_press", split_end_button_press_cb)
        _gc = gtkimage.getGC()
        _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
                                gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
        _gc.set_function(gtk.gdk.INVERT)
        
def split_object_init(tool):
    tool.initialize()
    tool.setHandler("button_press", split_object_button_press_cb)
    tool.setHandler("initialize", split_object_init)

#
# transfer objects from one layer to another
#

def transfer_end_button_press_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getPoint()
    _y1 = tool.popObject()
    _x1 = tool.popObject()
    _xmin = min(_x1, _x2)
    _xmax = max(_x1, _x2)
    _ymin = min(_y1, _y2)
    _ymax = max(_y1, _y2)
    tool.delHandler("motion_notify")
    _active_layer = gtkimage.getActiveLayer()
    _layers = [gtkimage.getTopLayer()]
    _objdict = {}
    while len(_layers):
        _layer = _layers.pop()
        if _layer is not _active_layer:
            if _layer.isVisible():
                _objs = _layer.objsInRegion(_xmin, _ymin, _xmax, _ymax)
                if len(_objs):
                    _objdict[_layer] = _objs
        _layers.extend(_layer.getSublayers())
    if len(_objdict):
        gtkimage.startAction()
        try:
            for _layer in _objdict:
                if _layer is not _active_layer:
                    _objs = _objdict[_layer]
                    Generic.transfer.transfer_objects(_objs, _layer, _active_layer)
        finally:
            gtkimage.endAction()
        gtkimage.redraw()
    tool.clearLocation()
    tool.clearCurrentPoint()
    tool.setHandler("button_press", transfer_object_button_press_cb)
    
def transfer_object_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, None)
    if len(_objdict):
        gtkimage.startAction()
        try:
            for _layer in _objdict:
                if _layer is not _active_layer:
                    _objs = []
                    for _obj, _pt in _objdict[_layer]:
                        _objs.append(_obj)
                    Generic.transfer.transfer_objects(_objs, _layer, _active_layer)
        finally:
            gtkimage.endAction()
        gtkimage.redraw()
    else:
        tool.pushObject(_x)
        tool.pushObject(_y)
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", select_motion_notify)
        tool.setHandler("button_press", transfer_end_button_press_cb)
        gc = gtkimage.getGC()
        gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
                               gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
        gc.set_function(gtk.gdk.INVERT)
        
def transfer_object_init(tool):
    tool.initialize()
    tool.setHandler("button_press", transfer_object_button_press_cb)
    
#
# change color
#

def change_color_init(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()
        _red = int((_gtk_color.red/65535.0) * 255.0)
        _green = int((_gtk_color.green/65535.0) * 255.0)
        _blue = int((_gtk_color.blue/65535.0) * 255.0)
        _col = Generic.color.get_color(_red, _green, _blue)
        if gtkimage.hasSelection():
            _active_layer = gtkimage.getActiveLayer()
            _objs = []
            for _layer, _obj in gtkimage.getSelectedObjects():
                if _layer is _active_layer:
                    if isinstance(_obj, Generic.graphicobject.GraphicObject):
                        _objs.append(_obj)
            if len(_objs):
                gtkimage.startAction()
                try:
                    for _obj in _objs:
                        _obj.setColor(_col)
                finally:
                    gtkimage.endAction()
    _dialog.destroy()
    gtkimage.redraw()

#
# change linetypes
#

def change_linetype_init(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change 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)
    _clt = gtkimage.getOption('LINE_TYPE')
    _linetypes = gtkimage.getAllLinetypes()
    _menu = gtk.Menu()
    _idx = 0
    for _i in range(len(_linetypes)):
        _lt = _linetypes[_i]
        if _lt is _clt:
            _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()]
        if gtkimage.hasSelection():
            _active_layer = gtkimage.getActiveLayer()
            _objs = []
            for _layer, _obj in gtkimage.getSelectedObjects():
                if _layer is _active_layer:
                    if isinstance(_obj, Generic.graphicobject.GraphicObject):
                        _objs.append(_obj)
            if len(_objs):
                gtkimage.startAction()
                try:
                    for _obj in _objs:
                        _obj.setLinetype(_lt)
                finally:
                    gtkimage.endAction()
    _dialog.destroy()
    gtkimage.redraw()

#
# change thickness
#

def change_thickness_init(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change 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 = gtkimage.getOption('LINE_THICKNESS')
    _adj = gtk.Adjustment(_thickness, 0.0001, 20.0, 0.1, 1.0, 1.0)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(1)
    _sb.set_numeric(gtk.FALSE)
    _hbox.pack_start(_sb, gtk.TRUE, gtk.TRUE, 0)
    _dialog.show_all()
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _nt = float(_sb.get_value())
        if gtkimage.hasSelection():
            _active_layer = gtkimage.getActiveLayer()
            _objs = []
            for _layer, _obj in gtkimage.getSelectedObjects():
                if _layer is _active_layer:
                    if isinstance(_obj, Generic.graphicobject.GraphicObject):
                        _objs.append(_obj)
            if len(_objs):
                gtkimage.startAction()
                try:
                    for _obj in _objs:
                        _obj.setThickness(_nt)
                finally:
                    gtkimage.endAction()
    _dialog.destroy()
    gtkimage.redraw()

#
# change the style
#

def change_style_init(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change 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:")
    _cst = gtkimage.getOption('LINE_STYLE')
    _styles = gtkimage.getAllStyles()
    _menu = gtk.Menu()
    _idx = 0
    for _i in range(len(_styles)):
        _s = _styles[_i]
        if _s is _cst:
            _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()]
        if gtkimage.hasSelection():
            _active_layer = gtkimage.getActiveLayer()
            _objs = []
            for _layer, _obj in gtkimage.getSelectedObjects():
                if _layer is _active_layer:
                    if isinstance(_obj, Generic.graphicobject.GraphicObject):
                        _objs.append(_obj)
            if len(_objs):
                gtkimage.startAction()
                try:
                    for _obj in _objs:
                        _obj.setStyle(_s)
                finally:
                    gtkimage.endAction()
    _dialog.destroy()
    gtkimage.redraw()

#
# arbitrary zoom
#

def zoom_end_button_press_cb(gtkimage, widget, event, tool):
    _xp, _yp = gtkimage.getPoint()
    _x1, _y1 = tool.getLocation()
    _xmin = min(_xp, _x1)
    _ymin = min(_yp, _y1)
    _width, _height = gtkimage.getSize()
    _fw = float(_width)
    _fh = float(_height)
    _wpp = abs(_x1 - _xp)/_fw
    _hpp = abs(_y1 - _yp)/_fh
    if _wpp > _hpp:
        _scale = _wpp
    else:
        _scale = _hpp
    gtkimage.setView(_xmin, _ymin, _scale)
    gtkimage.setPrompt("Click in the drawing.")
    gtkimage.redraw()
    zoom_init(tool)

def zoom_motion_notify(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()
    #
    # it would be nice to draw the rectangle in the current
    # shape of the window ...
    #
    if _cp is not None:
        _xc, _yc = _cp
        _xmin = min(_px, _xc)
        _ymin = min(_py, _yc)
        _rw = abs(_xc - _px)
        _rh = abs(_yc - _py)
        widget.window.draw_rectangle(_gc, gtk.FALSE, _xmin, _ymin, _rw, _rh)
    _xmin = min(_x, _px)
    _ymin = min(_y, _py)
    tool.setCurrentPoint(_x, _y)
    _rw = abs(_x - _px)
    _rh = abs(_y - _py)
    widget.window.draw_rectangle(_gc, gtk.FALSE, _xmin, _ymin, _rw, _rh)

def zoom_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    tool.setLocation(_x, _y)
    tool.setHandler("motion_notify", zoom_motion_notify)
    tool.setHandler("button_press", zoom_end_button_press_cb)
    gtkimage.setPrompt("Click a second point to define the zoom window.")
    _gc = gtkimage.getGC()
    _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
                            gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
    _gc.set_function(gtk.gdk.INVERT)

def zoom_init(tool):
    tool.initialize()
    tool.setHandler("button_press", zoom_button_press_cb)
