#
# Copyright (c) 2002, 2003, 2004, 2005, 2006 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

from PythonCAD.Generic.dimension import Dimension
from PythonCAD.Generic.dimension import RadialDimension
from PythonCAD.Generic.dimension import AngularDimension
from PythonCAD.Generic import color
from PythonCAD.Generic.graphicobject import GraphicObject
from PythonCAD.Generic.segment import Segment
from PythonCAD.Generic.circle import Circle
from PythonCAD.Generic.arc import Arc
from PythonCAD.Generic.polyline import Polyline
from PythonCAD.Generic.text import TextStyle, TextBlock
from PythonCAD.Generic import util

import PythonCAD.Generic.units
import PythonCAD.Generic.move
import PythonCAD.Generic.transfer
import PythonCAD.Generic.split
import PythonCAD.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, 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, False, _xmin, _ymin, _rw, _rh)
    return True

#
# move objects
#

def move_objects(gtkimage, objlist, tool):
    _init_func = tool.getHandler("initialize")
    _dx, _dy = tool.getDistance()
    gtkimage.startAction()
    try:
        PythonCAD.Generic.move.move_objects(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.getCurrentPoint()
    #
    # need to find if the point is an intersection of drawing objects ...
    #
    tool.pushObject(_x)
    tool.pushObject(_y)
    return True

def move_end_button_press_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getCurrentPoint()
    _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)
    return True

def move_elem_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _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, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
        if _pt is not None:
            _x, _y = _pt.getCoords()
        else:
            _x, _y = _pc
        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)
    return True
    
#
# move horizontal
#

def move_horizontal_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_text()
    _entry.delete_text(0,-1)
    if len(_text):
        _dist = util.get_float(eval(_text, gtkimage.getImageVariables()))
        tool.setDistance(_dist, 0.0)
        if gtkimage.hasSelection():
            move_objects(gtkimage, gtkimage.getSelectedObjects(), tool)
        else:
            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.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    _x1, _y1 = tool.getLocation()
    tool.setDistance((_x - _x1), 0.0)
    if gtkimage.hasSelection():
        move_objects(gtkimage, gtkimage.getSelectedObjects(), tool)
    else:
        tool.clearLocation()
        gtkimage.setPrompt("Select the objects to move.")
        tool.setHandler("button_press", move_elem_button_press_cb)
        tool.delHandler("entry_event")
    return True

def move_horizontal_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    tool.setLocation(_x, _y)
    gtkimage.setPrompt("Click another point to define the distance")
    tool.setHandler("button_press", move_horizontal_second_button_press_cb)
    return True
    
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_text()
    _entry.delete_text(0,-1)
    if len(_text):
        _dist = util.get_float(eval(_text, gtkimage.getImageVariables()))
        tool.setDistance(0.0, _dist)
        if gtkimage.hasSelection():
            move_objects(gtkimage, gtkimage.getSelectedObjects(), tool)
        else:
            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.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    _x1, _y1 = tool.getLocation()
    tool.setDistance(0.0, (_y - _y1))
    if gtkimage.hasSelection():
        move_objects(gtkimage, gtkimage.getSelectedObjects(), tool)
    else:
        tool.clearLocation()
        gtkimage.setPrompt("Select the objects to move.")
        tool.setHandler("button_press", move_elem_button_press_cb)
        tool.delHandler("entry_event")
    return True

def move_vertical_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    tool.setLocation(_x, _y)
    gtkimage.setPrompt("Click another point to define the distance")
    tool.setHandler("button_press", move_vertical_second_button_press_cb)
    return True

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
#

def move_xy_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_text()
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = eval(_text, gtkimage.getImageVariables())
        tool.setDistance(util.get_float(_x), util.get_float(_y))
        if gtkimage.hasSelection():
            move_objects(gtkimage, gtkimage.getSelectedObjects(), tool)
        else:
            gtkimage.setPrompt("Click on the objects to move.")
            tool.setHandler("button_press", move_elem_button_press_cb)
            tool.delHandler("entry_event")

def move_xy_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    _x1, _y1 = tool.getLocation()
    tool.setDistance((_x - _x1), (_y - _y1))
    if gtkimage.hasSelection():
        move_objects(gtkimage, gtkimage.getSelectedObjects(), tool)
    else:
        tool.clearLocation()
        gtkimage.setPrompt("Select the objects to move.")
        tool.setHandler("button_press", move_elem_button_press_cb)
        tool.delHandler("entry_event")
    return True

def move_xy_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    tool.setLocation(_x, _y)
    gtkimage.setPrompt("Click another point to define the distance")
    tool.setHandler("button_press", move_xy_second_button_press_cb)
    return True

def move_twopoint_init(tool):
    tool.setHandler("button_press", move_xy_first_button_press_cb)
    tool.setHandler("entry_event", move_xy_entry_event)    
    tool.setHandler("initialize", move_twopoint_init)

#
# delete objects
#

def delete_region_end_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getCurrentPoint()
    _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:
            PythonCAD.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.getCurrentPoint()
    _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, 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)
    return True
        
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.getCurrentPoint()
    _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()
    gtkimage.startAction()
    try:
        for _pt in _active_layer.getLayerEntities("point"):
            if _pt.inRegion(_xmin, _ymin, _xmax, _ymax):
                _pt.move(_dx, _dy)
    finally:
        gtkimage.endAction()
    gtkimage.redraw()
    tool.clearLocation()
    tool.clearCurrentPoint()
    tool.setHandler("button_press", stretch_elem_button_press_cb)
    return True

def stretch_elem_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _pt = None
    _pts = _active_layer.find('point', _x, _y, _tol)
    if len(_pts):
        _dx, _dy = tool.getDistance()
        gtkimage.startAction()
        try:
            for _pt in _pts:
                _pt.move(_dx, _dy)
        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", 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)
    return True

#
# 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.getCurrentPoint()
        #
        # 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)
    return True

def stretch_horiz_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_text()
    _entry.delete_text(0,-1)
    if len(_text):
        _dist = util.get_float(eval(_text, gtkimage.getImageVariables()))
        tool.setDistance(_dist, 0.0)
        gtkimage.setPrompt("Select the points to move.")
        tool.setHandler("button_press", stretch_elem_button_press_cb)
        tool.delHandler("entry_event")

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.getCurrentPoint()
        #
        # 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)
    return True

def stretch_vert_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_text()
    _entry.delete_text(0,-1)
    if len(_text):
        _dist = util.get_float(eval(_text, gtkimage.getImageVariables()))
        tool.setDistance(0.0, _dist)
        gtkimage.setPrompt("Select the points to move.")
        tool.setHandler("button_press", stretch_elem_button_press_cb)
        tool.delHandler("entry_event")

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

#
# stretch x/y
#

def stretch_xy_entry_event(gtkimage, widget, tool):
    _entry = gtkimage.getEntry()
    _text = _entry.get_text()
    _entry.delete_text(0,-1)
    if len(_text):
        _x, _y = eval(_text, gtkimage.getImageVariables())
        tool.setDistance(util.get_float(_x), util.get_float(_y))
        gtkimage.setPrompt("Select the points to move.")
        tool.setHandler("button_press", stretch_elem_button_press_cb)
        tool.delHandler("entry_event")

def stretch_xy_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    _x1, _y1 = tool.getLocation()
    tool.setDistance((_x - _x1), (_y - _y1))
    tool.clearLocation()
    gtkimage.setPrompt("Select the points to move.")
    tool.setHandler("button_press", stretch_elem_button_press_cb)
    tool.delHandler("entry_event")
    return True

