1 /*
2 * Copyright (C) 2018-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <stdlib.h>
21 #include <math.h>
22
23 #include "audio/control_port.h"
24 #include "audio/position.h"
25 #include "audio/quantize_options.h"
26 #include "audio/router.h"
27 #include "audio/snap_grid.h"
28 #include "audio/tempo_track.h"
29 #include "audio/transport.h"
30 #include "gui/backend/event.h"
31 #include "gui/backend/event_manager.h"
32 #include "gui/widgets/bot_dock_edge.h"
33 #include "gui/widgets/center_dock.h"
34 #include "gui/widgets/digital_meter.h"
35 #include "gui/widgets/main_window.h"
36 #include "gui/widgets/midi_arranger.h"
37 #include "gui/widgets/midi_modifier_arranger.h"
38 #include "gui/widgets/editor_ruler.h"
39 #include "gui/widgets/ruler.h"
40 #include "gui/widgets/timeline_arranger.h"
41 #include "gui/widgets/timeline_ruler.h"
42 #include "project.h"
43 #include "settings/settings.h"
44 #include "utils/cairo.h"
45 #include "utils/error.h"
46 #include "utils/flags.h"
47 #include "utils/gtk.h"
48 #include "zrythm.h"
49 #include "zrythm_app.h"
50
51 #include <gtk/gtk.h>
52
53 #include <glib/gi18n.h>
54
G_DEFINE_TYPE(DigitalMeterWidget,digital_meter_widget,GTK_TYPE_DRAWING_AREA)55 G_DEFINE_TYPE (
56 DigitalMeterWidget,
57 digital_meter_widget,
58 GTK_TYPE_DRAWING_AREA)
59
60 #define FONT_SIZE 16
61 /*#define SEG7_FONT "Segment7 Bold 16"*/
62 #define SEG7_FONT "DSEG14 Classic Mini Italic 12"
63 #define NORMAL_FONT "sans-serif Bold 11"
64 #define CAPTION_FONT "sans-serif 7"
65 #define SPACE_BETWEEN 6
66 #define HALF_SPACE_BETWEEN 2
67 #define PADDING_W 4
68 #define PADDING_TOP 0
69
70 #define DISPLAY_TIME \
71 (self->is_transport && \
72 g_settings_get_enum ( \
73 S_UI, "transport-display") == \
74 TRANSPORT_DISPLAY_TIME)
75
76 #define SET_POS \
77 ((*self->setter) (self->obj, &pos))
78 #define GET_POS \
79 ((*self->getter) (self->obj, &pos))
80
81
82 static gboolean
83 digital_meter_draw_cb (
84 GtkWidget * widget,
85 cairo_t *cr,
86 DigitalMeterWidget * self)
87 {
88 if (!PROJECT->loaded)
89 return FALSE;
90
91 g_return_val_if_fail (
92 Z_IS_DIGITAL_METER_WIDGET (self) &&
93 PANGO_IS_LAYOUT (self->caption_layout) &&
94 PANGO_IS_LAYOUT (self->seg7_layout) &&
95 PANGO_IS_LAYOUT (self->normal_layout),
96 FALSE);
97
98 GtkStyleContext *context =
99 gtk_widget_get_style_context (widget);
100 int width =
101 gtk_widget_get_allocated_width (widget);
102 int height =
103 gtk_widget_get_allocated_height (widget);
104
105 gtk_render_background (
106 context, cr, 0, 0, width, height);
107
108 /* draw caption and get its extents */
109 int caption_textw, caption_texth;
110 if (gtk_widget_is_sensitive (GTK_WIDGET (self)))
111 {
112 cairo_set_source_rgba (
113 cr, 1.0, 1.0, 1.0, 1.0);
114 }
115 else
116 {
117 cairo_set_source_rgba (
118 cr, 0.6, 0.6, 0.6, 1.0);
119 }
120 z_cairo_get_text_extents_for_widget (
121 self, self->caption_layout, self->caption,
122 &caption_textw, &caption_texth);
123 z_cairo_draw_text_full (
124 cr, widget, self->caption_layout,
125 self->caption,
126 width / 2 - caption_textw / 2,
127 PADDING_TOP);
128 /* uncomment to make text slightly thickerr */
129 /*z_cairo_draw_text_full (*/
130 /*cr, self->caption, width / 2 - caption_textw / 2,*/
131 /*PADDING_TOP, CAPTION_FONT);*/
132
133 /* draw line */
134 if (self->draw_line)
135 {
136 cairo_set_line_width (cr, 1.0);
137 cairo_move_to (
138 cr, 0,
139 caption_texth +
140 PADDING_TOP);
141 cairo_line_to (
142 cr, width,
143 caption_texth +
144 PADDING_TOP);
145 cairo_stroke (cr);
146 }
147
148 /*GdkRGBA color;*/
149 /*gdk_rgba_parse (&color, "#D68A0C");*/
150 /*gdk_cairo_set_source_rgba (cr, &color);*/
151 if (gtk_widget_is_sensitive (GTK_WIDGET (self)))
152 {
153 cairo_set_source_rgba (
154 cr, 0.0, 1.0, 0.1, 1.0);
155 }
156 else
157 {
158 cairo_set_source_rgba (
159 cr, 0.0, 0.6, 0.06, 1.0);
160 }
161 char text[20];
162 char * heap_text = NULL;
163 int num_part, dec_part, bars, beats, sixteenths, ticks;
164 int textw, texth;
165 Position pos;
166 switch (self->type)
167 {
168 case DIGITAL_METER_TYPE_BPM:
169
170 num_part =
171 (int)
172 tempo_track_get_current_bpm (P_TEMPO_TRACK);
173 dec_part =
174 (int)
175 (tempo_track_get_current_bpm (
176 P_TEMPO_TRACK) * 100) % 100;
177
178 z_cairo_get_text_extents_for_widget (
179 widget, self->seg7_layout, "88888",
180 &textw, &texth);
181 self->num_part_start_pos =
182 ((width / 2) - textw / 2) -
183 HALF_SPACE_BETWEEN;
184 z_cairo_get_text_extents_for_widget (
185 widget, self->seg7_layout, "888",
186 &textw, &texth);
187 self->num_part_end_pos =
188 self->num_part_start_pos + textw;
189 self->dec_part_start_pos =
190 self->num_part_end_pos + SPACE_BETWEEN;
191 z_cairo_get_text_extents_for_widget (
192 widget, self->seg7_layout, "88",
193 &textw, &texth);
194 self->dec_part_end_pos =
195 self->dec_part_start_pos + textw;
196 self->height_start_pos =
197 PADDING_TOP +
198 caption_texth + HALF_SPACE_BETWEEN;
199 self->height_end_pos =
200 self->height_start_pos + texth;
201
202 /* draw integer part */
203 if (num_part < 100)
204 sprintf (text, "!%d.", num_part);
205 else
206 sprintf (text, "%d.", num_part);
207 z_cairo_draw_text_full (
208 cr, widget, self->seg7_layout, text,
209 self->num_part_start_pos,
210 self->height_start_pos);
211
212 /* draw decimal part */
213 if (dec_part < 10)
214 sprintf (text, "0%d", dec_part);
215 else
216 sprintf (text, "%d", dec_part);
217 z_cairo_draw_text_full (
218 cr, widget, self->seg7_layout, text,
219 self->dec_part_start_pos,
220 self->height_start_pos);
221
222 break;
223 case DIGITAL_METER_TYPE_POSITION:
224
225 GET_POS;
226 if (DISPLAY_TIME)
227 {
228 long ms = position_to_ms (&pos);
229 long secs = ms / 1000;
230 int mins = (int) secs / 60;
231 ms = ms % 1000;
232 secs = secs % 60;
233
234 z_cairo_get_text_extents_for_widget (
235 widget, self->seg7_layout,
236 /* MM:SS:ms 1 for each digit */
237 "888888888", &textw, &texth);
238 self->minutes_start_pos =
239 ((width / 2) - textw / 2) -
240 HALF_SPACE_BETWEEN * 3;
241 z_cairo_get_text_extents_for_widget (
242 widget, self->seg7_layout, "88",
243 &textw, &texth);
244 self->minutes_end_pos =
245 self->minutes_start_pos + textw;
246 self->seconds_start_pos =
247 self->minutes_end_pos + SPACE_BETWEEN;
248 z_cairo_get_text_extents_for_widget (
249 widget, self->seg7_layout, "88",
250 &textw, &texth);
251 self->seconds_end_pos =
252 self->seconds_start_pos + textw;
253 self->ms_start_pos =
254 self->seconds_end_pos + SPACE_BETWEEN;
255 z_cairo_get_text_extents_for_widget (
256 widget, self->seg7_layout, "888",
257 &textw, &texth);
258 self->ms_end_pos =
259 self->ms_start_pos + textw;
260
261 self->height_start_pos =
262 PADDING_TOP +
263 caption_texth + HALF_SPACE_BETWEEN;
264 self->height_end_pos =
265 self->height_start_pos + texth;
266
267 /* draw minutes */
268 if (mins < 10)
269 sprintf (text, "!%d.", mins);
270 else
271 sprintf (text, "%d.", mins);
272 z_cairo_draw_text_full (
273 cr, widget, self->seg7_layout, text,
274 self->minutes_start_pos,
275 self->height_start_pos);
276
277 /* draw seconds */
278 if (secs < 10)
279 sprintf (text, "0%ld.", secs);
280 else
281 sprintf (text, "%ld.", secs);
282 z_cairo_draw_text_full (
283 cr, widget, self->seg7_layout, text,
284 self->seconds_start_pos,
285 self->height_start_pos);
286
287 /* draw ms */
288 if (ms < 10)
289 sprintf (text, "00%ld", ms);
290 else if (ms < 100)
291 sprintf (text, "0%ld", ms);
292 else
293 sprintf (text, "%ld", ms);
294 z_cairo_draw_text_full (
295 cr, widget, self->seg7_layout, text,
296 self->ms_start_pos,
297 self->height_start_pos);
298 }
299 else
300 {
301 bars = position_get_bars (&pos, true);
302 beats = position_get_beats (&pos, true);
303 sixteenths =
304 position_get_sixteenths (&pos, true);
305 ticks =
306 (int) floor (position_get_ticks (&pos));
307
308 z_cairo_get_text_extents_for_widget (
309 widget, self->seg7_layout,
310 "-8888888888", &textw, &texth);
311 self->bars_start_pos =
312 ((width / 2) - textw / 2) -
313 HALF_SPACE_BETWEEN * 3;
314 z_cairo_get_text_extents_for_widget (
315 widget, self->seg7_layout, "-888",
316 &textw, &texth);
317 self->bars_end_pos =
318 self->bars_start_pos + textw;
319 self->beats_start_pos =
320 self->bars_end_pos + SPACE_BETWEEN;
321 z_cairo_get_text_extents_for_widget (
322 widget, self->seg7_layout, "8",
323 &textw, &texth);
324 self->beats_end_pos =
325 self->beats_start_pos + textw;
326 self->sixteenths_start_pos =
327 self->beats_end_pos + SPACE_BETWEEN;
328 self->sixteenths_end_pos =
329 self->sixteenths_start_pos + textw;
330 self->ticks_start_pos =
331 self->sixteenths_end_pos + SPACE_BETWEEN;
332 z_cairo_get_text_extents_for_widget (
333 widget, self->seg7_layout, "888",
334 &textw, &texth);
335 self->ticks_end_pos =
336 self->ticks_start_pos + textw;
337 self->height_start_pos =
338 PADDING_TOP +
339 caption_texth + HALF_SPACE_BETWEEN;
340 self->height_end_pos =
341 self->height_start_pos + texth;
342
343 if (bars < -100)
344 sprintf (text, "%d", bars);
345 else if (bars < -10)
346 sprintf (text, "!%d", bars);
347 else if (bars < 0)
348 sprintf (text, "!!%d", bars);
349 else if (bars < 10)
350 sprintf (text, "!!!%d", bars);
351 else if (bars < 100)
352 sprintf (text, "!!%d", bars);
353 else
354 sprintf (text, "!%d", bars);
355 strcat (text, ".");
356 z_cairo_draw_text_full (
357 cr, widget, self->seg7_layout, text,
358 self->bars_start_pos,
359 self->height_start_pos);
360
361 sprintf (text, "%d.", abs (beats));
362 z_cairo_draw_text_full (
363 cr, widget, self->seg7_layout, text,
364 self->beats_start_pos,
365 self->height_start_pos);
366
367 sprintf (text, "%d.", abs (sixteenths));
368 z_cairo_draw_text_full (
369 cr, widget, self->seg7_layout, text,
370 self->sixteenths_start_pos,
371 self->height_start_pos);
372
373 if (abs (ticks) < 10)
374 sprintf (text, "00%d", abs (ticks));
375 else if (abs (ticks) < 100)
376 sprintf (text, "0%d", abs (ticks));
377 else
378 sprintf (text, "%d", abs (ticks));
379 z_cairo_draw_text_full (
380 cr, widget, self->seg7_layout, text,
381 self->ticks_start_pos,
382 self->height_start_pos);
383 }
384 break;
385 case DIGITAL_METER_TYPE_NOTE_LENGTH:
386 heap_text =
387 snap_grid_stringize (
388 *self->note_length,
389 *self->note_type);
390 z_cairo_get_text_extents_for_widget (
391 widget, self->seg7_layout, heap_text,
392 &textw, &texth);
393 self->height_start_pos =
394 PADDING_TOP +
395 caption_texth + HALF_SPACE_BETWEEN;
396 self->height_end_pos =
397 self->height_start_pos + texth;
398 z_cairo_draw_text_full (
399 cr, widget, self->seg7_layout,
400 heap_text, width / 2 - textw / 2,
401 self->height_start_pos);
402 g_free (heap_text);
403
404 break;
405 case DIGITAL_METER_TYPE_NOTE_TYPE:
406 switch (*self->note_type)
407 {
408 case NOTE_TYPE_NORMAL:
409 heap_text = _("normal");
410 break;
411 case NOTE_TYPE_DOTTED:
412 heap_text = _("dotted");
413 break;
414 case NOTE_TYPE_TRIPLET:
415 heap_text = _("triplet");
416 break;
417 }
418 z_cairo_get_text_extents_for_widget (
419 widget, self->seg7_layout, heap_text,
420 &textw, &texth);
421 self->height_start_pos =
422 PADDING_TOP +
423 caption_texth + HALF_SPACE_BETWEEN;
424 self->height_end_pos =
425 self->height_start_pos + texth;
426 z_cairo_draw_text_full (
427 cr, widget, self->seg7_layout, heap_text,
428 width / 2 - textw / 2,
429 self->height_start_pos);
430
431 break;
432 case DIGITAL_METER_TYPE_TIMESIG:
433
434 z_cairo_get_text_extents_for_widget (
435 widget, self->seg7_layout, "16/16",
436 &textw, &texth);
437 self->height_start_pos =
438 PADDING_TOP +
439 caption_texth + HALF_SPACE_BETWEEN;
440 self->height_end_pos =
441 self->height_start_pos + texth;
442
443 BeatUnit bu =
444 tempo_track_get_beat_unit_enum (
445 P_TEMPO_TRACK);
446 const char * beat_unit =
447 beat_unit_strings[bu].str;
448 int beats_per_bar =
449 tempo_track_get_beats_per_bar (
450 P_TEMPO_TRACK);
451 if (beats_per_bar < 10)
452 {
453 text[0] = ' ';
454 text[1] =
455 (char) (beats_per_bar + '0');
456 }
457 else
458 {
459 text[0] =
460 (char) ((beats_per_bar / 10) + '0');
461 text[1] =
462 (char) ((beats_per_bar % 10) + '0');
463 }
464 text[2] = '\0';
465 heap_text =
466 g_strdup_printf ("%s/%s", text, beat_unit);
467 z_cairo_draw_text_full (
468 cr, widget, self->seg7_layout, heap_text,
469 width / 2 - textw / 2,
470 self->height_start_pos);
471 g_free (heap_text);
472
473 break;
474 }
475
476 return FALSE;
477 }
478
479 /**
480 * Updates the flags to know what to update when
481 * scrolling/dragging.
482 */
483 static void
update_flags(DigitalMeterWidget * self,double x,double y)484 update_flags (
485 DigitalMeterWidget * self,
486 double x,
487 double y)
488 {
489 int width =
490 gtk_widget_get_allocated_width (
491 GTK_WIDGET (self));
492 switch (self->type)
493 {
494 case DIGITAL_METER_TYPE_BPM:
495 if (y >= self->height_start_pos &&
496 y <= self->height_end_pos)
497 {
498 if (x >= self->num_part_start_pos &&
499 x <= self->num_part_end_pos)
500 {
501 self->update_num = 1;
502 }
503 else if (x >= self->dec_part_start_pos &&
504 x <= self->dec_part_end_pos)
505 {
506 self->update_dec = 1;
507 }
508 }
509 break;
510
511 case DIGITAL_METER_TYPE_POSITION:
512 if (y >= self->height_start_pos &&
513 y <= self->height_end_pos)
514 {
515 if (DISPLAY_TIME)
516 {
517 if (x >= self->minutes_start_pos &&
518 x <= self->minutes_end_pos)
519 {
520 self->update_minutes = 1;
521 }
522 else if (x >= self->seconds_start_pos &&
523 x <= self->seconds_end_pos)
524 {
525 self->update_seconds = 1;
526 }
527 else if (x >= self->ms_start_pos &&
528 x <= self->ms_end_pos)
529 {
530 self->update_ms = 1;
531 }
532 }
533 else
534 {
535 if (x >= self->bars_start_pos &&
536 x <= self->bars_end_pos)
537 {
538 self->update_bars = 1;
539 }
540 else if (x >= self->beats_start_pos &&
541 x <= self->beats_end_pos)
542 {
543 self->update_beats = 1;
544 }
545 else if (x >= self->sixteenths_start_pos &&
546 x <= self->sixteenths_end_pos)
547 {
548 self->update_sixteenths = 1;
549 }
550 else if (x >= self->ticks_start_pos &&
551 x <= self->ticks_end_pos)
552 {
553 self->update_ticks = 1;
554 }
555 }
556 }
557
558 break;
559 case DIGITAL_METER_TYPE_NOTE_LENGTH:
560 self->update_note_length = 1;
561 break;
562 case DIGITAL_METER_TYPE_NOTE_TYPE:
563 self->update_note_type = 1;
564 break;
565 case DIGITAL_METER_TYPE_TIMESIG:
566 if (x <= width / 2)
567 {
568 self->update_timesig_top = 1;
569 }
570 else
571 {
572 self->update_timesig_bot = 1;
573 }
574 break;
575 }
576 }
577
578 /**
579 * To be called when a change has started (eg
580 * drag or scroll).
581 */
582 static void
on_change_started(DigitalMeterWidget * self)583 on_change_started (
584 DigitalMeterWidget * self)
585 {
586 switch (self->type)
587 {
588 case DIGITAL_METER_TYPE_NOTE_LENGTH:
589 self->start_note_length =
590 (NoteLength)
591 *self->note_length;
592 break;
593 case DIGITAL_METER_TYPE_NOTE_TYPE:
594 self->start_note_type =
595 (NoteType)
596 *self->note_type;
597 break;
598 case DIGITAL_METER_TYPE_TIMESIG:
599 self->beats_per_bar_at_start =
600 tempo_track_get_beats_per_bar (
601 P_TEMPO_TRACK);
602 self->beat_unit_at_start =
603 tempo_track_get_beat_unit (P_TEMPO_TRACK);
604 break;
605 case DIGITAL_METER_TYPE_POSITION:
606 if (self->on_drag_begin)
607 ((*self->on_drag_begin) (self->obj));
608 break;
609 case DIGITAL_METER_TYPE_BPM:
610 self->bpm_at_start =
611 tempo_track_get_current_bpm (P_TEMPO_TRACK);
612 self->last_set_bpm = self->bpm_at_start;
613 transport_prepare_audio_regions_for_stretch (
614 TRANSPORT, NULL);
615 break;
616 default:
617 break;
618 }
619 }
620
621 /**
622 * To be called when a change has completed (eg
623 * drag or scroll).
624 */
625 static void
on_change_finished(DigitalMeterWidget * self)626 on_change_finished (
627 DigitalMeterWidget * self)
628 {
629 self->last_x = 0;
630 self->last_y = 0;
631 self->update_num = 0;
632 self->update_dec = 0;
633 self->update_bars = 0;
634 self->update_beats = 0;
635 self->update_sixteenths = 0;
636 self->update_ticks = 0;
637 self->update_minutes = 0;
638 self->update_seconds = 0;
639 self->update_ms = 0;
640 /* FIXME super reduntant */
641 if (self->update_note_length ||
642 self->update_note_type)
643 {
644 snap_grid_update_snap_points_default (
645 SNAP_GRID_TIMELINE);
646 snap_grid_update_snap_points_default (
647 SNAP_GRID_EDITOR);
648 quantize_options_update_quantize_points (
649 QUANTIZE_OPTIONS_TIMELINE);
650 quantize_options_update_quantize_points (
651 QUANTIZE_OPTIONS_EDITOR);
652 }
653 self->update_note_length = 0;
654 self->update_note_type = 0;
655 self->update_timesig_top = 0;
656 self->update_timesig_bot = 0;
657
658 switch (self->type)
659 {
660 case DIGITAL_METER_TYPE_POSITION:
661 if (self->on_drag_end)
662 ((*self->on_drag_end) (self->obj));
663 break;
664 case DIGITAL_METER_TYPE_BPM:
665 tempo_track_set_bpm (
666 P_TEMPO_TRACK,
667 self->last_set_bpm, self->bpm_at_start,
668 Z_F_NOT_TEMPORARY, F_PUBLISH_EVENTS);
669 break;
670 case DIGITAL_METER_TYPE_TIMESIG:
671 {
672 /* no update if rolling */
673 if (TRANSPORT_IS_ROLLING)
674 {
675 break;
676 }
677
678 int beats_per_bar =
679 tempo_track_get_beats_per_bar (
680 P_TEMPO_TRACK);
681 int beat_unit =
682 tempo_track_get_beat_unit (
683 P_TEMPO_TRACK);
684 if (self->beats_per_bar_at_start !=
685 beats_per_bar)
686 {
687 GError * err = NULL;
688 bool ret =
689 transport_action_perform_time_sig_change (
690 TRANSPORT_ACTION_BEATS_PER_BAR_CHANGE,
691 self->beats_per_bar_at_start,
692 beats_per_bar,
693 F_ALREADY_EDITED, &err);
694 if (!ret)
695 {
696 HANDLE_ERROR (
697 err, "%s",
698 _("Failed to change time "
699 "signature"));
700 }
701 }
702 else if (self->beat_unit_at_start !=
703 beat_unit)
704 {
705 GError * err = NULL;
706 bool ret =
707 transport_action_perform_time_sig_change (
708 TRANSPORT_ACTION_BEAT_UNIT_CHANGE,
709 self->beat_unit_at_start, beat_unit,
710 F_ALREADY_EDITED, &err);
711 if (!ret)
712 {
713 HANDLE_ERROR (
714 err, "%s",
715 _("Failed to change time "
716 "signature"));
717 }
718 }
719 }
720 break;
721 default:
722 break;
723 }
724 }
725
726 void
digital_meter_set_draw_line(DigitalMeterWidget * self,int draw_line)727 digital_meter_set_draw_line (
728 DigitalMeterWidget * self,
729 int draw_line)
730 {
731 self->draw_line = draw_line;
732 gtk_widget_queue_draw (GTK_WIDGET (self));
733 }
734
735 static int
on_scroll(GtkWidget * widget,GdkEventScroll * event,DigitalMeterWidget * self)736 on_scroll (
737 GtkWidget * widget,
738 GdkEventScroll * event,
739 DigitalMeterWidget * self)
740 {
741 int num =
742 event->direction == GDK_SCROLL_UP ? 1 : -1;
743 Position pos;
744
745 update_flags (self, event->x, event->y);
746 on_change_started (self);
747
748 ControlPortChange change = { 0 };
749
750 switch (self->type)
751 {
752 case DIGITAL_METER_TYPE_BPM:
753 change.flag1 = PORT_FLAG_BPM;
754 if (self->update_num)
755 {
756 change.real_val =
757 self->last_set_bpm + (bpm_t) num;
758 self->last_set_bpm = change.real_val;
759 router_queue_control_port_change (
760 ROUTER, &change);
761 }
762 else if (self->update_dec)
763 {
764 change.real_val =
765 self->last_set_bpm +
766 (bpm_t) num / 100.f;
767 self->last_set_bpm = change.real_val;
768 router_queue_control_port_change (
769 ROUTER, &change);
770 }
771
772 break;
773
774 case DIGITAL_METER_TYPE_POSITION:
775 GET_POS;
776 if (DISPLAY_TIME)
777 {
778 long ms = 0;
779 if (self->update_minutes)
780 {
781 ms = num * 60 * 1000;
782 }
783 else if (self->update_seconds)
784 {
785 ms = num * 1000;
786 }
787 else if (self->update_ms)
788 {
789 ms = num;
790 }
791 position_add_ms (&pos, ms);
792 }
793 else
794 {
795 if (self->update_bars)
796 {
797 position_add_bars (&pos, num);
798 }
799 else if (self->update_beats)
800 {
801 position_add_beats (&pos, num);
802 }
803 else if (self->update_sixteenths)
804 {
805 position_add_sixteenths (&pos, num);
806 }
807 else if (self->update_ticks)
808 {
809 position_add_ticks (&pos, num);
810 }
811 SET_POS;
812 }
813 break;
814 case DIGITAL_METER_TYPE_NOTE_LENGTH:
815 if (self->update_note_length)
816 {
817 num += self->start_note_length;
818 if (num < 0)
819 *self->note_length = 0;
820 else
821 *self->note_length =
822 (NoteLength)
823 (num > NOTE_LENGTH_1_128
824 ? NOTE_LENGTH_1_128 : num);
825 }
826 break;
827 case DIGITAL_METER_TYPE_NOTE_TYPE:
828 if (self->update_note_type)
829 {
830 num += self->start_note_type;
831 if (num < 0)
832 *self->note_type = 0;
833 else
834 *self->note_type =
835 (NoteType)
836 (num > NOTE_TYPE_TRIPLET
837 ? NOTE_TYPE_TRIPLET : num);
838 }
839 break;
840 case DIGITAL_METER_TYPE_TIMESIG:
841 /* no update if rolling */
842 if (TRANSPORT_IS_ROLLING)
843 {
844 break;
845 }
846 if (self->update_timesig_top)
847 {
848 num += self->beats_per_bar_at_start;
849 change.flag2 =
850 PORT_FLAG2_BEATS_PER_BAR;
851 if (num < TEMPO_TRACK_MIN_BEATS_PER_BAR)
852 {
853 change.ival =
854 TEMPO_TRACK_MIN_BEATS_PER_BAR;
855 }
856 else
857 {
858 change.ival =
859 num > TEMPO_TRACK_MAX_BEATS_PER_BAR
860 ? TEMPO_TRACK_MAX_BEATS_PER_BAR
861 : num;
862 }
863 router_queue_control_port_change (
864 ROUTER, &change);
865 }
866 else if (self->update_timesig_bot)
867 {
868 num +=
869 (int)
870 tempo_track_beat_unit_to_enum (
871 self->beat_unit_at_start);
872 change.flag2 = PORT_FLAG2_BEAT_UNIT;
873 if (num < 0)
874 {
875 change.beat_unit = BEAT_UNIT_2;
876 }
877 else
878 {
879 change.beat_unit =
880 num > BEAT_UNIT_16
881 ? BEAT_UNIT_16 : (BeatUnit) num;
882 }
883 router_queue_control_port_change (
884 ROUTER, &change);
885 }
886 if (self->update_timesig_top ||
887 self->update_timesig_bot)
888 {
889 EVENTS_PUSH (
890 ET_TIME_SIGNATURE_CHANGED, NULL);
891 }
892 break;
893 }
894 on_change_finished (self);
895 gtk_widget_queue_draw (GTK_WIDGET (self));
896
897 return FALSE;
898 }
899
900 static void
drag_begin(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,DigitalMeterWidget * self)901 drag_begin (
902 GtkGestureDrag * gesture,
903 gdouble offset_x,
904 gdouble offset_y,
905 DigitalMeterWidget * self)
906 {
907 on_change_started (self);
908 }
909
910 static void
drag_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,DigitalMeterWidget * self)911 drag_update (
912 GtkGestureDrag * gesture,
913 gdouble offset_x,
914 gdouble offset_y,
915 DigitalMeterWidget * self)
916 {
917 offset_y = - offset_y;
918 int use_x = fabs (offset_x) > fabs (offset_y);
919 double diff =
920 use_x ?
921 offset_x - self->last_x :
922 offset_y - self->last_y;
923 int num;
924 float dec;
925 Position pos;
926 switch (self->type)
927 {
928 case DIGITAL_METER_TYPE_BPM:
929 /*g_message ("update num ? %d", self->update_num);*/
930 if (self->update_num)
931 {
932 num = (int) diff / 4;
933 /*g_message ("updating num with %d", num);*/
934 if (abs (num) > 0)
935 {
936 ControlPortChange change = { 0 };
937 change.flag1 = PORT_FLAG_BPM;
938 change.real_val =
939 self->last_set_bpm + (bpm_t) num;
940 self->last_set_bpm = change.real_val;
941 router_queue_control_port_change (
942 ROUTER, &change);
943 self->last_y = offset_y;
944 self->last_x = offset_x;
945 }
946 }
947 else if (self->update_dec)
948 {
949 dec = (float) diff / 400.f;
950 g_message ("%f", (double) dec);
951 if (fabs (dec) > 0)
952 {
953 ControlPortChange change = { 0 };
954 change.flag1 = PORT_FLAG_BPM;
955 change.real_val =
956 self->last_set_bpm + (bpm_t) dec;
957 self->last_set_bpm = change.real_val;
958 router_queue_control_port_change (
959 ROUTER, &change);
960 self->last_y = offset_y;
961 self->last_x = offset_x;
962 }
963
964 }
965
966 break;
967
968 case DIGITAL_METER_TYPE_POSITION:
969 GET_POS;
970 if (DISPLAY_TIME)
971 {
972 if (self->update_minutes)
973 {
974 g_message ("UPDATE MINS");
975 num = (int) diff / 4;
976 if (abs (num) > 0)
977 {
978 g_message ("UPDATE MINS %d", num);
979 position_print (&pos);
980 position_add_minutes (
981 &pos, num);
982 position_print (&pos);
983 self->last_y = offset_y;
984 self->last_x = offset_x;
985 }
986 }
987 else if (self->update_seconds)
988 {
989 num = (int) diff / 4;
990 if (abs (num) > 0)
991 {
992 position_add_seconds (
993 &pos, num);
994 self->last_y = offset_y;
995 self->last_x = offset_x;
996 }
997 }
998 else if (self->update_ms)
999 {
1000 num = (int) diff / 4;
1001 if (abs (num) > 0)
1002 {
1003 position_add_ms (
1004 &pos, num);
1005 self->last_y = offset_y;
1006 self->last_x = offset_x;
1007 }
1008 }
1009 }
1010 else
1011 {
1012 if (self->update_bars)
1013 {
1014 num = (int) diff / 4;
1015 if (abs (num) > 0)
1016 {
1017 position_add_bars (&pos, num);
1018 self->last_y = offset_y;
1019 self->last_x = offset_x;
1020 }
1021 }
1022 else if (self->update_beats)
1023 {
1024 num = (int) diff / 4;
1025 if (abs (num) > 0)
1026 {
1027 position_add_beats (&pos, num);
1028 self->last_y = offset_y;
1029 self->last_x = offset_x;
1030 }
1031 }
1032 else if (self->update_sixteenths)
1033 {
1034 num = (int) diff / 4;
1035 if (abs (num) > 0)
1036 {
1037 position_add_sixteenths (
1038 &pos, num);
1039 self->last_y = offset_y;
1040 self->last_x = offset_x;
1041 }
1042 }
1043 else if (self->update_ticks)
1044 {
1045 num = (int) diff / 4;
1046 if (abs (num) > 0)
1047 {
1048 position_add_ticks (&pos, num);
1049 self->last_y = offset_y;
1050 self->last_x = offset_x;
1051 }
1052 }
1053 }
1054 SET_POS;
1055 break;
1056 case DIGITAL_METER_TYPE_NOTE_LENGTH:
1057 if (self->update_note_length)
1058 {
1059 num = self->start_note_length + (int) offset_y / 24;
1060 if (num < 0)
1061 *self->note_length = 0;
1062 else
1063 *self->note_length =
1064 (NoteLength)
1065 (num > NOTE_LENGTH_1_128
1066 ? NOTE_LENGTH_1_128 : num);
1067 }
1068 break;
1069 case DIGITAL_METER_TYPE_NOTE_TYPE:
1070 if (self->update_note_type)
1071 {
1072 num = self->start_note_type + (int) offset_y / 24;
1073 if (num < 0)
1074 *self->note_type = 0;
1075 else
1076 *self->note_type =
1077 num > NOTE_TYPE_TRIPLET
1078 ? NOTE_TYPE_TRIPLET : (NoteType) num;
1079 }
1080 break;
1081 case DIGITAL_METER_TYPE_TIMESIG:
1082 /* no update if rolling */
1083 if (TRANSPORT_IS_ROLLING)
1084 {
1085 break;
1086 }
1087 if (self->update_timesig_top)
1088 {
1089 num =
1090 self->beats_per_bar_at_start +
1091 (int) diff / 24;
1092 ControlPortChange change = { 0 };
1093 change.flag2 =
1094 PORT_FLAG2_BEATS_PER_BAR;
1095 if (num < TEMPO_TRACK_MIN_BEATS_PER_BAR)
1096 {
1097 change.ival =
1098 TEMPO_TRACK_MIN_BEATS_PER_BAR;
1099 }
1100 else
1101 {
1102 change.ival =
1103 num > TEMPO_TRACK_MAX_BEATS_PER_BAR
1104 ? TEMPO_TRACK_MAX_BEATS_PER_BAR
1105 : num;
1106 }
1107 router_queue_control_port_change (
1108 ROUTER, &change);
1109 }
1110 else if (self->update_timesig_bot)
1111 {
1112 num =
1113 (int)
1114 tempo_track_beat_unit_to_enum (
1115 self->beat_unit_at_start) +
1116 (int) diff / 24;
1117 ControlPortChange change = { 0 };
1118 change.flag2 = PORT_FLAG2_BEAT_UNIT;
1119 if (num < 0)
1120 {
1121 change.beat_unit = BEAT_UNIT_2;
1122 }
1123 else
1124 {
1125 change.beat_unit =
1126 num > BEAT_UNIT_16
1127 ? BEAT_UNIT_16 : (BeatUnit) num;
1128 }
1129 router_queue_control_port_change (
1130 ROUTER, &change);
1131 }
1132 if (self->update_timesig_top ||
1133 self->update_timesig_bot)
1134 {
1135 EVENTS_PUSH (
1136 ET_TIME_SIGNATURE_CHANGED, NULL);
1137 }
1138 break;
1139 }
1140 gtk_widget_queue_draw (GTK_WIDGET (self));
1141 }
1142
1143 static void
drag_end(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,DigitalMeterWidget * self)1144 drag_end (
1145 GtkGestureDrag *gesture,
1146 gdouble offset_x,
1147 gdouble offset_y,
1148 DigitalMeterWidget * self)
1149 {
1150 on_change_finished (self);
1151 }
1152
1153 static gboolean
button_press_cb(GtkWidget * event_box,GdkEventButton * event,DigitalMeterWidget * self)1154 button_press_cb (
1155 GtkWidget * event_box,
1156 GdkEventButton * event,
1157 DigitalMeterWidget * self)
1158 {
1159 /*g_message ("%d, %d", self->height_start_pos, self->height_end_pos);*/
1160 update_flags (self, event->x, event->y);
1161 return 0;
1162 }
1163
1164 static void
recreate_pango_layouts(DigitalMeterWidget * self)1165 recreate_pango_layouts (
1166 DigitalMeterWidget * self)
1167 {
1168 if (PANGO_IS_LAYOUT (self->caption_layout))
1169 g_object_unref (self->caption_layout);
1170 if (PANGO_IS_LAYOUT (self->seg7_layout))
1171 g_object_unref (self->seg7_layout);
1172 if (PANGO_IS_LAYOUT (self->normal_layout))
1173 g_object_unref (self->normal_layout);
1174
1175 self->caption_layout =
1176 z_cairo_create_pango_layout_from_string (
1177 GTK_WIDGET (self), CAPTION_FONT,
1178 PANGO_ELLIPSIZE_NONE, -1);
1179 self->seg7_layout =
1180 z_cairo_create_pango_layout_from_string (
1181 GTK_WIDGET (self), SEG7_FONT,
1182 PANGO_ELLIPSIZE_NONE, -1);
1183 self->normal_layout =
1184 z_cairo_create_pango_layout_from_string (
1185 GTK_WIDGET (self), NORMAL_FONT,
1186 PANGO_ELLIPSIZE_NONE, -1);
1187 }
1188
1189 static void
digital_meter_widget_on_size_allocate(GtkWidget * widget,GdkRectangle * allocation,DigitalMeterWidget * self)1190 digital_meter_widget_on_size_allocate (
1191 GtkWidget * widget,
1192 GdkRectangle * allocation,
1193 DigitalMeterWidget * self)
1194 {
1195 recreate_pango_layouts (self);
1196 }
1197
1198 static void
on_screen_changed(GtkWidget * widget,GdkScreen * previous_screen,DigitalMeterWidget * self)1199 on_screen_changed (
1200 GtkWidget * widget,
1201 GdkScreen * previous_screen,
1202 DigitalMeterWidget * self)
1203 {
1204 recreate_pango_layouts (self);
1205 }
1206
1207 static void
init_dm(DigitalMeterWidget * self)1208 init_dm (
1209 DigitalMeterWidget * self)
1210 {
1211 g_return_if_fail (Z_DIGITAL_METER_WIDGET (self));
1212
1213 recreate_pango_layouts (self);
1214
1215 int caption_textw, caption_texth;
1216 z_cairo_get_text_extents_for_widget (
1217 self, self->caption_layout, self->caption,
1218 &caption_textw, &caption_texth);
1219 int textw, texth;
1220 switch (self->type)
1221 {
1222 case DIGITAL_METER_TYPE_BPM:
1223 {
1224 gtk_widget_set_tooltip_text (
1225 (GtkWidget *) self, _("Tempo/BPM"));
1226 z_cairo_get_text_extents_for_widget (
1227 self, self->seg7_layout, "888888",
1228 &textw, &texth);
1229 /* caption + padding between caption and
1230 * BPM + padding top/bottom */
1231 gtk_widget_set_size_request (
1232 GTK_WIDGET (self), textw + PADDING_W * 2,
1233 caption_texth + HALF_SPACE_BETWEEN +
1234 texth + PADDING_TOP * 2);
1235 }
1236 break;
1237 case DIGITAL_METER_TYPE_POSITION:
1238 gtk_widget_set_tooltip_text (
1239 (GtkWidget *) self, _("Position"));
1240 z_cairo_get_text_extents_for_widget (
1241 self, self->seg7_layout, "-888888888",
1242 &textw, &texth);
1243 /* caption + padding between caption and
1244 * BPM + padding top/bottom */
1245 gtk_widget_set_size_request (
1246 GTK_WIDGET (self),
1247 textw + PADDING_W * 2 + HALF_SPACE_BETWEEN * 3,
1248 caption_texth + HALF_SPACE_BETWEEN +
1249 texth + PADDING_TOP * 2);
1250
1251 break;
1252 case DIGITAL_METER_TYPE_NOTE_LENGTH:
1253 gtk_widget_set_size_request (
1254 GTK_WIDGET (self),
1255 -1,
1256 30);
1257 break;
1258
1259 case DIGITAL_METER_TYPE_NOTE_TYPE:
1260 gtk_widget_set_size_request (
1261 GTK_WIDGET (self),
1262 -1,
1263 30);
1264 break;
1265 case DIGITAL_METER_TYPE_TIMESIG:
1266 {
1267 gtk_widget_set_tooltip_text (
1268 GTK_WIDGET (self),
1269 _("Time Signature - Beats per bar / "
1270 "Beat unit"));
1271 z_cairo_get_text_extents_for_widget (
1272 self, self->seg7_layout, "16/16",
1273 &textw, &texth);
1274 /* caption + padding between caption and
1275 * BPM + padding top/bottom */
1276 gtk_widget_set_size_request (
1277 GTK_WIDGET (self), textw + PADDING_W * 2,
1278 caption_texth + HALF_SPACE_BETWEEN +
1279 texth + PADDING_TOP * 2);
1280 }
1281 break;
1282 }
1283
1284 g_signal_connect (
1285 G_OBJECT (self), "draw",
1286 G_CALLBACK (digital_meter_draw_cb), self);
1287 }
1288
1289 /**
1290 * Creates a digital meter with the given type (bpm or position).
1291 */
1292 DigitalMeterWidget *
digital_meter_widget_new(DigitalMeterType type,NoteLength * note_length,NoteType * note_type,const char * caption)1293 digital_meter_widget_new (
1294 DigitalMeterType type,
1295 NoteLength * note_length,
1296 NoteType * note_type,
1297 const char * caption)
1298 {
1299 DigitalMeterWidget * self =
1300 g_object_new (DIGITAL_METER_WIDGET_TYPE, NULL);
1301
1302 self->type = type;
1303 self->caption = g_strdup (caption);
1304 self->note_length = note_length;
1305 self->note_type = note_type;
1306 init_dm (self);
1307
1308 return self;
1309 }
1310
1311 /**
1312 * Creates a digital meter for an arbitrary position.
1313 *
1314 * @param obj The object to call the get/setters with.
1315 *
1316 * E.g. Region.
1317 * @param get_val The getter func to get the position,
1318 * passing the obj and the position to save to.
1319 * @param set_val The setter function to set the
1320 * position.
1321 */
1322 DigitalMeterWidget *
_digital_meter_widget_new_for_position(void * obj,void (* on_drag_begin)(void *),void (* get_val)(void *,Position *),void (* set_val)(void *,Position *),void (* on_drag_end)(void *),const char * caption)1323 _digital_meter_widget_new_for_position (
1324 void * obj,
1325 void (*on_drag_begin)(void *),
1326 void (*get_val)(void *, Position *),
1327 void (*set_val)(void *, Position *),
1328 void (*on_drag_end)(void *),
1329 const char * caption)
1330 {
1331 DigitalMeterWidget * self =
1332 g_object_new (DIGITAL_METER_WIDGET_TYPE, NULL);
1333
1334 self->obj = obj;
1335 self->on_drag_begin = on_drag_begin;
1336 self->getter = get_val;
1337 self->setter = set_val;
1338 self->on_drag_end = on_drag_end;
1339 self->caption = g_strdup (caption);
1340 self->type = DIGITAL_METER_TYPE_POSITION;
1341 init_dm (self);
1342
1343 return self;
1344 }
1345
1346 static void
finalize(DigitalMeterWidget * self)1347 finalize (
1348 DigitalMeterWidget * self)
1349 {
1350 if (self->caption)
1351 g_free (self->caption);
1352 if (self->drag)
1353 g_object_unref (self->drag);
1354 if (self->caption_layout)
1355 g_object_unref (self->caption_layout);
1356 if (self->seg7_layout)
1357 g_object_unref (self->seg7_layout);
1358 if (self->normal_layout)
1359 g_object_unref (self->normal_layout);
1360
1361 G_OBJECT_CLASS (
1362 digital_meter_widget_parent_class)->
1363 finalize (G_OBJECT (self));
1364 }
1365
1366 static void
digital_meter_widget_class_init(DigitalMeterWidgetClass * klass)1367 digital_meter_widget_class_init (
1368 DigitalMeterWidgetClass * klass)
1369 {
1370 GObjectClass * oklass =
1371 G_OBJECT_CLASS (klass);
1372 oklass->finalize =
1373 (GObjectFinalizeFunc) finalize;
1374 }
1375
1376 static void
digital_meter_widget_init(DigitalMeterWidget * self)1377 digital_meter_widget_init (
1378 DigitalMeterWidget * self)
1379 {
1380 g_return_if_fail (
1381 Z_IS_DIGITAL_METER_WIDGET (self));
1382
1383 /* make it able to notify */
1384 gtk_widget_set_has_window (
1385 (GtkWidget *) self, TRUE);
1386 gtk_widget_add_events (
1387 (GtkWidget *) self,
1388 GDK_SCROLL_MASK);
1389
1390 self->drag =
1391 GTK_GESTURE_DRAG (
1392 gtk_gesture_drag_new (GTK_WIDGET (self)));
1393
1394 g_signal_connect (
1395 G_OBJECT (self), "scroll-event",
1396 G_CALLBACK (on_scroll), self);
1397 g_signal_connect (
1398 G_OBJECT (self->drag), "drag-begin",
1399 G_CALLBACK (drag_begin), self);
1400 g_signal_connect (
1401 G_OBJECT (self->drag), "drag-update",
1402 G_CALLBACK (drag_update), self);
1403 g_signal_connect (
1404 G_OBJECT (self->drag), "drag-end",
1405 G_CALLBACK (drag_end), self);
1406 g_signal_connect (
1407 G_OBJECT (self), "button_press_event",
1408 G_CALLBACK (button_press_cb), self);
1409 g_signal_connect (
1410 G_OBJECT (self), "screen-changed",
1411 G_CALLBACK (on_screen_changed), self);
1412 g_signal_connect (
1413 G_OBJECT (self), "size-allocate",
1414 G_CALLBACK (
1415 digital_meter_widget_on_size_allocate),
1416 self);
1417
1418 gtk_widget_set_visible (
1419 GTK_WIDGET (self), 1);
1420 }
1421