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