correctly create Tracks, not Busses
[ardour.git] / libs / surfaces / push2 / menu.cc
1 /*
2         Copyright (C) 2016 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 #include <cairomm/context.h>
20 #include <cairomm/surface.h>
21 #include <cairomm/region.h>
22 #include <pangomm/layout.h>
23
24 #include "pbd/i18n.h"
25
26 #include "canvas/text.h"
27 #include "canvas/types.h"
28 #include "canvas/rectangle.h"
29 #include "gtkmm2ext/colors.h"
30
31 #include "canvas.h"
32 #include "gui.h"
33 #include "push2.h"
34
35 #include "menu.h"
36
37 #ifdef __APPLE__
38 #define Rect ArdourCanvas::Rect
39 #endif
40
41 using namespace ARDOUR;
42 using namespace std;
43 using namespace PBD;
44 using namespace Glib;
45 using namespace ArdourSurface;
46 using namespace ArdourCanvas;
47
48 Push2Menu::Push2Menu (Item* parent, vector<string> s)
49         : Container (parent)
50         , baseline (-1)
51         , ncols (0)
52         , nrows (0)
53         , wrap (true)
54         , first (0)
55         , last (0)
56         , _active (0)
57 {
58         Pango::FontDescription fd ("Sans 10");
59
60         if (baseline < 0) {
61                 Push2Canvas* p2c = dynamic_cast<Push2Canvas*> (canvas());
62                 Glib::RefPtr<Pango::Layout> throwaway = Pango::Layout::create (p2c->image_context());
63                 throwaway->set_font_description (fd);
64                 throwaway->set_text (X_("Hg")); /* ascender + descender) */
65                 int h, w;
66                 throwaway->get_pixel_size (w, h);
67                 baseline = h;
68         }
69
70         active_bg = new ArdourCanvas::Rectangle (this);
71
72         for (vector<string>::iterator si = s.begin(); si != s.end(); ++si) {
73                 Text* t = new Text (this);
74                 t->set_font_description (fd);
75                 t->set (*si);
76                 displays.push_back (t);
77         }
78
79 }
80
81 void
82 Push2Menu::set_layout (int c, int r)
83 {
84         ncols = c;
85         nrows = r;
86
87         set_active (_active);
88         rearrange (_active);
89 }
90
91 void
92 Push2Menu::rearrange (uint32_t initial_display)
93 {
94         if (initial_display >= displays.size()) {
95                 return;
96         }
97
98         vector<Text*>::iterator i = displays.begin();
99
100         /* move to first */
101
102         for (uint32_t n = 0; n < initial_display; ++n) {
103                 (*i)->hide ();
104                 ++i;
105         }
106
107         uint32_t index = initial_display;
108         uint32_t col = 0;
109         uint32_t row = 0;
110         bool active_shown = false;
111
112         while (i != displays.end()) {
113
114                 Coord x = col * Push2Canvas::inter_button_spacing();
115                 Coord y = 2 + (row * baseline);
116
117                 (*i)->set_position (Duple (x, y));
118
119                 if (index == _active) {
120                         active_bg->set (Rect (x - 1, y - 1,
121                                               x - 1 + Push2Canvas::inter_button_spacing(), y - 1 + baseline));
122                         active_bg->show ();
123                         active_shown = true;
124                 }
125
126                 (*i)->show ();
127                 last = index;
128                 ++i;
129                 ++index;
130
131                 if (++row >= nrows) {
132                         row = 0;
133                         if (++col >= ncols) {
134                                 /* no more to display */
135                                 break;
136                         }
137                 }
138
139         }
140
141         while (i != displays.end()) {
142                 (*i)->hide ();
143                 ++i;
144         }
145
146         if (!active_shown) {
147                 active_bg->hide ();
148         }
149
150         first = initial_display;
151
152         Rearranged (); /* EMIT SIGNAL */
153 }
154
155 void
156 Push2Menu::scroll (Direction dir, bool page)
157 {
158         switch (dir) {
159         case DirectionUp:
160                 if (_active == 0) {
161                         if (wrap) {
162                                 set_active (displays.size() - 1);
163                         }
164                 } else {
165                         set_active (_active - 1);
166                 }
167                 break;
168
169         case DirectionDown:
170                 if (_active == displays.size() - 1) {
171                         if (wrap) {
172                                 set_active (0);
173                         }
174                 } else {
175                         set_active (_active + 1);
176                 }
177                 break;
178
179         case DirectionLeft:
180                 if (page) {
181                         set_active (max (0, (int) (first - (nrows * ncols))));
182                 } else {
183                         if (_active / nrows == 0) {
184                                 /* in the first column, go to last column, same row */
185                                 if (wrap) {
186                                         set_active (displays.size() - 1 - active_row ());
187                                 }
188                         } else {
189                                 /* move to same row, previous column */
190                                 set_active (_active - nrows);
191                         }
192                 }
193                 break;
194
195         case DirectionRight:
196                 if (page) {
197                         set_active (min ((uint32_t) displays.size(), first + (nrows * ncols)));
198                 } else {
199                         if (_active / nrows == ncols) {
200                                 /* in the last column, go to same row in first column */
201                                 if (wrap) {
202                                 set_active (active_row());
203                                 }
204                         } else {
205                                 /* move to same row, next column */
206                                 set_active (_active + nrows);
207                         }
208                 }
209                 break;
210         }
211 }
212
213 void
214 Push2Menu::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
215 {
216         render_children (area, context);
217 }
218
219 void
220 Push2Menu::set_active (uint32_t index)
221 {
222         if (!parent() || (index == _active)) {
223                 return;
224         }
225
226         if (index >= displays.size()) {
227                 active_bg->hide ();
228                 return;
229         }
230
231         /* set text color for old active item, and the new one */
232
233         if (_active < displays.size()) {
234                 displays[_active]->set_color (text_color);
235         }
236
237         displays[index]->set_color (contrast_color);
238
239         Duple p = displays[index]->position ();
240
241         active_bg->set (Rect (p.x - 1, p.y - 1, p.x - 1 + Push2Canvas::inter_button_spacing(), p.y - 1 + baseline ));
242         active_bg->show ();
243         _active = index;
244
245         if (_active < first) {
246
247                 /* we jumped before current visible range : try to put its column first
248                  */
249
250                 rearrange (active_top());
251
252         } else if (_active > last) {
253
254                 /* we jumped after current visible range : try putting its
255                  * column last
256                  */
257
258                 rearrange (active_top() - ((ncols - 1) * nrows));
259         }
260
261         ActiveChanged (); /* EMIT SIGNAL */
262 }
263
264 void
265 Push2Menu::set_text_color (Gtkmm2ext::Color c)
266 {
267         text_color = c;
268
269         for (vector<Text*>::iterator t = displays.begin(); t != displays.end(); ++t) {
270                 (*t)->set_color (c);
271         }
272
273 }
274
275 void
276 Push2Menu::set_active_color (Gtkmm2ext::Color c)
277 {
278         active_color = c;
279         contrast_color = Gtkmm2ext::contrasting_text_color (active_color);
280         if (active_bg) {
281                 active_bg->set_fill_color (c);
282         }
283
284         if (_active < displays.size()) {
285                 displays[_active]->set_color (contrast_color);
286         }
287 }
288
289 void
290 Push2Menu::set_font_description (Pango::FontDescription fd)
291 {
292         font_description = fd;
293
294         for (vector<Text*>::iterator t = displays.begin(); t != displays.end(); ++t) {
295                 (*t)->set_font_description (fd);
296         }
297 }