|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // 00002 // "$Id: Fl_Browser.cxx 7903 2010-11-28 21:06:39Z matt $" 00003 // 00004 // Browser widget for the Fast Light Tool Kit (FLTK). 00005 // 00006 // Copyright 1998-2010 by Bill Spitzak and others. 00007 // 00008 // This library is free software; you can redistribute it and/or 00009 // modify it under the terms of the GNU Library General Public 00010 // License as published by the Free Software Foundation; either 00011 // version 2 of the License, or (at your option) any later version. 00012 // 00013 // This library is distributed in the hope that it will be useful, 00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 // Library General Public License for more details. 00017 // 00018 // You should have received a copy of the GNU Library General Public 00019 // License along with this library; if not, write to the Free Software 00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00021 // USA. 00022 // 00023 // Please report all bugs and problems on the following page: 00024 // 00025 // http://www.fltk.org/str.php 00026 // 00027 00028 #include <FL/Fl.H> 00029 #include <FL/Fl_Browser.H> 00030 #include <FL/fl_draw.H> 00031 #include "flstring.h" 00032 #include <stdlib.h> 00033 #include <math.h> 00034 00035 // I modified this from the original Forms data to use a linked list 00036 // so that the number of items in the browser and size of those items 00037 // is unlimited. The only problem is that the old browser used an 00038 // index number to identify a line, and it is slow to convert from/to 00039 // a pointer. I use a cache of the last match to try to speed this up. 00040 00041 // Also added the ability to "hide" a line. This sets its height to 00042 // zero, so the Fl_Browser_ cannot pick it. 00043 00044 #define SELECTED 1 00045 #define NOTDISPLAYED 2 00046 00047 // WARNING: 00048 // Fl_File_Chooser.cxx also has a definition of this structure (FL_BLINE). 00049 // Changes to FL_BLINE *must* be reflected in Fl_File_Chooser.cxx as well. 00050 // This hack in Fl_File_Chooser should be solved. 00051 // 00052 struct FL_BLINE { // data is in a linked list of these 00053 FL_BLINE* prev; 00054 FL_BLINE* next; 00055 void* data; 00056 Fl_Image* icon; 00057 short length; // sizeof(txt)-1, may be longer than string 00058 char flags; // selected, displayed 00059 char txt[1]; // start of allocated array 00060 }; 00061 00074 void* Fl_Browser::item_first() const {return first;} 00075 00082 void* Fl_Browser::item_next(void* item) const {return ((FL_BLINE*)item)->next;} 00083 00090 void* Fl_Browser::item_prev(void* item) const {return ((FL_BLINE*)item)->prev;} 00091 00104 void* Fl_Browser::item_last() const {return last;} 00105 00112 int Fl_Browser::item_selected(void* item) const { 00113 return ((FL_BLINE*)item)->flags&SELECTED; 00114 } 00121 void Fl_Browser::item_select(void *item, int val) { 00122 if (val) ((FL_BLINE*)item)->flags |= SELECTED; 00123 else ((FL_BLINE*)item)->flags &= ~SELECTED; 00124 } 00125 00131 const char *Fl_Browser::item_text(void *item) const { 00132 return ((FL_BLINE*)item)->txt; 00133 } 00134 00151 FL_BLINE* Fl_Browser::find_line(int line) const { 00152 int n; FL_BLINE* l; 00153 if (line == cacheline) return cache; 00154 if (cacheline && line > (cacheline/2) && line < ((cacheline+lines)/2)) { 00155 n = cacheline; l = cache; 00156 } else if (line <= (lines/2)) { 00157 n = 1; l = first; 00158 } else { 00159 n = lines; l = last; 00160 } 00161 for (; n < line && l; n++) l = l->next; 00162 for (; n > line && l; n--) l = l->prev; 00163 ((Fl_Browser*)this)->cacheline = line; 00164 ((Fl_Browser*)this)->cache = l; 00165 return l; 00166 } 00167 00175 int Fl_Browser::lineno(void *item) const { 00176 FL_BLINE* l = (FL_BLINE*)item; 00177 if (!l) return 0; 00178 if (l == cache) return cacheline; 00179 if (l == first) return 1; 00180 if (l == last) return lines; 00181 if (!cache) { 00182 ((Fl_Browser*)this)->cache = first; 00183 ((Fl_Browser*)this)->cacheline = 1; 00184 } 00185 // assume it is near cache, search both directions: 00186 FL_BLINE* b = cache->prev; 00187 int bnum = cacheline-1; 00188 FL_BLINE* f = cache->next; 00189 int fnum = cacheline+1; 00190 int n = 0; 00191 for (;;) { 00192 if (b == l) {n = bnum; break;} 00193 if (f == l) {n = fnum; break;} 00194 if (b) {b = b->prev; bnum--;} 00195 if (f) {f = f->next; fnum++;} 00196 } 00197 ((Fl_Browser*)this)->cache = l; 00198 ((Fl_Browser*)this)->cacheline = n; 00199 return n; 00200 } 00201 00210 FL_BLINE* Fl_Browser::_remove(int line) { 00211 FL_BLINE* ttt = find_line(line); 00212 deleting(ttt); 00213 00214 cacheline = line-1; 00215 cache = ttt->prev; 00216 lines--; 00217 full_height_ -= item_height(ttt); 00218 if (ttt->prev) ttt->prev->next = ttt->next; 00219 else first = ttt->next; 00220 if (ttt->next) ttt->next->prev = ttt->prev; 00221 else last = ttt->prev; 00222 00223 return(ttt); 00224 } 00225 00233 void Fl_Browser::remove(int line) { 00234 if (line < 1 || line > lines) return; 00235 free(_remove(line)); 00236 } 00237 00247 void Fl_Browser::insert(int line, FL_BLINE* item) { 00248 if (!first) { 00249 item->prev = item->next = 0; 00250 first = last = item; 00251 } else if (line <= 1) { 00252 inserting(first, item); 00253 item->prev = 0; 00254 item->next = first; 00255 item->next->prev = item; 00256 first = item; 00257 } else if (line > lines) { 00258 item->prev = last; 00259 item->prev->next = item; 00260 item->next = 0; 00261 last = item; 00262 } else { 00263 FL_BLINE* n = find_line(line); 00264 inserting(n, item); 00265 item->next = n; 00266 item->prev = n->prev; 00267 item->prev->next = item; 00268 n->prev = item; 00269 } 00270 cacheline = line; 00271 cache = item; 00272 lines++; 00273 full_height_ += item_height(item); 00274 redraw_line(item); 00275 } 00276 00290 void Fl_Browser::insert(int line, const char* newtext, void* d) { 00291 int l = strlen(newtext); 00292 FL_BLINE* t = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l); 00293 t->length = (short)l; 00294 t->flags = 0; 00295 strcpy(t->txt, newtext); 00296 t->data = d; 00297 t->icon = 0; 00298 insert(line, t); 00299 } 00300 00307 void Fl_Browser::move(int to, int from) { 00308 if (from < 1 || from > lines) return; 00309 insert(to, _remove(from)); 00310 } 00311 00323 void Fl_Browser::text(int line, const char* newtext) { 00324 if (line < 1 || line > lines) return; 00325 FL_BLINE* t = find_line(line); 00326 int l = strlen(newtext); 00327 if (l > t->length) { 00328 FL_BLINE* n = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l); 00329 replacing(t, n); 00330 cache = n; 00331 n->data = t->data; 00332 n->icon = t->icon; 00333 n->length = (short)l; 00334 n->flags = t->flags; 00335 n->prev = t->prev; 00336 if (n->prev) n->prev->next = n; else first = n; 00337 n->next = t->next; 00338 if (n->next) n->next->prev = n; else last = n; 00339 free(t); 00340 t = n; 00341 } 00342 strcpy(t->txt, newtext); 00343 redraw_line(t); 00344 } 00345 00352 void Fl_Browser::data(int line, void* d) { 00353 if (line < 1 || line > lines) return; 00354 find_line(line)->data = d; 00355 } 00356 00365 int Fl_Browser::item_height(void *item) const { 00366 FL_BLINE* l = (FL_BLINE*)item; 00367 if (l->flags & NOTDISPLAYED) return 0; 00368 00369 int hmax = 2; // use 2 to insure we don't return a zero! 00370 00371 if (!l->txt[0]) { 00372 // For blank lines set the height to exactly 1 line! 00373 fl_font(textfont(), textsize()); 00374 int hh = fl_height(); 00375 if (hh > hmax) hmax = hh; 00376 } else { 00377 const int* i = column_widths(); 00378 long int dummy; 00379 // do each column separately as they may all set different fonts: 00380 for (char* str = l->txt; str && *str; str++) { 00381 Fl_Font font = textfont(); // default font 00382 int tsize = textsize(); // default size 00383 while (*str==format_char()) { 00384 str++; 00385 switch (*str++) { 00386 case 'l': case 'L': tsize = 24; break; 00387 case 'm': case 'M': tsize = 18; break; 00388 case 's': tsize = 11; break; 00389 case 'b': font = (Fl_Font)(font|FL_BOLD); break; 00390 case 'i': font = (Fl_Font)(font|FL_ITALIC); break; 00391 case 'f': case 't': font = FL_COURIER; break; 00392 case 'B': 00393 case 'C': dummy = strtol(str, &str, 10); break;// skip a color number 00394 case 'F': font = (Fl_Font)strtol(str,&str,10); break; 00395 case 'S': tsize = strtol(str,&str,10); break; 00396 case 0: case '@': str--; 00397 case '.': goto END_FORMAT; 00398 } 00399 } 00400 END_FORMAT: 00401 char* ptr = str; 00402 if (ptr && *i++) str = strchr(str, column_char()); 00403 else str = NULL; 00404 if((!str && *ptr) || (str && ptr < str)) { 00405 fl_font(font, tsize); int hh = fl_height(); 00406 if (hh > hmax) hmax = hh; 00407 } 00408 if (!str || !*str) break; 00409 } 00410 } 00411 00412 if (l->icon && (l->icon->h()+2)>hmax) { 00413 hmax = l->icon->h() + 2; // leave 2px above/below 00414 } 00415 return hmax; // previous version returned hmax+2! 00416 } 00417 00426 int Fl_Browser::item_width(void *item) const { 00427 FL_BLINE* l=(FL_BLINE*)item; 00428 char* str = l->txt; 00429 const int* i = column_widths(); 00430 int ww = 0; 00431 00432 while (*i) { // add up all tab-separated fields 00433 char* e; 00434 e = strchr(str, column_char()); 00435 if (!e) break; // last one occupied by text 00436 str = e+1; 00437 ww += *i++; 00438 } 00439 00440 // OK, we gotta parse the string and find the string width... 00441 int tsize = textsize(); 00442 Fl_Font font = textfont(); 00443 int done = 0; 00444 00445 while (*str == format_char_ && str[1] && str[1] != format_char_) { 00446 long int dummy; 00447 str ++; 00448 switch (*str++) { 00449 case 'l': case 'L': tsize = 24; break; 00450 case 'm': case 'M': tsize = 18; break; 00451 case 's': tsize = 11; break; 00452 case 'b': font = (Fl_Font)(font|FL_BOLD); break; 00453 case 'i': font = (Fl_Font)(font|FL_ITALIC); break; 00454 case 'f': case 't': font = FL_COURIER; break; 00455 case 'B': 00456 case 'C': dummy = strtol(str, &str, 10); break;// skip a color number 00457 case 'F': font = (Fl_Font)strtol(str, &str, 10); break; 00458 case 'S': tsize = strtol(str, &str, 10); break; 00459 case '.': 00460 done = 1; 00461 break; 00462 case '@': 00463 str--; 00464 done = 1; 00465 } 00466 00467 if (done) 00468 break; 00469 } 00470 00471 if (*str == format_char_ && str[1]) 00472 str ++; 00473 00474 if (ww==0 && l->icon) ww = l->icon->w(); 00475 00476 fl_font(font, tsize); 00477 return ww + int(fl_width(str)) + 6; 00478 } 00479 00488 int Fl_Browser::full_height() const { 00489 return full_height_; 00490 } 00491 00499 int Fl_Browser::incr_height() const { 00500 return textsize()+2; 00501 } 00502 00510 void Fl_Browser::item_draw(void* item, int X, int Y, int W, int H) const { 00511 FL_BLINE* l = (FL_BLINE*)item; 00512 char* str = l->txt; 00513 const int* i = column_widths(); 00514 00515 bool first = true; // for icon 00516 while (W > 6) { // do each tab-separated field 00517 int w1 = W; // width for this field 00518 char* e = 0; // pointer to end of field or null if none 00519 if (*i) { // find end of field and temporarily replace with 0 00520 e = strchr(str, column_char()); 00521 if (e) {*e = 0; w1 = *i++;} 00522 } 00523 // Icon drawing code 00524 if (first) { 00525 first = false; 00526 if (l->icon) { 00527 l->icon->draw(X+2,Y+1); // leave 2px left, 1px above 00528 int iconw = l->icon->w()+2; 00529 X += iconw; W -= iconw; w1 -= iconw; 00530 } 00531 } 00532 int tsize = textsize(); 00533 Fl_Font font = textfont(); 00534 Fl_Color lcol = textcolor(); 00535 Fl_Align talign = FL_ALIGN_LEFT; 00536 // check for all the @-lines recognized by XForms: 00537 //#if defined(__GNUC__) 00538 //#warning FIXME This maybe needs to be more UTF8 aware now...? 00539 //#endif /*__GNUC__*/ 00540 while (*str == format_char() && *++str && *str != format_char()) { 00541 long int dummy; 00542 switch (*str++) { 00543 case 'l': case 'L': tsize = 24; break; 00544 case 'm': case 'M': tsize = 18; break; 00545 case 's': tsize = 11; break; 00546 case 'b': font = (Fl_Font)(font|FL_BOLD); break; 00547 case 'i': font = (Fl_Font)(font|FL_ITALIC); break; 00548 case 'f': case 't': font = FL_COURIER; break; 00549 case 'c': talign = FL_ALIGN_CENTER; break; 00550 case 'r': talign = FL_ALIGN_RIGHT; break; 00551 case 'B': 00552 if (!(l->flags & SELECTED)) { 00553 fl_color((Fl_Color)strtol(str, &str, 10)); 00554 fl_rectf(X, Y, w1, H); 00555 } else dummy = strtol(str, &str, 10); 00556 break; 00557 case 'C': 00558 lcol = (Fl_Color)strtol(str, &str, 10); 00559 break; 00560 case 'F': 00561 font = (Fl_Font)strtol(str, &str, 10); 00562 break; 00563 case 'N': 00564 lcol = FL_INACTIVE_COLOR; 00565 break; 00566 case 'S': 00567 tsize = strtol(str, &str, 10); 00568 break; 00569 case '-': 00570 fl_color(FL_DARK3); 00571 fl_line(X+3, Y+H/2, X+w1-3, Y+H/2); 00572 fl_color(FL_LIGHT3); 00573 fl_line(X+3, Y+H/2+1, X+w1-3, Y+H/2+1); 00574 break; 00575 case 'u': 00576 case '_': 00577 fl_color(lcol); 00578 fl_line(X+3, Y+H-1, X+w1-3, Y+H-1); 00579 break; 00580 case '.': 00581 goto BREAK; 00582 case '@': 00583 str--; goto BREAK; 00584 } 00585 } 00586 BREAK: 00587 fl_font(font, tsize); 00588 if (l->flags & SELECTED) 00589 lcol = fl_contrast(lcol, selection_color()); 00590 if (!active_r()) lcol = fl_inactive(lcol); 00591 fl_color(lcol); 00592 fl_draw(str, X+3, Y, w1-6, H, e ? Fl_Align(talign|FL_ALIGN_CLIP) : talign, 0, 0); 00593 if (!e) break; // no more fields... 00594 *e = column_char(); // put the separator back 00595 X += w1; 00596 W -= w1; 00597 str = e+1; 00598 } 00599 } 00600 00601 static const int no_columns[1] = {0}; 00602 00608 Fl_Browser::Fl_Browser(int X, int Y, int W, int H, const char *L) 00609 : Fl_Browser_(X, Y, W, H, L) { 00610 column_widths_ = no_columns; 00611 lines = 0; 00612 full_height_ = 0; 00613 cacheline = 0; 00614 format_char_ = '@'; 00615 column_char_ = '\t'; 00616 first = last = cache = 0; 00617 } 00618 00625 void Fl_Browser::lineposition(int line, Fl_Line_Position pos) { 00626 if (line<1) line = 1; 00627 if (line>lines) line = lines; 00628 int p = 0; 00629 00630 FL_BLINE* l; 00631 for (l=first; l && line>1; l = l->next) { 00632 line--; p += item_height(l); 00633 } 00634 if (l && (pos == BOTTOM)) p += item_height (l); 00635 00636 int final = p, X, Y, W, H; 00637 bbox(X, Y, W, H); 00638 00639 switch(pos) { 00640 case TOP: break; 00641 case BOTTOM: final -= H; break; 00642 case MIDDLE: final -= H/2; break; 00643 } 00644 00645 if (final > (full_height() - H)) final = full_height() -H; 00646 position(final); 00647 } 00648 00654 int Fl_Browser::topline() const { 00655 return lineno(top()); 00656 } 00657 00662 void Fl_Browser::clear() { 00663 for (FL_BLINE* l = first; l;) { 00664 FL_BLINE* n = l->next; 00665 free(l); 00666 l = n; 00667 } 00668 full_height_ = 0; 00669 first = 0; 00670 last = 0; 00671 lines = 0; 00672 new_list(); 00673 } 00674 00687 void Fl_Browser::add(const char* newtext, void* d) { 00688 insert(lines+1, newtext, d); 00689 //Fl_Browser_::display(last); 00690 } 00691 00699 const char* Fl_Browser::text(int line) const { 00700 if (line < 1 || line > lines) return 0; 00701 return find_line(line)->txt; 00702 } 00703 00712 void* Fl_Browser::data(int line) const { 00713 if (line < 1 || line > lines) return 0; 00714 return find_line(line)->data; 00715 } 00716 00725 int Fl_Browser::select(int line, int val) { 00726 if (line < 1 || line > lines) return 0; 00727 return Fl_Browser_::select(find_line(line), val); 00728 } 00729 00736 int Fl_Browser::selected(int line) const { 00737 if (line < 1 || line > lines) return 0; 00738 return find_line(line)->flags & SELECTED; 00739 } 00740 00749 void Fl_Browser::show(int line) { 00750 FL_BLINE* t = find_line(line); 00751 if (t->flags & NOTDISPLAYED) { 00752 t->flags &= ~NOTDISPLAYED; 00753 full_height_ += item_height(t); 00754 if (Fl_Browser_::displayed(t)) redraw(); 00755 } 00756 } 00757 00767 void Fl_Browser::hide(int line) { 00768 FL_BLINE* t = find_line(line); 00769 if (!(t->flags & NOTDISPLAYED)) { 00770 full_height_ -= item_height(t); 00771 t->flags |= NOTDISPLAYED; 00772 if (Fl_Browser_::displayed(t)) redraw(); 00773 } 00774 } 00775 00782 void Fl_Browser::display(int line, int val) { 00783 if (line < 1 || line > lines) return; 00784 if (val) show(line); else hide(line); 00785 } 00786 00793 int Fl_Browser::visible(int line) const { 00794 if (line < 1 || line > lines) return 0; 00795 return !(find_line(line)->flags&NOTDISPLAYED); 00796 } 00797 00803 int Fl_Browser::value() const { 00804 return lineno(selection()); 00805 } 00806 00813 void Fl_Browser::swap(FL_BLINE *a, FL_BLINE *b) { 00814 00815 if ( a == b || !a || !b) return; // nothing to do 00816 swapping(a, b); 00817 FL_BLINE *aprev = a->prev; 00818 FL_BLINE *anext = a->next; 00819 FL_BLINE *bprev = b->prev; 00820 FL_BLINE *bnext = b->next; 00821 if ( b->prev == a ) { // A ADJACENT TO B 00822 if ( aprev ) aprev->next = b; else first = b; 00823 b->next = a; 00824 a->next = bnext; 00825 b->prev = aprev; 00826 a->prev = b; 00827 if ( bnext ) bnext->prev = a; else last = a; 00828 } else if ( a->prev == b ) { // B ADJACENT TO A 00829 if ( bprev ) bprev->next = a; else first = a; 00830 a->next = b; 00831 b->next = anext; 00832 a->prev = bprev; 00833 b->prev = a; 00834 if ( anext ) anext->prev = b; else last = b; 00835 } else { // A AND B NOT ADJACENT 00836 // handle prev's 00837 b->prev = aprev; 00838 if ( anext ) anext->prev = b; else last = b; 00839 a->prev = bprev; 00840 if ( bnext ) bnext->prev = a; else last = a; 00841 // handle next's 00842 if ( aprev ) aprev->next = b; else first = b; 00843 b->next = anext; 00844 if ( bprev ) bprev->next = a; else first = a; 00845 a->next = bnext; 00846 } 00847 // Disable cache -- we played around with positions 00848 cacheline = 0; 00849 cache = 0; 00850 } 00851 00858 void Fl_Browser::swap(int a, int b) { 00859 if (a < 1 || a > lines || b < 1 || b > lines) return; 00860 FL_BLINE* ai = find_line(a); 00861 FL_BLINE* bi = find_line(b); 00862 swap(ai,bi); 00863 } 00864 00873 void Fl_Browser::icon(int line, Fl_Image* icon) { 00874 00875 if (line<1 || line > lines) return; 00876 00877 FL_BLINE* bl = find_line(line); 00878 00879 int old_h = bl->icon ? bl->icon->h()+2 : 0; // init with *old* icon height 00880 bl->icon = 0; // remove icon, if any 00881 int th = item_height(bl); // height of text only 00882 int new_h = icon ? icon->h()+2 : 0; // init with *new* icon height 00883 if (th > old_h) old_h = th; 00884 if (th > new_h) new_h = th; 00885 int dh = new_h - old_h; 00886 full_height_ += dh; // do this *always* 00887 00888 bl->icon = icon; // set new icon 00889 if (dh>0) { 00890 redraw(); // icon larger than item? must redraw widget 00891 } else { 00892 redraw_line(bl); // icon same or smaller? can redraw just this line 00893 } 00894 replacing(bl,bl); // recalc Fl_Browser_::max_width et al 00895 } 00896 00903 Fl_Image* Fl_Browser::icon(int line) const { 00904 FL_BLINE* l = find_line(line); 00905 return(l ? l->icon : NULL); 00906 } 00907 00913 void Fl_Browser::remove_icon(int line) { 00914 icon(line,0); 00915 } 00916 00917 // 00918 // End of "$Id: Fl_Browser.cxx 7903 2010-11-28 21:06:39Z matt $". 00919 //