New matrix-based editor for connections and bundles, based on thorwil's design.
[ardour.git] / gtk2_ardour / port_matrix_body.cc
1 /*
2     Copyright (C) 2002-2009 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21 #include "ardour/bundle.h"
22 #include "port_matrix_body.h"
23 #include "port_matrix.h"
24
25 PortMatrixBody::PortMatrixBody (PortMatrix* p)
26         : _port_matrix (p),
27           _column_labels (this),
28           _row_labels (p, this),
29           _grid (p, this),
30           _alloc_width (0),
31           _alloc_height (0),
32           _alloc_xdiv (0),
33           _alloc_ydiv (0),
34           _xoffset (0),
35           _yoffset (0)
36 {
37
38 }
39
40
41 bool
42 PortMatrixBody::on_expose_event (GdkEventExpose* event)
43 {
44         Gdk::Rectangle const exposure (
45                 event->area.x, event->area.y, event->area.width, event->area.height
46                 );
47
48         Gdk::Rectangle const col (0, 0, _alloc_width, _alloc_ydiv);
49         Gdk::Rectangle const row (_alloc_xdiv, _alloc_ydiv, _alloc_width - _alloc_xdiv, _alloc_height - _alloc_ydiv);
50         Gdk::Rectangle const grid (0, _alloc_ydiv, _alloc_xdiv, _alloc_height - _alloc_ydiv);
51
52         bool intersects;
53         Gdk::Rectangle r = exposure;
54         r.intersect (col, intersects);
55
56         if (intersects) {
57                 gdk_draw_drawable (
58                         get_window()->gobj(),
59                         get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
60                         _column_labels.get_pixmap (get_window()->gobj()),
61                         r.get_x() + _xoffset,
62                         r.get_y(),
63                         r.get_x(),
64                         r.get_y(),
65                         r.get_width(),
66                         r.get_height()
67                         );
68         }
69
70         r = exposure;
71         r.intersect (row, intersects);
72
73         if (intersects) {
74                 gdk_draw_drawable (
75                         get_window()->gobj(),
76                         get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
77                         _row_labels.get_pixmap (get_window()->gobj()),
78                         r.get_x() - _alloc_xdiv,
79                         r.get_y() + _yoffset - _alloc_ydiv,
80                         r.get_x(),
81                         r.get_y(),
82                         r.get_width(),
83                         r.get_height()
84                         );
85         }
86
87         r = exposure;
88         r.intersect (grid, intersects);
89
90         if (intersects) {
91                 gdk_draw_drawable (
92                         get_window()->gobj(),
93                         get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
94                         _grid.get_pixmap (get_window()->gobj()),
95                         r.get_x() + _xoffset,
96                         r.get_y() + _yoffset - _alloc_ydiv,
97                         r.get_x(),
98                         r.get_y(),
99                         r.get_width(),
100                         r.get_height()
101                         );
102         }
103
104         return true;
105 }
106
107 void
108 PortMatrixBody::on_size_request (Gtk::Requisition *req)
109 {
110         std::pair<int, int> const col = _column_labels.dimensions ();
111         std::pair<int, int> const row = _row_labels.dimensions ();
112         std::pair<int, int> const grid = _grid.dimensions ();
113
114         req->width = std::max (col.first, grid.first + row.first);
115         req->height = col.second + grid.second;
116 }
117
118 void
119 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
120 {
121         Gtk::EventBox::on_size_allocate (alloc);
122         set_allocation (alloc);
123
124         _alloc_width = alloc.get_width ();
125         _alloc_height = alloc.get_height ();
126
127         compute_divs ();
128         _port_matrix->setup_scrollbars ();
129 }
130
131 void
132 PortMatrixBody::compute_divs ()
133 {
134         std::pair<uint32_t, uint32_t> const col = _column_labels.dimensions ();
135         if (_alloc_height > col.second) {
136                 /* allocated height is enough for the column labels */
137                 _alloc_ydiv = col.second;
138         } else {
139                 /* not enough space for the column labels */
140                 _alloc_ydiv = _alloc_height;
141         }
142
143         std::pair<uint32_t, uint32_t> const grid = _grid.dimensions ();
144         std::pair<uint32_t, uint32_t> const row = _row_labels.dimensions ();
145
146         if (_alloc_width > (grid.first + row.first)) {
147                 /* allocated width is larger than we need, so
148                    put the x division at the extent of the grid */
149                 _alloc_xdiv = grid.first;
150         } else if (_alloc_width > row.first) {
151                 /* allocated width is large enough for the row labels
152                    but not for the whole grid, so display the whole
153                    row label section and cut part of the grid off */
154                 _alloc_xdiv = _alloc_width - row.first;
155         } else {
156                 /* allocated width isn't even enough for the row labels */
157                 _alloc_xdiv = 0;
158         }
159 }
160
161 void
162 PortMatrixBody::setup (
163         std::vector<boost::shared_ptr<ARDOUR::Bundle> > const & row,
164         std::vector<boost::shared_ptr<ARDOUR::Bundle> > const & column
165         )
166 {
167         for (std::list<sigc::connection>::iterator i = _bundle_connections.begin(); i != _bundle_connections.end(); ++i) {
168                 i->disconnect ();
169         }
170
171         _bundle_connections.clear ();
172         
173         _row_bundles = row;
174         _column_bundles = column;
175
176         for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::iterator i = _row_bundles.begin(); i != _row_bundles.end(); ++i) {
177                 
178                 _bundle_connections.push_back (
179                         (*i)->NameChanged.connect (sigc::mem_fun (*this, &PortMatrixBody::repaint_row_labels))
180                         );
181                 
182         }
183
184         for (std::vector<boost::shared_ptr<ARDOUR::Bundle> >::iterator i = _column_bundles.begin(); i != _column_bundles.end(); ++i) {
185                 _bundle_connections.push_back (
186                         (*i)->NameChanged.connect (sigc::mem_fun (*this, &PortMatrixBody::repaint_column_labels))
187                         );
188         }
189         
190         _column_labels.setup ();
191         _row_labels.setup ();
192         _grid.setup ();
193
194         compute_divs ();
195 }
196
197 uint32_t
198 PortMatrixBody::full_scroll_width ()
199 {
200         return _grid.dimensions().first;
201
202 }
203
204 uint32_t
205 PortMatrixBody::alloc_scroll_width ()
206 {
207         return _alloc_xdiv;
208 }
209
210 uint32_t
211 PortMatrixBody::full_scroll_height ()
212 {
213         return _grid.dimensions().second;
214 }
215
216 uint32_t
217 PortMatrixBody::alloc_scroll_height ()
218 {
219         return _alloc_height - _alloc_ydiv;
220 }
221
222 void
223 PortMatrixBody::set_xoffset (uint32_t xo)
224 {
225         _xoffset = xo;
226         queue_draw ();
227 }
228
229 void
230 PortMatrixBody::set_yoffset (uint32_t yo)
231 {
232         _yoffset = yo;
233         queue_draw ();
234 }
235
236 bool
237 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
238 {
239         if (ev->x < _alloc_xdiv && ev->y > _alloc_ydiv) {
240                 _grid.button_press (ev->x + _xoffset, ev->y + _yoffset - _alloc_ydiv, ev->button);
241         } else if (ev->x > _alloc_xdiv && ev->y > _alloc_ydiv) {
242                 _row_labels.button_press (ev->x - _alloc_xdiv, ev->y + _yoffset - _alloc_ydiv, ev->button, ev->time);
243         } else {
244                 return false;
245         }
246
247         return true;
248 }
249
250 void
251 PortMatrixBody::repaint_grid ()
252 {
253         _grid.require_render ();
254         queue_draw ();
255 }
256
257 void
258 PortMatrixBody::repaint_column_labels ()
259 {
260         _column_labels.require_render ();
261         queue_draw ();
262 }
263
264 void
265 PortMatrixBody::repaint_row_labels ()
266 {
267         _row_labels.require_render ();
268         queue_draw ();
269 }