source: rtems-graphics-toolkit/fltk-1.1.10/src/Fl_Browser_.cxx @ 513eea1

Last change on this file since 513eea1 was 513eea1, checked in by Joel Sherrill <joel.sherrill@…>, on 01/09/10 at 22:43:24

2010-01-08 Joel Sherrill <joel.sherrill@…>

fltk 1.1.10. imported

  • ORIGIN: Updated.
  • Property mode set to 100644
File size: 23.6 KB
Line 
1//
2// "$Id$"
3//
4// Base Browser widget class for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2006 by Bill Spitzak and others.
7//
8// This library is free software; you can redistribute it and/or
9// modify it under the terms of the GNU Library General Public
10// License as published by the Free Software Foundation; either
11// version 2 of the License, or (at your option) any later version.
12//
13// This library is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16// Library General Public License for more details.
17//
18// You should have received a copy of the GNU Library General Public
19// License along with this library; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21// USA.
22//
23// Please report all bugs and problems on the following page:
24//
25//     http://www.fltk.org/str.php
26//
27
28#define DISPLAY_SEARCH_BOTH_WAYS_AT_ONCE
29
30#include <stdio.h>
31#include <FL/Fl.H>
32#include <FL/Fl_Widget.H>
33#include <FL/Fl_Browser_.H>
34#include <FL/fl_draw.H>
35
36
37// This is the base class for browsers.  To be useful it must be
38// subclassed and several virtual functions defined.  The
39// Forms-compatable browser and the file chooser's browser are
40// subclassed off of this.
41
42// Yes, I know this should be a template...
43
44// This has been designed so that the subclass has complete control
45// over the storage of the data, although because next() and prev()
46// functions are used to index, it works best as a linked list or as a
47// large block of characters in which the line breaks must be searched
48// for.
49
50// A great deal of work has been done so that the "height" of a data
51// object does not need to be determined until it is drawn.  This was
52// done for the file chooser, because the height requires doing stat()
53// to see if the file is a directory, which can be annoyingly slow
54// over the network.
55
56/* redraw bits:
57   1 = redraw children (the scrollbar)
58   2 = redraw one or two items
59   4 = redraw all items
60*/
61
62static void scrollbar_callback(Fl_Widget* s, void*) {
63  ((Fl_Browser_*)(s->parent()))->position(int(((Fl_Scrollbar*)s)->value()));
64}
65
66static void hscrollbar_callback(Fl_Widget* s, void*) {
67  ((Fl_Browser_*)(s->parent()))->hposition(int(((Fl_Scrollbar*)s)->value()));
68}
69
70// Scrollbar size should be part of the Fl class, but is left here for
71// binary compatibility in 1.1.x - M. Sweet
72int Fl_Browser_::scrollbar_width_ = 16;
73
74// Get the standard scrollbar size
75int Fl::scrollbar_size() {
76  return Fl_Browser_::scrollbar_width();
77}
78
79// Set the standard scrollbar size
80void Fl::scrollbar_size(int W) {
81  Fl_Browser_::scrollbar_width(W);
82}
83
84// return where to draw the actual box:
85void Fl_Browser_::bbox(int& X, int& Y, int& W, int& H) const {
86  Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
87  X = x()+Fl::box_dx(b);
88  Y = y()+Fl::box_dy(b);
89  W = w()-Fl::box_dw(b);
90  H = h()-Fl::box_dh(b);
91  if (scrollbar.visible()) {
92    W -= scrollbar_width_;
93    if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar_width_;
94  }
95  if (W < 0) W = 0;
96  if (hscrollbar.visible()) {
97    H -= scrollbar_width_;
98    if (scrollbar.align() & FL_ALIGN_TOP) Y += scrollbar_width_;
99  }
100  if (H < 0) H = 0;
101}
102
103int Fl_Browser_::leftedge() const {
104  int X, Y, W, H; bbox(X, Y, W, H);
105  return X;
106}
107
108// The scrollbars may be moved again by draw(), since each one's size
109// depends on whether the other is visible or not.  This skips over
110// Fl_Group::resize since it moves the scrollbars uselessly.
111void Fl_Browser_::resize(int X, int Y, int W, int H) {
112  Fl_Widget::resize(X, Y, W, H);
113  // move the scrollbars so they can respond to events:
114  bbox(X,Y,W,H);
115  scrollbar.resize(
116        scrollbar.align()&FL_ALIGN_LEFT ? X-scrollbar_width_ : X+W,
117        Y, scrollbar_width_, H);
118  hscrollbar.resize(
119        X, scrollbar.align()&FL_ALIGN_TOP ? Y-scrollbar_width_ : Y+H,
120        W, scrollbar_width_);
121}
122
123// Cause minimal update to redraw the given item:
124void Fl_Browser_::redraw_line(void* l) {
125  if (!redraw1 || redraw1 == l) {redraw1 = l; damage(FL_DAMAGE_EXPOSE);}
126  else if (!redraw2 || redraw2 == l) {redraw2 = l; damage(FL_DAMAGE_EXPOSE);}
127  else damage(FL_DAMAGE_SCROLL);
128}
129
130// Figure out top() based on position():
131void Fl_Browser_::update_top() {
132  if (!top_) top_ = item_first();
133  if (position_ != real_position_) {
134    void* l;
135    int ly;
136    int yy = position_;
137    // start from either head or current position, whichever is closer:
138    if (!top_ || yy <= (real_position_/2)) {
139      l = item_first();
140      ly = 0;
141    } else {
142      l = top_;
143      ly = real_position_-offset_;
144    }
145    if (!l) {
146      top_ = 0;
147      offset_ = 0;
148      real_position_ = 0;
149    } else {
150      int hh = item_quick_height(l);
151      // step through list until we find line containing this point:
152      while (ly > yy) {
153        void* l1 = item_prev(l);
154        if (!l1) {ly = 0; break;} // hit the top
155        l  = l1;
156        hh = item_quick_height(l);
157        ly -= hh;
158      }
159      while ((ly+hh) <= yy) {
160        void* l1 = item_next(l);
161        if (!l1) {yy = ly+hh-1; break;}
162        l = l1;
163        ly += hh;
164        hh = item_quick_height(l);
165      }
166      // top item must *really* be visible, use slow height:
167      for (;;) {
168        hh = item_height(l);
169        if ((ly+hh) > yy) break; // it is big enough to see
170        // go up to top of previous item:
171        void* l1 = item_prev(l);
172        if (!l1) {ly = yy = 0; break;} // hit the top
173        l = l1; yy = position_ = ly = ly-item_quick_height(l);
174      }
175      // use it:
176      top_ = l;
177      offset_ = yy-ly;
178      real_position_ = yy;
179    }
180    damage(FL_DAMAGE_SCROLL);
181  }
182}
183
184// Change position(), top() will update when update_top() is called
185// (probably by draw() or handle()):
186void Fl_Browser_::position(int yy) {
187  if (yy < 0) yy = 0;
188  if (yy == position_) return;
189  position_ = yy;
190  if (yy != real_position_) redraw_lines();
191}
192
193void Fl_Browser_::hposition(int xx) {
194  if (xx < 0) xx = 0;
195  if (xx == hposition_) return;
196  hposition_ = xx;
197  if (xx != real_hposition_) redraw_lines();
198}
199
200// Tell whether item is currently displayed:
201int Fl_Browser_::displayed(void* p) const {
202  int X, Y, W, H; bbox(X, Y, W, H);
203  int yy = H+offset_;
204  for (void* l = top_; l && yy > 0; l = item_next(l)) {
205    if (l == p) return 1;
206    yy -= item_height(l);
207  }
208  return 0;
209}
210
211// Ensure this item is displayed:
212// Messy because we have no idea if it is before top or after bottom:
213void Fl_Browser_::display(void* p) {
214
215  // First special case - want to display first item in the list?
216  update_top();
217  if (p == item_first()) {position(0); return;}
218
219  int X, Y, W, H, Yp; bbox(X, Y, W, H);
220  void* l = top_;
221  Y = Yp = -offset_;
222  int h1;
223
224  // 2nd special case - want to display item already displayed at top of browser?
225  if (l == p) {position(real_position_+Y); return;} // scroll up a bit
226
227  // 3rd special case - want to display item just above top of browser?
228  void* lp = item_prev(l);
229  if (lp == p) {position(real_position_+Y-item_quick_height(lp)); return;}
230
231#ifdef DISPLAY_SEARCH_BOTH_WAYS_AT_ONCE
232  // search for item.  We search both up and down the list at the same time,
233  // this evens up the execution time for the two cases - the old way was
234  // much slower for going up than for going down.
235  while (l || lp) {
236    if (l) {
237      h1 = item_quick_height(l);
238      if (l == p) {
239        if (Y <= H) { // it is visible or right at bottom
240          Y = Y+h1-H; // find where bottom edge is
241          if (Y > 0) position(real_position_+Y); // scroll down a bit
242        } else {
243          position(real_position_+Y-(H-h1)/2); // center it
244        }
245        return;
246      }
247      Y += h1;
248      l = item_next(l);
249    }
250    if (lp) {
251      h1 = item_quick_height(lp);
252      Yp -= h1;
253      if (lp == p) {
254        if ((Yp + h1) >= 0) position(real_position_+Yp);
255        else position(real_position_+Yp-(H-h1)/2);
256        return;
257      }
258      lp = item_prev(lp);
259    }
260  }
261#else
262  // Old version went forwards and then backwards:
263  // search forward for it:
264  l = top_;
265  for (; l; l = item_next(l)) {
266    h1 = item_quick_height(l);
267    if (l == p) {
268      if (Y <= H) { // it is visible or right at bottom
269        Y = Y+h1-H; // find where bottom edge is
270        if (Y > 0) position(real_position_+Y); // scroll down a bit
271      } else {
272        position(real_position_+Y-(H-h1)/2); // center it
273      }
274      return;
275    }
276    Y += h1;
277  }
278  // search backward for it, if found center it:
279  l = lp;
280  Y = -offset_;
281  for (; l; l = item_prev(l)) {
282    h1 = item_quick_height(l);
283    Y -= h1;
284    if (l == p) {
285      if ((Y + h1) >= 0) position(real_position_+Y);
286      else position(real_position_+Y-(H-h1)/2);
287      return;
288    }
289  }
290#endif
291}
292
293// redraw, has side effect of updating top and setting scrollbar:
294
295void Fl_Browser_::draw() {
296  int drawsquare = 0;
297  update_top();
298  int full_width_ = full_width();
299  int full_height_ = full_height();
300  int X, Y, W, H; bbox(X, Y, W, H);
301  int dont_repeat = 0;
302J1:
303  if (damage() & FL_DAMAGE_ALL) { // redraw the box if full redraw
304    Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
305    draw_box(b, x(), y(), w(), h(), color());
306    drawsquare = 1;
307  }
308  // see if scrollbar needs to be switched on/off:
309  if ((has_scrollbar_ & VERTICAL) && (
310        (has_scrollbar_ & ALWAYS_ON) || position_ || full_height_ > H)) {
311    if (!scrollbar.visible()) {
312      scrollbar.set_visible();
313      drawsquare = 1;
314      bbox(X, Y, W, H);
315    }
316  } else {
317    top_ = item_first(); real_position_ = offset_ = 0;
318    if (scrollbar.visible()) {
319      scrollbar.clear_visible();
320      clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
321    }
322  }
323
324  if ((has_scrollbar_ & HORIZONTAL) && (
325        (has_scrollbar_ & ALWAYS_ON) || hposition_ || full_width_ > W)) {
326    if (!hscrollbar.visible()) {
327      hscrollbar.set_visible();
328      drawsquare = 1;
329      bbox(X, Y, W, H);
330    }
331  } else {
332    real_hposition_ = 0;
333    if (hscrollbar.visible()) {
334      hscrollbar.clear_visible();
335      clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
336    }
337  }
338
339  // Check the vertical scrollbar again, just in case it needs to be drawn
340  // because the horizontal one is drawn.  There should be a cleaner way
341  // to do this besides copying the same code...
342  if ((has_scrollbar_ & VERTICAL) && (
343        (has_scrollbar_ & ALWAYS_ON) || position_ || full_height_ > H)) {
344    if (!scrollbar.visible()) {
345      scrollbar.set_visible();
346      drawsquare = 1;
347      bbox(X, Y, W, H);
348    }
349  } else {
350    top_ = item_first(); real_position_ = offset_ = 0;
351    if (scrollbar.visible()) {
352      scrollbar.clear_visible();
353      clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
354    }
355  }
356
357  bbox(X, Y, W, H);
358
359  fl_clip(X, Y, W, H);
360  // for each line, draw it if full redraw or scrolled.  Erase background
361  // if not a full redraw or if it is selected:
362  void* l = top();
363  int yy = -offset_;
364  for (; l && yy < H; l = item_next(l)) {
365    int hh = item_height(l);
366    if (hh <= 0) continue;
367    if ((damage()&(FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) || l == redraw1 || l == redraw2) {
368      if (item_selected(l)) {
369        fl_color(active_r() ? selection_color() : fl_inactive(selection_color()));
370        fl_rectf(X, yy+Y, W, hh);
371      } else if (!(damage()&FL_DAMAGE_ALL)) {
372        fl_push_clip(X, yy+Y, W, hh);
373        draw_box(box() ? box() : FL_DOWN_BOX, x(), y(), w(), h(), color());
374        fl_pop_clip();
375      }
376      item_draw(l, X-hposition_, yy+Y, W+hposition_, hh);
377      if (l == selection_ && Fl::focus() == this) {
378        draw_box(FL_BORDER_FRAME, X, yy+Y, W, hh, color());
379        draw_focus(FL_NO_BOX, X, yy+Y, W+1, hh+1);
380      }
381      int ww = item_width(l);
382      if (ww > max_width) {max_width = ww; max_width_item = l;}
383    }
384    yy += hh;
385  }
386  // erase the area below last line:
387  if (!(damage()&FL_DAMAGE_ALL) && yy < H) {
388    fl_push_clip(X, yy+Y, W, H-yy);
389    draw_box(box() ? box() : FL_DOWN_BOX, x(), y(), w(), h(), color());
390    fl_pop_clip();
391  }
392  fl_pop_clip();
393  redraw1 = redraw2 = 0;
394
395  if (!dont_repeat) {
396    dont_repeat = 1;
397    // see if changes to full_height caused by calls to slow_height
398    // caused scrollbar state to change, in which case we have to redraw:
399    full_height_ = full_height();
400    full_width_ = full_width();
401    if ((has_scrollbar_ & VERTICAL) &&
402        ((has_scrollbar_ & ALWAYS_ON) || position_ || full_height_>H)) {
403      if (!scrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
404    } else {
405      if (scrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
406    }
407    if ((has_scrollbar_ & HORIZONTAL) &&
408        ((has_scrollbar_ & ALWAYS_ON) || hposition_ || full_width_>W)) {
409      if (!hscrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
410    } else {
411      if (hscrollbar.visible()) { damage(FL_DAMAGE_ALL); goto J1; }
412    }
413  }
414
415  // update the scrollbars and redraw them:
416  int dy = top_ ? item_quick_height(top_) : 0; if (dy < 10) dy = 10;
417  if (scrollbar.visible()) {
418    scrollbar.damage_resize(
419        scrollbar.align()&FL_ALIGN_LEFT ? X-scrollbar_width_ : X+W,
420        Y, scrollbar_width_, H);
421    scrollbar.value(position_, H, 0, full_height_);
422    scrollbar.linesize(dy);
423    if (drawsquare) draw_child(scrollbar);
424    else update_child(scrollbar);
425  }
426  if (hscrollbar.visible()) {
427    hscrollbar.damage_resize(
428        X, scrollbar.align()&FL_ALIGN_TOP ? Y-scrollbar_width_ : Y+H,
429        W, scrollbar_width_);
430    hscrollbar.value(hposition_, W, 0, full_width_);
431    hscrollbar.linesize(dy);
432    if (drawsquare) draw_child(hscrollbar);
433    else update_child(hscrollbar);
434  }
435
436  // draw that little square between the scrollbars:
437  if (drawsquare && scrollbar.visible() && hscrollbar.visible()) {
438    fl_color(parent()->color());
439    fl_rectf(scrollbar.x(), hscrollbar.y(), scrollbar_width_,scrollbar_width_);
440  }
441
442  real_hposition_ = hposition_;
443}
444
445// Quick way to delete and reset everything:
446void Fl_Browser_::new_list() {
447  top_ = 0;
448  position_ = real_position_ = 0;
449  hposition_ = real_hposition_ = 0;
450  selection_ = 0;
451  offset_ = 0;
452  max_width = 0;
453  max_width_item = 0;
454  redraw_lines();
455}
456
457// Tell it that this item is going away, and that this must remove
458// all pointers to it:
459void Fl_Browser_::deleting(void* l) {
460  if (displayed(l)) {
461    redraw_lines();
462    if (l == top_) {
463      real_position_ -= offset_;
464      offset_ = 0;
465      top_ = item_next(l);
466      if (!top_) top_ = item_prev(l);
467    }
468  } else {
469    // we don't know where this item is, recalculate top...
470    real_position_ = 0;
471    offset_ = 0;
472    top_ = 0;
473  }
474  if (l == selection_) selection_ = 0;
475  if (l == max_width_item) {max_width_item = 0; max_width = 0;}
476}
477
478void Fl_Browser_::replacing(void* a, void* b) {
479  redraw_line(a);
480  if (a == selection_) selection_ = b;
481  if (a == top_) top_ = b;
482  if (a == max_width_item) {max_width_item = 0; max_width = 0;}
483}
484
485void Fl_Browser_::swapping(void* a, void* b) {
486  redraw_line(a);
487  redraw_line(b);
488  if (a == selection_) selection_ = b;
489  else if (b == selection_) selection_ = a;
490  if (a == top_) top_ = b;
491  else if (b == top_) top_ = a;
492}
493
494void Fl_Browser_::inserting(void* a, void* b) {
495  if (displayed(a)) redraw_lines();
496  if (a == top_) top_ = b;
497}
498
499void* Fl_Browser_::find_item(int my) {
500  update_top();
501  int X, Y, W, H; bbox(X, Y, W, H);
502  void* l;
503  int yy = Y-offset_;
504  for (l = top_; l; l = item_next(l)) {
505    int hh = item_height(l); if (hh <= 0) continue;
506    yy += hh;
507    if (my <= yy || yy>=(Y+H)) return l;
508  }
509  return 0;
510}
511
512int Fl_Browser_::select(void* l, int i, int docallbacks) {
513  if (type() == FL_MULTI_BROWSER) {
514    if (selection_ != l) {
515      if (selection_) redraw_line(selection_);
516      selection_ = l;
517      redraw_line(l);
518    }
519    if ((!i)==(!item_selected(l))) return 0;
520    item_select(l, i);
521    redraw_line(l);
522  } else {
523    if (i && selection_ == l) return 0;
524    if (!i && selection_ != l) return 0;
525    if (selection_) {
526      item_select(selection_, 0);
527      redraw_line(selection_);
528      selection_ = 0;
529    }
530    if (i) {
531      item_select(l, 1);
532      selection_ = l;
533      redraw_line(l);
534      display(l);
535    }
536  }         
537  if (docallbacks) {
538    set_changed();
539    do_callback();
540  }
541  return 1;
542}
543
544int Fl_Browser_::deselect(int docallbacks) {
545  if (type() == FL_MULTI_BROWSER) {
546    int change = 0;
547    for (void* p = item_first(); p; p = item_next(p))
548      change |= select(p, 0, docallbacks);
549    return change;
550  } else {
551    if (!selection_) return 0;
552    item_select(selection_, 0);
553    redraw_line(selection_);
554    selection_ = 0;
555    return 1;
556  }
557}
558
559int Fl_Browser_::select_only(void* l, int docallbacks) {
560  if (!l) return deselect(docallbacks);
561  int change = 0;
562  if (type() == FL_MULTI_BROWSER) {
563    for (void* p = item_first(); p; p = item_next(p))
564      if (p != l) change |= select(p, 0, docallbacks);
565  }
566  change |= select(l, 1, docallbacks);
567  display(l);
568  return change;
569}
570
571int Fl_Browser_::handle(int event) {
572  // must do shortcuts first or the scrollbar will get them...
573  if (event == FL_ENTER || event == FL_LEAVE) return 1;
574  if (event == FL_KEYBOARD && type() >= FL_HOLD_BROWSER) {
575    void* l1 = selection_;
576    void* l = l1; if (!l) l = top_; if (!l) l = item_first();
577    if (l) {
578      if (type()==FL_HOLD_BROWSER) {
579        switch (Fl::event_key()) {
580        case FL_Down:
581          while ((l = item_next(l)))
582            if (item_height(l)>0) {select_only(l, when()); break;}
583            return 1;
584        case FL_Up:
585          while ((l = item_prev(l))) if (item_height(l)>0) {
586            select_only(l, when()); break;}
587          return 1;
588        }
589      } else  {
590        switch (Fl::event_key()) {
591        case FL_Enter:
592        case FL_KP_Enter:
593          select_only(l, when() & ~FL_WHEN_ENTER_KEY);
594          if (when() & FL_WHEN_ENTER_KEY) {
595            set_changed();
596            do_callback();
597          }
598          return 1;
599        case ' ':
600          selection_ = l;
601          select(l, !item_selected(l), when() & ~FL_WHEN_ENTER_KEY);
602          return 1;
603        case FL_Down:
604          while ((l = item_next(l))) {
605            if (Fl::event_state(FL_SHIFT|FL_CTRL))
606              select(l, l1 ? item_selected(l1) : 1, when());
607            if (item_height(l)>0) goto J1;
608          }
609          return 1;
610        case FL_Up:
611          while ((l = item_prev(l))) {
612            if (Fl::event_state(FL_SHIFT|FL_CTRL))
613              select(l, l1 ? item_selected(l1) : 1, when());
614            if (item_height(l)>0) goto J1;
615          }
616          return 1;
617J1:
618          if (selection_) redraw_line(selection_);
619          selection_ = l; redraw_line(l);
620          display(l);
621          return 1;
622        }
623      }
624    }
625  }
626 
627  if (Fl_Group::handle(event)) return 1;
628  int X, Y, W, H; bbox(X, Y, W, H);
629  int my;
630// NOTE:
631// instead of:
632//     change = select_only(find_item(my), when() & FL_WHEN_CHANGED)
633// we use the construct:
634//     change = select_only(find_item(my), 0);
635//     if (change && (when() & FL_WHEN_CHANGED)) {
636//       set_changed();
637//       do_callback();
638//     }
639// See str #834
640// The first form calls the callback *before* setting change.
641// The callback may execute an Fl::wait(), resulting in another
642// call of Fl_Browser_::handle() for the same widget. The sequence
643// of events can be an FL_PUSH followed by an FL_RELEASE.
644// This second call of Fl_Browser_::handle() may result in a -
645// somewhat unexpected - second concurrent invocation of the callback.
646
647  static char change;
648  static char whichway;
649  static int py;
650  switch (event) {
651  case FL_PUSH:
652    if (!Fl::event_inside(X, Y, W, H)) return 0;
653    if (Fl::visible_focus()) {
654      Fl::focus(this);
655      redraw();
656    }
657    my = py = Fl::event_y();
658    change = 0;
659    if (type() == FL_NORMAL_BROWSER || !top_)
660      ;
661    else if (type() != FL_MULTI_BROWSER) {
662      change = select_only(find_item(my), 0);
663      if (change && (when() & FL_WHEN_CHANGED)) {
664        set_changed();
665        do_callback();
666      }
667    } else {
668      void* l = find_item(my);
669      whichway = 1;
670      if (Fl::event_state(FL_CTRL)) { // toggle selection:
671      TOGGLE:
672        if (l) {
673          whichway = !item_selected(l);
674          change = select(l, whichway, 0);
675          if (change && (when() & FL_WHEN_CHANGED)) {
676            set_changed();
677            do_callback();
678          }
679        }
680      } else if (Fl::event_state(FL_SHIFT)) { // extend selection:
681        if (l == selection_) goto TOGGLE;
682        // state of previous selection determines new value:
683        whichway = l ? !item_selected(l) : 1;
684        // see which of the new item or previous selection is earlier,
685        // by searching from the previous forward for this one:
686        int down;
687        if (!l) down = 1;
688        else {for (void* m = selection_; ; m = item_next(m)) {
689          if (m == l) {down = 1; break;}
690          if (!m) {down = 0; break;}
691        }}
692        if (down) {
693          for (void* m = selection_; m != l; m = item_next(m))
694            select(m, whichway, when() & FL_WHEN_CHANGED);
695        } else {
696          void* e = selection_;
697          for (void* m = item_next(l); m; m = item_next(m)) {
698            select(m, whichway, when() & FL_WHEN_CHANGED);
699            if (m == e) break;
700          }
701        }
702        // do the clicked item last so the select box is around it:
703        change = 1;
704        if (l) select(l, whichway, when() & FL_WHEN_CHANGED);
705      } else { // select only this item
706        change = select_only(l, 0);
707        if (change && (when() & FL_WHEN_CHANGED)) {
708          set_changed();
709          do_callback();
710        }
711      }
712    }
713    return 1;
714  case FL_DRAG:
715    // do the scrolling first:
716    my = Fl::event_y();
717    if (my < Y && my < py) {
718      int p = real_position_+my-Y;
719      if (p<0) p = 0;
720      position(p);
721    } else if (my > (Y+H) && my > py) {
722      int p = real_position_+my-(Y+H);
723      int hh = full_height()-H; if (p > hh) p = hh;
724      if (p<0) p = 0;
725      position(p);
726    }
727    if (type() == FL_NORMAL_BROWSER || !top_)
728      ;
729    else if (type() == FL_MULTI_BROWSER) {
730      void* l = find_item(my);
731      void* t; void* b; // this will be the range to change
732      if (my > py) { // go down
733        t = selection_ ? item_next(selection_) : 0;
734        b = l ? item_next(l) : 0;
735      } else {  // go up
736        t = l;
737        b = selection_;
738      }
739      for (; t && t != b; t = item_next(t)) {
740        char change_t;
741        change_t = select(t, whichway, 0);
742        change |= change_t;
743        if (change_t && (when() & FL_WHEN_CHANGED)) {
744          set_changed();
745          do_callback();
746        }
747      }
748      if (l) selection_ = l;
749    } else {
750      void* l1 = selection_;
751      void* l =
752        (Fl::event_x()<x() || Fl::event_x()>x()+w()) ? selection_ :
753        find_item(my);
754      change = (l != l1);
755      select_only(l, when() & FL_WHEN_CHANGED);
756    }
757    py = my;
758    return 1;
759  case FL_RELEASE:
760    if (type() == FL_SELECT_BROWSER) {
761      void* t = selection_; deselect(); selection_ = t;
762    }
763    if (change) {
764      set_changed();
765      if (when() & FL_WHEN_RELEASE) do_callback();
766    } else {
767      if (when() & FL_WHEN_NOT_CHANGED) do_callback();
768    }
769   
770    // double click calls the callback: (like Enter Key)
771    if (Fl::event_clicks() && (when() & FL_WHEN_ENTER_KEY)) {
772      set_changed();
773      do_callback();
774    }
775    return 1;
776  case FL_FOCUS:
777  case FL_UNFOCUS:
778    if (type() >= FL_HOLD_BROWSER && Fl::visible_focus()) {
779      redraw();
780      return 1;
781    } else return 0;
782  }
783
784  return 0;
785}
786
787Fl_Browser_::Fl_Browser_(int X, int Y, int W, int H, const char* l)
788  : Fl_Group(X, Y, W, H, l),
789    scrollbar(0, 0, 0, 0, 0), // they will be resized by draw()
790    hscrollbar(0, 0, 0, 0, 0)
791{
792  box(FL_NO_BOX);
793  align(FL_ALIGN_BOTTOM);
794  position_ = real_position_ = 0;
795  hposition_ = real_hposition_ = 0;
796  offset_ = 0;
797  top_ = 0;
798  when(FL_WHEN_RELEASE_ALWAYS);
799  selection_ = 0;
800  color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
801  scrollbar.callback(scrollbar_callback);
802//scrollbar.align(FL_ALIGN_LEFT|FL_ALIGN_BOTTOM); // back compatability?
803  hscrollbar.callback(hscrollbar_callback);
804  hscrollbar.type(FL_HORIZONTAL);
805  textfont_ = FL_HELVETICA;
806  textsize_ = (uchar)FL_NORMAL_SIZE;
807  textcolor_ = FL_FOREGROUND_COLOR;
808  has_scrollbar_ = BOTH;
809  max_width = 0;
810  max_width_item = 0;
811  redraw1 = redraw2 = 0;
812  end();
813}
814
815// Default versions of some of the virtual functions:
816
817int Fl_Browser_::item_quick_height(void* l) const {
818  return item_height(l);
819}
820
821int Fl_Browser_::incr_height() const {
822  return item_quick_height(item_first());
823}
824
825int Fl_Browser_::full_height() const {
826  int t = 0;
827  for (void* p = item_first(); p; p = item_next(p))
828    t += item_quick_height(p);
829  return t;
830}
831
832int Fl_Browser_::full_width() const {
833  return max_width;
834}
835
836void Fl_Browser_::item_select(void*, int) {}
837
838int Fl_Browser_::item_selected(void* l) const {return l==selection_;}
839
840//
841// End of "$Id$".
842//
Note: See TracBrowser for help on using the repository browser.