#
# 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
#
#
# the GTK code for displaying a drawing
#

from __future__ import division

import math
import types

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

from PythonCAD.Interface.Gtk import gtkdimension
from PythonCAD.Interface.Gtk import gtklayers
from PythonCAD.Interface.Gtk import gtktext
from PythonCAD.Interface.Gtk import gtkactions

from PythonCAD.Generic.image import Image
from PythonCAD.Generic.point import Point
from PythonCAD.Generic.conobject import ConstructionObject
from PythonCAD.Generic.color import Color
from PythonCAD.Generic.layer import Layer
from PythonCAD.Generic.tools import Tool
from PythonCAD.Generic import globals
from PythonCAD.Generic import keywords
from PythonCAD.Generic import prompt

from PythonCAD.Interface.Gtk import gtkshell

#
# Global variables
#

_debug = False ##  SDB debug stuff
globals.gtkcolors = {}
globals.gtklinetypes = {}

#------------------------------------------------------------------
def da_expose_event(widget, event, gtkimage):
    _pixmap = gtkimage.getPixmap()
    _x, _y, _width, _height = event.area
    _gc = widget.get_style().fg_gc[gtk.STATE_NORMAL]
    # _gc.set_function(gtk.gdk.COPY)
    widget.window.draw_drawable(_gc, _pixmap, _x, _y, _x, _y, _width, _height)
    return True

#------------------------------------------------------------------
def da_realize_event(widget, gtkimage):
    _win = widget.window
    _width, _height = _win.get_size()
    gtkimage.setSize(_width, _height)
    widget.set_size_request(100,100)
    _gc = _win.new_gc()
    _gc.set_exposures(True)
    gtkimage.setGC(_gc)

#------------------------------------------------------------------
def da_configure_event(widget, event, gtkimage):
    _win = widget.window
    _width, _height = _win.get_size()
    _disp_width, _disp_height = gtkimage.getSize()
    if _disp_width != _width or _disp_height != _height:
        gtkimage.setSize(_width, _height)
        _pixmap = gtk.gdk.Pixmap(_win, _width, _height)
        _gc = widget.get_style().fg_gc[gtk.STATE_NORMAL]
        _pixmap.draw_rectangle(_gc, True, 0, 0, _width, _height)
        gtkimage.setPixmap(_pixmap)
        _xmin, _ymin = gtkimage.getView()[:2]
        if _xmin is None or _ymin is None:
            _xmin = 1.0
            _ymin = 1.0
        _upp = gtkimage.getUnitsPerPixel()
        gtkimage.setView(_xmin, _ymin, _upp)
        gtkimage.redraw()
    return True

#------------------------------------------------------------------

def window_key_press_event(widget, event, gtkimage):
    # print "window_key_press_event()"
    _entry = gtkimage.getEntry()
    if _entry.is_focus():
        return False
    _tool = gtkimage.getTool()
    if _tool is not None and _tool.hasHandler('entry_event'):
        _entry.grab_focus()
        return _entry.event(event)
    return False

def window_general_event(widget, event, gtkimage):
    _type = event.type
    debug_print("window_general_event: Event type: %d" % _type)
    debug_print("widget: " + `widget`)
    if _type == gtk.gdk.BUTTON_PRESS:
        _button = event.button
        debug_print("BUTTON_PRESS: %d" % _button)
    elif _type == gtk.gdk.BUTTON_RELEASE:
        _button = event.button
        debug_print("BUTTON_RELEASE: %d" % _button)
    elif _type == gtk.gdk.KEY_PRESS:
        debug_print("KEY_PRESS")
        if event.keyval == gtk.keysyms.Escape:
            debug_print("Got escape key")
            gtkimage.reset()
            return True
    elif _type == gtk.gdk.KEY_RELEASE:
        debug_print("KEY_RELEASE")
    else:
        pass
    return False

