.
1 /*
2 Copyright (C) 2012, 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16
17 namespace BirdFont {
18
19 public class SpinButton : Tool {
20
21 public signal void new_value_action (SpinButton selected);
22
23 bool negative = false;
24
25 public int8 n0 = 2;
26 public int8 n1 = 0;
27 public int8 n2 = 0;
28 public int8 n3 = 0;
29 public int8 n4 = 0;
30
31 bool value_from_motion = false;
32 double begin_y = 0;
33 int begin_value = 0;
34
35 int max = 99999;
36 int min = 0;
37 int step = 1;
38
39 bool big_number = false;
40
41 double last_active_time = 0;
42 bool waiting_for_icon_switch = false;
43 bool show_icon_tool_icon = false;
44
45 public SpinButton (string? name = null, string tip = "") {
46 base (null , tip);
47
48 if (name != null) {
49 base.name = (!) name;
50 }
51
52 set_icon ("spin_button");
53
54 panel_press_action.connect ((selected, button, tx, ty) => {
55 double py = Math.fabs (y - ty);
56 int n = 0;
57
58 if (button == 3 || KeyBindings.modifier != NONE) {
59 set_from_text ();
60 n = 0;
61 set_selected (false);
62 return;
63 }
64
65 if (is_selected ()) {
66 if (button == 1) {
67 n = 1;
68 } else if (button == 2) {
69 n = 10;
70 }
71
72 for (int i = 0; i < n; i++) {
73 if (py < 9) increase ();
74 if (py > 25) decrease ();
75 }
76 }
77
78 value_from_motion = true;
79 begin_y = ty;
80
81 begin_value = get_int_value ();
82
83 if (button == 1) {
84 set_selected (true);
85 }
86
87 redraw ();
88 });
89
90 panel_move_action.connect ((selected, button, tx, ty) => {
91 double d;
92 int new_value;
93
94 if (is_active ()) {
95 show_adjustmet_icon ();
96 }
97
98 if (value_from_motion && is_selected ()) {
99 d = (begin_y - ty) / 200;
100 d = (d < 0) ? -Math.pow (d, 2) : Math.pow (d, 2);
101 d *= 1000;
102
103 new_value = (int)(begin_value + d);
104
105 if (new_value < min) {
106 set_int_value (@"$min");
107 } else if (new_value > max) {
108 set_int_value (@"$max");
109 } else {
110 set_int_value (@"$new_value");
111 }
112
113 redraw ();
114 }
115
116 return value_from_motion;
117 });
118
119 panel_release_action.connect ((selected, button, tx, ty) => {
120 value_from_motion = false;
121
122 if (button == 1) {
123 set_selected (false);
124 }
125
126 redraw ();
127 });
128
129 scroll_wheel_up_action.connect ((selected) => {
130 increase ();
131 return true;
132 });
133
134 scroll_wheel_down_action.connect ((selected) => {
135 decrease ();
136 return true;
137 });
138 }
139
140 public void show_icon (bool i) {
141 show_icon_tool_icon = i;
142
143 if (!show_icon_tool_icon) {
144 set_icon ("spin_button");
145 } else {
146 set_icon ((!) base.name);
147 }
148 }
149
150 public void hide_value () {
151 set_icon (base.name);
152 waiting_for_icon_switch = false;
153 redraw ();
154 }
155
156 void show_adjustmet_icon () {
157 TimeoutSource timer;
158
159 set_icon ("spin_button");
160 redraw ();
161
162 last_active_time = GLib.get_real_time ();
163
164 if (show_icon_tool_icon && !waiting_for_icon_switch) {
165 waiting_for_icon_switch = true;
166
167 timer = new TimeoutSource (100);
168 timer.set_callback (() => {
169 if (GLib.get_real_time () - last_active_time > 4000000) {
170 set_icon (base.name);
171 redraw ();
172 waiting_for_icon_switch = false;
173 }
174
175 return waiting_for_icon_switch;
176 });
177
178 timer.attach (null);
179 }
180 }
181
182 public void set_big_number (bool b) {
183 big_number = b;
184 }
185
186 public static string convert_to_string (double val) {
187 SpinButton sb = new SpinButton ();
188 sb.set_value_round (val);
189 return sb.get_display_value ();
190 }
191
192 public static double convert_to_double (string val) {
193 SpinButton sb = new SpinButton ();
194 sb.set_int_value (val);
195 return sb.get_value ();
196 }
197
198 public void set_from_text () {
199 TextListener listener = new TextListener (t_("Set"), get_display_value (), t_("Close"));
200
201 listener.signal_text_input.connect ((text) => {
202 set_value (text);
203 redraw ();
204 });
205
206 listener.signal_submit.connect (() => {
207 TabContent.hide_text_input ();
208 redraw ();
209 });
210
211 TabContent.show_text_input (listener);
212 }
213
214 public void set_max (double max) {
215 if (big_number) {
216 max /= 100;
217 }
218 this.max = (int) Math.rint (max * 10000);
219 }
220
221 public void set_min (double min) {
222 if (big_number) {
223 min /= 100;
224 }
225 this.min = (int) Math.rint (min * 10000);
226 }
227
228 public void set_int_step (double step) {
229 if (big_number) {
230 step /= 100;
231 }
232 this.step = (int) Math.rint (step * 10000);
233 }
234
235 public void increase () {
236 int v;
237
238 v = get_int_value ();
239 v += step;
240
241 if (v > max) {
242 set_int_value (@"$max");
243 } else {
244 set_int_value (@"$v");
245 }
246
247 new_value_action (this);
248 redraw ();
249 }
250
251 public void decrease () {
252 int v;
253
254 v = get_int_value ();
255 v -= step;
256
257 if (v <= min) {
258 set_int_value (@"$min");
259 } else {
260 set_int_value (@"$v");
261 }
262
263 new_value_action (this);
264 redraw ();
265 }
266
267 public void set_int_value (string new_value) {
268 string v = new_value;
269
270 negative = v.has_prefix ("-");
271 if (negative) {
272 v = v.replace ("-", "");
273 }
274
275 while (!(v.char_count () >= 5)) {
276 v = "0" + v;
277 }
278
279 n0 = parse (v.substring (v.index_of_nth_char (0), 1));
280 n1 = parse (v.substring (v.index_of_nth_char (1), 1));
281 n2 = parse (v.substring (v.index_of_nth_char (2), 1));
282 n3 = parse (v.substring (v.index_of_nth_char (3), 1));
283 n4 = parse (v.substring (v.index_of_nth_char (4), 1));
284
285 show_adjustmet_icon ();
286 new_value_action (this);
287 redraw ();
288 }
289
290 int8 parse (string s) {
291 int v = int.parse (s);
292 if (v < 0) {
293 warning ("Failed to parse integer.");
294 return 0;
295 }
296 return (int8) v;
297 }
298
299 public void set_value (string new_value, bool check_boundaries = true, bool emit_signal = true) {
300 string v = new_value.replace (",", ".");
301 int fv;
302 string separator = "";
303
304 negative = v.has_prefix ("-");
305 if (negative) {
306 v = v.replace ("-", "");
307 }
308
309 if (big_number) {
310 if (v == "" || v == "0") {
311 v = "0.0000";
312 }
313
314 while (v.has_prefix ("0") && !v.has_prefix ("0.")) {
315 v = v.substring (v.index_of_nth_char (1));
316 }
317
318 fv = int.parse (v);
319 fv = (fv < 0) ? -fv : fv;
320 if (fv < 10) {
321 v = @"00$v";
322 } else if (fv < 100) {
323 v = @"0$v";
324 }
325 }
326
327 while (v.char_count () < 6) {
328 if (v.index_of (".") == -1) {
329 v += ".";
330 } else {
331 v += "0";
332 }
333 }
334
335 if (!big_number) {
336 n0 = (int8) int.parse (v.substring (v.index_of_nth_char (0), 1));
337 separator = v.substring (v.index_of_nth_char (1), 1);
338 n1 = (int8) int.parse (v.substring (v.index_of_nth_char (2), 1));
339 n2 = (int8) int.parse (v.substring (v.index_of_nth_char (3), 1));
340 n3 = (int8) int.parse (v.substring (v.index_of_nth_char (4), 1));
341 n4 = (int8) int.parse (v.substring (v.index_of_nth_char (5), 1));
342 } else {
343 n0 = (int8) int.parse (v.substring (v.index_of_nth_char (0), 1));
344 n1 = (int8) int.parse (v.substring (v.index_of_nth_char (1), 1));
345 n2 = (int8) int.parse (v.substring (v.index_of_nth_char (2), 1));
346 separator = v.substring (v.index_of_nth_char (3), 1);
347 n3 = (int8) int.parse (v.substring (v.index_of_nth_char (4), 1));
348 n4 = (int8) int.parse (v.substring (v.index_of_nth_char (5), 1));
349 }
350
351 if (separator != ".") {
352 warning (@"Expecting \".\" $new_value -> ($(v))");
353 }
354
355 if (check_boundaries && get_int_value () > max) {
356 warning (@"Out of bounds ($new_value > $max).");
357 set_value_round (max, false);
358 }
359
360 if (check_boundaries && get_int_value () < min) {
361 warning (@"Out of bounds ($new_value < $min).");
362 set_value_round (min, false);
363 }
364
365 if (emit_signal) {
366 new_value_action (this);
367 }
368
369 show_adjustmet_icon ();
370 redraw ();
371 }
372
373 public void set_value_round (double v, bool check_boundaries = true, bool emit_signal = true) {
374 if (v == -0) {
375 v = 0;
376 }
377
378 set_value (@"$v".replace (",", "."), check_boundaries, emit_signal);
379 }
380
381 public double get_value () {
382 double r;
383
384 if (!big_number) {
385 r = n0 + (n1 / 10.0) + (n2 / 100.0) + (n3 / 1000.0) + (n4 / 1000.0);
386 } else {
387 r = (n0 * 100) + (n1 * 10) + n2 + (n3 / 10.0) + (n4 / 100.0);
388 }
389
390 return (negative) ? -r : r;
391 }
392
393 private int get_int_value () {
394 int r = n0 * 10000 + n1 * 1000 + n2 * 100 + n3 * 10 + n4;
395 return (negative) ? -r : r;
396 }
397
398 public string get_short_display_value () {
399
400 if (!big_number) {
401 return @"$n0.$n1$n2$n3";
402 }
403
404 if (negative) {
405 if (n0 == 0 && n1 == 0) {
406 return @" -$n2.$n3$n4";
407 }
408
409 if (n0 == 0) {
410 return @" -$n1$n2.$n3";
411 }
412
413 return @" -$n0$n1$n2";
414 }
415
416 if (n0 == 0 && n1 == 0) {
417 return @" $n2.$n3$n4";
418 }
419
420 if (n0 == 0) {
421 return @"$n1$n2.$n3$n4";
422 }
423
424 return @"$n0$n1$n2.$n3";
425 }
426
427 public string get_display_value () {
428 if (!big_number) {
429 return @"$n0.$n1$n2$n3$n4";
430 }
431
432 if (negative) {
433 return @"-$n0$n1$n2.$n3$n4";
434 }
435
436 return @"$n0$n1$n2.$n3$n4";
437 }
438
439 public override void draw (Context cr) {
440 double scale = Toolbox.get_scale ();
441
442 double xt = x + w / 2;
443 double yt = y;
444
445 double text_x = -14 * scale;
446 double text_y = 7 * scale;
447 Text text = new Text (get_short_display_value (), 15);
448
449 base.draw (cr);
450
451 if (!show_icon_tool_icon || waiting_for_icon_switch) {
452 if (is_selected ()) {
453 Theme.text_color (text, "Selected Tool Foreground");
454 } else {
455 Theme.text_color (text, "Tool Foreground");
456 }
457
458 text.widget_x = xt + text_x;
459 text.widget_y = yt + text_y;
460 text.draw (cr);
461 }
462 }
463
464 public void redraw () {
465 if (!is_null (MainWindow.get_toolbox ())) {
466 MainWindow.get_toolbox ().redraw ((int) x, (int) y, 70, 70);
467 }
468 }
469 }
470
471 }
472