/[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 3157 - (show annotations) (download)
Mon May 8 17:30:10 2017 UTC (2 years, 9 months ago) by schoenebeck
File size: 154801 byte(s)
* Fixed potentially unhandled exceptions when app is loading
  its config file.
* WIP: Introduced user configurable list of macros which are
  auto assigned to keyboard accelerators (F1 ... F12) and
  saved along with the app's config file (added under new
  main menu bar section "Macro").
* Bumped version (1.0.0.svn38).

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 #include <gtkmm/accelmap.h>
42 #if GTKMM_MAJOR_VERSION < 3
43 #include "wrapLabel.hh"
44 #endif
45
46 #include "global.h"
47 #include "compat.h"
48
49 #include <stdio.h>
50 #include <sndfile.h>
51 #include <assert.h>
52
53 #include "mainwindow.h"
54 #include "Settings.h"
55 #include "CombineInstrumentsDialog.h"
56 #include "scripteditor.h"
57 #include "scriptslots.h"
58 #include "ReferencesView.h"
59 #include "../../gfx/status_attached.xpm"
60 #include "../../gfx/status_detached.xpm"
61 #include "gfx/builtinpix.h"
62 #include "MacroEditor.h"
63 #include "MacrosSetup.h"
64
65 MainWindow::MainWindow() :
66 m_DimRegionChooser(*this),
67 dimreg_label(_("Changes apply to:")),
68 dimreg_all_regions(_("all regions")),
69 dimreg_all_dimregs(_("all dimension splits")),
70 dimreg_stereo(_("both channels")),
71 labelLegend(_("Legend:")),
72 labelNoSample(_(" No Sample")),
73 labelMissingSample(_(" Missing some Sample(s)")),
74 labelLooped(_(" Looped")),
75 labelSomeLoops(_(" Some Loop(s)"))
76 {
77 loadBuiltInPix();
78
79 // set_border_width(5);
80 // set_default_size(400, 200);
81
82 add(m_VBox);
83
84 // Handle selection
85 m_TreeView.get_selection()->signal_changed().connect(
86 sigc::mem_fun(*this, &MainWindow::on_sel_change));
87
88 // m_TreeView.set_reorderable();
89
90 m_TreeView.signal_button_press_event().connect_notify(
91 sigc::mem_fun(*this, &MainWindow::on_button_release));
92
93 // Add the TreeView tab, inside a ScrolledWindow, with the button underneath:
94 m_ScrolledWindow.add(m_TreeView);
95 // m_ScrolledWindow.set_size_request(200, 600);
96 m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
97
98 m_ScrolledWindowSamples.add(m_TreeViewSamples);
99 m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
100
101 m_ScrolledWindowScripts.add(m_TreeViewScripts);
102 m_ScrolledWindowScripts.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
103
104
105 m_TreeViewNotebook.set_size_request(300);
106
107 m_HPaned.add1(m_TreeViewNotebook);
108 dimreg_hbox.add(dimreg_label);
109 dimreg_hbox.add(dimreg_all_regions);
110 dimreg_hbox.add(dimreg_all_dimregs);
111 dimreg_stereo.set_active();
112 dimreg_hbox.add(dimreg_stereo);
113 dimreg_vbox.add(dimreg_edit);
114 dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
115 {
116 legend_hbox.add(labelLegend);
117
118 imageNoSample.set(redDot);
119 imageNoSample.set_alignment(Gtk::ALIGN_END);
120 labelNoSample.set_alignment(Gtk::ALIGN_START);
121 legend_hbox.add(imageNoSample);
122 legend_hbox.add(labelNoSample);
123
124 imageMissingSample.set(yellowDot);
125 imageMissingSample.set_alignment(Gtk::ALIGN_END);
126 labelMissingSample.set_alignment(Gtk::ALIGN_START);
127 legend_hbox.add(imageMissingSample);
128 legend_hbox.add(labelMissingSample);
129
130 imageLooped.set(blackLoop);
131 imageLooped.set_alignment(Gtk::ALIGN_END);
132 labelLooped.set_alignment(Gtk::ALIGN_START);
133 legend_hbox.add(imageLooped);
134 legend_hbox.add(labelLooped);
135
136 imageSomeLoops.set(grayLoop);
137 imageSomeLoops.set_alignment(Gtk::ALIGN_END);
138 labelSomeLoops.set_alignment(Gtk::ALIGN_START);
139 legend_hbox.add(imageSomeLoops);
140 legend_hbox.add(labelSomeLoops);
141
142 legend_hbox.show_all_children();
143 }
144 dimreg_vbox.pack_start(legend_hbox, Gtk::PACK_SHRINK);
145 m_HPaned.add2(dimreg_vbox);
146
147 dimreg_label.set_tooltip_text(_("To automatically apply your changes above globally to the entire instrument, check all 3 check boxes on the right."));
148 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."));
149 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."));
150 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)."));
151
152 m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, _("Samples"));
153 m_TreeViewNotebook.append_page(m_ScrolledWindow, _("Instruments"));
154 m_TreeViewNotebook.append_page(m_ScrolledWindowScripts, _("Scripts"));
155
156 actionGroup = Gtk::ActionGroup::create();
157
158 actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
159 actionGroup->add(Gtk::Action::create("New", _("_New")),
160 Gtk::AccelKey("<control>n"),
161 sigc::mem_fun(
162 *this, &MainWindow::on_action_file_new));
163 actionGroup->add(Gtk::Action::create("Open", _("_Open...")),
164 Gtk::AccelKey("<control>o"),
165 sigc::mem_fun(
166 *this, &MainWindow::on_action_file_open));
167 actionGroup->add(Gtk::Action::create("Save", _("_Save")),
168 Gtk::AccelKey("<control>s"),
169 sigc::mem_fun(
170 *this, &MainWindow::on_action_file_save));
171 actionGroup->add(Gtk::Action::create("SaveAs", _("Save _As...")),
172 Gtk::AccelKey("<shift><control>s"),
173 sigc::mem_fun(
174 *this, &MainWindow::on_action_file_save_as));
175 actionGroup->add(Gtk::Action::create("Properties",
176 _("_Properties")),
177 sigc::mem_fun(
178 *this, &MainWindow::on_action_file_properties));
179 actionGroup->add(Gtk::Action::create("InstrProperties",
180 _("_Properties")),
181 sigc::mem_fun(
182 *this, &MainWindow::show_instr_props));
183 actionGroup->add(Gtk::Action::create("MidiRules",
184 _("_Midi Rules...")),
185 sigc::mem_fun(
186 *this, &MainWindow::show_midi_rules));
187 actionGroup->add(Gtk::Action::create("ScriptSlots",
188 _("_Script Slots...")),
189 sigc::mem_fun(
190 *this, &MainWindow::show_script_slots));
191 actionGroup->add(Gtk::Action::create("Quit", _("_Quit")),
192 Gtk::AccelKey("<control>q"),
193 sigc::mem_fun(
194 *this, &MainWindow::on_action_quit));
195 actionGroup->add(
196 Gtk::Action::create("MenuSample", _("_Sample")),
197 sigc::mem_fun(*this, &MainWindow::show_samples_tab)
198 );
199 actionGroup->add(
200 Gtk::Action::create("MenuInstrument", _("_Instrument")),
201 sigc::mem_fun(*this, &MainWindow::show_intruments_tab)
202 );
203 actionGroup->add(
204 Gtk::Action::create("MenuScript", _("Scr_ipt")),
205 sigc::mem_fun(*this, &MainWindow::show_scripts_tab)
206 );
207 actionGroup->add(Gtk::Action::create("AllInstruments", _("_Select")));
208
209 actionGroup->add(Gtk::Action::create("MenuEdit", _("_Edit")));
210
211 const Gdk::ModifierType primaryModifierKey =
212 #if defined(__APPLE__)
213 Gdk::META_MASK; // Cmd key on Mac
214 #else
215 Gdk::CONTROL_MASK; // Ctrl key on all other OSs
216 #endif
217
218 actionGroup->add(Gtk::Action::create("CopyDimRgn",
219 _("Copy selected dimension region")),
220 Gtk::AccelKey(GDK_KEY_c, Gdk::MOD1_MASK),
221 sigc::mem_fun(*this, &MainWindow::copy_selected_dimrgn));
222
223 actionGroup->add(Gtk::Action::create("PasteDimRgn",
224 _("Paste dimension region")),
225 Gtk::AccelKey(GDK_KEY_v, Gdk::MOD1_MASK),
226 sigc::mem_fun(*this, &MainWindow::paste_copied_dimrgn));
227
228 actionGroup->add(Gtk::Action::create("AdjustClipboard",
229 _("Adjust Clipboard Content")),
230 Gtk::AccelKey(GDK_KEY_x, Gdk::MOD1_MASK),
231 sigc::mem_fun(*this, &MainWindow::adjust_clipboard_content));
232
233 actionGroup->add(Gtk::Action::create("SelectPrevRegion",
234 _("Select Previous Region")),
235 Gtk::AccelKey(GDK_KEY_Left, primaryModifierKey),
236 sigc::mem_fun(*this, &MainWindow::select_prev_region));
237
238 actionGroup->add(Gtk::Action::create("SelectNextRegion",
239 _("Select Next Region")),
240 Gtk::AccelKey(GDK_KEY_Right, primaryModifierKey),
241 sigc::mem_fun(*this, &MainWindow::select_next_region));
242
243 actionGroup->add(Gtk::Action::create("SelectPrevDimRgnZone",
244 _("Select Previous Dimension Region Zone")),
245 Gtk::AccelKey(GDK_KEY_Left, Gdk::MOD1_MASK),
246 sigc::mem_fun(*this, &MainWindow::select_prev_dim_rgn_zone));
247
248 actionGroup->add(Gtk::Action::create("SelectNextDimRgnZone",
249 _("Select Next Dimension Region Zone")),
250 Gtk::AccelKey(GDK_KEY_Right, Gdk::MOD1_MASK),
251 sigc::mem_fun(*this, &MainWindow::select_next_dim_rgn_zone));
252
253 actionGroup->add(Gtk::Action::create("SelectPrevDimension",
254 _("Select Previous Dimension")),
255 Gtk::AccelKey(GDK_KEY_Up, Gdk::MOD1_MASK),
256 sigc::mem_fun(*this, &MainWindow::select_prev_dimension));
257
258 actionGroup->add(Gtk::Action::create("SelectNextDimension",
259 _("Select Next Dimension")),
260 Gtk::AccelKey(GDK_KEY_Down, Gdk::MOD1_MASK),
261 sigc::mem_fun(*this, &MainWindow::select_next_dimension));
262
263 actionGroup->add(Gtk::Action::create("SelectAddPrevDimRgnZone",
264 _("Add Previous Dimension Region Zone to Selection")),
265 Gtk::AccelKey(GDK_KEY_Left, Gdk::MOD1_MASK | Gdk::SHIFT_MASK),
266 sigc::mem_fun(*this, &MainWindow::select_add_prev_dim_rgn_zone));
267
268 actionGroup->add(Gtk::Action::create("SelectAddNextDimRgnZone",
269 _("Add Next Dimension Region Zone to Selection")),
270 Gtk::AccelKey(GDK_KEY_Right, Gdk::MOD1_MASK | Gdk::SHIFT_MASK),
271 sigc::mem_fun(*this, &MainWindow::select_add_next_dim_rgn_zone));
272
273 Glib::RefPtr<Gtk::ToggleAction> toggle_action =
274 Gtk::ToggleAction::create("CopySampleUnity", _("Copy Sample's _Unity Note"));
275 toggle_action->set_active(true);
276 actionGroup->add(toggle_action);
277
278 toggle_action =
279 Gtk::ToggleAction::create("CopySampleTune", _("Copy Sample's _Fine Tune"));
280 toggle_action->set_active(true);
281 actionGroup->add(toggle_action);
282
283 toggle_action =
284 Gtk::ToggleAction::create("CopySampleLoop", _("Copy Sample's _Loop Points"));
285 toggle_action->set_active(true);
286 actionGroup->add(toggle_action);
287
288
289 actionGroup->add(Gtk::Action::create("MenuMacro", _("_Macro")));
290
291
292 actionGroup->add(Gtk::Action::create("MenuView", _("Vie_w")));
293 toggle_action =
294 Gtk::ToggleAction::create("Statusbar", _("_Statusbar"));
295 toggle_action->set_active(true);
296 actionGroup->add(toggle_action,
297 sigc::mem_fun(
298 *this, &MainWindow::on_action_view_status_bar));
299
300 toggle_action =
301 Gtk::ToggleAction::create("AutoRestoreWinDim", _("_Auto Restore Window Dimension"));
302 toggle_action->set_active(Settings::singleton()->autoRestoreWindowDimension);
303 actionGroup->add(toggle_action,
304 sigc::mem_fun(
305 *this, &MainWindow::on_auto_restore_win_dim));
306
307 toggle_action =
308 Gtk::ToggleAction::create("SaveWithTemporaryFile", _("Save with _temporary file"));
309 toggle_action->set_active(Settings::singleton()->saveWithTemporaryFile);
310 actionGroup->add(toggle_action,
311 sigc::mem_fun(
312 *this, &MainWindow::on_save_with_temporary_file));
313
314 actionGroup->add(
315 Gtk::Action::create("RefreshAll", _("_Refresh All")),
316 sigc::mem_fun(*this, &MainWindow::on_action_refresh_all)
317 );
318
319 actionGroup->add(Gtk::Action::create("MenuHelp", _("_Help")));
320 actionGroup->add(Gtk::Action::create("About", _("_About")),
321 sigc::mem_fun(
322 *this, &MainWindow::on_action_help_about));
323 actionGroup->add(
324 Gtk::Action::create("AddInstrument", _("Add _Instrument")),
325 sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
326 );
327 actionGroup->add(
328 Gtk::Action::create("DupInstrument", _("_Duplicate Instrument")),
329 sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument)
330 );
331 actionGroup->add(
332 Gtk::Action::create("RemoveInstrument", _("_Remove")),
333 sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
334 );
335
336
337 actionGroup->add(Gtk::Action::create("MenuSettings", _("_Settings")));
338
339 toggle_action =
340 Gtk::ToggleAction::create("WarnUserOnExtensions", _("Show warning on format _extensions"));
341 toggle_action->set_active(Settings::singleton()->warnUserOnExtensions);
342 actionGroup->add(
343 toggle_action,
344 sigc::mem_fun(*this, &MainWindow::on_action_warn_user_on_extensions)
345 );
346
347 toggle_action =
348 Gtk::ToggleAction::create("SyncSamplerInstrumentSelection", _("Synchronize sampler's instrument selection"));
349 toggle_action->set_active(Settings::singleton()->syncSamplerInstrumentSelection);
350 actionGroup->add(
351 toggle_action,
352 sigc::mem_fun(*this, &MainWindow::on_action_sync_sampler_instrument_selection)
353 );
354
355 toggle_action =
356 Gtk::ToggleAction::create("MoveRootNoteWithRegionMoved", _("Move root note with region moved"));
357 toggle_action->set_active(Settings::singleton()->moveRootNoteWithRegionMoved);
358 actionGroup->add(
359 toggle_action,
360 sigc::mem_fun(*this, &MainWindow::on_action_move_root_note_with_region_moved)
361 );
362
363
364 actionGroup->add(Gtk::Action::create("MenuTools", _("_Tools")));
365
366 actionGroup->add(
367 Gtk::Action::create("CombineInstruments", _("_Combine Instruments...")),
368 sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments)
369 );
370
371 actionGroup->add(
372 Gtk::Action::create("MergeFiles", _("_Merge Files...")),
373 sigc::mem_fun(*this, &MainWindow::on_action_merge_files)
374 );
375
376
377 // sample right-click popup actions
378 actionGroup->add(
379 Gtk::Action::create("SampleProperties", _("_Properties")),
380 sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
381 );
382 actionGroup->add(
383 Gtk::Action::create("AddGroup", _("Add _Group")),
384 sigc::mem_fun(*this, &MainWindow::on_action_add_group)
385 );
386 actionGroup->add(
387 Gtk::Action::create("AddSample", _("Add _Sample(s)...")),
388 sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
389 );
390 actionGroup->add(
391 Gtk::Action::create("RemoveSample", _("_Remove")),
392 sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
393 );
394 actionGroup->add(
395 Gtk::Action::create("RemoveUnusedSamples", _("Remove _Unused Samples")),
396 sigc::mem_fun(*this, &MainWindow::on_action_remove_unused_samples)
397 );
398 actionGroup->add(
399 Gtk::Action::create("ShowSampleRefs", _("Show References...")),
400 sigc::mem_fun(*this, &MainWindow::on_action_view_references)
401 );
402 actionGroup->add(
403 Gtk::Action::create("ReplaceSample",
404 _("Replace Sample...")),
405 sigc::mem_fun(*this, &MainWindow::on_action_replace_sample)
406 );
407 actionGroup->add(
408 Gtk::Action::create("ReplaceAllSamplesInAllGroups",
409 _("Replace All Samples in All Groups...")),
410 sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups)
411 );
412
413 // script right-click popup actions
414 actionGroup->add(
415 Gtk::Action::create("AddScriptGroup", _("Add _Group")),
416 sigc::mem_fun(*this, &MainWindow::on_action_add_script_group)
417 );
418 actionGroup->add(
419 Gtk::Action::create("AddScript", _("Add _Script")),
420 sigc::mem_fun(*this, &MainWindow::on_action_add_script)
421 );
422 actionGroup->add(
423 Gtk::Action::create("EditScript", _("_Edit Script...")),
424 sigc::mem_fun(*this, &MainWindow::on_action_edit_script)
425 );
426 actionGroup->add(
427 Gtk::Action::create("RemoveScript", _("_Remove")),
428 sigc::mem_fun(*this, &MainWindow::on_action_remove_script)
429 );
430
431 uiManager = Gtk::UIManager::create();
432 uiManager->insert_action_group(actionGroup);
433 add_accel_group(uiManager->get_accel_group());
434
435 Glib::ustring ui_info =
436 "<ui>"
437 " <menubar name='MenuBar'>"
438 " <menu action='MenuFile'>"
439 " <menuitem action='New'/>"
440 " <menuitem action='Open'/>"
441 " <separator/>"
442 " <menuitem action='Save'/>"
443 " <menuitem action='SaveAs'/>"
444 " <separator/>"
445 " <menuitem action='Properties'/>"
446 " <separator/>"
447 " <menuitem action='Quit'/>"
448 " </menu>"
449 " <menu action='MenuEdit'>"
450 " <menuitem action='CopyDimRgn'/>"
451 " <menuitem action='AdjustClipboard'/>"
452 " <menuitem action='PasteDimRgn'/>"
453 " <separator/>"
454 " <menuitem action='SelectPrevRegion'/>"
455 " <menuitem action='SelectNextRegion'/>"
456 " <separator/>"
457 " <menuitem action='SelectPrevDimension'/>"
458 " <menuitem action='SelectNextDimension'/>"
459 " <menuitem action='SelectPrevDimRgnZone'/>"
460 " <menuitem action='SelectNextDimRgnZone'/>"
461 " <menuitem action='SelectAddPrevDimRgnZone'/>"
462 " <menuitem action='SelectAddNextDimRgnZone'/>"
463 " <separator/>"
464 " <menuitem action='CopySampleUnity'/>"
465 " <menuitem action='CopySampleTune'/>"
466 " <menuitem action='CopySampleLoop'/>"
467 " </menu>"
468 " <menu action='MenuMacro'>"
469 " </menu>"
470 " <menu action='MenuSample'>"
471 " <menuitem action='SampleProperties'/>"
472 " <menuitem action='AddGroup'/>"
473 " <menuitem action='AddSample'/>"
474 " <menuitem action='ShowSampleRefs'/>"
475 " <menuitem action='ReplaceSample' />"
476 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
477 " <separator/>"
478 " <menuitem action='RemoveSample'/>"
479 " <menuitem action='RemoveUnusedSamples'/>"
480 " </menu>"
481 " <menu action='MenuInstrument'>"
482 " <menu action='AllInstruments'>"
483 " </menu>"
484 " <separator/>"
485 " <menuitem action='InstrProperties'/>"
486 " <menuitem action='MidiRules'/>"
487 " <menuitem action='ScriptSlots'/>"
488 " <menuitem action='AddInstrument'/>"
489 " <menuitem action='DupInstrument'/>"
490 " <separator/>"
491 " <menuitem action='RemoveInstrument'/>"
492 " </menu>"
493 " <menu action='MenuScript'>"
494 " <menuitem action='AddScriptGroup'/>"
495 " <menuitem action='AddScript'/>"
496 " <menuitem action='EditScript'/>"
497 " <separator/>"
498 " <menuitem action='RemoveScript'/>"
499 " </menu>"
500 " <menu action='MenuView'>"
501 " <menuitem action='Statusbar'/>"
502 " <menuitem action='AutoRestoreWinDim'/>"
503 " <separator/>"
504 " <menuitem action='RefreshAll'/>"
505 " </menu>"
506 " <menu action='MenuTools'>"
507 " <menuitem action='CombineInstruments'/>"
508 " <menuitem action='MergeFiles'/>"
509 " </menu>"
510 " <menu action='MenuSettings'>"
511 " <menuitem action='WarnUserOnExtensions'/>"
512 " <menuitem action='SyncSamplerInstrumentSelection'/>"
513 " <menuitem action='MoveRootNoteWithRegionMoved'/>"
514 " <menuitem action='SaveWithTemporaryFile'/>"
515 " </menu>"
516 " <menu action='MenuHelp'>"
517 " <menuitem action='About'/>"
518 " </menu>"
519 " </menubar>"
520 " <popup name='PopupMenu'>"
521 " <menuitem action='InstrProperties'/>"
522 " <menuitem action='MidiRules'/>"
523 " <menuitem action='ScriptSlots'/>"
524 " <menuitem action='AddInstrument'/>"
525 " <menuitem action='DupInstrument'/>"
526 " <separator/>"
527 " <menuitem action='RemoveInstrument'/>"
528 " </popup>"
529 " <popup name='SamplePopupMenu'>"
530 " <menuitem action='SampleProperties'/>"
531 " <menuitem action='AddGroup'/>"
532 " <menuitem action='AddSample'/>"
533 " <menuitem action='ShowSampleRefs'/>"
534 " <menuitem action='ReplaceSample' />"
535 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
536 " <separator/>"
537 " <menuitem action='RemoveSample'/>"
538 " <menuitem action='RemoveUnusedSamples'/>"
539 " </popup>"
540 " <popup name='ScriptPopupMenu'>"
541 " <menuitem action='AddScriptGroup'/>"
542 " <menuitem action='AddScript'/>"
543 " <menuitem action='EditScript'/>"
544 " <separator/>"
545 " <menuitem action='RemoveScript'/>"
546 " </popup>"
547 "</ui>";
548 uiManager->add_ui_from_string(ui_info);
549
550 popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
551
552 // Set tooltips for menu items (for some reason, setting a tooltip on the
553 // respective Gtk::Action objects above will simply be ignored, no matter
554 // if using Gtk::Action::set_tooltip() or passing the tooltip string on
555 // Gtk::Action::create()).
556 {
557 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
558 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleUnity"));
559 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."));
560 }
561 {
562 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
563 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleTune"));
564 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."));
565 }
566 {
567 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
568 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleLoop"));
569 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."));
570 }
571 {
572 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
573 uiManager->get_widget("/MenuBar/MenuSettings/WarnUserOnExtensions"));
574 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."));
575 }
576 {
577 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
578 uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection"));
579 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)."));
580 }
581 {
582 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
583 uiManager->get_widget("/MenuBar/MenuSettings/MoveRootNoteWithRegionMoved"));
584 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."));
585 }
586 {
587 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
588 uiManager->get_widget("/MenuBar/MenuSample/RemoveUnusedSamples"));
589 item->set_tooltip_text(_("Removes all samples that are not referenced by any instrument (i.e. red ones)."));
590 // copy tooltip to popup menu
591 Gtk::MenuItem* item2 = dynamic_cast<Gtk::MenuItem*>(
592 uiManager->get_widget("/SamplePopupMenu/RemoveUnusedSamples"));
593 item2->set_tooltip_text(item->get_tooltip_text());
594 }
595 {
596 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
597 uiManager->get_widget("/MenuBar/MenuView/RefreshAll"));
598 item->set_tooltip_text(_("Reloads the currently open gig file and updates the entire graphical user interface."));
599 }
600 {
601 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
602 uiManager->get_widget("/MenuBar/MenuView/AutoRestoreWinDim"));
603 item->set_tooltip_text(_("If checked, size and position of all windows will be saved and automatically restored next time."));
604 }
605 {
606 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
607 uiManager->get_widget("/MenuBar/MenuTools/CombineInstruments"));
608 item->set_tooltip_text(_("Create combi sounds out of individual sounds of this .gig file."));
609 }
610 {
611 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
612 uiManager->get_widget("/MenuBar/MenuTools/MergeFiles"));
613 item->set_tooltip_text(_("Add instruments and samples of other .gig files to this .gig file."));
614 }
615
616
617 instrument_menu = static_cast<Gtk::MenuItem*>(
618 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments"))->get_submenu();
619
620 Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar");
621 m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
622 m_VBox.pack_start(m_HPaned);
623 m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
624 m_VBox.pack_start(m_RegionChooser.m_VirtKeybPropsBox, Gtk::PACK_SHRINK);
625 m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
626 m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
627
628 set_file_is_shared(false);
629
630 // Status Bar:
631 m_StatusBar.pack_start(m_AttachedStateLabel, Gtk::PACK_SHRINK);
632 m_StatusBar.pack_start(m_AttachedStateImage, Gtk::PACK_SHRINK);
633 m_StatusBar.show();
634
635 m_RegionChooser.signal_region_selected().connect(
636 sigc::mem_fun(*this, &MainWindow::region_changed) );
637 m_DimRegionChooser.signal_dimregion_selected().connect(
638 sigc::mem_fun(*this, &MainWindow::dimreg_changed) );
639
640
641 // Create the Tree model:
642 m_refTreeModel = Gtk::ListStore::create(m_Columns);
643 m_TreeView.set_model(m_refTreeModel);
644 m_TreeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
645 m_TreeView.set_tooltip_text(_("Right click here for actions on instruments & MIDI Rules. Drag & drop to change the order of instruments."));
646 instrument_name_connection = m_refTreeModel->signal_row_changed().connect(
647 sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
648 );
649
650 // Add the TreeView's view columns:
651 m_TreeView.append_column(_("Nr"), m_Columns.m_col_nr);
652 m_TreeView.append_column_editable(_("Instrument"), m_Columns.m_col_name);
653 m_TreeView.set_headers_visible(true);
654
655 // establish drag&drop within the instrument tree view, allowing to reorder
656 // the sequence of instruments within the gig file
657 {
658 std::vector<Gtk::TargetEntry> drag_target_instrument;
659 drag_target_instrument.push_back(Gtk::TargetEntry("gig::Instrument"));
660 m_TreeView.drag_source_set(drag_target_instrument);
661 m_TreeView.drag_dest_set(drag_target_instrument);
662 m_TreeView.signal_drag_begin().connect(
663 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_begin)
664 );
665 m_TreeView.signal_drag_data_get().connect(
666 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_data_get)
667 );
668 m_TreeView.signal_drag_data_received().connect(
669 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drop_drag_data_received)
670 );
671 }
672
673 // create samples treeview (including its data model)
674 m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
675 m_TreeViewSamples.set_model(m_refSamplesTreeModel);
676 m_TreeViewSamples.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
677 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."));
678 // m_TreeViewSamples.set_reorderable();
679 m_TreeViewSamples.append_column_editable(_("Name"), m_SamplesModel.m_col_name);
680 m_TreeViewSamples.append_column(_("Referenced"), m_SamplesModel.m_col_refcount);
681 {
682 Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(0);
683 Gtk::CellRendererText* cellrenderer =
684 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
685 column->add_attribute(
686 cellrenderer->property_foreground(), m_SamplesModel.m_color
687 );
688 }
689 {
690 Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(1);
691 Gtk::CellRendererText* cellrenderer =
692 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
693 column->add_attribute(
694 cellrenderer->property_foreground(), m_SamplesModel.m_color
695 );
696 }
697 m_TreeViewSamples.set_headers_visible(true);
698 m_TreeViewSamples.signal_button_press_event().connect_notify(
699 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
700 );
701 m_refSamplesTreeModel->signal_row_changed().connect(
702 sigc::mem_fun(*this, &MainWindow::sample_name_changed)
703 );
704
705 // create scripts treeview (including its data model)
706 m_refScriptsTreeModel = ScriptsTreeStore::create(m_ScriptsModel);
707 m_TreeViewScripts.set_model(m_refScriptsTreeModel);
708 m_TreeViewScripts.set_tooltip_text(_(
709 "Use CTRL + double click for editing a script."
710 "\n\n"
711 "Note: instrument scripts are a LinuxSampler extension of the gig "
712 "format. This feature will not work with the GigaStudio software!"
713 ));
714 // m_TreeViewScripts.set_reorderable();
715 m_TreeViewScripts.append_column_editable("Samples", m_ScriptsModel.m_col_name);
716 m_TreeViewScripts.set_headers_visible(false);
717 m_TreeViewScripts.signal_button_press_event().connect_notify(
718 sigc::mem_fun(*this, &MainWindow::on_script_treeview_button_release)
719 );
720 //FIXME: why the heck does this double click signal_row_activated() only fire while CTRL key is pressed ?
721 m_TreeViewScripts.signal_row_activated().connect(
722 sigc::mem_fun(*this, &MainWindow::script_double_clicked)
723 );
724 m_refScriptsTreeModel->signal_row_changed().connect(
725 sigc::mem_fun(*this, &MainWindow::script_name_changed)
726 );
727
728 // establish drag&drop between scripts tree view and ScriptSlots window
729 std::vector<Gtk::TargetEntry> drag_target_gig_script;
730 drag_target_gig_script.push_back(Gtk::TargetEntry("gig::Script"));
731 m_TreeViewScripts.drag_source_set(drag_target_gig_script);
732 m_TreeViewScripts.signal_drag_begin().connect(
733 sigc::mem_fun(*this, &MainWindow::on_scripts_treeview_drag_begin)
734 );
735 m_TreeViewScripts.signal_drag_data_get().connect(
736 sigc::mem_fun(*this, &MainWindow::on_scripts_treeview_drag_data_get)
737 );
738
739 // establish drag&drop between samples tree view and dimension region 'Sample' text entry
740 std::vector<Gtk::TargetEntry> drag_target_gig_sample;
741 drag_target_gig_sample.push_back(Gtk::TargetEntry("gig::Sample"));
742 m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
743 m_TreeViewSamples.signal_drag_begin().connect(
744 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_begin)
745 );
746 m_TreeViewSamples.signal_drag_data_get().connect(
747 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get)
748 );
749 dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample);
750 dimreg_edit.wSample->signal_drag_data_received().connect(
751 sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
752 );
753 dimreg_edit.signal_dimreg_changed().connect(
754 sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
755 m_RegionChooser.signal_instrument_changed().connect(
756 sigc::mem_fun(*this, &MainWindow::file_changed));
757 m_RegionChooser.signal_instrument_changed().connect(
758 sigc::mem_fun(*this, &MainWindow::region_changed));
759 m_DimRegionChooser.signal_region_changed().connect(
760 sigc::mem_fun(*this, &MainWindow::file_changed));
761 instrumentProps.signal_changed().connect(
762 sigc::mem_fun(*this, &MainWindow::file_changed));
763 propDialog.signal_changed().connect(
764 sigc::mem_fun(*this, &MainWindow::file_changed));
765 midiRules.signal_changed().connect(
766 sigc::mem_fun(*this, &MainWindow::file_changed));
767
768 dimreg_edit.signal_dimreg_to_be_changed().connect(
769 dimreg_to_be_changed_signal.make_slot());
770 dimreg_edit.signal_dimreg_changed().connect(
771 dimreg_changed_signal.make_slot());
772 dimreg_edit.signal_sample_ref_changed().connect(
773 sample_ref_changed_signal.make_slot());
774 sample_ref_changed_signal.connect(
775 sigc::mem_fun(*this, &MainWindow::on_sample_ref_changed)
776 );
777 samples_to_be_removed_signal.connect(
778 sigc::mem_fun(*this, &MainWindow::on_samples_to_be_removed)
779 );
780
781 dimreg_edit.signal_select_sample().connect(
782 sigc::mem_fun(*this, &MainWindow::select_sample)
783 );
784
785 m_RegionChooser.signal_instrument_struct_to_be_changed().connect(
786 sigc::hide(
787 sigc::bind(
788 file_structure_to_be_changed_signal.make_slot(),
789 sigc::ref(this->file)
790 )
791 )
792 );
793 m_RegionChooser.signal_instrument_struct_changed().connect(
794 sigc::hide(
795 sigc::bind(
796 file_structure_changed_signal.make_slot(),
797 sigc::ref(this->file)
798 )
799 )
800 );
801 m_RegionChooser.signal_region_to_be_changed().connect(
802 region_to_be_changed_signal.make_slot());
803 m_RegionChooser.signal_region_changed_signal().connect(
804 region_changed_signal.make_slot());
805
806 note_on_signal.connect(
807 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_on_event));
808 note_off_signal.connect(
809 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_off_event));
810
811 dimreg_all_regions.signal_toggled().connect(
812 sigc::mem_fun(*this, &MainWindow::update_dimregs));
813 dimreg_all_dimregs.signal_toggled().connect(
814 sigc::mem_fun(*this, &MainWindow::dimreg_all_dimregs_toggled));
815 dimreg_stereo.signal_toggled().connect(
816 sigc::mem_fun(*this, &MainWindow::update_dimregs));
817
818 file = 0;
819 file_is_changed = false;
820
821 show_all_children();
822
823 // start with a new gig file by default
824 on_action_file_new();
825
826 // select 'Instruments' tab by default
827 // (gtk allows this only if the tab childs are visible, thats why it's here)
828 m_TreeViewNotebook.set_current_page(1);
829
830 Gtk::Clipboard::get()->signal_owner_change().connect(
831 sigc::mem_fun(*this, &MainWindow::on_clipboard_owner_change)
832 );
833 updateClipboardPasteAvailable();
834 updateClipboardCopyAvailable();
835
836 // setup macros and their keyboard accelerators
837 {
838 Gtk::Menu* menuMacro = dynamic_cast<Gtk::MenuItem*>(
839 uiManager->get_widget("/MenuBar/MenuMacro")
840 )->get_submenu();
841
842 const Gdk::ModifierType primaryModifierKey =
843 #if defined(__APPLE__)
844 Gdk::META_MASK; // Cmd key on Mac
845 #else
846 Gdk::CONTROL_MASK; // Ctrl key on all other OSs
847 #endif
848
849 const Gdk::ModifierType noModifier = (Gdk::ModifierType)0;
850 Gtk::AccelMap::add_entry("<Macros>/macro_0", GDK_KEY_F1, noModifier);
851 Gtk::AccelMap::add_entry("<Macros>/macro_1", GDK_KEY_F2, noModifier);
852 Gtk::AccelMap::add_entry("<Macros>/macro_2", GDK_KEY_F3, noModifier);
853 Gtk::AccelMap::add_entry("<Macros>/macro_3", GDK_KEY_F4, noModifier);
854 Gtk::AccelMap::add_entry("<Macros>/macro_4", GDK_KEY_F5, noModifier);
855 Gtk::AccelMap::add_entry("<Macros>/macro_5", GDK_KEY_F6, noModifier);
856 Gtk::AccelMap::add_entry("<Macros>/macro_6", GDK_KEY_F7, noModifier);
857 Gtk::AccelMap::add_entry("<Macros>/macro_7", GDK_KEY_F8, noModifier);
858 Gtk::AccelMap::add_entry("<Macros>/macro_8", GDK_KEY_F9, noModifier);
859 Gtk::AccelMap::add_entry("<Macros>/macro_9", GDK_KEY_F10, noModifier);
860 Gtk::AccelMap::add_entry("<Macros>/macro_10", GDK_KEY_F11, noModifier);
861 Gtk::AccelMap::add_entry("<Macros>/macro_11", GDK_KEY_F12, noModifier);
862 Gtk::AccelMap::add_entry("<Macros>/SetupMacros", 'm', primaryModifierKey);
863
864 Glib::RefPtr<Gtk::AccelGroup> accelGroup = this->get_accel_group();
865 menuMacro->set_accel_group(accelGroup);
866
867 updateMacroMenu();
868 }
869 }
870
871 MainWindow::~MainWindow()
872 {
873 }
874
875 void MainWindow::updateMacroMenu() {
876 Gtk::Menu* menuMacro = dynamic_cast<Gtk::MenuItem*>(
877 uiManager->get_widget("/MenuBar/MenuMacro")
878 )->get_submenu();
879
880 // remove all entries from "Macro" menu
881 {
882 const std::vector<Gtk::Widget*> children = menuMacro->get_children();
883 for (int i = 0; i < children.size(); ++i) {
884 Gtk::Widget* child = children[i];
885 menuMacro->remove(*child);
886 delete child;
887 }
888 }
889
890 // (re)load all macros from config file
891 try {
892 Settings::singleton()->loadMacros(m_macros);
893 } catch (Serialization::Exception e) {
894 std::cerr << "Exception while loading macros: " << e.Message << std::endl;
895 } catch (...) {
896 std::cerr << "Unknown exception while loading macros!" << std::endl;
897 }
898
899 // add all configured macros as menu items to the "Macro" menu
900 for (int iMacro = 0; iMacro < m_macros.size(); ++iMacro) {
901 const Serialization::Archive& macro = m_macros[iMacro];
902 std::string name =
903 macro.name().empty() ?
904 (std::string(_("Unnamed Macro")) + " " + ToString(iMacro+1)) : macro.name();
905 Gtk::MenuItem* item = new Gtk::MenuItem(name);
906 item->signal_activate().connect(
907 sigc::bind(
908 sigc::mem_fun(*this, &MainWindow::onMacroSelected), iMacro
909 )
910 );
911 menuMacro->append(*item);
912 item->set_accel_path("<Macros>/macro_" + ToString(iMacro));
913 }
914 // if there are no macros configured at all, then show a dummy entry instead
915 if (m_macros.empty()) {
916 Gtk::MenuItem* item = new Gtk::MenuItem(_("No Macros"));
917 item->set_sensitive(false);
918 menuMacro->append(*item);
919 }
920
921 // add separator line to menu
922 menuMacro->append(*new Gtk::SeparatorMenuItem);
923
924 {
925 Gtk::MenuItem* item = new Gtk::MenuItem(_("Setup Macros ..."));
926 item->signal_activate().connect(
927 sigc::mem_fun(*this, &MainWindow::setupMacros)
928 );
929 menuMacro->append(*item);
930 item->set_accel_path("<Macros>/SetupMacros");
931 }
932
933 menuMacro->show_all_children();
934 }
935
936 void MainWindow::onMacroSelected(int iMacro) {
937 printf("onMacroSelected(%d)\n", iMacro);
938 if (iMacro < 0 || iMacro >= m_macros.size()) return;
939 Glib::ustring errorText;
940 try {
941 applyMacro(m_macros[iMacro]);
942 } catch (Serialization::Exception e) {
943 errorText = e.Message;
944 } catch (...) {
945 errorText = _("Unknown exception while applying macro");
946 }
947 if (!errorText.empty()) {
948 Glib::ustring txt = _("Applying macro failed:\n") + errorText;
949 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
950 msg.run();
951 }
952 }
953
954 void MainWindow::setupMacros() {
955 MacrosSetup* setup = new MacrosSetup();
956 setup->setMacros(m_macros);
957 setup->signal_macros_changed().connect(
958 sigc::mem_fun(*this, &MainWindow::onMacrosSetupChanged)
959 );
960 setup->show();
961 }
962
963 void MainWindow::onMacrosSetupChanged(const std::vector<Serialization::Archive>& macros) {
964 m_macros = macros;
965 Settings::singleton()->saveMacros(m_macros);
966 updateMacroMenu();
967 }
968
969 bool MainWindow::on_delete_event(GdkEventAny* event)
970 {
971 return !file_is_shared && file_is_changed && !close_confirmation_dialog();
972 }
973
974 void MainWindow::on_action_quit()
975 {
976 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
977 hide();
978 }
979
980 void MainWindow::region_changed()
981 {
982 m_DimRegionChooser.set_region(m_RegionChooser.get_region());
983 }
984
985 gig::Instrument* MainWindow::get_instrument()
986 {
987 gig::Instrument* instrument = 0;
988 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
989 if (rows.empty()) return NULL;
990 Gtk::TreeModel::const_iterator it = m_refTreeModel->get_iter(rows[0]);
991 if (it) {
992 Gtk::TreeModel::Row row = *it;
993 instrument = row[m_Columns.m_col_instr];
994 }
995 return instrument;
996 }
997
998 void MainWindow::add_region_to_dimregs(gig::Region* region, bool stereo, bool all_dimregs)
999 {
1000 if (all_dimregs) {
1001 for (int i = 0 ; i < region->DimensionRegions ; i++) {
1002 if (region->pDimensionRegions[i]) {
1003 dimreg_edit.dimregs.insert(region->pDimensionRegions[i]);
1004 }
1005 }
1006 } else {
1007 m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
1008 }
1009 }
1010
1011 void MainWindow::update_dimregs()
1012 {
1013 dimreg_edit.dimregs.clear();
1014 bool all_regions = dimreg_all_regions.get_active();
1015 bool stereo = dimreg_stereo.get_active();
1016 bool all_dimregs = dimreg_all_dimregs.get_active();
1017
1018 if (all_regions) {
1019 gig::Instrument* instrument = get_instrument();
1020 if (instrument) {
1021 for (gig::Region* region = instrument->GetFirstRegion() ;
1022 region ;
1023 region = instrument->GetNextRegion()) {
1024 add_region_to_dimregs(region, stereo, all_dimregs);
1025 }
1026 }
1027 } else {
1028 gig::Region* region = m_RegionChooser.get_region();
1029 if (region) {
1030 add_region_to_dimregs(region, stereo, all_dimregs);
1031 }
1032 }
1033
1034 m_RegionChooser.setModifyAllRegions(all_regions);
1035 m_DimRegionChooser.setModifyAllRegions(all_regions);
1036 m_DimRegionChooser.setModifyAllDimensionRegions(all_dimregs);
1037 m_DimRegionChooser.setModifyBothChannels(stereo);
1038
1039 updateClipboardCopyAvailable();
1040 }
1041
1042 void MainWindow::dimreg_all_dimregs_toggled()
1043 {
1044 dimreg_stereo.set_sensitive(!dimreg_all_dimregs.get_active());
1045 update_dimregs();
1046 }
1047
1048 void MainWindow::dimreg_changed()
1049 {
1050 update_dimregs();
1051 dimreg_edit.set_dim_region(m_DimRegionChooser.get_main_dimregion());
1052 }
1053
1054 void MainWindow::on_sel_change()
1055 {
1056 // select item in instrument menu
1057 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
1058 if (!rows.empty()) {
1059 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
1060 if (it) {
1061 Gtk::TreePath path(it);
1062 int index = path[0];
1063 const std::vector<Gtk::Widget*> children =
1064 instrument_menu->get_children();
1065 static_cast<Gtk::RadioMenuItem*>(children[index])->set_active();
1066 }
1067 }
1068
1069 m_RegionChooser.set_instrument(get_instrument());
1070
1071 if (Settings::singleton()->syncSamplerInstrumentSelection) {
1072 switch_sampler_instrument_signal.emit(get_instrument());
1073 }
1074 }
1075
1076 void loader_progress_callback(gig::progress_t* progress)
1077 {
1078 Loader* loader = static_cast<Loader*>(progress->custom);
1079 loader->progress_callback(progress->factor);
1080 }
1081
1082 void Loader::progress_callback(float fraction)
1083 {
1084 {
1085 Glib::Threads::Mutex::Lock lock(progressMutex);
1086 progress = fraction;
1087 }
1088 progress_dispatcher();
1089 }
1090
1091 #if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
1092 // make sure stack is 16-byte aligned for SSE instructions
1093 __attribute__((force_align_arg_pointer))
1094 #endif
1095 void Loader::thread_function()
1096 {
1097 printf("thread_function self=%p\n",
1098 static_cast<void*>(Glib::Threads::Thread::self()));
1099 printf("Start %s\n", filename.c_str());
1100 try {
1101 RIFF::File* riff = new RIFF::File(filename);
1102 gig = new gig::File(riff);
1103 gig::progress_t progress;
1104 progress.callback = loader_progress_callback;
1105 progress.custom = this;
1106
1107 gig->GetInstrument(0, &progress);
1108 printf("End\n");
1109 finished_dispatcher();
1110 } catch (RIFF::Exception e) {
1111 error_message = e.Message;
1112 error_dispatcher.emit();
1113 } catch (...) {
1114 error_message = _("Unknown exception occurred");
1115 error_dispatcher.emit();
1116 }
1117 }
1118
1119 Loader::Loader(const char* filename)
1120 : filename(filename), gig(0), thread(0), progress(0.f)
1121 {
1122 }
1123
1124 void Loader::launch()
1125 {
1126 #ifdef OLD_THREADS
1127 thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true);
1128 #else
1129 thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Loader::thread_function));
1130 #endif
1131 printf("launch thread=%p\n", static_cast<void*>(thread));
1132 }
1133
1134 float Loader::get_progress()
1135 {
1136 float res;
1137 {
1138 Glib::Threads::Mutex::Lock lock(progressMutex);
1139 res = progress;
1140 }
1141 return res;
1142 }
1143
1144 Glib::Dispatcher& Loader::signal_progress()
1145 {
1146 return progress_dispatcher;
1147 }
1148
1149 Glib::Dispatcher& Loader::signal_finished()
1150 {
1151 return finished_dispatcher;
1152 }
1153
1154 Glib::Dispatcher& Loader::signal_error()
1155 {
1156 return error_dispatcher;
1157 }
1158
1159 void saver_progress_callback(gig::progress_t* progress)
1160 {
1161 Saver* saver = static_cast<Saver*>(progress->custom);
1162 saver->progress_callback(progress->factor);
1163 }
1164
1165 void Saver::progress_callback(float fraction)
1166 {
1167 {
1168 Glib::Threads::Mutex::Lock lock(progressMutex);
1169 progress = fraction;
1170 }
1171 progress_dispatcher.emit();
1172 }
1173
1174 #if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
1175 // make sure stack is 16-byte aligned for SSE instructions
1176 __attribute__((force_align_arg_pointer))
1177 #endif
1178 void Saver::thread_function()
1179 {
1180 printf("thread_function self=%p\n",
1181 static_cast<void*>(Glib::Threads::Thread::self()));
1182 printf("Start %s\n", filename.c_str());
1183 try {
1184 gig::progress_t progress;
1185 progress.callback = saver_progress_callback;
1186 progress.custom = this;
1187
1188 // if no filename was provided, that means "save", if filename was provided means "save as"
1189 if (filename.empty()) {
1190 if (!Settings::singleton()->saveWithTemporaryFile) {
1191 // save directly over the existing .gig file
1192 // (requires less disk space than solution below
1193 // but may be slower)
1194 gig->Save(&progress);
1195 } else {
1196 // save the file as separate temporary file first,
1197 // then move the saved file over the old file
1198 // (may result in performance speedup during save)
1199 String tmpname = filename + ".TMP";
1200 gig->Save(tmpname, &progress);
1201 #if defined(WIN32)
1202 if (!DeleteFile(filename.c_str())) {
1203 throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file).");
1204 }
1205 #else // POSIX ...
1206 if (unlink(filename.c_str())) {
1207 throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file): " + String(strerror(errno)));
1208 }
1209 #endif
1210 if (rename(tmpname.c_str(), filename.c_str())) {
1211 #if defined(WIN32)
1212 throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file).");
1213 #else
1214 throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file): " + String(strerror(errno)));
1215 #endif
1216 }
1217 }
1218 } else {
1219 gig->Save(filename, &progress);
1220 }
1221
1222 printf("End\n");
1223 finished_dispatcher.emit();
1224 } catch (RIFF::Exception e) {
1225 error_message = e.Message;
1226 error_dispatcher.emit();
1227 } catch (...) {
1228 error_message = _("Unknown exception occurred");
1229 error_dispatcher.emit();
1230 }
1231 }
1232
1233 Saver::Saver(gig::File* file, Glib::ustring filename)
1234 : gig(file), filename(filename), thread(0), progress(0.f)
1235 {
1236 }
1237
1238 void Saver::launch()
1239 {
1240 #ifdef OLD_THREADS
1241 thread = Glib::Thread::create(sigc::mem_fun(*this, &Saver::thread_function), true);
1242 #else
1243 thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Saver::thread_function));
1244 #endif
1245 printf("launch thread=%p\n", static_cast<void*>(thread));
1246 }
1247
1248 float Saver::get_progress()
1249 {
1250 float res;
1251 {
1252 Glib::Threads::Mutex::Lock lock(progressMutex);
1253 res = progress;
1254 }
1255 return res;
1256 }
1257
1258 Glib::Dispatcher& Saver::signal_progress()
1259 {
1260 return progress_dispatcher;
1261 }
1262
1263 Glib::Dispatcher& Saver::signal_finished()
1264 {
1265 return finished_dispatcher;
1266 }
1267
1268 Glib::Dispatcher& Saver::signal_error()
1269 {
1270 return error_dispatcher;
1271 }
1272
1273 ProgressDialog::ProgressDialog(const Glib::ustring& title, Gtk::Window& parent)
1274 : Gtk::Dialog(title, parent, true)
1275 {
1276 get_vbox()->pack_start(progressBar);
1277 show_all_children();
1278 resize(600,50);
1279 }
1280
1281 // Clear all GUI elements / controls. This method is typically called
1282 // before a new .gig file is to be created or to be loaded.
1283 void MainWindow::__clear() {
1284 // forget all samples that ought to be imported
1285 m_SampleImportQueue.clear();
1286 // clear the samples and instruments tree views
1287 m_refTreeModel->clear();
1288 m_refSamplesTreeModel->clear();
1289 m_refScriptsTreeModel->clear();
1290 // remove all entries from "Instrument" menu
1291 while (!instrument_menu->get_children().empty()) {
1292 remove_instrument_from_menu(0);
1293 }
1294 // free libgig's gig::File instance
1295 if (file && !file_is_shared) delete file;
1296 file = NULL;
1297 set_file_is_shared(false);
1298 }
1299
1300 void MainWindow::__refreshEntireGUI() {
1301 // clear the samples and instruments tree views
1302 m_refTreeModel->clear();
1303 m_refSamplesTreeModel->clear();
1304 m_refScriptsTreeModel->clear();
1305 // remove all entries from "Instrument" menu
1306 while (!instrument_menu->get_children().empty()) {
1307 remove_instrument_from_menu(0);
1308 }
1309
1310 if (!this->file) return;
1311
1312 load_gig(
1313 this->file, this->file->pInfo->Name.c_str(), this->file_is_shared
1314 );
1315 }
1316
1317 void MainWindow::on_action_file_new()
1318 {
1319 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
1320
1321 if (file_is_shared && !leaving_shared_mode_dialog()) return;
1322
1323 // clear all GUI elements
1324 __clear();
1325 // create a new .gig file (virtually yet)
1326 gig::File* pFile = new gig::File;
1327 // already add one new instrument by default
1328 gig::Instrument* pInstrument = pFile->AddInstrument();
1329 pInstrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument"));
1330 // update GUI with that new gig::File
1331 load_gig(pFile, 0 /*no file name yet*/);
1332 }
1333
1334 bool MainWindow::close_confirmation_dialog()
1335 {
1336 gchar* msg = g_strdup_printf(_("Save changes to \"%s\" before closing?"),
1337 Glib::filename_display_basename(filename).c_str());
1338 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
1339 g_free(msg);
1340 dialog.set_secondary_text(_("If you close without saving, your changes will be lost."));
1341 dialog.add_button(_("Close _Without Saving"), Gtk::RESPONSE_NO);
1342 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1343 dialog.add_button(file_has_name ? _("_Save") : _("Save _As"), Gtk::RESPONSE_YES);
1344 dialog.set_default_response(Gtk::RESPONSE_YES);
1345 int response = dialog.run();
1346 dialog.hide();
1347
1348 // user decided to exit app without saving
1349 if (response == Gtk::RESPONSE_NO) return true;
1350
1351 // user cancelled dialog, thus don't close app
1352 if (response == Gtk::RESPONSE_CANCEL) return false;
1353
1354 // TODO: the following return valid is disabled and hard coded instead for
1355 // now, due to the fact that saving with progress bar is now implemented
1356 // asynchronously, as a result the app does not close automatically anymore
1357 // after saving the file has completed
1358 //
1359 // if (response == Gtk::RESPONSE_YES) return file_save();
1360 // return response != Gtk::RESPONSE_CANCEL;
1361 //
1362 if (response == Gtk::RESPONSE_YES) file_save();
1363 return false; // always prevent closing the app for now (see comment above)
1364 }
1365
1366 bool MainWindow::leaving_shared_mode_dialog() {
1367 Glib::ustring msg = _("Detach from sampler and proceed working stand-alone?");
1368 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
1369 dialog.set_secondary_text(
1370 _("If you proceed to work on another instrument file, it won't be "
1371 "used by the sampler until you tell the sampler explicitly to "
1372 "load it."));
1373 dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
1374 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1375 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
1376 int response = dialog.run();
1377 dialog.hide();
1378 return response == Gtk::RESPONSE_YES;
1379 }
1380
1381 void MainWindow::on_action_file_open()
1382 {
1383 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
1384
1385 if (file_is_shared && !leaving_shared_mode_dialog()) return;
1386
1387 Gtk::FileChooserDialog dialog(*this, _("Open file"));
1388 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1389 dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
1390 dialog.set_default_response(Gtk::RESPONSE_OK);
1391 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1392 Gtk::FileFilter filter;
1393 filter.add_pattern("*.gig");
1394 #else
1395 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
1396 filter->add_pattern("*.gig");
1397 #endif
1398 dialog.set_filter(filter);
1399 if (current_gig_dir != "") {
1400 dialog.set_current_folder(current_gig_dir);
1401 }
1402 if (dialog.run() == Gtk::RESPONSE_OK) {
1403 std::string filename = dialog.get_filename();
1404 printf("filename=%s\n", filename.c_str());
1405 printf("on_action_file_open self=%p\n",
1406 static_cast<void*>(Glib::Threads::Thread::self()));
1407 load_file(filename.c_str());
1408 current_gig_dir = Glib::path_get_dirname(filename);
1409 }
1410 }
1411
1412 void MainWindow::load_file(const char* name)
1413 {
1414 __clear();
1415
1416 progress_dialog = new ProgressDialog( //FIXME: memory leak!
1417 _("Loading") + Glib::ustring(" '") +
1418 Glib::filename_display_basename(name) + "' ...",
1419 *this
1420 );
1421 progress_dialog->show_all();
1422 loader = new Loader(name); //FIXME: memory leak!
1423 loader->signal_progress().connect(
1424 sigc::mem_fun(*this, &MainWindow::on_loader_progress));
1425 loader->signal_finished().connect(
1426 sigc::mem_fun(*this, &MainWindow::on_loader_finished));
1427 loader->signal_error().connect(
1428 sigc::mem_fun(*this, &MainWindow::on_loader_error));
1429 loader->launch();
1430 }
1431
1432 void MainWindow::load_instrument(gig::Instrument* instr) {
1433 if (!instr) {
1434 Glib::ustring txt = "Provided instrument is NULL!\n";
1435 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1436 msg.run();
1437 Gtk::Main::quit();
1438 }
1439 // clear all GUI elements
1440 __clear();
1441 // load the instrument
1442 gig::File* pFile = (gig::File*) instr->GetParent();
1443 load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
1444 // automatically select the given instrument
1445 int i = 0;
1446 for (gig::Instrument* instrument = pFile->GetFirstInstrument(); instrument;
1447 instrument = pFile->GetNextInstrument(), ++i)
1448 {
1449 if (instrument == instr) {
1450 // select item in "instruments" tree view
1451 m_TreeView.get_selection()->select(Gtk::TreePath(ToString(i)));
1452 // make sure the selected item in the "instruments" tree view is
1453 // visible (scroll to it)
1454 m_TreeView.scroll_to_row(Gtk::TreePath(ToString(i)));
1455 // select item in instrument menu
1456 {
1457 const std::vector<Gtk::Widget*> children =
1458 instrument_menu->get_children();
1459 static_cast<Gtk::RadioMenuItem*>(children[i])->set_active();
1460 }
1461 // update region chooser and dimension region chooser
1462 m_RegionChooser.set_instrument(instr);
1463 break;
1464 }
1465 }
1466 }
1467
1468 void MainWindow::on_loader_progress()
1469 {
1470 progress_dialog->set_fraction(loader->get_progress());
1471 }
1472
1473 void MainWindow::on_loader_finished()
1474 {
1475 printf("Loader finished!\n");
1476 printf("on_loader_finished self=%p\n",
1477 static_cast<void*>(Glib::Threads::Thread::self()));
1478 load_gig(loader->gig, loader->filename.c_str());
1479 progress_dialog->hide();
1480 }
1481
1482 void MainWindow::on_loader_error()
1483 {
1484 Glib::ustring txt = _("Could not load file: ") + loader->error_message;
1485 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1486 msg.run();
1487 progress_dialog->hide();
1488 }
1489
1490 void MainWindow::on_action_file_save()
1491 {
1492 file_save();
1493 }
1494
1495 bool MainWindow::check_if_savable()
1496 {
1497 if (!file) return false;
1498
1499 if (!file->GetFirstSample()) {
1500 Gtk::MessageDialog(*this, _("The file could not be saved "
1501 "because it contains no samples"),
1502 false, Gtk::MESSAGE_ERROR).run();
1503 return false;
1504 }
1505
1506 for (gig::Instrument* instrument = file->GetFirstInstrument() ; instrument ;
1507 instrument = file->GetNextInstrument()) {
1508 if (!instrument->GetFirstRegion()) {
1509 Gtk::MessageDialog(*this, _("The file could not be saved "
1510 "because there are instruments "
1511 "that have no regions"),
1512 false, Gtk::MESSAGE_ERROR).run();
1513 return false;
1514 }
1515 }
1516 return true;
1517 }
1518
1519 bool MainWindow::file_save()
1520 {
1521 if (!check_if_savable()) return false;
1522 if (!file_is_shared && !file_has_name) return file_save_as();
1523
1524 std::cout << "Saving file\n" << std::flush;
1525 file_structure_to_be_changed_signal.emit(this->file);
1526
1527 progress_dialog = new ProgressDialog( //FIXME: memory leak!
1528 _("Saving") + Glib::ustring(" '") +
1529 Glib::filename_display_basename(this->filename) + "' ...",
1530 *this
1531 );
1532 progress_dialog->show_all();
1533 saver = new Saver(this->file); //FIXME: memory leak!
1534 saver->signal_progress().connect(
1535 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
1536 saver->signal_finished().connect(
1537 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
1538 saver->signal_error().connect(
1539 sigc::mem_fun(*this, &MainWindow::on_saver_error));
1540 saver->launch();
1541
1542 return true;
1543 }
1544
1545 void MainWindow::on_saver_progress()
1546 {
1547 progress_dialog->set_fraction(saver->get_progress());
1548 }
1549
1550 void MainWindow::on_saver_error()
1551 {
1552 file_structure_changed_signal.emit(this->file);
1553 Glib::ustring txt = _("Could not save file: ") + saver->error_message;
1554 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1555 msg.run();
1556 }
1557
1558 void MainWindow::on_saver_finished()
1559 {
1560 this->file = saver->gig;
1561 this->filename = saver->filename;
1562 current_gig_dir = Glib::path_get_dirname(filename);
1563 set_title(Glib::filename_display_basename(filename));
1564 file_has_name = true;
1565 file_is_changed = false;
1566 std::cout << "Saving file done. Importing queued samples now ...\n" << std::flush;
1567 __import_queued_samples();
1568 std::cout << "Importing queued samples done.\n" << std::flush;
1569
1570 file_structure_changed_signal.emit(this->file);
1571
1572 __refreshEntireGUI();
1573 progress_dialog->hide();
1574 }
1575
1576 void MainWindow::on_action_file_save_as()
1577 {
1578 if (!check_if_savable()) return;
1579 file_save_as();
1580 }
1581
1582 bool MainWindow::file_save_as()
1583 {
1584 Gtk::FileChooserDialog dialog(*this, _("Save As"), Gtk::FILE_CHOOSER_ACTION_SAVE);
1585 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1586 dialog.add_button(_("_Save"), Gtk::RESPONSE_OK);
1587 dialog.set_default_response(Gtk::RESPONSE_OK);
1588 dialog.set_do_overwrite_confirmation();
1589
1590 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1591 Gtk::FileFilter filter;
1592 filter.add_pattern("*.gig");
1593 #else
1594 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
1595 filter->add_pattern("*.gig");
1596 #endif
1597 dialog.set_filter(filter);
1598
1599 // set initial dir and filename of the Save As dialog
1600 // and prepare that initial filename as a copy of the gig
1601 {
1602 std::string basename = Glib::path_get_basename(filename);
1603 std::string dir = Glib::path_get_dirname(filename);
1604 basename = std::string(_("copy_of_")) + basename;
1605 Glib::ustring copyFileName = Glib::build_filename(dir, basename);
1606 if (Glib::path_is_absolute(filename)) {
1607 dialog.set_filename(copyFileName);
1608 } else {
1609 if (current_gig_dir != "") dialog.set_current_folder(current_gig_dir);
1610 }
1611 dialog.set_current_name(Glib::filename_display_basename(copyFileName));
1612 }
1613
1614 // show warning in the dialog
1615 Gtk::HBox descriptionArea;
1616 descriptionArea.set_spacing(15);
1617 Gtk::Image warningIcon;
1618 warningIcon.set_from_icon_name("dialog-warning",
1619 Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
1620 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
1621 #if GTKMM_MAJOR_VERSION < 3
1622 view::WrapLabel description;
1623 #else
1624 Gtk::Label description;
1625 description.set_line_wrap();
1626 #endif
1627 description.set_markup(
1628 _("\n<b>CAUTION:</b> You <b>MUST</b> use the "
1629 "<span style=\"italic\">\"Save\"</span> dialog instead of "
1630 "<span style=\"italic\">\"Save As...\"</span> if you want to save "
1631 "to the same .gig file. Using "
1632 "<span style=\"italic\">\"Save As...\"</span> for writing to the "
1633 "same .gig file will end up in corrupted sample wave data!\n")
1634 );
1635 descriptionArea.pack_start(description);
1636 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
1637 descriptionArea.show_all();
1638
1639 if (dialog.run() == Gtk::RESPONSE_OK) {
1640 std::string filename = dialog.get_filename();
1641 if (!Glib::str_has_suffix(filename, ".gig")) {
1642 filename += ".gig";
1643 }
1644 printf("filename=%s\n", filename.c_str());
1645
1646 progress_dialog = new ProgressDialog( //FIXME: memory leak!
1647 _("Saving") + Glib::ustring(" '") +
1648 Glib::filename_display_basename(filename) + "' ...",
1649 *this
1650 );
1651 progress_dialog->show_all();
1652
1653 saver = new Saver(file, filename); //FIXME: memory leak!
1654 saver->signal_progress().connect(
1655 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
1656 saver->signal_finished().connect(
1657 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
1658 saver->signal_error().connect(
1659 sigc::mem_fun(*this, &MainWindow::on_saver_error));
1660 saver->launch();
1661
1662 return true;
1663 }
1664 return false;
1665 }
1666
1667 // actually write the sample(s)' data to the gig file
1668 void MainWindow::__import_queued_samples() {
1669 std::cout << "Starting sample import\n" << std::flush;
1670 Glib::ustring error_files;
1671 printf("Samples to import: %d\n", int(m_SampleImportQueue.size()));
1672 for (std::map<gig::Sample*, SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1673 iter != m_SampleImportQueue.end(); ) {
1674 printf("Importing sample %s\n",iter->second.sample_path.c_str());
1675 SF_INFO info;
1676 info.format = 0;
1677 SNDFILE* hFile = sf_open(iter->second.sample_path.c_str(), SFM_READ, &info);
1678 sf_command(hFile, SFC_SET_SCALE_FLOAT_INT_READ, 0, SF_TRUE);
1679 try {
1680 if (!hFile) throw std::string(_("could not open file"));
1681 // determine sample's bit depth
1682 int bitdepth;
1683 switch (info.format & 0xff) {
1684 case SF_FORMAT_PCM_S8:
1685 case SF_FORMAT_PCM_16:
1686 case SF_FORMAT_PCM_U8:
1687 bitdepth = 16;
1688 break;
1689 case SF_FORMAT_PCM_24:
1690 case SF_FORMAT_PCM_32:
1691 case SF_FORMAT_FLOAT:
1692 case SF_FORMAT_DOUBLE:
1693 bitdepth = 24;
1694 break;
1695 default:
1696 sf_close(hFile); // close sound file
1697 throw std::string(_("format not supported")); // unsupported subformat (yet?)
1698 }
1699
1700 // reset write position for sample
1701 iter->first->SetPos(0);
1702
1703 const int bufsize = 10000;
1704 switch (bitdepth) {
1705 case 16: {
1706 short* buffer = new short[bufsize * info.channels];
1707 sf_count_t cnt = info.frames;
1708 while (cnt) {
1709 // libsndfile does the conversion for us (if needed)
1710 int n = sf_readf_short(hFile, buffer, bufsize);
1711 // write from buffer directly (physically) into .gig file
1712 iter->first->Write(buffer, n);
1713 cnt -= n;
1714 }
1715 delete[] buffer;
1716 break;
1717 }
1718 case 24: {
1719 int* srcbuf = new int[bufsize * info.channels];
1720 uint8_t* dstbuf = new uint8_t[bufsize * 3 * info.channels];
1721 sf_count_t cnt = info.frames;
1722 while (cnt) {
1723 // libsndfile returns 32 bits, convert to 24
1724 int n = sf_readf_int(hFile, srcbuf, bufsize);
1725 int j = 0;
1726 for (int i = 0 ; i < n * info.channels ; i++) {
1727 dstbuf[j++] = srcbuf[i] >> 8;
1728 dstbuf[j++] = srcbuf[i] >> 16;
1729 dstbuf[j++] = srcbuf[i] >> 24;
1730 }
1731 // write from buffer directly (physically) into .gig file
1732 iter->first->Write(dstbuf, n);
1733 cnt -= n;
1734 }
1735 delete[] srcbuf;
1736 delete[] dstbuf;
1737 break;
1738 }
1739 }
1740 // cleanup
1741 sf_close(hFile);
1742 // let the sampler re-cache the sample if needed
1743 sample_changed_signal.emit(iter->first);
1744 // on success we remove the sample from the import queue,
1745 // otherwise keep it, maybe it works the next time ?
1746 std::map<gig::Sample*, SampleImportItem>::iterator cur = iter;
1747 ++iter;
1748 m_SampleImportQueue.erase(cur);
1749 } catch (std::string what) {
1750 // remember the files that made trouble (and their cause)
1751 if (!error_files.empty()) error_files += "\n";
1752 error_files += iter->second.sample_path += " (" + what + ")";
1753 ++iter;
1754 }
1755 }
1756 // show error message box when some sample(s) could not be imported
1757 if (!error_files.empty()) {
1758 Glib::ustring txt = _("Could not import the following sample(s):\n") + error_files;
1759 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1760 msg.run();
1761 }
1762 }
1763
1764 void MainWindow::on_action_file_properties()
1765 {
1766 propDialog.show();
1767 propDialog.deiconify();
1768 }
1769
1770 void MainWindow::on_action_warn_user_on_extensions() {
1771 Settings::singleton()->warnUserOnExtensions =
1772 !Settings::singleton()->warnUserOnExtensions;
1773 }
1774
1775 void MainWindow::on_action_sync_sampler_instrument_selection() {
1776 Settings::singleton()->syncSamplerInstrumentSelection =
1777 !Settings::singleton()->syncSamplerInstrumentSelection;
1778 }
1779
1780 void MainWindow::on_action_move_root_note_with_region_moved() {
1781 Settings::singleton()->moveRootNoteWithRegionMoved =
1782 !Settings::singleton()->moveRootNoteWithRegionMoved;
1783 }
1784
1785 void MainWindow::on_action_help_about()
1786 {
1787 Gtk::AboutDialog dialog;
1788 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 12) || GTKMM_MAJOR_VERSION > 2
1789 dialog.set_program_name("Gigedit");
1790 #else
1791 dialog.set_name("Gigedit");
1792 #endif
1793 dialog.set_version(VERSION);
1794 dialog.set_copyright("Copyright (C) 2006-2017 Andreas Persson");
1795 const std::string sComment =
1796 _("Built " __DATE__ "\nUsing ") +
1797 ::gig::libraryName() + " " + ::gig::libraryVersion() + "\n\n" +
1798 _(
1799 "Gigedit is released under the GNU General Public License.\n"
1800 "\n"
1801 "This program is distributed WITHOUT ANY WARRANTY; So better "
1802 "backup your Gigasampler/GigaStudio files before editing them with "
1803 "this application.\n"
1804 "\n"
1805 "Please report bugs to: http://bugs.linuxsampler.org"
1806 );
1807 dialog.set_comments(sComment.c_str());
1808 dialog.set_website("http://www.linuxsampler.org");
1809 dialog.set_website_label("http://www.linuxsampler.org");
1810 dialog.run();
1811 }
1812
1813 PropDialog::PropDialog()
1814 : eFileFormat(_("File Format")),
1815 eName(_("Name")),
1816 eCreationDate(_("Creation date")),
1817 eComments(_("Comments")),
1818 eProduct(_("Product")),
1819 eCopyright(_("Copyright")),
1820 eArtists(_("Artists")),
1821 eGenre(_("Genre")),
1822 eKeywords(_("Keywords")),
1823 eEngineer(_("Engineer")),
1824 eTechnician(_("Technician")),
1825 eSoftware(_("Software")),
1826 eMedium(_("Medium")),
1827 eSource(_("Source")),
1828 eSourceForm(_("Source form")),
1829 eCommissioned(_("Commissioned")),
1830 eSubject(_("Subject")),
1831 quitButton(_("_Close"), true),
1832 table(2, 1),
1833 m_file(NULL)
1834 {
1835 set_title(_("File Properties"));
1836 eName.set_width_chars(50);
1837
1838 connect(eName, &DLS::Info::Name);
1839 connect(eCreationDate, &DLS::Info::CreationDate);
1840 connect(eComments, &DLS::Info::Comments);
1841 connect(eProduct, &DLS::Info::Product);
1842 connect(eCopyright, &DLS::Info::Copyright);
1843 connect(eArtists, &DLS::Info::Artists);
1844 connect(eGenre, &DLS::Info::Genre);
1845 connect(eKeywords, &DLS::Info::Keywords);
1846 connect(eEngineer, &DLS::Info::Engineer);
1847 connect(eTechnician, &DLS::Info::Technician);
1848 connect(eSoftware, &DLS::Info::Software);
1849 connect(eMedium, &DLS::Info::Medium);
1850 connect(eSource, &DLS::Info::Source);
1851 connect(eSourceForm, &DLS::Info::SourceForm);
1852 connect(eCommissioned, &DLS::Info::Commissioned);
1853 connect(eSubject, &DLS::Info::Subject);
1854
1855 table.add(eFileFormat);
1856 table.add(eName);
1857 table.add(eCreationDate);
1858 table.add(eComments);
1859 table.add(eProduct);
1860 table.add(eCopyright);
1861 table.add(eArtists);
1862 table.add(eGenre);
1863 table.add(eKeywords);
1864 table.add(eEngineer);
1865 table.add(eTechnician);
1866 table.add(eSoftware);
1867 table.add(eMedium);
1868 table.add(eSource);
1869 table.add(eSourceForm);
1870 table.add(eCommissioned);
1871 table.add(eSubject);
1872
1873 table.set_col_spacings(5);
1874 add(vbox);
1875 table.set_border_width(5);
1876 vbox.add(table);
1877 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
1878 buttonBox.set_layout(Gtk::BUTTONBOX_END);
1879 buttonBox.set_border_width(5);
1880 buttonBox.show();
1881 buttonBox.pack_start(quitButton);
1882 quitButton.set_can_default();
1883 quitButton.grab_focus();
1884 quitButton.signal_clicked().connect(
1885 sigc::mem_fun(*this, &PropDialog::hide));
1886 eFileFormat.signal_value_changed().connect(
1887 sigc::mem_fun(*this, &PropDialog::onFileFormatChanged));
1888
1889 quitButton.show();
1890 vbox.show();
1891 show_all_children();
1892 }
1893
1894 void PropDialog::set_file(gig::File* file)
1895 {
1896 m_file = file;
1897
1898 // update file format version combo box
1899 const std::string sGiga = "Gigasampler/GigaStudio v";
1900 const int major = file->pVersion->major;
1901 std::vector<std::string> txts;
1902 std::vector<int> values;
1903 txts.push_back(sGiga + "2"); values.push_back(2);
1904 txts.push_back(sGiga + "3/v4"); values.push_back(3);
1905 if (major != 2 && major != 3) {
1906 txts.push_back(sGiga + ToString(major)); values.push_back(major);
1907 }
1908 std::vector<const char*> texts;
1909 for (int i = 0; i < txts.size(); ++i) texts.push_back(txts[i].c_str());
1910 texts.push_back(NULL); values.push_back(0);
1911 eFileFormat.set_choices(&texts[0], &values[0]);
1912 eFileFormat.set_value(major);
1913 }
1914
1915 void PropDialog::onFileFormatChanged() {
1916 const int major = eFileFormat.get_value();
1917 if (m_file) m_file->pVersion->major = major;
1918 }
1919
1920 void PropDialog::set_info(DLS::Info* info)
1921 {
1922 update(info);
1923 }
1924
1925
1926 void InstrumentProps::set_Name(const gig::String& name)
1927 {
1928 m->pInfo->Name = name;
1929 }
1930
1931 void InstrumentProps::update_name()
1932 {
1933 update_model++;
1934 eName.set_value(m->pInfo->Name);
1935 update_model--;
1936 }
1937
1938 void InstrumentProps::set_IsDrum(bool value)
1939 {
1940 m->IsDrum = value;
1941 }
1942
1943 void InstrumentProps::set_MIDIBank(uint16_t value)
1944 {
1945 m->MIDIBank = value;
1946 }
1947
1948 void InstrumentProps::set_MIDIProgram(uint32_t value)
1949 {
1950 m->MIDIProgram = value;
1951 }
1952
1953 InstrumentProps::InstrumentProps() :
1954 quitButton(_("_Close"), true),
1955 table(2,1),
1956 eName(_("Name")),
1957 eIsDrum(_("Is drum")),
1958 eMIDIBank(_("MIDI bank"), 0, 16383),
1959 eMIDIProgram(_("MIDI program")),
1960 eAttenuation(_("Attenuation"), 0, 96, 0, 1),
1961 eGainPlus6(_("Gain +6dB"), eAttenuation, -6),
1962 eEffectSend(_("Effect send"), 0, 65535),
1963 eFineTune(_("Fine tune"), -8400, 8400),
1964 ePitchbendRange(_("Pitchbend range"), 0, 12),
1965 ePianoReleaseMode(_("Piano release mode")),
1966 eDimensionKeyRangeLow(_("Keyswitching range low")),
1967 eDimensionKeyRangeHigh(_("Keyswitching range high"))
1968 {
1969 set_title(_("Instrument Properties"));
1970
1971 eDimensionKeyRangeLow.set_tip(
1972 _("start of the keyboard area which should switch the "
1973 "\"keyswitching\" dimension")
1974 );
1975 eDimensionKeyRangeHigh.set_tip(
1976 _("end of the keyboard area which should switch the "
1977 "\"keyswitching\" dimension")
1978 );
1979
1980 connect(eName, &InstrumentProps::set_Name);
1981 connect(eIsDrum, &InstrumentProps::set_IsDrum);
1982 connect(eMIDIBank, &InstrumentProps::set_MIDIBank);
1983 connect(eMIDIProgram, &InstrumentProps::set_MIDIProgram);
1984 connect(eAttenuation, &gig::Instrument::Attenuation);
1985 connect(eGainPlus6, &gig::Instrument::Attenuation);
1986 connect(eEffectSend, &gig::Instrument::EffectSend);
1987 connect(eFineTune, &gig::Instrument::FineTune);
1988 connect(ePitchbendRange, &gig::Instrument::PitchbendRange);
1989 connect(ePianoReleaseMode, &gig::Instrument::PianoReleaseMode);
1990 connect(eDimensionKeyRangeLow, eDimensionKeyRangeHigh,
1991 &gig::Instrument::DimensionKeyRange);
1992
1993 eName.signal_value_changed().connect(sig_name_changed.make_slot());
1994
1995 table.set_col_spacings(5);
1996
1997 table.add(eName);
1998 table.add(eIsDrum);
1999 table.add(eMIDIBank);
2000 table.add(eMIDIProgram);
2001 table.add(eAttenuation);
2002 table.add(eGainPlus6);
2003 table.add(eEffectSend);
2004 table.add(eFineTune);
2005 table.add(ePitchbendRange);
2006 table.add(ePianoReleaseMode);
2007 table.add(eDimensionKeyRangeLow);
2008 table.add(eDimensionKeyRangeHigh);
2009
2010 add(vbox);
2011 table.set_border_width(5);
2012 vbox.pack_start(table);
2013 table.show();
2014 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
2015 buttonBox.set_layout(Gtk::BUTTONBOX_END);
2016 buttonBox.set_border_width(5);
2017 buttonBox.show();
2018 buttonBox.pack_start(quitButton);
2019 quitButton.set_can_default();
2020 quitButton.grab_focus();
2021
2022 quitButton.signal_clicked().connect(
2023 sigc::mem_fun(*this, &InstrumentProps::hide));
2024
2025 quitButton.show();
2026 vbox.show();
2027 show_all_children();
2028 }
2029
2030 void InstrumentProps::set_instrument(gig::Instrument* instrument)
2031 {
2032 update(instrument);
2033
2034 update_model++;
2035 eName.set_value(instrument->pInfo->Name);
2036 eIsDrum.set_value(instrument->IsDrum);
2037 eMIDIBank.set_value(instrument->MIDIBank);
2038 eMIDIProgram.set_value(instrument->MIDIProgram);
2039 update_model--;
2040 }
2041
2042
2043 void MainWindow::file_changed()
2044 {
2045 if (file && !file_is_changed) {
2046 set_title("*" + get_title());
2047 file_is_changed = true;
2048 }
2049 }
2050
2051 void MainWindow::updateSampleRefCountMap(gig::File* gig) {
2052 sample_ref_count.clear();
2053
2054 if (!gig) return;
2055
2056 for (gig::Instrument* instrument = gig->GetFirstInstrument(); instrument;
2057 instrument = gig->GetNextInstrument())
2058 {
2059 for (gig::Region* rgn = instrument->GetFirstRegion(); rgn;
2060 rgn = instrument->GetNextRegion())
2061 {
2062 for (int i = 0; i < 256; ++i) {
2063 if (!rgn->pDimensionRegions[i]) continue;
2064 if (rgn->pDimensionRegions[i]->pSample) {
2065 sample_ref_count[rgn->pDimensionRegions[i]->pSample]++;
2066 }
2067 }
2068 }
2069 }
2070 }
2071
2072 void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument)
2073 {
2074 file = 0;
2075 set_file_is_shared(isSharedInstrument);
2076
2077 this->filename =
2078 (filename && strlen(filename) > 0) ?
2079 filename : (!gig->GetFileName().empty()) ?
2080 gig->GetFileName() : _("Unsaved Gig File");
2081 set_title(Glib::filename_display_basename(this->filename));
2082 file_has_name = filename;
2083 file_is_changed = false;
2084
2085 propDialog.set_file(gig);
2086 propDialog.set_info(gig->pInfo);
2087
2088 instrument_name_connection.block();
2089 int index = 0;
2090 for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ;
2091 instrument = gig->GetNextInstrument(), ++index) {
2092 Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
2093
2094 Gtk::TreeModel::iterator iter = m_refTreeModel->append();
2095 Gtk::TreeModel::Row row = *iter;
2096 row[m_Columns.m_col_nr] = index;
2097 row[m_Columns.m_col_name] = name;
2098 row[m_Columns.m_col_instr] = instrument;
2099
2100 add_instrument_to_menu(name);
2101 }
2102 instrument_name_connection.unblock();
2103 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments")->show();
2104
2105 updateSampleRefCountMap(gig);
2106
2107 for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) {
2108 if (group->Name != "") {
2109 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
2110 Gtk::TreeModel::Row rowGroup = *iterGroup;
2111 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
2112 rowGroup[m_SamplesModel.m_col_group] = group;
2113 rowGroup[m_SamplesModel.m_col_sample] = NULL;
2114 for (gig::Sample* sample = group->GetFirstSample();
2115 sample; sample = group->GetNextSample()) {
2116 Gtk::TreeModel::iterator iterSample =
2117 m_refSamplesTreeModel->append(rowGroup.children());
2118 Gtk::TreeModel::Row rowSample = *iterSample;
2119 rowSample[m_SamplesModel.m_col_name] =
2120 gig_to_utf8(sample->pInfo->Name);
2121 rowSample[m_SamplesModel.m_col_sample] = sample;
2122 rowSample[m_SamplesModel.m_col_group] = NULL;
2123 int refcount = sample_ref_count.count(sample) ? sample_ref_count[sample] : 0;
2124 rowSample[m_SamplesModel.m_col_refcount] = ToString(refcount) + " " + _("Refs.");
2125 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
2126 }
2127 }
2128 }
2129
2130 for (int i = 0; gig->GetScriptGroup(i); ++i) {
2131 gig::ScriptGroup* group = gig->GetScriptGroup(i);
2132
2133 Gtk::TreeModel::iterator iterGroup = m_refScriptsTreeModel->append();
2134 Gtk::TreeModel::Row rowGroup = *iterGroup;
2135 rowGroup[m_ScriptsModel.m_col_name] = gig_to_utf8(group->Name);
2136 rowGroup[m_ScriptsModel.m_col_group] = group;
2137 rowGroup[m_ScriptsModel.m_col_script] = NULL;
2138 for (int s = 0; group->GetScript(s); ++s) {
2139 gig::Script* script = group->GetScript(s);
2140
2141 Gtk::TreeModel::iterator iterScript =
2142 m_refScriptsTreeModel->append(rowGroup.children());
2143 Gtk::TreeModel::Row rowScript = *iterScript;
2144 rowScript[m_ScriptsModel.m_col_name] = gig_to_utf8(script->Name);
2145 rowScript[m_ScriptsModel.m_col_script] = script;
2146 rowScript[m_ScriptsModel.m_col_group] = NULL;
2147 }
2148 }
2149 // unfold all sample groups & script groups by default
2150 m_TreeViewSamples.expand_all();
2151 m_TreeViewScripts.expand_all();
2152
2153 file = gig;
2154
2155 // select the first instrument
2156 m_TreeView.get_selection()->select(Gtk::TreePath("0"));
2157
2158 instr_props_set_instrument();
2159 gig::Instrument* instrument = get_instrument();
2160 if (instrument) {
2161 midiRules.set_instrument(instrument);
2162 }
2163 }
2164
2165 bool MainWindow::instr_props_set_instrument()
2166 {
2167 instrumentProps.signal_name_changed().clear();
2168
2169 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
2170 if (rows.empty()) {
2171 instrumentProps.hide();
2172 return false;
2173 }
2174 Gtk::TreeModel::const_iterator it = m_refTreeModel->get_iter(rows[0]);
2175 if (it) {
2176 Gtk::TreeModel::Row row = *it;
2177 gig::Instrument* instrument = row[m_Columns.m_col_instr];
2178
2179 instrumentProps.set_instrument(instrument);
2180
2181 // make sure instrument tree is updated when user changes the
2182 // instrument name in instrument properties window
2183 instrumentProps.signal_name_changed().connect(
2184 sigc::bind(
2185 sigc::mem_fun(*this,
2186 &MainWindow::instr_name_changed_by_instr_props),
2187 it));
2188 } else {
2189 instrumentProps.hide();
2190 }
2191 return it;
2192 }
2193
2194 void MainWindow::show_instr_props()
2195 {
2196 if (instr_props_set_instrument()) {
2197 instrumentProps.show();
2198 instrumentProps.deiconify();
2199 }
2200 }
2201
2202 void MainWindow::instr_name_changed_by_instr_props(Gtk::TreeModel::iterator& it)
2203 {
2204 Gtk::TreeModel::Row row = *it;
2205 Glib::ustring name = row[m_Columns.m_col_name];
2206
2207 gig::Instrument* instrument = row[m_Columns.m_col_instr];
2208 Glib::ustring gigname(gig_to_utf8(instrument->pInfo->Name));
2209 if (gigname != name) {
2210 row[m_Columns.m_col_name] = gigname;
2211 }
2212 }
2213
2214 void MainWindow::show_midi_rules()
2215 {
2216 if (gig::Instrument* instrument = get_instrument())
2217 {
2218 midiRules.set_instrument(instrument);
2219 midiRules.show();
2220 midiRules.deiconify();
2221 }
2222 }
2223
2224 void MainWindow::show_script_slots() {
2225 if (!file) return;
2226 // get selected instrument
2227 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
2228 if (rows.empty()) return;
2229 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
2230 if (!it) return;
2231 Gtk::TreeModel::Row row = *it;
2232 gig::Instrument* instrument = row[m_Columns.m_col_instr];
2233 if (!instrument) return;
2234
2235 ScriptSlots* window = new ScriptSlots;
2236 window->setInstrument(instrument);
2237 //window->reparent(*this);
2238 window->show();
2239 }
2240
2241 void MainWindow::on_action_refresh_all() {
2242 __refreshEntireGUI();
2243 }
2244
2245 void MainWindow::on_action_view_status_bar() {
2246 Gtk::CheckMenuItem* item =
2247 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/Statusbar"));
2248 if (!item) {
2249 std::cerr << "/MenuBar/MenuView/Statusbar == NULL\n";
2250 return;
2251 }
2252 if (item->get_active()) m_StatusBar.show();
2253 else m_StatusBar.hide();
2254 }
2255
2256 void MainWindow::on_auto_restore_win_dim() {
2257 Gtk::CheckMenuItem* item =
2258 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/AutoRestoreWinDim"));
2259 if (!item) {
2260 std::cerr << "/MenuBar/MenuView/AutoRestoreWinDim == NULL\n";
2261 return;
2262 }
2263 Settings::singleton()->autoRestoreWindowDimension = item->get_active();
2264 }
2265
2266 void MainWindow::on_save_with_temporary_file() {
2267 Gtk::CheckMenuItem* item =
2268 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuSettings/SaveWithTemporaryFile"));
2269 if (!item) {
2270 std::cerr << "/MenuBar/MenuSettings/SaveWithTemporaryFile == NULL\n";
2271 return;
2272 }
2273 Settings::singleton()->saveWithTemporaryFile = item->get_active();
2274 }
2275
2276 bool MainWindow::is_copy_samples_unity_note_enabled() const {
2277 Gtk::CheckMenuItem* item =
2278 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleUnity"));
2279 if (!item) {
2280 std::cerr << "/MenuBar/MenuEdit/CopySampleUnity == NULL\n";
2281 return true;
2282 }
2283 return item->get_active();
2284 }
2285
2286 bool MainWindow::is_copy_samples_fine_tune_enabled() const {
2287 Gtk::CheckMenuItem* item =
2288 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleTune"));
2289 if (!item) {
2290 std::cerr << "/MenuBar/MenuEdit/CopySampleTune == NULL\n";
2291 return true;
2292 }
2293 return item->get_active();
2294 }
2295
2296 bool MainWindow::is_copy_samples_loop_enabled() const {
2297 Gtk::CheckMenuItem* item =
2298 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleLoop"));
2299 if (!item) {
2300 std::cerr << "/MenuBar/MenuEdit/CopySampleLoop == NULL\n";
2301 return true;
2302 }
2303 return item->get_active();
2304 }
2305
2306 void MainWindow::on_button_release(GdkEventButton* button)
2307 {
2308 if (button->type == GDK_2BUTTON_PRESS) {
2309 show_instr_props();
2310 } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
2311 // gig v2 files have no midi rules
2312 const bool bEnabled = !(file->pVersion && file->pVersion->major == 2);
2313 static_cast<Gtk::MenuItem*>(
2314 uiManager->get_widget("/MenuBar/MenuInstrument/MidiRules"))->set_sensitive(
2315 bEnabled
2316 );
2317 static_cast<Gtk::MenuItem*>(
2318 uiManager->get_widget("/PopupMenu/MidiRules"))->set_sensitive(
2319 bEnabled
2320 );
2321 popup_menu->popup(button->button, button->time);
2322 }
2323 }
2324
2325 void MainWindow::on_instrument_selection_change(Gtk::RadioMenuItem* item) {
2326 if (item->get_active()) {
2327 const std::vector<Gtk::Widget*> children =
2328 instrument_menu->get_children();
2329 std::vector<Gtk::Widget*>::const_iterator it =
2330 find(children.begin(), children.end(), item);
2331 if (it != children.end()) {
2332 int index = it - children.begin();
2333 m_TreeView.get_selection()->select(Gtk::TreePath(ToString(index)));
2334
2335 m_RegionChooser.set_instrument(file->GetInstrument(index));
2336 }
2337 }
2338 }
2339
2340 void MainWindow::select_instrument(gig::Instrument* instrument) {
2341 if (!instrument) return;
2342
2343 Glib::RefPtr<Gtk::TreeModel> model = m_TreeView.get_model();
2344 for (int i = 0; i < model->children().size(); ++i) {
2345 Gtk::TreeModel::Row row = model->children()[i];
2346 if (row[m_Columns.m_col_instr] == instrument) {
2347 // select and show the respective instrument in the list view
2348 show_intruments_tab();
2349 m_TreeView.get_selection()->unselect_all();
2350 m_TreeView.get_selection()->select(model->children()[i]);
2351 std::vector<Gtk::TreeModel::Path> rows =
2352 m_TreeView.get_selection()->get_selected_rows();
2353 if (!rows.empty())
2354 m_TreeView.scroll_to_row(rows[0]);
2355 on_sel_change(); // the regular instrument selection change callback
2356 }
2357 }
2358 }
2359
2360 /// Returns true if requested dimension region was successfully selected and scrolled to in the list view, false on error.
2361 bool MainWindow::select_dimension_region(gig::DimensionRegion* dimRgn) {
2362 gig::Region* pRegion = (gig::Region*) dimRgn->GetParent();
2363 gig::Instrument* pInstrument = (gig::Instrument*) pRegion->GetParent();
2364
2365 Glib::RefPtr<Gtk::TreeModel> model = m_TreeView.get_model();
2366 for (int i = 0; i < model->children().size(); ++i) {
2367 Gtk::TreeModel::Row row = model->children()[i];
2368 if (row[m_Columns.m_col_instr] == pInstrument) {
2369 // select and show the respective instrument in the list view
2370 show_intruments_tab();
2371 m_TreeView.get_selection()->unselect_all();
2372 m_TreeView.get_selection()->select(model->children()[i]);
2373 std::vector<Gtk::TreeModel::Path> rows =
2374 m_TreeView.get_selection()->get_selected_rows();
2375 if (!rows.empty())
2376 m_TreeView.scroll_to_row(rows[0]);
2377 on_sel_change(); // the regular instrument selection change callback
2378
2379 // select respective region in the region selector
2380 m_RegionChooser.set_region(pRegion);
2381
2382 // select and show the respective dimension region in the editor
2383 //update_dimregs();
2384 if (!m_DimRegionChooser.select_dimregion(dimRgn)) return false;
2385 //dimreg_edit.set_dim_region(dimRgn);
2386
2387 return true;
2388 }
2389 }
2390
2391 return false;
2392 }
2393
2394 void MainWindow::select_sample(gig::Sample* sample) {
2395 Glib::RefPtr<Gtk::TreeModel> model = m_TreeViewSamples.get_model();
2396 for (int g = 0; g < model->children().size(); ++g) {
2397 Gtk::TreeModel::Row rowGroup = model->children()[g];
2398 for (int s = 0; s < rowGroup.children().size(); ++s) {
2399 Gtk::TreeModel::Row rowSample = rowGroup.children()[s];
2400 if (rowSample[m_SamplesModel.m_col_sample] == sample) {
2401 show_samples_tab();
2402 m_TreeViewSamples.get_selection()->unselect_all();
2403 m_TreeViewSamples.get_selection()->select(rowGroup.children()[s]);
2404 std::vector<Gtk::TreeModel::Path> rows =
2405 m_TreeViewSamples.get_selection()->get_selected_rows();
2406 if (rows.empty()) return;
2407 m_TreeViewSamples.scroll_to_row(rows[0]);
2408 return;
2409 }
2410 }
2411 }
2412 }
2413
2414 void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) {
2415 if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
2416 // by default if Ctrl keys is pressed down, then a mouse right-click
2417 // does not select the respective row, so we must assure this
2418 // programmatically ...
2419 /*{
2420 Gtk::TreeModel::Path path;
2421 Gtk::TreeViewColumn* pColumn = NULL;
2422 int cellX, cellY;
2423 bool bSuccess = m_TreeViewSamples.get_path_at_pos(
2424 (int)button->x, (int)button->y,
2425 path, pColumn, cellX, cellY
2426 );
2427 if (bSuccess) {
2428 if (m_TreeViewSamples.get_selection()->count_selected_rows() <= 0) {
2429 printf("not selected !!!\n");
2430 m_TreeViewSamples.get_selection()->select(path);
2431 }
2432 }
2433 }*/
2434
2435 Gtk::Menu* sample_popup =
2436 dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/SamplePopupMenu"));
2437 // update enabled/disabled state of sample popup items
2438 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
2439 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
2440 const int n = rows.size();
2441 int nGroups = 0;
2442 int nSamples = 0;
2443 for (int r = 0; r < n; ++r) {
2444 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[r]);
2445 if (!it) continue;
2446 Gtk::TreeModel::Row row = *it;
2447 if (row[m_SamplesModel.m_col_group]) nGroups++;
2448 if (row[m_SamplesModel.m_col_sample]) nSamples++;
2449 }
2450
2451 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->
2452 set_sensitive(n == 1);
2453 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->
2454 set_sensitive(n);
2455 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->
2456 set_sensitive(file);
2457 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/ShowSampleRefs"))->
2458 set_sensitive(nSamples == 1);
2459 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->
2460 set_sensitive(n);
2461 // show sample popup
2462 sample_popup->popup(button->button, button->time);
2463
2464 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/SampleProperties"))->
2465 set_sensitive(n == 1);
2466 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/AddSample"))->
2467 set_sensitive(n);
2468 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/AddGroup"))->
2469 set_sensitive(file);
2470 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/ShowSampleRefs"))->
2471 set_sensitive(nSamples == 1);
2472 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/RemoveSample"))->
2473 set_sensitive(n);
2474 }
2475 }
2476
2477 void MainWindow::on_script_treeview_button_release(GdkEventButton* button) {
2478 if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
2479 Gtk::Menu* script_popup =
2480 dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/ScriptPopupMenu"));
2481 // update enabled/disabled state of sample popup items
2482 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2483 Gtk::TreeModel::iterator it = sel->get_selected();
2484 bool group_selected = false;
2485 bool script_selected = false;
2486 if (it) {
2487 Gtk::TreeModel::Row row = *it;
2488 group_selected = row[m_ScriptsModel.m_col_group];
2489 script_selected = row[m_ScriptsModel.m_col_script];
2490 }
2491 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/AddScript"))->
2492 set_sensitive(group_selected || script_selected);
2493 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/AddScriptGroup"))->
2494 set_sensitive(file);
2495 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/EditScript"))->
2496 set_sensitive(script_selected);
2497 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/RemoveScript"))->
2498 set_sensitive(group_selected || script_selected);
2499 // show sample popup
2500 script_popup->popup(button->button, button->time);
2501
2502 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/AddScript"))->
2503 set_sensitive(group_selected || script_selected);
2504 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/AddScriptGroup"))->
2505 set_sensitive(file);
2506 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/EditScript"))->
2507 set_sensitive(script_selected);
2508 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/RemoveScript"))->
2509 set_sensitive(group_selected || script_selected);
2510 }
2511 }
2512
2513 Gtk::RadioMenuItem* MainWindow::add_instrument_to_menu(
2514 const Glib::ustring& name, int position) {
2515
2516 Gtk::RadioMenuItem::Group instrument_group;
2517 const std::vector<Gtk::Widget*> children = instrument_menu->get_children();
2518 if (!children.empty()) {
2519 instrument_group =
2520 static_cast<Gtk::RadioMenuItem*>(children[0])->get_group();
2521 }
2522 Gtk::RadioMenuItem* item =
2523 new Gtk::RadioMenuItem(instrument_group, name);
2524 if (position < 0) {
2525 instrument_menu->append(*item);
2526 } else {
2527 instrument_menu->insert(*item, position);
2528 }
2529 item->show();
2530 item->signal_activate().connect(
2531 sigc::bind(
2532 sigc::mem_fun(*this, &MainWindow::on_instrument_selection_change),
2533 item));
2534 return item;
2535 }
2536
2537 void MainWindow::remove_instrument_from_menu(int index) {
2538 const std::vector<Gtk::Widget*> children =
2539 instrument_menu->get_children();
2540 Gtk::Widget* child = children[index];
2541 instrument_menu->remove(*child);
2542 delete child;
2543 }
2544
2545 void MainWindow::add_instrument(gig::Instrument* instrument) {
2546 const Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
2547
2548 // update instrument tree view
2549 instrument_name_connection.block();
2550 Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append();
2551 Gtk::TreeModel::Row rowInstr = *iterInstr;
2552 rowInstr[m_Columns.m_col_nr] = m_refTreeModel->children().size() - 1;
2553 rowInstr[m_Columns.m_col_name] = name;
2554 rowInstr[m_Columns.m_col_instr] = instrument;
2555 instrument_name_connection.unblock();
2556
2557 add_instrument_to_menu(name);
2558
2559 m_TreeView.get_selection()->select(iterInstr);
2560
2561 file_changed();
2562 }
2563
2564 void MainWindow::on_action_add_instrument() {
2565 static int __instrument_indexer = 0;
2566 if (!file) return;
2567 gig::Instrument* instrument = file->AddInstrument();
2568 __instrument_indexer++;
2569 instrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument ") +
2570 ToString(__instrument_indexer));
2571
2572 add_instrument(instrument);
2573 }
2574
2575 void MainWindow::on_action_duplicate_instrument() {
2576 if (!file) return;
2577
2578 // retrieve the currently selected instrument
2579 // (being the original instrument to be duplicated)
2580 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
2581 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
2582 for (int r = 0; r < rows.size(); ++r) {
2583 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]);
2584 if (it) {
2585 Gtk::TreeModel::Row row = *it;
2586 gig::Instrument* instrOrig = row[m_Columns.m_col_instr];
2587 if (instrOrig) {
2588 // duplicate the orginal instrument
2589 gig::Instrument* instrNew = file->AddDuplicateInstrument(instrOrig);
2590 instrNew->pInfo->Name =
2591 instrOrig->pInfo->Name +
2592 gig_from_utf8(Glib::ustring(" (") + _("Copy") + ")");
2593
2594 add_instrument(instrNew);
2595 }
2596 }
2597 }
2598 }
2599
2600 void MainWindow::on_action_remove_instrument() {
2601 if (!file) return;
2602 if (file_is_shared) {
2603 Gtk::MessageDialog msg(
2604 *this,
2605 _("You cannot delete an instrument from this file, since it's "
2606 "currently used by the sampler."),
2607 false, Gtk::MESSAGE_INFO
2608 );
2609 msg.run();
2610 return;
2611 }
2612
2613 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
2614 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
2615 for (int r = rows.size() - 1; r >= 0; --r) {
2616 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]);
2617 if (!it) continue;
2618 Gtk::TreeModel::Row row = *it;
2619 gig::Instrument* instr = row[m_Columns.m_col_instr];
2620 try {
2621 Gtk::TreePath path(it);
2622 int index = path[0];
2623
2624 // remove instrument from the gig file
2625 if (instr) file->DeleteInstrument(instr);
2626 file_changed();
2627
2628 remove_instrument_from_menu(index);
2629
2630 // remove row from instruments tree view
2631 m_refTreeModel->erase(it);
2632 // update "Nr" column of all instrument rows
2633 {
2634 int index = 0;
2635 for (Gtk::TreeModel::iterator it = m_refTreeModel->children().begin();
2636 it != m_refTreeModel->children().end(); ++it, ++index)
2637 {
2638 Gtk::TreeModel::Row row = *it;
2639 row[m_Columns.m_col_nr] = index;
2640 }
2641 }
2642
2643 #if GTKMM_MAJOR_VERSION < 3
2644 // select another instrument (in gtk3 this is done
2645 // automatically)
2646 if (!m_refTreeModel->children().empty()) {
2647 if (index == m_refTreeModel->children().size()) {
2648 index--;
2649 }
2650 m_TreeView.get_selection()->select(
2651 Gtk::TreePath(ToString(index)));
2652 }
2653 #endif
2654 instr_props_set_instrument();
2655 instr = get_instrument();
2656 if (instr) {
2657 midiRules.set_instrument(instr);
2658 } else {
2659 midiRules.hide();
2660 }
2661 } catch (RIFF::Exception e) {
2662 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
2663 msg.run();
2664 }
2665 }
2666 }
2667
2668 void MainWindow::on_action_sample_properties() {
2669 //TODO: show a dialog where the selected sample's properties can be edited
2670 Gtk::MessageDialog msg(
2671 *this, _("Sorry, yet to be implemented!"), false, Gtk::MESSAGE_INFO
2672 );
2673 msg.run();
2674 }
2675
2676 void MainWindow::on_action_add_script_group() {
2677 static int __script_indexer = 0;
2678 if (!file) return;
2679 gig::ScriptGroup* group = file->AddScriptGroup();
2680 group->Name = gig_from_utf8(_("Unnamed Group"));
2681 if (__script_indexer) group->Name += " " + ToString(__script_indexer);
2682 __script_indexer++;
2683 // update sample tree view
2684 Gtk::TreeModel::iterator iterGroup = m_refScriptsTreeModel->append();
2685 Gtk::TreeModel::Row rowGroup = *iterGroup;
2686 rowGroup[m_ScriptsModel.m_col_name] = gig_to_utf8(group->Name);
2687 rowGroup[m_ScriptsModel.m_col_script] = NULL;
2688 rowGroup[m_ScriptsModel.m_col_group] = group;
2689 file_changed();
2690 }
2691
2692 void MainWindow::on_action_add_script() {
2693 if (!file) return;
2694 // get selected group
2695 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2696 Gtk::TreeModel::iterator it = sel->get_selected();
2697 if (!it) return;
2698 Gtk::TreeModel::Row row = *it;
2699 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
2700 if (!group) { // not a group, but a script is selected (probably)
2701 gig::Script* script = row[m_ScriptsModel.m_col_script];
2702 if (!script) return;
2703 it = row.parent(); // resolve parent (that is the script's group)
2704 if (!it) return;
2705 row = *it;
2706 group = row[m_ScriptsModel.m_col_group];
2707 if (!group) return;
2708 }
2709
2710 // add a new script to the .gig file
2711 gig::Script* script = group->AddScript();
2712 Glib::ustring name = _("Unnamed Script");
2713 script->Name = gig_from_utf8(name);
2714
2715 // add script to the tree view
2716 Gtk::TreeModel::iterator iterScript =
2717 m_refScriptsTreeModel->append(row.children());
2718 Gtk::TreeModel::Row rowScript = *iterScript;
2719 rowScript[m_ScriptsModel.m_col_name] = name;
2720 rowScript[m_ScriptsModel.m_col_script] = script;
2721 rowScript[m_ScriptsModel.m_col_group] = NULL;
2722
2723 // unfold group of new script item in treeview
2724 Gtk::TreeModel::Path path(iterScript);
2725 m_TreeViewScripts.expand_to_path(path);
2726 }
2727
2728 void MainWindow::on_action_edit_script() {
2729 if (!file) return;
2730 // get selected script
2731 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2732 Gtk::TreeModel::iterator it = sel->get_selected();
2733 if (!it) return;
2734 Gtk::TreeModel::Row row = *it;
2735 gig::Script* script = row[m_ScriptsModel.m_col_script];
2736 if (!script) return;
2737
2738 ScriptEditor* editor = new ScriptEditor;
2739 editor->signal_script_to_be_changed.connect(
2740 signal_script_to_be_changed.make_slot()
2741 );
2742 editor->signal_script_changed.connect(
2743 signal_script_changed.make_slot()
2744 );
2745 editor->setScript(script);
2746 //editor->reparent(*this);
2747 editor->show();
2748 }
2749
2750 void MainWindow::on_action_remove_script() {
2751 if (!file) return;
2752 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
2753 Gtk::TreeModel::iterator it = sel->get_selected();
2754 if (it) {
2755 Gtk::TreeModel::Row row = *it;
2756 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
2757 gig::Script* script = row[m_ScriptsModel.m_col_script];
2758 Glib::ustring name = row[m_ScriptsModel.m_col_name];
2759 try {
2760 // remove script group or script from the gig file
2761 if (group) {
2762 // notify everybody that we're going to remove these samples
2763 //TODO: scripts_to_be_removed_signal.emit(members);
2764 // delete the group in the .gig file including the
2765 // samples that belong to the group
2766 file->DeleteScriptGroup(group);
2767 // notify that we're done with removal
2768 //TODO: scripts_removed_signal.emit();
2769 file_changed();
2770 } else if (script) {
2771 // notify everybody that we're going to remove this sample
2772 //TODO: std::list<gig::Script*> lscripts;
2773 //TODO: lscripts.push_back(script);
2774 //TODO: scripts_to_be_removed_signal.emit(lscripts);
2775 // remove sample from the .gig file
2776 script->GetGroup()->DeleteScript(script);
2777 // notify that we're done with removal
2778 //TODO: scripts_removed_signal.emit();
2779 dimreg_changed();
2780 file_changed();
2781 }
2782 // remove respective row(s) from samples tree view
2783 m_refScriptsTreeModel->erase(it);
2784 } catch (RIFF::Exception e) {
2785 // pretend we're done with removal (i.e. to avoid dead locks)
2786 //TODO: scripts_removed_signal.emit();
2787 // show error message
2788 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
2789 msg.run();
2790 }
2791 }
2792 }
2793
2794 void MainWindow::on_action_add_group() {
2795 static int __sample_indexer = 0;
2796 if (!file) return;
2797 gig::Group* group = file->AddGroup();
2798 group->Name = gig_from_utf8(_("Unnamed Group"));
2799 if (__sample_indexer) group->Name += " " + ToString(__sample_indexer);
2800 __sample_indexer++;
2801 // update sample tree view
2802 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
2803 Gtk::TreeModel::Row rowGroup = *iterGroup;
2804 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
2805 rowGroup[m_SamplesModel.m_col_sample] = NULL;
2806 rowGroup[m_SamplesModel.m_col_group] = group;
2807 file_changed();
2808 }
2809
2810 void MainWindow::on_action_replace_sample() {
2811 add_or_replace_sample(true);
2812 }
2813
2814 void MainWindow::on_action_add_sample() {
2815 add_or_replace_sample(false);
2816 }
2817
2818 void MainWindow::add_or_replace_sample(bool replace) {
2819 if (!file) return;
2820
2821 // get selected group (and probably selected sample)
2822 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
2823 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
2824 if (rows.empty()) return;
2825 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]);
2826 if (!it) return;
2827 Gtk::TreeModel::Row row = *it;
2828 gig::Sample* sample = NULL;
2829 gig::Group* group = row[m_SamplesModel.m_col_group];
2830 if (!group) { // not a group, but a sample is selected (probably)
2831 if (replace) sample = row[m_SamplesModel.m_col_sample];
2832 if (!row[m_SamplesModel.m_col_sample]) return;
2833 it = row.parent(); // resolve parent (that is the sample's group)
2834 if (!it) return;
2835 if (!replace) row = *it;
2836 group = (*it)[m_SamplesModel.m_col_group];
2837 if (!group) return;
2838 }
2839 if (replace && !sample) return;
2840
2841 // show 'browse for file' dialog
2842 Gtk::FileChooserDialog dialog(*this, replace ? _("Replace Sample with") : _("Add Sample(s)"));
2843 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2844 dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
2845 dialog.set_select_multiple(!replace); // allow multi audio file selection only when adding new samples, does not make sense when replacing a specific sample
2846
2847 // matches all file types supported by libsndfile
2848 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
2849 Gtk::FileFilter soundfilter;
2850 #else
2851 Glib::RefPtr<Gtk::FileFilter> soundfilter = Gtk::FileFilter::create();
2852 #endif
2853 const char* const supportedFileTypes[] = {
2854 "*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd",
2855 "*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF",
2856 "*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64",
2857 "*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK",
2858 "*.caf", "*.CAF", NULL
2859 };
2860 const char* soundfiles = _("Sound Files");
2861 const char* allfiles = _("All Files");
2862 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
2863 for (int i = 0; supportedFileTypes[i]; i++)
2864 soundfilter.add_pattern(supportedFileTypes[i]);
2865 soundfilter.set_name(soundfiles);
2866
2867 // matches every file
2868 Gtk::FileFilter allpassfilter;
2869 allpassfilter.add_pattern("*.*");
2870 allpassfilter.set_name(allfiles);
2871 #else
2872 for (int i = 0; supportedFileTypes[i]; i++)
2873 soundfilter->add_pattern(supportedFileTypes[i]);
2874 soundfilter->set_name(soundfiles);
2875
2876 // matches every file
2877 Glib::RefPtr<Gtk::FileFilter> allpassfilter = Gtk::FileFilter::create();
2878 allpassfilter->add_pattern("*.*");
2879 allpassfilter->set_name(allfiles);
2880 #endif
2881 dialog.add_filter(soundfilter);
2882 dialog.add_filter(allpassfilter);
2883 if (current_sample_dir != "") {
2884 dialog.set_current_folder(current_sample_dir);
2885 }
2886 if (dialog.run() == Gtk::RESPONSE_OK) {
2887 current_sample_dir = dialog.get_current_folder();
2888 Glib::ustring error_files;
2889 std::vector<std::string> filenames = dialog.get_filenames();
2890 for (std::vector<std::string>::iterator iter = filenames.begin();
2891 iter != filenames.end(); ++iter) {
2892 printf("Adding sample %s\n",(*iter).c_str());
2893 // use libsndfile to retrieve file informations
2894 SF_INFO info;
2895 info.format = 0;
2896 SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
2897 try {
2898 if (!hFile) throw std::string(_("could not open file"));
2899 int bitdepth;
2900 switch (info.format & 0xff) {
2901 case SF_FORMAT_PCM_S8:
2902 case SF_FORMAT_PCM_16:
2903 case SF_FORMAT_PCM_U8:
2904 bitdepth = 16;
2905 break;
2906 case SF_FORMAT_PCM_24:
2907 case SF_FORMAT_PCM_32:
2908 case SF_FORMAT_FLOAT:
2909 case SF_FORMAT_DOUBLE:
2910 bitdepth = 24;
2911 break;
2912 default:
2913 sf_close(hFile); // close sound file
2914 throw std::string(_("format not supported")); // unsupported subformat (yet?)
2915 }
2916 // add a new sample to the .gig file (if adding is requested actually)
2917 if (!replace) sample = file->AddSample();
2918 // file name without path
2919 Glib::ustring filename = Glib::filename_display_basename(*iter);
2920 // remove file extension if there is one
2921 for (int i = 0; supportedFileTypes[i]; i++) {
2922 if (Glib::str_has_suffix(filename, supportedFileTypes[i] + 1)) {
2923 filename.erase(filename.length() - strlen(supportedFileTypes[i] + 1));
2924 break;
2925 }
2926 }
2927 sample->pInfo->Name = gig_from_utf8(filename);
2928