ardour
motionfeedback.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010-2011 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18  $Id: motionfeedback.cc,v 1.5 2004/03/01 03:44:19 pauld Exp $
19 */
20 
21 #include <iostream>
22 #include <cmath>
23 #include <cstdlib>
24 #include <algorithm>
25 #include <unistd.h>
26 #include <stdio.h> /* for snprintf, grrr */
27 
28 #include <glib/gstdio.h>
29 
30 #include <gdk/gdkkeysyms.h>
31 #include <gtkmm.h>
32 
33 #include "pbd/controllable.h"
34 #include "pbd/compose.h"
35 #include "pbd/error.h"
36 
38 #include "gtkmm2ext/keyboard.h"
40 #include "gtkmm2ext/gui_thread.h"
41 
42 #include "i18n.h"
43 
44 using namespace Gtk;
45 using namespace Gtkmm2ext;
46 using namespace sigc;
47 
48 using PBD::error;
49 
50 Gdk::Color* MotionFeedback::base_color;
51 
52 MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix,
53  Type t,
55  double default_val,
56  double step_increment,
57  double page_increment,
58  const char *widget_name,
59  bool with_numeric_display,
60  int subw,
61  int subh)
62  : _controllable (c)
63  , value (0)
64  , default_value (default_val)
65  , step_inc (step_increment)
66  , page_inc (page_increment)
67  , type (t)
68  , value_packer (0)
69  , pixbuf (pix)
70  , subwidth (subw)
71  , subheight (subh)
72 {
73  if (!base_color) {
74  base_color = new Gdk::Color ("#1a5274");
75  }
76 
77  char value_name[1024];
78 
80  print_arg = 0;
81 
82 
83  HBox* hpacker = manage (new HBox);
84  hpacker->pack_start (pixwin, true, true);
85  hpacker->show ();
86  pack_start (*hpacker, false, false);
87  pixwin.show ();
88 
89  if (with_numeric_display) {
90 
91  value_packer = new EventBox;
92  value_packer->set_name ("MotionControllerValue");
93  value_packer->show ();
94  value_packer->set_border_width (6);
95 
96  value = new Label;
97  value->set_justify (Gtk::JUSTIFY_RIGHT);
98  value->show ();
99 
100  value_packer->add (*value);
101 
102  hpacker = manage (new HBox);
103  hpacker->pack_start (*value_packer, true, false);
104  hpacker->show ();
105  hpacker->set_border_width (6);
106 
107  pack_start (*hpacker, false, false);
108 
109  if (widget_name) {
110  snprintf (value_name, sizeof(value_name), "%sValue", widget_name);
111  value->set_name (value_name);
112  }
113 
114  if (_controllable) {
115  char buf[32];
117  value->set_text (buf);
118  }
119  }
120 
121  pixwin.set_events (Gdk::BUTTON_PRESS_MASK|
122  Gdk::BUTTON_RELEASE_MASK|
123  Gdk::POINTER_MOTION_MASK|
124  Gdk::ENTER_NOTIFY_MASK|
125  Gdk::LEAVE_NOTIFY_MASK|
126  Gdk::SCROLL_MASK|
127  Gdk::KEY_PRESS_MASK|
128  Gdk::KEY_RELEASE_MASK);
129 
130  pixwin.set_flags (CAN_FOCUS);
131 
132  /* Proxy all important events on the pixwin to ourselves */
133 
134  pixwin.signal_button_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_press_event));
135  pixwin.signal_button_release_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_release_event));
136  pixwin.signal_motion_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_motion_notify_event));
137  pixwin.signal_enter_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_enter_notify_event));
138  pixwin.signal_leave_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_leave_notify_event));
139  pixwin.signal_key_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_key_press_event));
140  pixwin.signal_scroll_event().connect(mem_fun (*this,&MotionFeedback::pixwin_scroll_event));
141  pixwin.signal_expose_event().connect(mem_fun (*this,&MotionFeedback::pixwin_expose_event), true);
142  pixwin.signal_size_request().connect(mem_fun (*this,&MotionFeedback::pixwin_size_request));
143 }
144 
146 {
147  delete value;
148  delete value_packer;
149 }
150 
151 bool
153 {
155  return true;
156  }
157 
158  switch (ev->button) {
159  case 1:
160  grab_is_fine = false;
161  break;
162  case 2:
163  grab_is_fine = true;
164  break;
165  case 3:
166  return false;
167  }
168 
169  gtk_grab_add(GTK_WIDGET(pixwin.gobj()));
170 
171  grabbed_y = ev->y_root;
172  grabbed_x = ev->x_root;
173 
174  return false;
175 }
176 
177 bool
179 {
180  if (!_controllable) {
181  return false;
182  }
183 
184  switch (ev->button) {
185  case 1:
186  if (pixwin.has_grab()) {
187  if (!grab_is_fine) {
188  gtk_grab_remove
189  (GTK_WIDGET(pixwin.gobj()));
190  }
191  }
193  /* shift click back to the default */
195  return true;
197  /* ctrl click back to the minimum value */
199  }
200  break;
201 
202  case 3:
203  if (pixwin.has_grab()) {
204  if (grab_is_fine) {
205  gtk_grab_remove
206  (GTK_WIDGET(pixwin.gobj()));
207  }
208  }
209  break;
210  }
211 
212  return VBox::on_button_release_event (ev);
213 }
214 
215 bool
217 {
218  if (!_controllable) {
219  return false;
220  }
221 
222  gfloat multiplier;
223  gfloat x_delta;
224  gfloat y_delta;
225 
226  if (!pixwin.has_grab()) {
227  return VBox::on_motion_notify_event (ev);
228  }
229 
230  multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
231  ((ev->state & Keyboard::PrimaryModifier) ? 10 : 1) *
232  ((ev->state & Keyboard::SecondaryModifier) ? 0.1 : 1);
233 
234  if (ev->state & Gdk::BUTTON1_MASK) {
235 
236  /* vertical control */
237 
238  y_delta = grabbed_y - ev->y_root;
239  grabbed_y = ev->y_root;
240 
241  x_delta = ev->x_root - grabbed_x;
242 
243  if (y_delta == 0) return TRUE;
244 
245  y_delta *= 1 + (x_delta/100);
246  y_delta *= multiplier;
247  y_delta /= 10;
248 
250 
251  } else if (ev->state & Gdk::BUTTON2_MASK) {
252 
253  /* rotary control */
254 
255  double x = ev->x - subwidth/2;
256  double y = - ev->y + subwidth/2;
257  double angle = std::atan2 (y, x) / M_PI;
258 
259  if (angle < -0.5) {
260  angle += 2.0;
261  }
262 
263  angle = -(2.0/3.0) * (angle - 1.25);
264  angle *= multiplier;
265 
267  }
268 
269 
270  return true;
271 }
272 
273 bool
275 {
276  pixwin.grab_focus();
277  return false;
278 }
279 
280 bool
282 {
283  pixwin.unset_flags (HAS_FOCUS);
284  return false;
285 }
286 
287 bool
289 {
290  if (!_controllable) {
291  return false;
292  }
293 
294  bool retval = false;
295  double multiplier;
296 
297  multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100.0 : 1.0) *
298  ((ev->state & Keyboard::SecondaryModifier) ? 10.0 : 1.0) *
299  ((ev->state & Keyboard::PrimaryModifier) ? 2.0 : 1.0);
300 
301  switch (ev->keyval) {
302  case GDK_Page_Up:
303  retval = true;
304  _controllable->set_value (adjust (multiplier * page_inc));
305  break;
306 
307  case GDK_Page_Down:
308  retval = true;
309  _controllable->set_value (adjust (-multiplier * page_inc));
310  break;
311 
312  case GDK_Up:
313  retval = true;
314  _controllable->set_value (adjust (multiplier * step_inc));
315  break;
316 
317  case GDK_Down:
318  retval = true;
319  _controllable->set_value (adjust (-multiplier * step_inc));
320  break;
321 
322  case GDK_Home:
323  retval = true;
325  break;
326 
327  case GDK_End:
328  retval = true;
330  break;
331  }
332 
333  return retval;
334 }
335 
336 bool
338 {
339  if (!_controllable) {
340  return true;
341  }
342 
343  GdkWindow *window = pixwin.get_window()->gobj();
344  double display_val = to_display_value (_controllable->get_value());
345  int32_t phase = lrint (display_val * 64.0);
346 
347  // skip middle phase except for true middle value
348 
349  if (type == Rotary && phase == 32) {
350  double pt = (display_val * 2.0) - 1.0;
351  if (pt < 0)
352  phase = 31;
353  if (pt > 0)
354  phase = 33;
355  }
356 
357  // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
358 
359  if (type == Endless && !(phase % 16)) {
360  if (phase == 64) {
361  phase = 0;
362  }
363 
364  double nom = phase / 64.0;
365  double diff = display_val - nom;
366 
367  if (diff > 0.0001)
368  phase = (phase + 1) % 64;
369  if (diff < -0.0001)
370  phase = (phase + 63) % 64;
371  }
372 
373  phase = std::min (phase, (int32_t) 63);
374 
375  GtkWidget* widget = GTK_WIDGET(pixwin.gobj());
376  gdk_draw_pixbuf (GDK_DRAWABLE(window), widget->style->fg_gc[0],
377  pixbuf->gobj(),
378  phase * subwidth, type * subheight,
379  /* center image in allocated area */
380  (get_width() - subwidth)/2,
381  0,
382  subwidth, subheight, GDK_RGB_DITHER_NORMAL, 0, 0);
383 
384  return true;
385 }
386 
387 bool
389 {
390  double scale;
391 
392  if (!_controllable) {
393  return false;
394  }
395 
396  if (ev->state & Keyboard::GainFineScaleModifier) {
397  if (ev->state & Keyboard::GainExtraFineScaleModifier) {
398  scale = 0.01;
399  } else {
400  scale = 0.10;
401  }
402  } else {
403  scale = 0.20;
404  }
405 
406  switch (ev->direction) {
407  case GDK_SCROLL_UP:
408  case GDK_SCROLL_RIGHT:
409  _controllable->set_value (adjust (scale * page_inc));
410  break;
411 
412  case GDK_SCROLL_DOWN:
413  case GDK_SCROLL_LEFT:
414  _controllable->set_value (adjust (-scale * page_inc));
415  break;
416  }
417 
418  return true;
419 }
420 
421 void
423 {
424  req->width = subwidth;
425  req->height = subheight;
426 }
427 
428 
429 void
431 {
432  if (value) {
433  char buf[32];
435  value->set_text (buf);
436  }
437 
438  pixwin.queue_draw ();
439 }
440 
441 void
443 {
444  _controllable = c;
447 
448  if (c) {
450 
451  char buf[32];
453  value->set_text (buf);
454  }
455 
456  pixwin.queue_draw ();
457 }
458 
461 {
462  return _controllable;
463 }
464 
465 void
467 {
468  if (c) {
469  sprintf (buf, "%.2f", c->get_value());
470  } else {
471  buf[0] = '\0';
472  }
473 }
474 
475 Glib::RefPtr<Gdk::Pixbuf>
477 {
478  Glib::RefPtr<Gdk::Pixbuf> pixbuf;
479  char *path;
480  int fd;
481  GError *gerror = NULL;
482 
483  fd = g_file_open_tmp ("mfimgXXXXXX", &path, &gerror);
484 
485  if (gerror) {
486  error << string_compose (_("motionfeedback: failed to open a temporary file for writing: %1"), gerror->message) << endmsg;
487  g_error_free (gerror);
488  return pixbuf;
489  } else {
490  ::close (fd);
491  }
492 
493 
494  GdkColor col2 = {0,0,0,0};
495  GdkColor col3 = {0,0,0,0};
496  GdkColor dark;
497  GdkColor bright;
498  ProlooksHSV* hsv;
499 
501  bright = (prolooks_hsv_to_gdk_color (hsv, &col2), col2);
502  prolooks_hsv_set_saturation (hsv, 0.66);
503  prolooks_hsv_set_value (hsv, 0.67);
504  dark = (prolooks_hsv_to_gdk_color (hsv, &col3), col3);
505 
506  cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size * 64, size);
507  cairo_t* cr = cairo_create (surface);
508 
509  for (int i = 0; i < 64; ++i) {
510  cairo_save (cr);
511  core_draw (cr, i, size, 20, size*i, 0, &bright, &dark);
512  cairo_restore (cr);
513  }
514 
515  if (cairo_surface_write_to_png (surface, path) != CAIRO_STATUS_SUCCESS) {
516  error << string_compose (_("motionfeedback: could not save image set to %1"), path) << endmsg;
517  return pixbuf;
518  }
519 
520  cairo_destroy (cr);
521  cairo_surface_destroy (surface);
522 
523  try {
524  pixbuf = Gdk::Pixbuf::create_from_file (path);
525  } catch (const Gdk::PixbufError &e) {
526  error << string_compose (_("motionfeedback: caught PixbufError: %1"), e.what()) << endmsg;
527  } catch (...) {
528  error << _("motionfeedback: unknown exception") << endmsg;
529  }
530 
531  g_unlink (path);
532  g_free (path);
533 
534  return pixbuf;
535 }
536 
537 void
538 MotionFeedback::core_draw (cairo_t* cr, int phase, double size, double progress_width, double xorigin, double yorigin,
539  const GdkColor* bright, const GdkColor* dark)
540 {
541  double xc;
542  double yc;
543  double start_angle;
544  double end_angle;
545  double value_angle;
546  double value;
547  double value_x;
548  double value_y;
549  double start_angle_x;
550  double start_angle_y;
551  double end_angle_x;
552  double end_angle_y;
553  double progress_radius;
554  double progress_radius_inner;
555  double progress_radius_outer;
556 
557  g_return_if_fail (cr != NULL);
558 
559  progress_radius = 40.0;
560  progress_radius_inner = progress_radius - (progress_width / 2.0);
561  progress_radius_outer = progress_radius + (progress_width / 2.0);
562 
563  const double pad = 2.0; /* line width for boundary of progress ring */
564  const double actual_width = ((2.0 * pad) + (2.0 * progress_radius_outer));
565  const double scale_factor = size / actual_width;
566 
567  /* knob center is at middle of the area bounded by (xorigin,yorigin) and (xorigin+size, yorigin+size)
568  but the coordinates will be scaled by the scale factor when cairo uses them so first
569  adjust them by the reciprocal of the scale factor.
570  */
571 
572  xc = (xorigin + (size / 2.0)) * (1.0/scale_factor);
573  yc = (yorigin + (size / 2.0)) * (1.0/scale_factor);
574 
575  value = (phase * 1.0) / (65 - 1);
576 
577  start_angle = ((180 - 65) * G_PI) / 180;
578  end_angle = ((360 + 65) * G_PI) / 180;
579 
580  value_angle = start_angle + (value * (end_angle - start_angle));
581  value_x = cos (value_angle);
582  value_y = sin (value_angle);
583  start_angle_x = cos (start_angle);
584  start_angle_y = sin (start_angle);
585  end_angle_x = cos (end_angle);
586  end_angle_y = sin (end_angle);
587 
588  cairo_scale (cr, scale_factor, scale_factor);
589 
590  //dark arc background
591  cairo_set_source_rgb (cr, 0.3, 0.3, 0.3 );
592  cairo_set_line_width (cr, progress_width);
593  cairo_arc (cr, xc, yc, progress_radius, start_angle, end_angle);
594  cairo_stroke (cr);
595 
596 
597  float r = (value) * (((float)bright->red)/G_MAXUINT16) + (1.0-value)*(((float)dark->red)/G_MAXUINT16);
598  float g = (value) * (((float)bright->green)/G_MAXUINT16) + (1.0-value)*(((float)dark->green)/G_MAXUINT16);
599  float b = (value) * (((float)bright->blue)/G_MAXUINT16) + (1.0-value)*(((float)dark->blue)/G_MAXUINT16);
600 
601  //colored arc
602  cairo_set_source_rgb (cr, r,g,b);
603  cairo_set_line_width (cr, progress_width);
604  cairo_arc (cr, xc, yc, progress_radius, start_angle, value_angle);
605  cairo_stroke (cr);
606 
607  //overall shade
608  cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, progress_radius_outer);
609  cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 1,1,1, 0.3);
610  cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 1,1,1, 0.0);
611  cairo_set_source (cr, shade_pattern);
612  cairo_arc (cr, xc, yc, progress_radius_outer-1, 0, 2.0*G_PI);
613  cairo_fill (cr);
614  cairo_pattern_destroy (shade_pattern);
615 
616  //black border
617  cairo_set_source_rgb (cr, 0, 0, 0 );
618  cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
619  cairo_set_line_width (cr, 1.0/scale_factor);
620  cairo_move_to (cr, xc + (progress_radius_outer * start_angle_x), yc + (progress_radius_outer * start_angle_y));
621  cairo_line_to (cr, xc + (progress_radius_inner * start_angle_x), yc + (progress_radius_inner * start_angle_y));
622  cairo_stroke (cr);
623  cairo_move_to (cr, xc + (progress_radius_outer * end_angle_x), yc + (progress_radius_outer * end_angle_y));
624  cairo_line_to (cr, xc + (progress_radius_inner * end_angle_x), yc + (progress_radius_inner * end_angle_y));
625  cairo_stroke (cr);
626  cairo_arc (cr, xc, yc, progress_radius_outer, start_angle, end_angle);
627  cairo_stroke (cr);
628  cairo_arc (cr, xc, yc, progress_radius_inner, start_angle, end_angle);
629  cairo_stroke (cr);
630 
631  //knob shadow
632  cairo_save(cr);
633  cairo_translate(cr, 6, 6 );
634  cairo_set_source_rgba (cr, 0,0,0,0.1 );
635  cairo_arc (cr, xc, yc, progress_radius_inner-1, 0, 2.0*G_PI);
636  cairo_fill (cr);
637  cairo_restore(cr);
638 
639  //inner circle
640  cairo_set_source_rgba (cr, 0.3, 0.3, 0.3, 1 );
641  cairo_arc (cr, xc, yc, progress_radius_inner-1, 0, 2.0*G_PI);
642  cairo_fill (cr);
643 
644  //knob shade
645  shade_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, progress_radius_outer);
646  cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 1,1,1, 0.5);
647  cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 0,0,0, 0.3);
648  cairo_set_source (cr, shade_pattern);
649  cairo_arc (cr, xc, yc, progress_radius_inner-1, 0, 2.0*G_PI);
650  cairo_fill (cr);
651  cairo_pattern_destroy (shade_pattern);
652 
653  //inner circle
654  cairo_set_source_rgba (cr, 0.3, 0.3, 0.3, 0.5 );
655  cairo_arc (cr, xc, yc, progress_radius_inner-5, 0, 2.0*G_PI);
656  cairo_fill (cr);
657 
658  //line
659  cairo_save(cr);
660  cairo_translate(cr, 2, 2 );
661  cairo_set_source_rgba (cr, 0,0,0,0.5 );
662  cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
663  cairo_set_line_width (cr, 4);
664  cairo_move_to (cr, xc + (progress_radius_inner * value_x), yc + (progress_radius_inner * value_y));
665  cairo_line_to (cr, xc + ((progress_radius_inner*0.4) * value_x), yc + ((progress_radius_inner*0.4) * value_y));
666  cairo_stroke (cr);
667  cairo_restore(cr);
668  cairo_set_source_rgba (cr, 1,1,1,0.7 );
669  cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
670  cairo_set_line_width (cr, 4.0);
671  cairo_move_to (cr, xc + (progress_radius_inner * value_x), yc + (progress_radius_inner * value_y));
672  cairo_line_to (cr, xc + ((progress_radius_inner*0.4) * value_x), yc + ((progress_radius_inner*0.4) * value_y));
673  cairo_stroke (cr);
674 
675 
676  //highlight if focused (damn, this is a cached image which doesn't (yet) have a "focused" state
677 // if (pixwin.has_focus()) {
678 // cairo_set_source_rgba (cr, 1,1,1, 0.5 );
679 // cairo_arc (cr, xc, yc, progress_radius_inner-1, 0, 2.0*G_PI);
680 // cairo_fill (cr);
681 // }
682 }
683 
684 void
685 MotionFeedback::set_lamp_color (const std::string& str)
686 {
687  if (base_color) {
688  *base_color = Gdk::Color (str);
689  } else {
690  base_color = new Gdk::Color (str);
691  }
692 }
virtual double to_display_value(double)=0
virtual double adjust(double nominal_delta)=0
LIBGTKMM2EXT_API void prolooks_hsv_set_value(ProlooksHSV *self, double value)
static void default_printer(char buf[32], const boost::shared_ptr< PBD::Controllable > &, void *)
bool pixwin_button_press_event(GdkEventButton *)
virtual double to_control_value(double)=0
static uint32_t GainFineScaleModifier
Definition: keyboard.h:61
Definition: ardour_ui.h:130
bool pixwin_motion_notify_event(GdkEventMotion *)
void set_controllable(boost::shared_ptr< PBD::Controllable >)
LIBPBD_API Transmitter error
bool pixwin_scroll_event(GdkEventScroll *)
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
PBD::ScopedConnection controller_connection
virtual double upper() const
Definition: controllable.h:108
static uint32_t TertiaryModifier
Definition: keyboard.h:57
#define _(Text)
Definition: i18n.h:11
static uint32_t PrimaryModifier
Definition: keyboard.h:55
void pixwin_size_request(GtkRequisition *)
LIBGTKMM2EXT_API void prolooks_hsv_set_saturation(ProlooksHSV *self, double value)
static void core_draw(cairo_t *, int, double, double, double, double, const GdkColor *bright, const GdkColor *dark)
bool pixwin_button_release_event(GdkEventButton *)
static uint32_t SecondaryModifier
Definition: keyboard.h:56
#define gui_context()
Definition: gui_thread.h:36
bool pixwin_key_press_event(GdkEventKey *)
static Gdk::Color * base_color
virtual double lower() const
Definition: controllable.h:107
Glib::RefPtr< Gdk::Pixbuf > pixbuf
virtual double get_value(void) const =0
bool button_press_handler(GdkEventButton *)
Gtk::EventBox * value_packer
#define page_increment
Definition: auto_spin.cc:31
static Glib::RefPtr< Gdk::Pixbuf > render_pixbuf(int size)
boost::shared_ptr< PBD::Controllable > controllable() const
virtual void set_value(double)=0
bool pixwin_enter_notify_event(GdkEventCrossing *)
PBD::Signal0< void > Changed
Definition: controllable.h:94
LIBGTKMM2EXT_API void prolooks_hsv_to_gdk_color(ProlooksHSV *self, GdkColor *result)
LIBGTKMM2EXT_API ProlooksHSV * prolooks_hsv_new_for_gdk_color(const GdkColor *color)
#define step_increment
Definition: auto_spin.cc:30
boost::shared_ptr< PBD::Controllable > _controllable
static bool modifier_state_equals(guint state, ModifierMask)
Definition: keyboard.cc:526
bool pixwin_leave_notify_event(GdkEventCrossing *)
static void set_lamp_color(const std::string &)
void(* print_func)(char buf[32], const boost::shared_ptr< PBD::Controllable > &, void *)
#define MISSING_INVALIDATOR
Definition: event_loop.h:86
bool pixwin_expose_event(GdkEventExpose *)
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
virtual void set_controllable(boost::shared_ptr< PBD::Controllable > c)
static uint32_t GainExtraFineScaleModifier
Definition: keyboard.h:62