/[svn]/gigedit/trunk/src/gigedit/paramedit.h
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/paramedit.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3364 - (show annotations) (download) (as text)
Tue Nov 14 18:07:25 2017 UTC (6 years, 5 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 13731 byte(s)
* Added experimental support for upcoming GTK(MM)4
  (for now up to GTKMM 3.91.2 while still preserving backward compatibility
  down to GTKMM 2).
* Re-merged r2845 to compile now with and without Gtk "Stock ID" API
  (see also r3158).

1 /* -*- c++ -*-
2 * Copyright (C) 2006-2017 Andreas Persson
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2, or (at
7 * your option) any later version.
8 *
9 * This program 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 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with program; see the file COPYING. If not, write to the Free
16 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #ifndef GIGEDIT_PARAMEDIT_H
21 #define GIGEDIT_PARAMEDIT_H
22
23 #ifdef LIBGIG_HEADER_FILE
24 # include LIBGIG_HEADER_FILE(gig.h)
25 #else
26 # include <gig.h>
27 #endif
28
29 #include <cmath>
30
31 #include "compat.h"
32
33 #include <glibmm/convert.h>
34 #include <gtkmm/box.h>
35 #include <gtkmm/adjustment.h>
36 #if HAS_GTKMM_ALIGNMENT
37 # include <gtkmm/alignment.h>
38 #endif
39 #include <gtkmm/checkbutton.h>
40 #include <gtkmm/comboboxtext.h>
41 #include <gtkmm/frame.h>
42 #include <gtkmm/label.h>
43 #include <gtkmm/scale.h>
44 #include <gtkmm/spinbutton.h>
45 #if USE_GTKMM_GRID
46 # include <gtkmm/grid.h>
47 #else
48 # include <gtkmm/table.h>
49 #endif
50 #include <gtkmm/textview.h>
51
52 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 12) || GTKMM_MAJOR_VERSION < 2
53 #define OLD_TOOLTIPS
54 #include <gtkmm/tooltips.h>
55 #endif
56
57 int note_value(const Glib::ustring& note);
58 Glib::ustring note_str(int note);
59
60 void spin_button_show_notes(Gtk::SpinButton& spin_button);
61
62 class LabelWidget {
63 public:
64 Gtk::Label label;
65 Gtk::Widget& widget;
66
67 LabelWidget(const char* labelText, Gtk::Widget& widget);
68 void set_sensitive(bool sensitive = true);
69 sigc::signal<void>& signal_value_changed() {
70 return sig_changed;
71 }
72 protected:
73 #ifdef OLD_TOOLTIPS
74 Gtk::Tooltips tooltips;
75 #endif
76 sigc::signal<void> sig_changed;
77 };
78
79 class ReadOnlyLabelWidget : public LabelWidget {
80 public:
81 Gtk::Label text;
82
83 ReadOnlyLabelWidget(const char* leftHandText);
84 ReadOnlyLabelWidget(const char* leftHandText, const char* rightHandText);
85 };
86
87 class NumEntry : public LabelWidget {
88 protected:
89 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
90 Gtk::Adjustment adjust;
91 #else
92 Glib::RefPtr<Gtk::Adjustment> adjust;
93 #endif
94 HScale scale;
95 Gtk::SpinButton spinbutton;
96 HBox box;
97
98 static int round_to_int(double x) {
99 return int(x < 0.0 ? x - 0.5 : x + 0.5);
100 }
101 public:
102 NumEntry(const char* labelText, double lower = 0, double upper = 127,
103 int decimals = 0);
104 void set_tip(const Glib::ustring& tip_text) {
105 #ifdef OLD_TOOLTIPS
106 tooltips.set_tip(spinbutton, tip_text);
107 #else
108 spinbutton.set_tooltip_text(tip_text);
109 #endif
110 }
111 void set_upper(double upper) {
112 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
113 adjust.set_upper(upper);
114 #else
115 adjust->set_upper(upper);
116 #endif
117 }
118 };
119
120 class NumEntryGain : public NumEntry {
121 private:
122 int32_t value;
123 void value_changed();
124 double coeff;
125 bool connected;
126 public:
127 NumEntryGain(const char* labelText,
128 double lower, double upper, int decimals, double coeff);
129 int32_t get_value() const { return value; }
130 void set_value(int32_t value);
131 };
132
133 template<typename T>
134 class NumEntryTemp : public NumEntry {
135 private:
136 T value;
137 void value_changed();
138 public:
139 NumEntryTemp(const char* labelText,
140 double lower = 0, double upper = 127, int decimals = 0);
141 T get_value() const { return value; }
142 void set_value(T value);
143 };
144
145 template<typename T>
146 NumEntryTemp<T>::NumEntryTemp(const char* labelText,
147 double lower, double upper, int decimals) :
148 NumEntry(labelText, lower, upper, decimals),
149 value(0)
150 {
151 spinbutton.signal_value_changed().connect(
152 sigc::mem_fun(*this, &NumEntryTemp::value_changed));
153 }
154
155 template<typename T>
156 void NumEntryTemp<T>::value_changed()
157 {
158 const double f = pow(10, spinbutton.get_digits());
159 int new_value = round_to_int(spinbutton.get_value() * f);
160 if (new_value != round_to_int(value * f)) {
161 value = T(new_value / f);
162 sig_changed();
163 }
164 }
165
166 template<typename T>
167 void NumEntryTemp<T>::set_value(T value)
168 {
169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
170 if (value > adjust.get_upper()) value = T(adjust.get_upper());
171 #else
172 if (value > adjust->get_upper()) value = T(adjust->get_upper());
173 #endif
174 if (this->value != value) {
175 this->value = value;
176 const double f = pow(10, spinbutton.get_digits());
177 if (round_to_int(spinbutton.get_value() * f) != round_to_int(value * f)) {
178 spinbutton.set_value(value);
179 }
180 sig_changed();
181 }
182 }
183
184
185 class NoteEntry : public NumEntryTemp<uint8_t> {
186 public:
187 NoteEntry(const char* labelText);
188 private:
189 int on_input(double* new_value);
190 bool on_output();
191 };
192
193
194 class NumEntryPermille : public NumEntry {
195 private:
196 uint16_t value;
197 void value_changed();
198 public:
199 NumEntryPermille(const char* labelText,
200 double lower = 0, double upper = 127, int decimals = 0);
201 uint16_t get_value() const { return value; }
202 void set_value(uint16_t value);
203 };
204
205
206 template<typename T>
207 class ChoiceEntry : public LabelWidget {
208 private:
209 Gtk::ComboBoxText combobox;
210 #if HAS_GTKMM_ALIGNMENT
211 Gtk::Alignment align;
212 #endif
213 const T* values;
214 public:
215 ChoiceEntry(const char* labelText);
216 T get_value() const;
217 void set_value(T value);
218 void set_choices(const char** texts, const T* values);
219
220 void set_tip(const Glib::ustring& tip_text) {
221 #ifdef OLD_TOOLTIPS
222 tooltips.set_tip(combobox, tip_text);
223 #else
224 combobox.set_tooltip_text(tip_text);
225 #endif
226 }
227 };
228
229 template<typename T>
230 ChoiceEntry<T>::ChoiceEntry(const char* labelText) :
231 #if HAS_GTKMM_ALIGNMENT
232 LabelWidget(labelText, align),
233 align(0, 0, 0, 0),
234 #else
235 LabelWidget(labelText, combobox),
236 #endif
237 values(0)
238 {
239 combobox.signal_changed().connect(sig_changed.make_slot());
240 #if HAS_GTKMM_ALIGNMENT
241 align.add(combobox);
242 #endif
243 }
244
245 template<typename T>
246 void ChoiceEntry<T>::set_choices(const char** texts, const T* values)
247 {
248 for (int i = 0 ; texts[i] ; i++) {
249 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
250 combobox.append_text(texts[i]);
251 #else
252 combobox.append(texts[i]);
253 #endif
254 }
255 this->values = values;
256 }
257
258 template<typename T>
259 T ChoiceEntry<T>::get_value() const
260 {
261 int rowno = combobox.get_active_row_number();
262 return values[rowno];
263 }
264
265 template<typename T>
266 void ChoiceEntry<T>::set_value(T value)
267 {
268 int row = 0;
269 int nb_rows = combobox.get_model()->children().size();
270 for (; row < nb_rows ; row++) {
271 if (value == values[row]) break;
272 }
273 combobox.set_active(row == nb_rows ? -1 : row);
274 }
275
276
277 class ChoiceEntryLeverageCtrl : public LabelWidget {
278 private:
279 gig::leverage_ctrl_t value;
280 Gtk::ComboBoxText combobox;
281 #if HAS_GTKMM_ALIGNMENT
282 Gtk::Alignment align;
283 #endif
284 void value_changed();
285 public:
286 ChoiceEntryLeverageCtrl(const char* labelText);
287 gig::leverage_ctrl_t get_value() const { return value; }
288 void set_value(gig::leverage_ctrl_t value);
289 void set_tip(const Glib::ustring& tip_text) {
290 combobox.set_tooltip_text(tip_text);
291 }
292 };
293
294
295 class BoolEntry : public LabelWidget {
296 private:
297 Gtk::CheckButton checkbutton;
298 public:
299 BoolEntry(const char* labelText);
300 bool get_value() const { return checkbutton.get_active(); }
301 void set_value(bool value) { checkbutton.set_active(value); }
302
303 void set_tip(const Glib::ustring& tip_text) {
304 #ifdef OLD_TOOLTIPS
305 tooltips.set_tip(checkbutton, tip_text);
306 #else
307 checkbutton.set_tooltip_text(tip_text);
308 #endif
309 }
310 };
311
312 class BoolBox : public Gtk::CheckButton {
313 public:
314 BoolBox(const char* labelText) : Gtk::CheckButton(labelText) {
315 signal_toggled().connect(sig_changed.make_slot());
316 }
317 bool get_value() const { return get_active(); }
318 void set_value(bool value) { set_active(value); }
319 sigc::signal<void>& signal_value_changed() { return sig_changed; }
320 protected:
321 sigc::signal<void> sig_changed;
322 };
323
324
325 class BoolEntryPlus6 : public LabelWidget {
326 private:
327 Gtk::CheckButton checkbutton;
328 void value_changed();
329 NumEntryGain& eGain;
330 int32_t plus6value;
331 public:
332 BoolEntryPlus6(const char* labelText, NumEntryGain& eGain, int32_t plus6value);
333 int32_t get_value() const;
334 void set_value(int32_t value);
335 };
336
337
338 class StringEntry : public LabelWidget {
339 private:
340 Gtk::Entry entry;
341 public:
342 StringEntry(const char* labelText);
343 gig::String get_value() const;
344 void set_value(const gig::String& value);
345 void set_width_chars(int n_chars) { entry.set_width_chars(n_chars); }
346 };
347
348 class StringEntryMultiLine : public LabelWidget {
349 private:
350 Gtk::TextView text_view;
351 Glib::RefPtr<Gtk::TextBuffer> text_buffer;
352 Gtk::Frame frame;
353 public:
354 StringEntryMultiLine(const char* labelText);
355 gig::String get_value() const;
356 void set_value(const gig::String& value);
357 };
358
359
360 /**
361 * Container widget for LabelWidgets.
362 */
363 class Table :
364 #if USE_GTKMM_GRID
365 public Gtk::Grid
366 #else
367 public Gtk::Table
368 #endif
369 {
370 public:
371 Table(int x, int y);
372 void add(BoolEntry& boolentry);
373 void add(BoolEntryPlus6& boolentry);
374 void add(LabelWidget& labelwidget);
375 private:
376 #if USE_GTKMM_GRID
377 int cols;
378 #endif
379 int rowno;
380 };
381
382
383 /**
384 * Base class for editor components that use LabelWidgets to edit
385 * member variables of the same class. By connecting the widgets to
386 * members of the model class, the model is automatically kept
387 * updated.
388 */
389 template<class M>
390 class PropEditor {
391 public:
392 sigc::signal<void>& signal_changed() {
393 return sig_changed;
394 }
395 protected:
396 M* m;
397 int update_model; // to prevent infinite update loops
398 PropEditor() : m(0), update_model(0) { }
399 sigc::signal<void> sig_changed;
400
401 template<class C, typename T>
402 void connect(C& widget, T M::* member) {
403 // gcc 4.1.2 needs this temporary variable to resolve the
404 // address
405 void (PropEditor::*f)(const C* w, T M::* member) =
406 &PropEditor::set_member;
407 widget.signal_value_changed().connect(
408 sigc::bind(sigc::mem_fun(*this, f), &widget, member));
409
410 void (PropEditor::*g)(C* w, T M::* member) =
411 &PropEditor::get_member;
412 sig.connect(
413 sigc::bind(sigc::mem_fun(*this, g), &widget, member));
414 }
415
416 template<class C, class S, typename T>
417 void connect(C& widget, void (S::*setter)(T)) {
418 void (PropEditor::*f)(const C* w, void (S::*setter)(T)) =
419 &PropEditor<M>::call_setter;
420 widget.signal_value_changed().connect(
421 sigc::bind(sigc::mem_fun(*this, f), &widget, setter));
422 }
423
424 void connect(NoteEntry& eKeyRangeLow, NoteEntry& eKeyRangeHigh,
425 gig::range_t M::* range) {
426 eKeyRangeLow.signal_value_changed().connect(
427 sigc::bind(
428 sigc::mem_fun(*this, &PropEditor::key_range_low_changed),
429 &eKeyRangeLow, &eKeyRangeHigh, range));
430 eKeyRangeHigh.signal_value_changed().connect(
431 sigc::bind(
432 sigc::mem_fun(*this, &PropEditor::key_range_high_changed),
433 &eKeyRangeLow, &eKeyRangeHigh, range));
434 sig.connect(
435 sigc::bind(sigc::mem_fun(*this, &PropEditor::get_key_range),
436 &eKeyRangeLow, &eKeyRangeHigh, range));
437 }
438
439 void update(M* m) {
440 update_model++;
441 this->m = m;
442 sig.emit();
443 update_model--;
444 }
445
446 private:
447 sigc::signal<void> sig;
448
449 void key_range_low_changed(NoteEntry* eKeyRangeLow,
450 NoteEntry* eKeyRangeHigh,
451 gig::range_t M::* range) {
452 if (update_model == 0) {
453 uint8_t value = eKeyRangeLow->get_value();
454 (m->*range).low = value;
455 if (value > (m->*range).high) {
456 eKeyRangeHigh->set_value(value);
457 }
458 sig_changed();
459 }
460 }
461
462 void key_range_high_changed(NoteEntry* eKeyRangeLow,
463 NoteEntry* eKeyRangeHigh,
464 gig::range_t M::* range) {
465 if (update_model == 0) {
466 uint8_t value = eKeyRangeHigh->get_value();
467 (m->*range).high = value;
468 if (value < (m->*range).low) {
469 eKeyRangeLow->set_value(value);
470 }
471 sig_changed();
472 }
473 }
474
475 template<class C, typename T>
476 void set_member(const C* w, T M::* member) {
477 if (update_model == 0) {
478 m->*member = w->get_value();
479 sig_changed();
480 }
481 }
482
483 template<class C, typename T>
484 void get_member(C* w, T M::* member) {
485 w->set_value(m->*member);
486 }
487
488 void get_key_range(NoteEntry* eKeyRangeLow,
489 NoteEntry* eKeyRangeHigh,
490 gig::range_t M::* range) const {
491 eKeyRangeLow->set_value((m->*range).low);
492 eKeyRangeHigh->set_value((m->*range).high);
493 }
494
495 template<class C, class S, typename T>
496 void call_setter(const C* w, void (S::*setter)(T)) {
497 if (update_model == 0) {
498 (static_cast<S*>(this)->*setter)(w->get_value());
499 sig_changed();
500 }
501 }
502 };
503
504 #endif

  ViewVC Help
Powered by ViewVC