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

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_Tabs.cxx 8101 2010-12-22 13:06:03Z AlbrechtS $"
00003 //
00004 // Tab 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 
00029 // This is the "file card tabs" interface to allow you to put lots and lots
00030 // of buttons and switches in a panel, as popularized by many toolkits.
00031 
00032 // Each child widget is a card, and its label() is printed on the card tab.
00033 // Clicking the tab makes that card visible.
00034 
00035 #include <stdio.h>
00036 #include <FL/Fl.H>
00037 #include <FL/Fl_Tabs.H>
00038 #include <FL/fl_draw.H>
00039 #include <FL/Fl_Tooltip.H>
00040 
00041 #define BORDER 2
00042 #define EXTRASPACE 10
00043 #define SELECTION_BORDER 5
00044 
00045 // Return the left edges of each tab (plus a fake left edge for a tab
00046 // past the right-hand one).  These positions are actually of the left
00047 // edge of the slope.  They are either separated by the correct distance
00048 // or by EXTRASPACE or by zero.
00049 // These positions are updated in the private arrays tab_pos[] and
00050 // tab_width[], resp.. If needed, these arrays are (re)allocated.
00051 // Return value is the index of the selected item.
00052 
00053 int Fl_Tabs::tab_positions() {
00054   int nc = children();
00055   if (nc != tab_count) {
00056     clear_tab_positions();
00057     if (nc) {
00058       tab_pos   = (int*)malloc((nc+1)*sizeof(int));
00059       tab_width = (int*)malloc((nc+1)*sizeof(int));
00060     }
00061     tab_count = nc;
00062   }
00063   if (nc == 0) return 0;
00064   int selected = 0;
00065   Fl_Widget*const* a = array();
00066   int i;
00067   char prev_draw_shortcut = fl_draw_shortcut;
00068   fl_draw_shortcut = 1;
00069 
00070   tab_pos[0] = Fl::box_dx(box());
00071   for (i=0; i<nc; i++) {
00072     Fl_Widget* o = *a++;
00073     if (o->visible()) selected = i;
00074 
00075     int wt = 0; int ht = 0;
00076     o->measure_label(wt,ht);
00077 
00078     tab_width[i] = wt + EXTRASPACE;
00079     tab_pos[i+1] = tab_pos[i] + tab_width[i] + BORDER;
00080   }
00081   fl_draw_shortcut = prev_draw_shortcut;
00082 
00083   int r = w();
00084   if (tab_pos[i] <= r) return selected;
00085   // uh oh, they are too big:
00086   // pack them against right edge:
00087   tab_pos[i] = r;
00088   for (i = nc; i--;) {
00089     int l = r-tab_width[i];
00090     if (tab_pos[i+1] < l) l = tab_pos[i+1];
00091     if (tab_pos[i] <= l) break;
00092     tab_pos[i] = l;
00093     r -= EXTRASPACE;
00094   }
00095   // pack them against left edge and truncate width if they still don't fit:
00096   for (i = 0; i<nc; i++) {
00097     if (tab_pos[i] >= i*EXTRASPACE) break;
00098     tab_pos[i] = i*EXTRASPACE;
00099     int W = w()-1-EXTRASPACE*(children()-i) - tab_pos[i];
00100     if (tab_width[i] > W) tab_width[i] = W;
00101   }
00102   // adjust edges according to visiblity:
00103   for (i = nc; i > selected; i--) {
00104     tab_pos[i] = tab_pos[i-1] + tab_width[i-1];
00105   }
00106   return selected;
00107 }
00108 
00109 // Returns space (height) in pixels needed for tabs. Negative to put them on the bottom.
00110 // Returns full height, if children() = 0.
00111 int Fl_Tabs::tab_height() {
00112   if (children() == 0) return h();
00113   int H = h();
00114   int H2 = y();
00115   Fl_Widget*const* a = array();
00116   for (int i=children(); i--;) {
00117     Fl_Widget* o = *a++;
00118     if (o->y() < y()+H) H = o->y()-y();
00119     if (o->y()+o->h() > H2) H2 = o->y()+o->h();
00120   }
00121   H2 = y()+h()-H2;
00122   if (H2 > H) return (H2 <= 0) ? 0 : -H2;
00123   else return (H <= 0) ? 0 : H;
00124 }
00125 
00126 // This is used for event handling (clicks) and by fluid to pick tabs.
00127 // Returns 0, if children() = 0, or if the event is outside of the tabs area.
00128 Fl_Widget *Fl_Tabs::which(int event_x, int event_y) {
00129   if (children() == 0) return 0;
00130   int H = tab_height();
00131   if (H < 0) {
00132     if (event_y > y()+h() || event_y < y()+h()+H) return 0;
00133   } else {
00134     if (event_y > y()+H || event_y < y()) return 0;
00135   }
00136   if (event_x < x()) return 0;
00137   Fl_Widget *ret = 0L;
00138   int nc = children();
00139   tab_positions();
00140   for (int i=0; i<nc; i++) {
00141     if (event_x < x()+tab_pos[i+1]) {
00142       ret = child(i);
00143       break;
00144     }
00145   }
00146   return ret;
00147 }
00148 
00149 void Fl_Tabs::redraw_tabs()
00150 {
00151   int H = tab_height();
00152   if (H >= 0) {
00153     H += Fl::box_dy(box());
00154     damage(FL_DAMAGE_SCROLL, x(), y(), w(), H);
00155   } else {
00156     H = Fl::box_dy(box()) - H;
00157     damage(FL_DAMAGE_SCROLL, x(), y() + h() - H, w(), H);
00158   }
00159 }
00160 
00161 int Fl_Tabs::handle(int event) {
00162 
00163   Fl_Widget *o;
00164   int i;
00165 
00166   switch (event) {
00167 
00168   case FL_PUSH: {
00169     int H = tab_height();
00170     if (H >= 0) {
00171       if (Fl::event_y() > y()+H) return Fl_Group::handle(event);
00172     } else {
00173       if (Fl::event_y() < y()+h()+H) return Fl_Group::handle(event);
00174     }}
00175     /* FALLTHROUGH */
00176   case FL_DRAG:
00177   case FL_RELEASE:
00178     o = which(Fl::event_x(), Fl::event_y());
00179     if (event == FL_RELEASE) {
00180       push(0);
00181       if (o && Fl::visible_focus() && Fl::focus()!=this) { 
00182         Fl::focus(this);
00183         redraw_tabs();
00184       }
00185       if (o && value(o)) {
00186         Fl_Widget_Tracker wp(o);
00187         set_changed();
00188         do_callback();
00189         if (wp.deleted()) return 1;
00190       }
00191       Fl_Tooltip::current(o);
00192     } else {
00193       push(o);
00194     }
00195     return 1;
00196   case FL_MOVE: {
00197     int ret = Fl_Group::handle(event);
00198     Fl_Widget *o = Fl_Tooltip::current(), *n = o;
00199     int H = tab_height();
00200     if ( (H>=0) && (Fl::event_y()>y()+H) )
00201       return ret;
00202     else if ( (H<0) && (Fl::event_y() < y()+h()+H) )
00203       return ret;
00204     else { 
00205       n = which(Fl::event_x(), Fl::event_y());
00206       if (!n) n = this;
00207     }
00208     if (n!=o)
00209       Fl_Tooltip::enter(n);
00210     return ret; }
00211   case FL_FOCUS:
00212   case FL_UNFOCUS:
00213     if (!Fl::visible_focus()) return Fl_Group::handle(event);
00214     if (Fl::event() == FL_RELEASE ||
00215         Fl::event() == FL_SHORTCUT ||
00216         Fl::event() == FL_KEYBOARD ||
00217         Fl::event() == FL_FOCUS ||
00218         Fl::event() == FL_UNFOCUS) {
00219       redraw_tabs();
00220       if (Fl::event() == FL_FOCUS || Fl::event() == FL_UNFOCUS) return 0;
00221       else return 1;
00222     } else return Fl_Group::handle(event);
00223   case FL_KEYBOARD:
00224     switch (Fl::event_key()) {
00225       case FL_Left:
00226         if (child(0)->visible()) return 0;
00227         for (i = 1; i < children(); i ++)
00228           if (child(i)->visible()) break;
00229         value(child(i - 1));
00230         set_changed();
00231         do_callback();
00232         return 1;
00233       case FL_Right:
00234         if (child(children() - 1)->visible()) return 0;
00235         for (i = 0; i < children(); i ++)
00236           if (child(i)->visible()) break;
00237         value(child(i + 1));
00238         set_changed();
00239         do_callback();
00240         return 1;
00241       case FL_Down:
00242         redraw();
00243         return Fl_Group::handle(FL_FOCUS);
00244       default:
00245         break;
00246     }
00247     return Fl_Group::handle(event);
00248   case FL_SHORTCUT:
00249     for (i = 0; i < children(); ++i) {
00250       Fl_Widget *c = child(i);
00251       if (c->test_shortcut(c->label())) {
00252         char sc = !c->visible();
00253         value(c);
00254         if (sc) set_changed();
00255         do_callback();
00256         return 1;
00257       }
00258     }
00259     return Fl_Group::handle(event);
00260   case FL_SHOW:
00261     value(); // update visibilities and fall through
00262   default:
00263     return Fl_Group::handle(event);
00264 
00265   }
00266 }
00267 
00268 int Fl_Tabs::push(Fl_Widget *o) {
00269   if (push_ == o) return 0;
00270   if ( (push_ && !push_->visible()) || (o && !o->visible()) )
00271     redraw_tabs();
00272   push_ = o;
00273   return 1;
00274 }
00275 
00283 Fl_Widget* Fl_Tabs::value() {
00284   Fl_Widget* v = 0;
00285   Fl_Widget*const* a = array();
00286   for (int i=children(); i--;) {
00287     Fl_Widget* o = *a++;
00288     if (v) o->hide();
00289     else if (o->visible()) v = o;
00290     else if (!i) {o->show(); v = o;}
00291   }
00292   return v;
00293 }
00294 
00300 int Fl_Tabs::value(Fl_Widget *newvalue) {
00301   Fl_Widget*const* a = array();
00302   int ret = 0;
00303   for (int i=children(); i--;) {
00304     Fl_Widget* o = *a++;
00305     if (o == newvalue) {
00306       if (!o->visible()) ret = 1;
00307       o->show();
00308     } else {
00309       o->hide();
00310     }
00311   }
00312   return ret;
00313 }
00314 
00315 enum {LEFT, RIGHT, SELECTED};
00316 
00317 void Fl_Tabs::draw() {
00318   Fl_Widget *v = value();
00319   int H = tab_height();
00320 
00321   if (damage() & FL_DAMAGE_ALL) { // redraw the entire thing:
00322     Fl_Color c = v ? v->color() : color();
00323 
00324     draw_box(box(), x(), y()+(H>=0?H:0), w(), h()-(H>=0?H:-H), c);
00325 
00326     if (selection_color() != c) {
00327       // Draw the top or bottom SELECTION_BORDER lines of the tab pane in the
00328       // selection color so that the user knows which tab is selected...
00329       int clip_y = (H >= 0) ? y() + H : y() + h() + H - SELECTION_BORDER;
00330       fl_push_clip(x(), clip_y, w(), SELECTION_BORDER);
00331       draw_box(box(), x(), clip_y, w(), SELECTION_BORDER, selection_color());
00332       fl_pop_clip();
00333     }
00334     if (v) draw_child(*v);
00335   } else { // redraw the child
00336     if (v) update_child(*v);
00337   }
00338   if (damage() & (FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) {
00339     int nc = children();
00340     int selected = tab_positions();
00341     int i;
00342     Fl_Widget*const* a = array();
00343     for (i=0; i<selected; i++)
00344       draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
00345                tab_width[i], H, a[i], LEFT);
00346     for (i=nc-1; i > selected; i--)
00347       draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
00348                tab_width[i], H, a[i], RIGHT);
00349     if (v) {
00350       i = selected;
00351       draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
00352                tab_width[i], H, a[i], SELECTED);
00353     }
00354   }
00355 }
00356 
00357 void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int what) {
00358   int sel = (what == SELECTED);
00359   int dh = Fl::box_dh(box());
00360   int dy = Fl::box_dy(box());
00361   char prev_draw_shortcut = fl_draw_shortcut;
00362   fl_draw_shortcut = 1;
00363 
00364   Fl_Boxtype bt = (o==push_ &&!sel) ? fl_down(box()) : box();
00365 
00366   // compute offsets to make selected tab look bigger
00367   int yofs = sel ? 0 : BORDER;
00368 
00369   if ((x2 < x1+W) && what == RIGHT) x1 = x2 - W;
00370 
00371   if (H >= 0) {
00372     if (sel) fl_push_clip(x1, y(), x2 - x1, H + dh - dy);
00373     else fl_push_clip(x1, y(), x2 - x1, H);
00374 
00375     H += dh;
00376 
00377     Fl_Color c = sel ? selection_color() : o->selection_color();
00378 
00379     draw_box(bt, x1, y() + yofs, W, H + 10 - yofs, c);
00380 
00381     // Save the previous label color
00382     Fl_Color oc = o->labelcolor();
00383 
00384     // Draw the label using the current color...
00385     o->labelcolor(sel ? labelcolor() : o->labelcolor());    
00386     o->draw_label(x1, y() + yofs, W, H - yofs, FL_ALIGN_CENTER);
00387 
00388     // Restore the original label color...
00389     o->labelcolor(oc);
00390 
00391     if (Fl::focus() == this && o->visible())
00392       draw_focus(box(), x1, y(), W, H);
00393 
00394     fl_pop_clip();
00395   } else {
00396     H = -H;
00397 
00398     if (sel) fl_push_clip(x1, y() + h() - H - dy, x2 - x1, H + dy);
00399     else fl_push_clip(x1, y() + h() - H, x2 - x1, H);
00400 
00401     H += dh;
00402 
00403     Fl_Color c = sel ? selection_color() : o->selection_color();
00404 
00405     draw_box(bt, x1, y() + h() - H - 10, W, H + 10 - yofs, c);
00406 
00407     // Save the previous label color
00408     Fl_Color oc = o->labelcolor();
00409 
00410     // Draw the label using the current color...
00411     o->labelcolor(sel ? labelcolor() : o->labelcolor());
00412     o->draw_label(x1, y() + h() - H, W, H - yofs, FL_ALIGN_CENTER);
00413 
00414     // Restore the original label color...
00415     o->labelcolor(oc);
00416 
00417     if (Fl::focus() == this && o->visible())
00418       draw_focus(box(), x1, y() + h() - H, W, H);
00419 
00420     fl_pop_clip();
00421   }
00422   fl_draw_shortcut = prev_draw_shortcut;
00423 }
00424 
00446 Fl_Tabs::Fl_Tabs(int X,int Y,int W, int H, const char *l) :
00447   Fl_Group(X,Y,W,H,l)
00448 {
00449   box(FL_THIN_UP_BOX);
00450   push_ = 0;
00451   tab_pos = 0;
00452   tab_width = 0;
00453   tab_count = 0;
00454 }
00455 
00456 Fl_Tabs::~Fl_Tabs() {
00457   clear_tab_positions();
00458 }
00459 
00481 void Fl_Tabs::client_area(int &rx, int &ry, int &rw, int &rh, int tabh) {
00482 
00483   if (children()) {                     // use existing values
00484 
00485     rx = child(0)->x();
00486     ry = child(0)->y();
00487     rw = child(0)->w();
00488     rh = child(0)->h();
00489 
00490   } else {                              // calculate values
00491 
00492     int y_offset;
00493     int label_height = fl_height(labelfont(), labelsize()) + BORDER*2;
00494 
00495     if (tabh == 0)                      // use default (at top)
00496       y_offset = label_height;
00497     else if (tabh == -1)                // use default (at bottom)
00498       y_offset = -label_height;
00499     else
00500       y_offset = tabh;                  // user given value
00501 
00502     rx = x();
00503     rw = w();
00504 
00505     if (y_offset >= 0) {                // labels at top
00506       ry = y() + y_offset;
00507       rh = h() - y_offset;
00508     } else {                            // labels at bottom
00509       ry = y();
00510       rh = h() + y_offset;
00511     }
00512   }
00513 }
00514 
00515 void Fl_Tabs::clear_tab_positions() {
00516   if (tab_pos) {
00517     free(tab_pos);
00518     tab_pos = 0;
00519   }
00520   if (tab_width){
00521     free(tab_width);
00522     tab_width = 0;
00523   }
00524 }
00525 
00526 //
00527 // End of "$Id: Fl_Tabs.cxx 8101 2010-12-22 13:06:03Z AlbrechtS $".
00528 //