/[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 3738 - (show annotations) (download) (as text)
Mon Feb 3 18:47:02 2020 UTC (4 years, 2 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 14596 byte(s)
* Use more appropriate stepping/paging increments/decrements for spinboxes'
  plus/minus buttons (depending on the individual parameter spinbutton
  actually controls).

* Bumped version (1.1.1.svn15).

1 /* -*- c++ -*-
2 * Copyright (C) 2006-2019 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 virtual void on_show_tooltips_changed();
74 #ifdef OLD_TOOLTIPS
75 Gtk::Tooltips tooltips;
76 #endif
77 sigc::signal<void> sig_changed;
78 };
79
80 class ReadOnlyLabelWidget : public LabelWidget {
81 public:
82 Gtk::Label text;
83
84 ReadOnlyLabelWidget(const char* leftHandText);
85 ReadOnlyLabelWidget(const char* leftHandText, const char* rightHandText);
86 };
87
88 class NumEntry : public LabelWidget {
89 protected:
90 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
91 Gtk::Adjustment adjust;
92 #else
93 Glib::RefPtr<Gtk::Adjustment> adjust;
94 #endif
95 HScale scale;
96 Gtk::SpinButton spinbutton;
97 HBox box;
98
99 static int round_to_int(double x) {
100 return int(x < 0.0 ? x - 0.5 : x + 0.5);
101 }
102
103 public:
104 NumEntry(const char* labelText, double lower = 0, double upper = 127,
105 int decimals = 0);
106 void set_tip(const Glib::ustring& tip_text) {
107 #ifdef OLD_TOOLTIPS
108 tooltips.set_tip(spinbutton, tip_text);
109 #else
110 spinbutton.set_tooltip_text(tip_text);
111 #endif
112 }
113 void set_upper(double upper) {
114 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
115 adjust.set_upper(upper);
116 #else
117 adjust->set_upper(upper);
118 #endif
119 }
120 void set_increments(double step, double page) {
121 spinbutton.set_increments(step, page);
122 }
123 void on_show_tooltips_changed();
124 };
125
126 class NumEntryGain : public NumEntry {
127 private:
128 int32_t value;
129 void value_changed();
130 double coeff;
131 bool connected;
132 public:
133 NumEntryGain(const char* labelText,
134 double lower, double upper, int decimals, double coeff);
135 int32_t get_value() const { return value; }
136 void set_value(int32_t value);
137 };
138
139 template<typename T>
140 class NumEntryTemp : public NumEntry {
141 private:
142 T value;
143 void value_changed();
144 public:
145 NumEntryTemp(const char* labelText,
146 double lower = 0, double upper = 127, int decimals = 0);
147 T get_value() const { return value; }
148 void set_value(T value);
149 };
150
151 template<typename T>
152 NumEntryTemp<T>::NumEntryTemp(const char* labelText,
153 double lower, double upper, int decimals) :
154 NumEntry(labelText, lower, upper, decimals),
155 value(0)
156 {
157 spinbutton.signal_value_changed().connect(
158 sigc::mem_fun(*this, &NumEntryTemp::value_changed));
159 }
160
161 template<typename T>
162 void NumEntryTemp<T>::value_changed()
163 {
164 const double f = pow(10, spinbutton.get_digits());
165 int new_value = round_to_int(spinbutton.get_value() * f);
166 if (new_value != round_to_int(value * f)) {
167 value = T(new_value / f);
168 sig_changed();
169 }
170 }
171
172 template<typename T>
173 void NumEntryTemp<T>::set_value(T value)
174 {
175 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
176 if (value > adjust.get_upper()) value = T(adjust.get_upper());
177 #else
178 if (value > adjust->get_upper()) value = T(adjust->get_upper());
179 #endif
180 if (this->value != value) {
181 this->value = value;
182 const double f = pow(10, spinbutton.get_digits());
183 if (round_to_int(spinbutton.get_value() * f) != round_to_int(value * f)) {
184 spinbutton.set_value(value);
185 }
186 sig_changed();
187 }
188 }
189
190
191 class NoteEntry : public NumEntryTemp<uint8_t> {
192 public:
193 NoteEntry(const char* labelText);
194 private:
195 int on_input(double* new_value);
196 bool on_output();
197 };
198
199
200 class NumEntryPermille : public NumEntry {
201 private:
202 uint16_t value;
203 void value_changed();
204 public:
205 NumEntryPermille(const char* labelText,
206 double lower = 0, double upper = 127, int decimals = 0);
207 uint16_t get_value() const { return value; }
208 void set_value(uint16_t value);
209 };
210
211 class ChoiceEntryBase : public LabelWidget {
212 protected:
213 ChoiceEntryBase(const char* labelText, Gtk::Widget& widget) : LabelWidget(labelText, widget) {};
214 Gtk::ComboBoxText combobox;
215 void on_show_tooltips_changed();
216 };
217
218 template<typename T>
219 class ChoiceEntry : public ChoiceEntryBase {
220 private:
221 #if HAS_GTKMM_ALIGNMENT
222 Gtk::Alignment align;
223 #endif
224 const T* values;
225 public:
226 ChoiceEntry(const char* labelText);
227 T get_value() const;
228 void set_value(T value);
229 void set_choices(const char** texts, const T* values);
230
231 void set_tip(const Glib::ustring& tip_text) {
232 #ifdef OLD_TOOLTIPS
233 tooltips.set_tip(combobox, tip_text);
234 #else
235 combobox.set_tooltip_text(tip_text);
236 #endif
237 }
238 };
239
240 template<typename T>
241 ChoiceEntry<T>::ChoiceEntry(const char* labelText) :
242 #if HAS_GTKMM_ALIGNMENT
243 ChoiceEntryBase(labelText, align),
244 align(0, 0, 0, 0),
245 #else
246 ChoiceEntryBase(labelText, combobox),
247 #endif
248 values(0)
249 {
250 combobox.signal_changed().connect(sig_changed.make_slot());
251 #if HAS_GTKMM_ALIGNMENT
252 align.add(combobox);
253 #endif
254 }
255
256 template<typename T>
257 void ChoiceEntry<T>::set_choices(const char** texts, const T* values)
258 {
259 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
260 combobox.clear_items();
261 for (int i = 0 ; texts[i] ; i++) {
262 combobox.append_text(texts[i]);
263 }
264 #else
265 combobox.remove_all();
266 for (int i = 0 ; texts[i] ; i++) {
267 combobox.append(texts[i]);
268 }
269 #endif
270 this->values = values;
271 }
272
273 template<typename T>
274 T ChoiceEntry<T>::get_value() const
275 {
276 int rowno = combobox.get_active_row_number();
277 return values[rowno];
278 }
279
280 template<typename T>
281 void ChoiceEntry<T>::set_value(T value)
282 {
283 int row = 0;
284 int nb_rows = combobox.get_model()->children().size();
285 for (; row < nb_rows ; row++) {
286 if (value == values[row]) break;
287 }
288 combobox.set_active(row == nb_rows ? -1 : row);
289 }
290
291
292 class ChoiceEntryLeverageCtrl : public LabelWidget {
293 private:
294 gig::leverage_ctrl_t value;
295 Gtk::ComboBoxText combobox;
296 #if HAS_GTKMM_ALIGNMENT
297 Gtk::Alignment align;
298 #endif
299 void value_changed();
300 protected:
301 void on_show_tooltips_changed();
302 public:
303 ChoiceEntryLeverageCtrl(const char* labelText);
304 gig::leverage_ctrl_t get_value() const { return value; }
305 void set_value(gig::leverage_ctrl_t value);
306 void set_tip(const Glib::ustring& tip_text) {
307 combobox.set_tooltip_text(tip_text);
308 }
309 };
310
311 class ChoiceEntryLfoWave : public LabelWidget {
312 private:
313 gig::lfo_wave_t value;
314 Gtk::ComboBoxText combobox;
315 #if HAS_GTKMM_ALIGNMENT
316 Gtk::Alignment align;
317 #endif
318 void value_changed();
319 protected:
320 void on_show_tooltips_changed();
321 public:
322 ChoiceEntryLfoWave(const char* labelText);
323 gig::lfo_wave_t get_value() const { return value; }
324 void set_value(gig::lfo_wave_t value);
325 void set_tip(const Glib::ustring& tip_text) {
326 combobox.set_tooltip_text(tip_text);
327 }
328 };
329
330
331 class BoolEntry : public LabelWidget {
332 private:
333 Gtk::CheckButton checkbutton;
334 public:
335 BoolEntry(const char* labelText);
336 bool get_value() const { return checkbutton.get_active(); }
337 void set_value(bool value) { checkbutton.set_active(value); }
338
339 void set_tip(const Glib::ustring& tip_text) {
340 #ifdef OLD_TOOLTIPS
341 tooltips.set_tip(checkbutton, tip_text);
342 #else
343 checkbutton.set_tooltip_text(tip_text);
344 #endif
345 }
346 };
347
348 class BoolBox : public Gtk::CheckButton {
349 public:
350 BoolBox(const char* labelText);
351 bool get_value() const { return get_active(); }
352 void set_value(bool value) { set_active(value); }
353 sigc::signal<void>& signal_value_changed() { return sig_changed; }
354 protected:
355 void on_show_tooltips_changed();
356
357 sigc::signal<void> sig_changed;
358 };
359
360
361 class StringEntry : public LabelWidget {
362 private:
363 Gtk::Entry entry;
364 public:
365 StringEntry(const char* labelText);
366 gig::String get_value() const;
367 void set_value(const gig::String& value);
368 void set_width_chars(int n_chars) { entry.set_width_chars(n_chars); }
369 };
370
371 class StringEntryMultiLine : public LabelWidget {
372 private:
373 Gtk::TextView text_view;
374 Glib::RefPtr<Gtk::TextBuffer> text_buffer;
375 Gtk::Frame frame;
376 protected:
377 void on_show_tooltips_changed();
378 public:
379 StringEntryMultiLine(const char* labelText);
380 gig::String get_value() const;
381 void set_value(const gig::String& value);
382 };
383
384
385 /**
386 * Container widget for LabelWidgets.
387 */
388 class Table :
389 #if USE_GTKMM_GRID
390 public Gtk::Grid
391 #else
392 public Gtk::Table
393 #endif
394 {
395 public:
396 Table(int x, int y);
397 void add(BoolEntry& boolentry);
398 void add(LabelWidget& labelwidget);
399 private:
400 #if USE_GTKMM_GRID
401 int cols;
402 #endif
403 int rowno;
404 };
405
406
407 /**
408 * Base class for editor components that use LabelWidgets to edit
409 * member variables of the same class. By connecting the widgets to
410 * members of the model class, the model is automatically kept
411 * updated.
412 */
413 template<class M>
414 class PropEditor {
415 public:
416 sigc::signal<void>& signal_changed() {
417 return sig_changed;
418 }
419 protected:
420 M* m;
421 int update_model; // to prevent infinite update loops
422 PropEditor() : m(0), update_model(0) { }
423 sigc::signal<void> sig_changed;
424
425 template<class C, typename T>
426 void connect(C& widget, T M::* member) {
427 // gcc 4.1.2 needs this temporary variable to resolve the
428 // address
429 void (PropEditor::*f)(const C* w, T M::* member) =
430 &PropEditor::set_member;
431 widget.signal_value_changed().connect(
432 sigc::bind(sigc::mem_fun(*this, f), &widget, member));
433
434 void (PropEditor::*g)(C* w, T M::* member) =
435 &PropEditor::get_member;
436 sig.connect(
437 sigc::bind(sigc::mem_fun(*this, g), &widget, member));
438 }
439
440 template<class C, class S, typename T>
441 void connect(C& widget, void (S::*setter)(T)) {
442 void (PropEditor::*f)(const C* w, void (S::*setter)(T)) =
443 &PropEditor<M>::call_setter;
444 widget.signal_value_changed().connect(
445 sigc::bind(sigc::mem_fun(*this, f), &widget, setter));
446 }
447
448 template<class C, class F>
449 void connectLambda(C& widget, F fn) {
450 widget.signal_value_changed().connect([&widget,fn]{
451 fn( widget.get_value() );
452 });
453 }
454
455 void connect(NoteEntry& eKeyRangeLow, NoteEntry& eKeyRangeHigh,
456 gig::range_t M::* range) {
457 eKeyRangeLow.signal_value_changed().connect(
458 sigc::bind(
459 sigc::mem_fun(*this, &PropEditor::key_range_low_changed),
460 &eKeyRangeLow, &eKeyRangeHigh, range));
461 eKeyRangeHigh.signal_value_changed().connect(
462 sigc::bind(
463 sigc::mem_fun(*this, &PropEditor::key_range_high_changed),
464 &eKeyRangeLow, &eKeyRangeHigh, range));
465 sig.connect(
466 sigc::bind(sigc::mem_fun(*this, &PropEditor::get_key_range),
467 &eKeyRangeLow, &eKeyRangeHigh, range));
468 }
469
470 void update(M* m) {
471 update_model++;
472 this->m = m;
473 sig.emit();
474 update_model--;
475 }
476
477 private:
478 sigc::signal<void> sig;
479
480 void key_range_low_changed(NoteEntry* eKeyRangeLow,
481 NoteEntry* eKeyRangeHigh,
482 gig::range_t M::* range) {
483 if (update_model == 0) {
484 uint8_t value = eKeyRangeLow->get_value();
485 (m->*range).low = value;
486 if (value > (m->*range).high) {
487 eKeyRangeHigh->set_value(value);
488 }
489 sig_changed();
490 }
491 }
492
493 void key_range_high_changed(NoteEntry* eKeyRangeLow,
494 NoteEntry* eKeyRangeHigh,
495 gig::range_t M::* range) {
496 if (update_model == 0) {
497 uint8_t value = eKeyRangeHigh->get_value();
498 (m->*range).high = value;
499 if (value < (m->*range).low) {
500 eKeyRangeLow->set_value(value);
501 }
502 sig_changed();
503 }
504 }
505
506 template<class C, typename T>
507 void set_member(const C* w, T M::* member) {
508 if (update_model == 0) {
509 m->*member = w->get_value();
510 sig_changed();
511 }
512 }
513
514 template<class C, typename T>
515 void get_member(C* w, T M::* member) {
516 w->set_value(m->*member);
517 }
518
519 void get_key_range(NoteEntry* eKeyRangeLow,
520 NoteEntry* eKeyRangeHigh,
521 gig::range_t M::* range) const {
522 eKeyRangeLow->set_value((m->*range).low);
523 eKeyRangeHigh->set_value((m->*range).high);
524 }
525
526 template<class C, class S, typename T>
527 void call_setter(const C* w, void (S::*setter)(T)) {
528 if (update_model == 0) {
529 (static_cast<S*>(this)->*setter)(w->get_value());
530 sig_changed();
531 }
532 }
533 };
534
535 #endif

  ViewVC Help
Powered by ViewVC