Lumiera  0.pre.03
»edit your freedom«
timecode-widget.cpp
Go to the documentation of this file.
1 /*
2  TimecodeWidget - widget for timecode display / input
3 
4  Copyright (C) Ardour.org
5  1999, Paul Davis
6 
7  Copyright (C)
8  2010, Stefan Kangas <skangas@skangas.se
9 
10   **Lumiera** is free software; you can redistribute it and/or modify it
11   under the terms of the GNU General Public License as published by the
12   Free Software Foundation; either version 2 of the License, or (at your
13   option) any later version. See the file COPYING for further details.
14 
15 * *****************************************************************/
16 
17 
25 
26 #include "lib/time/diagnostics.hpp"
27 
28 #include <boost/lexical_cast.hpp>
29 #include <cmath>
30 #include <stdint.h>
31 #include <cstdio> // for sprintf
32 #include <cstdlib>
33 #include <locale>
34 #include <string>
35 
36 #include <gavl/gavl.h>
37 #include <sigc++/bind.h>
38 
39 
40 using boost::lexical_cast;
41 using sigc::mem_fun;
42 using sigc::bind;
43 using std::string;
44 
45 
46 namespace stage {
47 namespace widget {
48 
49  using namespace Gtk;
50 
51  // TODO: frame rate should not be a constant, but instead be per sequence
52  const float framerate = 25;
53 
54 
55  sigc::signal<void> TimeCode::ModeChanged;
56 
57  const uint TimeCode::field_length[(int) TimeCode::VFrames+1] = {
58  2, /* SMPTE_Hours */
59  2, /* SMPTE_Minutes */
60  2, /* SMPTE_Seconds */
61  2, /* SMPTE_Frames */
62  2, /* MS_Hours */
63  2, /* MS_Minutes */
64  5, /* MS_Seconds */
65  10 /* VFrames */
66  };
67 
68 
69  TimeCode::TimeCode (string clock_name, string widget_name, bool allow_edit) /*, bool duration,*/
70  : _name(clock_name)
71 // , is_duration(duration)
72  , editable(allow_edit)
73  , colon1(":")
74  , colon2(":")
75  , colon3(":")
76  , colon4(":")
77  , colon5(":")
78  {
79  last_when = Time::ZERO;
80  last_pdelta = 0;
81  last_sdelta = 0;
82  key_entry_state = 0;
83  ops_menu = 0;
84  dragging = false;
85 
86  audio_frames_ebox.add(audio_frames_label);
87 
88  frames_packer.set_homogeneous(false);
89  frames_packer.set_border_width(2);
90  frames_packer.pack_start(audio_frames_ebox, false, false);
91 
92  frames_packer_hbox.pack_start(frames_packer, true, false);
93 
94  hours_ebox.add(hours_label);
95  minutes_ebox.add(minutes_label);
96  seconds_ebox.add(seconds_label);
97  frames_ebox.add(frames_label);
98  ms_hours_ebox.add(ms_hours_label);
99  ms_minutes_ebox.add(ms_minutes_label);
100  ms_seconds_ebox.add(ms_seconds_label);
101 
102  smpte_packer.set_homogeneous(false);
103  smpte_packer.set_border_width(2);
104  smpte_packer.pack_start(hours_ebox, false, false);
105  smpte_packer.pack_start(colon1, false, false);
106  smpte_packer.pack_start(minutes_ebox, false, false);
107  smpte_packer.pack_start(colon2, false, false);
108  smpte_packer.pack_start(seconds_ebox, false, false);
109  smpte_packer.pack_start(colon3, false, false);
110  smpte_packer.pack_start(frames_ebox, false, false);
111 
112  smpte_packer_hbox.pack_start(smpte_packer, true, false);
113 
114  minsec_packer.set_homogeneous(false);
115  minsec_packer.set_border_width(2);
116  minsec_packer.pack_start(ms_hours_ebox, false, false);
117  minsec_packer.pack_start(colon4, false, false);
118  minsec_packer.pack_start(ms_minutes_ebox, false, false);
119  minsec_packer.pack_start(colon5, false, false);
120  minsec_packer.pack_start(ms_seconds_ebox, false, false);
121 
122  minsec_packer_hbox.pack_start(minsec_packer, true, false);
123 
124  clock_frame.set_shadow_type(Gtk::SHADOW_IN);
125  clock_frame.set_name("BaseFrame");
126 
127  clock_frame.add(clock_base);
128 
129  set_widget_name(widget_name);
130 
131  // Set mode to force update
132  _mode = Off;
133  set_mode(SMPTE);
134 
135  pack_start(clock_frame, true, true);
136 
137  /* the clock base handles button releases for menu popup regardless of
138  editable status. if the clock is editable, the clock base is where
139  we pass focus to after leaving the last editable "field", which
140  will then shutdown editing till the user starts it up again.
141 
142  it does this because the focus out event on the field disables
143  keyboard event handling, and we don't connect anything up to
144  notice focus in on the clock base. hence, keyboard event handling
145  stays disabled.
146  */
147 
148  clock_base.add_events(Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
149  clock_base.signal_button_release_event().connect(bind(mem_fun(
150  *this, &TimeCode::field_button_release_event), SMPTE_Hours));
151 
152  // Session::SMPTEOffsetChanged.connect(mem_fun(*this, &TimeCode::smpte_offset_changed));
153 
154  if (editable) {
155  setup_events();
156  }
157 
158  set(last_when, true);
159  }
160 
161 
162 
163  void
164  TimeCode::set_widget_name(string name)
165  {
166  Widget::set_name(name);
167 
168  clock_base.set_name(name);
169 
170  audio_frames_label.set_name(name);
171  hours_label.set_name(name);
172  minutes_label.set_name(name);
173  seconds_label.set_name(name);
174  frames_label.set_name(name);
175  ms_hours_label.set_name(name);
176  ms_minutes_label.set_name(name);
177  ms_seconds_label.set_name(name);
178  hours_ebox.set_name(name);
179  minutes_ebox.set_name(name);
180  seconds_ebox.set_name(name);
181  frames_ebox.set_name(name);
182  audio_frames_ebox.set_name(name);
183  ms_hours_ebox.set_name(name);
184  ms_minutes_ebox.set_name(name);
185  ms_seconds_ebox.set_name(name);
186 
187  colon1.set_name(name);
188  colon2.set_name(name);
189  colon3.set_name(name);
190  colon4.set_name(name);
191  colon5.set_name(name);
192 
193  queue_draw();
194  }
195 
196 
197  void
198  TimeCode::setup_events()
199  {
200  clock_base.set_can_focus(true);
201 
202  const Gdk::EventMask eventMask =
203  Gdk::BUTTON_PRESS_MASK|
204  Gdk::BUTTON_RELEASE_MASK|
205  Gdk::KEY_PRESS_MASK|
206  Gdk::KEY_RELEASE_MASK|
207  Gdk::FOCUS_CHANGE_MASK|
208  Gdk::POINTER_MOTION_MASK|
209  Gdk::SCROLL_MASK;
210 
211  hours_ebox.add_events(eventMask);
212  minutes_ebox.add_events(eventMask);
213  seconds_ebox.add_events(eventMask);
214  frames_ebox.add_events(eventMask);
215  ms_hours_ebox.add_events(eventMask);
216  ms_minutes_ebox.add_events(eventMask);
217  ms_seconds_ebox.add_events(eventMask);
218  audio_frames_ebox.add_events(eventMask);
219 
220  hours_ebox.set_can_focus(true);
221  minutes_ebox.set_can_focus(true);
222  seconds_ebox.set_can_focus(true);
223  frames_ebox.set_can_focus(true);
224  audio_frames_ebox.set_can_focus(true);
225  ms_hours_ebox.set_can_focus(true);
226  ms_minutes_ebox.set_can_focus(true);
227  ms_seconds_ebox.set_can_focus(true);
228 
229 
230  auto connect_motion_event = [=] (EventBox& guiElm, Field fieldID)
231  {
232  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_motion_notify_event), fieldID);
233  guiElm.signal_motion_notify_event().connect (handlerSlot);
234  };
235 
236  connect_motion_event (hours_ebox, SMPTE_Hours);
237  connect_motion_event (minutes_ebox, SMPTE_Minutes);
238  connect_motion_event (seconds_ebox, SMPTE_Seconds);
239  connect_motion_event (frames_ebox, SMPTE_Frames);
240 
241  connect_motion_event (audio_frames_ebox, VFrames);
242 
243  connect_motion_event (ms_hours_ebox, MS_Hours);
244  connect_motion_event (ms_minutes_ebox, MS_Minutes);
245  connect_motion_event (ms_seconds_ebox, MS_Seconds);
246 
247 
248  auto connect_button_press = [=] (EventBox& guiElm, Field fieldID)
249  {
250  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_button_press_event), fieldID);
251  guiElm.signal_button_press_event().connect (handlerSlot);
252  };
253 
254  connect_button_press (hours_ebox, SMPTE_Hours);
255  connect_button_press (minutes_ebox, SMPTE_Minutes);
256  connect_button_press (seconds_ebox, SMPTE_Seconds);
257  connect_button_press (frames_ebox, SMPTE_Frames);
258 
259  connect_button_press (audio_frames_ebox, VFrames);
260 
261  connect_button_press (ms_hours_ebox, MS_Hours);
262  connect_button_press (ms_minutes_ebox, MS_Minutes);
263  connect_button_press (ms_seconds_ebox, MS_Seconds);
264 
265 
266  auto connect_button_release = [=] (EventBox& guiElm, Field fieldID)
267  {
268  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_button_release_event), fieldID);
269  guiElm.signal_button_release_event().connect (handlerSlot);
270  };
271 
272  connect_button_release (hours_ebox, SMPTE_Hours);
273  connect_button_release (minutes_ebox, SMPTE_Minutes);
274  connect_button_release (seconds_ebox, SMPTE_Seconds);
275  connect_button_release (frames_ebox, SMPTE_Frames);
276 
277  connect_button_release (audio_frames_ebox, VFrames);
278 
279  connect_button_release (ms_hours_ebox, MS_Hours);
280  connect_button_release (ms_minutes_ebox, MS_Minutes);
281  connect_button_release (ms_seconds_ebox, MS_Seconds);
282 
283 
284  auto connect_scroll_event = [=] (EventBox& guiElm, Field fieldID)
285  {
286  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_button_scroll_event), fieldID);
287  guiElm.signal_scroll_event().connect (handlerSlot);
288  };
289 
290  connect_scroll_event (hours_ebox, SMPTE_Hours);
291  connect_scroll_event (minutes_ebox, SMPTE_Minutes);
292  connect_scroll_event (seconds_ebox, SMPTE_Seconds);
293  connect_scroll_event (frames_ebox, SMPTE_Frames);
294 
295  connect_scroll_event (audio_frames_ebox, VFrames);
296 
297  connect_scroll_event (ms_hours_ebox, MS_Hours);
298  connect_scroll_event (ms_minutes_ebox, MS_Minutes);
299  connect_scroll_event (ms_seconds_ebox, MS_Seconds);
300 
301 
302  auto connect_key_press = [=] (EventBox& guiElm, Field fieldID)
303  {
304  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_key_press_event), fieldID);
305  guiElm.signal_key_press_event().connect (handlerSlot);
306  };
307 
308  connect_key_press (hours_ebox, SMPTE_Hours);
309  connect_key_press (minutes_ebox, SMPTE_Minutes);
310  connect_key_press (seconds_ebox, SMPTE_Seconds);
311  connect_key_press (frames_ebox, SMPTE_Frames);
312 
313  connect_key_press (audio_frames_ebox, VFrames);
314 
315  connect_key_press (ms_hours_ebox, MS_Hours);
316  connect_key_press (ms_minutes_ebox, MS_Minutes);
317  connect_key_press (ms_seconds_ebox, MS_Seconds);
318 
319 
320  auto connect_key_release = [=] (EventBox& guiElm, Field fieldID)
321  {
322  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_key_release_event), fieldID);
323  guiElm.signal_key_release_event().connect (handlerSlot);
324  };
325 
326  connect_key_release (hours_ebox, SMPTE_Hours);
327  connect_key_release (minutes_ebox, SMPTE_Minutes);
328  connect_key_release (seconds_ebox, SMPTE_Seconds);
329  connect_key_release (frames_ebox, SMPTE_Frames);
330 
331  connect_key_release (audio_frames_ebox, VFrames);
332 
333  connect_key_release (ms_hours_ebox, MS_Hours);
334  connect_key_release (ms_minutes_ebox, MS_Minutes);
335  connect_key_release (ms_seconds_ebox, MS_Seconds);
336 
337 
338  auto connect_focus_gain = [=] (EventBox& guiElm, Field fieldID)
339  {
340  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_focus_gain_event), fieldID);
341  guiElm.signal_focus_in_event().connect (handlerSlot);
342  };
343 
344  connect_focus_gain (hours_ebox, SMPTE_Hours);
345  connect_focus_gain (minutes_ebox, SMPTE_Minutes);
346  connect_focus_gain (seconds_ebox, SMPTE_Seconds);
347  connect_focus_gain (frames_ebox, SMPTE_Frames);
348 
349  connect_focus_gain (audio_frames_ebox, VFrames);
350 
351  connect_focus_gain (ms_hours_ebox, MS_Hours);
352  connect_focus_gain (ms_minutes_ebox, MS_Minutes);
353  connect_focus_gain (ms_seconds_ebox, MS_Seconds);
354 
355 
356  auto connect_focus_loss = [=] (EventBox& guiElm, Field fieldID)
357  {
358  auto handlerSlot = bind (mem_fun(this, &TimeCode::field_focus_loss_event), fieldID);
359  guiElm.signal_focus_out_event().connect (handlerSlot);
360  };
361 
362  connect_focus_loss (hours_ebox, SMPTE_Hours);
363  connect_focus_loss (minutes_ebox, SMPTE_Minutes);
364  connect_focus_loss (seconds_ebox, SMPTE_Seconds);
365  connect_focus_loss (frames_ebox, SMPTE_Frames);
366 
367  connect_focus_loss (audio_frames_ebox, VFrames);
368 
369  connect_focus_loss (ms_hours_ebox, MS_Hours);
370  connect_focus_loss (ms_minutes_ebox, MS_Minutes);
371  connect_focus_loss (ms_seconds_ebox, MS_Seconds);
372 
373 
374  clock_base.signal_focus_in_event().connect(mem_fun(
375  *this, &TimeCode::drop_focus_handler));
376  }
377 
378 
379  bool
380  TimeCode::drop_focus_handler(GdkEventFocus*)
381  {
382  // Keyboard::magic_widget_drop_focus();
383  return false;
384  }
385 
386 
387  void
388  TimeCode::on_realize()
389  {
390  HBox::on_realize();
391 
392  /* styles are not available until the widgets are bound to a window */
393 
394  set_size_requests();
395  }
396 
397 
398  void
399  TimeCode::set (Time when, bool force)
400  {
401  if (!force && when == last_when) return;
402 
403  switch (_mode)
404  {
405  case SMPTE:
406  set_smpte(when, force);
407  break;
408 
409  case MinSec:
410  set_minsec(when, force);
411  break;
412 
413  case Frames:
414  set_frames(when, force);
415  break;
416 
417  case Off:
418  break;
419  }
420 
421  last_when = when;
422  }
423 
424 
425  // void
426  // TimeCode::smpte_offset_changed()
427  // {
428  // gavl_time_t current;
429 
430  // switch (_mode) {
431  // case SMPTE:
432  // // if(is_duration) {
433  // // current = current_duration();
434  // // } else {
435  // current = current_time();
436  // // }
437  // set(current, true);
438  // break;
439  // default:
440  // break;
441  // }
442  // }
443 
444 
445  void
446  TimeCode::set_frames (Time when, bool force)
447  {
449 
450  char buf[32];
451  snprintf(buf, sizeof(buf), "%u", uint(123));
452  audio_frames_label.set_text(buf);
453  }
454 
455 
456  void
457  TimeCode::set_minsec (Time when, bool force)
458  {
459  char buf[32];
461  int hrs = getHours(when);
462  int mins = getMins (when);
463  float secs = getSecs (when);
464 
465  if (force || hrs != ms_last_hrs)
466  {
467  sprintf(buf, "%02d", hrs);
468  ms_hours_label.set_text(buf);
469  ms_last_hrs = hrs;
470  }
471 
472  if (force || mins != ms_last_mins)
473  {
474  sprintf(buf, "%02d", mins);
475  ms_minutes_label.set_text(buf);
476  ms_last_mins = mins;
477  }
478 
479  if (force || secs != ms_last_secs)
480  {
481  sprintf(buf, "%06.3f", secs);
482  ms_seconds_label.set_text(buf);
483  ms_last_secs = secs;
484  }
485  }
486 
487 
488  void
489  TimeCode::set_smpte (Time when, bool force)
490  {
491  char buf[32];
493  int smpte_negative = 0; // FIXME: when < 0;
494  int smpte_hours = getHours(when);
495  int smpte_minutes = getMins(when);
496  int smpte_seconds = getSecs(when);
497  int smpte_frames = 0; //when.getFrames(framerate);
498 
499  // if (is_duration) {
500  // session->smpte_duration(when, smpte);
501  // } else {
502  // session->smpte_time(when, smpte);
503  // }
504 
505  if (force || smpte_hours != last_hrs || smpte_negative != last_negative)
506  {
507  if (smpte_negative)
508  {
509  sprintf(buf, "-%02d", smpte_hours);
510  }
511  else
512  {
513  sprintf(buf, " %02d", smpte_hours);
514  }
515  hours_label.set_text(buf);
516  last_hrs = smpte_hours;
517  last_negative = smpte_negative;
518  }
519 
520  if (force || smpte_minutes != last_mins)
521  {
522  sprintf(buf, "%02d", smpte_minutes);
523  minutes_label.set_text(buf);
524  last_mins = smpte_minutes;
525  }
526 
527  if (force || smpte_seconds != last_secs)
528  {
529  sprintf(buf, "%02d", smpte_seconds);
530  seconds_label.set_text(buf);
531  last_secs = smpte_seconds;
532  }
533 
534  if (force || smpte_frames != last_frames)
535  {
536  sprintf(buf, "%02d", smpte_frames);
537  frames_label.set_text(buf);
538  last_frames = smpte_frames;
539  }
540  }
541 
542 
543  void
544  TimeCode::focus()
545  {
546  switch (_mode)
547  {
548  case SMPTE:
549  hours_ebox.grab_focus();
550  break;
551 
552  case MinSec:
553  ms_hours_ebox.grab_focus();
554  break;
555 
556  case Frames:
557  frames_ebox.grab_focus();
558  break;
559 
560  case Off:
561  break;
562  }
563  }
564 
565 
566  bool
567  TimeCode::field_key_press_event (GdkEventKey *ev, Field field)
568  {
569  /* all key activity is handled on key release */
570  return true;
571  }
572 
573 
574  bool
575  TimeCode::field_key_release_event (GdkEventKey *ev, Field field)
576  {
577  Label *label = 0;
578  string new_text;
579  char new_char = 0;
580  bool move_on = false;
581 
582  switch (field)
583  {
584  case SMPTE_Hours:
585  label = &hours_label;
586  break;
587  case SMPTE_Minutes:
588  label = &minutes_label;
589  break;
590  case SMPTE_Seconds:
591  label = &seconds_label;
592  break;
593  case SMPTE_Frames:
594  label = &frames_label;
595  break;
596 
597  case VFrames:
598  label = &audio_frames_label;
599  break;
600 
601  case MS_Hours:
602  label = &ms_hours_label;
603  break;
604  case MS_Minutes:
605  label = &ms_minutes_label;
606  break;
607  case MS_Seconds:
608  label = &ms_seconds_label;
609  break;
610 
611  default:
612  return false;
613  }
614 
615  switch (ev->keyval) {
616 #if false
617  case GDK_0:
618  case GDK_KP_0:
619  new_char = '0';
620  break;
621  case GDK_1:
622  case GDK_KP_1:
623  new_char = '1';
624  break;
625  case GDK_2:
626  case GDK_KP_2:
627  new_char = '2';
628  break;
629  case GDK_3:
630  case GDK_KP_3:
631  new_char = '3';
632  break;
633  case GDK_4:
634  case GDK_KP_4:
635  new_char = '4';
636  break;
637  case GDK_5:
638  case GDK_KP_5:
639  new_char = '5';
640  break;
641  case GDK_6:
642  case GDK_KP_6:
643  new_char = '6';
644  break;
645  case GDK_7:
646  case GDK_KP_7:
647  new_char = '7';
648  break;
649  case GDK_8:
650  case GDK_KP_8:
651  new_char = '8';
652  break;
653  case GDK_9:
654  case GDK_KP_9:
655  new_char = '9';
656  break;
657 
658  case GDK_period:
659  case GDK_KP_Decimal:
660  if (_mode == MinSec && field == MS_Seconds) {
661  new_char = '.';
662  } else {
663  return false;
664  }
665  break;
666 
667  case GDK_Tab:
668  case GDK_Return:
669  case GDK_KP_Enter:
670  move_on = true;
671  break;
672 
673  case GDK_Escape:
674  key_entry_state = 0;
675  clock_base.grab_focus();
676  ChangeAborted(); /* EMIT SIGNAL */
677  return true;
678 #endif
679  default:
680  return false;
681  }
682 
683  if (!move_on)
684  {
685  if (key_entry_state == 0)
686  {
687  // Initialise with a fresh new string
688  if (field != VFrames)
689  {
690  for (uint xn = 0; xn < field_length[field] - 1; ++xn)
691  new_text += '0';
692  }
693  else
694  {
695  new_text = "";
696  }
697  }
698  else
699  {
700  string existing = label->get_text();
701  if (existing.length() >= field_length[field])
702  new_text = existing.substr(1, field_length[field] - 1);
703  else
704  new_text = existing.substr(0, field_length[field] - 1);
705  }
706 
707  new_text += new_char;
708  label->set_text(new_text);
709  key_entry_state++;
710  }
711 
712  move_on = (key_entry_state == field_length[field]);
713 
714  if (move_on)
715  {
716  if (key_entry_state)
717  {
718  switch (field)
719  {
720  case SMPTE_Hours:
721  case SMPTE_Minutes:
722  case SMPTE_Seconds:
723  case SMPTE_Frames:
724  // Check SMPTE fields for sanity (may also adjust fields)
725  smpte_sanitize_display();
726  break;
727  default:
728  break;
729  }
730 
731  ValueChanged(); /* EMIT_SIGNAL */
732  }
733 
734 
735  /* move on to the next field. */
736  switch (field)
737  {
738  /* SMPTE */
739 
740  case SMPTE_Hours:
741  minutes_ebox.grab_focus();
742  break;
743  case SMPTE_Minutes:
744  seconds_ebox.grab_focus();
745  break;
746  case SMPTE_Seconds:
747  frames_ebox.grab_focus();
748  break;
749  case SMPTE_Frames:
750  clock_base.grab_focus();
751  break;
752 
753  /* frames */
754 
755  case VFrames:
756  clock_base.grab_focus();
757  break;
758 
759  /* Min:Sec */
760 
761  case MS_Hours:
762  ms_minutes_ebox.grab_focus();
763  break;
764  case MS_Minutes:
765  ms_seconds_ebox.grab_focus();
766  break;
767  case MS_Seconds:
768  clock_base.grab_focus();
769  break;
770 
771  default:
772  break;
773  }
774  }//if (move_on)
775 
776 #if false
777  //if user hit Enter, lose focus
778  switch (ev->keyval) {
779  case GDK_Return:
780  case GDK_KP_Enter:
781  clock_base.grab_focus();
782  }
783 #endif
784  return true;
785  }
786 
787 
788  bool
789  TimeCode::field_focus_gain_event (GdkEventFocus*, Field field)
790  {
791  key_entry_state = 0;
792 
793  // Keyboard::magic_widget_grab_focus();
794 
795  switch (field) {
796  case SMPTE_Hours:
797  //hours_ebox.set_flags(Gtk::HAS_FOCUS);
798  hours_ebox.set_state(Gtk::STATE_ACTIVE);
799  break;
800  case SMPTE_Minutes:
801  //minutes_ebox.set_flags(Gtk::HAS_FOCUS);
802  minutes_ebox.set_state(Gtk::STATE_ACTIVE);
803  break;
804  case SMPTE_Seconds:
805  //seconds_ebox.set_flags(Gtk::HAS_FOCUS);
806  seconds_ebox.set_state(Gtk::STATE_ACTIVE);
807  break;
808  case SMPTE_Frames:
809  //frames_ebox.set_flags(Gtk::HAS_FOCUS);
810  frames_ebox.set_state(Gtk::STATE_ACTIVE);
811  break;
812 
813  case VFrames:
815  audio_frames_ebox.set_state(Gtk::STATE_ACTIVE);
816  break;
817 
818  case MS_Hours:
819  //ms_hours_ebox.set_flags(Gtk::HAS_FOCUS);
820  ms_hours_ebox.set_state(Gtk::STATE_ACTIVE);
821  break;
822  case MS_Minutes:
823  //ms_minutes_ebox.set_flags(Gtk::HAS_FOCUS);
824  ms_minutes_ebox.set_state(Gtk::STATE_ACTIVE);
825  break;
826  case MS_Seconds:
827  //ms_seconds_ebox.set_flags(Gtk::HAS_FOCUS);
828  ms_seconds_ebox.set_state(Gtk::STATE_ACTIVE);
829  break;
830  }
831 
832  return false;
833  }
834 
835 
836  bool
837  TimeCode::field_focus_loss_event (GdkEventFocus*, Field field)
838  {
839  switch (field) {
840 
841  case SMPTE_Hours:
842  //hours_ebox.unset_flags(Gtk::HAS_FOCUS);
843  hours_ebox.set_state(Gtk::STATE_NORMAL);
844  break;
845  case SMPTE_Minutes:
846  //minutes_ebox.unset_flags(Gtk::HAS_FOCUS);
847  minutes_ebox.set_state(Gtk::STATE_NORMAL);
848  break;
849  case SMPTE_Seconds:
850  //seconds_ebox.unset_flags(Gtk::HAS_FOCUS);
851  seconds_ebox.set_state(Gtk::STATE_NORMAL);
852  break;
853  case SMPTE_Frames:
854  //frames_ebox.unset_flags(Gtk::HAS_FOCUS);
855  frames_ebox.set_state(Gtk::STATE_NORMAL);
856  break;
857 
858  case VFrames:
859  //audio_frames_ebox.unset_flags(Gtk::HAS_FOCUS);
860  audio_frames_ebox.set_state(Gtk::STATE_NORMAL);
861  break;
862 
863  case MS_Hours:
864  //ms_hours_ebox.unset_flags(Gtk::HAS_FOCUS);
865  ms_hours_ebox.set_state(Gtk::STATE_NORMAL);
866  break;
867  case MS_Minutes:
868  //ms_minutes_ebox.unset_flags(Gtk::HAS_FOCUS);
869  ms_minutes_ebox.set_state(Gtk::STATE_NORMAL);
870  break;
871  case MS_Seconds:
872  //ms_seconds_ebox.unset_flags(Gtk::HAS_FOCUS);
873  ms_seconds_ebox.set_state(Gtk::STATE_NORMAL);
874  break;
875  }
876 
877  // Keyboard::magic_widget_drop_focus();
878 
879  return false;
880  }
881 
882 
883  bool
884  TimeCode::field_button_release_event (GdkEventButton *ev, Field field)
885  {
886  if (dragging)
887  {
888  gdk_pointer_ungrab(GDK_CURRENT_TIME);
889  dragging = false;
890  if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1
891  || (ev->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
892  {
893  // we actually dragged so return without setting editing focus, or we shift clicked
894  return true;
895  }
896  }
897 
898  if (!editable)
899  {
900  if (ops_menu == 0) {
901  build_ops_menu();
902  }
903  ops_menu->popup(1, ev->time);
904  return true;
905  }
906 
907  switch (ev->button)
908  {
909  case 1:
910  switch (field)
911  {
912  case SMPTE_Hours:
913  hours_ebox.grab_focus();
914  break;
915  case SMPTE_Minutes:
916  minutes_ebox.grab_focus();
917  break;
918  case SMPTE_Seconds:
919  seconds_ebox.grab_focus();
920  break;
921  case SMPTE_Frames:
922  frames_ebox.grab_focus();
923  break;
924 
925  case VFrames:
926  audio_frames_ebox.grab_focus();
927  break;
928 
929  case MS_Hours:
930  ms_hours_ebox.grab_focus();
931  break;
932  case MS_Minutes:
933  ms_minutes_ebox.grab_focus();
934  break;
935  case MS_Seconds:
936  ms_seconds_ebox.grab_focus();
937  break;
938  }
939  break;
940 
941  case 3:
942  if (ops_menu == 0) {
943  build_ops_menu();
944  }
945  ops_menu->popup(1, ev->time);
946  return true;
947 
948  default:
949  break;
950  }
951 
952  return true;
953  }
954 
955 
956  bool
957  TimeCode::field_button_press_event (GdkEventButton *ev, Field field)
958  {
959  return false;
960  // if (session == 0) return false;
961  // Time frames = 0;
962 
963  switch (ev->button)
964  {
965  case 1:
966  // if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
967  // set (frames, true);
968  // ValueChanged (); /* EMIT_SIGNAL */
969  // }
970 
971  /* make absolutely sure that the pointer is grabbed */
972  gdk_pointer_grab(ev->window,false ,
973  GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
974  NULL,NULL,ev->time);
975  dragging = true;
976  drag_accum = 0;
977  drag_start_y = ev->y;
978  drag_y = ev->y;
979  break;
980 
981  case 2:
982  // if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
983  // set (frames, true);
984  // ValueChanged (); /* EMIT_SIGNAL */
985  // }
986  break;
987 
988  case 3:
989  /* used for context sensitive menu */
990  return false;
991  break;
992 
993  default:
994  return false;
995  break;
996  }
997  return true;
998  }
999 
1000  bool
1001  TimeCode::field_button_scroll_event (GdkEventScroll *ev, Field field)
1002  {
1003  return false;
1004 
1005  // if (session == 0) {
1006  // return false;
1007  // }
1008 
1009  // Time frames = 0;
1010 
1011  switch (ev->direction) {
1012 
1013  case GDK_SCROLL_UP:
1014  // frames = get_frames (field);
1015  // if (frames != 0) {
1016  // // if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1017  // // frames *= 10;
1018  // // }
1019  // set (current_time() + frames, true);
1020  // ValueChanged (); /* EMIT_SIGNAL */
1021  // }
1022  break;
1023 
1024  case GDK_SCROLL_DOWN:
1025  // frames = get_frames (field);
1026  // if (frames != 0) {
1027  // // if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1028  // // frames *= 10;
1029  // // }
1030 
1031  // if ((double)current_time() - (double)frames < 0.0) {
1032  // set (0, true);
1033  // } else {
1034  // set (current_time() - frames, true);
1035  // }
1036 
1037  // ValueChanged (); /* EMIT_SIGNAL */
1038  // }
1039  break;
1040 
1041  default:
1042  return false;
1043  break;
1044  }
1045 
1046  return true;
1047  }
1048 
1049 
1050  bool
1051  TimeCode::field_motion_notify_event (GdkEventMotion *ev, Field field)
1052  {
1053  if (!dragging) return false;
1054 
1055  float pixel_frame_scale_factor = 0.2f;
1056 
1057  /*
1058  if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1059  pixel_frame_scale_factor = 0.1f;
1060  }
1061 
1062  if (Keyboard::modifier_state_contains (ev->state,
1063  Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) {
1064 
1065  pixel_frame_scale_factor = 0.025f;
1066  }
1067  */
1068  double y_delta = ev->y - drag_y;
1069 
1070  drag_accum += y_delta*pixel_frame_scale_factor;
1071 
1072  drag_y = ev->y;
1073 
1074  if (trunc(drag_accum) != 0)
1075  {
1076  int frames;
1077  TimeVar pos(current_time());
1078  int dir;
1079  dir = (drag_accum < 0 ? 1:-1);
1080  frames = get_frames(field,pos,dir);
1083 
1084  if (frames != 0 && frames * drag_accum < (_raw(current_time())))
1085  {
1086  // minus because up is negative in computer-land
1087  pos = TimeValue (floor (pos - drag_accum * frames));
1088  set (pos, false);
1089  }
1090  else
1091  {
1092  set (Time::ZERO, false);
1093  }
1094 
1095  drag_accum = 0;
1096  ValueChanged(); /* EMIT_SIGNAL */
1097  }
1098 
1099  return true;
1100  }
1101 
1102 
1103  int
1104  TimeCode::get_frames (Field field, Time pos, int dir)
1105  {
1107  int frames = 0;
1108  // switch (field)
1109  // {
1110  // case SMPTE_Hours:
1111  // frames = (Time) floor (3600.0 * session->frame_rate());
1112  // break;
1113  // case SMPTE_Minutes:
1114  // frames = (Time) floor (60.0 * session->frame_rate());
1115  // break;
1116  // case SMPTE_Seconds:
1117  // frames = session->frame_rate();
1118  // break;
1119  // case SMPTE_Frames:
1120  // frames = (Time) floor (session->frame_rate() / session->smpte_frames_per_second());
1121  // break;
1122 
1123  // case VFrames:
1124  // frames = 1;
1125  // break;
1126 
1127  // case MS_Hours:
1128  // frames = (Time) floor (3600.0 * session->frame_rate());
1129  // break;
1130  // case MS_Minutes:
1131  // frames = (Time) floor (60.0 * session->frame_rate());
1132  // break;
1133  // case MS_Seconds:
1134  // frames = session->frame_rate();
1135  // break;
1136  // }
1137 
1138  return frames;
1139  }
1140 
1141 
1142  Time
1143  TimeCode::current_time (Time pos) const
1144  {
1146  TimeVar ret;
1147 
1148  switch (_mode)
1149  {
1150  case SMPTE:
1151  ret = smpte_time_from_display();
1152  break;
1153 
1154  case MinSec:
1155  ret = minsec_time_from_display();
1156  break;
1157 
1158  case Frames:
1159  ret = audio_time_from_display();
1160  break;
1161 
1162  case Off:
1163  break;
1164  }
1165 
1166  return ret;
1167  }
1168 
1169 
1170  Time
1171  TimeCode::current_duration (Time pos) const
1172  {
1174  TimeVar ret;
1175 
1176  switch (_mode)
1177  {
1178  case SMPTE:
1179  ret = smpte_time_from_display();
1180  break;
1181 
1182  case MinSec:
1183  ret = minsec_time_from_display();
1184  break;
1185 
1186  case Frames:
1187  ret = audio_time_from_display();
1188  break;
1189 
1190  case Off:
1191  break;
1192  }
1193 
1194  return ret;
1195  }
1196 
1197 
1198  void
1199  TimeCode::smpte_sanitize_display()
1200  {
1202 
1203 
1204  // Check SMPTE fields for sanity, possibly adjusting values
1205  if (59 < lexical_cast<int>(minutes_label.get_text()))
1206  {
1207  minutes_label.set_text("59");
1208  }
1209 
1210  if (59 < lexical_cast<int>(seconds_label.get_text()))
1211  {
1212  seconds_label.set_text("59");
1213  }
1214 
1215  if (framerate - 1 < lexical_cast<int>(frames_label.get_text()))
1216  {
1217  char buf[32];
1218  sprintf(buf, "%02d", int(framerate - 1));
1219  frames_label.set_text(buf);
1220  }
1221 
1223 
1224  // if (session->smpte_drop_frames()) {
1225  // if ((atoi(minutes_label.get_text()) % 10) && (atoi(seconds_label.get_text()) == 0) && (atoi(frames_label.get_text()) < 2)) {
1226  // frames_label.set_text("02");
1227  // }
1228  // }
1229  }
1230 
1231 
1232  Time
1233  TimeCode::smpte_time_from_display() const
1234  {
1236 
1237  // SMPTE::Time smpte;
1238  // Time sample;
1239 
1240  // smpte.hours = atoi (hours_label.get_text());
1241  // smpte.minutes = atoi (minutes_label.get_text());
1242  // smpte.seconds = atoi (seconds_label.get_text());
1243  // smpte.frames = atoi (frames_label.get_text());
1244  // smpte.rate = session->smpte_frames_per_second();
1245  // smpte.drop= session->smpte_drop_frames();
1246 
1247  // session->smpte_to_sample(smpte, sample, false /* use_offset */, false /* use_subframes */ );
1248 
1249  return Time::ZERO;
1250  }
1251 
1252 
1253  Time
1254  TimeCode::minsec_time_from_display () const
1255  {
1257 
1258  // int hrs = atoi (ms_hours_label.get_text());
1259  // int mins = atoi (ms_minutes_label.get_text());
1260  // float secs = atof (ms_seconds_label.get_text());
1261 
1262  // Time sr = session->frame_rate();
1263 
1264  // return (Time) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr));
1265 
1266  return Time::ZERO;
1267  }
1268 
1269 
1270  Time
1271  TimeCode::audio_time_from_display () const
1272  {
1273  gavl_time_t parsedAudioFrames = lexical_cast<int>(audio_frames_label.get_text());
1274  return Time(TimeValue(parsedAudioFrames));
1275  }
1276 
1277 
1278  void
1279  TimeCode::build_ops_menu ()
1280  {
1281 #if false
1282  using namespace Menu_Helpers;
1283  ops_menu = new Menu;
1284  MenuList& ops_items = ops_menu->items();
1285  ops_menu->set_name ("LumieraContextMenu");
1286 
1287  ops_items.push_back (MenuElem ("SMPTE", bind (mem_fun(*this, &TimeCode::set_mode), SMPTE)));
1288  ops_items.push_back (MenuElem ("Minutes:Seconds", bind (mem_fun(*this, &TimeCode::set_mode), MinSec)));
1289  ops_items.push_back (MenuElem ("Frames", bind (mem_fun(*this, &TimeCode::set_mode), Frames)));
1290  ops_items.push_back (MenuElem ("Off", bind (mem_fun(*this, &TimeCode::set_mode), Off)));
1291 #endif
1292  }
1293 
1294 
1295  void
1296  TimeCode::set_mode(Mode m)
1297  {
1298  /* slightly tricky: this is called from within the ARDOUR_UI
1299  constructor by some of its clock members. at that time
1300  the instance pointer is unset, so we have to be careful.
1301  the main idea is to drop keyboard focus in case we had
1302  started editing the clock and then we switch clock mode.
1303  */
1304 
1305  clock_base.grab_focus();
1306 
1307  if (_mode == m)
1308  return;
1309 
1310  clock_base.remove();
1311 
1312  _mode = m;
1313 
1314  switch (_mode)
1315  {
1316  case SMPTE:
1317  clock_base.add(smpte_packer_hbox);
1318  break;
1319 
1320  case MinSec:
1321  clock_base.add(minsec_packer_hbox);
1322  break;
1323 
1324  case Frames:
1325  clock_base.add(frames_packer_hbox);
1326  break;
1327 
1328  case Off:
1329  clock_base.add(off_hbox);
1330  break;
1331  }
1332 
1333  set_size_requests();
1334 
1335  set(last_when, true);
1336  clock_base.show_all();
1337  key_entry_state = 0;
1338 
1339  ModeChanged(); /* EMIT SIGNAL */
1340  }
1341 
1342 
1343  void
1344  TimeCode::set_size_requests()
1345  {
1346  /* note that in some fonts, "88" is narrower than "00", hence the 2 pixel padding */
1347 
1348  switch (_mode) {
1349  case SMPTE:
1350  set_size_request_to_display_given_text(hours_label, "-00", 5, 5);
1351  set_size_request_to_display_given_text(minutes_label, "00", 5, 5);
1352  set_size_request_to_display_given_text(seconds_label, "00", 5, 5);
1353  set_size_request_to_display_given_text(frames_label, "00", 5, 5);
1354  break;
1355 
1356  case MinSec:
1357  set_size_request_to_display_given_text(ms_hours_label, "00", 5, 5);
1358  set_size_request_to_display_given_text(ms_minutes_label, "00", 5, 5);
1359  set_size_request_to_display_given_text(ms_seconds_label, "00.000", 5, 5);
1360  break;
1361 
1362  case Frames:
1363  set_size_request_to_display_given_text(audio_frames_label, "0000000000", 5, 5);
1364  break;
1365 
1366  case Off:
1367  set_size_request_to_display_given_text(off_hbox, "00000", 5, 5);
1368  break;
1369 
1370  }
1371  }
1372 
1373 
1374  void
1375  TimeCode::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
1376  gint hpadding, gint vpadding)
1377  {
1378  int width, height;
1379  //w.ensure_style();
1380 
1381  get_ink_pixel_size(w.create_pango_layout(text), width, height);
1382  w.set_size_request(width + hpadding, height + vpadding);
1383  }
1384 
1385 
1386  void
1387  TimeCode::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
1388  int& width, int& height)
1389  {
1390  Pango::Rectangle ink_rect = layout->get_ink_extents ();
1391 
1392  width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
1393  height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
1394  }
1395 
1396 
1397 
1398 }}// stage::widget
a mutable time value, behaving like a plain number, allowing copy and re-accessing ...
Definition: timevalue.hpp:232
Extension to the lib::time::Time wrapper, adding output and further diagnostic aids.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:299
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:37
Widget for timecode display and input.
basic constant internal time value.
Definition: timevalue.hpp:133
ElementBoxWidget::Config::Qualifier name(string id)
define the name-ID displayed in the caption