add remove binding button to key binding editor; change progress bar in stretch dialo...
[ardour.git] / gtk2_ardour / keyeditor.cc
1 #include <map>
2
3 #include <ardour/profile.h>
4
5 #include <gtkmm/stock.h>
6 #include <gtkmm/accelkey.h>
7 #include <gtkmm/accelmap.h>
8 #include <gtkmm/uimanager.h>
9
10 #include <pbd/strsplit.h>
11 #include <pbd/replace_all.h>
12
13 #include "actions.h"
14 #include "keyboard.h"
15 #include "keyeditor.h"
16
17 #include "i18n.h"
18
19 using namespace std;
20 using namespace Gtk;
21 using namespace Gdk;
22 using namespace PBD;
23
24 KeyEditor::KeyEditor ()
25         : ArdourDialog (_("Shortcut Editor"), false)
26         , unbind_button (_("Remove shortcut"))
27         , unbind_box (BUTTONBOX_END)
28         
29 {
30         can_bind = false;
31         last_state = 0;
32
33         model = TreeStore::create(columns);
34
35         view.set_model (model);
36         view.append_column (_("Action"), columns.action);
37         view.append_column (_("Shortcut"), columns.binding);
38         view.set_headers_visible (true);
39         view.get_selection()->set_mode (SELECTION_SINGLE);
40         view.set_reorderable (false);
41         view.set_size_request (500,300);
42         view.set_enable_search (false);
43         view.set_rules_hint (true);
44         view.set_name (X_("KeyEditorTree"));
45         
46         view.get_selection()->signal_changed().connect (mem_fun (*this, &KeyEditor::action_selected));
47         
48         scroller.add (view);
49         scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
50
51         unbind_box.pack_start (unbind_button, false, false);
52
53         unbind_button.signal_clicked().connect (mem_fun (*this, &KeyEditor::unbind));
54
55         get_vbox()->set_spacing (6);
56         get_vbox()->pack_start (scroller);
57         get_vbox()->pack_start (unbind_box, false, false);
58         get_vbox()->set_border_width (12);
59
60         scroller.show ();
61         view.show ();
62         unbind_box.show ();
63         unbind_button.show ();
64         unbind_button.set_sensitive (false);
65 }
66
67 void
68 KeyEditor::unbind ()
69 {
70         TreeModel::iterator i = view.get_selection()->get_selected();
71         
72         unbind_button.set_sensitive (false);
73
74         if (i != model->children().end()) {
75                 string path = (*i)[columns.path];
76                 
77                 if (!(*i)[columns.bindable]) {
78                         return;
79                 } 
80
81                 bool result = AccelMap::change_entry (path,
82                                                       0,
83                                                       (ModifierType) 0,
84                                                       true);
85                 if (result) {
86                         (*i)[columns.binding] = string ();
87                 }
88         }
89 }
90
91 void
92 KeyEditor::on_show ()
93 {
94         populate ();
95         view.get_selection()->unselect_all ();
96         ArdourDialog::on_show ();
97 }
98
99 void
100 KeyEditor::on_unmap ()
101 {
102         ArdourDialog::on_unmap ();
103 }
104
105 void
106 KeyEditor::action_selected ()
107 {
108         if (view.get_selection()->count_selected_rows() == 0) {
109                 return;
110         }
111
112         TreeModel::iterator i = view.get_selection()->get_selected();
113         
114         unbind_button.set_sensitive (false);
115
116         if (i != model->children().end()) {
117
118                 string path = (*i)[columns.path];
119                 
120                 if (!(*i)[columns.bindable]) {
121                         return;
122                 } 
123
124                 string binding = (*i)[columns.binding];
125
126                 if (!binding.empty()) {
127                         unbind_button.set_sensitive (true);
128                 }
129         }
130 }
131
132 bool
133 KeyEditor::on_key_press_event (GdkEventKey* ev)
134 {
135         can_bind = true;
136         last_state = ev->state;
137         return false;
138 }
139
140 bool
141 KeyEditor::on_key_release_event (GdkEventKey* ev)
142 {
143         if (ARDOUR::Profile->get_sae() || !can_bind || ev->state != last_state) {
144                 return false;
145         }
146
147         TreeModel::iterator i = view.get_selection()->get_selected();
148
149         if (i != model->children().end()) {
150                 string path = (*i)[columns.path];
151                 
152                 if (!(*i)[columns.bindable]) {
153                         goto out;
154                 } 
155
156                 bool result = AccelMap::change_entry (path,
157                                                       ev->keyval,
158                                                       (ModifierType) ev->state,
159                                                       true);
160
161                 if (result) {
162                         bool known;
163                         AccelKey key;
164
165                         known = ActionManager::lookup_entry (path, key);
166                         
167                         if (known) {
168                                 (*i)[columns.binding] = ActionManager::ui_manager->get_accel_group()->name (key.get_key(), Gdk::ModifierType (key.get_mod()));
169                         } else {
170                                 (*i)[columns.binding] = string();
171                         }
172                 }
173         }
174
175   out:
176         can_bind = false;
177         return true;
178 }
179
180 void
181 KeyEditor::populate ()
182 {
183         vector<string> paths;
184         vector<string> labels;
185         vector<string> keys;
186         vector<AccelKey> bindings;
187         typedef std::map<string,TreeIter> NodeMap;
188         NodeMap nodes;
189         NodeMap::iterator r;
190         
191         ActionManager::get_all_actions (labels, paths, keys, bindings);
192         
193         vector<string>::iterator k;
194         vector<string>::iterator p;
195         vector<string>::iterator l;
196
197         model->clear ();
198
199         for (l = labels.begin(), k = keys.begin(), p = paths.begin(); l != labels.end(); ++k, ++p, ++l) {
200
201                 TreeModel::Row row;
202                 vector<string> parts;
203                 
204                 parts.clear ();
205
206                 split (*p, parts, '/');
207                 
208                 if (parts.empty()) {
209                         continue;
210                 }
211
212                 if ((r = nodes.find (parts[1])) == nodes.end()) {
213
214                         /* top level is missing */
215
216                         TreeIter rowp;
217                         TreeModel::Row parent;
218                         rowp = model->append();
219                         nodes[parts[1]] = rowp;
220                         parent = *(rowp);
221                         parent[columns.action] = parts[1];
222                         parent[columns.bindable] = false;
223
224                         row = *(model->append (parent.children()));
225
226                 } else {
227                         
228                         row = *(model->append ((*r->second)->children()));
229
230                 }
231                 
232                 /* add this action */
233
234                 row[columns.action] = (*l);
235                 row[columns.path] = (*p);
236                 row[columns.bindable] = true;
237                 
238                 if (*k == ActionManager::unbound_string) {
239                         row[columns.binding] = string();
240                 } else {
241
242 #ifdef GTKOSX
243                         string label = (*k);
244                         replace_all (label, "<Mod5>", _("Command-"));
245                         replace_all (label, "<Alt>", _("Option-"));
246                         replace_all (label, "<Shift>", _("Shift-"));
247                         row[columns.binding] = label;
248 #else           
249                         row[columns.binding] = (*k);
250 #endif
251                 }
252         }
253 }