#
# Copyright (c) 2002, 2003, 2004, 2005 Art Haas
#
# This file is part of PythonCAD.
#
# PythonCAD is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PythonCAD is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PythonCAD; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# GTK Menu building code
#

import os
import stat
import sys

try:
    import gc
    gc.set_debug(gc.DEBUG_LEAK)
except ImportError:
    pass

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

from PythonCAD.Interface.Gtk.gtkimage import GTKImage
from PythonCAD.Interface.Gtk import gtkedit
from PythonCAD.Interface.Gtk import gtkentities
from PythonCAD.Interface.Gtk import gtkconobjs
from PythonCAD.Interface.Gtk import gtkprefs
from PythonCAD.Interface.Gtk import gtkmodify
from PythonCAD.Interface.Gtk import gtkmirror
from PythonCAD.Interface.Gtk import gtktext
from PythonCAD.Interface.Gtk import gtklayers
from PythonCAD.Interface.Gtk import gtkdimension
from PythonCAD.Interface.Gtk import gtkprinting
from PythonCAD.Generic import globals
from PythonCAD.Generic import fileio
from PythonCAD.Generic import imageio
from PythonCAD.Generic import tools
from PythonCAD.Generic import menus
from PythonCAD.Generic import plotfile

#
# dictionary used to map strings to stock icons
#

iconmap = {
    'NEW' : gtk.STOCK_NEW,
    'QUIT' : gtk.STOCK_QUIT,
    'OPEN' : gtk.STOCK_OPEN,
    'CLOSE': gtk.STOCK_CLOSE,
    'DELETE' : gtk.STOCK_DELETE,
    'SAVE' : gtk.STOCK_SAVE,
    'SAVE_AS' : gtk.STOCK_SAVE_AS,
    'UNDO' : gtk.STOCK_UNDO,
    'REDO' : gtk.STOCK_REDO,
    'CUT' : gtk.STOCK_CUT,
    'COPY' : gtk.STOCK_COPY,
    'PASTE': gtk.STOCK_PASTE,
    'PREFS' : gtk.STOCK_PREFERENCES,
    'PRINT' : gtk.STOCK_PRINT,
    'ZOOM_IN' : gtk.STOCK_ZOOM_IN,
    'ZOOM_OUT' : gtk.STOCK_ZOOM_OUT,
    'ZOOM_FIT' : gtk.STOCK_ZOOM_FIT,
    }

#
# dictionary used to map letters to keysyms
#

accelmap = {
    'N' : gtk.keysyms.N,
    'O' : gtk.keysyms.O,
    'W' : gtk.keysyms.W,
    'S' : gtk.keysyms.S,
    'Q' : gtk.keysyms.Q,
    'Z' : gtk.keysyms.Z,
    'X' : gtk.keysyms.X,
    'C' : gtk.keysyms.C,
    'P' : gtk.keysyms.P,
    'V' : gtk.keysyms.V
    }

#
# callbacks for the menu items
#

def file_menu_init_cb(menuitem, gtkimage):
    # print "in file_menu_init_cb()"
    return gtk.FALSE

def file_new_cb(menuitem, gtkimage):
    _image = GTKImage()
    _background = globals.prefs['BACKGROUND_COLOR']
    _image.setOption('BACKGROUND_COLOR', _background)
    globals.imagelist.append(_image)
    _image.window.show_all()
    return gtk.FALSE

def file_open_cb(menuitem, gtkimage):
    _open = False
    _fname = None
    _dialog = gtk.FileSelection("Open File ...")
    _dialog.set_transient_for(gtkimage.getWindow())
    # _dialog.hide_fileop_buttons()
    while not _open:
        _response = _dialog.run()
        if _response == gtk.RESPONSE_OK:
            _fname = _dialog.get_filename()
            if os.path.isdir(_fname):
                _fname += "/"
                _dialog.set_filename(_fname)
                _response = _dialog.run()
            else:
                _open = True
        else:
            break
    _dialog.destroy()
    if _open:
        _image = GTKImage()
        try:
            _handle = fileio.CompFile(_fname, "r")
            try:
                imageio.load_image(_image, _handle)
            finally:
                _handle.close()
        except (IOError, OSError), e:
            _errmsg = "Error opening '%s' : %s'" % (_fname, _e)
            _error_dialog(gtkimage, _msg)
            return gtk.FALSE
        except StandardError, e:
            _errmsg = "Non-system error opening '%s' : %s'" % (_fname, _e)
            _error_dialog(gtkimage, _msg)
            return gtk.FALSE
        globals.imagelist.append(_image)
        _image.setFilename(_fname)
        _window = _image.getWindow()
        _window.set_title(os.path.basename(_fname))
        _window.show_all()
        _image.fitImage()
    return gtk.FALSE

def file_close_cb(menuitem, gtkimage):
    for _i in xrange(len(globals.imagelist)):
        _image = globals.imagelist[_i]
        if _image is gtkimage:
            _log = _image.getLog()
            if _log is not None:
                _log.detatch()
            del globals.imagelist[_i]
            _image.window.destroy()
            if not len(globals.imagelist):
                gtk.main_quit()
            break
    return gtk.FALSE

def _error_dialog(gtkimage, errmsg):
    _window = gtkimage.getWindow()
    _dialog = gtk.MessageDialog( _window,
                                 gtk.DIALOG_DESTROY_WITH_PARENT,
                                 gtk.MESSAGE_ERROR,
                                 gtk.BUTTONS_CLOSE,
                                 errmsg)
    _dialog.run()
    _dialog.destroy()

