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

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

from PythonCAD.Generic.text import TextStyle

def set_textblock_bounds(gtkimage, tblock):
    # print "set_textblock_bounds() ..."
    _text = tblock.getText()
    if len(_text) == 0:
        tblock.setBounds(0, 0)
        tblock.setFontScale(1/float(pango.SCALE))
        return
    _family = tblock.getFamily()
    _style = tblock.getStyle()
    _weight = tblock.getWeight()
    _size = tblock.getSize()
    _da = gtkimage.getDA()
    _upp = gtkimage.getUnitsPerPixel()
    #
    # initial test layout ...
    #
    _layout = _da.create_pango_layout(_text)
    _fd = pango.FontDescription()
    _fd.set_family(_family)
    _fd.set_style(_style)
    _fd.set_weight(_weight)
    #
    # use an 18-point font as first size guess
    #
    _fs = 18
    _fd.set_size(pango.SCALE * _fs)
    _layout.set_font_description(_fd)
    _nlines = _layout.get_line_count()
    #
    # the pixel height of the TextBlock
    #
    _th = _nlines * _size
    # print "TextBlock height: %g" % _th
    _ph = _th/_upp 
    _iph = int(_ph)
    if _iph/_nlines < 3: # tiny text - use the 18-point values for boundary
        _w, _h = _layout.get_size()
        _tw = (_w * _th)/float(_h)
        tblock.setBounds(_tw, _th)
        tblock.setFontScale(1/float(pango.SCALE))
        return
    # print "TextBlock pixel height: %g [%d]" % (_ph, _iph)
    _w, _h = _layout.get_pixel_size()
    # print "first layout: w: %d; h: %d" % (_w, _h)
    _i = 0
    if _h != _iph:
        #
        # loop until the layout pixel height equals the "correct" pixel height
        #
        _diff = abs(_h - _iph)
        _ofs = _fs
        _fs = (_fs * _ph)/float(_h)
        # print "adjusted font size: %g" % (_fs)
        while True:
            del _layout
            _layout = _da.create_pango_layout(_text)
            _fd = pango.FontDescription()
            _fd.set_family(_family)
            _fd.set_style(_style)
            _fd.set_weight(_weight)
            _fd.set_size(int(pango.SCALE * _fs))
            _layout.set_font_description(_fd)
            _w, _h = _layout.get_pixel_size()
            # print "adjusted layout: w: %d; h: %d" % (_w, _h)
            #
            # tests to bail out
            #
            # all the inexact comparisons and iteration max
            # count text are arbitrary ...
            #
            if _h == _iph:
                # print "exact match"
                break
            if ((_iph > 10) and (abs(_iph - _h) < 2)):
                # print "within 2"
                break
            if ((_iph > 100) and (abs(_iph - _h) < 10)):
                # print "within 10"
                break
            if ((_iph > 1000) and (abs(_iph - _h) < 40)):
                # print "within 40"
                break
            if _i > 25:
                # print "25 iterations"
                break
            #
            # bah, another iteration
            #
            _d = abs(_h - _iph)
            if  _d < _diff:
                _diff = _d
                _ofs = _fs
                _fs = (_fs * _ph)/float(_h)
            else:
                # split the difference in the changed font size
                # and try again, but do not reset the old font
                # size
                _fs = _ofs + ((_fs - _ofs)/2.0)
            # print "adjusted font size: %g" % (_fs)
            _i = _i + 1
    #
    # with the layout sized "correctly", calculate the TextBlock width
    #
    _w, _h = _layout.get_size()
    _tw = (_w * _th)/float(_h)
    # print "TextBlock width: %g" % _tw
    tblock.setBounds(_tw, _th)
    tblock.setFontScale(_fs)
    del _layout
    
def draw_textblock(gtkimage, tblock, active):
    _da = gtkimage.getDA()
    _gc = _da.get_style().fg_gc[gtk.STATE_NORMAL]
    _text = tblock.getText()
    _family = tblock.getFamily()
    _style = tblock.getStyle()
    _weight = tblock.getWeight()
    _color = tblock.getColor()
    _angle = tblock.getAngle()
    if tblock.getBounds() is None:
        set_textblock_bounds(gtkimage, tblock)
    _scale = tblock.getFontScale()
    _layout = make_pango_layout(gtkimage, _text, _family, _style,
                                _weight, _color, _scale)
    _w, _h = _layout.get_pixel_size()
    # print "pixel size: w: %d; h: %d" % (_w, _h)
    _upp = gtkimage.getUnitsPerPixel()
    # print "upp: %g" % _upp
    # print "pw: %g; ph: %g" % ((_w * _upp), (_h * _upp))
    _align = tblock.getAlignment()
    if _align == TextStyle.ALIGN_LEFT:
        _xoff = 0
        _layout.set_alignment(pango.ALIGN_LEFT)
    elif _align == TextStyle.ALIGN_CENTER:
        _xoff = _w//2
        _layout.set_alignment(pango.ALIGN_CENTER)
    elif _align == TextStyle.ALIGN_RIGHT:
        _xoff = _w
        _layout.set_alignment(pango.ALIGN_RIGHT)
    else:
        raise ValueError, "Unexpected alignment value: %d" % _align
    _x, _y = tblock.getLocation()
    _px, _py = gtkimage.coordToPixTransform(_x, _y)
    _px = _px - _xoff
    _pixmap = gtkimage.getPixmap()
    _pixmap.draw_layout(_gc, _px, _py, _layout)
    
