/[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 3711 - (show annotations) (download)
Fri Jan 10 14:22:25 2020 UTC (4 years, 2 months ago) by schoenebeck
File size: 227002 byte(s)
* Added boolean setting to main menu -> "View" -> "Instrument Properties
  by Double Click", which allows to prevent double clicking an
  instrument on the instruments list view from opening its properties
  dialog.

* Bumped version (1.1.1.svn11).

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

  ViewVC Help
Powered by ViewVC