#------------------------------------------------------------------
def da_general_event(widget, event, gtkimage):
    _rv = False
    _type = event.type
    # debug_print("da_general_event: Event type: %d" % _type)
    _tool = gtkimage.getTool()
    if _type == gtk.gdk.BUTTON_PRESS:
        gtkimage.setToolpoint(event)
        button = event.button
        if button == 1:
            if _tool is not None and _tool.hasHandler("button_press"):
                _rv = _tool.getHandler("button_press")(gtkimage, widget,
                                                       event, _tool)
    elif _type == gtk.gdk.BUTTON_RELEASE:
        gtkimage.setToolpoint(event)
        button = event.button
        if button == 1:
            if _tool is not None and _tool.hasHandler("button_release"):
                _rv =_tool.getHandler("button_release")(gtkimage, widget,
                                                        event, _tool)
    elif _type == gtk.gdk.MOTION_NOTIFY:
        gtkimage.setToolpoint(event)
        if _tool is not None and _tool.hasHandler("motion_notify"):
            _rv = _tool.getHandler('motion_notify')(gtkimage, widget,
                                                    event, _tool)
    elif _type == gtk.gdk.KEY_PRESS:
        debug_print("In da_general_event, got key press!")
        _key = event.keyval
        if (_key == gtk.keysyms.Page_Up or
            _key == gtk.keysyms.Page_Down or
            _key == gtk.keysyms.Left or
            _key == gtk.keysyms.Right or
            _key == gtk.keysyms.Up or
            _key == gtk.keysyms.Down):
            debug_print("Got Arrow/PageUp/PageDown key")
            pass # handle moving the drawing in some fashion ...
        elif _key == gtk.keysyms.Escape:
            debug_print("Got escape key")
            gtkimage.reset()
            _rv = True
        elif _tool is not None and _tool.hasHandler("key_press"):
            _rv = _tool.getHandler("key_press")(gtkimage, widget,
                                                   event, _tool)
        else:
            _entry = gtkimage.getEntry()
            _entry.grab_focus()
            if _key == gtk.keysyms.Tab:
                _rv = True
            else:
                _rv = _entry.event(event)
    elif _type == gtk.gdk.ENTER_NOTIFY:
        gtkimage.setToolpoint(event)
        _rv = True
    elif _type == gtk.gdk.LEAVE_NOTIFY:
        gtkimage.setToolpoint(event)
        _rv = True
    else:
        # print "Got type %d" % _type
        pass
    return _rv

#------------------------------------------------------------------
def da_focus_in_event_cb(widget, event, gtkimage):
    debug_print("in da_focus_in_event_cb() ...")
    return False

#------------------------------------------------------------------
def da_focus_out_event_cb(widget, event, gtkimage):
    debug_print("in da_focus_out_event_cb() ...")
    return False

#------------------------------------------------------------------
def entry_event(widget, gtkimage):
    #
    # The error handling in this function needs work, and probably
    # a rethink as how the commands are implemented is in order. Perhaps
    # the command set should be stored in the image's global dictionary?
    #
    debug_print("Entered entry_event")
    _entry = gtkimage.getEntry()
    _text = _entry.get_text().strip()
    if len(_text):
        _text = _text.lower()
        if _text == 'end' or _text == 'stop':
            _entry.delete_text(0,-1)
            gtkimage.reset()
        else:
            _tool = gtkimage.getTool()
            if _tool is not None and _tool.hasHandler("entry_event"):
                handler = _tool.getHandler("entry_event")
                try:
                    handler(gtkimage, widget, _tool)
                except StandardError, e:
                    print "exception called: " + str(e)
            else:
                _cmds = keywords.defaultglobals
                _entry.delete_text(0,-1)
                # print "text is '%s'" % _text
                if _text in _cmds:
                    # print "valid command"
                    _opt = _cmds[_text]
                    # print "opt: '%s'" % _opt
                    _cmd = prompt.lookup(_opt)
                    # print "cmd: '%s'" % _cmd
                    eval(_cmd) # this will go away ...
                else:
                    # print "Calling exec for '%s'" % _text
                    # print "Command Error; See http://www.pythoncad.org/commands.html for reference page."
                    try:
                        exec _text in gtkimage.getImageVariables()
                    except:
                        print "error executing '%s' " % _text
    #
    # set the focus back to the DisplayArea widget
    #
    gtkimage.getDA().grab_focus()
    return False

#------------------------------------------------------------------
def close_image(widget, gtkimage):
    """Close a window containing a GTKImage.

close_image(gtkimage)
    """
    gtkimage.close()
    for _i in xrange(len(globals.imagelist)):
        _gimage = globals.imagelist[_i]
        if gtkimage is _gimage:
            del globals.imagelist[_i]
            _gimage.window.destroy()
            if not len(globals.imagelist):
                gtk.main_quit()
            break
    return False

