/*******************************************************************************/ /* Copyright (C) 2013 Jonathan Moore Liles */ /* */ /* This program is free software; you can redistribute it and/or modify it */ /* under the terms of the GNU General Public License as published by the */ /* Free Software Foundation; either version 2 of the License, or (at your */ /* option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ /* more details. */ /* */ /* You should have received a copy of the GNU General Public License along */ /* with This program; see the file COPYING. If not,write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*******************************************************************************/ #include "Fl_SliderX.H" #include #include #include void Fl_SliderX::draw ( int X, int Y, int W, int H) { slider_size( horizontal() ? H / (float)W : W / (float)H ); int act = active_r(); if (damage()&FL_DAMAGE_ALL) draw_box(); int ww = (horizontal() ? W : H); int hh = (horizontal() ? H : W); int xx, S; xx = slider_position( value(), ww ); S = (horizontal() ? H : W ); int xsl, ysl, wsl, hsl; if (horizontal()) { xsl = X+xx; wsl = S; ysl = Y + hh/2; hsl = hh/4; } else { ysl = Y+xx; hsl = S; xsl = X + hh/2; wsl = hh/4; } { fl_push_clip(X, Y, W, H); draw_box(); fl_pop_clip(); } //draw_bg(X, Y, W, H); fl_line_style( FL_SOLID, hh/6 ); Fl_Color c = fl_darker(color()); if ( !act ) c = fl_inactive(c); fl_color(c); if ( horizontal() ) fl_line ( X + S/2, Y + hh/2, X + W - S/2, Y + hh/2 ); else fl_line ( X + hh/2, Y + S/2, X + hh/2, Y + H - S/2 ); c = selection_color(); if ( !act ) c = fl_inactive(c); fl_color( c ); if ( horizontal() ) fl_line ( X + S/2, ysl, xsl + S/2, ysl ); else fl_line ( X + S/2, Y + H - S/2, xsl, ysl + (S/2) ); fl_line_style( FL_SOLID, 0 ); if ( act ) { fl_push_matrix(); if ( horizontal() ) fl_translate( xsl + (hh/2), ysl); else fl_translate( xsl, ysl + (hh/2) ); fl_color( fl_color_add_alpha( FL_WHITE, 127 )); fl_begin_polygon(); fl_circle(0.0,0.0, hh/3); fl_end_polygon(); fl_color( FL_WHITE ); fl_begin_polygon(); fl_circle(0.0,0.0, hh/6); fl_end_polygon(); fl_pop_matrix(); } draw_label(xsl, ysl, wsl, hsl); if (Fl::focus() == this) { draw_focus(); } /* draw(x()+Fl::box_dx(box()), */ /* y()+Fl::box_dy(box()), */ /* w()-Fl::box_dw(box()), */ /* h()-Fl::box_dh(box())); */ } /** return a value between 0.0 and 1.0 which represents the current slider position. */ int Fl_SliderX::slider_position ( double value, int w ) { double A = minimum(); double B = maximum(); if (B == A) return 0; bool flip = B < A; if (flip) {A = B; B = minimum();} // if (!horizontal()) flip = !flip; // if both are negative, make the range positive: if (B <= 0) {flip = !flip; double t = A; A = -B; B = -t; value = -value;} double fraction; if (!log()) { // linear slider fraction = (value-A)/(B-A); } else if (A > 0) { // logatithmic slider if (value <= A) fraction = 0; else fraction = (::log(value)-::log(A))/(::log(B)-::log(A)); } else if (A == 0) { // squared slider if (value <= 0) fraction = 0; else fraction = sqrt(value/B); } else { // squared signed slider if (value < 0) fraction = (1-sqrt(value/A))*.5; else fraction = (1+sqrt(value/B))*.5; } if (flip) fraction = 1-fraction; w -= int(slider_size()*w+.5); if (w <= 0) return 0; if (fraction >= 1) return w; else if (fraction <= 0) return 0; else return int(fraction*w+.5); } double Fl_SliderX::slider_value ( int X, int w ) { w -= int(slider_size()*w+.5); if (w <= 0) return minimum(); double A = minimum(); double B = maximum(); bool flip = B < A; if (flip) {A = B; B = minimum();} // if (!horizontal()) flip = !flip; if (flip) X = w-X; double fraction = double(X)/w; if (fraction <= 0) return A; if (fraction >= 1) return B; // if both are negative, make the range positive: flip = (B <= 0); if (flip) {double t = A; A = -B; B = -t; fraction = 1-fraction;} double value; double derivative; if (!log()) { // linear slider value = fraction*(B-A)+A; derivative = (B-A)/w; } else if (A > 0) { // log slider double d = (::log(B)-::log(A)); value = exp(fraction*d+::log(A)); derivative = value*d/w; } else if (A == 0) { // squared slider value = fraction*fraction*B; derivative = 2*fraction*B/w; } else { // squared signed slider fraction = 2*fraction - 1; if (fraction < 0) B = A; value = fraction*fraction*B; derivative = 4*fraction*B/w; } // find nicest multiple of 10,5, or 2 of step() that is close to 1 pixel: if (step() && derivative > step()) { double w = log10(derivative); double l = ceil(w); int num = 1; int i; for (i = 0; i < l; i++) num *= 10; int denom = 1; for (i = -1; i >= l; i--) denom *= 10; if (l-w > 0.69897) denom *= 5; else if (l-w > 0.30103) denom *= 2; value = floor(value*denom/num+.5)*num/denom; } if (flip) return -value; return value; } int Fl_SliderX::handle(int event, int X, int Y, int W, int H) { // Fl_Widget_Tracker wp(this); switch (event) { case FL_PUSH: { Fl_Widget_Tracker wp(this); if (!Fl::event_inside(X, Y, W, H)) return 0; handle_push(); if (wp.deleted()) return 1; } // fall through ... case FL_DRAG: { static int offcenter; int ww = (horizontal() ? W : H); if ( event == FL_PUSH ) { int x = slider_position( value(), ww ); offcenter = (horizontal() ? (Fl::event_x()-X) - x : (Fl::event_y()-Y) - x ); } try_again: int mx = (horizontal() ? Fl::event_x()-X : Fl::event_y()-Y) - offcenter; double v = slider_value( mx, ww ); if (event == FL_PUSH ) // && v == value()) { { int os = int(slider_size()*ww+0.5)/2; if ( abs( offcenter ) > os ) { offcenter = os; event = FL_DRAG; goto try_again; } } handle_drag(clamp(v)); } return 1; case FL_RELEASE: handle_release(); return 1; case FL_KEYBOARD: { Fl_Widget_Tracker wp(this); switch (Fl::event_key()) { case FL_Up: if (horizontal()) return 0; handle_push(); if (wp.deleted()) return 1; handle_drag(clamp(increment(value(),-1))); if (wp.deleted()) return 1; handle_release(); return 1; case FL_Down: if (horizontal()) return 0; handle_push(); if (wp.deleted()) return 1; handle_drag(clamp(increment(value(),1))); if (wp.deleted()) return 1; handle_release(); return 1; case FL_Left: if (!horizontal()) return 0; handle_push(); if (wp.deleted()) return 1; handle_drag(clamp(increment(value(),-1))); if (wp.deleted()) return 1; handle_release(); return 1; case FL_Right: if (!horizontal()) return 0; handle_push(); if (wp.deleted()) return 1; handle_drag(clamp(increment(value(),1))); if (wp.deleted()) return 1; handle_release(); return 1; default: return 0; } } // break not required because of switch... case FL_FOCUS : case FL_UNFOCUS : if (Fl::visible_focus()) { redraw(); return 1; } else return 0; case FL_ENTER : case FL_LEAVE : return 1; case FL_MOUSEWHEEL : { if ( this != Fl::belowmouse() ) return 0; if (Fl::e_dy==0) return 0; const int steps = Fl::event_ctrl() ? 128 : 16; const float step = fabs( maximum() - minimum() ) / (float)steps; int dy = Fl::e_dy; /* slider is in 'upside down' configuration, invert meaning of mousewheel */ if ( minimum() > maximum() ) dy = 0 - dy; handle_drag(clamp(value() + step * dy)); return 1; } default: return 0; } } int Fl_SliderX::handle(int event) { if (event == FL_PUSH && Fl::visible_focus()) { Fl::focus(this); redraw(); } return handle(event, x()+Fl::box_dx(box()), y()+Fl::box_dy(box()), w()-Fl::box_dw(box()), h()-Fl::box_dh(box())); }