1 /*
2  * Copyright (C) 2018-2019 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 /**
21  * \file
22  *
23  * Timeline background inheriting from arranger_bg.
24  */
25 
26 #include "zrythm.h"
27 #include "audio/automation_track.h"
28 #include "audio/automation_tracklist.h"
29 #include "audio/channel.h"
30 #include "audio/instrument_track.h"
31 #include "audio/router.h"
32 #include "audio/track.h"
33 #include "audio/tracklist.h"
34 #include "audio/transport.h"
35 #include "gui/widgets/arranger.h"
36 #include "gui/widgets/automation_point.h"
37 #include "gui/widgets/center_dock.h"
38 #include "gui/widgets/main_window.h"
39 #include "gui/widgets/ruler.h"
40 #include "gui/widgets/timeline_arranger.h"
41 #include "gui/widgets/timeline_bg.h"
42 #include "gui/widgets/timeline_panel.h"
43 #include "gui/widgets/timeline_ruler.h"
44 #include "gui/widgets/track.h"
45 #include "gui/widgets/tracklist.h"
46 #include "project.h"
47 #include "settings/settings.h"
48 #include "utils/cairo.h"
49 
50 #include <gtk/gtk.h>
51 
G_DEFINE_TYPE(TimelineBgWidget,timeline_bg_widget,ARRANGER_BG_WIDGET_TYPE)52 G_DEFINE_TYPE (
53   TimelineBgWidget,
54   timeline_bg_widget,
55   ARRANGER_BG_WIDGET_TYPE)
56 
57 /**
58  * To be called by the arranger bg during drawing.
59  */
60 void
61 timeline_bg_widget_draw (
62   TimelineBgWidget * self,
63   cairo_t *          cr,
64   GdkRectangle *     rect)
65 {
66   ArrangerBgWidgetPrivate * prv =
67     arranger_bg_widget_get_private (
68       Z_ARRANGER_BG_WIDGET (self));
69 
70   /* handle horizontal drawing for tracks */
71   GtkWidget * tw_widget;
72   gint track_start_offset;
73   Track * track;
74   TrackWidget * tw;
75   int line_y, i, j;
76   int is_unpinned_timeline =
77     prv->arranger ==
78       (ArrangerWidget *) (MW_TIMELINE);
79   int is_pinned_timeline =
80     prv->arranger ==
81       (ArrangerWidget *) (MW_PINNED_TIMELINE);
82   for (i = 0; i < TRACKLIST->num_tracks; i++)
83     {
84       track = TRACKLIST->tracks[i];
85 
86       /* skip tracks in the other timeline (pinned/
87        * non-pinned) */
88       if (!track->visible ||
89           (is_unpinned_timeline &&
90            track->pinned) ||
91           (is_pinned_timeline &&
92            !track->pinned))
93         continue;
94 
95       /* draw line below widget */
96       tw = track->widget;
97       if (!GTK_IS_WIDGET (tw))
98         continue;
99       tw_widget = (GtkWidget *) tw;
100 
101       int full_track_height =
102         track_get_full_visible_height (track);
103 
104       gtk_widget_translate_coordinates (
105         tw_widget,
106         GTK_WIDGET (
107           is_pinned_timeline ?
108           MW_TRACKLIST->pinned_box :
109           MW_TRACKLIST->unpinned_box),
110         0, 0, NULL, &track_start_offset);
111 
112       line_y =
113         track_start_offset + full_track_height;
114 
115       if (line_y >= rect->y &&
116           line_y < rect->y + rect->height)
117         {
118           z_cairo_draw_horizontal_line (
119             cr, line_y - rect->y, 0,
120             rect->width, 1.0);
121         }
122 
123       int total_height = track->main_height;
124 
125 #define OFFSET_PLUS_TOTAL_HEIGHT \
126   (track_start_offset + total_height)
127 
128       /* --- draw lanes --- */
129 
130       if (track->lanes_visible)
131         {
132           for (j = 0; j < track->num_lanes; j++)
133             {
134               TrackLane * lane = track->lanes[j];
135 
136               /* horizontal line above lane */
137               if (OFFSET_PLUS_TOTAL_HEIGHT >
138                     rect->y &&
139                   OFFSET_PLUS_TOTAL_HEIGHT  <
140                     rect->y + rect->height)
141                 {
142                   z_cairo_draw_horizontal_line (
143                     cr,
144                     OFFSET_PLUS_TOTAL_HEIGHT -
145                       rect->y,
146                     0, rect->width, 0.4);
147                 }
148 
149               total_height += lane->height;
150             }
151         }
152 
153       /* --- draw automation related stuff --- */
154 
155       /* skip tracks without visible automation */
156       if (!track->automation_visible)
157         continue;
158 
159       AutomationTracklist * atl =
160         track_get_automation_tracklist (track);
161       if (atl)
162         {
163           AutomationTrack * at;
164           for (j = 0; j < atl->num_ats; j++)
165             {
166               at = atl->ats[j];
167 
168               if (!at->created || !at->visible)
169                 continue;
170 
171               /* horizontal line above automation
172                * track */
173               if (OFFSET_PLUS_TOTAL_HEIGHT >
174                     rect->y &&
175                   OFFSET_PLUS_TOTAL_HEIGHT  <
176                     rect->y + rect->height)
177                 {
178                   z_cairo_draw_horizontal_line (
179                     cr,
180                     OFFSET_PLUS_TOTAL_HEIGHT -
181                       rect->y,
182                     0, rect->width, 0.2);
183                 }
184 
185               float normalized_val =
186                 automation_track_get_normalized_val_at_pos (
187                   at, PLAYHEAD);
188               if (normalized_val < 0.f)
189                 normalized_val =
190                   automatable_real_val_to_normalized (
191                     at->automatable,
192                     automatable_get_val (
193                       at->automatable));
194 
195               int y_px =
196                 automation_track_get_y_px_from_normalized_val (
197                   at,
198                   normalized_val);
199 
200               /* line at current val */
201               cairo_set_source_rgba (
202                 cr,
203                 track->color.red,
204                 track->color.green,
205                 track->color.blue,
206                 0.3);
207               cairo_set_line_width (cr, 1);
208               cairo_move_to (
209                 cr, 0,
210                 (OFFSET_PLUS_TOTAL_HEIGHT + y_px) -
211                   rect->y);
212               cairo_line_to (
213                 cr, rect->width,
214                 (OFFSET_PLUS_TOTAL_HEIGHT + y_px) -
215                   rect->y);
216               cairo_stroke (cr);
217 
218               /* show shade under the line */
219               /*cairo_set_source_rgba (*/
220                 /*cr,*/
221                 /*track->color.red,*/
222                 /*track->color.green,*/
223                 /*track->color.blue,*/
224                 /*0.06);*/
225               /*cairo_rectangle (*/
226                 /*cr,*/
227                 /*0,*/
228                 /*(OFFSET_PLUS_TOTAL_HEIGHT + y_px) -*/
229                   /*rect->y,*/
230                 /*rect->width,*/
231                 /*at->height - y_px);*/
232               /*cairo_fill (cr);*/
233 
234               total_height += at->height;
235             }
236         }
237     }
238 }
239 
240 /*static gboolean*/
241 /*on_motion (TimelineBgWidget * self,*/
242            /*GdkEventMotion *event)*/
243 /*{*/
244   /*return FALSE;*/
245 /*}*/
246 
247 TimelineBgWidget *
timeline_bg_widget_new(RulerWidget * ruler,ArrangerWidget * arranger)248 timeline_bg_widget_new (
249   RulerWidget *    ruler,
250   ArrangerWidget * arranger)
251 {
252   TimelineBgWidget * self =
253     g_object_new (TIMELINE_BG_WIDGET_TYPE,
254                   NULL);
255 
256   ARRANGER_BG_WIDGET_GET_PRIVATE (self);
257   ab_prv->ruler = ruler;
258   ab_prv->arranger = arranger;
259 
260   return self;
261 }
262 
263 static void
timeline_bg_widget_class_init(TimelineBgWidgetClass * _klass)264 timeline_bg_widget_class_init (
265   TimelineBgWidgetClass * _klass)
266 {
267 }
268 
269 static void
timeline_bg_widget_init(TimelineBgWidget * self)270 timeline_bg_widget_init (TimelineBgWidget *self )
271 {
272   gtk_widget_add_events (
273     GTK_WIDGET (self),
274     GDK_ALL_EVENTS_MASK);
275 }
276