def _test_focus(widget):
    if isinstance(widget, gtk.Container):
        print "container: " + `widget`
        widget.foreach(_test_focus)
    else:
        print "widget: " + `widget`
        if (widget.flags() & gtk.CAN_FOCUS):
            print "widget can accept focus"
            if widget.is_focus():
                print "is focus widget"
            else:
                print "not focus widget"
        else:
            print "widget can not accept focus"

#------------------------------------------------------------------
class GTKImage(Image):
    """The GTK wrapping around an Image

The GTKImage class is derived from the Image class, so it shares all
the attributes and methods of that class. The GTKImage class has the
following addtional methods:

close(): Close a GTKImage.
getWindow(): Get the GTK Window used in the GTKImage.
getEntry(): Get the GTK Entry used in the GTKImage.
getDA(): Get the GTK Drawing Area used in the GTKImage.
{get/set}Pixmap(): Get/Set the GTK Pixmap used in the GTKImage.
{get/set}Prompt(): Get/Set the prompt.
{get/set}Tool(): Get/Set the tool used for working in the GTKImage.
{get/set}UnitsPerPixel(): Get/Set this display parameter.
{get/set}View(): Get/Set the current view seen in the GTKImage.
getTolerance(): Get the current drawing tolerance.
{get/set}GC(): Get/Set the graphic context used in the GTKImage.
{get/set}Point(): Get/Set the current coordinates of the tool.
{get/set}Size(): Get/Set the size of the drawing area.
pixToCoordTransform(): Convert pixels to x/y coordinates.
coordToPixTransform(): Convert x/y coordinates to pixels.
refresh(): Redraw the screen using the current pixmap.
redraw(): Recalculate the visible entities and redraw the screen.
addGroup(): Add a new ActionGroup to the GTKImage.
getGroup(): Retrieve an ActionGroup from the GTKImage.
deleteGroup(): Remove an ActionGroup from the GTKImage.
    """
    def __init__(self):
        debug_print("Initialized another GTKImage class instance...")
        super(GTKImage, self).__init__()
        self.__window = gtk.Window()
        self.__window.set_title("Untitled")
        self.__window.connect("destroy", close_image, self)
        self.__window.connect("event", window_general_event, self)
        self.__window.connect("key_press_event", window_key_press_event, self)
        _width = min(1024, int(0.8 * float(gtk.gdk.screen_width())))
        _height = min(768, int(0.8 * float(gtk.gdk.screen_height())))
        self.__window.set_default_size(_width, _height)

        main_vbox = gtk.VBox(False, 2)
        main_vbox.set_border_width(2)
        self.__window.add(main_vbox)

        #
        # accelerators
        #

        self.__accel = gtk.AccelGroup()
        self.__window.add_accel_group(self.__accel)

        #
        # menu bar
        #

        self.__mb = gtk.MenuBar()
        main_vbox.pack_start(self.__mb, False, False)

        #
        # action group dictionary
        #

        self.__groups = {}

        #
        # fixme - try to rework code to avoid this import ...
        #
        from PythonCAD.Interface.Gtk.gtkmenus import fill_menubar
        fill_menubar(self.__mb, self)

        #
        # drawing window has Horizontal Pane:
        # left side: stuff for layer display
        # right side: drawing area
        #

        pane = gtk.HPaned()
        main_vbox.pack_start(pane)

        frame1 = gtk.Frame()
        pane.pack1(frame1, True, False)
        pane.set_position(100)

        #
        # layer display stuff
        #

        _ld = gtkshell.LayerDisplay(self)
        frame1.add(_ld.getWindow())
        self.__layerdisplay = _ld

        #
        # drawing area
        #

        self.__disp_width = None
        self.__disp_height = None
        self.__units_per_pixel = 1.0
        self.__da = gtk.DrawingArea()

        black = gtk.gdk.color_parse('black')
        self.__da.modify_fg(gtk.STATE_NORMAL, black)
        self.__da.modify_bg(gtk.STATE_NORMAL, black)
        pane.pack2(self.__da, True, False)
        self.__da.set_flags(gtk.CAN_FOCUS)

        self.__da.connect("event", da_general_event, self)
        self.__da.connect("expose_event", da_expose_event, self)
        self.__da.connect("realize", da_realize_event, self)
        self.__da.connect("configure_event", da_configure_event, self)
        # self.__da.connect("focus_in_event", da_focus_in_event_cb, self)
        # self.__da.connect("focus_out_event", da_focus_out_event_cb, self)

        self.__da.set_events(gtk.gdk.EXPOSURE_MASK |
                             gtk.gdk.LEAVE_NOTIFY_MASK |
                             gtk.gdk.BUTTON_PRESS_MASK |
                             gtk.gdk.BUTTON_RELEASE_MASK |
                             gtk.gdk.ENTER_NOTIFY_MASK|
                             gtk.gdk.LEAVE_NOTIFY_MASK|
                             gtk.gdk.KEY_PRESS_MASK |
                             gtk.gdk.KEY_RELEASE_MASK |
                             gtk.gdk.FOCUS_CHANGE_MASK |
                             gtk.gdk.POINTER_MOTION_MASK)

        lower_hbox = gtk.HBox(False, 2)
        main_vbox.pack_start(lower_hbox, False, False)

        self.__prompt = gtk.Label("Enter Command:")
        lower_hbox.pack_start(self.__prompt, False, False)

        #
        # where the action is taking place
        #

        self.__coords = gtk.Label('(0,0)')
        lower_hbox.pack_end(self.__coords, False, False)

        super(GTKImage, self).setCurrentPoint(0.0, 0.0)

        #
        # command entry area
        #

        self.__entry = gtk.Entry()
        main_vbox.pack_start(self.__entry, False, False)
        self.__entry.connect("activate", entry_event, self)

        #
        # the Pixmap and GraphicContext for the drawing
        #

        self.__pixmap = None
        self.__gc = None

        #
        # our tool used to draw stuff
        #

        self.__tool = None
        self.__oldtool = Tool()

        #
        # the viewable region and tolerance in the drawing
        #

        self.__xmin = None
        self.__ymin = None
        self.__xmax = None
        self.__ymax = None
        self.__tolerance = 1e-10

        #

        self.connect('selected_object', self._selectedObject)
        self.connect('deselected_object', self._deselectedObject)
        
        #
        # set the background color
        #

        # self.setOption('BACKGROUND_COLOR', Color(0,0,0))

        # _test_focus(self.__window)

    #------------------------------------------------------------------
    def close(self):
        """Release the entites stored in the drawing.

close()
        """
        super(GTKImage, self).close()
        self.__window.destroy()

    #------------------------------------------------------------------
    def _selectedObject(self, img, *args):
        _alen = len(args)
        if _alen < 1:
            raise ValueError, "Invalid argument count: %d" % _alen
        _obj = args[0]
        if _debug:
            print "Selected object: " + `_obj`
        _parent = _obj.getParent()
        if _parent.isVisible() and _obj.isVisible():
            # print "Redrawing ..."
            # _obj.draw(self, self.getOption('BACKGROUND_COLOR'))
            # self.__da.queue_draw()
            _color = Color('#ff7733')
            _obj.draw(self, _color)
            self.__da.queue_draw()

    #------------------------------------------------------------------
    def _deselectedObject(self, img, *args):
        _alen = len(args)
        if _alen < 1:
            raise ValueError, "Invalid argument count: %d" % _alen
        _obj = args[0]
        if _debug:
            print "Deselected object: " + `_obj`
        _parent = _obj.getParent()
        if _parent.isVisible() and _obj.isVisible():
            # print "Redrawing ..."
            # _obj.draw(self, self.getOption('BACKGROUND_COLOR'))
            # self.__da.queue_draw()
            _obj.draw(self)
            self.__da.queue_draw()
        
    #------------------------------------------------------------------
    def getAccel(self):
        """Return the gtk.AccelGroup in the GTKImage.

getAccel()
        """
        return self.__accel

    accel = property(getAccel, None, None, "Accel group in the GTKImage.")

    #------------------------------------------------------------------
    def getWindow(self):
        """Return a handle to the gtk.Window in the GTKImage.

getWindow()
        """
        return self.__window

    window = property(getWindow, None, None, "Main GTK Window for a GTKImage.")

    #------------------------------------------------------------------
    def getEntry(self):
        """Return a handle to the gtk.Entry in the GTKImage.

getEntry()
        """
        return self.__entry

    entry = property(getEntry, None, None, "Entry box for a GTKImage.")

    #------------------------------------------------------------------
    def getDA(self):
        """Return the gtk.DrawingArea in the GTKImage.

getDA()
        """
        return self.__da

    da = property(getDA, None, None, "DrawingArea for a GTKImage.")

    #------------------------------------------------------------------
    def getPixmap(self):
        """Return the Pixmap for the GTKImage.

getPixmap()
        """
        return self.__pixmap

    #------------------------------------------------------------------
    def setPixmap(self, pixmap):
        """Set the Pixmap for the GTKImage.

setPixmap(pixmap)
        """
        self.__pixmap = pixmap

    pixmap = property(getPixmap, setPixmap, None, "Pixmap for a GTKImage.")

    #------------------------------------------------------------------
    def getPrompt(self):
        """Return the current prompt string.

getPrompt()
        """
        return self.__prompt.get_label()

    #------------------------------------------------------------------
    def setPrompt(self, p):
        """Set the current prompt string.

setPrompt(p)
        """
        if not isinstance(p, types.StringTypes):
            raise TypeError, "Invalid prompt type: " + `type(p)`
        self.__prompt.set_text(p)

    prompt = property(getPrompt, setPrompt, None, "Prompt string.")

    #------------------------------------------------------------------
    def setTool(self, tool):
        """Replace the tool in the image with a new Tool.

setTool(tool)

The argument 'tool' should be an instance of a Tool object.
        """
        if not isinstance(tool, Tool):
            raise TypeError, "Invalid tool: " + str(tool)
        self.__tool = tool

    #------------------------------------------------------------------
    def getTool(self):
        """Return the current Tool used in the drawing.

getTool()
        """
        return self.__tool

    tool = property(getTool, None, None, "Tool for adding/modifying entities.")

    #------------------------------------------------------------------
    def setOption(self, key, value):
        """

setOption(key, value)

The 'key' must be a string, and 'value' can be anything.
Using the same key twice will result on the second value
overwriting the first.

This method extends the Image::setOption() method and
handles options that are related to the graphical appearance
of the image.
        """
        super(GTKImage, self).setOption(key, value)
        if key == 'BACKGROUND_COLOR':
            _col = gtk.gdk.color_parse(str(value))
            self.__da.modify_fg(gtk.STATE_NORMAL, _col)
            self.__da.modify_bg(gtk.STATE_NORMAL, _col)
            self.redraw()

    #------------------------------------------------------------------
    def getUnitsPerPixel(self):
        """Return the current value of units/pixel.

getUnitsPerPixel()
        """
        return self.__units_per_pixel

    #------------------------------------------------------------------
    def setUnitsPerPixel(self, upp):
        """Set the current value of units/pixel.

setUnitsPerPixel(upp)

The argument 'upp' should be a positive float value.
        """
        _upp = upp
        if not isinstance(_upp, float):
            _upp = float(upp)
        if _upp < 1e-10:
            raise ValueError, "Invalid scale value: %g" % _upp
        self.__units_per_pixel = _upp
        self.__tolerance = _upp * 5.0

    #------------------------------------------------------------------
    def setView(self, xmin, ymin, scale=None):
        """Set the current visible area in a drawing.

setView(xmin, ymin[, scale])

xmin: Minimum visible x-coordinate
ymin: Minimum visible y-coordinate

The optional argument 'scale' defaults to the current
value of units/pixel (set with getUnitsPerPixel() method.)
This value must be a positive float.
        """
        _xmin = xmin
        if not isinstance(_xmin, float):
            _xmin = float(xmin)
        _ymin = ymin
        if not isinstance(_ymin, float):
            _ymin = float(ymin)
        _scale = scale
        if _scale is None:
            _scale = self.__units_per_pixel
        if not isinstance(_scale, float):
            _scale = float(scale)
        if _scale < 1e-10:
            raise ValueError, "Invalid scale value: %g" % _scale
        _xmax = _xmin + (_scale * self.__disp_width)
        _ymax = _ymin + (_scale * self.__disp_height)
        _recalc = False
        if abs(_scale - self.__units_per_pixel) > 1e-10:
            self.__units_per_pixel = _scale
            _recalc = True
        self.__tolerance = self.__units_per_pixel * 5.0
        self.__xmin = _xmin
        self.__ymin = _ymin
        self.__xmax = _xmax
        self.__ymax = _ymax
        if _recalc:
            self.calcTextWidths()

    #------------------------------------------------------------------
    def calcTextWidths(self):
        """Calculate the width of the text strings in the Image.

calcTextWidths()  
        """
        _layers = [self.getTopLayer()]
        while len(_layers):
            _layer = _layers.pop()
            for _tblock in _layer.getLayerEntities('text'):
                gtktext.set_textblock_bounds(self, _tblock)
            for _dim in _layer.getLayerEntities('linear_dimension'):
                _ds1, _ds2 = _dim.getDimstrings()
                gtktext.set_textblock_bounds(self, _ds1)
                if _dim.getDualDimMode():
                    gtktext.set_textblock_bounds(self, _ds2)
            for _dim in _layer.getLayerEntities('horizontal_dimension'):
                _ds1, _ds2 = _dim.getDimstrings()
                gtktext.set_textblock_bounds(self, _ds1)
                if _dim.getDualDimMode():
                    gtktext.set_textblock_bounds(self, _ds2)
            for _dim in _layer.getLayerEntities('vertical_dimension'):
                _ds1, _ds2 = _dim.getDimstrings()
                gtktext.set_textblock_bounds(self, _ds1)
                if _dim.getDualDimMode():
                    gtktext.set_textblock_bounds(self, _ds2)
            for _dim in _layer.getLayerEntities('radial_dimension'):
                _ds1, _ds2 = _dim.getDimstrings()
                gtktext.set_textblock_bounds(self, _ds1)
                if _dim.getDualDimMode():
                    gtktext.set_textblock_bounds(self, _ds2)
            for _dim in _layer.getLayerEntities('angular_dimension'):
                _ds1, _ds2 = _dim.getDimstrings()
                gtktext.set_textblock_bounds(self, _ds1)
                if _dim.getDualDimMode():
                    gtktext.set_textblock_bounds(self, _ds2)
            _layers.extend(_layer.getSublayers())

    #------------------------------------------------------------------        
    def getView(self):
        """Return the current visible area in a drawing.

getView()

This method returns a tuple with four float values:

(xmin, ymin, xmax, ymax)

If the view has never been set, each of these values
will be None.
        """
        return (self.__xmin, self.__ymin, self.__xmax, self.__ymax)

    view = property(getView, setView, None, "The visible area in a drawing.")

    #------------------------------------------------------------------
    def setActiveLayer(self, l=None):
        """Set the active Layer in the Image.

setActiveLayer(l)

This method extends the image::setActiveLayer() method.
        """
        try:
            super(GTKImage, self).setActiveLayer(l)
            self.redraw()
        except RuntimeError, _e: # need better error handling
            print "Error during GTKImage::setActiveLayer(): %s" % _e

    #------------------------------------------------------------------
    def addLayer(self, l):
        """Add a new Layer as a Child of the active layer's parent.

addLayer(l)

This method extends the image::addLayer() method.
        """
        try:
            super(GTKImage, self).addLayer(l)
        except RuntimeError, _e: # need better error handling
            print "Error during GTKImage::addLayer(): %s" % _e

    #------------------------------------------------------------------
    def addChildLayer(self, l, p=None):
        """Add a new Layer as a child to another layer.

addChildLayer(l [, p])

There is a one required argument:

l: The child Layer

There is one optional argument:

p: The new parent Layer of the child

The default parent is the currently active layer.

This method extends the image::addChildLayer() method.
        """
        try:
            super(GTKImage, self).addChildLayer(l, p)
        except RuntimeError, _e: # need better error handling
            print "Error during GTKImage::addChildLayer(): %s" % _e

    #------------------------------------------------------------------
    def delLayer(self, l):
        """Remove a Layer from the drawing.

delLayer(l)

This method extends the image::delLayer() method.
        """
        try:
            super(GTKImage, self).delLayer(l)
        except RuntimeError, _e: # need better error handling
            print "Error during GTKImage::delLayer(): %s" % _e

    #------------------------------------------------------------------
    def addObject(self, obj, l=None):
        """Add an object to the Drawing.

addObject(obj [, l])

This method extends the image::addObject() method.
        """
        try:
            super(GTKImage, self).addObject(obj, l)
        except StandardError, e:
            print "Failed to add object: " + `obj` + ": " + str(e)
        #
        # it may be good to call redraw() here instead of
        # the multitude of places where it is called now ...
        #
        # self.redraw()

    #------------------------------------------------------------------
    def delObject(self, obj):
        """Remove an object from the Drawing.

delObject(obj)

This method extends the image::delObject() method.
        """
        try:
            super(GTKImage, self).delObject(obj)
        except StandardError, e:
            print "Failed to delete object: " + `obj` + str(e)

    #------------------------------------------------------------------
    def getTolerance(self):
        """Return the current drawing tolerance.

getTolerance()
        """
        return self.__tolerance

    tolerance = property(getTolerance, None, None, "Drawing tolerance.")

    def getGC(self):
        """Return the GraphicContext allocated for the GTKImage.

getGC()
        """
        return self.__gc

    #------------------------------------------------------------------
    def setGC(self, gc):
        """Set the GraphicContext for the GTKImage.

setGC(gc)
        """
        if not isinstance(gc, gtk.gdk.GC):
            raise TypeError, "Invalid GC object: " + `gc`
        if self.__gc is None:
            self.__gc = gc

    gc = property(getGC, None, None, "GraphicContext for the GTKImage.")

    #------------------------------------------------------------------
    def getSize(self):
        """Return the size of the DrawingArea window.

getSize()
        """
        return (self.__disp_width, self.__disp_height)

    #------------------------------------------------------------------
    def setSize(self, width, height):
        """Set the size of the DrawingArea window.

setSize(width, height)
        """
        _width = width
        if not isinstance(_width, int):
            _width = int(width)
        if _width < 0:
            raise ValueError, "Invalid drawing area width: %d" % _width
        _height = height
        if not isinstance(_height, int):
            _height = int(height)
        if _height < 0:
            raise ValueError, "Invalid drawing area height: %d" % _height
        self.__disp_width = _width
        self.__disp_height = _height

    #------------------------------------------------------------------
    def setCurrentPoint(self, x, y):
        super(GTKImage, self).setCurrentPoint(x, y)
        self.__coords.set_text("%.4f, %.4f" % (x, y))

    #------------------------------------------------------------------        
    def setToolpoint(self, event):
        _x = event.x
        _y = event.y
        _tx, _ty = self.pixToCoordTransform(_x, _y)
        self.setCurrentPoint(_tx, _ty)

    #------------------------------------------------------------------
    def addGroup(self, group):
        """Add an ActionGroup to the GTKImage.

addGroup(group)

Argument 'group' must be either an instance of either
gtk.Action gtk.stdAction.
        """
        if not isinstance(group, gtk.ActionGroup):
            if not isinstance(gtkactions.stdActionGroup):
                raise TypeError, "Invalid group type: " + `type(group)`
        self.__groups[group.get_name()] = group

    #------------------------------------------------------------------
    def getGroup(self, name):
        """Return an ActionGroup stored in the GTKImage.

getGroup(name)

Argument 'name' should be the name of the ActionGroup. This method
will return None if no group by that name is stored.
        """
        return self.__groups.get(name)

    #------------------------------------------------------------------
    def deleteGroup(self, name):
        """Remove an ActionGroup stored in the GTKImage.

deleteGroup(name)

Argument 'name' should be the name of the ActionGroup to be removed.
        """
        if name in self.__groups:
            del self.__groups[name]

    #------------------------------------------------------------------
    def pixToCoordTransform(self, xp, yp):
        """Convert from pixel coordinates to x-y coordinates.

pixToCoordTransform(xp, yp)

The function arguments are:

xp: pixel x value
yp: pixel y value

The function returns a tuple holding two float values
        """
        _upp = self.__units_per_pixel
        _xc = self.__xmin + (xp * _upp)
        _yc = self.__ymax - (yp * _upp)
        return (_xc, _yc)
            
    #------------------------------------------------------------------
    def coordToPixTransform(self, xc, yc):
        """Convert from x-y coordinates to pixel coordinates

coordToPixTransform(xc, yc)

The function arguments are:

xc: x coordinate
yp: y coordinate

The function returns a tuple holding two integer values
        """
        _upp = self.__units_per_pixel
        _xp = int((xc - self.__xmin)/_upp)
        _yp = int((self.__ymax - yc)/_upp)
        return _xp, _yp

    #------------------------------------------------------------------
    def getColor(self, c):
        """Return an allocated color for a given Color object.

getColor(c)

Argument 'c' must be a Color object. This method will return

        """
        if not isinstance(c, Color):
            raise TypeError, "Invalid Color object: " + `type(c)`
        _color = globals.gtkcolors.get(c)
        if _color is None:
            # _r = int(round(65535.0 * (c.r/255.0)))
            # _g = int(round(65535.0 * (c.g/255.0)))
            # _b = int(round(65535.0 * (c.b/255.0)))
            # _color = self.__da.get_colormap().alloc_color(_r, _g, _b)
            _color = self.__da.get_colormap().alloc_color(str(c))
            globals.gtkcolors[c] = _color
        return _color

    #------------------------------------------------------------------
    def fitImage(self):
        """Redraw the image so all entities are visible in the window.

fitImage()
        """
        _fw = float(self.__disp_width)
        _fh = float(self.__disp_height)
        _xmin, _ymin, _xmax, _ymax = self.getExtents()
        _xdiff = abs(_xmax - _xmin)
        _ydiff = abs(_ymax - _ymin)
        _xmid = (_xmin + _xmax)/2.0
        _ymid = (_ymin + _ymax)/2.0
        _xs = _xdiff/_fw
        _ys = _ydiff/_fh
        if _xs > _ys:
            _scale = _xs * 1.05 # make it a little larger
        else:
            _scale = _ys * 1.05 # make it a little larger
        _xm = _xmid - (_fw/2.0) * _scale
        _ym = _ymid - (_fh/2.0) * _scale
        self.setView(_xm, _ym, _scale)
        self.redraw()

    #------------------------------------------------------------------
    def refresh(self):
        """This method does a screen refresh.

refresh()

If entities in the drawing have been added, removed, or
modified, use the redraw() method.
        """
        _da = self.__da
        if (_da.flags() & gtk.MAPPED):
            _gc = _da.get_style().fg_gc[gtk.STATE_NORMAL]
            _gc.set_function(gtk.gdk.COPY)
            _da.queue_draw()

    #------------------------------------------------------------------
    def redraw(self):
        """This method draws all the objects visible in the window.

redraw()
        """
        _da = self.__da
        if (_da.flags() & gtk.MAPPED):
            if _debug:
                print "Redrawing image"
            _xmin = self.__xmin
            _ymin = self.__ymin
            _xmax = self.__xmax
            _ymax = self.__ymax
            _gc = _da.get_style().fg_gc[gtk.STATE_NORMAL]
            self.__pixmap.draw_rectangle(_gc, True, 0, 0,
                                         self.__disp_width, self.__disp_height)
            _active_layer = self.getActiveLayer()
            _layers = [self.getTopLayer()]
            while (len(_layers)):
                _layer = _layers.pop()
                if _layer is not _active_layer:
                    self.drawLayer(_layer)
                _layers.extend(_layer.getSublayers())
            self.drawLayer(_active_layer)
            #
            # redraw selected entities
            #
            _color = Color('#ff7733')
            for _obj in self.getSelectedObjects(False):
                _obj.draw(self, _color)
            _da.queue_draw() # generate an expose event

    #------------------------------------------------------------------
    def drawLayer(self, l):
        if not isinstance(l, Layer):
            raise TypeError, "Invalid layer type: " + `type(l)`
        if l.getParent() is not self:
            raise ValueError, "Layer not found in Image"
        if l.isVisible():
            _col = self.getOption('INACTIVE_LAYER_COLOR')
            if l is self.getActiveLayer():
                _col = None
            _cobjs = []
            _objs = []
            _pts = []
            for _obj in l.objsInRegion(self.__xmin, self.__ymin, self.__xmax, self.__ymax):
                if _obj.isVisible():
                    if isinstance(_obj, Point):
                        _pts.append(_obj)
                    elif isinstance(_obj, ConstructionObject):
                        _cobjs.append(_obj)
                    else:
                        _objs.append(_obj)
            for _obj in _cobjs:
                _obj.draw(self, _col)
            for _obj in _pts:
                _obj.draw(self, _col)
            for _obj in _objs:
                _obj.draw(self, _col)

    #------------------------------------------------------------------
    def reset(self):
        """Set the image to an initial drawing state.

reset()
        """
        if self.__tool is None:
            #
            # If __tool is none, deselect any selected objects in view.
            # This way, if you are currently using a tool, then the
            # first time you hit escape, you just clear the tool.
            # The second time clears all selections.
            debug_print("Entered reset")
            if self.__tool is None:
                debug_print(".....This is second time to be in reset")
                self.clearSelectedObjects()
        self.__tool = None
        self.redraw()
        self.setPrompt("Enter command")

#------------------------------------------------------------------
def debug_print(string):
    if _debug is True:
        print "SDB Debug: " + string
