2 // "$Id: fl_draw.cxx 8763 2011-05-30 16:08:46Z manolo $"
4 // Label drawing code for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2011 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 // Implementation of fl_draw(const char*,int,int,int,int,Fl_Align)
29 // Used to draw all the labels and text, this routine:
30 // Word wraps the labels to fit into their bounding box.
31 // Breaks them into lines at the newlines.
32 // Expands all unprintable characters to ^X or \nnn notation
33 // Aligns them against the inside of the box.
35 #include <FL/fl_utf8.h>
37 #include <FL/fl_draw.H>
38 #include <FL/Fl_Image.H>
46 char fl_draw_shortcut
; // set by fl_labeltypes.cxx
48 static char* underline_at
;
51 utf8 multibyte char seq. detection an pass-thru routine.
52 \retval false if no utf8 seq detected, no change made. true if utf8 and d copied with s seq.
53 note that for n bytes copied dest incremented of n, but s of n-1 for compatible loop use see below.
55 #define C_IN(c,a,b) ((c)>=(a) && (c)<=(b))
56 #define C_UTF8(c) C_IN(c,0x80,0xBF)
58 static bool handle_utf8_seq(const char * &s
,char * &d
) {
59 register const unsigned char* p
=(const unsigned char*)s
;
60 if (p
[0] < 0xc2 || p
[0] > 0xf4)
61 return false; // not adressed in this function
62 else if ( C_IN(p
[0], 0xc2, 0xdf) && C_UTF8(p
[1]) ) {
65 // non-overlong 2-byte
67 else if ( p
[0]==0xe0 && C_IN(p
[1], 0xa0, 0xbf) && C_UTF8(p
[2]) ) {
68 d
[0]=s
[0]; d
[1]=s
[1];d
[2]=s
[2];
70 // excluding overlongs
72 else if (p
[0]==0xed && C_IN(p
[1], 0x80, 0x9f) && C_UTF8(p
[2]) ) {
73 d
[0]=s
[0]; d
[1]=s
[1];d
[2]=s
[2];
75 // excluding surrogates
77 else if (p
[0]!=0xed && C_IN(p
[0], 0xe1, 0xef) && C_UTF8(p
[1]) && C_UTF8(p
[2]) ) {
78 d
[0]=s
[0]; d
[1]=s
[1];d
[2]=s
[2];
82 else if (p
[0]==0xf0 && C_IN(p
[1], 0x90, 0xbf) && C_UTF8(p
[2]) && C_UTF8(p
[3]) ) {
83 d
[0]=s
[0]; d
[1]=s
[1]; d
[2]=s
[2]; d
[3]=s
[3];
87 else if (C_IN(p
[0], 0xf1, 0xf3) && C_UTF8(p
[1]) && C_UTF8(p
[2]) && C_UTF8(p
[3]) ) {
88 d
[0]=s
[0]; d
[1]=s
[1]; d
[2]=s
[2]; d
[3]=s
[3];
92 else if (p
[0]==0xf4 && C_IN(p
[1], 0x80, 0x8f) && C_UTF8(p
[2]) && C_UTF8(p
[3]) ) {
93 d
[0]=s
[0]; d
[1]=s
[1]; d
[2]=s
[2]; d
[3]=s
[3];
96 } else { // non utf8 compliant, maybe CP125x or broken utf8 string
97 // fprintf(stderr, "Not UTF8 char \n");
100 return true; // we did handled and copied the utf8 multibyte char seq.
104 Copy \p from to \p buf, replacing unprintable characters with ^X and \\nnn.
106 Stop at a newline or if MAXBUF characters written to buffer.
107 Also word-wrap if width exceeds maxw.
108 Returns a pointer to the start of the next line of characters.
109 Sets n to the number of characters put into the buffer.
110 Sets width to the width of the string in the current font.
113 fl_expand_text(const char* from
, char* buf
, int maxbuf
, double maxw
, int& n
,
114 double &width
, int wrap
, int draw_symbols
) {
116 char* e
= buf
+(maxbuf
-4);
119 const char* word_start
= from
;
122 const char* p
= from
;
127 if (!c
|| c
== ' ' || c
== '\n') {
128 // test for word-wrap:
129 if (word_start
< p
&& wrap
) {
130 double newwidth
= w
+ fl_width(word_end
, o
-word_end
);
131 if (word_end
> buf
&& newwidth
> maxw
) { // break before this word
140 else if (c
== '\n') {p
++; break;}
144 if (o
> e
) break; // don't overflow buffer
147 for (c
= fl_utf_nb_char((uchar
*)buf
, o
-buf
)%8; c
<8 && o
<e
; c
++)
149 } else if (c
== '&' && fl_draw_shortcut
&& *(p
+1)) {
150 if (*(p
+1) == '&') {p
++; *o
++ = '&';}
151 else if (fl_draw_shortcut
!= 2) underline_at
= o
;
152 } else if (c
< ' ' || c
== 127) { // ^X
155 } else if (handle_utf8_seq(p
, o
)) { // figure out if we have an utf8 valid sequence before we determine the nbsp test validity:
157 } else if (c
== 0xCA) { // non-breaking space in MacRoman
159 } else if (c
== 0xA0) { // non-breaking space in ISO 8859
163 } else if (c
== '@' && draw_symbols
) { // Symbol???
164 if (p
[1] && p
[1] != '@') break;
172 width
= w
+ fl_width(word_end
, o
-word_end
);
179 The same as fl_draw(const char*,int,int,int,int,Fl_Align,Fl_Image*,int) with
180 the addition of the \p callthis parameter, which is a pointer to a text drawing
181 function such as fl_draw(const char*, int, int, int) to do the real work
184 const char* str
, // the (multi-line) string
185 int x
, int y
, int w
, int h
, // bounding box
187 void (*callthis
)(const char*,int,int,int),
188 Fl_Image
* img
, int draw_symbols
)
194 char symbol
[2][255], *symptr
;
195 int symwidth
[2], symoffset
, symtotal
, imgtotal
;
197 // count how many lines and put the last one into the buffer:
201 // if the image is set as a backdrop, ignore it here
202 if (img
&& (align
& FL_ALIGN_IMAGE_BACKDROP
)) img
= 0;
211 if (str
&& str
[0] == '@' && str
[1] && str
[1] != '@') {
212 // Start with a symbol...
213 for (symptr
= symbol
[0];
214 *str
&& !isspace(*str
) && symptr
< (symbol
[0] + sizeof(symbol
[0]) - 1);
217 if (isspace(*str
)) str
++;
218 symwidth
[0] = (w
< h
? w
: h
);
221 if (str
&& (p
= strrchr(str
, '@')) != NULL
&& p
> (str
+ 1) && p
[-1] != '@') {
222 strlcpy(symbol
[1], p
, sizeof(symbol
[1]));
223 symwidth
[1] = (w
< h
? w
: h
);
227 symtotal
= symwidth
[0] + symwidth
[1];
228 imgtotal
= (img
&& (align
&FL_ALIGN_IMAGE_NEXT_TO_TEXT
)) ? img
->w() : 0;
234 for (p
= str
, lines
=0; p
;) {
235 e
= fl_expand_text(p
, buf
, MAXBUF
, w
- symtotal
- imgtotal
, buflen
, width
,
236 align
&FL_ALIGN_WRAP
, draw_symbols
);
237 if (strw
<width
) strw
= (int)width
;
239 if (!*e
|| (*e
== '@' && e
[1] != '@' && draw_symbols
)) break;
244 if ((symwidth
[0] || symwidth
[1]) && lines
) {
245 if (symwidth
[0]) symwidth
[0] = lines
* fl_height();
246 if (symwidth
[1]) symwidth
[1] = lines
* fl_height();
249 symtotal
= symwidth
[0] + symwidth
[1];
250 strh
= lines
* fl_height();
252 // figure out vertical position of the first line:
255 int height
= fl_height();
256 int imgvert
= ((align
&FL_ALIGN_IMAGE_NEXT_TO_TEXT
)==0);
257 int imgh
= img
&& imgvert
? img
->h() : 0;
258 int imgw
[2] = {0, 0};
262 if (align
& FL_ALIGN_BOTTOM
) ypos
= y
+h
-(lines
-1)*height
-imgh
;
263 else if (align
& FL_ALIGN_TOP
) ypos
= y
+height
;
264 else ypos
= y
+(h
-lines
*height
-imgh
)/2+height
;
266 // draw the image unless the "text over image" alignment flag is set...
267 if (img
&& imgvert
&& !(align
& FL_ALIGN_TEXT_OVER_IMAGE
)) {
268 if (img
->w() > symoffset
) symoffset
= img
->w();
270 if (align
& FL_ALIGN_LEFT
) xpos
= x
+ symwidth
[0];
271 else if (align
& FL_ALIGN_RIGHT
) xpos
= x
+ w
- img
->w() - symwidth
[1];
272 else xpos
= x
+ (w
- img
->w() - symtotal
) / 2 + symwidth
[0];
274 img
->draw(xpos
, ypos
- height
);
278 // draw the image to the side of the text
279 if (img
&& !imgvert
/* && (align & !FL_ALIGN_TEXT_NEXT_TO_IMAGE)*/ ) {
280 if (align
& FL_ALIGN_TEXT_OVER_IMAGE
) { // image is right of text
282 if (align
& FL_ALIGN_LEFT
) xpos
= x
+ symwidth
[0] + strw
+ 1;
283 else if (align
& FL_ALIGN_RIGHT
) xpos
= x
+ w
- symwidth
[1] - imgw
[1] + 1;
284 else xpos
= x
+ (w
- strw
- symtotal
- imgw
[1]) / 2 + symwidth
[0] + strw
+ 1;
285 } else { // image is to the left of the text
287 if (align
& FL_ALIGN_LEFT
) xpos
= x
+ symwidth
[0] - 1;
288 else if (align
& FL_ALIGN_RIGHT
) xpos
= x
+ w
- symwidth
[1] - strw
- imgw
[0] - 1;
289 else xpos
= x
+ (w
- strw
- symtotal
- imgw
[0]) / 2 - 1;
291 int yimg
= ypos
- height
;
292 if (align
& FL_ALIGN_TOP
) ;
293 else if (align
& FL_ALIGN_BOTTOM
) yimg
+= strh
- img
->h() - 1;
294 else yimg
+= (strh
- img
->h() - 1) / 2;
295 img
->draw(xpos
, yimg
);
298 // now draw all the lines:
300 int desc
= fl_descent();
301 for (p
=str
; ; ypos
+= height
) {
302 if (lines
>1) e
= fl_expand_text(p
, buf
, MAXBUF
, w
- symtotal
- imgtotal
, buflen
,
303 width
, align
&FL_ALIGN_WRAP
, draw_symbols
);
306 if (width
> symoffset
) symoffset
= (int)(width
+ 0.5);
308 if (align
& FL_ALIGN_LEFT
) xpos
= x
+ symwidth
[0] + imgw
[0];
309 else if (align
& FL_ALIGN_RIGHT
) xpos
= x
+ w
- (int)(width
+ .5) - symwidth
[1] - imgw
[1];
310 else xpos
= x
+ (w
- (int)(width
+ .5) - symtotal
- imgw
[0] - imgw
[1]) / 2 + symwidth
[0] + imgw
[0];
312 callthis(buf
,buflen
,xpos
,ypos
-desc
);
314 if (underline_at
&& underline_at
>= buf
&& underline_at
< (buf
+ buflen
))
315 callthis("_",1,xpos
+int(fl_width(buf
,underline_at
-buf
)),ypos
-desc
);
317 if (!*e
|| (*e
== '@' && e
[1] != '@')) break;
322 // draw the image if the "text over image" alignment flag is set...
323 if (img
&& imgvert
&& (align
& FL_ALIGN_TEXT_OVER_IMAGE
)) {
324 if (img
->w() > symoffset
) symoffset
= img
->w();
326 if (align
& FL_ALIGN_LEFT
) xpos
= x
+ symwidth
[0];
327 else if (align
& FL_ALIGN_RIGHT
) xpos
= x
+ w
- img
->w() - symwidth
[1];
328 else xpos
= x
+ (w
- img
->w() - symtotal
) / 2 + symwidth
[0];
330 img
->draw(xpos
, ypos
);
333 // draw the symbols, if any...
336 if (align
& FL_ALIGN_LEFT
) xpos
= x
;
337 else if (align
& FL_ALIGN_RIGHT
) xpos
= x
+ w
- symtotal
- symoffset
;
338 else xpos
= x
+ (w
- symoffset
- symtotal
) / 2;
340 if (align
& FL_ALIGN_BOTTOM
) ypos
= y
+ h
- symwidth
[0];
341 else if (align
& FL_ALIGN_TOP
) ypos
= y
;
342 else ypos
= y
+ (h
- symwidth
[0]) / 2;
344 fl_draw_symbol(symbol
[0], xpos
, ypos
, symwidth
[0], symwidth
[0], fl_color());
349 if (align
& FL_ALIGN_LEFT
) xpos
= x
+ symoffset
+ symwidth
[0];
350 else if (align
& FL_ALIGN_RIGHT
) xpos
= x
+ w
- symwidth
[1];
351 else xpos
= x
+ (w
- symoffset
- symtotal
) / 2 + symoffset
+ symwidth
[0];
353 if (align
& FL_ALIGN_BOTTOM
) ypos
= y
+ h
- symwidth
[1];
354 else if (align
& FL_ALIGN_TOP
) ypos
= y
;
355 else ypos
= y
+ (h
- symwidth
[1]) / 2;
357 fl_draw_symbol(symbol
[1], xpos
, ypos
, symwidth
[1], symwidth
[1], fl_color());
362 Fancy string drawing function which is used to draw all the labels.
364 The string is formatted and aligned inside the passed box.
365 Handles '\\t' and '\\n', expands all other control characters to '^X',
366 and aligns inside or against the edges of the box.
367 See Fl_Widget::align() for values of \p align. The value FL_ALIGN_INSIDE
368 is ignored, as this function always prints inside the box.
369 If \p img is provided and is not \p NULL, the image is drawn above or
370 below the text as specified by the \p align value.
371 The \p draw_symbols argument specifies whether or not to look for symbol
372 names starting with the '\@' character'
373 The text length is limited to 1024 characters per line.
377 int x
, int y
, int w
, int h
,
382 if ((!str
|| !*str
) && !img
) return;
383 if (w
&& h
&& !fl_not_clipped(x
, y
, w
, h
) && (align
& FL_ALIGN_INSIDE
)) return;
384 if (align
& FL_ALIGN_CLIP
)
385 fl_push_clip(x
, y
, w
, h
);
386 fl_draw(str
, x
, y
, w
, h
, align
, fl_draw
, img
, draw_symbols
);
387 if (align
& FL_ALIGN_CLIP
)
392 Measure how wide and tall the string will be when printed by the
393 fl_draw() function with \p align parameter. If the incoming \p w
394 is non-zero it will wrap to that width.
395 \param[in] str nul-terminated string
396 \param[out] w,h width and height of string in current font
397 \param[in] draw_symbols non-zero to enable @@symbol handling [default=1]
399 void fl_measure(const char* str
, int& w
, int& h
, int draw_symbols
) {
400 if (!str
|| !*str
) {w
= 0; h
= 0; return;}
409 char symbol
[2][255], *symptr
;
410 int symwidth
[2], symtotal
;
412 // count how many lines and put the last one into the buffer:
420 if (str
&& str
[0] == '@' && str
[1] && str
[1] != '@') {
421 // Start with a symbol...
422 for (symptr
= symbol
[0];
423 *str
&& !isspace(*str
) && symptr
< (symbol
[0] + sizeof(symbol
[0]) - 1);
426 if (isspace(*str
)) str
++;
430 if (str
&& (p
= strrchr(str
, '@')) != NULL
&& p
> (str
+ 1) && p
[-1]!='@') {
431 strlcpy(symbol
[1], p
, sizeof(symbol
[1]));
436 symtotal
= symwidth
[0] + symwidth
[1];
438 for (p
= str
, lines
=0; p
;) {
439 // e = expand(p, buf, w - symtotal, buflen, width, w != 0, draw_symbols);
440 e
= fl_expand_text(p
, buf
, MAXBUF
, w
- symtotal
, buflen
, width
,
441 w
!= 0, draw_symbols
);
442 if ((int)ceil(width
) > W
) W
= (int)ceil(width
);
444 if (!*e
|| (*e
== '@' && e
[1] != '@' && draw_symbols
)) break;
448 if ((symwidth
[0] || symwidth
[1]) && lines
) {
449 if (symwidth
[0]) symwidth
[0] = lines
* fl_height();
450 if (symwidth
[1]) symwidth
[1] = lines
* fl_height();
453 symtotal
= symwidth
[0] + symwidth
[1];
460 This function returns the actual height of the specified \p font
461 and \p size. Normally the font height should always be 'size',
462 but with the advent of XFT, there are (currently) complexities
463 that seem to only be solved by asking the font what its actual
464 font height is. (See STR#2115)
466 This function was originally undocumented in 1.1.x, and was used
467 only by Fl_Text_Display. We're now documenting it in 1.3.x so that
468 apps that need precise height info can get it with this function.
470 \returns the height of the font in pixels.
472 \todo In the future, when the XFT issues are resolved, this function
473 should simply return the 'size' value.
475 int fl_height(int font
, int size
) {
476 if ( font
== fl_font() && size
== fl_size() ) return(fl_height());
477 int tf
= fl_font(), ts
= fl_size(); // save
479 int height
= fl_height();
480 fl_font(tf
,ts
); // restore
485 // End of "$Id: fl_draw.cxx 8763 2011-05-30 16:08:46Z manolo $".