/[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 (6 years, 10 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 sample->Channels = info.channels;
2929 sample->BitDepth = bitdepth;
2930 sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
2931 sample->SamplesPerSecond = info.samplerate;
2932 sample->AverageBytesPerSecond = sample->FrameSize * sample->SamplesPerSecond;
2933 sample->BlockAlign = sample->FrameSize;
2934 sample->SamplesTotal = info.frames;
2935
2936 SF_INSTRUMENT instrument;
2937 if (sf_command(hFile, SFC_GET_INSTRUMENT,
2938 &instrument, sizeof(instrument)) != SF_FALSE)
2939 {
2940 sample->MIDIUnityNote = instrument.basenote;
2941 sample->FineTune = instrument.detune;
2942
2943 if (instrument.loop_count && instrument.loops[0].mode != SF_LOOP_NONE) {
2944 sample->Loops = 1;
2945
2946 switch (instrument.loops[0].mode) {
2947 case SF_LOOP_FORWARD:
2948 sample->LoopType = gig::loop_type_normal;
2949 break;
2950 case SF_LOOP_BACKWARD:
2951 sample->LoopType = gig::loop_type_backward;
2952 break;
2953 case SF_LOOP_ALTERNATING:
2954 sample->LoopType = gig::loop_type_bidirectional;
2955 break;
2956 }
2957 sample->LoopStart = instrument.loops[0].start;
2958 sample->LoopEnd = instrument.loops[0].end;
2959 sample->LoopPlayCount = instrument.loops[0].count;
2960 sample->LoopSize = sample->LoopEnd - sample->LoopStart + 1;
2961 }
2962 }
2963
2964 // schedule resizing the sample (which will be done
2965 // physically when File::Save() is called)
2966 sample->Resize(info.frames);
2967 // make sure sample is part of the selected group
2968 if (!replace) group->AddSample(sample);
2969 // schedule that physical resize and sample import
2970 // (data copying), performed when "Save" is requested
2971 SampleImportItem sched_item;
2972 sched_item.gig_sample = sample;
2973 sched_item.sample_path = *iter;
2974 m_SampleImportQueue[sample] = sched_item;
2975 // add sample to the tree view
2976 if (replace) {
2977 row[m_SamplesModel.m_col_name] = gig_to_utf8(sample->pInfo->Name);
2978 } else {
2979 Gtk::TreeModel::iterator iterSample =
2980 m_refSamplesTreeModel->append(row.children());
2981 Gtk::TreeModel::Row rowSample = *iterSample;
2982 rowSample[m_SamplesModel.m_col_name] =
2983 gig_to_utf8(sample->pInfo->Name);
2984 rowSample[m_SamplesModel.m_col_sample] = sample;
2985 rowSample[m_SamplesModel.m_col_group] = NULL;
2986 }
2987 // close sound file
2988 sf_close(hFile);
2989 file_changed();
2990 } catch (std::string what) { // remember the files that made trouble (and their cause)
2991 if (!error_files.empty()) error_files += "\n";
2992 error_files += *iter += " (" + what + ")";
2993 }
2994 }
2995 // show error message box when some file(s) could not be opened / added
2996 if (!error_files.empty()) {
2997 Glib::ustring txt =
2998 (replace
2999 ? _("Failed to replace sample with:\n")
3000 : _("Could not add the following sample(s):\n"))
3001 + error_files;
3002 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
3003 msg.run();
3004 }
3005 }
3006 }
3007
3008 void MainWindow::on_action_replace_all_samples_in_all_groups()
3009 {
3010 if (!file) return;
3011 Gtk::FileChooserDialog dialog(*this, _("Select Folder"),
3012 Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
3013 const char* str =
3014 _("This is a very specific function. It tries to replace all samples "
3015 "in the current gig file by samples located in the chosen "
3016 "directory.\n\n"
3017 "It works like this: For each sample in the gig file, it tries to "
3018 "find a sample file in the selected directory with the same name as "
3019 "the sample in the gig file. Optionally, you can add a filename "
3020 "extension below, which will be added to the filename expected to be "
3021 "found. That is, assume you have a gig file with a sample called "
3022 "'Snare', if you enter '.wav' below (like it's done by default), it "
3023 "expects to find a sample file called 'Snare.wav' and will replace "
3024 "the sample in the gig file accordingly. If you don't need an "
3025 "extension, blank the field below. Any gig sample where no "
3026 "appropriate sample file could be found will be reported and left "
3027 "untouched.\n");
3028 #if GTKMM_MAJOR_VERSION < 3
3029 view::WrapLabel description(str);
3030 #else
3031 Gtk::Label description(str);
3032 description.set_line_wrap();
3033 #endif
3034 Gtk::HBox entryArea;
3035 Gtk::Label entryLabel( _("Add filename extension: "), Gtk::ALIGN_START);
3036 Gtk::Entry postfixEntryBox;
3037 postfixEntryBox.set_text(".wav");
3038 entryArea.pack_start(entryLabel);
3039 entryArea.pack_start(postfixEntryBox);
3040 dialog.get_vbox()->pack_start(description, Gtk::PACK_SHRINK);
3041 dialog.get_vbox()->pack_start(entryArea, Gtk::PACK_SHRINK);
3042 description.show();
3043 entryArea.show_all();
3044 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
3045 dialog.add_button(_("Select"), Gtk::RESPONSE_OK);
3046 dialog.set_select_multiple(false);
3047 if (current_sample_dir != "") {
3048 dialog.set_current_folder(current_sample_dir);
3049 }
3050 if (dialog.run() == Gtk::RESPONSE_OK)
3051 {
3052 current_sample_dir = dialog.get_current_folder();
3053 Glib::ustring error_files;
3054 std::string folder = dialog.get_filename();
3055 for (gig::Sample* sample = file->GetFirstSample();
3056 sample; sample = file->GetNextSample())
3057 {
3058 std::string filename =
3059 folder + G_DIR_SEPARATOR_S +
3060 Glib::filename_from_utf8(gig_to_utf8(sample->pInfo->Name) +
3061 postfixEntryBox.get_text());
3062 SF_INFO info;
3063 info.format = 0;
3064 SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &info);
3065 try
3066 {
3067 if (!hFile) throw std::string(_("could not open file"));
3068 switch (info.format & 0xff) {
3069 case SF_FORMAT_PCM_S8:
3070 case SF_FORMAT_PCM_16:
3071 case SF_FORMAT_PCM_U8:
3072 case SF_FORMAT_PCM_24:
3073 case SF_FORMAT_PCM_32:
3074 case SF_FORMAT_FLOAT:
3075 case SF_FORMAT_DOUBLE:
3076 break;
3077 default:
3078 sf_close(hFile);
3079 throw std::string(_("format not supported"));
3080 }
3081 SampleImportItem sched_item;
3082 sched_item.gig_sample = sample;
3083 sched_item.sample_path = filename;
3084 m_SampleImportQueue[sample] = sched_item;
3085 sf_close(hFile);
3086 file_changed();
3087 }
3088 catch (std::string what)
3089 {
3090 if (!error_files.empty()) error_files += "\n";
3091 error_files += Glib::filename_to_utf8(filename) +
3092 " (" + what + ")";
3093 }
3094 }
3095 // show error message box when some file(s) could not be opened / added
3096 if (!error_files.empty()) {
3097 Glib::ustring txt =
3098 _("Could not replace the following sample(s):\n") + error_files;
3099 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
3100 msg.run();
3101 }
3102 }
3103 }
3104
3105 void MainWindow::on_action_remove_sample() {
3106 if (!file) return;
3107 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3108 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3109 for (int r = rows.size() - 1; r >= 0; --r) {
3110 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[r]);
3111 if (!it) continue;
3112 Gtk::TreeModel::Row row = *it;
3113 gig::Group* group = row[m_SamplesModel.m_col_group];
3114 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
3115 Glib::ustring name = row[m_SamplesModel.m_col_name];
3116 try {
3117 // remove group or sample from the gig file
3118 if (group) {
3119 // temporarily remember the samples that belong to
3120 // that group (we need that to clean the queue)
3121 std::list<gig::Sample*> members;
3122 for (gig::Sample* pSample = group->GetFirstSample();
3123 pSample; pSample = group->GetNextSample()) {
3124 members.push_back(pSample);
3125 }
3126 // notify everybody that we're going to remove these samples
3127 samples_to_be_removed_signal.emit(members);
3128 // delete the group in the .gig file including the
3129 // samples that belong to the group
3130 file->DeleteGroup(group);
3131 // notify that we're done with removal
3132 samples_removed_signal.emit();
3133 // if sample(s) were just previously added, remove
3134 // them from the import queue
3135 for (std::list<gig::Sample*>::iterator member = members.begin();
3136 member != members.end(); ++member)
3137 {
3138 if (m_SampleImportQueue.count(*member)) {
3139 printf("Removing previously added sample '%s' from group '%s'\n",
3140 m_SampleImportQueue[sample].sample_path.c_str(), name.c_str());
3141 m_SampleImportQueue.erase(*member);
3142 }
3143 }
3144 file_changed();
3145 } else if (sample) {
3146 // notify everybody that we're going to remove this sample
3147 std::list<gig::Sample*> lsamples;
3148 lsamples.push_back(sample);
3149 samples_to_be_removed_signal.emit(lsamples);
3150 // remove sample from the .gig file
3151 file->DeleteSample(sample);
3152 // notify that we're done with removal
3153 samples_removed_signal.emit();
3154 // if sample was just previously added, remove it from
3155 // the import queue
3156 if (m_SampleImportQueue.count(sample)) {
3157 printf("Removing previously added sample '%s'\n",
3158 m_SampleImportQueue[sample].sample_path.c_str());
3159 m_SampleImportQueue.erase(sample);
3160 }
3161 dimreg_changed();
3162 file_changed();
3163 }
3164 // remove respective row(s) from samples tree view
3165 m_refSamplesTreeModel->erase(it);
3166 } catch (RIFF::Exception e) {
3167 // pretend we're done with removal (i.e. to avoid dead locks)
3168 samples_removed_signal.emit();
3169 // show error message
3170 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
3171 msg.run();
3172 }
3173 }
3174 }
3175
3176 void MainWindow::on_action_remove_unused_samples() {
3177 if (!file) return;
3178
3179 // collect all samples that are not referenced by any instrument
3180 std::list<gig::Sample*> lsamples;
3181 for (int iSample = 0; file->GetSample(iSample); ++iSample) {
3182 gig::Sample* sample = file->GetSample(iSample);
3183 bool isUsed = false;
3184 for (gig::Instrument* instrument = file->GetFirstInstrument(); instrument;
3185 instrument = file->GetNextInstrument())
3186 {
3187 for (gig::Region* rgn = instrument->GetFirstRegion(); rgn;
3188 rgn = instrument->GetNextRegion())
3189 {
3190 for (int i = 0; i < 256; ++i) {
3191 if (!rgn->pDimensionRegions[i]) continue;
3192 if (rgn->pDimensionRegions[i]->pSample != sample) continue;
3193 isUsed = true;
3194 goto endOfRefSearch;
3195 }
3196 }
3197 }
3198 endOfRefSearch:
3199 if (!isUsed) lsamples.push_back(sample);
3200 }
3201
3202 if (lsamples.empty()) return;
3203
3204 // notify everybody that we're going to remove these samples
3205 samples_to_be_removed_signal.emit(lsamples);
3206
3207 // remove collected samples
3208 try {
3209 for (std::list<gig::Sample*>::iterator itSample = lsamples.begin();
3210 itSample != lsamples.end(); ++itSample)
3211 {
3212 gig::Sample* sample = *itSample;
3213 // remove sample from the .gig file
3214 file->DeleteSample(sample);
3215 // if sample was just previously added, remove it from the import queue
3216 if (m_SampleImportQueue.count(sample)) {
3217 printf("Removing previously added sample '%s'\n",
3218 m_SampleImportQueue[sample].sample_path.c_str());
3219 m_SampleImportQueue.erase(sample);
3220 }
3221 }
3222 } catch (RIFF::Exception e) {
3223 // show error message
3224 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
3225 msg.run();
3226 }
3227
3228 // notify everybody that we're done with removal
3229 samples_removed_signal.emit();
3230
3231 dimreg_changed();
3232 file_changed();
3233 __refreshEntireGUI();
3234 }
3235
3236 // see comment on on_sample_treeview_drag_begin()
3237 void MainWindow::on_scripts_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
3238 {
3239 first_call_to_drag_data_get = true;
3240 }
3241
3242 void MainWindow::on_scripts_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
3243 Gtk::SelectionData& selection_data, guint, guint)
3244 {
3245 if (!first_call_to_drag_data_get) return;
3246 first_call_to_drag_data_get = false;
3247
3248 // get selected script
3249 gig::Script* script = NULL;
3250 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
3251 Gtk::TreeModel::iterator it = sel->get_selected();
3252 if (it) {
3253 Gtk::TreeModel::Row row = *it;
3254 script = row[m_ScriptsModel.m_col_script];
3255 }
3256 // pass the gig::Script as pointer
3257 selection_data.set(selection_data.get_target(), 0/*unused*/,
3258 (const guchar*)&script,
3259 sizeof(script)/*length of data in bytes*/);
3260 }
3261
3262 // see comment on on_sample_treeview_drag_begin()
3263 void MainWindow::on_instruments_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
3264 {
3265 first_call_to_drag_data_get = true;
3266 }
3267
3268 void MainWindow::on_instruments_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
3269 Gtk::SelectionData& selection_data, guint, guint)
3270 {
3271 if (!first_call_to_drag_data_get) return;
3272 first_call_to_drag_data_get = false;
3273
3274 // get selected source instrument
3275 gig::Instrument* src = NULL;
3276 {
3277 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
3278 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3279 if (!rows.empty()) {
3280 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
3281 if (it) {
3282 Gtk::TreeModel::Row row = *it;
3283 src = row[m_Columns.m_col_instr];
3284 }
3285 }
3286 }
3287 if (!src) return;
3288
3289 // pass the source gig::Instrument as pointer
3290 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&src,
3291 sizeof(src)/*length of data in bytes*/);
3292 }
3293
3294 void MainWindow::on_instruments_treeview_drop_drag_data_received(
3295 const Glib::RefPtr<Gdk::DragContext>& context, int x, int y,
3296 const Gtk::SelectionData& selection_data, guint, guint time)
3297 {
3298 gig::Instrument* src = *((gig::Instrument**) selection_data.get_data());
3299 if (!src || selection_data.get_length() != sizeof(gig::Instrument*))
3300 return;
3301
3302 gig::Instrument* dst = NULL;
3303 {
3304 Gtk::TreeModel::Path path;
3305 const bool found = m_TreeView.get_path_at_pos(x, y, path);
3306 if (!found) return;
3307
3308 Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path);
3309 if (!iter) return;
3310 Gtk::TreeModel::Row row = *iter;
3311 dst = row[m_Columns.m_col_instr];
3312 }
3313 if (!dst) return;
3314
3315 //printf("dragdrop received src=%s dst=%s\n", src->pInfo->Name.c_str(), dst->pInfo->Name.c_str());
3316 src->MoveTo(dst);
3317 __refreshEntireGUI();
3318 select_instrument(src);
3319 }
3320
3321 // For some reason drag_data_get gets called two times for each
3322 // drag'n'drop (at least when target is an Entry). This work-around
3323 // makes sure the code in drag_data_get and drop_drag_data_received is
3324 // only executed once, as drag_begin only gets called once.
3325 void MainWindow::on_sample_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
3326 {
3327 first_call_to_drag_data_get = true;
3328 }
3329
3330 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
3331 Gtk::SelectionData& selection_data, guint, guint)
3332 {
3333 if (!first_call_to_drag_data_get) return;
3334 first_call_to_drag_data_get = false;
3335
3336 // get selected sample
3337 gig::Sample* sample = NULL;
3338 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3339 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3340 if (!rows.empty()) {
3341 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]);
3342 if (it) {
3343 Gtk::TreeModel::Row row = *it;
3344 sample = row[m_SamplesModel.m_col_sample];
3345 }
3346 }
3347 // pass the gig::Sample as pointer
3348 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
3349 sizeof(sample)/*length of data in bytes*/);
3350 }
3351
3352 void MainWindow::on_sample_label_drop_drag_data_received(
3353 const Glib::RefPtr<Gdk::DragContext>& context, int, int,
3354 const Gtk::SelectionData& selection_data, guint, guint time)
3355 {
3356 gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
3357
3358 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
3359 std::cout << "Drop received sample \"" <<
3360 sample->pInfo->Name << "\"" << std::endl;
3361 // drop success
3362 context->drop_reply(true, time);
3363
3364 //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
3365
3366 // notify everybody that we're going to alter the region
3367 gig::Region* region = m_RegionChooser.get_region();
3368 region_to_be_changed_signal.emit(region);
3369
3370 // find the samplechannel dimension
3371 gig::dimension_def_t* stereo_dimension = 0;
3372 for (int i = 0 ; i < region->Dimensions ; i++) {
3373 if (region->pDimensionDefinitions[i].dimension ==
3374 gig::dimension_samplechannel) {
3375 stereo_dimension = &region->pDimensionDefinitions[i];
3376 break;
3377 }
3378 }
3379 bool channels_changed = false;
3380 if (sample->Channels == 1 && stereo_dimension) {
3381 // remove the samplechannel dimension
3382 /* commented out, because it makes it impossible building up an instrument from scratch using two separate L/R samples
3383 region->DeleteDimension(stereo_dimension);
3384 channels_changed = true;
3385 region_changed();
3386 */
3387 }
3388 dimreg_edit.set_sample(
3389 sample,
3390 is_copy_samples_unity_note_enabled(),
3391 is_copy_samples_fine_tune_enabled(),
3392 is_copy_samples_loop_enabled()
3393 );
3394
3395 if (sample->Channels == 2 && !stereo_dimension) {
3396 // add samplechannel dimension
3397 gig::dimension_def_t dim;
3398 dim.dimension = gig::dimension_samplechannel;
3399 dim.bits = 1;
3400 dim.zones = 2;
3401 region->AddDimension(&dim);
3402 channels_changed = true;
3403 region_changed();
3404 }
3405 if (channels_changed) {
3406 // unmap all samples with wrong number of channels
3407 // TODO: maybe there should be a warning dialog for this
3408 for (int i = 0 ; i < region->DimensionRegions ; i++) {
3409 gig::DimensionRegion* d = region->pDimensionRegions[i];
3410 if (d->pSample && d->pSample->Channels != sample->Channels) {
3411 gig::Sample* oldref = d->pSample;
3412 d->pSample = NULL;
3413 sample_ref_changed_signal.emit(oldref, NULL);
3414 }
3415 }
3416 }
3417
3418 // notify we're done with altering
3419 region_changed_signal.emit(region);
3420
3421 file_changed();
3422
3423 return;
3424 }
3425 // drop failed
3426 context->drop_reply(false, time);
3427 }
3428
3429 void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
3430 const Gtk::TreeModel::iterator& iter) {
3431 if (!iter) return;
3432 Gtk::TreeModel::Row row = *iter;
3433 Glib::ustring name = row[m_SamplesModel.m_col_name];
3434 gig::Group* group = row[m_SamplesModel.m_col_group];
3435 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
3436 gig::String gigname(gig_from_utf8(name));
3437 if (group) {
3438 if (group->Name != gigname) {
3439 group->Name = gigname;
3440 printf("group name changed\n");
3441 file_changed();
3442 }
3443 } else if (sample) {
3444 if (sample->pInfo->Name != gigname) {
3445 sample->pInfo->Name = gigname;
3446 printf("sample name changed\n");
3447 file_changed();
3448 }
3449 }
3450 }
3451
3452 void MainWindow::script_name_changed(const Gtk::TreeModel::Path& path,
3453 const Gtk::TreeModel::iterator& iter) {
3454 if (!iter) return;
3455 Gtk::TreeModel::Row row = *iter;
3456 Glib::ustring name = row[m_ScriptsModel.m_col_name];
3457 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
3458 gig::Script* script = row[m_ScriptsModel.m_col_script];
3459 gig::String gigname(gig_from_utf8(name));
3460 if (group) {
3461 if (group->Name != gigname) {
3462 group->Name = gigname;
3463 printf("script group name changed\n");
3464 file_changed();
3465 }
3466 } else if (script) {
3467 if (script->Name != gigname) {
3468 script->Name = gigname;
3469 printf("script name changed\n");
3470 file_changed();
3471 }
3472 }
3473 }
3474
3475 void MainWindow::script_double_clicked(const Gtk::TreeModel::Path& path,
3476 Gtk::TreeViewColumn* column)
3477 {
3478 Gtk::TreeModel::iterator iter = m_refScriptsTreeModel->get_iter(path);
3479 if (!iter) return;
3480 Gtk::TreeModel::Row row = *iter;
3481 gig::Script* script = row[m_ScriptsModel.m_col_script];
3482 if (!script) return;
3483
3484 ScriptEditor* editor = new ScriptEditor;
3485 editor->signal_script_to_be_changed.connect(
3486 signal_script_to_be_changed.make_slot()
3487 );
3488 editor->signal_script_changed.connect(
3489 signal_script_changed.make_slot()
3490 );
3491 editor->setScript(script);
3492 //editor->reparent(*this);
3493 editor->show();
3494 }
3495
3496 void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
3497 const Gtk::TreeModel::iterator& iter) {
3498 if (!iter) return;
3499 Gtk::TreeModel::Row row = *iter;
3500 Glib::ustring name = row[m_Columns.m_col_name];
3501
3502 // change name in instrument menu
3503 int index = path[0];
3504 const std::vector<Gtk::Widget*> children = instrument_menu->get_children();
3505 if (index < children.size()) {
3506 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 16) || GTKMM_MAJOR_VERSION > 2
3507 static_cast<Gtk::RadioMenuItem*>(children[index])->set_label(name);
3508 #else
3509 remove_instrument_from_menu(index);
3510 Gtk::RadioMenuItem* item = add_instrument_to_menu(name, index);
3511 item->set_active();
3512 #endif
3513 }
3514
3515 // change name in gig
3516 gig::Instrument* instrument = row[m_Columns.m_col_instr];
3517 gig::String gigname(gig_from_utf8(name));
3518 if (instrument && instrument->pInfo->Name != gigname) {
3519 instrument->pInfo->Name = gigname;
3520
3521 // change name in the instrument properties window
3522 if (instrumentProps.get_instrument() == instrument) {
3523 instrumentProps.update_name();
3524 }
3525
3526 file_changed();
3527 }
3528 }
3529
3530 void MainWindow::on_action_combine_instruments() {
3531 CombineInstrumentsDialog* d = new CombineInstrumentsDialog(*this, file);
3532 d->show_all();
3533 d->resize(500, 400);
3534 d->run();
3535 if (d->fileWasChanged()) {
3536 // update GUI with new instrument just created
3537 add_instrument(d->newCombinedInstrument());
3538 }
3539 delete d;
3540 }
3541
3542 void MainWindow::on_action_view_references() {
3543 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3544 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3545 if (rows.empty()) return;
3546 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]);
3547 if (!it) return;
3548 Gtk::TreeModel::Row row = *it;
3549 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
3550 if (!sample) return;
3551
3552 ReferencesView* d = new ReferencesView(*this);
3553 d->setSample(sample);
3554 d->dimension_region_selected.connect(
3555 sigc::mem_fun(*this, &MainWindow::select_dimension_region)
3556 );
3557 d->show_all();
3558 d->resize(500, 400);
3559 d->run();
3560 delete d;
3561 }
3562
3563 void MainWindow::mergeFiles(const std::vector<std::string>& filenames) {
3564 struct _Source {
3565 std::vector<RIFF::File*> riffs;
3566 std::vector<gig::File*> gigs;
3567
3568 ~_Source() {
3569 for (int k = 0; k < gigs.size(); ++k) delete gigs[k];
3570 for (int k = 0; k < riffs.size(); ++k) delete riffs[k];
3571 riffs.clear();
3572 gigs.clear();
3573 }
3574 } sources;
3575
3576 if (filenames.empty())
3577 throw RIFF::Exception(_("No files selected, so nothing done."));
3578
3579 // first open all input files (to avoid output file corruption)
3580 int i;
3581 try {
3582 for (i = 0; i < filenames.size(); ++i) {
3583 const std::string& filename = filenames[i];
3584 printf("opening file=%s\n", filename.c_str());
3585
3586 RIFF::File* riff = new RIFF::File(filename);
3587 sources.riffs.push_back(riff);
3588
3589 gig::File* gig = new gig::File(riff);
3590 sources.gigs.push_back(gig);
3591 }
3592 } catch (RIFF::Exception e) {
3593 throw RIFF::Exception(
3594 _("Error occurred while opening '") +
3595 filenames[i] +
3596 "': " +
3597 e.Message
3598 );
3599 } catch (...) {
3600 throw RIFF::Exception(
3601 _("Unknown exception occurred while opening '") +
3602 filenames[i] + "'"
3603 );
3604 }
3605
3606 // now merge the opened .gig files to the main .gig file currently being
3607 // open in gigedit
3608 try {
3609 for (i = 0; i < filenames.size(); ++i) {
3610 const std::string& filename = filenames[i];
3611 printf("merging file=%s\n", filename.c_str());
3612 assert(i < sources.gigs.size());
3613
3614 this->file->AddContentOf(sources.gigs[i]);
3615 }
3616 } catch (RIFF::Exception e) {
3617 throw RIFF::Exception(
3618 _("Error occurred while merging '") +
3619 filenames[i] +
3620 "': " +
3621 e.Message
3622 );
3623 } catch (...) {
3624 throw RIFF::Exception(
3625 _("Unknown exception occurred while merging '") +
3626 filenames[i] + "'"
3627 );
3628 }
3629
3630 // Finally save gig file persistently to disk ...
3631 //NOTE: requires that this gig file already has a filename !
3632 {
3633 std::cout << "Saving file\n" << std::flush;
3634 file_structure_to_be_changed_signal.emit(this->file);
3635
3636 progress_dialog = new ProgressDialog( //FIXME: memory leak!
3637 _("Saving") + Glib::ustring(" '") +
3638 Glib::filename_display_basename(this->filename) + "' ...",
3639 *this
3640 );
3641 progress_dialog->show_all();
3642 saver = new Saver(this->file); //FIXME: memory leak!
3643 saver->signal_progress().connect(
3644 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
3645 saver->signal_finished().connect(
3646 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
3647 saver->signal_error().connect(
3648 sigc::mem_fun(*this, &MainWindow::on_saver_error));
3649 saver->launch();
3650 }
3651 }
3652
3653 void MainWindow::on_action_merge_files() {
3654 if (this->file->GetFileName().empty()) {
3655 Glib::ustring txt = _(
3656 "You seem to have a new .gig file open that has not been saved "
3657 "yet. You must save it somewhere before starting to merge it with "
3658 "other .gig files though, because during the merge operation the "
3659 "other files' sample data must be written on file level to the "
3660 "target .gig file."
3661 );
3662 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
3663 msg.run();
3664 return;
3665 }
3666
3667 Gtk::FileChooserDialog dialog(*this, _("Merge .gig files"));
3668 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
3669 dialog.add_button(_("Merge"), Gtk::RESPONSE_OK);
3670 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
3671 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
3672 Gtk::FileFilter filter;
3673 filter.add_pattern("*.gig");
3674 #else
3675 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
3676 filter->add_pattern("*.gig");
3677 #endif
3678 dialog.set_filter(filter);
3679 if (current_gig_dir != "") {
3680 dialog.set_current_folder(current_gig_dir);
3681 }
3682 dialog.set_select_multiple(true);
3683
3684 // show warning in the file picker dialog
3685 Gtk::HBox descriptionArea;
3686 descriptionArea.set_spacing(15);
3687 Gtk::Image warningIcon;
3688 warningIcon.set_from_icon_name("dialog-warning",
3689 Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
3690 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
3691 #if GTKMM_MAJOR_VERSION < 3
3692 view::WrapLabel description;
3693 #else
3694 Gtk::Label description;
3695 description.set_line_wrap();
3696 #endif
3697 description.set_markup(_(
3698 "\nSelect at least one .gig file that shall be merged to the .gig file "
3699 "currently being open in gigedit.\n\n"
3700 "<b>Please Note:</b> Merging with other files will modify your "
3701 "currently open .gig file on file level! And be aware that the current "
3702 "merge algorithm does not detect duplicate samples yet. So if you are "
3703 "merging files which are using equivalent sample data, those "
3704 "equivalent samples will currently be treated as separate samples and "
3705 "will accordingly be stored separately in the target .gig file!"
3706 ));
3707 descriptionArea.pack_start(description);
3708 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
3709 descriptionArea.show_all();
3710
3711 if (dialog.run() == Gtk::RESPONSE_OK) {
3712 printf("on_action_merge_files self=%p\n",
3713 static_cast<void*>(Glib::Threads::Thread::self()));
3714 std::vector<std::string> filenames = dialog.get_filenames();
3715
3716 // merge the selected files to the currently open .gig file
3717 try {
3718 mergeFiles(filenames);
3719 } catch (RIFF::Exception e) {
3720 Gtk::MessageDialog msg(*this, e.Message, false, Gtk::MESSAGE_ERROR);
3721 msg.run();
3722 }
3723
3724 // update GUI
3725 __refreshEntireGUI();
3726 }
3727 }
3728
3729 void MainWindow::set_file_is_shared(bool b) {
3730 this->file_is_shared = b;
3731
3732 if (file_is_shared) {
3733 m_AttachedStateLabel.set_label(_("live-mode"));
3734 m_AttachedStateImage.set(
3735 Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
3736 );
3737 } else {
3738 m_AttachedStateLabel.set_label(_("stand-alone"));
3739 m_AttachedStateImage.set(
3740 Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
3741 );
3742 }
3743
3744 {
3745 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
3746 uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection"));
3747 if (item) item->set_sensitive(b);
3748 }
3749 }
3750
3751 void MainWindow::on_sample_ref_count_incremented(gig::Sample* sample, int offset) {
3752 if (!sample) return;
3753 sample_ref_count[sample] += offset;
3754 const int refcount = sample_ref_count[sample];
3755
3756 Glib::RefPtr<Gtk::TreeModel> model = m_TreeViewSamples.get_model();
3757 for (int g = 0; g < model->children().size(); ++g) {
3758 Gtk::TreeModel::Row rowGroup = model->children()[g];
3759 for (int s = 0; s < rowGroup.children().size(); ++s) {
3760 Gtk::TreeModel::Row rowSample = rowGroup.children()[s];
3761 if (rowSample[m_SamplesModel.m_col_sample] != sample) continue;
3762 rowSample[m_SamplesModel.m_col_refcount] = ToString(refcount) + " " + _("Refs.");
3763 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
3764 }
3765 }
3766 }
3767
3768 void MainWindow::on_sample_ref_changed(gig::Sample* oldSample, gig::Sample* newSample) {
3769 on_sample_ref_count_incremented(oldSample, -1);
3770 on_sample_ref_count_incremented(newSample, +1);
3771 }
3772
3773 void MainWindow::on_samples_to_be_removed(std::list<gig::Sample*> samples) {
3774 // just in case a new sample is added later with exactly the same memory
3775 // address, which would lead to incorrect refcount if not deleted here
3776 for (std::list<gig::Sample*>::const_iterator it = samples.begin();
3777 it != samples.end(); ++it)
3778 {
3779 sample_ref_count.erase(*it);
3780 }
3781 }
3782
3783 void MainWindow::show_samples_tab() {
3784 m_TreeViewNotebook.set_current_page(0);
3785 }
3786
3787 void MainWindow::show_intruments_tab() {
3788 m_TreeViewNotebook.set_current_page(1);
3789 }
3790
3791 void MainWindow::show_scripts_tab() {
3792 m_TreeViewNotebook.set_current_page(2);
3793 }
3794
3795 void MainWindow::select_prev_region() {
3796 m_RegionChooser.select_prev_region();
3797 }
3798
3799 void MainWindow::select_next_region() {
3800 m_RegionChooser.select_next_region();
3801 }
3802
3803 void MainWindow::select_next_dim_rgn_zone() {
3804 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
3805 m_DimRegionChooser.select_next_dimzone();
3806 }
3807
3808 void MainWindow::select_prev_dim_rgn_zone() {
3809 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
3810 m_DimRegionChooser.select_prev_dimzone();
3811 }
3812
3813 void MainWindow::select_add_next_dim_rgn_zone() {
3814 m_DimRegionChooser.select_next_dimzone(true);
3815 }
3816
3817 void MainWindow::select_add_prev_dim_rgn_zone() {
3818 m_DimRegionChooser.select_prev_dimzone(true);
3819 }
3820
3821 void MainWindow::select_prev_dimension() {
3822 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
3823 m_DimRegionChooser.select_prev_dimension();
3824 }
3825
3826 void MainWindow::select_next_dimension() {
3827 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
3828 m_DimRegionChooser.select_next_dimension();
3829 }
3830
3831 #define CLIPBOARD_DIMENSIONREGION_TARGET \
3832 ("libgig.DimensionRegion." + m_serializationArchive.rawDataFormat())
3833
3834 void MainWindow::copy_selected_dimrgn() {
3835 gig::DimensionRegion* pDimRgn = m_DimRegionChooser.get_main_dimregion();
3836 if (!pDimRgn) {
3837 updateClipboardPasteAvailable();
3838 updateClipboardCopyAvailable();
3839 return;
3840 }
3841
3842 std::vector<Gtk::TargetEntry> targets;
3843 targets.push_back( Gtk::TargetEntry(CLIPBOARD_DIMENSIONREGION_TARGET) );
3844
3845 Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
3846 clipboard->set(
3847 targets,
3848 sigc::mem_fun(*this, &MainWindow::on_clipboard_get),
3849 sigc::mem_fun(*this, &MainWindow::on_clipboard_clear)
3850 );
3851
3852 m_serializationArchive.serialize(pDimRgn);
3853
3854 updateClipboardPasteAvailable();
3855 }
3856
3857 void MainWindow::paste_copied_dimrgn() {
3858 Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
3859 clipboard->request_contents(
3860 CLIPBOARD_DIMENSIONREGION_TARGET,
3861 sigc::mem_fun(*this, &MainWindow::on_clipboard_received)
3862 );
3863 updateClipboardPasteAvailable();
3864 }
3865
3866 void MainWindow::adjust_clipboard_content() {
3867 MacroEditor* editor = new MacroEditor();
3868 editor->setMacro(&m_serializationArchive);
3869 editor->show();
3870 }
3871
3872 void MainWindow::updateClipboardPasteAvailable() {
3873 Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
3874 clipboard->request_targets(
3875 sigc::mem_fun(*this, &MainWindow::on_clipboard_received_targets)
3876 );
3877 }
3878
3879 void MainWindow::updateClipboardCopyAvailable() {
3880 bool bDimensionRegionCopyIsPossible = m_DimRegionChooser.get_main_dimregion();
3881 static_cast<Gtk::MenuItem*>(
3882 uiManager->get_widget("/MenuBar/MenuEdit/CopyDimRgn")
3883 )->set_sensitive(bDimensionRegionCopyIsPossible);
3884 }
3885
3886 void MainWindow::on_clipboard_owner_change(GdkEventOwnerChange* event) {
3887 updateClipboardPasteAvailable();
3888 }
3889
3890 void MainWindow::on_clipboard_get(Gtk::SelectionData& selection_data, guint /*info*/) {
3891 const std::string target = selection_data.get_target();
3892 if (target == CLIPBOARD_DIMENSIONREGION_TARGET) {
3893 selection_data.set(
3894 CLIPBOARD_DIMENSIONREGION_TARGET, 8 /* "format": probably unused*/,
3895 &m_serializationArchive.rawData()[0],
3896 m_serializationArchive.rawData().size()
3897 );
3898 } else {
3899 std::cerr << "Clipboard: content for unknown target '" << target << "' requested\n";
3900 }
3901 }
3902
3903 void MainWindow::on_clipboard_clear() {
3904 m_serializationArchive.clear();
3905 updateClipboardPasteAvailable();
3906 updateClipboardCopyAvailable();
3907 }
3908
3909 //NOTE: Might throw exception !!!
3910 void MainWindow::applyMacro(Serialization::Archive& macro) {
3911 gig::DimensionRegion* pDimRgn = m_DimRegionChooser.get_main_dimregion();
3912 if (!pDimRgn) return;
3913
3914 for (std::set<gig::DimensionRegion*>::iterator itDimReg = dimreg_edit.dimregs.begin();
3915 itDimReg != dimreg_edit.dimregs.end(); ++itDimReg)
3916 {
3917 gig::DimensionRegion* pDimRgn = *itDimReg;
3918 dimreg_to_be_changed_signal.emit(pDimRgn);
3919 m_serializationArchive.deserialize(pDimRgn);
3920 dimreg_changed_signal.emit(pDimRgn);
3921 }
3922 //region_changed()
3923 file_changed();
3924 dimreg_changed();
3925 }
3926
3927 void MainWindow::on_clipboard_received(const Gtk::SelectionData& selection_data) {
3928 const std::string target = selection_data.get_target();
3929 if (target == CLIPBOARD_DIMENSIONREGION_TARGET) {
3930 Glib::ustring errorText;
3931 try {
3932 m_serializationArchive.decode(
3933 selection_data.get_data(), selection_data.get_length()
3934 );
3935 applyMacro(m_serializationArchive);
3936 } catch (Serialization::Exception e) {
3937 errorText = e.Message;
3938 } catch (...) {
3939 errorText = _("Unknown exception while pasting DimensionRegion");
3940 }
3941 if (!errorText.empty()) {
3942 Glib::ustring txt = _("Pasting DimensionRegion failed:\n") + errorText;
3943 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
3944 msg.run();
3945 }
3946 }
3947 }
3948
3949 void MainWindow::on_clipboard_received_targets(const std::vector<Glib::ustring>& targets) {
3950 const bool bDimensionRegionPasteIsPossible =
3951 std::find(targets.begin(), targets.end(),
3952 CLIPBOARD_DIMENSIONREGION_TARGET) != targets.end();
3953
3954 static_cast<Gtk::MenuItem*>(
3955 uiManager->get_widget("/MenuBar/MenuEdit/PasteDimRgn")
3956 )->set_sensitive(bDimensionRegionPasteIsPossible);
3957
3958 static_cast<Gtk::MenuItem*>(
3959 uiManager->get_widget("/MenuBar/MenuEdit/AdjustClipboard")
3960 )->set_sensitive(bDimensionRegionPasteIsPossible);
3961 }
3962
3963 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
3964 return file_structure_to_be_changed_signal;
3965 }
3966
3967 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
3968 return file_structure_changed_signal;
3969 }
3970
3971 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
3972 return samples_to_be_removed_signal;
3973 }
3974
3975 sigc::signal<void>& MainWindow::signal_samples_removed() {
3976 return samples_removed_signal;
3977 }
3978
3979 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
3980 return region_to_be_changed_signal;
3981 }
3982
3983 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
3984 return region_changed_signal;
3985 }
3986
3987 sigc::signal<void, gig::Sample*>& MainWindow::signal_sample_changed() {
3988 return sample_changed_signal;
3989 }
3990
3991 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
3992 return sample_ref_changed_signal;
3993 }
3994
3995 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
3996 return dimreg_to_be_changed_signal;
3997 }
3998
3999 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
4000 return dimreg_changed_signal;
4001 }
4002
4003 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
4004 return note_on_signal;
4005 }
4006
4007 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
4008 return note_off_signal;
4009 }
4010
4011 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_hit() {
4012 return m_RegionChooser.signal_keyboard_key_hit();
4013 }
4014
4015 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_released() {
4016 return m_RegionChooser.signal_keyboard_key_released();
4017 }
4018
4019 sigc::signal<void, gig::Instrument*>& MainWindow::signal_switch_sampler_instrument() {
4020 return switch_sampler_instrument_signal;
4021 }

  ViewVC Help
Powered by ViewVC