/[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 3155 - (hide annotations) (download)
Sun May 7 15:32:43 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 149746 byte(s)
* Macro Editor: implemented reverse selected deletion of rows
* Macro Editor: Fixed "modified" state not being updated after
  clicking on "Apply" button.
* Macro Editor: Show implemented keyboard accelerators as
  symbols on the "Delete" (Ctrl+Del) and "Inverse Delete"
  (Alt+Del) buttons.
* Bumped version (1.0.0.svn37).

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

  ViewVC Help
Powered by ViewVC