themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / fl_draw.cxx
blobb88cc70da0322f3e5a4e2f87b0f2308d6aa841d5
1 //
2 // "$Id: fl_draw.cxx 8763 2011-05-30 16:08:46Z manolo $"
3 //
4 // Label drawing code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2011 by Bill Spitzak and others.
7 //
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
21 // USA.
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>
36 #include <FL/Fl.H>
37 #include <FL/fl_draw.H>
38 #include <FL/Fl_Image.H>
40 #include "flstring.h"
41 #include <ctype.h>
42 #include <math.h>
44 #define MAXBUF 1024
46 char fl_draw_shortcut; // set by fl_labeltypes.cxx
48 static char* underline_at;
50 /**
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]) ) {
63 d[0]=s[0]; d[1]=s[1];
64 d+=2; s++;
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];
69 d+=3; 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];
74 d+=3; 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];
79 d+=3; s+=2;
80 // straight 3-byte
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];
84 d+=4; s+=3;
85 // planes 1-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];
89 d+=4; s+=3;
90 // planes 4-15
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];
94 d+=4; s+=3;
95 // planes 16
96 } else { // non utf8 compliant, maybe CP125x or broken utf8 string
97 // fprintf(stderr, "Not UTF8 char \n");
98 return false;
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.
112 const char*
113 fl_expand_text(const char* from, char* buf, int maxbuf, double maxw, int& n,
114 double &width, int wrap, int draw_symbols) {
115 char* o = buf;
116 char* e = buf+(maxbuf-4);
117 underline_at = 0;
118 char* word_end = o;
119 const char* word_start = from;
120 double w = 0;
122 const char* p = from;
123 for (;; p++) {
125 int c = *p & 255;
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
132 o = word_end;
133 p = word_start;
134 break;
136 word_end = o;
137 w = newwidth;
139 if (!c) break;
140 else if (c == '\n') {p++; break;}
141 word_start = p+1;
144 if (o > e) break; // don't overflow buffer
146 if (c == '\t') {
147 for (c = fl_utf_nb_char((uchar*)buf, o-buf)%8; c<8 && o<e; c++)
148 *o++ = ' ';
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
153 *o++ = '^';
154 *o++ = c ^ 0x40;
155 } else if (handle_utf8_seq(p, o)) { // figure out if we have an utf8 valid sequence before we determine the nbsp test validity:
156 #ifdef __APPLE__
157 } else if (c == 0xCA) { // non-breaking space in MacRoman
158 #else
159 } else if (c == 0xA0) { // non-breaking space in ISO 8859
160 #endif
161 *o++ = ' ';
163 } else if (c == '@' && draw_symbols) { // Symbol???
164 if (p[1] && p[1] != '@') break;
165 *o++ = c;
166 if (p[1]) p++;
167 } else {
168 *o++ = c;
172 width = w + fl_width(word_end, o-word_end);
173 *o = 0;
174 n = o-buf;
175 return p;
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
183 void fl_draw(
184 const char* str, // the (multi-line) string
185 int x, int y, int w, int h, // bounding box
186 Fl_Align align,
187 void (*callthis)(const char*,int,int,int),
188 Fl_Image* img, int draw_symbols)
190 const char* p;
191 const char* e;
192 char buf[MAXBUF];
193 int buflen;
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:
198 int lines;
199 double width;
201 // if the image is set as a backdrop, ignore it here
202 if (img && (align & FL_ALIGN_IMAGE_BACKDROP)) img = 0;
204 symbol[0][0] = '\0';
205 symwidth[0] = 0;
207 symbol[1][0] = '\0';
208 symwidth[1] = 0;
210 if (draw_symbols) {
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);
215 *symptr++ = *str++);
216 *symptr = '\0';
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;
230 int strw = 0;
231 int strh;
233 if (str) {
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;
238 lines++;
239 if (!*e || (*e == '@' && e[1] != '@' && draw_symbols)) break;
240 p = e;
242 } else lines = 0;
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:
253 int xpos;
254 int ypos;
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};
260 symoffset = 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);
275 ypos += img->h();
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
281 imgw[1] = img->w();
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
286 imgw[0] = img->w();
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:
299 if (str) {
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);
304 else e = "";
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;
318 p = e;
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...
334 if (symwidth[0]) {
335 // draw to the left
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());
347 if (symwidth[1]) {
348 // draw to the right
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.
375 void fl_draw(
376 const char* str,
377 int x, int y, int w, int h,
378 Fl_Align align,
379 Fl_Image* img,
380 int draw_symbols)
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)
388 fl_pop_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;}
401 h = fl_height();
402 const char* p;
403 const char* e;
404 char buf[MAXBUF];
405 int buflen;
406 int lines;
407 double width=0;
408 int W = 0;
409 char symbol[2][255], *symptr;
410 int symwidth[2], symtotal;
412 // count how many lines and put the last one into the buffer:
413 symbol[0][0] = '\0';
414 symwidth[0] = 0;
416 symbol[1][0] = '\0';
417 symwidth[1] = 0;
419 if (draw_symbols) {
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);
424 *symptr++ = *str++);
425 *symptr = '\0';
426 if (isspace(*str)) str++;
427 symwidth[0] = h;
430 if (str && (p = strrchr(str, '@')) != NULL && p > (str + 1) && p[-1]!='@') {
431 strlcpy(symbol[1], p, sizeof(symbol[1]));
432 symwidth[1] = h;
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);
443 lines++;
444 if (!*e || (*e == '@' && e[1] != '@' && draw_symbols)) break;
445 p = e;
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];
455 w = W + symtotal;
456 h = lines*h;
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
478 fl_font(font,size);
479 int height = fl_height();
480 fl_font(tf,ts); // restore
481 return(height);
485 // End of "$Id: fl_draw.cxx 8763 2011-05-30 16:08:46Z manolo $".