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_Tree.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_Tree.cxx 7903 2010-11-28 21:06:39Z matt $"
00003 //
00004 
00005 #include <stdio.h>
00006 #include <stdlib.h>
00007 #include <string.h>
00008 
00009 #include <FL/Fl_Tree.H>
00010 #include <FL/Fl_Preferences.H>
00011 
00013 // Fl_Tree.cxx
00015 //
00016 // Fl_Tree -- This file is part of the Fl_Tree widget for FLTK
00017 // Copyright (C) 2009-2010 by Greg Ercolano.
00018 //
00019 // This library is free software; you can redistribute it and/or
00020 // modify it under the terms of the GNU Library General Public
00021 // License as published by the Free Software Foundation; either
00022 // version 2 of the License, or (at your option) any later version.
00023 //
00024 // This library is distributed in the hope that it will be useful,
00025 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00026 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00027 // Library General Public License for more details.
00028 //
00029 // You should have received a copy of the GNU Library General Public
00030 // License along with this library; if not, write to the Free Software
00031 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00032 // USA.
00033 //
00034 
00035 // INTERNAL: scroller callback
00036 static void scroll_cb(Fl_Widget*,void *data) {
00037   ((Fl_Tree*)data)->redraw();
00038 }
00039 
00040 // INTERNAL: Parse elements from path into an array of null terminated strings
00041 //    Path="/aa/bb"
00042 //    Return: arr[0]="aa", arr[1]="bb", arr[2]=0
00043 //    Caller must call free_path(arr).
00044 //
00045 static char **parse_path(const char *path) {
00046   while ( *path == '/' ) path++;        // skip leading '/' 
00047   // First pass: identify, null terminate, and count separators
00048   int seps = 1;                         // separator count (1: first item)
00049   int arrsize = 1;                      // array size (1: first item)
00050   char *save = strdup(path);            // make copy we can modify
00051   char *s = save;
00052   while ( ( s = strchr(s, '/') ) ) {
00053     while ( *s == '/' ) { *s++ = 0; seps++; }
00054     if ( *s ) { arrsize++; }
00055   }
00056   arrsize++;                            // (room for terminating NULL) 
00057   // Second pass: create array, save nonblank elements
00058   char **arr = (char**)malloc(sizeof(char*) * arrsize);
00059   int t = 0;
00060   s = save;
00061   while ( seps-- > 0 ) {
00062     if ( *s ) { arr[t++] = s; }         // skips empty fields, eg. '//'
00063     s += (strlen(s) + 1);
00064   }
00065   arr[t] = 0;
00066   return(arr);
00067 }
00068 
00069 // INTERNAL: Free the array returned by parse_path()
00070 static void free_path(char **arr) {
00071   if ( arr ) {
00072     if ( arr[0] ) { free((void*)arr[0]); }
00073     free((void*)arr);
00074   }
00075 }
00076 
00077 // INTERNAL: Recursively descend tree hierarchy, accumulating total child count
00078 static int find_total_children(Fl_Tree_Item *item, int count=0) {
00079   count++;
00080   for ( int t=0; t<item->children(); t++ ) {
00081     count = find_total_children(item->child(t), count);
00082   }
00083   return(count);
00084 }
00085 
00087 Fl_Tree::Fl_Tree(int X, int Y, int W, int H, const char *L) : Fl_Group(X,Y,W,H,L) { 
00088   _root = new Fl_Tree_Item(_prefs);
00089   _root->parent(0);                             // we are root of tree
00090   _root->label("ROOT");
00091   _item_focus      = 0;
00092   _callback_item   = 0;
00093   _callback_reason = FL_TREE_REASON_NONE;
00094   _scrollbar_size  = 0;                         // 0: uses Fl::scrollbar_size()
00095   box(FL_DOWN_BOX);
00096   color(FL_WHITE);
00097   when(FL_WHEN_CHANGED);
00098   _vscroll = new Fl_Scrollbar(0,0,0,0);         // will be resized by draw()
00099   _vscroll->hide();
00100   _vscroll->type(FL_VERTICAL);
00101   _vscroll->step(1);
00102   _vscroll->callback(scroll_cb, (void*)this);
00103   end();
00104 }
00105 
00107 Fl_Tree::~Fl_Tree() {
00108   if ( _root ) { delete _root; _root = 0; }
00109 }
00110 
00116 Fl_Tree_Item* Fl_Tree::add(const char *path) {
00117   if ( ! _root ) {                                      // Create root if none
00118     _root = new Fl_Tree_Item(_prefs);
00119     _root->parent(0);
00120     _root->label("ROOT");
00121   }
00122   char **arr = parse_path(path);
00123   Fl_Tree_Item *item = _root->add(_prefs, arr);
00124   free_path(arr);
00125   return(item);
00126 }
00127 
00131 Fl_Tree_Item* Fl_Tree::insert_above(Fl_Tree_Item *above, const char *name) {
00132   return(above->insert_above(_prefs, name));
00133 }
00134 
00142 Fl_Tree_Item* Fl_Tree::insert(Fl_Tree_Item *item, const char *name, int pos) {
00143   return(item->insert(_prefs, name, pos));
00144 }
00145 
00152 Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *item, const char *name) {
00153   return(item->add(_prefs, name));
00154 }
00155 
00167 Fl_Tree_Item *Fl_Tree::find_item(const char *path) {
00168   if ( ! _root ) return(0);
00169   char **arr = parse_path(path);
00170   Fl_Tree_Item *item = _root->find_item(arr);
00171   free_path(arr);
00172   return(item);
00173 }
00174 
00176 const Fl_Tree_Item *Fl_Tree::find_item(const char *path) const {
00177   if ( ! _root ) return(0);
00178   char **arr = parse_path(path);
00179   const Fl_Tree_Item *item = _root->find_item(arr);
00180   free_path(arr);
00181   return(item);
00182 }
00183 
00184 // Handle safe 'reverse string concatenation'.
00185 //   In the following we build the pathname from right-to-left,
00186 //   since we start at the child and work our way up to the root.
00187 //
00188 #define SAFE_RCAT(c) { \
00189   slen += 1; if ( slen >= pathnamelen ) { pathname[0] = '\0'; return(-2); } \
00190   *s-- = c; \
00191   }
00192 
00205 int Fl_Tree::item_pathname(char *pathname, int pathnamelen, const Fl_Tree_Item *item) const {
00206   pathname[0] = '\0';
00207   item = item ? item : _root;
00208   if ( !item ) return(-1);
00209   // Build pathname starting at end
00210   char *s = (pathname+pathnamelen-1);
00211   int slen = 0;                 // length of string compiled so far (including NULL)
00212   SAFE_RCAT('\0');
00213   while ( item ) {
00214     if ( item->is_root() && showroot() == 0 ) break;            // don't include root in path if showroot() off
00215     // Find name of current item
00216     const char *name = item->label() ? item->label() : "???";   // name for this item
00217     int len = strlen(name);
00218     // Add name to end of pathname[]
00219     for ( --len; len>=0; len-- ) { SAFE_RCAT(name[len]); }      // rcat name of item
00220     SAFE_RCAT('/');                                             // rcat leading slash
00221     item = item->parent();                                      // move up tree (NULL==root)
00222   }
00223   if ( *(++s) == '/' ) ++s;                             // leave off leading slash from pathname
00224   if ( s != pathname ) memmove(pathname, s, slen);      // Shift down right-aligned string
00225   return(0);
00226 }
00227 
00229 void Fl_Tree::draw() {
00230   // Let group draw box+label but *NOT* children.
00231   // We handle drawing children ourselves by calling each item's draw()
00232   //
00233   // Handle group's bg
00234   Fl_Group::draw_box();
00235   Fl_Group::draw_label();
00236   // Handle tree
00237   if ( ! _root ) return;
00238   int cx = x() + Fl::box_dx(box());
00239   int cy = y() + Fl::box_dy(box());
00240   int cw = w() - Fl::box_dw(box());
00241   int ch = h() - Fl::box_dh(box());
00242   // These values are changed during drawing
00243   // 'Y' will be the lowest point on the tree
00244   int X = cx + _prefs.marginleft();
00245   int Y = cy + _prefs.margintop() - (_vscroll->visible() ? _vscroll->value() : 0);
00246   int W = cw - _prefs.marginleft();                     // - _prefs.marginright();
00247   int Ysave = Y;
00248   fl_push_clip(cx,cy,cw,ch);
00249   {
00250     fl_font(_prefs.labelfont(), _prefs.labelsize());
00251     _root->draw(X, Y, W, this,
00252                 (Fl::focus()==this)?_item_focus:0,      // show focus item ONLY if Fl_Tree has focus
00253                 _prefs);
00254   }
00255   fl_pop_clip();
00256   
00257   // Show vertical scrollbar?
00258   int ydiff = (Y+_prefs.margintop())-Ysave;             // ydiff=size of tree
00259   int ytoofar = (cy+ch) - Y;                            // ytoofar -- scrolled beyond bottom (eg. stow)
00260   
00261   //printf("ydiff=%d ch=%d Ysave=%d ytoofar=%d value=%d\n",
00262   //int(ydiff),int(ch),int(Ysave),int(ytoofar), int(_vscroll->value()));
00263   
00264   if ( ytoofar > 0 ) ydiff += ytoofar;
00265   if ( Ysave<cy || ydiff > ch || int(_vscroll->value()) > 1 ) {
00266     _vscroll->visible();
00267 
00268     int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size();
00269     int sx = x()+w()-Fl::box_dx(box())-scrollsize;
00270     int sy = y()+Fl::box_dy(box());
00271     int sw = scrollsize;
00272     int sh = h()-Fl::box_dh(box());
00273     _vscroll->show();
00274     _vscroll->range(0.0,ydiff-ch);
00275     _vscroll->resize(sx,sy,sw,sh);
00276     _vscroll->slider_size(float(ch)/float(ydiff));
00277   } else {
00278     _vscroll->Fl_Slider::value(0);
00279     _vscroll->hide();
00280   }
00281   fl_push_clip(cx,cy,cw,ch);
00282   Fl_Group::draw_children();    // draws any FLTK children set via Fl_Tree::widget()
00283   fl_pop_clip();
00284 }
00285 
00294 Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) {
00295   if ( ! item ) {                               // no start item?
00296     item = ( dir == FL_Up ) ? last() : first(); // start at top or bottom
00297     if ( ! item ) return(0);
00298     if ( item->visible_r() ) return(item);      // return first/last visible item
00299   }
00300   switch ( dir ) {
00301     case FL_Up:   return(item->prev_displayed(_prefs));
00302     case FL_Down: return(item->next_displayed(_prefs));
00303     default:      return(item->next_displayed(_prefs));
00304   }
00305 }
00306 
00312 void Fl_Tree::set_item_focus(Fl_Tree_Item *item) {
00313   if ( _item_focus != item ) {          // changed?
00314     _item_focus = item;                 // update
00315     if ( visible_focus() ) redraw();    // redraw to update focus box
00316   }
00317 }
00318 
00331 const Fl_Tree_Item* Fl_Tree::find_clicked() const {
00332   if ( ! _root ) return(0);
00333   return(_root->find_clicked(_prefs));
00334 }
00335 
00342 void Fl_Tree::item_clicked(Fl_Tree_Item* val) {
00343   _callback_item = val;
00344 }
00345 
00358 Fl_Tree_Item* Fl_Tree::first() {
00359   return(_root);                                        // first item always root
00360 }
00361 
00377 Fl_Tree_Item *Fl_Tree::next(Fl_Tree_Item *item) {
00378   if ( ! item ) return(0);
00379   return(item->next());
00380 }
00381 
00398 Fl_Tree_Item *Fl_Tree::prev(Fl_Tree_Item *item) {
00399   if ( ! item ) return(0);
00400   return(item->prev());
00401 }
00402 
00417 Fl_Tree_Item* Fl_Tree::last() {
00418   if ( ! _root ) return(0);
00419   Fl_Tree_Item *item = _root;
00420   while ( item->has_children() ) {
00421     item = item->child(item->children()-1);
00422   }
00423   return(item);
00424 }
00425 
00438 Fl_Tree_Item *Fl_Tree::first_selected_item() {
00439   return(next_selected_item(0));
00440 }
00441 
00456 Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item) {
00457   if ( ! item ) {
00458     if ( ! (item = first()) ) return(0);
00459     if ( item->is_selected() ) return(item);
00460   }
00461   while ( (item = item->next()) )
00462     if ( item->is_selected() )
00463       return(item);
00464   return(0);
00465 }
00466 
00468 int Fl_Tree::handle(int e) {
00469   int ret = 0;
00470   // Developer note: Fl_Browser_::handle() used for reference here..
00471   // #include <FL/names.h>      // for event debugging
00472   // fprintf(stderr, "DEBUG: %s (%d)\n", fl_eventnames[e], e);
00473   if (e == FL_ENTER || e == FL_LEAVE) return(1);
00474   switch (e) {
00475     case FL_FOCUS: {
00476       // FLTK tests if we want focus. 
00477       //     If a nav key was used to give us focus, and we've got no saved
00478       //     focus widget, determine which item gets focus depending on nav key.
00479       //
00480       if ( ! _item_focus ) {                                    // no focus established yet?
00481         switch (Fl::event_key()) {                              // determine if focus was navigated..
00482           case FL_Tab: {                                        // received focus via TAB?
00483             if ( Fl::event_state(FL_SHIFT) ) {                  // SHIFT-TAB similar to FL_Up
00484               set_item_focus(next_visible_item(0, FL_Up));
00485             } else {                                            // TAB similar to FL_Down
00486               set_item_focus(next_visible_item(0, FL_Down));
00487             }
00488             break;
00489           }
00490           case FL_Left:         // received focus via LEFT or UP?
00491           case FL_Up: {         // XK_ISO_Left_Tab
00492             set_item_focus(next_visible_item(0, FL_Up));
00493             break;
00494           }
00495           case FL_Right:        // received focus via RIGHT or DOWN?
00496           case FL_Down:
00497           default: {
00498             set_item_focus(next_visible_item(0, FL_Down));
00499             break;
00500           }
00501         }
00502       }
00503       if ( visible_focus() ) redraw();  // draw focus change
00504       return(1);
00505     }
00506     case FL_UNFOCUS: {          // FLTK telling us some other widget took focus.
00507       if ( visible_focus() ) redraw();  // draw focus change
00508       return(1);
00509     }
00510     case FL_KEYBOARD: {         // keyboard shortcut
00511       // Do shortcuts first or scrollbar will get them...
00512       if (_prefs.selectmode() > FL_TREE_SELECT_NONE ) {
00513         if ( !_item_focus ) {
00514           set_item_focus(first());
00515         }
00516         if ( _item_focus ) {
00517           int ekey = Fl::event_key();
00518           switch (ekey) {
00519             case FL_Enter:      // ENTER: selects current item only
00520             case FL_KP_Enter:
00521               if ( when() & ~FL_WHEN_ENTER_KEY) {
00522                 select_only(_item_focus);
00523                 show_item(_item_focus);         // STR #2426
00524                 return(1);
00525               }
00526               break;
00527             case ' ':           // toggle selection state
00528               switch ( _prefs.selectmode() ) {
00529                 case FL_TREE_SELECT_NONE:
00530                   break;
00531                 case FL_TREE_SELECT_SINGLE:
00532                   if ( ! _item_focus->is_selected() )           // not selected?
00533                     select_only(_item_focus);                   // select only this
00534                   else
00535                     deselect_all();                             // select nothing
00536                   break;
00537                 case FL_TREE_SELECT_MULTI:
00538                   select_toggle(_item_focus);
00539                   break;
00540               }
00541               break;
00542             case FL_Right:      // open children (if any)
00543             case FL_Left: {     // close children (if any)
00544               if ( _item_focus ) {
00545                 if ( ekey == FL_Right && _item_focus->is_close() ) {
00546                   // Open closed item
00547                   open(_item_focus);
00548                   redraw();
00549                   ret = 1;
00550                 } else if ( ekey == FL_Left && _item_focus->is_open() ) {
00551                   // Close open item
00552                   close(_item_focus);
00553                   redraw();     
00554                   ret = 1;
00555                 }
00556                 return(1);
00557               }
00558               break;
00559             }
00560             case FL_Up:         // next item up
00561             case FL_Down: {     // next item down
00562               set_item_focus(next_visible_item(_item_focus, ekey));     // next item up|dn
00563               if ( _item_focus ) {                                      // item in focus?
00564                 // Autoscroll
00565                 int itemtop = _item_focus->y();
00566                 int itembot = _item_focus->y()+_item_focus->h();
00567                 if ( itemtop < y() ) { show_item_top(_item_focus); }
00568                 if ( itembot > y()+h() ) { show_item_bottom(_item_focus); }
00569                 // Extend selection
00570                 if ( _prefs.selectmode() == FL_TREE_SELECT_MULTI &&     // multiselect on?
00571                      (Fl::event_state() & FL_SHIFT) &&                  // shift key?
00572                      ! _item_focus->is_selected() ) {                   // not already selected?
00573                     select(_item_focus);                                // extend selection..
00574                 }
00575                 return(1);
00576               }
00577               break;
00578             }
00579           }
00580         }
00581       }
00582       break;
00583     }
00584   }
00585 
00586   // Let Fl_Group take a shot at handling the event
00587   if (Fl_Group::handle(e)) {
00588     return(1);                  // handled? don't continue below
00589   }
00590 
00591   // Handle events the child FLTK widgets didn't need
00592 
00593   static Fl_Tree_Item *lastselect = 0;
00594   // fprintf(stderr, "ERCODEBUG: Fl_Tree::handle(): Event was %s (%d)\n", fl_eventnames[e], e); // DEBUGGING
00595   if ( ! _root ) return(ret);
00596   switch ( e ) {
00597     case FL_PUSH: {                                     // clicked on a tree item?
00598       if (Fl::visible_focus() && handle(FL_FOCUS)) {
00599         Fl::focus(this);
00600       }
00601       lastselect = 0;
00602       Fl_Tree_Item *o = _root->find_clicked(_prefs);
00603       if ( ! o ) break;
00604       set_item_focus(o);                                // becomes new focus widget
00605       redraw();
00606       ret |= 1;                                         // handled
00607       if ( Fl::event_button() == FL_LEFT_MOUSE ) {
00608         if ( o->event_on_collapse_icon(_prefs) ) {      // collapse icon clicked?
00609           open_toggle(o);
00610         } else if ( o->event_on_label(_prefs) &&        // label clicked?
00611                  (!o->widget() || !Fl::event_inside(o->widget())) &&            // not inside widget
00612                  (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) {      // not on scroller
00613           switch ( _prefs.selectmode() ) {
00614             case FL_TREE_SELECT_NONE:
00615               break;
00616             case FL_TREE_SELECT_SINGLE:
00617               select_only(o);
00618               break;
00619             case FL_TREE_SELECT_MULTI: {
00620               if ( Fl::event_state() & FL_SHIFT ) {             // SHIFT+PUSH?
00621                 select(o);                                      // add to selection
00622               } else if ( Fl::event_state() & FL_CTRL ) {       // CTRL+PUSH?
00623                 select_toggle(o);                               // toggle selection state
00624                 lastselect = o;                                 // save toggled item (prevent oscillation)
00625               } else {
00626                 select_only(o);
00627               }
00628               break;
00629             }
00630           }
00631         }
00632       }
00633       break;
00634     }
00635     case FL_DRAG: {
00636       // do the scrolling first:
00637       int my = Fl::event_y();
00638       if ( my < y() ) {                         // above top?
00639         int p = vposition()-(y()-my);
00640         if ( p < 0 ) p = 0;
00641         vposition(p);
00642       } else if ( my > (y()+h()) ) {            // below bottom?
00643         int p = vposition()+(my-y()-h());
00644         if ( p > (int)_vscroll->maximum() ) p = (int)_vscroll->maximum();
00645         vposition(p);
00646       }
00647       if ( Fl::event_button() != FL_LEFT_MOUSE ) break;
00648       Fl_Tree_Item *o = _root->find_clicked(_prefs);
00649       if ( ! o ) break;
00650       set_item_focus(o);                        // becomes new focus widget
00651       redraw();
00652       ret |= 1;
00653       // Item's label clicked?
00654       if ( o->event_on_label(_prefs) && 
00655            (!o->widget() || !Fl::event_inside(o->widget())) &&
00656            (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) {
00657         // Handle selection behavior
00658         switch ( _prefs.selectmode() ) {
00659           case FL_TREE_SELECT_NONE: break;      // no selection changes
00660           case FL_TREE_SELECT_SINGLE:
00661             select_only(o);
00662             break;
00663           case FL_TREE_SELECT_MULTI:
00664             if ( Fl::event_state() & FL_CTRL && // CTRL-DRAG: toggle?
00665                  lastselect != o ) {            // not already toggled from last microdrag?
00666               select_toggle(o);                 // toggle selection
00667               lastselect = o;                   // save we toggled it (prevents oscillation)
00668             } else {
00669               select(o);                        // select this
00670             }
00671             break;
00672         }
00673       }
00674       break;
00675     }
00676   }
00677   return(ret);
00678 }
00679 
00696 int Fl_Tree::deselect_all(Fl_Tree_Item *item, int docallback) {
00697   item = item ? item : first();                 // NULL? use first()
00698   if ( ! item ) return(0);
00699   int count = 0;
00700   for ( ; item; item = next(item) ) {
00701     if ( item->is_selected() )
00702       if ( deselect(item, docallback) )
00703         ++count;
00704   }
00705   return(count);
00706 }
00707 
00725 int Fl_Tree::select_all(Fl_Tree_Item *item, int docallback) {
00726   item = item ? item : first();                 // NULL? use first()
00727   if ( ! item ) return(0);
00728   int count = 0;
00729   for ( ; item; item = next(item) ) {
00730     if ( !item->is_selected() )
00731       if ( select(item, docallback) )
00732         ++count;
00733   }
00734   return(count);
00735 }
00736 
00754 int Fl_Tree::select_only(Fl_Tree_Item *selitem, int docallback) {
00755   selitem = selitem ? selitem : first();        // NULL? use first()
00756   if ( ! selitem ) return(0);
00757   int changed = 0;
00758   for ( Fl_Tree_Item *item = first(); item; item = item->next() ) {
00759     if ( item == selitem ) {
00760       if ( item->is_selected() ) continue;      // don't count if already selected
00761       select(item, docallback);
00762       ++changed;
00763     } else {
00764       if ( item->is_selected() ) {
00765         deselect(item, docallback);
00766         ++changed;
00767       }
00768     }
00769   }
00770   return(changed);
00771 }
00772 
00784 void Fl_Tree::show_item(Fl_Tree_Item *item, int yoff) {
00785   if ( ! item ) return;
00786   int newval = item->y() - y() - yoff + (int)_vscroll->value();
00787   if ( newval < _vscroll->minimum() ) newval = (int)_vscroll->minimum();
00788   if ( newval > _vscroll->maximum() ) newval = (int)_vscroll->maximum();
00789   _vscroll->value(newval);
00790   redraw();
00791 }
00792 
00799 int Fl_Tree::displayed(Fl_Tree_Item *item) {
00800   return( (item->y() >= y() && item->y() <= (y()+h()-item->h())) ? 1 : 0);
00801 }
00802 
00809 void Fl_Tree::show_item(Fl_Tree_Item *item) {
00810   if ( displayed(item) ) return;
00811   show_item_top(item);
00812 }
00813 
00815 void Fl_Tree::show_item_top(Fl_Tree_Item *item) {
00816   item = item ? item : first();
00817   if ( ! item ) return;
00818   show_item(item, 0);
00819 }
00820 
00822 void Fl_Tree::show_item_middle(Fl_Tree_Item *item) {
00823   item = item ? item : first();
00824   if ( ! item ) return;
00825   show_item(item, h()/2 - item->h()/2);
00826 }
00827 
00829 void Fl_Tree::show_item_bottom(Fl_Tree_Item *item) {
00830   item = item ? item : first();
00831   if ( ! item ) return;
00832   show_item(item, h() - item->h());
00833 }
00834 
00841 int Fl_Tree::vposition() const {
00842   return((int)_vscroll->value());
00843 }
00844 
00851 void Fl_Tree::vposition(int pos) {
00852   if (pos < 0) pos = 0;
00853   if (pos > _vscroll->maximum()) pos = (int)_vscroll->maximum();
00854   if (pos == _vscroll->value()) return;
00855   _vscroll->value(pos);
00856   redraw();
00857 }
00858 
00862 void Fl_Tree::display(Fl_Tree_Item *item) {
00863   if ( ! item ) return;
00864   show_item_middle(item);
00865 }
00866 
00873 void Fl_Tree::load(Fl_Preferences &prefs) 
00874 {
00875   int i, j, n, pn = strlen(prefs.path());
00876   char *p;
00877   const char *path = prefs.path();
00878   if (strcmp(path, ".")==0)
00879     path += 1; // root path is empty
00880   else
00881     path += 2; // child path starts with "./"
00882   n = prefs.groups();
00883   for (i=0; i<n; i++) {
00884     Fl_Preferences prefsChild(prefs, i);
00885     add(prefsChild.path()+2); // children always start with "./"
00886     load(prefsChild);
00887   }
00888   n = prefs.entries();
00889   for (i=0; i<n; i++) {
00890     // We must remove all fwd slashes in the key and value strings. Replace with backslash.
00891     char *key = strdup(prefs.entry(i));
00892     int kn = strlen(key);
00893     for (j=0; j<kn; j++) {
00894       if (key[j]=='/') key[j]='\\'; 
00895     }
00896     char *val;  prefs.get(key, val, "");
00897     int vn = strlen(val);
00898     for (j=0; j<vn; j++) {
00899       if (val[j]=='/') val[j]='\\'; 
00900     }
00901     if (vn<40) {
00902       int sze = pn + strlen(key) + vn;
00903       p = (char*)malloc(sze+5);
00904       sprintf(p, "%s/%s = %s", path, key, val);
00905     } else {
00906       int sze = pn + strlen(key) + 40;
00907       p = (char*)malloc(sze+5);
00908       sprintf(p, "%s/%s = %.40s...", path, key, val);
00909     }
00910     add(p[0]=='/'?p+1:p);
00911     free(p);
00912     free(val);
00913     free(key);
00914   }
00915 }
00916 
00917 //
00918 // End of "$Id: Fl_Tree.cxx 7903 2010-11-28 21:06:39Z matt $".
00919 //