/[svn]/gigedit/trunk/src/gigedit/mainwindow.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/mainwindow.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2903 - (show annotations) (download)
Tue May 3 14:08:34 2016 UTC (7 years, 10 months ago) by schoenebeck
File size: 131432 byte(s)
* Script Editor: altered keyboard shortcut Ctrl-X to Ctrl-Q (to avoid
  masking the common cut text shortcut).
* Script Editor: if editor is used in live-mode, inform the sampler that
  it needs to automatically reload the script after the script has been
  altered and applied with the script editor.
* Bumped version (1.0.0.svn13).

1 /*
2 * Copyright (C) 2006-2016 Andreas Persson
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with program; see the file COPYING. If not, write to the Free
16 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include <iostream>
21 #include <cstring>
22
23 #include <glibmmconfig.h>
24 // threads.h must be included first to be able to build with
25 // G_DISABLE_DEPRECATED
26 #if (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION == 31 && GLIBMM_MICRO_VERSION >= 2) || \
27 (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION > 31) || GLIBMM_MAJOR_VERSION > 2
28 #include <glibmm/threads.h>
29 #endif
30
31 #include <glibmm/convert.h>
32 #include <glibmm/dispatcher.h>
33 #include <glibmm/miscutils.h>
34 #include <glibmm/stringutils.h>
35 #include <gtkmm/aboutdialog.h>
36 #include <gtkmm/filechooserdialog.h>
37 #include <gtkmm/messagedialog.h>
38 #include <gtkmm/targetentry.h>
39 #include <gtkmm/main.h>
40 #include <gtkmm/toggleaction.h>
41 #if GTKMM_MAJOR_VERSION < 3
42 #include "wrapLabel.hh"
43 #endif
44
45 #include "global.h"
46 #include "compat.h"
47
48 #include <stdio.h>
49 #include <sndfile.h>
50 #include <assert.h>
51
52 #include "mainwindow.h"
53 #include "Settings.h"
54 #include "CombineInstrumentsDialog.h"
55 #include "scripteditor.h"
56 #include "scriptslots.h"
57 #include "ReferencesView.h"
58 #include "../../gfx/status_attached.xpm"
59 #include "../../gfx/status_detached.xpm"
60
61
62 MainWindow::MainWindow() :
63 m_DimRegionChooser(*this),
64 dimreg_label(_("Changes apply to:")),
65 dimreg_all_regions(_("all regions")),
66 dimreg_all_dimregs(_("all dimension splits")),
67 dimreg_stereo(_("both channels"))
68 {
69 // set_border_width(5);
70 // set_default_size(400, 200);
71
72
73 add(m_VBox);
74
75 // Handle selection
76 m_TreeView.get_selection()->signal_changed().connect(
77 sigc::mem_fun(*this, &MainWindow::on_sel_change));
78
79 // m_TreeView.set_reorderable();
80
81 m_TreeView.signal_button_press_event().connect_notify(
82 sigc::mem_fun(*this, &MainWindow::on_button_release));
83
84 // Add the TreeView tab, inside a ScrolledWindow, with the button underneath:
85 m_ScrolledWindow.add(m_TreeView);
86 // m_ScrolledWindow.set_size_request(200, 600);
87 m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
88
89 m_ScrolledWindowSamples.add(m_TreeViewSamples);
90 m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
91
92 m_ScrolledWindowScripts.add(m_TreeViewScripts);
93 m_ScrolledWindowScripts.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
94
95
96 m_TreeViewNotebook.set_size_request(300);
97
98 m_HPaned.add1(m_TreeViewNotebook);
99 dimreg_hbox.add(dimreg_label);
100 dimreg_hbox.add(dimreg_all_regions);
101 dimreg_hbox.add(dimreg_all_dimregs);
102 dimreg_stereo.set_active();
103 dimreg_hbox.add(dimreg_stereo);
104 dimreg_vbox.add(dimreg_edit);
105 dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
106 m_HPaned.add2(dimreg_vbox);
107
108 dimreg_label.set_tooltip_text(_("To automatically apply your changes above globally to the entire instrument, check all 3 check boxes on the right."));
109 dimreg_all_regions.set_tooltip_text(_("If checked: all changes you perform above will automatically be applied to all regions of this instrument as well."));
110 dimreg_all_dimregs.set_tooltip_text(_("If checked: all changes you perform above will automatically be applied as well to all dimension splits of the region selected below."));
111 dimreg_stereo.set_tooltip_text(_("If checked: all changes you perform above will automatically be applied to both audio channel splits (only if a \"stereo\" dimension is defined below)."));
112
113 m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, _("Samples"));
114 m_TreeViewNotebook.append_page(m_ScrolledWindow, _("Instruments"));
115 m_TreeViewNotebook.append_page(m_ScrolledWindowScripts, _("Scripts"));
116
117 actionGroup = Gtk::ActionGroup::create();
118
119 actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
120 actionGroup->add(Gtk::Action::create("New", _("_New")),
121 Gtk::AccelKey("<control>n"),
122 sigc::mem_fun(
123 *this, &MainWindow::on_action_file_new));
124 actionGroup->add(Gtk::Action::create("Open", _("_Open...")),
125 Gtk::AccelKey("<control>o"),
126 sigc::mem_fun(
127 *this, &MainWindow::on_action_file_open));
128 actionGroup->add(Gtk::Action::create("Save", _("_Save")),
129 Gtk::AccelKey("<control>s"),
130 sigc::mem_fun(
131 *this, &MainWindow::on_action_file_save));
132 actionGroup->add(Gtk::Action::create("SaveAs", _("Save _As...")),
133 Gtk::AccelKey("<shift><control>s"),
134 sigc::mem_fun(
135 *this, &MainWindow::on_action_file_save_as));
136 actionGroup->add(Gtk::Action::create("Properties",
137 _("_Properties")),
138 sigc::mem_fun(
139 *this, &MainWindow::on_action_file_properties));
140 actionGroup->add(Gtk::Action::create("InstrProperties",
141 _("_Properties")),
142 sigc::mem_fun(
143 *this, &MainWindow::show_instr_props));
144 actionGroup->add(Gtk::Action::create("MidiRules",
145 _("_Midi Rules...")),
146 sigc::mem_fun(
147 *this, &MainWindow::show_midi_rules));
148 actionGroup->add(Gtk::Action::create("ScriptSlots",
149 _("_Script Slots...")),
150 sigc::mem_fun(
151 *this, &MainWindow::show_script_slots));
152 actionGroup->add(Gtk::Action::create("Quit", _("_Quit")),
153 Gtk::AccelKey("<control>q"),
154 sigc::mem_fun(
155 *this, &MainWindow::on_action_quit));
156 actionGroup->add(
157 Gtk::Action::create("MenuSample", _("_Sample")),
158 sigc::mem_fun(*this, &MainWindow::show_samples_tab)
159 );
160 actionGroup->add(
161 Gtk::Action::create("MenuInstrument", _("_Instrument")),
162 sigc::mem_fun(*this, &MainWindow::show_intruments_tab)
163 );
164 actionGroup->add(
165 Gtk::Action::create("MenuScript", _("S_cript")),
166 sigc::mem_fun(*this, &MainWindow::show_scripts_tab)
167 );
168 actionGroup->add(Gtk::Action::create("AllInstruments", _("_Select")));
169
170 actionGroup->add(Gtk::Action::create("MenuEdit", _("_Edit")));
171
172 Glib::RefPtr<Gtk::ToggleAction> toggle_action =
173 Gtk::ToggleAction::create("CopySampleUnity", _("Copy Sample's _Unity Note"));
174 toggle_action->set_active(true);
175 actionGroup->add(toggle_action);
176
177 toggle_action =
178 Gtk::ToggleAction::create("CopySampleTune", _("Copy Sample's _Fine Tune"));
179 toggle_action->set_active(true);
180 actionGroup->add(toggle_action);
181
182 toggle_action =
183 Gtk::ToggleAction::create("CopySampleLoop", _("Copy Sample's _Loop Points"));
184 toggle_action->set_active(true);
185 actionGroup->add(toggle_action);
186
187
188 actionGroup->add(Gtk::Action::create("MenuView", _("_View")));
189 toggle_action =
190 Gtk::ToggleAction::create("Statusbar", _("_Statusbar"));
191 toggle_action->set_active(true);
192 actionGroup->add(toggle_action,
193 sigc::mem_fun(
194 *this, &MainWindow::on_action_view_status_bar));
195 actionGroup->add(
196 Gtk::Action::create("RefreshAll", _("_Refresh All")),
197 sigc::mem_fun(*this, &MainWindow::on_action_refresh_all)
198 );
199
200 actionGroup->add(Gtk::Action::create("MenuHelp", _("_Help")));
201 actionGroup->add(Gtk::Action::create("About", _("_About")),
202 sigc::mem_fun(
203 *this, &MainWindow::on_action_help_about));
204 actionGroup->add(
205 Gtk::Action::create("AddInstrument", _("Add _Instrument")),
206 sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
207 );
208 actionGroup->add(
209 Gtk::Action::create("DupInstrument", _("_Duplicate Instrument")),
210 sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument)
211 );
212 actionGroup->add(
213 Gtk::Action::create("RemoveInstrument", _("_Remove")),
214 sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
215 );
216
217
218 actionGroup->add(Gtk::Action::create("MenuSettings", _("_Settings")));
219
220 toggle_action =
221 Gtk::ToggleAction::create("WarnUserOnExtensions", _("Show warning on format _extensions"));
222 toggle_action->set_active(Settings::singleton()->warnUserOnExtensions);
223 actionGroup->add(
224 toggle_action,
225 sigc::mem_fun(*this, &MainWindow::on_action_warn_user_on_extensions)
226 );
227
228 toggle_action =
229 Gtk::ToggleAction::create("SyncSamplerInstrumentSelection", _("Synchronize sampler's instrument selection"));
230 toggle_action->set_active(Settings::singleton()->syncSamplerInstrumentSelection);
231 actionGroup->add(
232 toggle_action,
233 sigc::mem_fun(*this, &MainWindow::on_action_sync_sampler_instrument_selection)
234 );
235
236 toggle_action =
237 Gtk::ToggleAction::create("MoveRootNoteWithRegionMoved", _("Move root note with region moved"));
238 toggle_action->set_active(Settings::singleton()->moveRootNoteWithRegionMoved);
239 actionGroup->add(
240 toggle_action,
241 sigc::mem_fun(*this, &MainWindow::on_action_move_root_note_with_region_moved)
242 );
243
244
245 actionGroup->add(Gtk::Action::create("MenuTools", _("_Tools")));
246
247 actionGroup->add(
248 Gtk::Action::create("CombineInstruments", _("_Combine Instruments...")),
249 sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments)
250 );
251
252 actionGroup->add(
253 Gtk::Action::create("MergeFiles", _("_Merge Files...")),
254 sigc::mem_fun(*this, &MainWindow::on_action_merge_files)
255 );
256
257
258 // sample right-click popup actions
259 actionGroup->add(
260 Gtk::Action::create("SampleProperties", _("_Properties")),
261 sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
262 );
263 actionGroup->add(
264 Gtk::Action::create("AddGroup", _("Add _Group")),
265 sigc::mem_fun(*this, &MainWindow::on_action_add_group)
266 );
267 actionGroup->add(
268 Gtk::Action::create("AddSample", _("Add _Sample(s)...")),
269 sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
270 );
271 actionGroup->add(
272 Gtk::Action::create("RemoveSample", _("_Remove")),
273 sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
274 );
275 actionGroup->add(
276 Gtk::Action::create("RemoveUnusedSamples", _("Remove _Unused Samples")),
277 sigc::mem_fun(*this, &MainWindow::on_action_remove_unused_samples)
278 );
279 actionGroup->add(
280 Gtk::Action::create("ShowSampleRefs", _("Show References...")),
281 sigc::mem_fun(*this, &MainWindow::on_action_view_references)
282 );
283 actionGroup->add(
284 Gtk::Action::create("ReplaceSample",
285 _("Replace Sample...")),
286 sigc::mem_fun(*this, &MainWindow::on_action_replace_sample)
287 );
288 actionGroup->add(
289 Gtk::Action::create("ReplaceAllSamplesInAllGroups",
290 _("Replace All Samples in All Groups...")),
291 sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups)
292 );
293
294 // script right-click popup actions
295 actionGroup->add(
296 Gtk::Action::create("AddScriptGroup", _("Add _Group")),
297 sigc::mem_fun(*this, &MainWindow::on_action_add_script_group)
298 );
299 actionGroup->add(
300 Gtk::Action::create("AddScript", _("Add _Script")),
301 sigc::mem_fun(*this, &MainWindow::on_action_add_script)
302 );
303 actionGroup->add(
304 Gtk::Action::create("EditScript", _("_Edit Script...")),
305 sigc::mem_fun(*this, &MainWindow::on_action_edit_script)
306 );
307 actionGroup->add(
308 Gtk::Action::create("RemoveScript", _("_Remove")),
309 sigc::mem_fun(*this, &MainWindow::on_action_remove_script)
310 );
311
312 uiManager = Gtk::UIManager::create();
313 uiManager->insert_action_group(actionGroup);
314 add_accel_group(uiManager->get_accel_group());
315
316 Glib::ustring ui_info =
317 "<ui>"
318 " <menubar name='MenuBar'>"
319 " <menu action='MenuFile'>"
320 " <menuitem action='New'/>"
321 " <menuitem action='Open'/>"
322 " <separator/>"
323 " <menuitem action='Save'/>"
324 " <menuitem action='SaveAs'/>"
325 " <separator/>"
326 " <menuitem action='Properties'/>"
327 " <separator/>"
328 " <menuitem action='Quit'/>"
329 " </menu>"
330 " <menu action='MenuEdit'>"
331 " <menuitem action='CopySampleUnity'/>"
332 " <menuitem action='CopySampleTune'/>"
333 " <menuitem action='CopySampleLoop'/>"
334 " </menu>"
335 " <menu action='MenuSample'>"
336 " <menuitem action='SampleProperties'/>"
337 " <menuitem action='AddGroup'/>"
338 " <menuitem action='AddSample'/>"
339 " <menuitem action='ShowSampleRefs'/>"
340 " <menuitem action='ReplaceSample' />"
341 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
342 " <separator/>"
343 " <menuitem action='RemoveSample'/>"
344 " <menuitem action='RemoveUnusedSamples'/>"
345 " </menu>"
346 " <menu action='MenuInstrument'>"
347 " <menu action='AllInstruments'>"
348 " </menu>"
349 " <separator/>"
350 " <menuitem action='InstrProperties'/>"
351 " <menuitem action='MidiRules'/>"
352 " <menuitem action='ScriptSlots'/>"
353 " <menuitem action='AddInstrument'/>"
354 " <menuitem action='DupInstrument'/>"
355 " <separator/>"
356 " <menuitem action='RemoveInstrument'/>"
357 " </menu>"
358 " <menu action='MenuScript'>"
359 " <menuitem action='AddScriptGroup'/>"
360 " <menuitem action='AddScript'/>"
361 " <menuitem action='EditScript'/>"
362 " <separator/>"
363 " <menuitem action='RemoveScript'/>"
364 " </menu>"
365 " <menu action='MenuView'>"
366 " <menuitem action='Statusbar'/>"
367 " <separator/>"
368 " <menuitem action='RefreshAll'/>"
369 " </menu>"
370 " <menu action='MenuTools'>"
371 " <menuitem action='CombineInstruments'/>"
372 " <menuitem action='MergeFiles'/>"
373 " </menu>"
374 " <menu action='MenuSettings'>"
375 " <menuitem action='WarnUserOnExtensions'/>"
376 " <menuitem action='SyncSamplerInstrumentSelection'/>"
377 " <menuitem action='MoveRootNoteWithRegionMoved'/>"
378 " </menu>"
379 " <menu action='MenuHelp'>"
380 " <menuitem action='About'/>"
381 " </menu>"
382 " </menubar>"
383 " <popup name='PopupMenu'>"
384 " <menuitem action='InstrProperties'/>"
385 " <menuitem action='MidiRules'/>"
386 " <menuitem action='ScriptSlots'/>"
387 " <menuitem action='AddInstrument'/>"
388 " <menuitem action='DupInstrument'/>"
389 " <separator/>"
390 " <menuitem action='RemoveInstrument'/>"
391 " </popup>"
392 " <popup name='SamplePopupMenu'>"
393 " <menuitem action='SampleProperties'/>"
394 " <menuitem action='AddGroup'/>"
395 " <menuitem action='AddSample'/>"
396 " <menuitem action='ShowSampleRefs'/>"
397 " <menuitem action='ReplaceSample' />"
398 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
399 " <separator/>"
400 " <menuitem action='RemoveSample'/>"
401 " <menuitem action='RemoveUnusedSamples'/>"
402 " </popup>"
403 " <popup name='ScriptPopupMenu'>"
404 " <menuitem action='AddScriptGroup'/>"
405 " <menuitem action='AddScript'/>"
406 " <menuitem action='EditScript'/>"
407 " <separator/>"
408 " <menuitem action='RemoveScript'/>"
409 " </popup>"
410 "</ui>";
411 uiManager->add_ui_from_string(ui_info);
412
413 popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
414
415 // Set tooltips for menu items (for some reason, setting a tooltip on the
416 // respective Gtk::Action objects above will simply be ignored, no matter
417 // if using Gtk::Action::set_tooltip() or passing the tooltip string on
418 // Gtk::Action::create()).
419 {
420 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
421 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleUnity"));
422 item->set_tooltip_text(_("Used when dragging a sample to a region's sample reference field. You may disable this for example if you want to replace an existing sample in a region with a new sample, but don't want that the region's current unity note setting will be altered by this action."));
423 }
424 {
425 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
426 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleTune"));
427 item->set_tooltip_text(_("Used when dragging a sample to a region's sample reference field. You may disable this for example if you want to replace an existing sample in a region with a new sample, but don't want that the region's current sample playback tuning will be altered by this action."));
428 }
429 {
430 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
431 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleLoop"));
432 item->set_tooltip_text(_("Used when dragging a sample to a region's sample reference field. You may disable this for example if you want to replace an existing sample in a region with a new sample, but don't want that the region's current loop informations to be altered by this action."));
433 }
434 {
435 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
436 uiManager->get_widget("/MenuBar/MenuSettings/WarnUserOnExtensions"));
437 item->set_tooltip_text(_("If checked, a warning will be shown whenever you try to use a feature which is based on a LinuxSampler extension ontop of the original gig format, which would not work with the Gigasampler/GigaStudio application."));
438 }
439 {
440 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
441 uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection"));
442 item->set_tooltip_text(_("If checked, the sampler's current instrument will automatically be switched whenever another instrument was selected in gigedit (only available in live-mode)."));
443 }
444 {
445 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
446 uiManager->get_widget("/MenuBar/MenuSettings/MoveRootNoteWithRegionMoved"));
447 item->set_tooltip_text(_("If checked, and when a region is moved by dragging it around on the virtual keyboard, the keybord position dependent pitch will move exactly with the amount of semi tones the region was moved around."));
448 }
449 {
450 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
451 uiManager->get_widget("/MenuBar/MenuSample/RemoveUnusedSamples"));
452 item->set_tooltip_text(_("Removes all samples that are not referenced by any instrument (i.e. red ones)."));
453 // copy tooltip to popup menu
454 Gtk::MenuItem* item2 = dynamic_cast<Gtk::MenuItem*>(
455 uiManager->get_widget("/SamplePopupMenu/RemoveUnusedSamples"));
456 item2->set_tooltip_text(item->get_tooltip_text());
457 }
458 {
459 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
460 uiManager->get_widget("/MenuBar/MenuView/RefreshAll"));
461 item->set_tooltip_text(_("Reloads the currently open gig file and updates the entire graphical user interface."));
462 }
463 {
464 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
465 uiManager->get_widget("/MenuBar/MenuTools/CombineInstruments"));
466 item->set_tooltip_text(_("Create combi sounds out of individual sounds of this .gig file."));
467 }
468 {
469 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
470 uiManager->get_widget("/MenuBar/MenuTools/MergeFiles"));
471 item->set_tooltip_text(_("Add instruments and samples of other .gig files to this .gig file."));
472 }
473
474
475 instrument_menu = static_cast<Gtk::MenuItem*>(
476 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments"))->get_submenu();
477
478 Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar");
479 m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
480 m_VBox.pack_start(m_HPaned);
481 m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
482 m_VBox.pack_start(m_RegionChooser.m_VirtKeybPropsBox, Gtk::PACK_SHRINK);
483 m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
484 m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
485
486 set_file_is_shared(false);
487
488 // Status Bar:
489 m_StatusBar.pack_start(m_AttachedStateLabel, Gtk::PACK_SHRINK);
490 m_StatusBar.pack_start(m_AttachedStateImage, Gtk::PACK_SHRINK);
491 m_StatusBar.show();
492
493 m_RegionChooser.signal_region_selected().connect(
494 sigc::mem_fun(*this, &MainWindow::region_changed) );
495 m_DimRegionChooser.signal_dimregion_selected().connect(
496 sigc::mem_fun(*this, &MainWindow::dimreg_changed) );
497
498
499 // Create the Tree model:
500 m_refTreeModel = Gtk::ListStore::create(m_Columns);
501 m_TreeView.set_model(m_refTreeModel);
502 m_TreeView.set_tooltip_text(_("Right click here for actions on instruments & MIDI Rules. Drag & drop to change the order of instruments."));
503 instrument_name_connection = m_refTreeModel->signal_row_changed().connect(
504 sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
505 );
506
507 // Add the TreeView's view columns:
508 m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name);
509 m_TreeView.set_headers_visible(false);
510
511 // establish drag&drop within the instrument tree view, allowing to reorder
512 // the sequence of instruments within the gig file
513 {
514 std::vector<Gtk::TargetEntry> drag_target_instrument;
515 drag_target_instrument.push_back(Gtk::TargetEntry("gig::Instrument"));
516 m_TreeView.drag_source_set(drag_target_instrument);
517 m_TreeView.drag_dest_set(drag_target_instrument);
518 m_TreeView.signal_drag_begin().connect(
519 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_begin)
520 );
521 m_TreeView.signal_drag_data_get().connect(
522 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_data_get)
523 );
524 m_TreeView.signal_drag_data_received().connect(
525 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drop_drag_data_received)
526 );
527 }
528
529 // create samples treeview (including its data model)
530 m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
531 m_TreeViewSamples.set_model(m_refSamplesTreeModel);
532 m_TreeViewSamples.set_tooltip_text(_("To actually use a sample, drag it from this list view to \"Sample\" -> \"Sample:\" on the region's settings pane on the right.\n\nRight click here for more actions on samples."));
533 // m_TreeViewSamples.set_reorderable();
534 m_TreeViewSamples.append_column_editable(_("Name"), m_SamplesModel.m_col_name);
535 m_TreeViewSamples.append_column(_("Referenced"), m_SamplesModel.m_col_refcount);
536 {
537 Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(0);
538 Gtk::CellRendererText* cellrenderer =
539 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
540 column->add_attribute(
541 cellrenderer->property_foreground(), m_SamplesModel.m_color
542 );
543 }
544 {
545 Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(1);
546 Gtk::CellRendererText* cellrenderer =
547 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
548 column->add_attribute(
549 cellrenderer->property_foreground(), m_SamplesModel.m_color
550 );
551 }
552 m_TreeViewSamples.set_headers_visible(true);
553 m_TreeViewSamples.signal_button_press_event().connect_notify(
554 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
555 );
556 m_refSamplesTreeModel->signal_row_changed().connect(
557 sigc::mem_fun(*this, &MainWindow::sample_name_changed)
558 );
559
560 // create scripts treeview (including its data model)
561 m_refScriptsTreeModel = ScriptsTreeStore::create(m_ScriptsModel);
562 m_TreeViewScripts.set_model(m_refScriptsTreeModel);
563 m_TreeViewScripts.set_tooltip_text(_(
564 "Use CTRL + double click for editing a script."
565 "\n\n"
566 "Note: instrument scripts are a LinuxSampler extension of the gig "
567 "format. This feature will not work with the GigaStudio software!"
568 ));
569 // m_TreeViewScripts.set_reorderable();
570 m_TreeViewScripts.append_column_editable("Samples", m_ScriptsModel.m_col_name);
571 m_TreeViewScripts.set_headers_visible(false);
572 m_TreeViewScripts.signal_button_press_event().connect_notify(
573 sigc::mem_fun(*this, &MainWindow::on_script_treeview_button_release)
574 );
575 //FIXME: why the heck does this double click signal_row_activated() only fire while CTRL key is pressed ?
576 m_TreeViewScripts.signal_row_activated().connect(
577 sigc::mem_fun(*this, &MainWindow::script_double_clicked)
578 );
579 m_refScriptsTreeModel->signal_row_changed().connect(
580 sigc::mem_fun(*this, &MainWindow::script_name_changed)
581 );
582
583 // establish drag&drop between scripts tree view and ScriptSlots window
584 std::vector<Gtk::TargetEntry> drag_target_gig_script;
585 drag_target_gig_script.push_back(Gtk::TargetEntry("gig::Script"));
586 m_TreeViewScripts.drag_source_set(drag_target_gig_script);
587 m_TreeViewScripts.signal_drag_begin().connect(
588 sigc::mem_fun(*this, &MainWindow::on_scripts_treeview_drag_begin)
589 );
590 m_TreeViewScripts.signal_drag_data_get().connect(
591 sigc::mem_fun(*this, &MainWindow::on_scripts_treeview_drag_data_get)
592 );
593
594 // establish drag&drop between samples tree view and dimension region 'Sample' text entry
595 std::vector<Gtk::TargetEntry> drag_target_gig_sample;
596 drag_target_gig_sample.push_back(Gtk::TargetEntry("gig::Sample"));
597 m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
598 m_TreeViewSamples.signal_drag_begin().connect(
599 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_begin)
600 );
601 m_TreeViewSamples.signal_drag_data_get().connect(
602 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get)
603 );
604 dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample);
605 dimreg_edit.wSample->signal_drag_data_received().connect(
606 sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
607 );
608 dimreg_edit.signal_dimreg_changed().connect(
609 sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
610 m_RegionChooser.signal_instrument_changed().connect(
611 sigc::mem_fun(*this, &MainWindow::file_changed));
612 m_RegionChooser.signal_instrument_changed().connect(
613 sigc::mem_fun(*this, &MainWindow::region_changed));
614 m_DimRegionChooser.signal_region_changed().connect(
615 sigc::mem_fun(*this, &MainWindow::file_changed));
616 instrumentProps.signal_changed().connect(
617 sigc::mem_fun(*this, &MainWindow::file_changed));
618 propDialog.signal_changed().connect(
619 sigc::mem_fun(*this, &MainWindow::file_changed));
620 midiRules.signal_changed().connect(
621 sigc::mem_fun(*this, &MainWindow::file_changed));
622
623 dimreg_edit.signal_dimreg_to_be_changed().connect(
624 dimreg_to_be_changed_signal.make_slot());
625 dimreg_edit.signal_dimreg_changed().connect(
626 dimreg_changed_signal.make_slot());
627 dimreg_edit.signal_sample_ref_changed().connect(
628 sample_ref_changed_signal.make_slot());
629 sample_ref_changed_signal.connect(
630 sigc::mem_fun(*this, &MainWindow::on_sample_ref_changed)
631 );
632 samples_to_be_removed_signal.connect(
633 sigc::mem_fun(*this, &MainWindow::on_samples_to_be_removed)
634 );
635
636 dimreg_edit.signal_select_sample().connect(
637 sigc::mem_fun(*this, &MainWindow::select_sample)
638 );
639
640 m_RegionChooser.signal_instrument_struct_to_be_changed().connect(
641 sigc::hide(
642 sigc::bind(
643 file_structure_to_be_changed_signal.make_slot(),
644 sigc::ref(this->file)
645 )
646 )
647 );
648 m_RegionChooser.signal_instrument_struct_changed().connect(
649 sigc::hide(
650 sigc::bind(
651 file_structure_changed_signal.make_slot(),
652 sigc::ref(this->file)
653 )
654 )
655 );
656 m_RegionChooser.signal_region_to_be_changed().connect(
657 region_to_be_changed_signal.make_slot());
658 m_RegionChooser.signal_region_changed_signal().connect(
659 region_changed_signal.make_slot());
660
661 note_on_signal.connect(
662 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_on_event));
663 note_off_signal.connect(
664 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_off_event));
665
666 dimreg_all_regions.signal_toggled().connect(
667 sigc::mem_fun(*this, &MainWindow::update_dimregs));
668 dimreg_all_dimregs.signal_toggled().connect(
669 sigc::mem_fun(*this, &MainWindow::dimreg_all_dimregs_toggled));
670 dimreg_stereo.signal_toggled().connect(
671 sigc::mem_fun(*this, &MainWindow::update_dimregs));
672
673 file = 0;
674 file_is_changed = false;
675
676 show_all_children();
677
678 // start with a new gig file by default
679 on_action_file_new();
680
681 // select 'Instruments' tab by default
682 // (gtk allows this only if the tab childs are visible, thats why it's here)
683 m_TreeViewNotebook.set_current_page(1);
684 }
685
686 MainWindow::~MainWindow()
687 {
688 }
689
690 bool MainWindow::on_delete_event(GdkEventAny* event)
691 {
692 return !file_is_shared && file_is_changed && !close_confirmation_dialog();
693 }
694
695 void MainWindow::on_action_quit()
696 {
697 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
698 hide();
699 }
700
701 void MainWindow::region_changed()
702 {
703 m_DimRegionChooser.set_region(m_RegionChooser.get_region());
704 }
705
706 gig::Instrument* MainWindow::get_instrument()
707 {
708 gig::Instrument* instrument = 0;
709 Gtk::TreeModel::const_iterator it =
710 m_TreeView.get_selection()->get_selected();
711 if (it) {
712 Gtk::TreeModel::Row row = *it;
713 instrument = row[m_Columns.m_col_instr];
714 }
715 return instrument;
716 }
717
718 void MainWindow::add_region_to_dimregs(gig::Region* region, bool stereo, bool all_dimregs)
719 {
720 if (all_dimregs) {
721 for (int i = 0 ; i < region->DimensionRegions ; i++) {
722 if (region->pDimensionRegions[i]) {
723 dimreg_edit.dimregs.insert(region->pDimensionRegions[i]);
724 }
725 }
726 } else {
727 m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
728 }
729 }
730
731 void MainWindow::update_dimregs()
732 {
733 dimreg_edit.dimregs.clear();
734 bool all_regions = dimreg_all_regions.get_active();
735 bool stereo = dimreg_stereo.get_active();
736 bool all_dimregs = dimreg_all_dimregs.get_active();
737
738 if (all_regions) {
739 gig::Instrument* instrument = get_instrument();
740 if (instrument) {
741 for (gig::Region* region = instrument->GetFirstRegion() ;
742 region ;
743 region = instrument->GetNextRegion()) {
744 add_region_to_dimregs(region, stereo, all_dimregs);
745 }
746 }
747 } else {
748 gig::Region* region = m_RegionChooser.get_region();
749 if (region) {
750 add_region_to_dimregs(region, stereo, all_dimregs);
751 }
752 }
753 }
754
755 void MainWindow::dimreg_all_dimregs_toggled()
756 {
757 dimreg_stereo.set_sensitive(!dimreg_all_dimregs.get_active());
758 update_dimregs();
759 }
760
761 void MainWindow::dimreg_changed()
762 {
763 update_dimregs();
764 dimreg_edit.set_dim_region(m_DimRegionChooser.get_main_dimregion());
765 }
766
767 void MainWindow::on_sel_change()
768 {
769 // select item in instrument menu
770 Gtk::TreeModel::iterator it = m_TreeView.get_selection()->get_selected();
771 if (it) {
772 Gtk::TreePath path(it);
773 int index = path[0];
774 const std::vector<Gtk::Widget*> children =
775 instrument_menu->get_children();
776 static_cast<Gtk::RadioMenuItem*>(children[index])->set_active();
777 }
778
779 m_RegionChooser.set_instrument(get_instrument());
780
781 if (Settings::singleton()->syncSamplerInstrumentSelection) {
782 switch_sampler_instrument_signal.emit(get_instrument());
783 }
784 }
785
786 void loader_progress_callback(gig::progress_t* progress)
787 {
788 Loader* loader = static_cast<Loader*>(progress->custom);
789 loader->progress_callback(progress->factor);
790 }
791
792 void Loader::progress_callback(float fraction)
793 {
794 {
795 Glib::Threads::Mutex::Lock lock(progressMutex);
796 progress = fraction;
797 }
798 progress_dispatcher();
799 }
800
801 void Loader::thread_function()
802 {
803 printf("thread_function self=%p\n",
804 static_cast<void*>(Glib::Threads::Thread::self()));
805 printf("Start %s\n", filename.c_str());
806 try {
807 RIFF::File* riff = new RIFF::File(filename);
808 gig = new gig::File(riff);
809 gig::progress_t progress;
810 progress.callback = loader_progress_callback;
811 progress.custom = this;
812
813 gig->GetInstrument(0, &progress);
814 printf("End\n");
815 finished_dispatcher();
816 } catch (RIFF::Exception e) {
817 error_message = e.Message;
818 error_dispatcher.emit();
819 } catch (...) {
820 error_message = _("Unknown exception occurred");
821 error_dispatcher.emit();
822 }
823 }
824
825 Loader::Loader(const char* filename)
826 : filename(filename), gig(0), thread(0), progress(0.f)
827 {
828 }
829
830 void Loader::launch()
831 {
832 #ifdef OLD_THREADS
833 thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true);
834 #else
835 thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Loader::thread_function));
836 #endif
837 printf("launch thread=%p\n", static_cast<void*>(thread));
838 }
839
840 float Loader::get_progress()
841 {
842 float res;
843 {
844 Glib::Threads::Mutex::Lock lock(progressMutex);
845 res = progress;
846 }
847 return res;
848 }
849
850 Glib::Dispatcher& Loader::signal_progress()
851 {
852 return progress_dispatcher;
853 }
854
855 Glib::Dispatcher& Loader::signal_finished()
856 {
857 return finished_dispatcher;
858 }
859
860 Glib::Dispatcher& Loader::signal_error()
861 {
862 return error_dispatcher;
863 }
864
865 void saver_progress_callback(gig::progress_t* progress)
866 {
867 Saver* saver = static_cast<Saver*>(progress->custom);
868 saver->progress_callback(progress->factor);
869 }
870
871 void Saver::progress_callback(float fraction)
872 {
873 {
874 Glib::Threads::Mutex::Lock lock(progressMutex);
875 progress = fraction;
876 }
877 progress_dispatcher.emit();
878 }
879
880 void Saver::thread_function()
881 {
882 printf("thread_function self=%p\n",
883 static_cast<void*>(Glib::Threads::Thread::self()));
884 printf("Start %s\n", filename.c_str());
885 try {
886 gig::progress_t progress;
887 progress.callback = saver_progress_callback;
888 progress.custom = this;
889
890 // if no filename was provided, that means "save", if filename was provided means "save as"
891 if (filename.empty()) {
892 gig->Save(&progress);
893 } else {
894 gig->Save(filename, &progress);
895 }
896
897 printf("End\n");
898 finished_dispatcher.emit();
899 } catch (RIFF::Exception e) {
900 error_message = e.Message;
901 error_dispatcher.emit();
902 } catch (...) {
903 error_message = _("Unknown exception occurred");
904 error_dispatcher.emit();
905 }
906 }
907
908 Saver::Saver(gig::File* file, Glib::ustring filename)
909 : gig(file), filename(filename), thread(0), progress(0.f)
910 {
911 }
912
913 void Saver::launch()
914 {
915 #ifdef OLD_THREADS
916 thread = Glib::Thread::create(sigc::mem_fun(*this, &Saver::thread_function), true);
917 #else
918 thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Saver::thread_function));
919 #endif
920 printf("launch thread=%p\n", static_cast<void*>(thread));
921 }
922
923 float Saver::get_progress()
924 {
925 float res;
926 {
927 Glib::Threads::Mutex::Lock lock(progressMutex);
928 res = progress;
929 }
930 return res;
931 }
932
933 Glib::Dispatcher& Saver::signal_progress()
934 {
935 return progress_dispatcher;
936 }
937
938 Glib::Dispatcher& Saver::signal_finished()
939 {
940 return finished_dispatcher;
941 }
942
943 Glib::Dispatcher& Saver::signal_error()
944 {
945 return error_dispatcher;
946 }
947
948 ProgressDialog::ProgressDialog(const Glib::ustring& title, Gtk::Window& parent)
949 : Gtk::Dialog(title, parent, true)
950 {
951 get_vbox()->pack_start(progressBar);
952 show_all_children();
953 resize(600,50);
954 }
955
956 // Clear all GUI elements / controls. This method is typically called
957 // before a new .gig file is to be created or to be loaded.
958 void MainWindow::__clear() {
959 // forget all samples that ought to be imported
960 m_SampleImportQueue.clear();
961 // clear the samples and instruments tree views
962 m_refTreeModel->clear();
963 m_refSamplesTreeModel->clear();
964 m_refScriptsTreeModel->clear();
965 // remove all entries from "Instrument" menu
966 while (!instrument_menu->get_children().empty()) {
967 remove_instrument_from_menu(0);
968 }
969 // free libgig's gig::File instance
970 if (file && !file_is_shared) delete file;
971 file = NULL;
972 set_file_is_shared(false);
973 }
974
975 void MainWindow::__refreshEntireGUI() {
976 // clear the samples and instruments tree views
977 m_refTreeModel->clear();
978 m_refSamplesTreeModel->clear();
979 m_refScriptsTreeModel->clear();
980 // remove all entries from "Instrument" menu
981 while (!instrument_menu->get_children().empty()) {
982 remove_instrument_from_menu(0);
983 }
984
985 if (!this->file) return;
986
987 load_gig(
988 this->file, this->file->pInfo->Name.c_str(), this->file_is_shared
989 );
990 }
991
992 void MainWindow::on_action_file_new()
993 {
994 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
995
996 if (file_is_shared && !leaving_shared_mode_dialog()) return;
997
998 // clear all GUI elements
999 __clear();
1000 // create a new .gig file (virtually yet)
1001 gig::File* pFile = new gig::File;
1002 // already add one new instrument by default
1003 gig::Instrument* pInstrument = pFile->AddInstrument();
1004 pInstrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument"));
1005 // update GUI with that new gig::File
1006 load_gig(pFile, 0 /*no file name yet*/);
1007 }
1008
1009 bool MainWindow::close_confirmation_dialog()
1010 {
1011 gchar* msg = g_strdup_printf(_("Save changes to \"%s\" before closing?"),
1012 Glib::filename_display_basename(filename).c_str());
1013 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
1014 g_free(msg);
1015 dialog.set_secondary_text(_("If you close without saving, your changes will be lost."));
1016 dialog.add_button(_("Close _Without Saving"), Gtk::RESPONSE_NO);
1017 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1018 dialog.add_button(file_has_name ? _("_Save") : _("Save _As"), Gtk::RESPONSE_YES);
1019 dialog.set_default_response(Gtk::RESPONSE_YES);
1020 int response = dialog.run();
1021 dialog.hide();
1022
1023 // user decided to exit app without saving
1024 if (response == Gtk::RESPONSE_NO) return true;
1025
1026 // user cancelled dialog, thus don't close app
1027 if (response == Gtk::RESPONSE_CANCEL) return false;
1028
1029 // TODO: the following return valid is disabled and hard coded instead for
1030 // now, due to the fact that saving with progress bar is now implemented
1031 // asynchronously, as a result the app does not close automatically anymore
1032 // after saving the file has completed
1033 //
1034 // if (response == Gtk::RESPONSE_YES) return file_save();
1035 // return response != Gtk::RESPONSE_CANCEL;
1036 //
1037 if (response == Gtk::RESPONSE_YES) file_save();
1038 return false; // always prevent closing the app for now (see comment above)
1039 }
1040
1041 bool MainWindow::leaving_shared_mode_dialog() {
1042 Glib::ustring msg = _("Detach from sampler and proceed working stand-alone?");
1043 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
1044 dialog.set_secondary_text(
1045 _("If you proceed to work on another instrument file, it won't be "
1046 "used by the sampler until you tell the sampler explicitly to "
1047 "load it."));
1048 dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
1049 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1050 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
1051 int response = dialog.run();
1052 dialog.hide();
1053 return response == Gtk::RESPONSE_YES;
1054 }
1055
1056 void MainWindow::on_action_file_open()
1057 {
1058 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
1059
1060 if (file_is_shared && !leaving_shared_mode_dialog()) return;
1061
1062 Gtk::FileChooserDialog dialog(*this, _("Open file"));
1063 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1064 dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
1065 dialog.set_default_response(Gtk::RESPONSE_OK);
1066 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1067 Gtk::FileFilter filter;
1068 filter.add_pattern("*.gig");
1069 #else
1070 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
1071 filter->add_pattern("*.gig");
1072 #endif
1073 dialog.set_filter(filter);
1074 if (current_gig_dir != "") {
1075 dialog.set_current_folder(current_gig_dir);
1076 }
1077 if (dialog.run() == Gtk::RESPONSE_OK) {
1078 std::string filename = dialog.get_filename();
1079 printf("filename=%s\n", filename.c_str());
1080 printf("on_action_file_open self=%p\n",
1081 static_cast<void*>(Glib::Threads::Thread::self()));
1082 load_file(filename.c_str());
1083 current_gig_dir = Glib::path_get_dirname(filename);
1084 }
1085 }
1086
1087 void MainWindow::load_file(const char* name)
1088 {
1089 __clear();
1090
1091 progress_dialog = new ProgressDialog( //FIXME: memory leak!
1092 _("Loading") + Glib::ustring(" '") +
1093 Glib::filename_display_basename(name) + "' ...",
1094 *this
1095 );
1096 progress_dialog->show_all();
1097 loader = new Loader(name); //FIXME: memory leak!
1098 loader->signal_progress().connect(
1099 sigc::mem_fun(*this, &MainWindow::on_loader_progress));
1100 loader->signal_finished().connect(
1101 sigc::mem_fun(*this, &MainWindow::on_loader_finished));
1102 loader->signal_error().connect(
1103 sigc::mem_fun(*this, &MainWindow::on_loader_error));
1104 loader->launch();
1105 }
1106
1107 void MainWindow::load_instrument(gig::Instrument* instr) {
1108 if (!instr) {
1109 Glib::ustring txt = "Provided instrument is NULL!\n";
1110 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1111 msg.run();
1112 Gtk::Main::quit();
1113 }
1114 // clear all GUI elements
1115 __clear();
1116 // load the instrument
1117 gig::File* pFile = (gig::File*) instr->GetParent();
1118 load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
1119 // automatically select the given instrument
1120 int i = 0;
1121 for (gig::Instrument* instrument = pFile->GetFirstInstrument(); instrument;
1122 instrument = pFile->GetNextInstrument(), ++i)
1123 {
1124 if (instrument == instr) {
1125 // select item in "instruments" tree view
1126 m_TreeView.get_selection()->select(Gtk::TreePath(ToString(i)));
1127 // make sure the selected item in the "instruments" tree view is
1128 // visible (scroll to it)
1129 m_TreeView.scroll_to_row(Gtk::TreePath(ToString(i)));
1130 // select item in instrument menu
1131 {
1132 const std::vector<Gtk::Widget*> children =
1133 instrument_menu->get_children();
1134 static_cast<Gtk::RadioMenuItem*>(children[i])->set_active();
1135 }
1136 // update region chooser and dimension region chooser
1137 m_RegionChooser.set_instrument(instr);
1138 break;
1139 }
1140 }
1141 }
1142
1143 void MainWindow::on_loader_progress()
1144 {
1145 progress_dialog->set_fraction(loader->get_progress());
1146 }
1147
1148 void MainWindow::on_loader_finished()
1149 {
1150 printf("Loader finished!\n");
1151 printf("on_loader_finished self=%p\n",
1152 static_cast<void*>(Glib::Threads::Thread::self()));
1153 load_gig(loader->gig, loader->filename.c_str());
1154 progress_dialog->hide();
1155 }
1156
1157 void MainWindow::on_loader_error()
1158 {
1159 Glib::ustring txt = _("Could not load file: ") + loader->error_message;
1160 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1161 msg.run();
1162 progress_dialog->hide();
1163 }
1164
1165 void MainWindow::on_action_file_save()
1166 {
1167 file_save();
1168 }
1169
1170 bool MainWindow::check_if_savable()
1171 {
1172 if (!file) return false;
1173
1174 if (!file->GetFirstSample()) {
1175 Gtk::MessageDialog(*this, _("The file could not be saved "
1176 "because it contains no samples"),
1177 false, Gtk::MESSAGE_ERROR).run();
1178 return false;
1179 }
1180
1181 for (gig::Instrument* instrument = file->GetFirstInstrument() ; instrument ;
1182 instrument = file->GetNextInstrument()) {
1183 if (!instrument->GetFirstRegion()) {
1184 Gtk::MessageDialog(*this, _("The file could not be saved "
1185 "because there are instruments "
1186 "that have no regions"),
1187 false, Gtk::MESSAGE_ERROR).run();
1188 return false;
1189 }
1190 }
1191 return true;
1192 }
1193
1194 bool MainWindow::file_save()
1195 {
1196 if (!check_if_savable()) return false;
1197 if (!file_is_shared && !file_has_name) return file_save_as();
1198
1199 std::cout << "Saving file\n" << std::flush;
1200 file_structure_to_be_changed_signal.emit(this->file);
1201
1202 progress_dialog = new ProgressDialog( //FIXME: memory leak!
1203 _("Saving") + Glib::ustring(" '") +
1204 Glib::filename_display_basename(this->filename) + "' ...",
1205 *this
1206 );
1207 progress_dialog->show_all();
1208 saver = new Saver(this->file); //FIXME: memory leak!
1209 saver->signal_progress().connect(
1210 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
1211 saver->signal_finished().connect(
1212 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
1213 saver->signal_error().connect(
1214 sigc::mem_fun(*this, &MainWindow::on_saver_error));
1215 saver->launch();
1216
1217 return true;
1218 }
1219
1220 void MainWindow::on_saver_progress()
1221 {
1222 progress_dialog->set_fraction(saver->get_progress());
1223 }
1224
1225 void MainWindow::on_saver_error()
1226 {
1227 file_structure_changed_signal.emit(this->file);
1228 Glib::ustring txt = _("Could not save file: ") + saver->error_message;
1229 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1230 msg.run();
1231 }
1232
1233 void MainWindow::on_saver_finished()
1234 {
1235 this->file = saver->gig;
1236 this->filename = saver->filename;
1237 current_gig_dir = Glib::path_get_dirname(filename);
1238 set_title(Glib::filename_display_basename(filename));
1239 file_has_name = true;
1240 file_is_changed = false;
1241 std::cout << "Saving file done. Importing queued samples now ...\n" << std::flush;
1242 __import_queued_samples();
1243 std::cout << "Importing queued samples done.\n" << std::flush;
1244
1245 file_structure_changed_signal.emit(this->file);
1246
1247 __refreshEntireGUI();
1248 progress_dialog->hide();
1249 }
1250
1251 void MainWindow::on_action_file_save_as()
1252 {
1253 if (!check_if_savable()) return;
1254 file_save_as();
1255 }
1256
1257 bool MainWindow::file_save_as()
1258 {
1259 Gtk::FileChooserDialog dialog(*this, _("Save As"), Gtk::FILE_CHOOSER_ACTION_SAVE);
1260 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1261 dialog.add_button(_("_Save"), Gtk::RESPONSE_OK);
1262 dialog.set_default_response(Gtk::RESPONSE_OK);
1263 dialog.set_do_overwrite_confirmation();
1264
1265 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1266 Gtk::FileFilter filter;
1267 filter.add_pattern("*.gig");
1268 #else
1269 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
1270 filter->add_pattern("*.gig");
1271 #endif
1272 dialog.set_filter(filter);
1273
1274 // set initial dir and filename of the Save As dialog
1275 // and prepare that initial filename as a copy of the gig
1276 {
1277 std::string basename = Glib::path_get_basename(filename);
1278 std::string dir = Glib::path_get_dirname(filename);
1279 basename = std::string(_("copy_of_")) + basename;
1280 Glib::ustring copyFileName = Glib::build_filename(dir, basename);
1281 if (Glib::path_is_absolute(filename)) {
1282 dialog.set_filename(copyFileName);
1283 } else {
1284 if (current_gig_dir != "") dialog.set_current_folder(current_gig_dir);
1285 }
1286 dialog.set_current_name(Glib::filename_display_basename(copyFileName));
1287 }
1288
1289 // show warning in the dialog
1290 Gtk::HBox descriptionArea;
1291 descriptionArea.set_spacing(15);
1292 Gtk::Image warningIcon;
1293 warningIcon.set_from_icon_name("dialog-warning",
1294 Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
1295 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
1296 #if GTKMM_MAJOR_VERSION < 3
1297 view::WrapLabel description;
1298 #else
1299 Gtk::Label description;
1300 description.set_line_wrap();
1301 #endif
1302 description.set_markup(
1303 _("\n<b>CAUTION:</b> You <b>MUST</b> use the "
1304 "<span style=\"italic\">\"Save\"</span> dialog instead of "
1305 "<span style=\"italic\">\"Save As...\"</span> if you want to save "
1306 "to the same .gig file. Using "
1307 "<span style=\"italic\">\"Save As...\"</span> for writing to the "
1308 "same .gig file will end up in corrupted sample wave data!\n")
1309 );
1310 descriptionArea.pack_start(description);
1311 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
1312 descriptionArea.show_all();
1313
1314 if (dialog.run() == Gtk::RESPONSE_OK) {
1315 std::string filename = dialog.get_filename();
1316 if (!Glib::str_has_suffix(filename, ".gig")) {
1317 filename += ".gig";
1318 }
1319 printf("filename=%s\n", filename.c_str());
1320
1321 progress_dialog = new ProgressDialog( //FIXME: memory leak!
1322 _("Saving") + Glib::ustring(" '") +
1323 Glib::filename_display_basename(filename) + "' ...",
1324 *this
1325 );
1326 progress_dialog->show_all();
1327
1328 saver = new Saver(file, filename); //FIXME: memory leak!
1329 saver->signal_progress().connect(
1330 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
1331 saver->signal_finished().connect(
1332 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
1333 saver->signal_error().connect(
1334 sigc::mem_fun(*this, &MainWindow::on_saver_error));
1335 saver->launch();
1336
1337 return true;
1338 }
1339 return false;
1340 }
1341
1342 // actually write the sample(s)' data to the gig file
1343 void MainWindow::__import_queued_samples() {
1344 std::cout << "Starting sample import\n" << std::flush;
1345 Glib::ustring error_files;
1346 printf("Samples to import: %d\n", int(m_SampleImportQueue.size()));
1347 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1348 iter != m_SampleImportQueue.end(); ) {
1349 printf("Importing sample %s\n",(*iter).sample_path.c_str());
1350 SF_INFO info;
1351 info.format = 0;
1352 SNDFILE* hFile = sf_open((*iter).sample_path.c_str(), SFM_READ, &info);
1353 sf_command(hFile, SFC_SET_SCALE_FLOAT_INT_READ, 0, SF_TRUE);
1354 try {
1355 if (!hFile) throw std::string(_("could not open file"));
1356 // determine sample's bit depth
1357 int bitdepth;
1358 switch (info.format & 0xff) {
1359 case SF_FORMAT_PCM_S8:
1360 case SF_FORMAT_PCM_16:
1361 case SF_FORMAT_PCM_U8:
1362 bitdepth = 16;
1363 break;
1364 case SF_FORMAT_PCM_24:
1365 case SF_FORMAT_PCM_32:
1366 case SF_FORMAT_FLOAT:
1367 case SF_FORMAT_DOUBLE:
1368 bitdepth = 24;
1369 break;
1370 default:
1371 sf_close(hFile); // close sound file
1372 throw std::string(_("format not supported")); // unsupported subformat (yet?)
1373 }
1374
1375 const int bufsize = 10000;
1376 switch (bitdepth) {
1377 case 16: {
1378 short* buffer = new short[bufsize * info.channels];
1379 sf_count_t cnt = info.frames;
1380 while (cnt) {
1381 // libsndfile does the conversion for us (if needed)
1382 int n = sf_readf_short(hFile, buffer, bufsize);
1383 // write from buffer directly (physically) into .gig file
1384 iter->gig_sample->Write(buffer, n);
1385 cnt -= n;
1386 }
1387 delete[] buffer;
1388 break;
1389 }
1390 case 24: {
1391 int* srcbuf = new int[bufsize * info.channels];
1392 uint8_t* dstbuf = new uint8_t[bufsize * 3 * info.channels];
1393 sf_count_t cnt = info.frames;
1394 while (cnt) {
1395 // libsndfile returns 32 bits, convert to 24
1396 int n = sf_readf_int(hFile, srcbuf, bufsize);
1397 int j = 0;
1398 for (int i = 0 ; i < n * info.channels ; i++) {
1399 dstbuf[j++] = srcbuf[i] >> 8;
1400 dstbuf[j++] = srcbuf[i] >> 16;
1401 dstbuf[j++] = srcbuf[i] >> 24;
1402 }
1403 // write from buffer directly (physically) into .gig file
1404 iter->gig_sample->Write(dstbuf, n);
1405 cnt -= n;
1406 }
1407 delete[] srcbuf;
1408 delete[] dstbuf;
1409 break;
1410 }
1411 }
1412 // cleanup
1413 sf_close(hFile);
1414 // let the sampler re-cache the sample if needed
1415 sample_changed_signal.emit(iter->gig_sample);
1416 // on success we remove the sample from the import queue,
1417 // otherwise keep it, maybe it works the next time ?
1418 std::list<SampleImportItem>::iterator cur = iter;
1419 ++iter;
1420 m_SampleImportQueue.erase(cur);
1421 } catch (std::string what) {
1422 // remember the files that made trouble (and their cause)
1423 if (!error_files.empty()) error_files += "\n";
1424 error_files += (*iter).sample_path += " (" + what + ")";
1425 ++iter;
1426 }
1427 }
1428 // show error message box when some sample(s) could not be imported
1429 if (!error_files.empty()) {
1430 Glib::ustring txt = _("Could not import the following sample(s):\n") + error_files;
1431 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1432 msg.run();
1433 }
1434 }
1435
1436 void MainWindow::on_action_file_properties()
1437 {
1438 propDialog.show();
1439 propDialog.deiconify();
1440 }
1441
1442 void MainWindow::on_action_warn_user_on_extensions() {
1443 Settings::singleton()->warnUserOnExtensions =
1444 !Settings::singleton()->warnUserOnExtensions;
1445 }
1446
1447 void MainWindow::on_action_sync_sampler_instrument_selection() {
1448 Settings::singleton()->syncSamplerInstrumentSelection =
1449 !Settings::singleton()->syncSamplerInstrumentSelection;
1450 }
1451
1452 void MainWindow::on_action_move_root_note_with_region_moved() {
1453 Settings::singleton()->moveRootNoteWithRegionMoved =
1454 !Settings::singleton()->moveRootNoteWithRegionMoved;
1455 }
1456
1457 void MainWindow::on_action_help_about()
1458 {
1459 Gtk::AboutDialog dialog;
1460 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 12) || GTKMM_MAJOR_VERSION > 2
1461 dialog.set_program_name("Gigedit");
1462 #else
1463 dialog.set_name("Gigedit");
1464 #endif
1465 dialog.set_version(VERSION);
1466 dialog.set_copyright("Copyright (C) 2006-2016 Andreas Persson");
1467 const std::string sComment =
1468 _("Built " __DATE__ "\nUsing ") +
1469 ::gig::libraryName() + " " + ::gig::libraryVersion() + "\n\n" +
1470 _(
1471 "Gigedit is released under the GNU General Public License.\n"
1472 "\n"
1473 "This program is distributed WITHOUT ANY WARRANTY; So better "
1474 "backup your Gigasampler/GigaStudio files before editing them with "
1475 "this application.\n"
1476 "\n"
1477 "Please report bugs to: http://bugs.linuxsampler.org"
1478 );
1479 dialog.set_comments(sComment.c_str());
1480 dialog.set_website("http://www.linuxsampler.org");
1481 dialog.set_website_label("http://www.linuxsampler.org");
1482 dialog.run();
1483 }
1484
1485 PropDialog::PropDialog()
1486 : eFileFormat(_("File Format")),
1487 eName(_("Name")),
1488 eCreationDate(_("Creation date")),
1489 eComments(_("Comments")),
1490 eProduct(_("Product")),
1491 eCopyright(_("Copyright")),
1492 eArtists(_("Artists")),
1493 eGenre(_("Genre")),
1494 eKeywords(_("Keywords")),
1495 eEngineer(_("Engineer")),
1496 eTechnician(_("Technician")),
1497 eSoftware(_("Software")),
1498 eMedium(_("Medium")),
1499 eSource(_("Source")),
1500 eSourceForm(_("Source form")),
1501 eCommissioned(_("Commissioned")),
1502 eSubject(_("Subject")),
1503 quitButton(_("_Close"), true),
1504 table(2, 1),
1505 m_file(NULL)
1506 {
1507 set_title(_("File Properties"));
1508 eName.set_width_chars(50);
1509
1510 connect(eName, &DLS::Info::Name);
1511 connect(eCreationDate, &DLS::Info::CreationDate);
1512 connect(eComments, &DLS::Info::Comments);
1513 connect(eProduct, &DLS::Info::Product);
1514 connect(eCopyright, &DLS::Info::Copyright);
1515 connect(eArtists, &DLS::Info::Artists);
1516 connect(eGenre, &DLS::Info::Genre);
1517 connect(eKeywords, &DLS::Info::Keywords);
1518 connect(eEngineer, &DLS::Info::Engineer);
1519 connect(eTechnician, &DLS::Info::Technician);
1520 connect(eSoftware, &DLS::Info::Software);
1521 connect(eMedium, &DLS::Info::Medium);
1522 connect(eSource, &DLS::Info::Source);
1523 connect(eSourceForm, &DLS::Info::SourceForm);
1524 connect(eCommissioned, &DLS::Info::Commissioned);
1525 connect(eSubject, &DLS::Info::Subject);
1526
1527 table.add(eFileFormat);
1528 table.add(eName);
1529 table.add(eCreationDate);
1530 table.add(eComments);
1531 table.add(eProduct);
1532 table.add(eCopyright);
1533 table.add(eArtists);
1534 table.add(eGenre);
1535 table.add(eKeywords);
1536 table.add(eEngineer);
1537 table.add(eTechnician);
1538 table.add(eSoftware);
1539 table.add(eMedium);
1540 table.add(eSource);
1541 table.add(eSourceForm);
1542 table.add(eCommissioned);
1543 table.add(eSubject);
1544
1545 table.set_col_spacings(5);
1546 add(vbox);
1547 table.set_border_width(5);
1548 vbox.add(table);
1549 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
1550 buttonBox.set_layout(Gtk::BUTTONBOX_END);
1551 buttonBox.set_border_width(5);
1552 buttonBox.show();
1553 buttonBox.pack_start(quitButton);
1554 quitButton.set_can_default();
1555 quitButton.grab_focus();
1556 quitButton.signal_clicked().connect(
1557 sigc::mem_fun(*this, &PropDialog::hide));
1558 eFileFormat.signal_value_changed().connect(
1559 sigc::mem_fun(*this, &PropDialog::onFileFormatChanged));
1560
1561 quitButton.show();
1562 vbox.show();
1563 show_all_children();
1564 }
1565
1566 void PropDialog::set_file(gig::File* file)
1567 {
1568 m_file = file;
1569
1570 // update file format version combo box
1571 const std::string sGiga = "Gigasampler/GigaStudio v";
1572 const int major = file->pVersion->major;
1573 std::vector<std::string> txts;
1574 std::vector<int> values;
1575 txts.push_back(sGiga + "2"); values.push_back(2);
1576 txts.push_back(sGiga + "3/v4"); values.push_back(3);
1577 if (major != 2 && major != 3) {
1578 txts.push_back(sGiga + ToString(major)); values.push_back(major);
1579 }
1580 std::vector<const char*> texts;
1581 for (int i = 0; i < txts.size(); ++i) texts.push_back(txts[i].c_str());
1582 texts.push_back(NULL); values.push_back(0);
1583 eFileFormat.set_choices(&texts[0], &values[0]);
1584 eFileFormat.set_value(major);
1585 }
1586
1587 void PropDialog::onFileFormatChanged() {
1588 const int major = eFileFormat.get_value();
1589 if (m_file) m_file->pVersion->major = major;
1590 }
1591
1592 void PropDialog::set_info(DLS::Info* info)
1593 {
1594 update(info);
1595 }
1596
1597
1598 void InstrumentProps::set_Name(const gig::String& name)
1599 {
1600 m->pInfo->Name = name;
1601 }
1602
1603 void InstrumentProps::update_name()
1604 {
1605 update_model++;
1606 eName.set_value(m->pInfo->Name);
1607 update_model--;
1608 }
1609
1610 void InstrumentProps::set_IsDrum(bool value)
1611 {
1612 m->IsDrum = value;
1613 }
1614
1615 void InstrumentProps::set_MIDIBank(uint16_t value)
1616 {
1617 m->MIDIBank = value;
1618 }
1619
1620 void InstrumentProps::set_MIDIProgram(uint32_t value)
1621 {
1622 m->MIDIProgram = value;
1623 }
1624
1625 InstrumentProps::InstrumentProps() :
1626 quitButton(_("_Close"), true),
1627 table(2,1),
1628 eName(_("Name")),
1629 eIsDrum(_("Is drum")),
1630 eMIDIBank(_("MIDI bank"), 0, 16383),
1631 eMIDIProgram(_("MIDI program")),
1632 eAttenuation(_("Attenuation"), 0, 96, 0, 1),
1633 eGainPlus6(_("Gain +6dB"), eAttenuation, -6),
1634 eEffectSend(_("Effect send"), 0, 65535),
1635 eFineTune(_("Fine tune"), -8400, 8400),
1636 ePitchbendRange(_("Pitchbend range"), 0, 12),
1637 ePianoReleaseMode(_("Piano release mode")),
1638 eDimensionKeyRangeLow(_("Keyswitching range low")),
1639 eDimensionKeyRangeHigh(_("Keyswitching range high"))
1640 {
1641 set_title(_("Instrument Properties"));
1642
1643 eDimensionKeyRangeLow.set_tip(
1644 _("start of the keyboard area which should switch the "
1645 "\"keyswitching\" dimension")
1646 );
1647 eDimensionKeyRangeHigh.set_tip(
1648 _("end of the keyboard area which should switch the "
1649 "\"keyswitching\" dimension")
1650 );
1651
1652 connect(eName, &InstrumentProps::set_Name);
1653 connect(eIsDrum, &InstrumentProps::set_IsDrum);
1654 connect(eMIDIBank, &InstrumentProps::set_MIDIBank);
1655 connect(eMIDIProgram, &InstrumentProps::set_MIDIProgram);
1656 connect(eAttenuation, &gig::Instrument::Attenuation);
1657 connect(eGainPlus6, &gig::Instrument::Attenuation);
1658 connect(eEffectSend, &gig::Instrument::EffectSend);
1659 connect(eFineTune, &gig::Instrument::FineTune);
1660 connect(ePitchbendRange, &gig::Instrument::PitchbendRange);
1661 connect(ePianoReleaseMode, &gig::Instrument::PianoReleaseMode);
1662 connect(eDimensionKeyRangeLow, eDimensionKeyRangeHigh,
1663 &gig::Instrument::DimensionKeyRange);
1664
1665 eName.signal_value_changed().connect(sig_name_changed.make_slot());
1666
1667 table.set_col_spacings(5);
1668
1669 table.add(eName);
1670 table.add(eIsDrum);
1671 table.add(eMIDIBank);
1672 table.add(eMIDIProgram);
1673 table.add(eAttenuation);
1674 table.add(eGainPlus6);
1675 table.add(eEffectSend);
1676 table.add(eFineTune);
1677 table.add(ePitchbendRange);
1678 table.add(ePianoReleaseMode);
1679 table.add(eDimensionKeyRangeLow);
1680 table.add(eDimensionKeyRangeHigh);
1681
1682 add(vbox);
1683 table.set_border_width(5);
1684 vbox.pack_start(table);
1685 table.show();
1686 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
1687 buttonBox.set_layout(Gtk::BUTTONBOX_END);
1688 buttonBox.set_border_width(5);
1689 buttonBox.show();
1690 buttonBox.pack_start(quitButton);
1691 quitButton.set_can_default();
1692 quitButton.grab_focus();
1693
1694 quitButton.signal_clicked().connect(
1695 sigc::mem_fun(*this, &InstrumentProps::hide));
1696
1697 quitButton.show();
1698 vbox.show();
1699 show_all_children();
1700 }
1701
1702 void InstrumentProps::set_instrument(gig::Instrument* instrument)
1703 {
1704 update(instrument);
1705
1706 update_model++;
1707 eName.set_value(instrument->pInfo->Name);
1708 eIsDrum.set_value(instrument->IsDrum);
1709 eMIDIBank.set_value(instrument->MIDIBank);
1710 eMIDIProgram.set_value(instrument->MIDIProgram);
1711 update_model--;
1712 }
1713
1714
1715 void MainWindow::file_changed()
1716 {
1717 if (file && !file_is_changed) {
1718 set_title("*" + get_title());
1719 file_is_changed = true;
1720 }
1721 }
1722
1723 void MainWindow::updateSampleRefCountMap(gig::File* gig) {
1724 sample_ref_count.clear();
1725
1726 if (!gig) return;
1727
1728 for (gig::Instrument* instrument = gig->GetFirstInstrument(); instrument;
1729 instrument = gig->GetNextInstrument())
1730 {
1731 for (gig::Region* rgn = instrument->GetFirstRegion(); rgn;
1732 rgn = instrument->GetNextRegion())
1733 {
1734 for (int i = 0; i < 256; ++i) {
1735 if (!rgn->pDimensionRegions[i]) continue;
1736 if (rgn->pDimensionRegions[i]->pSample) {
1737 sample_ref_count[rgn->pDimensionRegions[i]->pSample]++;
1738 }
1739 }
1740 }
1741 }
1742 }
1743
1744 void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument)
1745 {
1746 file = 0;
1747 set_file_is_shared(isSharedInstrument);
1748
1749 this->filename =
1750 (filename && strlen(filename) > 0) ?
1751 filename : (!gig->GetFileName().empty()) ?
1752 gig->GetFileName() : _("Unsaved Gig File");
1753 set_title(Glib::filename_display_basename(this->filename));
1754 file_has_name = filename;
1755 file_is_changed = false;
1756
1757 propDialog.set_file(gig);
1758 propDialog.set_info(gig->pInfo);
1759
1760 instrument_name_connection.block();
1761 for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ;
1762 instrument = gig->GetNextInstrument()) {
1763 Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
1764
1765 Gtk::TreeModel::iterator iter = m_refTreeModel->append();
1766 Gtk::TreeModel::Row row = *iter;
1767 row[m_Columns.m_col_name] = name;
1768 row[m_Columns.m_col_instr] = instrument;
1769
1770 add_instrument_to_menu(name);
1771 }
1772 instrument_name_connection.unblock();
1773 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments")->show();
1774
1775 updateSampleRefCountMap(gig);
1776
1777 for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) {
1778 if (group->Name != "") {
1779 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
1780 Gtk::TreeModel::Row rowGroup = *iterGroup;
1781 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
1782 rowGroup[m_SamplesModel.m_col_group] = group;
1783 rowGroup[m_SamplesModel.m_col_sample] = NULL;
1784 for (gig::Sample* sample = group->GetFirstSample();
1785 sample; sample = group->GetNextSample()) {
1786 Gtk::TreeModel::iterator iterSample =
1787 m_refSamplesTreeModel->append(rowGroup.children());
1788 Gtk::TreeModel::Row rowSample = *iterSample;
1789 rowSample[m_SamplesModel.m_col_name] =
1790 gig_to_utf8(sample->pInfo->Name);
1791 rowSample[m_SamplesModel.m_col_sample] = sample;
1792 rowSample[m_SamplesModel.m_col_group] = NULL;
1793 int refcount = sample_ref_count.count(sample) ? sample_ref_count[sample] : 0;
1794 rowSample[m_SamplesModel.m_col_refcount] = ToString(refcount) + " " + _("Refs.");
1795 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
1796 }
1797 }
1798 }
1799
1800 for (int i = 0; gig->GetScriptGroup(i); ++i) {
1801 gig::ScriptGroup* group = gig->GetScriptGroup(i);
1802
1803 Gtk::TreeModel::iterator iterGroup = m_refScriptsTreeModel->append();
1804 Gtk::TreeModel::Row rowGroup = *iterGroup;
1805 rowGroup[m_ScriptsModel.m_col_name] = gig_to_utf8(group->Name);
1806 rowGroup[m_ScriptsModel.m_col_group] = group;
1807 rowGroup[m_ScriptsModel.m_col_script] = NULL;
1808 for (int s = 0; group->GetScript(s); ++s) {
1809 gig::Script* script = group->GetScript(s);
1810
1811 Gtk::TreeModel::iterator iterScript =
1812 m_refScriptsTreeModel->append(rowGroup.children());
1813 Gtk::TreeModel::Row rowScript = *iterScript;
1814 rowScript[m_ScriptsModel.m_col_name] = gig_to_utf8(script->Name);
1815 rowScript[m_ScriptsModel.m_col_script] = script;
1816 rowScript[m_ScriptsModel.m_col_group] = NULL;
1817 }
1818 }
1819 // unfold all sample groups & script groups by default
1820 m_TreeViewSamples.expand_all();
1821 m_TreeViewScripts.expand_all();
1822
1823 file = gig;
1824
1825 // select the first instrument
1826 m_TreeView.get_selection()->select(Gtk::TreePath("0"));
1827
1828 instr_props_set_instrument();
1829 gig::Instrument* instrument = get_instrument();
1830 if (instrument) {
1831 midiRules.set_instrument(instrument);
1832 }
1833 }
1834
1835 bool MainWindow::instr_props_set_instrument()
1836 {
1837 instrumentProps.signal_name_changed().clear();
1838
1839 Gtk::TreeModel::const_iterator it =
1840 m_TreeView.get_selection()->get_selected();
1841 if (it) {
1842 Gtk::TreeModel::Row row = *it;
1843 gig::Instrument* instrument = row[m_Columns.m_col_instr];
1844
1845 instrumentProps.set_instrument(instrument);
1846
1847 // make sure instrument tree is updated when user changes the
1848 // instrument name in instrument properties window
1849 instrumentProps.signal_name_changed().connect(
1850 sigc::bind(
1851 sigc::mem_fun(*this,
1852 &MainWindow::instr_name_changed_by_instr_props),
1853 it));
1854 } else {
1855 instrumentProps.hide();
1856 }
1857 return it;
1858 }
1859
1860 void MainWindow::show_instr_props()
1861 {
1862 if (instr_props_set_instrument()) {
1863 instrumentProps.show();
1864 instrumentProps.deiconify();
1865 }
1866 }
1867
1868 void MainWindow::instr_name_changed_by_instr_props(Gtk::TreeModel::iterator& it)
1869 {
1870 Gtk::TreeModel::Row row = *it;
1871 Glib::ustring name = row[m_Columns.m_col_name];
1872
1873 gig::Instrument* instrument = row[m_Columns.m_col_instr];
1874 Glib::ustring gigname(gig_to_utf8(instrument->pInfo->Name));
1875 if (gigname != name) {
1876 row[m_Columns.m_col_name] = gigname;
1877 }
1878 }
1879
1880 void MainWindow::show_midi_rules()
1881 {
1882 if (gig::Instrument* instrument = get_instrument())
1883 {
1884 midiRules.set_instrument(instrument);
1885 midiRules.show();
1886 midiRules.deiconify();
1887 }
1888 }
1889
1890 void MainWindow::show_script_slots() {
1891 if (!file) return;
1892 // get selected instrument
1893 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
1894 Gtk::TreeModel::iterator it = sel->get_selected();
1895 if (!it) return;
1896 Gtk::TreeModel::Row row = *it;
1897 gig::Instrument* instrument = row[m_Columns.m_col_instr];
1898 if (!instrument) return;
1899
1900 ScriptSlots* window = new ScriptSlots;
1901 window->setInstrument(instrument);
1902 //window->reparent(*this);
1903 window->show();
1904 }
1905
1906 void MainWindow::on_action_refresh_all() {
1907 __refreshEntireGUI();
1908 }
1909
1910 void MainWindow::on_action_view_status_bar() {
1911 Gtk::CheckMenuItem* item =
1912 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/Statusbar"));
1913 if (!item) {
1914 std::cerr << "/MenuBar/MenuView/Statusbar == NULL\n";
1915 return;
1916 }
1917 if (item->get_active()) m_StatusBar.show();
1918 else m_StatusBar.hide();
1919 }
1920
1921 bool MainWindow::is_copy_samples_unity_note_enabled() const {
1922 Gtk::CheckMenuItem* item =
1923 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleUnity"));
1924 if (!item) {
1925 std::cerr << "/MenuBar/MenuEdit/CopySampleUnity == NULL\n";
1926 return true;
1927 }
1928 return item->get_active();
1929 }
1930
1931 bool MainWindow::is_copy_samples_fine_tune_enabled() const {
1932 Gtk::CheckMenuItem* item =
1933 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleTune"));
1934 if (!item) {
1935 std::cerr << "/MenuBar/MenuEdit/CopySampleTune == NULL\n";
1936 return true;
1937 }
1938 return item->get_active();
1939 }
1940
1941 bool MainWindow::is_copy_samples_loop_enabled() const {
1942 Gtk::CheckMenuItem* item =
1943 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleLoop"));
1944 if (!item) {
1945 std::cerr << "/MenuBar/MenuEdit/CopySampleLoop == NULL\n";
1946 return true;
1947 }
1948 return item->get_active();
1949 }
1950
1951 void MainWindow::on_button_release(GdkEventButton* button)
1952 {
1953 if (button->type == GDK_2BUTTON_PRESS) {
1954 show_instr_props();
1955 } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
1956 // gig v2 files have no midi rules
1957 const bool bEnabled = !(file->pVersion && file->pVersion->major == 2);
1958 static_cast<Gtk::MenuItem*>(
1959 uiManager->get_widget("/MenuBar/MenuInstrument/MidiRules"))->set_sensitive(
1960 bEnabled
1961 );
1962 static_cast<Gtk::MenuItem*>(
1963 uiManager->get_widget("/PopupMenu/MidiRules"))->set_sensitive(
1964 bEnabled
1965 );
1966 popup_menu->popup(button->button, button->time);
1967 }
1968 }
1969
1970 void MainWindow::on_instrument_selection_change(Gtk::RadioMenuItem* item) {
1971 if (item->get_active()) {
1972 const std::vector<Gtk::Widget*> children =
1973 instrument_menu->get_children();
1974 std::vector<Gtk::Widget*>::const_iterator it =
1975 find(children.begin(), children.end(), item);
1976 if (it != children.end()) {
1977 int index = it - children.begin();
1978 m_TreeView.get_selection()->select(Gtk::TreePath(ToString(index)));
1979
1980 m_RegionChooser.set_instrument(file->GetInstrument(index));
1981 }
1982 }
1983 }
1984
1985 void MainWindow::select_instrument(gig::Instrument* instrument) {
1986 if (!instrument) return;
1987
1988 Glib::RefPtr<Gtk::TreeModel> model = m_TreeView.get_model();
1989 for (int i = 0; i < model->children().size(); ++i) {
1990 Gtk::TreeModel::Row row = model->children()[i];
1991 if (row[m_Columns.m_col_instr] == instrument) {
1992 // select and show the respective instrument in the list view
1993 show_intruments_tab();
1994 m_TreeView.get_selection()->select(model->children()[i]);
1995 Gtk::TreePath path(
1996 m_TreeView.get_selection()->get_selected()
1997 );
1998 m_TreeView.scroll_to_row(path);
1999 on_sel_change(); // the regular instrument selection change callback
2000 }
2001 }
2002 }
2003
2004 /// Returns true if requested dimension region was successfully selected and scrolled to in the list view, false on error.
2005 bool MainWindow::select_dimension_region(gig::DimensionRegion* dimRgn) {
2006 gig::Region* pRegion = (gig::Region*) dimRgn->GetParent();
2007 gig::Instrument* pInstrument = (gig::Instrument*) pRegion->GetParent();
2008
2009 Glib::RefPtr<Gtk::TreeModel> model = m_TreeView.get_model();
2010 for (int i = 0; i < model->children().size(); ++i) {
2011 Gtk::TreeModel::Row row = model->children()[i];
2012 if (row[m_Columns.m_col_instr] == pInstrument) {
2013 // select and show the respective instrument in the list view
2014 show_intruments_tab();
2015 m_TreeView.get_selection()->select(model->children()[i]);
2016 Gtk::TreePath path(
2017 m_TreeView.get_selection()->get_selected()
2018 );
2019 m_TreeView.scroll_to_row(path);
2020 on_sel_change(); // the regular instrument selection change callback
2021
2022 // select respective region in the region selector
2023 m_RegionChooser.set_region(pRegion);
2024
2025 // select and show the respective dimension region in the editor
2026 //update_dimregs();
2027 if (!m_DimRegionChooser.select_dimregion(dimRgn)) return false;
2028 //dimreg_edit.set_dim_region(dimRgn);
2029
2030 return true;
2031 }
2032 }
2033
2034 return false;
2035 }
2036
2037 void MainWindow::select_sample(gig::Sample* sample) {
2038 Glib::RefPtr<Gtk::TreeModel> model = m_TreeViewSamples.get_model();
2039 for (int g = 0; g < model->children().size(); ++g) {
2040 Gtk::TreeModel::Row rowGroup = model->children()[g];
2041 for (int s = 0; s < rowGroup.children().size(); ++s) {
2042 Gtk::TreeModel::Row rowSample = rowGroup.children()[s];
2043 if (rowSample[m_SamplesModel.m_col_sample] == sample) {
2044 show_samples_tab();
2045 m_TreeViewSamples.get_selection()->select(rowGroup.children()[s]);
2046 Gtk::TreePath path(
2047 m_TreeViewSamples.get_selection()->get_selected()
2048 );
2049 m_TreeViewSamples.scroll_to_row(path);
2050 return;
2051 }
2052 }
2053 }
2054 }
2055
2056 void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) {
2057 if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
2058 Gtk::Menu* sample_popup =
2059 dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/SamplePopupMenu"));
2060 // update enabled/disabled state of sample popup items
2061 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
2062 Gtk::TreeModel::iterator it = sel->get_selected();
2063 bool group_selected = false;
2064 bool sample_selected = false;
2065 if (it) {
2066 Gtk::TreeModel::Row row = *it;
2067 group_selected = row[m_SamplesModel.m_col_group];
2068 sample_selected = row[m_SamplesModel.m_col_sample];
2069 }
2070
2071
2072 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->
2073 set_sensitive(group_selected || sample_selected);
2074 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->
2075 set_sensitive(group_selected || sample_selected);
2076 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->
2077 set_sensitive(file);
2078 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/ShowSampleRefs"))->
2079 set_sensitive(sample_selected);
2080 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->
2081 set_sensitive(group_selected || sample_selected);
2082 // show sample popup
2083 sample_popup->popup(button->button, button->time);
2084
2085 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/SampleProperties"))->
2086 set_sensitive(group_selected || sample_selected);
2087 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/AddSample"))->
2088 set_sensitive(group_selected || sample_selected);
2089 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/AddGroup"))->
2090 set_sensitive(file);
2091 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/ShowSampleRefs"))->
2092 set_sensitive(sample_selected);
2093 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/RemoveSample"))->
2094 set_sensitive(group_selected || sample_selected);
2095 }
2096 }
2097
2098 void MainWindow::on_script_treeview_button_release(GdkEventButton* button) {
2099 if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
2100 Gtk::Menu* script_popup =
2101 dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/ScriptPopupMenu"));
2102 // update enabled/disabled state of sample popup items
2103 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2104 Gtk::TreeModel::iterator it = sel->get_selected();
2105 bool group_selected = false;
2106 bool script_selected = false;
2107 if (it) {
2108 Gtk::TreeModel::Row row = *it;
2109 group_selected = row[m_ScriptsModel.m_col_group];
2110 script_selected = row[m_ScriptsModel.m_col_script];
2111 }
2112 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/AddScript"))->
2113 set_sensitive(group_selected || script_selected);
2114 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/AddScriptGroup"))->
2115 set_sensitive(file);
2116 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/EditScript"))->
2117 set_sensitive(script_selected);
2118 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/RemoveScript"))->
2119 set_sensitive(group_selected || script_selected);
2120 // show sample popup
2121 script_popup->popup(button->button, button->time);
2122
2123 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/AddScript"))->
2124 set_sensitive(group_selected || script_selected);
2125 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/AddScriptGroup"))->
2126 set_sensitive(file);
2127 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/EditScript"))->
2128 set_sensitive(script_selected);
2129 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/RemoveScript"))->
2130 set_sensitive(group_selected || script_selected);
2131 }
2132 }
2133
2134 Gtk::RadioMenuItem* MainWindow::add_instrument_to_menu(
2135 const Glib::ustring& name, int position) {
2136
2137 Gtk::RadioMenuItem::Group instrument_group;
2138 const std::vector<Gtk::Widget*> children = instrument_menu->get_children();
2139 if (!children.empty()) {
2140 instrument_group =
2141 static_cast<Gtk::RadioMenuItem*>(children[0])->get_group();
2142 }
2143 Gtk::RadioMenuItem* item =
2144 new Gtk::RadioMenuItem(instrument_group, name);
2145 if (position < 0) {
2146 instrument_menu->append(*item);
2147 } else {
2148 instrument_menu->insert(*item, position);
2149 }
2150 item->show();
2151 item->signal_activate().connect(
2152 sigc::bind(
2153 sigc::mem_fun(*this, &MainWindow::on_instrument_selection_change),
2154 item));
2155 return item;
2156 }
2157
2158 void MainWindow::remove_instrument_from_menu(int index) {
2159 const std::vector<Gtk::Widget*> children =
2160 instrument_menu->get_children();
2161 Gtk::Widget* child = children[index];
2162 instrument_menu->remove(*child);
2163 delete child;
2164 }
2165
2166 void MainWindow::add_instrument(gig::Instrument* instrument) {
2167 const Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
2168
2169 // update instrument tree view
2170 instrument_name_connection.block();
2171 Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append();
2172 Gtk::TreeModel::Row rowInstr = *iterInstr;
2173 rowInstr[m_Columns.m_col_name] = name;
2174 rowInstr[m_Columns.m_col_instr] = instrument;
2175 instrument_name_connection.unblock();
2176
2177 add_instrument_to_menu(name);
2178
2179 m_TreeView.get_selection()->select(iterInstr);
2180
2181 file_changed();
2182 }
2183
2184 void MainWindow::on_action_add_instrument() {
2185 static int __instrument_indexer = 0;
2186 if (!file) return;
2187 gig::Instrument* instrument = file->AddInstrument();
2188 __instrument_indexer++;
2189 instrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument ") +
2190 ToString(__instrument_indexer));
2191
2192 add_instrument(instrument);
2193 }
2194
2195 void MainWindow::on_action_duplicate_instrument() {
2196 if (!file) return;
2197
2198 // retrieve the currently selected instrument
2199 // (being the original instrument to be duplicated)
2200 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
2201 Gtk::TreeModel::iterator itSelection = sel->get_selected();
2202 if (!itSelection) return;
2203 Gtk::TreeModel::Row row = *itSelection;
2204 gig::Instrument* instrOrig = row[m_Columns.m_col_instr];
2205 if (!instrOrig) return;
2206
2207 // duplicate the orginal instrument
2208 gig::Instrument* instrNew = file->AddDuplicateInstrument(instrOrig);
2209 instrNew->pInfo->Name =
2210 instrOrig->pInfo->Name +
2211 gig_from_utf8(Glib::ustring(" (") + _("Copy") + ")");
2212
2213 add_instrument(instrNew);
2214 }
2215
2216 void MainWindow::on_action_remove_instrument() {
2217 if (!file) return;
2218 if (file_is_shared) {
2219 Gtk::MessageDialog msg(
2220 *this,
2221 _("You cannot delete an instrument from this file, since it's "
2222 "currently used by the sampler."),
2223 false, Gtk::MESSAGE_INFO
2224 );
2225 msg.run();
2226 return;
2227 }
2228
2229 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
2230 Gtk::TreeModel::iterator it = sel->get_selected();
2231 if (it) {
2232 Gtk::TreeModel::Row row = *it;
2233 gig::Instrument* instr = row[m_Columns.m_col_instr];
2234 try {
2235 Gtk::TreePath path(it);
2236 int index = path[0];
2237
2238 // remove instrument from the gig file
2239 if (instr) file->DeleteInstrument(instr);
2240 file_changed();
2241
2242 remove_instrument_from_menu(index);
2243
2244 // remove row from instruments tree view
2245 m_refTreeModel->erase(it);
2246
2247 #if GTKMM_MAJOR_VERSION < 3
2248 // select another instrument (in gtk3 this is done
2249 // automatically)
2250 if (!m_refTreeModel->children().empty()) {
2251 if (index == m_refTreeModel->children().size()) {
2252 index--;
2253 }
2254 m_TreeView.get_selection()->select(
2255 Gtk::TreePath(ToString(index)));
2256 }
2257 #endif
2258 instr_props_set_instrument();
2259 instr = get_instrument();
2260 if (instr) {
2261 midiRules.set_instrument(instr);
2262 } else {
2263 midiRules.hide();
2264 }
2265 } catch (RIFF::Exception e) {
2266 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
2267 msg.run();
2268 }
2269 }
2270 }
2271
2272 void MainWindow::on_action_sample_properties() {
2273 //TODO: show a dialog where the selected sample's properties can be edited
2274 Gtk::MessageDialog msg(
2275 *this, _("Sorry, yet to be implemented!"), false, Gtk::MESSAGE_INFO
2276 );
2277 msg.run();
2278 }
2279
2280 void MainWindow::on_action_add_script_group() {
2281 static int __script_indexer = 0;
2282 if (!file) return;
2283 gig::ScriptGroup* group = file->AddScriptGroup();
2284 group->Name = gig_from_utf8(_("Unnamed Group"));
2285 if (__script_indexer) group->Name += " " + ToString(__script_indexer);
2286 __script_indexer++;
2287 // update sample tree view
2288 Gtk::TreeModel::iterator iterGroup = m_refScriptsTreeModel->append();
2289 Gtk::TreeModel::Row rowGroup = *iterGroup;
2290 rowGroup[m_ScriptsModel.m_col_name] = gig_to_utf8(group->Name);
2291 rowGroup[m_ScriptsModel.m_col_script] = NULL;
2292 rowGroup[m_ScriptsModel.m_col_group] = group;
2293 file_changed();
2294 }
2295
2296 void MainWindow::on_action_add_script() {
2297 if (!file) return;
2298 // get selected group
2299 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2300 Gtk::TreeModel::iterator it = sel->get_selected();
2301 if (!it) return;
2302 Gtk::TreeModel::Row row = *it;
2303 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
2304 if (!group) { // not a group, but a script is selected (probably)
2305 gig::Script* script = row[m_ScriptsModel.m_col_script];
2306 if (!script) return;
2307 it = row.parent(); // resolve parent (that is the script's group)
2308 if (!it) return;
2309 row = *it;
2310 group = row[m_ScriptsModel.m_col_group];
2311 if (!group) return;
2312 }
2313
2314 // add a new script to the .gig file
2315 gig::Script* script = group->AddScript();
2316 Glib::ustring name = _("Unnamed Script");
2317 script->Name = gig_from_utf8(name);
2318
2319 // add script to the tree view
2320 Gtk::TreeModel::iterator iterScript =
2321 m_refScriptsTreeModel->append(row.children());
2322 Gtk::TreeModel::Row rowScript = *iterScript;
2323 rowScript[m_ScriptsModel.m_col_name] = name;
2324 rowScript[m_ScriptsModel.m_col_script] = script;
2325 rowScript[m_ScriptsModel.m_col_group] = NULL;
2326
2327 // unfold group of new script item in treeview
2328 Gtk::TreeModel::Path path(iterScript);
2329 m_TreeViewScripts.expand_to_path(path);
2330 }
2331
2332 void MainWindow::on_action_edit_script() {
2333 if (!file) return;
2334 // get selected script
2335 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2336 Gtk::TreeModel::iterator it = sel->get_selected();
2337 if (!it) return;
2338 Gtk::TreeModel::Row row = *it;
2339 gig::Script* script = row[m_ScriptsModel.m_col_script];
2340 if (!script) return;
2341
2342 ScriptEditor* editor = new ScriptEditor;
2343 editor->signal_script_to_be_changed.connect(
2344 signal_script_to_be_changed.make_slot()
2345 );
2346 editor->signal_script_changed.connect(
2347 signal_script_changed.make_slot()
2348 );
2349 editor->setScript(script);
2350 //editor->reparent(*this);
2351 editor->show();
2352 }
2353
2354 void MainWindow::on_action_remove_script() {
2355 if (!file) return;
2356 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2357 Gtk::TreeModel::iterator it = sel->get_selected();
2358 if (it) {
2359 Gtk::TreeModel::Row row = *it;
2360 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
2361 gig::Script* script = row[m_ScriptsModel.m_col_script];
2362 Glib::ustring name = row[m_ScriptsModel.m_col_name];
2363 try {
2364 // remove script group or script from the gig file
2365 if (group) {
2366 // notify everybody that we're going to remove these samples
2367 //TODO: scripts_to_be_removed_signal.emit(members);
2368 // delete the group in the .gig file including the
2369 // samples that belong to the group
2370 file->DeleteScriptGroup(group);
2371 // notify that we're done with removal
2372 //TODO: scripts_removed_signal.emit();
2373 file_changed();
2374 } else if (script) {
2375 // notify everybody that we're going to remove this sample
2376 //TODO: std::list<gig::Script*> lscripts;
2377 //TODO: lscripts.push_back(script);
2378 //TODO: scripts_to_be_removed_signal.emit(lscripts);
2379 // remove sample from the .gig file
2380 script->GetGroup()->DeleteScript(script);
2381 // notify that we're done with removal
2382 //TODO: scripts_removed_signal.emit();
2383 dimreg_changed();
2384 file_changed();
2385 }
2386 // remove respective row(s) from samples tree view
2387 m_refScriptsTreeModel->erase(it);
2388 } catch (RIFF::Exception e) {
2389 // pretend we're done with removal (i.e. to avoid dead locks)
2390 //TODO: scripts_removed_signal.emit();
2391 // show error message
2392 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
2393 msg.run();
2394 }
2395 }
2396 }
2397
2398 void MainWindow::on_action_add_group() {
2399 static int __sample_indexer = 0;
2400 if (!file) return;
2401 gig::Group* group = file->AddGroup();
2402 group->Name = gig_from_utf8(_("Unnamed Group"));
2403 if (__sample_indexer) group->Name += " " + ToString(__sample_indexer);
2404 __sample_indexer++;
2405 // update sample tree view
2406 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
2407 Gtk::TreeModel::Row rowGroup = *iterGroup;
2408 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
2409 rowGroup[m_SamplesModel.m_col_sample] = NULL;
2410 rowGroup[m_SamplesModel.m_col_group] = group;
2411 file_changed();
2412 }
2413
2414 void MainWindow::on_action_replace_sample() {
2415 add_or_replace_sample(true);
2416 }
2417
2418 void MainWindow::on_action_add_sample() {
2419 add_or_replace_sample(false);
2420 }
2421
2422 void MainWindow::add_or_replace_sample(bool replace) {
2423 if (!file) return;
2424
2425 // get selected group (and probably selected sample)
2426 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
2427 Gtk::TreeModel::iterator it = sel->get_selected();
2428 if (!it) return;
2429 Gtk::TreeModel::Row row = *it;
2430 gig::Sample* sample = NULL;
2431 gig::Group* group = row[m_SamplesModel.m_col_group];
2432 if (!group) { // not a group, but a sample is selected (probably)
2433 if (replace) sample = row[m_SamplesModel.m_col_sample];
2434 if (!row[m_SamplesModel.m_col_sample]) return;
2435 it = row.parent(); // resolve parent (that is the sample's group)
2436 if (!it) return;
2437 if (!replace) row = *it;
2438 group = (*it)[m_SamplesModel.m_col_group];
2439 if (!group) return;
2440 }
2441 if (replace && !sample) return;
2442
2443 // show 'browse for file' dialog
2444 Gtk::FileChooserDialog dialog(*this, replace ? _("Replace Sample with") : _("Add Sample(s)"));
2445 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2446 dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
2447 dialog.set_select_multiple(!replace); // allow multi audio file selection only when adding new samples, does not make sense when replacing a specific sample
2448
2449 // matches all file types supported by libsndfile
2450 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
2451 Gtk::FileFilter soundfilter;
2452 #else
2453 Glib::RefPtr<Gtk::FileFilter> soundfilter = Gtk::FileFilter::create();
2454 #endif
2455 const char* const supportedFileTypes[] = {
2456 "*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd",
2457 "*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF",
2458 "*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64",
2459 "*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK",
2460 "*.caf", "*.CAF", NULL
2461 };
2462 const char* soundfiles = _("Sound Files");
2463 const char* allfiles = _("All Files");
2464 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
2465 for (int i = 0; supportedFileTypes[i]; i++)
2466 soundfilter.add_pattern(supportedFileTypes[i]);
2467 soundfilter.set_name(soundfiles);
2468
2469 // matches every file
2470 Gtk::FileFilter allpassfilter;
2471 allpassfilter.add_pattern("*.*");
2472 allpassfilter.set_name(allfiles);
2473 #else
2474 for (int i = 0; supportedFileTypes[i]; i++)
2475 soundfilter->add_pattern(supportedFileTypes[i]);
2476 soundfilter->set_name(soundfiles);
2477
2478 // matches every file
2479 Glib::RefPtr<Gtk::FileFilter> allpassfilter = Gtk::FileFilter::create();
2480 allpassfilter->add_pattern("*.*");
2481 allpassfilter->set_name(allfiles);
2482 #endif
2483 dialog.add_filter(soundfilter);
2484 dialog.add_filter(allpassfilter);
2485 if (current_sample_dir != "") {
2486 dialog.set_current_folder(current_sample_dir);
2487 }
2488 if (dialog.run() == Gtk::RESPONSE_OK) {
2489 current_sample_dir = dialog.get_current_folder();
2490 Glib::ustring error_files;
2491 std::vector<std::string> filenames = dialog.get_filenames();
2492 for (std::vector<std::string>::iterator iter = filenames.begin();
2493 iter != filenames.end(); ++iter) {
2494 printf("Adding sample %s\n",(*iter).c_str());
2495 // use libsndfile to retrieve file informations
2496 SF_INFO info;
2497 info.format = 0;
2498 SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
2499 try {
2500 if (!hFile) throw std::string(_("could not open file"));
2501 int bitdepth;
2502 switch (info.format & 0xff) {
2503 case SF_FORMAT_PCM_S8:
2504 case SF_FORMAT_PCM_16:
2505 case SF_FORMAT_PCM_U8:
2506 bitdepth = 16;
2507 break;
2508 case SF_FORMAT_PCM_24:
2509 case SF_FORMAT_PCM_32:
2510 case SF_FORMAT_FLOAT:
2511 case SF_FORMAT_DOUBLE:
2512 bitdepth = 24;
2513 break;
2514 default:
2515 sf_close(hFile); // close sound file
2516 throw std::string(_("format not supported")); // unsupported subformat (yet?)
2517 }
2518 // add a new sample to the .gig file (if adding is requested actually)
2519 if (!replace) sample = file->AddSample();
2520 // file name without path
2521 Glib::ustring filename = Glib::filename_display_basename(*iter);
2522 // remove file extension if there is one
2523 for (int i = 0; supportedFileTypes[i]; i++) {
2524 if (Glib::str_has_suffix(filename, supportedFileTypes[i] + 1)) {
2525 filename.erase(filename.length() - strlen(supportedFileTypes[i] + 1));
2526 break;
2527 }
2528 }
2529 sample->pInfo->Name = gig_from_utf8(filename);
2530 sample->Channels = info.channels;
2531 sample->BitDepth = bitdepth;
2532 sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
2533 sample->SamplesPerSecond = info.samplerate;
2534 sample->AverageBytesPerSecond = sample->FrameSize * sample->SamplesPerSecond;
2535 sample->BlockAlign = sample->FrameSize;
2536 sample->SamplesTotal = info.frames;
2537
2538 SF_INSTRUMENT instrument;
2539 if (sf_command(hFile, SFC_GET_INSTRUMENT,
2540 &instrument, sizeof(instrument)) != SF_FALSE)
2541 {
2542 sample->MIDIUnityNote = instrument.basenote;
2543 sample->FineTune = instrument.detune;
2544
2545 if (instrument.loop_count && instrument.loops[0].mode != SF_LOOP_NONE) {
2546 sample->Loops = 1;
2547
2548 switch (instrument.loops[0].mode) {
2549 case SF_LOOP_FORWARD:
2550 sample->LoopType = gig::loop_type_normal;
2551 break;
2552 case SF_LOOP_BACKWARD:
2553 sample->LoopType = gig::loop_type_backward;
2554 break;
2555 case SF_LOOP_ALTERNATING:
2556 sample->LoopType = gig::loop_type_bidirectional;
2557 break;
2558 }
2559 sample->LoopStart = instrument.loops[0].start;
2560 sample->LoopEnd = instrument.loops[0].end;
2561 sample->LoopPlayCount = instrument.loops[0].count;
2562 sample->LoopSize = sample->LoopEnd - sample->LoopStart + 1;
2563 }
2564 }
2565
2566 // schedule resizing the sample (which will be done
2567 // physically when File::Save() is called)
2568 sample->Resize(info.frames);
2569 // make sure sample is part of the selected group
2570 if (!replace) group->AddSample(sample);
2571 // schedule that physical resize and sample import
2572 // (data copying), performed when "Save" is requested
2573 SampleImportItem sched_item;
2574 sched_item.gig_sample = sample;
2575 sched_item.sample_path = *iter;
2576 m_SampleImportQueue.push_back(sched_item);
2577 // add sample to the tree view
2578 if (replace) {
2579 row[m_SamplesModel.m_col_name] = gig_to_utf8(sample->pInfo->Name);
2580 } else {
2581 Gtk::TreeModel::iterator iterSample =
2582 m_refSamplesTreeModel->append(row.children());
2583 Gtk::TreeModel::Row rowSample = *iterSample;
2584 rowSample[m_SamplesModel.m_col_name] =
2585 gig_to_utf8(sample->pInfo->Name);
2586 rowSample[m_SamplesModel.m_col_sample] = sample;
2587 rowSample[m_SamplesModel.m_col_group] = NULL;
2588 }
2589 // close sound file
2590 sf_close(hFile);
2591 file_changed();
2592 } catch (std::string what) { // remember the files that made trouble (and their cause)
2593 if (!error_files.empty()) error_files += "\n";
2594 error_files += *iter += " (" + what + ")";
2595 }
2596 }
2597 // show error message box when some file(s) could not be opened / added
2598 if (!error_files.empty()) {
2599 Glib::ustring txt =
2600 (replace
2601 ? _("Failed to replace sample with:\n")
2602 : _("Could not add the following sample(s):\n"))
2603 + error_files;
2604 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
2605 msg.run();
2606 }
2607 }
2608 }
2609
2610 void MainWindow::on_action_replace_all_samples_in_all_groups()
2611 {
2612 if (!file) return;
2613 Gtk::FileChooserDialog dialog(*this, _("Select Folder"),
2614 Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
2615 const char* str =
2616 _("This is a very specific function. It tries to replace all samples "
2617 "in the current gig file by samples located in the chosen "
2618 "directory.\n\n"
2619 "It works like this: For each sample in the gig file, it tries to "
2620 "find a sample file in the selected directory with the same name as "
2621 "the sample in the gig file. Optionally, you can add a filename "
2622 "extension below, which will be added to the filename expected to be "
2623 "found. That is, assume you have a gig file with a sample called "
2624 "'Snare', if you enter '.wav' below (like it's done by default), it "
2625 "expects to find a sample file called 'Snare.wav' and will replace "
2626 "the sample in the gig file accordingly. If you don't need an "
2627 "extension, blank the field below. Any gig sample where no "
2628 "appropriate sample file could be found will be reported and left "
2629 "untouched.\n");
2630 #if GTKMM_MAJOR_VERSION < 3
2631 view::WrapLabel description(str);
2632 #else
2633 Gtk::Label description(str);
2634 description.set_line_wrap();
2635 #endif
2636 Gtk::HBox entryArea;
2637 Gtk::Label entryLabel( _("Add filename extension: "), Gtk::ALIGN_START);
2638 Gtk::Entry postfixEntryBox;
2639 postfixEntryBox.set_text(".wav");
2640 entryArea.pack_start(entryLabel);
2641 entryArea.pack_start(postfixEntryBox);
2642 dialog.get_vbox()->pack_start(description, Gtk::PACK_SHRINK);
2643 dialog.get_vbox()->pack_start(entryArea, Gtk::PACK_SHRINK);
2644 description.show();
2645 entryArea.show_all();
2646 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2647 dialog.add_button(_("Select"), Gtk::RESPONSE_OK);
2648 dialog.set_select_multiple(false);
2649 if (current_sample_dir != "") {
2650 dialog.set_current_folder(current_sample_dir);
2651 }
2652 if (dialog.run() == Gtk::RESPONSE_OK)
2653 {
2654 current_sample_dir = dialog.get_current_folder();
2655 Glib::ustring error_files;
2656 std::string folder = dialog.get_filename();
2657 for (gig::Sample* sample = file->GetFirstSample();
2658 sample; sample = file->GetNextSample())
2659 {
2660 std::string filename =
2661 folder + G_DIR_SEPARATOR_S +
2662 Glib::filename_from_utf8(gig_to_utf8(sample->pInfo->Name) +
2663 postfixEntryBox.get_text());
2664 SF_INFO info;
2665 info.format = 0;
2666 SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &info);
2667 try
2668 {
2669 if (!hFile) throw std::string(_("could not open file"));
2670 switch (info.format & 0xff) {
2671 case SF_FORMAT_PCM_S8:
2672 case SF_FORMAT_PCM_16:
2673 case SF_FORMAT_PCM_U8:
2674 case SF_FORMAT_PCM_24:
2675 case SF_FORMAT_PCM_32:
2676 case SF_FORMAT_FLOAT:
2677 case SF_FORMAT_DOUBLE:
2678 break;
2679 default:
2680 sf_close(hFile);
2681 throw std::string(_("format not supported"));
2682 }
2683 SampleImportItem sched_item;
2684 sched_item.gig_sample = sample;
2685 sched_item.sample_path = filename;
2686 m_SampleImportQueue.push_back(sched_item);
2687 sf_close(hFile);
2688 file_changed();
2689 }
2690 catch (std::string what)
2691 {
2692 if (!error_files.empty()) error_files += "\n";
2693 error_files += Glib::filename_to_utf8(filename) +
2694 " (" + what + ")";
2695 }
2696 }
2697 // show error message box when some file(s) could not be opened / added
2698 if (!error_files.empty()) {
2699 Glib::ustring txt =
2700 _("Could not replace the following sample(s):\n") + error_files;
2701 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
2702 msg.run();
2703 }
2704 }
2705 }
2706
2707 void MainWindow::on_action_remove_sample() {
2708 if (!file) return;
2709 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
2710 Gtk::TreeModel::iterator it = sel->get_selected();
2711 if (it) {
2712 Gtk::TreeModel::Row row = *it;
2713 gig::Group* group = row[m_SamplesModel.m_col_group];
2714 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
2715 Glib::ustring name = row[m_SamplesModel.m_col_name];
2716 try {
2717 // remove group or sample from the gig file
2718 if (group) {
2719 // temporarily remember the samples that belong to
2720 // that group (we need that to clean the queue)
2721 std::list<gig::Sample*> members;
2722 for (gig::Sample* pSample = group->GetFirstSample();
2723 pSample; pSample = group->GetNextSample()) {
2724 members.push_back(pSample);
2725 }
2726 // notify everybody that we're going to remove these samples
2727 samples_to_be_removed_signal.emit(members);
2728 // delete the group in the .gig file including the
2729 // samples that belong to the group
2730 file->DeleteGroup(group);
2731 // notify that we're done with removal
2732 samples_removed_signal.emit();
2733 // if sample(s) were just previously added, remove
2734 // them from the import queue
2735 for (std::list<gig::Sample*>::iterator member = members.begin();
2736 member != members.end(); ++member) {
2737 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
2738 iter != m_SampleImportQueue.end(); ++iter) {
2739 if ((*iter).gig_sample == *member) {
2740 printf("Removing previously added sample '%s' from group '%s'\n",
2741 (*iter).sample_path.c_str(), name.c_str());
2742 m_SampleImportQueue.erase(iter);
2743 break;
2744 }
2745 }
2746 }
2747 file_changed();
2748 } else if (sample) {
2749 // notify everybody that we're going to remove this sample
2750 std::list<gig::Sample*> lsamples;
2751 lsamples.push_back(sample);
2752 samples_to_be_removed_signal.emit(lsamples);
2753 // remove sample from the .gig file
2754 file->DeleteSample(sample);
2755 // notify that we're done with removal
2756 samples_removed_signal.emit();
2757 // if sample was just previously added, remove it from
2758 // the import queue
2759 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
2760 iter != m_SampleImportQueue.end(); ++iter) {
2761 if ((*iter).gig_sample == sample) {
2762 printf("Removing previously added sample '%s'\n",
2763 (*iter).sample_path.c_str());
2764 m_SampleImportQueue.erase(iter);
2765 break;
2766 }
2767 }
2768 dimreg_changed();
2769 file_changed();
2770 }
2771 // remove respective row(s) from samples tree view
2772 m_refSamplesTreeModel->erase(it);
2773 } catch (RIFF::Exception e) {
2774 // pretend we're done with removal (i.e. to avoid dead locks)
2775 samples_removed_signal.emit();
2776 // show error message
2777 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
2778 msg.run();
2779 }
2780 }
2781 }
2782
2783 void MainWindow::on_action_remove_unused_samples() {
2784 if (!file) return;
2785
2786 // collect all samples that are not referenced by any instrument
2787 std::list<gig::Sample*> lsamples;
2788 for (int iSample = 0; file->GetSample(iSample); ++iSample) {
2789 gig::Sample* sample = file->GetSample(iSample);
2790 bool isUsed = false;
2791 for (gig::Instrument* instrument = file->GetFirstInstrument(); instrument;
2792 instrument = file->GetNextInstrument())
2793 {
2794 for (gig::Region* rgn = instrument->GetFirstRegion(); rgn;
2795 rgn = instrument->GetNextRegion())
2796 {
2797 for (int i = 0; i < 256; ++i) {
2798 if (!rgn->pDimensionRegions[i]) continue;
2799 if (rgn->pDimensionRegions[i]->pSample != sample) continue;
2800 isUsed = true;
2801 goto endOfRefSearch;
2802 }
2803 }
2804 }
2805 endOfRefSearch:
2806 if (!isUsed) lsamples.push_back(sample);
2807 }
2808
2809 if (lsamples.empty()) return;
2810
2811 // notify everybody that we're going to remove these samples
2812 samples_to_be_removed_signal.emit(lsamples);
2813
2814 // remove collected samples
2815 try {
2816 for (std::list<gig::Sample*>::iterator itSample = lsamples.begin();
2817 itSample != lsamples.end(); ++itSample)
2818 {
2819 gig::Sample* sample = *itSample;
2820 // remove sample from the .gig file
2821 file->DeleteSample(sample);
2822 // if sample was just previously added, remove it fro the import queue
2823 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
2824 iter != m_SampleImportQueue.end(); ++iter)
2825 {
2826 if ((*iter).gig_sample == sample) {
2827 printf("Removing previously added sample '%s'\n",
2828 (*iter).sample_path.c_str());
2829 m_SampleImportQueue.erase(iter);
2830 break;
2831 }
2832 }
2833 }
2834 } catch (RIFF::Exception e) {
2835 // show error message
2836 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
2837 msg.run();
2838 }
2839
2840 // notify everybody that we're done with removal
2841 samples_removed_signal.emit();
2842
2843 dimreg_changed();
2844 file_changed();
2845 __refreshEntireGUI();
2846 }
2847
2848 // see comment on on_sample_treeview_drag_begin()
2849 void MainWindow::on_scripts_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
2850 {
2851 first_call_to_drag_data_get = true;
2852 }
2853
2854 void MainWindow::on_scripts_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
2855 Gtk::SelectionData& selection_data, guint, guint)
2856 {
2857 if (!first_call_to_drag_data_get) return;
2858 first_call_to_drag_data_get = false;
2859
2860 // get selected script
2861 gig::Script* script = NULL;
2862 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2863 Gtk::TreeModel::iterator it = sel->get_selected();
2864 if (it) {
2865 Gtk::TreeModel::Row row = *it;
2866 script = row[m_ScriptsModel.m_col_script];
2867 }
2868 // pass the gig::Script as pointer
2869 selection_data.set(selection_data.get_target(), 0/*unused*/,
2870 (const guchar*)&script,
2871 sizeof(script)/*length of data in bytes*/);
2872 }
2873
2874 // see comment on on_sample_treeview_drag_begin()
2875 void MainWindow::on_instruments_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
2876 {
2877 first_call_to_drag_data_get = true;
2878 }
2879
2880 void MainWindow::on_instruments_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
2881 Gtk::SelectionData& selection_data, guint, guint)
2882 {
2883 if (!first_call_to_drag_data_get) return;
2884 first_call_to_drag_data_get = false;
2885
2886 // get selected source instrument
2887 gig::Instrument* src = NULL;
2888 {
2889 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
2890 Gtk::TreeModel::iterator it = sel->get_selected();
2891 if (it) {
2892 Gtk::TreeModel::Row row = *it;
2893 src = row[m_Columns.m_col_instr];
2894 }
2895 }
2896 if (!src) return;
2897
2898 // pass the source gig::Instrument as pointer
2899 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&src,
2900 sizeof(src)/*length of data in bytes*/);
2901 }
2902
2903 void MainWindow::on_instruments_treeview_drop_drag_data_received(
2904 const Glib::RefPtr<Gdk::DragContext>& context, int x, int y,
2905 const Gtk::SelectionData& selection_data, guint, guint time)
2906 {
2907 gig::Instrument* src = *((gig::Instrument**) selection_data.get_data());
2908 if (!src || selection_data.get_length() != sizeof(gig::Instrument*))
2909 return;
2910
2911 gig::Instrument* dst = NULL;
2912 {
2913 Gtk::TreeModel::Path path;
2914 const bool found = m_TreeView.get_path_at_pos(x, y, path);
2915 if (!found) return;
2916
2917 Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path);
2918 if (!iter) return;
2919 Gtk::TreeModel::Row row = *iter;
2920 dst = row[m_Columns.m_col_instr];
2921 }
2922 if (!dst) return;
2923
2924 //printf("dragdrop received src=%s dst=%s\n", src->pInfo->Name.c_str(), dst->pInfo->Name.c_str());
2925 src->MoveTo(dst);
2926 __refreshEntireGUI();
2927 select_instrument(src);
2928 }
2929
2930 // For some reason drag_data_get gets called two times for each
2931 // drag'n'drop (at least when target is an Entry). This work-around
2932 // makes sure the code in drag_data_get and drop_drag_data_received is
2933 // only executed once, as drag_begin only gets called once.
2934 void MainWindow::on_sample_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
2935 {
2936 first_call_to_drag_data_get = true;
2937 }
2938
2939 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
2940 Gtk::SelectionData& selection_data, guint, guint)
2941 {
2942 if (!first_call_to_drag_data_get) return;
2943 first_call_to_drag_data_get = false;
2944
2945 // get selected sample
2946 gig::Sample* sample = NULL;
2947 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
2948 Gtk::TreeModel::iterator it = sel->get_selected();
2949 if (it) {
2950 Gtk::TreeModel::Row row = *it;
2951 sample = row[m_SamplesModel.m_col_sample];
2952 }
2953 // pass the gig::Sample as pointer
2954 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
2955 sizeof(sample)/*length of data in bytes*/);
2956 }
2957
2958 void MainWindow::on_sample_label_drop_drag_data_received(
2959 const Glib::RefPtr<Gdk::DragContext>& context, int, int,
2960 const Gtk::SelectionData& selection_data, guint, guint time)
2961 {
2962 gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
2963
2964 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
2965 std::cout << "Drop received sample \"" <<
2966 sample->pInfo->Name << "\"" << std::endl;
2967 // drop success
2968 context->drop_reply(true, time);
2969
2970 //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
2971
2972 // notify everybody that we're going to alter the region
2973 gig::Region* region = m_RegionChooser.get_region();
2974 region_to_be_changed_signal.emit(region);
2975
2976 // find the samplechannel dimension
2977 gig::dimension_def_t* stereo_dimension = 0;
2978 for (int i = 0 ; i < region->Dimensions ; i++) {
2979 if (region->pDimensionDefinitions[i].dimension ==
2980 gig::dimension_samplechannel) {
2981 stereo_dimension = &region->pDimensionDefinitions[i];
2982 break;
2983 }
2984 }
2985 bool channels_changed = false;
2986 if (sample->Channels == 1 && stereo_dimension) {
2987 // remove the samplechannel dimension
2988 /* commented out, because it makes it impossible building up an instrument from scratch using two separate L/R samples
2989 region->DeleteDimension(stereo_dimension);
2990 channels_changed = true;
2991 region_changed();
2992 */
2993 }
2994 dimreg_edit.set_sample(
2995 sample,
2996 is_copy_samples_unity_note_enabled(),
2997 is_copy_samples_fine_tune_enabled(),
2998 is_copy_samples_loop_enabled()
2999 );
3000
3001 if (sample->Channels == 2 && !stereo_dimension) {
3002 // add samplechannel dimension
3003 gig::dimension_def_t dim;
3004 dim.dimension = gig::dimension_samplechannel;
3005 dim.bits = 1;
3006 dim.zones = 2;
3007 region->AddDimension(&dim);
3008 channels_changed = true;
3009 region_changed();
3010 }
3011 if (channels_changed) {
3012 // unmap all samples with wrong number of channels
3013 // TODO: maybe there should be a warning dialog for this
3014 for (int i = 0 ; i < region->DimensionRegions ; i++) {
3015 gig::DimensionRegion* d = region->pDimensionRegions[i];
3016 if (d->pSample && d->pSample->Channels != sample->Channels) {
3017 gig::Sample* oldref = d->pSample;
3018 d->pSample = NULL;
3019 sample_ref_changed_signal.emit(oldref, NULL);
3020 }
3021 }
3022 }
3023
3024 // notify we're done with altering
3025 region_changed_signal.emit(region);
3026
3027 file_changed();
3028
3029 return;
3030 }
3031 // drop failed
3032 context->drop_reply(false, time);
3033 }
3034
3035 void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
3036 const Gtk::TreeModel::iterator& iter) {
3037 if (!iter) return;
3038 Gtk::TreeModel::Row row = *iter;
3039 Glib::ustring name = row[m_SamplesModel.m_col_name];
3040 gig::Group* group = row[m_SamplesModel.m_col_group];
3041 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
3042 gig::String gigname(gig_from_utf8(name));
3043 if (group) {
3044 if (group->Name != gigname) {
3045 group->Name = gigname;
3046 printf("group name changed\n");
3047 file_changed();
3048 }
3049 } else if (sample) {
3050 if (sample->pInfo->Name != gigname) {
3051 sample->pInfo->Name = gigname;
3052 printf("sample name changed\n");
3053 file_changed();
3054 }
3055 }
3056 }
3057
3058 void MainWindow::script_name_changed(const Gtk::TreeModel::Path& path,
3059 const Gtk::TreeModel::iterator& iter) {
3060 if (!iter) return;
3061 Gtk::TreeModel::Row row = *iter;
3062 Glib::ustring name = row[m_ScriptsModel.m_col_name];
3063 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
3064 gig::Script* script = row[m_ScriptsModel.m_col_script];
3065 gig::String gigname(gig_from_utf8(name));
3066 if (group) {
3067 if (group->Name != gigname) {
3068 group->Name = gigname;
3069 printf("script group name changed\n");
3070 file_changed();
3071 }
3072 } else if (script) {
3073 if (script->Name != gigname) {
3074 script->Name = gigname;
3075 printf("script name changed\n");
3076 file_changed();
3077 }
3078 }
3079 }
3080
3081 void MainWindow::script_double_clicked(const Gtk::TreeModel::Path& path,
3082 Gtk::TreeViewColumn* column)
3083 {
3084 Gtk::TreeModel::iterator iter = m_refScriptsTreeModel->get_iter(path);
3085 if (!iter) return;
3086 Gtk::TreeModel::Row row = *iter;
3087 gig::Script* script = row[m_ScriptsModel.m_col_script];
3088 if (!script) return;
3089
3090 ScriptEditor* editor = new ScriptEditor;
3091 editor->signal_script_to_be_changed.connect(
3092 signal_script_to_be_changed.make_slot()
3093 );
3094 editor->signal_script_changed.connect(
3095 signal_script_changed.make_slot()
3096 );
3097 editor->setScript(script);
3098 //editor->reparent(*this);
3099 editor->show();
3100 }
3101
3102 void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
3103 const Gtk::TreeModel::iterator& iter) {
3104 if (!iter) return;
3105 Gtk::TreeModel::Row row = *iter;
3106 Glib::ustring name = row[m_Columns.m_col_name];
3107
3108 // change name in instrument menu
3109 int index = path[0];
3110 const std::vector<Gtk::Widget*> children = instrument_menu->get_children();
3111 if (index < children.size()) {
3112 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 16) || GTKMM_MAJOR_VERSION > 2
3113 static_cast<Gtk::RadioMenuItem*>(children[index])->set_label(name);
3114 #else
3115 remove_instrument_from_menu(index);
3116 Gtk::RadioMenuItem* item = add_instrument_to_menu(name, index);
3117 item->set_active();
3118 #endif
3119 }
3120
3121 // change name in gig
3122 gig::Instrument* instrument = row[m_Columns.m_col_instr];
3123 gig::String gigname(gig_from_utf8(name));
3124 if (instrument && instrument->pInfo->Name != gigname) {
3125 instrument->pInfo->Name = gigname;
3126
3127 // change name in the instrument properties window
3128 if (instrumentProps.get_instrument() == instrument) {
3129 instrumentProps.update_name();
3130 }
3131
3132 file_changed();
3133 }
3134 }
3135
3136 void MainWindow::on_action_combine_instruments() {
3137 CombineInstrumentsDialog* d = new CombineInstrumentsDialog(*this, file);
3138 d->show_all();
3139 d->resize(500, 400);
3140 d->run();
3141 if (d->fileWasChanged()) {
3142 // update GUI with new instrument just created
3143 add_instrument(d->newCombinedInstrument());
3144 }
3145 delete d;
3146 }
3147
3148 void MainWindow::on_action_view_references() {
3149 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3150 Gtk::TreeModel::iterator it = sel->get_selected();
3151 if (!it) return;
3152 Gtk::TreeModel::Row row = *it;
3153 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
3154 if (!sample) return;
3155
3156 ReferencesView* d = new ReferencesView(*this);
3157 d->setSample(sample);
3158 d->dimension_region_selected.connect(
3159 sigc::mem_fun(*this, &MainWindow::select_dimension_region)
3160 );
3161 d->show_all();
3162 d->resize(500, 400);
3163 d->run();
3164 delete d;
3165 }
3166
3167 void MainWindow::mergeFiles(const std::vector<std::string>& filenames) {
3168 struct _Source {
3169 std::vector<RIFF::File*> riffs;
3170 std::vector<gig::File*> gigs;
3171
3172 ~_Source() {
3173 for (int k = 0; k < gigs.size(); ++k) delete gigs[k];
3174 for (int k = 0; k < riffs.size(); ++k) delete riffs[k];
3175 riffs.clear();
3176 gigs.clear();
3177 }
3178 } sources;
3179
3180 if (filenames.empty())
3181 throw RIFF::Exception(_("No files selected, so nothing done."));
3182
3183 // first open all input files (to avoid output file corruption)
3184 int i;
3185 try {
3186 for (i = 0; i < filenames.size(); ++i) {
3187 const std::string& filename = filenames[i];
3188 printf("opening file=%s\n", filename.c_str());
3189
3190 RIFF::File* riff = new RIFF::File(filename);
3191 sources.riffs.push_back(riff);
3192
3193 gig::File* gig = new gig::File(riff);
3194 sources.gigs.push_back(gig);
3195 }
3196 } catch (RIFF::Exception e) {
3197 throw RIFF::Exception(
3198 _("Error occurred while opening '") +
3199 filenames[i] +
3200 "': " +
3201 e.Message
3202 );
3203 } catch (...) {
3204 throw RIFF::Exception(
3205 _("Unknown exception occurred while opening '") +
3206 filenames[i] + "'"
3207 );
3208 }
3209
3210 // now merge the opened .gig files to the main .gig file currently being
3211 // open in gigedit
3212 try {
3213 for (i = 0; i < filenames.size(); ++i) {
3214 const std::string& filename = filenames[i];
3215 printf("merging file=%s\n", filename.c_str());
3216 assert(i < sources.gigs.size());
3217
3218 this->file->AddContentOf(sources.gigs[i]);
3219 }
3220 } catch (RIFF::Exception e) {
3221 throw RIFF::Exception(
3222 _("Error occurred while merging '") +
3223 filenames[i] +
3224 "': " +
3225 e.Message
3226 );
3227 } catch (...) {
3228 throw RIFF::Exception(
3229 _("Unknown exception occurred while merging '") +
3230 filenames[i] + "'"
3231 );
3232 }
3233
3234 // Finally save gig file persistently to disk ...
3235 //NOTE: requires that this gig file already has a filename !
3236 {
3237 std::cout << "Saving file\n" << std::flush;
3238 file_structure_to_be_changed_signal.emit(this->file);
3239
3240 progress_dialog = new ProgressDialog( //FIXME: memory leak!
3241 _("Saving") + Glib::ustring(" '") +
3242 Glib::filename_display_basename(this->filename) + "' ...",
3243 *this
3244 );
3245 progress_dialog->show_all();
3246 saver = new Saver(this->file); //FIXME: memory leak!
3247 saver->signal_progress().connect(
3248 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
3249 saver->signal_finished().connect(
3250 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
3251 saver->signal_error().connect(
3252 sigc::mem_fun(*this, &MainWindow::on_saver_error));
3253 saver->launch();
3254 }
3255 }
3256
3257 void MainWindow::on_action_merge_files() {
3258 if (this->file->GetFileName().empty()) {
3259 Glib::ustring txt = _(
3260 "You seem to have a new .gig file open that has not been saved "
3261 "yet. You must save it somewhere before starting to merge it with "
3262 "other .gig files though, because during the merge operation the "
3263 "other files' sample data must be written on file level to the "
3264 "target .gig file."
3265 );
3266 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
3267 msg.run();
3268 return;
3269 }
3270
3271 Gtk::FileChooserDialog dialog(*this, _("Merge .gig files"));
3272 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
3273 dialog.add_button(_("Merge"), Gtk::RESPONSE_OK);
3274 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
3275 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
3276 Gtk::FileFilter filter;
3277 filter.add_pattern("*.gig");
3278 #else
3279 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
3280 filter->add_pattern("*.gig");
3281 #endif
3282 dialog.set_filter(filter);
3283 if (current_gig_dir != "") {
3284 dialog.set_current_folder(current_gig_dir);
3285 }
3286 dialog.set_select_multiple(true);
3287
3288 // show warning in the file picker dialog
3289 Gtk::HBox descriptionArea;
3290 descriptionArea.set_spacing(15);
3291 Gtk::Image warningIcon;
3292 warningIcon.set_from_icon_name("dialog-warning",
3293 Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
3294 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
3295 #if GTKMM_MAJOR_VERSION < 3
3296 view::WrapLabel description;
3297 #else
3298 Gtk::Label description;
3299 description.set_line_wrap();
3300 #endif
3301 description.set_markup(_(
3302 "\nSelect at least one .gig file that shall be merged to the .gig file "
3303 "currently being open in gigedit.\n\n"
3304 "<b>Please Note:</b> Merging with other files will modify your "
3305 "currently open .gig file on file level! And be aware that the current "
3306 "merge algorithm does not detect duplicate samples yet. So if you are "
3307 "merging files which are using equivalent sample data, those "
3308 "equivalent samples will currently be treated as separate samples and "
3309 "will accordingly be stored separately in the target .gig file!"
3310 ));
3311 descriptionArea.pack_start(description);
3312 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
3313 descriptionArea.show_all();
3314
3315 if (dialog.run() == Gtk::RESPONSE_OK) {
3316 printf("on_action_merge_files self=%p\n",
3317 static_cast<void*>(Glib::Threads::Thread::self()));
3318 std::vector<std::string> filenames = dialog.get_filenames();
3319
3320 // merge the selected files to the currently open .gig file
3321 try {
3322 mergeFiles(filenames);
3323 } catch (RIFF::Exception e) {
3324 Gtk::MessageDialog msg(*this, e.Message, false, Gtk::MESSAGE_ERROR);
3325 msg.run();
3326 }
3327
3328 // update GUI
3329 __refreshEntireGUI();
3330 }
3331 }
3332
3333 void MainWindow::set_file_is_shared(bool b) {
3334 this->file_is_shared = b;
3335
3336 if (file_is_shared) {
3337 m_AttachedStateLabel.set_label(_("live-mode"));
3338 m_AttachedStateImage.set(
3339 Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
3340 );
3341 } else {
3342 m_AttachedStateLabel.set_label(_("stand-alone"));
3343 m_AttachedStateImage.set(
3344 Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
3345 );
3346 }
3347
3348 {
3349 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
3350 uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection"));
3351 if (item) item->set_sensitive(b);
3352 }
3353 }
3354
3355 void MainWindow::on_sample_ref_count_incremented(gig::Sample* sample, int offset) {
3356 if (!sample) return;
3357 sample_ref_count[sample] += offset;
3358 const int refcount = sample_ref_count[sample];
3359
3360 Glib::RefPtr<Gtk::TreeModel> model = m_TreeViewSamples.get_model();
3361 for (int g = 0; g < model->children().size(); ++g) {
3362 Gtk::TreeModel::Row rowGroup = model->children()[g];
3363 for (int s = 0; s < rowGroup.children().size(); ++s) {
3364 Gtk::TreeModel::Row rowSample = rowGroup.children()[s];
3365 if (rowSample[m_SamplesModel.m_col_sample] != sample) continue;
3366 rowSample[m_SamplesModel.m_col_refcount] = ToString(refcount) + " " + _("Refs.");
3367 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
3368 }
3369 }
3370 }
3371
3372 void MainWindow::on_sample_ref_changed(gig::Sample* oldSample, gig::Sample* newSample) {
3373 on_sample_ref_count_incremented(oldSample, -1);
3374 on_sample_ref_count_incremented(newSample, +1);
3375 }
3376
3377 void MainWindow::on_samples_to_be_removed(std::list<gig::Sample*> samples) {
3378 // just in case a new sample is added later with exactly the same memory
3379 // address, which would lead to incorrect refcount if not deleted here
3380 for (std::list<gig::Sample*>::const_iterator it = samples.begin();
3381 it != samples.end(); ++it)
3382 {
3383 sample_ref_count.erase(*it);
3384 }
3385 }
3386
3387 void MainWindow::show_samples_tab() {
3388 m_TreeViewNotebook.set_current_page(0);
3389 }
3390
3391 void MainWindow::show_intruments_tab() {
3392 m_TreeViewNotebook.set_current_page(1);
3393 }
3394
3395 void MainWindow::show_scripts_tab() {
3396 m_TreeViewNotebook.set_current_page(2);
3397 }
3398
3399 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
3400 return file_structure_to_be_changed_signal;
3401 }
3402
3403 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
3404 return file_structure_changed_signal;
3405 }
3406
3407 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
3408 return samples_to_be_removed_signal;
3409 }
3410
3411 sigc::signal<void>& MainWindow::signal_samples_removed() {
3412 return samples_removed_signal;
3413 }
3414
3415 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
3416 return region_to_be_changed_signal;
3417 }
3418
3419 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
3420 return region_changed_signal;
3421 }
3422
3423 sigc::signal<void, gig::Sample*>& MainWindow::signal_sample_changed() {
3424 return sample_changed_signal;
3425 }
3426
3427 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
3428 return sample_ref_changed_signal;
3429 }
3430
3431 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
3432 return dimreg_to_be_changed_signal;
3433 }
3434
3435 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
3436 return dimreg_changed_signal;
3437 }
3438
3439 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
3440 return note_on_signal;
3441 }
3442
3443 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
3444 return note_off_signal;
3445 }
3446
3447 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_hit() {
3448 return m_RegionChooser.signal_keyboard_key_hit();
3449 }
3450
3451 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_released() {
3452 return m_RegionChooser.signal_keyboard_key_released();
3453 }
3454
3455 sigc::signal<void, gig::Instrument*>& MainWindow::signal_switch_sampler_instrument() {
3456 return switch_sampler_instrument_signal;
3457 }

  ViewVC Help
Powered by ViewVC