/[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 3116 - (show annotations) (download)
Sat Apr 15 20:21:41 2017 UTC (7 years ago) by schoenebeck
File size: 139527 byte(s)
* Fixed crash on saving file if a sample was requested to be
  replaced twice by the user.
* Bumped version (1.0.0.svn28).

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

  ViewVC Help
Powered by ViewVC