/[svn]/gigedit/trunk/src/gigedit/mainwindow.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/mainwindow.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3364 - (show annotations) (download)
Tue Nov 14 18:07:25 2017 UTC (6 years, 5 months ago) by schoenebeck
File size: 205326 byte(s)
* Added experimental support for upcoming GTK(MM)4
  (for now up to GTKMM 3.91.2 while still preserving backward compatibility
  down to GTKMM 2).
* Re-merged r2845 to compile now with and without Gtk "Stock ID" API
  (see also r3158).

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 "compat.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 #if HAS_GTKMM_STOCK
40 # include <gtkmm/stock.h>
41 #endif
42 #include <gtkmm/targetentry.h>
43 #include <gtkmm/main.h>
44 #if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION < 89)
45 # include <gtkmm/toggleaction.h>
46 #endif
47 #include <gtkmm/accelmap.h>
48 #if GTKMM_MAJOR_VERSION < 3
49 #include "wrapLabel.hh"
50 #endif
51
52 #include "global.h"
53 #include "compat.h"
54
55 #include <stdio.h>
56 #ifdef LIBSNDFILE_HEADER_FILE
57 # include LIBSNDFILE_HEADER_FILE(sndfile.h)
58 #else
59 # include <sndfile.h>
60 #endif
61 #include <assert.h>
62
63 #include "mainwindow.h"
64 #include "Settings.h"
65 #include "CombineInstrumentsDialog.h"
66 #include "scripteditor.h"
67 #include "scriptslots.h"
68 #include "ReferencesView.h"
69 #include "../../gfx/status_attached.xpm"
70 #include "../../gfx/status_detached.xpm"
71 #include "gfx/builtinpix.h"
72 #include "MacroEditor.h"
73 #include "MacrosSetup.h"
74 #if defined(__APPLE__)
75 # include "MacHelper.h"
76 #endif
77
78 static const Gdk::ModifierType primaryModifierKey =
79 #if defined(__APPLE__)
80 Gdk::META_MASK; // Cmd key on Mac
81 #else
82 Gdk::CONTROL_MASK; // Ctrl key on all other OSs
83 #endif
84
85 MainWindow::MainWindow() :
86 m_DimRegionChooser(*this),
87 dimreg_label(_("Changes apply to:")),
88 dimreg_all_regions(_("all regions")),
89 dimreg_all_dimregs(_("all dimension splits")),
90 dimreg_stereo(_("both channels")),
91 labelLegend(_("Legend:")),
92 labelNoSample(_(" No Sample")),
93 labelMissingSample(_(" Missing some Sample(s)")),
94 labelLooped(_(" Looped")),
95 labelSomeLoops(_(" Some Loop(s)"))
96 {
97 loadBuiltInPix();
98
99 this->file = NULL;
100
101 // set_border_width(5);
102
103 if (!Settings::singleton()->autoRestoreWindowDimension) {
104 set_default_size(800, 600);
105 set_position(Gtk::WIN_POS_CENTER);
106 }
107
108 add(m_VBox);
109
110 // Handle selection
111 m_TreeView.get_selection()->signal_changed().connect(
112 sigc::mem_fun(*this, &MainWindow::on_sel_change));
113
114 // m_TreeView.set_reorderable();
115
116 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
117 m_TreeView.signal_button_press_event().connect(
118 sigc::mem_fun(*this, &MainWindow::on_button_release));
119 #else
120 m_TreeView.signal_button_press_event().connect_notify(
121 sigc::mem_fun(*this, &MainWindow::on_button_release));
122 #endif
123
124 // Add the TreeView tab, inside a ScrolledWindow, with the button underneath:
125 m_ScrolledWindow.add(m_TreeView);
126 // m_ScrolledWindow.set_size_request(200, 600);
127 m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
128
129 m_ScrolledWindowSamples.add(m_TreeViewSamples);
130 m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
131
132 m_ScrolledWindowScripts.add(m_TreeViewScripts);
133 m_ScrolledWindowScripts.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
134
135
136 m_TreeViewNotebook.set_size_request(300);
137
138 m_searchLabel.set_text(Glib::ustring(" ") + _("Filter:"));
139 m_searchField.pack_start(m_searchLabel, Gtk::PACK_SHRINK);
140 m_searchField.pack_start(m_searchText);
141 m_searchField.set_spacing(5);
142
143 m_left_vbox.pack_start(m_TreeViewNotebook);
144 m_left_vbox.pack_start(m_searchField, Gtk::PACK_SHRINK);
145
146 m_HPaned.add1(m_left_vbox);
147
148 dimreg_hbox.add(dimreg_label);
149 dimreg_hbox.add(dimreg_all_regions);
150 dimreg_hbox.add(dimreg_all_dimregs);
151 dimreg_stereo.set_active();
152 dimreg_hbox.add(dimreg_stereo);
153 dimreg_vbox.add(dimreg_edit);
154 dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
155 {
156 legend_hbox.add(labelLegend);
157
158 imageNoSample.set(redDot);
159 #if HAS_GTKMM_ALIGNMENT
160 imageNoSample.set_alignment(Gtk::ALIGN_END);
161 labelNoSample.set_alignment(Gtk::ALIGN_START);
162 #else
163 imageNoSample.set_halign(Gtk::ALIGN_END);
164 labelNoSample.set_halign(Gtk::ALIGN_START);
165 #endif
166 legend_hbox.add(imageNoSample);
167 legend_hbox.add(labelNoSample);
168
169 imageMissingSample.set(yellowDot);
170 #if HAS_GTKMM_ALIGNMENT
171 imageMissingSample.set_alignment(Gtk::ALIGN_END);
172 labelMissingSample.set_alignment(Gtk::ALIGN_START);
173 #else
174 imageMissingSample.set_halign(Gtk::ALIGN_END);
175 labelMissingSample.set_halign(Gtk::ALIGN_START);
176 #endif
177 legend_hbox.add(imageMissingSample);
178 legend_hbox.add(labelMissingSample);
179
180 imageLooped.set(blackLoop);
181 #if HAS_GTKMM_ALIGNMENT
182 imageLooped.set_alignment(Gtk::ALIGN_END);
183 labelLooped.set_alignment(Gtk::ALIGN_START);
184 #else
185 imageLooped.set_halign(Gtk::ALIGN_END);
186 labelLooped.set_halign(Gtk::ALIGN_START);
187 #endif
188 legend_hbox.add(imageLooped);
189 legend_hbox.add(labelLooped);
190
191 imageSomeLoops.set(grayLoop);
192 #if HAS_GTKMM_ALIGNMENT
193 imageSomeLoops.set_alignment(Gtk::ALIGN_END);
194 labelSomeLoops.set_alignment(Gtk::ALIGN_START);
195 #else
196 imageSomeLoops.set_halign(Gtk::ALIGN_END);
197 labelSomeLoops.set_halign(Gtk::ALIGN_START);
198 #endif
199 legend_hbox.add(imageSomeLoops);
200 legend_hbox.add(labelSomeLoops);
201
202 #if HAS_GTKMM_SHOW_ALL_CHILDREN
203 legend_hbox.show_all_children();
204 #endif
205 }
206 dimreg_vbox.pack_start(legend_hbox, Gtk::PACK_SHRINK);
207 m_HPaned.add2(dimreg_vbox);
208
209 dimreg_label.set_tooltip_text(_("To automatically apply your changes above globally to the entire instrument, check all 3 check boxes on the right."));
210 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."));
211 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."));
212 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)."));
213
214 m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, _("Samples"));
215 m_TreeViewNotebook.append_page(m_ScrolledWindow, _("Instruments"));
216 m_TreeViewNotebook.append_page(m_ScrolledWindowScripts, _("Scripts"));
217
218 #if USE_GLIB_ACTION
219 m_actionGroup = Gio::SimpleActionGroup::create();
220 m_actionGroup->add_action(
221 "New", sigc::mem_fun(*this, &MainWindow::on_action_file_new)
222 );
223 m_actionGroup->add_action(
224 "Open", sigc::mem_fun(*this, &MainWindow::on_action_file_open)
225 );
226 m_actionGroup->add_action(
227 "Save", sigc::mem_fun(*this, &MainWindow::on_action_file_save)
228 );
229 m_actionGroup->add_action(
230 "SaveAs", sigc::mem_fun(*this, &MainWindow::on_action_file_save_as)
231 );
232 m_actionGroup->add_action(
233 "Properties", sigc::mem_fun(*this, &MainWindow::on_action_file_properties)
234 );
235 m_actionGroup->add_action(
236 "InstrProperties", sigc::mem_fun(*this, &MainWindow::show_instr_props)
237 );
238 m_actionMIDIRules = m_actionGroup->add_action(
239 "MidiRules", sigc::mem_fun(*this, &MainWindow::show_midi_rules)
240 );
241 m_actionGroup->add_action(
242 "ScriptSlots", sigc::mem_fun(*this, &MainWindow::show_script_slots)
243 );
244 m_actionGroup->add_action(
245 "Quit", sigc::mem_fun(*this, &MainWindow::on_action_quit)
246 );
247 m_actionGroup->add_action(
248 "MenuSample", sigc::mem_fun(*this, &MainWindow::show_samples_tab)
249 );
250 m_actionGroup->add_action(
251 "MenuInstrument", sigc::mem_fun(*this, &MainWindow::show_intruments_tab)
252 );
253 m_actionGroup->add_action(
254 "MenuScript", sigc::mem_fun(*this, &MainWindow::show_scripts_tab)
255 );
256 #else
257 actionGroup = Gtk::ActionGroup::create();
258
259 actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
260 actionGroup->add(Gtk::Action::create("New", Gtk::Stock::NEW),
261 sigc::mem_fun(
262 *this, &MainWindow::on_action_file_new));
263 Glib::RefPtr<Gtk::Action> action =
264 Gtk::Action::create("Open", Gtk::Stock::OPEN);
265 action->property_label() = action->property_label() + "...";
266 actionGroup->add(action,
267 sigc::mem_fun(
268 *this, &MainWindow::on_action_file_open));
269 actionGroup->add(Gtk::Action::create("Save", Gtk::Stock::SAVE),
270 sigc::mem_fun(
271 *this, &MainWindow::on_action_file_save));
272 action = Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS);
273 action->property_label() = action->property_label() + "...";
274 actionGroup->add(action,
275 Gtk::AccelKey("<shift><control>s"),
276 sigc::mem_fun(
277 *this, &MainWindow::on_action_file_save_as));
278 actionGroup->add(Gtk::Action::create("Properties",
279 Gtk::Stock::PROPERTIES),
280 sigc::mem_fun(
281 *this, &MainWindow::on_action_file_properties));
282 actionGroup->add(Gtk::Action::create("InstrProperties",
283 Gtk::Stock::PROPERTIES),
284 sigc::mem_fun(
285 *this, &MainWindow::show_instr_props));
286 actionGroup->add(Gtk::Action::create("MidiRules",
287 _("_Midi Rules...")),
288 sigc::mem_fun(
289 *this, &MainWindow::show_midi_rules));
290 actionGroup->add(Gtk::Action::create("ScriptSlots",
291 _("_Script Slots...")),
292 sigc::mem_fun(
293 *this, &MainWindow::show_script_slots));
294 actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT),
295 sigc::mem_fun(
296 *this, &MainWindow::on_action_quit));
297 actionGroup->add(
298 Gtk::Action::create("MenuSample", _("_Sample")),
299 sigc::mem_fun(*this, &MainWindow::show_samples_tab)
300 );
301 actionGroup->add(
302 Gtk::Action::create("MenuInstrument", _("_Instrument")),
303 sigc::mem_fun(*this, &MainWindow::show_intruments_tab)
304 );
305 actionGroup->add(
306 Gtk::Action::create("MenuScript", _("Scr_ipt")),
307 sigc::mem_fun(*this, &MainWindow::show_scripts_tab)
308 );
309 actionGroup->add(Gtk::Action::create("AllInstruments", _("_Select")));
310 actionGroup->add(Gtk::Action::create("AssignScripts", _("Assign Script")));
311
312 actionGroup->add(Gtk::Action::create("MenuEdit", _("_Edit")));
313 #endif
314
315 const Gdk::ModifierType primaryModifierKey =
316 #if defined(__APPLE__)
317 Gdk::META_MASK; // Cmd key on Mac
318 #else
319 Gdk::CONTROL_MASK; // Ctrl key on all other OSs
320 #endif
321
322 #if USE_GLIB_ACTION
323 m_actionCopyDimRgn = m_actionGroup->add_action(
324 "CopyDimRgn", sigc::mem_fun(*this, &MainWindow::copy_selected_dimrgn)
325 );
326 m_actionPasteDimRgn = m_actionGroup->add_action(
327 "PasteDimRgn", sigc::mem_fun(*this, &MainWindow::paste_copied_dimrgn)
328 );
329 m_actionAdjustClipboard = m_actionGroup->add_action(
330 "AdjustClipboard", sigc::mem_fun(*this, &MainWindow::adjust_clipboard_content)
331 );
332 m_actionGroup->add_action(
333 "SelectPrevInstr", sigc::mem_fun(*this, &MainWindow::select_prev_instrument)
334 );
335 m_actionGroup->add_action(
336 "SelectNextInstr", sigc::mem_fun(*this, &MainWindow::select_next_instrument)
337 );
338 m_actionGroup->add_action(
339 "SelectPrevRegion", sigc::mem_fun(*this, &MainWindow::select_prev_region)
340 );
341 m_actionGroup->add_action(
342 "SelectNextRegion", sigc::mem_fun(*this, &MainWindow::select_next_region)
343 );
344 m_actionGroup->add_action(
345 "SelectPrevDimRgnZone", sigc::mem_fun(*this, &MainWindow::select_prev_dim_rgn_zone)
346 );
347 m_actionGroup->add_action(
348 "SelectNextDimRgnZone", sigc::mem_fun(*this, &MainWindow::select_next_dim_rgn_zone)
349 );
350 m_actionGroup->add_action(
351 "SelectPrevDimension", sigc::mem_fun(*this, &MainWindow::select_prev_dimension)
352 );
353 m_actionGroup->add_action(
354 "SelectNextDimension", sigc::mem_fun(*this, &MainWindow::select_next_dimension)
355 );
356 m_actionGroup->add_action(
357 "SelectAddPrevDimRgnZone", sigc::mem_fun(*this, &MainWindow::select_add_prev_dim_rgn_zone)
358 );
359 m_actionGroup->add_action(
360 "SelectAddNextDimRgnZone", sigc::mem_fun(*this, &MainWindow::select_add_next_dim_rgn_zone)
361 );
362 #else
363 actionGroup->add(Gtk::Action::create("CopyDimRgn",
364 _("Copy selected dimension region")),
365 Gtk::AccelKey(GDK_KEY_c, Gdk::MOD1_MASK),
366 sigc::mem_fun(*this, &MainWindow::copy_selected_dimrgn));
367
368 actionGroup->add(Gtk::Action::create("PasteDimRgn",
369 _("Paste dimension region")),
370 Gtk::AccelKey(GDK_KEY_v, Gdk::MOD1_MASK),
371 sigc::mem_fun(*this, &MainWindow::paste_copied_dimrgn));
372
373 actionGroup->add(Gtk::Action::create("AdjustClipboard",
374 _("Adjust Clipboard Content")),
375 Gtk::AccelKey(GDK_KEY_x, Gdk::MOD1_MASK),
376 sigc::mem_fun(*this, &MainWindow::adjust_clipboard_content));
377
378 actionGroup->add(Gtk::Action::create("SelectPrevInstr",
379 _("Select Previous Instrument")),
380 Gtk::AccelKey(GDK_KEY_Up, primaryModifierKey),
381 sigc::mem_fun(*this, &MainWindow::select_prev_instrument));
382
383 actionGroup->add(Gtk::Action::create("SelectNextInstr",
384 _("Select Next Instrument")),
385 Gtk::AccelKey(GDK_KEY_Down, primaryModifierKey),
386 sigc::mem_fun(*this, &MainWindow::select_next_instrument));
387
388 actionGroup->add(Gtk::Action::create("SelectPrevRegion",
389 _("Select Previous Region")),
390 Gtk::AccelKey(GDK_KEY_Left, primaryModifierKey),
391 sigc::mem_fun(*this, &MainWindow::select_prev_region));
392
393 actionGroup->add(Gtk::Action::create("SelectNextRegion",
394 _("Select Next Region")),
395 Gtk::AccelKey(GDK_KEY_Right, primaryModifierKey),
396 sigc::mem_fun(*this, &MainWindow::select_next_region));
397
398 actionGroup->add(Gtk::Action::create("SelectPrevDimRgnZone",
399 _("Select Previous Dimension Region Zone")),
400 Gtk::AccelKey(GDK_KEY_Left, Gdk::MOD1_MASK),
401 sigc::mem_fun(*this, &MainWindow::select_prev_dim_rgn_zone));
402
403 actionGroup->add(Gtk::Action::create("SelectNextDimRgnZone",
404 _("Select Next Dimension Region Zone")),
405 Gtk::AccelKey(GDK_KEY_Right, Gdk::MOD1_MASK),
406 sigc::mem_fun(*this, &MainWindow::select_next_dim_rgn_zone));
407
408 actionGroup->add(Gtk::Action::create("SelectPrevDimension",
409 _("Select Previous Dimension")),
410 Gtk::AccelKey(GDK_KEY_Up, Gdk::MOD1_MASK),
411 sigc::mem_fun(*this, &MainWindow::select_prev_dimension));
412
413 actionGroup->add(Gtk::Action::create("SelectNextDimension",
414 _("Select Next Dimension")),
415 Gtk::AccelKey(GDK_KEY_Down, Gdk::MOD1_MASK),
416 sigc::mem_fun(*this, &MainWindow::select_next_dimension));
417
418 actionGroup->add(Gtk::Action::create("SelectAddPrevDimRgnZone",
419 _("Add Previous Dimension Region Zone to Selection")),
420 Gtk::AccelKey(GDK_KEY_Left, Gdk::MOD1_MASK | Gdk::SHIFT_MASK),
421 sigc::mem_fun(*this, &MainWindow::select_add_prev_dim_rgn_zone));
422
423 actionGroup->add(Gtk::Action::create("SelectAddNextDimRgnZone",
424 _("Add Next Dimension Region Zone to Selection")),
425 Gtk::AccelKey(GDK_KEY_Right, Gdk::MOD1_MASK | Gdk::SHIFT_MASK),
426 sigc::mem_fun(*this, &MainWindow::select_add_next_dim_rgn_zone));
427 #endif
428
429 #if USE_GLIB_ACTION
430 m_actionToggleCopySampleUnity = m_actionGroup->add_action_bool("CopySampleUnity", true);
431 m_actionToggleCopySampleTune = m_actionGroup->add_action_bool("CopySampleTune", true);
432 m_actionToggleCopySampleLoop = m_actionGroup->add_action_bool("CopySampleLoop", true);
433 #else
434 Glib::RefPtr<Gtk::ToggleAction> toggle_action =
435 Gtk::ToggleAction::create("CopySampleUnity", _("Copy Sample's _Unity Note"));
436 toggle_action->set_active(true);
437 actionGroup->add(toggle_action);
438
439 toggle_action =
440 Gtk::ToggleAction::create("CopySampleTune", _("Copy Sample's _Fine Tune"));
441 toggle_action->set_active(true);
442 actionGroup->add(toggle_action);
443
444 toggle_action =
445 Gtk::ToggleAction::create("CopySampleLoop", _("Copy Sample's _Loop Points"));
446 toggle_action->set_active(true);
447 actionGroup->add(toggle_action);
448 #endif
449
450 #if USE_GLIB_ACTION
451 m_actionToggleStatusBar =
452 m_actionGroup->add_action_bool("Statusbar", sigc::mem_fun(*this, &MainWindow::on_action_view_status_bar), true);
453 m_actionToggleRestoreWinDim =
454 m_actionGroup->add_action_bool("AutoRestoreWinDim", sigc::mem_fun(*this, &MainWindow::on_auto_restore_win_dim), Settings::singleton()->autoRestoreWindowDimension);
455 m_actionToggleSaveWithTempFile =
456 m_actionGroup->add_action_bool("SaveWithTemporaryFile", sigc::mem_fun(*this, &MainWindow::on_save_with_temporary_file), Settings::singleton()->saveWithTemporaryFile);
457 m_actionGroup->add_action("RefreshAll", sigc::mem_fun(*this, &MainWindow::on_action_refresh_all));
458 #else
459 actionGroup->add(Gtk::Action::create("MenuMacro", _("_Macro")));
460
461
462 actionGroup->add(Gtk::Action::create("MenuView", _("Vie_w")));
463 toggle_action =
464 Gtk::ToggleAction::create("Statusbar", _("_Statusbar"));
465 toggle_action->set_active(true);
466 actionGroup->add(toggle_action,
467 sigc::mem_fun(
468 *this, &MainWindow::on_action_view_status_bar));
469
470 toggle_action =
471 Gtk::ToggleAction::create("AutoRestoreWinDim", _("_Auto Restore Window Dimension"));
472 toggle_action->set_active(Settings::singleton()->autoRestoreWindowDimension);
473 actionGroup->add(toggle_action,
474 sigc::mem_fun(
475 *this, &MainWindow::on_auto_restore_win_dim));
476
477 toggle_action =
478 Gtk::ToggleAction::create("SaveWithTemporaryFile", _("Save with _temporary file"));
479 toggle_action->set_active(Settings::singleton()->saveWithTemporaryFile);
480 actionGroup->add(toggle_action,
481 sigc::mem_fun(
482 *this, &MainWindow::on_save_with_temporary_file));
483
484 actionGroup->add(
485 Gtk::Action::create("RefreshAll", _("_Refresh All")),
486 sigc::mem_fun(*this, &MainWindow::on_action_refresh_all)
487 );
488 #endif
489
490 #if USE_GLIB_ACTION
491 m_actionGroup->add_action(
492 "About", sigc::mem_fun(*this, &MainWindow::on_action_help_about)
493 );
494 m_actionGroup->add_action(
495 "AddInstrument", sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
496 );
497 m_actionGroup->add_action(
498 "DupInstrument", sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument)
499 );
500 m_actionGroup->add_action(
501 "CombInstruments", sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments)
502 );
503 m_actionGroup->add_action(
504 "RemoveInstrument", sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
505 );
506 #else
507 action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP);
508 actionGroup->add(Gtk::Action::create("MenuHelp",
509 action->property_label()));
510 actionGroup->add(Gtk::Action::create("About", Gtk::Stock::ABOUT),
511 sigc::mem_fun(
512 *this, &MainWindow::on_action_help_about));
513 actionGroup->add(
514 Gtk::Action::create("AddInstrument", _("Add _Instrument")),
515 sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
516 );
517 actionGroup->add(
518 Gtk::Action::create("DupInstrument", _("_Duplicate Instrument")),
519 sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument)
520 );
521 actionGroup->add(
522 Gtk::Action::create("CombInstruments", _("_Combine Instruments ...")),
523 Gtk::AccelKey(GDK_KEY_j, primaryModifierKey),
524 sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments)
525 );
526 actionGroup->add(
527 Gtk::Action::create("RemoveInstrument", Gtk::Stock::REMOVE),
528 sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
529 );
530 #endif
531
532 #if USE_GLIB_ACTION
533 m_actionToggleWarnOnExtensions = m_actionGroup->add_action_bool(
534 "WarnUserOnExtensions", sigc::mem_fun(*this, &MainWindow::on_action_warn_user_on_extensions),
535 Settings::singleton()->warnUserOnExtensions
536 );
537 m_actionToggleSyncSamplerSelection = m_actionGroup->add_action_bool(
538 "SyncSamplerInstrumentSelection", sigc::mem_fun(*this, &MainWindow::on_action_sync_sampler_instrument_selection),
539 Settings::singleton()->syncSamplerInstrumentSelection
540 );
541 m_actionToggleMoveRootNoteWithRegion = m_actionGroup->add_action_bool(
542 "MoveRootNoteWithRegionMoved", sigc::mem_fun(*this, &MainWindow::on_action_move_root_note_with_region_moved),
543 Settings::singleton()->moveRootNoteWithRegionMoved
544 );
545 #else
546 actionGroup->add(Gtk::Action::create("MenuSettings", _("_Settings")));
547
548 toggle_action =
549 Gtk::ToggleAction::create("WarnUserOnExtensions", _("Show warning on format _extensions"));
550 toggle_action->set_active(Settings::singleton()->warnUserOnExtensions);
551 actionGroup->add(
552 toggle_action,
553 sigc::mem_fun(*this, &MainWindow::on_action_warn_user_on_extensions)
554 );
555
556 toggle_action =
557 Gtk::ToggleAction::create("SyncSamplerInstrumentSelection", _("Synchronize sampler's instrument selection"));
558 toggle_action->set_active(Settings::singleton()->syncSamplerInstrumentSelection);
559 actionGroup->add(
560 toggle_action,
561 sigc::mem_fun(*this, &MainWindow::on_action_sync_sampler_instrument_selection)
562 );
563
564 toggle_action =
565 Gtk::ToggleAction::create("MoveRootNoteWithRegionMoved", _("Move root note with region moved"));
566 toggle_action->set_active(Settings::singleton()->moveRootNoteWithRegionMoved);
567 actionGroup->add(
568 toggle_action,
569 sigc::mem_fun(*this, &MainWindow::on_action_move_root_note_with_region_moved)
570 );
571 #endif
572
573 #if USE_GLIB_ACTION
574 m_actionGroup->add_action(
575 "CombineInstruments", sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments)
576 );
577 m_actionGroup->add_action(
578 "MergeFiles", sigc::mem_fun(*this, &MainWindow::on_action_merge_files)
579 );
580 #else
581 actionGroup->add(Gtk::Action::create("MenuTools", _("_Tools")));
582
583 actionGroup->add(
584 Gtk::Action::create("CombineInstruments", _("_Combine Instruments...")),
585 sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments)
586 );
587
588 actionGroup->add(
589 Gtk::Action::create("MergeFiles", _("_Merge Files...")),
590 sigc::mem_fun(*this, &MainWindow::on_action_merge_files)
591 );
592 #endif
593
594 // sample right-click popup actions
595 #if USE_GLIB_ACTION
596 m_actionSampleProperties = m_actionGroup->add_action(
597 "SampleProperties", sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
598 );
599 m_actionAddSampleGroup = m_actionGroup->add_action(
600 "AddGroup", sigc::mem_fun(*this, &MainWindow::on_action_add_group)
601 );
602 m_actionAddSample = m_actionGroup->add_action(
603 "AddSample", sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
604 );
605 m_actionRemoveSample = m_actionGroup->add_action(
606 "RemoveSample", sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
607 );
608 m_actionGroup->add_action(
609 "RemoveUnusedSamples", sigc::mem_fun(*this, &MainWindow::on_action_remove_unused_samples)
610 );
611 m_actionViewSampleRefs = m_actionGroup->add_action(
612 "ShowSampleRefs", sigc::mem_fun(*this, &MainWindow::on_action_view_references)
613 );
614 m_actionReplaceSample = m_actionGroup->add_action(
615 "ReplaceSample", sigc::mem_fun(*this, &MainWindow::on_action_replace_sample)
616 );
617 m_actionGroup->add_action(
618 "ReplaceAllSamplesInAllGroups", sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups)
619 );
620 #else
621 actionGroup->add(
622 Gtk::Action::create("SampleProperties", Gtk::Stock::PROPERTIES),
623 sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
624 );
625 actionGroup->add(
626 Gtk::Action::create("AddGroup", _("Add _Group")),
627 sigc::mem_fun(*this, &MainWindow::on_action_add_group)
628 );
629 actionGroup->add(
630 Gtk::Action::create("AddSample", _("Add _Sample(s)...")),
631 sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
632 );
633 actionGroup->add(
634 Gtk::Action::create("RemoveSample", Gtk::Stock::REMOVE),
635 sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
636 );
637 actionGroup->add(
638 Gtk::Action::create("RemoveUnusedSamples", _("Remove _Unused Samples")),
639 sigc::mem_fun(*this, &MainWindow::on_action_remove_unused_samples)
640 );
641 actionGroup->add(
642 Gtk::Action::create("ShowSampleRefs", _("Show References...")),
643 sigc::mem_fun(*this, &MainWindow::on_action_view_references)
644 );
645 actionGroup->add(
646 Gtk::Action::create("ReplaceSample",
647 _("Replace Sample...")),
648 sigc::mem_fun(*this, &MainWindow::on_action_replace_sample)
649 );
650 actionGroup->add(
651 Gtk::Action::create("ReplaceAllSamplesInAllGroups",
652 _("Replace All Samples in All Groups...")),
653 sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups)
654 );
655 #endif
656
657 // script right-click popup actions
658 #if USE_GLIB_ACTION
659 m_actionAddScriptGroup = m_actionGroup->add_action(
660 "AddScriptGroup", sigc::mem_fun(*this, &MainWindow::on_action_add_script_group)
661 );
662 m_actionAddScript = m_actionGroup->add_action(
663 "AddScript", sigc::mem_fun(*this, &MainWindow::on_action_add_script)
664 );
665 m_actionEditScript = m_actionGroup->add_action(
666 "EditScript", sigc::mem_fun(*this, &MainWindow::on_action_edit_script)
667 );
668 m_actionRemoveScript = m_actionGroup->add_action(
669 "RemoveScript", sigc::mem_fun(*this, &MainWindow::on_action_remove_script)
670 );
671 #else
672 actionGroup->add(
673 Gtk::Action::create("AddScriptGroup", _("Add _Group")),
674 sigc::mem_fun(*this, &MainWindow::on_action_add_script_group)
675 );
676 actionGroup->add(
677 Gtk::Action::create("AddScript", _("Add _Script")),
678 sigc::mem_fun(*this, &MainWindow::on_action_add_script)
679 );
680 actionGroup->add(
681 Gtk::Action::create("EditScript", _("_Edit Script...")),
682 sigc::mem_fun(*this, &MainWindow::on_action_edit_script)
683 );
684 actionGroup->add(
685 Gtk::Action::create("RemoveScript", Gtk::Stock::REMOVE),
686 sigc::mem_fun(*this, &MainWindow::on_action_remove_script)
687 );
688 #endif
689
690 #if USE_GTKMM_BUILDER
691 insert_action_group("AppMenu", m_actionGroup);
692
693 m_uiManager = Gtk::Builder::create();
694 Glib::ustring ui_info =
695 "<interface>"
696 " <menubar id='MenuBar'>"
697 " <menu id='MenuFile'>"
698 " <attribute name='label' translatable='yes'>_File</attribute>"
699 " <section>"
700 " <item id='New'>"
701 " <attribute name='label' translatable='yes'>New</attribute>"
702 " <attribute name='action'>AppMenu.New</attribute>"
703 " </item>"
704 " <item id='Open'>"
705 " <attribute name='label' translatable='yes'>Open</attribute>"
706 " <attribute name='action'>AppMenu.Open</attribute>"
707 " </item>"
708 " </section>"
709 " <section>"
710 " <item id='Save'>"
711 " <attribute name='label' translatable='yes'>Save</attribute>"
712 " <attribute name='action'>AppMenu.Save</attribute>"
713 " </item>"
714 " <item id='SaveAs'>"
715 " <attribute name='label' translatable='yes'>Save As</attribute>"
716 " <attribute name='action'>AppMenu.SaveAs</attribute>"
717 " </item>"
718 " </section>"
719 " <section>"
720 " <item id='Properties'>"
721 " <attribute name='label' translatable='yes'>Properties</attribute>"
722 " <attribute name='action'>AppMenu.Properties</attribute>"
723 " </item>"
724 " </section>"
725 " <section>"
726 " <item id='Quit'>"
727 " <attribute name='label' translatable='yes'>Quit</attribute>"
728 " <attribute name='action'>AppMenu.Quit</attribute>"
729 " </item>"
730 " </section>"
731 " </menu>"
732 " <menu id='MenuEdit'>"
733 " <attribute name='label' translatable='yes'>Edit</attribute>"
734 " <section>"
735 " <item id='CopyDimRgn'>"
736 " <attribute name='label' translatable='yes'>Copy Dimension Region</attribute>"
737 " <attribute name='action'>AppMenu.CopyDimRgn</attribute>"
738 " </item>"
739 " <item id='AdjustClipboard'>"
740 " <attribute name='label' translatable='yes'>Adjust Clipboard</attribute>"
741 " <attribute name='action'>AppMenu.AdjustClipboard</attribute>"
742 " </item>"
743 " <item id='PasteDimRgn'>"
744 " <attribute name='label' translatable='yes'>Paste Dimension Region</attribute>"
745 " <attribute name='action'>AppMenu.PasteDimRgn</attribute>"
746 " </item>"
747 " </section>"
748 " <item id='SelectPrevInstr'>"
749 " <attribute name='label' translatable='yes'>Previous Instrument</attribute>"
750 " <attribute name='action'>AppMenu.SelectPrevInstr</attribute>"
751 " </item>"
752 " <item id='SelectNextInstr'>"
753 " <attribute name='label' translatable='yes'>Next Instrument</attribute>"
754 " <attribute name='action'>AppMenu.SelectNextInstr</attribute>"
755 " </item>"
756 " <section>"
757 " <item id='SelectPrevRegion'>"
758 " <attribute name='label' translatable='yes'>Previous Region</attribute>"
759 " <attribute name='action'>AppMenu.SelectPrevRegion</attribute>"
760 " </item>"
761 " <item id='SelectNextRegion'>"
762 " <attribute name='label' translatable='yes'>Next Region</attribute>"
763 " <attribute name='action'>AppMenu.SelectNextRegion</attribute>"
764 " </item>"
765 " </section>"
766 " <item id='SelectPrevDimension'>"
767 " <attribute name='label' translatable='yes'>Previous Dimension</attribute>"
768 " <attribute name='action'>AppMenu.SelectPrevDimension</attribute>"
769 " </item>"
770 " <item id='SelectNextDimension'>"
771 " <attribute name='label' translatable='yes'>Next Dimension</attribute>"
772 " <attribute name='action'>AppMenu.SelectNextDimension</attribute>"
773 " </item>"
774 " <item id='SelectPrevDimRgnZone'>"
775 " <attribute name='label' translatable='yes'>Previous Dimension Region Zone</attribute>"
776 " <attribute name='action'>AppMenu.SelectPrevDimRgnZone</attribute>"
777 " </item>"
778 " <item id='SelectNextDimRgnZone'>"
779 " <attribute name='label' translatable='yes'>Next Dimension Region Zone</attribute>"
780 " <attribute name='action'>AppMenu.SelectNextDimRgnZone</attribute>"
781 " </item>"
782 " <item id='SelectAddPrevDimRgnZone'>"
783 " <attribute name='label' translatable='yes'>Add Previous Dimension Region Zone</attribute>"
784 " <attribute name='action'>AppMenu.SelectAddPrevDimRgnZone</attribute>"
785 " </item>"
786 " <item id='SelectAddNextDimRgnZone'>"
787 " <attribute name='label' translatable='yes'>Add Next Dimension Region Zone</attribute>"
788 " <attribute name='action'>AppMenu.SelectAddNextDimRgnZone</attribute>"
789 " </item>"
790 " <section>"
791 " <item id='CopySampleUnity'>"
792 " <attribute name='label' translatable='yes'>Copy Sample Unity</attribute>"
793 " <attribute name='action'>AppMenu.CopySampleUnity</attribute>"
794 " </item>"
795 " <item id='CopySampleTune'>"
796 " <attribute name='label' translatable='yes'>Copy Sample Tune</attribute>"
797 " <attribute name='action'>AppMenu.CopySampleTune</attribute>"
798 " </item>"
799 " <item id='CopySampleLoop'>"
800 " <attribute name='label' translatable='yes'>Copy Sample Loop</attribute>"
801 " <attribute name='action'>AppMenu.CopySampleLoop</attribute>"
802 " </item>"
803 " </section>"
804 " </menu>"
805 " <menu id='MenuMacro'>"
806 " <attribute name='label' translatable='yes'>Macro</attribute>"
807 " <section>"
808 " </section>"
809 " </menu>"
810 " <menu id='MenuSample'>"
811 " <attribute name='label' translatable='yes'>Sample</attribute>"
812 " <section>"
813 " <item id='SampleProperties'>"
814 " <attribute name='label' translatable='yes'>Properties</attribute>"
815 " <attribute name='action'>AppMenu.SampleProperties</attribute>"
816 " </item>"
817 " <item id='AddGroup'>"
818 " <attribute name='label' translatable='yes'>Add Group</attribute>"
819 " <attribute name='action'>AppMenu.AddGroup</attribute>"
820 " </item>"
821 " <item id='AddSample'>"
822 " <attribute name='label' translatable='yes'>Add Sample</attribute>"
823 " <attribute name='action'>AppMenu.AddSample</attribute>"
824 " </item>"
825 " <item id='ShowSampleRefs'>"
826 " <attribute name='label' translatable='yes'>Show Sample References</attribute>"
827 " <attribute name='action'>AppMenu.ShowSampleRefs</attribute>"
828 " </item>"
829 " <item id='ReplaceSample'>"
830 " <attribute name='label' translatable='yes'>Replace Sample</attribute>"
831 " <attribute name='action'>AppMenu.ReplaceSample</attribute>"
832 " </item>"
833 " <item id='ReplaceAllSamplesInAllGroups'>"
834 " <attribute name='label' translatable='yes'>Replace all Samples in all Groups</attribute>"
835 " <attribute name='action'>AppMenu.ReplaceAllSamplesInAllGroups</attribute>"
836 " </item>"
837 " </section>"
838 " <section>"
839 " <item id='RemoveSample'>"
840 " <attribute name='label' translatable='yes'>Remove Sample</attribute>"
841 " <attribute name='action'>AppMenu.RemoveSample</attribute>"
842 " </item>"
843 " <item id='RemoveUnusedSamples'>"
844 " <attribute name='label' translatable='yes'>Remove unused Samples</attribute>"
845 " <attribute name='action'>AppMenu.RemoveUnusedSamples</attribute>"
846 " </item>"
847 " </section>"
848 " </menu>"
849 " <menu id='MenuInstrument'>"
850 " <attribute name='label' translatable='yes'>Instrument</attribute>"
851 " <section>"
852 " <item id='InstrProperties'>"
853 " <attribute name='label' translatable='yes'>Properties</attribute>"
854 " <attribute name='action'>AppMenu.InstrProperties</attribute>"
855 " </item>"
856 " <item id='MidiRules'>"
857 " <attribute name='label' translatable='yes'>MIDI Rules</attribute>"
858 " <attribute name='action'>AppMenu.MidiRules</attribute>"
859 " </item>"
860 " <item id='ScriptSlots'>"
861 " <attribute name='label' translatable='yes'>Script Slots</attribute>"
862 " <attribute name='action'>AppMenu.ScriptSlots</attribute>"
863 " </item>"
864 " </section>"
865 " <submenu id='AssignScripts'>"
866 " <attribute name='label' translatable='yes'>Assign Scripts</attribute>"
867 " </submenu>"
868 " <section>"
869 " <item id='AddInstrument'>"
870 " <attribute name='label' translatable='yes'>Add Instrument</attribute>"
871 " <attribute name='action'>AppMenu.AddInstrument</attribute>"
872 " </item>"
873 " <item id='DupInstrument'>"
874 " <attribute name='label' translatable='yes'>Duplicate Instrument</attribute>"
875 " <attribute name='action'>AppMenu.DupInstrument</attribute>"
876 " </item>"
877 " <item id='CombInstruments'>"
878 " <attribute name='label' translatable='yes'>Combine Instrument</attribute>"
879 " <attribute name='action'>AppMenu.CombInstruments</attribute>"
880 " </item>"
881 " </section>"
882 " <section>"
883 " <item id='RemoveInstrument'>"
884 " <attribute name='label' translatable='yes'>Remove Instrument</attribute>"
885 " <attribute name='action'>AppMenu.RemoveInstrument</attribute>"
886 " </item>"
887 " </section>"
888 " </menu>"
889 " <menu id='MenuScript'>"
890 " <attribute name='label' translatable='yes'>Script</attribute>"
891 " <section>"
892 " <item id='AddScriptGroup'>"
893 " <attribute name='label' translatable='yes'>Add Script Group</attribute>"
894 " <attribute name='action'>AppMenu.AddScriptGroup</attribute>"
895 " </item>"
896 " <item id='AddScript'>"
897 " <attribute name='label' translatable='yes'>Add Script</attribute>"
898 " <attribute name='action'>AppMenu.AddScript</attribute>"
899 " </item>"
900 " <item id='EditScript'>"
901 " <attribute name='label' translatable='yes'>Edit Script</attribute>"
902 " <attribute name='action'>AppMenu.EditScript</attribute>"
903 " </item>"
904 " </section>"
905 " <section>"
906 " <item id='RemoveScript'>"
907 " <attribute name='label' translatable='yes'>Remove Script</attribute>"
908 " <attribute name='action'>AppMenu.RemoveScript</attribute>"
909 " </item>"
910 " </section>"
911 " </menu>"
912 " <menu id='MenuView'>"
913 " <attribute name='label' translatable='yes'>View</attribute>"
914 " <section>"
915 " <item id='Statusbar'>"
916 " <attribute name='label' translatable='yes'>Statusbar</attribute>"
917 " <attribute name='action'>AppMenu.Statusbar</attribute>"
918 " </item>"
919 " <item id='AutoRestoreWinDim'>"
920 " <attribute name='label' translatable='yes'>Auto restore Window Dimensions</attribute>"
921 " <attribute name='action'>AppMenu.AutoRestoreWinDim</attribute>"
922 " </item>"
923 " </section>"
924 " <section>"
925 " <item id='RefreshAll'>"
926 " <attribute name='label' translatable='yes'>Refresh All</attribute>"
927 " <attribute name='action'>AppMenu.RefreshAll</attribute>"
928 " </item>"
929 " </section>"
930 " </menu>"
931 " <menu id='MenuTools'>"
932 " <attribute name='label' translatable='yes'>Tools</attribute>"
933 " <section>"
934 " <item id='CombineInstruments'>"
935 " <attribute name='label' translatable='yes'>Combine Instruments ...</attribute>"
936 " <attribute name='action'>AppMenu.CombineInstruments</attribute>"
937 " </item>"
938 " <item id='MergeFiles'>"
939 " <attribute name='label' translatable='yes'>Merge Files ...</attribute>"
940 " <attribute name='action'>AppMenu.MergeFiles</attribute>"
941 " </item>"
942 " </section>"
943 " </menu>"
944 " <menu id='MenuSettings'>"
945 " <attribute name='label' translatable='yes'>Settings</attribute>"
946 " <section>"
947 " <item id='WarnUserOnExtensions'>"
948 " <attribute name='label' translatable='yes'>Warning on Format Extensions</attribute>"
949 " <attribute name='action'>AppMenu.WarnUserOnExtensions</attribute>"
950 " </item>"
951 " <item id='SyncSamplerInstrumentSelection'>"
952 " <attribute name='label' translatable='yes'>Synchronize Sampler Selection</attribute>"
953 " <attribute name='action'>AppMenu.SyncSamplerInstrumentSelection</attribute>"
954 " </item>"
955 " <item id='MoveRootNoteWithRegionMoved'>"
956 " <attribute name='label' translatable='yes'>Move Root Note with Region moved</attribute>"
957 " <attribute name='action'>AppMenu.MoveRootNoteWithRegionMoved</attribute>"
958 " </item>"
959 " <item id='SaveWithTemporaryFile'>"
960 " <attribute name='label' translatable='yes'>Save with temporary file</attribute>"
961 " <attribute name='action'>AppMenu.SaveWithTemporaryFile</attribute>"
962 " </item>"
963 " </section>"
964 " </menu>"
965 " <menu id='MenuHelp'>"
966 " <attribute name='label' translatable='yes'>Help</attribute>"
967 " <section>"
968 " <item id='About'>"
969 " <attribute name='label' translatable='yes'>About ...</attribute>"
970 " <attribute name='action'>AppMenu.About</attribute>"
971 " </item>"
972 " </section>"
973 " </menu>"
974 " </menubar>"
975 // popups
976 " <menu id='PopupMenu'>"
977 " <section>"
978 " <item id='InstrProperties'>"
979 " <attribute name='label' translatable='yes'>Instrument Properties</attribute>"
980 " <attribute name='action'>AppMenu.InstrProperties</attribute>"
981 " </item>"
982 " <item id='MidiRules'>"
983 " <attribute name='label' translatable='yes'>MIDI Rules</attribute>"
984 " <attribute name='action'>AppMenu.MidiRules</attribute>"
985 " </item>"
986 " <item id='ScriptSlots'>"
987 " <attribute name='label' translatable='yes'>Script Slots</attribute>"
988 " <attribute name='action'>AppMenu.ScriptSlots</attribute>"
989 " </item>"
990 " <item id='AddInstrument'>"
991 " <attribute name='label' translatable='yes'>Add Instrument</attribute>"
992 " <attribute name='action'>AppMenu.AddInstrument</attribute>"
993 " </item>"
994 " <item id='DupInstrument'>"
995 " <attribute name='label' translatable='yes'>Duplicate Instrument</attribute>"
996 " <attribute name='action'>AppMenu.DupInstrument</attribute>"
997 " </item>"
998 " <item id='CombInstruments'>"
999 " <attribute name='label' translatable='yes'>Combine Instruments</attribute>"
1000 " <attribute name='action'>AppMenu.CombInstruments</attribute>"
1001 " </item>"
1002 " </section>"
1003 " <section>"
1004 " <item id='RemoveInstrument'>"
1005 " <attribute name='label' translatable='yes'>Remove Instruments</attribute>"
1006 " <attribute name='action'>AppMenu.RemoveInstrument</attribute>"
1007 " </item>"
1008 " </section>"
1009 " </menu>"
1010 " <menu id='SamplePopupMenu'>"
1011 " <section>"
1012 " <item id='SampleProperties'>"
1013 " <attribute name='label' translatable='yes'>Sample Properties</attribute>"
1014 " <attribute name='action'>AppMenu.SampleProperties</attribute>"
1015 " </item>"
1016 " <item id='AddGroup'>"
1017 " <attribute name='label' translatable='yes'>Add Sample Group</attribute>"
1018 " <attribute name='action'>AppMenu.AddGroup</attribute>"
1019 " </item>"
1020 " <item id='AddSample'>"
1021 " <attribute name='label' translatable='yes'>Add Sample</attribute>"
1022 " <attribute name='action'>AppMenu.AddSample</attribute>"
1023 " </item>"
1024 " <item id='ShowSampleRefs'>"
1025 " <attribute name='label' translatable='yes'>Show Sample References ...</attribute>"
1026 " <attribute name='action'>AppMenu.ShowSampleRefs</attribute>"
1027 " </item>"
1028 " <item id='ReplaceSample'>"
1029 " <attribute name='label' translatable='yes'>Replace Sample</attribute>"
1030 " <attribute name='action'>AppMenu.ReplaceSample</attribute>"
1031 " </item>"
1032 " <item id='ReplaceAllSamplesInAllGroups'>"
1033 " <attribute name='label' translatable='yes'>Replace all Samples ...</attribute>"
1034 " <attribute name='action'>AppMenu.ReplaceAllSamplesInAllGroups</attribute>"
1035 " </item>"
1036 " </section>"
1037 " <section>"
1038 " <item id='RemoveSample'>"
1039 " <attribute name='label' translatable='yes'>Remove Sample</attribute>"
1040 " <attribute name='action'>AppMenu.RemoveSample</attribute>"
1041 " </item>"
1042 " <item id='RemoveUnusedSamples'>"
1043 " <attribute name='label' translatable='yes'>Remove unused Samples</attribute>"
1044 " <attribute name='action'>AppMenu.RemoveUnusedSamples</attribute>"
1045 " </item>"
1046 " </section>"
1047 " </menu>"
1048 " <menu id='ScriptPopupMenu'>"
1049 " <section>"
1050 " <item id='AddScriptGroup'>"
1051 " <attribute name='label' translatable='yes'>Add Script Group</attribute>"
1052 " <attribute name='action'>AppMenu.AddScriptGroup</attribute>"
1053 " </item>"
1054 " <item id='AddScript'>"
1055 " <attribute name='label' translatable='yes'>Add Script</attribute>"
1056 " <attribute name='action'>AppMenu.AddScript</attribute>"
1057 " </item>"
1058 " <item id='EditScript'>"
1059 " <attribute name='label' translatable='yes'>Edit Script</attribute>"
1060 " <attribute name='action'>AppMenu.EditScript</attribute>"
1061 " </item>"
1062 " </section>"
1063 " <section>"
1064 " <item id='RemoveScript'>"
1065 " <attribute name='label' translatable='yes'>Remove Script</attribute>"
1066 " <attribute name='action'>AppMenu.RemoveScript</attribute>"
1067 " </item>"
1068 " </section>"
1069 " </menu>"
1070 "</interface>";
1071 m_uiManager->add_from_string(ui_info);
1072 #else
1073 uiManager = Gtk::UIManager::create();
1074 uiManager->insert_action_group(actionGroup);
1075 add_accel_group(uiManager->get_accel_group());
1076
1077 Glib::ustring ui_info =
1078 "<ui>"
1079 " <menubar name='MenuBar'>"
1080 " <menu action='MenuFile'>"
1081 " <menuitem action='New'/>"
1082 " <menuitem action='Open'/>"
1083 " <separator/>"
1084 " <menuitem action='Save'/>"
1085 " <menuitem action='SaveAs'/>"
1086 " <separator/>"
1087 " <menuitem action='Properties'/>"
1088 " <separator/>"
1089 " <menuitem action='Quit'/>"
1090 " </menu>"
1091 " <menu action='MenuEdit'>"
1092 " <menuitem action='CopyDimRgn'/>"
1093 " <menuitem action='AdjustClipboard'/>"
1094 " <menuitem action='PasteDimRgn'/>"
1095 " <separator/>"
1096 " <menuitem action='SelectPrevInstr'/>"
1097 " <menuitem action='SelectNextInstr'/>"
1098 " <separator/>"
1099 " <menuitem action='SelectPrevRegion'/>"
1100 " <menuitem action='SelectNextRegion'/>"
1101 " <separator/>"
1102 " <menuitem action='SelectPrevDimension'/>"
1103 " <menuitem action='SelectNextDimension'/>"
1104 " <menuitem action='SelectPrevDimRgnZone'/>"
1105 " <menuitem action='SelectNextDimRgnZone'/>"
1106 " <menuitem action='SelectAddPrevDimRgnZone'/>"
1107 " <menuitem action='SelectAddNextDimRgnZone'/>"
1108 " <separator/>"
1109 " <menuitem action='CopySampleUnity'/>"
1110 " <menuitem action='CopySampleTune'/>"
1111 " <menuitem action='CopySampleLoop'/>"
1112 " </menu>"
1113 " <menu action='MenuMacro'>"
1114 " </menu>"
1115 " <menu action='MenuSample'>"
1116 " <menuitem action='SampleProperties'/>"
1117 " <menuitem action='AddGroup'/>"
1118 " <menuitem action='AddSample'/>"
1119 " <menuitem action='ShowSampleRefs'/>"
1120 " <menuitem action='ReplaceSample' />"
1121 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
1122 " <separator/>"
1123 " <menuitem action='RemoveSample'/>"
1124 " <menuitem action='RemoveUnusedSamples'/>"
1125 " </menu>"
1126 " <menu action='MenuInstrument'>"
1127 " <menu action='AllInstruments'>"
1128 " </menu>"
1129 " <separator/>"
1130 " <menuitem action='InstrProperties'/>"
1131 " <menuitem action='MidiRules'/>"
1132 " <menuitem action='ScriptSlots'/>"
1133 " <menu action='AssignScripts'/>"
1134 " <menuitem action='AddInstrument'/>"
1135 " <menuitem action='DupInstrument'/>"
1136 " <menuitem action='CombInstruments'/>"
1137 " <separator/>"
1138 " <menuitem action='RemoveInstrument'/>"
1139 " </menu>"
1140 " <menu action='MenuScript'>"
1141 " <menuitem action='AddScriptGroup'/>"
1142 " <menuitem action='AddScript'/>"
1143 " <menuitem action='EditScript'/>"
1144 " <separator/>"
1145 " <menuitem action='RemoveScript'/>"
1146 " </menu>"
1147 " <menu action='MenuView'>"
1148 " <menuitem action='Statusbar'/>"
1149 " <menuitem action='AutoRestoreWinDim'/>"
1150 " <separator/>"
1151 " <menuitem action='RefreshAll'/>"
1152 " </menu>"
1153 " <menu action='MenuTools'>"
1154 " <menuitem action='CombineInstruments'/>"
1155 " <menuitem action='MergeFiles'/>"
1156 " </menu>"
1157 " <menu action='MenuSettings'>"
1158 " <menuitem action='WarnUserOnExtensions'/>"
1159 " <menuitem action='SyncSamplerInstrumentSelection'/>"
1160 " <menuitem action='MoveRootNoteWithRegionMoved'/>"
1161 " <menuitem action='SaveWithTemporaryFile'/>"
1162 " </menu>"
1163 " <menu action='MenuHelp'>"
1164 " <menuitem action='About'/>"
1165 " </menu>"
1166 " </menubar>"
1167 " <popup name='PopupMenu'>"
1168 " <menuitem action='InstrProperties'/>"
1169 " <menuitem action='MidiRules'/>"
1170 " <menuitem action='ScriptSlots'/>"
1171 " <menuitem action='AddInstrument'/>"
1172 " <menuitem action='DupInstrument'/>"
1173 " <menuitem action='CombInstruments'/>"
1174 " <separator/>"
1175 " <menuitem action='RemoveInstrument'/>"
1176 " </popup>"
1177 " <popup name='SamplePopupMenu'>"
1178 " <menuitem action='SampleProperties'/>"
1179 " <menuitem action='AddGroup'/>"
1180 " <menuitem action='AddSample'/>"
1181 " <menuitem action='ShowSampleRefs'/>"
1182 " <menuitem action='ReplaceSample' />"
1183 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
1184 " <separator/>"
1185 " <menuitem action='RemoveSample'/>"
1186 " <menuitem action='RemoveUnusedSamples'/>"
1187 " </popup>"
1188 " <popup name='ScriptPopupMenu'>"
1189 " <menuitem action='AddScriptGroup'/>"
1190 " <menuitem action='AddScript'/>"
1191 " <menuitem action='EditScript'/>"
1192 " <separator/>"
1193 " <menuitem action='RemoveScript'/>"
1194 " </popup>"
1195 "</ui>";
1196 uiManager->add_ui_from_string(ui_info);
1197 #endif
1198
1199 #if USE_GTKMM_BUILDER
1200 popup_menu = new Gtk::Menu(
1201 Glib::RefPtr<Gio::Menu>::cast_dynamic(
1202 m_uiManager->get_object("PopupMenu")
1203 )
1204 );
1205 sample_popup = new Gtk::Menu(
1206 Glib::RefPtr<Gio::Menu>::cast_dynamic(
1207 m_uiManager->get_object("SamplePopupMenu")
1208 )
1209 );
1210 script_popup = new Gtk::Menu(
1211 Glib::RefPtr<Gio::Menu>::cast_dynamic(
1212 m_uiManager->get_object("ScriptPopupMenu")
1213 )
1214 );
1215 #else
1216 popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
1217
1218 // Set tooltips for menu items (for some reason, setting a tooltip on the
1219 // respective Gtk::Action objects above will simply be ignored, no matter
1220 // if using Gtk::Action::set_tooltip() or passing the tooltip string on
1221 // Gtk::Action::create()).
1222 {
1223 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1224 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleUnity"));
1225 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."));
1226 }
1227 {
1228 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1229 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleTune"));
1230 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."));
1231 }
1232 {
1233 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1234 uiManager->get_widget("/MenuBar/MenuEdit/CopySampleLoop"));
1235 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."));
1236 }
1237 {
1238 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1239 uiManager->get_widget("/MenuBar/MenuSettings/WarnUserOnExtensions"));
1240 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."));
1241 }
1242 {
1243 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1244 uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection"));
1245 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)."));
1246 }
1247 {
1248 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1249 uiManager->get_widget("/MenuBar/MenuSettings/MoveRootNoteWithRegionMoved"));
1250 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."));
1251 }
1252 {
1253 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1254 uiManager->get_widget("/MenuBar/MenuSample/RemoveUnusedSamples"));
1255 item->set_tooltip_text(_("Removes all samples that are not referenced by any instrument (i.e. red ones)."));
1256 // copy tooltip to popup menu
1257 Gtk::MenuItem* item2 = dynamic_cast<Gtk::MenuItem*>(
1258 uiManager->get_widget("/SamplePopupMenu/RemoveUnusedSamples"));
1259 item2->set_tooltip_text(item->get_tooltip_text());
1260 }
1261 {
1262 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1263 uiManager->get_widget("/MenuBar/MenuView/RefreshAll"));
1264 item->set_tooltip_text(_("Reloads the currently open gig file and updates the entire graphical user interface."));
1265 }
1266 {
1267 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1268 uiManager->get_widget("/MenuBar/MenuView/AutoRestoreWinDim"));
1269 item->set_tooltip_text(_("If checked, size and position of all windows will be saved and automatically restored next time."));
1270 }
1271 {
1272 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1273 uiManager->get_widget("/MenuBar/MenuTools/CombineInstruments"));
1274 item->set_tooltip_text(_("Create combi sounds out of individual sounds of this .gig file."));
1275 }
1276 {
1277 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
1278 uiManager->get_widget("/MenuBar/MenuTools/MergeFiles"));
1279 item->set_tooltip_text(_("Add instruments and samples of other .gig files to this .gig file."));
1280 }
1281 #endif
1282
1283 #if USE_GTKMM_BUILDER
1284 assign_scripts_menu = new Gtk::Menu(
1285 Glib::RefPtr<Gio::Menu>::cast_dynamic(
1286 m_uiManager->get_object("AssignScripts")
1287 )
1288 );
1289 #else
1290 instrument_menu = static_cast<Gtk::MenuItem*>(
1291 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments"))->get_submenu();
1292
1293 assign_scripts_menu = static_cast<Gtk::MenuItem*>(
1294 uiManager->get_widget("/MenuBar/MenuInstrument/AssignScripts"))->get_submenu();
1295 #endif
1296
1297 #if USE_GTKMM_BUILDER
1298 Gtk::Widget* menuBar = NULL;
1299 m_uiManager->get_widget("MenuBar", menuBar);
1300 #else
1301 Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar");
1302 #endif
1303
1304 m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
1305 m_VBox.pack_start(m_HPaned);
1306 m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
1307 m_VBox.pack_start(m_RegionChooser.m_VirtKeybPropsBox, Gtk::PACK_SHRINK);
1308 m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
1309 m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
1310
1311 set_file_is_shared(false);
1312
1313 // Status Bar:
1314 #if USE_GTKMM_BOX
1315 # warning No status bar layout for GTKMM 4 yet
1316 #else
1317 m_StatusBar.pack_start(m_AttachedStateLabel, Gtk::PACK_SHRINK);
1318 m_StatusBar.pack_start(m_AttachedStateImage, Gtk::PACK_SHRINK);
1319 #endif
1320 m_StatusBar.show();
1321
1322 m_RegionChooser.signal_region_selected().connect(
1323 sigc::mem_fun(*this, &MainWindow::region_changed) );
1324 m_DimRegionChooser.signal_dimregion_selected().connect(
1325 sigc::mem_fun(*this, &MainWindow::dimreg_changed) );
1326
1327
1328 // Create the Tree model:
1329 m_refTreeModel = Gtk::ListStore::create(m_Columns);
1330 m_refTreeModelFilter = Gtk::TreeModelFilter::create(m_refTreeModel);
1331 m_refTreeModelFilter->set_visible_func(
1332 sigc::mem_fun(*this, &MainWindow::instrument_row_visible)
1333 );
1334 m_TreeView.set_model(m_refTreeModelFilter);
1335
1336 m_TreeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
1337 m_TreeView.set_tooltip_text(_("Right click here for actions on instruments & MIDI Rules. Drag & drop to change the order of instruments."));
1338 instrument_name_connection = m_refTreeModel->signal_row_changed().connect(
1339 sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
1340 );
1341
1342 // Add the TreeView's view columns:
1343 m_TreeView.append_column(_("Nr"), m_Columns.m_col_nr);
1344 m_TreeView.append_column_editable(_("Instrument"), m_Columns.m_col_name);
1345 m_TreeView.append_column(_("Scripts"), m_Columns.m_col_scripts);
1346 m_TreeView.set_headers_visible(true);
1347
1348 // establish drag&drop within the instrument tree view, allowing to reorder
1349 // the sequence of instruments within the gig file
1350 {
1351 std::vector<Gtk::TargetEntry> drag_target_instrument;
1352 drag_target_instrument.push_back(Gtk::TargetEntry("gig::Instrument"));
1353 m_TreeView.drag_source_set(drag_target_instrument);
1354 m_TreeView.drag_dest_set(drag_target_instrument);
1355 m_TreeView.signal_drag_begin().connect(
1356 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_begin)
1357 );
1358 m_TreeView.signal_drag_data_get().connect(
1359 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_data_get)
1360 );
1361 m_TreeView.signal_drag_data_received().connect(
1362 sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drop_drag_data_received)
1363 );
1364 }
1365
1366 // create samples treeview (including its data model)
1367 m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
1368 m_TreeViewSamples.set_model(m_refSamplesTreeModel);
1369 m_TreeViewSamples.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
1370 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."));
1371 // m_TreeViewSamples.set_reorderable();
1372 m_TreeViewSamples.append_column_editable(_("Name"), m_SamplesModel.m_col_name);
1373 m_TreeViewSamples.append_column(_("Referenced"), m_SamplesModel.m_col_refcount);
1374 {
1375 Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(0);
1376 Gtk::CellRendererText* cellrenderer =
1377 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
1378 column->add_attribute(
1379 cellrenderer->property_foreground(), m_SamplesModel.m_color
1380 );
1381 }
1382 {
1383 Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(1);
1384 Gtk::CellRendererText* cellrenderer =
1385 dynamic_cast<Gtk::CellRendererText*>(column->get_first_cell());
1386 column->add_attribute(
1387 cellrenderer->property_foreground(), m_SamplesModel.m_color
1388 );
1389 }
1390 m_TreeViewSamples.set_headers_visible(true);
1391 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
1392 m_TreeViewSamples.signal_button_press_event().connect(
1393 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
1394 );
1395 #else
1396 m_TreeViewSamples.signal_button_press_event().connect_notify(
1397 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
1398 );
1399 #endif
1400 m_refSamplesTreeModel->signal_row_changed().connect(
1401 sigc::mem_fun(*this, &MainWindow::sample_name_changed)
1402 );
1403
1404 // create scripts treeview (including its data model)
1405 m_refScriptsTreeModel = ScriptsTreeStore::create(m_ScriptsModel);
1406 m_TreeViewScripts.set_model(m_refScriptsTreeModel);
1407 m_TreeViewScripts.set_tooltip_text(_(
1408 "Use CTRL + double click for editing a script."
1409 "\n\n"
1410 "Note: instrument scripts are a LinuxSampler extension of the gig "
1411 "format. This feature will not work with the GigaStudio software!"
1412 ));
1413 // m_TreeViewScripts.set_reorderable();
1414 m_TreeViewScripts.append_column_editable("Samples", m_ScriptsModel.m_col_name);
1415 m_TreeViewScripts.set_headers_visible(false);
1416 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
1417 m_TreeViewScripts.signal_button_press_event().connect(
1418 sigc::mem_fun(*this, &MainWindow::on_script_treeview_button_release)
1419 );
1420 #else
1421 m_TreeViewScripts.signal_button_press_event().connect_notify(
1422 sigc::mem_fun(*this, &MainWindow::on_script_treeview_button_release)
1423 );
1424 #endif
1425 //FIXME: why the heck does this double click signal_row_activated() only fire while CTRL key is pressed ?
1426 m_TreeViewScripts.signal_row_activated().connect(
1427 sigc::mem_fun(*this, &MainWindow::script_double_clicked)
1428 );
1429 m_refScriptsTreeModel->signal_row_changed().connect(
1430 sigc::mem_fun(*this, &MainWindow::script_name_changed)
1431 );
1432
1433 // establish drag&drop between scripts tree view and ScriptSlots window
1434 std::vector<Gtk::TargetEntry> drag_target_gig_script;
1435 drag_target_gig_script.push_back(Gtk::TargetEntry("gig::Script"));
1436 m_TreeViewScripts.drag_source_set(drag_target_gig_script);
1437 m_TreeViewScripts.signal_drag_begin().connect(
1438 sigc::mem_fun(*this, &MainWindow::on_scripts_treeview_drag_begin)
1439 );
1440 m_TreeViewScripts.signal_drag_data_get().connect(
1441 sigc::mem_fun(*this, &MainWindow::on_scripts_treeview_drag_data_get)
1442 );
1443
1444 // establish drag&drop between samples tree view and dimension region 'Sample' text entry
1445 std::vector<Gtk::TargetEntry> drag_target_gig_sample;
1446 drag_target_gig_sample.push_back(Gtk::TargetEntry("gig::Sample"));
1447 m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
1448 m_TreeViewSamples.signal_drag_begin().connect(
1449 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_begin)
1450 );
1451 m_TreeViewSamples.signal_drag_data_get().connect(
1452 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get)
1453 );
1454 dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample);
1455 dimreg_edit.wSample->signal_drag_data_received().connect(
1456 sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
1457 );
1458 dimreg_edit.signal_dimreg_changed().connect(
1459 sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
1460 m_RegionChooser.signal_instrument_changed().connect(
1461 sigc::mem_fun(*this, &MainWindow::file_changed));
1462 m_RegionChooser.signal_instrument_changed().connect(
1463 sigc::mem_fun(*this, &MainWindow::region_changed));
1464 m_DimRegionChooser.signal_region_changed().connect(
1465 sigc::mem_fun(*this, &MainWindow::file_changed));
1466 instrumentProps.signal_changed().connect(
1467 sigc::mem_fun(*this, &MainWindow::file_changed));
1468 propDialog.signal_changed().connect(
1469 sigc::mem_fun(*this, &MainWindow::file_changed));
1470 midiRules.signal_changed().connect(
1471 sigc::mem_fun(*this, &MainWindow::file_changed));
1472
1473 dimreg_edit.signal_dimreg_to_be_changed().connect(
1474 dimreg_to_be_changed_signal.make_slot());
1475 dimreg_edit.signal_dimreg_changed().connect(
1476 dimreg_changed_signal.make_slot());
1477 dimreg_edit.signal_sample_ref_changed().connect(
1478 sample_ref_changed_signal.make_slot());
1479 sample_ref_changed_signal.connect(
1480 sigc::mem_fun(*this, &MainWindow::on_sample_ref_changed)
1481 );
1482 samples_to_be_removed_signal.connect(
1483 sigc::mem_fun(*this, &MainWindow::on_samples_to_be_removed)
1484 );
1485
1486 dimreg_edit.signal_select_sample().connect(
1487 sigc::mem_fun(*this, &MainWindow::select_sample)
1488 );
1489
1490 m_RegionChooser.signal_instrument_struct_to_be_changed().connect(
1491 sigc::hide(
1492 sigc::bind(
1493 file_structure_to_be_changed_signal.make_slot(),
1494 sigc::ref(this->file)
1495 )
1496 )
1497 );
1498 m_RegionChooser.signal_instrument_struct_changed().connect(
1499 sigc::hide(
1500 sigc::bind(
1501 file_structure_changed_signal.make_slot(),
1502 sigc::ref(this->file)
1503 )
1504 )
1505 );
1506 m_RegionChooser.signal_region_to_be_changed().connect(
1507 region_to_be_changed_signal.make_slot());
1508 m_RegionChooser.signal_region_changed_signal().connect(
1509 region_changed_signal.make_slot());
1510
1511 note_on_signal.connect(
1512 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_on_event));
1513 note_off_signal.connect(
1514 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_off_event));
1515
1516 dimreg_all_regions.signal_toggled().connect(
1517 sigc::mem_fun(*this, &MainWindow::update_dimregs));
1518 dimreg_all_dimregs.signal_toggled().connect(
1519 sigc::mem_fun(*this, &MainWindow::dimreg_all_dimregs_toggled));
1520 dimreg_stereo.signal_toggled().connect(
1521 sigc::mem_fun(*this, &MainWindow::update_dimregs));
1522
1523 m_searchText.signal_changed().connect(
1524 sigc::mem_fun(m_refTreeModelFilter.operator->(), &Gtk::TreeModelFilter::refilter)
1525 );
1526
1527 file = 0;
1528 file_is_changed = false;
1529
1530 #if HAS_GTKMM_SHOW_ALL_CHILDREN
1531 show_all_children();
1532 #endif
1533
1534 // start with a new gig file by default
1535 on_action_file_new();
1536
1537 m_TreeViewNotebook.signal_switch_page().connect(
1538 sigc::mem_fun(*this, &MainWindow::on_notebook_tab_switched)
1539 );
1540
1541 // select 'Instruments' tab by default
1542 // (gtk allows this only if the tab childs are visible, thats why it's here)
1543 m_TreeViewNotebook.set_current_page(1);
1544
1545 Gtk::Clipboard::get()->signal_owner_change().connect(
1546 sigc::mem_fun(*this, &MainWindow::on_clipboard_owner_change)
1547 );
1548 updateClipboardPasteAvailable();
1549 updateClipboardCopyAvailable();
1550
1551 // setup macros and their keyboard accelerators
1552 {
1553 #if USE_GTKMM_BUILDER
1554 menuMacro = new Gtk::Menu(
1555 Glib::RefPtr<Gio::Menu>::cast_dynamic(
1556 m_uiManager->get_object("MenuMacro")
1557 )
1558 );
1559 #else
1560 Gtk::Menu* menuMacro = dynamic_cast<Gtk::MenuItem*>(
1561 uiManager->get_widget("/MenuBar/MenuMacro")
1562 )->get_submenu();
1563 #endif
1564
1565 const Gdk::ModifierType noModifier = (Gdk::ModifierType)0;
1566 Gtk::AccelMap::add_entry("<Macros>/macro_0", GDK_KEY_F1, noModifier);
1567 Gtk::AccelMap::add_entry("<Macros>/macro_1", GDK_KEY_F2, noModifier);
1568 Gtk::AccelMap::add_entry("<Macros>/macro_2", GDK_KEY_F3, noModifier);
1569 Gtk::AccelMap::add_entry("<Macros>/macro_3", GDK_KEY_F4, noModifier);
1570 Gtk::AccelMap::add_entry("<Macros>/macro_4", GDK_KEY_F5, noModifier);
1571 Gtk::AccelMap::add_entry("<Macros>/macro_5", GDK_KEY_F6, noModifier);
1572 Gtk::AccelMap::add_entry("<Macros>/macro_6", GDK_KEY_F7, noModifier);
1573 Gtk::AccelMap::add_entry("<Macros>/macro_7", GDK_KEY_F8, noModifier);
1574 Gtk::AccelMap::add_entry("<Macros>/macro_8", GDK_KEY_F9, noModifier);
1575 Gtk::AccelMap::add_entry("<Macros>/macro_9", GDK_KEY_F10, noModifier);
1576 Gtk::AccelMap::add_entry("<Macros>/macro_10", GDK_KEY_F11, noModifier);
1577 Gtk::AccelMap::add_entry("<Macros>/macro_11", GDK_KEY_F12, noModifier);
1578 Gtk::AccelMap::add_entry("<Macros>/SetupMacros", 'm', primaryModifierKey);
1579
1580 Glib::RefPtr<Gtk::AccelGroup> accelGroup = this->get_accel_group();
1581 menuMacro->set_accel_group(accelGroup);
1582
1583 updateMacroMenu();
1584 }
1585
1586 // setup "Assign Scripts" keyboard accelerators
1587 {
1588 Gtk::AccelMap::add_entry("<Scripts>/script_0", GDK_KEY_F1, Gdk::SHIFT_MASK);
1589 Gtk::AccelMap::add_entry("<Scripts>/script_1", GDK_KEY_F2, Gdk::SHIFT_MASK);
1590 Gtk::AccelMap::add_entry("<Scripts>/script_2", GDK_KEY_F3, Gdk::SHIFT_MASK);
1591 Gtk::AccelMap::add_entry("<Scripts>/script_3", GDK_KEY_F4, Gdk::SHIFT_MASK);
1592 Gtk::AccelMap::add_entry("<Scripts>/script_4", GDK_KEY_F5, Gdk::SHIFT_MASK);
1593 Gtk::AccelMap::add_entry("<Scripts>/script_5", GDK_KEY_F6, Gdk::SHIFT_MASK);
1594 Gtk::AccelMap::add_entry("<Scripts>/script_6", GDK_KEY_F7, Gdk::SHIFT_MASK);
1595 Gtk::AccelMap::add_entry("<Scripts>/script_7", GDK_KEY_F8, Gdk::SHIFT_MASK);
1596 Gtk::AccelMap::add_entry("<Scripts>/script_8", GDK_KEY_F9, Gdk::SHIFT_MASK);
1597 Gtk::AccelMap::add_entry("<Scripts>/script_9", GDK_KEY_F10, Gdk::SHIFT_MASK);
1598 Gtk::AccelMap::add_entry("<Scripts>/script_10", GDK_KEY_F11, Gdk::SHIFT_MASK);
1599 Gtk::AccelMap::add_entry("<Scripts>/script_11", GDK_KEY_F12, Gdk::SHIFT_MASK);
1600
1601 Glib::RefPtr<Gtk::AccelGroup> accelGroup = this->get_accel_group();
1602 assign_scripts_menu->set_accel_group(accelGroup);
1603 }
1604
1605 Glib::signal_idle().connect_once(
1606 sigc::mem_fun(*this, &MainWindow::bringToFront),
1607 200
1608 );
1609 }
1610
1611 MainWindow::~MainWindow()
1612 {
1613 }
1614
1615 void MainWindow::bringToFront() {
1616 #if defined(__APPLE__)
1617 macRaiseAppWindow();
1618 #endif
1619 raise();
1620 present();
1621 }
1622
1623 void MainWindow::updateMacroMenu() {
1624 #if !USE_GTKMM_BUILDER
1625 Gtk::Menu* menuMacro = dynamic_cast<Gtk::MenuItem*>(
1626 uiManager->get_widget("/MenuBar/MenuMacro")
1627 )->get_submenu();
1628 #endif
1629
1630 // remove all entries from "Macro" menu
1631 {
1632 const std::vector<Gtk::Widget*> children = menuMacro->get_children();
1633 for (int i = 0; i < children.size(); ++i) {
1634 Gtk::Widget* child = children[i];
1635 menuMacro->remove(*child);
1636 delete child;
1637 }
1638 }
1639
1640 // (re)load all macros from config file
1641 try {
1642 Settings::singleton()->loadMacros(m_macros);
1643 } catch (Serialization::Exception e) {
1644 std::cerr << "Exception while loading macros: " << e.Message << std::endl;
1645 } catch (...) {
1646 std::cerr << "Unknown exception while loading macros!" << std::endl;
1647 }
1648
1649 // add all configured macros as menu items to the "Macro" menu
1650 for (int iMacro = 0; iMacro < m_macros.size(); ++iMacro) {
1651 const Serialization::Archive& macro = m_macros[iMacro];
1652 std::string name =
1653 macro.name().empty() ?
1654 (std::string(_("Unnamed Macro")) + " " + ToString(iMacro+1)) : macro.name();
1655 Gtk::MenuItem* item = new Gtk::MenuItem(name);
1656 item->signal_activate().connect(
1657 sigc::bind(
1658 sigc::mem_fun(*this, &MainWindow::onMacroSelected), iMacro
1659 )
1660 );
1661 menuMacro->append(*item);
1662 item->set_accel_path("<Macros>/macro_" + ToString(iMacro));
1663 Glib::ustring comment = macro.comment();
1664 if (!comment.empty())
1665 item->set_tooltip_text(comment);
1666 }
1667 // if there are no macros configured at all, then show a dummy entry instead
1668 if (m_macros.empty()) {
1669 Gtk::MenuItem* item = new Gtk::MenuItem(_("No Macros"));
1670 item->set_sensitive(false);
1671 menuMacro->append(*item);
1672 }
1673
1674 // add separator line to menu
1675 menuMacro->append(*new Gtk::SeparatorMenuItem);
1676
1677 {
1678 Gtk::MenuItem* item = new Gtk::MenuItem(_("Setup Macros ..."));
1679 item->signal_activate().connect(
1680 sigc::mem_fun(*this, &MainWindow::setupMacros)
1681 );
1682 menuMacro->append(*item);
1683 item->set_accel_path("<Macros>/SetupMacros");
1684 }
1685
1686 #if HAS_GTKMM_SHOW_ALL_CHILDREN
1687 menuMacro->show_all_children();
1688 #endif
1689 }
1690
1691 void MainWindow::onMacroSelected(int iMacro) {
1692 printf("onMacroSelected(%d)\n", iMacro);
1693 if (iMacro < 0 || iMacro >= m_macros.size()) return;
1694 Glib::ustring errorText;
1695 try {
1696 applyMacro(m_macros[iMacro]);
1697 } catch (Serialization::Exception e) {
1698 errorText = e.Message;
1699 } catch (...) {
1700 errorText = _("Unknown exception while applying macro");
1701 }
1702 if (!errorText.empty()) {
1703 Glib::ustring txt = _("Applying macro failed:\n") + errorText;
1704 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1705 msg.run();
1706 }
1707 }
1708
1709 void MainWindow::setupMacros() {
1710 MacrosSetup* setup = new MacrosSetup();
1711 gig::DimensionRegion* pDimRgn = m_DimRegionChooser.get_main_dimregion();
1712 setup->setMacros(m_macros, &m_serializationArchive, pDimRgn);
1713 setup->signal_macros_changed().connect(
1714 sigc::mem_fun(*this, &MainWindow::onMacrosSetupChanged)
1715 );
1716 setup->show();
1717 }
1718
1719 void MainWindow::onMacrosSetupChanged(const std::vector<Serialization::Archive>& macros) {
1720 m_macros = macros;
1721 Settings::singleton()->saveMacros(m_macros);
1722 updateMacroMenu();
1723 }
1724
1725 //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.
1726 void MainWindow::on_notebook_tab_switched(void* page, guint page_num) {
1727 bool isInstrumentsPage = (page_num == 1);
1728 // so far we only support filtering for the instruments list, so hide the
1729 // filter text entry field if another tab is selected
1730 m_searchField.set_visible(isInstrumentsPage);
1731 }
1732
1733 bool MainWindow::on_delete_event(GdkEventAny* event)
1734 {
1735 return !file_is_shared && file_is_changed && !close_confirmation_dialog();
1736 }
1737
1738 void MainWindow::on_action_quit()
1739 {
1740 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
1741 hide();
1742 }
1743
1744 void MainWindow::region_changed()
1745 {
1746 m_DimRegionChooser.set_region(m_RegionChooser.get_region());
1747 }
1748
1749 gig::Instrument* MainWindow::get_instrument()
1750 {
1751 gig::Instrument* instrument = 0;
1752 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
1753 if (rows.empty()) return NULL;
1754 //NOTE: was const_iterator before, which did not compile with GTKMM4 development branch, probably going to be fixed before final GTKMM4 release though.
1755 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
1756 if (it) {
1757 Gtk::TreeModel::Row row = *it;
1758 instrument = row[m_Columns.m_col_instr];
1759 }
1760 return instrument;
1761 }
1762
1763 void MainWindow::add_region_to_dimregs(gig::Region* region, bool stereo, bool all_dimregs)
1764 {
1765 if (all_dimregs) {
1766 for (int i = 0 ; i < region->DimensionRegions ; i++) {
1767 if (region->pDimensionRegions[i]) {
1768 dimreg_edit.dimregs.insert(region->pDimensionRegions[i]);
1769 }
1770 }
1771 } else {
1772 m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
1773 }
1774 }
1775
1776 void MainWindow::update_dimregs()
1777 {
1778 dimreg_edit.dimregs.clear();
1779 bool all_regions = dimreg_all_regions.get_active();
1780 bool stereo = dimreg_stereo.get_active();
1781 bool all_dimregs = dimreg_all_dimregs.get_active();
1782
1783 if (all_regions) {
1784 gig::Instrument* instrument = get_instrument();
1785 if (instrument) {
1786 for (gig::Region* region = instrument->GetFirstRegion() ;
1787 region ;
1788 region = instrument->GetNextRegion()) {
1789 add_region_to_dimregs(region, stereo, all_dimregs);
1790 }
1791 }
1792 } else {
1793 gig::Region* region = m_RegionChooser.get_region();
1794 if (region) {
1795 add_region_to_dimregs(region, stereo, all_dimregs);
1796 }
1797 }
1798
1799 m_RegionChooser.setModifyAllRegions(all_regions);
1800 m_DimRegionChooser.setModifyAllRegions(all_regions);
1801 m_DimRegionChooser.setModifyAllDimensionRegions(all_dimregs);
1802 m_DimRegionChooser.setModifyBothChannels(stereo);
1803
1804 updateClipboardCopyAvailable();
1805 }
1806
1807 void MainWindow::dimreg_all_dimregs_toggled()
1808 {
1809 dimreg_stereo.set_sensitive(!dimreg_all_dimregs.get_active());
1810 update_dimregs();
1811 }
1812
1813 void MainWindow::dimreg_changed()
1814 {
1815 update_dimregs();
1816 dimreg_edit.set_dim_region(m_DimRegionChooser.get_main_dimregion());
1817 }
1818
1819 void MainWindow::on_sel_change()
1820 {
1821 #if !USE_GTKMM_BUILDER
1822 // select item in instrument menu
1823 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
1824 if (!rows.empty()) {
1825 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
1826 if (it) {
1827 Gtk::TreePath path(it);
1828 int index = path[0];
1829 const std::vector<Gtk::Widget*> children =
1830 instrument_menu->get_children();
1831 static_cast<Gtk::RadioMenuItem*>(children[index])->set_active();
1832 }
1833 }
1834 #endif
1835
1836 updateScriptListOfMenu();
1837
1838 m_RegionChooser.set_instrument(get_instrument());
1839
1840 if (Settings::singleton()->syncSamplerInstrumentSelection) {
1841 switch_sampler_instrument_signal.emit(get_instrument());
1842 }
1843 }
1844
1845 void loader_progress_callback(gig::progress_t* progress)
1846 {
1847 Loader* loader = static_cast<Loader*>(progress->custom);
1848 loader->progress_callback(progress->factor);
1849 }
1850
1851 void Loader::progress_callback(float fraction)
1852 {
1853 {
1854 Glib::Threads::Mutex::Lock lock(progressMutex);
1855 progress = fraction;
1856 }
1857 progress_dispatcher();
1858 }
1859
1860 #if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
1861 // make sure stack is 16-byte aligned for SSE instructions
1862 __attribute__((force_align_arg_pointer))
1863 #endif
1864 void Loader::thread_function()
1865 {
1866 printf("thread_function self=%p\n",
1867 static_cast<void*>(Glib::Threads::Thread::self()));
1868 printf("Start %s\n", filename.c_str());
1869 try {
1870 RIFF::File* riff = new RIFF::File(filename);
1871 gig = new gig::File(riff);
1872 gig::progress_t progress;
1873 progress.callback = loader_progress_callback;
1874 progress.custom = this;
1875
1876 gig->GetInstrument(0, &progress);
1877 printf("End\n");
1878 finished_dispatcher();
1879 } catch (RIFF::Exception e) {
1880 error_message = e.Message;
1881 error_dispatcher.emit();
1882 } catch (...) {
1883 error_message = _("Unknown exception occurred");
1884 error_dispatcher.emit();
1885 }
1886 }
1887
1888 Loader::Loader(const char* filename)
1889 : filename(filename), gig(0), thread(0), progress(0.f)
1890 {
1891 }
1892
1893 void Loader::launch()
1894 {
1895 #ifdef OLD_THREADS
1896 thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true);
1897 #else
1898 thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Loader::thread_function));
1899 #endif
1900 printf("launch thread=%p\n", static_cast<void*>(thread));
1901 }
1902
1903 float Loader::get_progress()
1904 {
1905 float res;
1906 {
1907 Glib::Threads::Mutex::Lock lock(progressMutex);
1908 res = progress;
1909 }
1910 return res;
1911 }
1912
1913 Glib::Dispatcher& Loader::signal_progress()
1914 {
1915 return progress_dispatcher;
1916 }
1917
1918 Glib::Dispatcher& Loader::signal_finished()
1919 {
1920 return finished_dispatcher;
1921 }
1922
1923 Glib::Dispatcher& Loader::signal_error()
1924 {
1925 return error_dispatcher;
1926 }
1927
1928 void saver_progress_callback(gig::progress_t* progress)
1929 {
1930 Saver* saver = static_cast<Saver*>(progress->custom);
1931 saver->progress_callback(progress->factor);
1932 }
1933
1934 void Saver::progress_callback(float fraction)
1935 {
1936 {
1937 Glib::Threads::Mutex::Lock lock(progressMutex);
1938 progress = fraction;
1939 }
1940 progress_dispatcher.emit();
1941 }
1942
1943 #if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
1944 // make sure stack is 16-byte aligned for SSE instructions
1945 __attribute__((force_align_arg_pointer))
1946 #endif
1947 void Saver::thread_function()
1948 {
1949 printf("thread_function self=%p\n",
1950 static_cast<void*>(Glib::Threads::Thread::self()));
1951 printf("Start %s\n", filename.c_str());
1952 try {
1953 gig::progress_t progress;
1954 progress.callback = saver_progress_callback;
1955 progress.custom = this;
1956
1957 // if no filename was provided, that means "save", if filename was provided means "save as"
1958 if (filename.empty()) {
1959 if (!Settings::singleton()->saveWithTemporaryFile) {
1960 // save directly over the existing .gig file
1961 // (requires less disk space than solution below
1962 // but may be slower)
1963 gig->Save(&progress);
1964 } else {
1965 // save the file as separate temporary file first,
1966 // then move the saved file over the old file
1967 // (may result in performance speedup during save)
1968 gig::String tmpname = filename + ".TMP";
1969 gig->Save(tmpname, &progress);
1970 #if defined(WIN32)
1971 if (!DeleteFile(filename.c_str())) {
1972 throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file).");
1973 }
1974 #else // POSIX ...
1975 if (unlink(filename.c_str())) {
1976 throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file): " + gig::String(strerror(errno)));
1977 }
1978 #endif
1979 if (rename(tmpname.c_str(), filename.c_str())) {
1980 #if defined(WIN32)
1981 throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file).");
1982 #else
1983 throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file): " + gig::String(strerror(errno)));
1984 #endif
1985 }
1986 }
1987 } else {
1988 gig->Save(filename, &progress);
1989 }
1990
1991 printf("End\n");
1992 finished_dispatcher.emit();
1993 } catch (RIFF::Exception e) {
1994 error_message = e.Message;
1995 error_dispatcher.emit();
1996 } catch (...) {
1997 error_message = _("Unknown exception occurred");
1998 error_dispatcher.emit();
1999 }
2000 }
2001
2002 Saver::Saver(gig::File* file, Glib::ustring filename)
2003 : gig(file), filename(filename), thread(0), progress(0.f)
2004 {
2005 }
2006
2007 void Saver::launch()
2008 {
2009 #ifdef OLD_THREADS
2010 thread = Glib::Thread::create(sigc::mem_fun(*this, &Saver::thread_function), true);
2011 #else
2012 thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Saver::thread_function));
2013 #endif
2014 printf("launch thread=%p\n", static_cast<void*>(thread));
2015 }
2016
2017 float Saver::get_progress()
2018 {
2019 float res;
2020 {
2021 Glib::Threads::Mutex::Lock lock(progressMutex);
2022 res = progress;
2023 }
2024 return res;
2025 }
2026
2027 Glib::Dispatcher& Saver::signal_progress()
2028 {
2029 return progress_dispatcher;
2030 }
2031
2032 Glib::Dispatcher& Saver::signal_finished()
2033 {
2034 return finished_dispatcher;
2035 }
2036
2037 Glib::Dispatcher& Saver::signal_error()
2038 {
2039 return error_dispatcher;
2040 }
2041
2042 ProgressDialog::ProgressDialog(const Glib::ustring& title, Gtk::Window& parent)
2043 : Gtk::Dialog(title, parent, true)
2044 {
2045 #if USE_GTKMM_BOX
2046 get_content_area()->pack_start(progressBar);
2047 #else
2048 get_vbox()->pack_start(progressBar);
2049 #endif
2050 #if HAS_GTKMM_SHOW_ALL_CHILDREN
2051 show_all_children();
2052 #endif
2053 resize(600,50);
2054 }
2055
2056 // Clear all GUI elements / controls. This method is typically called
2057 // before a new .gig file is to be created or to be loaded.
2058 void MainWindow::__clear() {
2059 // forget all samples that ought to be imported
2060 m_SampleImportQueue.clear();
2061 // clear the samples and instruments tree views
2062 m_refTreeModel->clear();
2063 m_refSamplesTreeModel->clear();
2064 m_refScriptsTreeModel->clear();
2065 #if !USE_GTKMM_BUILDER
2066 // remove all entries from "Instrument" menu
2067 while (!instrument_menu->get_children().empty()) {
2068 remove_instrument_from_menu(0);
2069 }
2070 #endif
2071 // free libgig's gig::File instance
2072 if (file && !file_is_shared) delete file;
2073 file = NULL;
2074 set_file_is_shared(false);
2075 }
2076
2077 void MainWindow::__refreshEntireGUI() {
2078 // clear the samples and instruments tree views
2079 m_refTreeModel->clear();
2080 m_refSamplesTreeModel->clear();
2081 m_refScriptsTreeModel->clear();
2082 #if !USE_GTKMM_BUILDER
2083 // remove all entries from "Instrument" menu
2084 while (!instrument_menu->get_children().empty()) {
2085 remove_instrument_from_menu(0);
2086 }
2087 #endif
2088
2089 if (!this->file) return;
2090
2091 load_gig(
2092 this->file, this->file->pInfo->Name.c_str(), this->file_is_shared
2093 );
2094 }
2095
2096 void MainWindow::on_action_file_new()
2097 {
2098 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
2099
2100 if (file_is_shared && !leaving_shared_mode_dialog()) return;
2101
2102 // clear all GUI elements
2103 __clear();
2104 // create a new .gig file (virtually yet)
2105 gig::File* pFile = new gig::File;
2106 // already add one new instrument by default
2107 gig::Instrument* pInstrument = pFile->AddInstrument();
2108 pInstrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument"));
2109 // update GUI with that new gig::File
2110 load_gig(pFile, 0 /*no file name yet*/);
2111 }
2112
2113 bool MainWindow::close_confirmation_dialog()
2114 {
2115 gchar* msg = g_strdup_printf(_("Save changes to \"%s\" before closing?"),
2116 Glib::filename_display_basename(filename).c_str());
2117 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
2118 g_free(msg);
2119 dialog.set_secondary_text(_("If you close without saving, your changes will be lost."));
2120 dialog.add_button(_("Close _Without Saving"), Gtk::RESPONSE_NO);
2121 #if HAS_GTKMM_STOCK
2122 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2123 dialog.add_button(file_has_name ? Gtk::Stock::SAVE : Gtk::Stock::SAVE_AS, Gtk::RESPONSE_YES);
2124 #else
2125 dialog.add_button(_("_OK"), Gtk::RESPONSE_OK);
2126 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2127 #endif
2128 dialog.set_default_response(Gtk::RESPONSE_YES);
2129 int response = dialog.run();
2130 dialog.hide();
2131
2132 // user decided to exit app without saving
2133 if (response == Gtk::RESPONSE_NO) return true;
2134
2135 // user cancelled dialog, thus don't close app
2136 if (response == Gtk::RESPONSE_CANCEL) return false;
2137
2138 // TODO: the following return valid is disabled and hard coded instead for
2139 // now, due to the fact that saving with progress bar is now implemented
2140 // asynchronously, as a result the app does not close automatically anymore
2141 // after saving the file has completed
2142 //
2143 // if (response == Gtk::RESPONSE_YES) return file_save();
2144 // return response != Gtk::RESPONSE_CANCEL;
2145 //
2146 if (response == Gtk::RESPONSE_YES) file_save();
2147 return false; // always prevent closing the app for now (see comment above)
2148 }
2149
2150 bool MainWindow::leaving_shared_mode_dialog() {
2151 Glib::ustring msg = _("Detach from sampler and proceed working stand-alone?");
2152 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
2153 dialog.set_secondary_text(
2154 _("If you proceed to work on another instrument file, it won't be "
2155 "used by the sampler until you tell the sampler explicitly to "
2156 "load it."));
2157 dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
2158 #if HAS_GTKMM_STOCK
2159 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2160 #else
2161 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2162 #endif
2163 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
2164 int response = dialog.run();
2165 dialog.hide();
2166 return response == Gtk::RESPONSE_YES;
2167 }
2168
2169 void MainWindow::on_action_file_open()
2170 {
2171 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
2172
2173 if (file_is_shared && !leaving_shared_mode_dialog()) return;
2174
2175 Gtk::FileChooserDialog dialog(*this, _("Open file"));
2176 #if HAS_GTKMM_STOCK
2177 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2178 dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
2179 #else
2180 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2181 dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
2182 #endif
2183 dialog.set_default_response(Gtk::RESPONSE_OK);
2184 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
2185 Gtk::FileFilter filter;
2186 filter.add_pattern("*.gig");
2187 #else
2188 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
2189 filter->add_pattern("*.gig");
2190 #endif
2191 dialog.set_filter(filter);
2192 if (current_gig_dir != "") {
2193 dialog.set_current_folder(current_gig_dir);
2194 }
2195 if (dialog.run() == Gtk::RESPONSE_OK) {
2196 std::string filename = dialog.get_filename();
2197 printf("filename=%s\n", filename.c_str());
2198 printf("on_action_file_open self=%p\n",
2199 static_cast<void*>(Glib::Threads::Thread::self()));
2200 load_file(filename.c_str());
2201 current_gig_dir = Glib::path_get_dirname(filename);
2202 }
2203 }
2204
2205 void MainWindow::load_file(const char* name)
2206 {
2207 __clear();
2208
2209 progress_dialog = new ProgressDialog( //FIXME: memory leak!
2210 _("Loading") + Glib::ustring(" '") +
2211 Glib::filename_display_basename(name) + "' ...",
2212 *this
2213 );
2214 #if HAS_GTKMM_SHOW_ALL_CHILDREN
2215 progress_dialog->show_all();
2216 #endif
2217 loader = new Loader(name); //FIXME: memory leak!
2218 loader->signal_progress().connect(
2219 sigc::mem_fun(*this, &MainWindow::on_loader_progress));
2220 loader->signal_finished().connect(
2221 sigc::mem_fun(*this, &MainWindow::on_loader_finished));
2222 loader->signal_error().connect(
2223 sigc::mem_fun(*this, &MainWindow::on_loader_error));
2224 loader->launch();
2225 }
2226
2227 void MainWindow::load_instrument(gig::Instrument* instr) {
2228 if (!instr) {
2229 Glib::ustring txt = "Provided instrument is NULL!\n";
2230 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
2231 msg.run();
2232 Gtk::Main::quit();
2233 }
2234 // clear all GUI elements
2235 __clear();
2236 // load the instrument
2237 gig::File* pFile = (gig::File*) instr->GetParent();
2238 load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
2239 // automatically select the given instrument
2240 int i = 0;
2241 for (gig::Instrument* instrument = pFile->GetFirstInstrument(); instrument;
2242 instrument = pFile->GetNextInstrument(), ++i)
2243 {
2244 if (instrument == instr) {
2245 // select item in "instruments" tree view
2246 m_TreeView.get_selection()->select(Gtk::TreePath(ToString(i)));
2247 // make sure the selected item in the "instruments" tree view is
2248 // visible (scroll to it)
2249 m_TreeView.scroll_to_row(Gtk::TreePath(ToString(i)));
2250 #if !USE_GTKMM_BUILDER
2251 // select item in instrument menu
2252 {
2253 const std::vector<Gtk::Widget*> children =
2254 instrument_menu->get_children();
2255 static_cast<Gtk::RadioMenuItem*>(children[i])->set_active();
2256 }
2257 #endif
2258 // update region chooser and dimension region chooser
2259 m_RegionChooser.set_instrument(instr);
2260 break;
2261 }
2262 }
2263 }
2264
2265 void MainWindow::on_loader_progress()
2266 {
2267 progress_dialog->set_fraction(loader->get_progress());
2268 }
2269
2270 void MainWindow::on_loader_finished()
2271 {
2272 printf("Loader finished!\n");
2273 printf("on_loader_finished self=%p\n",
2274 static_cast<void*>(Glib::Threads::Thread::self()));
2275 load_gig(loader->gig, loader->filename.c_str());
2276 progress_dialog->hide();
2277 }
2278
2279 void MainWindow::on_loader_error()
2280 {
2281 Glib::ustring txt = _("Could not load file: ") + loader->error_message;
2282 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
2283 msg.run();
2284 progress_dialog->hide();
2285 }
2286
2287 void MainWindow::on_action_file_save()
2288 {
2289 file_save();
2290 }
2291
2292 bool MainWindow::check_if_savable()
2293 {
2294 if (!file) return false;
2295
2296 if (!file->GetFirstSample()) {
2297 Gtk::MessageDialog(*this, _("The file could not be saved "
2298 "because it contains no samples"),
2299 false, Gtk::MESSAGE_ERROR).run();
2300 return false;
2301 }
2302
2303 for (gig::Instrument* instrument = file->GetFirstInstrument() ; instrument ;
2304 instrument = file->GetNextInstrument()) {
2305 if (!instrument->GetFirstRegion()) {
2306 Gtk::MessageDialog(*this, _("The file could not be saved "
2307 "because there are instruments "
2308 "that have no regions"),
2309 false, Gtk::MESSAGE_ERROR).run();
2310 return false;
2311 }
2312 }
2313 return true;
2314 }
2315
2316 bool MainWindow::file_save()
2317 {
2318 if (!check_if_savable()) return false;
2319 if (!file_is_shared && !file_has_name) return file_save_as();
2320
2321 std::cout << "Saving file\n" << std::flush;
2322 file_structure_to_be_changed_signal.emit(this->file);
2323
2324 progress_dialog = new ProgressDialog( //FIXME: memory leak!
2325 _("Saving") + Glib::ustring(" '") +
2326 Glib::filename_display_basename(this->filename) + "' ...",
2327 *this
2328 );
2329 #if HAS_GTKMM_SHOW_ALL_CHILDREN
2330 progress_dialog->show_all();
2331 #endif
2332 saver = new Saver(this->file); //FIXME: memory leak!
2333 saver->signal_progress().connect(
2334 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
2335 saver->signal_finished().connect(
2336 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
2337 saver->signal_error().connect(
2338 sigc::mem_fun(*this, &MainWindow::on_saver_error));
2339 saver->launch();
2340
2341 return true;
2342 }
2343
2344 void MainWindow::on_saver_progress()
2345 {
2346 progress_dialog->set_fraction(saver->get_progress());
2347 }
2348
2349 void MainWindow::on_saver_error()
2350 {
2351 file_structure_changed_signal.emit(this->file);
2352 Glib::ustring txt = _("Could not save file: ") + saver->error_message;
2353 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
2354 msg.run();
2355 }
2356
2357 void MainWindow::on_saver_finished()
2358 {
2359 this->file = saver->gig;
2360 this->filename = saver->filename;
2361 current_gig_dir = Glib::path_get_dirname(filename);
2362 set_title(Glib::filename_display_basename(filename));
2363 file_has_name = true;
2364 file_is_changed = false;
2365 std::cout << "Saving file done. Importing queued samples now ...\n" << std::flush;
2366 __import_queued_samples();
2367 std::cout << "Importing queued samples done.\n" << std::flush;
2368
2369 file_structure_changed_signal.emit(this->file);
2370
2371 __refreshEntireGUI();
2372 progress_dialog->hide();
2373 }
2374
2375 void MainWindow::on_action_file_save_as()
2376 {
2377 if (!check_if_savable()) return;
2378 file_save_as();
2379 }
2380
2381 bool MainWindow::file_save_as()
2382 {
2383 Gtk::FileChooserDialog dialog(*this, _("Save as"), Gtk::FILE_CHOOSER_ACTION_SAVE);
2384 #if HAS_GTKMM_STOCK
2385 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2386 dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
2387 #else
2388 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
2389 dialog.add_button(_("_Save"), Gtk::RESPONSE_OK);
2390 #endif
2391 dialog.set_default_response(Gtk::RESPONSE_OK);
2392 dialog.set_do_overwrite_confirmation();
2393
2394 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
2395 Gtk::FileFilter filter;
2396 filter.add_pattern("*.gig");
2397 #else
2398 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
2399 filter->add_pattern("*.gig");
2400 #endif
2401 dialog.set_filter(filter);
2402
2403 // set initial dir and filename of the Save As dialog
2404 // and prepare that initial filename as a copy of the gig
2405 {
2406 std::string basename = Glib::path_get_basename(filename);
2407 std::string dir = Glib::path_get_dirname(filename);
2408 basename = std::string(_("copy_of_")) + basename;
2409 Glib::ustring copyFileName = Glib::build_filename(dir, basename);
2410 if (Glib::path_is_absolute(filename)) {
2411 dialog.set_filename(copyFileName);
2412 } else {
2413 if (current_gig_dir != "") dialog.set_current_folder(current_gig_dir);
2414 }
2415 dialog.set_current_name(Glib::filename_display_basename(copyFileName));
2416 }
2417
2418 // show warning in the dialog
2419 HBox descriptionArea;
2420 descriptionArea.set_spacing(15);
2421 Gtk::Image warningIcon;
2422 warningIcon.set_from_icon_name("dialog-warning",
2423 Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
2424 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
2425 #if GTKMM_MAJOR_VERSION < 3
2426 view::WrapLabel description;
2427 #else
2428 Gtk::Label description;
2429 description.set_line_wrap();
2430 #endif
2431 description.set_markup(
2432 _("\n<b>CAUTION:</b> You <b>MUST</b> use the "
2433 "<span style=\"italic\">\"Save\"</span> dialog instead of "
2434 "<span style=\"italic\">\"Save As...\"</span> if you want to save "
2435 "to the same .gig file. Using "
2436 "<span style=\"italic\">\"Save As...\"</span> for writing to the "
2437 "same .gig file will end up in corrupted sample wave data!\n")
2438 );
2439 descriptionArea.pack_start(description);
2440 #if USE_GTKMM_BOX
2441 dialog.get_content_area()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
2442 #else
2443 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
2444 #endif
2445 #if HAS_GTKMM_SHOW_ALL_CHILDREN
2446 descriptionArea.show_all();
2447 #endif
2448
2449 if (dialog.run() == Gtk::RESPONSE_OK) {
2450 std::string filename = dialog.get_filename();
2451 if (!Glib::str_has_suffix(filename, ".gig")) {
2452 filename += ".gig";
2453 }
2454 printf("filename=%s\n", filename.c_str());
2455
2456 progress_dialog = new ProgressDialog( //FIXME: memory leak!
2457 _("Saving") + Glib::ustring(" '") +
2458 Glib::filename_display_basename(filename) + "' ...",
2459 *this
2460 );
2461 #if HAS_GTKMM_SHOW_ALL_CHILDREN
2462 progress_dialog->show_all();
2463 #endif
2464
2465 saver = new Saver(file, filename); //FIXME: memory leak!
2466 saver->signal_progress().connect(
2467 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
2468 saver->signal_finished().connect(
2469 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
2470 saver->signal_error().connect(
2471 sigc::mem_fun(*this, &MainWindow::on_saver_error));
2472 saver->launch();
2473
2474 return true;
2475 }
2476 return false;
2477 }
2478
2479 // actually write the sample(s)' data to the gig file
2480 void MainWindow::__import_queued_samples() {
2481 std::cout << "Starting sample import\n" << std::flush;
2482 Glib::ustring error_files;
2483 printf("Samples to import: %d\n", int(m_SampleImportQueue.size()));
2484 for (std::map<gig::Sample*, SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
2485 iter != m_SampleImportQueue.end(); ) {
2486 printf("Importing sample %s\n",iter->second.sample_path.c_str());
2487 SF_INFO info;
2488 info.format = 0;
2489 SNDFILE* hFile = sf_open(iter->second.sample_path.c_str(), SFM_READ, &info);
2490 sf_command(hFile, SFC_SET_SCALE_FLOAT_INT_READ, 0, SF_TRUE);
2491 try {
2492 if (!hFile) throw std::string(_("could not open file"));
2493 // determine sample's bit depth
2494 int bitdepth;
2495 switch (info.format & 0xff) {
2496 case SF_FORMAT_PCM_S8:
2497 case SF_FORMAT_PCM_16:
2498 case SF_FORMAT_PCM_U8:
2499 bitdepth = 16;
2500 break;
2501 case SF_FORMAT_PCM_24:
2502 case SF_FORMAT_PCM_32:
2503 case SF_FORMAT_FLOAT:
2504 case SF_FORMAT_DOUBLE:
2505 bitdepth = 24;
2506 break;
2507 default:
2508 sf_close(hFile); // close sound file
2509 throw std::string(_("format not supported")); // unsupported subformat (yet?)
2510 }
2511
2512 // reset write position for sample
2513 iter->first->SetPos(0);
2514
2515 const int bufsize = 10000;
2516 switch (bitdepth) {
2517 case 16: {
2518 short* buffer = new short[bufsize * info.channels];
2519 sf_count_t cnt = info.frames;
2520 while (cnt) {
2521 // libsndfile does the conversion for us (if needed)
2522 int n = sf_readf_short(hFile, buffer, bufsize);
2523 // write from buffer directly (physically) into .gig file
2524 iter->first->Write(buffer, n);
2525 cnt -= n;
2526 }
2527 delete[] buffer;
2528 break;
2529 }
2530 case 24: {
2531 int* srcbuf = new int[bufsize * info.channels];
2532 uint8_t* dstbuf = new uint8_t[bufsize * 3 * info.channels];
2533 sf_count_t cnt = info.frames;
2534 while (cnt) {
2535 // libsndfile returns 32 bits, convert to 24
2536 int n = sf_readf_int(hFile, srcbuf, bufsize);
2537 int j = 0;
2538 for (int i = 0 ; i < n * info.channels ; i++) {
2539 dstbuf[j++] = srcbuf[i] >> 8;
2540 dstbuf[j++] = srcbuf[i] >> 16;
2541 dstbuf[j++] = srcbuf[i] >> 24;
2542 }
2543 // write from buffer directly (physically) into .gig file
2544 iter->first->Write(dstbuf, n);
2545 cnt -= n;
2546 }
2547 delete[] srcbuf;
2548 delete[] dstbuf;
2549 break;
2550 }
2551 }
2552 // cleanup
2553 sf_close(hFile);
2554 // let the sampler re-cache the sample if needed
2555 sample_changed_signal.emit(iter->first);
2556 // on success we remove the sample from the import queue,
2557 // otherwise keep it, maybe it works the next time ?
2558 std::map<gig::Sample*, SampleImportItem>::iterator cur = iter;
2559 ++iter;
2560 m_SampleImportQueue.erase(cur);
2561 } catch (std::string what) {
2562 // remember the files that made trouble (and their cause)
2563 if (!error_files.empty()) error_files += "\n";
2564 error_files += iter->second.sample_path += " (" + what + ")";
2565 ++iter;
2566 }
2567 }
2568 // show error message box when some sample(s) could not be imported
2569 if (!error_files.empty()) {
2570 Glib::ustring txt = _("Could not import the following sample(s):\n") + error_files;
2571 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
2572 msg.run();
2573 }
2574 }
2575
2576 void MainWindow::on_action_file_properties()
2577 {
2578 propDialog.show();
2579 propDialog.deiconify();
2580 }
2581
2582 void MainWindow::on_action_warn_user_on_extensions() {
2583 Settings::singleton()->warnUserOnExtensions =
2584 !Settings::singleton()->warnUserOnExtensions;
2585 }
2586
2587 void MainWindow::on_action_sync_sampler_instrument_selection() {
2588 Settings::singleton()->syncSamplerInstrumentSelection =
2589 !Settings::singleton()->syncSamplerInstrumentSelection;
2590 }
2591
2592 void MainWindow::on_action_move_root_note_with_region_moved() {
2593 Settings::singleton()->moveRootNoteWithRegionMoved =
2594 !Settings::singleton()->moveRootNoteWithRegionMoved;
2595 }
2596
2597 void MainWindow::on_action_help_about()
2598 {
2599 Gtk::AboutDialog dialog;
2600 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 12) || GTKMM_MAJOR_VERSION > 2
2601 dialog.set_program_name("Gigedit");
2602 #else
2603 dialog.set_name("Gigedit");
2604 #endif
2605 dialog.set_version(VERSION);
2606 dialog.set_copyright("Copyright (C) 2006-2017 Andreas Persson");
2607 const std::string sComment =
2608 _("Built " __DATE__ "\nUsing ") +
2609 ::gig::libraryName() + " " + ::gig::libraryVersion() + "\n\n" +
2610 _(
2611 "Gigedit is released under the GNU General Public License.\n"
2612 "\n"
2613 "This program is distributed WITHOUT ANY WARRANTY; So better "
2614 "backup your Gigasampler/GigaStudio files before editing them with "
2615 "this application.\n"
2616 "\n"
2617 "Please report bugs to: http://bugs.linuxsampler.org"
2618 );
2619 dialog.set_comments(sComment.c_str());
2620 dialog.set_website("http://www.linuxsampler.org");
2621 dialog.set_website_label("http://www.linuxsampler.org");
2622 dialog.set_position(Gtk::WIN_POS_CENTER);
2623 dialog.run();
2624 }
2625
2626 PropDialog::PropDialog()
2627 : eFileFormat(_("File Format")),
2628 eName(_("Name")),
2629 eCreationDate(_("Creation date")),
2630 eComments(_("Comments")),
2631 eProduct(_("Product")),
2632 eCopyright(_("Copyright")),
2633 eArtists(_("Artists")),
2634 eGenre(_("Genre")),
2635 eKeywords(_("Keywords")),
2636 eEngineer(_("Engineer")),
2637 eTechnician(_("Technician")),
2638 eSoftware(_("Software")),
2639 eMedium(_("Medium")),
2640 eSource(_("Source")),
2641 eSourceForm(_("Source form")),
2642 eCommissioned(_("Commissioned")),
2643 eSubject(_("Subject")),
2644 #if HAS_GTKMM_STOCK
2645 quitButton(Gtk::Stock::CLOSE),
2646 #else
2647 quitButton(_("_Close")),
2648 #endif
2649 table(2, 1),
2650 m_file(NULL)
2651 {
2652 if (!Settings::singleton()->autoRestoreWindowDimension) {
2653 set_default_size(470, 390);
2654 set_position(Gtk::WIN_POS_MOUSE);
2655 }
2656
2657 set_title(_("File Properties"));
2658 eName.set_width_chars(50);
2659
2660 connect(eName, &DLS::Info::Name);
2661 connect(eCreationDate, &DLS::Info::CreationDate);
2662 connect(eComments, &DLS::Info::Comments);
2663 connect(eProduct, &DLS::Info::Product);
2664 connect(eCopyright, &DLS::Info::Copyright);
2665 connect(eArtists, &DLS::Info::Artists);
2666 connect(eGenre, &DLS::Info::Genre);
2667 connect(eKeywords, &DLS::Info::Keywords);
2668 connect(eEngineer, &DLS::Info::Engineer);
2669 connect(eTechnician, &DLS::Info::Technician);
2670 connect(eSoftware, &DLS::Info::Software);
2671 connect(eMedium, &DLS::Info::Medium);
2672 connect(eSource, &DLS::Info::Source);
2673 connect(eSourceForm, &DLS::Info::SourceForm);
2674 connect(eCommissioned, &DLS::Info::Commissioned);
2675 connect(eSubject, &DLS::Info::Subject);
2676
2677 table.add(eFileFormat);
2678 table.add(eName);
2679 table.add(eCreationDate);
2680 table.add(eComments);
2681 table.add(eProduct);
2682 table.add(eCopyright);
2683 table.add(eArtists);
2684 table.add(eGenre);
2685 table.add(eKeywords);
2686 table.add(eEngineer);
2687 table.add(eTechnician);
2688 table.add(eSoftware);
2689 table.add(eMedium);
2690 table.add(eSource);
2691 table.add(eSourceForm);
2692 table.add(eCommissioned);
2693 table.add(eSubject);
2694
2695 #if USE_GTKMM_GRID
2696 table.set_column_spacing(5);
2697 #else
2698 table.set_col_spacings(5);
2699 #endif
2700
2701 add(vbox);
2702 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
2703 table.set_margin(5);
2704 #else
2705 table.set_border_width(5);
2706 #endif
2707 vbox.add(table);
2708 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
2709 buttonBox.set_layout(Gtk::BUTTONBOX_END);
2710 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
2711 buttonBox.set_margin(5);
2712 #else
2713 buttonBox.set_border_width(5);
2714 #endif
2715 buttonBox.show();
2716 buttonBox.pack_start(quitButton);
2717 quitButton.set_can_default();
2718 quitButton.grab_focus();
2719 quitButton.signal_clicked().connect(
2720 sigc::mem_fun(*this, &PropDialog::hide));
2721 eFileFormat.signal_value_changed().connect(
2722 sigc::mem_fun(*this, &PropDialog::onFileFormatChanged));
2723
2724 quitButton.show();
2725 vbox.show();
2726 #if HAS_GTKMM_SHOW_ALL_CHILDREN
2727 show_all_children();
2728 #endif
2729 }
2730
2731 void PropDialog::set_file(gig::File* file)
2732 {
2733 m_file = file;
2734
2735 // update file format version combo box
2736 const std::string sGiga = "Gigasampler/GigaStudio v";
2737 const int major = file->pVersion->major;
2738 std::vector<std::string> txts;
2739 std::vector<int> values;
2740 txts.push_back(sGiga + "2"); values.push_back(2);
2741 txts.push_back(sGiga + "3/v4"); values.push_back(3);
2742 if (major != 2 && major != 3) {
2743 txts.push_back(sGiga + ToString(major)); values.push_back(major);
2744 }
2745 std::vector<const char*> texts;
2746 for (int i = 0; i < txts.size(); ++i) texts.push_back(txts[i].c_str());
2747 texts.push_back(NULL); values.push_back(0);
2748 eFileFormat.set_choices(&texts[0], &values[0]);
2749 eFileFormat.set_value(major);
2750 }
2751
2752 void PropDialog::onFileFormatChanged() {
2753 const int major = eFileFormat.get_value();
2754 if (m_file) m_file->pVersion->major = major;
2755 }
2756
2757 void PropDialog::set_info(DLS::Info* info)
2758 {
2759 update(info);
2760 }
2761
2762
2763 void InstrumentProps::set_Name(const gig::String& name)
2764 {
2765 m->pInfo->Name = name;
2766 }
2767
2768 void InstrumentProps::update_name()
2769 {
2770 update_model++;
2771 eName.set_value(m->pInfo->Name);
2772 update_model--;
2773 }
2774
2775 void InstrumentProps::set_IsDrum(bool value)
2776 {
2777 m->IsDrum = value;
2778 }
2779
2780 void InstrumentProps::set_MIDIBank(uint16_t value)
2781 {
2782 m->MIDIBank = value;
2783 }
2784
2785 void InstrumentProps::set_MIDIProgram(uint32_t value)
2786 {
2787 m->MIDIProgram = value;
2788 }
2789
2790 InstrumentProps::InstrumentProps() :
2791 #if HAS_GTKMM_STOCK
2792 quitButton(Gtk::Stock::CLOSE),
2793 #else
2794 quitButton(_("_Close")),
2795 #endif
2796 table(2,1),
2797 eName(_("Name")),
2798 eIsDrum(_("Is drum")),
2799 eMIDIBank(_("MIDI bank"), 0, 16383),
2800 eMIDIProgram(_("MIDI program")),
2801 eAttenuation(_("Attenuation"), 0, 96, 0, 1),
2802 eGainPlus6(_("Gain +6dB"), eAttenuation, -6),
2803 eEffectSend(_("Effect send"), 0, 65535),
2804 eFineTune(_("Fine tune"), -8400, 8400),
2805 ePitchbendRange(_("Pitchbend range"), 0, 48),
2806 ePianoReleaseMode(_("Piano release mode")),
2807 eDimensionKeyRangeLow(_("Keyswitching range low")),
2808 eDimensionKeyRangeHigh(_("Keyswitching range high"))
2809 {
2810 if (!Settings::singleton()->autoRestoreWindowDimension) {
2811 //set_default_size(470, 390);
2812 set_position(Gtk::WIN_POS_MOUSE);
2813 }
2814
2815 set_title(_("Instrument Properties"));
2816
2817 eDimensionKeyRangeLow.set_tip(
2818 _("start of the keyboard area which should switch the "
2819 "\"keyswitching\" dimension")
2820 );
2821 eDimensionKeyRangeHigh.set_tip(
2822 _("end of the keyboard area which should switch the "
2823 "\"keyswitching\" dimension")
2824 );
2825
2826 connect(eName, &InstrumentProps::set_Name);
2827 connect(eIsDrum, &InstrumentProps::set_IsDrum);
2828 connect(eMIDIBank, &InstrumentProps::set_MIDIBank);
2829 connect(eMIDIProgram, &InstrumentProps::set_MIDIProgram);
2830 connect(eAttenuation, &gig::Instrument::Attenuation);
2831 connect(eGainPlus6, &gig::Instrument::Attenuation);
2832 connect(eEffectSend, &gig::Instrument::EffectSend);
2833 connect(eFineTune, &gig::Instrument::FineTune);
2834 connect(ePitchbendRange, &gig::Instrument::PitchbendRange);
2835 connect(ePianoReleaseMode, &gig::Instrument::PianoReleaseMode);
2836 connect(eDimensionKeyRangeLow, eDimensionKeyRangeHigh,
2837 &gig::Instrument::DimensionKeyRange);
2838
2839 eName.signal_value_changed().connect(sig_name_changed.make_slot());
2840
2841 #if USE_GTKMM_GRID
2842 table.set_column_spacing(5);
2843 #else
2844 table.set_col_spacings(5);
2845 #endif
2846
2847 table.add(eName);
2848 table.add(eIsDrum);
2849 table.add(eMIDIBank);
2850 table.add(eMIDIProgram);
2851 table.add(eAttenuation);
2852 table.add(eGainPlus6);
2853 table.add(eEffectSend);
2854 table.add(eFineTune);
2855 table.add(ePitchbendRange);
2856 table.add(ePianoReleaseMode);
2857 table.add(eDimensionKeyRangeLow);
2858 table.add(eDimensionKeyRangeHigh);
2859
2860 add(vbox);
2861 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
2862 table.set_margin(5);
2863 #else
2864 table.set_border_width(5);
2865 #endif
2866 vbox.pack_start(table);
2867 table.show();
2868 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
2869 buttonBox.set_layout(Gtk::BUTTONBOX_END);
2870 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
2871 buttonBox.set_margin(5);
2872 #else
2873 buttonBox.set_border_width(5);
2874 #endif
2875 buttonBox.show();
2876 buttonBox.pack_start(quitButton);
2877 quitButton.set_can_default();
2878 quitButton.grab_focus();
2879
2880 quitButton.signal_clicked().connect(
2881 sigc::mem_fun(*this, &InstrumentProps::hide));
2882
2883 quitButton.show();
2884 vbox.show();
2885 #if HAS_GTKMM_SHOW_ALL_CHILDREN
2886 show_all_children();
2887 #endif
2888 }
2889
2890 void InstrumentProps::set_instrument(gig::Instrument* instrument)
2891 {
2892 update(instrument);
2893
2894 update_model++;
2895 eName.set_value(instrument->pInfo->Name);
2896 eIsDrum.set_value(instrument->IsDrum);
2897 eMIDIBank.set_value(instrument->MIDIBank);
2898 eMIDIProgram.set_value(instrument->MIDIProgram);
2899 update_model--;
2900 }
2901
2902
2903 void MainWindow::file_changed()
2904 {
2905 if (file && !file_is_changed) {
2906 set_title("*" + get_title());
2907 file_is_changed = true;
2908 }
2909 }
2910
2911 void MainWindow::updateSampleRefCountMap(gig::File* gig) {
2912 sample_ref_count.clear();
2913
2914 if (!gig) return;
2915
2916 for (gig::Instrument* instrument = gig->GetFirstInstrument(); instrument;
2917 instrument = gig->GetNextInstrument())
2918 {
2919 for (gig::Region* rgn = instrument->GetFirstRegion(); rgn;
2920 rgn = instrument->GetNextRegion())
2921 {
2922 for (int i = 0; i < 256; ++i) {
2923 if (!rgn->pDimensionRegions[i]) continue;
2924 if (rgn->pDimensionRegions[i]->pSample) {
2925 sample_ref_count[rgn->pDimensionRegions[i]->pSample]++;
2926 }
2927 }
2928 }
2929 }
2930 }
2931
2932 void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument)
2933 {
2934 file = 0;
2935 set_file_is_shared(isSharedInstrument);
2936
2937 this->filename =
2938 (filename && strlen(filename) > 0) ?
2939 filename : (!gig->GetFileName().empty()) ?
2940 gig->GetFileName() : _("Unsaved Gig File");
2941 set_title(Glib::filename_display_basename(this->filename));
2942 file_has_name = filename;
2943 file_is_changed = false;
2944
2945 propDialog.set_file(gig);
2946 propDialog.set_info(gig->pInfo);
2947
2948 instrument_name_connection.block();
2949 int index = 0;
2950 for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ;
2951 instrument = gig->GetNextInstrument(), ++index) {
2952 Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
2953 const int iScriptSlots = instrument->ScriptSlotCount();
2954
2955 Gtk::TreeModel::iterator iter = m_refTreeModel->append();
2956 Gtk::TreeModel::Row row = *iter;
2957 row[m_Columns.m_col_nr] = index;
2958 row[m_Columns.m_col_name] = name;
2959 row[m_Columns.m_col_instr] = instrument;
2960 row[m_Columns.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : "";
2961
2962 #if !USE_GTKMM_BUILDER
2963 add_instrument_to_menu(name);
2964 #endif
2965 }
2966 instrument_name_connection.unblock();
2967 #if !USE_GTKMM_BUILDER
2968 uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments")->show();
2969 #endif
2970
2971 updateSampleRefCountMap(gig);
2972
2973 for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) {
2974 if (group->Name != "") {
2975 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
2976 Gtk::TreeModel::Row rowGroup = *iterGroup;
2977 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
2978 rowGroup[m_SamplesModel.m_col_group] = group;
2979 rowGroup[m_SamplesModel.m_col_sample] = NULL;
2980 for (gig::Sample* sample = group->GetFirstSample();
2981 sample; sample = group->GetNextSample()) {
2982 Gtk::TreeModel::iterator iterSample =
2983 m_refSamplesTreeModel->append(rowGroup.children());
2984 Gtk::TreeModel::Row rowSample = *iterSample;
2985 rowSample[m_SamplesModel.m_col_name] =
2986 gig_to_utf8(sample->pInfo->Name);
2987 rowSample[m_SamplesModel.m_col_sample] = sample;
2988 rowSample[m_SamplesModel.m_col_group] = NULL;
2989 int refcount = sample_ref_count.count(sample) ? sample_ref_count[sample] : 0;
2990 rowSample[m_SamplesModel.m_col_refcount] = ToString(refcount) + " " + _("Refs.");
2991 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
2992 }
2993 }
2994 }
2995
2996 for (int i = 0; gig->GetScriptGroup(i); ++i) {
2997 gig::ScriptGroup* group = gig->GetScriptGroup(i);
2998
2999 Gtk::TreeModel::iterator iterGroup = m_refScriptsTreeModel->append();
3000 Gtk::TreeModel::Row rowGroup = *iterGroup;
3001 rowGroup[m_ScriptsModel.m_col_name] = gig_to_utf8(group->Name);
3002 rowGroup[m_ScriptsModel.m_col_group] = group;
3003 rowGroup[m_ScriptsModel.m_col_script] = NULL;
3004 for (int s = 0; group->GetScript(s); ++s) {
3005 gig::Script* script = group->GetScript(s);
3006
3007 Gtk::TreeModel::iterator iterScript =
3008 m_refScriptsTreeModel->append(rowGroup.children());
3009 Gtk::TreeModel::Row rowScript = *iterScript;
3010 rowScript[m_ScriptsModel.m_col_name] = gig_to_utf8(script->Name);
3011 rowScript[m_ScriptsModel.m_col_script] = script;
3012 rowScript[m_ScriptsModel.m_col_group] = NULL;
3013 }
3014 }
3015 // unfold all sample groups & script groups by default
3016 m_TreeViewSamples.expand_all();
3017 m_TreeViewScripts.expand_all();
3018
3019 file = gig;
3020
3021 // select the first instrument
3022 m_TreeView.get_selection()->select(Gtk::TreePath("0"));
3023
3024 instr_props_set_instrument();
3025 gig::Instrument* instrument = get_instrument();
3026 if (instrument) {
3027 midiRules.set_instrument(instrument);
3028 }
3029 }
3030
3031 bool MainWindow::instr_props_set_instrument()
3032 {
3033 instrumentProps.signal_name_changed().clear();
3034
3035 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
3036 if (rows.empty()) {
3037 instrumentProps.hide();
3038 return false;
3039 }
3040 //NOTE: was const_iterator before, which did not compile with GTKMM4 development branch, probably going to be fixed before final GTKMM4 release though.
3041 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
3042 if (it) {
3043 Gtk::TreeModel::Row row = *it;
3044 gig::Instrument* instrument = row[m_Columns.m_col_instr];
3045
3046 instrumentProps.set_instrument(instrument);
3047
3048 // make sure instrument tree is updated when user changes the
3049 // instrument name in instrument properties window
3050 instrumentProps.signal_name_changed().connect(
3051 sigc::bind(
3052 sigc::mem_fun(*this,
3053 &MainWindow::instr_name_changed_by_instr_props),
3054 it));
3055 } else {
3056 instrumentProps.hide();
3057 }
3058 //NOTE: explicit boolean cast required for GTKMM4 development branch here
3059 return it ? true : false;
3060 }
3061
3062 void MainWindow::show_instr_props()
3063 {
3064 if (instr_props_set_instrument()) {
3065 instrumentProps.show();
3066 instrumentProps.deiconify();
3067 }
3068 }
3069
3070 void MainWindow::instr_name_changed_by_instr_props(Gtk::TreeModel::iterator& it)
3071 {
3072 Gtk::TreeModel::Row row = *it;
3073 Glib::ustring name = row[m_Columns.m_col_name];
3074
3075 gig::Instrument* instrument = row[m_Columns.m_col_instr];
3076 Glib::ustring gigname(gig_to_utf8(instrument->pInfo->Name));
3077 if (gigname != name) {
3078 row[m_Columns.m_col_name] = gigname;
3079 }
3080 }
3081
3082 void MainWindow::show_midi_rules()
3083 {
3084 if (gig::Instrument* instrument = get_instrument())
3085 {
3086 midiRules.set_instrument(instrument);
3087 midiRules.show();
3088 midiRules.deiconify();
3089 }
3090 }
3091
3092 void MainWindow::show_script_slots() {
3093 if (!file) return;
3094 // get selected instrument
3095 std::vector<Gtk::TreeModel::Path> rows = m_TreeView.get_selection()->get_selected_rows();
3096 if (rows.empty()) return;
3097 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
3098 if (!it) return;
3099 Gtk::TreeModel::Row row = *it;
3100 gig::Instrument* instrument = row[m_Columns.m_col_instr];
3101 if (!instrument) return;
3102
3103 ScriptSlots* window = new ScriptSlots;
3104 window->setInstrument(instrument);
3105 window->signal_script_slots_changed().connect(
3106 sigc::mem_fun(*this, &MainWindow::onScriptSlotsModified)
3107 );
3108 //window->reparent(*this);
3109 window->show();
3110 }
3111
3112 void MainWindow::onScriptSlotsModified(gig::Instrument* pInstrument) {
3113 if (!pInstrument) return;
3114 const int iScriptSlots = pInstrument->ScriptSlotCount();
3115
3116 //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!
3117 //Glib::RefPtr<Gtk::TreeModel> model = m_TreeView.get_model();
3118 Glib::RefPtr<Gtk::TreeModel> model = m_refTreeModelFilter->get_model();
3119
3120 for (int i = 0; i < model->children().size(); ++i) {
3121 Gtk::TreeModel::Row row = model->children()[i];
3122 if (row[m_Columns.m_col_instr] != pInstrument) continue;
3123 row[m_Columns.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : "";
3124 break;
3125 }
3126
3127 // causes the sampler to reload the instrument with the new script
3128 on_sel_change();
3129 }
3130
3131 void MainWindow::assignScript(gig::Script* pScript) {
3132 if (!pScript) {
3133 printf("assignScript() : !script\n");
3134 return;
3135 }
3136 printf("assignScript('%s')\n", pScript->Name.c_str());
3137
3138 gig::Instrument* pInstrument = get_instrument();
3139 if (!pInstrument) {
3140 printf("!instrument\n");
3141 return;
3142 }
3143
3144 pInstrument->AddScriptSlot(pScript);
3145
3146 onScriptSlotsModified(pInstrument);
3147 }
3148
3149 void MainWindow::on_action_refresh_all() {
3150 __refreshEntireGUI();
3151 }
3152
3153 void MainWindow::on_action_view_status_bar() {
3154 #if USE_GLIB_ACTION
3155 bool active = false;
3156 m_actionToggleStatusBar->get_state(active);
3157 // for some reason toggle state does not change automatically
3158 active = !active;
3159 m_actionToggleStatusBar->change_state(active);
3160 if (active)
3161 m_StatusBar.show();
3162 else
3163 m_StatusBar.hide();
3164 #else
3165 Gtk::CheckMenuItem* item =
3166 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/Statusbar"));
3167 if (!item) {
3168 std::cerr << "/MenuBar/MenuView/Statusbar == NULL\n";
3169 return;
3170 }
3171 if (item->get_active()) m_StatusBar.show();
3172 else m_StatusBar.hide();
3173 #endif
3174 }
3175
3176 void MainWindow::on_auto_restore_win_dim() {
3177 #if USE_GLIB_ACTION
3178 bool active = false;
3179 m_actionToggleRestoreWinDim->get_state(active);
3180 // for some reason toggle state does not change automatically
3181 active = !active;
3182 m_actionToggleRestoreWinDim->change_state(active);
3183 Settings::singleton()->autoRestoreWindowDimension = active;
3184 #else
3185 Gtk::CheckMenuItem* item =
3186 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/AutoRestoreWinDim"));
3187 if (!item) {
3188 std::cerr << "/MenuBar/MenuView/AutoRestoreWinDim == NULL\n";
3189 return;
3190 }
3191 Settings::singleton()->autoRestoreWindowDimension = item->get_active();
3192 #endif
3193 }
3194
3195 void MainWindow::on_save_with_temporary_file() {
3196 #if USE_GLIB_ACTION
3197 bool active = false;
3198 m_actionToggleSaveWithTempFile->get_state(active);
3199 // for some reason toggle state does not change automatically
3200 active = !active;
3201 m_actionToggleSaveWithTempFile->change_state(active);
3202 Settings::singleton()->saveWithTemporaryFile = active;
3203 #else
3204 Gtk::CheckMenuItem* item =
3205 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuSettings/SaveWithTemporaryFile"));
3206 if (!item) {
3207 std::cerr << "/MenuBar/MenuSettings/SaveWithTemporaryFile == NULL\n";
3208 return;
3209 }
3210 Settings::singleton()->saveWithTemporaryFile = item->get_active();
3211 #endif
3212 }
3213
3214 bool MainWindow::is_copy_samples_unity_note_enabled() const {
3215 #if USE_GLIB_ACTION
3216 bool active = false;
3217 m_actionToggleCopySampleUnity->get_state(active);
3218 return active;
3219 #else
3220 Gtk::CheckMenuItem* item =
3221 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleUnity"));
3222 if (!item) {
3223 std::cerr << "/MenuBar/MenuEdit/CopySampleUnity == NULL\n";
3224 return true;
3225 }
3226 return item->get_active();
3227 #endif
3228 }
3229
3230 bool MainWindow::is_copy_samples_fine_tune_enabled() const {
3231 #if USE_GLIB_ACTION
3232 bool active = false;
3233 m_actionToggleCopySampleTune->get_state(active);
3234 return active;
3235 #else
3236 Gtk::CheckMenuItem* item =
3237 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleTune"));
3238 if (!item) {
3239 std::cerr << "/MenuBar/MenuEdit/CopySampleTune == NULL\n";
3240 return true;
3241 }
3242 return item->get_active();
3243 #endif
3244 }
3245
3246 bool MainWindow::is_copy_samples_loop_enabled() const {
3247 #if USE_GLIB_ACTION
3248 bool active = false;
3249 m_actionToggleCopySampleLoop->get_state(active);
3250 return active;
3251 #else
3252 Gtk::CheckMenuItem* item =
3253 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleLoop"));
3254 if (!item) {
3255 std::cerr << "/MenuBar/MenuEdit/CopySampleLoop == NULL\n";
3256 return true;
3257 }
3258 return item->get_active();
3259 #endif
3260 }
3261
3262 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
3263 bool MainWindow::on_button_release(Gdk::EventButton& _button) {
3264 GdkEventButton* button = _button.gobj();
3265 #else
3266 void MainWindow::on_button_release(GdkEventButton* button) {
3267 #endif
3268 if (button->type == GDK_2BUTTON_PRESS) {
3269 show_instr_props();
3270 } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
3271 // gig v2 files have no midi rules
3272 const bool bEnabled = !(file->pVersion && file->pVersion->major == 2);
3273 #if USE_GTKMM_BUILDER
3274 m_actionMIDIRules->property_enabled() = bEnabled;
3275 #else
3276 static_cast<Gtk::MenuItem*>(
3277 uiManager->get_widget("/MenuBar/MenuInstrument/MidiRules"))->set_sensitive(
3278 bEnabled
3279 );
3280 static_cast<Gtk::MenuItem*>(
3281 uiManager->get_widget("/PopupMenu/MidiRules"))->set_sensitive(
3282 bEnabled
3283 );
3284 #endif
3285 popup_menu->popup(button->button, button->time);
3286 }
3287 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
3288 return false;
3289 #endif
3290 }
3291
3292 #if !USE_GTKMM_BUILDER
3293 void MainWindow::on_instrument_selection_change(Gtk::RadioMenuItem* item) {
3294 if (item->get_active()) {
3295 const std::vector<Gtk::Widget*> children =
3296 instrument_menu->get_children();
3297 std::vector<Gtk::Widget*>::const_iterator it =
3298 find(children.begin(), children.end(), item);
3299 if (it != children.end()) {
3300 int index = it - children.begin();
3301 m_TreeView.get_selection()->select(Gtk::TreePath(ToString(index)));
3302
3303 m_RegionChooser.set_instrument(file->GetInstrument(index));
3304 }
3305 }
3306 }
3307 #endif
3308
3309 void MainWindow::select_instrument(gig::Instrument* instrument) {
3310 if (!instrument) return;
3311
3312 //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!
3313 Glib::RefPtr<Gtk::TreeModel> model = m_TreeView.get_model();
3314 //Glib::RefPtr<Gtk::TreeModel> model = m_refTreeModelFilter->get_model();
3315
3316 for (int i = 0; i < model->children().size(); ++i) {
3317 Gtk::TreeModel::Row row = model->children()[i];
3318 if (row[m_Columns.m_col_instr] == instrument) {
3319 // select and show the respective instrument in the list view
3320 show_intruments_tab();
3321 m_TreeView.get_selection()->unselect_all();
3322
3323 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
3324 auto iterSel = model->children()[i].get_iter();
3325 m_TreeView.get_selection()->select(iterSel);
3326 #else
3327 m_TreeView.get_selection()->select(model->children()[i]);
3328 #endif
3329 std::vector<Gtk::TreeModel::Path> rows =
3330 m_TreeView.get_selection()->get_selected_rows();
3331 if (!rows.empty())
3332 m_TreeView.scroll_to_row(rows[0]);
3333 on_sel_change(); // the regular instrument selection change callback
3334 }
3335 }
3336 }
3337
3338 /// Returns true if requested dimension region was successfully selected and scrolled to in the list view, false on error.
3339 bool MainWindow::select_dimension_region(gig::DimensionRegion* dimRgn) {
3340 gig::Region* pRegion = (gig::Region*) dimRgn->GetParent();
3341 gig::Instrument* pInstrument = (gig::Instrument*) pRegion->GetParent();
3342
3343 //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!
3344 Glib::RefPtr<Gtk::TreeModel> model = m_TreeView.get_model();
3345 //Glib::RefPtr<Gtk::TreeModel> model = m_refTreeModelFilter->get_model();
3346
3347 for (int i = 0; i < model->children().size(); ++i) {
3348 Gtk::TreeModel::Row row = model->children()[i];
3349 if (row[m_Columns.m_col_instr] == pInstrument) {
3350 // select and show the respective instrument in the list view
3351 show_intruments_tab();
3352 m_TreeView.get_selection()->unselect_all();
3353 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
3354 auto iterSel = model->children()[i].get_iter();
3355 m_TreeView.get_selection()->select(iterSel);
3356 #else
3357 m_TreeView.get_selection()->select(model->children()[i]);
3358 #endif
3359 std::vector<Gtk::TreeModel::Path> rows =
3360 m_TreeView.get_selection()->get_selected_rows();
3361 if (!rows.empty())
3362 m_TreeView.scroll_to_row(rows[0]);
3363 on_sel_change(); // the regular instrument selection change callback
3364
3365 // select respective region in the region selector
3366 m_RegionChooser.set_region(pRegion);
3367
3368 // select and show the respective dimension region in the editor
3369 //update_dimregs();
3370 if (!m_DimRegionChooser.select_dimregion(dimRgn)) return false;
3371 //dimreg_edit.set_dim_region(dimRgn);
3372
3373 return true;
3374 }
3375 }
3376
3377 return false;
3378 }
3379
3380 void MainWindow::select_sample(gig::Sample* sample) {
3381 Glib::RefPtr<Gtk::TreeModel> model = m_TreeViewSamples.get_model();
3382 for (int g = 0; g < model->children().size(); ++g) {
3383 Gtk::TreeModel::Row rowGroup = model->children()[g];
3384 for (int s = 0; s < rowGroup.children().size(); ++s) {
3385 Gtk::TreeModel::Row rowSample = rowGroup.children()[s];
3386 if (rowSample[m_SamplesModel.m_col_sample] == sample) {
3387 show_samples_tab();
3388 m_TreeViewSamples.get_selection()->unselect_all();
3389 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
3390 auto iterSel = rowGroup.children()[s].get_iter();
3391 m_TreeViewSamples.get_selection()->select(iterSel);
3392 #else
3393 m_TreeViewSamples.get_selection()->select(rowGroup.children()[s]);
3394 #endif
3395 std::vector<Gtk::TreeModel::Path> rows =
3396 m_TreeViewSamples.get_selection()->get_selected_rows();
3397 if (rows.empty()) return;
3398 m_TreeViewSamples.scroll_to_row(rows[0]);
3399 return;
3400 }
3401 }
3402 }
3403 }
3404
3405 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
3406 bool MainWindow::on_sample_treeview_button_release(Gdk::EventButton& _button) {
3407 GdkEventButton* button = _button.gobj();
3408 #else
3409 void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) {
3410 #endif
3411 if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
3412 // by default if Ctrl keys is pressed down, then a mouse right-click
3413 // does not select the respective row, so we must assure this
3414 // programmatically ...
3415 /*{
3416 Gtk::TreeModel::Path path;
3417 Gtk::TreeViewColumn* pColumn = NULL;
3418 int cellX, cellY;
3419 bool bSuccess = m_TreeViewSamples.get_path_at_pos(
3420 (int)button->x, (int)button->y,
3421 path, pColumn, cellX, cellY
3422 );
3423 if (bSuccess) {
3424 if (m_TreeViewSamples.get_selection()->count_selected_rows() <= 0) {
3425 printf("not selected !!!\n");
3426 m_TreeViewSamples.get_selection()->select(path);
3427 }
3428 }
3429 }*/
3430
3431 #if !USE_GTKMM_BUILDER
3432 Gtk::Menu* sample_popup =
3433 dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/SamplePopupMenu"));
3434 #endif
3435
3436 // update enabled/disabled state of sample popup items
3437 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3438 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3439 const int n = rows.size();
3440 int nGroups = 0;
3441 int nSamples = 0;
3442 for (int r = 0; r < n; ++r) {
3443 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[r]);
3444 if (!it) continue;
3445 Gtk::TreeModel::Row row = *it;
3446 if (row[m_SamplesModel.m_col_group]) nGroups++;
3447 if (row[m_SamplesModel.m_col_sample]) nSamples++;
3448 }
3449
3450 #if USE_GTKMM_BUILDER
3451 m_actionSampleProperties->property_enabled() = (n == 1);
3452 m_actionAddSample->property_enabled() = (n);
3453 m_actionAddSampleGroup->property_enabled() = (file);
3454 m_actionViewSampleRefs->property_enabled() = (nSamples == 1);
3455 m_actionRemoveSample->property_enabled() = (n);
3456 m_actionReplaceSample->property_enabled() = (nSamples == 1);
3457 #else
3458 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->
3459 set_sensitive(n == 1);
3460 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->
3461 set_sensitive(n);
3462 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->
3463 set_sensitive(file);
3464 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/ShowSampleRefs"))->
3465 set_sensitive(nSamples == 1);
3466 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->
3467 set_sensitive(n);
3468 #endif
3469 // show sample popup
3470 sample_popup->popup(button->button, button->time);
3471
3472 #if !USE_GTKMM_BUILDER
3473 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/SampleProperties"))->
3474 set_sensitive(n == 1);
3475 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/AddSample"))->
3476 set_sensitive(n);
3477 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/AddGroup"))->
3478 set_sensitive(file);
3479 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/ShowSampleRefs"))->
3480 set_sensitive(nSamples == 1);
3481 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuSample/RemoveSample"))->
3482 set_sensitive(n);
3483 #endif
3484 }
3485
3486 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
3487 return false;
3488 #endif
3489 }
3490
3491 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
3492 bool MainWindow::on_script_treeview_button_release(Gdk::EventButton& _button) {
3493 GdkEventButton* button = _button.gobj();
3494 #else
3495 void MainWindow::on_script_treeview_button_release(GdkEventButton* button) {
3496 #endif
3497 if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
3498 #if !USE_GTKMM_BUILDER
3499 Gtk::Menu* script_popup =
3500 dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/ScriptPopupMenu"));
3501 #endif
3502 // update enabled/disabled state of sample popup items
3503 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
3504 Gtk::TreeModel::iterator it = sel->get_selected();
3505 bool group_selected = false;
3506 bool script_selected = false;
3507 if (it) {
3508 Gtk::TreeModel::Row row = *it;
3509 group_selected = row[m_ScriptsModel.m_col_group];
3510 script_selected = row[m_ScriptsModel.m_col_script];
3511 }
3512 #if USE_GTKMM_BUILDER
3513 m_actionAddScript->property_enabled() = (group_selected || script_selected);
3514 m_actionAddScriptGroup->property_enabled() = (file);
3515 m_actionEditScript->property_enabled() = (script_selected);
3516 m_actionRemoveScript->property_enabled() = (group_selected || script_selected);
3517 #else
3518 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/AddScript"))->
3519 set_sensitive(group_selected || script_selected);
3520 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/AddScriptGroup"))->
3521 set_sensitive(file);
3522 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/EditScript"))->
3523 set_sensitive(script_selected);
3524 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/ScriptPopupMenu/RemoveScript"))->
3525 set_sensitive(group_selected || script_selected);
3526 #endif
3527 // show sample popup
3528 script_popup->popup(button->button, button->time);
3529
3530 #if !USE_GTKMM_BUILDER
3531 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/AddScript"))->
3532 set_sensitive(group_selected || script_selected);
3533 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/AddScriptGroup"))->
3534 set_sensitive(file);
3535 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/EditScript"))->
3536 set_sensitive(script_selected);
3537 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuScript/RemoveScript"))->
3538 set_sensitive(group_selected || script_selected);
3539 #endif
3540 }
3541 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
3542 return false;
3543 #endif
3544 }
3545
3546 void MainWindow::updateScriptListOfMenu() {
3547 // remove all entries from "Assign Script" menu
3548 {
3549 const std::vector<Gtk::Widget*> children = assign_scripts_menu->get_children();
3550 for (int i = 0; i < children.size(); ++i) {
3551 Gtk::Widget* child = children[i];
3552 assign_scripts_menu->remove(*child);
3553 delete child;
3554 }
3555 }
3556
3557 int iTotalScripts = 0;
3558
3559 if (!file) goto noScripts;
3560
3561 // add all configured macros as menu items to the "Macro" menu
3562 for (int iGroup = 0; file->GetScriptGroup(iGroup); ++iGroup) {
3563 gig::ScriptGroup* pGroup = file->GetScriptGroup(iGroup);
3564 for (int iScript = 0; pGroup->GetScript(iScript); ++iScript, ++iTotalScripts) {
3565 gig::Script* pScript = pGroup->GetScript(iScript);
3566 std::string name = pScript->Name;
3567
3568 Gtk::MenuItem* item = new Gtk::MenuItem(name);
3569 item->signal_activate().connect(
3570 sigc::bind(
3571 sigc::mem_fun(*this, &MainWindow::assignScript), pScript
3572 )
3573 );
3574 assign_scripts_menu->append(*item);
3575 item->set_accel_path("<Scripts>/script_" + ToString(iTotalScripts));
3576 //item->set_tooltip_text(comment);
3577 }
3578 }
3579
3580 noScripts:
3581
3582 // if there are no macros configured at all, then show a dummy entry instead
3583 if (!iTotalScripts) {
3584 Gtk::MenuItem* item = new Gtk::MenuItem(_("No Scripts"));
3585 item->set_sensitive(false);
3586 assign_scripts_menu->append(*item);
3587 }
3588
3589 #if HAS_GTKMM_SHOW_ALL_CHILDREN
3590 assign_scripts_menu->show_all_children();
3591 #endif
3592 }
3593
3594 #if !USE_GTKMM_BUILDER
3595 Gtk::RadioMenuItem* MainWindow::add_instrument_to_menu(
3596 const Glib::ustring& name, int position) {
3597
3598 Gtk::RadioMenuItem::Group instrument_group;
3599 const std::vector<Gtk::Widget*> children = instrument_menu->get_children();
3600 if (!children.empty()) {
3601 instrument_group =
3602 static_cast<Gtk::RadioMenuItem*>(children[0])->get_group();
3603 }
3604 Gtk::RadioMenuItem* item =
3605 new Gtk::RadioMenuItem(instrument_group, name);
3606 if (position < 0) {
3607 instrument_menu->append(*item);
3608 } else {
3609 instrument_menu->insert(*item, position);
3610 }
3611 item->show();
3612 item->signal_activate().connect(
3613 sigc::bind(
3614 sigc::mem_fun(*this, &MainWindow::on_instrument_selection_change),
3615 item));
3616 return item;
3617 }
3618 #endif
3619
3620 #if !USE_GTKMM_BUILDER
3621 void MainWindow::remove_instrument_from_menu(int index) {
3622 const std::vector<Gtk::Widget*> children =
3623 instrument_menu->get_children();
3624 Gtk::Widget* child = children[index];
3625 instrument_menu->remove(*child);
3626 delete child;
3627 }
3628 #endif
3629
3630 void MainWindow::add_instrument(gig::Instrument* instrument) {
3631 const Glib::ustring name(gig_to_utf8(instrument->pInfo->Name));
3632
3633 // update instrument tree view
3634 instrument_name_connection.block();
3635 Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append();
3636 Gtk::TreeModel::Row rowInstr = *iterInstr;
3637 rowInstr[m_Columns.m_col_nr] = m_refTreeModel->children().size() - 1;
3638 rowInstr[m_Columns.m_col_name] = name;
3639 rowInstr[m_Columns.m_col_instr] = instrument;
3640 rowInstr[m_Columns.m_col_scripts] = "";
3641 instrument_name_connection.unblock();
3642
3643 #if !USE_GTKMM_BUILDER
3644 add_instrument_to_menu(name);
3645 #endif
3646 select_instrument(instrument);
3647 file_changed();
3648 }
3649
3650 void MainWindow::on_action_add_instrument() {
3651 static int __instrument_indexer = 0;
3652 if (!file) return;
3653 gig::Instrument* instrument = file->AddInstrument();
3654 __instrument_indexer++;
3655 instrument->pInfo->Name = gig_from_utf8(_("Unnamed Instrument ") +
3656 ToString(__instrument_indexer));
3657
3658 add_instrument(instrument);
3659 }
3660
3661 void MainWindow::on_action_duplicate_instrument() {
3662 if (!file) return;
3663
3664 // retrieve the currently selected instrument
3665 // (being the original instrument to be duplicated)
3666 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
3667 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3668 for (int r = 0; r < rows.size(); ++r) {
3669 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]);
3670 if (it) {
3671 Gtk::TreeModel::Row row = *it;
3672 gig::Instrument* instrOrig = row[m_Columns.m_col_instr];
3673 if (instrOrig) {
3674 // duplicate the orginal instrument
3675 gig::Instrument* instrNew = file->AddDuplicateInstrument(instrOrig);
3676 instrNew->pInfo->Name =
3677 instrOrig->pInfo->Name +
3678 gig_from_utf8(Glib::ustring(" (") + _("Copy") + ")");
3679
3680 add_instrument(instrNew);
3681 }
3682 }
3683 }
3684 }
3685
3686 void MainWindow::on_action_remove_instrument() {
3687 if (!file) return;
3688 if (file_is_shared) {
3689 Gtk::MessageDialog msg(
3690 *this,
3691 _("You cannot delete an instrument from this file, since it's "
3692 "currently used by the sampler."),
3693 false, Gtk::MESSAGE_INFO
3694 );
3695 msg.run();
3696 return;
3697 }
3698
3699 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
3700 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3701 for (int r = rows.size() - 1; r >= 0; --r) {
3702 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]);
3703 if (!it) continue;
3704 Gtk::TreeModel::Row row = *it;
3705 gig::Instrument* instr = row[m_Columns.m_col_instr];
3706 try {
3707 Gtk::TreePath path(it);
3708 int index = path[0];
3709
3710 // remove instrument from the gig file
3711 if (instr) file->DeleteInstrument(instr);
3712 file_changed();
3713
3714 #if !USE_GTKMM_BUILDER
3715 remove_instrument_from_menu(index);
3716 #endif
3717
3718 // remove row from instruments tree view
3719 m_refTreeModel->erase(it);
3720 // update "Nr" column of all instrument rows
3721 {
3722 int index = 0;
3723 for (Gtk::TreeModel::iterator it = m_refTreeModel->children().begin();
3724 it != m_refTreeModel->children().end(); ++it, ++index)
3725 {
3726 Gtk::TreeModel::Row row = *it;
3727 row[m_Columns.m_col_nr] = index;
3728 }
3729 }
3730
3731 #if GTKMM_MAJOR_VERSION < 3
3732 // select another instrument (in gtk3 this is done
3733 // automatically)
3734 if (!m_refTreeModel->children().empty()) {
3735 if (index == m_refTreeModel->children().size()) {
3736 index--;
3737 }
3738 m_TreeView.get_selection()->select(
3739 Gtk::TreePath(ToString(index)));
3740 }
3741 #endif
3742 instr_props_set_instrument();
3743 instr = get_instrument();
3744 if (instr) {
3745 midiRules.set_instrument(instr);
3746 } else {
3747 midiRules.hide();
3748 }
3749 } catch (RIFF::Exception e) {
3750 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
3751 msg.run();
3752 }
3753 }
3754 }
3755
3756 void MainWindow::on_action_sample_properties() {
3757 //TODO: show a dialog where the selected sample's properties can be edited
3758 Gtk::MessageDialog msg(
3759 *this, _("Sorry, yet to be implemented!"), false, Gtk::MESSAGE_INFO
3760 );
3761 msg.run();
3762 }
3763
3764 void MainWindow::on_action_add_script_group() {
3765 static int __script_indexer = 0;
3766 if (!file) return;
3767 gig::ScriptGroup* group = file->AddScriptGroup();
3768 group->Name = gig_from_utf8(_("Unnamed Group"));
3769 if (__script_indexer) group->Name += " " + ToString(__script_indexer);
3770 __script_indexer++;
3771 // update sample tree view
3772 Gtk::TreeModel::iterator iterGroup = m_refScriptsTreeModel->append();
3773 Gtk::TreeModel::Row rowGroup = *iterGroup;
3774 rowGroup[m_ScriptsModel.m_col_name] = gig_to_utf8(group->Name);
3775 rowGroup[m_ScriptsModel.m_col_script] = NULL;
3776 rowGroup[m_ScriptsModel.m_col_group] = group;
3777 file_changed();
3778 }
3779
3780 void MainWindow::on_action_add_script() {
3781 if (!file) return;
3782 // get selected group
3783 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
3784 Gtk::TreeModel::iterator it = sel->get_selected();
3785 if (!it) return;
3786 Gtk::TreeModel::Row row = *it;
3787 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
3788 if (!group) { // not a group, but a script is selected (probably)
3789 gig::Script* script = row[m_ScriptsModel.m_col_script];
3790 if (!script) return;
3791 it = row.parent(); // resolve parent (that is the script's group)
3792 if (!it) return;
3793 row = *it;
3794 group = row[m_ScriptsModel.m_col_group];
3795 if (!group) return;
3796 }
3797
3798 // add a new script to the .gig file
3799 gig::Script* script = group->AddScript();
3800 Glib::ustring name = _("Unnamed Script");
3801 script->Name = gig_from_utf8(name);
3802
3803 // add script to the tree view
3804 Gtk::TreeModel::iterator iterScript =
3805 m_refScriptsTreeModel->append(row.children());
3806 Gtk::TreeModel::Row rowScript = *iterScript;
3807 rowScript[m_ScriptsModel.m_col_name] = name;
3808 rowScript[m_ScriptsModel.m_col_script] = script;
3809 rowScript[m_ScriptsModel.m_col_group] = NULL;
3810
3811 // unfold group of new script item in treeview
3812 Gtk::TreeModel::Path path(iterScript);
3813 m_TreeViewScripts.expand_to_path(path);
3814 }
3815
3816 void MainWindow::on_action_edit_script() {
3817 if (!file) return;
3818 // get selected script
3819 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
3820 Gtk::TreeModel::iterator it = sel->get_selected();
3821 if (!it) return;
3822 Gtk::TreeModel::Row row = *it;
3823 gig::Script* script = row[m_ScriptsModel.m_col_script];
3824 if (!script) return;
3825
3826 ScriptEditor* editor = new ScriptEditor;
3827 editor->signal_script_to_be_changed.connect(
3828 signal_script_to_be_changed.make_slot()
3829 );
3830 editor->signal_script_changed.connect(
3831 signal_script_changed.make_slot()
3832 );
3833 editor->setScript(script);
3834 //editor->reparent(*this);
3835 editor->show();
3836 }
3837
3838 void MainWindow::on_action_remove_script() {
3839 if (!file) return;
3840 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
3841 Gtk::TreeModel::iterator it = sel->get_selected();
3842 if (it) {
3843 Gtk::TreeModel::Row row = *it;
3844 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
3845 gig::Script* script = row[m_ScriptsModel.m_col_script];
3846 Glib::ustring name = row[m_ScriptsModel.m_col_name];
3847 try {
3848 // remove script group or script from the gig file
3849 if (group) {
3850 // notify everybody that we're going to remove these samples
3851 //TODO: scripts_to_be_removed_signal.emit(members);
3852 // delete the group in the .gig file including the
3853 // samples that belong to the group
3854 file->DeleteScriptGroup(group);
3855 // notify that we're done with removal
3856 //TODO: scripts_removed_signal.emit();
3857 file_changed();
3858 } else if (script) {
3859 // notify everybody that we're going to remove this sample
3860 //TODO: std::list<gig::Script*> lscripts;
3861 //TODO: lscripts.push_back(script);
3862 //TODO: scripts_to_be_removed_signal.emit(lscripts);
3863 // remove sample from the .gig file
3864 script->GetGroup()->DeleteScript(script);
3865 // notify that we're done with removal
3866 //TODO: scripts_removed_signal.emit();
3867 dimreg_changed();
3868 file_changed();
3869 }
3870 // remove respective row(s) from samples tree view
3871 m_refScriptsTreeModel->erase(it);
3872 } catch (RIFF::Exception e) {
3873 // pretend we're done with removal (i.e. to avoid dead locks)
3874 //TODO: scripts_removed_signal.emit();
3875 // show error message
3876 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
3877 msg.run();
3878 }
3879 }
3880 }
3881
3882 void MainWindow::on_action_add_group() {
3883 static int __sample_indexer = 0;
3884 if (!file) return;
3885 gig::Group* group = file->AddGroup();
3886 group->Name = gig_from_utf8(_("Unnamed Group"));
3887 if (__sample_indexer) group->Name += " " + ToString(__sample_indexer);
3888 __sample_indexer++;
3889 // update sample tree view
3890 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
3891 Gtk::TreeModel::Row rowGroup = *iterGroup;
3892 rowGroup[m_SamplesModel.m_col_name] = gig_to_utf8(group->Name);
3893 rowGroup[m_SamplesModel.m_col_sample] = NULL;
3894 rowGroup[m_SamplesModel.m_col_group] = group;
3895 file_changed();
3896 }
3897
3898 void MainWindow::on_action_replace_sample() {
3899 add_or_replace_sample(true);
3900 }
3901
3902 void MainWindow::on_action_add_sample() {
3903 add_or_replace_sample(false);
3904 }
3905
3906 void MainWindow::add_or_replace_sample(bool replace) {
3907 if (!file) return;
3908
3909 // get selected group (and probably selected sample)
3910 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
3911 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
3912 if (rows.empty()) return;
3913 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]);
3914 if (!it) return;
3915 Gtk::TreeModel::Row row = *it;
3916 gig::Sample* sample = NULL;
3917 gig::Group* group = row[m_SamplesModel.m_col_group];
3918 if (!group) { // not a group, but a sample is selected (probably)
3919 if (replace) sample = row[m_SamplesModel.m_col_sample];
3920 if (!row[m_SamplesModel.m_col_sample]) return;
3921 it = row.parent(); // resolve parent (that is the sample's group)
3922 if (!it) return;
3923 if (!replace) row = *it;
3924 group = (*it)[m_SamplesModel.m_col_group];
3925 if (!group) return;
3926 }
3927 if (replace && !sample) return;
3928
3929 // show 'browse for file' dialog
3930 Gtk::FileChooserDialog dialog(*this, replace ? _("Replace Sample with") : _("Add Sample(s)"));
3931 #if HAS_GTKMM_STOCK
3932 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3933 dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
3934 #else
3935 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
3936 dialog.add_button(_("_Open"), Gtk::RESPONSE_OK);
3937 #endif
3938 dialog.set_select_multiple(!replace); // allow multi audio file selection only when adding new samples, does not make sense when replacing a specific sample
3939
3940 // matches all file types supported by libsndfile
3941 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
3942 Gtk::FileFilter soundfilter;
3943 #else
3944 Glib::RefPtr<Gtk::FileFilter> soundfilter = Gtk::FileFilter::create();
3945 #endif
3946 const char* const supportedFileTypes[] = {
3947 "*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd",
3948 "*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF",
3949 "*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64",
3950 "*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK",
3951 "*.caf", "*.CAF", NULL
3952 };
3953 const char* soundfiles = _("Sound Files");
3954 const char* allfiles = _("All Files");
3955 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
3956 for (int i = 0; supportedFileTypes[i]; i++)
3957 soundfilter.add_pattern(supportedFileTypes[i]);
3958 soundfilter.set_name(soundfiles);
3959
3960 // matches every file
3961 Gtk::FileFilter allpassfilter;
3962 allpassfilter.add_pattern("*.*");
3963 allpassfilter.set_name(allfiles);
3964 #else
3965 for (int i = 0; supportedFileTypes[i]; i++)
3966 soundfilter->add_pattern(supportedFileTypes[i]);
3967 soundfilter->set_name(soundfiles);
3968
3969 // matches every file
3970 Glib::RefPtr<Gtk::FileFilter> allpassfilter = Gtk::FileFilter::create();
3971 allpassfilter->add_pattern("*.*");
3972 allpassfilter->set_name(allfiles);
3973 #endif
3974 dialog.add_filter(soundfilter);
3975 dialog.add_filter(allpassfilter);
3976 if (current_sample_dir != "") {
3977 dialog.set_current_folder(current_sample_dir);
3978 }
3979 if (dialog.run() == Gtk::RESPONSE_OK) {
3980 current_sample_dir = dialog.get_current_folder();
3981 Glib::ustring error_files;
3982 std::vector<std::string> filenames = dialog.get_filenames();
3983 for (std::vector<std::string>::iterator iter = filenames.begin();
3984 iter != filenames.end(); ++iter) {
3985 printf("Adding sample %s\n",(*iter).c_str());
3986 // use libsndfile to retrieve file informations
3987 SF_INFO info;
3988 info.format = 0;
3989 SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
3990 try {
3991 if (!hFile) throw std::string(_("could not open file"));
3992 int bitdepth;
3993 switch (info.format & 0xff) {
3994 case SF_FORMAT_PCM_S8:
3995 case SF_FORMAT_PCM_16:
3996 case SF_FORMAT_PCM_U8:
3997 bitdepth = 16;
3998 break;
3999 case SF_FORMAT_PCM_24:
4000 case SF_FORMAT_PCM_32:
4001 case SF_FORMAT_FLOAT:
4002 case SF_FORMAT_DOUBLE:
4003 bitdepth = 24;
4004 break;
4005 default:
4006 sf_close(hFile); // close sound file
4007 throw std::string(_("format not supported")); // unsupported subformat (yet?)
4008 }
4009 // add a new sample to the .gig file (if adding is requested actually)
4010 if (!replace) sample = file->AddSample();
4011 // file name without path
4012 Glib::ustring filename = Glib::filename_display_basename(*iter);
4013 // remove file extension if there is one
4014 for (int i = 0; supportedFileTypes[i]; i++) {
4015 if (Glib::str_has_suffix(filename, supportedFileTypes[i] + 1)) {
4016 filename.erase(filename.length() - strlen(supportedFileTypes[i] + 1));
4017 break;
4018 }
4019 }
4020 sample->pInfo->Name = gig_from_utf8(filename);
4021 sample->Channels = info.channels;
4022 sample->BitDepth = bitdepth;
4023 sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
4024 sample->SamplesPerSecond = info.samplerate;
4025 sample->AverageBytesPerSecond = sample->FrameSize * sample->SamplesPerSecond;
4026 sample->BlockAlign = sample->FrameSize;
4027 sample->SamplesTotal = info.frames;
4028
4029 SF_INSTRUMENT instrument;
4030 if (sf_command(hFile, SFC_GET_INSTRUMENT,
4031 &instrument, sizeof(instrument)) != SF_FALSE)
4032 {
4033 sample->MIDIUnityNote = instrument.basenote;
4034 sample->FineTune = instrument.detune;
4035
4036 if (instrument.loop_count && instrument.loops[0].mode != SF_LOOP_NONE) {
4037 sample->Loops = 1;
4038
4039 switch (instrument.loops[0].mode) {
4040 case SF_LOOP_FORWARD:
4041 sample->LoopType = gig::loop_type_normal;
4042 break;
4043 case SF_LOOP_BACKWARD:
4044 sample->LoopType = gig::loop_type_backward;
4045 break;
4046 case SF_LOOP_ALTERNATING:
4047 sample->LoopType = gig::loop_type_bidirectional;
4048 break;
4049 }
4050 sample->LoopStart = instrument.loops[0].start;
4051 sample->LoopEnd = instrument.loops[0].end;
4052 sample->LoopPlayCount = instrument.loops[0].count;
4053 sample->LoopSize = sample->LoopEnd - sample->LoopStart + 1;
4054 }
4055 }
4056
4057 // schedule resizing the sample (which will be done
4058 // physically when File::Save() is called)
4059 sample->Resize(info.frames);
4060 // make sure sample is part of the selected group
4061 if (!replace) group->AddSample(sample);
4062 // schedule that physical resize and sample import
4063 // (data copying), performed when "Save" is requested
4064 SampleImportItem sched_item;
4065 sched_item.gig_sample = sample;
4066 sched_item.sample_path = *iter;
4067 m_SampleImportQueue[sample] = sched_item;
4068 // add sample to the tree view
4069 if (replace) {
4070 row[m_SamplesModel.m_col_name] = gig_to_utf8(sample->pInfo->Name);
4071 } else {
4072 Gtk::TreeModel::iterator iterSample =
4073 m_refSamplesTreeModel->append(row.children());
4074 Gtk::TreeModel::Row rowSample = *iterSample;
4075 rowSample[m_SamplesModel.m_col_name] =
4076 gig_to_utf8(sample->pInfo->Name);
4077 rowSample[m_SamplesModel.m_col_sample] = sample;
4078 rowSample[m_SamplesModel.m_col_group] = NULL;
4079 }
4080 // close sound file
4081 sf_close(hFile);
4082 file_changed();
4083 } catch (std::string what) { // remember the files that made trouble (and their cause)
4084 if (!error_files.empty()) error_files += "\n";
4085 error_files += *iter += " (" + what + ")";
4086 }
4087 }
4088 // show error message box when some file(s) could not be opened / added
4089 if (!error_files.empty()) {
4090 Glib::ustring txt =
4091 (replace
4092 ? _("Failed to replace sample with:\n")
4093 : _("Could not add the following sample(s):\n"))
4094 + error_files;
4095 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
4096 msg.run();
4097 }
4098 }
4099 }
4100
4101 void MainWindow::on_action_replace_all_samples_in_all_groups()
4102 {
4103 if (!file) return;
4104 Gtk::FileChooserDialog dialog(*this, _("Select Folder"),
4105 Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
4106 const char* str =
4107 _("This is a very specific function. It tries to replace all samples "
4108 "in the current gig file by samples located in the chosen "
4109 "directory.\n\n"
4110 "It works like this: For each sample in the gig file, it tries to "
4111 "find a sample file in the selected directory with the same name as "
4112 "the sample in the gig file. Optionally, you can add a filename "
4113 "extension below, which will be added to the filename expected to be "
4114 "found. That is, assume you have a gig file with a sample called "
4115 "'Snare', if you enter '.wav' below (like it's done by default), it "
4116 "expects to find a sample file called 'Snare.wav' and will replace "
4117 "the sample in the gig file accordingly. If you don't need an "
4118 "extension, blank the field below. Any gig sample where no "
4119 "appropriate sample file could be found will be reported and left "
4120 "untouched.\n");
4121 #if GTKMM_MAJOR_VERSION < 3
4122 view::WrapLabel description(str);
4123 #else
4124 Gtk::Label description(str);
4125 description.set_line_wrap();
4126 #endif
4127 HBox entryArea;
4128 Gtk::Label entryLabel( _("Add filename extension: "), Gtk::ALIGN_START);
4129 Gtk::Entry postfixEntryBox;
4130 postfixEntryBox.set_text(".wav");
4131 entryArea.pack_start(entryLabel);
4132 entryArea.pack_start(postfixEntryBox);
4133 #if USE_GTKMM_BOX
4134 dialog.get_content_area()->pack_start(description, Gtk::PACK_SHRINK);
4135 dialog.get_content_area()->pack_start(entryArea, Gtk::PACK_SHRINK);
4136 #else
4137 dialog.get_vbox()->pack_start(description, Gtk::PACK_SHRINK);
4138 dialog.get_vbox()->pack_start(entryArea, Gtk::PACK_SHRINK);
4139 #endif
4140 description.show();
4141
4142 #if HAS_GTKMM_SHOW_ALL_CHILDREN
4143 entryArea.show_all();
4144 #else
4145 entryArea.show();
4146 #endif
4147
4148 #if HAS_GTKMM_STOCK
4149 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
4150 #else
4151 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
4152 #endif
4153 dialog.add_button(_("Select"), Gtk::RESPONSE_OK);
4154 dialog.set_select_multiple(false);
4155 if (current_sample_dir != "") {
4156 dialog.set_current_folder(current_sample_dir);
4157 }
4158 if (dialog.run() == Gtk::RESPONSE_OK)
4159 {
4160 current_sample_dir = dialog.get_current_folder();
4161 Glib::ustring error_files;
4162 std::string folder = dialog.get_filename();
4163 for (gig::Sample* sample = file->GetFirstSample();
4164 sample; sample = file->GetNextSample())
4165 {
4166 std::string filename =
4167 folder + G_DIR_SEPARATOR_S +
4168 Glib::filename_from_utf8(gig_to_utf8(sample->pInfo->Name) +
4169 postfixEntryBox.get_text());
4170 SF_INFO info;
4171 info.format = 0;
4172 SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &info);
4173 try
4174 {
4175 if (!hFile) throw std::string(_("could not open file"));
4176 switch (info.format & 0xff) {
4177 case SF_FORMAT_PCM_S8:
4178 case SF_FORMAT_PCM_16:
4179 case SF_FORMAT_PCM_U8:
4180 case SF_FORMAT_PCM_24:
4181 case SF_FORMAT_PCM_32:
4182 case SF_FORMAT_FLOAT:
4183 case SF_FORMAT_DOUBLE:
4184 break;
4185 default:
4186 sf_close(hFile);
4187 throw std::string(_("format not supported"));
4188 }
4189 SampleImportItem sched_item;
4190 sched_item.gig_sample = sample;
4191 sched_item.sample_path = filename;
4192 m_SampleImportQueue[sample] = sched_item;
4193 sf_close(hFile);
4194 file_changed();
4195 }
4196 catch (std::string what)
4197 {
4198 if (!error_files.empty()) error_files += "\n";
4199 error_files += Glib::filename_to_utf8(filename) +
4200 " (" + what + ")";
4201 }
4202 }
4203 // show error message box when some file(s) could not be opened / added
4204 if (!error_files.empty()) {
4205 Glib::ustring txt =
4206 _("Could not replace the following sample(s):\n") + error_files;
4207 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
4208 msg.run();
4209 }
4210 }
4211 }
4212
4213 void MainWindow::on_action_remove_sample() {
4214 if (!file) return;
4215 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
4216 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
4217 for (int r = rows.size() - 1; r >= 0; --r) {
4218 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[r]);
4219 if (!it) continue;
4220 Gtk::TreeModel::Row row = *it;
4221 gig::Group* group = row[m_SamplesModel.m_col_group];
4222 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
4223 Glib::ustring name = row[m_SamplesModel.m_col_name];
4224 try {
4225 // remove group or sample from the gig file
4226 if (group) {
4227 // temporarily remember the samples that belong to
4228 // that group (we need that to clean the queue)
4229 std::list<gig::Sample*> members;
4230 for (gig::Sample* pSample = group->GetFirstSample();
4231 pSample; pSample = group->GetNextSample()) {
4232 members.push_back(pSample);
4233 }
4234 // notify everybody that we're going to remove these samples
4235 samples_to_be_removed_signal.emit(members);
4236 // delete the group in the .gig file including the
4237 // samples that belong to the group
4238 file->DeleteGroup(group);
4239 // notify that we're done with removal
4240 samples_removed_signal.emit();
4241 // if sample(s) were just previously added, remove
4242 // them from the import queue
4243 for (std::list<gig::Sample*>::iterator member = members.begin();
4244 member != members.end(); ++member)
4245 {
4246 if (m_SampleImportQueue.count(*member)) {
4247 printf("Removing previously added sample '%s' from group '%s'\n",
4248 m_SampleImportQueue[sample].sample_path.c_str(), name.c_str());
4249 m_SampleImportQueue.erase(*member);
4250 }
4251 }
4252 file_changed();
4253 } else if (sample) {
4254 // notify everybody that we're going to remove this sample
4255 std::list<gig::Sample*> lsamples;
4256 lsamples.push_back(sample);
4257 samples_to_be_removed_signal.emit(lsamples);
4258 // remove sample from the .gig file
4259 file->DeleteSample(sample);
4260 // notify that we're done with removal
4261 samples_removed_signal.emit();
4262 // if sample was just previously added, remove it from
4263 // the import queue
4264 if (m_SampleImportQueue.count(sample)) {
4265 printf("Removing previously added sample '%s'\n",
4266 m_SampleImportQueue[sample].sample_path.c_str());
4267 m_SampleImportQueue.erase(sample);
4268 }
4269 dimreg_changed();
4270 file_changed();
4271 }
4272 // remove respective row(s) from samples tree view
4273 m_refSamplesTreeModel->erase(it);
4274 } catch (RIFF::Exception e) {
4275 // pretend we're done with removal (i.e. to avoid dead locks)
4276 samples_removed_signal.emit();
4277 // show error message
4278 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
4279 msg.run();
4280 }
4281 }
4282 }
4283
4284 void MainWindow::on_action_remove_unused_samples() {
4285 if (!file) return;
4286
4287 // collect all samples that are not referenced by any instrument
4288 std::list<gig::Sample*> lsamples;
4289 for (int iSample = 0; file->GetSample(iSample); ++iSample) {
4290 gig::Sample* sample = file->GetSample(iSample);
4291 bool isUsed = false;
4292 for (gig::Instrument* instrument = file->GetFirstInstrument(); instrument;
4293 instrument = file->GetNextInstrument())
4294 {
4295 for (gig::Region* rgn = instrument->GetFirstRegion(); rgn;
4296 rgn = instrument->GetNextRegion())
4297 {
4298 for (int i = 0; i < 256; ++i) {
4299 if (!rgn->pDimensionRegions[i]) continue;
4300 if (rgn->pDimensionRegions[i]->pSample != sample) continue;
4301 isUsed = true;
4302 goto endOfRefSearch;
4303 }
4304 }
4305 }
4306 endOfRefSearch:
4307 if (!isUsed) lsamples.push_back(sample);
4308 }
4309
4310 if (lsamples.empty()) return;
4311
4312 // notify everybody that we're going to remove these samples
4313 samples_to_be_removed_signal.emit(lsamples);
4314
4315 // remove collected samples
4316 try {
4317 for (std::list<gig::Sample*>::iterator itSample = lsamples.begin();
4318 itSample != lsamples.end(); ++itSample)
4319 {
4320 gig::Sample* sample = *itSample;
4321 // remove sample from the .gig file
4322 file->DeleteSample(sample);
4323 // if sample was just previously added, remove it from the import queue
4324 if (m_SampleImportQueue.count(sample)) {
4325 printf("Removing previously added sample '%s'\n",
4326 m_SampleImportQueue[sample].sample_path.c_str());
4327 m_SampleImportQueue.erase(sample);
4328 }
4329 }
4330 } catch (RIFF::Exception e) {
4331 // show error message
4332 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
4333 msg.run();
4334 }
4335
4336 // notify everybody that we're done with removal
4337 samples_removed_signal.emit();
4338
4339 dimreg_changed();
4340 file_changed();
4341 __refreshEntireGUI();
4342 }
4343
4344 // see comment on on_sample_treeview_drag_begin()
4345 void MainWindow::on_scripts_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
4346 {
4347 first_call_to_drag_data_get = true;
4348 }
4349
4350 void MainWindow::on_scripts_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
4351 Gtk::SelectionData& selection_data, guint, guint)
4352 {
4353 if (!first_call_to_drag_data_get) return;
4354 first_call_to_drag_data_get = false;
4355
4356 // get selected script
4357 gig::Script* script = NULL;
4358 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewScripts.get_selection();
4359 Gtk::TreeModel::iterator it = sel->get_selected();
4360 if (it) {
4361 Gtk::TreeModel::Row row = *it;
4362 script = row[m_ScriptsModel.m_col_script];
4363 }
4364 // pass the gig::Script as pointer
4365 selection_data.set(selection_data.get_target(), 0/*unused*/,
4366 (const guchar*)&script,
4367 sizeof(script)/*length of data in bytes*/);
4368 }
4369
4370 // see comment on on_sample_treeview_drag_begin()
4371 void MainWindow::on_instruments_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
4372 {
4373 first_call_to_drag_data_get = true;
4374 }
4375
4376 void MainWindow::on_instruments_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
4377 Gtk::SelectionData& selection_data, guint, guint)
4378 {
4379 if (!first_call_to_drag_data_get) return;
4380 first_call_to_drag_data_get = false;
4381
4382 // get selected source instrument
4383 gig::Instrument* src = NULL;
4384 {
4385 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
4386 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
4387 if (!rows.empty()) {
4388 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]);
4389 if (it) {
4390 Gtk::TreeModel::Row row = *it;
4391 src = row[m_Columns.m_col_instr];
4392 }
4393 }
4394 }
4395 if (!src) return;
4396
4397 // pass the source gig::Instrument as pointer
4398 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&src,
4399 sizeof(src)/*length of data in bytes*/);
4400 }
4401
4402 void MainWindow::on_instruments_treeview_drop_drag_data_received(
4403 const Glib::RefPtr<Gdk::DragContext>& context, int x, int y,
4404 const Gtk::SelectionData& selection_data, guint, guint time)
4405 {
4406 gig::Instrument* src = *((gig::Instrument**) selection_data.get_data());
4407 if (!src || selection_data.get_length() != sizeof(gig::Instrument*))
4408 return;
4409
4410 gig::Instrument* dst = NULL;
4411 {
4412 Gtk::TreeModel::Path path;
4413 const bool found = m_TreeView.get_path_at_pos(x, y, path);
4414 if (!found) return;
4415
4416 Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path);
4417 if (!iter) return;
4418 Gtk::TreeModel::Row row = *iter;
4419 dst = row[m_Columns.m_col_instr];
4420 }
4421 if (!dst) return;
4422
4423 //printf("dragdrop received src=%s dst=%s\n", src->pInfo->Name.c_str(), dst->pInfo->Name.c_str());
4424 src->MoveTo(dst);
4425 __refreshEntireGUI();
4426 select_instrument(src);
4427 }
4428
4429 // For some reason drag_data_get gets called two times for each
4430 // drag'n'drop (at least when target is an Entry). This work-around
4431 // makes sure the code in drag_data_get and drop_drag_data_received is
4432 // only executed once, as drag_begin only gets called once.
4433 void MainWindow::on_sample_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
4434 {
4435 first_call_to_drag_data_get = true;
4436 }
4437
4438 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
4439 Gtk::SelectionData& selection_data, guint, guint)
4440 {
4441 if (!first_call_to_drag_data_get) return;
4442 first_call_to_drag_data_get = false;
4443
4444 // get selected sample
4445 gig::Sample* sample = NULL;
4446 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
4447 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
4448 if (!rows.empty()) {
4449 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]);
4450 if (it) {
4451 Gtk::TreeModel::Row row = *it;
4452 sample = row[m_SamplesModel.m_col_sample];
4453 }
4454 }
4455 // pass the gig::Sample as pointer
4456 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
4457 sizeof(sample)/*length of data in bytes*/);
4458 }
4459
4460 void MainWindow::on_sample_label_drop_drag_data_received(
4461 const Glib::RefPtr<Gdk::DragContext>& context, int, int,
4462 const Gtk::SelectionData& selection_data, guint, guint time)
4463 {
4464 gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
4465
4466 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
4467 std::cout << "Drop received sample \"" <<
4468 sample->pInfo->Name << "\"" << std::endl;
4469 // drop success
4470 context->drop_reply(true, time);
4471
4472 //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
4473
4474 // notify everybody that we're going to alter the region
4475 gig::Region* region = m_RegionChooser.get_region();
4476 region_to_be_changed_signal.emit(region);
4477
4478 // find the samplechannel dimension
4479 gig::dimension_def_t* stereo_dimension = 0;
4480 for (int i = 0 ; i < region->Dimensions ; i++) {
4481 if (region->pDimensionDefinitions[i].dimension ==
4482 gig::dimension_samplechannel) {
4483 stereo_dimension = &region->pDimensionDefinitions[i];
4484 break;
4485 }
4486 }
4487 bool channels_changed = false;
4488 if (sample->Channels == 1 && stereo_dimension) {
4489 // remove the samplechannel dimension
4490 /* commented out, because it makes it impossible building up an instrument from scratch using two separate L/R samples
4491 region->DeleteDimension(stereo_dimension);
4492 channels_changed = true;
4493 region_changed();
4494 */
4495 }
4496 dimreg_edit.set_sample(
4497 sample,
4498 is_copy_samples_unity_note_enabled(),
4499 is_copy_samples_fine_tune_enabled(),
4500 is_copy_samples_loop_enabled()
4501 );
4502
4503 if (sample->Channels == 2 && !stereo_dimension) {
4504 // add samplechannel dimension
4505 gig::dimension_def_t dim;
4506 dim.dimension = gig::dimension_samplechannel;
4507 dim.bits = 1;
4508 dim.zones = 2;
4509 region->AddDimension(&dim);
4510 channels_changed = true;
4511 region_changed();
4512 }
4513 if (channels_changed) {
4514 // unmap all samples with wrong number of channels
4515 // TODO: maybe there should be a warning dialog for this
4516 for (int i = 0 ; i < region->DimensionRegions ; i++) {
4517 gig::DimensionRegion* d = region->pDimensionRegions[i];
4518 if (d->pSample && d->pSample->Channels != sample->Channels) {
4519 gig::Sample* oldref = d->pSample;
4520 d->pSample = NULL;
4521 sample_ref_changed_signal.emit(oldref, NULL);
4522 }
4523 }
4524 }
4525
4526 // notify we're done with altering
4527 region_changed_signal.emit(region);
4528
4529 file_changed();
4530
4531 return;
4532 }
4533 // drop failed
4534 context->drop_reply(false, time);
4535 }
4536
4537 void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
4538 const Gtk::TreeModel::iterator& iter) {
4539 if (!iter) return;
4540 Gtk::TreeModel::Row row = *iter;
4541 Glib::ustring name = row[m_SamplesModel.m_col_name];
4542 gig::Group* group = row[m_SamplesModel.m_col_group];
4543 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
4544 gig::String gigname(gig_from_utf8(name));
4545 if (group) {
4546 if (group->Name != gigname) {
4547 group->Name = gigname;
4548 printf("group name changed\n");
4549 file_changed();
4550 }
4551 } else if (sample) {
4552 if (sample->pInfo->Name != gigname) {
4553 sample->pInfo->Name = gigname;
4554 printf("sample name changed\n");
4555 file_changed();
4556 }
4557 }
4558 }
4559
4560 void MainWindow::script_name_changed(const Gtk::TreeModel::Path& path,
4561 const Gtk::TreeModel::iterator& iter) {
4562 if (!iter) return;
4563 Gtk::TreeModel::Row row = *iter;
4564 Glib::ustring name = row[m_ScriptsModel.m_col_name];
4565 gig::ScriptGroup* group = row[m_ScriptsModel.m_col_group];
4566 gig::Script* script = row[m_ScriptsModel.m_col_script];
4567 gig::String gigname(gig_from_utf8(name));
4568 if (group) {
4569 if (group->Name != gigname) {
4570 group->Name = gigname;
4571 printf("script group name changed\n");
4572 file_changed();
4573 }
4574 } else if (script) {
4575 if (script->Name != gigname) {
4576 script->Name = gigname;
4577 printf("script name changed\n");
4578 file_changed();
4579 }
4580 }
4581 }
4582
4583 void MainWindow::script_double_clicked(const Gtk::TreeModel::Path& path,
4584 Gtk::TreeViewColumn* column)
4585 {
4586 Gtk::TreeModel::iterator iter = m_refScriptsTreeModel->get_iter(path);
4587 if (!iter) return;
4588 Gtk::TreeModel::Row row = *iter;
4589 gig::Script* script = row[m_ScriptsModel.m_col_script];
4590 if (!script) return;
4591
4592 ScriptEditor* editor = new ScriptEditor;
4593 editor->signal_script_to_be_changed.connect(
4594 signal_script_to_be_changed.make_slot()
4595 );
4596 editor->signal_script_changed.connect(
4597 signal_script_changed.make_slot()
4598 );
4599 editor->setScript(script);
4600 //editor->reparent(*this);
4601 editor->show();
4602 }
4603
4604 void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
4605 const Gtk::TreeModel::iterator& iter) {
4606 if (!iter) return;
4607 Gtk::TreeModel::Row row = *iter;
4608 Glib::ustring name = row[m_Columns.m_col_name];
4609
4610 #if !USE_GTKMM_BUILDER
4611 // change name in instrument menu
4612 int index = path[0];
4613 const std::vector<Gtk::Widget*> children = instrument_menu->get_children();
4614 if (index < children.size()) {
4615 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 16) || GTKMM_MAJOR_VERSION > 2
4616 static_cast<Gtk::RadioMenuItem*>(children[index])->set_label(name);
4617 #else
4618 remove_instrument_from_menu(index);
4619 Gtk::RadioMenuItem* item = add_instrument_to_menu(name, index);
4620 item->set_active();
4621 #endif
4622 }
4623 #endif
4624
4625 // change name in gig
4626 gig::Instrument* instrument = row[m_Columns.m_col_instr];
4627 gig::String gigname(gig_from_utf8(name));
4628 if (instrument && instrument->pInfo->Name != gigname) {
4629 instrument->pInfo->Name = gigname;
4630
4631 // change name in the instrument properties window
4632 if (instrumentProps.get_instrument() == instrument) {
4633 instrumentProps.update_name();
4634 }
4635
4636 file_changed();
4637 }
4638 }
4639
4640 bool MainWindow::instrument_row_visible(const Gtk::TreeModel::const_iterator& iter) {
4641 if (!iter)
4642 return true;
4643
4644 Glib::ustring pattern = m_searchText.get_text().lowercase();
4645 trim(pattern);
4646 if (pattern.empty()) return true;
4647
4648 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
4649 //HACK: on GTKMM4 development branch const_iterator cannot be easily converted to iterator, probably going to be fixed before final GTKMM4 release though.
4650 Gtk::TreeModel::Row row = **(Gtk::TreeModel::iterator*)(&iter);
4651 #else
4652 Gtk::TreeModel::Row row = *iter;
4653 #endif
4654 Glib::ustring name = row[m_Columns.m_col_name];
4655 name = name.lowercase();
4656
4657 std::vector<Glib::ustring> tokens = Glib::Regex::split_simple(" ", pattern);
4658 for (int t = 0; t < tokens.size(); ++t)
4659 if (name.find(tokens[t]) == Glib::ustring::npos)
4660 return false;
4661
4662 return true;
4663 }
4664
4665 void MainWindow::on_action_combine_instruments() {
4666 CombineInstrumentsDialog* d = new CombineInstrumentsDialog(*this, file);
4667
4668 // take over selection from instruments list view for the combine dialog's
4669 // list view as pre-selection
4670 std::set<int> indeces;
4671 {
4672 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
4673 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
4674 for (int r = 0; r < rows.size(); ++r) {
4675 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]);
4676 if (it) {
4677 Gtk::TreeModel::Row row = *it;
4678 int index = row[m_Columns.m_col_nr];
4679 indeces.insert(index);
4680 }
4681 }
4682 }
4683 d->setSelectedInstruments(indeces);
4684
4685 #if HAS_GTKMM_SHOW_ALL_CHILDREN
4686 d->show_all();
4687 #else
4688 d->show();
4689 #endif
4690 d->run();
4691 if (d->fileWasChanged()) {
4692 // update GUI with new instrument just created
4693 add_instrument(d->newCombinedInstrument());
4694 }
4695 delete d;
4696 }
4697
4698 void MainWindow::on_action_view_references() {
4699 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
4700 std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
4701 if (rows.empty()) return;
4702 Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]);
4703 if (!it) return;
4704 Gtk::TreeModel::Row row = *it;
4705 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
4706 if (!sample) return;
4707
4708 ReferencesView* d = new ReferencesView(*this);
4709 d->setSample(sample);
4710 d->dimension_region_selected.connect(
4711 sigc::mem_fun(*this, &MainWindow::select_dimension_region)
4712 );
4713 #if HAS_GTKMM_SHOW_ALL_CHILDREN
4714 d->show_all();
4715 #else
4716 d->show();
4717 #endif
4718 d->resize(500, 400);
4719 d->run();
4720 delete d;
4721 }
4722
4723 void MainWindow::mergeFiles(const std::vector<std::string>& filenames) {
4724 struct _Source {
4725 std::vector<RIFF::File*> riffs;
4726 std::vector<gig::File*> gigs;
4727
4728 ~_Source() {
4729 for (int k = 0; k < gigs.size(); ++k) delete gigs[k];
4730 for (int k = 0; k < riffs.size(); ++k) delete riffs[k];
4731 riffs.clear();
4732 gigs.clear();
4733 }
4734 } sources;
4735
4736 if (filenames.empty())
4737 throw RIFF::Exception(_("No files selected, so nothing done."));
4738
4739 // first open all input files (to avoid output file corruption)
4740 int i;
4741 try {
4742 for (i = 0; i < filenames.size(); ++i) {
4743 const std::string& filename = filenames[i];
4744 printf("opening file=%s\n", filename.c_str());
4745
4746 RIFF::File* riff = new RIFF::File(filename);
4747 sources.riffs.push_back(riff);
4748
4749 gig::File* gig = new gig::File(riff);
4750 sources.gigs.push_back(gig);
4751 }
4752 } catch (RIFF::Exception e) {
4753 throw RIFF::Exception(
4754 _("Error occurred while opening '") +
4755 filenames[i] +
4756 "': " +
4757 e.Message
4758 );
4759 } catch (...) {
4760 throw RIFF::Exception(
4761 _("Unknown exception occurred while opening '") +
4762 filenames[i] + "'"
4763 );
4764 }
4765
4766 // now merge the opened .gig files to the main .gig file currently being
4767 // open in gigedit
4768 try {
4769 for (i = 0; i < filenames.size(); ++i) {
4770 const std::string& filename = filenames[i];
4771 printf("merging file=%s\n", filename.c_str());
4772 assert(i < sources.gigs.size());
4773
4774 this->file->AddContentOf(sources.gigs[i]);
4775 }
4776 } catch (RIFF::Exception e) {
4777 throw RIFF::Exception(
4778 _("Error occurred while merging '") +
4779 filenames[i] +
4780 "': " +
4781 e.Message
4782 );
4783 } catch (...) {
4784 throw RIFF::Exception(
4785 _("Unknown exception occurred while merging '") +
4786 filenames[i] + "'"
4787 );
4788 }
4789
4790 // Finally save gig file persistently to disk ...
4791 //NOTE: requires that this gig file already has a filename !
4792 {
4793 std::cout << "Saving file\n" << std::flush;
4794 file_structure_to_be_changed_signal.emit(this->file);
4795
4796 progress_dialog = new ProgressDialog( //FIXME: memory leak!
4797 _("Saving") + Glib::ustring(" '") +
4798 Glib::filename_display_basename(this->filename) + "' ...",
4799 *this
4800 );
4801 #if HAS_GTKMM_SHOW_ALL_CHILDREN
4802 progress_dialog->show_all();
4803 #else
4804 progress_dialog->show();
4805 #endif
4806 saver = new Saver(this->file); //FIXME: memory leak!
4807 saver->signal_progress().connect(
4808 sigc::mem_fun(*this, &MainWindow::on_saver_progress));
4809 saver->signal_finished().connect(
4810 sigc::mem_fun(*this, &MainWindow::on_saver_finished));
4811 saver->signal_error().connect(
4812 sigc::mem_fun(*this, &MainWindow::on_saver_error));
4813 saver->launch();
4814 }
4815 }
4816
4817 void MainWindow::on_action_merge_files() {
4818 if (this->file->GetFileName().empty()) {
4819 Glib::ustring txt = _(
4820 "You seem to have a new .gig file open that has not been saved "
4821 "yet. You must save it somewhere before starting to merge it with "
4822 "other .gig files though, because during the merge operation the "
4823 "other files' sample data must be written on file level to the "
4824 "target .gig file."
4825 );
4826 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
4827 msg.run();
4828 return;
4829 }
4830
4831 Gtk::FileChooserDialog dialog(*this, _("Merge .gig files"));
4832 #if HAS_GTKMM_STOCK
4833 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
4834 #else
4835 dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
4836 #endif
4837 dialog.add_button(_("Merge"), Gtk::RESPONSE_OK);
4838 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
4839 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
4840 Gtk::FileFilter filter;
4841 filter.add_pattern("*.gig");
4842 #else
4843 Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
4844 filter->add_pattern("*.gig");
4845 #endif
4846 dialog.set_filter(filter);
4847 if (current_gig_dir != "") {
4848 dialog.set_current_folder(current_gig_dir);
4849 }
4850 dialog.set_select_multiple(true);
4851
4852 // show warning in the file picker dialog
4853 HBox descriptionArea;
4854 descriptionArea.set_spacing(15);
4855 Gtk::Image warningIcon;
4856 warningIcon.set_from_icon_name("dialog-warning",
4857 Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
4858 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
4859 #if GTKMM_MAJOR_VERSION < 3
4860 view::WrapLabel description;
4861 #else
4862 Gtk::Label description;
4863 description.set_line_wrap();
4864 #endif
4865 description.set_markup(_(
4866 "\nSelect at least one .gig file that shall be merged to the .gig file "
4867 "currently being open in gigedit.\n\n"
4868 "<b>Please Note:</b> Merging with other files will modify your "
4869 "currently open .gig file on file level! And be aware that the current "
4870 "merge algorithm does not detect duplicate samples yet. So if you are "
4871 "merging files which are using equivalent sample data, those "
4872 "equivalent samples will currently be treated as separate samples and "
4873 "will accordingly be stored separately in the target .gig file!"
4874 ));
4875 descriptionArea.pack_start(description);
4876 #if USE_GTKMM_BOX
4877 # warning No description area implemented for dialog on GTKMM 3
4878 #else
4879 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
4880 #endif
4881 #if HAS_GTKMM_SHOW_ALL_CHILDREN
4882 descriptionArea.show_all();
4883 #else
4884 descriptionArea.show();
4885 #endif
4886
4887 if (dialog.run() == Gtk::RESPONSE_OK) {
4888 printf("on_action_merge_files self=%p\n",
4889 static_cast<void*>(Glib::Threads::Thread::self()));
4890 std::vector<std::string> filenames = dialog.get_filenames();
4891
4892 // merge the selected files to the currently open .gig file
4893 try {
4894 mergeFiles(filenames);
4895 } catch (RIFF::Exception e) {
4896 Gtk::MessageDialog msg(*this, e.Message, false, Gtk::MESSAGE_ERROR);
4897 msg.run();
4898 }
4899
4900 // update GUI
4901 __refreshEntireGUI();
4902 }
4903 }
4904
4905 void MainWindow::set_file_is_shared(bool b) {
4906 this->file_is_shared = b;
4907
4908 if (file_is_shared) {
4909 m_AttachedStateLabel.set_label(_("live-mode"));
4910 m_AttachedStateImage.set(
4911 Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
4912 );
4913 } else {
4914 m_AttachedStateLabel.set_label(_("stand-alone"));
4915 m_AttachedStateImage.set(
4916 Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
4917 );
4918 }
4919
4920 {
4921 #if USE_GTKMM_BUILDER
4922 m_actionToggleSyncSamplerSelection->property_enabled() = b;
4923 #else
4924 Gtk::MenuItem* item = dynamic_cast<Gtk::MenuItem*>(
4925 uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection"));
4926 if (item) item->set_sensitive(b);
4927 #endif
4928 }
4929 }
4930
4931 void MainWindow::on_sample_ref_count_incremented(gig::Sample* sample, int offset) {
4932 if (!sample) return;
4933 sample_ref_count[sample] += offset;
4934 const int refcount = sample_ref_count[sample];
4935
4936 Glib::RefPtr<Gtk::TreeModel> model = m_TreeViewSamples.get_model();
4937 for (int g = 0; g < model->children().size(); ++g) {
4938 Gtk::TreeModel::Row rowGroup = model->children()[g];
4939 for (int s = 0; s < rowGroup.children().size(); ++s) {
4940 Gtk::TreeModel::Row rowSample = rowGroup.children()[s];
4941 if (rowSample[m_SamplesModel.m_col_sample] != sample) continue;
4942 rowSample[m_SamplesModel.m_col_refcount] = ToString(refcount) + " " + _("Refs.");
4943 rowSample[m_SamplesModel.m_color] = refcount ? "black" : "red";
4944 }
4945 }
4946 }
4947
4948 void MainWindow::on_sample_ref_changed(gig::Sample* oldSample, gig::Sample* newSample) {
4949 on_sample_ref_count_incremented(oldSample, -1);
4950 on_sample_ref_count_incremented(newSample, +1);
4951 }
4952
4953 void MainWindow::on_samples_to_be_removed(std::list<gig::Sample*> samples) {
4954 // just in case a new sample is added later with exactly the same memory
4955 // address, which would lead to incorrect refcount if not deleted here
4956 for (std::list<gig::Sample*>::const_iterator it = samples.begin();
4957 it != samples.end(); ++it)
4958 {
4959 sample_ref_count.erase(*it);
4960 }
4961 }
4962
4963 void MainWindow::show_samples_tab() {
4964 m_TreeViewNotebook.set_current_page(0);
4965 }
4966
4967 void MainWindow::show_intruments_tab() {
4968 m_TreeViewNotebook.set_current_page(1);
4969 }
4970
4971 void MainWindow::show_scripts_tab() {
4972 m_TreeViewNotebook.set_current_page(2);
4973 }
4974
4975 void MainWindow::select_instrument_by_dir(int dir) {
4976 if (!file) return;
4977 gig::Instrument* pInstrument = get_instrument();
4978 if (!pInstrument) {
4979 select_instrument( file->GetInstrument(0) );
4980 return;
4981 }
4982 for (int i = 0; file->GetInstrument(i); ++i) {
4983 if (file->GetInstrument(i) == pInstrument) {
4984 select_instrument( file->GetInstrument(i + dir) );
4985 return;
4986 }
4987 }
4988 }
4989
4990 void MainWindow::select_prev_instrument() {
4991 select_instrument_by_dir(-1);
4992 }
4993
4994 void MainWindow::select_next_instrument() {
4995 select_instrument_by_dir(1);
4996 }
4997
4998 void MainWindow::select_prev_region() {
4999 m_RegionChooser.select_prev_region();
5000 }
5001
5002 void MainWindow::select_next_region() {
5003 m_RegionChooser.select_next_region();
5004 }
5005
5006 void MainWindow::select_next_dim_rgn_zone() {
5007 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
5008 m_DimRegionChooser.select_next_dimzone();
5009 }
5010
5011 void MainWindow::select_prev_dim_rgn_zone() {
5012 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
5013 m_DimRegionChooser.select_prev_dimzone();
5014 }
5015
5016 void MainWindow::select_add_next_dim_rgn_zone() {
5017 m_DimRegionChooser.select_next_dimzone(true);
5018 }
5019
5020 void MainWindow::select_add_prev_dim_rgn_zone() {
5021 m_DimRegionChooser.select_prev_dimzone(true);
5022 }
5023
5024 void MainWindow::select_prev_dimension() {
5025 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
5026 m_DimRegionChooser.select_prev_dimension();
5027 }
5028
5029 void MainWindow::select_next_dimension() {
5030 if (m_DimRegionChooser.has_focus()) return; // avoid conflict with key stroke handler of DimenionRegionChooser
5031 m_DimRegionChooser.select_next_dimension();
5032 }
5033
5034 #define CLIPBOARD_DIMENSIONREGION_TARGET \
5035 ("libgig.DimensionRegion." + m_serializationArchive.rawDataFormat())
5036
5037 void MainWindow::copy_selected_dimrgn() {
5038 gig::DimensionRegion* pDimRgn = m_DimRegionChooser.get_main_dimregion();
5039 if (!pDimRgn) {
5040 updateClipboardPasteAvailable();
5041 updateClipboardCopyAvailable();
5042 return;
5043 }
5044
5045 std::vector<Gtk::TargetEntry> targets;
5046 targets.push_back( Gtk::TargetEntry(CLIPBOARD_DIMENSIONREGION_TARGET) );
5047
5048 Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
5049 clipboard->set(
5050 targets,
5051 sigc::mem_fun(*this, &MainWindow::on_clipboard_get),
5052 sigc::mem_fun(*this, &MainWindow::on_clipboard_clear)
5053 );
5054
5055 m_serializationArchive.serialize(pDimRgn);
5056
5057 updateClipboardPasteAvailable();
5058 }
5059
5060 void MainWindow::paste_copied_dimrgn() {
5061 Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
5062 clipboard->request_contents(
5063 CLIPBOARD_DIMENSIONREGION_TARGET,
5064 sigc::mem_fun(*this, &MainWindow::on_clipboard_received)
5065 );
5066 updateClipboardPasteAvailable();
5067 }
5068
5069 void MainWindow::adjust_clipboard_content() {
5070 MacroEditor* editor = new MacroEditor();
5071 editor->setMacro(&m_serializationArchive, true);
5072 editor->show();
5073 }
5074
5075 void MainWindow::updateClipboardPasteAvailable() {
5076 Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
5077 clipboard->request_targets(
5078 sigc::mem_fun(*this, &MainWindow::on_clipboard_received_targets)
5079 );
5080 }
5081
5082 void MainWindow::updateClipboardCopyAvailable() {
5083 bool bDimensionRegionCopyIsPossible = m_DimRegionChooser.get_main_dimregion();
5084 #if USE_GTKMM_BUILDER
5085 m_actionCopyDimRgn->property_enabled() = bDimensionRegionCopyIsPossible;
5086 #else
5087 static_cast<Gtk::MenuItem*>(
5088 uiManager->get_widget("/MenuBar/MenuEdit/CopyDimRgn")
5089 )->set_sensitive(bDimensionRegionCopyIsPossible);
5090 #endif
5091 }
5092
5093 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2
5094 void MainWindow::on_clipboard_owner_change(Gdk::EventOwnerChange& event) {
5095 #else
5096 void MainWindow::on_clipboard_owner_change(GdkEventOwnerChange* event) {
5097 #endif
5098 updateClipboardPasteAvailable();
5099 }
5100
5101 void MainWindow::on_clipboard_get(Gtk::SelectionData& selection_data, guint /*info*/) {
5102 const std::string target = selection_data.get_target();
5103 if (target == CLIPBOARD_DIMENSIONREGION_TARGET) {
5104 selection_data.set(
5105 CLIPBOARD_DIMENSIONREGION_TARGET, 8 /* "format": probably unused*/,
5106 &m_serializationArchive.rawData()[0],
5107 m_serializationArchive.rawData().size()
5108 );
5109 } else {
5110 std::cerr << "Clipboard: content for unknown target '" << target << "' requested\n";
5111 }
5112 }
5113
5114 void MainWindow::on_clipboard_clear() {
5115 m_serializationArchive.clear();
5116 updateClipboardPasteAvailable();
5117 updateClipboardCopyAvailable();
5118 }
5119
5120 //NOTE: Might throw exception !!!
5121 void MainWindow::applyMacro(Serialization::Archive& macro) {
5122 gig::DimensionRegion* pDimRgn = m_DimRegionChooser.get_main_dimregion();
5123 if (!pDimRgn) return;
5124
5125 for (std::set<gig::DimensionRegion*>::iterator itDimReg = dimreg_edit.dimregs.begin();
5126 itDimReg != dimreg_edit.dimregs.end(); ++itDimReg)
5127 {
5128 gig::DimensionRegion* pDimRgn = *itDimReg;
5129 DimRegionChangeGuard(this, pDimRgn);
5130 macro.deserialize(pDimRgn);
5131 }
5132 //region_changed()
5133 file_changed();
5134 dimreg_changed();
5135 }
5136
5137 void MainWindow::on_clipboard_received(const Gtk::SelectionData& selection_data) {
5138 const std::string target = selection_data.get_target();
5139 if (target == CLIPBOARD_DIMENSIONREGION_TARGET) {
5140 Glib::ustring errorText;
5141 try {
5142 m_serializationArchive.decode(
5143 selection_data.get_data(), selection_data.get_length()
5144 );
5145 applyMacro(m_serializationArchive);
5146 } catch (Serialization::Exception e) {
5147 errorText = e.Message;
5148 } catch (...) {
5149 errorText = _("Unknown exception while pasting DimensionRegion");
5150 }
5151 if (!errorText.empty()) {
5152 Glib::ustring txt = _("Pasting DimensionRegion failed:\n") + errorText;
5153 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
5154 msg.run();
5155 }
5156 }
5157 }
5158
5159 void MainWindow::on_clipboard_received_targets(const std::vector<Glib::ustring>& targets) {
5160 const bool bDimensionRegionPasteIsPossible =
5161 std::find(targets.begin(), targets.end(),
5162 CLIPBOARD_DIMENSIONREGION_TARGET) != targets.end();
5163
5164 #if USE_GTKMM_BUILDER
5165 m_actionPasteDimRgn->property_enabled() = bDimensionRegionPasteIsPossible;
5166 m_actionAdjustClipboard->property_enabled() = bDimensionRegionPasteIsPossible;
5167 #else
5168 static_cast<Gtk::MenuItem*>(
5169 uiManager->get_widget("/MenuBar/MenuEdit/PasteDimRgn")
5170 )->set_sensitive(bDimensionRegionPasteIsPossible);
5171
5172 static_cast<Gtk::MenuItem*>(
5173 uiManager->get_widget("/MenuBar/MenuEdit/AdjustClipboard")
5174 )->set_sensitive(bDimensionRegionPasteIsPossible);
5175 #endif
5176 }
5177
5178 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
5179 return file_structure_to_be_changed_signal;
5180 }
5181
5182 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
5183 return file_structure_changed_signal;
5184 }
5185
5186 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
5187 return samples_to_be_removed_signal;
5188 }
5189
5190 sigc::signal<void>& MainWindow::signal_samples_removed() {
5191 return samples_removed_signal;
5192 }
5193
5194 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
5195 return region_to_be_changed_signal;
5196 }
5197
5198 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
5199 return region_changed_signal;
5200 }
5201
5202 sigc::signal<void, gig::Sample*>& MainWindow::signal_sample_changed() {
5203 return sample_changed_signal;
5204 }
5205
5206 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
5207 return sample_ref_changed_signal;
5208 }
5209
5210 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
5211 return dimreg_to_be_changed_signal;
5212 }
5213
5214 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
5215 return dimreg_changed_signal;
5216 }
5217
5218 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
5219 return note_on_signal;
5220 }
5221
5222 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
5223 return note_off_signal;
5224 }
5225
5226 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_hit() {
5227 return m_RegionChooser.signal_keyboard_key_hit();
5228 }
5229
5230 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_released() {
5231 return m_RegionChooser.signal_keyboard_key_released();
5232 }
5233
5234 sigc::signal<void, gig::Instrument*>& MainWindow::signal_switch_sampler_instrument() {
5235 return switch_sampler_instrument_signal;
5236 }

  ViewVC Help
Powered by ViewVC