def _save_file(gtkimage, filename):
    _abs = os.path.abspath(filename)
    _bname = os.path.basename(_abs)
    if _bname.endswith('.gz'):
        _bname = _bname[:-3]
    _newfile = _abs + '.new'
    _handle = fileio.CompFile(_newfile, "w", truename=_bname)
    try:
        imageio.save_image(gtkimage, _handle)
    finally:
        _handle.close()
    _backup = _abs + '~'
    if os.path.exists(_backup):
        os.unlink(_backup)
    _mode = None
    if os.path.exists(_abs):
        _st = os.stat(_abs)
        _mode = stat.S_IMODE(_st.st_mode)
        os.rename(_abs, _backup)
    try:
        os.rename(_newfile, _abs)
    except:
        os.rename(_backup, _abs)
        raise
    if _mode is not None and hasattr(os, 'chmod'):
        os.chmod(_abs, _mode)
    if gtkimage.getFilename() is None:
        gtkimage.setFilename(_abs)

def _writecopy(src, dst):
    if sys.platform == 'win32':
        _rmode = 'rb'
        _wmode = 'wb'
    else:
        _rmode = 'r'
        _wmode = 'w'
    _from = file(src, _rmode)
    try:
        _to = file(dst, _wmode)
        try:
            while True:
                _data = _from.read(8192)
                if _data == '':
                    break
                _to.write(_data)
        finally:
            _to.close()
    finally:
        _from.close()

def _save_file_by_copy(gtkimage, filename):
    _abs = os.path.abspath(filename)
    _bname = os.path.basename(_abs)
    if _bname.endswith('.gz'):
        _bname = _bname[:-3]
    _newfile = _abs + '.new'
    _handle = fileio.CompFile(_newfile, "w", truename=_bname)
    try:
        imageio.save_image(gtkimage, _handle)
    finally:
        _handle.close()
    # print "saved new file %s" % _newfile
    _backup = _abs + '~'
    if os.path.exists(_abs):
        _writecopy(_abs, _backup)
        # print "writing existing to backup %s" % _backup
    try:
        _writecopy(_newfile, _abs)
    except:
        _writecopy(_backup, _abs)
        raise
    # print "writing new file to filename %s" % _abs
    os.unlink(_newfile)
    # print "removing temporary new file %s" % _abs
    if gtkimage.getFilename() is None:
        gtkimage.setFilename(_abs)
    
