non/FL/Fl_SliderX.C

342 lines
8.9 KiB
C

/*******************************************************************************/
/* 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 <FL/Fl.H>
#include <FL/fl_draw.H>
#include <math.h>
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()));
}