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

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

  ViewVC Help
Powered by ViewVC