def _get_filename_and_save(gtkimage, fname=None):
    _window = gtkimage.getWindow()
    _fname = fname
    if _fname is None:
        _fname = _window.get_title()
    _fname = _window.get_title()
    _dialog = gtk.FileSelection("Save As ...")
    _dialog.set_transient_for(gtkimage.getWindow())
    _dialog.set_filename(_fname)
    _response = _dialog.run()
    _save = False
    if _response == gtk.RESPONSE_OK:
        _save = True
        _fname = _dialog.get_filename()
        if _fname == "":
            _fname = 'Untitled.xml'
        if not _fname.endswith('.xml.gz'):
            if not _fname.endswith('.xml'):
                _fname = _fname + '.xml'
        #
        # if the filename already exists see that the user
        # really wants to overwrite it ...
        #
        # test for the filename + '.gz'
        #
        if _fname.endswith('.xml.gz'):
            _gzfile = _fname
        else:
            _gzfile = _fname + '.gz'
        if os.path.exists(_gzfile):
            _save = False
            _dialog2 = gtk.Dialog("Overwrite Existing File", _window,
                                  gtk.DIALOG_MODAL,
                                  (gtk.STOCK_OK, gtk.RESPONSE_OK,
                                   gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
            _hbox = gtk.HBox(gtk.FALSE, 10)
            _hbox.set_border_width(10)
            _dialog2.vbox.pack_start(_hbox, gtk.FALSE, gtk.FALSE, 0)
            _stock = gtk.image_new_from_stock(gtk.STOCK_DIALOG_QUESTION,
                                              gtk.ICON_SIZE_DIALOG)
            _hbox.pack_start(_stock, gtk.FALSE, gtk.FALSE, 0)
            _label = gtk.Label("File already exists. Delete it?")
            _hbox.pack_start(_label, gtk.FALSE, gtk.FALSE, 0)
            _dialog2.show_all()
            _response = _dialog2.run()
            if _response == gtk.RESPONSE_OK:
                _save = True
            _dialog2.destroy()
    _dialog.destroy()
    if _save:
        # print "name: " + _gzfile
        gtkimage.setFilename(_gzfile)
        _window.set_title(os.path.basename(_gzfile))
        try:
            _save_file(gtkimage, _gzfile)
        except (IOError, OSError), _e:
            _errmsg = "Error saving '%s' : %s'" % (_gzfile, _e)
            _error_dialog(gtkimage, _errmsg)
        except StandardError, _e:
            _errmsg = "Non-system error saving '%s' : %s'" % (_gzfile, _e)
            _error_dialog(gtkimage, _errmsg)

def file_save_cb(menuitem, gtkimage):
    _fname = gtkimage.getFilename()
    if _fname is None:
        _get_filename_and_save(gtkimage)
    else:
        try:
            _save_file(gtkimage, _fname)
        except (IOError, OSError), _e:
            _errmsg = "Error saving '%s' : %s'" % (_fname, _e)
            _error_dialog(gtkimage, _errmsg)
        except StandardError, _e:
            _errmsg = "Non-system error saving '%s' : %s'" % (_fname, _e)
            _error_dialog(gtkimage, _errmsg)
    return gtk.FALSE

def file_save_as_cb(menuitem, gtkimage):
    _fname = gtkimage.getFilename()
    if _fname is None:
        _fname = gtkimage.getWindow().get_title()
    _get_filename_and_save(gtkimage, _fname)
    return gtk.FALSE

def file_save_layer_cb(menuitem, gtkimage):
    # print "called file_save_layer_cb()"
    active = gtkimage.getActiveLayer()
    layer_name = active.getName()
    dialog = gtk.FileSelection("Save Layer As ...")
    dialog.set_transient_for(gtkimage.getWindow())
    dialog.set_filename(layer_name)
    response = dialog.run()
    if response == gtk.RESPONSE_OK:
        fname = dialog.get_filename()
        print "Saving layer as '%s'" % fname
        #
        # fixme - add the layer saving code ...
        #
    dialog.destroy()
    return gtk.FALSE

def file_print_screen_cb(menuitem, gtkimage):
    _plot = plotfile.Plot(gtkimage)
    _xmin, _ymin, _xmax, _ymax = gtkimage.getView()
    _plot.setBounds(_xmin, _ymin, _xmax, _ymax)
    gtkprinting.print_dialog(gtkimage, _plot)
    return gtk.FALSE
    
def file_print_cb(menuitem, gtkimage):
    _tool = tools.PlotTool()
    gtkimage.setTool(_tool)
    gtkprinting.plot_mode_init(gtkimage, _tool)
    return gtk.FALSE

def file_quit_cb(menuitem, gtkimage):
    gtk.main_quit()
    return gtk.FALSE

def adjust_edit_menu(menuitem, gtkimage):
    _key = gtkimage.getMenuKey(menuitem)
    if _key is not None:
        if _key == 'edit_undo':
            menuitem.set_sensitive(gtkimage.canUndo())
        elif _key == 'edit_redo':
            menuitem.set_sensitive(gtkimage.canRedo())
        elif _key == 'edit_copy' or _key == 'edit_cut':
            menuitem.set_sensitive(gtkimage.hasSelection())
        elif _key == 'edit_paste':
            menuitem.set_sensitive(globals.selectobj.hasObjects())
        else:
            pass

def edit_menu_init_cb(menuitem, gtkimage):
    menuitem.get_submenu().foreach(adjust_edit_menu, gtkimage)
    return gtk.FALSE

def edit_undo_cb(menuitem, gtkimage):
    gtkimage.doUndo()
    gtkimage.redraw()
    return gtk.FALSE

def edit_redo_cb(menuitem, gtkimage):
    gtkimage.doRedo()
    gtkimage.redraw()
    return gtk.FALSE

def edit_copy_cb(menuitem, gtkimage):
    for lyr, obj in gtkimage.getSelectedObjects():
        globals.selectobj.storeObject(gtkimage, lyr, obj)
    return gtk.FALSE

def edit_cut_cb(menuitem, gtkimage):
    for lyr, obj in gtkimage.getSelectedObjects():
        globals.selectobj.storeObject(gtkimage, lyr, obj)
        #
        # check that the object is still in the layer - objects like
        # dimensions get deleted if the points they refer to are
        # deleted ...
        #
        if obj in lyr:
            gtkimage.delObject(obj,lyr)
    gtkimage.redraw()
    return gtk.FALSE

def edit_paste_cb(menuitem, gtkimage):
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click where you want to paste the objects")
    gtkedit.paste_mode_init(_tool)
    return gtk.FALSE

def select_cb(menuitem, gtkimage):
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the items you want to select.")
    gtkedit.select_mode_init(_tool)
    return gtk.FALSE

def select_all_points_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _point in _active_layer.getLayerEntities("point"):
        gtkimage.selectObject(_active_layer, _point)
    return gtk.FALSE

def select_all_segments_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _segment in _active_layer.getLayerEntities("segment"):
        gtkimage.selectObject(_active_layer, _segment)
    return gtk.FALSE

def select_all_circles_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _circle in _active_layer.getLayerEntities("circle"):
        gtkimage.selectObject(_active_layer, _circle)
    return gtk.FALSE

def select_all_arcs_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _arc in _active_layer.getLayerEntities("arc"):
        gtkimage.selectObject(_active_layer, _arc)
    return gtk.FALSE

def select_all_hclines_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _hcl in _active_layer.getLayerEntities("hcline"):
        gtkimage.selectObject(_active_layer, _hcl)
    return gtk.FALSE

def select_all_vclines_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _vcl in _active_layer.getLayerEntities("vcline"):
        gtkimage.selectObject(_active_layer, _vcl)
    return gtk.FALSE

def select_all_aclines_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _acl in _active_layer.getLayerEntities("acline"):
        gtkimage.selectObject(_active_layer, _acl)
    return gtk.FALSE

def select_all_clines_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _cline in _active_layer.getLayerEntities("cline"):
        gtkimage.selectObject(_active_layer, _cline)
    return gtk.FALSE

def select_all_ccircles_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _ccircle in _active_layer.getLayerEntities("ccircle"):
        gtkimage.selectObject(_active_layer, _ccircle)
    return gtk.FALSE

def select_all_leaders_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _leader in _active_layer.getLayerEntities("leader"):
        gtkimage.selectObject(_active_layer, _leader)
    return gtk.FALSE

def select_all_ldims_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _ldim in _active_layer.getLayerEntities("linear_dimension"):
        gtkimage.selectObject(_active_layer, _ldim)
    return gtk.FALSE

def select_all_hdims_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _hdim in _active_layer.getLayerEntities("horizontal_dimension"):
        gtkimage.selectObject(_active_layer, _hdim)
    return gtk.FALSE

def select_all_vdims_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _vdim in _active_layer.getLayerEntities("vertical_dimension"):
        gtkimage.selectObject(_active_layer, _vdim)
    return gtk.FALSE

def select_all_rdims_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _rdim in _active_layer.getLayerEntities("radial_dimension"):
        gtkimage.selectObject(_active_layer, _rdim)
    return gtk.FALSE

def select_all_adims_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _adim in _active_layer.getLayerEntities("angular_dimension"):
        gtkimage.selectObject(_active_layer, _adim)
    return gtk.FALSE

def select_all_tblocks_cb(menuitem, gtkimage):
    _active_layer = gtkimage.getActiveLayer()
    for _tb in _active_layer.getLayerEntities("textblock"):
        gtkimage.selectObject(_active_layer, _tb)
    return gtk.FALSE

def prefs_cb(menuitem, gtkimage):
    gtkprefs.prefs_dialog(gtkimage)
    return gtk.FALSE

def draw_menu_init_cb(menuitem, gtkimage):
    # print "in draw_menu_init_cb()"
    return gtk.FALSE

def draw_point_cb(menuitem, gtkimage):
    _tool = tools.PointTool()
    gtkimage.setTool(_tool)
    gtkentities.point_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_segment_cb(menuitem, gtkimage):
    _tool = tools.SegmentTool()
    gtkimage.setTool(_tool)
    gtkentities.segment_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_rectangle_cb(menuitem, gtkimage):
    _tool = tools.RectangleTool()
    gtkimage.setTool(_tool)
    gtkentities.rectangle_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_circle_center_cb(menuitem, gtkimage):
    _tool = tools.CircleTool()
    gtkimage.setTool(_tool)
    gtkentities.circle_center_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_circle_tp_cb(menuitem, gtkimage):
    _tool = tools.TwoPointCircleTool()
    gtkimage.setTool(_tool)
    gtkentities.circle_tp_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_arc_center_cb(menuitem, gtkimage):
    _tool = tools.ArcTool()
    gtkimage.setTool(_tool)
    gtkentities.arc_center_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_hcl_cb(menuitem, gtkimage):
    _tool = tools.HCLineTool()
    gtkimage.setTool(_tool)
    gtkconobjs.hcline_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_vcl_cb(menuitem, gtkimage):
    _tool = tools.VCLineTool()
    gtkimage.setTool(_tool)
    gtkconobjs.vcline_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_acl_cb(menuitem, gtkimage):
    _tool = tools.ACLineTool()
    gtkimage.setTool(_tool)
    gtkconobjs.acline_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_cl_cb(menuitem, gtkimage):
    _tool = tools.CLineTool()
    gtkimage.setTool(_tool)
    gtkconobjs.cline_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_perpendicular_cline_cb(menuitem, gtkimage):
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the object you want a perpendicular to")
    gtkconobjs.perpendicular_cline_mode_init(_tool)
    return gtk.FALSE

def draw_tangent_cline_cb(menuitem, gtkimage):
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the circle object you want a tangent to")
    gtkconobjs.tangent_cline_mode_init(_tool)
    return gtk.FALSE

def draw_tangent_two_ccircles_cb(menuitem, gtkimage):
    _tool = tools.CCircleTangentLineTool()
    gtkimage.setTool(_tool)
    gtkconobjs.two_circle_tangent_line_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_poffset_cline_cb(menuitem, gtkimage):
    _tool = tools.ParallelOffsetTool()
    gtkimage.setTool(_tool)
    gtkconobjs.parallel_offset_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_ccirc_cp_cb(menuitem, gtkimage):
    _tool = tools.CCircleTool()
    gtkimage.setTool(_tool)
    # gtkimage.setPrompt("Click in the drawing area or enter a Point")
    gtkconobjs.ccircle_cpmode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_ccirc_tp_cb(menuitem, gtkimage):
    _tool = tools.TwoPointCCircleTool()
    gtkimage.setTool(_tool)
    # gtkimage.setPrompt("Click in the drawing area or enter a Point")
    gtkconobjs.ccircle_tpmode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_tangent_ccircle_menu_init_cb(menuitem, gtkimage):
    # print "in draw_tangent_ccircle_menu_init_cb()"
    return gtk.FALSE

def draw_tangent_single_conobj_cb(menuitem, gtkimage):
    _tool = tools.TangentCCircleTool()
    gtkimage.setTool(_tool)
    gtkconobjs.tangent_ccircle_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_tangent_two_conobjs_cb(menuitem, gtkimage):
    # print "two_conobjs_cb() ..."
    _tool = tools.TwoPointTangentCCircleTool()
    gtkimage.setTool(_tool)
    gtkconobjs.two_cline_tancc_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_chamfer_cb(menuitem, gtkimage):
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the points where you want a chamfer.")
    gtkentities.chamfer_mode_init(_tool)
    return gtk.FALSE

def draw_fillet_cb(menuitem, gtkimage):
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the points where you want a fillet.")
    gtkentities.fillet_mode_init(_tool)
    return gtk.FALSE

def draw_leader_cb(menuitem, gtkimage):
    _tool = tools.LeaderTool()
    gtkimage.setTool(_tool)
    gtkentities.leader_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_polyline_cb(menuitem, gtkimage):
    _tool = tools.PolylineTool()
    gtkimage.setTool(_tool)
    gtkentities.polyline_mode_init(gtkimage, _tool)
    return gtk.FALSE

def _get_polygon_side_count(gtkimage):
    _sides = 0
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Polygon Sides", _window,
                         gtk.DIALOG_MODAL,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _hbox = gtk.HBox(gtk.FALSE, 10)
    _hbox.set_border_width(10)
    _dialog.vbox.pack_start(_hbox, gtk.FALSE, gtk.FALSE, 0)
    _stock = gtk.image_new_from_stock(gtk.STOCK_DIALOG_QUESTION,
                                      gtk.ICON_SIZE_DIALOG)
    _hbox.pack_start(_stock, gtk.FALSE, gtk.FALSE, 0)
    _label = gtk.Label("Number of sides:")
    _hbox.pack_start(_label, gtk.FALSE, gtk.FALSE, 0)
    _adj = gtk.Adjustment(3, 3, 3600, 1, 1, 1) # arbitrary max ...
    _sb = gtk.SpinButton(_adj)
    _sb.set_numeric(gtk.TRUE)
    _hbox.pack_start(_sb, gtk.TRUE, gtk.TRUE, 0)
    _dialog.show_all()
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _sides = _sb.get_value()
    _dialog.destroy()
    return _sides

def draw_polygon_cb(menuitem, gtkimage):
    _sides = _get_polygon_side_count(gtkimage)
    if _sides > 0:
        _tool = tools.PolygonTool()
        _tool.setSideCount(_sides)
        gtkimage.setTool(_tool)
        gtkentities.polygon_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_ext_polygon_cb(menuitem, gtkimage):
    _sides = _get_polygon_side_count(gtkimage)
    if _sides > 0:
        _tool = tools.PolygonTool()
        _tool.setExternal()
        _tool.setSideCount(_sides)
        gtkimage.setTool(_tool)
        gtkentities.polygon_mode_init(gtkimage, _tool)
    return gtk.FALSE

def draw_set_style(menuitem, gtkimage):
    gtkentities.set_active_style(gtkimage)
    return gtk.FALSE

def draw_set_linetype(menuitem, gtkimage):
    gtkentities.set_active_linetype(gtkimage)
    return gtk.FALSE

def draw_set_color(menuitem, gtkimage):
    gtkentities.set_active_color(gtkimage)
    return gtk.FALSE

def draw_set_thickness(menuitem, gtkimage):
    gtkentities.set_line_thickness(gtkimage)
    return gtk.FALSE

def draw_text_cb(menuitem, gtkimage):
    _tool = tools.TextTool()
    gtkimage.setTool(_tool)
    gtktext.get_new_text(gtkimage)
    if _tool.hasText():
        gtktext.text_add_init(gtkimage, _tool)
    return gtk.FALSE

def modify_menu_init_cb(menuitem, gtkimage):
    # print "in modify_menu_init_cb()"
    return gtk.FALSE

def move_horizontal_cb(menuitem, gtkimage):
    _tool = tools.HorizontalMoveTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click in the drawing area or enter the distance.")
    gtkmodify.move_horizontal_init(_tool)
    return gtk.FALSE

def move_vertical_cb(menuitem, gtkimage):
    _tool = tools.VerticalMoveTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click in the drawing area or enter the distance.")
    gtkmodify.move_vertical_init(_tool)
    return gtk.FALSE

def move_twopoint_cb(menuitem, gtkimage):
    _tool = tools.MoveTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click in the drawing area.")
    gtkmodify.move_twopoint_init(_tool)
    return gtk.FALSE

def stretch_horiz_cb(menuitem, gtkimage):
    _tool = tools.HorizontalStretchTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on a point or enter the horizontal distance.")
    gtkmodify.stretch_horizontal_init(_tool)
    return gtk.FALSE

def stretch_vert_cb(menuitem, gtkimage):
    _tool = tools.VerticalStretchTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click in the drawing area or enter the distance.")
    gtkmodify.stretch_vertical_init(_tool)
    return gtk.FALSE

def stretch_twopoint_cb(menuitem, gtkimage):
    _tool = tools.StretchTool()
    gtkimage.setTool(_tool)
    print "in stretch_twopoint_cb()" # fixme
    return gtk.FALSE

def transfer_object_cb(menuitem, gtkimage):
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    prompt = "Click on the objects you want to transfer to the active layer"
    gtkimage.setPrompt(prompt)
    gtkmodify.transfer_object_init(_tool)
    return gtk.FALSE

def split_object_cb(menuitem, gtkimage):
    _tool = tools.SplitTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the objects you want to split")
    gtkmodify.split_object_init(_tool)
    return gtk.FALSE

def mirror_object_cb(menuitem, gtkimage):
    _tool = tools.MirrorTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the mirroring construction line.")
    gtkmirror.mirror_mode_init(_tool)
    return gtk.FALSE

def delete_cb(menuitem, gtkimage):
    _tool = tools.DeleteTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the items you want to delete.")
    gtkmodify.delete_mode_init(_tool)
    return gtk.FALSE

def change_style_cb(menuitem, gtkimage):
    gtkmodify.change_style_init(gtkimage)
    return gtk.FALSE

def change_color_cb(menuitem, gtkimage):
    gtkmodify.change_color_init(gtkimage)
    return gtk.FALSE

def change_linetype_cb(menuitem, gtkimage):
    gtkmodify.change_linetype_init(gtkimage)
    return gtk.FALSE

def change_thickness_cb(menuitem, gtkimage):
    gtkmodify.change_thickness_init(gtkimage)
    return gtk.FALSE

def modify_textblock_init_cb(menuitem, gtkimage):
    # print "in modify_textblock_init_cb()"
    return gtk.FALSE

def change_textblock_style_cb(menuitem, gtkimage):
    gtkmodify.change_textblock_style_init(gtkimage)
    return gtk.FALSE

def change_textblock_weight_cb(menuitem, gtkimage):
    gtkmodify.change_textblock_weight_init(gtkimage)
    return gtk.FALSE

def change_textblock_alignment_cb(menuitem, gtkimage):
    gtkmodify.change_textblock_alignment_init(gtkimage)
    return gtk.FALSE

def change_textblock_size_cb(menuitem, gtkimage):
    gtkmodify.change_textblock_size_init(gtkimage)
    return gtk.FALSE

def change_textblock_family_cb(menuitem, gtkimage):
    gtkmodify.change_textblock_family_init(gtkimage)
    return gtk.FALSE

def change_textblock_color_cb(menuitem, gtkimage):
    gtkmodify.change_textblock_color_init(gtkimage)
    return gtk.FALSE

def modify_dimension_init_cb(menuitem, gtkimage):
    # print "in modify_textblock_init_cb()"
    return gtk.FALSE

def change_dim_endpoint_cb(menuitem, gtkimage):
    gtkmodify.change_dim_endpoint_init(gtkimage)
    return gtk.FALSE

def change_dim_endpoint_size_cb(menuitem, gtkimage):
    gtkmodify.change_dim_endpoint_size_init(gtkimage)
    return gtk.FALSE

def change_dim_offset_cb(menuitem, gtkimage):
    gtkmodify.change_dim_offset_init(gtkimage)
    return gtk.FALSE

def change_dim_extension_cb(menuitem, gtkimage):
    gtkmodify.change_dim_extension_init(gtkimage)
    return gtk.FALSE

def change_dim_dual_mode_cb(menuitem, gtkimage):
    gtkmodify.change_dim_dual_mode_init(gtkimage)
    return gtk.FALSE

def change_dim_dual_mode_offset_cb(menuitem, gtkimage):
    gtkmodify.change_dim_dual_mode_offset_init(gtkimage)
    return gtk.FALSE

def change_dim_thickness_cb(menuitem, gtkimage):
    gtkmodify.change_dim_thickness_init(gtkimage)
    return gtk.FALSE

def change_dim_color_cb(menuitem, gtkimage):
    gtkmodify.change_dim_color_init(gtkimage)
    return gtk.FALSE

def change_primary_dim_cb(menuitem, gtkimage):
    gtkmodify.change_primary_dimstring_init(gtkimage)
    return gtk.FALSE

def change_secondary_dim_cb(menuitem, gtkimage):
    gtkmodify.change_secondary_dimstring_init(gtkimage)
    return gtk.FALSE

def zoom_cb(menuitem, gtkimage):
    # print "in zoom_cb()"
    _tool = tools.Tool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click in the window.")
    gtkmodify.zoom_init(_tool)
    return gtk.FALSE

def zoom_in_cb(menuitem, gtkimage):
    _xmin, _ymin, _xmax, _ymax = gtkimage.getView()
    _scale = gtkimage.getUnitsPerPixel()
    _xdiff = abs(_xmax - _xmin)
    _ydiff = abs(_ymax - _ymin)
    _xmin = (_xmin + _xmax)/2.0 - _xdiff/4.0
    _ymin = (_ymin + _ymax)/2.0 - _ydiff/4.0
    gtkimage.setView(_xmin, _ymin, (_scale/2.0))
    gtkimage.redraw()
    return gtk.FALSE

def zoom_out_cb(menuitem, gtkimage):
    _xmin, _ymin, _xmax, _ymax = gtkimage.getView()
    _scale = gtkimage.getUnitsPerPixel()
    _xdiff = abs(_xmax - _xmin)
    _ydiff = abs(_ymax - _ymin)
    _xmin = (_xmin + _xmax)/2.0 - _xdiff
    _ymin = (_ymin + _ymax)/2.0 - _ydiff
    gtkimage.setView(_xmin, _ymin, (_scale * 2.0))
    gtkimage.redraw()
    return gtk.FALSE

def zoom_fit_cb(menuitem, gtkimage):
    gtkimage.fitImage()
    return gtk.FALSE

def dimension_linear_cb(menuitem, gtkimage):
    _tool = tools.LinearDimensionTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the first point for the dimension.")
    gtkdimension.linear_mode_init(_tool)
    return gtk.FALSE

def dimension_menu_init_cb(menuitem, gtkimage):
    # print "in dimension_menu_init_cb()"
    return gtk.FALSE

def dimension_horizontal_cb(menuitem, gtkimage):
    _tool = tools.HorizontalDimensionTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the first point for the dimension.")
    gtkdimension.horizontal_mode_init(_tool)
    return gtk.FALSE

def dimension_vertical_cb(menuitem, gtkimage):
    _tool = tools.VerticalDimensionTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the first point for the dimension.")
    gtkdimension.vertical_mode_init(_tool)
    return gtk.FALSE

def dimension_radial_cb(menuitem, gtkimage):
    _tool = tools.RadialDimensionTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on an arc or a circle to dimension.")
    gtkdimension.radial_mode_init(_tool)
    return gtk.FALSE

def dimension_angular_cb(menuitem, gtkimage):
    _tool = tools.AngularDimensionTool()
    gtkimage.setTool(_tool)
    gtkimage.setPrompt("Click on the angle vertex point or an arc.")
    gtkdimension.angular_mode_init(_tool)
    return gtk.FALSE

def dimension_prefs_cb(menuitem, gtkimage):
    # print "in dimension_prefs_cb()"
    # gtkdimprefs.check_dim_prefs(gtkimage)
    return gtk.FALSE

def debug_menu_init_cb(menuitem, gtkimage):
    return gtk.FALSE

def get_focus_widget_cb(menuitem, gtkimage):
    _widget = gtkimage.getWindow().get_focus()
    print "Focus widget: " + str(_widget)
    return gtk.FALSE

def get_undo_stack_cb(menuitem, gtkimage):
    _layer = gtkimage.getActiveLayer()
    _log = _layer.getLog()
    if _log is not None:
        _log.printUndoData()
    return gtk.FALSE

def get_redo_stack_cb(menuitem, gtkimage):
    _layer = gtkimage.getActiveLayer()
    _log = _layer.getLog()
    if _log is not None:
        _log.printRedoData()
    return gtk.FALSE

def get_image_undo_cb(menuitem, gtkimage):
    gtkimage.printStack(True)
    return gtk.FALSE

def get_image_redo_cb(menuitem, gtkimage):
    gtkimage.printStack(False)
    return gtk.FALSE

def collect_garbage_cb(menuitem, gtkimage):
    if 'gc' in sys.modules:
        _lost = gc.collect()
        print "%d lost objects: " % _lost
    return gtk.FALSE
    
def cb(menuitem, window):
    print "called cb"
    return gtk.FALSE

#
# dictionary to bind callbacks
#

callbacks = {
    'file_menu_init' : file_menu_init_cb,
    'file_new' : file_new_cb,
    'file_open' : file_open_cb,
    'file_close' : file_close_cb,
    'file_save' : file_save_cb,
    'file_save_as' : file_save_as_cb,
    'file_save_layer' : file_save_layer_cb,
    'file_print_screen' : file_print_screen_cb,
    'file_print' : file_print_cb,
    'file_quit' : file_quit_cb,
    'edit_menu_init' : edit_menu_init_cb,
    'edit_undo' : edit_undo_cb,
    'edit_redo' : edit_redo_cb,
    'edit_cut' : edit_cut_cb,
    'edit_copy' : edit_copy_cb,
    'edit_paste' : edit_paste_cb,
    'select_cb' : select_cb,
    'select_all_points' : select_all_points_cb,
    'select_all_segments' : select_all_segments_cb,
    'select_all_circles' : select_all_circles_cb,
    'select_all_arcs' : select_all_arcs_cb,
    'select_all_hclines' : select_all_hclines_cb,
    'select_all_vclines' : select_all_vclines_cb,
    'select_all_aclines' : select_all_aclines_cb,
    'select_all_clines' : select_all_clines_cb,
    'select_all_ccircles' : select_all_ccircles_cb,
    'select_all_ldims' : select_all_ldims_cb,
    'select_all_hdims' : select_all_hdims_cb,
    'select_all_vdims' : select_all_vdims_cb,
    'select_all_rdims' : select_all_rdims_cb,
    'select_all_adims' : select_all_adims_cb,
    'select_all_tblocks' : select_all_tblocks_cb,
    'prefs' : prefs_cb,
    'draw_menu_init' : draw_menu_init_cb,
    'draw_point' : draw_point_cb,
    'draw_segment' : draw_segment_cb,
    'draw_rectangle' : draw_rectangle_cb,
    'draw_circle_center' : draw_circle_center_cb,
    'draw_circle_tp' : draw_circle_tp_cb,
    'draw_arc_center' : draw_arc_center_cb,
    'draw_hcl' : draw_hcl_cb,
    'draw_vcl' : draw_vcl_cb,
    'draw_acl' : draw_acl_cb,
    'draw_cl' : draw_cl_cb,
    'draw_perpendicular_cline' : draw_perpendicular_cline_cb,
    'draw_tangent_cline' : draw_tangent_cline_cb,
    'draw_tangent_two_ccircles' : draw_tangent_two_ccircles_cb,
    'draw_poffset_cline' : draw_poffset_cline_cb,
    'draw_ccirc_cp' : draw_ccirc_cp_cb,
    'draw_ccirc_tp' : draw_ccirc_tp_cb,
    'draw_tangent_ccircle_menu_init' : draw_tangent_ccircle_menu_init_cb,
    'draw_tangent_single_conobj' : draw_tangent_single_conobj_cb,
    'draw_tangent_two_conobjs' : draw_tangent_two_conobjs_cb,
    'draw_chamfer' : draw_chamfer_cb,
    'draw_fillet' : draw_fillet_cb,
    'draw_leader' : draw_leader_cb,
    'draw_polyline' : draw_polyline_cb,
    'draw_polygon' : draw_polygon_cb,
    'draw_ext_polygon' : draw_ext_polygon_cb,
    'draw_set_style' : draw_set_style,
    'draw_set_linetype' : draw_set_linetype,
    'draw_set_color': draw_set_color,
    'draw_set_thickness' : draw_set_thickness,
    'draw_text' : draw_text_cb,
    'modify_menu_init' : modify_menu_init_cb,
    'transfer' : transfer_object_cb,
    'split' : split_object_cb,
    'mirror' : mirror_object_cb,
    'delete' : delete_cb,
    'zoom' : zoom_cb,
    'zoom_in' : zoom_in_cb,
    'zoom_out' : zoom_out_cb,
    'zoom_fit' : zoom_fit_cb,
    'move_horiz' : move_horizontal_cb,
    'move_vert' : move_vertical_cb,
    'move_twopoint' : move_twopoint_cb,
    'stretch_horiz' : stretch_horiz_cb,
    'stretch_vert' : stretch_vert_cb,
    'stretch_twopoint' : stretch_twopoint_cb,
    'change_style' : change_style_cb,
    'change_color' : change_color_cb,
    'change_linetype' : change_linetype_cb,
    'change_thickness' : change_thickness_cb,
    'modify_textblock_init' : modify_textblock_init_cb,
    'change_textblock_size' : change_textblock_size_cb,
    'change_textblock_style' : change_textblock_style_cb,
    'change_textblock_weight' : change_textblock_weight_cb,
    'change_textblock_alignment' : change_textblock_alignment_cb,
    'change_textblock_family': change_textblock_family_cb,
    'change_textblock_color' : change_textblock_color_cb,
    'modify_dimension_init' : modify_dimension_init_cb,
    'change_dim_endpoint' : change_dim_endpoint_cb,
    'change_dim_endpoint_size' : change_dim_endpoint_size_cb,
    'change_dim_offset' : change_dim_offset_cb,
    'change_dim_extension' : change_dim_extension_cb,
    'change_dim_dual_mode' : change_dim_dual_mode_cb,
    'change_dim_dual_mode_offset' : change_dim_dual_mode_offset_cb,
    'change_dim_thickness' : change_dim_thickness_cb,
    'change_dim_color' : change_dim_color_cb,
    'change_primary_dim' : change_primary_dim_cb,
    'change_secondary_dim' : change_secondary_dim_cb,
    'dimension_menu_init' : dimension_menu_init_cb,
    'dimension_linear' : dimension_linear_cb,
    'dimension_horiz' : dimension_horizontal_cb,
    'dimension_vert' : dimension_vertical_cb,
    'dimension_rad' : dimension_radial_cb,
    'dimension_ang' : dimension_angular_cb,
    # 'dimension_prefs' : dimension_prefs_cb,
    'debug_menu_init' : debug_menu_init_cb,
    'get_focus_widget' : get_focus_widget_cb,
    'undo_stack' : get_undo_stack_cb,
    'redo_stack' : get_redo_stack_cb,
    'image_undo' : get_image_undo_cb,
    'image_redo' : get_image_redo_cb,
    'collect_garbage' : collect_garbage_cb,
    'cb' : cb
    }

#
# create a Menu object for a list-based menu representation
#

def build_menu(menulist, mdict, gtkimage):
    if not isinstance(menulist, list):
        raise TypeError, "Invalid list for build_menu(): " + `menulist`
    _menu = gtk.Menu()
    for _i in range(len(menulist)):
        _name = menulist[_i][0]
        if _name is None:
            _item = gtk.SeparatorMenuItem()
        else:
            _obj = menulist[_i][1]
            if menus.submenus.has_key(_obj):
                _cbkey = menulist[_i][2]
                _mlist = menus.submenus[_obj]
                _item = gtk.MenuItem(_name)
                _submenu = build_menu(_mlist, mdict, gtkimage)
                _item.set_submenu(_submenu)
            else:
                if not callbacks.has_key(_obj):
                    raise KeyError, "Missing callback: %s" % _obj
                _icon, _accel = menulist[_i][2:]
                if _icon is None:
                    _item = gtk.MenuItem(_name)
                else:
                    _item = gtk.ImageMenuItem(_name)
                    if iconmap.has_key(_icon):
                        _image = gtk.image_new_from_stock(iconmap[_icon],
                                                          gtk.ICON_SIZE_MENU)
                        _item.set_image(_image)
                if _accel is not None and accelmap.has_key(_accel):
                    _item.add_accelerator("activate", gtkimage.accel,
                                          accelmap[_accel],
                                          gtk.gdk.CONTROL_MASK,
                                          gtk.ACCEL_VISIBLE)
                #
                # turn off menu items that don't do anything - this
                # is a temporary means of presenting the menus with
                # the items in their positions but make them unselectable
                #
                # as the code to handle them is added, remove them
                # from the test condition ...
                #
                if (_obj == 'cb' or _obj == 'file_save_layer'):
                    _item.set_sensitive(gtk.FALSE)
                else:
                    _item.connect("activate", callbacks[_obj] , gtkimage)
                mdict[id(_item)] = _obj
        _menu.append(_item)
    return _menu

#
# fill a menubar with the menu template info
#

def init_menu(menuitem, gtkimage):
    # print "in init_menu()"
    return gtk.FALSE

def fill_menubar(mb, gtkimage):
    if not isinstance(mb, gtk.MenuBar):
        raise TypeError, "Invalid gtk.MenuBar object: " + `mb`
    _mdict = {}
    for _i in range(len(menus.primary_menus)):
        _name, _key, _cbkey = menus.primary_menus[_i]
        assert menus.submenus.has_key(_key), "Missing primary menu key!"
        if not callbacks.has_key(_cbkey):
            raise ValueError, "Missing menu initializing key: %s" % _cbkey
        _mlist = menus.submenus[_key]
        _item = gtk.MenuItem(_name)
        _item.connect("activate", callbacks[_cbkey], gtkimage)
        _submenu = build_menu(_mlist, _mdict, gtkimage)
        _item.set_submenu(_submenu)
        mb.append(_item)
    return _mdict
