6c316261795fe5ae09cedfb3adeaffa5a97c133b
[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 {
25         
26 }
27
28 /** Set the session.
29  *  @param s Session.
30  */
31 void
32 EditorSummary::set_session (Session* s)
33 {
34         _session = s;
35
36         Region::RegionPropertyChanged.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
37
38         _session->RegionRemoved.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
39         _session->EndTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
40         _session->StartTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
41
42         set_dirty ();
43 }
44
45 /** Destroy */
46 EditorSummary::~EditorSummary ()
47 {
48         if (_pixmap) {
49                 gdk_pixmap_unref (_pixmap);
50         }
51 }
52
53 /** Handle an expose event.
54  *  @param event Event from GTK.
55  */
56 bool
57 EditorSummary::on_expose_event (GdkEventExpose* event)
58 {
59         /* Render the regions pixmap */
60         
61         Gdk::Rectangle const exposure (
62                 event->area.x, event->area.y, event->area.width, event->area.height
63                 );
64
65         Gdk::Rectangle r = exposure;
66         Gdk::Rectangle content (0, 0, _width, _height);
67         bool intersects;
68         r.intersect (content, intersects);
69         
70         if (intersects) {
71
72                 GdkPixmap* p = get_pixmap (get_window()->gobj ());
73
74                 gdk_draw_drawable (
75                         get_window()->gobj(),
76                         get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
77                         p,
78                         r.get_x(),
79                         r.get_y(),
80                         r.get_x(),
81                         r.get_y(),
82                         r.get_width(),
83                         r.get_height()
84                         );
85         }
86
87         /* Render the view beginning and end markers */
88         
89         cairo_t* cr = gdk_cairo_create (get_window()->gobj());
90
91         cairo_set_source_rgb (cr, 0, 1, 0);
92         cairo_set_line_width (cr, 2);
93
94         double const s = (_editor->leftmost_position () - _session->current_start_frame ()) * _pixels_per_frame; 
95         cairo_move_to (cr, s, 0);
96         cairo_line_to (cr, s, _height);
97         cairo_stroke (cr);
98
99         double const e = s + _editor->current_page_frames() * _pixels_per_frame;
100         cairo_move_to (cr, e, 0);
101         cairo_line_to (cr, e, _height);
102         cairo_stroke (cr);
103         
104         cairo_destroy (cr);
105         
106         return true;
107 }
108
109 /** @param drawable GDK drawable.
110  *  @return pixmap for the regions.
111  */
112 GdkPixmap *
113 EditorSummary::get_pixmap (GdkDrawable* drawable)
114 {
115         if (_regions_dirty) {
116
117                 if (_pixmap) {
118                         gdk_pixmap_unref (_pixmap);
119                 }
120                 _pixmap = gdk_pixmap_new (drawable, _width, _height, -1);
121
122                 cairo_t* cr = gdk_cairo_create (_pixmap);
123                 render (cr);
124                 cairo_destroy (cr);
125
126                 _regions_dirty = false;
127         }
128
129         return _pixmap;
130 }
131
132 /** Render the required regions to a cairo context.
133  *  @param cr Context.
134  */
135 void
136 EditorSummary::render (cairo_t* cr)
137 {
138         if (_session == 0) {
139                 return;
140         }
141
142         /* background */
143         
144         cairo_set_source_rgb (cr, 0, 0, 0);
145         cairo_rectangle (cr, 0, 0, _width, _height);
146         cairo_fill (cr);
147
148         int N = 0;
149
150         /* count tracks to render */
151         for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
152                 if ((*i)->view()) {
153                         ++N;
154                 }
155         }
156
157         nframes_t const start = _session->current_start_frame ();
158         _pixels_per_frame = static_cast<double> (_width) / (_session->current_end_frame() - start);
159         double const track_height = static_cast<double> (_height) / N;
160
161         cairo_set_line_width (cr, track_height);
162
163         /* render regions */
164         
165         int n = 0;
166         for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
167                 StreamView* s = (*i)->view ();
168                 if (s) {
169
170                         double const v = ((n % 2) == 0) ? 1 : 0.5;
171                         cairo_set_source_rgb (cr, v, v, v);
172
173                         s->foreach_regionview (bind (
174                                                        mem_fun (*this, &EditorSummary::render_region),
175                                                        cr,
176                                                        start,
177                                                        track_height * (n + 0.5)
178                                                        ));
179                         ++n;
180                 }
181         }
182
183 }
184
185 /** Render a region for the summary.
186  *  @param r Region view.
187  *  @param cr Cairo context.
188  *  @param start Frame offset that the summary starts at.
189  *  @param y y coordinate to render at.
190  */
191 void
192 EditorSummary::render_region (RegionView* r, cairo_t* cr, nframes_t start, double y) const
193 {
194         cairo_move_to (cr, (r->region()->position() - start) * _pixels_per_frame, y);
195         cairo_line_to (cr, (r->region()->position() - start + r->region()->length()) * _pixels_per_frame, y);
196         cairo_stroke (cr);
197 }
198
199 /** Set the summary so that the whole thing will be re-rendered next time it is required */
200 void
201 EditorSummary::set_dirty ()
202 {
203         ENSURE_GUI_THREAD (mem_fun (*this, &EditorSummary::set_dirty));
204
205         _regions_dirty = true;
206         queue_draw ();
207 }
208
209 /** Set the summary so that just the view boundary markers will be re-rendered */
210 void
211 EditorSummary::set_bounds_dirty ()
212 {
213         ENSURE_GUI_THREAD (mem_fun (*this, &EditorSummary::set_bounds_dirty));
214         queue_draw ();
215 }
216
217 /** Handle a size request.
218  *  @param req GTK requisition
219  */
220 void
221 EditorSummary::on_size_request (Gtk::Requisition *req)
222 {
223         /* Use a dummy, small width and the actual height that we want */
224         req->width = 64;
225         req->height = 64;
226 }
227
228 /** Handle a size allocation.
229  *  @param alloc GTK allocation.
230  */
231 void
232 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
233 {
234         Gtk::EventBox::on_size_allocate (alloc);
235
236         _width = alloc.get_width ();
237         _height = alloc.get_height ();
238
239         set_dirty ();
240 }
241
242 /** Handle a button press.
243  *  @param ev GTK event.
244  */
245 bool
246 EditorSummary::on_button_press_event (GdkEventButton* ev)
247 {
248         if (ev->button == 1) {
249
250                 /* centre the editor view around the mouse click */
251                 
252                 nframes_t f = (ev->x / _pixels_per_frame) + _session->current_start_frame();
253
254                 nframes_t const h = _editor->current_page_frames () / 2;
255                 if (f > h) {
256                         f -= h;
257                 } else {
258                         f = 0;
259                 }
260                 
261                 _editor->reset_x_origin (f);
262         }
263
264         return true;
265 }