DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1 | // |
| 2 | // "$Id: Fl_Slider.cxx 8726 2011-05-23 18:32:47Z AlbrechtS $" |
| 3 | // |
| 4 | // Slider widget for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2011 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_Slider.H> |
| 30 | #include <FL/fl_draw.H> |
| 31 | #include <math.h> |
| 32 | #include "flstring.h" |
| 33 | |
| 34 | #if defined(FL_DLL) // really needed for c'tors for MS VC++ only |
| 35 | #include <FL/Fl_Hor_Slider.H> |
| 36 | #endif |
| 37 | |
| 38 | void Fl_Slider::_Fl_Slider() { |
| 39 | slider_size_ = 0; |
| 40 | slider_ = 0; // FL_UP_BOX; |
| 41 | } |
| 42 | |
| 43 | /** |
| 44 | Creates a new Fl_Slider widget using the given position, |
| 45 | size, and label string. The default boxtype is FL_DOWN_BOX. |
| 46 | */ |
| 47 | Fl_Slider::Fl_Slider(int X, int Y, int W, int H, const char* L) |
| 48 | : Fl_Valuator(X, Y, W, H, L) { |
| 49 | box(FL_DOWN_BOX); |
| 50 | _Fl_Slider(); |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | Creates a new Fl_Slider widget using the given box type, position, |
| 55 | size, and label string. |
| 56 | */ |
| 57 | Fl_Slider::Fl_Slider(uchar t, int X, int Y, int W, int H, const char* L) |
| 58 | : Fl_Valuator(X, Y, W, H, L) { |
| 59 | type(t); |
| 60 | box(t==FL_HOR_NICE_SLIDER || t==FL_VERT_NICE_SLIDER ? |
| 61 | FL_FLAT_BOX : FL_DOWN_BOX); |
| 62 | _Fl_Slider(); |
| 63 | } |
| 64 | |
| 65 | void Fl_Slider::slider_size(double v) { |
| 66 | if (v < 0) v = 0; |
| 67 | if (v > 1) v = 1; |
| 68 | if (slider_size_ != float(v)) { |
| 69 | slider_size_ = float(v); |
| 70 | damage(FL_DAMAGE_EXPOSE); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | Sets the minimum (a) and maximum (b) values for the valuator widget. |
| 76 | if at least one of the values is changed, a partial redraw is asked. |
| 77 | */ |
| 78 | void Fl_Slider::bounds(double a, double b) { |
| 79 | if (minimum() != a || maximum() != b) { |
| 80 | Fl_Valuator::bounds(a, b); |
| 81 | damage(FL_DAMAGE_EXPOSE); |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | Sets the size and position of the sliding knob in the box. |
| 87 | \param[in] pos position of first line displayed |
| 88 | \param[in] size size of window in lines |
| 89 | \param[in] first number of first line |
| 90 | \param[in] total total number of lines |
| 91 | Returns Fl_Valuator::value(p) |
| 92 | */ |
| 93 | int Fl_Slider::scrollvalue(int pos, int size, int first, int total) { |
| 94 | step(1, 1); |
| 95 | if (pos+size > first+total) total = pos+size-first; |
| 96 | slider_size(size >= total ? 1.0 : double(size)/double(total)); |
| 97 | bounds(first, total-size+first); |
| 98 | return value(pos); |
| 99 | } |
| 100 | |
| 101 | // All slider interaction is done as though the slider ranges from |
| 102 | // zero to one, and the left (bottom) edge of the slider is at the |
| 103 | // given position. Since when the slider is all the way to the |
| 104 | // right (top) the left (bottom) edge is not all the way over, a |
| 105 | // position on the widget itself covers a wider range than 0-1, |
| 106 | // actually it ranges from 0 to 1/(1-size). |
| 107 | |
| 108 | void Fl_Slider::draw_bg(int X, int Y, int W, int H) { |
| 109 | fl_push_clip(X, Y, W, H); |
| 110 | draw_box(); |
| 111 | fl_pop_clip(); |
| 112 | |
| 113 | Fl_Color black = active_r() ? FL_FOREGROUND_COLOR : FL_INACTIVE_COLOR; |
| 114 | if (type() == FL_VERT_NICE_SLIDER) { |
| 115 | draw_box(FL_THIN_DOWN_BOX, X+W/2-2, Y, 4, H, black); |
| 116 | } else if (type() == FL_HOR_NICE_SLIDER) { |
| 117 | draw_box(FL_THIN_DOWN_BOX, X, Y+H/2-2, W, 4, black); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | void Fl_Slider::draw(int X, int Y, int W, int H) { |
| 122 | |
| 123 | double val; |
| 124 | if (minimum() == maximum()) |
| 125 | val = 0.5; |
| 126 | else { |
| 127 | val = (value()-minimum())/(maximum()-minimum()); |
| 128 | if (val > 1.0) val = 1.0; |
| 129 | else if (val < 0.0) val = 0.0; |
| 130 | } |
| 131 | |
| 132 | int ww = (horizontal() ? W : H); |
| 133 | int xx, S; |
| 134 | if (type()==FL_HOR_FILL_SLIDER || type() == FL_VERT_FILL_SLIDER) { |
| 135 | S = int(val*ww+.5); |
| 136 | if (minimum()>maximum()) {S = ww-S; xx = ww-S;} |
| 137 | else xx = 0; |
| 138 | } else { |
| 139 | S = int(slider_size_*ww+.5); |
| 140 | int T = (horizontal() ? H : W)/2+1; |
| 141 | if (type()==FL_VERT_NICE_SLIDER || type()==FL_HOR_NICE_SLIDER) T += 4; |
| 142 | if (S < T) S = T; |
| 143 | xx = int(val*(ww-S)+.5); |
| 144 | } |
| 145 | int xsl, ysl, wsl, hsl; |
| 146 | if (horizontal()) { |
| 147 | xsl = X+xx; |
| 148 | wsl = S; |
| 149 | ysl = Y; |
| 150 | hsl = H; |
| 151 | } else { |
| 152 | ysl = Y+xx; |
| 153 | hsl = S; |
| 154 | xsl = X; |
| 155 | wsl = W; |
| 156 | } |
| 157 | |
| 158 | draw_bg(X, Y, W, H); |
| 159 | |
| 160 | Fl_Boxtype box1 = slider(); |
| 161 | if (!box1) {box1 = (Fl_Boxtype)(box()&-2); if (!box1) box1 = FL_UP_BOX;} |
| 162 | if (type() == FL_VERT_NICE_SLIDER) { |
| 163 | draw_box(box1, xsl, ysl, wsl, hsl, FL_GRAY); |
| 164 | int d = (hsl-4)/2; |
| 165 | draw_box(FL_THIN_DOWN_BOX, xsl+2, ysl+d, wsl-4, hsl-2*d,selection_color()); |
| 166 | } else if (type() == FL_HOR_NICE_SLIDER) { |
| 167 | draw_box(box1, xsl, ysl, wsl, hsl, FL_GRAY); |
| 168 | int d = (wsl-4)/2; |
| 169 | draw_box(FL_THIN_DOWN_BOX, xsl+d, ysl+2, wsl-2*d, hsl-4,selection_color()); |
| 170 | } else { |
| 171 | if (wsl>0 && hsl>0) draw_box(box1, xsl, ysl, wsl, hsl, selection_color()); |
| 172 | |
| 173 | if (type()!=FL_HOR_FILL_SLIDER && type() != FL_VERT_FILL_SLIDER && |
| 174 | Fl::scheme_ && !strcmp(Fl::scheme_, "gtk+")) { |
| 175 | if (W>H && wsl>(hsl+8)) { |
| 176 | // Draw horizontal grippers |
| 177 | int yy, hh; |
| 178 | hh = hsl-8; |
| 179 | xx = xsl+(wsl-hsl-4)/2; |
| 180 | yy = ysl+3; |
| 181 | |
| 182 | fl_color(fl_darker(selection_color())); |
| 183 | fl_line(xx, yy+hh, xx+hh, yy); |
| 184 | fl_line(xx+6, yy+hh, xx+hh+6, yy); |
| 185 | fl_line(xx+12, yy+hh, xx+hh+12, yy); |
| 186 | |
| 187 | xx++; |
| 188 | fl_color(fl_lighter(selection_color())); |
| 189 | fl_line(xx, yy+hh, xx+hh, yy); |
| 190 | fl_line(xx+6, yy+hh, xx+hh+6, yy); |
| 191 | fl_line(xx+12, yy+hh, xx+hh+12, yy); |
| 192 | } else if (H>W && hsl>(wsl+8)) { |
| 193 | // Draw vertical grippers |
| 194 | int yy; |
| 195 | xx = xsl+4; |
| 196 | ww = wsl-8; |
| 197 | yy = ysl+(hsl-wsl-4)/2; |
| 198 | |
| 199 | fl_color(fl_darker(selection_color())); |
| 200 | fl_line(xx, yy+ww, xx+ww, yy); |
| 201 | fl_line(xx, yy+ww+6, xx+ww, yy+6); |
| 202 | fl_line(xx, yy+ww+12, xx+ww, yy+12); |
| 203 | |
| 204 | yy++; |
| 205 | fl_color(fl_lighter(selection_color())); |
| 206 | fl_line(xx, yy+ww, xx+ww, yy); |
| 207 | fl_line(xx, yy+ww+6, xx+ww, yy+6); |
| 208 | fl_line(xx, yy+ww+12, xx+ww, yy+12); |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | draw_label(xsl, ysl, wsl, hsl); |
| 214 | if (Fl::focus() == this) { |
| 215 | if (type() == FL_HOR_FILL_SLIDER || type() == FL_VERT_FILL_SLIDER) draw_focus(); |
| 216 | else draw_focus(box1, xsl, ysl, wsl, hsl); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | void Fl_Slider::draw() { |
| 221 | if (damage()&FL_DAMAGE_ALL) draw_box(); |
| 222 | draw(x()+Fl::box_dx(box()), |
| 223 | y()+Fl::box_dy(box()), |
| 224 | w()-Fl::box_dw(box()), |
| 225 | h()-Fl::box_dh(box())); |
| 226 | } |
| 227 | |
| 228 | int Fl_Slider::handle(int event, int X, int Y, int W, int H) { |
| 229 | // Fl_Widget_Tracker wp(this); |
| 230 | switch (event) { |
| 231 | case FL_PUSH: { |
| 232 | Fl_Widget_Tracker wp(this); |
| 233 | if (!Fl::event_inside(X, Y, W, H)) return 0; |
| 234 | handle_push(); |
| 235 | if (wp.deleted()) return 1; } |
| 236 | // fall through ... |
| 237 | case FL_DRAG: { |
| 238 | |
| 239 | double val; |
| 240 | if (minimum() == maximum()) |
| 241 | val = 0.5; |
| 242 | else { |
| 243 | val = (value()-minimum())/(maximum()-minimum()); |
| 244 | if (val > 1.0) val = 1.0; |
| 245 | else if (val < 0.0) val = 0.0; |
| 246 | } |
| 247 | |
| 248 | int ww = (horizontal() ? W : H); |
| 249 | int mx = (horizontal() ? Fl::event_x()-X : Fl::event_y()-Y); |
| 250 | int S; |
| 251 | static int offcenter; |
| 252 | |
| 253 | if (type() == FL_HOR_FILL_SLIDER || type() == FL_VERT_FILL_SLIDER) { |
| 254 | |
| 255 | S = 0; |
| 256 | if (event == FL_PUSH) { |
| 257 | int xx = int(val*ww+.5); |
| 258 | offcenter = mx-xx; |
| 259 | if (offcenter < -10 || offcenter > 10) offcenter = 0; |
| 260 | else return 1; |
| 261 | } |
| 262 | |
| 263 | } else { |
| 264 | |
| 265 | S = int(slider_size_*ww+.5); if (S >= ww) return 0; |
| 266 | int T = (horizontal() ? H : W)/2+1; |
| 267 | if (type()==FL_VERT_NICE_SLIDER || type()==FL_HOR_NICE_SLIDER) T += 4; |
| 268 | if (S < T) S = T; |
| 269 | if (event == FL_PUSH) { |
| 270 | int xx = int(val*(ww-S)+.5); |
| 271 | offcenter = mx-xx; |
| 272 | if (offcenter < 0) offcenter = 0; |
| 273 | else if (offcenter > S) offcenter = S; |
| 274 | else return 1; |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | int xx = mx-offcenter; |
| 279 | double v; |
| 280 | char tryAgain = 1; |
| 281 | while (tryAgain) |
| 282 | { |
| 283 | tryAgain = 0; |
| 284 | if (xx < 0) { |
| 285 | xx = 0; |
| 286 | offcenter = mx; if (offcenter < 0) offcenter = 0; |
| 287 | } else if (xx > (ww-S)) { |
| 288 | xx = ww-S; |
| 289 | offcenter = mx-xx; if (offcenter > S) offcenter = S; |
| 290 | } |
| 291 | v = round(xx*(maximum()-minimum())/(ww-S) + minimum()); |
| 292 | // make sure a click outside the sliderbar moves it: |
| 293 | if (event == FL_PUSH && v == value()) { |
| 294 | offcenter = S/2; |
| 295 | event = FL_DRAG; |
| 296 | tryAgain = 1; |
| 297 | } |
| 298 | } |
| 299 | handle_drag(clamp(v)); |
| 300 | } return 1; |
| 301 | case FL_RELEASE: |
| 302 | handle_release(); |
| 303 | return 1; |
| 304 | case FL_KEYBOARD: |
| 305 | { Fl_Widget_Tracker wp(this); |
| 306 | switch (Fl::event_key()) { |
| 307 | case FL_Up: |
| 308 | if (horizontal()) return 0; |
| 309 | handle_push(); |
| 310 | if (wp.deleted()) return 1; |
| 311 | handle_drag(clamp(increment(value(),-1))); |
| 312 | if (wp.deleted()) return 1; |
| 313 | handle_release(); |
| 314 | return 1; |
| 315 | case FL_Down: |
| 316 | if (horizontal()) return 0; |
| 317 | handle_push(); |
| 318 | if (wp.deleted()) return 1; |
| 319 | handle_drag(clamp(increment(value(),1))); |
| 320 | if (wp.deleted()) return 1; |
| 321 | handle_release(); |
| 322 | return 1; |
| 323 | case FL_Left: |
| 324 | if (!horizontal()) return 0; |
| 325 | handle_push(); |
| 326 | if (wp.deleted()) return 1; |
| 327 | handle_drag(clamp(increment(value(),-1))); |
| 328 | if (wp.deleted()) return 1; |
| 329 | handle_release(); |
| 330 | return 1; |
| 331 | case FL_Right: |
| 332 | if (!horizontal()) return 0; |
| 333 | handle_push(); |
| 334 | if (wp.deleted()) return 1; |
| 335 | handle_drag(clamp(increment(value(),1))); |
| 336 | if (wp.deleted()) return 1; |
| 337 | handle_release(); |
| 338 | return 1; |
| 339 | default: |
| 340 | return 0; |
| 341 | } |
| 342 | } |
| 343 | // break not required because of switch... |
| 344 | case FL_FOCUS : |
| 345 | case FL_UNFOCUS : |
| 346 | if (Fl::visible_focus()) { |
| 347 | redraw(); |
| 348 | return 1; |
| 349 | } else return 0; |
| 350 | case FL_ENTER : |
| 351 | case FL_LEAVE : |
| 352 | return 1; |
| 353 | default: |
| 354 | return 0; |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | int Fl_Slider::handle(int event) { |
| 359 | if (event == FL_PUSH && Fl::visible_focus()) { |
| 360 | Fl::focus(this); |
| 361 | redraw(); |
| 362 | } |
| 363 | |
| 364 | return handle(event, |
| 365 | x()+Fl::box_dx(box()), |
| 366 | y()+Fl::box_dy(box()), |
| 367 | w()-Fl::box_dw(box()), |
| 368 | h()-Fl::box_dh(box())); |
| 369 | } |
| 370 | |
| 371 | /* |
| 372 | The following constructor must not be in the header file if we |
| 373 | build a shared object (DLL). Instead it is defined here to force |
| 374 | the constructor (and default destructor as well) to be defined |
| 375 | in the DLL and exported (STR #2632). |
| 376 | |
| 377 | Note: if you the ctor here, do the same changes in the specific |
| 378 | header file as well. This redundant definition was chosen to enable |
| 379 | inline constructors in the header files (for static linking) as well |
| 380 | as the one here for dynamic linking (Windows DLL). |
| 381 | */ |
| 382 | |
| 383 | #if defined(FL_DLL) |
| 384 | |
| 385 | Fl_Hor_Slider::Fl_Hor_Slider(int X,int Y,int W,int H,const char *l) |
| 386 | : Fl_Slider(X,Y,W,H,l) {type(FL_HOR_SLIDER);} |
| 387 | |
| 388 | #endif // FL_DLL |
| 389 | |
| 390 | // |
| 391 | // End of "$Id: Fl_Slider.cxx 8726 2011-05-23 18:32:47Z AlbrechtS $". |
| 392 | // |