/[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 3021 - (show annotations) (download)
Sun Oct 23 07:24:40 2016 UTC (7 years, 5 months ago) by persson
File size: 138528 byte(s)
* windows, 32-bit: fixed crashes by making sure the stack in sub
  threads is 16-byte aligned

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

  ViewVC Help
Powered by ViewVC