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