68dbbe3b0dcd82c3d10899403c0df42a8825a4d5
[ardour.git] / gtk2_ardour / canvas_patch_change.cc
1 /*
2     Copyright (C) 2000-2010 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
22 #include <boost/algorithm/string.hpp>
23
24 #include "pbd/stacktrace.h"
25
26 #include "gtkmm2ext/keyboard.h"
27 #include "ardour/instrument_info.h"
28 #include "midi++/midnam_patch.h"
29
30 #include "ardour_ui.h"
31 #include "canvas_patch_change.h"
32 #include "editor.h"
33 #include "editor_drag.h"
34
35 using namespace Gnome::Canvas;
36 using namespace MIDI::Name;
37 using namespace Gtkmm2ext;
38 using namespace std;
39
40 /** @param x x position in pixels.
41  */
42 CanvasPatchChange::CanvasPatchChange(
43         MidiRegionView&                   region,
44         Group&                            parent,
45         const string&                     text,
46         double                            height,
47         double                            x,
48         double                            y,
49         ARDOUR::InstrumentInfo&           info,
50         ARDOUR::MidiModel::PatchChangePtr patch,
51         bool                              active_channel)
52         : CanvasFlag(
53                 region,
54                 parent,
55                 height,
56                 (active_channel
57                  ? ARDOUR_UI::config()->get_MidiPatchChangeOutline()
58                  : ARDOUR_UI::config()->get_MidiPatchChangeInactiveChannelOutline()),
59                 (active_channel
60                  ? ARDOUR_UI::config()->get_MidiPatchChangeFill()
61                  : ARDOUR_UI::config()->get_MidiPatchChangeInactiveChannelFill()),
62                 x,
63                 y)
64         , _info (info)
65         , _patch (patch)
66         , _popup_initialized(false)
67 {
68         set_text (text);
69 }
70
71 CanvasPatchChange::~CanvasPatchChange()
72 {
73 }
74
75 void
76 CanvasPatchChange::initialize_popup_menus()
77 {
78         boost::shared_ptr<ChannelNameSet> channel_name_set = _info.get_patches (_patch->channel());
79
80         if (!channel_name_set) {
81                 return;
82         }
83
84         const ChannelNameSet::PatchBanks& patch_banks = channel_name_set->patch_banks();
85
86         if (patch_banks.size() > 1) {
87                 // fill popup menu:
88                 Gtk::Menu::MenuList& patch_bank_menus = _popup.items();
89                 
90                 for (ChannelNameSet::PatchBanks::const_iterator bank = patch_banks.begin();
91                      bank != patch_banks.end();
92                      ++bank) {
93                         Gtk::Menu& patch_bank_menu = *manage(new Gtk::Menu());
94                         
95                         const PatchNameList& patches = (*bank)->patch_name_list();
96                         Gtk::Menu::MenuList& patch_menus = patch_bank_menu.items();
97                         
98                         for (PatchNameList::const_iterator patch = patches.begin();
99                              patch != patches.end();
100                              ++patch) {
101                                 std::string name = (*patch)->name();
102                                 boost::replace_all (name, "_", " ");
103                                 
104                                 patch_menus.push_back(
105                                         Gtk::Menu_Helpers::MenuElem(
106                                                 name,
107                                                 sigc::bind(sigc::mem_fun(*this, &CanvasPatchChange::on_patch_menu_selected),
108                                                            (*patch)->patch_primary_key())) );
109                         }
110                         
111                         std::string name = (*bank)->name();
112                         boost::replace_all (name, "_", " ");
113                         
114                         patch_bank_menus.push_back(
115                                 Gtk::Menu_Helpers::MenuElem(
116                                         name,
117                                         patch_bank_menu) );
118                 }
119         } else {
120                 /* only one patch bank, so make it the initial menu */
121
122                 const PatchNameList& patches = patch_banks.front()->patch_name_list();
123                 Gtk::Menu::MenuList& patch_menus = _popup.items();
124                 
125                 for (PatchNameList::const_iterator patch = patches.begin();
126                      patch != patches.end();
127                      ++patch) {
128                         std::string name = (*patch)->name();
129                         boost::replace_all (name, "_", " ");
130                         
131                         patch_menus.push_back (
132                                 Gtk::Menu_Helpers::MenuElem (
133                                         name,
134                                         sigc::bind (sigc::mem_fun(*this, &CanvasPatchChange::on_patch_menu_selected),
135                                                     (*patch)->patch_primary_key())));
136                 }
137         }
138 }
139         
140 void
141 CanvasPatchChange::on_patch_menu_selected(const PatchPrimaryKey& key)
142 {
143         _region.change_patch_change (*this, key);
144 }
145
146 static bool
147 in_edit_mode(Editor* editor)
148 {
149         return (editor->internal_editing() &&
150                 (editor->current_mouse_mode() == Editing::MouseObject ||
151                  editor->current_mouse_mode() == Editing::MouseDraw));
152 }
153
154 bool
155 CanvasPatchChange::on_event (GdkEvent* ev)
156 {
157         /* XXX: icky dcast */
158         Editor* e = dynamic_cast<Editor*> (&_region.get_time_axis_view().editor());
159         
160         if (!in_edit_mode(e)) {
161                 return false;
162         }
163
164         switch (ev->type) {
165         case GDK_BUTTON_PRESS:
166                 if (Gtkmm2ext::Keyboard::is_delete_event (&ev->button)) {
167                         
168                         _region.delete_patch_change (this);
169                         return true;
170                         
171                 } else if (Gtkmm2ext::Keyboard::is_edit_event (&ev->button)) {
172                         
173                         _region.edit_patch_change (this);
174                         return true;
175                         
176                 } else if (ev->button.button == 1) {
177                         e->drags()->set (new PatchChangeDrag (e, this, &_region), ev);
178                         return true;
179                 }
180
181                 if (ev->button.button == 3) {
182                         if (!_popup_initialized) {
183                                 initialize_popup_menus();
184                                 _popup_initialized = true;
185                         }
186                         if (!_popup.items().empty()) {
187                                 _popup.popup(ev->button.button, ev->button.time);
188                         }
189                         return true;
190                 }
191                 break;
192
193         case GDK_KEY_PRESS:
194                 switch (ev->key.keyval) {
195                 case GDK_Up:
196                 case GDK_KP_Up:
197                 case GDK_uparrow:
198                         if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
199                                 _region.previous_bank (*this);
200                         } else {
201                                 _region.previous_patch (*this);
202                         }
203                         break;
204                 case GDK_Down:
205                 case GDK_KP_Down:
206                 case GDK_downarrow:
207                         if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
208                                 _region.next_bank (*this);
209                         } else {
210                                 _region.next_patch (*this);
211                         }
212                         break;
213                 case GDK_Delete:
214                 case GDK_BackSpace:
215                         _region.delete_patch_change (this);
216                         break;
217                 default:
218                         break;
219                 }
220                 break;
221
222         case GDK_SCROLL:
223                 if (ev->scroll.direction == GDK_SCROLL_UP) {
224                         if (Keyboard::modifier_state_contains (ev->scroll.state, Keyboard::PrimaryModifier)) {
225                                 _region.previous_bank (*this);
226                         } else {
227                                 _region.previous_patch (*this);
228                         }
229                 } else if (ev->scroll.direction == GDK_SCROLL_DOWN) {
230                                 if (Keyboard::modifier_state_contains (ev->scroll.state, Keyboard::PrimaryModifier)) {
231                                         _region.next_bank (*this);
232                                 } else {
233                                         _region.next_patch (*this);
234                                 }
235                 }
236                 return true;
237
238         case GDK_ENTER_NOTIFY:
239                 _region.patch_entered (this);
240                 return true;
241                 break;
242
243         case GDK_LEAVE_NOTIFY:
244                 _region.patch_left (this);
245                 return true;
246                 break;
247
248         default:
249                 break;
250         }
251
252         return false;
253 }