def stretch_xy_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    else:
        _x, _y = _pc
    tool.setLocation(_x, _y)
    gtkimage.setPrompt("Click another point to define the distance")
    tool.setHandler("button_press", stretch_xy_second_button_press_cb)
    return True

def stretch_xy_init(tool):
    tool.setHandler("button_press", stretch_xy_first_button_press_cb)
    tool.setHandler("entry_event", stretch_xy_entry_event)    
    tool.setHandler("initialize", stretch_xy_init)

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

def split_end_button_press_cb(gtkimage, widget, event, tool):
    _x2, _y2 = gtkimage.getCurrentPoint()
    _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, (Segment, Circle, Arc, Polyline)):
                _splitable.append(_obj)
        if len(_splitable):
            gtkimage.startAction()
            try:
                PythonCAD.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)
    return True
    
def split_object_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _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, Segment):
                _redraw = True
                _p1, _p2 = _obj.getEndpoints()
                gtkimage.startAction()
                try:
                    if _pt.getParent() is None:
                        _active_layer.addObject(_pt)
                    _s1, _s2 = PythonCAD.Generic.split.split_segment(_obj, _pt)
                    _active_layer.addObject(_s1)
                    _active_layer.addObject(_s2)
                    _active_layer.delObject(_obj)
                finally:
                    gtkimage.endAction()
            elif isinstance(_obj, Arc):
                _redraw = True
                _arc1, _arc2 = PythonCAD.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, Circle):
                _redraw = True
                _arc = PythonCAD.Generic.split.split_circle(_obj, _pt)
                gtkimage.startAction()
                try:
                    _active_layer.addObject(_arc)
                    _active_layer.delObject(_obj)
                finally:
                    gtkimage.endAction()
            elif isinstance(_obj, Polyline):
                _redraw = True
                gtkimage.startAction()
                try:
                    if _pt.getParent() is None:
                        _active_layer.addObject(_pt)
                    PythonCAD.Generic.split.split_polyline(_obj, _pt)
                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)
    return True
        
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.getCurrentPoint()
    _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]
                    PythonCAD.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)
    return True
    
def transfer_object_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _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)
                    PythonCAD.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)
    return True
        
def transfer_object_init(tool):
    tool.initialize()
    tool.setHandler("button_press", transfer_object_button_press_cb)
    
#
# common attribute changing code
#

def _change_attribute(gtkimage, objlist, tool):
    _init = tool.getHandler('initialize')
    _attr = tool.getAttribute()
    _value = tool.getValue()
    if len(objlist):
        gtkimage.startAction()
        try:
            for _obj in objlist:
                getattr(_obj, _attr)(_value)
        finally:
            gtkimage.endAction()
        gtkimage.redraw()
    tool.reset()
    _init(gtkimage, tool)

def change_attr_second_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _active_layer = gtkimage.getActiveLayer()
    _pts = _active_layer.find('point', _x, _y)
    if len(_pts) > 0:
        _x, _y = _pts[0].getCoords()
    _x1, _y1 = tool.getLocation()
    _xmin = min(_x1, _x)
    _ymin = min(_y1, _y)
    _xmax = max(_x1, _x)
    _ymax = max(_y1, _y)
    _objs = []
    _filter = tool.getFilter()
    _objtype = tool.getObjtype()
    for _obj in _active_layer.objsInRegion(_xmin, _ymin, _xmax, _ymax):
        if _filter is not None:
            _fobj = _filter(tool, _obj)
            if _fobj is not None:
                _objs.append(_fobj)
        elif _objtype is not None:
            if isinstance(_obj, _objtype):
                _objs.append(_obj)
        else:
            _objs.append(_obj)
    _change_attribute(gtkimage, _objs, tool)
    return True

def change_attr_first_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    _tol = gtkimage.getTolerance()
    _objdict = gtkimage.mapPoint(_x, _y, _tol, None)
    if len(_objdict):
        _active_layer = gtkimage.getActiveLayer()
        if _active_layer in _objdict:
            _objs = []
            _objtype = tool.getObjtype()
            for _obj, _pt in _objdict[_active_layer]:
                if _objtype is None:
                    _objs.append(_obj)
                else:
                    if isinstance(_obj, _objtype):
                        _objs.append(_obj)
            _change_attribute(gtkimage, _objs, tool)
    else:
        _pt, _pc = gtkimage.getClosestPoint(_x, _y, tolerance=_tol)
        if _pt is not None:
            _x, _y = _pt.getCoords()
        else:
            _x, _y = _pc
        tool.setLocation(_x, _y)
        tool.setHandler("motion_notify", select_motion_notify)
        tool.setHandler("button_press", change_attr_second_button_press_cb)
        gtkimage.setPrompt("Click the second point for defining the region")
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
    return True

#
# change color
#

def change_color_init(gtkimage, tool):
    tool.setHandler("initialize", change_color_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, GraphicObject):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the items you want the color to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)
    
def change_color_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.ColorSelectionDialog("Set Active Color")
    _dialog.set_transient_for(_window)
    _colorsel = _dialog.colorsel

    _prev_color = gtkimage.getOption('LINE_COLOR')
    _gtk_color = gtkimage.getColor(_prev_color)
    _colorsel.set_previous_color(_gtk_color)
    _colorsel.set_current_color(_gtk_color)
    _colorsel.set_has_palette(True)
    _color = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _gtk_color = _colorsel.get_current_color()
        _r = int(round((_gtk_color.red/65535.0) * 255.0))
        _g = int(round((_gtk_color.green/65535.0) * 255.0))
        _b = int(round((_gtk_color.blue/65535.0) * 255.0))
        for _c in gtkimage.getImageEntities('color'):
            if _c.r == _r and _c.g == _g and _c.b == _b:
                _color = _c
                break
        if _color is None:
            _color = color.Color(_r, _g, _b)
    _dialog.destroy()
    return _color

#
# change linetypes
#

