DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1 | // |
| 2 | // "$Id: Fl_Browser.cxx 8736 2011-05-24 20:00:56Z AlbrechtS $" |
| 3 | // |
| 4 | // Browser widget for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2010 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. |
| 12 | // |
| 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. |
| 17 | // |
| 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. |
| 22 | // |
| 23 | // Please report all bugs and problems on the following page: |
| 24 | // |
| 25 | // http://www.fltk.org/str.php |
| 26 | // |
| 27 | |
| 28 | #include <FL/Fl.H> |
| 29 | #include <FL/Fl_Browser.H> |
| 30 | #include <FL/fl_draw.H> |
| 31 | #include "flstring.h" |
| 32 | #include <stdlib.h> |
| 33 | #include <math.h> |
| 34 | |
| 35 | #if defined(FL_DLL) // really needed for c'tors for MS VC++ only |
| 36 | #include <FL/Fl_Hold_Browser.H> |
| 37 | #include <FL/Fl_Multi_Browser.H> |
| 38 | #include <FL/Fl_Select_Browser.H> |
| 39 | #endif |
| 40 | |
| 41 | // I modified this from the original Forms data to use a linked list |
| 42 | // so that the number of items in the browser and size of those items |
| 43 | // is unlimited. The only problem is that the old browser used an |
| 44 | // index number to identify a line, and it is slow to convert from/to |
| 45 | // a pointer. I use a cache of the last match to try to speed this up. |
| 46 | |
| 47 | // Also added the ability to "hide" a line. This sets its height to |
| 48 | // zero, so the Fl_Browser_ cannot pick it. |
| 49 | |
| 50 | #define SELECTED 1 |
| 51 | #define NOTDISPLAYED 2 |
| 52 | |
| 53 | // WARNING: |
| 54 | // Fl_File_Chooser.cxx also has a definition of this structure (FL_BLINE). |
| 55 | // Changes to FL_BLINE *must* be reflected in Fl_File_Chooser.cxx as well. |
| 56 | // This hack in Fl_File_Chooser should be solved. |
| 57 | // |
| 58 | struct FL_BLINE { // data is in a linked list of these |
| 59 | FL_BLINE* prev; |
| 60 | FL_BLINE* next; |
| 61 | void* data; |
| 62 | Fl_Image* icon; |
| 63 | short length; // sizeof(txt)-1, may be longer than string |
| 64 | char flags; // selected, displayed |
| 65 | char txt[1]; // start of allocated array |
| 66 | }; |
| 67 | |
| 68 | /** |
| 69 | Returns the very first item in the list. |
| 70 | Example of use: |
| 71 | \code |
| 72 | // Walk the browser from beginning to end |
| 73 | for ( void *i=item_first(); i; i=item_next(i) ) { |
| 74 | printf("item label='%s'\n", item_text(i)); |
| 75 | } |
| 76 | \endcode |
| 77 | \returns The first item, or NULL if list is empty. |
| 78 | \see item_first(), item_last(), item_next(), item_prev() |
| 79 | */ |
| 80 | void* Fl_Browser::item_first() const {return first;} |
| 81 | |
| 82 | /** |
| 83 | Returns the next item after \p item. |
| 84 | \param[in] item The 'current' item |
| 85 | \returns The next item after \p item, or NULL if there are none after this one. |
| 86 | \see item_first(), item_last(), item_next(), item_prev() |
| 87 | */ |
| 88 | void* Fl_Browser::item_next(void* item) const {return ((FL_BLINE*)item)->next;} |
| 89 | |
| 90 | /** |
| 91 | Returns the previous item before \p item. |
| 92 | \param[in] item The 'current' item |
| 93 | \returns The previous item before \p item, or NULL if there none before this one. |
| 94 | \see item_first(), item_last(), item_next(), item_prev() |
| 95 | */ |
| 96 | void* Fl_Browser::item_prev(void* item) const {return ((FL_BLINE*)item)->prev;} |
| 97 | |
| 98 | /** |
| 99 | Returns the very last item in the list. |
| 100 | Example of use: |
| 101 | \code |
| 102 | // Walk the browser in reverse, from end to start |
| 103 | for ( void *i=item_last(); i; i=item_prev(i) ) { |
| 104 | printf("item label='%s'\n", item_text(i)); |
| 105 | } |
| 106 | \endcode |
| 107 | \returns The last item, or NULL if list is empty. |
| 108 | \see item_first(), item_last(), item_next(), item_prev() |
| 109 | */ |
| 110 | void* Fl_Browser::item_last() const {return last;} |
| 111 | |
| 112 | /** |
| 113 | See if \p item is selected. |
| 114 | \param[in] item The item whose selection state is to be checked. |
| 115 | \returns 1 if selected, 0 if not. |
| 116 | \see select(), selected(), value(), item_select(), item_selected() |
| 117 | */ |
| 118 | int Fl_Browser::item_selected(void* item) const { |
| 119 | return ((FL_BLINE*)item)->flags&SELECTED; |
| 120 | } |
| 121 | /** |
| 122 | Change the selection state of \p item to the value \p val. |
| 123 | \param[in] item The item to be changed. |
| 124 | \param[in] val The new selection state: 1 selects, 0 de-selects. |
| 125 | \see select(), selected(), value(), item_select(), item_selected() |
| 126 | */ |
| 127 | void Fl_Browser::item_select(void *item, int val) { |
| 128 | if (val) ((FL_BLINE*)item)->flags |= SELECTED; |
| 129 | else ((FL_BLINE*)item)->flags &= ~SELECTED; |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | Returns the label text for \p item. |
| 134 | \param[in] item The item whose label text is returned. |
| 135 | \returns The item's text string. (Can be NULL) |
| 136 | */ |
| 137 | const char *Fl_Browser::item_text(void *item) const { |
| 138 | return ((FL_BLINE*)item)->txt; |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | Returns the item for specified \p line. |
| 143 | |
| 144 | Note: This call is slow. It's fine for e.g. responding to user |
| 145 | clicks, but slow if called often, such as in a tight sorting loop. |
| 146 | Finding an item 'by line' involves a linear lookup on the internal |
| 147 | linked list. The performance hit can be significant if the browser's |
| 148 | contents is large, and the method is called often (e.g. during a sort). |
| 149 | If you're writing a subclass, use the protected methods item_first(), |
| 150 | item_next(), etc. to access the internal linked list more efficiently. |
| 151 | |
| 152 | \param[in] line The line number of the item to return. (1 based) |
| 153 | \retval item that was found. |
| 154 | \retval NULL if line is out of range. |
| 155 | \see item_at(), find_line(), lineno() |
| 156 | */ |
| 157 | FL_BLINE* Fl_Browser::find_line(int line) const { |
| 158 | int n; FL_BLINE* l; |
| 159 | if (line == cacheline) return cache; |
| 160 | if (cacheline && line > (cacheline/2) && line < ((cacheline+lines)/2)) { |
| 161 | n = cacheline; l = cache; |
| 162 | } else if (line <= (lines/2)) { |
| 163 | n = 1; l = first; |
| 164 | } else { |
| 165 | n = lines; l = last; |
| 166 | } |
| 167 | for (; n < line && l; n++) l = l->next; |
| 168 | for (; n > line && l; n--) l = l->prev; |
| 169 | ((Fl_Browser*)this)->cacheline = line; |
| 170 | ((Fl_Browser*)this)->cache = l; |
| 171 | return l; |
| 172 | } |
| 173 | |
| 174 | /** |
| 175 | Returns line number corresponding to \p item, or zero if not found. |
| 176 | Caveat: See efficiency note in find_line(). |
| 177 | \param[in] item The item to be found |
| 178 | \returns The line number of the item, or 0 if not found. |
| 179 | \see item_at(), find_line(), lineno() |
| 180 | */ |
| 181 | int Fl_Browser::lineno(void *item) const { |
| 182 | FL_BLINE* l = (FL_BLINE*)item; |
| 183 | if (!l) return 0; |
| 184 | if (l == cache) return cacheline; |
| 185 | if (l == first) return 1; |
| 186 | if (l == last) return lines; |
| 187 | if (!cache) { |
| 188 | ((Fl_Browser*)this)->cache = first; |
| 189 | ((Fl_Browser*)this)->cacheline = 1; |
| 190 | } |
| 191 | // assume it is near cache, search both directions: |
| 192 | FL_BLINE* b = cache->prev; |
| 193 | int bnum = cacheline-1; |
| 194 | FL_BLINE* f = cache->next; |
| 195 | int fnum = cacheline+1; |
| 196 | int n = 0; |
| 197 | for (;;) { |
| 198 | if (b == l) {n = bnum; break;} |
| 199 | if (f == l) {n = fnum; break;} |
| 200 | if (b) {b = b->prev; bnum--;} |
| 201 | if (f) {f = f->next; fnum++;} |
| 202 | } |
| 203 | ((Fl_Browser*)this)->cache = l; |
| 204 | ((Fl_Browser*)this)->cacheline = n; |
| 205 | return n; |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | Removes the item at the specified \p line. |
| 210 | Caveat: See efficiency note in find_line(). |
| 211 | You must call redraw() to make any changes visible. |
| 212 | \param[in] line The line number to be removed. (1 based) Must be in range! |
| 213 | \returns Pointer to browser item that was removed (and is no longer valid). |
| 214 | \see add(), insert(), remove(), swap(int,int), clear() |
| 215 | */ |
| 216 | FL_BLINE* Fl_Browser::_remove(int line) { |
| 217 | FL_BLINE* ttt = find_line(line); |
| 218 | deleting(ttt); |
| 219 | |
| 220 | cacheline = line-1; |
| 221 | cache = ttt->prev; |
| 222 | lines--; |
| 223 | full_height_ -= item_height(ttt); |
| 224 | if (ttt->prev) ttt->prev->next = ttt->next; |
| 225 | else first = ttt->next; |
| 226 | if (ttt->next) ttt->next->prev = ttt->prev; |
| 227 | else last = ttt->prev; |
| 228 | |
| 229 | return(ttt); |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | Remove entry for given \p line number, making the browser one line shorter. |
| 234 | You must call redraw() to make any changes visible. |
| 235 | \param[in] line Line to be removed. (1 based) \n |
| 236 | If \p line is out of range, no action is taken. |
| 237 | \see add(), insert(), remove(), swap(int,int), clear() |
| 238 | */ |
| 239 | void Fl_Browser::remove(int line) { |
| 240 | if (line < 1 || line > lines) return; |
| 241 | free(_remove(line)); |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | Insert specified \p item above \p line. |
| 246 | If \p line > size() then the line is added to the end. |
| 247 | |
| 248 | Caveat: See efficiency note in find_line(). |
| 249 | |
| 250 | \param[in] line The new line will be inserted above this line (1 based). |
| 251 | \param[in] item The item to be added. |
| 252 | */ |
| 253 | void Fl_Browser::insert(int line, FL_BLINE* item) { |
| 254 | if (!first) { |
| 255 | item->prev = item->next = 0; |
| 256 | first = last = item; |
| 257 | } else if (line <= 1) { |
| 258 | inserting(first, item); |
| 259 | item->prev = 0; |
| 260 | item->next = first; |
| 261 | item->next->prev = item; |
| 262 | first = item; |
| 263 | } else if (line > lines) { |
| 264 | item->prev = last; |
| 265 | item->prev->next = item; |
| 266 | item->next = 0; |
| 267 | last = item; |
| 268 | } else { |
| 269 | FL_BLINE* n = find_line(line); |
| 270 | inserting(n, item); |
| 271 | item->next = n; |
| 272 | item->prev = n->prev; |
| 273 | item->prev->next = item; |
| 274 | n->prev = item; |
| 275 | } |
| 276 | cacheline = line; |
| 277 | cache = item; |
| 278 | lines++; |
| 279 | full_height_ += item_height(item); |
| 280 | redraw_line(item); |
| 281 | } |
| 282 | |
| 283 | /** |
| 284 | Insert a new entry whose label is \p newtext \e above given \p line, optional data \p d. |
| 285 | |
| 286 | Text may contain format characters; see format_char() for details. |
| 287 | \p newtext is copied using the strdup() function, and can be NULL to make a blank line. |
| 288 | |
| 289 | The optional void * argument \p d will be the data() of the new item. |
| 290 | |
| 291 | \param[in] line Line position for insert. (1 based) \n |
| 292 | If \p line > size(), the entry will be added at the end. |
| 293 | \param[in] newtext The label text for the new line. |
| 294 | \param[in] d Optional pointer to user data to be associated with the new line. |
| 295 | */ |
| 296 | void Fl_Browser::insert(int line, const char* newtext, void* d) { |
| 297 | int l = strlen(newtext); |
| 298 | FL_BLINE* t = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l); |
| 299 | t->length = (short)l; |
| 300 | t->flags = 0; |
| 301 | strcpy(t->txt, newtext); |
| 302 | t->data = d; |
| 303 | t->icon = 0; |
| 304 | insert(line, t); |
| 305 | } |
| 306 | |
| 307 | /** |
| 308 | Line \p from is removed and reinserted at \p to. |
| 309 | Note: \p to is calculated \e after line \p from gets removed. |
| 310 | \param[in] to Destination line number (calculated \e after line \p from is removed) |
| 311 | \param[in] from Line number of item to be moved |
| 312 | */ |
| 313 | void Fl_Browser::move(int to, int from) { |
| 314 | if (from < 1 || from > lines) return; |
| 315 | insert(to, _remove(from)); |
| 316 | } |
| 317 | |
| 318 | /** |
| 319 | Sets the text for the specified \p line to \p newtext. |
| 320 | |
| 321 | Text may contain format characters; see format_char() for details. |
| 322 | \p newtext is copied using the strdup() function, and can be NULL to make a blank line. |
| 323 | |
| 324 | Does nothing if \p line is out of range. |
| 325 | |
| 326 | \param[in] line The line of the item whose text will be changed. (1 based) |
| 327 | \param[in] newtext The new string to be assigned to the item. |
| 328 | */ |
| 329 | void Fl_Browser::text(int line, const char* newtext) { |
| 330 | if (line < 1 || line > lines) return; |
| 331 | FL_BLINE* t = find_line(line); |
| 332 | int l = strlen(newtext); |
| 333 | if (l > t->length) { |
| 334 | FL_BLINE* n = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l); |
| 335 | replacing(t, n); |
| 336 | cache = n; |
| 337 | n->data = t->data; |
| 338 | n->icon = t->icon; |
| 339 | n->length = (short)l; |
| 340 | n->flags = t->flags; |
| 341 | n->prev = t->prev; |
| 342 | if (n->prev) n->prev->next = n; else first = n; |
| 343 | n->next = t->next; |
| 344 | if (n->next) n->next->prev = n; else last = n; |
| 345 | free(t); |
| 346 | t = n; |
| 347 | } |
| 348 | strcpy(t->txt, newtext); |
| 349 | redraw_line(t); |
| 350 | } |
| 351 | |
| 352 | /** |
| 353 | Sets the user data for specified \p line to \p d. |
| 354 | Does nothing if \p line is out of range. |
| 355 | \param[in] line The line of the item whose data() is to be changed. (1 based) |
| 356 | \param[in] d The new data to be assigned to the item. (can be NULL) |
| 357 | */ |
| 358 | void Fl_Browser::data(int line, void* d) { |
| 359 | if (line < 1 || line > lines) return; |
| 360 | find_line(line)->data = d; |
| 361 | } |
| 362 | |
| 363 | /** |
| 364 | Returns height of \p item in pixels. |
| 365 | This takes into account embedded \@ codes within the text() label. |
| 366 | \param[in] item The item whose height is returned. |
| 367 | \returns The height of the item in pixels. |
| 368 | \see item_height(), item_width(),\n |
| 369 | incr_height(), full_height() |
| 370 | */ |
| 371 | int Fl_Browser::item_height(void *item) const { |
| 372 | FL_BLINE* l = (FL_BLINE*)item; |
| 373 | if (l->flags & NOTDISPLAYED) return 0; |
| 374 | |
| 375 | int hmax = 2; // use 2 to insure we don't return a zero! |
| 376 | |
| 377 | if (!l->txt[0]) { |
| 378 | // For blank lines set the height to exactly 1 line! |
| 379 | fl_font(textfont(), textsize()); |
| 380 | int hh = fl_height(); |
| 381 | if (hh > hmax) hmax = hh; |
| 382 | } else { |
| 383 | const int* i = column_widths(); |
| 384 | long int dummy; |
| 385 | // do each column separately as they may all set different fonts: |
| 386 | for (char* str = l->txt; str && *str; str++) { |
| 387 | Fl_Font font = textfont(); // default font |
| 388 | int tsize = textsize(); // default size |
| 389 | while (*str==format_char()) { |
| 390 | str++; |
| 391 | switch (*str++) { |
| 392 | case 'l': case 'L': tsize = 24; break; |
| 393 | case 'm': case 'M': tsize = 18; break; |
| 394 | case 's': tsize = 11; break; |
| 395 | case 'b': font = (Fl_Font)(font|FL_BOLD); break; |
| 396 | case 'i': font = (Fl_Font)(font|FL_ITALIC); break; |
| 397 | case 'f': case 't': font = FL_COURIER; break; |
| 398 | case 'B': |
| 399 | case 'C': dummy = strtol(str, &str, 10); break;// skip a color number |
| 400 | case 'F': font = (Fl_Font)strtol(str,&str,10); break; |
| 401 | case 'S': tsize = strtol(str,&str,10); break; |
| 402 | case 0: case '@': str--; |
| 403 | case '.': goto END_FORMAT; |
| 404 | } |
| 405 | } |
| 406 | END_FORMAT: |
| 407 | char* ptr = str; |
| 408 | if (ptr && *i++) str = strchr(str, column_char()); |
| 409 | else str = NULL; |
| 410 | if((!str && *ptr) || (str && ptr < str)) { |
| 411 | fl_font(font, tsize); int hh = fl_height(); |
| 412 | if (hh > hmax) hmax = hh; |
| 413 | } |
| 414 | if (!str || !*str) break; |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | if (l->icon && (l->icon->h()+2)>hmax) { |
| 419 | hmax = l->icon->h() + 2; // leave 2px above/below |
| 420 | } |
| 421 | return hmax; // previous version returned hmax+2! |
| 422 | } |
| 423 | |
| 424 | /** |
| 425 | Returns width of \p item in pixels. |
| 426 | This takes into account embedded \@ codes within the text() label. |
| 427 | \param[in] item The item whose width is returned. |
| 428 | \returns The width of the item in pixels. |
| 429 | \see item_height(), item_width(),\n |
| 430 | incr_height(), full_height() |
| 431 | */ |
| 432 | int Fl_Browser::item_width(void *item) const { |
| 433 | FL_BLINE* l=(FL_BLINE*)item; |
| 434 | char* str = l->txt; |
| 435 | const int* i = column_widths(); |
| 436 | int ww = 0; |
| 437 | |
| 438 | while (*i) { // add up all tab-separated fields |
| 439 | char* e; |
| 440 | e = strchr(str, column_char()); |
| 441 | if (!e) break; // last one occupied by text |
| 442 | str = e+1; |
| 443 | ww += *i++; |
| 444 | } |
| 445 | |
| 446 | // OK, we gotta parse the string and find the string width... |
| 447 | int tsize = textsize(); |
| 448 | Fl_Font font = textfont(); |
| 449 | int done = 0; |
| 450 | |
| 451 | while (*str == format_char_ && str[1] && str[1] != format_char_) { |
| 452 | long int dummy; |
| 453 | str ++; |
| 454 | switch (*str++) { |
| 455 | case 'l': case 'L': tsize = 24; break; |
| 456 | case 'm': case 'M': tsize = 18; break; |
| 457 | case 's': tsize = 11; break; |
| 458 | case 'b': font = (Fl_Font)(font|FL_BOLD); break; |
| 459 | case 'i': font = (Fl_Font)(font|FL_ITALIC); break; |
| 460 | case 'f': case 't': font = FL_COURIER; break; |
| 461 | case 'B': |
| 462 | case 'C': dummy = strtol(str, &str, 10); break;// skip a color number |
| 463 | case 'F': font = (Fl_Font)strtol(str, &str, 10); break; |
| 464 | case 'S': tsize = strtol(str, &str, 10); break; |
| 465 | case '.': |
| 466 | done = 1; |
| 467 | break; |
| 468 | case '@': |
| 469 | str--; |
| 470 | done = 1; |
| 471 | } |
| 472 | |
| 473 | if (done) |
| 474 | break; |
| 475 | } |
| 476 | |
| 477 | if (*str == format_char_ && str[1]) |
| 478 | str ++; |
| 479 | |
| 480 | if (ww==0 && l->icon) ww = l->icon->w(); |
| 481 | |
| 482 | fl_font(font, tsize); |
| 483 | return ww + int(fl_width(str)) + 6; |
| 484 | } |
| 485 | |
| 486 | /** |
| 487 | The height of the entire list of all visible() items in pixels. |
| 488 | This returns the accumulated height of *all* the items in the browser |
| 489 | that are not hidden with hide(), including items scrolled off screen. |
| 490 | \returns The accumulated size of all the visible items in pixels. |
| 491 | \see item_height(), item_width(),\n |
| 492 | incr_height(), full_height() |
| 493 | */ |
| 494 | int Fl_Browser::full_height() const { |
| 495 | return full_height_; |
| 496 | } |
| 497 | |
| 498 | /** |
| 499 | The default 'average' item height (including inter-item spacing) in pixels. |
| 500 | This currently returns textsize() + 2. |
| 501 | \returns The value in pixels. |
| 502 | \see item_height(), item_width(),\n |
| 503 | incr_height(), full_height() |
| 504 | */ |
| 505 | int Fl_Browser::incr_height() const { |
| 506 | return textsize()+2; |
| 507 | } |
| 508 | |
| 509 | /** |
| 510 | Draws \p item at the position specified by \p X \p Y \p W \p H. |
| 511 | The \p W and \p H values are used for clipping. |
| 512 | Should only be called within the context of an FLTK draw(). |
| 513 | \param[in] item The item to be drawn |
| 514 | \param[in] X,Y,W,H position and size. |
| 515 | */ |
| 516 | void Fl_Browser::item_draw(void* item, int X, int Y, int W, int H) const { |
| 517 | FL_BLINE* l = (FL_BLINE*)item; |
| 518 | char* str = l->txt; |
| 519 | const int* i = column_widths(); |
| 520 | |
| 521 | bool first = true; // for icon |
| 522 | while (W > 6) { // do each tab-separated field |
| 523 | int w1 = W; // width for this field |
| 524 | char* e = 0; // pointer to end of field or null if none |
| 525 | if (*i) { // find end of field and temporarily replace with 0 |
| 526 | e = strchr(str, column_char()); |
| 527 | if (e) {*e = 0; w1 = *i++;} |
| 528 | } |
| 529 | // Icon drawing code |
| 530 | if (first) { |
| 531 | first = false; |
| 532 | if (l->icon) { |
| 533 | l->icon->draw(X+2,Y+1); // leave 2px left, 1px above |
| 534 | int iconw = l->icon->w()+2; |
| 535 | X += iconw; W -= iconw; w1 -= iconw; |
| 536 | } |
| 537 | } |
| 538 | int tsize = textsize(); |
| 539 | Fl_Font font = textfont(); |
| 540 | Fl_Color lcol = textcolor(); |
| 541 | Fl_Align talign = FL_ALIGN_LEFT; |
| 542 | // check for all the @-lines recognized by XForms: |
| 543 | //#if defined(__GNUC__) |
| 544 | //#warning FIXME This maybe needs to be more UTF8 aware now...? |
| 545 | //#endif /*__GNUC__*/ |
| 546 | while (*str == format_char() && *++str && *str != format_char()) { |
| 547 | long int dummy; |
| 548 | switch (*str++) { |
| 549 | case 'l': case 'L': tsize = 24; break; |
| 550 | case 'm': case 'M': tsize = 18; break; |
| 551 | case 's': tsize = 11; break; |
| 552 | case 'b': font = (Fl_Font)(font|FL_BOLD); break; |
| 553 | case 'i': font = (Fl_Font)(font|FL_ITALIC); break; |
| 554 | case 'f': case 't': font = FL_COURIER; break; |
| 555 | case 'c': talign = FL_ALIGN_CENTER; break; |
| 556 | case 'r': talign = FL_ALIGN_RIGHT; break; |
| 557 | case 'B': |
| 558 | if (!(l->flags & SELECTED)) { |
| 559 | fl_color((Fl_Color)strtol(str, &str, 10)); |
| 560 | fl_rectf(X, Y, w1, H); |
| 561 | } else dummy = strtol(str, &str, 10); |
| 562 | break; |
| 563 | case 'C': |
| 564 | lcol = (Fl_Color)strtol(str, &str, 10); |
| 565 | break; |
| 566 | case 'F': |
| 567 | font = (Fl_Font)strtol(str, &str, 10); |
| 568 | break; |
| 569 | case 'N': |
| 570 | lcol = FL_INACTIVE_COLOR; |
| 571 | break; |
| 572 | case 'S': |
| 573 | tsize = strtol(str, &str, 10); |
| 574 | break; |
| 575 | case '-': |
| 576 | fl_color(FL_DARK3); |
| 577 | fl_line(X+3, Y+H/2, X+w1-3, Y+H/2); |
| 578 | fl_color(FL_LIGHT3); |
| 579 | fl_line(X+3, Y+H/2+1, X+w1-3, Y+H/2+1); |
| 580 | break; |
| 581 | case 'u': |
| 582 | case '_': |
| 583 | fl_color(lcol); |
| 584 | fl_line(X+3, Y+H-1, X+w1-3, Y+H-1); |
| 585 | break; |
| 586 | case '.': |
| 587 | goto BREAK; |
| 588 | case '@': |
| 589 | str--; goto BREAK; |
| 590 | } |
| 591 | } |
| 592 | BREAK: |
| 593 | fl_font(font, tsize); |
| 594 | if (l->flags & SELECTED) |
| 595 | lcol = fl_contrast(lcol, selection_color()); |
| 596 | if (!active_r()) lcol = fl_inactive(lcol); |
| 597 | fl_color(lcol); |
| 598 | fl_draw(str, X+3, Y, w1-6, H, e ? Fl_Align(talign|FL_ALIGN_CLIP) : talign, 0, 0); |
| 599 | if (!e) break; // no more fields... |
| 600 | *e = column_char(); // put the separator back |
| 601 | X += w1; |
| 602 | W -= w1; |
| 603 | str = e+1; |
| 604 | } |
| 605 | } |
| 606 | |
| 607 | static const int no_columns[1] = {0}; |
| 608 | |
| 609 | /** |
| 610 | The constructor makes an empty browser. |
| 611 | \param[in] X,Y,W,H position and size. |
| 612 | \param[in] L label string, may be NULL. |
| 613 | */ |
| 614 | Fl_Browser::Fl_Browser(int X, int Y, int W, int H, const char *L) |
| 615 | : Fl_Browser_(X, Y, W, H, L) { |
| 616 | column_widths_ = no_columns; |
| 617 | lines = 0; |
| 618 | full_height_ = 0; |
| 619 | cacheline = 0; |
| 620 | format_char_ = '@'; |
| 621 | column_char_ = '\t'; |
| 622 | first = last = cache = 0; |
| 623 | } |
| 624 | |
| 625 | /** |
| 626 | Updates the browser so that \p line is shown at position \p pos. |
| 627 | \param[in] line line number. (1 based) |
| 628 | \param[in] pos position. |
| 629 | \see topline(), middleline(), bottomline() |
| 630 | */ |
| 631 | void Fl_Browser::lineposition(int line, Fl_Line_Position pos) { |
| 632 | if (line<1) line = 1; |
| 633 | if (line>lines) line = lines; |
| 634 | int p = 0; |
| 635 | |
| 636 | FL_BLINE* l; |
| 637 | for (l=first; l && line>1; l = l->next) { |
| 638 | line--; p += item_height(l); |
| 639 | } |
| 640 | if (l && (pos == BOTTOM)) p += item_height (l); |
| 641 | |
| 642 | int final = p, X, Y, W, H; |
| 643 | bbox(X, Y, W, H); |
| 644 | |
| 645 | switch(pos) { |
| 646 | case TOP: break; |
| 647 | case BOTTOM: final -= H; break; |
| 648 | case MIDDLE: final -= H/2; break; |
| 649 | } |
| 650 | |
| 651 | if (final > (full_height() - H)) final = full_height() -H; |
| 652 | position(final); |
| 653 | } |
| 654 | |
| 655 | /** |
| 656 | Returns the line that is currently visible at the top of the browser. |
| 657 | If there is no vertical scrollbar then this will always return 1. |
| 658 | \returns The lineno() of the top() of the browser. |
| 659 | */ |
| 660 | int Fl_Browser::topline() const { |
| 661 | return lineno(top()); |
| 662 | } |
| 663 | |
| 664 | /** |
| 665 | Removes all the lines in the browser. |
| 666 | \see add(), insert(), remove(), swap(int,int), clear() |
| 667 | */ |
| 668 | void Fl_Browser::clear() { |
| 669 | for (FL_BLINE* l = first; l;) { |
| 670 | FL_BLINE* n = l->next; |
| 671 | free(l); |
| 672 | l = n; |
| 673 | } |
| 674 | full_height_ = 0; |
| 675 | first = 0; |
| 676 | last = 0; |
| 677 | lines = 0; |
| 678 | new_list(); |
| 679 | } |
| 680 | |
| 681 | /** |
| 682 | Adds a new line to the end of the browser. |
| 683 | |
| 684 | The text string \p newtext may contain format characters; see format_char() for details. |
| 685 | \p newtext is copied using the strdup() function, and can be NULL to make a blank line. |
| 686 | |
| 687 | The optional void* argument \p d will be the data() for the new item. |
| 688 | |
| 689 | \param[in] newtext The label text used for the added item |
| 690 | \param[in] d Optional user data() for the item (0 if unspecified) |
| 691 | \see add(), insert(), remove(), swap(int,int), clear() |
| 692 | */ |
| 693 | void Fl_Browser::add(const char* newtext, void* d) { |
| 694 | insert(lines+1, newtext, d); |
| 695 | //Fl_Browser_::display(last); |
| 696 | } |
| 697 | |
| 698 | /** |
| 699 | Returns the label text for the specified \p line. |
| 700 | Return value can be NULL if \p line is out of range or unset. |
| 701 | The parameter \p line is 1 based. |
| 702 | \param[in] line The line number of the item whose text is returned. (1 based) |
| 703 | \returns The text string (can be NULL) |
| 704 | */ |
| 705 | const char* Fl_Browser::text(int line) const { |
| 706 | if (line < 1 || line > lines) return 0; |
| 707 | return find_line(line)->txt; |
| 708 | } |
| 709 | |
| 710 | /** |
| 711 | Returns the user data() for specified \p line. |
| 712 | Return value can be NULL if \p line is out of range or no user data() was defined. |
| 713 | The parameter \p line is 1 based (1 will be the first item in the list). |
| 714 | \param[in] line The line number of the item whose data() is returned. (1 based) |
| 715 | \returns The user data pointer (can be NULL) |
| 716 | |
| 717 | */ |
| 718 | void* Fl_Browser::data(int line) const { |
| 719 | if (line < 1 || line > lines) return 0; |
| 720 | return find_line(line)->data; |
| 721 | } |
| 722 | |
| 723 | /** |
| 724 | Sets the selection state of the item at \p line to the value \p val. |
| 725 | If \p val is not specified, the default is 1 (selects the item). |
| 726 | \param[in] line The line number of the item to be changed. (1 based) |
| 727 | \param[in] val The new selection state (1=select, 0=de-select). |
| 728 | \returns 1 if the state changed, 0 if not. |
| 729 | \see select(), selected(), value(), item_select(), item_selected() |
| 730 | */ |
| 731 | int Fl_Browser::select(int line, int val) { |
| 732 | if (line < 1 || line > lines) return 0; |
| 733 | return Fl_Browser_::select(find_line(line), val); |
| 734 | } |
| 735 | |
| 736 | /** |
| 737 | Returns 1 if specified \p line is selected, 0 if not. |
| 738 | \param[in] line The line being checked (1 based) |
| 739 | \returns 1 if item selected, 0 if not. |
| 740 | \see select(), selected(), value(), item_select(), item_selected() |
| 741 | */ |
| 742 | int Fl_Browser::selected(int line) const { |
| 743 | if (line < 1 || line > lines) return 0; |
| 744 | return find_line(line)->flags & SELECTED; |
| 745 | } |
| 746 | |
| 747 | /** |
| 748 | Makes \p line visible, and available for selection by user. |
| 749 | Opposite of hide(int). |
| 750 | This changes the full_height() if the state was changed. |
| 751 | redraw() is called automatically if a change occurred. |
| 752 | \param[in] line The line to be shown. (1 based) |
| 753 | \see show(int), hide(int), display(), visible(), make_visible() |
| 754 | */ |
| 755 | void Fl_Browser::show(int line) { |
| 756 | FL_BLINE* t = find_line(line); |
| 757 | if (t->flags & NOTDISPLAYED) { |
| 758 | t->flags &= ~NOTDISPLAYED; |
| 759 | full_height_ += item_height(t); |
| 760 | if (Fl_Browser_::displayed(t)) redraw(); |
| 761 | } |
| 762 | } |
| 763 | |
| 764 | /** |
| 765 | Makes \p line invisible, preventing selection by the user. |
| 766 | The line can still be selected under program control. |
| 767 | This changes the full_height() if the state was changed. |
| 768 | When a line is made invisible, lines below it are moved up in the display. |
| 769 | redraw() is called automatically if a change occurred. |
| 770 | \param[in] line The line to be hidden. (1 based) |
| 771 | \see show(int), hide(int), display(), visible(), make_visible() |
| 772 | */ |
| 773 | void Fl_Browser::hide(int line) { |
| 774 | FL_BLINE* t = find_line(line); |
| 775 | if (!(t->flags & NOTDISPLAYED)) { |
| 776 | full_height_ -= item_height(t); |
| 777 | t->flags |= NOTDISPLAYED; |
| 778 | if (Fl_Browser_::displayed(t)) redraw(); |
| 779 | } |
| 780 | } |
| 781 | |
| 782 | /** |
| 783 | For back compatibility. |
| 784 | This calls show(line) if \p val is true, and hide(line) otherwise. |
| 785 | If \p val is not specified, the default is 1 (makes the line visible). |
| 786 | \see show(int), hide(int), display(), visible(), make_visible() |
| 787 | */ |
| 788 | void Fl_Browser::display(int line, int val) { |
| 789 | if (line < 1 || line > lines) return; |
| 790 | if (val) show(line); else hide(line); |
| 791 | } |
| 792 | |
| 793 | /** |
| 794 | Returns non-zero if the specified \p line is visible, 0 if hidden. |
| 795 | Use show(int), hide(int), or make_visible(int) to change an item's visible state. |
| 796 | \param[in] line The line in the browser to be tested. (1 based) |
| 797 | \see show(int), hide(int), display(), visible(), make_visible() |
| 798 | */ |
| 799 | int Fl_Browser::visible(int line) const { |
| 800 | if (line < 1 || line > lines) return 0; |
| 801 | return !(find_line(line)->flags&NOTDISPLAYED); |
| 802 | } |
| 803 | |
| 804 | /** |
| 805 | Returns the line number of the currently selected line, or 0 if none. |
| 806 | \returns The line number of current selection, or 0 if none selected. |
| 807 | \see select(), selected(), value(), item_select(), item_selected() |
| 808 | */ |
| 809 | int Fl_Browser::value() const { |
| 810 | return lineno(selection()); |
| 811 | } |
| 812 | |
| 813 | /** |
| 814 | Swap the two items \p a and \p b. |
| 815 | Uses swapping() to ensure list updates correctly. |
| 816 | \param[in] a,b The two items to be swapped. |
| 817 | \see swap(int,int), item_swap() |
| 818 | */ |
| 819 | void Fl_Browser::swap(FL_BLINE *a, FL_BLINE *b) { |
| 820 | |
| 821 | if ( a == b || !a || !b) return; // nothing to do |
| 822 | swapping(a, b); |
| 823 | FL_BLINE *aprev = a->prev; |
| 824 | FL_BLINE *anext = a->next; |
| 825 | FL_BLINE *bprev = b->prev; |
| 826 | FL_BLINE *bnext = b->next; |
| 827 | if ( b->prev == a ) { // A ADJACENT TO B |
| 828 | if ( aprev ) aprev->next = b; else first = b; |
| 829 | b->next = a; |
| 830 | a->next = bnext; |
| 831 | b->prev = aprev; |
| 832 | a->prev = b; |
| 833 | if ( bnext ) bnext->prev = a; else last = a; |
| 834 | } else if ( a->prev == b ) { // B ADJACENT TO A |
| 835 | if ( bprev ) bprev->next = a; else first = a; |
| 836 | a->next = b; |
| 837 | b->next = anext; |
| 838 | a->prev = bprev; |
| 839 | b->prev = a; |
| 840 | if ( anext ) anext->prev = b; else last = b; |
| 841 | } else { // A AND B NOT ADJACENT |
| 842 | // handle prev's |
| 843 | b->prev = aprev; |
| 844 | if ( anext ) anext->prev = b; else last = b; |
| 845 | a->prev = bprev; |
| 846 | if ( bnext ) bnext->prev = a; else last = a; |
| 847 | // handle next's |
| 848 | if ( aprev ) aprev->next = b; else first = b; |
| 849 | b->next = anext; |
| 850 | if ( bprev ) bprev->next = a; else first = a; |
| 851 | a->next = bnext; |
| 852 | } |
| 853 | // Disable cache -- we played around with positions |
| 854 | cacheline = 0; |
| 855 | cache = 0; |
| 856 | } |
| 857 | |
| 858 | /** |
| 859 | Swaps two browser lines \p a and \p b. |
| 860 | You must call redraw() to make any changes visible. |
| 861 | \param[in] a,b The two lines to be swapped. (both 1 based) |
| 862 | \see swap(int,int), item_swap() |
| 863 | */ |
| 864 | void Fl_Browser::swap(int a, int b) { |
| 865 | if (a < 1 || a > lines || b < 1 || b > lines) return; |
| 866 | FL_BLINE* ai = find_line(a); |
| 867 | FL_BLINE* bi = find_line(b); |
| 868 | swap(ai,bi); |
| 869 | } |
| 870 | |
| 871 | /** |
| 872 | Set the image icon for \p line to the value \p icon. |
| 873 | Caller is responsible for keeping the icon allocated. |
| 874 | The \p line is automatically redrawn. |
| 875 | \param[in] line The line to be modified. If out of range, nothing is done. |
| 876 | \param[in] icon The image icon to be assigned to the \p line. |
| 877 | If NULL, any previous icon is removed. |
| 878 | */ |
| 879 | void Fl_Browser::icon(int line, Fl_Image* icon) { |
| 880 | |
| 881 | if (line<1 || line > lines) return; |
| 882 | |
| 883 | FL_BLINE* bl = find_line(line); |
| 884 | |
| 885 | int old_h = bl->icon ? bl->icon->h()+2 : 0; // init with *old* icon height |
| 886 | bl->icon = 0; // remove icon, if any |
| 887 | int th = item_height(bl); // height of text only |
| 888 | int new_h = icon ? icon->h()+2 : 0; // init with *new* icon height |
| 889 | if (th > old_h) old_h = th; |
| 890 | if (th > new_h) new_h = th; |
| 891 | int dh = new_h - old_h; |
| 892 | full_height_ += dh; // do this *always* |
| 893 | |
| 894 | bl->icon = icon; // set new icon |
| 895 | if (dh>0) { |
| 896 | redraw(); // icon larger than item? must redraw widget |
| 897 | } else { |
| 898 | redraw_line(bl); // icon same or smaller? can redraw just this line |
| 899 | } |
| 900 | replacing(bl,bl); // recalc Fl_Browser_::max_width et al |
| 901 | } |
| 902 | |
| 903 | /** |
| 904 | Returns the icon currently defined for \p line. |
| 905 | If no icon is defined, NULL is returned. |
| 906 | \param[in] line The line whose icon is returned. |
| 907 | \returns The icon defined, or NULL if none. |
| 908 | */ |
| 909 | Fl_Image* Fl_Browser::icon(int line) const { |
| 910 | FL_BLINE* l = find_line(line); |
| 911 | return(l ? l->icon : NULL); |
| 912 | } |
| 913 | |
| 914 | /** |
| 915 | Removes the icon for \p line. |
| 916 | It's ok to remove an icon if none has been defined. |
| 917 | \param[in] line The line whose icon is to be removed. |
| 918 | */ |
| 919 | void Fl_Browser::remove_icon(int line) { |
| 920 | icon(line,0); |
| 921 | } |
| 922 | |
| 923 | /* |
| 924 | The following constructors must not be in the header file(s) if we |
| 925 | build a shared object (DLL). Instead they are defined here to force |
| 926 | the constructor (and default destructor as well) to be defined in |
| 927 | the DLL and exported (STR #2632, #2645). |
| 928 | |
| 929 | Note: if you change any of them, do the same changes in the specific |
| 930 | header file as well. This redundant definition was chosen to enable |
| 931 | inline constructors in the header files (for static linking) as well |
| 932 | as those here for dynamic linking (Windows DLL). |
| 933 | */ |
| 934 | #if defined(FL_DLL) |
| 935 | |
| 936 | Fl_Hold_Browser::Fl_Hold_Browser(int X,int Y,int W,int H,const char *L) |
| 937 | : Fl_Browser(X,Y,W,H,L) {type(FL_HOLD_BROWSER);} |
| 938 | |
| 939 | Fl_Multi_Browser::Fl_Multi_Browser(int X,int Y,int W,int H,const char *L) |
| 940 | : Fl_Browser(X,Y,W,H,L) {type(FL_MULTI_BROWSER);} |
| 941 | |
| 942 | Fl_Select_Browser::Fl_Select_Browser(int X,int Y,int W,int H,const char *L) |
| 943 | : Fl_Browser(X,Y,W,H,L) {type(FL_SELECT_BROWSER);} |
| 944 | |
| 945 | #endif // FL_DLL |
| 946 | |
| 947 | // |
| 948 | // End of "$Id: Fl_Browser.cxx 8736 2011-05-24 20:00:56Z AlbrechtS $". |
| 949 | // |