Replace a bunch of potential crashes with graceful handling of the situation.
[ardour.git] / gtk2_ardour / midi_channel_selector.cc
1 /*
2     Copyright (C) 2008 Paul Davis
3     Author: Hans Baier
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <algorithm>
21 #include <sstream>
22
23 #include "midi_channel_selector.h"
24 #include "gtkmm/separator.h"
25 #include "i18n.h"
26 #include "rgb_macros.h"
27
28 using namespace std;
29 using namespace Gtk;
30 using namespace ARDOUR;
31
32 MidiChannelSelector::MidiChannelSelector(int n_rows, int n_columns, int start_row, int start_column)
33         : Table(n_rows, n_columns, true)
34         , _recursion_counter(0)
35 {
36         n_rows    = std::max(4, n_rows);
37         n_rows    = std::max(4, start_row + 4);
38         n_columns = std::max(4, n_columns);
39         n_columns = std::max(4, start_column + 4);
40
41         property_column_spacing() = 0;
42         property_row_spacing() = 0;
43
44         uint8_t channel_nr = 0;
45         for (int row = 0; row < 4; ++row) {
46                 for (int column = 0; column < 4; ++column) {
47                         ostringstream channel;
48                         channel << int(++channel_nr);
49                         _button_labels[row][column].set_text(channel.str());
50                         _button_labels[row][column].set_justify(JUSTIFY_RIGHT);
51                         _buttons[row][column].add(_button_labels[row][column]);
52                         _buttons[row][column].signal_toggled().connect(
53                                 sigc::bind(
54                                         sigc::mem_fun(this, &MidiChannelSelector::button_toggled),
55                                         &_buttons[row][column],
56                                         channel_nr - 1));
57                         _buttons[row][column].set_widget_name (X_("MidiChannelSelectorButton"));
58
59                         _buttons[row][column].signal_button_release_event().connect(
60                                 sigc::mem_fun(this, &MidiChannelSelector::was_clicked), false);
61
62                         int table_row    = start_row + row;
63                         int table_column = start_column + column;
64                         attach(_buttons[row][column], table_column, table_column + 1, table_row, table_row + 1);
65                 }
66         }
67 }
68
69 MidiChannelSelector::~MidiChannelSelector()
70 {
71 }
72
73 bool
74 MidiChannelSelector::was_clicked (GdkEventButton*)
75 {
76         clicked ();
77         return false;
78 }
79
80 void
81 MidiChannelSelector::set_channel_colors(const uint32_t new_channel_colors[16])
82 {
83         for (int row = 0; row < 4; ++row) {
84                 for (int column = 0; column < 4; ++column) {
85                         char color_normal[8];
86                         char color_active[8];
87                         snprintf(color_normal, 8, "#%x", UINT_INTERPOLATE(new_channel_colors[row * 4 + column], 0x000000ff, 0.6));
88                         snprintf(color_active, 8, "#%x", new_channel_colors[row * 4 + column]);
89                         _buttons[row][column].modify_bg(STATE_NORMAL, Gdk::Color(color_normal));
90                         _buttons[row][column].modify_bg(STATE_ACTIVE, Gdk::Color(color_active));
91                 }
92         }
93 }
94
95 void
96 MidiChannelSelector::set_default_channel_color()
97 {
98         for (int row = 0; row < 4; ++row) {
99                 for (int column = 0; column < 4; ++column) {
100                         _buttons[row][column].unset_fg (STATE_NORMAL);
101                         _buttons[row][column].unset_fg (STATE_ACTIVE);
102                         _buttons[row][column].unset_bg (STATE_NORMAL);
103                         _buttons[row][column].unset_bg (STATE_ACTIVE);
104                 }
105         }
106 }
107
108 SingleMidiChannelSelector::SingleMidiChannelSelector(uint8_t active_channel)
109         : MidiChannelSelector()
110 {
111         _last_active_button = 0;
112         ToggleButton* button = &_buttons[active_channel / 4][active_channel % 4];
113         _active_channel = active_channel;
114         button->set_active(true);
115         _last_active_button = button;
116 }
117
118 void
119 SingleMidiChannelSelector::button_toggled(ToggleButton* button, uint8_t channel)
120 {
121         ++_recursion_counter;
122         if (_recursion_counter == 1) {
123                 // if the current button is active it must
124                 // be different from the first one
125                 if (button->get_active()) {
126                         if (_last_active_button) {
127                                 _last_active_button->set_active(false);
128                                 _active_channel = channel;
129                                 _last_active_button = button;
130                                 channel_selected.emit(channel);
131                         }
132                 } else {
133                         // if not, the user pressed the already active button
134                         button->set_active(true);
135                         _active_channel = channel;
136                 }
137         }
138         --_recursion_counter;
139 }
140
141 MidiMultipleChannelSelector::MidiMultipleChannelSelector(ChannelMode mode, uint16_t mask)
142         : MidiChannelSelector(4, 6, 0, 0)
143         , _channel_mode(mode)
144 {
145         _select_all.add(*manage(new Label(_("All"))));
146         _select_all.signal_clicked().connect(
147                         sigc::bind(sigc::mem_fun(this, &MidiMultipleChannelSelector::select_all), true));
148
149         _select_none.add(*manage(new Label(_("None"))));
150         _select_none.signal_clicked().connect(
151                         sigc::bind(sigc::mem_fun(this, &MidiMultipleChannelSelector::select_all), false));
152
153         _invert_selection.add(*manage(new Label(_("Invert"))));
154         _invert_selection.signal_clicked().connect(
155                         sigc::mem_fun(this, &MidiMultipleChannelSelector::invert_selection));
156
157         _force_channel.add(*manage(new Label(_("Force"))));
158         _force_channel.signal_toggled().connect(
159                         sigc::mem_fun(this, &MidiMultipleChannelSelector::force_channels_button_toggled));
160
161         set_homogeneous(false);
162         attach(*manage(new VSeparator()), 4, 5, 0, 4, SHRINK, FILL, 0, 0);
163         //set_row_spacing(4, -5);
164         attach(_select_all,       5, 6, 0, 1);
165         attach(_select_none,      5, 6, 1, 2);
166         attach(_invert_selection, 5, 6, 2, 3);
167         attach(_force_channel,    5, 6, 3, 4);
168
169         set_selected_channels(mask);
170 }
171
172 MidiMultipleChannelSelector::~MidiMultipleChannelSelector()
173 {
174         mode_changed.clear();
175 }
176
177 void
178 MidiMultipleChannelSelector::set_channel_mode(ChannelMode mode, uint16_t mask)
179 {
180         switch (mode) {
181         case AllChannels:
182                 _force_channel.set_active(false);
183                 set_selected_channels(0xFFFF);
184                 break;
185         case FilterChannels:
186                 _force_channel.set_active(false);
187                 set_selected_channels(mask);
188                 break;
189         case ForceChannel:
190                 _force_channel.set_active(true);
191                 for (uint16_t i = 0; i < 16; i++) {
192                         ToggleButton* button = &_buttons[i / 4][i % 4];
193                         button->set_active(i == mask);
194                 }
195         }
196 }
197
198 uint16_t
199 MidiMultipleChannelSelector::get_selected_channels() const
200 {
201         uint16_t selected_channels = 0;
202         for (uint16_t i = 0; i < 16; i++) {
203                 const ToggleButton* button = &_buttons[i / 4][i % 4];
204                 if (button->get_active()) {
205                         selected_channels |= (1L << i);
206                 }
207         }
208
209         return selected_channels;
210 }
211
212 void
213 MidiMultipleChannelSelector::set_selected_channels(uint16_t selected_channels)
214 {
215         for (uint16_t i = 0; i < 16; i++) {
216                 ToggleButton* button = &_buttons[i / 4][i % 4];
217                 if (selected_channels & (1L << i)) {
218                         button->set_active(true);
219                 } else {
220                         button->set_active(false);
221                 }
222         }
223 }
224
225 void
226 MidiMultipleChannelSelector::button_toggled(ToggleButton */*button*/, uint8_t channel)
227 {
228         ++_recursion_counter;
229         if (_recursion_counter == 1) {
230                 if (_channel_mode == ForceChannel) {
231                         mode_changed.emit(_channel_mode, channel);
232                         set_selected_channels(1 << channel);
233                 } else {
234                         mode_changed.emit(_channel_mode, get_selected_channels());
235                 }
236         }
237         --_recursion_counter;
238 }
239
240 void
241 MidiMultipleChannelSelector::force_channels_button_toggled()
242 {
243         if (_force_channel.get_active()) {
244                 _channel_mode = ForceChannel;
245                 bool found_first_active = false;
246                 // leave only the first button enabled
247                 uint16_t active_channel = 0;
248                 for (int i = 0; i <= 15; i++) {
249                         ToggleButton* button = &_buttons[i / 4][i % 4];
250                         if (button->get_active()) {
251                                 if (found_first_active) {
252                                         ++_recursion_counter;
253                                         button->set_active(false);
254                                         --_recursion_counter;
255                                 } else {
256                                         found_first_active = true;
257                                         active_channel = i;
258                                 }
259                         }
260                 }
261
262                 if (!found_first_active) {
263                         _buttons[0][0].set_active(true);
264                 }
265
266                 _select_all.set_sensitive(false);
267                 _select_none.set_sensitive(false);
268                 _invert_selection.set_sensitive(false);
269                 mode_changed.emit(_channel_mode, active_channel);
270         } else {
271                 _channel_mode = FilterChannels;
272                 _select_all.set_sensitive(true);
273                 _select_none.set_sensitive(true);
274                 _invert_selection.set_sensitive(true);
275                 mode_changed.emit(FilterChannels, get_selected_channels());
276         }
277 }
278
279 void
280 MidiMultipleChannelSelector::select_all(bool on)
281 {
282         if (_channel_mode == ForceChannel)
283                 return;
284
285         ++_recursion_counter;
286         for (uint16_t i = 0; i < 16; i++) {
287                 ToggleButton* button = &_buttons[i / 4][i % 4];
288                 button->set_active(on);
289         }
290         --_recursion_counter;
291         mode_changed.emit(_channel_mode, get_selected_channels());
292 }
293
294 void
295 MidiMultipleChannelSelector::invert_selection(void)
296 {
297         if (_channel_mode == ForceChannel)
298                 return;
299
300         ++_recursion_counter;
301         for (uint16_t i = 0; i < 16; i++) {
302                 ToggleButton* button = &_buttons[i / 4][i % 4];
303                 if (button->get_active()) {
304                         button->set_active(false);
305                 } else {
306                         button->set_active(true);
307                 }
308         }
309         --_recursion_counter;
310         mode_changed.emit(_channel_mode, get_selected_channels());
311 }
312