def change_linetype_init(gtkimage, tool):
    tool.setHandler("initialize", change_linetype_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, GraphicObject):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the items you want the linetype to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_linetype_dialog(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(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    
    _label = gtk.Label("Linetype:")
    _hbox.pack_start(_label, False, False, 0)
    _clt = gtkimage.getOption('LINE_TYPE')
    _linetypes = gtkimage.getImageEntities('linetype')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _widget = gtk.combo_box_new_text()
        for _i in range(len(_linetypes)):
            _lt = _linetypes[_i]
            if _lt is _clt:
                _idx = _i
            _widget.append_text(_lt.getName())
        _widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        for _i in range(len(_linetypes)):
            _lt = _linetypes[_i]
            if _lt is _clt:
                _idx = _i
            _item = gtk.MenuItem(_lt.getName())
            _menu.append(_item)
        _widget = gtk.OptionMenu()
        _widget.set_menu(_menu)
        _widget.set_history(_idx)
    _hbox.pack_start(_widget, True, True, 0)
    _dialog.show_all()
    _lt = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if isinstance(_widget, gtk.ComboBox):
            _idx = _widget.get_active()
        elif isinstance(_widget, gtk.OptionMenu):
            _idx = _widget.get_history()
        else:
            raise TypeError, "Unexpected widget: " + `type(_widget)`
        _lt = _linetypes[_idx]
    _dialog.destroy()
    return _lt

#
# change thickness
#

def change_thickness_init(gtkimage, tool):
    tool.setHandler("initialize", change_thickness_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, GraphicObject):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the items you want the thickness to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_thickness_dialog(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(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Thickness:")
    _hbox.pack_start(_label, False, 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(False)
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _t = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _t = float(_sb.get_value())
    _dialog.destroy()
    return _t

#
# change the style
#

def change_style_init(gtkimage, tool):
    tool.setHandler("initialize", change_style_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, GraphicObject):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the items you want the style to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_style_dialog(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(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Style:")
    _cst = gtkimage.getOption('LINE_STYLE')
    _styles = gtkimage.getImageEntities('style')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _widget = gtk.combo_box_new_text()
        for _i in range(len(_styles)):
            _s = _styles[_i]
            if _s is _cst:
                _idx = _i
            _widget.append_text(_s.getName())
        _widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        for _i in range(len(_styles)):
            _s = _styles[_i]
            if _s is _cst:
                _idx = _i
            _item = gtk.MenuItem(_s.getName())
            _menu.append(_item)
        _widget = gtk.OptionMenu()
        _widget.set_menu(_menu)
        _widget.set_history(_idx)
    _hbox.pack_start(_label, False, False, 0)
    _hbox.pack_start(_widget, True, True, 0)
    _dialog.show_all()
    _s = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if isinstance(_widget, gtk.ComboBox):
            _idx = _widget.get_active()
        elif isinstance(_widget, gtk.OptionMenu):
            _idx = _widget.get_history()
        else:
            raise TypeError, "Unexpected widget: " + `type(_widget)`
        _s = _styles[_idx]
    _dialog.destroy()
    return _s

#
# Change TextBlock properties
#

def change_textblock_size_init(gtkimage, tool):
    tool.setHandler("initialize", change_textblock_size_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, TextBlock):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the items you want the size to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_textblock_size_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Text Size", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Text Size:")
    _hbox.pack_start(_label, False, False, 0)
    _size = gtkimage.getOption(key)
    _adj = gtk.Adjustment(_size, 0.0001, 400.0, 0.1, 1.0, 1.0)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(1)
    _sb.set_numeric(False)
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _size = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _size = float(_sb.get_value())
    _dialog.destroy()
    return _size

def change_textblock_family_init(gtkimage, tool):
    tool.setHandler("initialize", change_textblock_family_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, TextBlock):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the objects you want the family to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_textblock_family_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Font Family", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _families = []
    for _family in _window.get_pango_context().list_families():
        _families.append(_family.get_name())
    _families.sort()
    _label = gtk.Label("Family:")
    _family = gtkimage.getOption(key)
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _widget = gtk.combo_box_new_text()
        for _i in range(len(_families)):
            _f = _families[_i]
            if _f == _family:
                _idx = _i
            _widget.append_text(_f)
        _widget.set_active(_idx)
    else:
        _menu = gtk.Menu()    
        for _i in range(len(_families)):
            _f = _families[_i]
            if _f == _family:
                _idx = _i
            _item = gtk.MenuItem(_f)
            _menu.append(_item)
        _widget = gtk.OptionMenu()
        _widget.set_menu(_menu)
        _widget.set_history(_idx)
    _hbox.pack_start(_label, False, False, 0)
    _hbox.pack_start(_widget, True, True, 0)
    _dialog.show_all()
    _family = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if isinstance(_widget, gtk.ComboBox):
            _idx = _widget.get_active()
        elif isinstance(_widget, gtk.OptionMenu):
            _idx = _widget.get_history()
        else:
            raise TypeError, "Unexpected widget: " + `type(_widget)`
        _family = _families[_idx]
    _dialog.destroy()
    return _family

def change_textblock_weight_init(gtkimage, tool):
    tool.setHandler("initialize", change_textblock_weight_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, TextBlock):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the objects you want the weight to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_textblock_weight_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Text Weight", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Text Weight:")
    _weight = gtkimage.getOption(key)
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _widget = gtk.combo_box_new_text()
        _widget.append_text('NORMAL')
        if _weight == TextStyle.WEIGHT_NORMAL:
            _idx = 0
        _widget.append_text('LIGHT')
        if _weight == TextStyle.WEIGHT_LIGHT:
            _idx = 1
        _widget.append_text('BOLD')
        if _weight == TextStyle.WEIGHT_BOLD:
            _idx = 2
        _widget.append_text('HEAVY')
        if _weight == TextStyle.WEIGHT_HEAVY:
            _idx = 3
        _widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('NORMAL')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_NORMAL:
            _idx = 0
        _item = gtk.MenuItem('LIGHT')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_LIGHT:
            _idx = 1
        _item = gtk.MenuItem('BOLD')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_BOLD:
            _idx = 2
        _item = gtk.MenuItem('HEAVY')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_HEAVY:
            _idx = 3
        _widget = gtk.OptionMenu()
        _widget.set_menu(_menu)
        _widget.set_history(_idx)
    _hbox.pack_start(_label, False, False, 0)
    _hbox.pack_start(_widget, True, True, 0)
    _dialog.show_all()
    _weight = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if isinstance(_widget, gtk.ComboBox):
            _weight = _widget.get_active()
        elif isinstance(_widget, gtk.OptionMenu):
            _weight = _widget.get_history()
        else:
            raise TypeError, "Unexpected widget: " + `type(_widget)`
    _dialog.destroy()
    return _weight

def change_textblock_style_init(gtkimage, tool):
    tool.setHandler("initialize", change_textblock_style_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, TextBlock):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the objects you want the style to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_textblock_style_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Text 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(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Text Style:")
    _style = gtkimage.getOption(key)
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _widget = gtk.combo_box_new_text()
        _widget.append_text('NORMAL')
        if _style == TextStyle.FONT_NORMAL:
            _idx = 0
        _widget.append_text('OBLIQUE')
        if _style == TextStyle.FONT_OBLIQUE:
            _idx = 1
        _widget.append_text('ITALIC')
        if _style == TextStyle.FONT_ITALIC:
            _idx = 2
        _widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('NORMAL')
        _menu.append(_item)
        if _style == TextStyle.FONT_NORMAL:
            _idx = 0
        _item = gtk.MenuItem('OBLIQUE')
        _menu.append(_item)
        if _style == TextStyle.FONT_OBLIQUE:
            _idx = 1
        _item = gtk.MenuItem('ITALIC')
        _menu.append(_item)
        if _style == TextStyle.FONT_ITALIC:
            _idx = 2
        _widget = gtk.OptionMenu()
        _widget.set_menu(_menu)
        _widget.set_history(_idx)
    _hbox.pack_start(_label, False, False, 0)
    _hbox.pack_start(_widget, True, True, 0)
    _dialog.show_all()
    _style = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if isinstance(_widget, gtk.ComboBox):
            _style = _widget.get_active()
        elif isinstance(_widget, gtk.OptionMenu):
            _style = _widget.get_history()
        else:
            raise TypeError, "Unexpected widget: " + `type(_widget)`
    _dialog.destroy()
    return _style

def change_textblock_alignment_init(gtkimage, tool):
    tool.setHandler("initialize", change_textblock_alignment_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, TextBlock):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the objects you want the alignment to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_textblock_alignment_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Text Alignment", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Text Alignment:")
    _align = gtkimage.getOption(key)
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _widget = gtk.combo_box_new_text()
        _widget.append_text('LEFT')
        if _align == TextStyle.ALIGN_LEFT:
            _idx = 0
        _widget.append_text('CENTER')
        if _align == TextStyle.ALIGN_CENTER:
            _idx = 1
        _widget.append_text('RIGHT')
        if _align == TextStyle.ALIGN_RIGHT:
            _idx = 2
        _widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('LEFT')
        _menu.append(_item)
        if _align == TextStyle.ALIGN_LEFT:
            _idx = 0
        _item = gtk.MenuItem('CENTER')
        _menu.append(_item)
        if _align == TextStyle.ALIGN_CENTER:
            _idx = 1
        _item = gtk.MenuItem('RIGHT')
        _menu.append(_item)
        if _align == TextStyle.ALIGN_RIGHT:
            _idx = 2
        _widget = gtk.OptionMenu()
        _widget.set_menu(_menu)
        _widget.set_history(_idx)
    _hbox.pack_start(_label, False, False, 0)
    _hbox.pack_start(_widget, True, True, 0)
    _dialog.show_all()
    _align = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if isinstance(_widget, gtk.ComboBox):
            _align = _widget.get_active()
        elif isinstance(_widget, gtk.OptionMenu):
            _align = _widget.get_history()
        else:
            raise TypeError, "Unexpected widget: " + `type(_widget)`
    _dialog.destroy()
    return _align

def change_textblock_color_init(gtkimage, tool):
    tool.setHandler("initialize", change_textblock_color_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, TextBlock):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the objects you want the color to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_textblock_color_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.ColorSelectionDialog("Change Font Color")
    _dialog.set_transient_for(_window)
    _colorsel = _dialog.colorsel
    _color = gtkimage.getOption(key)
    _gtk_color = gtkimage.getColor(_color)
    _colorsel.set_previous_color(_gtk_color)
    _colorsel.set_current_color(_gtk_color)
    _colorsel.set_has_palette(True)
    _color = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _gtk_color = _colorsel.get_current_color()
        _r = int(round((_gtk_color.red/65535.0) * 255.0))
        _g = int(round((_gtk_color.green/65535.0) * 255.0))
        _b = int(round((_gtk_color.blue/65535.0) * 255.0))
        for _c in gtkimage.getImageEntities('color'):
            if _c.r == _r and _c.g == _g and _c.b == _b:
                _color = _c
                break
        if _color is None:
            _color = color.Color(_r, _g, _b)
    _dialog.destroy()
    return _color

#
# Change Dimension Properties
#

def change_dim_offset_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_offset_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want the offset to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_offset_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Offset Length", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Length:")
    _hbox.pack_start(_label, False, False, 0)
    _offset = gtkimage.getOption('DIM_OFFSET')
    _adj = gtk.Adjustment(_offset, 0.01, 200.0, 0.1, 1.0, 1.0)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(2)
    _sb.set_numeric(False)
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _offset = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _offset = float(_sb.get_value())
    _dialog.destroy()
    return _offset

def change_dim_extension_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_extension_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want the extension to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_extension_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Extension Length", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Length:")
    _hbox.pack_start(_label, False, False, 0)
    _extlen = gtkimage.getOption('DIM_EXTENSION')
    _adj = gtk.Adjustment(_extlen, 0.01, 200.0, 0.1, 1.0, 1.0)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(2)
    _sb.set_numeric(False)
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _extlen = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _extlen = float(_sb.get_value())
    _dialog.destroy()
    return _extlen

def change_dim_endpoint_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_endpoint_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want the endpoint type to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_endpoint_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Endpoint Markers", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Endpoints:")
    _endpt = gtkimage.getOption('DIM_ENDPOINT')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _widget = gtk.combo_box_new_text()
        _widget.append_text('None')
        if _endpt == Dimension.DIM_ENDPT_NONE:
            _idx = 0
        _widget.append_text('Arrow')
        if _endpt == Dimension.DIM_ENDPT_ARROW:
            _idx = 1
        _widget.append_text('Filled Arrow')
        if _endpt == Dimension.DIM_ENDPT_FILLED_ARROW:
            _idx = 2
        _widget.append_text('Slash')
        if _endpt == Dimension.DIM_ENDPT_SLASH:
            _idx = 3
        _widget.append_text('Circle')
        if _endpt == Dimension.DIM_ENDPT_CIRCLE:
            _idx = 4
        _widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('None')
        _menu.append(_item)
        if _endpt == Dimension.DIM_ENDPT_NONE:
            _idx = 0
        _item = gtk.MenuItem('Arrow')
        _menu.append(_item)
        if _endpt == Dimension.DIM_ENDPT_ARROW:
            _idx = 1
        _item = gtk.MenuItem('Filled Arrow')
        _menu.append(_item)
        if _endpt == Dimension.DIM_ENDPT_FILLED_ARROW:
            _idx = 2
        _item = gtk.MenuItem('Slash')
        _menu.append(_item)
        if _endpt == Dimension.DIM_ENDPT_SLASH:
            _idx = 3
        _item = gtk.MenuItem('Circle')
        _menu.append(_item)
        if _endpt == Dimension.DIM_ENDPT_CIRCLE:
            _idx = 4
        _widget = gtk.OptionMenu()
        _widget.set_menu(_menu)
        _widget.set_history(_idx)
    _hbox.pack_start(_label, False, False, 0)
    _hbox.pack_start(_widget, True, True, 0)
    _dialog.show_all()
    _endpt = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if isinstance(_widget, gtk.ComboBox):
            _endpt = _widget.get_active()
        elif isinstance(_widget, gtk.OptionMenu):
            _endpt = _widget.get_history()
        else:
            raise TypeError, "Unexpected widget: " + `type(_widget)`
    _dialog.destroy()
    return _endpt

def change_dim_endpoint_size_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_endpoint_size_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want the endpoint size to change.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_endpoint_size_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Endpoint Size", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Length:")
    _hbox.pack_start(_label, False, False, 0)
    _size = gtkimage.getOption('DIM_ENDPOINT_SIZE')
    _adj = gtk.Adjustment(_size, 0.01, 200.0, 0.1, 1.0, 1.0)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(2)
    _sb.set_numeric(False)
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _size = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _size = float(_sb.get_value())
    _dialog.destroy()
    return _size

def change_dim_dual_mode_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_dual_mode_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want to change the dual dimension display.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_dual_mode_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Dual Mode", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _cb = gtk.CheckButton("Display Two Dimension Values")
    _mode = gtkimage.getOption('DIM_DUAL_MODE')
    _cb.set_active(_mode)
    _hbox.pack_start(_cb, True, True, 0)
    _dialog.show_all()
    _mode = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _mode = _cb.get_active()
    _dialog.destroy()
    return _mode

def change_dim_dual_mode_offset_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_dual_mode_offset_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want to change the dual dimension offset value.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_dual_mode_offset_init(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Dual Mode Offset", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Length:")
    _hbox.pack_start(_label, False, False, 0)
    _offset = gtkimage.getOption('DIM_DUAL_MODE_OFFSET')
    _adj = gtk.Adjustment(_offset, 0.01, 200.0, 0.1, 1.0, 1.0)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(2)
    _sb.set_numeric(False)
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _offset = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _offset = float(_sb.get_value())
    _dialog.destroy()
    return _offset

def change_dim_thickness_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_thickness_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want to change the thickess.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_thickness_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Dim 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(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Thickness:")
    _hbox.pack_start(_label, False, False, 0)
    _t = gtkimage.getOption('DIM_THICKNESS')
    _adj = gtk.Adjustment(_t, 0.01, 200.0, 0.1, 1.0, 1.0)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(2)
    _sb.set_numeric(False)
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _t = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _t = float(_sb.get_value())
    _dialog.destroy()
    return _t

def change_dim_color_init(gtkimage, tool):
    tool.setHandler("initialize", change_dim_color_init)
    if gtkimage.hasSelection():
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                _objs.append(_obj)
        _change_attribute(gtkimage, _objs, tool)
    else:
        gtkimage.setPrompt("Click the dimension you want to change the color.")
        tool.setHandler("button_press", change_attr_first_button_press_cb)

def change_dim_color_dialog(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.ColorSelectionDialog("Change Dimension Color")
    _dialog.set_transient_for(_window)
    _colorsel = _dialog.colorsel
    _color = gtkimage.getOption('DIM_COLOR')
    _gtk_color = gtkimage.getColor(_color)
    _colorsel.set_previous_color(_gtk_color)
    _colorsel.set_current_color(_gtk_color)
    _colorsel.set_has_palette(True)
    _color = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _gtk_color = _colorsel.get_current_color()
        _r = int(round((_gtk_color.red/65535.0) * 255.0))
        _g = int(round((_gtk_color.green/65535.0) * 255.0))
        _b = int(round((_gtk_color.blue/65535.0) * 255.0))
        for _c in gtkimage.getImageEntities('color'):
            if _c.r == _r and _c.g == _g and _c.b == _b:
                _color = _c
                break
        if _color is None:
            _color = color.Color(_r, _g, _b)
    _dialog.destroy()
    return _color

#
# Change DimString properties
#

def _dimstring_filter_proc(tool, obj):
    _ds = None
    if isinstance(obj, Dimension):
        if tool.getPrimary():
            _ds = obj.getPrimaryDimstring()
        else:
            _ds = obj.getSecondaryDimstring()
    return _ds

def change_primary_dimstring_init(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Primary Dimstring", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))

    _label_size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
    _entry_size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
    _menu_size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
    
    #
    # Font Family
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Family:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _families = []
    for _family in _window.get_pango_context().list_families():
        _families.append(_family.get_name())
    _families.sort()
    _idx = 0
    _family = gtkimage.getOption('DIM_PRIMARY_FONT_FAMILY')
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _family_widget = gtk.combo_box_new_text()
        for _i in range(len(_families)):
            _f = _families[_i]
            if _family == _f:
                _idx = _i
            _family_widget.append_text(_f)
        _family_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        for _i in range(len(_families)):
            _f = _families[_i]
            if _family == _f:
                _idx = _i
            _item = gtk.MenuItem(_f)
            _menu.append(_item)
        _family_widget = gtk.OptionMenu()
        _family_widget.set_menu(_menu)
        _family_widget.set_history(_idx)
    _menu_size_group.add_widget(_family_widget)
    _hbox.pack_start(_family_widget, False, False, 0)
    #
    # Font Style
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Style:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _style = gtkimage.getOption('DIM_PRIMARY_FONT_STYLE')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _style_widget = gtk.combo_box_new_text()
        _style_widget.append_text('NORMAL')
        if _style == TextStyle.FONT_NORMAL:
            _idx = 0
        _style_widget.append_text('OBLIQUE')
        if _style == TextStyle.FONT_OBLIQUE:
            _idx = 1
        _style_widget.append_text('ITALIC')
        if _style == TextStyle.FONT_ITALIC:
            _idx = 2
        _style_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('NORMAL')
        _menu.append(_item)
        if _style == TextStyle.FONT_NORMAL:
            _idx = 0
        _item = gtk.MenuItem('OBLIQUE')
        _menu.append(_item)
        if _style == TextStyle.FONT_OBLIQUE:
            _idx = 1
        _item = gtk.MenuItem('ITALIC')
        _menu.append(_item)
        if _style == TextStyle.FONT_ITALIC:
            _idx = 2
        _style_widget = gtk.OptionMenu()
        _style_widget.set_menu(_menu)
        _style_widget.set_history(_idx)
    _menu_size_group.add_widget(_style_widget)
    _hbox.pack_start(_style_widget, False, False, 0)
    #
    # Font weight
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Weight:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _weight = gtkimage.getOption('DIM_PRIMARY_FONT_WEIGHT')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _weight_widget = gtk.combo_box_new_text()
        _weight_widget.append_text('NORMAL')
        if _weight == TextStyle.WEIGHT_NORMAL:
            _idx = 0
        _weight_widget.append_text('LIGHT')
        if _weight == TextStyle.WEIGHT_LIGHT:
            _idx = 1
        _weight_widget.append_text('BOLD')
        if _weight == TextStyle.WEIGHT_BOLD:
            _idx = 2
        _weight_widget.append_text('HEAVY')
        if _weight == TextStyle.WEIGHT_HEAVY:
            _idx = 3
        _weight_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('NORMAL')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_NORMAL:
            _idx = 0
        _item = gtk.MenuItem('LIGHT')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_LIGHT:
            _idx = 1
        _item = gtk.MenuItem('BOLD')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_BOLD:
            _idx = 2
        _item = gtk.MenuItem('HEAVY')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_HEAVY:
            _idx = 3
        _weight_widget = gtk.OptionMenu()
        _weight_widget.set_menu(_menu)
        _weight_widget.set_history(_idx)
    _menu_size_group.add_widget(_weight_widget)
    _hbox.pack_start(_weight_widget, False, False, 0)
    #
    # Text Size
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Size:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _size = gtkimage.getOption('DIM_PRIMARY_TEXT_SIZE')
    _adj = gtk.Adjustment(_size, 0.0001, 400.0, 0.1, 1.0, 1.0)
    _size_sb = gtk.SpinButton(_adj)
    _size_sb.set_digits(1)
    _size_sb.set_numeric(False)
    _hbox.pack_start(_size_sb, False, False, 0)
    #
    # fixme: add color changer ...
    #
    # Prefix/Suffix
    #
    _table = gtk.Table(4, 3, False)
    _table.set_border_width(2)
    _table.set_row_spacings(2)
    _table.set_col_spacings(2)
    _dialog.vbox.pack_start(_table, False, False, 0)
    #
    _label = gtk.Label("Prefix")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 1, 2, 0, 1,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _label = gtk.Label("Suffix")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 2, 3, 0, 1,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    # Linear/Horizontal/Vertical
    _label = gtk.Label("Linear")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 0, 1, 1, 2,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _prefix = gtkimage.getOption('DIM_PRIMARY_PREFIX')
    _lpentry = gtk.Entry()
    _lpentry.set_text(_prefix)
    _entry_size_group.add_widget(_lpentry)
    _table.attach(_lpentry, 1, 2, 1, 2,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _suffix = gtkimage.getOption('DIM_PRIMARY_SUFFIX')
    _lsentry = gtk.Entry()
    _lsentry.set_text(_suffix)
    _entry_size_group.add_widget(_lsentry)
    _table.attach(_lsentry, 2, 3, 1, 2,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    # Radial
    _label = gtk.Label("Radial")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 0, 1, 2, 3,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _prefix = gtkimage.getOption('RADIAL_DIM_PRIMARY_PREFIX')
    _rpentry = gtk.Entry()
    _rpentry.set_text(_prefix)
    _entry_size_group.add_widget(_rpentry)
    _table.attach(_rpentry, 1, 2, 2, 3,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _suffix = gtkimage.getOption('RADIAL_DIM_PRIMARY_SUFFIX')
    _rsentry = gtk.Entry()
    _rsentry.set_text(_suffix)
    _entry_size_group.add_widget(_rsentry)
    _table.attach(_rsentry, 2, 3, 2, 3,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    # Angular
    _label = gtk.Label("Angular")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 0, 1, 3, 4,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _prefix = gtkimage.getOption('ANGULAR_DIM_PRIMARY_PREFIX')
    _apentry = gtk.Entry()
    _apentry.set_text(_prefix)
    _entry_size_group.add_widget(_apentry)
    _table.attach(_apentry, 1, 2, 3, 4,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _suffix = gtkimage.getOption('ANGULAR_DIM_PRIMARY_SUFFIX')
    _asentry = gtk.Entry()
    _asentry.set_text(_suffix)
    _entry_size_group.add_widget(_asentry)
    _table.attach(_asentry, 2, 3, 3, 4,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    #
    # Precision
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Precision:")
    _label_size_group.add_widget(_label)    
    _hbox.pack_start(_label, False, False, 0)
    _prec = gtkimage.getOption('DIM_PRIMARY_PRECISION')
    _adj = gtk.Adjustment(_prec, 0, 15, 1, 1, 1)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(0)
    _sb.set_numeric(True)    
    _hbox.pack_start(_sb, False, False, 0)
    #
    # Units
    #
    _units = [('Millimeters', PythonCAD.Generic.units.MILLIMETERS),
              ('Micrometers', PythonCAD.Generic.units.MICROMETERS),
              ('Meters', PythonCAD.Generic.units.METERS),
              ('Kilometers', PythonCAD.Generic.units.KILOMETERS),
              ('Inches', PythonCAD.Generic.units.INCHES),
              ('Feet', PythonCAD.Generic.units.FEET),
              ('Yards', PythonCAD.Generic.units.YARDS),
              ('Miles', PythonCAD.Generic.units.MILES),
              ]
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Units:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _unit = gtkimage.getOption('DIM_PRIMARY_UNITS')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _unit_widget = gtk.combo_box_new_text()
        for _i in range(len(_units)):
            _str, _val = _units[_i]
            if _unit == _val:
                _idx = _i
            _unit_widget.append_text(_str)
        _unit_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        for _i in range(len(_units)):
            _str, _val = _units[_i]
            if _unit == _val:
                _idx = _i
            _item = gtk.MenuItem(_str)
            _menu.append(_item)
        _unit_widget = gtk.OptionMenu()
        _unit_widget.set_menu(_menu)
        _unit_widget.set_history(_idx)
    _menu_size_group.add_widget(_unit_widget)
    _hbox.pack_start(_unit_widget, False, False, 0)
    #
    # Print Zero
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _pzcb = gtk.CheckButton("Print Leading Zero")
    _pzro = gtkimage.getOption('DIM_PRIMARY_LEADING_ZERO')
    _pzcb.set_active(_pzro)
    _hbox.pack_start(_pzcb, True, True, 0)
    #
    # Print Decimal
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _pdcb = gtk.CheckButton("Print Trailing Decimal")
    _pdec = gtkimage.getOption('DIM_PRIMARY_TRAILING_DECIMAL')
    _pdcb.set_active(_pdec)
    _hbox.pack_start(_pdcb, True, True, 0)
    #
    _dialog.show_all()
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
            _idx = _family_widget.get_active()
            _family = _families[_idx]
            _style = _style_widget.get_active()
            _weight = _weight_widget.get_active()
            _idx = _unit_widget.get_active()
            _unit = _units[_idx][1]
        else:
            _idx = _family_widget.get_history()
            _family = _families[_idx]
            _style = _style_widget.get_history()
            _weight = _weight_widget.get_history()
            _idx = _unit_widget.get_history()
            _unit = _units[_idx][1]
        _size = float(_size_sb.get_value())
        _lprefix = unicode(_lpentry.get_text())
        _lsuffix = unicode(_lsentry.get_text())
        _rprefix = unicode(_rpentry.get_text())
        _rsuffix = unicode(_rsentry.get_text())
        _aprefix = unicode(_apentry.get_text())
        _asuffix = unicode(_asentry.get_text())
        _prec = int(_sb.get_value())
        _pzro = _pzcb.get_active()
        _pdec = _pdcb.get_active()
        if gtkimage.hasSelection():
            _active_layer = gtkimage.getActiveLayer()
            _objs = []
            for _obj in gtkimage.getSelectedObjects():
                if ((_obj.getParent() is _active_layer) and
                    (isinstance(_obj, Dimension))):
                    _objs.append(_obj)
            if len(_objs):
                gtkimage.startAction()
                try:
                    for _obj in _objs:
                        _ds = _obj.getPrimaryDimstring()
                        _ds.setFamily(_family)
                        _ds.setStyle(_style)
                        _ds.setWeight(_weight)
                        _ds.setSize(_size)
                        if isinstance(_obj, AngularDimension):
                            _ds.setPrefix(_aprefix)
                            _ds.setSuffix(_asuffix)
                        elif isinstance(_obj, RadialDimension):
                            _ds.setPrefix(_rprefix)
                            _ds.setSuffix(_rsuffix)
                        else:
                            _ds.setPrefix(_lprefix)
                            _ds.setSuffix(_lsuffix)
                        _ds.setPrecision(_prec)
                        _ds.setUnits(_unit)
                        _ds.setPrintZero(_pzro)
                        _ds.setPrintDecimal(_pdec)
                finally:
                    gtkimage.endAction()
    _dialog.destroy()
    gtkimage.redraw()

def change_secondary_dimstring_init(gtkimage):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Secondary Dimstring", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))

    _label_size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
    _entry_size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
    _menu_size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
    
    #
    # Font Family
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Family:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _families = []
    for _family in _window.get_pango_context().list_families():
        _families.append(_family.get_name())
    _families.sort()
    _family = gtkimage.getOption('DIM_SECONDARY_FONT_FAMILY')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _family_widget = gtk.combo_box_new_text()
        for _i in range(len(_families)):
            _f = _families[_i]
            if _family == _f:
                _idx = _i
            _family_widget.append_text(_f)
        _family_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        for _i in range(len(_families)):
            _f = _families[_i]
            if _family == _f:
                _idx = _i
            _item = gtk.MenuItem(_f)
            _menu.append(_item)
        _family_widget = gtk.OptionMenu()
        _family_widget.set_menu(_menu)
        _family_widget.set_history(_idx)
    _menu_size_group.add_widget(_family_widget)
    _hbox.pack_start(_family_widget, False, False, 0)
    #
    # Font Style
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Style:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _style = gtkimage.getOption('DIM_SECONDARY_FONT_STYLE')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _style_widget = gtk.combo_box_new_text()
        _style_widget.append_text('NORMAL')
        if _style == TextStyle.FONT_NORMAL:
            _idx = 0
        _style_widget.append_text('OBLIQUE')
        if _style == TextStyle.FONT_OBLIQUE:
            _idx = 1
        _style_widget.append_text('ITALIC')
        if _style == TextStyle.FONT_ITALIC:
            _idx = 2
        _style_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('NORMAL')
        _menu.append(_item)
        if _style == TextStyle.FONT_NORMAL:
            _idx = 0
        _item = gtk.MenuItem('OBLIQUE')
        _menu.append(_item)
        if _style == TextStyle.FONT_OBLIQUE:
            _idx = 1
        _item = gtk.MenuItem('ITALIC')
        _menu.append(_item)
        if _style == TextStyle.FONT_ITALIC:
            _idx = 2
        _style_widget = gtk.OptionMenu()
        _style_widget.set_menu(_menu)
        _style_widget.set_history(_idx)
    _menu_size_group.add_widget(_style_widget)
    _hbox.pack_start(_style_widget, False, False, 0)
    #
    # Font weight
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Weight:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _weight = gtkimage.getOption('DIM_SECONDARY_FONT_WEIGHT')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _weight_widget = gtk.combo_box_new_text()
        _weight_widget.append_text('NORMAL')
        if _weight == TextStyle.WEIGHT_NORMAL:
            _idx = 0
        _weight_widget.append_text('LIGHT')
        if _weight == TextStyle.WEIGHT_LIGHT:
            _idx = 1
        _weight_widget.append_text('BOLD')
        if _weight == TextStyle.WEIGHT_BOLD:
            _idx = 2
        _weight_widget.append_text('HEAVY')
        if _weight == TextStyle.WEIGHT_HEAVY:
            _idx = 3
        _weight_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        _item = gtk.MenuItem('NORMAL')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_NORMAL:
            _idx = 0
        _item = gtk.MenuItem('LIGHT')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_LIGHT:
            _idx = 1
        _item = gtk.MenuItem('BOLD')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_BOLD:
            _idx = 2
        _item = gtk.MenuItem('HEAVY')
        _menu.append(_item)
        if _weight == TextStyle.WEIGHT_HEAVY:
            _idx = 3
        _weight_widget = gtk.OptionMenu()
        _weight_widget.set_menu(_menu)
        _weight_widget.set_history(_idx)
    _menu_size_group.add_widget(_weight_widget)
    _hbox.pack_start(_weight_widget, False, False, 0)
    #
    # Text Size
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Size:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _size = gtkimage.getOption('DIM_SECONDARY_TEXT_SIZE')
    _adj = gtk.Adjustment(_size, 0.0001, 400.0, 0.1, 1.0, 1.0)
    _size_sb = gtk.SpinButton(_adj)
    _size_sb.set_digits(1)
    _size_sb.set_numeric(False)
    _hbox.pack_start(_size_sb, False, False, 0)
    #
    # fixme: add color changer ...
    #
    # Prefix/Suffix
    #
    _table = gtk.Table(4, 3, False)
    _table.set_border_width(2)
    _table.set_row_spacings(2)
    _table.set_col_spacings(2)
    _dialog.vbox.pack_start(_table, False, False, 0)
    #
    _label = gtk.Label("Prefix")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 1, 2, 0, 1,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _label = gtk.Label("Suffix")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 2, 3, 0, 1,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    # Linear/Horizontal/Vertical
    _label = gtk.Label("Linear")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 0, 1, 1, 2,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _prefix = gtkimage.getOption('DIM_SECONDARY_PREFIX')
    _lpentry = gtk.Entry()
    _lpentry.set_text(_prefix)
    _entry_size_group.add_widget(_lpentry)
    _table.attach(_lpentry, 1, 2, 1, 2,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _suffix = gtkimage.getOption('DIM_SECONDARY_SUFFIX')
    _lsentry = gtk.Entry()
    _lsentry.set_text(_suffix)
    _entry_size_group.add_widget(_lsentry)
    _table.attach(_lsentry, 2, 3, 1, 2,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    # Radial
    _label = gtk.Label("Radial")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 0, 1, 2, 3,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _prefix = gtkimage.getOption('RADIAL_DIM_SECONDARY_PREFIX')
    _rpentry = gtk.Entry()
    _rpentry.set_text(_prefix)
    _entry_size_group.add_widget(_rpentry)
    _table.attach(_rpentry, 1, 2, 2, 3,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _suffix = gtkimage.getOption('RADIAL_DIM_SECONDARY_SUFFIX')
    _rsentry = gtk.Entry()
    _rsentry.set_text(_suffix)
    _entry_size_group.add_widget(_rsentry)
    _table.attach(_rsentry, 2, 3, 2, 3,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    # Angular
    _label = gtk.Label("Angular")
    _label_size_group.add_widget(_label)
    _table.attach(_label, 0, 1, 3, 4,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _prefix = gtkimage.getOption('ANGULAR_DIM_SECONDARY_PREFIX')
    _apentry = gtk.Entry()
    _apentry.set_text(_prefix)
    _entry_size_group.add_widget(_apentry)
    _table.attach(_apentry, 1, 2, 3, 4,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    _suffix = gtkimage.getOption('ANGULAR_DIM_SECONDARY_SUFFIX')
    _asentry = gtk.Entry()
    _asentry.set_text(_suffix)
    _entry_size_group.add_widget(_asentry)
    _table.attach(_asentry, 2, 3, 3, 4,
                  gtk.EXPAND,
                  gtk.EXPAND,
                  2, 2)
    #
    # Precision
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Precision:")
    _label_size_group.add_widget(_label)    
    _hbox.pack_start(_label, False, False, 0)
    _prec = gtkimage.getOption('DIM_SECONDARY_PRECISION')
    _adj = gtk.Adjustment(_prec, 0, 15, 1, 1, 1)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(0)
    _sb.set_numeric(True)    
    _hbox.pack_start(_sb, False, False, 0)
    #
    # Units
    #
    _units = [('Millimeters', PythonCAD.Generic.units.MILLIMETERS),
              ('Micrometers', PythonCAD.Generic.units.MICROMETERS),
              ('Meters', PythonCAD.Generic.units.METERS),
              ('Kilometers', PythonCAD.Generic.units.KILOMETERS),
              ('Inches', PythonCAD.Generic.units.INCHES),
              ('Feet', PythonCAD.Generic.units.FEET),
              ('Yards', PythonCAD.Generic.units.YARDS),
              ('Miles', PythonCAD.Generic.units.MILES),
              ]
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Units:")
    _label_size_group.add_widget(_label)
    _hbox.pack_start(_label, False, False, 0)
    _unit = gtkimage.getOption('DIM_SECONDARY_UNITS')
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _unit_widget = gtk.combo_box_new_text()
        for _i in range(len(_units)):
            _str, _val = _units[_i]
            if _unit == _val:
                _idx = _i
            _unit_widget.append_text(_str)
        _unit_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        for _i in range(len(_units)):
            _str, _val = _units[_i]
            if _unit == _val:
                _idx = _i
            _item = gtk.MenuItem(_str)
            _menu.append(_item)
        _unit_widget = gtk.OptionMenu()
        _unit_widget.set_menu(_menu)
        _unit_widget.set_history(_idx)
    _menu_size_group.add_widget(_unit_widget)
    _hbox.pack_start(_unit_widget, False, False, 0);
    #
    # Print Zero
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _pzcb = gtk.CheckButton("Print Leading Zero")
    _pzro = gtkimage.getOption('DIM_SECONDARY_LEADING_ZERO')
    _pzcb.set_active(_pzro)
    _hbox.pack_start(_pzcb, True, True, 0)
    #
    # Print Decimal
    #
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _pdcb = gtk.CheckButton("Print Trailing Decimal")
    _pdec = gtkimage.getOption('DIM_SECONDARY_TRAILING_DECIMAL')
    _pdcb.set_active(_pdec)
    _hbox.pack_start(_pdcb, True, True, 0)
    #
    _dialog.show_all()
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
            _idx = _family_widget.get_active()
            _family = _families[_idx]
            _style = _style_widget.get_active()
            _weight = _weight_widget.get_active()
            _idx = _unit_widget.get_active()
            _unit = _units[_idx][1]
        else:
            _idx = _family_widget.get_history()
            _family = _families[_idx]
            _style = _style_widget.get_history()
            _weight = _weight_widget.get_history()
            _idx = _unit_widget.get_history()
            _unit = _units[_idx][1]
        _size = float(_size_sb.get_value())
        _lprefix = unicode(_lpentry.get_text())
        _lsuffix = unicode(_lsentry.get_text())
        _rprefix = unicode(_rpentry.get_text())
        _rsuffix = unicode(_rsentry.get_text())
        _aprefix = unicode(_apentry.get_text())
        _asuffix = unicode(_asentry.get_text())
        _prec = int(_sb.get_value())
        _pzro = _pzcb.get_active()
        _pdec = _pdcb.get_active()
        if gtkimage.hasSelection():
            _active_layer = gtkimage.getActiveLayer()
            _objs = []
            for _obj in gtkimage.getSelectedObjects():
                if ((_obj.getParent() is _active_layer) and
                    (isinstance(_obj, Dimension))):
                    _objs.append(_obj)
            if len(_objs):
                gtkimage.startAction()
                try:
                    for _obj in _objs:
                        _ds = _obj.getSecondaryDimstring()
                        _ds.setFamily(_family)
                        _ds.setStyle(_style)
                        _ds.setWeight(_weight)
                        _ds.setSize(_size)
                        if isinstance(_obj, AngularDimension):
                            _ds.setPrefix(_aprefix)
                            _ds.setSuffix(_asuffix)
                        elif isinstance(_obj, RadialDimension):
                            _ds.setPrefix(_rprefix)
                            _ds.setSuffix(_rsuffix)
                        else:
                            _ds.setPrefix(_lprefix)
                            _ds.setSuffix(_lsuffix)
                        _ds.setPrecision(_prec)
                        _ds.setUnits(_unit)
                        _ds.setPrintZero(_pzro)
                        _ds.setPrintDecimal(_pdec)
                finally:
                    gtkimage.endAction()
    _dialog.destroy()
    gtkimage.redraw()

def change_dimstr_prefix_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Prefix", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Prefix")
    _hbox.pack_start(_label, True, True, 0)
    _prefix = gtkimage.getOption(key)
    _entry = gtk.Entry()
    _entry.set_text(_prefix)
    _hbox.pack_start(_entry, True, True, 0)
    _dialog.show_all()
    _prefix = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _prefix = _entry.get_text()
    _dialog.destroy()
    return _prefix

def change_dimstr_suffix_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Suffix", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Suffix")
    _hbox.pack_start(_label, True, True, 0)
    _suffix = gtkimage.getOption(key)
    _entry = gtk.Entry()
    _entry.set_text(_suffix)
    _hbox.pack_start(_entry, True, True, 0)
    _dialog.show_all()
    _suffix = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _suffix = _entry.get_text()
    _dialog.destroy()
    return _suffix

def change_dimstr_precision_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Precision", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Precision:")
    _hbox.pack_start(_label, False, False, 0)
    _prec = gtkimage.getOption(key)
    _adj = gtk.Adjustment(_prec, 0, 15, 1, 1, 1)
    _sb = gtk.SpinButton(_adj)
    _sb.set_digits(0)
    _sb.set_numeric(True)    
    _hbox.pack_start(_sb, True, True, 0)
    _dialog.show_all()
    _prec = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _prec = int(_sb.get_value())
    _dialog.destroy()
    return _prec

def change_dimstr_units_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Units", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _label = gtk.Label("Units")
    _hbox.pack_start(_label, False, False, 0)
    _units = [('Millimeters', PythonCAD.Generic.units.MILLIMETERS),
              ('Micrometers', PythonCAD.Generic.units.MICROMETERS),
              ('Meters', PythonCAD.Generic.units.METERS),
              ('Kilometers', PythonCAD.Generic.units.KILOMETERS),
              ('Inches', PythonCAD.Generic.units.INCHES),
              ('Feet', PythonCAD.Generic.units.FEET),
              ('Yards', PythonCAD.Generic.units.YARDS),
              ('Miles', PythonCAD.Generic.units.MILES),
              ]
    _unit = gtkimage.getOption(key)
    _idx = 0
    if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
        _unit_widget = gtk.combo_box_new_text()
        for _i in range(len(_units)):
            _str, _val = _units[_i]
            if _unit == _val:
                _idx = _i
            _unit_widget.append_text(_str)
        _unit_widget.set_active(_idx)
    else:
        _menu = gtk.Menu()
        for _i in range(len(_units)):
            _str, _val = _units[_i]
            if _unit == _val:
                _idx = _i
            _item = gtk.MenuItem(_str)
            _menu.append(_item)
        _unit_widget = gtk.OptionMenu()
        _unit_widget.set_menu(_menu)
        _unit_widget.set_history(_idx)
    _hbox.pack_start(_unit_widget, True, True, 0);
    _dialog.show_all()
    _unit = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        if hasattr(gtk, 'ComboBox'): # PyGTK 2.4
            _idx = _unit_widget.get_active()
        else:
            _idx = _unit_widget.get_history()
        _unit = _units[_idx][1]
    _dialog.destroy()
    return _unit

def change_dimstr_print_decimal_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Print Zero", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _cb = gtk.CheckButton("Print Trailing Decimal")
    _mode = gtkimage.getOption(key)
    _cb.set_active(_mode)
    _hbox.pack_start(_cb, True, True, 0)
    _dialog.show_all()
    _mode = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _mode = _cb.get_active()
    _dialog.destroy()
    return _mode

def change_dimstr_print_zero_dialog(gtkimage, key):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Change Print Zero", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(False, 2)
    _hbox.set_border_width(2)
    _dialog.vbox.pack_start(_hbox, False, False, 0)
    _cb = gtk.CheckButton("Print Leading Zero")
    _mode = gtkimage.getOption(key)
    _cb.set_active(_mode)
    _hbox.pack_start(_cb, True, True, 0)
    _dialog.show_all()
    _mode = None
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _mode = _cb.get_active()
    _dialog.destroy()
    return _mode

def _change_dimstring_init(gtkimage, tool):
    if gtkimage.hasSelection():
        _primary = tool.getPrimary()
        _objs = []
        for _obj in gtkimage.getSelectedObjects():
            if isinstance(_obj, Dimension):
                if _primary:
                    _objs.append(_obj.getPrimaryDimstring())
                else:
                    _objs.append(_obj.getSecondaryDimstring())
        _change_attribute(gtkimage, _objs, tool)
    else:
        tool.setHandler("button_press", change_attr_first_button_press_cb)
    
def change_dimstr_family_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_family_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString family.")

def change_dimstr_style_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_style_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString style.")

def change_dimstr_weight_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_weight_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString weight.")

def change_dimstr_alignment_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_alignment_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString alignment.")

def change_dimstr_size_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_size_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString size.")

def change_dimstr_color_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_color_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString color.")

def change_dimstr_prefix_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_prefix_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString prefix.")

def change_dimstr_suffix_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_suffix_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString prefix.")

def change_dimstr_precision_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_precision_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString precision.")

def change_dimstr_units_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_units_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString units.")

def change_dimstr_print_zero_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_print_zero_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString print leading zero flag.")

def change_dimstr_print_decimal_init(gtkimage, tool):
    tool.setHandler("initialize", change_dimstr_print_decimal_init)
    tool.setFilter(_dimstring_filter_proc)
    _change_dimstring_init(gtkimage, tool)
    gtkimage.setPrompt("Click the dimension you want to change the DimString print trailing decimal flag.")

#
# arbitrary zoom
#

def zoom_end_button_press_cb(gtkimage, widget, event, tool):
    _xp, _yp = gtkimage.getCurrentPoint()
    _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)
    return True

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, 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, False, _xmin, _ymin, _rw, _rh)
    return True

def zoom_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getCurrentPoint()
    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)
    return True

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