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 <math.h>
21
22 #include "actions/actions.h"
23 #include "audio/position.h"
24 #include "audio/tempo_track.h"
25 #include "audio/transport.h"
26 #include "gui/backend/event.h"
27 #include "gui/backend/event_manager.h"
28 #include "gui/backend/timeline.h"
29 #include "gui/widgets/arranger.h"
30 #include "gui/widgets/bot_bar.h"
31 #include "gui/widgets/bot_dock_edge.h"
32 #include "gui/widgets/center_dock.h"
33 #include "gui/widgets/clip_editor.h"
34 #include "gui/widgets/clip_editor_inner.h"
35 #include "gui/widgets/main_notebook.h"
36 #include "gui/widgets/main_window.h"
37 #include "gui/widgets/midi_arranger.h"
38 #include "gui/widgets/midi_modifier_arranger.h"
39 #include "gui/widgets/editor_ruler.h"
40 #include "gui/widgets/ruler.h"
41 #include "gui/widgets/ruler_marker.h"
42 #include "gui/widgets/ruler_range.h"
43 #include "gui/widgets/timeline_arranger.h"
44 #include "gui/widgets/timeline_panel.h"
45 #include "gui/widgets/timeline_ruler.h"
46 #include "project.h"
47 #include "settings/settings.h"
48 #include "utils/cairo.h"
49 #include "utils/math.h"
50 #include "utils/string.h"
51 #include "utils/ui.h"
52 #include "zrythm.h"
53 #include "zrythm_app.h"
54
55 #include <gtk/gtk.h>
56 #include <glib/gi18n.h>
57
G_DEFINE_TYPE(RulerWidget,ruler_widget,GTK_TYPE_DRAWING_AREA)58 G_DEFINE_TYPE (
59 RulerWidget,
60 ruler_widget,
61 GTK_TYPE_DRAWING_AREA)
62
63 #define Y_SPACING 5
64
65 /* FIXME delete these, see ruler_marker.h */
66 #define START_MARKER_TRIANGLE_HEIGHT 8
67 #define START_MARKER_TRIANGLE_WIDTH 8
68 #define Q_HEIGHT 12
69 #define Q_WIDTH 7
70
71 #define TYPE(x) RULER_WIDGET_TYPE_##x
72
73 #define ACTION_IS(x) \
74 (self->action == UI_OVERLAY_ACTION_##x)
75
76 double
77 ruler_widget_get_zoom_level (
78 RulerWidget * self)
79 {
80 if (self->type == RULER_WIDGET_TYPE_TIMELINE)
81 {
82 return
83 PRJ_TIMELINE->editor_settings.hzoom_level;
84 }
85 else if (self->type ==
86 RULER_WIDGET_TYPE_EDITOR)
87 {
88 ArrangerWidget * arr =
89 clip_editor_inner_widget_get_visible_arranger (
90 MW_CLIP_EDITOR_INNER);
91 EditorSettings * settings =
92 arranger_widget_get_editor_settings (arr);
93 g_return_val_if_fail (settings, 1.f);
94
95 return settings->hzoom_level;
96 }
97
98 g_return_val_if_reached (1.f);
99 }
100
101 /**
102 * Returns the beat interval for drawing vertical
103 * lines.
104 */
105 int
ruler_widget_get_beat_interval(RulerWidget * self)106 ruler_widget_get_beat_interval (
107 RulerWidget * self)
108 {
109 int i;
110
111 /* gather divisors of the number of beats per
112 * bar */
113 int beats_per_bar =
114 tempo_track_get_beats_per_bar (P_TEMPO_TRACK);
115 int beat_divisors[16];
116 int num_beat_divisors = 0;
117 for (i = 1; i <= beats_per_bar; i++)
118 {
119 if (beats_per_bar % i == 0)
120 beat_divisors[num_beat_divisors++] = i;
121 }
122
123 /* decide the raw interval to keep between beats */
124 int _beat_interval =
125 MAX (
126 (int)
127 (RW_PX_TO_HIDE_BEATS / self->px_per_beat),
128 1);
129
130 /* round the interval to the divisors */
131 int beat_interval = -1;
132 for (i = 0; i < num_beat_divisors; i++)
133 {
134 if (_beat_interval <= beat_divisors[i])
135 {
136 if (beat_divisors[i] != beats_per_bar)
137 beat_interval = beat_divisors[i];
138 break;
139 }
140 }
141
142 return beat_interval;
143 }
144
145 /**
146 * Returns the sixteenth interval for drawing
147 * vertical lines.
148 */
149 int
ruler_widget_get_sixteenth_interval(RulerWidget * self)150 ruler_widget_get_sixteenth_interval (
151 RulerWidget * self)
152 {
153 int i;
154
155 /* gather divisors of the number of sixteenths per
156 * beat */
157 #define sixteenths_per_beat \
158 TRANSPORT->sixteenths_per_beat
159 int divisors[16];
160 int num_divisors = 0;
161 for (i = 1; i <= sixteenths_per_beat; i++)
162 {
163 if (sixteenths_per_beat % i == 0)
164 divisors[num_divisors++] = i;
165 }
166
167 /* decide the raw interval to keep between sixteenths */
168 int _sixteenth_interval =
169 MAX (
170 (int)
171 (RW_PX_TO_HIDE_BEATS /
172 self->px_per_sixteenth), 1);
173
174 /* round the interval to the divisors */
175 int sixteenth_interval = -1;
176 for (i = 0; i < num_divisors; i++)
177 {
178 if (_sixteenth_interval <= divisors[i])
179 {
180 if (divisors[i] != sixteenths_per_beat)
181 sixteenth_interval = divisors[i];
182 break;
183 }
184 }
185
186 return sixteenth_interval;
187 }
188
189 /**
190 * Returns the 10 sec interval.
191 */
192 int
ruler_widget_get_10sec_interval(RulerWidget * self)193 ruler_widget_get_10sec_interval (
194 RulerWidget * self)
195 {
196 int i;
197
198 /* gather divisors of the number of beats per
199 * bar */
200 #define ten_secs_per_min 6
201 int ten_sec_divisors[36];
202 int num_ten_sec_divisors = 0;
203 for (i = 1; i <= ten_secs_per_min; i++)
204 {
205 if (ten_secs_per_min % i == 0)
206 ten_sec_divisors[num_ten_sec_divisors++] = i;
207 }
208
209 /* decide the raw interval to keep between
210 * 10 secs */
211 int _10sec_interval =
212 MAX (
213 (int)
214 (RW_PX_TO_HIDE_BEATS / self->px_per_10sec),
215 1);
216
217 /* round the interval to the divisors */
218 int ten_sec_interval = -1;
219 for (i = 0; i < num_ten_sec_divisors; i++)
220 {
221 if (_10sec_interval <= ten_sec_divisors[i])
222 {
223 if (ten_sec_divisors[i] != ten_secs_per_min)
224 ten_sec_interval = ten_sec_divisors[i];
225 break;
226 }
227 }
228
229 return ten_sec_interval;
230 }
231
232 /**
233 * Returns the sec interval.
234 */
235 int
ruler_widget_get_sec_interval(RulerWidget * self)236 ruler_widget_get_sec_interval (
237 RulerWidget * self)
238 {
239 int i;
240
241 /* gather divisors of the number of sixteenths per
242 * beat */
243 #define secs_per_10_sec 10
244 int divisors[16];
245 int num_divisors = 0;
246 for (i = 1; i <= secs_per_10_sec; i++)
247 {
248 if (secs_per_10_sec % i == 0)
249 divisors[num_divisors++] = i;
250 }
251
252 /* decide the raw interval to keep between secs */
253 int _sec_interval =
254 MAX (
255 (int)
256 (RW_PX_TO_HIDE_BEATS /
257 self->px_per_sec), 1);
258
259 /* round the interval to the divisors */
260 int sec_interval = -1;
261 for (i = 0; i < num_divisors; i++)
262 {
263 if (_sec_interval <= divisors[i])
264 {
265 if (divisors[i] != secs_per_10_sec)
266 sec_interval = divisors[i];
267 break;
268 }
269 }
270
271 return sec_interval;
272 }
273
274 /**
275 * Draws a region other than the editor one.
276 */
277 static void
draw_other_region(RulerWidget * self,cairo_t * cr,ZRegion * region)278 draw_other_region (
279 RulerWidget * self,
280 cairo_t * cr,
281 ZRegion * region)
282 {
283 /*g_debug (
284 * "drawing other region %s", region->name);*/
285
286 int height =
287 gtk_widget_get_allocated_height (
288 GTK_WIDGET (self));
289
290 ArrangerObject * r_obj =
291 (ArrangerObject *) region;
292 Track * track = arranger_object_get_track (r_obj);
293 cairo_set_source_rgba (
294 cr, track->color.red, track->color.green,
295 track->color.blue, 0.5);
296
297 int px_start, px_end;
298 px_start =
299 ui_pos_to_px_editor (&r_obj->pos, true);
300 px_end =
301 ui_pos_to_px_editor (&r_obj->end_pos, true);
302 cairo_rectangle (
303 cr, px_start, 0,
304 px_end - px_start, height / 4.0);
305 cairo_fill (cr);
306 }
307
308 /**
309 * Draws the regions in the editor ruler.
310 */
311 static void
draw_regions(RulerWidget * self,GdkRectangle * rect)312 draw_regions (
313 RulerWidget * self,
314 GdkRectangle * rect)
315 {
316 cairo_t * cr = self->cached_cr;
317
318 int height =
319 gtk_widget_get_allocated_height (
320 GTK_WIDGET (self));
321
322 /* get a visible region - the clip editor
323 * region is removed temporarily while moving
324 * regions so this could be NULL */
325 ZRegion * region =
326 clip_editor_get_region (CLIP_EDITOR);
327 if (!region)
328 return;
329
330 ArrangerObject * region_obj =
331 (ArrangerObject *) region;
332
333 Track * track =
334 arranger_object_get_track (region_obj);
335
336 int px_start, px_end;
337
338 /* draw the main region */
339 GdkRGBA color = track->color;
340 ui_get_arranger_object_color (
341 &color,
342 MW_TIMELINE->hovered_object == region_obj,
343 region_is_selected (region),
344 false, arranger_object_get_muted (region_obj));
345 gdk_cairo_set_source_rgba (cr, &color);
346 px_start =
347 ui_pos_to_px_editor (
348 ®ion_obj->pos, 1);
349 px_end =
350 ui_pos_to_px_editor (
351 ®ion_obj->end_pos, 1);
352 cairo_rectangle (
353 cr, px_start - rect->x, - rect->y,
354 px_end - px_start, height / 4.0);
355 cairo_fill (cr);
356
357 /* draw its transient if copy-moving TODO */
358 if (arranger_object_should_orig_be_visible (
359 region_obj))
360 {
361 px_start =
362 ui_pos_to_px_editor (
363 ®ion_obj->pos, 1);
364 px_end =
365 ui_pos_to_px_editor (
366 ®ion_obj->end_pos, 1);
367 cairo_rectangle (
368 cr, px_start - rect->x, - rect->y,
369 px_end - px_start, height / 4.0);
370 cairo_fill (cr);
371 }
372
373 /* draw the other regions */
374 ZRegion * other_regions[1000];
375 int num_other_regions =
376 track_get_regions_in_range (
377 track, NULL, NULL, other_regions);
378 for (int i = 0; i < num_other_regions; i++)
379 {
380 ZRegion * other_region = other_regions[i];
381 if (region_identifier_is_equal (
382 ®ion->id, &other_region->id) ||
383 region->id.type != other_region->id.type)
384 {
385 continue;
386 }
387
388 cairo_save (cr);
389 cairo_translate (
390 cr, - rect->x, - rect->y);
391
392 draw_other_region (
393 self, cr, other_region);
394
395 cairo_restore (cr);
396 }
397 }
398
399 static void
get_loop_start_rect(RulerWidget * self,GdkRectangle * rect)400 get_loop_start_rect (
401 RulerWidget * self,
402 GdkRectangle * rect)
403 {
404 rect->x = 0;
405 if (self->type == TYPE (EDITOR))
406 {
407 ZRegion * region =
408 clip_editor_get_region (CLIP_EDITOR);
409 if (region)
410 {
411 ArrangerObject * region_obj =
412 (ArrangerObject *) region;
413 double start_ticks =
414 position_to_ticks (®ion_obj->pos);
415 double loop_start_ticks =
416 position_to_ticks (
417 ®ion_obj->loop_start_pos) +
418 start_ticks;
419 Position tmp;
420 position_from_ticks (
421 &tmp, loop_start_ticks);
422 rect->x =
423 ui_pos_to_px_editor (&tmp, 1);
424 }
425 else
426 {
427 rect->x = 0;
428 }
429 }
430 else if (self->type == TYPE (TIMELINE))
431 {
432 rect->x =
433 ui_pos_to_px_timeline (
434 &TRANSPORT->loop_start_pos, 1);
435 }
436 rect->y = 0;
437 rect->width = RW_RULER_MARKER_SIZE;
438 rect->height = RW_RULER_MARKER_SIZE;
439 }
440
441 static void
draw_loop_start(RulerWidget * self,cairo_t * cr,GdkRectangle * rect)442 draw_loop_start (
443 RulerWidget * self,
444 cairo_t * cr,
445 GdkRectangle * rect)
446 {
447 /* draw rect */
448 GdkRectangle dr;
449 get_loop_start_rect (self, &dr);
450
451 if (dr.x >=
452 rect->x - dr.width &&
453 dr.x <=
454 rect->x + rect->width)
455 {
456 cairo_set_source_rgb (cr, 0, 0.9, 0.7);
457 cairo_set_line_width (cr, 2);
458 cairo_move_to (
459 cr, dr.x - rect->x, dr.y);
460 cairo_line_to (
461 cr, dr.x - rect->x, dr.y + dr.height);
462 cairo_line_to (
463 cr, (dr.x + dr.width) - rect->x, dr.y);
464 cairo_fill (cr);
465 }
466 }
467
468 static void
get_loop_end_rect(RulerWidget * self,GdkRectangle * rect)469 get_loop_end_rect (
470 RulerWidget * self,
471 GdkRectangle * rect)
472 {
473 rect->x = 0;
474 if (self->type == TYPE (EDITOR))
475 {
476 ZRegion * region =
477 clip_editor_get_region (CLIP_EDITOR);
478 if (region)
479 {
480 ArrangerObject * region_obj =
481 (ArrangerObject *) region;
482 double start_ticks =
483 position_to_ticks (
484 ®ion_obj->pos);
485 double loop_end_ticks =
486 position_to_ticks (
487 ®ion_obj->loop_end_pos) +
488 start_ticks;
489 Position tmp;
490 position_from_ticks (
491 &tmp, loop_end_ticks);
492 rect->x =
493 ui_pos_to_px_editor (
494 &tmp, 1) - RW_RULER_MARKER_SIZE;
495 }
496 else
497 {
498 rect->x = 0;
499 }
500 }
501 else if (self->type == TYPE (TIMELINE))
502 {
503 rect->x =
504 ui_pos_to_px_timeline (
505 &TRANSPORT->loop_end_pos, 1) -
506 RW_RULER_MARKER_SIZE;
507 }
508 rect->y = 0;
509 rect->width = RW_RULER_MARKER_SIZE;
510 rect->height = RW_RULER_MARKER_SIZE;
511 }
512
513 static void
draw_loop_end(RulerWidget * self,cairo_t * cr,GdkRectangle * rect)514 draw_loop_end (
515 RulerWidget * self,
516 cairo_t * cr,
517 GdkRectangle * rect)
518 {
519 /* draw rect */
520 GdkRectangle dr;
521 get_loop_end_rect (self, &dr);
522
523 if (dr.x >=
524 rect->x - dr.width &&
525 dr.x <=
526 rect->x + rect->width)
527 {
528 cairo_set_source_rgb (cr, 0, 0.9, 0.7);
529 cairo_set_line_width (cr, 2);
530 cairo_move_to (cr, dr.x - rect->x, dr.y);
531 cairo_line_to (
532 cr, (dr.x + dr.width) - rect->x, dr.y);
533 cairo_line_to (
534 cr, (dr.x + dr.width) - rect->x,
535 dr.y + dr.height);
536 cairo_fill (cr);
537 }
538 }
539
540 static void
get_punch_in_rect(RulerWidget * self,GdkRectangle * rect)541 get_punch_in_rect (
542 RulerWidget * self,
543 GdkRectangle * rect)
544 {
545 rect->x =
546 ui_pos_to_px_timeline (
547 &TRANSPORT->punch_in_pos, 1);
548 rect->y = RW_RULER_MARKER_SIZE;
549 rect->width = RW_RULER_MARKER_SIZE;
550 rect->height = RW_RULER_MARKER_SIZE;
551 }
552
553 static void
draw_punch_in(RulerWidget * self,cairo_t * cr,GdkRectangle * rect)554 draw_punch_in (
555 RulerWidget * self,
556 cairo_t * cr,
557 GdkRectangle * rect)
558 {
559 /* draw rect */
560 GdkRectangle dr;
561 get_punch_in_rect (self, &dr);
562
563 if (dr.x >=
564 rect->x - dr.width &&
565 dr.x <=
566 rect->x + rect->width)
567 {
568 cairo_set_source_rgb (cr, 0.9, 0.1, 0.1);
569 cairo_set_line_width (cr, 2);
570 cairo_move_to (
571 cr, dr.x - rect->x, dr.y);
572 cairo_line_to (
573 cr, dr.x - rect->x, dr.y + dr.height);
574 cairo_line_to (
575 cr, (dr.x + dr.width) - rect->x, dr.y);
576 cairo_fill (cr);
577 }
578 }
579
580 static void
get_punch_out_rect(RulerWidget * self,GdkRectangle * rect)581 get_punch_out_rect (
582 RulerWidget * self,
583 GdkRectangle * rect)
584 {
585 rect->x =
586 ui_pos_to_px_timeline (
587 &TRANSPORT->punch_out_pos, 1) -
588 RW_RULER_MARKER_SIZE;
589 rect->y = RW_RULER_MARKER_SIZE;
590 rect->width = RW_RULER_MARKER_SIZE;
591 rect->height = RW_RULER_MARKER_SIZE;
592 }
593
594 static void
draw_punch_out(RulerWidget * self,cairo_t * cr,GdkRectangle * rect)595 draw_punch_out (
596 RulerWidget * self,
597 cairo_t * cr,
598 GdkRectangle * rect)
599 {
600 /* draw rect */
601 GdkRectangle dr;
602 get_punch_out_rect (self, &dr);
603
604 if (dr.x >=
605 rect->x - dr.width &&
606 dr.x <=
607 rect->x + rect->width)
608 {
609 cairo_set_source_rgb (cr, 0.9, 0.1, 0.1);
610 cairo_set_line_width (cr, 2);
611 cairo_move_to (cr, dr.x - rect->x, dr.y);
612 cairo_line_to (
613 cr, (dr.x + dr.width) - rect->x, dr.y);
614 cairo_line_to (
615 cr, (dr.x + dr.width) - rect->x,
616 dr.y + dr.height);
617 cairo_fill (cr);
618 }
619 }
620
621 static void
get_clip_start_rect(RulerWidget * self,GdkRectangle * rect)622 get_clip_start_rect (
623 RulerWidget * self,
624 GdkRectangle * rect)
625 {
626 /* TODO these were added to fix unused
627 * warnings - check again if valid */
628 rect->x = 0;
629 rect->y = 0;
630
631 if (self->type == TYPE (EDITOR))
632 {
633 ZRegion * region =
634 clip_editor_get_region (CLIP_EDITOR);
635 if (region)
636 {
637 ArrangerObject * region_obj =
638 (ArrangerObject *) region;
639 double start_ticks =
640 position_to_ticks (
641 ®ion_obj->pos);
642 double clip_start_ticks =
643 position_to_ticks (
644 ®ion_obj->clip_start_pos) +
645 start_ticks;
646 Position tmp;
647 position_from_ticks (
648 &tmp, clip_start_ticks);
649 rect->x =
650 ui_pos_to_px_editor (&tmp, 1);
651 }
652 else
653 {
654 rect->x = 0;
655 }
656 rect->y =
657 ((gtk_widget_get_allocated_height (
658 GTK_WIDGET (self)) -
659 RW_RULER_MARKER_SIZE) -
660 RW_CUE_MARKER_HEIGHT) - 1;
661 }
662 rect->width = RW_CUE_MARKER_WIDTH;
663 rect->height = RW_CUE_MARKER_HEIGHT;
664 }
665
666 /**
667 * Draws the cue point (or clip start if this is
668 * the editor ruler.
669 */
670 static void
draw_cue_point(RulerWidget * self,cairo_t * cr,GdkRectangle * rect)671 draw_cue_point (
672 RulerWidget * self,
673 cairo_t * cr,
674 GdkRectangle * rect)
675 {
676 /* draw rect */
677 GdkRectangle dr;
678 get_clip_start_rect (self, &dr);
679
680 if (self->type == TYPE (TIMELINE))
681 {
682 dr.x =
683 ui_pos_to_px_timeline (
684 &TRANSPORT->cue_pos,
685 1);
686 dr.y =
687 RW_RULER_MARKER_SIZE;
688 }
689
690 if (dr.x >=
691 rect->x - dr.width &&
692 dr.x <=
693 rect->x + rect->width)
694 {
695 if (self->type == TYPE (EDITOR))
696 {
697 cairo_set_source_rgb (cr, 0.2, 0.6, 0.9);
698 }
699 else if (self->type == TYPE (TIMELINE))
700 {
701 cairo_set_source_rgb (cr, 0, 0.6, 0.9);
702 }
703 cairo_set_line_width (cr, 2);
704 cairo_move_to (
705 cr, dr.x - rect->x, dr.y);
706 cairo_line_to (
707 cr, (dr.x + dr.width) - rect->x,
708 dr.y + dr.height / 2);
709 cairo_line_to (
710 cr, dr.x - rect->x, dr.y + dr.height);
711 cairo_fill (cr);
712 }
713 }
714
715 /**
716 * Returns the playhead's x coordinate in absolute
717 * coordinates.
718 */
719 static int
get_playhead_px(RulerWidget * self)720 get_playhead_px (
721 RulerWidget * self)
722 {
723 if (self->type == TYPE (EDITOR))
724 {
725 return
726 ui_pos_to_px_editor (PLAYHEAD, 1);
727 }
728 else if (self->type == TYPE (TIMELINE))
729 {
730 return
731 ui_pos_to_px_timeline (PLAYHEAD, 1);
732 }
733 g_return_val_if_reached (-1);
734 }
735
736 static void
draw_playhead(RulerWidget * self,cairo_t * cr,GdkRectangle * rect)737 draw_playhead (
738 RulerWidget * self,
739 cairo_t * cr,
740 GdkRectangle * rect)
741 {
742 int px = get_playhead_px (self);
743
744 if (px >=
745 rect->x - RW_PLAYHEAD_TRIANGLE_WIDTH / 2 &&
746 px <=
747 (rect->x + rect->width) -
748 RW_PLAYHEAD_TRIANGLE_WIDTH / 2)
749 {
750 self->last_playhead_px = px;
751
752 /* draw rect */
753 GdkRectangle dr = { 0, 0, 0, 0 };
754 dr.x =
755 px -
756 (RW_PLAYHEAD_TRIANGLE_WIDTH / 2);
757 dr.y =
758 gtk_widget_get_allocated_height (
759 GTK_WIDGET (self)) -
760 RW_PLAYHEAD_TRIANGLE_HEIGHT;
761 dr.width = RW_PLAYHEAD_TRIANGLE_WIDTH;
762 dr.height = RW_PLAYHEAD_TRIANGLE_HEIGHT;
763
764 cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
765 cairo_set_line_width (cr, 2);
766 cairo_move_to (cr, dr.x - rect->x, dr.y);
767 cairo_line_to (
768 cr, (dr.x + dr.width / 2) - rect->x,
769 dr.y + dr.height);
770 cairo_line_to (
771 cr, (dr.x + dr.width) - rect->x, dr.y);
772 cairo_fill (cr);
773 }
774 }
775
776 /**
777 * Draws the grid lines and labels.
778 *
779 * @param rect Clip rectangle.
780 */
781 static void
draw_lines_and_labels(RulerWidget * self,cairo_t * cr,GdkRectangle * rect)782 draw_lines_and_labels (
783 RulerWidget * self,
784 cairo_t * cr,
785 GdkRectangle * rect)
786 {
787 /* draw lines */
788 int i = 0;
789 double curr_px;
790 char text[40];
791 int textw, texth;
792
793 int beats_per_bar =
794 tempo_track_get_beats_per_bar (P_TEMPO_TRACK);
795 int height =
796 gtk_widget_get_allocated_height (
797 GTK_WIDGET (self));
798
799 /* if time display */
800 if (g_settings_get_enum (
801 S_UI, "ruler-display") ==
802 TRANSPORT_DISPLAY_TIME)
803 {
804 /* get sec interval */
805 int sec_interval =
806 ruler_widget_get_sec_interval (self);
807
808 /* get 10 sec interval */
809 int ten_sec_interval =
810 ruler_widget_get_10sec_interval (self);
811
812 /* get the interval for mins */
813 int min_interval =
814 (int)
815 MAX ((RW_PX_TO_HIDE_BEATS) /
816 (double) self->px_per_min, 1.0);
817
818 /* draw mins */
819 i = - min_interval;
820 while (
821 (curr_px =
822 self->px_per_min * (i += min_interval) +
823 SPACE_BEFORE_START) <
824 rect->x + rect->width + 20.0)
825 {
826 if (curr_px + 20.0 < rect->x)
827 continue;
828
829 cairo_set_source_rgb (cr, 1, 1, 1);
830 cairo_set_line_width (cr, 1);
831 double x = curr_px - rect->x;
832 cairo_move_to (cr, x, 0);
833 cairo_line_to (cr, x, height / 3);
834 cairo_stroke (cr);
835 cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
836 sprintf (text, "%d", i);
837 pango_layout_set_markup (
838 self->layout_normal, text, -1);
839 pango_layout_get_pixel_size (
840 self->layout_normal, &textw, &texth);
841 cairo_move_to (
842 cr,
843 x - textw / 2, height / 3 + 2);
844 pango_cairo_update_layout (
845 cr, self->layout_normal);
846 pango_cairo_show_layout (
847 cr, self->layout_normal);
848 }
849 /* draw 10secs */
850 i = 0;
851 if (ten_sec_interval > 0)
852 {
853 while ((curr_px =
854 self->px_per_10sec *
855 (i += ten_sec_interval) +
856 SPACE_BEFORE_START) <
857 rect->x + rect->width)
858 {
859 if (curr_px < rect->x)
860 continue;
861
862 cairo_set_source_rgb (
863 cr, 0.7, 0.7, 0.7);
864 cairo_set_line_width (
865 cr, 0.5);
866 double x = curr_px - rect->x;
867 cairo_move_to (
868 cr, x, 0);
869 cairo_line_to (
870 cr, x, height / 4);
871 cairo_stroke (cr);
872 if ((self->px_per_10sec >
873 RW_PX_TO_HIDE_BEATS * 2) &&
874 i % ten_secs_per_min != 0)
875 {
876 cairo_set_source_rgb (
877 cr,
878 0.5, 0.5, 0.5);
879 sprintf (
880 text, "%d:%02d",
881 i / ten_secs_per_min,
882 (i % ten_secs_per_min) * 10);
883 pango_layout_set_markup (
884 self->layout_small, text, -1);
885 pango_layout_get_pixel_size (
886 self->layout_small, &textw, &texth);
887 cairo_move_to (
888 cr, x - textw / 2,
889 height / 4 + 2);
890 pango_cairo_update_layout (
891 cr, self->layout_small);
892 pango_cairo_show_layout (
893 cr, self->layout_small);
894 }
895 }
896 }
897 /* draw secs */
898 i = 0;
899 if (sec_interval > 0)
900 {
901 while ((curr_px =
902 self->px_per_sec *
903 (i += sec_interval) +
904 SPACE_BEFORE_START) <
905 rect->x + rect->width)
906 {
907 if (curr_px < rect->x)
908 continue;
909
910 cairo_set_source_rgb (
911 cr, 0.6, 0.6, 0.6);
912 cairo_set_line_width (
913 cr, 0.3);
914 double x = curr_px - rect->x;
915 cairo_move_to (
916 cr, x, 0);
917 cairo_line_to (
918 cr, x, height / 6);
919 cairo_stroke (cr);
920
921 if ((self->px_per_sec >
922 RW_PX_TO_HIDE_BEATS * 2) &&
923 i % secs_per_10_sec != 0)
924 {
925 cairo_set_source_rgb (
926 cr, 0.5, 0.5, 0.5);
927 int secs_per_min = 60;
928 sprintf (
929 text, "%d:%02d",
930 i / secs_per_min,
931 ((i / secs_per_10_sec) %
932 ten_secs_per_min) * 10 +
933 i % secs_per_10_sec);
934 pango_layout_set_markup (
935 self->layout_small, text, -1);
936 pango_layout_get_pixel_size (
937 self->layout_small, &textw, &texth);
938 cairo_move_to (
939 cr, x - textw / 2,
940 height / 4 + 2);
941 pango_cairo_update_layout (
942 cr, self->layout_small);
943 pango_cairo_show_layout (
944 cr, self->layout_small);
945 }
946 }
947 }
948 }
949 else /* else if BBT display */
950 {
951 /* get sixteenth interval */
952 int sixteenth_interval =
953 ruler_widget_get_sixteenth_interval (self);
954
955 /* get beat interval */
956 int beat_interval =
957 ruler_widget_get_beat_interval (self);
958
959 /* get the interval for bars */
960 int bar_interval =
961 (int)
962 MAX ((RW_PX_TO_HIDE_BEATS) /
963 (double) self->px_per_bar, 1.0);
964
965 /* draw bars */
966 i = - bar_interval;
967 while (
968 (curr_px =
969 self->px_per_bar * (i += bar_interval) +
970 SPACE_BEFORE_START) <
971 rect->x + rect->width + 20.0)
972 {
973 if (curr_px + 20.0 < rect->x)
974 continue;
975
976 cairo_set_source_rgb (cr, 1, 1, 1);
977 cairo_set_line_width (cr, 1);
978 double x = curr_px - rect->x;
979 cairo_move_to (cr, x, 0);
980 cairo_line_to (cr, x, height / 3);
981 cairo_stroke (cr);
982 cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
983 sprintf (text, "%d", i + 1);
984 pango_layout_set_markup (
985 self->layout_normal, text, -1);
986 pango_layout_get_pixel_size (
987 self->layout_normal, &textw, &texth);
988 cairo_move_to (
989 cr,
990 x - textw / 2, height / 3 + 2);
991 pango_cairo_update_layout (
992 cr, self->layout_normal);
993 pango_cairo_show_layout (
994 cr, self->layout_normal);
995 }
996 /* draw beats */
997 i = 0;
998 if (beat_interval > 0)
999 {
1000 while ((curr_px =
1001 self->px_per_beat *
1002 (i += beat_interval) +
1003 SPACE_BEFORE_START) <
1004 rect->x + rect->width)
1005 {
1006 if (curr_px < rect->x)
1007 continue;
1008
1009 cairo_set_source_rgb (
1010 cr, 0.7, 0.7, 0.7);
1011 cairo_set_line_width (
1012 cr, 0.5);
1013 double x = curr_px - rect->x;
1014 cairo_move_to (
1015 cr, x, 0);
1016 cairo_line_to (
1017 cr, x, height / 4);
1018 cairo_stroke (cr);
1019 if ((self->px_per_beat >
1020 RW_PX_TO_HIDE_BEATS * 2) &&
1021 i % beats_per_bar != 0)
1022 {
1023 cairo_set_source_rgb (
1024 cr,
1025 0.5, 0.5, 0.5);
1026 sprintf (
1027 text, "%d.%d",
1028 i / beats_per_bar + 1,
1029 i % beats_per_bar + 1);
1030 pango_layout_set_markup (
1031 self->layout_small, text, -1);
1032 pango_layout_get_pixel_size (
1033 self->layout_small, &textw, &texth);
1034 cairo_move_to (
1035 cr, x - textw / 2,
1036 height / 4 + 2);
1037 pango_cairo_update_layout (
1038 cr, self->layout_small);
1039 pango_cairo_show_layout (
1040 cr, self->layout_small);
1041 }
1042 }
1043 }
1044 /* draw sixteenths */
1045 i = 0;
1046 if (sixteenth_interval > 0)
1047 {
1048 while ((curr_px =
1049 self->px_per_sixteenth *
1050 (i += sixteenth_interval) +
1051 SPACE_BEFORE_START) <
1052 rect->x + rect->width)
1053 {
1054 if (curr_px < rect->x)
1055 continue;
1056
1057 cairo_set_source_rgb (
1058 cr, 0.6, 0.6, 0.6);
1059 cairo_set_line_width (
1060 cr, 0.3);
1061 double x = curr_px - rect->x;
1062 cairo_move_to (
1063 cr, x, 0);
1064 cairo_line_to (
1065 cr, x, height / 6);
1066 cairo_stroke (cr);
1067
1068 if ((self->px_per_sixteenth >
1069 RW_PX_TO_HIDE_BEATS * 2) &&
1070 i % sixteenths_per_beat != 0)
1071 {
1072 cairo_set_source_rgb (
1073 cr, 0.5, 0.5, 0.5);
1074 sprintf (
1075 text, "%d.%d.%d",
1076 i / TRANSPORT->
1077 sixteenths_per_bar + 1,
1078 ((i / sixteenths_per_beat) %
1079 beats_per_bar) + 1,
1080 i % sixteenths_per_beat + 1);
1081 pango_layout_set_markup (
1082 self->layout_small, text, -1);
1083 pango_layout_get_pixel_size (
1084 self->layout_small, &textw, &texth);
1085 cairo_move_to (
1086 cr, x - textw / 2,
1087 height / 4 + 2);
1088 pango_cairo_update_layout (
1089 cr, self->layout_small);
1090 pango_cairo_show_layout (
1091 cr, self->layout_small);
1092 }
1093 }
1094 }
1095 }
1096 }
1097
1098 static gboolean
ruler_draw_cb(GtkWidget * widget,cairo_t * cr,RulerWidget * self)1099 ruler_draw_cb (
1100 GtkWidget * widget,
1101 cairo_t * cr,
1102 RulerWidget * self)
1103 {
1104 /* engine is run only set after everything is set
1105 * up so this is a good way to decide if we
1106 * should draw or not */
1107 if (!PROJECT || !AUDIO_ENGINE ||
1108 !g_atomic_int_get (&AUDIO_ENGINE->run) ||
1109 self->px_per_bar < 2.0)
1110 {
1111 return FALSE;
1112 }
1113
1114 GdkRectangle rect;
1115 gdk_cairo_get_clip_rectangle (cr, &rect);
1116
1117 if (self->redraw ||
1118 !gdk_rectangle_equal (
1119 &rect, &self->last_rect))
1120 {
1121 self->last_rect = rect;
1122
1123 GtkStyleContext *context =
1124 gtk_widget_get_style_context (
1125 GTK_WIDGET (self));
1126
1127 z_cairo_reset_caches (
1128 &self->cached_cr,
1129 &self->cached_surface, rect.width,
1130 rect.height, cr);
1131
1132 cairo_t * cr_to_use = self->cached_cr;
1133
1134 /* ----- ruler background ------- */
1135
1136 int height =
1137 gtk_widget_get_allocated_height (
1138 GTK_WIDGET (self));
1139
1140 gtk_render_background (
1141 context, cr_to_use,
1142 0, 0, rect.width, rect.height);
1143
1144 /* if timeline, draw loop background */
1145 /* FIXME use rect */
1146 double start_px = 0, end_px = 0;
1147 if (self->type == TYPE (TIMELINE))
1148 {
1149 start_px =
1150 ui_pos_to_px_timeline (
1151 &TRANSPORT->loop_start_pos, 1);
1152 end_px =
1153 ui_pos_to_px_timeline (
1154 &TRANSPORT->loop_end_pos, 1);
1155 }
1156 else if (self->type == TYPE (EDITOR))
1157 {
1158 start_px =
1159 ui_pos_to_px_editor (
1160 &TRANSPORT->loop_start_pos, 1);
1161 end_px =
1162 ui_pos_to_px_editor (
1163 &TRANSPORT->loop_end_pos, 1);
1164 }
1165
1166 if (TRANSPORT->loop)
1167 cairo_set_source_rgba (
1168 cr_to_use, 0, 0.9, 0.7, 0.25);
1169 else
1170 cairo_set_source_rgba (
1171 cr_to_use, 0.5, 0.5, 0.5, 0.25);
1172 cairo_set_line_width (cr_to_use, 2);
1173
1174 /* if transport loop start is within the
1175 * screen */
1176 if (start_px + 2.0 > rect.x &&
1177 start_px <= rect.x + rect.width)
1178 {
1179 /* draw the loop start line */
1180 double x =
1181 (start_px - rect.x);
1182 cairo_move_to (
1183 cr_to_use, x, 0);
1184 cairo_line_to (
1185 cr_to_use, x, rect.height);
1186 cairo_stroke (cr_to_use);
1187 }
1188 /* if transport loop end is within the
1189 * screen */
1190 if (end_px + 2.0 > rect.x &&
1191 end_px <= rect.x + rect.width)
1192 {
1193 /* draw the loop end line */
1194 double x =
1195 (end_px - rect.x);
1196 cairo_move_to (
1197 cr_to_use, x, 0);
1198 cairo_line_to (
1199 cr_to_use, x, rect.height);
1200 cairo_stroke (cr_to_use);
1201 }
1202
1203 /* create gradient for loop area */
1204 cairo_pattern_t * pat =
1205 cairo_pattern_create_linear (
1206 0.0, 0.0, 0.0, (height * 3) / 4);
1207 if (TRANSPORT->loop)
1208 {
1209 cairo_pattern_add_color_stop_rgba (
1210 pat, 1, 0, 0.9, 0.7, 0.1);
1211 cairo_pattern_add_color_stop_rgba (
1212 pat, 0, 0, 0.9, 0.7, 0.2);
1213 }
1214 else
1215 {
1216 cairo_pattern_add_color_stop_rgba (
1217 pat, 1, 0.5, 0.5, 0.5, 0.1);
1218 cairo_pattern_add_color_stop_rgba (
1219 pat, 0, 0.5, 0.5, 0.5, 0.2);
1220 }
1221
1222 double loop_start_local_x =
1223 MAX (0, start_px - rect.x);
1224 cairo_rectangle (
1225 cr_to_use,
1226 loop_start_local_x, 0,
1227 end_px - MAX (rect.x, start_px),
1228 rect.height);
1229 cairo_set_source (cr_to_use, pat);
1230 cairo_fill (cr_to_use);
1231 cairo_pattern_destroy (pat);
1232
1233 draw_lines_and_labels (
1234 self, cr_to_use, &rect);
1235
1236 /* ----- draw range --------- */
1237
1238 if (TRANSPORT->has_range)
1239 {
1240 int range1_first =
1241 position_is_before_or_equal (
1242 &TRANSPORT->range_1,
1243 &TRANSPORT->range_2);
1244
1245 GdkRectangle dr;
1246 if (range1_first)
1247 {
1248 dr.x =
1249 ui_pos_to_px_timeline (
1250 &TRANSPORT->range_1, true);
1251 dr.width =
1252 ui_pos_to_px_timeline (
1253 &TRANSPORT->range_2, true) - dr.x;
1254 }
1255 else
1256 {
1257 dr.x =
1258 ui_pos_to_px_timeline (
1259 &TRANSPORT->range_2, true);
1260 dr.width =
1261 ui_pos_to_px_timeline (
1262 &TRANSPORT->range_1, true) - dr.x;
1263 }
1264 dr.y = 0;
1265 dr.height =
1266 gtk_widget_get_allocated_height (
1267 GTK_WIDGET (self)) /
1268 RW_RANGE_HEIGHT_DIVISOR;
1269
1270 dr.x -= rect.x;
1271
1272 /* fill */
1273 cairo_set_source_rgba (
1274 cr_to_use, 1, 1, 1, 0.27);
1275 cairo_rectangle (
1276 cr_to_use, dr.x, dr.y,
1277 dr.width, dr.height);
1278 cairo_fill (cr_to_use);
1279
1280 /* draw edges */
1281 cairo_set_source_rgba (
1282 cr_to_use, 1, 1, 1, 0.7);
1283 cairo_rectangle (
1284 cr_to_use, dr.x, dr.y, 2, dr.height);
1285 cairo_fill (cr_to_use);
1286 cairo_rectangle (
1287 cr_to_use, dr.x + dr.width, dr.y, 2,
1288 dr.height - dr.y);
1289 cairo_fill (cr_to_use);
1290 }
1291
1292 /* ----- draw regions --------- */
1293
1294 if (self->type == TYPE (EDITOR))
1295 {
1296 draw_regions (self, &rect);
1297 }
1298
1299 /* ------ draw markers ------- */
1300
1301 draw_cue_point (
1302 self, cr_to_use, &rect);
1303 draw_loop_start (
1304 self, cr_to_use, &rect);
1305 draw_loop_end (
1306 self, cr_to_use, &rect);
1307
1308 if (self->type == TYPE (TIMELINE) &&
1309 TRANSPORT->punch_mode)
1310 {
1311 draw_punch_in (
1312 self, cr_to_use, &rect);
1313 draw_punch_out (
1314 self, cr_to_use, &rect);
1315 }
1316
1317 /* --------- draw playhead ---------- */
1318
1319 draw_playhead (self, cr_to_use, &rect);
1320
1321 self->redraw = 0;
1322 }
1323
1324 cairo_set_source_surface (
1325 cr, self->cached_surface, rect.x, rect.y);
1326 cairo_paint (cr);
1327
1328 return FALSE;
1329 }
1330
1331 #undef beats_per_bar
1332 #undef sixteenths_per_beat
1333
1334 static int
is_loop_start_hit(RulerWidget * self,double x,double y)1335 is_loop_start_hit (
1336 RulerWidget * self,
1337 double x,
1338 double y)
1339 {
1340 GdkRectangle rect;
1341 get_loop_start_rect (self, &rect);
1342
1343 return
1344 x >= rect.x && x <= rect.x + rect.width &&
1345 y >= rect.y && y <= rect.y + rect.height;
1346 }
1347
1348 static int
is_loop_end_hit(RulerWidget * self,double x,double y)1349 is_loop_end_hit (
1350 RulerWidget * self,
1351 double x,
1352 double y)
1353 {
1354 GdkRectangle rect;
1355 get_loop_end_rect (self, &rect);
1356
1357 return
1358 x >= rect.x && x <= rect.x + rect.width &&
1359 y >= rect.y && y <= rect.y + rect.height;
1360 }
1361
1362 static int
is_punch_in_hit(RulerWidget * self,double x,double y)1363 is_punch_in_hit (
1364 RulerWidget * self,
1365 double x,
1366 double y)
1367 {
1368 GdkRectangle rect;
1369 get_punch_in_rect (self, &rect);
1370
1371 return
1372 x >= rect.x && x <= rect.x + rect.width &&
1373 y >= rect.y && y <= rect.y + rect.height;
1374 }
1375
1376 static int
is_punch_out_hit(RulerWidget * self,double x,double y)1377 is_punch_out_hit (
1378 RulerWidget * self,
1379 double x,
1380 double y)
1381 {
1382 GdkRectangle rect;
1383 get_punch_out_rect (self, &rect);
1384
1385 return
1386 x >= rect.x && x <= rect.x + rect.width &&
1387 y >= rect.y && y <= rect.y + rect.height;
1388 }
1389
1390 static bool
is_clip_start_hit(RulerWidget * self,double x,double y)1391 is_clip_start_hit (
1392 RulerWidget * self,
1393 double x,
1394 double y)
1395 {
1396 if (self->type == TYPE (EDITOR))
1397 {
1398 GdkRectangle rect;
1399 get_clip_start_rect (self, &rect);
1400
1401 return
1402 x >= rect.x && x <= rect.x + rect.width &&
1403 y >= rect.y && y <= rect.y + rect.height;
1404 }
1405 else
1406 return false;
1407 }
1408
1409 static void
get_range_rect(RulerWidget * self,RulerWidgetRangeType type,GdkRectangle * rect)1410 get_range_rect (
1411 RulerWidget * self,
1412 RulerWidgetRangeType type,
1413 GdkRectangle * rect)
1414 {
1415 g_return_if_fail (self->type == TYPE (TIMELINE));
1416
1417 Position tmp;
1418 transport_get_range_pos (
1419 TRANSPORT,
1420 type == RW_RANGE_END ? false : true,
1421 &tmp);
1422 rect->x = ui_pos_to_px_timeline (&tmp, true);
1423 if (type == RW_RANGE_END)
1424 {
1425 rect->x -= RW_CUE_MARKER_WIDTH;
1426 }
1427 rect->y = 0;
1428 if (type == RW_RANGE_FULL)
1429 {
1430 transport_get_range_pos (
1431 TRANSPORT, false, &tmp);
1432 double px =
1433 ui_pos_to_px_timeline (&tmp, true);
1434 rect->width = (int) px - rect->x;
1435 }
1436 else
1437 {
1438 rect->width = RW_CUE_MARKER_WIDTH;
1439 }
1440 rect->height =
1441 gtk_widget_get_allocated_height (
1442 GTK_WIDGET (self)) /
1443 RW_RANGE_HEIGHT_DIVISOR;
1444 }
1445
1446 bool
ruler_widget_is_range_hit(RulerWidget * self,RulerWidgetRangeType type,double x,double y)1447 ruler_widget_is_range_hit (
1448 RulerWidget * self,
1449 RulerWidgetRangeType type,
1450 double x,
1451 double y)
1452 {
1453 if (self->type == TYPE (TIMELINE) &&
1454 TRANSPORT->has_range)
1455 {
1456 GdkRectangle rect;
1457 memset (&rect, 0, sizeof (GdkRectangle));
1458 get_range_rect (self, type, &rect);
1459
1460 return
1461 x >= rect.x && x <= rect.x + rect.width &&
1462 y >= rect.y && y <= rect.y + rect.height;
1463 }
1464 else
1465 {
1466 return false;
1467 }
1468 }
1469
1470 static gboolean
multipress_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,RulerWidget * self)1471 multipress_pressed (
1472 GtkGestureMultiPress *gesture,
1473 gint n_press,
1474 gdouble x,
1475 gdouble y,
1476 RulerWidget * self)
1477 {
1478 GdkModifierType state_mask;
1479 ui_get_modifier_type_from_gesture (
1480 GTK_GESTURE_SINGLE (gesture),
1481 &state_mask);
1482 if (state_mask & GDK_SHIFT_MASK)
1483 self->shift_held = 1;
1484
1485 if (n_press == 2)
1486 {
1487 if (self->type == TYPE (TIMELINE))
1488 {
1489 Position pos;
1490 ui_px_to_pos_timeline (
1491 x, &pos, 1);
1492 if (!self->shift_held
1493 &&
1494 SNAP_GRID_ANY_SNAP (
1495 SNAP_GRID_TIMELINE))
1496 {
1497 position_snap (
1498 &pos, &pos, NULL, NULL,
1499 SNAP_GRID_TIMELINE);
1500 }
1501 position_set_to_pos (&TRANSPORT->cue_pos,
1502 &pos);
1503 }
1504 if (self->type == TYPE (EDITOR))
1505 {
1506 }
1507 }
1508
1509 return FALSE;
1510 }
1511
1512 static void
on_display_type_changed(GtkCheckMenuItem * menuitem,RulerWidget * self)1513 on_display_type_changed (
1514 GtkCheckMenuItem * menuitem,
1515 RulerWidget * self)
1516 {
1517 if (menuitem == self->bbt_display_check &&
1518 gtk_check_menu_item_get_active (menuitem))
1519 {
1520 g_settings_set_enum (
1521 S_UI, "ruler-display",
1522 TRANSPORT_DISPLAY_BBT);
1523 }
1524 else if (menuitem == self->time_display_check &&
1525 gtk_check_menu_item_get_active (
1526 menuitem))
1527 {
1528 g_settings_set_enum (
1529 S_UI, "ruler-display",
1530 TRANSPORT_DISPLAY_TIME);
1531 }
1532
1533 EVENTS_PUSH (ET_RULER_DISPLAY_TYPE_CHANGED, NULL);
1534 }
1535
1536 static gboolean
multipress_right_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,RulerWidget * self)1537 multipress_right_pressed (
1538 GtkGestureMultiPress *gesture,
1539 gint n_press,
1540 gdouble x,
1541 gdouble y,
1542 RulerWidget * self)
1543 {
1544 /* right click */
1545 if (n_press == 1)
1546 {
1547 GtkWidget *menu, *menuitem;
1548 menu = gtk_menu_new();
1549
1550 /* switch display */
1551 GSList *group = NULL;
1552 menuitem =
1553 gtk_radio_menu_item_new_with_label (
1554 group, _("BBT display"));
1555 group =
1556 gtk_radio_menu_item_get_group (
1557 GTK_RADIO_MENU_ITEM (menuitem));
1558 gtk_check_menu_item_set_active (
1559 GTK_CHECK_MENU_ITEM (menuitem),
1560 g_settings_get_enum (
1561 S_UI, "ruler-display") ==
1562 TRANSPORT_DISPLAY_BBT);
1563 g_signal_connect (
1564 G_OBJECT (menuitem), "toggled",
1565 G_CALLBACK (on_display_type_changed), self);
1566 self->bbt_display_check =
1567 GTK_CHECK_MENU_ITEM (menuitem);
1568 gtk_menu_shell_append (
1569 GTK_MENU_SHELL (menu), menuitem);
1570
1571 menuitem =
1572 gtk_radio_menu_item_new_with_label (
1573 group, _("Time display"));
1574 gtk_check_menu_item_set_active (
1575 GTK_CHECK_MENU_ITEM (menuitem),
1576 g_settings_get_enum (
1577 S_UI, "ruler-display") ==
1578 TRANSPORT_DISPLAY_TIME);
1579 g_signal_connect (
1580 G_OBJECT (menuitem), "toggled",
1581 G_CALLBACK (on_display_type_changed), self);
1582 self->time_display_check =
1583 GTK_CHECK_MENU_ITEM (menuitem);
1584 gtk_menu_shell_append (
1585 GTK_MENU_SHELL (menu), menuitem);
1586
1587 gtk_widget_show_all (menu);
1588
1589 gtk_menu_popup_at_pointer (
1590 GTK_MENU (menu), NULL);
1591 }
1592
1593 return FALSE;
1594 }
1595
1596 static void
drag_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,RulerWidget * self)1597 drag_begin (
1598 GtkGestureDrag * gesture,
1599 gdouble start_x,
1600 gdouble start_y,
1601 RulerWidget * self)
1602 {
1603 self->start_x = start_x;
1604 self->start_y = start_y;
1605
1606 if (self->type == TYPE (TIMELINE))
1607 {
1608 self->range1_first =
1609 position_is_before_or_equal (
1610 &TRANSPORT->range_1,
1611 &TRANSPORT->range_2);
1612 }
1613
1614 int height =
1615 gtk_widget_get_allocated_height (
1616 GTK_WIDGET (self));
1617
1618 int punch_in_hit =
1619 is_punch_in_hit (self, start_x, start_y);
1620 int punch_out_hit =
1621 is_punch_out_hit (self, start_x, start_y);
1622 int loop_start_hit =
1623 is_loop_start_hit (self, start_x, start_y);
1624 int loop_end_hit =
1625 is_loop_end_hit (self, start_x, start_y);
1626 int clip_start_hit =
1627 is_clip_start_hit (self, start_x, start_y);
1628
1629 ZRegion * region =
1630 clip_editor_get_region (CLIP_EDITOR);
1631 ArrangerObject * r_obj =
1632 (ArrangerObject *) region;
1633
1634 /* if one of the markers hit */
1635 if (punch_in_hit)
1636 {
1637 self->action =
1638 UI_OVERLAY_ACTION_STARTING_MOVING;
1639 self->target =
1640 RW_TARGET_PUNCH_IN;
1641 }
1642 else if (punch_out_hit)
1643 {
1644 self->action =
1645 UI_OVERLAY_ACTION_STARTING_MOVING;
1646 self->target =
1647 RW_TARGET_PUNCH_OUT;
1648 }
1649 else if (loop_start_hit)
1650 {
1651 self->action =
1652 UI_OVERLAY_ACTION_STARTING_MOVING;
1653 self->target =
1654 RW_TARGET_LOOP_START;
1655 if (self->type == TYPE (EDITOR))
1656 {
1657 g_return_if_fail (region);
1658 self->drag_start_pos =
1659 r_obj->loop_start_pos;
1660 }
1661 else
1662 {
1663 self->drag_start_pos =
1664 TRANSPORT->loop_start_pos;
1665 }
1666 }
1667 else if (loop_end_hit)
1668 {
1669 self->action =
1670 UI_OVERLAY_ACTION_STARTING_MOVING;
1671 self->target =
1672 RW_TARGET_LOOP_END;
1673 if (self->type == TYPE (EDITOR))
1674 {
1675 g_return_if_fail (region);
1676 self->drag_start_pos =
1677 r_obj->loop_end_pos;
1678 }
1679 else
1680 {
1681 self->drag_start_pos =
1682 TRANSPORT->loop_end_pos;
1683 }
1684 }
1685 else if (clip_start_hit)
1686 {
1687 self->action =
1688 UI_OVERLAY_ACTION_STARTING_MOVING;
1689 self->target = RW_TARGET_CLIP_START;
1690 if (self->type == TYPE (EDITOR))
1691 {
1692 g_return_if_fail (region);
1693 self->drag_start_pos =
1694 r_obj->clip_start_pos;
1695 }
1696 }
1697 else
1698 {
1699 if (self->type == TYPE (TIMELINE))
1700 {
1701 timeline_ruler_on_drag_begin_no_marker_hit (
1702 self, start_x, start_y, height);
1703 }
1704 else if (self->type == TYPE (EDITOR))
1705 {
1706 editor_ruler_on_drag_begin_no_marker_hit (
1707 self, start_x, start_y);
1708 }
1709 }
1710
1711 self->last_offset_x = 0;
1712 self->dragging = 1;
1713 }
1714
1715 static void
drag_end(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,RulerWidget * self)1716 drag_end (GtkGestureDrag *gesture,
1717 gdouble offset_x,
1718 gdouble offset_y,
1719 RulerWidget * self)
1720 {
1721 self->start_x = 0;
1722 self->start_y = 0;
1723 self->shift_held = 0;
1724 self->dragging = 0;
1725
1726 if (self->type == TYPE (TIMELINE))
1727 timeline_ruler_on_drag_end (self);
1728 else if (self->type == TYPE (EDITOR))
1729 editor_ruler_on_drag_end (self);
1730
1731 self->action = UI_OVERLAY_ACTION_NONE;
1732 }
1733
1734 static gboolean
on_grab_broken(GtkWidget * widget,GdkEvent * event,gpointer user_data)1735 on_grab_broken (
1736 GtkWidget *widget,
1737 GdkEvent *event,
1738 gpointer user_data)
1739 {
1740 g_message ("ruler grab broken");
1741 return FALSE;
1742 }
1743
1744 static void
on_motion(GtkDrawingArea * da,GdkEventMotion * event,RulerWidget * self)1745 on_motion (
1746 GtkDrawingArea * da,
1747 GdkEventMotion *event,
1748 RulerWidget * self)
1749 {
1750 /* drag-update didn't work so do the drag-update
1751 * here */
1752 if (self->dragging
1753 && event->type != GDK_LEAVE_NOTIFY)
1754 {
1755 GdkModifierType state_mask =
1756 event->state;
1757
1758 if (state_mask & GDK_SHIFT_MASK)
1759 self->shift_held = 1;
1760 else
1761 self->shift_held = 0;
1762
1763 if (ACTION_IS (STARTING_MOVING))
1764 {
1765 self->action = UI_OVERLAY_ACTION_MOVING;
1766 }
1767
1768 if (self->type == TYPE (TIMELINE))
1769 {
1770 timeline_ruler_on_drag_update (
1771 self, event->x - self->start_x,
1772 event->y - self->start_y);
1773 }
1774 else if (self->type == TYPE (EDITOR))
1775 {
1776 editor_ruler_on_drag_update (
1777 self, event->x - self->start_x,
1778 event->y - self->start_y);
1779 }
1780
1781 self->last_offset_x =
1782 event->x - self->start_x;
1783
1784 return;
1785 }
1786
1787 int height =
1788 gtk_widget_get_allocated_height (
1789 GTK_WIDGET (self));
1790 if (event->type == GDK_MOTION_NOTIFY)
1791 {
1792 bool punch_in_hit =
1793 is_punch_in_hit (
1794 self, event->x, event->y);
1795 bool punch_out_hit =
1796 is_punch_out_hit (
1797 self, event->x, event->y);
1798 bool loop_start_hit =
1799 is_loop_start_hit (
1800 self, event->x, event->y);
1801 bool loop_end_hit =
1802 is_loop_end_hit (
1803 self, event->x, event->y);
1804 bool clip_start_hit =
1805 is_clip_start_hit (
1806 self, event->x, event->y);
1807 bool range_start_hit =
1808 ruler_widget_is_range_hit (
1809 self, RW_RANGE_START,
1810 event->x, event->y);
1811 bool range_end_hit =
1812 ruler_widget_is_range_hit (
1813 self, RW_RANGE_END, event->x, event->y);
1814
1815 if (punch_in_hit ||
1816 loop_start_hit || clip_start_hit ||
1817 range_start_hit)
1818 {
1819 ui_set_cursor_from_name (
1820 GTK_WIDGET (self), "w-resize");
1821 }
1822 else if (punch_out_hit ||
1823 loop_end_hit || range_end_hit)
1824 {
1825 ui_set_cursor_from_name (
1826 GTK_WIDGET (self), "e-resize");
1827 }
1828 /* if lower 3/4ths */
1829 else if (event->y > (height * 1) / 4)
1830 {
1831 /* set cursor to normal */
1832 ui_set_cursor_from_name (
1833 GTK_WIDGET (self), "default");
1834 }
1835 else /* upper 1/4th */
1836 {
1837 if (ruler_widget_is_range_hit (
1838 self, RW_RANGE_FULL, event->x,
1839 event->y))
1840 {
1841 /* set cursor to movable */
1842 ui_set_hand_cursor (self);
1843 }
1844 else
1845 {
1846 /* set cursor to range selection */
1847 ui_set_cursor_from_name (
1848 GTK_WIDGET (self), "text");
1849 }
1850 }
1851 }
1852 else if (event->type == GDK_LEAVE_NOTIFY)
1853 {
1854 ui_set_cursor_from_name (
1855 GTK_WIDGET (self), "default");
1856 }
1857 }
1858
1859 static GtkScrolledWindow *
get_scrolled_window(RulerWidget * self)1860 get_scrolled_window (
1861 RulerWidget * self)
1862 {
1863 switch (self->type)
1864 {
1865 case TYPE (TIMELINE):
1866 return MW_TIMELINE_PANEL->ruler_scroll;
1867 case TYPE (EDITOR):
1868 return MW_CLIP_EDITOR_INNER->ruler_scroll;
1869 }
1870
1871 return NULL;
1872 }
1873
1874 /**
1875 * Returns the current rectangle to draw in.
1876 *
1877 * @param rect The rectangle to fill in.
1878 */
1879 static void
get_current_rect(RulerWidget * self,GdkRectangle * rect)1880 get_current_rect (
1881 RulerWidget * self,
1882 GdkRectangle * rect)
1883 {
1884 GtkScrolledWindow * scroll =
1885 get_scrolled_window (self);
1886 GtkAdjustment * xadj =
1887 gtk_scrolled_window_get_hadjustment (
1888 scroll);
1889 rect->x =
1890 (int) gtk_adjustment_get_value (xadj);
1891 GtkAdjustment * yadj =
1892 gtk_scrolled_window_get_vadjustment (scroll);
1893 rect->y =
1894 (int) gtk_adjustment_get_value (yadj);
1895 rect->height =
1896 gtk_widget_get_allocated_height (
1897 GTK_WIDGET (scroll));
1898 rect->width =
1899 gtk_widget_get_allocated_width (
1900 GTK_WIDGET (scroll));
1901 }
1902
1903 /**
1904 * Queues a redraw of the whole visible ruler.
1905 */
1906 void
ruler_widget_redraw_whole(RulerWidget * self)1907 ruler_widget_redraw_whole (
1908 RulerWidget * self)
1909 {
1910 GdkRectangle rect;
1911 get_current_rect (self, &rect);
1912
1913 /* redraw visible area */
1914 self->redraw = 1;
1915 gtk_widget_queue_draw_area (
1916 GTK_WIDGET (self), rect.x, rect.y,
1917 rect.width, rect.height);
1918 }
1919
1920 /**
1921 * Only redraws the playhead part.
1922 */
1923 void
ruler_widget_redraw_playhead(RulerWidget * self)1924 ruler_widget_redraw_playhead (
1925 RulerWidget * self)
1926 {
1927 GdkRectangle rect;
1928 get_current_rect (self, &rect);
1929
1930 int playhead_x = get_playhead_px (self);
1931 int min_x =
1932 MIN (self->last_playhead_px, playhead_x);
1933 min_x =
1934 MAX (
1935 min_x - (RW_PLAYHEAD_TRIANGLE_WIDTH + 40),
1936 rect.x);
1937 int max_x =
1938 MAX (self->last_playhead_px, playhead_x);
1939 max_x =
1940 MIN (
1941 max_x + RW_PLAYHEAD_TRIANGLE_WIDTH,
1942 rect.x + rect.width);
1943
1944 /* skip if playhead is not in the visible
1945 * rectangle */
1946 int width = max_x - min_x;
1947 if (width < 0)
1948 {
1949 #if 0
1950 g_debug (
1951 "playhead not currently visible in ruler, "
1952 "skipping redraw");
1953 #endif
1954 return;
1955 }
1956
1957 gtk_widget_queue_draw_area (
1958 GTK_WIDGET (self), min_x, rect.y,
1959 width, rect.height);
1960 }
1961
1962 void
ruler_widget_refresh(RulerWidget * self)1963 ruler_widget_refresh (RulerWidget * self)
1964 {
1965 /*adjust for zoom level*/
1966 self->px_per_tick =
1967 DEFAULT_PX_PER_TICK *
1968 ruler_widget_get_zoom_level (self);
1969 self->px_per_sixteenth =
1970 self->px_per_tick * TICKS_PER_SIXTEENTH_NOTE;
1971 self->px_per_beat =
1972 self->px_per_tick * TRANSPORT->ticks_per_beat;
1973 int beats_per_bar =
1974 tempo_track_get_beats_per_bar (P_TEMPO_TRACK);
1975 self->px_per_bar =
1976 self->px_per_beat * beats_per_bar;
1977
1978 Position pos;
1979 position_from_seconds (&pos, 1.0);
1980 self->px_per_min =
1981 60.0 * pos.ticks * self->px_per_tick;
1982 self->px_per_10sec =
1983 10.0 * pos.ticks * self->px_per_tick;
1984 self->px_per_sec =
1985 pos.ticks * self->px_per_tick;
1986 self->px_per_100ms =
1987 0.1 * pos.ticks * self->px_per_tick;
1988
1989 position_set_to_bar (
1990 &pos, TRANSPORT->total_bars + 1);
1991 double prev_total_px = self->total_px;
1992 self->total_px =
1993 self->px_per_tick *
1994 (double) position_to_ticks (&pos);
1995
1996 /* if size changed */
1997 if (!math_doubles_equal_epsilon (
1998 prev_total_px, self->total_px, 0.1))
1999 {
2000 /* set the size */
2001 gtk_widget_set_size_request (
2002 GTK_WIDGET (self),
2003 (int) self->total_px, -1);
2004
2005 if (self->type == TYPE (TIMELINE))
2006 {
2007 /*gtk_widget_set_visible (*/
2008 /*GTK_WIDGET (timeline_ruler->range),*/
2009 /*PROJECT->has_range);*/
2010 }
2011
2012 EVENTS_PUSH (
2013 ET_RULER_SIZE_CHANGED, self);
2014 }
2015 }
2016
2017 GtkScrolledWindow *
ruler_widget_get_parent_scroll(RulerWidget * self)2018 ruler_widget_get_parent_scroll (
2019 RulerWidget * self)
2020 {
2021 if (self->type == TYPE (TIMELINE))
2022 {
2023 return MW_TIMELINE_PANEL->ruler_scroll;
2024 }
2025 else if (self->type == TYPE (EDITOR))
2026 {
2027 return MW_CLIP_EDITOR_INNER->ruler_scroll;
2028 }
2029
2030 g_return_val_if_reached (NULL);
2031 }
2032
2033 /**
2034 * Sets zoom level and disables/enables buttons
2035 * accordingly.
2036 *
2037 * @return Whether the zoom level was set.
2038 */
2039 bool
ruler_widget_set_zoom_level(RulerWidget * self,double zoom_level)2040 ruler_widget_set_zoom_level (
2041 RulerWidget * self,
2042 double zoom_level)
2043 {
2044 if (zoom_level > MAX_ZOOM_LEVEL)
2045 {
2046 actions_set_app_action_enabled (
2047 "zoom-in", false);
2048 }
2049 else
2050 {
2051 actions_set_app_action_enabled (
2052 "zoom-in", true);
2053 }
2054 if (zoom_level < MIN_ZOOM_LEVEL)
2055 {
2056 actions_set_app_action_enabled (
2057 "zoom-out", false);
2058 }
2059 else
2060 {
2061 actions_set_app_action_enabled (
2062 "zoom-out", true);
2063 }
2064
2065 int update = zoom_level >= MIN_ZOOM_LEVEL &&
2066 zoom_level <= MAX_ZOOM_LEVEL;
2067
2068 if (update)
2069 {
2070 if (self->type == RULER_WIDGET_TYPE_TIMELINE)
2071 {
2072 PRJ_TIMELINE->editor_settings.
2073 hzoom_level =
2074 zoom_level;
2075 }
2076 else if (self->type ==
2077 RULER_WIDGET_TYPE_EDITOR)
2078 {
2079 ArrangerWidget * arr =
2080 clip_editor_inner_widget_get_visible_arranger (
2081 MW_CLIP_EDITOR_INNER);
2082 EditorSettings * settings =
2083 arranger_widget_get_editor_settings (
2084 arr);
2085 g_return_val_if_fail (settings, false);
2086 settings->hzoom_level = zoom_level;
2087 }
2088 ruler_widget_refresh (self);
2089 return true;
2090 }
2091 else
2092 {
2093 return false;
2094 }
2095 }
2096
2097 static void
ruler_widget_init(RulerWidget * self)2098 ruler_widget_init (RulerWidget * self)
2099 {
2100 /* make the widget able to notify */
2101 gtk_widget_add_events (
2102 GTK_WIDGET (self), GDK_ALL_EVENTS_MASK);
2103
2104 self->drag = GTK_GESTURE_DRAG (
2105 gtk_gesture_drag_new (GTK_WIDGET (self)));
2106 self->multipress = GTK_GESTURE_MULTI_PRESS (
2107 gtk_gesture_multi_press_new (
2108 GTK_WIDGET (self)));
2109 GtkGestureMultiPress * right_mp =
2110 GTK_GESTURE_MULTI_PRESS (
2111 gtk_gesture_multi_press_new (
2112 GTK_WIDGET (self)));
2113 gtk_gesture_single_set_button (
2114 GTK_GESTURE_SINGLE (right_mp),
2115 GDK_BUTTON_SECONDARY);
2116
2117 self->layout_normal =
2118 gtk_widget_create_pango_layout (
2119 GTK_WIDGET (self), NULL);
2120 PangoFontDescription *desc =
2121 pango_font_description_from_string (
2122 "Monospace 11");
2123 pango_layout_set_font_description (
2124 self->layout_normal, desc);
2125 pango_font_description_free (desc);
2126 self->layout_small =
2127 gtk_widget_create_pango_layout (
2128 GTK_WIDGET (self), NULL);
2129 desc =
2130 pango_font_description_from_string (
2131 "Monospace 6");
2132 pango_layout_set_font_description (
2133 self->layout_small, desc);
2134 pango_font_description_free (desc);
2135
2136 g_signal_connect (
2137 G_OBJECT (self), "draw",
2138 G_CALLBACK (ruler_draw_cb), self);
2139 g_signal_connect (
2140 G_OBJECT (self), "motion-notify-event",
2141 G_CALLBACK (on_motion), self);
2142 g_signal_connect (
2143 G_OBJECT (self), "leave-notify-event",
2144 G_CALLBACK (on_motion), self);
2145 g_signal_connect (
2146 G_OBJECT(self->drag), "drag-begin",
2147 G_CALLBACK (drag_begin), self);
2148 /*g_signal_connect (*/
2149 /*G_OBJECT(self->drag), "drag-update",*/
2150 /*G_CALLBACK (drag_update), self);*/
2151 g_signal_connect (
2152 G_OBJECT(self->drag), "drag-end",
2153 G_CALLBACK (drag_end), self);
2154 g_signal_connect (
2155 G_OBJECT (self), "grab-broken-event",
2156 G_CALLBACK (on_grab_broken), self);
2157 g_signal_connect (
2158 G_OBJECT (self->multipress), "pressed",
2159 G_CALLBACK (multipress_pressed), self);
2160 g_signal_connect (
2161 G_OBJECT (right_mp), "pressed",
2162 G_CALLBACK (multipress_right_pressed), self);
2163 }
2164
2165 static void
ruler_widget_class_init(RulerWidgetClass * _klass)2166 ruler_widget_class_init (RulerWidgetClass * _klass)
2167 {
2168 GtkWidgetClass * klass =
2169 GTK_WIDGET_CLASS (_klass);
2170 gtk_widget_class_set_css_name (
2171 klass, "ruler");
2172 }
2173