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  if (! machine.get_jack()) usleep(5000);
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  std::string NAME_;
801  std::string FILE_;
802  std::string INFO_;
803  std::string line;
804  bool set_name = false;
805  bool set_file = false;
806  bool set_info = false;
807  while ( in->read_line(line) )
808  {
809  if (line.find("<<NAME>>") != string::npos) {
810  set_name = true;
811  set_file = false;
812  set_info = false;
813  NAME_ = "";
814  continue;
815  } else if (line.find("<<FILE>>") != string::npos ) {
816  set_name = false;
817  set_file = true;
818  set_info = false;
819  FILE_ = "";
820  continue;
821  } else if (line.find("<<INFO>>") != string::npos ) {
822  set_name = false;
823  set_file = false;
824  set_info = true;
825  INFO_ = "";
826  continue;
827  } else if (line.find("<<END>>") != string::npos ) {
828  set_name = false;
829  set_file = false;
830  set_info = false;
831  olp.push_back(std::tuple<std::string,std::string,std::string>(NAME_,FILE_,INFO_));
832  NAME_ = "";
833  FILE_ = "";
834  INFO_ = "";
835  continue;
836  }
837  if ( set_name ) NAME_ += line;
838  else if ( set_file ) FILE_ += line;
839  else if ( set_info ) INFO_ += line+"\n";
840  }
841  in->close ();
842 }
843 
844 void PresetWindow::popup_pos( int& x, int& y, bool& push_in ){
845  online_preset->get_window()->get_origin( x, y );
846  x +=150;
847  y -= 450;
848  push_in = false;
849 }
850 
851 void PresetWindow::create_preset_menu(bool is_new) {
852 
853  static bool read_new = true;
854  if (read_new || is_new) {
855  read_preset_menu();
856  read_new = false;
857  }
858 
859  Gtk::MenuItem* item;
860  Gtk::Menu *presetMenu = Gtk::manage(new Gtk::Menu());
861  presetMenu->set_size_request (-1, 600);
862  for(std::vector<std::tuple<std::string,std::string,std::string> >::iterator it = olp.begin(); it != olp.end(); it++) {
863  item = Gtk::manage(new Gtk::MenuItem(get<0>(*it), true));
864  item->set_tooltip_text(get<2>(*it));
865  std::string f = get<1>(*it);
866  item->signal_activate().connect(sigc::bind(sigc::bind(sigc::mem_fun(
867  *this, &PresetWindow::downloadPreset),f),presetMenu));
868  presetMenu->append(*item);
869 
870  }
871  presetMenu->show_all();
872  presetMenu->popup(Gtk::Menu::SlotPositionCalc(sigc::mem_fun(
873  *this, &PresetWindow::popup_pos ) ),0,gtk_get_current_event_time());
874 }
875 
876 void PresetWindow::replace_inline(std::string& subject, const std::string& search,
877  const std::string& replace) {
878  size_t pos = 0;
879  while ((pos = subject.find(search, pos)) != std::string::npos) {
880  subject.replace(pos, search.length(), replace);
881  pos += replace.length();
882  }
883 }
884 
885 void PresetWindow::show_online_preset() {
886 
887  Glib::RefPtr<Gio::File> dest = Gio::File::create_for_uri(Glib::filename_to_uri(options.get_online_presets_filename(), resolve_hostname()));
888  static bool load_new = true;
889  static bool load = false;
890  Glib::RefPtr<Gdk::Window> window = preset_scrolledbox->get_toplevel()->get_window();
891  if (load_new || ! dest->query_exists()) {
892  Gdk::Cursor cursor(Gdk::WATCH);
893  window->set_cursor(cursor);
894  if (dest->query_exists()) {
895  Gtk::MessageDialog *d = new Gtk::MessageDialog(*dynamic_cast<Gtk::Window*>(online_preset->get_toplevel()),
896  "Do you wont to check for new presets from\n https://musical-artifacts.com ? \n Note, that may take a while",
897  false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true);
898  d->set_position(Gtk::WIN_POS_MOUSE);
899  if (d->run() == Gtk::RESPONSE_YES) load = true;
900  delete d;
901  }
902  if (load || ! dest->query_exists()) {
903  if (download_file("https://musical-artifacts.com/artifacts.json?apps=guitarix", options.get_online_config_filename())) {
904  machine.load_online_presets();
905  } else {
906  window->set_cursor();
907  return;
908  }
909  }
910  window->set_cursor();
911  }
912  load_new = false;
913  create_preset_menu(load_new);
914 }
915 
916 void PresetWindow::on_online_preset() {
917  Glib::signal_idle().connect_once(sigc::mem_fun(*this, &PresetWindow::show_online_preset));
918 }
919 
920 bool PresetWindow::on_bank_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint timestamp) {
921  Gtk::Widget *source_widget = Gtk::Widget::drag_get_source_widget(context);
922  if (!source_widget) {
923  // URI from other application
924  Gdk::DragAction a;
925  if (context->get_suggested_action() == Gdk::ACTION_MOVE) {
926  a = context->get_suggested_action();
927  } else {
928  a = Gdk::ACTION_COPY;
929  }
930  context->drag_status(a, timestamp);
931  } else if (source_widget != bank_treeview) {
932  // other window
933  context->drag_status((Gdk::DragAction)0, timestamp);
934  } else {
935  // reorder
936  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
937  if (!it) {
938  return true;
939  }
940  int tp = it->get_value(bank_col.tp);
942  context->drag_status((Gdk::DragAction)0, timestamp);
943  return true;
944  }
945  Gtk::TreeModel::Path pt;
946  Gtk::TreeViewDropPosition dst;
947  if (bank_treeview->get_dest_row_at_pos(x, y, pt, dst)) {
948  tp = bank_treeview->get_model()->get_iter(pt)->get_value(bank_col.tp);
950  context->drag_status((Gdk::DragAction)0, timestamp);
951  return true;
952  }
953  }
954  bank_treeview->on_drag_motion(context, x, y, timestamp);
955  context->drag_status(Gdk::ACTION_MOVE, timestamp);
956  }
957  return true;
958 }
959 
960 void PresetWindow::on_bank_changed() {
961  preset_row_del_conn.block();
962  if (in_edit) {
963  pstore->prepend();
964  }
965  pstore->clear();
966  preset_row_del_conn.unblock();
967  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
968  if (!it) {
969  in_current_preset = false;
970  return;
971  }
972  Glib::ustring nm = it->get_value(bank_col.name);
973  preset_title->set_text(nm);
974  in_current_preset = (nm == machine.get_current_bank());
975  Gtk::TreeIter i;
976  gx_system::PresetFileGui& ll = *machine.get_bank_file(nm);
978  ((ll.get_flags() & gx_system::PRESET_FLAG_READONLY) && !actions.organize->get_active())) {
979  preset_treeview->unset_rows_drag_source();
980  } else {
981  preset_treeview->unset_rows_drag_source(); //FIXME: needed?
982  preset_treeview->set_reorderable(true);
983  std::vector<Gtk::TargetEntry> listTargets2;
984  listTargets2.push_back(Gtk::TargetEntry("GTK_TREE_MODEL_ROW", Gtk::TARGET_SAME_WIDGET, 0));
985  listTargets2.push_back(Gtk::TargetEntry("application/x-guitarix-preset", Gtk::TARGET_SAME_APP, 1));
986  preset_treeview->enable_model_drag_source(listTargets2, Gdk::BUTTON1_MASK, Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
987  }
988  bool modifiable = ll.is_mutable();
989  for (gx_system::PresetFile::iterator s = ll.begin(); s != ll.end(); ++s) {
990  i = pstore->append();
991  i->set_value(pstore->col.name, s->name);
992  if (modifiable) {
993  i->set_value(pstore->col.edit_pb, pb_edit);
994  i->set_value(pstore->col.del_pb, pb_del);
995  }
996  if (in_current_preset && s->name == machine.get_current_name()) {
997  if (preset_treeview->get_mapped()) {
998  preset_treeview->scroll_to_row(pstore->get_path(i));
999  }
1000  }
1001  }
1002  if (modifiable) {
1003  i = pstore->append();
1004  }
1005 }
1006 
1007 void PresetWindow::set_row_for_presetfile(Gtk::TreeIter i, gx_system::PresetFileGui *f) {
1008  i->set_value(bank_col.name, f->get_name());
1010  i->set_value(bank_col.type_pb, pb_del);
1011  } else if (f->get_flags() & gx_system::PRESET_FLAG_VERSIONDIFF) {
1012  i->set_value(bank_col.type_pb, pb_versiondiff);
1013  } else if (f->get_flags() & gx_system::PRESET_FLAG_READONLY) {
1014  i->set_value(bank_col.type_pb, pb_readonly);
1015  }
1016  int tp = f->get_type();
1017  i->set_value(bank_col.tp, tp);
1019  i->set_value(bank_col.type_pb, pb_scratch);
1020  } else if (tp == gx_system::PresetFile::PRESET_FACTORY) {
1021  i->set_value(bank_col.type_pb, pb_factory);
1022  } else if (f->is_mutable() || f->get_flags() & gx_system::PRESET_FLAG_VERSIONDIFF) {
1023  i->set_value(bank_col.edit_pb, pb_edit);
1024  i->set_value(bank_col.del_pb, pb_del);
1025  }
1026 }
1027 
1028 void PresetWindow::reload_banks(const Glib::ustring& sel_bank) {
1029  preset_row_del_conn.block();
1030  pstore->clear();
1031  preset_row_del_conn.unblock();
1032  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(bank_treeview->get_model());
1033  bank_row_del_conn.block();
1034  ls->clear();
1035  bank_row_del_conn.unblock();
1036  Gtk::TreeIter i;
1037  int in_factory = false;
1038  for (gx_engine::bank_iterator v = machine.bank_begin(); v != machine.bank_end(); ++v) {
1039  if (!in_factory && v->get_type() == gx_system::PresetFile::PRESET_FACTORY) {
1040  i = ls->append();
1041  i->set_value(bank_col.tp, static_cast<int>(gx_system::PresetFile::PRESET_SEP));
1042  in_factory = true;
1043  }
1044  i = ls->append();
1045  set_row_for_presetfile(i, *v);
1046  if (v->get_name() == sel_bank) {
1047  bank_treeview->get_selection()->select(i);
1048  }
1049  }
1050  reload_combo();
1051 }
1052 
1053 void PresetWindow::set_presets() {
1054  reload_banks(machine.get_current_bank());
1055 }
1056 
1057 void PresetWindow::on_bank_reordered(const Gtk::TreeModel::Path& path) {
1058  Glib::RefPtr<Gtk::TreeModel> ls = bank_treeview->get_model();
1059  Gtk::TreeModel::Children ch = ls->children();
1060  std::vector<Glib::ustring> l;
1061  for (Gtk::TreeIter i = ch.begin(); i != ch.end(); ++i) {
1062  int tp = i->get_value(bank_col.tp);
1063  Glib::ustring nm = i->get_value(bank_col.name);
1064  if (!nm.empty() && (tp == gx_system::PresetFile::PRESET_SCRATCH || tp == gx_system::PresetFile::PRESET_FILE)) {
1065  l.push_back(nm);
1066  }
1067  }
1068  machine.bank_reorder(l);
1069 }
1070 
1071 /*
1072  ** list of presets
1073  */
1074 
1075 void PresetWindow::text_func(Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator& iter) {
1076  Glib::ustring val = iter->get_value(pstore->col.name);
1077  Glib::ustring t = val;
1078 #if false // gtk 2.24
1079  if (t.empty() && !cell->property_editing().get_value()) {
1080  } //to make indent happy (remove)
1081 #else
1082  gboolean editing;
1083  g_object_get(cell->gobj(), "editing", &editing, NULL);
1084  if (t.empty() && !editing) {
1085 #endif
1086  t = "<new>";
1087  } else if (in_current_preset) {
1088  int n = *pstore->get_path(iter).begin();
1089  if (n > 9) {
1090  t = " " + t;
1091  } else {
1092  t = Glib::ustring::compose("%1: %2", n, t);
1093  }
1094  }
1095  cell->set_property("text", t);
1096  Gtk::CellRendererText *tc = dynamic_cast<Gtk::CellRendererText*>(cell);
1097  if (in_current_preset && machine.setting_is_preset() && val == machine.get_current_name()) {
1098  tc->property_foreground().set_value("#f00");
1099  } else{
1100  tc->property_foreground_set().set_value(false);
1101  }
1102 }
1103 
1104 bool PresetWindow::on_preset_button_release(GdkEventButton *ev) {
1105  Gtk::TreeModel::Path pt;
1106  Gtk::TreeViewColumn *col;
1107  int dx, dy;
1108  if (!preset_treeview->get_path_at_pos(ev->x, ev->y, pt, col, dx, dy)) {
1109  return false;
1110  }
1111  Gtk::TreeModel::Path path;
1112  Gtk::TreeViewColumn *focus_column;
1113  preset_treeview->get_cursor(path, focus_column);
1114  if (col != focus_column || !path || pt != path) {
1115  return false;
1116  }
1117  Gtk::TreeIter bank_iter = get_current_bank_iter();
1118  int tp = bank_iter->get_value(bank_col.tp);
1119  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()) {
1120  return false;
1121  }
1122  if (col == preset_treeview->get_column(1)) {
1123  col = preset_treeview->get_column(0);
1124  start_edit(pt, *col, *col->get_first_cell_renderer());
1125  } else if (col == preset_treeview->get_column(2)) {
1126  Glib::ustring nm = pstore->get_iter(pt)->get_value(pstore->col.name);
1127  Gtk::MessageDialog d(*dynamic_cast<Gtk::Window*>(preset_treeview->get_toplevel()), Glib::ustring::compose("delete preset %1?", nm), false,
1128  Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true);
1129  d.set_position(Gtk::WIN_POS_MOUSE);
1130  if (d.run() == Gtk::RESPONSE_OK) {
1131  preset_row_del_conn.block();
1132  pstore->erase(pstore->get_iter(pt));
1133  preset_row_del_conn.unblock();
1134  machine.erase_preset(*machine.get_bank_file(bank_iter->get_value(bank_col.name)), nm);
1135  reload_target();
1136  }
1137  }
1138  return false;
1139 }
1140 
1141 void PresetWindow::on_preset_edited(const Glib::ustring& path, const Glib::ustring& newtext) {
1142  Gtk::TreeIter it = pstore->get_iter(path);
1143  Glib::ustring oldname = it->get_value(pstore->col.name);
1144  Glib::ustring newname = newtext;
1145  gx_system::strip(newname);
1146  if (newname.empty() || newname == oldname) {
1147  reset_edit(*preset_treeview->get_column(0));
1148  return;
1149  }
1150  // vowel mutation lead to a Glib::markup error when building the menu action,
1151  // so check for it and avoid them
1152  if (newname.find_first_of("ÜüūûúùÖöøøōôóòǫ°Ääæǣāàáâąþćēêèéīîíìȳŷýỳźżłśñ&%$§!?*#|<>/~_") != Glib::ustring::npos) {
1153  gx_print_error("PresetWindow", "Vowel mutation isn't allowed in preset names, please choose a name without \n(ÜüūûúùÖöøøōôóòǫ°Ääæǣāàáâąþēêèéīîíìȳŷýỳñ&%$§!?*#|<>/~_)");
1154  reset_edit(*preset_treeview->get_column(0));
1155  return;
1156  }
1157  gx_system::PresetFileGui& fl = *machine.get_bank_file(get_current_bank());
1158  Glib::ustring t = newname;
1159  int n = 1;
1160  while (fl.has_entry(newname)) {
1161  newname = Glib::ustring::compose("%1-%2", t, n);
1162  n += 1;
1163  }
1164  it->set_value(pstore->col.name, newname);
1165  it->set_value(pstore->col.edit_pb, pb_edit);
1166  it->set_value(pstore->col.del_pb, pb_del);
1167  if (oldname.empty()) {
1168  // check if current preset is scratch and needs to be saved
1169  if (!machine.get_current_bank().empty()) {
1170  gx_system::PresetFileGui *cpf = machine.get_bank_file(machine.get_current_bank());
1171  if (cpf && cpf->has_entry(machine.get_current_name())) {
1172  if (cpf->get_type() == gx_system::PresetFile::PRESET_SCRATCH && cpf->is_mutable()) {
1173  machine.pf_save(*cpf, machine.get_current_name());
1174  }
1175  }
1176  }
1177  pstore->append();
1178  machine.pf_save(fl, newname);
1179  } else {
1180  machine.rename_preset(fl, oldname, newname);
1181  }
1182  reload_target();
1183  reset_edit(*preset_treeview->get_column(0));
1184 }
1185 
1186 void PresetWindow::on_cursor_changed() {
1187  if (in_edit) {
1188  return;
1189  }
1190  Gtk::TreeModel::Path path;
1191  Gtk::TreeViewColumn *focus_column;
1192  preset_treeview->get_cursor(path, focus_column);
1193  if (!pstore->get_iter(path)->get_value(pstore->col.name).empty()) {
1194  return;
1195  }
1196  in_edit = true;
1197  start_edit(path, *preset_treeview->get_column(0), *preset_cellrenderer);
1198 }
1199 
1200 bool PresetWindow::on_preset_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint timestamp) {
1201  if (Gtk::Widget::drag_get_source_widget(context) == preset_treeview) {
1202  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
1203  bool ro = it && (machine.get_bank_file(it->get_value(bank_col.name))->get_flags() & gx_system::PRESET_FLAG_READONLY);
1204  if (!ro) {
1205  preset_treeview->on_drag_motion(context, x, y, timestamp);
1206  Gtk::TreeModel::Path pt;
1207  Gtk::TreeViewDropPosition dst;
1208  if (preset_treeview->get_dest_row_at_pos(x, y, pt, dst)) {
1209  if (dst == Gtk::TREE_VIEW_DROP_BEFORE ||
1210  (dst == Gtk::TREE_VIEW_DROP_AFTER &&
1211  !pstore->get_iter(pt)->get_value(pstore->col.name).empty())) {
1212  context->drag_status(Gdk::ACTION_MOVE, timestamp);
1213  return true;
1214  }
1215  }
1216  }
1217  }
1218  context->drag_status((Gdk::DragAction)0, timestamp);
1219  return true;
1220 }
1221 
1222 void PresetWindow::on_preset_reordered(const Gtk::TreeModel::Path& path) {
1223  // save changed order to file
1224  Gtk::TreeModel::Children ch = pstore->children();
1225  std::vector<Glib::ustring> l;
1226  for (Gtk::TreeIter i = ch.begin(); i != ch.end(); ++i) {
1227  Glib::ustring s = i->get_value(pstore->col.name);
1228  if (!s.empty()) {
1229  l.push_back(s);
1230  }
1231  }
1232  machine.reorder_preset(*machine.get_bank_file(get_current_bank()), l);
1233  reload_target();
1234 }
1235 
1236 void PresetWindow::autosize() {
1237  if (bank_treeview->get_mapped()) {
1238  bank_treeview->columns_autosize();
1239  preset_treeview->columns_autosize();
1240  }
1241 }
1242 
1243 void PresetWindow::on_organize() {
1244  bool v = organize_presets->get_active();
1245  bank_column_edit->set_visible(v);
1246  bank_column_delete->set_visible(v);
1247  preset_column_edit->set_visible(v);
1248  preset_column_delete->set_visible(v);
1249  Glib::RefPtr<Gtk::TreeSelection> sel = preset_treeview->get_selection();
1250  if (v) {
1251  actions.presets->set_active(true);
1252  sel->set_mode(Gtk::SELECTION_NONE);
1253  banks_combobox->set_active(-1);
1254  banks_combobox->show();
1255  presets_target_scrolledbox->show();
1256  actions.save_changes->set_sensitive(false);
1257  } else {
1258  sel->set_mode(Gtk::SELECTION_BROWSE);
1259  banks_combobox->hide();
1260  presets_target_scrolledbox->hide();
1261  if (machine.setting_is_preset()) {
1262  if (machine.get_bank_file(machine.get_current_bank())->is_mutable()) {
1263  actions.save_changes->set_sensitive(true);
1264  }
1265  }
1266  }
1267  on_bank_changed(); // reload for DnD adjustment of readonly banks
1268  autosize();
1269 }
1270 
1271 void PresetWindow::on_presets_close() {
1272  actions.presets->set_active(false); // FIXME
1273 }
1274 
1275 /*
1276  ** preset window
1277  */
1278 
1279 bool PresetWindow::animate_preset_show() {
1280  vpaned_pos -= vpaned_step;
1281  if (vpaned_pos <= vpaned_target) {
1282  main_vpaned->set_position(vpaned_target);
1283  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", false);
1284  Gtk::TreeIter it = get_current_bank_iter();
1285  if (it) {
1286  bank_treeview->scroll_to_row(bank_treeview->get_model()->get_path(it));
1287  }
1288  return false;
1289  }
1290  main_vpaned->set_position(vpaned_pos);
1291  return true;
1292 }
1293 
1294 bool PresetWindow::animate_preset_hide() {
1295  vpaned_pos += vpaned_step;
1296  if (vpaned_pos >= vpaned_target) {
1297  preset_scrolledbox->hide();
1298  return false;
1299  }
1300  main_vpaned->set_position(vpaned_pos);
1301  return true;
1302 }
1303 
1304 void PresetWindow::on_preset_changed() {
1305  if (actions.organize->get_active()) {
1306  return;
1307  }
1308  Glib::ustring bank;
1309  Glib::ustring name;
1310  Gtk::TreeIter it = bank_treeview->get_selection()->get_selected();
1311  if (!it) {
1312  return;
1313  }
1314  bank = it->get_value(bank_col.name);
1315  it = preset_treeview->get_selection()->get_selected();
1316  if (!it) {
1317  return;
1318  }
1319  name = it->get_value(pstore->col.name);
1320  bool is_scratch = false;
1321  gx_system::PresetFileGui *cpf = 0;
1322  if (!machine.get_current_bank().empty()) {
1323  cpf = machine.get_bank_file(machine.get_current_bank());
1324  if (cpf && cpf->has_entry(machine.get_current_name())) {
1325  is_scratch = (cpf->get_type() == gx_system::PresetFile::PRESET_SCRATCH);
1326  }
1327  }
1328  if (is_scratch) {
1329  if (bank == machine.get_current_bank() && name == machine.get_current_name()) {
1330  machine.pf_save(*cpf, machine.get_current_name());
1331  // no reload necessary
1332  return;
1333  }
1334  }
1335  in_current_preset = true;
1336  cpf = machine.get_bank_file(bank);
1337  machine.load_preset(cpf, name);
1338  actions.save_changes->set_sensitive(cpf && cpf->is_mutable());
1339 }
1340 
1341 void PresetWindow::on_preset_save() {
1342  if (!machine.setting_is_preset()) {
1343  return;
1344  }
1345  gx_system::PresetFileGui *pf = machine.get_bank_file(machine.get_current_bank());
1346  if (!pf->is_mutable()) {
1347  return;
1348  }
1349  machine.pf_save(*pf, machine.get_current_name());
1350 }
1351 
1352 void PresetWindow::display_paned(bool show_preset, int paned_child_height) {
1353  if (preset_scrolledbox->get_parent() == main_vpaned) {
1354  vpaned_pos = main_vpaned->get_allocation().get_height();
1355  int w, h;
1356  main_vpaned->get_handle_window()->get_size(w, h);
1357  vpaned_target = vpaned_pos - paned_child_height - h;
1358  main_vpaned->set_position(vpaned_target);
1359  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", false);
1360  }
1361  preset_scrolledbox->show();
1362  if (!show_preset || !in_current_preset) {
1363  return;
1364  }
1365  on_map_conn.disconnect();
1366  // make the current entry in the preset list window
1367  // visible (in case it's outside the displayed range).
1368  // apparently only works after the window is mapped
1369  // and some size calculations are done, so put it into
1370  // an idle handler.
1371  Gtk::TreeIter it = get_current_bank_iter();
1372  if (it) {
1373  Glib::signal_idle().connect_once(
1374  sigc::bind(
1375  sigc::mem_fun1(bank_treeview, &MyTreeView::scroll_to_row),
1376  bank_treeview->get_model()->get_path(it)));
1377  }
1378  Gtk::TreeNodeChildren ch = pstore->children();
1379  for (it = ch.begin(); it != ch.end(); ++it) {
1380  if (it->get_value(pstore->col.name) == machine.get_current_name()) {
1381  Glib::signal_idle().connect_once(
1382  sigc::bind(
1383  sigc::mem_fun1(*preset_treeview, &MyTreeView::scroll_to_row),
1384  pstore->get_path(it)));
1385  break;
1386  }
1387  }
1388 }
1389 
1390 void PresetWindow::on_preset_select(bool v, bool animated, int paned_child_height) {
1391  static bool first_time = true;
1392  if (first_time) {
1393  //FIXME needed to fix first time display height, not clear why
1394  paned_child_height += 1;
1395  first_time = false;
1396  }
1397  on_map_conn.disconnect();
1398  bool is_mapped = main_vpaned->get_toplevel()->get_mapped();
1399  bool rack_visible = actions.show_rack->get_active();
1400  if (v) {
1401  if (machine.bank_check_reparse()) {
1402  set_presets();
1403  } else if (!get_current_bank_iter()) {
1404  Glib::ustring bank = machine.get_current_bank();
1405  if (!bank.empty()) {
1406  reload_banks(bank);
1407  }
1408  }
1409  autosize();
1410  Gtk::TreeIter it = get_current_bank_iter();
1411  if (it && animated && is_mapped) {
1412  bank_treeview->scroll_to_row(bank_treeview->get_model()->get_path(it));
1413  }
1414  if (!is_mapped) {
1415  // don't have widget height to calculate paned separator
1416  // position before window is mapped
1417  on_map_conn = main_vpaned->get_toplevel()->signal_map().connect(
1418  sigc::bind(
1419  sigc::mem_fun(*this, &PresetWindow::display_paned),
1420  true, paned_child_height));
1421  } else if (animated && rack_visible) {
1422  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", true);
1423  vpaned_pos = main_vpaned->get_allocation().get_height();
1424  int w, h;
1425  main_vpaned->get_handle_window()->get_size(w, h);
1426  vpaned_target = vpaned_pos - paned_child_height - h;
1427  main_vpaned->set_position(vpaned_pos);
1428  vpaned_step = paned_child_height / 5;
1429  preset_scrolledbox->show();
1430  animate_preset_show();
1431  Glib::signal_timeout().connect(sigc::mem_fun(*this, &PresetWindow::animate_preset_show), 20);
1432  } else {
1433  display_paned(false, paned_child_height);
1434  }
1435  } else {
1436  vpaned_target = main_vpaned->get_allocation().get_height();
1437  vpaned_pos = main_vpaned->get_position();
1438  if (animated && is_mapped && rack_visible) {
1439  vpaned_step = paned_child_height / 5;
1440  gx_gui::child_set_property(*main_vpaned, *preset_scrolledbox, "shrink", true);
1441  Glib::signal_timeout().connect(sigc::mem_fun(*this, &PresetWindow::animate_preset_hide), 20);
1442  } else {
1443  preset_scrolledbox->hide();
1444  }
1445  actions.organize->set_active(false);
1446  }
1447 }
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