/[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 2918 - (show annotations) (download)
Wed May 18 10:10:25 2016 UTC (7 years, 11 months ago) by schoenebeck
File size: 132479 byte(s)
* Added setting to main menu "View" -> "Auto Restore Window Dimension"
  (disabled by default since it causes issues on some machines).
* Bumped version (1.0.0.svn14).

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

  ViewVC Help
Powered by ViewVC