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 // Base Browser widget class 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 #define DISPLAY_SEARCH_BOTH_WAYS_AT_ONCE
00029 
00030 #include <stdio.h>
00031 #include <FL/Fl.H>
00032 #include <FL/Fl_Widget.H>
00033 #include <FL/Fl_Browser_.H>
00034 #include <FL/fl_draw.H>
00035 
00036 
00037 // This is the base class for browsers.  To be useful it must be
00038 // subclassed and several virtual functions defined.  The
00039 // Forms-compatible browser and the file chooser's browser are
00040 // subclassed off of this.
00041 
00042 // Yes, I know this should be a template...
00043 
00044 // This has been designed so that the subclass has complete control
00045 // over the storage of the data, although because next() and prev()
00046 // functions are used to index, it works best as a linked list or as a
00047 // large block of characters in which the line breaks must be searched
00048 // for.
00049 
00050 // A great deal of work has been done so that the "height" of a data
00051 // object does not need to be determined until it is drawn.  This was
00052 // done for the file chooser, because the height requires doing stat()
00053 // to see if the file is a directory, which can be annoyingly slow
00054 // over the network.
00055 
00056 /* redraw bits:
00057    1 = redraw children (the scrollbar)
00058    2 = redraw one or two items
00059    4 = redraw all items
00060 */
00061 
00062 static void scrollbar_callback(Fl_Widget* s, void*) {
00063   ((Fl_Browser_*)(s->parent()))->position(int(((Fl_Scrollbar*)s)->value()));
00064 }
00065 
00066 static void hscrollbar_callback(Fl_Widget* s, void*) {
00067   ((Fl_Browser_*)(s->parent()))->hposition(int(((Fl_Scrollbar*)s)->value()));
00068 }
00069 
00070 // return where to draw the actual box:
00077 void Fl_Browser_::bbox(int& X, int& Y, int& W, int& H) const {
00078   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
00079   Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
00080   X = x()+Fl::box_dx(b);
00081   Y = y()+Fl::box_dy(b);
00082   W = w()-Fl::box_dw(b);
00083   H = h()-Fl::box_dh(b);
00084   if (scrollbar.visible()) {
00085     W -= scrollsize;
00086     if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollsize;
00087   }
00088   if (W < 0) W = 0;
00089   if (hscrollbar.visible()) {
00090     H -= scrollsize;
00091     if (scrollbar.align() & FL_ALIGN_TOP) Y += scrollsize;
00092   }
00093   if (H < 0) H = 0;
00094 }
00095 
00102 int Fl_Browser_::leftedge() const {
00103   int X, Y, W, H; bbox(X, Y, W, H);
00104   return X;
00105 }
00106 
00107 // The scrollbars may be moved again by draw(), since each one's size
00108 // depends on whether the other is visible or not.  This skips over
00109 // Fl_Group::resize since it moves the scrollbars uselessly.
00114 void Fl_Browser_::resize(int X, int Y, int W, int H) {
00115   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
00116   Fl_Widget::resize(X, Y, W, H);
00117   // move the scrollbars so they can respond to events:
00118   bbox(X,Y,W,H);
00119   scrollbar.resize(
00120         scrollbar.align()&FL_ALIGN_LEFT ? X-scrollsize : X+W,
00121         Y, scrollsize, H);
00122   hscrollbar.resize(
00123         X, scrollbar.align()&FL_ALIGN_TOP ? Y-scrollsize : Y+H,
00124         W, scrollsize);
00125 }
00126 
00127 // Cause minimal update to redraw the given item:
00134 void Fl_Browser_::redraw_line(void* item) {
00135   if (!redraw1 || redraw1 == item) {redraw1 = item; damage(FL_DAMAGE_EXPOSE);}
00136   else if (!redraw2 || redraw2 == item) {redraw2 = item; damage(FL_DAMAGE_EXPOSE);}
00137   else damage(FL_DAMAGE_SCROLL);
00138 }
00139 
00140 // Figure out top() based on position():
00141 void Fl_Browser_::update_top() {
00142   if (!top_) top_ = item_first();
00143   if (position_ != real_position_) {
00144     void* l;
00145     int ly;
00146     int yy = position_;
00147     // start from either head or current position, whichever is closer:
00148     if (!top_ || yy <= (real_position_/2)) {
00149       l = item_first();
00150       ly = 0;
00151     } else {
00152       l = top_;
00153       ly = real_position_-offset_;
00154     }
00155     if (!l) {
00156       top_ = 0;
00157       offset_ = 0;
00158       real_position_ = 0;
00159     } else {
00160       int hh = item_quick_height(l);
00161       // step through list until we find line containing this point:
00162       while (ly > yy) {
00163         void* l1 = item_prev(l);
00164         if (!l1) {ly = 0; break;} // hit the top
00165         l  = l1;
00166         hh = item_quick_height(l);
00167         ly -= hh;
00168       }
00169       while ((ly+hh) <= yy) {
00170         void* l1 = item_next(l);
00171         if (!l1) {yy = ly+hh-1; break;}
00172         l = l1;
00173         ly += hh;
00174         hh = item_quick_height(l);
00175       }
00176       // top item must *really* be visible, use slow height:
00177       for (;;) {
00178         hh = item_height(l);
00179         if ((ly+hh) > yy) break; // it is big enough to see
00180         // go up to top of previous item:
00181         void* l1 = item_prev(l);
00182         if (!l1) {ly = yy = 0; break;} // hit the top
00183         l = l1; yy = position_ = ly = ly-item_quick_height(l);
00184       }
00185       // use it:
00186       top_ = l;
00187       offset_ = yy-ly;
00188       real_position_ = yy;
00189     }
00190     damage(FL_DAMAGE_SCROLL);
00191   }
00192 }
00193 
00194 // Change position(), top() will update when update_top() is called
00195 // (probably by draw() or handle()):
00204 void Fl_Browser_::position(int pos) {
00205   if (pos < 0) pos = 0;
00206   if (pos == position_) return;
00207   position_ = pos;
00208   if (pos != real_position_) redraw_lines();
00209 }
00210 
00219 void Fl_Browser_::hposition(int pos) {
00220   if (pos < 0) pos = 0;
00221   if (pos == hposition_) return;
00222   hposition_ = pos;
00223   if (pos != real_hposition_) redraw_lines();
00224 }
00225 
00226 // Tell whether item is currently displayed:
00236 int Fl_Browser_::displayed(void* item) const {
00237   int X, Y, W, H; bbox(X, Y, W, H);
00238   int yy = H+offset_;
00239   for (void* l = top_; l && yy > 0; l = item_next(l)) {
00240     if (l == item) return 1;
00241     yy -= item_height(l);
00242   }
00243   return 0;
00244 }
00245 
00246 // Ensure this item is displayed:
00247 // Messy because we have no idea if it is before top or after bottom:
00253 void Fl_Browser_::display(void* item) {
00254 
00255   // First special case - want to display first item in the list?
00256   update_top();
00257   if (item == item_first()) {position(0); return;}
00258 
00259   int X, Y, W, H, Yp; bbox(X, Y, W, H);
00260   void* l = top_;
00261   Y = Yp = -offset_;
00262   int h1;
00263 
00264   // 2nd special case - want to display item already displayed at top of browser?
00265   if (l == item) {position(real_position_+Y); return;} // scroll up a bit
00266 
00267   // 3rd special case - want to display item just above top of browser?
00268   void* lp = item_prev(l);
00269   if (lp == item) {position(real_position_+Y-item_quick_height(lp)); return;}
00270 
00271 #ifdef DISPLAY_SEARCH_BOTH_WAYS_AT_ONCE
00272   // search for item.  We search both up and down the list at the same time,
00273   // this evens up the execution time for the two cases - the old way was
00274   // much slower for going up than for going down.
00275   while (l || lp) {
00276     if (l) {
00277       h1 = item_quick_height(l);
00278       if (l == item) {
00279         if (Y <= H) { // it is visible or right at bottom
00280           Y = Y+h1-H; // find where bottom edge is
00281           if (Y > 0) position(real_position_+Y); // scroll down a bit
00282         } else {
00283           position(real_position_+Y-(H-h1)/2); // center it
00284         }
00285         return;
00286       }
00287       Y += h1;
00288       l = item_next(l);
00289     }
00290     if (lp) {
00291       h1 = item_quick_height(lp);
00292       Yp -= h1;
00293       if (lp == item) {
00294         if ((Yp + h1) >= 0) position(real_position_+Yp);
00295         else position(real_position_+Yp-(H-h1)/2);
00296         return;
00297       }
00298       lp = item_prev(lp);
00299     }
00300   }
00301 #else
00302   // Old version went forwards and then backwards:
00303   // search forward for it:
00304   l = top_;
00305   for (; l; l = item_next(l)) {
00306     h1 = item_quick_height(l);
00307     if (l == item) {
00308       if (Y <= H) { // it is visible or right at bottom
00309         Y = Y+h1-H; // find where bottom edge is
00310         if (Y > 0) position(real_position_+Y); // scroll down a bit
00311       } else {
00312         position(real_position_+Y-(H-h1)/2); // center it
00313       }
00314       return;
00315     }
00316     Y += h1;
00317   }
00318   // search backward for it, if found center it:
00319   l = lp;
00320   Y = -offset_;
00321   for (; l; l = item_prev(l)) {
00322     h1 = item_quick_height(l);
00323     Y -= h1;
00324     if (l == item) {
00325       if ((Y + h1) >= 0) position(real_position_+Y);
00326       else position(real_position_+Y-(H-h1)/2);
00327       return;
00328     }
00329   }
00330 #endif
00331 }
00332 
00333 // redraw, has side effect of updating top and setting scrollbar:
00337 void Fl_Browser_::draw() {
00338   int drawsquare = 0;
00339   update_top();
00340   int full_width_ = full_width();
00341   int full_height_ = full_height();
00342   int X, Y, W, H; bbox(X, Y, W, H);
00343   int dont_repeat = 0;
00344 J1:
00345   if (damage() & FL_DAMAGE_ALL) { // redraw the box if full redraw
00346     Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
00347     draw_box(b, x(), y(), w(), h(), color());
00348     drawsquare = 1;
00349   }
00350   // see if scrollbar needs to be switched on/off:
00351   if ((has_scrollbar_ & VERTICAL) && (
00352         (has_scrollbar_ & ALWAYS_ON) || position_ || full_height_ > H)) {
00353     if (!scrollbar.visible()) {
00354       scrollbar.set_visible();
00355       drawsquare = 1;
00356       bbox(X, Y, W, H);
00357     }
00358   } else {
00359     top_ = item_first(); real_position_ = offset_ = 0;
00360     if (scrollbar.visible()) {
00361       scrollbar.clear_visible();
00362       clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
00363     }
00364   }
00365 
00366   if ((has_scrollbar_ & HORIZONTAL) && (
00367         (has_scrollbar_ & ALWAYS_ON) || hposition_ || full_width_ > W)) {
00368     if (!hscrollbar.visible()) {
00369       hscrollbar.set_visible();
00370       drawsquare = 1;
00371       bbox(X, Y, W, H);
00372     }
00373   } else {
00374     real_hposition_ = 0;
00375     if (hscrollbar.visible()) {
00376       hscrollbar.clear_visible();
00377       clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
00378     }
00379   }
00380 
00381   // Check the vertical scrollbar again, just in case it needs to be drawn
00382   // because the horizontal one is drawn.  There should be a cleaner way
00383   // to do this besides copying the same code...
00384   if ((has_scrollbar_ & VERTICAL) && (
00385         (has_scrollbar_ & ALWAYS_ON) || position_ || full_height_ > H)) {
00386     if (!scrollbar.visible()) {
00387       scrollbar.set_visible();
00388       drawsquare = 1;
00389       bbox(X, Y, W, H);
00390     }
00391   } else {
00392     top_ = item_first(); real_position_ = offset_ = 0;
00393     if (scrollbar.visible()) {
00394       scrollbar.clear_visible();
00395       clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
00396     }
00397   }
00398 
00399   bbox(X, Y, W, H);
00400 
00401   fl_push_clip(X, Y, W, H);
00402   // for each line, draw it if full redraw or scrolled.  Erase background
00403   // if not a full redraw or if it is selected:
00404   void* l = top();
00405   int yy = -offset_;
00406   for (; l && yy < H; l = item_next(l)) {
00407     int hh = item_height(l);
00408     if (hh <= 0) continue;
00409     if ((damage()&(FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) || l == redraw1 || l == redraw2) {
00410       if (item_selected(l)) {
00411         fl_color(active_r() ? selection_color() : fl_inactive(selection_color()));
00412         fl_rectf(X, yy+Y, W, hh);
00413       } else if (!(damage()&FL_DAMAGE_ALL)) {
00414         fl_push_clip(X, yy+Y, W, hh);
00415         draw_box(box() ? box() : FL_DOWN_BOX, x(), y(), w(), h(), color());
00416         fl_pop_clip();
00417       }
00418       item_draw(l, X-hposition_, yy+Y, W+hposition_, hh);
00419       if (l == selection_ && Fl::focus() == this) {
00420         draw_box(FL_BORDER_FRAME, X, yy+Y, W, hh, color());
00421         draw_focus(FL_NO_BOX, X, yy+Y, W+1, hh+1);
00422       }
00423       int ww = item_width(l);
00424       if (ww > max_width) {max_width = ww; max_width_item = l;}
00425     }
00426     yy += hh;
00427   }
00428   // erase the area below last line:
00429   if (!(damage()&FL_DAMAGE_ALL) && yy < H) {
00430     fl_push_clip(X, yy+Y, W, H-yy);
00431     draw_box(box() ? box() : FL_DOWN_BOX, x(), y(), w(), h(), color());
00432     fl_pop_clip();
00433   }
00434   fl_pop_clip();
00435   redraw1 = redraw2 = 0;
00436 
00437   if (!dont_repeat) {
00438     dont_repeat = 1;
00439     // see if changes to full_height caused by calls to slow_height
00440     // caused scrollbar state to change, in which case we have to redraw:
00441     full_height_ = full_height();
00442     full_width_ = full_width();
00443     if ((has_scrollbar_ & VERTICAL) &&
00444         ((has_scrollbar_ & ALWAYS_ON) || position_ || full_height_>H)) {
00445       if (!scrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
00446     } else {
00447       if (scrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
00448     }
00449     if ((has_scrollbar_ & HORIZONTAL) &&
00450         ((has_scrollbar_ & ALWAYS_ON) || hposition_ || full_width_>W)) {
00451       if (!hscrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
00452     } else {
00453       if (hscrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
00454     }
00455   }
00456 
00457   // update the scrollbars and redraw them:
00458   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
00459   int dy = top_ ? item_quick_height(top_) : 0; if (dy < 10) dy = 10;
00460   if (scrollbar.visible()) {
00461     scrollbar.damage_resize(
00462         scrollbar.align()&FL_ALIGN_LEFT ? X-scrollsize : X+W,
00463         Y, scrollsize, H);
00464     scrollbar.value(position_, H, 0, full_height_);
00465     scrollbar.linesize(dy);
00466     if (drawsquare) draw_child(scrollbar);
00467     else update_child(scrollbar);
00468   }
00469   if (hscrollbar.visible()) {
00470     hscrollbar.damage_resize(
00471         X, scrollbar.align()&FL_ALIGN_TOP ? Y-scrollsize : Y+H,
00472         W, scrollsize);
00473     hscrollbar.value(hposition_, W, 0, full_width_);
00474     hscrollbar.linesize(dy);
00475     if (drawsquare) draw_child(hscrollbar);
00476     else update_child(hscrollbar);
00477   }
00478 
00479   // draw that little square between the scrollbars:
00480   if (drawsquare && scrollbar.visible() && hscrollbar.visible()) {
00481     fl_color(parent()->color());
00482     fl_rectf(scrollbar.x(), hscrollbar.y(), scrollsize, scrollsize);
00483   }
00484 
00485   real_hposition_ = hposition_;
00486 }
00487 
00488 // Quick way to delete and reset everything:
00496 void Fl_Browser_::new_list() {
00497   top_ = 0;
00498   position_ = real_position_ = 0;
00499   hposition_ = real_hposition_ = 0;
00500   selection_ = 0;
00501   offset_ = 0;
00502   max_width = 0;
00503   max_width_item = 0;
00504   redraw_lines();
00505 }
00506 
00507 // Tell it that this item is going away, and that this must remove
00508 // all pointers to it:
00516 void Fl_Browser_::deleting(void* item) {
00517   if (displayed(item)) {
00518     redraw_lines();
00519     if (item == top_) {
00520       real_position_ -= offset_;
00521       offset_ = 0;
00522       top_ = item_next(item);
00523       if (!top_) top_ = item_prev(item);
00524     }
00525   } else {
00526     // we don't know where this item is, recalculate top...
00527     real_position_ = 0;
00528     offset_ = 0;
00529     top_ = 0;
00530   }
00531   if (item == selection_) selection_ = 0;
00532   if (item == max_width_item) {max_width_item = 0; max_width = 0;}
00533 }
00534 
00544 void Fl_Browser_::replacing(void* a, void* b) {
00545   redraw_line(a);
00546   if (a == selection_) selection_ = b;
00547   if (a == top_) top_ = b;
00548   if (a == max_width_item) {max_width_item = 0; max_width = 0;}
00549 }
00550 
00559 void Fl_Browser_::swapping(void* a, void* b) {
00560   redraw_line(a);
00561   redraw_line(b);
00562   if (a == selection_) selection_ = b;
00563   else if (b == selection_) selection_ = a;
00564   if (a == top_) top_ = b;
00565   else if (b == top_) top_ = a;
00566 }
00567 
00578 void Fl_Browser_::inserting(void* a, void* b) {
00579   if (displayed(a)) redraw_lines();
00580   if (a == top_) top_ = b;
00581 }
00582 
00589 void* Fl_Browser_::find_item(int ypos) {
00590   update_top();
00591   int X, Y, W, H; bbox(X, Y, W, H);
00592   int yy = Y-offset_;
00593   for (void *l = top_; l; l = item_next(l)) {
00594     int hh = item_height(l); if (hh <= 0) continue;
00595     yy += hh;
00596     if (ypos <= yy || yy>=(Y+H)) return l;
00597   }
00598   return 0;
00599 }
00600 
00614 int Fl_Browser_::select(void* item, int val, int docallbacks) {
00615   if (type() == FL_MULTI_BROWSER) {
00616     if (selection_ != item) {
00617       if (selection_) redraw_line(selection_);
00618       selection_ = item;
00619       redraw_line(item);
00620     }
00621     if ((!val)==(!item_selected(item))) return 0;
00622     item_select(item, val);
00623     redraw_line(item);
00624   } else {
00625     if (val && selection_ == item) return 0;
00626     if (!val && selection_ != item) return 0;
00627     if (selection_) {
00628       item_select(selection_, 0);
00629       redraw_line(selection_);
00630       selection_ = 0;
00631     }
00632     if (val) {
00633       item_select(item, 1);
00634       selection_ = item;
00635       redraw_line(item);
00636       display(item);
00637     }
00638   }         
00639   if (docallbacks) {
00640     set_changed();
00641     do_callback();
00642   }
00643   return 1;
00644 }
00645 
00656 int Fl_Browser_::deselect(int docallbacks) {
00657   if (type() == FL_MULTI_BROWSER) {
00658     int change = 0;
00659     for (void* p = item_first(); p; p = item_next(p))
00660       change |= select(p, 0, docallbacks);
00661     return change;
00662   } else {
00663     if (!selection_) return 0;
00664     item_select(selection_, 0);
00665     redraw_line(selection_);
00666     selection_ = 0;
00667     return 1;
00668   }
00669 }
00670 
00678 int Fl_Browser_::select_only(void* item, int docallbacks) {
00679   if (!item) return deselect(docallbacks);
00680   int change = 0;
00681   Fl_Widget_Tracker wp(this);
00682   if (type() == FL_MULTI_BROWSER) {
00683     for (void* p = item_first(); p; p = item_next(p)) {
00684       if (p != item) change |= select(p, 0, docallbacks);
00685       if (wp.deleted()) return change;
00686     }
00687   }
00688   change |= select(item, 1, docallbacks);
00689   if (wp.deleted()) return change;
00690   display(item);
00691   return change;
00692 }
00693 
00699 int Fl_Browser_::handle(int event) {
00700 
00701   // NOTE:
00702   // We use Fl_Widget_Tracker to test if the user has deleted
00703   // this widget in a callback. Callbacks can be called by:
00704   //  - do_callback()
00705   //  - select()
00706   //  - select_only()
00707   //  - deselect()
00708   // Thus we must test wp.deleted() after each of these calls,
00709   // unless we return directly after one of these.
00710   // If wp.deleted() is true, we return 1 because we used the event.
00711 
00712   Fl_Widget_Tracker wp(this);
00713 
00714   // must do shortcuts first or the scrollbar will get them...
00715   if (event == FL_ENTER || event == FL_LEAVE) return 1;
00716   if (event == FL_KEYBOARD && type() >= FL_HOLD_BROWSER) {
00717     void* l1 = selection_;
00718     void* l = l1; if (!l) l = top_; if (!l) l = item_first();
00719     if (l) {
00720       if (type()==FL_HOLD_BROWSER) {
00721         switch (Fl::event_key()) {
00722         case FL_Down:
00723           while ((l = item_next(l)))
00724             if (item_height(l)>0) {select_only(l, when()); break;}
00725             return 1;
00726         case FL_Up:
00727           while ((l = item_prev(l))) {
00728             if (item_height(l)>0) {
00729               select_only(l, when());
00730               break; // no need to test wp (return 1)
00731             }
00732           }
00733           return 1;
00734         } 
00735       } else  {
00736         switch (Fl::event_key()) {
00737         case FL_Enter:
00738         case FL_KP_Enter:
00739           select_only(l, when() & ~FL_WHEN_ENTER_KEY);
00740           if (wp.deleted()) return 1;
00741           if (when() & FL_WHEN_ENTER_KEY) {
00742             set_changed();
00743             do_callback();
00744           }
00745           return 1;
00746         case ' ':
00747           selection_ = l;
00748           select(l, !item_selected(l), when() & ~FL_WHEN_ENTER_KEY);
00749           return 1;
00750         case FL_Down:
00751           while ((l = item_next(l))) {
00752             if (Fl::event_state(FL_SHIFT|FL_CTRL))
00753               select(l, l1 ? item_selected(l1) : 1, when());
00754             if (wp.deleted()) return 1;
00755             if (item_height(l)>0) goto J1;
00756           }
00757           return 1;
00758         case FL_Up:
00759           while ((l = item_prev(l))) {
00760             if (Fl::event_state(FL_SHIFT|FL_CTRL))
00761               select(l, l1 ? item_selected(l1) : 1, when());
00762             if (wp.deleted()) return 1;
00763             if (item_height(l)>0) goto J1;
00764           }
00765           return 1;
00766 J1:
00767           if (selection_) redraw_line(selection_);
00768           selection_ = l; redraw_line(l);
00769           display(l);
00770           return 1;
00771         }
00772       }
00773     }
00774   }
00775   
00776   if (Fl_Group::handle(event)) return 1;
00777   if (wp.deleted()) return 1;
00778 
00779   int X, Y, W, H; bbox(X, Y, W, H);
00780   int my;
00781 // NOTE:
00782 // instead of:
00783 //     change = select_only(find_item(my), when() & FL_WHEN_CHANGED)
00784 // we use the construct:
00785 //     change = select_only(find_item(my), 0);
00786 //     if (change && (when() & FL_WHEN_CHANGED)) {
00787 //       set_changed();
00788 //       do_callback();
00789 //     }
00790 // See str #834
00791 // The first form calls the callback *before* setting change.
00792 // The callback may execute an Fl::wait(), resulting in another
00793 // call of Fl_Browser_::handle() for the same widget. The sequence
00794 // of events can be an FL_PUSH followed by an FL_RELEASE.
00795 // This second call of Fl_Browser_::handle() may result in a -
00796 // somewhat unexpected - second concurrent invocation of the callback.
00797 
00798   static char change;
00799   static char whichway;
00800   static int py;
00801   switch (event) {
00802   case FL_PUSH:
00803     if (!Fl::event_inside(X, Y, W, H)) return 0;
00804     if (Fl::visible_focus()) {
00805       Fl::focus(this);
00806       redraw();
00807     }
00808     my = py = Fl::event_y();
00809     change = 0;
00810     if (type() == FL_NORMAL_BROWSER || !top_)
00811       ;
00812     else if (type() != FL_MULTI_BROWSER) {
00813       change = select_only(find_item(my), 0);
00814       if (wp.deleted()) return 1;
00815       if (change && (when() & FL_WHEN_CHANGED)) {
00816         set_changed();
00817         do_callback();
00818         if (wp.deleted()) return 1;
00819       }
00820     } else {
00821       void* l = find_item(my);
00822       whichway = 1;
00823       if (Fl::event_state(FL_CTRL)) { // toggle selection:
00824       TOGGLE:
00825         if (l) {
00826           whichway = !item_selected(l);
00827           change = select(l, whichway, 0);
00828           if (wp.deleted()) return 1;
00829           if (change && (when() & FL_WHEN_CHANGED)) {
00830             set_changed();
00831             do_callback();
00832             if (wp.deleted()) return 1;
00833           }
00834         }
00835       } else if (Fl::event_state(FL_SHIFT)) { // extend selection:
00836         if (l == selection_) goto TOGGLE;
00837         // state of previous selection determines new value:
00838         whichway = l ? !item_selected(l) : 1;
00839         // see which of the new item or previous selection is earlier,
00840         // by searching from the previous forward for this one:
00841         int down;
00842         if (!l) down = 1;
00843         else {for (void* m = selection_; ; m = item_next(m)) {
00844           if (m == l) {down = 1; break;}
00845           if (!m) {down = 0; break;}
00846         }}
00847         if (down) {
00848           for (void* m = selection_; m != l; m = item_next(m)) {
00849             select(m, whichway, when() & FL_WHEN_CHANGED);
00850             if (wp.deleted()) return 1;
00851           }
00852         } else {
00853           void* e = selection_;
00854           for (void* m = item_next(l); m; m = item_next(m)) {
00855             select(m, whichway, when() & FL_WHEN_CHANGED);
00856             if (wp.deleted()) return 1;
00857             if (m == e) break;
00858           }
00859         }
00860         // do the clicked item last so the select box is around it:
00861         change = 1;
00862         if (l) select(l, whichway, when() & FL_WHEN_CHANGED);
00863         if (wp.deleted()) return 1;
00864       } else { // select only this item
00865         change = select_only(l, 0);
00866         if (wp.deleted()) return 1;
00867         if (change && (when() & FL_WHEN_CHANGED)) {
00868           set_changed();
00869           do_callback();
00870           if (wp.deleted()) return 1;
00871         }
00872       }
00873     }
00874     return 1;
00875   case FL_DRAG:
00876     // do the scrolling first:
00877     my = Fl::event_y();
00878     if (my < Y && my < py) {
00879       int p = real_position_+my-Y;
00880       if (p<0) p = 0;
00881       position(p);
00882     } else if (my > (Y+H) && my > py) {
00883       int p = real_position_+my-(Y+H);
00884       int hh = full_height()-H; if (p > hh) p = hh;
00885       if (p<0) p = 0;
00886       position(p);
00887     }
00888     if (type() == FL_NORMAL_BROWSER || !top_)
00889       ;
00890     else if (type() == FL_MULTI_BROWSER) {
00891       void* l = find_item(my);
00892       void* t; void* b; // this will be the range to change
00893       if (my > py) { // go down
00894         t = selection_ ? item_next(selection_) : 0;
00895         b = l ? item_next(l) : 0;
00896       } else {  // go up
00897         t = l;
00898         b = selection_;
00899       }
00900       for (; t && t != b; t = item_next(t)) {
00901         char change_t;
00902         change_t = select(t, whichway, 0);
00903         if (wp.deleted()) return 1;
00904         change |= change_t;
00905         if (change_t && (when() & FL_WHEN_CHANGED)) {
00906           set_changed();
00907           do_callback();
00908           if (wp.deleted()) return 1;
00909         }
00910       }
00911       if (l) selection_ = l;
00912     } else {
00913       void* l1 = selection_;
00914       void* l =
00915         (Fl::event_x()<x() || Fl::event_x()>x()+w()) ? selection_ :
00916         find_item(my);
00917       change = (l != l1);
00918       select_only(l, when() & FL_WHEN_CHANGED);
00919       if (wp.deleted()) return 1;
00920     }
00921     py = my;
00922     return 1;
00923   case FL_RELEASE:
00924     if (type() == FL_SELECT_BROWSER) {
00925       void* t = selection_;
00926       deselect();
00927       if (wp.deleted()) return 1;
00928       selection_ = t;
00929     }
00930     if (change) {
00931       set_changed();
00932       if (when() & FL_WHEN_RELEASE) do_callback();
00933     } else {
00934       if (when() & FL_WHEN_NOT_CHANGED) do_callback();
00935     }
00936     if (wp.deleted()) return 1;
00937 
00938     // double click calls the callback: (like Enter Key)
00939     if (Fl::event_clicks() && (when() & FL_WHEN_ENTER_KEY)) {
00940       set_changed();
00941       do_callback();
00942     }
00943     return 1;
00944   case FL_FOCUS:
00945   case FL_UNFOCUS:
00946     if (type() >= FL_HOLD_BROWSER && Fl::visible_focus()) {
00947       redraw();
00948       return 1;
00949     } else return 0;
00950   }
00951 
00952   return 0;
00953 }
00954 
00960 Fl_Browser_::Fl_Browser_(int X, int Y, int W, int H, const char* L)
00961   : Fl_Group(X, Y, W, H, L),
00962     scrollbar(0, 0, 0, 0, 0), // they will be resized by draw()
00963     hscrollbar(0, 0, 0, 0, 0)
00964 {
00965   box(FL_NO_BOX);
00966   align(FL_ALIGN_BOTTOM);
00967   position_ = real_position_ = 0;
00968   hposition_ = real_hposition_ = 0;
00969   offset_ = 0;
00970   top_ = 0;
00971   when(FL_WHEN_RELEASE_ALWAYS);
00972   selection_ = 0;
00973   color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
00974   scrollbar.callback(scrollbar_callback);
00975 //scrollbar.align(FL_ALIGN_LEFT|FL_ALIGN_BOTTOM); // back compatibility?
00976   hscrollbar.callback(hscrollbar_callback);
00977   hscrollbar.type(FL_HORIZONTAL);
00978   textfont_ = FL_HELVETICA;
00979   textsize_ = FL_NORMAL_SIZE;
00980   textcolor_ = FL_FOREGROUND_COLOR;
00981   has_scrollbar_ = BOTH;
00982   max_width = 0;
00983   max_width_item = 0;
00984   scrollbar_size_ = 0;
00985   redraw1 = redraw2 = 0;
00986   end();
00987 }
00988 
00998 void Fl_Browser_::sort(int flags) {
00999   //
01000   // Simple bubble sort - pure lazyness on my side.
01001   //
01002   int i, j, n = -1, desc = ((flags&FL_SORT_DESCENDING)==FL_SORT_DESCENDING);
01003   void *a =item_first(), *b, *c;
01004   if (!a) return;
01005   while (a) {
01006     a = item_next(a);
01007     n++;
01008   }
01009   for (i=n; i>0; i--) {
01010     char swapped = 0;
01011     a = item_first();
01012     b = item_next(a);
01013     for (j=0; j<i; j++) {
01014       const char *ta = item_text(a);
01015       const char *tb = item_text(b);
01016       c = item_next(b);
01017       if (desc) {
01018         if (strcmp(ta, tb)<0) {
01019           item_swap(a, b);
01020           swapped = 1;
01021         }
01022       } else {
01023         if (strcmp(ta, tb)>0) {
01024           item_swap(a, b);
01025           swapped = 1;
01026         }
01027       }
01028       if (!c) break;
01029       b = c; a = item_prev(b);
01030     }
01031     if (!swapped)
01032       break;
01033   }
01034 }
01035 
01036 // Default versions of some of the virtual functions:
01037 
01048 int Fl_Browser_::item_quick_height(void* item) const {
01049   return item_height(item);
01050 }
01051 
01058 int Fl_Browser_::incr_height() const {
01059   return item_quick_height(item_first());
01060 }
01061 
01069 int Fl_Browser_::full_height() const {
01070   int t = 0;
01071   for (void* p = item_first(); p; p = item_next(p))
01072     t += item_quick_height(p);
01073   return t;
01074 }
01075 
01082 int Fl_Browser_::full_width() const {
01083   return max_width;
01084 }
01085 
01095 void Fl_Browser_::item_select(void *item, int val) {}
01096 
01103 int Fl_Browser_::item_selected(void* item) const { return item==selection_ ? 1 : 0; }
01104 
01105 //
01106 // End of "$Id: Fl_Browser_.cxx 7903 2010-11-28 21:06:39Z matt $".
01107 //