Guitarix
gx_main_midi.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3  * Copyright (C) 2011 Pete Shorthose
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * ---------------------------------------------------------------------------
19  *
20  * This file is part of the guitarix GUI main class
21  *
22  * ----------------------------------------------------------------------------
23  */
24 
25 #include "guitarix.h" // NOLINT
26 
27 /****************************************************************
28  ** MidiControllerTable
29  */
30 namespace gx_main_midi {
31 
32 GtkWidget *MidiControllerTable::window = 0;
33 
34 void MidiControllerTable::response_cb(GtkWidget *widget, gint response_id, gpointer data) {
35  MidiControllerTable& m = *reinterpret_cast<MidiControllerTable*>(data);
36  if (response_id == RESPONSE_DELETE_SELECTED) {
37  GtkTreeModel *model;
38  GList *list = gtk_tree_selection_get_selected_rows(m.selection, &model);
39  gtk_tree_selection_unselect_all(m.selection);
40  for (GList *p = g_list_last(list); p; p = g_list_previous(p)) {
41  GtkTreeIter iter;
42  gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter,
43  reinterpret_cast<GtkTreePath*>(p->data));
44  const char* id;
45  gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 7, &id, -1);
46  m.midi_conn.block();
47  m.machine.midi_deleteParameter(m.machine.get_parameter(id));
48  m.midi_conn.unblock();
49  gtk_tree_path_free(reinterpret_cast<GtkTreePath*>(p->data));
50  }
51  g_list_free(list);
52  m.machine.signal_midi_changed()();
53  return;
54  }
55  m.menuaction->set_active(false);
56 }
57 
58 void MidiControllerTable::destroy_cb(GtkWidget*, gpointer data) {
59  delete reinterpret_cast<MidiControllerTable*>(data);
60 }
61 
62 void MidiControllerTable::edited_cb(
63  GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer data) {
64  GtkListStore *store = GTK_LIST_STORE(data);
65  GtkTreeIter iter;
66  gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path);
67  int ctrl;
68  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &ctrl, -1);
69  gx_engine::midi_std_ctr.replace(ctrl, new_text);
70  bool valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
71  const char *name = gx_engine::midi_std_ctr[ctrl].c_str();
72  while (valid) {
73  int n;
74  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &n, -1);
75  if (n == ctrl) {
76  gtk_list_store_set(store, &iter, 1, name, -1);
77  }
78  valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
79  }
80 }
81 
82 void MidiControllerTable::toggleButtonSetSwitch(GtkWidget *w, gpointer data) {
84  p->set(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));
85 }
86 
87 void MidiControllerTable::set(bool v) {
88  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), v);
89 }
90 
91 void MidiControllerTable::load() {
92  GtkTreeIter iter;
93  gtk_list_store_clear(store);
94  for (int i = 0; i < machine.midi_size(); i++) {
96  for (gx_engine::midi_controller_list::iterator j = cl.begin(); j != cl.end(); ++j) {
97  gx_engine::Parameter& p = j->getParameter();
98  string low, up;
99  const char *tp;
100  float step = p.getStepAsFloat();
102  tp = "Scale";
103  low = gx_gui::fformat(j->lower(), step);
104  up = gx_gui::fformat(j->upper(), step);
105  } else if (p.getControlType() == gx_engine::Parameter::Enum) {
106  tp = "Select";
107  low = gx_gui::fformat(j->lower(), step);
108  up = gx_gui::fformat(j->upper(), step);
109  } else if (p.getControlType() == gx_engine::Parameter::Switch) {
110  if (j->is_toggle()) {
111  tp = "Toggle";
112  } else {
113  tp = "Switch";
114  }
115  low = up = "";
116  } else {
117  tp = "??";
118  assert(false);
119  }
120  gtk_list_store_append(store, &iter);
121  gtk_list_store_set(store, &iter,
122  0, i,
123  1, gx_engine::midi_std_ctr[i].c_str(),
124  2, p.l_group().c_str(),
125  3, p.l_name().c_str(),
126  4, tp,
127  5, low.c_str(),
128  6, up.c_str(),
129  7, p.id().c_str(),
130  -1);
131  }
132  }
133 }
134 
135 void MidiControllerTable::toggle(gx_engine::GxMachineBase& machine, Glib::RefPtr<Gtk::ToggleAction> item) {
136  if (!item->get_active()) {
137  if (window) {
138  gtk_widget_destroy(window);
139  }
140  } else {
141  if (!window) {
142  new MidiControllerTable(machine, item);
143  }
144  }
145 }
146 
147 MidiControllerTable::~MidiControllerTable() {
148  window = NULL;
149 }
150 
151 MidiControllerTable::MidiControllerTable(gx_engine::GxMachineBase& machine_, Glib::RefPtr<Gtk::ToggleAction> item)
152  : menuaction(item),
153  machine(machine_),
154  midi_conn() {
155 
156  GtkBuilder * builder = gtk_builder_new();
157  window = gx_gui::load_toplevel(builder, "midi.glade", "MidiControllerTable");
158  store = GTK_LIST_STORE(gtk_builder_get_object(builder, "liststore1"));
159  togglebutton = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "save_controller"));
160 
161  gx_engine::BoolParameter& p = machine.get_parameter("system.midi_in_preset").getBool();
162  gtk_toggle_button_set_active(togglebutton, p.get_value());
163  machine.signal_parameter_value<bool>("system.midi_in_preset").connect(sigc::mem_fun(*this, &MidiControllerTable::set));
164  g_signal_connect(GTK_OBJECT(togglebutton), "toggled",
165  G_CALLBACK(toggleButtonSetSwitch), (gpointer)&p);
166  //g_signal_connect(gtk_builder_get_object(builder, "dialog-vbox1"),"expose-event",
167  //G_CALLBACK(gx_cairo::rectangle_skin_color_expose), NULL);
168  //g_signal_connect(gtk_builder_get_object(builder, "dialog-vbox2"),"expose-event",
169  //G_CALLBACK(gx_cairo::rectangle_skin_color_expose), NULL);
170  selection = gtk_tree_view_get_selection(
171  GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview1")));
172  gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
173  gtk_widget_set_redraw_on_allocate(GTK_WIDGET(gtk_builder_get_object(builder, "dialog-vbox1")),true);
174  gtk_widget_set_redraw_on_allocate(GTK_WIDGET(gtk_builder_get_object(builder, "dialog-vbox2")),true);
175  load();
176 
177  g_signal_connect(window, "destroy", G_CALLBACK(destroy_cb), this);
178  g_signal_connect(window, "response", G_CALLBACK(response_cb), this);
179  g_signal_connect(G_OBJECT(gtk_builder_get_object(builder, "cellrenderertext2")),
180  "edited", G_CALLBACK(edited_cb), store);
181 
182  //gtk_window_add_accel_group(GTK_WINDOW(window),
183  // gx_gui::GxMainInterface::get_instance().fAccelGroup->gobj());
184 
185  gtk_widget_show(window);
186  g_object_unref(G_OBJECT(builder));
187  midi_conn = machine.signal_midi_changed().connect(
188  sigc::mem_fun(*this, &MidiControllerTable::load));
189 }
190 
191 
192 /*****************************************************************
193  ** Midi Control
194  */
195 
196 string MidiConnect::ctr_desc(int ctr) {
197  string p = gx_engine::midi_std_ctr[ctr];
198  if (p.empty())
199  return p;
200  return "(" + p + ")";
201 }
202 
203 
204 void MidiConnect::midi_response_cb(GtkWidget *widget, gint response_id, gpointer data) {
205  MidiConnect* m = reinterpret_cast<MidiConnect*>(data);
206  switch (response_id) {
207  case GTK_RESPONSE_OK:
210  assert(m->adj_lower);
211  assert(m->adj_upper);
212  float lower = gtk_adjustment_get_value(m->adj_lower);
213  float upper = gtk_adjustment_get_value(m->adj_upper);
214  m->machine.midi_modifyCurrent(m->param, lower, upper, false, gx_engine::Parameter::toggle_type::OnOff);
215  } else {
216  bool toggle = gtk_toggle_button_get_active(m->use_toggle);
217  int toggle_behaviour = gtk_combo_box_get_active(GTK_COMBO_BOX(m->toggle_behaviours));
218  m->machine.midi_modifyCurrent(m->param, 0, 0, toggle, toggle_behaviour);
219  }
220  break;
221  case RESPONSE_DELETE:
222  m->machine.midi_deleteParameter(m->param);
223  break;
224  case GTK_RESPONSE_HELP:
225  static string midiconnecthelp;
226  if (midiconnecthelp.empty()) {
227  midiconnecthelp +=_("\n Guitarix:Midi learn \n");
228  midiconnecthelp +=
229  _(" Just move your midi controller to connect it \n"
230  " with the selected guitarix Controller. \n"
231  " As soon the Midi Controller is detected, \n"
232  " you will see the Controller Number in the \n"
233  " Midi Controller Number field. Press 'OK' to connect it, \n"
234  " or move a other Midi controller. \n"
235  " A exception is the MIDI BEAT CLOCK, \n"
236  " which isn't a Controller itself,\n"
237  " but could be used here to sync BPM controllers \n"
238  " with external devices. \n"
239  " To use it, you must insert '22' as Midi Controller Number \n"
240  " \n"
241  " The same is true for the MIDI CLOCK start/stop function, \n"
242  " which could be used to switch effects on/off. \n"
243  " To use it, you must insert '23' as Midi Controller Number. \n\n"
244  " Also Jack Transport is supported and can be used to control \n"
245  " switch controllers (on/off) by connect then to \n"
246  " Midi Controller Number '24'. \n"
247 
248  "");
249 
250  }
251 
252  gx_gui::gx_message_popup(midiconnecthelp.c_str());
253  return;
254  break;
255  }
256  gtk_widget_destroy(m->dialog);
257 }
258 
259 void MidiConnect::midi_destroy_cb(GtkWidget *widget, gpointer data) {
260  MidiConnect *m = reinterpret_cast<MidiConnect*>(data);
261  m->machine.midi_set_config_mode(false);
262 }
263 
264 void MidiConnect::toggle_behaviours_visibility(GtkWidget *widget, gpointer data) {
265  MidiConnect *m = reinterpret_cast<MidiConnect*>(data);
266  int b = gtk_toggle_button_get_active(m->use_toggle);
267  gtk_widget_set_sensitive(gtk_widget_get_parent(m->toggle_behaviours), b);
268 }
269 
270 const char* MidiConnect::ctl_to_str(int n) {
271  static char buf[12];
272  if (n < 0)
273  strcpy(buf, "---"); // NOLINT
274  else
275  snprintf(buf, sizeof(buf), "%3d", n);
276  return buf;
277 }
278 
279 gboolean MidiConnect::check_midi_cb(gpointer data) {
280  MidiConnect *m = reinterpret_cast<MidiConnect*>(data);
281  int ctl;
282  if (!m->machine.midi_get_config_mode(&ctl)) {
283  delete m;
284  return FALSE;
285  }
286  if (m->current_control == ctl)
287  return TRUE;
288  m->current_control = ctl;
289  gtk_entry_set_text(GTK_ENTRY(m->entry_new), ctl_to_str(ctl));
290  if ( ctl>200) {
291  gtk_toggle_button_set_active(m->use_toggle, true);
292  gtk_combo_box_set_active(GTK_COMBO_BOX(m->toggle_behaviours), gx_engine::Parameter::toggle_type::Constant);
293  }
294  return TRUE;
295 }
296 
297 void MidiConnect::changed_text_handler(GtkEditable *editable, gpointer data) {
298  MidiConnect *m = reinterpret_cast<MidiConnect*>(data);
299  gchar *p = gtk_editable_get_chars(editable, 0, -1);
300  ostringstream buf;
301  for (const char *q = p; *q; q++) {
302  if (isdigit(*q)) {
303  buf << *q;
304  }
305  }
306  string str = buf.str();
307  int n = -1;
308  if (!str.empty()) {
309  istringstream i(buf.str());
310  i >> n;
311  if (n > 327) {
312  n = 327;
313  }
314  ostringstream b;
315  b << n;
316  str = b.str().substr(0, 3);
317  }
318  // prevent infinite loop because after it has changed the text
319  // the handler will be called again (and make sure the text
320  // tranformation in this handler is idempotent!)
321  if (str == p) {
322  if (str.empty()) {
323  gtk_dialog_set_response_sensitive(GTK_DIALOG(m->dialog), GTK_RESPONSE_OK, FALSE);
324  gtk_dialog_set_default_response(GTK_DIALOG(m->dialog), GTK_RESPONSE_CANCEL);
325  } else {
326  gtk_dialog_set_response_sensitive(GTK_DIALOG(m->dialog), GTK_RESPONSE_OK, TRUE);
327  gtk_dialog_set_default_response(GTK_DIALOG(m->dialog), GTK_RESPONSE_OK);
328  }
329  gtk_label_set_text(GTK_LABEL(m->label_desc), ctr_desc(n).c_str());
330  m->machine.midi_set_current_control(n);
331  m->current_control = n;
332  return;
333  }
334  gtk_editable_delete_text(editable, 0, -1);
335  gint position = 0;
336  gtk_editable_insert_text(editable, str.c_str(), str.size(), &position);
337 }
338 
339 
341  : param(param_),
342  machine(machine_),
343  current_control(-1),
344  adj_lower(),
345  adj_upper() {
346  GtkBuilder * builder = gtk_builder_new();
347  dialog = gx_gui::load_toplevel(builder, "midi.glade", "MidiConnect");
348  use_toggle = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "use_toggle"));
349  toggle_behaviours = GTK_WIDGET(gtk_builder_get_object(builder, "toggle_behaviours"));
350  GtkWidget *zn = GTK_WIDGET(gtk_builder_get_object(builder, "zone_name"));
351  GtkStyle *style = gtk_widget_get_style(zn);
352  pango_font_description_set_size(style->font_desc, 12*PANGO_SCALE);
353  pango_font_description_set_weight(style->font_desc, PANGO_WEIGHT_BOLD);
354  gtk_widget_modify_font(zn, style->font_desc);
355  gtk_label_set_text(GTK_LABEL(zn), (param.l_group() + ": " + param.l_name()).c_str());
356  gtk_widget_set_tooltip_text(zn, (_("Parameter ID: ")+param.id()).c_str());
357  zn = GTK_WIDGET(gtk_builder_get_object(builder, "desc_box"));
358  if (param.desc().empty()) {
359  gtk_widget_hide(zn);
360  } else {
361  GtkWidget *desc = GTK_WIDGET(gtk_builder_get_object(builder, "desc"));
362  gtk_label_set_text(GTK_LABEL(desc), param.l_desc().c_str());
363  gtk_widget_show(zn);
364  }
365  const gx_engine::MidiController *pctrl;
366  int nctl = machine.midi_param2controller(param, &pctrl);
369  float lower = param.getLowerAsFloat();
370  float upper = param.getUpperAsFloat();
371  float step = param.getStepAsFloat();
372  GtkSpinButton *spinner;
373  adj_lower = GTK_ADJUSTMENT(gtk_adjustment_new(lower, lower, upper, step, 10*step, 0));
374  spinner = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "lower"));
375  float climb_rate = 0.0;
376  gtk_spin_button_configure(spinner, adj_lower, climb_rate, gx_gui::precision(step));
377  adj_upper = GTK_ADJUSTMENT(gtk_adjustment_new(upper, lower, upper, step, 10*step, 0));
378  spinner = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "upper"));
379  gtk_spin_button_configure(spinner, adj_upper, climb_rate, gx_gui::precision(step));
380  if (nctl != -1) {
381  gtk_adjustment_set_value(adj_lower, pctrl->lower());
382  gtk_adjustment_set_value(adj_upper, pctrl->upper());
383  }
384  gtk_widget_hide(gtk_widget_get_parent(gtk_widget_get_parent(GTK_WIDGET(use_toggle))));
385  } else {
386  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "range_label")));
387  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "range_box")));
388 
389  store = GTK_LIST_STORE(gtk_builder_get_object(builder, "liststore2"));
390  GtkTreeIter iter;
391  gtk_list_store_clear(store);
392  for (std::map<gx_engine::Parameter::toggle_type, const char*>::iterator it = toggle_behaviour_descriptions.begin();
393  it!=toggle_behaviour_descriptions.end(); ++it) {
394  gtk_list_store_append(store, &iter);
395  gtk_list_store_set(store, &iter, 0, it->first, 1, it->second, -1);
396  }
397  gtk_combo_box_set_model(GTK_COMBO_BOX(toggle_behaviours), GTK_TREE_MODEL(store));
398  //g_object_unref(store); // this THROW A GLib-GObject-CRITICAL WARNING
399  GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
400  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(toggle_behaviours), renderer, TRUE);
401  gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(toggle_behaviours), renderer, "text", 1, NULL); // "cell-background", 0, -> throw Gtk-WARNING **: Don't know color `0'
402  gtk_combo_box_set_active(GTK_COMBO_BOX(toggle_behaviours), gx_engine::Parameter::toggle_type::OnOff);
403  if (nctl != -1) {
404  gtk_toggle_button_set_active(use_toggle, pctrl->is_toggle());
405  gtk_combo_box_set_active(GTK_COMBO_BOX(toggle_behaviours), pctrl->toggle_behaviour());
406  }
407  int b = gtk_toggle_button_get_active(use_toggle);
408  gtk_widget_set_sensitive(gtk_widget_get_parent(toggle_behaviours), b);
409  }
410  entry_new = GTK_WIDGET(gtk_builder_get_object(builder, "new"));
411  label_desc = GTK_WIDGET(gtk_builder_get_object(builder, "new_desc"));
412 
413  g_signal_connect(dialog, "response", G_CALLBACK(midi_response_cb), this);
414  g_signal_connect(dialog, "destroy", G_CALLBACK(midi_destroy_cb), this);
415  g_signal_connect(entry_new, "changed", G_CALLBACK(changed_text_handler), this);
416  g_signal_connect(use_toggle, "toggled", G_CALLBACK(toggle_behaviours_visibility), this);
417  if (nctl == -1) {
418  gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), RESPONSE_DELETE, FALSE);
419  gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, FALSE);
420  gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_HELP, TRUE);
421  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
422  }
423  machine.midi_set_config_mode(true, nctl);
424  check_midi_cb(this);
425  gtk_widget_show(dialog);
426  g_timeout_add(40, check_midi_cb, this);
427  g_object_unref(G_OBJECT(builder));
428 }
429 } // end namespace
static void changed_text_handler(GtkEditable *entry, gpointer data)
string l_desc() const
Definition: gx_parameter.h:180
MidiStandardControllers midi_std_ctr
virtual Parameter & get_parameter(const std::string &id)=0
virtual float getUpperAsFloat() const
MidiConnect(GdkEventButton *event, gx_engine::Parameter &param, gx_engine::GxMachineBase &machine)
virtual int midi_size()=0
int precision(double n)
ctrl_type getControlType() const
Definition: gx_parameter.h:168
static void midi_response_cb(GtkWidget *widget, gint response_id, gpointer data)
static gboolean check_midi_cb(gpointer)
virtual midi_controller_list & midi_get(int n)=0
virtual float getStepAsFloat() const
virtual void midi_set_config_mode(bool v, int ctl=-1)=0
virtual float getLowerAsFloat() const
virtual void midi_modifyCurrent(Parameter &param, float lower, float upper, bool toggle, int toggle_behaviour)=0
string l_group() const
Definition: gx_parameter.h:175
string l_name() const
Definition: gx_parameter.h:177
static void toggle(gx_engine::GxMachineBase &machine, Glib::RefPtr< Gtk::ToggleAction > item)
virtual sigc::signal< void > & signal_midi_changed()=0
virtual int midi_param2controller(Parameter &param, const MidiController **p)=0
list< MidiController > midi_controller_list
Definition: gx_parameter.h:698
const string & desc() const
Definition: gx_parameter.h:178
static void toggle_behaviours_visibility(GtkWidget *widget, gpointer data)
virtual void midi_set_current_control(int v)=0
GtkWidget * load_toplevel(GtkBuilder *builder, const char *filename, const char *windowname)
virtual void midi_deleteParameter(Parameter &param)=0
static void midi_destroy_cb(GtkWidget *widget, gpointer data)
void replace(int ctr, const string &name)
virtual bool midi_get_config_mode(int *ctl=0)=0
sigc::signal< void, T > & signal_parameter_value(const std::string &id)
BoolParameter & getBool()
Definition: gx_parameter.h:469
std::string fformat(float value, float step)
const string & id() const
Definition: gx_parameter.h:173
int gx_message_popup(const char *)