themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_Scroll.cxx
blob9f91c1bd20d1795310cb2553c849566572694245
1 //
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.
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.
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.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <FL/Fl.H>
29 #include <FL/Fl_Tiled_Image.H>
30 #include <FL/Fl_Scroll.H>
31 #include <FL/fl_draw.H>
33 /** Clear all but the scrollbars... */
34 void 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).
40 remove(scrollbar);
41 remove(hscrollbar);
42 Fl_Group::clear();
43 add(hscrollbar);
44 add(scrollbar);
47 /** Insure the scrollbars are the last children */
48 void 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;
58 // Draw widget's background and children within a specific clip region
59 // So widget can just redraw damaged parts.
61 void 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 if (s->parent() == (Fl_Group *)s->window() && Fl::scheme_bg_) {
78 Fl::scheme_bg_->draw(X-(X%((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->w()),
79 Y-(Y%((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->h()),
80 W+((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->w(),
81 H+((Fl_Tiled_Image *)Fl::scheme_bg_)->image()->h());
82 break;
85 default :
86 fl_color(s->color());
87 fl_rectf(X,Y,W,H);
88 break;
90 Fl_Widget*const* a = s->array();
91 for (int i=s->children()-2; i--;) {
92 Fl_Widget& o = **a++;
93 s->draw_child(o);
94 s->draw_outside_label(o);
96 fl_pop_clip();
99 /**
100 Calculate visibility/size/position of scrollbars, find children's bounding box.
101 The \p si paramater will be filled with data from the calculations.
102 Derived classes can make use of this call to figure out the scrolling area
103 eg. during resize() handling.
104 \param[in] si -- ScrollInfo structure
105 \returns Structure containing the calculated info.
107 void Fl_Scroll::recalc_scrollbars(ScrollInfo &si) {
109 // inner box of widget (excluding scrollbars)
110 si.innerbox_x = x()+Fl::box_dx(box());
111 si.innerbox_y = y()+Fl::box_dy(box());
112 si.innerbox_w = w()-Fl::box_dw(box());
113 si.innerbox_h = h()-Fl::box_dh(box());
115 // accumulate a bounding box for all the children
116 si.child_l = si.innerbox_x;
117 si.child_r = si.innerbox_x;
118 si.child_b = si.innerbox_y;
119 si.child_t = si.innerbox_y;
120 int first = 1;
121 Fl_Widget*const* a = array();
122 for (int i=children()-2; i--;) {
123 Fl_Widget* o = *a++;
124 if ( first ) {
125 first = 0;
126 si.child_l = o->x();
127 si.child_r = o->x()+o->w();
128 si.child_b = o->y()+o->h();
129 si.child_t = o->y();
130 } else {
131 if (o->x() < si.child_l) si.child_l = o->x();
132 if (o->y() < si.child_t) si.child_t = o->y();
133 if (o->x()+o->w() > si.child_r) si.child_r = o->x()+o->w();
134 if (o->y()+o->h() > si.child_b) si.child_b = o->y()+o->h();
138 // Turn the scrollbars on and off as necessary.
139 // See if children would fit if we had no scrollbars...
141 int X = si.innerbox_x;
142 int Y = si.innerbox_y;
143 int W = si.innerbox_w;
144 int H = si.innerbox_h;
146 si.scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
147 si.vneeded = 0;
148 si.hneeded = 0;
149 if (type() & VERTICAL) {
150 if ((type() & ALWAYS_ON) || si.child_t < Y || si.child_b > Y+H) {
151 si.vneeded = 1;
152 W -= si.scrollsize;
153 if (scrollbar.align() & FL_ALIGN_LEFT) X += si.scrollsize;
156 if (type() & HORIZONTAL) {
157 if ((type() & ALWAYS_ON) || si.child_l < X || si.child_r > X+W) {
158 si.hneeded = 1;
159 H -= si.scrollsize;
160 if (scrollbar.align() & FL_ALIGN_TOP) Y += si.scrollsize;
161 // recheck vertical since we added a horizontal scrollbar
162 if (!si.vneeded && (type() & VERTICAL)) {
163 if ((type() & ALWAYS_ON) || si.child_t < Y || si.child_b > Y+H) {
164 si.vneeded = 1;
165 W -= si.scrollsize;
166 if (scrollbar.align() & FL_ALIGN_LEFT) X += si.scrollsize;
171 si.innerchild_x = X;
172 si.innerchild_y = Y;
173 si.innerchild_w = W;
174 si.innerchild_h = H;
177 // calculate hor scrollbar position
178 si.hscroll_x = si.innerchild_x;
179 si.hscroll_y = (scrollbar.align() & FL_ALIGN_TOP)
180 ? si.innerbox_y
181 : si.innerbox_y + si.innerbox_h - si.scrollsize;
182 si.hscroll_w = si.innerchild_w;
183 si.hscroll_h = si.scrollsize;
185 // calculate ver scrollbar position
186 si.vscroll_x = (scrollbar.align() & FL_ALIGN_LEFT)
187 ? si.innerbox_x
188 : si.innerbox_x + si.innerbox_w - si.scrollsize;
189 si.vscroll_y = si.innerchild_y;
190 si.vscroll_w = si.scrollsize;
191 si.vscroll_h = si.innerchild_h;
193 // calculate h/v scrollbar values (pos/size/first/total)
194 si.hpos = si.innerchild_x - si.child_l;
195 si.hsize = si.innerchild_w;
196 si.hfirst = 0;
197 si.htotal = si.child_r - si.child_l;
198 if ( si.hpos < 0 ) { si.htotal += (-si.hpos); si.hfirst = si.hpos; }
200 si.vpos = si.innerchild_y - si.child_t;
201 si.vsize = si.innerchild_h;
202 si.vfirst = 0;
203 si.vtotal = si.child_b - si.child_t;
204 if ( si.vpos < 0 ) { si.vtotal += (-si.vpos); si.vfirst = si.vpos; }
206 // printf("DEBUG --- ScrollInfo ---\n");
207 // printf("DEBUG scrollsize: %d\n", si.scrollsize);
208 // printf("DEBUG hneeded, vneeded: %d %d\n", si.hneeded, si.vneeded);
209 // printf("DEBUG innerbox xywh: %d %d %d %d\n", si.innerbox_x, si.innerbox_y, si.innerbox_w, si.innerbox_h);
210 // printf("DEBUG innerchild xywh: %d %d %d %d\n", si.innerchild_x, si.innerchild_y, si.innerchild_w, si.innerchild_h);
211 // printf("DEBUG child lrbt: %d %d %d %d\n", si.child_l, si.child_r, si.child_b, si.child_t);
212 // printf("DEBUG hscroll xywh: %d %d %d %d\n", si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
213 // printf("DEBUG vscroll xywh: %d %d %d %d\n", si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
214 // printf("DEBUG horz scroll vals: %d %d %d %d\n", si.hpos, si.hsize, si.hfirst, si.htotal);
215 // printf("DEBUG vert scroll vals: %d %d %d %d\n", si.vpos, si.vsize, si.vfirst, si.vtotal);
216 // printf("DEBUG \n");
220 Returns the bounding box for the interior of the scrolling area, inside
221 the scrollbars.
223 Currently this is only reliable after draw(), and before any resizing of
224 the Fl_Scroll or any child widgets occur.
226 \todo The visibility of the scrollbars ought to be checked/calculated
227 outside of the draw() method (STR #1895).
229 void Fl_Scroll::bbox(int& X, int& Y, int& W, int& H) {
230 X = x()+Fl::box_dx(box());
231 Y = y()+Fl::box_dy(box());
232 W = w()-Fl::box_dw(box());
233 H = h()-Fl::box_dh(box());
234 if (scrollbar.visible()) {
235 W -= scrollbar.w();
236 if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar.w();
238 if (hscrollbar.visible()) {
239 H -= hscrollbar.h();
240 if (scrollbar.align() & FL_ALIGN_TOP) Y += hscrollbar.h();
244 void Fl_Scroll::draw() {
245 box( FL_FLAT_BOX );
246 fix_scrollbar_order();
247 int X,Y,W,H; bbox(X,Y,W,H);
249 fl_damage_t d = damage();
251 if ( d & FL_DAMAGE_ALL ) { // full redraw
252 draw_box(box(),x(),y(),w(),h(),color());
253 draw_clip(this, X, Y, W, H);
254 } else {
255 if ( d & FL_DAMAGE_SCROLL) {
256 // scroll the contents:
257 fl_scroll(X, Y, W, H, oldx-xposition_, oldy-yposition_, draw_clip, this);
259 // Erase the background as needed...
260 Fl_Widget*const* a = array();
261 int L, R, T, B;
262 L = 999999;
263 R = 0;
264 T = 999999;
265 B = 0;
266 for (int i=children()-2; i--; a++) {
267 if ((*a)->x() < L) L = (*a)->x();
268 if (((*a)->x() + (*a)->w()) > R) R = (*a)->x() + (*a)->w();
269 if ((*a)->y() < T) T = (*a)->y();
270 if (((*a)->y() + (*a)->h()) > B) B = (*a)->y() + (*a)->h();
272 if (L > X) draw_clip(this, X, Y, L - X, H);
273 if (R < (X + W)) draw_clip(this, R, Y, X + W - R, H);
274 if (T > Y) draw_clip(this, X, Y, W, T - Y);
275 if (B < (Y + H)) draw_clip(this, X, B, W, Y + H - B);
277 if ( d & FL_DAMAGE_CHILD) { // draw damaged children
278 fl_push_clip(X, Y, W, H);
279 Fl_Widget*const* a = array();
280 for (int i=children()-2; i--;) update_child(**a++);
281 fl_pop_clip();
285 // Calculate where scrollbars should go, and draw them
287 ScrollInfo si;
288 recalc_scrollbars(si);
290 // Now that we know what's needed, make it so.
291 if (si.vneeded && !scrollbar.visible()) {
292 scrollbar.set_visible();
293 d = FL_DAMAGE_ALL;
295 else if (!si.vneeded && scrollbar.visible()) {
296 scrollbar.clear_visible();
297 draw_clip(this, si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
298 d = FL_DAMAGE_ALL;
300 if (si.hneeded && !hscrollbar.visible()) {
301 hscrollbar.set_visible();
302 d = FL_DAMAGE_ALL;
304 else if (!si.hneeded && hscrollbar.visible()) {
305 hscrollbar.clear_visible();
306 draw_clip(this, si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
307 d = FL_DAMAGE_ALL;
309 else if ( hscrollbar.h() != si.scrollsize || scrollbar.w() != si.scrollsize ) {
310 // scrollsize changed
311 d = FL_DAMAGE_ALL;
314 scrollbar.resize(si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
315 oldy = yposition_ = si.vpos; // si.innerchild_y - si.child_t;
316 scrollbar.value(si.vpos, si.vsize, si.vfirst, si.vtotal);
318 hscrollbar.resize(si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
319 oldx = xposition_ = si.hpos; // si.innerchild_x - si.child_l;
320 hscrollbar.value(si.hpos, si.hsize, si.hfirst, si.htotal);
323 // draw the scrollbars:
324 if (d & FL_DAMAGE_ALL) {
325 draw_child(scrollbar);
326 draw_child(hscrollbar);
327 if (scrollbar.visible() && hscrollbar.visible()) {
328 // fill in the little box in the corner
329 fl_color(color());
330 fl_rectf(scrollbar.x(), hscrollbar.y(), scrollbar.w(), hscrollbar.h());
332 } else {
333 update_child(scrollbar);
334 update_child(hscrollbar);
338 void Fl_Scroll::resize(int X, int Y, int W, int H) {
339 int dx = X-x(), dy = Y-y();
340 int dw = W-w(), dh = H-h();
341 Fl_Widget::resize(X,Y,W,H); // resize _before_ moving children around
342 fix_scrollbar_order();
343 // move all the children:
344 Fl_Widget*const* a = array();
345 for (int i=children()-2; i--;) {
346 Fl_Widget* o = *a++;
347 o->position(o->x()+dx, o->y()+dy);
349 if (dw==0 && dh==0) {
350 char pad = ( scrollbar.visible() && hscrollbar.visible() );
351 char al = ( (scrollbar.align() & FL_ALIGN_LEFT) != 0 );
352 char at = ( (scrollbar.align() & FL_ALIGN_TOP) !=0 );
353 scrollbar.position(al?X:X+W-scrollbar.w(), (at&&pad)?Y+hscrollbar.h():Y);
354 hscrollbar.position((al&&pad)?X+scrollbar.w():X, at?Y:Y+H-hscrollbar.h());
355 } else {
356 // FIXME recalculation of scrollbars needs to be moved out fo "draw()" (STR #1895)
357 redraw(); // need full recalculation of scrollbars
361 /** Moves the contents of the scroll group to a new position.*/
362 void Fl_Scroll::scroll_to(int X, int Y) {
363 int dx = xposition_-X;
364 int dy = yposition_-Y;
365 if (!dx && !dy) return;
366 xposition_ = X;
367 yposition_ = Y;
368 Fl_Widget*const* a = array();
369 for (int i=children(); i--;) {
370 Fl_Widget* o = *a++;
371 if (o == &hscrollbar || o == &scrollbar) continue;
372 o->position(o->x()+dx, o->y()+dy);
374 /* if (parent() == (Fl_Group *)window() && Fl::scheme_bg_) damage(FL_DAMAGE_ALL); */
375 /* else */
376 damage(FL_DAMAGE_SCROLL);
379 void 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());
384 void 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()));
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.
398 Fl_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);
413 int Fl_Scroll::handle(int event) {
414 fix_scrollbar_order();
415 return Fl_Group::handle(event);
419 // End of "$Id: Fl_Scroll.cxx 8591 2011-04-14 13:21:12Z manolo $".