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