/[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 2918 - (hide annotations) (download)
Wed May 18 10:10:25 2016 UTC (7 years, 11 months ago) by schoenebeck
File size: 132479 byte(s)
* Added setting to main menu "View" -> "Auto Restore Window Dimension"
  (disabled by default since it causes issues on some machines).
* Bumped version (1.0.0.svn14).

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

  ViewVC Help
Powered by ViewVC