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