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