def make_pango_layout(gtkimage, text, family, style, weight, color, scale):
    _layout = gtkimage.getDA().create_pango_layout(text)
    _fd = pango.FontDescription()
    _fd.set_family(family)
    if style == TextStyle.FONT_NORMAL:
        _style = pango.STYLE_NORMAL
    elif style == TextStyle.FONT_OBLIQUE:
        _style = pango.STYLE_OBLIQUE
    elif style == TextStyle.FONT_ITALIC:
        _style = pango.STYLE_ITALIC
    else:
        raise ValueError, "Unexpected font style: %d" % style
    _fd.set_style(_style)
    if weight == TextStyle.WEIGHT_NORMAL:
        _weight = pango.WEIGHT_NORMAL
    elif weight == TextStyle.WEIGHT_LIGHT:
        _weight = pango.WEIGHT_LIGHT
    elif weight == TextStyle.WEIGHT_BOLD:
        _weight = pango.WEIGHT_BOLD
    elif weight == TextStyle.WEIGHT_HEAVY:
        _weight = pango.WEIGHT_HEAVY
    else:
        raise ValueError, "Unexpected font weight: %d" % weight
    _fd.set_weight(_weight)
    _sz = pango.SCALE * scale
    if _sz < 1.0:
        _sz = 1.0
    _fd.set_size(int(_sz))
    _attlist = pango.AttrList()
    _r, _g, _b = color.getColors()
    _rs = int(65535.0 * (_r/255.0))
    _gs = int(65535.0 * (_g/255.0))
    _bs = int(65535.0 * (_b/255.0))
    _fg = pango.AttrForeground(_rs, _gs, _bs)
    _fg.start_index = 0
    _fg.end_index = -1
    _attlist.insert(_fg)
    _layout.set_attributes(_attlist)
    _layout.set_font_description(_fd)
    return _layout

def text_button_press(gtkimage, widget, event, tool):
    _x, _y = gtkimage.getPoint()
    _tol = gtkimage.getTolerance()
    _pt, _flag = gtkimage.findPoint(_x, _y, _tol)
    if _pt is not None:
        _x, _y = _pt.getCoords()
    tool.setTextLocation(_x, _y)
    gtkimage.startAction()
    try:
        tool.create(gtkimage)
    finally:
        gtkimage.endAction()
    gtkimage.redraw()
    # tool.clearCurrentPoint()
    
def text_motion_notify(gtkimage, widget, event, tool):
    _tw, _th = tool.getPixelSize()
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _align = tool.getAlignment()
    if _align == TextStyle.ALIGN_LEFT:    
        _xoff = 0
    elif _align == TextStyle.ALIGN_CENTER:
        _xoff = _tw//2
    elif _align == TextStyle.ALIGN_RIGHT:
        _xoff = _tw
    else:
        raise ValueError, "Unexpected alignment value: %d" % _align
    _cp = tool.getCurrentPoint()
    if _cp is not None:
        _xc, _yc = _cp
        _xc = _xc - _xoff
        widget.window.draw_rectangle(_gc, gtk.FALSE, _xc, _yc, _tw, _th)
    tool.setCurrentPoint(_x, _y)
    _x = _x - _xoff
    widget.window.draw_rectangle(_gc, gtk.FALSE, _x, _y, _tw, _th)

def text_add_init(gtkimage, tool):
    _text = tool.getText()
    _family = gtkimage.getOption('FONT_FAMILY')
    _weight = gtkimage.getOption('FONT_WEIGHT')
    _style = gtkimage.getOption('FONT_STYLE')
    _color = gtkimage.getOption('FONT_COLOR')
    _size = gtkimage.getOption('TEXT_SIZE')
    _align = gtkimage.getOption('TEXT_ALIGNMENT')
    _angle = gtkimage.getOption('TEXT_ANGLE')
    _layout = make_pango_layout(gtkimage, _text, _family, _style, _weight,
                                _color, _size)
    tool.setLayout(_layout)
    tool.setAlignment(_align)
    tool.setAngle(_angle)
    _lw, _lh = _layout.get_pixel_size()
    tool.setPixelSize(_lw, _lh)
    _upp = gtkimage.getUnitsPerPixel()
    #
    # the width and height calculations can be somewhat inaccurate
    # as the unitsPerPixel value gets large
    #
    _w = _lw * _upp
    _h = _lh * _upp
    tool.setBounds(_w, _h)
    tool.setHandler("motion_notify", text_motion_notify)
    tool.setHandler("button_press", text_button_press)
    gtkimage.setPrompt("Click where to place the text")
    _gc = gtkimage.getGC()
    _gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
                            gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
    _gc.set_function(gtk.gdk.INVERT)
    
    
def make_text_dialog(gtkimage, text=None):
    _window = gtkimage.getWindow()
    _dialog = gtk.Dialog("Enter text", _window,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         (gtk.STOCK_OK, gtk.RESPONSE_OK,
                          gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
    _tb = gtk.TextBuffer()
    _tv = gtk.TextView(_tb)
    _sw = gtk.ScrolledWindow()
    _sw.set_size_request(400, 300)
    _sw.add_with_viewport(_tv)
    _dialog.vbox.pack_start(_sw, gtk.FALSE, gtk.FALSE, 0)
    _dialog.show_all()
    return _dialog, _tb

def get_new_text(gtkimage):
    _tool = gtkimage.getTool()
    _dialog, _tb = make_text_dialog(gtkimage)
    _response = _dialog.run()
    if _response == gtk.RESPONSE_OK:
        _start_iter, _end_iter = _tb.get_bounds()
        _text = _tb.get_text(_start_iter, _end_iter)
        if len(_text):
            _tool.setText(_text)
    _dialog.destroy()

