blob: d93b43b6ccf718701d990f76a41ed3bb591bcb38 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
2// "$Id: Fl_Scroll.cxx 8591 2011-04-14 13:21:12Z manolo $"
3//
4// Scroll widget for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2010 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#include <FL/Fl.H>
29#include <FL/Fl_Tiled_Image.H>
30#include <FL/Fl_Scroll.H>
31#include <FL/fl_draw.H>
32
33/** Clear all but the scrollbars... */
34void Fl_Scroll::clear() {
35 // Note: the scrollbars are removed from the group before calling
36 // Fl_Group::clear() to take advantage of the optimized widget removal
37 // and deletion. Finally they are added to Fl_Scroll's group again. This
38 // is MUCH faster than removing the widgets one by one (STR #2409).
39
40 remove(scrollbar);
41 remove(hscrollbar);
42 Fl_Group::clear();
43 add(hscrollbar);
44 add(scrollbar);
45}
46
47/** Insure the scrollbars are the last children */
48void Fl_Scroll::fix_scrollbar_order() {
49 Fl_Widget** a = (Fl_Widget**)array();
50 if (a[children()-1] != &scrollbar) {
51 int i,j; for (i = j = 0; j < children(); j++)
52 if (a[j] != &hscrollbar && a[j] != &scrollbar) a[i++] = a[j];
53 a[i++] = &hscrollbar;
54 a[i++] = &scrollbar;
55 }
56}
57
58// Draw widget's background and children within a specific clip region
59// So widget can just redraw damaged parts.
60//
61void Fl_Scroll::draw_clip(void* v,int X, int Y, int W, int H) {
62 fl_push_clip(X,Y,W,H);
63 Fl_Scroll* s = (Fl_Scroll*)v;
64 // erase background as needed...
65 switch (s->box()) {
66 case FL_NO_BOX :
67 case FL_UP_FRAME :
68 case FL_DOWN_FRAME :
69 case FL_THIN_UP_FRAME :
70 case FL_THIN_DOWN_FRAME :
71 case FL_ENGRAVED_FRAME :
72 case FL_EMBOSSED_FRAME :
73 case FL_BORDER_FRAME :
74 case _FL_SHADOW_FRAME :
75 case _FL_ROUNDED_FRAME :
76 case _FL_OVAL_FRAME :
77 case _FL_PLASTIC_UP_FRAME :
78 case _FL_PLASTIC_DOWN_FRAME :
79 if (s->parent() == (Fl_Group *)s->window() && Fl::scheme_bg_) {
80 Fl::scheme_bg_->draw(X-(X%((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->w()),
81 Y-(Y%((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->h()),
82 W+((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->w(),
83 H+((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->h());
84 break;
85 }
86
87 default :
88 fl_color(s->color());
89 fl_rectf(X,Y,W,H);
90 break;
91 }
92 Fl_Widget*const* a = s->array();
93 for (int i=s->children()-2; i--;) {
94 Fl_Widget& o = **a++;
95 s->draw_child(o);
96 s->draw_outside_label(o);
97 }
98 fl_pop_clip();
99}
100
101/**
102 Calculate visibility/size/position of scrollbars, find children's bounding box.
103 The \p si paramater will be filled with data from the calculations.
104 Derived classes can make use of this call to figure out the scrolling area
105 eg. during resize() handling.
106 \param[in] si -- ScrollInfo structure
107 \returns Structure containing the calculated info.
108*/
109void Fl_Scroll::recalc_scrollbars(ScrollInfo &si) {
110
111 // inner box of widget (excluding scrollbars)
112 si.innerbox_x = x()+Fl::box_dx(box());
113 si.innerbox_y = y()+Fl::box_dy(box());
114 si.innerbox_w = w()-Fl::box_dw(box());
115 si.innerbox_h = h()-Fl::box_dh(box());
116
117 // accumulate a bounding box for all the children
118 si.child_l = si.innerbox_x;
119 si.child_r = si.innerbox_x;
120 si.child_b = si.innerbox_y;
121 si.child_t = si.innerbox_y;
122 int first = 1;
123 Fl_Widget*const* a = array();
124 for (int i=children()-2; i--;) {
125 Fl_Widget* o = *a++;
126 if ( first ) {
127 first = 0;
128 si.child_l = o->x();
129 si.child_r = o->x()+o->w();
130 si.child_b = o->y()+o->h();
131 si.child_t = o->y();
132 } else {
133 if (o->x() < si.child_l) si.child_l = o->x();
134 if (o->y() < si.child_t) si.child_t = o->y();
135 if (o->x()+o->w() > si.child_r) si.child_r = o->x()+o->w();
136 if (o->y()+o->h() > si.child_b) si.child_b = o->y()+o->h();
137 }
138 }
139
140 // Turn the scrollbars on and off as necessary.
141 // See if children would fit if we had no scrollbars...
142 {
143 int X = si.innerbox_x;
144 int Y = si.innerbox_y;
145 int W = si.innerbox_w;
146 int H = si.innerbox_h;
147
148 si.scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
149 si.vneeded = 0;
150 si.hneeded = 0;
151 if (type() & VERTICAL) {
152 if ((type() & ALWAYS_ON) || si.child_t < Y || si.child_b > Y+H) {
153 si.vneeded = 1;
154 W -= si.scrollsize;
155 if (scrollbar.align() & FL_ALIGN_LEFT) X += si.scrollsize;
156 }
157 }
158 if (type() & HORIZONTAL) {
159 if ((type() & ALWAYS_ON) || si.child_l < X || si.child_r > X+W) {
160 si.hneeded = 1;
161 H -= si.scrollsize;
162 if (scrollbar.align() & FL_ALIGN_TOP) Y += si.scrollsize;
163 // recheck vertical since we added a horizontal scrollbar
164 if (!si.vneeded && (type() & VERTICAL)) {
165 if ((type() & ALWAYS_ON) || si.child_t < Y || si.child_b > Y+H) {
166 si.vneeded = 1;
167 W -= si.scrollsize;
168 if (scrollbar.align() & FL_ALIGN_LEFT) X += si.scrollsize;
169 }
170 }
171 }
172 }
173 si.innerchild_x = X;
174 si.innerchild_y = Y;
175 si.innerchild_w = W;
176 si.innerchild_h = H;
177 }
178
179 // calculate hor scrollbar position
180 si.hscroll_x = si.innerchild_x;
181 si.hscroll_y = (scrollbar.align() & FL_ALIGN_TOP)
182 ? si.innerbox_y
183 : si.innerbox_y + si.innerbox_h - si.scrollsize;
184 si.hscroll_w = si.innerchild_w;
185 si.hscroll_h = si.scrollsize;
186
187 // calculate ver scrollbar position
188 si.vscroll_x = (scrollbar.align() & FL_ALIGN_LEFT)
189 ? si.innerbox_x
190 : si.innerbox_x + si.innerbox_w - si.scrollsize;
191 si.vscroll_y = si.innerchild_y;
192 si.vscroll_w = si.scrollsize;
193 si.vscroll_h = si.innerchild_h;
194
195 // calculate h/v scrollbar values (pos/size/first/total)
196 si.hpos = si.innerchild_x - si.child_l;
197 si.hsize = si.innerchild_w;
198 si.hfirst = 0;
199 si.htotal = si.child_r - si.child_l;
200 if ( si.hpos < 0 ) { si.htotal += (-si.hpos); si.hfirst = si.hpos; }
201
202 si.vpos = si.innerchild_y - si.child_t;
203 si.vsize = si.innerchild_h;
204 si.vfirst = 0;
205 si.vtotal = si.child_b - si.child_t;
206 if ( si.vpos < 0 ) { si.vtotal += (-si.vpos); si.vfirst = si.vpos; }
207
208// printf("DEBUG --- ScrollInfo ---\n");
209// printf("DEBUG scrollsize: %d\n", si.scrollsize);
210// printf("DEBUG hneeded, vneeded: %d %d\n", si.hneeded, si.vneeded);
211// printf("DEBUG innerbox xywh: %d %d %d %d\n", si.innerbox_x, si.innerbox_y, si.innerbox_w, si.innerbox_h);
212// printf("DEBUG innerchild xywh: %d %d %d %d\n", si.innerchild_x, si.innerchild_y, si.innerchild_w, si.innerchild_h);
213// printf("DEBUG child lrbt: %d %d %d %d\n", si.child_l, si.child_r, si.child_b, si.child_t);
214// printf("DEBUG hscroll xywh: %d %d %d %d\n", si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
215// printf("DEBUG vscroll xywh: %d %d %d %d\n", si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
216// printf("DEBUG horz scroll vals: %d %d %d %d\n", si.hpos, si.hsize, si.hfirst, si.htotal);
217// printf("DEBUG vert scroll vals: %d %d %d %d\n", si.vpos, si.vsize, si.vfirst, si.vtotal);
218// printf("DEBUG \n");
219}
220
221/**
222 Returns the bounding box for the interior of the scrolling area, inside
223 the scrollbars.
224
225 Currently this is only reliable after draw(), and before any resizing of
226 the Fl_Scroll or any child widgets occur.
227
228 \todo The visibility of the scrollbars ought to be checked/calculated
229 outside of the draw() method (STR #1895).
230*/
231void Fl_Scroll::bbox(int& X, int& Y, int& W, int& H) {
232 X = x()+Fl::box_dx(box());
233 Y = y()+Fl::box_dy(box());
234 W = w()-Fl::box_dw(box());
235 H = h()-Fl::box_dh(box());
236 if (scrollbar.visible()) {
237 W -= scrollbar.w();
238 if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar.w();
239 }
240 if (hscrollbar.visible()) {
241 H -= hscrollbar.h();
242 if (scrollbar.align() & FL_ALIGN_TOP) Y += hscrollbar.h();
243 }
244}
245
246void Fl_Scroll::draw() {
247 fix_scrollbar_order();
248 int X,Y,W,H; bbox(X,Y,W,H);
249
250 uchar d = damage();
251
252 if (d & FL_DAMAGE_ALL) { // full redraw
253 draw_box(box(),x(),y(),w(),h(),color());
254 draw_clip(this, X, Y, W, H);
255 } else {
256 if (d & FL_DAMAGE_SCROLL) {
257 // scroll the contents:
258 fl_scroll(X, Y, W, H, oldx-xposition_, oldy-yposition_, draw_clip, this);
259
260 // Erase the background as needed...
261 Fl_Widget*const* a = array();
262 int L, R, T, B;
263 L = 999999;
264 R = 0;
265 T = 999999;
266 B = 0;
267 for (int i=children()-2; i--; a++) {
268 if ((*a)->x() < L) L = (*a)->x();
269 if (((*a)->x() + (*a)->w()) > R) R = (*a)->x() + (*a)->w();
270 if ((*a)->y() < T) T = (*a)->y();
271 if (((*a)->y() + (*a)->h()) > B) B = (*a)->y() + (*a)->h();
272 }
273 if (L > X) draw_clip(this, X, Y, L - X, H);
274 if (R < (X + W)) draw_clip(this, R, Y, X + W - R, H);
275 if (T > Y) draw_clip(this, X, Y, W, T - Y);
276 if (B < (Y + H)) draw_clip(this, X, B, W, Y + H - B);
277 }
278 if (d & FL_DAMAGE_CHILD) { // draw damaged children
279 fl_push_clip(X, Y, W, H);
280 Fl_Widget*const* a = array();
281 for (int i=children()-2; i--;) update_child(**a++);
282 fl_pop_clip();
283 }
284 }
285
286 // Calculate where scrollbars should go, and draw them
287 {
288 ScrollInfo si;
289 recalc_scrollbars(si);
290
291 // Now that we know what's needed, make it so.
292 if (si.vneeded && !scrollbar.visible()) {
293 scrollbar.set_visible();
294 d = FL_DAMAGE_ALL;
295 }
296 else if (!si.vneeded && scrollbar.visible()) {
297 scrollbar.clear_visible();
298 draw_clip(this, si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
299 d = FL_DAMAGE_ALL;
300 }
301 if (si.hneeded && !hscrollbar.visible()) {
302 hscrollbar.set_visible();
303 d = FL_DAMAGE_ALL;
304 }
305 else if (!si.hneeded && hscrollbar.visible()) {
306 hscrollbar.clear_visible();
307 draw_clip(this, si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
308 d = FL_DAMAGE_ALL;
309 }
310 else if ( hscrollbar.h() != si.scrollsize || scrollbar.w() != si.scrollsize ) {
311 // scrollsize changed
312 d = FL_DAMAGE_ALL;
313 }
314
315 scrollbar.resize(si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
316 oldy = yposition_ = si.vpos; // si.innerchild_y - si.child_t;
317 scrollbar.value(si.vpos, si.vsize, si.vfirst, si.vtotal);
318
319 hscrollbar.resize(si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
320 oldx = xposition_ = si.hpos; // si.innerchild_x - si.child_l;
321 hscrollbar.value(si.hpos, si.hsize, si.hfirst, si.htotal);
322 }
323
324 // draw the scrollbars:
325 if (d & FL_DAMAGE_ALL) {
326 draw_child(scrollbar);
327 draw_child(hscrollbar);
328 if (scrollbar.visible() && hscrollbar.visible()) {
329 // fill in the little box in the corner
330 fl_color(color());
331 fl_rectf(scrollbar.x(), hscrollbar.y(), scrollbar.w(), hscrollbar.h());
332 }
333 } else {
334 update_child(scrollbar);
335 update_child(hscrollbar);
336 }
337}
338
339void Fl_Scroll::resize(int X, int Y, int W, int H) {
340 int dx = X-x(), dy = Y-y();
341 int dw = W-w(), dh = H-h();
342 Fl_Widget::resize(X,Y,W,H); // resize _before_ moving children around
343 fix_scrollbar_order();
344 // move all the children:
345 Fl_Widget*const* a = array();
346 for (int i=children()-2; i--;) {
347 Fl_Widget* o = *a++;
348 o->position(o->x()+dx, o->y()+dy);
349 }
350 if (dw==0 && dh==0) {
351 char pad = ( scrollbar.visible() && hscrollbar.visible() );
352 char al = ( (scrollbar.align() & FL_ALIGN_LEFT) != 0 );
353 char at = ( (scrollbar.align() & FL_ALIGN_TOP) !=0 );
354 scrollbar.position(al?X:X+W-scrollbar.w(), (at&&pad)?Y+hscrollbar.h():Y);
355 hscrollbar.position((al&&pad)?X+scrollbar.w():X, at?Y:Y+H-hscrollbar.h());
356 } else {
357 // FIXME recalculation of scrollbars needs to be moved out fo "draw()" (STR #1895)
358 redraw(); // need full recalculation of scrollbars
359 }
360}
361
362/** Moves the contents of the scroll group to a new position.*/
363void Fl_Scroll::scroll_to(int X, int Y) {
364 int dx = xposition_-X;
365 int dy = yposition_-Y;
366 if (!dx && !dy) return;
367 xposition_ = X;
368 yposition_ = Y;
369 Fl_Widget*const* a = array();
370 for (int i=children(); i--;) {
371 Fl_Widget* o = *a++;
372 if (o == &hscrollbar || o == &scrollbar) continue;
373 o->position(o->x()+dx, o->y()+dy);
374 }
375 if (parent() == (Fl_Group *)window() && Fl::scheme_bg_) damage(FL_DAMAGE_ALL);
376 else damage(FL_DAMAGE_SCROLL);
377}
378
379void Fl_Scroll::hscrollbar_cb(Fl_Widget* o, void*) {
380 Fl_Scroll* s = (Fl_Scroll*)(o->parent());
381 s->scroll_to(int(((Fl_Scrollbar*)o)->value()), s->yposition());
382}
383
384void Fl_Scroll::scrollbar_cb(Fl_Widget* o, void*) {
385 Fl_Scroll* s = (Fl_Scroll*)(o->parent());
386 s->scroll_to(s->xposition(), int(((Fl_Scrollbar*)o)->value()));
387}
388/**
389 Creates a new Fl_Scroll widget using the given position,
390 size, and label string. The default boxtype is FL_NO_BOX.
391 <P>The destructor <I>also deletes all the children</I>. This allows a
392 whole tree to be deleted at once, without having to keep a pointer to
393 all the children in the user code. A kludge has been done so the
394 Fl_Scroll and all of its children can be automatic (local)
395 variables, but you must declare the Fl_Scroll<I>first</I>, so
396 that it is destroyed last.
397*/
398Fl_Scroll::Fl_Scroll(int X,int Y,int W,int H,const char* L)
399 : Fl_Group(X,Y,W,H,L),
400 scrollbar(X+W-Fl::scrollbar_size(),Y,
401 Fl::scrollbar_size(),H-Fl::scrollbar_size()),
402 hscrollbar(X,Y+H-Fl::scrollbar_size(),
403 W-Fl::scrollbar_size(),Fl::scrollbar_size()) {
404 type(BOTH);
405 xposition_ = oldx = 0;
406 yposition_ = oldy = 0;
407 scrollbar_size_ = 0;
408 hscrollbar.type(FL_HORIZONTAL);
409 hscrollbar.callback(hscrollbar_cb);
410 scrollbar.callback(scrollbar_cb);
411}
412
413int Fl_Scroll::handle(int event) {
414 fix_scrollbar_order();
415 return Fl_Group::handle(event);
416}
417
418//
419// End of "$Id: Fl_Scroll.cxx 8591 2011-04-14 13:21:12Z manolo $".
420//