/[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 2610 - (hide annotations) (download)
Sun Jun 8 19:09:26 2014 UTC (9 years, 9 months ago) by schoenebeck
File size: 102547 byte(s)
* GIG SOUND FORMAT EXTENSION: Added support for managing script slots for
  instruments. Script slots define which scripts shall be executed for an
  instrument by the sampler and in which order.

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

  ViewVC Help
Powered by ViewVC