Guitarix
gx_preset_window.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  *
21  * ----------------------------------------------------------------------------
22  */
23 
24 #include <guitarix.h>
25 
26 /****************************************************************
27  ** class PresetWindow
28  */
29 
30 PresetStore::PresetStore(): Gtk::ListStore(), col() {
31  set_column_types(col);
32 }
33 
34 bool PresetStore::row_draggable_vfunc(const TreeModel::Path& path) const {
35  Gtk::TreeModel::const_iterator i = const_cast<PresetStore*>(this)->get_iter(path); // Bug in Gtkmm: no get_iter() const
36  Glib::ustring s(i->get_value(col.name));
37  if (s.empty()) {
38  return false;
39  } else {
40  return true;
41  }
42 }
43 
44 
45 PresetWindow::PresetWindow(Glib::RefPtr<gx_gui::GxBuilder> bld, gx_engine::GxMachineBase& machine_,
46  const gx_system::CmdlineOptions& options_, GxActions& actions_, GtkSizeGroup *lc)
47  : sigc::trackable(),
48  machine(machine_),
49  actions(actions_),
50  in_edit(false),
51  edit_iter(),
52  pb_edit(),
53  pb_del(),
54  pb_scratch(),
55  pb_versiondiff(),
56  pb_readonly(),
57  pb_factory(),
58  pstore(new PresetStore),
59  target_col(),
60  bank_col(),
61  bank_row_del_conn(),
62  preset_row_del_conn(),
63  vpaned_pos(),
64  vpaned_step(),
65  vpaned_target(),
66  options(options_),
67  in_current_preset(false),
68  on_map_conn()
69  /* widget pointers not initialized */ {
70  load_widget_pointers(bld);
71  actions.new_bank = Gtk::Action::create("NewBank");
72  actions.group->add(actions.new_bank, sigc::mem_fun(*this, &PresetWindow::on_new_bank));
73  gtk_activatable_set_related_action(GTK_ACTIVATABLE(new_preset_bank->gobj()), actions.new_bank->gobj());
74  actions.save_changes = Gtk::Action::create("Save", _("_Save changes"));
75  actions.group->add(actions.save_changes, sigc::mem_fun(*this, &PresetWindow::on_preset_save));
76  gtk_activatable_set_related_action(GTK_ACTIVATABLE(save_preset->gobj()), actions.save_changes->gobj());
77  actions.organize = Gtk::ToggleAction::create("Organize", _("_Organize"));
78  actions.group->add(actions.organize, sigc::mem_fun(*this, &PresetWindow::on_organize));
79  gtk_activatable_set_related_action(GTK_ACTIVATABLE(organize_presets->gobj()), GTK_ACTION(actions.organize->gobj()));
80  //act = Gtk::Action::create("ClosePresetsAction");
81  //actiongroup->add(act, sigc::mem_fun(*this, &PresetWindow::on_presets_close));
82  //gtk_activatable_set_related_action(GTK_ACTIVATABLE(close_preset->gobj()), act->gobj());
83  close_preset->hide(); // disable (maybe remove later)
84  actions.online_preset_bank = Gtk::Action::create("OnlineBank");
85  actions.group->add(actions.online_preset_bank, sigc::mem_fun(*this, &PresetWindow::on_online_preset));
86  gtk_activatable_set_related_action(GTK_ACTIVATABLE(online_preset->gobj()), actions.online_preset_bank->gobj());
87  // if (!machine.get_jack()) online_preset->set_sensitive(false);
88  bank_treeview->set_model(Gtk::ListStore::create(bank_col));
89  bank_treeview->set_name("PresetView");
90  bank_treeview->get_selection()->set_select_function(
91  sigc::mem_fun(*this, &PresetWindow::select_func));
92  bank_treeview->set_has_tooltip(true);
93  bank_treeview->signal_query_tooltip().connect(
94  sigc::mem_fun(*this, &PresetWindow::on_bank_query_tooltip));
95  pb_edit = bank_treeview->render_icon(Gtk::Stock::EDIT, Gtk::ICON_SIZE_MENU);
96  pb_del = bank_treeview->render_icon(Gtk::Stock::DELETE, Gtk::ICON_SIZE_MENU);
97  pb_scratch = Gdk::Pixbuf::create_from_file(options.get_style_filepath("scratch.png"));
98  pb_versiondiff = Gdk::Pixbuf::create_from_file(options.get_style_filepath("versiondiff.png"));
99  pb_readonly = Gdk::Pixbuf::create_from_file(options.get_style_filepath("readonly.png"));
100  pb_factory = Gdk::Pixbuf::create_from_file(options.get_style_filepath("factory.png"));
101  bank_treeview->set_row_separator_func(sigc::mem_fun(*this, &PresetWindow::is_row_separator));
102  bank_cellrenderer->signal_edited().connect(
103  sigc::bind(sigc::mem_fun(*this, &PresetWindow::on_bank_edited), bank_treeview));
104  bank_cellrenderer->signal_editing_canceled().connect(
105  sigc::bind(sigc::mem_fun(*this, &PresetWindow::on_edit_canceled), bank_treeview->get_column(1)));
106  bank_cellrenderer->signal_editing_started().connect(
107  sigc::bind(sigc::mem_fun(*this, &PresetWindow::on_editing_started), bank_treeview->get_model()));
108  Gtk::TreeViewColumn *col = bank_treeview->get_column(1);
109  col->set_cell_data_func(**col->get_cell_renderers().begin(), sigc::mem_fun(*this, &PresetWindow::highlight_current_bank));
110 
111  std::vector<Gtk::TargetEntry> listTargets;
112  listTargets.push_back(Gtk::TargetEntry("GTK_TREE_MODEL_ROW", Gtk::TARGET_SAME_WIDGET, MODELROW_TARGET));
113  listTargets.push_back(Gtk::TargetEntry("text/uri-list", Gtk::TARGET_OTHER_APP, URILIST_TARGET));
114  bank_treeview->enable_model_drag_source(listTargets, Gdk::BUTTON1_MASK, Gdk::ACTION_COPY);
115  bank_treeview->drag_source_add_text_targets(); // sets info == 0 (TEXT_TARGETS)
116  bank_treeview->signal_drag_motion().connect(sigc::mem_fun(*this, &PresetWindow::on_bank_drag_motion), false);
117  bank_treeview->enable_model_drag_dest(listTargets, Gdk::ACTION_COPY);
118  bank_treeview->signal_drag_data_received().connect(
119  sigc::mem_fun(*this, &PresetWindow::on_bank_drag_data_received));
120  bank_treeview->signal_drag_data_get().connect(
121  sigc::mem_fun(*this, &PresetWindow::on_bank_drag_data_get));
122  bank_treeview->signal_drag_begin().connect(
123  sigc::hide(sigc::mem_fun(machine, &gx_engine::GxMachineBase::bank_drag_begin)));
124  Glib::RefPtr<Gtk::TreeSelection> sel = bank_treeview->get_selection();
125  sel->set_mode(Gtk::SELECTION_BROWSE);
126  sel->signal_changed().connect(sigc::mem_fun(*this, &PresetWindow::on_bank_changed));
127  bank_treeview->signal_button_release_event().connect(sigc::mem_fun(*this, &PresetWindow::on_bank_button_release), true);
128  Glib::RefPtr<Gtk::TreeModel> ls = bank_treeview->get_model();
129  bank_row_del_conn = ls->signal_row_deleted().connect(sigc::mem_fun(*this, &PresetWindow::on_bank_reordered));
130 
131  preset_treeview->set_model(pstore);
132  preset_treeview->set_name("PresetView");
133  preset_treeview->signal_drag_motion().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_drag_motion), false);
134  preset_treeview->signal_drag_data_get().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_drag_data_get));
135  preset_treeview->signal_row_activated().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_row_activated));
136  preset_treeview->signal_button_press_event().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_button_press));
137  preset_treeview->signal_button_release_event().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_button_release), true);
138  preset_row_del_conn = pstore->signal_row_deleted().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_reordered));
139  preset_treeview->get_selection()->set_mode(Gtk::SELECTION_BROWSE);
140  preset_treeview->get_selection()->set_select_function(
141  sigc::mem_fun(*this, &PresetWindow::select_func));
142  preset_treeview->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_changed));
143  preset_treeview->signal_cursor_changed().connect(sigc::mem_fun(*this, &PresetWindow::on_cursor_changed));
144  preset_cellrenderer->signal_edited().connect(sigc::mem_fun(*this, &PresetWindow::on_preset_edited));
145  preset_cellrenderer->signal_editing_canceled().connect(
146  sigc::bind(sigc::mem_fun(*this, &PresetWindow::on_edit_canceled), preset_treeview->get_column(0)));
147  preset_cellrenderer->signal_editing_started().connect(
148  sigc::bind(
149  sigc::mem_fun(*this, &PresetWindow::on_editing_started),
150  Glib::RefPtr<Gtk::TreeModel>::cast_static(pstore)));
151  preset_treeview->get_column(0)->set_cell_data_func(
152  *preset_cellrenderer, sigc::mem_fun(*this, &PresetWindow::text_func));
153  banks_combobox->signal_changed().connect(
154  sigc::mem_fun(*this, &PresetWindow::on_preset_combo_changed));
155  presets_target_treeview->get_selection()->set_mode(Gtk::SELECTION_NONE);
156  std::vector<Gtk::TargetEntry> listTargets3;
157  listTargets3.push_back(
158  Gtk::TargetEntry("application/x-guitarix-preset", Gtk::TARGET_SAME_APP, 0));
159  presets_target_treeview->enable_model_drag_dest(listTargets3, Gdk::ACTION_COPY);
160  presets_target_treeview->signal_drag_motion().connect(
161  sigc::mem_fun(*this, &PresetWindow::on_target_drag_motion), false);
162  presets_target_treeview->signal_drag_data_received().connect_notify(
163  sigc::mem_fun(*this, &PresetWindow::target_drag_data_received));
164  machine.signal_selection_changed().connect(
165  sigc::mem_fun(*this, &PresetWindow::on_selection_changed));
166 
167  left_column = lc;
168  gtk_size_group_add_widget(lc, GTK_WIDGET(close_preset->gobj()));
169  gtk_size_group_add_widget(lc, GTK_WIDGET(save_preset->gobj()));
170  gtk_size_group_add_widget(lc, GTK_WIDGET(new_preset_bank->gobj()));
171  gtk_size_group_add_widget(lc, GTK_WIDGET(organize_presets->gobj()));
172  gtk_size_group_add_widget(lc, GTK_WIDGET(online_preset->gobj()));
173 }
174 
176 }
177 
178 void PresetWindow::on_selection_changed() {
179  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
180  if (it && it->get_value(bank_col.name) == machine.get_current_bank()) {
181  it = preset_treeview->get_selection()->get_selected();
182  if (it && it->get_value(pstore->col.name) == machine.get_current_name()) {
183  Glib::RefPtr<Gdk::Window> w = bank_treeview->get_window();
184  if (w) { // might not yet be initialized
185  gdk_window_invalidate_rect(w->gobj(), 0, true);
186  gdk_window_invalidate_rect(preset_treeview->get_window()->gobj(), 0, true);
187  }
188  return;
189  }
190  }
191  set_presets();
192  bool s = false;
193  if (machine.setting_is_preset()) {
195  if (pf && pf->is_mutable()) {
196  s = true;
197  }
198  }
199  actions.save_changes->set_sensitive(s);
200 }
201 
202 void PresetWindow::load_widget_pointers(Glib::RefPtr<gx_gui::GxBuilder> bld) {
203  bld->find_widget("close_preset", close_preset);
204  bld->find_widget("save_preset", save_preset);
205  bld->find_widget("new_preset_bank", new_preset_bank);
206  bld->find_widget("organize_presets", organize_presets);
207  bld->find_widget("online_preset", online_preset);
208  bld->find_widget_derived("bank_treeview", bank_treeview, sigc::ptr_fun(MyTreeView::create_from_builder));
209  bld->find_widget("bank_cellrenderer", bank_cellrenderer);
210  bld->find_widget_derived("preset_treeview", preset_treeview, sigc::ptr_fun(MyTreeView::create_from_builder));
211  bld->find_widget("preset_cellrenderer", preset_cellrenderer);
212  bld->find_widget("banks_combobox", banks_combobox);
213  bld->find_widget_derived("presets_target_treeview", presets_target_treeview, sigc::ptr_fun(MyTreeView::create_from_builder));
214  bld->find_widget("preset_title", preset_title);
215  bld->find_widget("presets_target_scrolledbox", presets_target_scrolledbox);
216  bld->find_widget("bank_column_edit", bank_column_edit);
217  bld->find_widget("bank_column_delete", bank_column_delete);
218  bld->find_widget("preset_column_edit", preset_column_edit);
219  bld->find_widget("preset_column_delete", preset_column_delete);
220  bld->find_widget("main_vpaned", main_vpaned);
221  bld->find_widget("preset_scrolledbox", preset_scrolledbox);
222 }
223 
224 static bool preset_button_press_idle(Gtk::Widget& w) {
225  w.grab_focus();
226  return false;
227 }
228 
229 bool PresetWindow::on_bank_query_tooltip(int x, int y, bool kb_tooltip, Glib::RefPtr<Gtk::Tooltip> tooltip) {
230  Gtk::TreeIter it;
231  if (!bank_treeview->get_tooltip_context_iter(x, y, kb_tooltip, it)) {
232  return false;
233  }
234  int tp = it->get_value(bank_col.tp);
236  return false;
237  }
238  Gtk::TreeModel::Path pt;
239  Gtk::TreeViewColumn *col;
240  int dx, dy;
241  if (!bank_treeview->get_path_at_pos(x, y, pt, col, dx, dy)) {
242  col = 0;
243  }
244  Glib::ustring nm = it->get_value(bank_col.name);
245  if (nm.empty()) {
246  return false;
247  }
248  gx_system::PresetFileGui *f = machine.get_bank_file(nm);
249  if (col == bank_treeview->get_column(0) || col == bank_treeview->get_column(1)) {
252  tooltip->set_text(_("damaged bank file; click to delete"));
253  } else if (f->get_flags() & gx_system::PRESET_FLAG_VERSIONDIFF) {
254  tooltip->set_text(
255  Glib::ustring::compose(_("wrong format version (is %1, need %2)\nclick to convert"),
256  f->get_header().version_string(),
258  } else if (f->get_flags() & gx_system::PRESET_FLAG_READONLY) {
259  tooltip->set_text(_("readonly bank, click to change to read-write"));
260  } else if (col == bank_treeview->get_column(0)){
261  tooltip->set_text(_("click to set to readonly"));
262  } else {
263  return false;
264  }
265  } else if (tp == gx_system::PresetFile::PRESET_FACTORY) {
266  tooltip->set_text(_("predefined factory preset bank"));
267  } else if (tp == gx_system::PresetFile::PRESET_SCRATCH) {
268  tooltip->set_text(_("scratch preset bank: changes will be persistent (without explicit saving)"));
269  } else {
270  return false;
271  }
272  } else if (col == bank_treeview->get_column(2)) {
273  if (f->get_flags()) {
274  return false;
275  }
276  tooltip->set_text(_("click to edit the bank name"));
277  } else if (col == bank_treeview->get_column(3)) {
278  if (f->get_flags()) {
279  return false;
280  }
281  tooltip->set_text(_("click to delete the bank"));
282  } else {
283  return false;
284  }
285  bank_treeview->set_tooltip_cell(tooltip, &pt, col, 0);
286  return true;
287 }
288 
289 void PresetWindow::on_preset_row_activated(const Gtk::TreePath& path, Gtk::TreeViewColumn* column) {
290  bool in_organize = actions.organize->get_active();
291  on_presets_close();
292  if (in_organize) {
293  preset_treeview->get_selection()->select(path);
294  }
295 }
296 
297 bool PresetWindow::on_preset_button_press(GdkEventButton *ev) {
298  if (ev->type == GDK_BUTTON_PRESS) {
299  Glib::signal_idle().connect(sigc::bind(sigc::ptr_fun(preset_button_press_idle), sigc::ref(*preset_treeview)));
300  }
301  return false;
302 }
303 
304 
305 void PresetWindow::on_preset_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context, Gtk::SelectionData& selection, int info, int timestamp) {
306  if (selection.get_target() == "application/x-guitarix-preset") {
307  Gtk::TreeModel::Path path;
308  Gtk::TreeViewColumn *focus_column;
309  preset_treeview->get_cursor(path, focus_column);
310  Glib::ustring data = pstore->get_iter(path)->get_value(pstore->col.name);
311  selection.set("application/x-guitarix-preset", data);
312  }
313 }
314 
315 void PresetWindow::on_bank_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context, Gtk::SelectionData& selection, int info, int timestamp) {
316  if (info != URILIST_TARGET && info != TEXT_TARGETS) {
317  return;
318  }
319  Gtk::TreeModel::Path path;
320  Gtk::TreeViewColumn *focus_column;
321  bank_treeview->get_cursor(path, focus_column);
322  Glib::RefPtr<Gio::File> f =
323  Gio::File::create_for_path(
324  machine.bank_get_filename(
325  bank_treeview->get_model()->get_iter(path)->get_value(bank_col.name)));
326  if (info == TEXT_TARGETS) {
327  selection.set_text(f->get_path());
328  } else {
329  std::vector<std::string> uris;
330  uris.push_back(f->get_uri());
331  selection.set_uris(uris);
332  }
333 }
334 
335 void PresetWindow::on_bank_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint timestamp) {
336  if (info != URILIST_TARGET) {
337  return;
338  }
339 #if false //gtk 2.22
340  bool is_move = context->get_selected_action() == Gdk::ACTION_MOVE;
341 #else
342  bool is_move = context->gobj()->action == GDK_ACTION_MOVE;
343 #endif
344  bool success = false;
345  std::vector<Glib::ustring> uris = data.get_uris();
346  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(bank_treeview->get_model());
347  for (std::vector<Glib::ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
348  gx_system::PresetFileGui *f = machine.bank_insert_uri(*i, is_move);
349  if (f) {
350  Gtk::TreeIter i = ls->prepend();
351  set_row_for_presetfile(i,f);
352  bank_treeview->set_cursor(ls->get_path(i));
353  bank_treeview->get_selection()->select(i);
354  success = true;
355  }
356  }
357  context->drag_finish(success, false, timestamp);
358 }
359 
360 Glib::ustring PresetWindow::get_combo_selection() {
361  Gtk::TreeIter idx = banks_combobox->get_active();
362  if (!idx) {
363  return "";
364  }
365  return idx->get_value(target_col.name);
366 }
367 
368 /*
369  ** dnd target
370  */
371 
372 void PresetWindow::target_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint timestamp) {
373  Glib::ustring bank = get_combo_selection();
374  if (bank.empty()) {
375  presets_target_treeview->signal_drag_data_received().emission_stop();
376  return;
377  }
378  gx_system::PresetFileGui& fl = *machine.get_bank_file(bank);
379  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(presets_target_treeview->get_model());
380  Glib::ustring srcnm = data.get_data_as_string();
381  Glib::ustring dstnm = srcnm;
382  int n = 1;
383  while (fl.has_entry(dstnm)) {
384  dstnm = srcnm + "-" + gx_system::to_string(n);
385  n += 1;
386  }
387  Glib::ustring src_bank = get_current_bank();
388  gx_system::PresetFileGui& pf = *machine.bank_get_file(src_bank);
389  if (src_bank == bank) {
390  gx_print_error("preset", "can't copy inside the same bank");
391  return;
392  }
393  Gtk::TreeModel::Path pt;
394  Gtk::TreeViewDropPosition dst;
395  if (!presets_target_treeview->get_dest_row_at_pos(x, y, pt, dst)) {
396  ls->append()->set_value(target_col.name, dstnm);
397  machine.pf_append(pf, srcnm, fl, dstnm);
398  } else {
399  Gtk::TreeIter it = ls->get_iter(pt);
400  if (dst == Gtk::TREE_VIEW_DROP_BEFORE || dst == Gtk::TREE_VIEW_DROP_INTO_OR_BEFORE) {
401  ls->insert(it)->set_value(target_col.name, dstnm);
402  machine.pf_insert_before(pf, srcnm, fl, it->get_value(target_col.name), dstnm);
403  } else { // gtk.TREE_VIEW_DROP_INTO_OR_AFTER, gtk.TREE_VIEW_DROP_AFTER
404  ls->insert_after(it)->set_value(target_col.name, dstnm);
405  machine.pf_insert_after(pf, srcnm, fl, it->get_value(target_col.name), dstnm);
406  }
407  }
408  if (context->get_action() == Gdk::ACTION_MOVE) {
409  Gtk::TreeModel::Path pt;
410  Gtk::TreeViewColumn *col;
411  preset_treeview->get_cursor(pt, col);
412  preset_row_del_conn.block();
413  pstore->erase(pstore->get_iter(pt));
414  preset_row_del_conn.unblock();
415  machine.erase_preset(pf, srcnm);
416  }
417  if (src_bank == bank) {
418  on_bank_changed();
419  }
420 }
421 
422 bool PresetWindow::on_target_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint timestamp) {
423  Gtk::Widget *source_widget = Gtk::Widget::drag_get_source_widget(context);
424  if (source_widget != preset_treeview || get_combo_selection().empty()) {
425  context->drag_status((Gdk::DragAction)0, timestamp);
426  return true;
427  }
428  presets_target_treeview->on_drag_motion(context, x, y, timestamp);
429  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
430  int tp = it->get_value(bank_col.tp);
431  Glib::ustring nm = it->get_value(bank_col.name);
433  get_combo_selection() == nm) {
434  context->drag_status(Gdk::ACTION_COPY, timestamp);
435  }
436  return true;
437 }
438 
439 void PresetWindow::reload_combo() {
440  Glib::ustring old = get_combo_selection();
441  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(banks_combobox->get_model());
442  ls->clear();
443  int n = 0;
444  int nn = -1;
445  for (gx_engine::bank_iterator i = machine.bank_begin(); i != machine.bank_end(); ++i) {
446  int tp = i->get_type();
448  continue;
449  }
450  if (i->get_flags()) {
451  continue;
452  }
453  Glib::ustring s = i->get_name();
454  ls->append()->set_value(bank_col.name, s);
455  if (s == old) {
456  nn = n;
457  }
458  n += 1;
459  }
460  banks_combobox->set_active(nn);
461 }
462 
463 void PresetWindow::on_preset_combo_changed() {
464  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(presets_target_treeview->get_model());
465  ls->clear();
466  Glib::ustring nm = get_combo_selection();
467  if (nm.empty()) {
468  return;
469  }
470  gx_system::PresetFileGui& f = *machine.get_bank_file(nm);
471  for (gx_system::PresetFile::iterator i = f.begin(); i != f.end(); ++i) {
472  ls->append()->set_value(bank_col.name, i->name);
473  }
474 }
475 
476 void PresetWindow::reload_target() {
477  on_preset_combo_changed();
478 }
479 
480 /*
481  ** name edit
482  */
483 
484 bool PresetWindow::select_func(const Glib::RefPtr<Gtk::TreeModel>& model, const Gtk::TreePath& path, bool path_currently_selected) {
485  Glib::ustring s = model->get_iter(path)->get_value(bank_col.name);
486  if (s.empty()) {
487  return false;
488  }
489  return true;
490 }
491 
492 void PresetWindow::on_editing_started(const Gtk::CellEditable* edit, const Glib::ustring& path, Glib::RefPtr<Gtk::TreeModel>& model) {
493  Glib::ustring s = model->get_iter(path)->get_value(bank_col.name);
494  if (s.empty()) {
495  dynamic_cast<Gtk::Entry*>(const_cast<Gtk::CellEditable*>(edit))->set_text("");
496  } else {
497  dynamic_cast<Gtk::Entry*>(const_cast<Gtk::CellEditable*>(edit))->set_text(s);
498  }
499  dynamic_cast<Gtk::Window*>(main_vpaned->get_toplevel())->remove_accel_group(actions.accels);
500 }
501 
502 bool PresetWindow::edit_cell(const Gtk::TreeModel::Path pt, Gtk::TreeViewColumn& col, Gtk::CellRenderer& cell) {
503  dynamic_cast<Gtk::CellRendererText*>(&cell)->property_editable().set_value(true);
504  col.get_tree_view()->set_cursor(pt, col, true);
505  return false;
506 }
507 
508 void PresetWindow::reset_edit(Gtk::TreeViewColumn& col) {
509  if (edit_iter) {
510  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(col.get_tree_view()->get_model());
511  ls->erase(edit_iter);
512  edit_iter = ls->children().end();
513  }
514  Gtk::CellRendererText& cell = *dynamic_cast<Gtk::CellRendererText*>(col.get_first_cell_renderer());
515  cell.property_editable().set_value(false);
516  col.set_min_width(0);
517  col.queue_resize();
518  in_edit = false;
519  dynamic_cast<Gtk::Window*>(main_vpaned->get_toplevel())->add_accel_group(actions.accels);
520 }
521 
522 void PresetWindow::on_edit_canceled(Gtk::TreeViewColumn *col) {
523  reset_edit(*col);
524 }
525 
526 void PresetWindow::start_edit(const Gtk::TreeModel::Path& pt, Gtk::TreeViewColumn& col, Gtk::CellRenderer& cell) {
527  col.set_min_width(100);
528  Glib::signal_idle().connect(
529  sigc::bind(sigc::mem_fun(*this, &PresetWindow::edit_cell),
530  pt, sigc::ref(col), sigc::ref(cell)));
531 }
532 
533 /*
534  ** list of banks
535  */
536 
537 void PresetWindow::highlight_current_bank(Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator& iter) {
538  Glib::ustring t = iter->get_value(bank_col.name);
539  if (t.empty()) {
540  return;
541  }
542  Gtk::CellRendererText *tc = dynamic_cast<Gtk::CellRendererText*>(cell);
543  if (machine.setting_is_preset() && t == machine.get_current_bank()) {
544  tc->property_foreground().set_value("#f00");
545  } else{
546  tc->property_foreground_set().set_value(false);
547  }
548  int n = machine.bank_size() - *bank_treeview->get_model()->get_path(iter).begin();
550  n -= 1;
551  }
552  if (n > 26) {
553  t = " " + t;
554  } else {
555  t = Glib::ustring::compose("%1: %2", char('A'+n), t);
556  }
557  cell->set_property("text", t);
558 }
559 
560 Glib::ustring PresetWindow::get_current_bank() {
561  Gtk::TreeIter it = get_current_bank_iter();
562  if (!it) {
563  return "";
564  }
565  return it->get_value(bank_col.name);
566 }
567 
568 bool PresetWindow::run_message_dialog(Gtk::Widget& w, const Glib::ustring& msg) {
569  Gtk::MessageDialog d(*dynamic_cast<Gtk::Window*>(w.get_toplevel()), msg, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true);
570  d.set_position(Gtk::WIN_POS_MOUSE);
571  return d.run() == Gtk::RESPONSE_OK;
572 }
573 
574 bool PresetWindow::on_bank_button_release(GdkEventButton *ev) {
575  // edit bank name / delete bank
576  Gtk::TreeModel::Path pt;
577  Gtk::TreeViewColumn *col;
578  int dx, dy;
579  if (!bank_treeview->get_path_at_pos(ev->x, ev->y, pt, col, dx, dy)) {
580  return false;
581  }
582  Gtk::TreeModel::Path path;
583  Gtk::TreeViewColumn *focus_column;
584  bank_treeview->get_cursor(path, focus_column);
585  if (col != focus_column || pt != path) {
586  return false;
587  }
588  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(bank_treeview->get_model());
589  Gtk::TreeModel::iterator it = ls->get_iter(pt);
590  Glib::ustring nm = it->get_value(bank_col.name);
591  int tp = it->get_value(bank_col.tp);
592  if (col == bank_treeview->get_column(0)) {
594  int flags = machine.get_bank_file(nm)->get_flags();
595  gx_system::PresetFileGui *f = machine.get_bank_file(nm);
596  if (flags == 0 && tp == gx_system::PresetFile::PRESET_FILE) {
597  /*if (run_message_dialog(*bank_treeview, "set bank " + nm + " to readonly?")) {*/
599  reload_banks(nm);
600  } else if (flags == gx_system::PRESET_FLAG_VERSIONDIFF) {
601  if (run_message_dialog(*bank_treeview, "convert bank " + nm + " to new version?")) {
602  if (machine.convert_preset(*f)) {
603  reload_banks(nm);
604  }
605  }
606  } else if (flags == gx_system::PRESET_FLAG_READONLY) {
607  /*if (run_message_dialog(*bank_treeview, "set bank " + nm + " to read/write?")) {*/
609  reload_banks(nm);
611  if (run_message_dialog(*bank_treeview, "convert readonly bank " + nm + " to new version?")) {
612  if (machine.convert_preset(*f)) {
613  reload_banks(nm);
614  }
615  }
616  } else if (flags & gx_system::PRESET_FLAG_INVALID) {
617  if (run_message_dialog(
618  *bank_treeview, "delete damaged bank " + nm + "?"
619  " Export it before deleting and ask in the"
620  " guitarix online forum if you want to try to repair it!")) {
621  machine.bank_remove(nm);
622  reload_banks(nm);
623  }
624  }
625  }
626  return false;
627  }
628  if (tp != gx_system::PresetFile::PRESET_FILE || machine.get_bank_file(nm)->get_flags()) {
629  return false;
630  }
631  if (col == bank_treeview->get_column(2)) {
632  col = bank_treeview->get_column(1);
633  start_edit(pt, *col, *col->get_first_cell_renderer());
634  } else if (col == bank_treeview->get_column(3)) {
635  Gtk::MessageDialog d(*dynamic_cast<Gtk::Window*>(bank_treeview->get_toplevel()), "delete bank " + nm + "?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true);
636  d.set_position(Gtk::WIN_POS_MOUSE);
637  if (d.run() == Gtk::RESPONSE_OK) {
638  bank_row_del_conn.block();
639  ls->erase(it);
640  bank_row_del_conn.unblock();
641  machine.bank_remove(nm);
642  reload_combo();
643  if (!machine.setting_is_preset()) { // if current bank was removed
644  actions.save_changes->set_sensitive(false);
645  }
646  }
647  }
648  return false;
649 }
650 
651 void PresetWindow::on_bank_edited(const Glib::ustring& path, const Glib::ustring& newtext, Gtk::TreeView* w) {
652  Gtk::TreeIter sel = w->get_model()->get_iter(path);
653  Glib::ustring oldname = sel->get_value(bank_col.name);
654  Glib::ustring newname = newtext;
655  gx_system::strip(newname);
656  if (newname.empty() || newname == oldname) {
657  Gtk::TreeViewColumn *p = w->get_column(1);
658  reset_edit(*p);
659  return;
660  }
661  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(banks_combobox->get_model());
662  if (edit_iter) {
663  gx_system::PresetFileGui *f = machine.bank_insert_new(newname);
664  if (f) {
665  ls->prepend()->set_value(target_col.name, f->get_name());
666  edit_iter = ls->children().end();
667  set_row_for_presetfile(sel, f);
668  w->get_selection()->select(sel);
669  }
670  } else {
671  machine.rename_bank(oldname, newname);
672  Gtk::TreeNodeChildren ch = ls->children();
673  for (Gtk::TreeIter it = ch.begin(); it != ch.end(); ++it) {
674  if (it->get_value(bank_col.name) == oldname) {
675  it->set_value(bank_col.name, newname);
676  }
677  }
678  w->get_model()->get_iter(path)->set_value(bank_col.name, newname);
679  }
680  Gtk::TreeViewColumn *p = w->get_column(1);
681  reset_edit(*p);
682 }
683 
684 bool PresetWindow::is_row_separator(const Glib::RefPtr<Gtk::TreeModel>& model, const Gtk::TreeModel::iterator& iter) {
685  return iter->get_value(bank_col.tp) == gx_system::PresetFile::PRESET_SEP;
686 }
687 
688 void PresetWindow::on_new_bank() {
689  Glib::RefPtr<Gtk::ListStore> m = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(bank_treeview->get_model());
690  edit_iter = m->prepend();
691  edit_iter->set_value(bank_col.tp, static_cast<int>(gx_system::PresetFile::PRESET_FILE));
692  in_edit = true;
693  start_edit(m->get_path(edit_iter), *bank_treeview->get_column(1), *bank_cellrenderer);
694 }
695 
696 // Online Preset Downloader
697 
699  cancellable = Gio::Cancellable::create ();
700  file_state = sigc::ptr_fun(&f_progress);
701  watcher = Glib::Thread::create(sigc::mem_fun(*this, &DownloadWatch::watch), true);
702 }
703 
705  while(true) {
706  {
707  Glib::Mutex::Lock lock (w_mutex);
708  if (stop) break;
709  }
710  sleep(15); // time out for the server response
711  cancellable->cancel();
712  if (!stop) timeout();
713  break;
714  }
715 }
716 
717 void DownloadWatch::f_progress(goffset read, goffset total)
718 {
719  if(Gtk::Main::events_pending())
720  Gtk::Main::iteration(false);
721  //std::cout << read << "/" << total << std::endl;
722 }
723 
725  {
726  Glib::Mutex::Lock lock (w_mutex);
727  stop = true;
728  }
729  if (watcher) watcher->join();
730 }
731 
732 void PresetWindow::go_watch () {
733  if(watch != NULL) return;
734  watch = new DownloadWatch();
735  watch->timeout.connect(sigc::mem_fun(*this, &PresetWindow::watch_done));
736  watch->start();
737 }
738 
739 void PresetWindow::watch_done() {
740  if(watch == NULL) return;
741  delete watch;
742  watch = NULL;
743 }
744 
745 Glib::ustring PresetWindow::resolve_hostname() {
746  static Glib::ustring hostname = "localhost";
747  if (! machine.get_jack()) {
748  hostname = Gio::Resolver::get_default()->lookup_by_address
749  (Gio::InetAddress::create( machine.get_options().get_rpcaddress()));
750  }
751  return hostname;
752 }
753 
754 bool PresetWindow::download_file(Glib::ustring from_uri, Glib::ustring to_path) {
755 
756  go_watch();
757  Glib::RefPtr<Gio::File> rem = Gio::File::create_for_uri(from_uri);
758  Glib::RefPtr<Gio::File> dest = Gio::File::create_for_uri(Glib::filename_to_uri(to_path, resolve_hostname()));
759  try {
760  rem->copy(dest, watch->file_state, watch->cancellable,Gio::FILE_COPY_OVERWRITE);
761  } catch (Gio::Error& e) {
762  if (watch->cancellable->is_cancelled()) {
763  gx_print_error( _("Time out, download cancelled"), _("the server on https://musical-artifacts.com/ takes to long to respond"));
764  } else {
765  gx_print_error(e.what().c_str(), Glib::ustring::compose("can't download '%1 ' from https://musical-artifacts.com/", from_uri));
766  if (watch) watch_done();
767  }
768  return false;
769  }
770  if (watch) watch_done();
771  return true;
772 }
773 
774 void PresetWindow::downloadPreset(Gtk::Menu *presetMenu,std::string uri) {
775 
776  std::string::size_type n = uri.find_last_of('/');
777  if (n != std::string::npos) {
778  std::string fn = uri.substr(n);
779  std::string ff = "/tmp"+fn;
780 
781  if (download_file(uri, ff)) {
782  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(bank_treeview->get_model());
783  gx_system::PresetFileGui *f = machine.bank_insert_uri(Glib::filename_to_uri(ff, resolve_hostname()), false);
784  if (f) {
785  Gtk::TreeIter i = ls->prepend();
786  set_row_for_presetfile(i,f);
787  bank_treeview->set_cursor(ls->get_path(i));
788  bank_treeview->get_selection()->select(i);
789  }
790  }
791  } else {
792  gx_print_error("downloadPreset", _("can't download preset from https://musical-artifacts.com/"));
793  }
794 }
795 
796 void PresetWindow::read_preset_menu() {
797 
798  Glib::RefPtr<Gio::File> dest = Gio::File::create_for_uri(Glib::filename_to_uri(options.get_online_presets_filename(), resolve_hostname()));
799  Glib::RefPtr<Gio::DataInputStream> in = Gio::DataInputStream::create(dest->read());
800 
801  std::string NAME_;
802  std::string FILE_;
803  std::string INFO_;
804  std::string line;
805  bool set_name = false;
806  bool set_file = false;
807  bool set_info = false;
808  while ( in->read_line(line) )
809  {
810  if (line.find("<<NAME>>") != string::npos) {
811  set_name = true;
812  set_file = false;
813  set_info = false;
814  NAME_ = "";
815  continue;
816  } else if (line.find("<<FILE>>") != string::npos ) {
817  set_name = false;
818  set_file = true;
819  set_info = false;
820  FILE_ = "";
821  continue;
822  } else if (line.find("<<INFO>>") != string::npos ) {
823  set_name = false;
824  set_file = false;
825  set_info = true;
826  INFO_ = "";
827  continue;
828  } else if (line.find("<<END>>") != string::npos ) {
829  set_name = false;
830  set_file = false;
831  set_info = false;
832  olp.push_back(std::tuple<std::string,std::string,std::string>(NAME_,FILE_,INFO_));
833  NAME_ = "";
834  FILE_ = "";
835  INFO_ = "";
836  continue;
837  }
838  if ( set_name ) NAME_ += line;
839  else if ( set_file ) FILE_ += line;
840  else if ( set_info ) INFO_ += line+"\n";
841  }
842  in->close ();
843 }
844 
845 void PresetWindow::popup_pos( int& x, int& y, bool& push_in ){
846  online_preset->get_window()->get_origin( x, y );
847  x +=150;
848  y -= 450;
849  push_in = false;
850 }
851 
852 void PresetWindow::create_preset_menu(bool is_new) {
853 
854  static bool read_new = true;
855  if (read_new || is_new) {
856  read_preset_menu();
857  read_new = false;
858  }
859 
860  Gtk::MenuItem* item;
861  Gtk::Menu *presetMenu = Gtk::manage(new Gtk::Menu());
862  presetMenu->set_size_request (-1, 600);
863  for(std::vector<std::tuple<std::string,std::string,std::string> >::iterator it = olp.begin(); it != olp.end(); it++) {
864  item = Gtk::manage(new Gtk::MenuItem(get<0>(*it), true));
865  item->set_tooltip_text(get<2>(*it));
866  std::string f = get<1>(*it);
867  item->signal_activate().connect(sigc::bind(sigc::bind(sigc::mem_fun(
868  *this, &PresetWindow::downloadPreset),f),presetMenu));
869  presetMenu->append(*item);
870 
871  }
872  presetMenu->show_all();
873  presetMenu->popup(Gtk::Menu::SlotPositionCalc(sigc::mem_fun(
874  *this, &PresetWindow::popup_pos ) ),0,gtk_get_current_event_time());
875 }
876 
877 void PresetWindow::replace_inline(std::string& subject, const std::string& search,
878  const std::string& replace) {
879  size_t pos = 0;
880  while ((pos = subject.find(search, pos)) != std::string::npos) {
881  subject.replace(pos, search.length(), replace);
882  pos += replace.length();
883  }
884 }
885 
886 void PresetWindow::show_online_preset() {
887 
888  Glib::RefPtr<Gio::File> dest = Gio::File::create_for_uri(Glib::filename_to_uri(options.get_online_presets_filename(), resolve_hostname()));
889  static bool load_new = true;
890  static bool load = false;
891  Glib::RefPtr<Gdk::Window> window = preset_scrolledbox->get_toplevel()->get_window();
892  if (load_new || ! dest->query_exists()) {
893  Gdk::Cursor cursor(Gdk::WATCH);
894  window->set_cursor(cursor);
895  if (dest->query_exists()) {
896  Gtk::MessageDialog *d = new Gtk::MessageDialog(*dynamic_cast<Gtk::Window*>(online_preset->get_toplevel()),
897  "Do you wont to check for new presets from\n https://musical-artifacts.com ? \n Note, that may take a while",
898  false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true);
899  d->set_position(Gtk::WIN_POS_MOUSE);
900  if (d->run() == Gtk::RESPONSE_YES) load = true;
901  delete d;
902  }
903  if (load || ! dest->query_exists()) {
904  if (download_file("https://musical-artifacts.com/artifacts.json?apps=guitarix", options.get_online_config_filename())) {
905  machine.load_online_presets();
906  } else {
907  window->set_cursor();
908  return;
909  }
910  }
911  window->set_cursor();
912  }
913  load_new = false;
914  create_preset_menu(load_new);
915 }
916 
917 void PresetWindow::on_online_preset() {
918  Glib::signal_idle().connect_once(sigc::mem_fun(*this, &PresetWindow::show_online_preset));
919 }
920 
921 bool PresetWindow::on_bank_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint timestamp) {
922  Gtk::Widget *source_widget = Gtk::Widget::drag_get_source_widget(context);
923  if (!source_widget) {
924  // URI from other application
925  Gdk::DragAction a;
926  if (context->get_suggested_action() == Gdk::ACTION_MOVE) {
927  a = context->get_suggested_action();
928  } else {
929  a = Gdk::ACTION_COPY;
930  }
931  context->drag_status(a, timestamp);
932  } else if (source_widget != bank_treeview) {
933  // other window
934  context->drag_status((Gdk::DragAction)0, timestamp);
935  } else {
936  // reorder
937  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
938  if (!it) {
939  return true;
940  }
941  int tp = it->get_value(bank_col.tp);
943  context->drag_status((Gdk::DragAction)0, timestamp);
944  return true;
945  }
946  Gtk::TreeModel::Path pt;
947  Gtk::TreeViewDropPosition dst;
948  if (bank_treeview->get_dest_row_at_pos(x, y, pt, dst)) {
949  tp = bank_treeview->get_model()->get_iter(pt)->get_value(bank_col.tp);
951  context->drag_status((Gdk::DragAction)0, timestamp);
952  return true;
953  }
954  }
955  bank_treeview->on_drag_motion(context, x, y, timestamp);
956  context->drag_status(Gdk::ACTION_MOVE, timestamp);
957  }
958  return true;
959 }
960 
961 void PresetWindow::on_bank_changed() {
962  preset_row_del_conn.block();
963  if (in_edit) {
964  pstore->prepend();
965  }
966  pstore->clear();
967  preset_row_del_conn.unblock();
968  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
969  if (!it) {
970  in_current_preset = false;
971  return;
972  }
973  Glib::ustring nm = it->get_value(bank_col.name);
974  preset_title->set_text(nm);
975  in_current_preset = (nm == machine.get_current_bank());
976  Gtk::TreeIter i;
977  gx_system::PresetFileGui& ll = *machine.get_bank_file(nm);
979  ((ll.get_flags() & gx_system::PRESET_FLAG_READONLY) && !actions.organize->get_active())) {
980  preset_treeview->unset_rows_drag_source();
981  } else {
982  preset_treeview->unset_rows_drag_source(); //FIXME: needed?
983  preset_treeview->set_reorderable(true);
984  std::vector<Gtk::TargetEntry> listTargets2;
985  listTargets2.push_back(Gtk::TargetEntry("GTK_TREE_MODEL_ROW", Gtk::TARGET_SAME_WIDGET, 0));
986  listTargets2.push_back(Gtk::TargetEntry("application/x-guitarix-preset", Gtk::TARGET_SAME_APP, 1));
987  preset_treeview->enable_model_drag_source(listTargets2, Gdk::BUTTON1_MASK, Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
988  }
989  bool modifiable = ll.is_mutable();
990  for (gx_system::PresetFile::iterator s = ll.begin(); s != ll.end(); ++s) {
991  i = pstore->append();
992  i->set_value(pstore->col.name, s->name);
993  if (modifiable) {
994  i->set_value(pstore->col.edit_pb, pb_edit);
995  i->set_value(pstore->col.del_pb, pb_del);
996  }
997  if (in_current_preset && s->name == machine.get_current_name()) {
998  if (preset_treeview->get_mapped()) {
999  preset_treeview->scroll_to_row(pstore->get_path(i));
1000  }
1001  }
1002  }
1003  if (modifiable) {
1004  i = pstore->append();
1005  }
1006 }
1007 
1008 void PresetWindow::set_row_for_presetfile(Gtk::TreeIter i, gx_system::PresetFileGui *f) {
1009  i->set_value(bank_col.name, f->get_name());
1011  i->set_value(bank_col.type_pb, pb_del);
1012  } else if (f->get_flags() & gx_system::PRESET_FLAG_VERSIONDIFF) {
1013  i->set_value(bank_col.type_pb, pb_versiondiff);
1014  } else if (f->get_flags() & gx_system::PRESET_FLAG_READONLY) {
1015  i->set_value(bank_col.type_pb, pb_readonly);
1016  }
1017  int tp = f->get_type();
1018  i->set_value(bank_col.tp, tp);
1020  i->set_value(bank_col.type_pb, pb_scratch);
1021  } else if (tp == gx_system::PresetFile::PRESET_FACTORY) {
1022  i->set_value(bank_col.type_pb, pb_factory);
1023  } else if (f->is_mutable() || f->get_flags() & gx_system::PRESET_FLAG_VERSIONDIFF) {
1024  i->set_value(bank_col.edit_pb, pb_edit);
1025  i->set_value(bank_col.del_pb, pb_del);
1026  }
1027 }
1028 
1029 void PresetWindow::reload_banks(const Glib::ustring& sel_bank) {
1030  preset_row_del_conn.block();
1031  pstore->clear();
1032  preset_row_del_conn.unblock();
1033  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(bank_treeview->get_model());
1034  bank_row_del_conn.block();
1035  ls->clear();
1036  bank_row_del_conn.unblock();
1037  Gtk::TreeIter i;
1038  int in_factory = false;
1039  for (gx_engine::bank_iterator v = machine.bank_begin(); v != machine.bank_end(); ++v) {
1040  if (!in_factory && v->get_type() == gx_system::PresetFile::PRESET_FACTORY) {
1041  i = ls->append();
1042  i->set_value(bank_col.tp, static_cast<int>(gx_system::PresetFile::PRESET_SEP));
1043  in_factory = true;
1044  }
1045  i = ls->append();
1046  set_row_for_presetfile(i, *v);
1047  if (v->get_name() == sel_bank) {
1048  bank_treeview->get_selection()->select(i);
1049  }
1050  }
1051  reload_combo();
1052 }
1053 
1054 void PresetWindow::set_presets() {
1055  reload_banks(machine.get_current_bank());
1056 }
1057 
1058 void PresetWindow::on_bank_reordered(const Gtk::TreeModel::Path& path) {
1059  Glib::RefPtr<Gtk::TreeModel> ls = bank_treeview->get_model();
1060  Gtk::TreeModel::Children ch = ls->children();
1061  std::vector<Glib::ustring> l;
1062  for (Gtk::TreeIter i = ch.begin(); i != ch.end(); ++i) {
1063  int tp = i->get_value(bank_col.tp);
1064  Glib::ustring nm = i->get_value(bank_col.name);
1065  if (!nm.empty() && (tp == gx_system::PresetFile::PRESET_SCRATCH || tp == gx_system::PresetFile::PRESET_FILE)) {
1066  l.push_back(nm);
1067  }
1068  }
1069  machine.bank_reorder(l);
1070 }
1071 
1072 /*
1073  ** list of presets
1074  */
1075 
1076 void PresetWindow::text_func(Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator& iter) {
1077  Glib::ustring val = iter->get_value(pstore->col.name);
1078  Glib::ustring t = val;
1079 #if false // gtk 2.24
1080  if (t.empty() && !cell->property_editing().get_value()) {
1081  } //to make indent happy (remove)
1082 #else
1083  gboolean editing;
1084  g_object_get(cell->gobj(), "editing", &editing, NULL);
1085  if (t.empty() && !editing) {
1086 #endif
1087  t = "<new>";
1088  } else if (in_current_preset) {
1089  int n = *pstore->get_path(iter).begin();
1090  if (n > 9) {
1091  t = " " + t;
1092  } else {
1093  t = Glib::ustring::compose("%1: %2", n, t);
1094  }
1095  }
1096  cell->set_property("text", t);
1097  Gtk::CellRendererText *tc = dynamic_cast<Gtk::CellRendererText*>(cell);
1098  if (in_current_preset && machine.setting_is_preset() && val == machine.get_current_name()) {
1099  tc->property_foreground().set_value("#f00");
1100  } else{
1101  tc->property_foreground_set().set_value(false);
1102  }
1103 }
1104 
1105 bool PresetWindow::on_preset_button_release(GdkEventButton *ev) {
1106  Gtk::TreeModel::Path pt;
1107  Gtk::TreeViewColumn *col;
1108  int dx, dy;
1109  if (!preset_treeview->get_path_at_pos(ev->x, ev->y, pt, col, dx, dy)) {
1110  return false;
1111  }
1112  Gtk::TreeModel::Path path;
1113  Gtk::TreeViewColumn *focus_column;
1114  preset_treeview->get_cursor(path, focus_column);
1115  if (col != focus_column || !path || pt != path) {
1116  return false;
1117  }
1118  Gtk::TreeIter bank_iter = get_current_bank_iter();
1119  int tp = bank_iter->get_value(bank_col.tp);
1120  if ((tp != gx_system::PresetFile::PRESET_SCRATCH && tp != gx_system::PresetFile::PRESET_FILE) || machine.get_bank_file(bank_iter->get_value(bank_col.name))->get_flags()) {
1121  return false;
1122  }
1123  if (col == preset_treeview->get_column(1)) {
1124  col = preset_treeview->get_column(0);
1125  start_edit(pt, *col, *col->get_first_cell_renderer());
1126  } else if (col == preset_treeview->get_column(2)) {
1127  Glib::ustring nm = pstore->get_iter(pt)->get_value(pstore->col.name);
1128  Gtk::MessageDialog d(*dynamic_cast<Gtk::Window*>(preset_treeview->get_toplevel()), Glib::ustring::compose("delete preset %1?", nm), false,
1129  Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true);
1130  d.set_position(Gtk::WIN_POS_MOUSE);
1131  if (d.run() == Gtk::RESPONSE_OK) {
1132  preset_row_del_conn.block();
1133  pstore->erase(pstore->get_iter(pt));
1134  preset_row_del_conn.unblock();
1135  machine.erase_preset(*machine.get_bank_file(bank_iter->get_value(bank_col.name)), nm);
1136  reload_target();
1137  }
1138  }
1139  return false;
1140 }
1141 
1142 void PresetWindow::on_preset_edited(const Glib::ustring& path, const Glib::ustring& newtext) {
1143  Gtk::TreeIter it = pstore->get_iter(path);
1144  Glib::ustring oldname = it->get_value(pstore->col.name);
1145  Glib::ustring newname = newtext;
1146  gx_system::strip(newname);
1147  if (newname.empty() || newname == oldname) {
1148  reset_edit(*preset_treeview->get_column(0));
1149  return;
1150  }
1151  // vowel mutation lead to a Glib::markup error when building the menu action,
1152  // so check for it and avoid them
1153  if (newname.find_first_of("ÜüūûúùÖöøøōôóòǫ°Ääæǣāàáâąþćēêèéīîíìȳŷýỳźżłśñ&%$§!?*#|<>/~_") != Glib::ustring::npos) {
1154  gx_print_error("PresetWindow", "Vowel mutation isn't allowed in preset names, please choose a name without \n(ÜüūûúùÖöøøōôóòǫ°Ääæǣāàáâąþēêèéīîíìȳŷýỳñ&%$§!?*#|<>/~_)");
1155  reset_edit(*preset_treeview->get_column(0));
1156  return;
1157  }
1158  gx_system::PresetFileGui& fl = *machine.get_bank_file(get_current_bank());
1159  Glib::ustring t = newname;
1160  int n = 1;
1161  while (fl.has_entry(newname)) {
1162  newname = Glib::ustring::compose("%1-%2", t, n);
1163  n += 1;
1164  }
1165  it->set_value(pstore->col.name, newname);
1166  it->set_value(pstore->col.edit_pb, pb_edit);
1167  it->set_value(pstore->col.del_pb, pb_del);
1168  if (oldname.empty()) {
1169  // check if current preset is scratch and needs to be saved
1170  if (!machine.get_current_bank().empty()) {
1171  gx_system::PresetFileGui *cpf = machine.get_bank_file(machine.get_current_bank());
1172  if (cpf && cpf->has_entry(machine.get_current_name())) {
1173  if (cpf->get_type() == gx_system::PresetFile::PRESET_SCRATCH && cpf->is_mutable()) {
1174  machine.pf_save(*cpf, machine.get_current_name());
1175  }
1176  }
1177  }
1178  pstore->append();
1179  machine.pf_save(fl, newname);
1180  } else {
1181  machine.rename_preset(fl, oldname, newname);
1182  }
1183  reload_target();
1184  reset_edit(*preset_treeview->get_column(0));
1185 }
1186 
1187 void PresetWindow::on_cursor_changed() {
1188  if (in_edit) {
1189  return;
1190  }
1191  Gtk::TreeModel::Path path;
1192  Gtk::TreeViewColumn *focus_column;
1193  preset_treeview->get_cursor(path, focus_column);
1194  if (!pstore->get_iter(path)->get_value(pstore->col.name).empty()) {
1195  return;
1196  }
1197  in_edit = true;
1198  start_edit(path, *preset_treeview->get_column(0), *preset_cellrenderer);
1199 }
1200 
1201 bool PresetWindow::on_preset_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint timestamp) {
1202  if (Gtk::Widget::drag_get_source_widget(context) == preset_treeview) {
1203  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
1204  bool ro = it && (machine.get_bank_file(it->get_value(bank_col.name))->get_flags() & gx_system::PRESET_FLAG_READONLY);
1205  if (!ro) {
1206  preset_treeview->on_drag_motion(context, x, y, timestamp);
1207  Gtk::TreeModel::Path pt;
1208  Gtk::TreeViewDropPosition dst;
1209  if (preset_treeview->get_dest_row_at_pos(x, y, pt, dst)) {
1210  if (dst == Gtk::TREE_VIEW_DROP_BEFORE ||
1211  (dst == Gtk::TREE_VIEW_DROP_AFTER &&
1212  !pstore->get_iter(pt)->get_value(pstore->col.name).empty())) {
1213  context->drag_status(Gdk::ACTION_MOVE, timestamp);
1214  return true;
1215  }
1216  }
1217  }
1218  }
1219  context->drag_status((Gdk::DragAction)0, timestamp);
1220  return true;
1221 }
1222 
1223 void PresetWindow::on_preset_reordered(const Gtk::TreeModel::Path& path) {
1224  // save changed order to file
1225  Gtk::TreeModel::Children ch = pstore->children();
1226  std::vector<Glib::ustring> l;
1227  for (Gtk::TreeIter i = ch.begin(); i != ch.end(); ++i) {
1228  Glib::ustring s = i->get_value(pstore->col.name);
1229  if (!s.empty()) {
1230  l.push_back(s);
1231  }
1232  }
1233  machine.reorder_preset(*machine.get_bank_file(get_current_bank()), l);
1234  reload_target();
1235 }
1236 
1237 void PresetWindow::autosize() {
1238  if (bank_treeview->get_mapped()) {
1239  bank_treeview->columns_autosize();
1240  preset_treeview->columns_autosize();
1241  }
1242 }
1243 
1244 void PresetWindow::on_organize() {
1245  bool v = organize_presets->get_active();
1246  bank_column_edit->set_visible(v);
1247  bank_column_delete->set_visible(v);
1248  preset_column_edit->set_visible(v);
1249  preset_column_delete->set_visible(v);
1250  Glib::RefPtr<Gtk::TreeSelection> sel = preset_treeview->get_selection();
1251  if (v) {
1252  actions.presets->set_active(true);
1253  sel->set_mode(Gtk::SELECTION_NONE);
1254  banks_combobox->set_active(-1);
1255  banks_combobox->show();
1256  presets_target_scrolledbox->show();
1257  actions.save_changes->set_sensitive(false);
1258  } else {
1259  sel->set_mode(Gtk::SELECTION_BROWSE);
1260  banks_combobox->hide();
1261  presets_target_scrolledbox->hide();
1262  if (machine.setting_is_preset()) {
1263  if (machine.get_bank_file(machine.get_current_bank())->is_mutable()) {
1264  actions.save_changes->set_sensitive(true);
1265  }
1266  }
1267  }
1268  on_bank_changed(); // reload for DnD adjustment of readonly banks
1269  autosize();
1270 }
1271 
1272 void PresetWindow::on_presets_close() {
1273  actions.presets->set_active(false); // FIXME
1274 }
1275 
1276 /*
1277  ** preset window
1278  */
1279 
1280 bool PresetWindow::animate_preset_show() {
1281  vpaned_pos -= vpaned_step;
1282  if (vpaned_pos <= vpaned_target) {
1283  main_vpaned->set_position(vpaned_target);
1284  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", false);
1285  Gtk::TreeIter it = get_current_bank_iter();
1286  if (it) {
1287  bank_treeview->scroll_to_row(bank_treeview->get_model()->get_path(it));
1288  }
1289  return false;
1290  }
1291  main_vpaned->set_position(vpaned_pos);
1292  return true;
1293 }
1294 
1295 bool PresetWindow::animate_preset_hide() {
1296  vpaned_pos += vpaned_step;
1297  if (vpaned_pos >= vpaned_target) {
1298  preset_scrolledbox->hide();
1299  return false;
1300  }
1301  main_vpaned->set_position(vpaned_pos);
1302  return true;
1303 }
1304 
1305 void PresetWindow::on_preset_changed() {
1306  if (actions.organize->get_active()) {
1307  return;
1308  }
1309  Glib::ustring bank;
1310  Glib::ustring name;
1311  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
1312  if (!it) {
1313  return;
1314  }
1315  bank = it->get_value(bank_col.name);
1316  it = preset_treeview->get_selection()->get_selected();
1317  if (!it) {
1318  return;
1319  }
1320  name = it->get_value(pstore->col.name);
1321  bool is_scratch = false;
1322  gx_system::PresetFileGui *cpf = 0;
1323  if (!machine.get_current_bank().empty()) {
1324  cpf = machine.get_bank_file(machine.get_current_bank());
1325  if (cpf && cpf->has_entry(machine.get_current_name())) {
1326  is_scratch = (cpf->get_type() == gx_system::PresetFile::PRESET_SCRATCH);
1327  }
1328  }
1329  if (is_scratch) {
1330  if (bank == machine.get_current_bank() && name == machine.get_current_name()) {
1331  machine.pf_save(*cpf, machine.get_current_name());
1332  // no reload necessary
1333  return;
1334  }
1335  }
1336  in_current_preset = true;
1337  cpf = machine.get_bank_file(bank);
1338  machine.load_preset(cpf, name);
1339  actions.save_changes->set_sensitive(cpf && cpf->is_mutable());
1340 }
1341 
1342 void PresetWindow::on_preset_save() {
1343  if (!machine.setting_is_preset()) {
1344  return;
1345  }
1346  gx_system::PresetFileGui *pf = machine.get_bank_file(machine.get_current_bank());
1347  if (!pf->is_mutable()) {
1348  return;
1349  }
1350  machine.pf_save(*pf, machine.get_current_name());
1351 }
1352 
1353 void PresetWindow::display_paned(bool show_preset, int paned_child_height) {
1354  if (preset_scrolledbox->get_parent() == main_vpaned) {
1355  vpaned_pos = main_vpaned->get_allocation().get_height();
1356  int w, h;
1357  main_vpaned->get_handle_window()->get_size(w, h);
1358  vpaned_target = vpaned_pos - paned_child_height - h;
1359  main_vpaned->set_position(vpaned_target);
1360  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", false);
1361  }
1362  preset_scrolledbox->show();
1363  if (!show_preset || !in_current_preset) {
1364  return;
1365  }
1366  on_map_conn.disconnect();
1367  // make the current entry in the preset list window
1368  // visible (in case it's outside the displayed range).
1369  // apparently only works after the window is mapped
1370  // and some size calculations are done, so put it into
1371  // an idle handler.
1372  Gtk::TreeIter it = get_current_bank_iter();
1373  if (it) {
1374  Glib::signal_idle().connect_once(
1375  sigc::bind(
1376  sigc::mem_fun1(bank_treeview, &MyTreeView::scroll_to_row),
1377  bank_treeview->get_model()->get_path(it)));
1378  }
1379  Gtk::TreeNodeChildren ch = pstore->children();
1380  for (it = ch.begin(); it != ch.end(); ++it) {
1381  if (it->get_value(pstore->col.name) == machine.get_current_name()) {
1382  Glib::signal_idle().connect_once(
1383  sigc::bind(
1384  sigc::mem_fun1(*preset_treeview, &MyTreeView::scroll_to_row),
1385  pstore->get_path(it)));
1386  break;
1387  }
1388  }
1389 }
1390 
1391 void PresetWindow::on_preset_select(bool v, bool animated, int paned_child_height) {
1392  static bool first_time = true;
1393  if (first_time) {
1394  //FIXME needed to fix first time display height, not clear why
1395  paned_child_height += 1;
1396  first_time = false;
1397  }
1398  on_map_conn.disconnect();
1399  bool is_mapped = main_vpaned->get_toplevel()->get_mapped();
1400  bool rack_visible = actions.show_rack->get_active();
1401  if (v) {
1402  if (machine.bank_check_reparse()) {
1403  set_presets();
1404  } else if (!get_current_bank_iter()) {
1405  Glib::ustring bank = machine.get_current_bank();
1406  if (!bank.empty()) {
1407  reload_banks(bank);
1408  }
1409  }
1410  autosize();
1411  Gtk::TreeIter it = get_current_bank_iter();
1412  if (it && animated && is_mapped) {
1413  bank_treeview->scroll_to_row(bank_treeview->get_model()->get_path(it));
1414  }
1415  if (!is_mapped) {
1416  // don't have widget height to calculate paned separator
1417  // position before window is mapped
1418  on_map_conn = main_vpaned->get_toplevel()->signal_map().connect(
1419  sigc::bind(
1420  sigc::mem_fun(*this, &PresetWindow::display_paned),
1421  true, paned_child_height));
1422  } else if (animated && rack_visible) {
1423  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", true);
1424  vpaned_pos = main_vpaned->get_allocation().get_height();
1425  int w, h;
1426  main_vpaned->get_handle_window()->get_size(w, h);
1427  vpaned_target = vpaned_pos - paned_child_height - h;
1428  main_vpaned->set_position(vpaned_pos);
1429  vpaned_step = paned_child_height / 5;
1430  preset_scrolledbox->show();
1431  animate_preset_show();
1432  Glib::signal_timeout().connect(sigc::mem_fun(*this, &PresetWindow::animate_preset_show), 20);
1433  } else {
1434  display_paned(false, paned_child_height);
1435  }
1436  } else {
1437  vpaned_target = main_vpaned->get_allocation().get_height();
1438  vpaned_pos = main_vpaned->get_position();
1439  if (animated && is_mapped && rack_visible) {
1440  vpaned_step = paned_child_height / 5;
1441  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", true);
1442  Glib::signal_timeout().connect(sigc::mem_fun(*this, &PresetWindow::animate_preset_hide), 20);
1443  } else {
1444  preset_scrolledbox->hide();
1445  }
1446  actions.organize->set_active(false);
1447  }
1448 }
iterator end()
Definition: gx_json.h:366
Gio::File::SlotFileProgress file_state
virtual gx_system::PresetFileGui * get_current_bank_file()=0
virtual void reorder_preset(gx_system::PresetFileGui &pf, const std::vector< Glib::ustring > &neworder)=0
Glib::RefPtr< Gio::Cancellable > cancellable
virtual void bank_set_flag(gx_system::PresetFileGui *pf, int flag, bool v)=0
bool has_entry(const Glib::ustring &name)
Definition: gx_json.h:354
virtual void load_online_presets()=0
virtual bank_iterator bank_begin()=0
virtual std::string bank_get_filename(const Glib::ustring &bank)=0
Gtk::TreeModelColumn< Glib::RefPtr< Gdk::Pixbuf > > type_pb
std::string get_online_presets_filename() const
Definition: gx_system.h:481
virtual void pf_insert_before(gx_system::PresetFileGui &pf, const Glib::ustring &src, gx_system::PresetFileGui &pftgt, const Glib::ustring &pos, const Glib::ustring &name)=0
virtual void pf_insert_after(gx_system::PresetFileGui &pf, const Glib::ustring &src, gx_system::PresetFileGui &pftgt, const Glib::ustring &pos, const Glib::ustring &name)=0
virtual int bank_size()=0
virtual gx_system::PresetFileGui * get_bank_file(const Glib::ustring &bank) const =0
virtual gx_jack::GxJack * get_jack()=0
std::vector< Position >::iterator iterator
Definition: gx_json.h:321
int get_type() const
Definition: gx_json.h:361
virtual bool convert_preset(gx_system::PresetFileGui &pf)=0
Glib::RefPtr< Gtk::Action > save_changes
virtual gx_system::PresetFileGui * bank_insert_new(const Glib::ustring &newname)=0
virtual bool setting_is_preset()=0
std::string get_style_filepath(const std::string &basename) const
Definition: gx_system.h:461
const Glib::ustring & get_rpcaddress()
Definition: gx_system.h:492
bool is_mutable() const
Definition: gx_json.h:367
static MyTreeView * create_from_builder(BaseObjectType *cobject)
Glib::RefPtr< Gtk::ToggleAction > show_rack
virtual void erase_preset(gx_system::PresetFileGui &pf, const Glib::ustring &name)=0
virtual bool rename_bank(const Glib::ustring &oldname, Glib::ustring &newname)=0
const SettingsFileHeader & get_header()
Definition: gx_json.cpp:1117
void gx_print_error(const char *, const std::string &)
Definition: gx_logging.cpp:166
Glib::RefPtr< Gtk::ActionGroup > group
virtual bool bank_remove(const Glib::ustring &bank)=0
virtual const Glib::ustring & get_current_bank()=0
Gtk::TreeModelColumn< Glib::RefPtr< Gdk::Pixbuf > > del_pb
void append(const Glib::ustring &name)
virtual bool bank_check_reparse()=0
virtual bool row_draggable_vfunc(const TreeModel::Path &path) const
static void f_progress(goffset read, goffset total)
Gtk::TreeModelColumn< Glib::RefPtr< Gdk::Pixbuf > > edit_pb
Glib::RefPtr< Gtk::Action > new_bank
virtual sigc::signal< void > & signal_selection_changed()=0
Glib::RefPtr< Gtk::ToggleAction > organize
std::string to_string(const T &t)
Definition: gx_system.h:525
void on_preset_select(bool v, bool animated, int preset_window_height)
PresetStore::PresetModelColumns col
std::string get_online_config_filename() const
Definition: gx_system.h:480
virtual void load_preset(gx_system::PresetFileGui *pf, const Glib::ustring &name)=0
Glib::RefPtr< Gtk::AccelGroup > accels
Glib::RefPtr< Gtk::Action > online_preset_bank
virtual gx_system::CmdlineOptions & get_options() const =0
void strip(Glib::ustring &s)
Definition: gx_system.cpp:972
Gtk::TreeModelColumn< Glib::ustring > name
PresetWindow(Glib::RefPtr< gx_gui::GxBuilder > bld, gx_engine::GxMachineBase &machine, const gx_system::CmdlineOptions &options, GxActions &actions, GtkSizeGroup *lc)
void child_set_property(Gtk::Container &container, Gtk::Widget &child, const char *property_name, bool value)
Glib::RefPtr< Gtk::ToggleAction > presets
virtual bool rename_preset(gx_system::PresetFileGui &pf, const Glib::ustring &oldname, const Glib::ustring &newname)=0
Gtk::TreeModelColumn< int > tp
virtual void bank_reorder(const std::vector< Glib::ustring > &neworder)=0
const Glib::ustring & get_name(int n)
Definition: gx_json.cpp:1129
virtual const Glib::ustring & get_current_name()=0
int get_flags() const
Definition: gx_json.h:358
virtual void bank_drag_begin()=0
Gtk::TreeModelColumn< Glib::ustring > name
virtual void pf_append(gx_system::PresetFileGui &pf, const Glib::ustring &src, gx_system::PresetFileGui &pftgt, const Glib::ustring &name)=0
virtual void pf_save(gx_system::PresetFileGui &pf, const Glib::ustring &name)=0
virtual gx_system::PresetFileGui * bank_insert_uri(const Glib::ustring &uri, bool move)=0
virtual gx_system::PresetFileGui * bank_get_file(const Glib::ustring &bank) const =0
Gtk::TreeModelColumn< Glib::ustring > name
virtual bank_iterator bank_end()=0
Glib::ustring current_version_string() const
Definition: gx_json.h:248
Glib::Dispatcher timeout
Glib::ustring version_string() const
Definition: gx_json.h:247