/[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 2644 - (hide annotations) (download)
Tue Jun 17 22:55:02 2014 UTC (9 years, 9 months ago) by schoenebeck
File size: 111141 byte(s)
* Script Tree: Double click opens script editor.

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

  ViewVC Help
Powered by ViewVC