2 # Copyright (c) 2003, 2004, 2006, 2007 Art Haas
4 # This file is part of PythonCAD.
6 # PythonCAD is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # PythonCAD is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with PythonCAD; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 # GTK interface code for dealing with text entities
29 from PythonCAD
.Generic
.text
import TextStyle
, TextBlock
31 def set_textblock_bounds(gtkimage
, tblock
):
32 # print "set_textblock_bounds() ..."
33 _text
= tblock
.getText()
35 tblock
.setBounds(0, 0)
36 tblock
.setFontScale(1/float(pango
.SCALE
))
38 _family
= tblock
.getFamily()
39 _style
= tblock
.getStyle()
40 _weight
= tblock
.getWeight()
41 _size
= tblock
.getSize()
42 _da
= gtkimage
.getDA()
43 _upp
= gtkimage
.getUnitsPerPixel()
45 # initial test layout ...
47 _layout
= _da
.create_pango_layout(_text
)
48 _fd
= pango
.FontDescription()
49 _fd
.set_family(_family
)
51 _fd
.set_weight(_weight
)
53 # use an 18-point font as first size guess
56 _fd
.set_size(pango
.SCALE
* _fs
)
57 _layout
.set_font_description(_fd
)
58 _nlines
= _layout
.get_line_count()
60 # the pixel height of the TextBlock
63 # print "TextBlock height: %g" % _th
66 if _iph
/_nlines
< 3: # tiny text - use the 18-point values for boundary
67 _w
, _h
= _layout
.get_size()
68 _tw
= (_w
* _th
)/float(_h
)
69 tblock
.setBounds(_tw
, _th
)
70 tblock
.setFontScale(1/float(pango
.SCALE
))
73 # print "TextBlock pixel height: %g [%d]" % (_ph, _iph)
74 _w
, _h
= _layout
.get_pixel_size()
75 # print "first layout: w: %d; h: %d" % (_w, _h)
79 # loop until the layout pixel height equals the "correct" pixel height
81 _diff
= abs(_h
- _iph
)
83 _fs
= (_fs
* _ph
)/float(_h
)
84 # print "adjusted font size: %g" % (_fs)
86 _layout
= _da
.create_pango_layout(_text
)
87 _fd
= pango
.FontDescription()
88 _fd
.set_family(_family
)
90 _fd
.set_weight(_weight
)
91 _fd
.set_size(int(pango
.SCALE
* _fs
))
92 _layout
.set_font_description(_fd
)
93 _w
, _h
= _layout
.get_pixel_size()
94 # print "adjusted layout: w: %d; h: %d" % (_w, _h)
98 # all the inexact comparisons and iteration max
99 # count text are arbitrary ...
102 # print "exact match"
104 if ((_iph
> 10) and (abs(_iph
- _h
) < 2)):
107 if ((_iph
> 100) and (abs(_iph
- _h
) < 10)):
110 if ((_iph
> 1000) and (abs(_iph
- _h
) < 40)):
114 # print "25 iterations"
117 # bah, another iteration
123 _fs
= (_fs
* _ph
)/float(_h
)
125 # split the difference in the changed font size
126 # and try again, but do not reset the old font
128 _fs
= _ofs
+ ((_fs
- _ofs
)/2.0)
129 # print "adjusted font size: %g" % (_fs)
132 # with the layout sized "correctly", calculate the TextBlock width
134 _w
, _h
= _layout
.get_size()
135 _tw
= (_w
* _th
)/float(_h
)
136 # print "TextBlock width: %g" % _tw
137 tblock
.setBounds(_tw
, _th
)
138 tblock
.setFontScale(_fs
)
141 def _make_pango_layout(gtkimage
, text
, family
, style
, weight
, size
):
142 _layout
= gtkimage
.getDA().create_pango_layout(text
)
143 _fd
= pango
.FontDescription()
144 _fd
.set_family(family
)
145 if style
== TextStyle
.FONT_NORMAL
:
146 _style
= pango
.STYLE_NORMAL
147 elif style
== TextStyle
.FONT_OBLIQUE
:
148 _style
= pango
.STYLE_OBLIQUE
149 elif style
== TextStyle
.FONT_ITALIC
:
150 _style
= pango
.STYLE_ITALIC
152 raise ValueError, "Unexpected font style: %d" % style
153 _fd
.set_style(_style
)
154 if weight
== TextStyle
.WEIGHT_NORMAL
:
155 _weight
= pango
.WEIGHT_NORMAL
156 elif weight
== TextStyle
.WEIGHT_LIGHT
:
157 _weight
= pango
.WEIGHT_LIGHT
158 elif weight
== TextStyle
.WEIGHT_BOLD
:
159 _weight
= pango
.WEIGHT_BOLD
160 elif weight
== TextStyle
.WEIGHT_HEAVY
:
161 _weight
= pango
.WEIGHT_HEAVY
163 raise ValueError, "Unexpected font weight: %d" % weight
164 _fd
.set_weight(_weight
)
165 _sz
= int(pango
.SCALE
* (size
/gtkimage
.getUnitsPerPixel()))
166 if _sz
< pango
.SCALE
:
169 _layout
.set_font_description(_fd
)
172 def text_button_press(gtkimage
, widget
, event
, tool
):
173 _tol
= gtkimage
.getTolerance()
174 _image
= gtkimage
.getImage()
175 _x
, _y
= _image
.getCurrentPoint()
176 _pt
, _flag
= _image
.findPoint(_x
, _y
, _tol
)
178 _x
, _y
= _pt
.getCoords()
179 tool
.getTextBlock().setLocation(_x
, _y
)
185 # tool.clearCurrentPoint()
188 def text_motion_notify(gtkimage
, widget
, event
, tool
):
189 _tblock
= tool
.getTextBlock()
190 _tw
, _th
= tool
.getPixelSize()
191 _gc
= gtkimage
.getGC()
194 _align
= _tblock
.getAlignment()
195 if _align
== TextStyle
.ALIGN_LEFT
:
197 elif _align
== TextStyle
.ALIGN_CENTER
:
199 elif _align
== TextStyle
.ALIGN_RIGHT
:
202 raise ValueError, "Unexpected alignment value: %d" % _align
203 _cp
= tool
.getCurrentPoint()
207 widget
.window
.draw_rectangle(_gc
, False, _xc
, _yc
, _tw
, _th
)
208 tool
.setCurrentPoint(_x
, _y
)
210 widget
.window
.draw_rectangle(_gc
, False, _x
, _y
, _tw
, _th
)
213 def text_add_init(gtkimage
, tool
=None):
214 _image
= gtkimage
.getImage()
215 _x
, _y
= _image
.getCurrentPoint()
216 _tool
= _image
.getTool()
217 _text
= _tool
.getText()
218 _ts
= _image
.getOption('TEXT_STYLE')
219 _tb
= TextBlock(_x
, _y
, _text
, _ts
)
220 _f
= _image
.getOption('FONT_FAMILY')
221 if _f
!= _ts
.getFamily():
223 _s
= _image
.getOption('FONT_STYLE')
224 if _s
!= _ts
.getStyle():
226 _w
= _image
.getOption('FONT_WEIGHT')
227 if _w
!= _ts
.getWeight():
229 _c
= _image
.getOption('FONT_COLOR')
230 if _c
!= _ts
.getColor():
232 _sz
= _image
.getOption('TEXT_SIZE')
233 if abs(_sz
- _ts
.getSize()) > 1e-10:
235 _a
= _image
.getOption('TEXT_ANGLE')
236 if abs(_a
- _ts
.getAngle()) > 1e-10:
238 _al
= _image
.getOption('TEXT_ALIGNMENT')
239 if _al
!= _ts
.getAlignment():
240 _tb
.setAlignment(_al
)
241 _tool
.setTextBlock(_tb
)
242 _layout
= _make_pango_layout(gtkimage
, _text
, _f
, _s
, _w
, _sz
)
243 _tool
.setLayout(_layout
)
244 _lw
, _lh
= _layout
.get_pixel_size()
245 _tool
.setPixelSize(_lw
, _lh
)
246 _upp
= gtkimage
.getUnitsPerPixel()
248 # the width and height calculations can be somewhat inaccurate
249 # as the unitsPerPixel value gets large
253 _tool
.setBounds(_w
, _h
)
254 _tool
.setHandler("motion_notify", text_motion_notify
)
255 _tool
.setHandler("button_press", text_button_press
)
256 gtkimage
.setPrompt(_('Click where to place the text'))
257 _gc
= gtkimage
.getGC()
258 _gc
.set_line_attributes(1, gtk
.gdk
.LINE_SOLID
,
259 gtk
.gdk
.CAP_BUTT
, gtk
.gdk
.JOIN_MITER
)
260 _gc
.set_function(gtk
.gdk
.INVERT
)
262 def text_add_dialog(gtkimage
):
263 _window
= gtkimage
.getWindow()
264 _dialog
= gtk
.Dialog(_('Enter text'), _window
,
265 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
266 (gtk
.STOCK_OK
, gtk
.RESPONSE_OK
,
267 gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
))
268 _tb
= gtk
.TextBuffer()
269 _tv
= gtk
.TextView(_tb
)
270 _sw
= gtk
.ScrolledWindow()
271 _sw
.set_size_request(400, 300)
272 _sw
.add_with_viewport(_tv
)
273 _dialog
.vbox
.pack_start(_sw
, False, False, 0)
276 _response
= _dialog
.run()
277 if _response
== gtk
.RESPONSE_OK
:
278 _start_iter
, _end_iter
= _tb
.get_bounds()
279 _text
= _tb
.get_text(_start_iter
, _end_iter
)