/[svn]/gigedit/trunk/src/gigedit/mainwindow.cpp
ViewVC logotype

Annotation of /gigedit/trunk/src/gigedit/mainwindow.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3157 - (hide 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 schoenebeck 1225 /*
2 schoenebeck 3089 * Copyright (C) 2006-2017 Andreas Persson
3 schoenebeck 1225 *
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 persson 1714 #include <cstring>
22 schoenebeck 1225
23 persson 2841 #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 persson 2325 #include <glibmm/convert.h>
32     #include <glibmm/dispatcher.h>
33     #include <glibmm/miscutils.h>
34     #include <glibmm/stringutils.h>
35 persson 2151 #include <gtkmm/aboutdialog.h>
36 schoenebeck 1225 #include <gtkmm/filechooserdialog.h>
37     #include <gtkmm/messagedialog.h>
38     #include <gtkmm/targetentry.h>
39     #include <gtkmm/main.h>
40 schoenebeck 1415 #include <gtkmm/toggleaction.h>
41 schoenebeck 3157 #include <gtkmm/accelmap.h>
42 persson 2344 #if GTKMM_MAJOR_VERSION < 3
43 persson 1799 #include "wrapLabel.hh"
44 persson 2344 #endif
45 schoenebeck 1225
46 schoenebeck 1396 #include "global.h"
47 persson 2169 #include "compat.h"
48 schoenebeck 1396
49 schoenebeck 1225 #include <stdio.h>
50     #include <sndfile.h>
51 schoenebeck 2553 #include <assert.h>
52 schoenebeck 1225
53     #include "mainwindow.h"
54 schoenebeck 2541 #include "Settings.h"
55 schoenebeck 2548 #include "CombineInstrumentsDialog.h"
56 schoenebeck 2604 #include "scripteditor.h"
57 schoenebeck 2610 #include "scriptslots.h"
58 schoenebeck 2624 #include "ReferencesView.h"
59 schoenebeck 1411 #include "../../gfx/status_attached.xpm"
60     #include "../../gfx/status_detached.xpm"
61 schoenebeck 3106 #include "gfx/builtinpix.h"
62 schoenebeck 3151 #include "MacroEditor.h"
63 schoenebeck 3157 #include "MacrosSetup.h"
64 schoenebeck 1411
65 persson 1533 MainWindow::MainWindow() :
66 schoenebeck 2626 m_DimRegionChooser(*this),
67 persson 1533 dimreg_label(_("Changes apply to:")),
68     dimreg_all_regions(_("all regions")),
69     dimreg_all_dimregs(_("all dimension splits")),
70 schoenebeck 3106 dimreg_stereo(_("both channels")),
71 schoenebeck 3109 labelLegend(_("Legend:")),
72 schoenebeck 3106 labelNoSample(_(" No Sample")),
73     labelMissingSample(_(" Missing some Sample(s)")),
74     labelLooped(_(" Looped")),
75     labelSomeLoops(_(" Some Loop(s)"))
76 schoenebeck 1225 {
77 schoenebeck 3106 loadBuiltInPix();
78    
79 schoenebeck 1225 // set_border_width(5);
80     // set_default_size(400, 200);
81    
82     add(m_VBox);
83    
84     // Handle selection
85 persson 2442 m_TreeView.get_selection()->signal_changed().connect(
86 schoenebeck 1225 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 schoenebeck 2604 m_ScrolledWindowScripts.add(m_TreeViewScripts);
102     m_ScrolledWindowScripts.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
103 schoenebeck 1225
104 schoenebeck 2604
105 schoenebeck 1225 m_TreeViewNotebook.set_size_request(300);
106    
107     m_HPaned.add1(m_TreeViewNotebook);
108 persson 1533 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 persson 1582 dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
115 schoenebeck 3106 {
116 schoenebeck 3109 legend_hbox.add(labelLegend);
117    
118 schoenebeck 3106 imageNoSample.set(redDot);
119 schoenebeck 3108 imageNoSample.set_alignment(Gtk::ALIGN_END);
120     labelNoSample.set_alignment(Gtk::ALIGN_START);
121 schoenebeck 3106 legend_hbox.add(imageNoSample);
122     legend_hbox.add(labelNoSample);
123    
124     imageMissingSample.set(yellowDot);
125 schoenebeck 3108 imageMissingSample.set_alignment(Gtk::ALIGN_END);
126     labelMissingSample.set_alignment(Gtk::ALIGN_START);
127 schoenebeck 3106 legend_hbox.add(imageMissingSample);
128     legend_hbox.add(labelMissingSample);
129    
130     imageLooped.set(blackLoop);
131 schoenebeck 3108 imageLooped.set_alignment(Gtk::ALIGN_END);
132     labelLooped.set_alignment(Gtk::ALIGN_START);
133 schoenebeck 3106 legend_hbox.add(imageLooped);
134     legend_hbox.add(labelLooped);
135    
136     imageSomeLoops.set(grayLoop);
137 schoenebeck 3108 imageSomeLoops.set_alignment(Gtk::ALIGN_END);
138     labelSomeLoops.set_alignment(Gtk::ALIGN_START);
139 schoenebeck 3106 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 persson 1533 m_HPaned.add2(dimreg_vbox);
146 schoenebeck 1225
147 schoenebeck 2536 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 schoenebeck 1225
152 persson 1831 m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, _("Samples"));
153     m_TreeViewNotebook.append_page(m_ScrolledWindow, _("Instruments"));
154 schoenebeck 2604 m_TreeViewNotebook.append_page(m_ScrolledWindowScripts, _("Scripts"));
155 schoenebeck 1225
156     actionGroup = Gtk::ActionGroup::create();
157    
158     actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
159 persson 2845 actionGroup->add(Gtk::Action::create("New", _("_New")),
160     Gtk::AccelKey("<control>n"),
161 schoenebeck 1225 sigc::mem_fun(
162     *this, &MainWindow::on_action_file_new));
163 persson 2845 actionGroup->add(Gtk::Action::create("Open", _("_Open...")),
164     Gtk::AccelKey("<control>o"),
165 schoenebeck 1225 sigc::mem_fun(
166     *this, &MainWindow::on_action_file_open));
167 persson 2845 actionGroup->add(Gtk::Action::create("Save", _("_Save")),
168     Gtk::AccelKey("<control>s"),
169 schoenebeck 1225 sigc::mem_fun(
170     *this, &MainWindow::on_action_file_save));
171 persson 2845 actionGroup->add(Gtk::Action::create("SaveAs", _("Save _As...")),
172 persson 1261 Gtk::AccelKey("<shift><control>s"),
173 schoenebeck 1225 sigc::mem_fun(
174 persson 1261 *this, &MainWindow::on_action_file_save_as));
175 schoenebeck 1225 actionGroup->add(Gtk::Action::create("Properties",
176 persson 2845 _("_Properties")),
177 schoenebeck 1225 sigc::mem_fun(
178     *this, &MainWindow::on_action_file_properties));
179     actionGroup->add(Gtk::Action::create("InstrProperties",
180 persson 2845 _("_Properties")),
181 schoenebeck 1225 sigc::mem_fun(
182     *this, &MainWindow::show_instr_props));
183 persson 2507 actionGroup->add(Gtk::Action::create("MidiRules",
184 schoenebeck 2610 _("_Midi Rules...")),
185 persson 2507 sigc::mem_fun(
186     *this, &MainWindow::show_midi_rules));
187 schoenebeck 2610 actionGroup->add(Gtk::Action::create("ScriptSlots",
188     _("_Script Slots...")),
189     sigc::mem_fun(
190     *this, &MainWindow::show_script_slots));
191 persson 2845 actionGroup->add(Gtk::Action::create("Quit", _("_Quit")),
192     Gtk::AccelKey("<control>q"),
193 schoenebeck 1225 sigc::mem_fun(
194 persson 1261 *this, &MainWindow::on_action_quit));
195 schoenebeck 2625 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 schoenebeck 3144 Gtk::Action::create("MenuScript", _("Scr_ipt")),
205 schoenebeck 2625 sigc::mem_fun(*this, &MainWindow::show_scripts_tab)
206     );
207     actionGroup->add(Gtk::Action::create("AllInstruments", _("_Select")));
208 schoenebeck 1225
209 schoenebeck 2464 actionGroup->add(Gtk::Action::create("MenuEdit", _("_Edit")));
210    
211 schoenebeck 3123 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 schoenebeck 3144 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 schoenebeck 3151 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 schoenebeck 3123 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 schoenebeck 3134 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 schoenebeck 3123
268 schoenebeck 3134 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 schoenebeck 2464 Glib::RefPtr<Gtk::ToggleAction> toggle_action =
274 schoenebeck 2536 Gtk::ToggleAction::create("CopySampleUnity", _("Copy Sample's _Unity Note"));
275 schoenebeck 2464 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 schoenebeck 3157 actionGroup->add(Gtk::Action::create("MenuMacro", _("_Macro")));
290    
291    
292 schoenebeck 3144 actionGroup->add(Gtk::Action::create("MenuView", _("Vie_w")));
293 schoenebeck 2464 toggle_action =
294 schoenebeck 1415 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 schoenebeck 2918
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 schoenebeck 2967 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 schoenebeck 2772 actionGroup->add(
315     Gtk::Action::create("RefreshAll", _("_Refresh All")),
316     sigc::mem_fun(*this, &MainWindow::on_action_refresh_all)
317     );
318 schoenebeck 1415
319 persson 2845 actionGroup->add(Gtk::Action::create("MenuHelp", _("_Help")));
320     actionGroup->add(Gtk::Action::create("About", _("_About")),
321 schoenebeck 1225 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 schoenebeck 2395 Gtk::Action::create("DupInstrument", _("_Duplicate Instrument")),
329     sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument)
330     );
331     actionGroup->add(
332 persson 2845 Gtk::Action::create("RemoveInstrument", _("_Remove")),
333 schoenebeck 1225 sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
334     );
335    
336 schoenebeck 2541
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 schoenebeck 2689 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 schoenebeck 2541
355 schoenebeck 2773 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 schoenebeck 2689
363 schoenebeck 2773
364 schoenebeck 2548 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 schoenebeck 2553 actionGroup->add(
372     Gtk::Action::create("MergeFiles", _("_Merge Files...")),
373     sigc::mem_fun(*this, &MainWindow::on_action_merge_files)
374     );
375 schoenebeck 2548
376 schoenebeck 2553
377 schoenebeck 1225 // sample right-click popup actions
378     actionGroup->add(
379 persson 2845 Gtk::Action::create("SampleProperties", _("_Properties")),
380 schoenebeck 1225 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 persson 1799 Gtk::Action::create("AddSample", _("Add _Sample(s)...")),
388 schoenebeck 1225 sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
389     );
390     actionGroup->add(
391 persson 2845 Gtk::Action::create("RemoveSample", _("_Remove")),
392 schoenebeck 1225 sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
393     );
394 schoenebeck 1673 actionGroup->add(
395 schoenebeck 2772 Gtk::Action::create("RemoveUnusedSamples", _("Remove _Unused Samples")),
396     sigc::mem_fun(*this, &MainWindow::on_action_remove_unused_samples)
397     );
398     actionGroup->add(
399 schoenebeck 2624 Gtk::Action::create("ShowSampleRefs", _("Show References...")),
400     sigc::mem_fun(*this, &MainWindow::on_action_view_references)
401     );
402     actionGroup->add(
403 schoenebeck 2715 Gtk::Action::create("ReplaceSample",
404     _("Replace Sample...")),
405     sigc::mem_fun(*this, &MainWindow::on_action_replace_sample)
406     );
407     actionGroup->add(
408 persson 1799 Gtk::Action::create("ReplaceAllSamplesInAllGroups",
409     _("Replace All Samples in All Groups...")),
410 schoenebeck 1673 sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups)
411     );
412 schoenebeck 2604
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 persson 2845 Gtk::Action::create("RemoveScript", _("_Remove")),
428 schoenebeck 2604 sigc::mem_fun(*this, &MainWindow::on_action_remove_script)
429     );
430 schoenebeck 1225
431     uiManager = Gtk::UIManager::create();
432     uiManager->insert_action_group(actionGroup);
433 persson 1261 add_accel_group(uiManager->get_accel_group());
434 schoenebeck 1225
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 schoenebeck 2464 " <menu action='MenuEdit'>"
450 schoenebeck 3144 " <menuitem action='CopyDimRgn'/>"
451 schoenebeck 3151 " <menuitem action='AdjustClipboard'/>"
452 schoenebeck 3144 " <menuitem action='PasteDimRgn'/>"
453     " <separator/>"
454 schoenebeck 3123 " <menuitem action='SelectPrevRegion'/>"
455     " <menuitem action='SelectNextRegion'/>"
456     " <separator/>"
457 schoenebeck 3134 " <menuitem action='SelectPrevDimension'/>"
458     " <menuitem action='SelectNextDimension'/>"
459 schoenebeck 3123 " <menuitem action='SelectPrevDimRgnZone'/>"
460     " <menuitem action='SelectNextDimRgnZone'/>"
461 schoenebeck 3134 " <menuitem action='SelectAddPrevDimRgnZone'/>"
462     " <menuitem action='SelectAddNextDimRgnZone'/>"
463 schoenebeck 3123 " <separator/>"
464 schoenebeck 2464 " <menuitem action='CopySampleUnity'/>"
465     " <menuitem action='CopySampleTune'/>"
466     " <menuitem action='CopySampleLoop'/>"
467     " </menu>"
468 schoenebeck 3157 " <menu action='MenuMacro'>"
469     " </menu>"
470 schoenebeck 2625 " <menu action='MenuSample'>"
471     " <menuitem action='SampleProperties'/>"
472     " <menuitem action='AddGroup'/>"
473     " <menuitem action='AddSample'/>"
474     " <menuitem action='ShowSampleRefs'/>"
475 schoenebeck 2715 " <menuitem action='ReplaceSample' />"
476 schoenebeck 2625 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
477     " <separator/>"
478     " <menuitem action='RemoveSample'/>"
479 schoenebeck 2772 " <menuitem action='RemoveUnusedSamples'/>"
480 schoenebeck 2625 " </menu>"
481 schoenebeck 1225 " <menu action='MenuInstrument'>"
482 schoenebeck 2625 " <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 schoenebeck 1225 " </menu>"
493 schoenebeck 2625 " <menu action='MenuScript'>"
494     " <menuitem action='AddScriptGroup'/>"
495     " <menuitem action='AddScript'/>"
496     " <menuitem action='EditScript'/>"
497     " <separator/>"
498     " <menuitem action='RemoveScript'/>"
499     " </menu>"
500 schoenebeck 1415 " <menu action='MenuView'>"
501     " <menuitem action='Statusbar'/>"
502 schoenebeck 2918 " <menuitem action='AutoRestoreWinDim'/>"
503 schoenebeck 2772 " <separator/>"
504     " <menuitem action='RefreshAll'/>"
505 schoenebeck 1415 " </menu>"
506 schoenebeck 2548 " <menu action='MenuTools'>"
507     " <menuitem action='CombineInstruments'/>"
508 schoenebeck 2553 " <menuitem action='MergeFiles'/>"
509 schoenebeck 2548 " </menu>"
510 schoenebeck 2541 " <menu action='MenuSettings'>"
511     " <menuitem action='WarnUserOnExtensions'/>"
512 schoenebeck 2689 " <menuitem action='SyncSamplerInstrumentSelection'/>"
513 schoenebeck 2773 " <menuitem action='MoveRootNoteWithRegionMoved'/>"
514 schoenebeck 2967 " <menuitem action='SaveWithTemporaryFile'/>"
515 schoenebeck 2541 " </menu>"
516 schoenebeck 1225 " <menu action='MenuHelp'>"
517     " <menuitem action='About'/>"
518     " </menu>"
519     " </menubar>"
520     " <popup name='PopupMenu'>"
521     " <menuitem action='InstrProperties'/>"
522 persson 2507 " <menuitem action='MidiRules'/>"
523 schoenebeck 2610 " <menuitem action='ScriptSlots'/>"
524 schoenebeck 1225 " <menuitem action='AddInstrument'/>"
525 schoenebeck 2395 " <menuitem action='DupInstrument'/>"
526 schoenebeck 1225 " <separator/>"
527     " <menuitem action='RemoveInstrument'/>"
528     " </popup>"
529     " <popup name='SamplePopupMenu'>"
530     " <menuitem action='SampleProperties'/>"
531     " <menuitem action='AddGroup'/>"
532     " <menuitem action='AddSample'/>"
533 schoenebeck 2624 " <menuitem action='ShowSampleRefs'/>"
534 schoenebeck 2715 " <menuitem action='ReplaceSample' />"
535 persson 2442 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
536 schoenebeck 1225 " <separator/>"
537     " <menuitem action='RemoveSample'/>"
538 schoenebeck 2772 " <menuitem action='RemoveUnusedSamples'/>"
539 schoenebeck 1225 " </popup>"
540 schoenebeck 2604 " <popup name='ScriptPopupMenu'>"
541     " <menuitem action='AddScriptGroup'/>"
542     " <menuitem action='AddScript'/>"
543     " <menuitem action='EditScript'/>"
544     " <separator/>"
545     " <menuitem action='RemoveScript'/>"
546     " </popup>"
547 schoenebeck 1225 "</ui>";
548     uiManager->add_ui_from_string(ui_info);
549    
550     popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
551 schoenebeck 2536
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 schoenebeck 2541 {
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 schoenebeck 2553 {
577     Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
578 schoenebeck 2689 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 schoenebeck 2773 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 schoenebeck 2772 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 schoenebeck 2918 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 schoenebeck 2553 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 schoenebeck 1225
616 schoenebeck 2553
617 persson 2442 instrument_menu = static_cast<Gtk::MenuItem*>(
618 schoenebeck 2625 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments"))->get_submenu();
619 persson 2442
620 schoenebeck 1225 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 schoenebeck 1661 m_VBox.pack_start(m_RegionChooser.m_VirtKeybPropsBox, Gtk::PACK_SHRINK);
625 schoenebeck 1225 m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
626 schoenebeck 1411 m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
627 schoenebeck 1225
628 persson 2246 set_file_is_shared(false);
629    
630 schoenebeck 1411 // 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 persson 1261 m_RegionChooser.signal_region_selected().connect(
636 schoenebeck 1225 sigc::mem_fun(*this, &MainWindow::region_changed) );
637 persson 1261 m_DimRegionChooser.signal_dimregion_selected().connect(
638 schoenebeck 1225 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 schoenebeck 2994 m_TreeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
645 schoenebeck 2701 m_TreeView.set_tooltip_text(_("Right click here for actions on instruments & MIDI Rules. Drag & drop to change the order of instruments."));
646 persson 2442 instrument_name_connection = m_refTreeModel->signal_row_changed().connect(
647 schoenebeck 1225 sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
648     );
649    
650     // Add the TreeView's view columns:
651 schoenebeck 2994 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 schoenebeck 2701
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 schoenebeck 1225
673     // create samples treeview (including its data model)
674     m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
675     m_TreeViewSamples.set_model(m_refSamplesTreeModel);
676 schoenebeck 2994 m_TreeViewSamples.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
677 schoenebeck 2536 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 schoenebeck 1225 // m_TreeViewSamples.set_reorderable();
679 schoenebeck 2621 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 persson 2658 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
685 schoenebeck 2621 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 persson 2658 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
693 schoenebeck 2621 column->add_attribute(
694     cellrenderer->property_foreground(), m_SamplesModel.m_color
695     );
696     }
697     m_TreeViewSamples.set_headers_visible(true);
698 schoenebeck 1225 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 schoenebeck 2604 // 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 schoenebeck 2644 "Use CTRL + double click for editing a script."
710     "\n\n"
711 schoenebeck 2604 "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 schoenebeck 2695 //FIXME: why the heck does this double click signal_row_activated() only fire while CTRL key is pressed ?
721 schoenebeck 2644 m_TreeViewScripts.signal_row_activated().connect(
722     sigc::mem_fun(*this, &MainWindow::script_double_clicked)
723     );
724 schoenebeck 2604 m_refScriptsTreeModel->signal_row_changed().connect(
725     sigc::mem_fun(*this, &MainWindow::script_name_changed)
726     );
727    
728 schoenebeck 2610 // 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 schoenebeck 1225 // establish drag&drop between samples tree view and dimension region 'Sample' text entry
740 persson 2169 std::vector<Gtk::TargetEntry> drag_target_gig_sample;
741     drag_target_gig_sample.push_back(Gtk::TargetEntry("gig::Sample"));
742 schoenebeck 1225 m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
743 persson 1303 m_TreeViewSamples.signal_drag_begin().connect(
744     sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_begin)
745     );
746 schoenebeck 1225 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 persson 1261 dimreg_edit.signal_dimreg_changed().connect(
754 schoenebeck 1322 sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
755 persson 1261 m_RegionChooser.signal_instrument_changed().connect(
756     sigc::mem_fun(*this, &MainWindow::file_changed));
757 schoenebeck 2773 m_RegionChooser.signal_instrument_changed().connect(
758     sigc::mem_fun(*this, &MainWindow::region_changed));
759 persson 1261 m_DimRegionChooser.signal_region_changed().connect(
760     sigc::mem_fun(*this, &MainWindow::file_changed));
761 persson 2423 instrumentProps.signal_changed().connect(
762 persson 1261 sigc::mem_fun(*this, &MainWindow::file_changed));
763 persson 2423 propDialog.signal_changed().connect(
764 persson 1582 sigc::mem_fun(*this, &MainWindow::file_changed));
765 persson 2507 midiRules.signal_changed().connect(
766     sigc::mem_fun(*this, &MainWindow::file_changed));
767 schoenebeck 1322
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 schoenebeck 2621 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 schoenebeck 1322
781 schoenebeck 2691 dimreg_edit.signal_select_sample().connect(
782     sigc::mem_fun(*this, &MainWindow::select_sample)
783     );
784    
785 schoenebeck 1322 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 schoenebeck 1654 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 persson 1533 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 schoenebeck 1225 file = 0;
819 persson 1261 file_is_changed = false;
820 schoenebeck 1225
821     show_all_children();
822 schoenebeck 1300
823     // start with a new gig file by default
824     on_action_file_new();
825 schoenebeck 2550
826     // select 'Instruments' tab by default
827     // (gtk allows this only if the tab childs are visible, thats why it's here)
828 schoenebeck 3144 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 schoenebeck 3157
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 schoenebeck 1225 }
870    
871     MainWindow::~MainWindow()
872     {
873     }
874    
875 schoenebeck 3157 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 persson 1261 bool MainWindow::on_delete_event(GdkEventAny* event)
970     {
971 schoenebeck 1382 return !file_is_shared && file_is_changed && !close_confirmation_dialog();
972 persson 1261 }
973    
974     void MainWindow::on_action_quit()
975     {
976 schoenebeck 1382 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
977 persson 1261 hide();
978     }
979    
980 schoenebeck 1225 void MainWindow::region_changed()
981     {
982     m_DimRegionChooser.set_region(m_RegionChooser.get_region());
983     }
984    
985 persson 1533 gig::Instrument* MainWindow::get_instrument()
986 schoenebeck 1225 {
987 persson 1533 gig::Instrument* instrument = 0;
988 schoenebeck 2994 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 schoenebeck 1225 if (it) {
992     Gtk::TreeModel::Row row = *it;
993 persson 1533 instrument = row[m_Columns.m_col_instr];
994     }
995     return instrument;
996     }
997 schoenebeck 1225
998 persson 1533 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 schoenebeck 1225 } else {
1007 persson 1533 m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
1008 schoenebeck 1225 }
1009     }
1010    
1011 persson 1533 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 schoenebeck 3089
1034 schoenebeck 3148 m_RegionChooser.setModifyAllRegions(all_regions);
1035 schoenebeck 3089 m_DimRegionChooser.setModifyAllRegions(all_regions);
1036     m_DimRegionChooser.setModifyAllDimensionRegions(all_dimregs);
1037     m_DimRegionChooser.setModifyBothChannels(stereo);
1038 schoenebeck 3144
1039     updateClipboardCopyAvailable();
1040 persson 1533 }
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 schoenebeck 2626 dimreg_edit.set_dim_region(m_DimRegionChooser.get_main_dimregion());
1052 persson 1533 }
1053    
1054     void MainWindow::on_sel_change()
1055     {
1056 persson 2442 // select item in instrument menu
1057 schoenebeck 2994 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 persson 2442 }
1068    
1069 persson 1533 m_RegionChooser.set_instrument(get_instrument());
1070 schoenebeck 2689
1071     if (Settings::singleton()->syncSamplerInstrumentSelection) {
1072     switch_sampler_instrument_signal.emit(get_instrument());
1073     }
1074 persson 1533 }
1075    
1076 schoenebeck 1225 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 persson 2325 Glib::Threads::Mutex::Lock lock(progressMutex);
1086 schoenebeck 1225 progress = fraction;
1087     }
1088     progress_dispatcher();
1089     }
1090    
1091 persson 3021 #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 schoenebeck 1225 void Loader::thread_function()
1096     {
1097 persson 2841 printf("thread_function self=%p\n",
1098     static_cast<void*>(Glib::Threads::Thread::self()));
1099 schoenebeck 2683 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 schoenebeck 1225
1107 schoenebeck 2683 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 schoenebeck 1225 }
1118    
1119     Loader::Loader(const char* filename)
1120 persson 2841 : filename(filename), gig(0), thread(0), progress(0.f)
1121 schoenebeck 1225 {
1122     }
1123    
1124     void Loader::launch()
1125     {
1126 persson 2332 #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 persson 2841 printf("launch thread=%p\n", static_cast<void*>(thread));
1132 schoenebeck 1225 }
1133    
1134     float Loader::get_progress()
1135     {
1136     float res;
1137     {
1138 persson 2325 Glib::Threads::Mutex::Lock lock(progressMutex);
1139 schoenebeck 1225 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 schoenebeck 2683 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 persson 3021 #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 schoenebeck 2683 void Saver::thread_function()
1179     {
1180 persson 2841 printf("thread_function self=%p\n",
1181     static_cast<void*>(Glib::Threads::Thread::self()));
1182 schoenebeck 2683 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 schoenebeck 2967 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 schoenebeck 2968 if (!DeleteFile(filename.c_str())) {
1203 schoenebeck 2967 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 schoenebeck 2683 } 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 persson 2841 printf("launch thread=%p\n", static_cast<void*>(thread));
1246 schoenebeck 2683 }
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 schoenebeck 1225 : Gtk::Dialog(title, parent, true)
1275     {
1276     get_vbox()->pack_start(progressBar);
1277     show_all_children();
1278 schoenebeck 2683 resize(600,50);
1279 schoenebeck 1225 }
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 schoenebeck 2615 m_refScriptsTreeModel->clear();
1290 persson 2442 // remove all entries from "Instrument" menu
1291     while (!instrument_menu->get_children().empty()) {
1292     remove_instrument_from_menu(0);
1293     }
1294 schoenebeck 1225 // free libgig's gig::File instance
1295 schoenebeck 1382 if (file && !file_is_shared) delete file;
1296     file = NULL;
1297 schoenebeck 1411 set_file_is_shared(false);
1298 schoenebeck 1225 }
1299    
1300 schoenebeck 2553 void MainWindow::__refreshEntireGUI() {
1301     // clear the samples and instruments tree views
1302     m_refTreeModel->clear();
1303     m_refSamplesTreeModel->clear();
1304 schoenebeck 2615 m_refScriptsTreeModel->clear();
1305 schoenebeck 2553 // 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 schoenebeck 1225 void MainWindow::on_action_file_new()
1318     {
1319 schoenebeck 1382 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
1320 persson 1261
1321 schoenebeck 1382 if (file_is_shared && !leaving_shared_mode_dialog()) return;
1322    
1323 schoenebeck 1225 // 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 persson 2446 pInstrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument"));
1330 schoenebeck 1225 // update GUI with that new gig::File
1331 persson 1261 load_gig(pFile, 0 /*no file name yet*/);
1332 schoenebeck 1225 }
1333    
1334 persson 1261 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 persson 2845 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1343     dialog.add_button(file_has_name ? _("_Save") : _("Save _As"), Gtk::RESPONSE_YES);
1344 persson 1261 dialog.set_default_response(Gtk::RESPONSE_YES);
1345     int response = dialog.run();
1346 persson 1303 dialog.hide();
1347 schoenebeck 2683
1348 schoenebeck 2694 // 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 schoenebeck 2683 // 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 persson 1261 }
1365    
1366 schoenebeck 1382 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 persson 2151 "load it."));
1373 schoenebeck 1382 dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
1374 persson 2845 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1375 schoenebeck 1382 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
1376     int response = dialog.run();
1377     dialog.hide();
1378     return response == Gtk::RESPONSE_YES;
1379     }
1380    
1381 schoenebeck 1225 void MainWindow::on_action_file_open()
1382     {
1383 schoenebeck 1382 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
1384 persson 1261
1385 schoenebeck 1382 if (file_is_shared && !leaving_shared_mode_dialog()) return;
1386    
1387 schoenebeck 1225 Gtk::FileChooserDialog dialog(*this, _("Open file"));
1388 persson 2845 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
1389     dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
1390 persson 1261 dialog.set_default_response(Gtk::RESPONSE_OK);
1391 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1392 schoenebeck 1225 Gtk::FileFilter filter;
1393     filter.add_pattern("*.gig");
1394 persson 2169 #else
1395     Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
1396     filter->add_pattern("*.gig");
1397     #endif
1398 schoenebeck 1225 dialog.set_filter(filter);
1399 persson 1725 if (current_gig_dir != "") {
1400     dialog.set_current_folder(current_gig_dir);
1401 persson 1261 }
1402 schoenebeck 1225 if (dialog.run() == Gtk::RESPONSE_OK) {
1403 persson 1261 std::string filename = dialog.get_filename();
1404     printf("filename=%s\n", filename.c_str());
1405 persson 2841 printf("on_action_file_open self=%p\n",
1406     static_cast<void*>(Glib::Threads::Thread::self()));
1407 persson 1261 load_file(filename.c_str());
1408 persson 1725 current_gig_dir = Glib::path_get_dirname(filename);
1409 schoenebeck 1225 }
1410     }
1411    
1412     void MainWindow::load_file(const char* name)
1413     {
1414 persson 1303 __clear();
1415 schoenebeck 2683
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 schoenebeck 1225 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 schoenebeck 2683 loader->signal_error().connect(
1428     sigc::mem_fun(*this, &MainWindow::on_loader_error));
1429 schoenebeck 1225 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 schoenebeck 1328 // clear all GUI elements
1440     __clear();
1441     // load the instrument
1442 schoenebeck 1225 gig::File* pFile = (gig::File*) instr->GetParent();
1443 schoenebeck 1382 load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
1444 schoenebeck 2664 // 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 schoenebeck 1225 }
1467    
1468     void MainWindow::on_loader_progress()
1469     {
1470 schoenebeck 2683 progress_dialog->set_fraction(loader->get_progress());
1471 schoenebeck 1225 }
1472    
1473     void MainWindow::on_loader_finished()
1474     {
1475     printf("Loader finished!\n");
1476 persson 2841 printf("on_loader_finished self=%p\n",
1477     static_cast<void*>(Glib::Threads::Thread::self()));
1478 schoenebeck 2683 load_gig(loader->gig, loader->filename.c_str());
1479     progress_dialog->hide();
1480 schoenebeck 1225 }
1481    
1482 schoenebeck 2683 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 schoenebeck 1225 void MainWindow::on_action_file_save()
1491     {
1492 persson 1261 file_save();
1493     }
1494    
1495 persson 1303 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 persson 1261 bool MainWindow::file_save()
1520     {
1521 persson 1303 if (!check_if_savable()) return false;
1522 schoenebeck 1382 if (!file_is_shared && !file_has_name) return file_save_as();
1523 persson 1261
1524 schoenebeck 1225 std::cout << "Saving file\n" << std::flush;
1525 schoenebeck 1322 file_structure_to_be_changed_signal.emit(this->file);
1526 schoenebeck 2683
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 schoenebeck 1225 __import_queued_samples();
1568 schoenebeck 2683 std::cout << "Importing queued samples done.\n" << std::flush;
1569    
1570 schoenebeck 1322 file_structure_changed_signal.emit(this->file);
1571 schoenebeck 2683
1572 schoenebeck 2697 __refreshEntireGUI();
1573 schoenebeck 2683 progress_dialog->hide();
1574 schoenebeck 1225 }
1575    
1576     void MainWindow::on_action_file_save_as()
1577     {
1578 persson 1303 if (!check_if_savable()) return;
1579 persson 1261 file_save_as();
1580     }
1581    
1582     bool MainWindow::file_save_as()
1583     {
1584 persson 2845 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 persson 1261 dialog.set_default_response(Gtk::RESPONSE_OK);
1588 persson 2151 dialog.set_do_overwrite_confirmation();
1589 persson 1261
1590 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
1591 schoenebeck 1225 Gtk::FileFilter filter;
1592     filter.add_pattern("*.gig");
1593 persson 2169 #else
1594     Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
1595     filter->add_pattern("*.gig");
1596     #endif
1597 schoenebeck 1225 dialog.set_filter(filter);
1598 persson 1261
1599 schoenebeck 1679 // 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 persson 1831 basename = std::string(_("copy_of_")) + basename;
1605 schoenebeck 1679 Glib::ustring copyFileName = Glib::build_filename(dir, basename);
1606     if (Glib::path_is_absolute(filename)) {
1607     dialog.set_filename(copyFileName);
1608     } else {
1609 persson 1725 if (current_gig_dir != "") dialog.set_current_folder(current_gig_dir);
1610 schoenebeck 1679 }
1611     dialog.set_current_name(Glib::filename_display_basename(copyFileName));
1612 persson 1261 }
1613    
1614 schoenebeck 1679 // show warning in the dialog
1615     Gtk::HBox descriptionArea;
1616     descriptionArea.set_spacing(15);
1617 persson 2845 Gtk::Image warningIcon;
1618     warningIcon.set_from_icon_name("dialog-warning",
1619     Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
1620 schoenebeck 1679 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
1621 persson 2344 #if GTKMM_MAJOR_VERSION < 3
1622 persson 1799 view::WrapLabel description;
1623 persson 2344 #else
1624     Gtk::Label description;
1625     description.set_line_wrap();
1626     #endif
1627 schoenebeck 1679 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 persson 1799 descriptionArea.pack_start(description);
1636 schoenebeck 1679 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
1637 persson 1799 descriptionArea.show_all();
1638 schoenebeck 1679
1639 schoenebeck 1225 if (dialog.run() == Gtk::RESPONSE_OK) {
1640 schoenebeck 2683 std::string filename = dialog.get_filename();
1641     if (!Glib::str_has_suffix(filename, ".gig")) {
1642     filename += ".gig";
1643 schoenebeck 1225 }
1644 schoenebeck 2683 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 persson 1261 return true;
1663 schoenebeck 1225 }
1664 persson 1261 return false;
1665 schoenebeck 1225 }
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 persson 2841 printf("Samples to import: %d\n", int(m_SampleImportQueue.size()));
1672 schoenebeck 3116 for (std::map<gig::Sample*, SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1673 schoenebeck 1225 iter != m_SampleImportQueue.end(); ) {
1674 schoenebeck 3116 printf("Importing sample %s\n",iter->second.sample_path.c_str());
1675 schoenebeck 1225 SF_INFO info;
1676     info.format = 0;
1677 schoenebeck 3116 SNDFILE* hFile = sf_open(iter->second.sample_path.c_str(), SFM_READ, &info);
1678 persson 2398 sf_command(hFile, SFC_SET_SCALE_FLOAT_INT_READ, 0, SF_TRUE);
1679 schoenebeck 1225 try {
1680 persson 1831 if (!hFile) throw std::string(_("could not open file"));
1681 schoenebeck 1225 // 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 persson 1265 case SF_FORMAT_PCM_U8:
1687 schoenebeck 1225 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 persson 1265 bitdepth = 24;
1694 schoenebeck 1225 break;
1695     default:
1696     sf_close(hFile); // close sound file
1697 persson 1831 throw std::string(_("format not supported")); // unsupported subformat (yet?)
1698 schoenebeck 1225 }
1699 persson 1265
1700 schoenebeck 3116 // reset write position for sample
1701     iter->first->SetPos(0);
1702    
1703 persson 1265 const int bufsize = 10000;
1704 schoenebeck 1225 switch (bitdepth) {
1705 persson 1265 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 schoenebeck 3116 iter->first->Write(buffer, n);
1713 persson 1265 cnt -= n;
1714     }
1715     delete[] buffer;
1716 schoenebeck 1225 break;
1717 persson 1265 }
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 schoenebeck 3116 iter->first->Write(dstbuf, n);
1733 persson 1265 cnt -= n;
1734     }
1735     delete[] srcbuf;
1736     delete[] dstbuf;
1737 schoenebeck 1225 break;
1738 persson 1265 }
1739 schoenebeck 1225 }
1740     // cleanup
1741     sf_close(hFile);
1742 schoenebeck 1853 // let the sampler re-cache the sample if needed
1743 schoenebeck 3116 sample_changed_signal.emit(iter->first);
1744 schoenebeck 1225 // on success we remove the sample from the import queue,
1745     // otherwise keep it, maybe it works the next time ?
1746 schoenebeck 3116 std::map<gig::Sample*, SampleImportItem>::iterator cur = iter;
1747 schoenebeck 1225 ++iter;
1748     m_SampleImportQueue.erase(cur);
1749     } catch (std::string what) {
1750     // remember the files that made trouble (and their cause)
1751 persson 2442 if (!error_files.empty()) error_files += "\n";
1752 schoenebeck 3116 error_files += iter->second.sample_path += " (" + what + ")";
1753 schoenebeck 1225 ++iter;
1754     }
1755     }
1756     // show error message box when some sample(s) could not be imported
1757 persson 2442 if (!error_files.empty()) {
1758 schoenebeck 1382 Glib::ustring txt = _("Could not import the following sample(s):\n") + error_files;
1759 schoenebeck 1225 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 schoenebeck 2541 void MainWindow::on_action_warn_user_on_extensions() {
1771     Settings::singleton()->warnUserOnExtensions =
1772     !Settings::singleton()->warnUserOnExtensions;
1773     }
1774    
1775 schoenebeck 2689 void MainWindow::on_action_sync_sampler_instrument_selection() {
1776     Settings::singleton()->syncSamplerInstrumentSelection =
1777     !Settings::singleton()->syncSamplerInstrumentSelection;
1778     }
1779    
1780 schoenebeck 2773 void MainWindow::on_action_move_root_note_with_region_moved() {
1781     Settings::singleton()->moveRootNoteWithRegionMoved =
1782     !Settings::singleton()->moveRootNoteWithRegionMoved;
1783     }
1784    
1785 schoenebeck 1225 void MainWindow::on_action_help_about()
1786     {
1787     Gtk::AboutDialog dialog;
1788 persson 1959 #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 schoenebeck 1225 dialog.set_version(VERSION);
1794 schoenebeck 3089 dialog.set_copyright("Copyright (C) 2006-2017 Andreas Persson");
1795 schoenebeck 2476 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 schoenebeck 2627 "This program is distributed WITHOUT ANY WARRANTY; So better "
1802     "backup your Gigasampler/GigaStudio files before editing them with "
1803 schoenebeck 2476 "this application.\n"
1804     "\n"
1805     "Please report bugs to: http://bugs.linuxsampler.org"
1806     );
1807     dialog.set_comments(sComment.c_str());
1808 schoenebeck 1436 dialog.set_website("http://www.linuxsampler.org");
1809     dialog.set_website_label("http://www.linuxsampler.org");
1810 schoenebeck 1225 dialog.run();
1811     }
1812    
1813     PropDialog::PropDialog()
1814 schoenebeck 2560 : eFileFormat(_("File Format")),
1815     eName(_("Name")),
1816 persson 1831 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 persson 2845 quitButton(_("_Close"), true),
1832 schoenebeck 2560 table(2, 1),
1833     m_file(NULL)
1834 schoenebeck 1225 {
1835 persson 1831 set_title(_("File Properties"));
1836 persson 1582 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 schoenebeck 2560 table.add(eFileFormat);
1856 persson 1582 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 schoenebeck 1225 table.set_col_spacings(5);
1874 persson 1582 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 persson 2151 quitButton.set_can_default();
1883 persson 1582 quitButton.grab_focus();
1884     quitButton.signal_clicked().connect(
1885     sigc::mem_fun(*this, &PropDialog::hide));
1886 schoenebeck 2560 eFileFormat.signal_value_changed().connect(
1887     sigc::mem_fun(*this, &PropDialog::onFileFormatChanged));
1888 schoenebeck 1225
1889 persson 1582 quitButton.show();
1890     vbox.show();
1891 schoenebeck 1225 show_all_children();
1892     }
1893    
1894 schoenebeck 2560 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 schoenebeck 2566 txts.push_back(sGiga + "3/v4"); values.push_back(3);
1905 schoenebeck 2560 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 persson 2579 texts.push_back(NULL); values.push_back(0);
1911 schoenebeck 2560 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 schoenebeck 1225 void PropDialog::set_info(DLS::Info* info)
1921     {
1922 persson 2423 update(info);
1923 schoenebeck 1225 }
1924    
1925 persson 1582
1926 persson 2445 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 persson 1460 void InstrumentProps::set_IsDrum(bool value)
1939     {
1940 persson 2423 m->IsDrum = value;
1941 persson 1460 }
1942    
1943     void InstrumentProps::set_MIDIBank(uint16_t value)
1944     {
1945 persson 2423 m->MIDIBank = value;
1946 persson 1460 }
1947    
1948     void InstrumentProps::set_MIDIProgram(uint32_t value)
1949     {
1950 persson 2423 m->MIDIProgram = value;
1951 persson 1460 }
1952    
1953 persson 2423 InstrumentProps::InstrumentProps() :
1954 persson 2845 quitButton(_("_Close"), true),
1955 persson 2423 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 persson 1460 {
1969 persson 1831 set_title(_("Instrument Properties"));
1970 schoenebeck 1225
1971 schoenebeck 1656 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 persson 2445 connect(eName, &InstrumentProps::set_Name);
1981 persson 1460 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 persson 2423 connect(eDimensionKeyRangeLow, eDimensionKeyRangeHigh,
1991     &gig::Instrument::DimensionKeyRange);
1992 persson 1460
1993 persson 2445 eName.signal_value_changed().connect(sig_name_changed.make_slot());
1994    
1995 schoenebeck 1225 table.set_col_spacings(5);
1996    
1997 persson 1582 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 schoenebeck 1225
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 persson 2151 quitButton.set_can_default();
2020 schoenebeck 1225 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 persson 2423 update(instrument);
2033 persson 1460
2034     update_model++;
2035 persson 2445 eName.set_value(instrument->pInfo->Name);
2036 persson 1460 eIsDrum.set_value(instrument->IsDrum);
2037     eMIDIBank.set_value(instrument->MIDIBank);
2038     eMIDIProgram.set_value(instrument->MIDIProgram);
2039     update_model--;
2040 schoenebeck 1225 }
2041    
2042    
2043 persson 1261 void MainWindow::file_changed()
2044     {
2045     if (file && !file_is_changed) {
2046     set_title("*" + get_title());
2047     file_is_changed = true;
2048 schoenebeck 1225 }
2049 persson 1261 }
2050 schoenebeck 1225
2051 schoenebeck 2621 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 schoenebeck 1382 void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument)
2073 persson 1261 {
2074     file = 0;
2075 schoenebeck 1411 set_file_is_shared(isSharedInstrument);
2076 persson 1261
2077 schoenebeck 2772 this->filename =
2078     (filename && strlen(filename) > 0) ?
2079     filename : (!gig->GetFileName().empty()) ?
2080     gig->GetFileName() : _("Unsaved Gig File");
2081 persson 1261 set_title(Glib::filename_display_basename(this->filename));
2082     file_has_name = filename;
2083     file_is_changed = false;
2084    
2085 schoenebeck 2560 propDialog.set_file(gig);
2086 schoenebeck 1225 propDialog.set_info(gig->pInfo);
2087    
2088 persson 2442 instrument_name_connection.block();
2089 schoenebeck 2994 int index = 0;
2090 schoenebeck 1225 for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ;
2091 schoenebeck 2994 instrument = gig->GetNextInstrument(), ++index) {
2092 persson 2446 Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
2093 persson 2442
2094 schoenebeck 1225 Gtk::TreeModel::iterator iter = m_refTreeModel->append();
2095     Gtk::TreeModel::Row row = *iter;
2096 schoenebeck 2994 row[m_Columns.m_col_nr] = index;
2097 persson 2442 row[m_Columns.m_col_name] = name;
2098 schoenebeck 1225 row[m_Columns.m_col_instr] = instrument;
2099 persson 2442
2100     add_instrument_to_menu(name);
2101 schoenebeck 1225 }
2102 persson 2442 instrument_name_connection.unblock();
2103 schoenebeck 2625 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments")->show();
2104 schoenebeck 1225
2105 schoenebeck 2621 updateSampleRefCountMap(gig);
2106    
2107 schoenebeck 1225 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 persson 2446 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
2112 schoenebeck 1225 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 persson 2446 rowSample[m_SamplesModel.m_col_name] =
2120     gig_to_utf8(sample->pInfo->Name);
2121 schoenebeck 1225 rowSample[m_SamplesModel.m_col_sample] = sample;
2122     rowSample[m_SamplesModel.m_col_group] = NULL;
2123 schoenebeck 2621 int refcount = sample_ref_count.count(sample) ? sample_ref_count[sample] : 0;
2124     rowSample[m_SamplesModel.m_col_refcount] = ToString(refcount) + " " + _("Refs.");
2125 schoenebeck 2625 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
2126 schoenebeck 1225 }
2127     }
2128     }
2129 schoenebeck 2604
2130     for (int i = 0; gig->GetScriptGroup(i); ++i) {
2131     gig::ScriptGroup* group = gig->GetScriptGroup(i);
2132 schoenebeck 1225
2133 schoenebeck 2604 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 schoenebeck 2624 // unfold all sample groups & script groups by default
2150     m_TreeViewSamples.expand_all();
2151 schoenebeck 2604 m_TreeViewScripts.expand_all();
2152    
2153 persson 1261 file = gig;
2154    
2155 schoenebeck 1225 // select the first instrument
2156 persson 2442 m_TreeView.get_selection()->select(Gtk::TreePath("0"));
2157 persson 2423
2158 persson 2445 instr_props_set_instrument();
2159 persson 2507 gig::Instrument* instrument = get_instrument();
2160     if (instrument) {
2161     midiRules.set_instrument(instrument);
2162     }
2163 persson 2445 }
2164    
2165     bool MainWindow::instr_props_set_instrument()
2166     {
2167     instrumentProps.signal_name_changed().clear();
2168    
2169 schoenebeck 2994 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 persson 2445 if (it) {
2176     Gtk::TreeModel::Row row = *it;
2177     gig::Instrument* instrument = row[m_Columns.m_col_instr];
2178    
2179 persson 2423 instrumentProps.set_instrument(instrument);
2180 persson 2445
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 persson 2423 }
2191 persson 2445 return it;
2192 schoenebeck 1225 }
2193    
2194     void MainWindow::show_instr_props()
2195     {
2196 persson 2445 if (instr_props_set_instrument()) {
2197 persson 1533 instrumentProps.show();
2198     instrumentProps.deiconify();
2199 schoenebeck 1225 }
2200     }
2201    
2202 persson 2445 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 persson 2446 Glib::ustring gigname(gig_to_utf8(instrument->pInfo->Name));
2209     if (gigname != name) {
2210     row[m_Columns.m_col_name] = gigname;
2211 persson 2445 }
2212     }
2213    
2214 persson 2507 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 schoenebeck 2610 void MainWindow::show_script_slots() {
2225     if (!file) return;
2226     // get selected instrument
2227 schoenebeck 2994 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 schoenebeck 2610 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 schoenebeck 2772 void MainWindow::on_action_refresh_all() {
2242     __refreshEntireGUI();
2243     }
2244    
2245 schoenebeck 1415 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 schoenebeck 2918 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 schoenebeck 2967 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 schoenebeck 2464 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 schoenebeck 1225 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 persson 2507 // gig v2 files have no midi rules
2312 schoenebeck 2625 const bool bEnabled = !(file->pVersion && file->pVersion->major == 2);
2313 persson 2507 static_cast<Gtk::MenuItem*>(
2314 schoenebeck 2625 uiManager->get_widget("/MenuBar/MenuInstrument/MidiRules"))->set_sensitive(
2315     bEnabled
2316     );
2317     static_cast<Gtk::MenuItem*>(
2318 persson 2507 uiManager->get_widget("/PopupMenu/MidiRules"))->set_sensitive(
2319 schoenebeck 2625 bEnabled
2320     );
2321 schoenebeck 1225 popup_menu->popup(button->button, button->time);
2322     }
2323     }
2324    
2325 persson 2442 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 schoenebeck 1225 }
2339    
2340 schoenebeck 2701 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 schoenebeck 2994 m_TreeView.get_selection()->unselect_all();
2350 schoenebeck 2701 m_TreeView.get_selection()->select(model->children()[i]);
2351 schoenebeck 2994 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 schoenebeck 2701 on_sel_change(); // the regular instrument selection change callback
2356     }
2357     }
2358     }
2359    
2360 schoenebeck 2695 /// 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 schoenebeck 2994 m_TreeView.get_selection()->unselect_all();
2372 schoenebeck 2695 m_TreeView.get_selection()->select(model->children()[i]);
2373 schoenebeck 2994 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 schoenebeck 2695 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 schoenebeck 2691 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 schoenebeck 2994 m_TreeViewSamples.get_selection()->unselect_all();
2403 schoenebeck 2691 m_TreeViewSamples.get_selection()->select(rowGroup.children()[s]);
2404 schoenebeck 2994 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 schoenebeck 2691 return;
2409     }
2410     }
2411     }
2412     }
2413    
2414 schoenebeck 1225 void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) {
2415     if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
2416 schoenebeck 2994 // 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 schoenebeck 1225 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 schoenebeck 2994 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 schoenebeck 1225 Gtk::TreeModel::Row row = *it;
2447 schoenebeck 2994 if (row[m_SamplesModel.m_col_group]) nGroups++;
2448     if (row[m_SamplesModel.m_col_sample]) nSamples++;
2449 schoenebeck 1225 }
2450 schoenebeck 2994
2451 schoenebeck 1225 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->
2452 schoenebeck 2994 set_sensitive(n == 1);
2453 schoenebeck 1225 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->
2454 schoenebeck 2994 set_sensitive(n);
2455 schoenebeck 1225 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->
2456     set_sensitive(file);
2457 schoenebeck 2624 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/ShowSampleRefs"))->
2458 schoenebeck 2994 set_sensitive(nSamples == 1);
2459 schoenebeck 1225 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->
2460 schoenebeck 2994 set_sensitive(n);
2461 schoenebeck 1225 // show sample popup
2462     sample_popup->popup(button->button, button->time);
2463 schoenebeck 2625
2464     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/SampleProperties"))->
2465 schoenebeck 2994 set_sensitive(n == 1);
2466 schoenebeck 2625 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/AddSample"))->
2467 schoenebeck 2994 set_sensitive(n);
2468 schoenebeck 2625 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 schoenebeck 2994 set_sensitive(nSamples == 1);
2472 schoenebeck 2625 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/RemoveSample"))->
2473 schoenebeck 2994 set_sensitive(n);
2474 schoenebeck 1225 }
2475     }
2476    
2477 schoenebeck 2604 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 schoenebeck 2625
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 schoenebeck 2604 }
2511     }
2512 persson 2442
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 persson 2446 const Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
2547 persson 2442
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 schoenebeck 2994 rowInstr[m_Columns.m_col_nr] = m_refTreeModel->children().size() - 1;
2553 persson 2442 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 schoenebeck 1225 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 persson 2446 instrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument ") +
2570     ToString(__instrument_indexer));
2571 persson 2442
2572     add_instrument(instrument);
2573 schoenebeck 1225 }
2574    
2575 schoenebeck 2395 void MainWindow::on_action_duplicate_instrument() {
2576     if (!file) return;
2577 persson 2442
2578 schoenebeck 2395 // retrieve the currently selected instrument
2579     // (being the original instrument to be duplicated)
2580     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
2581 schoenebeck 2994 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 persson 2442
2594 schoenebeck 2994 add_instrument(instrNew);
2595     }
2596     }
2597     }
2598 schoenebeck 2395 }
2599    
2600 schoenebeck 1225 void MainWindow::on_action_remove_instrument() {
2601     if (!file) return;
2602 schoenebeck 1382 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 schoenebeck 1225 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
2614 schoenebeck 2994 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 schoenebeck 1225 Gtk::TreeModel::Row row = *it;
2619     gig::Instrument* instr = row[m_Columns.m_col_instr];
2620     try {
2621 persson 2442 Gtk::TreePath path(it);
2622     int index = path[0];
2623    
2624 schoenebeck 1225 // remove instrument from the gig file
2625     if (instr) file->DeleteInstrument(instr);
2626 persson 1261 file_changed();
2627 persson 2423
2628 persson 2442 remove_instrument_from_menu(index);
2629    
2630     // remove row from instruments tree view
2631     m_refTreeModel->erase(it);
2632 schoenebeck 2994 // 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 persson 2442
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 persson 2445 instr_props_set_instrument();
2655 persson 2507 instr = get_instrument();
2656     if (instr) {
2657     midiRules.set_instrument(instr);
2658     } else {
2659     midiRules.hide();
2660     }
2661 schoenebeck 1225 } 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 persson 1831 *this, _("Sorry, yet to be implemented!"), false, Gtk::MESSAGE_INFO
2672 schoenebeck 1225 );
2673     msg.run();
2674     }
2675    
2676 schoenebeck 2604 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 schoenebeck 2903 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 schoenebeck 2604 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 schoenebeck 1225 void MainWindow::on_action_add_group() {
2795     static int __sample_indexer = 0;
2796     if (!file) return;
2797     gig::Group* group = file->AddGroup();
2798 persson 2446 group->Name = gig_from_utf8(_("Unnamed Group"));
2799 schoenebeck 1225 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 persson 2446 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
2805 schoenebeck 1225 rowGroup[m_SamplesModel.m_col_sample] = NULL;
2806     rowGroup[m_SamplesModel.m_col_group] = group;
2807 persson 1261 file_changed();
2808 schoenebeck 1225 }
2809    
2810 schoenebeck 2715 void MainWindow::on_action_replace_sample() {
2811     add_or_replace_sample(true);
2812     }
2813    
2814 schoenebeck 1225 void MainWindow::on_action_add_sample() {
2815 schoenebeck 2715 add_or_replace_sample(false);
2816     }
2817    
2818     void MainWindow::add_or_replace_sample(bool replace) {
2819 schoenebeck 1225 if (!file) return;
2820 schoenebeck 2715
2821     // get selected group (and probably selected sample)
2822 schoenebeck 1225 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
2823 schoenebeck 2994 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 schoenebeck 1225 if (!it) return;
2827     Gtk::TreeModel::Row row = *it;
2828 schoenebeck 2715 gig::Sample* sample = NULL;
2829 schoenebeck 1225 gig::Group* group = row[m_SamplesModel.m_col_group];
2830     if (!group) { // not a group, but a sample is selected (probably)
2831 schoenebeck 2715 if (replace) sample = row[m_SamplesModel.m_col_sample];
2832     if (!row[m_SamplesModel.m_col_sample]) return;
2833 schoenebeck 1225 it = row.parent(); // resolve parent (that is the sample's group)
2834     if (!it) return;
2835 schoenebeck 2715 if (!replace) row = *it;
2836     group = (*it)[m_SamplesModel.m_col_group];
2837 schoenebeck 1225 if (!group) return;
2838     }
2839 schoenebeck 2715 if (replace && !sample) return;
2840    
2841 schoenebeck 1225 // show 'browse for file' dialog
2842 schoenebeck 2715 Gtk::FileChooserDialog dialog(*this, replace ? _("Replace Sample with") : _("Add Sample(s)"));
2843 persson 2845 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2844     dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
2845 schoenebeck 2715 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 persson 2169
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 persson 1262 const char* const supportedFileTypes[] = {
2854 schoenebeck 1225 "*.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 persson 2169 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 schoenebeck 1225 for (int i = 0; supportedFileTypes[i]; i++)
2864     soundfilter.add_pattern(supportedFileTypes[i]);
2865 persson 2169 soundfilter.set_name(soundfiles);
2866    
2867     // matches every file
2868     Gtk::FileFilter allpassfilter;
2869 schoenebeck 1225 allpassfilter.add_pattern("*.*");
2870 persson 2169 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 schoenebeck 1225 dialog.add_filter(soundfilter);
2882     dialog.add_filter(allpassfilter);
2883 persson 1725 if (current_sample_dir != "") {
2884     dialog.set_current_folder(current_sample_dir);
2885     }
2886 schoenebeck 1225 if (dialog.run() == Gtk::RESPONSE_OK) {
2887 persson 1725 current_sample_dir = dialog.get_current_folder();
2888 schoenebeck 1225 Glib::ustring error_files;
2889 persson 2169 std::vector<std::string> filenames = dialog.get_filenames();
2890     for (std::vector<std::string>::iterator iter = filenames.begin();
2891 schoenebeck 1225 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 persson 1831 if (!hFile) throw std::string(_("could not open file"));
2899 schoenebeck 1225 int bitdepth;
2900     switch (info.format & 0xff) {
2901     case SF_FORMAT_PCM_S8:
2902     case SF_FORMAT_PCM_16:
2903 persson 1265 case SF_FORMAT_PCM_U8:
2904 schoenebeck 1225 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 persson 1265 bitdepth = 24;
2911 schoenebeck 1225 break;
2912     default:
2913     sf_close(hFile); // close sound file
2914 persson 1831 throw std::string(_("format not supported")); // unsupported subformat (yet?)
2915 schoenebeck 1225 }
2916 schoenebeck 2715 // add a new sample to the .gig file (if adding is requested actually)
2917     if (!replace) sample = file->AddSample();
2918 schoenebeck 1225 // file name without path
2919 persson 1262 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 persson 2446 sample->pInfo->Name = gig_from_utf8(filename);
2928 schoenebeck 1225 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 persson 1265 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 schoenebeck 2466 sample->FineTune = instrument.detune;
2942 persson 1265
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 schoenebeck 1225 // 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 schoenebeck 2715 if (!replace) group->AddSample(sample);
2969 schoenebeck 1225 // 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 schoenebeck 3116 m_SampleImportQueue[sample] = sched_item;
2975 schoenebeck 1225 // add sample to the tree view
2976 schoenebeck 2715 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 schoenebeck 1225 // close sound file
2988     sf_close(hFile);
2989 persson 1261 file_changed();
2990 schoenebeck 1225 } catch (std::string what) { // remember the files that made trouble (and their cause)
2991 persson 2442 if (!error_files.empty()) error_files += "\n";
2992 schoenebeck 1225 error_files += *iter += " (" + what + ")";
2993     }
2994     }
2995     // show error message box when some file(s) could not be opened / added
2996 persson 2442 if (!error_files.empty()) {
2997 schoenebeck 2715 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 schoenebeck 1225 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
3003     msg.run();
3004     }
3005     }
3006     }
3007    
3008 schoenebeck 1673 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 persson 2442 const char* str =
3014 schoenebeck 1673 _("This is a very specific function. It tries to replace all samples "
3015 persson 1799 "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 schoenebeck 1673 "find a sample file in the selected directory with the same name as "
3019 persson 1799 "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 schoenebeck 1673 "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 persson 1799 "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 persson 2344 "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 schoenebeck 1673 Gtk::HBox entryArea;
3035 persson 2169 Gtk::Label entryLabel( _("Add filename extension: "), Gtk::ALIGN_START);
3036 schoenebeck 1673 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 persson 1799 entryArea.show_all();
3044 persson 2845 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
3045 schoenebeck 1673 dialog.add_button(_("Select"), Gtk::RESPONSE_OK);
3046     dialog.set_select_multiple(false);
3047 persson 1725 if (current_sample_dir != "") {
3048     dialog.set_current_folder(current_sample_dir);
3049     }
3050 schoenebeck 1673 if (dialog.run() == Gtk::RESPONSE_OK)
3051     {
3052 persson 1725 current_sample_dir = dialog.get_current_folder();
3053 schoenebeck 1673 Glib::ustring error_files;
3054 persson 2169 std::string folder = dialog.get_filename();
3055 schoenebeck 1673 for (gig::Sample* sample = file->GetFirstSample();
3056     sample; sample = file->GetNextSample())
3057     {
3058     std::string filename =
3059 persson 2446 folder + G_DIR_SEPARATOR_S +
3060     Glib::filename_from_utf8(gig_to_utf8(sample->pInfo->Name) +
3061     postfixEntryBox.get_text());
3062 schoenebeck 1673 SF_INFO info;
3063     info.format = 0;
3064     SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &info);
3065     try
3066     {
3067 persson 1831 if (!hFile) throw std::string(_("could not open file"));
3068 schoenebeck 1673 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 persson 1831 throw std::string(_("format not supported"));
3080 schoenebeck 1673 }
3081     SampleImportItem sched_item;
3082     sched_item.gig_sample = sample;
3083     sched_item.sample_path = filename;
3084 schoenebeck 3116 m_SampleImportQueue[sample] = sched_item;
3085 schoenebeck 1673 sf_close(hFile);
3086     file_changed();
3087     }
3088     catch (std::string what)
3089     {
3090 persson 2442 if (!error_files.empty()) error_files += "\n";
3091 persson 2446 error_files += Glib::filename_to_utf8(filename) +
3092     " (" + what + ")";
3093 schoenebeck 1673 }
3094     }
3095     // show error message box when some file(s) could not be opened / added
3096 persson 2442 if (!error_files.empty()) {
3097 schoenebeck 1673 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 schoenebeck 1225 void MainWindow::on_action_remove_sample() {
3106     if (!file) return;
3107     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3108 schoenebeck 2994 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 schoenebeck 1225 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 persson 2446 // temporarily remember the samples that belong to
3120 schoenebeck 1225 // 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 schoenebeck 1322 // notify everybody that we're going to remove these samples
3127     samples_to_be_removed_signal.emit(members);
3128 schoenebeck 1225 // delete the group in the .gig file including the
3129     // samples that belong to the group
3130     file->DeleteGroup(group);
3131 schoenebeck 1322 // notify that we're done with removal
3132     samples_removed_signal.emit();
3133 schoenebeck 1225 // 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 schoenebeck 3116 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 schoenebeck 1225 }
3143     }
3144 persson 1261 file_changed();
3145 schoenebeck 1225 } else if (sample) {
3146 schoenebeck 1322 // 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 schoenebeck 1225 // remove sample from the .gig file
3151     file->DeleteSample(sample);
3152 schoenebeck 1322 // notify that we're done with removal
3153     samples_removed_signal.emit();
3154 schoenebeck 1225 // if sample was just previously added, remove it from
3155     // the import queue
3156 schoenebeck 3116 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 schoenebeck 1225 }
3161 persson 1303 dimreg_changed();
3162 persson 1261 file_changed();
3163 schoenebeck 1225 }
3164     // remove respective row(s) from samples tree view
3165     m_refSamplesTreeModel->erase(it);
3166     } catch (RIFF::Exception e) {
3167 schoenebeck 1322 // pretend we're done with removal (i.e. to avoid dead locks)
3168     samples_removed_signal.emit();
3169     // show error message
3170 schoenebeck 1225 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
3171     msg.run();
3172     }
3173     }
3174     }
3175    
3176 schoenebeck 2772 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 schoenebeck 3116 // 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 schoenebeck 2772 }
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 schoenebeck 2610 // 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 schoenebeck 2701 // 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 schoenebeck 2994 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 schoenebeck 2701 }
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 persson 1303 // 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 schoenebeck 1225 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
3331     Gtk::SelectionData& selection_data, guint, guint)
3332     {
3333 persson 1303 if (!first_call_to_drag_data_get) return;
3334     first_call_to_drag_data_get = false;
3335    
3336 schoenebeck 1225 // get selected sample
3337     gig::Sample* sample = NULL;
3338     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3339 schoenebeck 2994 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 schoenebeck 1225 }
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 persson 1265 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
3359 persson 1303 std::cout << "Drop received sample \"" <<
3360     sample->pInfo->Name << "\"" << std::endl;
3361     // drop success
3362     context->drop_reply(true, time);
3363    
3364 schoenebeck 1322 //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 persson 1303 // 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 schoenebeck 1225 }
3379 persson 1303 bool channels_changed = false;
3380     if (sample->Channels == 1 && stereo_dimension) {
3381     // remove the samplechannel dimension
3382 schoenebeck 2550 /* commented out, because it makes it impossible building up an instrument from scratch using two separate L/R samples
3383 persson 1303 region->DeleteDimension(stereo_dimension);
3384     channels_changed = true;
3385     region_changed();
3386 schoenebeck 2550 */
3387 persson 1303 }
3388 schoenebeck 2464 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 persson 1303
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 schoenebeck 1322 gig::Sample* oldref = d->pSample;
3412     d->pSample = NULL;
3413     sample_ref_changed_signal.emit(oldref, NULL);
3414 persson 1303 }
3415     }
3416     }
3417    
3418 schoenebeck 1322 // notify we're done with altering
3419     region_changed_signal.emit(region);
3420    
3421 persson 1460 file_changed();
3422    
3423 persson 1303 return;
3424 schoenebeck 1225 }
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 persson 2446 gig::String gigname(gig_from_utf8(name));
3437 schoenebeck 1225 if (group) {
3438 persson 2446 if (group->Name != gigname) {
3439     group->Name = gigname;
3440 persson 1261 printf("group name changed\n");
3441     file_changed();
3442     }
3443 schoenebeck 1225 } else if (sample) {
3444 persson 2446 if (sample->pInfo->Name != gigname) {
3445     sample->pInfo->Name = gigname;
3446 persson 1261 printf("sample name changed\n");
3447     file_changed();
3448     }
3449 schoenebeck 1225 }
3450     }
3451    
3452 schoenebeck 2604 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 schoenebeck 2644 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 schoenebeck 2903 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 schoenebeck 2644 editor->setScript(script);
3492     //editor->reparent(*this);
3493     editor->show();
3494     }
3495    
3496 schoenebeck 1225 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 persson 2442
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 schoenebeck 1225 gig::Instrument* instrument = row[m_Columns.m_col_instr];
3517 persson 2446 gig::String gigname(gig_from_utf8(name));
3518     if (instrument && instrument->pInfo->Name != gigname) {
3519     instrument->pInfo->Name = gigname;
3520 persson 2445
3521     // change name in the instrument properties window
3522     if (instrumentProps.get_instrument() == instrument) {
3523     instrumentProps.update_name();
3524     }
3525    
3526 persson 1261 file_changed();
3527     }
3528 schoenebeck 1225 }
3529 schoenebeck 1322
3530 schoenebeck 2548 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 schoenebeck 2624 void MainWindow::on_action_view_references() {
3543     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3544 schoenebeck 2994 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 schoenebeck 2624 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 schoenebeck 2695 d->dimension_region_selected.connect(
3555     sigc::mem_fun(*this, &MainWindow::select_dimension_region)
3556     );
3557 schoenebeck 2624 d->show_all();
3558     d->resize(500, 400);
3559     d->run();
3560     delete d;
3561     }
3562    
3563 schoenebeck 2553 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 schoenebeck 2683 // 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 schoenebeck 2553 }
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 persson 2845 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
3669 schoenebeck 2553 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 persson 2845 Gtk::Image warningIcon;
3688     warningIcon.set_from_icon_name("dialog-warning",
3689     Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
3690 schoenebeck 2553 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 persson 2841 printf("on_action_merge_files self=%p\n",
3713     static_cast<void*>(Glib::Threads::Thread::self()));
3714 schoenebeck 2553 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 schoenebeck 2772 __refreshEntireGUI();
3726 schoenebeck 2553 }
3727     }
3728    
3729 schoenebeck 1411 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 schoenebeck 2689
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 schoenebeck 1411 }
3750    
3751 schoenebeck 2621 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 schoenebeck 2625 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
3764 schoenebeck 2621 }
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 schoenebeck 2666 it != samples.end(); ++it)
3778 schoenebeck 2621 {
3779     sample_ref_count.erase(*it);
3780     }
3781     }
3782    
3783 schoenebeck 2625 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 schoenebeck 3123 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 schoenebeck 3134 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 schoenebeck 3123 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 schoenebeck 3144 #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 schoenebeck 3151 void MainWindow::adjust_clipboard_content() {
3867 schoenebeck 3155 MacroEditor* editor = new MacroEditor();
3868 schoenebeck 3151 editor->setMacro(&m_serializationArchive);
3869     editor->show();
3870     }
3871    
3872 schoenebeck 3144 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 schoenebeck 3157 //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 schoenebeck 3144 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 schoenebeck 3157 applyMacro(m_serializationArchive);
3936 schoenebeck 3144 } catch (Serialization::Exception e) {
3937     errorText = e.Message;
3938     } catch (...) {
3939 schoenebeck 3157 errorText = _("Unknown exception while pasting DimensionRegion");
3940 schoenebeck 3144 }
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 schoenebeck 3151
3958     static_cast<Gtk::MenuItem*>(
3959     uiManager->get_widget("/MenuBar/MenuEdit/AdjustClipboard")
3960     )->set_sensitive(bDimensionRegionPasteIsPossible);
3961 schoenebeck 3144 }
3962    
3963 schoenebeck 1339 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
3964 schoenebeck 1322 return file_structure_to_be_changed_signal;
3965     }
3966    
3967 schoenebeck 1339 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
3968 schoenebeck 1322 return file_structure_changed_signal;
3969     }
3970    
3971 schoenebeck 1339 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
3972 schoenebeck 1322 return samples_to_be_removed_signal;
3973     }
3974    
3975 schoenebeck 1339 sigc::signal<void>& MainWindow::signal_samples_removed() {
3976 schoenebeck 1322 return samples_removed_signal;
3977     }
3978    
3979 schoenebeck 1339 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
3980 schoenebeck 1322 return region_to_be_changed_signal;
3981     }
3982    
3983 schoenebeck 1339 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
3984 schoenebeck 1322 return region_changed_signal;
3985     }
3986    
3987 schoenebeck 1853 sigc::signal<void, gig::Sample*>& MainWindow::signal_sample_changed() {
3988     return sample_changed_signal;
3989     }
3990    
3991 schoenebeck 1339 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
3992 schoenebeck 1322 return sample_ref_changed_signal;
3993     }
3994    
3995 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
3996 schoenebeck 1322 return dimreg_to_be_changed_signal;
3997     }
3998    
3999 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
4000 schoenebeck 1322 return dimreg_changed_signal;
4001     }
4002 schoenebeck 1654
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 schoenebeck 1660
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 schoenebeck 2689
4019     sigc::signal<void, gig::Instrument*>& MainWindow::signal_switch_sampler_instrument() {
4020     return switch_sampler_instrument_signal;
4021     }

  ViewVC Help
Powered by ViewVC