Use track colours for the summary view.
[ardour.git] / gtk2_ardour / editor_summary.cc
1 #include "ardour/session.h"
2 #include "time_axis_view.h"
3 #include "streamview.h"
4 #include "editor_summary.h"
5 #include "gui_thread.h"
6 #include "editor.h"
7 #include "region_view.h"
8
9 using namespace std;
10 using namespace sigc;
11 using namespace ARDOUR;
12
13 /** Construct an EditorSummary.
14  *  @param e Editor to represent.
15  */
16 EditorSummary::EditorSummary (Editor* e)
17         : _editor (e),
18           _session (0),
19           _pixmap (0),
20           _regions_dirty (true),
21           _width (512),
22           _height (64),
23           _pixels_per_frame (1),
24           _vertical_scale (1),
25           _dragging (false),
26           _moved (false)
27           
28 {
29         
30 }
31
32 /** Set the session.
33  *  @param s Session.
34  */
35 void
36 EditorSummary::set_session (Session* s)
37 {
38         _session = s;
39
40         Region::RegionPropertyChanged.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
41
42         _session->RegionRemoved.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
43         _session->EndTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
44         _session->StartTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
45
46         set_dirty ();
47 }
48
49 /** Destroy */
50 EditorSummary::~EditorSummary ()
51 {
52         if (_pixmap) {
53                 gdk_pixmap_unref (_pixmap);
54         }
55 }
56
57 /** Handle an expose event.
58  *  @param event Event from GTK.
59  */
60 bool
61 EditorSummary::on_expose_event (GdkEventExpose* event)
62 {
63         /* Render the regions pixmap */
64         
65         Gdk::Rectangle const exposure (
66                 event->area.x, event->area.y, event->area.width, event->area.height
67                 );
68
69         Gdk::Rectangle r = exposure;
70         Gdk::Rectangle content (0, 0, _width, _height);
71         bool intersects;
72         r.intersect (content, intersects);
73         
74         if (intersects) {
75
76                 GdkPixmap* p = get_pixmap (get_window()->gobj ());
77
78                 gdk_draw_drawable (
79                         get_window()->gobj(),
80                         get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
81                         p,
82                         r.get_x(),
83                         r.get_y(),
84                         r.get_x(),
85                         r.get_y(),
86                         r.get_width(),
87                         r.get_height()
88                         );
89         }
90
91         /* Render the view rectangle */
92         
93         pair<double, double> x;
94         pair<double, double> y;
95         editor_view (&x, &y);
96         
97         cairo_t* cr = gdk_cairo_create (get_window()->gobj());
98
99         cairo_set_source_rgba (cr, 0, 1, 0, 0.5);
100
101         cairo_move_to (cr, x.first, y.first);
102         cairo_line_to (cr, x.second, y.first);
103         cairo_line_to (cr, x.second, y.second);
104         cairo_line_to (cr, x.first, y.second);
105         cairo_line_to (cr, x.first, y.first);
106         cairo_stroke (cr);
107
108         cairo_destroy (cr);
109         
110         return true;
111 }
112
113 /** @param drawable GDK drawable.
114  *  @return pixmap for the regions.
115  */
116 GdkPixmap *
117 EditorSummary::get_pixmap (GdkDrawable* drawable)
118 {
119         if (_regions_dirty) {
120
121                 if (_pixmap) {
122                         gdk_pixmap_unref (_pixmap);
123                 }
124                 _pixmap = gdk_pixmap_new (drawable, _width, _height, -1);
125
126                 cairo_t* cr = gdk_cairo_create (_pixmap);
127                 render (cr);
128                 cairo_destroy (cr);
129
130                 _regions_dirty = false;
131         }
132
133         return _pixmap;
134 }
135
136 /** Render the required regions to a cairo context.
137  *  @param cr Context.
138  */
139 void
140 EditorSummary::render (cairo_t* cr)
141 {
142         if (_session == 0) {
143                 return;
144         }
145
146         /* background */
147         
148         cairo_set_source_rgb (cr, 0, 0, 0);
149         cairo_rectangle (cr, 0, 0, _width, _height);
150         cairo_fill (cr);
151
152         /* compute total height of all tracks */
153         
154         int h = 0;
155         for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
156                 h += (*i)->effective_height ();
157         }
158
159         nframes_t const start = _session->current_start_frame ();
160         _pixels_per_frame = static_cast<double> (_width) / (_session->current_end_frame() - start);
161         _vertical_scale = static_cast<double> (_height) / h;
162
163         /* render regions */
164
165         double y = 0;
166         for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
167                 StreamView* s = (*i)->view ();
168
169                 if (s) {
170                         double const h = (*i)->effective_height () * _vertical_scale;
171                         cairo_set_line_width (cr, h);
172
173                         Gdk::Color const c = (*i)->color ();
174                         cairo_set_source_rgb (cr, c.get_red_p (), c.get_green_p(), c.get_blue_p());
175                         
176                         s->foreach_regionview (bind (
177                                                        mem_fun (*this, &EditorSummary::render_region),
178                                                        cr,
179                                                        start,
180                                                        y + h / 2
181                                                        ));
182                         y += h;
183                 }
184         }
185
186 }
187
188 /** Render a region for the summary.
189  *  @param r Region view.
190  *  @param cr Cairo context.
191  *  @param start Frame offset that the summary starts at.
192  *  @param y y coordinate to render at.
193  */
194 void
195 EditorSummary::render_region (RegionView* r, cairo_t* cr, nframes_t start, double y) const
196 {
197         cairo_move_to (cr, (r->region()->position() - start) * _pixels_per_frame, y);
198         cairo_line_to (cr, ((r->region()->position() - start + r->region()->length())) * _pixels_per_frame, y);
199         cairo_stroke (cr);
200 }
201
202 /** Set the summary so that the whole thing will be re-rendered next time it is required */
203 void
204 EditorSummary::set_dirty ()
205 {
206         ENSURE_GUI_THREAD (mem_fun (*this, &EditorSummary::set_dirty));
207
208         _regions_dirty = true;
209         queue_draw ();
210 }
211
212 /** Set the summary so that just the view boundary markers will be re-rendered */
213 void
214 EditorSummary::set_bounds_dirty ()
215 {
216         ENSURE_GUI_THREAD (mem_fun (*this, &EditorSummary::set_bounds_dirty));
217         queue_draw ();
218 }
219
220 /** Handle a size request.
221  *  @param req GTK requisition
222  */
223 void
224 EditorSummary::on_size_request (Gtk::Requisition *req)
225 {
226         /* Use a dummy, small width and the actual height that we want */
227         req->width = 64;
228         req->height = 64;
229 }
230
231 /** Handle a size allocation.
232  *  @param alloc GTK allocation.
233  */
234 void
235 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
236 {
237         Gtk::EventBox::on_size_allocate (alloc);
238
239         _width = alloc.get_width ();
240         _height = alloc.get_height ();
241
242         set_dirty ();
243 }
244
245 void
246 EditorSummary::centre_on_click (GdkEventButton* ev)
247 {
248         nframes_t x = (ev->x / _pixels_per_frame) + _session->current_start_frame();
249         nframes_t const xh = _editor->current_page_frames () / 2;
250         if (x > xh) {
251                 x -= xh;
252         } else {
253                 x = 0;
254         }
255         
256         _editor->reset_x_origin (x);
257         
258         double y = ev->y / _vertical_scale;
259         double const yh = _editor->canvas_height () / 2;
260         if (y > yh) {
261                 y -= yh;
262         } else {
263                 y = 0;
264         }
265         
266         _editor->reset_y_origin (y);
267 }
268
269 /** Handle a button press.
270  *  @param ev GTK event.
271  */
272 bool
273 EditorSummary::on_button_press_event (GdkEventButton* ev)
274 {
275         if (ev->button == 1) {
276
277                 pair<double, double> xr;
278                 pair<double, double> yr;
279                 editor_view (&xr, &yr);
280
281                 if (xr.first <= ev->x && ev->x <= xr.second && yr.first <= ev->y && ev->y <= yr.second) {
282
283                         /* click inside the view rectangle: drag it */
284                         _dragging = true;
285                         _moved = false;
286                         _x_offset = ev->x - xr.first;
287                         _y_offset = ev->y - yr.first;
288                         _editor->_dragging_playhead = true;
289                         
290                 } else {
291                         
292                         /* click outside the view rectangle: centre the view around the mouse click */
293                         centre_on_click (ev);
294                 }
295         }
296
297         return true;
298 }
299
300 void
301 EditorSummary::editor_view (pair<double, double>* x, pair<double, double>* y) const
302 {
303         x->first = (_editor->leftmost_position () - _session->current_start_frame ()) * _pixels_per_frame;
304         x->second = x->first + _editor->current_page_frames() * _pixels_per_frame;
305
306         y->first = _editor->get_trackview_group_vertical_offset () * _vertical_scale;
307         y->second = y->first + _editor->canvas_height () * _vertical_scale;
308 }
309
310 bool
311 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
312 {
313         if (!_dragging) {
314                 return false;
315         }
316
317         _moved = true;
318
319         _editor->reset_x_origin (((ev->x - _x_offset) / _pixels_per_frame) + _session->current_start_frame ());
320         _editor->reset_y_origin ((ev->y - _y_offset) / _vertical_scale);
321
322         return true;
323 }
324
325 bool
326 EditorSummary::on_button_release_event (GdkEventButton* ev)
327 {
328         if (_dragging && !_moved) {
329                 centre_on_click (ev);
330         }
331         
332         _dragging = false;
333         _editor->_dragging_playhead = false;
334         return true;
335 }