Update GPL boilerplate and (C)
[ardour.git] / gtk2_ardour / patch_change.cc
1 /*
2  * Copyright (C) 2013-2016 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2014-2015 David Robillard <d@drobilla.net>
4  * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include <iostream>
22
23 #include <boost/algorithm/string.hpp>
24
25 #include <glibmm/regex.h>
26
27 #include "gtkmm2ext/keyboard.h"
28 #include "gtkmm2ext/menu_elems.h"
29 #include "gtkmm2ext/utils.h"
30
31 #include "midi++/midnam_patch.h"
32
33 #include "canvas/debug.h"
34
35 #include "editor.h"
36 #include "editor_drag.h"
37 #include "midi_region_view.h"
38 #include "patch_change.h"
39 #include "ui_config.h"
40
41 using namespace MIDI::Name;
42 using namespace std;
43 using Gtkmm2ext::Keyboard;
44
45 /** @param x x position in pixels.
46  */
47 PatchChange::PatchChange(MidiRegionView&                   region,
48                          ArdourCanvas::Container*          parent,
49                          const string&                     text,
50                          double                            height,
51                          double                            x,
52                          double                            y,
53                          ARDOUR::InstrumentInfo&           info,
54                          ARDOUR::MidiModel::PatchChangePtr patch,
55                          Gtkmm2ext::Color               outline_color,
56                          Gtkmm2ext::Color               fill_color)
57         : _region (region)
58         , _info (info)
59         , _patch (patch)
60         , _popup_initialized(false)
61 {
62         _flag = new ArdourCanvas::Flag (
63                 parent,
64                 height,
65                 outline_color,
66                 fill_color,
67                 ArdourCanvas::Duple (x, y),
68                 true);
69
70         CANVAS_DEBUG_NAME (_flag, text);
71
72         _flag->Event.connect (sigc::mem_fun (*this, &PatchChange::event_handler));
73         _flag->set_font_description (UIConfiguration::instance().get_SmallFont());
74         _flag->set_text(text);
75 }
76
77 PatchChange::~PatchChange()
78 {
79         delete _flag;
80 }
81
82 void
83 PatchChange::initialize_popup_menus()
84 {
85         using namespace MIDI::Name;
86
87         boost::shared_ptr<ChannelNameSet> channel_name_set = _info.get_patches (_patch->channel());
88
89         if (!channel_name_set || channel_name_set->patch_banks().size () == 0) {
90                 return;
91         }
92
93         const ChannelNameSet::PatchBanks& patch_banks = channel_name_set->patch_banks();
94
95         if (patch_banks.size() > 1) {
96
97                 // fill popup menu:
98                 Gtk::Menu::MenuList& patch_bank_menus = _popup.items();
99
100                 for (ChannelNameSet::PatchBanks::const_iterator bank = patch_banks.begin();
101                      bank != patch_banks.end();
102                      ++bank) {
103                         Glib::RefPtr<Glib::Regex> underscores = Glib::Regex::create("_");
104                         std::string replacement(" ");
105
106                         Gtk::Menu& patch_bank_menu = *manage(new Gtk::Menu());
107
108                         const PatchNameList& patches = (*bank)->patch_name_list();
109                         Gtk::Menu::MenuList& patch_menus = patch_bank_menu.items();
110
111                         for (PatchNameList::const_iterator patch = patches.begin();
112                              patch != patches.end();
113                              ++patch) {
114                                 std::string name = underscores->replace((*patch)->name().c_str(), -1, 0, replacement);
115
116                                 patch_menus.push_back(
117                                         Gtk::Menu_Helpers::MenuElem(
118                                                 name,
119                                                 sigc::bind(
120                                                         sigc::mem_fun(*this, &PatchChange::on_patch_menu_selected),
121                                                         (*patch)->patch_primary_key())) );
122                         }
123
124
125                         std::string name = underscores->replace((*bank)->name().c_str(), -1, 0, replacement);
126
127                         patch_bank_menus.push_back(
128                                 Gtk::Menu_Helpers::MenuElem(
129                                         name,
130                                         patch_bank_menu) );
131                 }
132
133         } else {
134                 /* only one patch bank, so make it the initial menu */
135
136                 const PatchNameList& patches = patch_banks.front()->patch_name_list();
137                 Gtk::Menu::MenuList& patch_menus = _popup.items();
138
139                 for (PatchNameList::const_iterator patch = patches.begin();
140                      patch != patches.end();
141                      ++patch) {
142                         patch_menus.push_back (Gtkmm2ext::MenuElemNoMnemonic ((*patch)->name(),
143                                                 sigc::bind (sigc::mem_fun(*this, &PatchChange::on_patch_menu_selected), (*patch)->patch_primary_key())));
144                 }
145         }
146 }
147
148 void
149 PatchChange::on_patch_menu_selected(const PatchPrimaryKey& key)
150 {
151         _region.change_patch_change (*this, key);
152 }
153
154 bool
155 PatchChange::event_handler (GdkEvent* ev)
156 {
157         /* XXX: icky dcast */
158         Editor* e = dynamic_cast<Editor*> (&_region.get_time_axis_view().editor());
159
160         if (!e->internal_editing()) {
161                 return false;
162         }
163
164         switch (ev->type) {
165         case GDK_BUTTON_PRESS:
166                 if (e->current_mouse_mode() == Editing::MouseContent) {
167
168                         if (Gtkmm2ext::Keyboard::is_delete_event (&ev->button)) {
169
170                                 _region.delete_patch_change (this);
171                                 return true;
172
173                         } else if (Gtkmm2ext::Keyboard::is_edit_event (&ev->button)) {
174
175                                 _region.edit_patch_change (this);
176                                 return true;
177
178                         } else if (ev->button.button == 1) {
179                                 e->drags()->set (new PatchChangeDrag (e, this, &_region), ev);
180                                 return true;
181                         }
182                 }
183
184                 if (Gtkmm2ext::Keyboard::is_context_menu_event (&ev->button)) {
185                         if (!_popup_initialized) {
186                                 initialize_popup_menus();
187                                 _popup_initialized = true;
188                         }
189                         _popup.popup(ev->button.button, ev->button.time);
190                         return true;
191                 }
192                 break;
193
194         case GDK_KEY_PRESS:
195                 switch (ev->key.keyval) {
196                 case GDK_Up:
197                 case GDK_KP_Up:
198                 case GDK_uparrow:
199                         _region.step_patch(
200                                 *this, Keyboard::modifier_state_contains(ev->key.state, Keyboard::TertiaryModifier), 1);
201                         return true;
202                 case GDK_Down:
203                 case GDK_KP_Down:
204                 case GDK_downarrow:
205                         _region.step_patch(
206                                 *this, Keyboard::modifier_state_contains(ev->key.state, Keyboard::TertiaryModifier), -1);
207                         return true;
208                 default:
209                         break;
210                 }
211                 break;
212
213         case GDK_KEY_RELEASE:
214                 switch (ev->key.keyval) {
215                 case GDK_BackSpace:
216                 case GDK_Delete:
217                         _region.delete_patch_change (this);
218                 default:
219                         break;
220                 }
221                 break;
222
223         case GDK_SCROLL:
224                 if (ev->scroll.direction == GDK_SCROLL_UP) {
225                         _region.step_patch(
226                                 *this, Keyboard::modifier_state_contains(ev->scroll.state, Keyboard::TertiaryModifier), 1);
227                         return true;
228                 } else if (ev->scroll.direction == GDK_SCROLL_DOWN) {
229                         _region.step_patch(
230                                 *this, Keyboard::modifier_state_contains(ev->scroll.state, Keyboard::TertiaryModifier), -1);
231                         return true;
232                 }
233                 break;
234
235         case GDK_ENTER_NOTIFY:
236                 _region.patch_entered (this);
237                 break;
238
239         case GDK_LEAVE_NOTIFY:
240                 _region.patch_left (this);
241                 break;
242
243         default:
244                 break;
245         }
246
247         return false;
248 }
249
250 void
251 PatchChange::move (ArdourCanvas::Duple d)
252 {
253         _flag->move (d);
254 }
255
256 void
257 PatchChange::set_height (ArdourCanvas::Distance height)
258 {
259         _flag->set_height (height);
260 }
261
262 void
263 PatchChange::hide ()
264 {
265         _flag->hide ();
266 }
267
268 void
269 PatchChange::show ()
270 {
271         _flag->show ();
272 }