|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // 00002 // "$Id: Fl_Browser_.cxx 7903 2010-11-28 21:06:39Z matt $" 00003 // 00004 // 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 //