1 #include "spin_button_angle.hpp"
2 #include "util/util.hpp"
3 #include "util/gtk_util.hpp"
4 #include <iomanip>
5
6 namespace horizon {
7
SpinButtonAngle()8 SpinButtonAngle::SpinButtonAngle() : Gtk::SpinButton()
9 {
10 set_wrap(false);
11 set_width_chars(6);
12 double step = (1.0 / 360.0) * 65536;
13 double page = (15.0 / 360.0) * 65536;
14 set_increments(step, page);
15
16 // The value normally is only within 0..65536, but the range has to be larger to simulate the wrapping by
17 // temporarily allowing smaller/larger values and then readjusting them.
18 set_range(-page, 65536 + page);
19
20 entry_set_tnum(*this);
21 }
22
on_output()23 bool SpinButtonAngle::on_output()
24 {
25 auto adj = get_adjustment();
26 double v = adj->get_value();
27
28 // When changing the value by scroll wheel, on_output() is called for each step, but on_input() isn't until later.
29 // Without this check, negative angles could be displayed or the range limits could be hit while scrolling.
30 if (v < 0) {
31 v += 65536.0;
32 set_value(v);
33 }
34 else if (v >= 65536.0) {
35 v -= 65536.0;
36 set_value(v);
37 }
38
39 std::stringstream stream;
40 stream.imbue(get_locale());
41 stream << std::fixed << std::setprecision(2) << (v / 65536.0) * 360 << "°";
42
43 set_text(stream.str());
44 return true;
45 }
46
on_input(double * v)47 int SpinButtonAngle::on_input(double *v)
48 {
49 auto txt = get_text().raw();
50 std::replace(txt.begin(), txt.end(), ',', '.');
51 int64_t va = 0;
52 try {
53 double d = std::fmod(std::stod(txt), 360.0);
54 if (d < 0) {
55 d += 360.0;
56 }
57 va = std::round((d / 360.0) * 65536);
58 *v = va;
59 }
60 catch (const std::exception &e) {
61 return false;
62 }
63 return true;
64 }
65
on_wrapped()66 void SpinButtonAngle::on_wrapped()
67 {
68 auto adj = get_adjustment();
69 if (adj->get_value() == adj->get_upper()) {
70 // Wrap 0 -> maximum
71 adj->set_value(std::floor(65535 / adj->get_step_increment()) * adj->get_step_increment());
72 }
73 }
74 } // namespace horizon
75