SpinButton.vala
/*
Copyright (C) 2012, 2014 Johan Mattsson
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
*/
using Cairo;
namespace BirdFont {
public class SpinButton : Tool {
public signal void new_value_action (SpinButton selected);
bool negative = false;
public int8 n0 = 2;
public int8 n1 = 0;
public int8 n2 = 0;
public int8 n3 = 0;
public int8 n4 = 0;
bool value_from_motion = false;
double begin_y = 0;
int begin_value = 0;
int max = 99999;
int min = 0;
int step = 1;
bool big_number = false;
bool integers = false;
double last_active_time = 0;
bool waiting_for_icon_switch = false;
bool show_icon_tool_icon = false;
/** Lock the button to a fixed value. */
public bool locked = false;
public SpinButton (string? name = null, string tip = "") {
base (null , tip);
if (name != null) {
base.name = (!) name;
}
set_icon ("spin_button");
panel_press_action.connect ((selected, button, tx, ty) => {
double py = Math.fabs (y - ty);
int n = 0;
if (button == 3 || KeyBindings.modifier != NONE) {
set_from_text ();
n = 0;
set_selected (false);
return;
}
if (is_selected ()) {
if (button == 1) {
n = 1;
} else if (button == 2) {
n = 10;
}
for (int i = 0; i < n; i++) {
if (py < 9 && !locked) {
increase ();
}
if (py > 25 && !locked) {
decrease ();
}
}
}
value_from_motion = !locked;
begin_y = ty;
begin_value = get_int_value ();
if (button == 1) {
set_selected (true);
}
redraw ();
});
panel_move_action.connect ((selected, button, tx, ty) => {
double d;
int new_value;
if (is_active ()) {
show_adjustmet_icon ();
}
if (value_from_motion && is_selected ()) {
d = (begin_y - ty) / 200;
d = (d < 0) ? -Math.pow (d, 2) : Math.pow (d, 2);
d *= 1000;
new_value = (int)(begin_value + d);
if (new_value < min) {
set_int_value (@"$min");
} else if (new_value > max) {
set_int_value (@"$max");
} else {
set_int_value (@"$new_value");
}
redraw ();
}
return value_from_motion;
});
panel_release_action.connect ((selected, button, tx, ty) => {
value_from_motion = false;
if (button == 1) {
set_selected (false);
}
redraw ();
});
scroll_wheel_up_action.connect ((selected) => {
increase ();
return true;
});
scroll_wheel_down_action.connect ((selected) => {
decrease ();
return true;
});
}
public void show_icon (bool i) {
show_icon_tool_icon = i;
if (!show_icon_tool_icon) {
set_icon ("spin_button");
} else {
set_icon ((!) base.name);
}
}
public void hide_value () {
set_icon (base.name);
waiting_for_icon_switch = false;
redraw ();
}
void show_adjustmet_icon () {
TimeoutSource timer;
set_icon ("spin_button");
redraw ();
last_active_time = GLib.get_real_time ();
if (show_icon_tool_icon && !waiting_for_icon_switch) {
waiting_for_icon_switch = true;
timer = new TimeoutSource (100);
timer.set_callback (() => {
if (GLib.get_real_time () - last_active_time > 4000000) {
set_icon (base.name);
redraw ();
waiting_for_icon_switch = false;
}
return waiting_for_icon_switch;
});
timer.attach (null);
}
}
public void set_big_number (bool b) {
big_number = b;
}
public void set_integers (bool i) {
integers = i;
big_number = i;
}
public static string convert_to_string (double val) {
SpinButton sb = new SpinButton ();
sb.set_value_round (val);
return sb.get_display_value ();
}
public static double convert_to_double (string val) {
SpinButton sb = new SpinButton ();
sb.set_int_value (val);
return sb.get_value ();
}
public void set_from_text () {
TextListener listener = new TextListener (t_("Set"), get_display_value (), t_("Close"));
listener.signal_text_input.connect ((text) => {
set_value (text);
redraw ();
});
listener.signal_submit.connect (() => {
TabContent.hide_text_input ();
redraw ();
});
TabContent.show_text_input (listener);
}
public void set_max (double max) {
if (big_number) {
max /= 100;
}
this.max = (int) Math.rint (max * 10000);
}
public void set_min (double min) {
if (big_number) {
min /= 100;
}
this.min = (int) Math.rint (min * 10000);
}
public void set_int_step (double step) {
if (big_number) {
step /= 100;
}
this.step = (int) Math.rint (step * 10000);
}
public void increase () {
int v;
v = get_int_value ();
v += step;
if (v > max) {
set_int_value (@"$max");
} else {
set_int_value (@"$v");
}
new_value_action (this);
redraw ();
}
public void decrease () {
int v;
v = get_int_value ();
v -= step;
if (v <= min) {
set_int_value (@"$min");
} else {
set_int_value (@"$v");
}
new_value_action (this);
redraw ();
}
public void set_int_value (string new_value) {
string v = new_value;
negative = v.has_prefix ("-");
if (negative) {
v = v.replace ("-", "");
}
while (!(v.char_count () >= 5)) {
v = "0" + v;
}
n0 = parse (v.substring (v.index_of_nth_char (0), 1));
n1 = parse (v.substring (v.index_of_nth_char (1), 1));
n2 = parse (v.substring (v.index_of_nth_char (2), 1));
n3 = parse (v.substring (v.index_of_nth_char (3), 1));
n4 = parse (v.substring (v.index_of_nth_char (4), 1));
show_adjustmet_icon ();
new_value_action (this);
}
int8 parse (string s) {
int v = int.parse (s);
if (v < 0) {
warning ("Failed to parse integer.");
return 0;
}
return (int8) v;
}
public void set_value (string new_value, bool check_boundaries = true, bool emit_signal = true) {
string v = new_value.replace (",", ".");
int fv;
string separator = "";
negative = v.has_prefix ("-");
if (negative) {
v = v.replace ("-", "");
}
if (big_number) {
if (v == "" || v == "0") {
v = "0.0000";
}
while (v.has_prefix ("0") && !v.has_prefix ("0.")) {
v = v.substring (v.index_of_nth_char (1));
}
fv = int.parse (v);
fv = (fv < 0) ? -fv : fv;
if (fv < 10) {
v = @"00$v";
} else if (fv < 100) {
v = @"0$v";
}
v = @"$v";
}
while (v.char_count () < 6) {
if (v.index_of (".") == -1) {
v += ".";
} else {
v += "0";
}
}
if (!big_number) {
n0 = (int8) int.parse (v.substring (v.index_of_nth_char (0), 1));
separator = v.substring (v.index_of_nth_char (1), 1);
n1 = (int8) int.parse (v.substring (v.index_of_nth_char (2), 1));
n2 = (int8) int.parse (v.substring (v.index_of_nth_char (3), 1));
n3 = (int8) int.parse (v.substring (v.index_of_nth_char (4), 1));
n4 = (int8) int.parse (v.substring (v.index_of_nth_char (5), 1));
} else {
n0 = (int8) int.parse (v.substring (v.index_of_nth_char (0), 1));
n1 = (int8) int.parse (v.substring (v.index_of_nth_char (1), 1));
n2 = (int8) int.parse (v.substring (v.index_of_nth_char (2), 1));
separator = v.substring (v.index_of_nth_char (3), 1);
n3 = (int8) int.parse (v.substring (v.index_of_nth_char (4), 1));
n4 = (int8) int.parse (v.substring (v.index_of_nth_char (5), 1));
if (integers) {
n3 = 0;
n4 = 0;
}
}
if (separator != ".") {
warning (@"Expecting \".\" $new_value -> ($(v))");
}
if (check_boundaries && get_int_value () > max) {
warning (@"Out of bounds ($new_value > $max).");
set_value_round (max, false);
}
if (check_boundaries && get_int_value () < min) {
warning (@"Out of bounds ($new_value < $min).");
set_value_round (min, false);
}
if (emit_signal) {
new_value_action (this);
}
show_adjustmet_icon ();
}
public void set_value_round (double v, bool check_boundaries = true, bool emit_signal = true) {
if (v == -0) {
v = 0;
}
set_value (@"$v".replace (",", "."), check_boundaries, emit_signal);
}
public double get_value () {
double r;
if (!big_number) {
r = n0 + (n1 / 10.0) + (n2 / 100.0) + (n3 / 1000.0) + (n4 / 1000.0);
} else {
r = (n0 * 100) + (n1 * 10) + n2 + (n3 / 10.0) + (n4 / 100.0);
}
return (negative) ? -r : r;
}
private int get_int_value () {
int r = n0 * 10000 + n1 * 1000 + n2 * 100 + n3 * 10 + n4;
return (negative) ? -r : r;
}
public string get_short_display_value () {
if (integers) {
if (n0 == 0 && n1 == 0) {
return @"$n2";
}
if (n0 == 0) {
return @"$n1$n2";
}
return @"$n0$n1$n2";
}
if (!big_number) {
return @"$n0.$n1$n2$n3";
}
if (negative) {
if (n0 == 0 && n1 == 0) {
return @"-$n2.$n3$n4";
}
if (n0 == 0) {
return @"-$n1$n2.$n3";
}
return @"-$n0$n1$n2";
}
if (n0 == 0 && n1 == 0) {
return @"$n2.$n3$n4";
}
if (n0 == 0) {
return @"$n1$n2.$n3$n4";
}
return @"$n0$n1$n2.$n3";
}
public string get_display_value () {
string v;
if (integers) {
return get_short_display_value ();
}
if (!big_number) {
return @"$n0.$n1$n2$n3$n4";
}
v = (negative) ? "-" : "";
if (n0 == 0 && n1 == 0) {
v = @"$v$n2.$n3$n4";
} else if (n0 == 0) {
v = @"$v$n1$n2.$n3$n4";
} else {
v = @"$v$n0$n1$n2.$n3$n4";
}
return v;
}
public override void draw_tool (Context cr, double px, double py) {
double scale = Toolbox.get_scale ();
double text_height = 14 * scale;
string display_value = get_short_display_value ();
Text text = new Text (display_value, text_height);
double x = x - px;
double y = y - py;
double text_x = x + (w - text.get_sidebearing_extent ()) / 2 + 1;
double text_y = y + (h - text_height) / 2;
if (!show_icon_tool_icon || waiting_for_icon_switch) {
if (is_selected ()) {
base.icon_color = "Active Spin Button";
} else {
base.icon_color = "Spin Button";
}
} else {
if (is_selected ()) {
base.icon_color = "Selected Tool Foreground";
} else {
base.icon_color = "Tool Foreground";
}
}
base.draw_tool (cr, px, py);
if (!show_icon_tool_icon || waiting_for_icon_switch) {
if (is_selected ()) {
Theme.text_color (text, "Selected Tool Foreground");
} else {
Theme.text_color (text, "Tool Foreground");
}
text.widget_x = text_x;
text.widget_y = text_y + text.get_decender ();
text.draw (cr);
}
}
}
}