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)  

Fl_Browser.cxx

Go to the documentation of this file.
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 //