/[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 1654 - (show annotations) (download)
Wed Jan 30 02:20:48 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 64203 byte(s)
* first step to make the virtual keyboard interactive: active keys of the
  sampler (in live-mode only of course) are highlighted on the virtual
  keyboard - NOTE: yet inaccurate draw of the keys and this mechanism
  yet only works on the first gigedit invocation by the sampler process,
  so this still has to be fixed

1 /*
2 * Copyright (C) 2006 - 2008 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
22 #include <gtkmm/filechooserdialog.h>
23 #include <gtkmm/messagedialog.h>
24 #include <gtkmm/stock.h>
25 #include <gtkmm/targetentry.h>
26 #include <gtkmm/main.h>
27 #include <gtkmm/toggleaction.h>
28
29 #include "global.h"
30
31 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
32 #define ABOUT_DIALOG
33 #include <gtkmm/aboutdialog.h>
34 #endif
35
36 #if (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION < 6) || GLIBMM_MAJOR_VERSION < 2
37 namespace Glib {
38 Glib::ustring filename_display_basename(const std::string& filename)
39 {
40 gchar* gstr = g_path_get_basename(filename.c_str());
41 Glib::ustring str(gstr);
42 g_free(gstr);
43 return Glib::filename_to_utf8(str);
44 }
45 }
46 #endif
47
48 #include <stdio.h>
49 #include <sndfile.h>
50
51 #include "mainwindow.h"
52
53 #include "../../gfx/status_attached.xpm"
54 #include "../../gfx/status_detached.xpm"
55
56 template<class T> inline std::string ToString(T o) {
57 std::stringstream ss;
58 ss << o;
59 return ss.str();
60 }
61
62 Table::Table(int x, int y) : Gtk::Table(x, y), rowno(0) { }
63
64 void Table::add(BoolEntry& boolentry)
65 {
66 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
67 Gtk::FILL, Gtk::SHRINK);
68 rowno++;
69 }
70
71 void Table::add(BoolEntryPlus6& boolentry)
72 {
73 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
74 Gtk::FILL, Gtk::SHRINK);
75 rowno++;
76 }
77
78 void Table::add(LabelWidget& prop)
79 {
80 attach(prop.label, 1, 2, rowno, rowno + 1,
81 Gtk::FILL, Gtk::SHRINK);
82 attach(prop.widget, 2, 3, rowno, rowno + 1,
83 Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK);
84 rowno++;
85 }
86
87 MainWindow::MainWindow() :
88 dimreg_label(_("Changes apply to:")),
89 dimreg_all_regions(_("all regions")),
90 dimreg_all_dimregs(_("all dimension splits")),
91 dimreg_stereo(_("both channels"))
92 {
93 // set_border_width(5);
94 // set_default_size(400, 200);
95
96
97 add(m_VBox);
98
99 // Handle selection
100 Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
101 tree_sel_ref->signal_changed().connect(
102 sigc::mem_fun(*this, &MainWindow::on_sel_change));
103
104 // m_TreeView.set_reorderable();
105
106 m_TreeView.signal_button_press_event().connect_notify(
107 sigc::mem_fun(*this, &MainWindow::on_button_release));
108
109 // Add the TreeView tab, inside a ScrolledWindow, with the button underneath:
110 m_ScrolledWindow.add(m_TreeView);
111 // m_ScrolledWindow.set_size_request(200, 600);
112 m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
113
114 m_ScrolledWindowSamples.add(m_TreeViewSamples);
115 m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
116
117
118 m_TreeViewNotebook.set_size_request(300);
119
120 m_HPaned.add1(m_TreeViewNotebook);
121 dimreg_hbox.add(dimreg_label);
122 dimreg_hbox.add(dimreg_all_regions);
123 dimreg_hbox.add(dimreg_all_dimregs);
124 dimreg_stereo.set_active();
125 dimreg_hbox.add(dimreg_stereo);
126 dimreg_vbox.add(dimreg_edit);
127 dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
128 m_HPaned.add2(dimreg_vbox);
129
130
131 m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples");
132 m_TreeViewNotebook.append_page(m_ScrolledWindow, "Instruments");
133
134
135 actionGroup = Gtk::ActionGroup::create();
136
137 actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
138 actionGroup->add(Gtk::Action::create("New", Gtk::Stock::NEW),
139 sigc::mem_fun(
140 *this, &MainWindow::on_action_file_new));
141 Glib::RefPtr<Gtk::Action> action =
142 Gtk::Action::create("Open", Gtk::Stock::OPEN);
143 action->property_label() = action->property_label() + "...";
144 actionGroup->add(action,
145 sigc::mem_fun(
146 *this, &MainWindow::on_action_file_open));
147 actionGroup->add(Gtk::Action::create("Save", Gtk::Stock::SAVE),
148 sigc::mem_fun(
149 *this, &MainWindow::on_action_file_save));
150 action = Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS);
151 action->property_label() = action->property_label() + "...";
152 actionGroup->add(action,
153 Gtk::AccelKey("<shift><control>s"),
154 sigc::mem_fun(
155 *this, &MainWindow::on_action_file_save_as));
156 actionGroup->add(Gtk::Action::create("Properties",
157 Gtk::Stock::PROPERTIES),
158 sigc::mem_fun(
159 *this, &MainWindow::on_action_file_properties));
160 actionGroup->add(Gtk::Action::create("InstrProperties",
161 Gtk::Stock::PROPERTIES),
162 sigc::mem_fun(
163 *this, &MainWindow::show_instr_props));
164 actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT),
165 sigc::mem_fun(
166 *this, &MainWindow::on_action_quit));
167 actionGroup->add(Gtk::Action::create("MenuInstrument", _("_Instrument")));
168
169 actionGroup->add(Gtk::Action::create("MenuView", _("_View")));
170 Glib::RefPtr<Gtk::ToggleAction> toggle_action =
171 Gtk::ToggleAction::create("Statusbar", _("_Statusbar"));
172 toggle_action->set_active(true);
173 actionGroup->add(toggle_action,
174 sigc::mem_fun(
175 *this, &MainWindow::on_action_view_status_bar));
176
177 action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP);
178 actionGroup->add(Gtk::Action::create("MenuHelp",
179 action->property_label()));
180 #ifdef ABOUT_DIALOG
181 actionGroup->add(Gtk::Action::create("About", Gtk::Stock::ABOUT),
182 sigc::mem_fun(
183 *this, &MainWindow::on_action_help_about));
184 #endif
185 actionGroup->add(
186 Gtk::Action::create("AddInstrument", _("Add _Instrument")),
187 sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
188 );
189 actionGroup->add(
190 Gtk::Action::create("RemoveInstrument", Gtk::Stock::REMOVE),
191 sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
192 );
193
194 // sample right-click popup actions
195 actionGroup->add(
196 Gtk::Action::create("SampleProperties", Gtk::Stock::PROPERTIES),
197 sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
198 );
199 actionGroup->add(
200 Gtk::Action::create("AddGroup", _("Add _Group")),
201 sigc::mem_fun(*this, &MainWindow::on_action_add_group)
202 );
203 actionGroup->add(
204 Gtk::Action::create("AddSample", _("Add _Sample(s)")),
205 sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
206 );
207 actionGroup->add(
208 Gtk::Action::create("RemoveSample", Gtk::Stock::REMOVE),
209 sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
210 );
211
212 uiManager = Gtk::UIManager::create();
213 uiManager->insert_action_group(actionGroup);
214 add_accel_group(uiManager->get_accel_group());
215
216 Glib::ustring ui_info =
217 "<ui>"
218 " <menubar name='MenuBar'>"
219 " <menu action='MenuFile'>"
220 " <menuitem action='New'/>"
221 " <menuitem action='Open'/>"
222 " <separator/>"
223 " <menuitem action='Save'/>"
224 " <menuitem action='SaveAs'/>"
225 " <separator/>"
226 " <menuitem action='Properties'/>"
227 " <separator/>"
228 " <menuitem action='Quit'/>"
229 " </menu>"
230 " <menu action='MenuInstrument'>"
231 " </menu>"
232 " <menu action='MenuView'>"
233 " <menuitem action='Statusbar'/>"
234 " </menu>"
235 #ifdef ABOUT_DIALOG
236 " <menu action='MenuHelp'>"
237 " <menuitem action='About'/>"
238 " </menu>"
239 #endif
240 " </menubar>"
241 " <popup name='PopupMenu'>"
242 " <menuitem action='InstrProperties'/>"
243 " <menuitem action='AddInstrument'/>"
244 " <separator/>"
245 " <menuitem action='RemoveInstrument'/>"
246 " </popup>"
247 " <popup name='SamplePopupMenu'>"
248 " <menuitem action='SampleProperties'/>"
249 " <menuitem action='AddGroup'/>"
250 " <menuitem action='AddSample'/>"
251 " <separator/>"
252 " <menuitem action='RemoveSample'/>"
253 " </popup>"
254 "</ui>";
255 uiManager->add_ui_from_string(ui_info);
256
257 popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
258
259 Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar");
260 m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
261 m_VBox.pack_start(m_HPaned);
262 m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
263 m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
264 m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
265
266 // Status Bar:
267 m_StatusBar.pack_start(m_AttachedStateLabel, Gtk::PACK_SHRINK);
268 m_StatusBar.pack_start(m_AttachedStateImage, Gtk::PACK_SHRINK);
269 m_StatusBar.show();
270
271 m_RegionChooser.signal_region_selected().connect(
272 sigc::mem_fun(*this, &MainWindow::region_changed) );
273 m_DimRegionChooser.signal_dimregion_selected().connect(
274 sigc::mem_fun(*this, &MainWindow::dimreg_changed) );
275
276
277 // Create the Tree model:
278 m_refTreeModel = Gtk::ListStore::create(m_Columns);
279 m_TreeView.set_model(m_refTreeModel);
280 m_refTreeModel->signal_row_changed().connect(
281 sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
282 );
283
284 // Add the TreeView's view columns:
285 m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name);
286 m_TreeView.set_headers_visible(false);
287
288 // create samples treeview (including its data model)
289 m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
290 m_TreeViewSamples.set_model(m_refSamplesTreeModel);
291 // m_TreeViewSamples.set_reorderable();
292 m_TreeViewSamples.append_column_editable("Samples", m_SamplesModel.m_col_name);
293 m_TreeViewSamples.set_headers_visible(false);
294 m_TreeViewSamples.signal_button_press_event().connect_notify(
295 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
296 );
297 m_refSamplesTreeModel->signal_row_changed().connect(
298 sigc::mem_fun(*this, &MainWindow::sample_name_changed)
299 );
300
301 // establish drag&drop between samples tree view and dimension region 'Sample' text entry
302 std::list<Gtk::TargetEntry> drag_target_gig_sample;
303 drag_target_gig_sample.push_back( Gtk::TargetEntry("gig::Sample") );
304 m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
305 m_TreeViewSamples.signal_drag_begin().connect(
306 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_begin)
307 );
308 m_TreeViewSamples.signal_drag_data_get().connect(
309 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get)
310 );
311 dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample);
312 dimreg_edit.wSample->signal_drag_data_received().connect(
313 sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
314 );
315 dimreg_edit.signal_dimreg_changed().connect(
316 sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
317 m_RegionChooser.signal_instrument_changed().connect(
318 sigc::mem_fun(*this, &MainWindow::file_changed));
319 m_DimRegionChooser.signal_region_changed().connect(
320 sigc::mem_fun(*this, &MainWindow::file_changed));
321 instrumentProps.signal_instrument_changed().connect(
322 sigc::mem_fun(*this, &MainWindow::file_changed));
323 propDialog.signal_info_changed().connect(
324 sigc::mem_fun(*this, &MainWindow::file_changed));
325
326 dimreg_edit.signal_dimreg_to_be_changed().connect(
327 dimreg_to_be_changed_signal.make_slot());
328 dimreg_edit.signal_dimreg_changed().connect(
329 dimreg_changed_signal.make_slot());
330 dimreg_edit.signal_sample_ref_changed().connect(
331 sample_ref_changed_signal.make_slot());
332
333 m_RegionChooser.signal_instrument_struct_to_be_changed().connect(
334 sigc::hide(
335 sigc::bind(
336 file_structure_to_be_changed_signal.make_slot(),
337 sigc::ref(this->file)
338 )
339 )
340 );
341 m_RegionChooser.signal_instrument_struct_changed().connect(
342 sigc::hide(
343 sigc::bind(
344 file_structure_changed_signal.make_slot(),
345 sigc::ref(this->file)
346 )
347 )
348 );
349 m_RegionChooser.signal_region_to_be_changed().connect(
350 region_to_be_changed_signal.make_slot());
351 m_RegionChooser.signal_region_changed_signal().connect(
352 region_changed_signal.make_slot());
353
354 note_on_signal.connect(
355 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_on_event));
356 note_off_signal.connect(
357 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_off_event));
358
359 dimreg_all_regions.signal_toggled().connect(
360 sigc::mem_fun(*this, &MainWindow::update_dimregs));
361 dimreg_all_dimregs.signal_toggled().connect(
362 sigc::mem_fun(*this, &MainWindow::dimreg_all_dimregs_toggled));
363 dimreg_stereo.signal_toggled().connect(
364 sigc::mem_fun(*this, &MainWindow::update_dimregs));
365
366 file = 0;
367 file_is_changed = false;
368 set_file_is_shared(false);
369
370 show_all_children();
371
372 // start with a new gig file by default
373 on_action_file_new();
374 }
375
376 MainWindow::~MainWindow()
377 {
378 }
379
380 bool MainWindow::on_delete_event(GdkEventAny* event)
381 {
382 return !file_is_shared && file_is_changed && !close_confirmation_dialog();
383 }
384
385 void MainWindow::on_action_quit()
386 {
387 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
388 hide();
389 }
390
391 void MainWindow::region_changed()
392 {
393 m_DimRegionChooser.set_region(m_RegionChooser.get_region());
394 }
395
396 gig::Instrument* MainWindow::get_instrument()
397 {
398 gig::Instrument* instrument = 0;
399 Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
400
401 Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();
402 if (it) {
403 Gtk::TreeModel::Row row = *it;
404 instrument = row[m_Columns.m_col_instr];
405 }
406 return instrument;
407 }
408
409 void MainWindow::add_region_to_dimregs(gig::Region* region, bool stereo, bool all_dimregs)
410 {
411 if (all_dimregs) {
412 for (int i = 0 ; i < region->DimensionRegions ; i++) {
413 if (region->pDimensionRegions[i]) {
414 dimreg_edit.dimregs.insert(region->pDimensionRegions[i]);
415 }
416 }
417 } else {
418 m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
419 }
420 }
421
422 void MainWindow::update_dimregs()
423 {
424 dimreg_edit.dimregs.clear();
425 bool all_regions = dimreg_all_regions.get_active();
426 bool stereo = dimreg_stereo.get_active();
427 bool all_dimregs = dimreg_all_dimregs.get_active();
428
429 if (all_regions) {
430 gig::Instrument* instrument = get_instrument();
431 if (instrument) {
432 for (gig::Region* region = instrument->GetFirstRegion() ;
433 region ;
434 region = instrument->GetNextRegion()) {
435 add_region_to_dimregs(region, stereo, all_dimregs);
436 }
437 }
438 } else {
439 gig::Region* region = m_RegionChooser.get_region();
440 if (region) {
441 add_region_to_dimregs(region, stereo, all_dimregs);
442 }
443 }
444 }
445
446 void MainWindow::dimreg_all_dimregs_toggled()
447 {
448 dimreg_stereo.set_sensitive(!dimreg_all_dimregs.get_active());
449 update_dimregs();
450 }
451
452 void MainWindow::dimreg_changed()
453 {
454 update_dimregs();
455 dimreg_edit.set_dim_region(m_DimRegionChooser.get_dimregion());
456 }
457
458 void MainWindow::on_sel_change()
459 {
460 m_RegionChooser.set_instrument(get_instrument());
461 }
462
463 void loader_progress_callback(gig::progress_t* progress)
464 {
465 Loader* loader = static_cast<Loader*>(progress->custom);
466 loader->progress_callback(progress->factor);
467 }
468
469 void Loader::progress_callback(float fraction)
470 {
471 {
472 Glib::Mutex::Lock lock(progressMutex);
473 progress = fraction;
474 }
475 progress_dispatcher();
476 }
477
478 void Loader::thread_function()
479 {
480 printf("thread_function self=%x\n", Glib::Thread::self());
481 printf("Start %s\n", filename);
482 RIFF::File* riff = new RIFF::File(filename);
483 gig = new gig::File(riff);
484 gig::progress_t progress;
485 progress.callback = loader_progress_callback;
486 progress.custom = this;
487
488 gig->GetInstrument(0, &progress);
489 printf("End\n");
490 finished_dispatcher();
491 }
492
493 Loader::Loader(const char* filename)
494 : thread(0), filename(filename)
495 {
496 }
497
498 void Loader::launch()
499 {
500 thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true);
501 printf("launch thread=%x\n", thread);
502 }
503
504 float Loader::get_progress()
505 {
506 float res;
507 {
508 Glib::Mutex::Lock lock(progressMutex);
509 res = progress;
510 }
511 return res;
512 }
513
514 Glib::Dispatcher& Loader::signal_progress()
515 {
516 return progress_dispatcher;
517 }
518
519 Glib::Dispatcher& Loader::signal_finished()
520 {
521 return finished_dispatcher;
522 }
523
524 LoadDialog::LoadDialog(const Glib::ustring& title, Gtk::Window& parent)
525 : Gtk::Dialog(title, parent, true)
526 {
527 get_vbox()->pack_start(progressBar);
528 show_all_children();
529 }
530
531 // Clear all GUI elements / controls. This method is typically called
532 // before a new .gig file is to be created or to be loaded.
533 void MainWindow::__clear() {
534 // remove all entries from "Instrument" menu
535 Gtk::MenuItem* instrument_menu =
536 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument"));
537 instrument_menu->hide();
538 for (int i = 0; i < instrument_menu->get_submenu()->items().size(); i++) {
539 delete &instrument_menu->get_submenu()->items()[i];
540 }
541 instrument_menu->get_submenu()->items().clear();
542 // forget all samples that ought to be imported
543 m_SampleImportQueue.clear();
544 // clear the samples and instruments tree views
545 m_refTreeModel->clear();
546 m_refSamplesTreeModel->clear();
547 // free libgig's gig::File instance
548 if (file && !file_is_shared) delete file;
549 file = NULL;
550 set_file_is_shared(false);
551 }
552
553 void MainWindow::on_action_file_new()
554 {
555 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
556
557 if (file_is_shared && !leaving_shared_mode_dialog()) return;
558
559 // clear all GUI elements
560 __clear();
561 // create a new .gig file (virtually yet)
562 gig::File* pFile = new gig::File;
563 // already add one new instrument by default
564 gig::Instrument* pInstrument = pFile->AddInstrument();
565 pInstrument->pInfo->Name = "Unnamed Instrument";
566 // update GUI with that new gig::File
567 load_gig(pFile, 0 /*no file name yet*/);
568 }
569
570 bool MainWindow::close_confirmation_dialog()
571 {
572 gchar* msg = g_strdup_printf(_("Save changes to \"%s\" before closing?"),
573 Glib::filename_display_basename(filename).c_str());
574 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
575 g_free(msg);
576 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
577 dialog.set_secondary_text(_("If you close without saving, your changes will be lost."));
578 #endif
579 dialog.add_button(_("Close _Without Saving"), Gtk::RESPONSE_NO);
580 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
581 dialog.add_button(file_has_name ? Gtk::Stock::SAVE : Gtk::Stock::SAVE_AS, Gtk::RESPONSE_YES);
582 dialog.set_default_response(Gtk::RESPONSE_YES);
583 int response = dialog.run();
584 dialog.hide();
585 if (response == Gtk::RESPONSE_YES) return file_save();
586 return response != Gtk::RESPONSE_CANCEL;
587 }
588
589 bool MainWindow::leaving_shared_mode_dialog() {
590 Glib::ustring msg = _("Detach from sampler and proceed working stand-alone?");
591 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
592 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
593 dialog.set_secondary_text(
594 _("If you proceed to work on another instrument file, it won't be "
595 "used by the sampler until you tell the sampler explicitly to "
596 "load it.")
597 );
598 #endif
599 dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
600 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
601 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
602 int response = dialog.run();
603 dialog.hide();
604 return response == Gtk::RESPONSE_YES;
605 }
606
607 void MainWindow::on_action_file_open()
608 {
609 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
610
611 if (file_is_shared && !leaving_shared_mode_dialog()) return;
612
613 Gtk::FileChooserDialog dialog(*this, _("Open file"));
614 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
615 dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
616 dialog.set_default_response(Gtk::RESPONSE_OK);
617 Gtk::FileFilter filter;
618 filter.add_pattern("*.gig");
619 dialog.set_filter(filter);
620 if (current_dir != "") {
621 dialog.set_current_folder(current_dir);
622 }
623 if (dialog.run() == Gtk::RESPONSE_OK) {
624 std::string filename = dialog.get_filename();
625 printf("filename=%s\n", filename.c_str());
626 printf("on_action_file_open self=%x\n", Glib::Thread::self());
627 load_file(filename.c_str());
628 current_dir = Glib::path_get_dirname(filename);
629 }
630 }
631
632 void MainWindow::load_file(const char* name)
633 {
634 __clear();
635 load_dialog = new LoadDialog("Loading...", *this);
636 load_dialog->show_all();
637 loader = new Loader(strdup(name));
638 loader->signal_progress().connect(
639 sigc::mem_fun(*this, &MainWindow::on_loader_progress));
640 loader->signal_finished().connect(
641 sigc::mem_fun(*this, &MainWindow::on_loader_finished));
642 loader->launch();
643 }
644
645 void MainWindow::load_instrument(gig::Instrument* instr) {
646 if (!instr) {
647 Glib::ustring txt = "Provided instrument is NULL!\n";
648 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
649 msg.run();
650 Gtk::Main::quit();
651 }
652 // clear all GUI elements
653 __clear();
654 // load the instrument
655 gig::File* pFile = (gig::File*) instr->GetParent();
656 load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
657 //TODO: automatically select the given instrument
658 }
659
660 void MainWindow::on_loader_progress()
661 {
662 load_dialog->set_fraction(loader->get_progress());
663 }
664
665 void MainWindow::on_loader_finished()
666 {
667 printf("Loader finished!\n");
668 printf("on_loader_finished self=%x\n", Glib::Thread::self());
669 load_gig(loader->gig, loader->filename);
670 load_dialog->hide();
671 }
672
673 void MainWindow::on_action_file_save()
674 {
675 file_save();
676 }
677
678 bool MainWindow::check_if_savable()
679 {
680 if (!file) return false;
681
682 if (!file->GetFirstSample()) {
683 Gtk::MessageDialog(*this, _("The file could not be saved "
684 "because it contains no samples"),
685 false, Gtk::MESSAGE_ERROR).run();
686 return false;
687 }
688
689 for (gig::Instrument* instrument = file->GetFirstInstrument() ; instrument ;
690 instrument = file->GetNextInstrument()) {
691 if (!instrument->GetFirstRegion()) {
692 Gtk::MessageDialog(*this, _("The file could not be saved "
693 "because there are instruments "
694 "that have no regions"),
695 false, Gtk::MESSAGE_ERROR).run();
696 return false;
697 }
698 }
699 return true;
700 }
701
702 bool MainWindow::file_save()
703 {
704 if (!check_if_savable()) return false;
705 if (!file_is_shared && !file_has_name) return file_save_as();
706
707 std::cout << "Saving file\n" << std::flush;
708 file_structure_to_be_changed_signal.emit(this->file);
709 try {
710 file->Save();
711 if (file_is_changed) {
712 set_title(get_title().substr(1));
713 file_is_changed = false;
714 }
715 } catch (RIFF::Exception e) {
716 file_structure_changed_signal.emit(this->file);
717 Glib::ustring txt = _("Could not save file: ") + e.Message;
718 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
719 msg.run();
720 return false;
721 }
722 std::cout << "Saving file done\n" << std::flush;
723 __import_queued_samples();
724 file_structure_changed_signal.emit(this->file);
725 return true;
726 }
727
728 void MainWindow::on_action_file_save_as()
729 {
730 if (!check_if_savable()) return;
731 file_save_as();
732 }
733
734 bool MainWindow::file_save_as()
735 {
736 Gtk::FileChooserDialog dialog(*this, _("Save as"), Gtk::FILE_CHOOSER_ACTION_SAVE);
737 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
738 dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
739 dialog.set_default_response(Gtk::RESPONSE_OK);
740
741 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 8) || GTKMM_MAJOR_VERSION > 2
742 dialog.set_do_overwrite_confirmation();
743 // TODO: an overwrite dialog for gtkmm < 2.8
744 #endif
745 Gtk::FileFilter filter;
746 filter.add_pattern("*.gig");
747 dialog.set_filter(filter);
748
749 if (Glib::path_is_absolute(filename)) {
750 dialog.set_filename(filename);
751 } else if (current_dir != "") {
752 dialog.set_current_folder(current_dir);
753 }
754 dialog.set_current_name(Glib::filename_display_basename(filename));
755
756 if (dialog.run() == Gtk::RESPONSE_OK) {
757 file_structure_to_be_changed_signal.emit(this->file);
758 try {
759 std::string filename = dialog.get_filename();
760 if (!Glib::str_has_suffix(filename, ".gig")) {
761 filename += ".gig";
762 }
763 printf("filename=%s\n", filename.c_str());
764 file->Save(filename);
765 this->filename = filename;
766 current_dir = Glib::path_get_dirname(filename);
767 set_title(Glib::filename_display_basename(filename));
768 file_has_name = true;
769 file_is_changed = false;
770 } catch (RIFF::Exception e) {
771 file_structure_changed_signal.emit(this->file);
772 Glib::ustring txt = _("Could not save file: ") + e.Message;
773 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
774 msg.run();
775 return false;
776 }
777 __import_queued_samples();
778 file_structure_changed_signal.emit(this->file);
779 return true;
780 }
781 return false;
782 }
783
784 // actually write the sample(s)' data to the gig file
785 void MainWindow::__import_queued_samples() {
786 std::cout << "Starting sample import\n" << std::flush;
787 Glib::ustring error_files;
788 printf("Samples to import: %d\n", m_SampleImportQueue.size());
789 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
790 iter != m_SampleImportQueue.end(); ) {
791 printf("Importing sample %s\n",(*iter).sample_path.c_str());
792 SF_INFO info;
793 info.format = 0;
794 SNDFILE* hFile = sf_open((*iter).sample_path.c_str(), SFM_READ, &info);
795 try {
796 if (!hFile) throw std::string("could not open file");
797 // determine sample's bit depth
798 int bitdepth;
799 switch (info.format & 0xff) {
800 case SF_FORMAT_PCM_S8:
801 case SF_FORMAT_PCM_16:
802 case SF_FORMAT_PCM_U8:
803 bitdepth = 16;
804 break;
805 case SF_FORMAT_PCM_24:
806 case SF_FORMAT_PCM_32:
807 case SF_FORMAT_FLOAT:
808 case SF_FORMAT_DOUBLE:
809 bitdepth = 24;
810 break;
811 default:
812 sf_close(hFile); // close sound file
813 throw std::string("format not supported"); // unsupported subformat (yet?)
814 }
815
816 const int bufsize = 10000;
817 switch (bitdepth) {
818 case 16: {
819 short* buffer = new short[bufsize * info.channels];
820 sf_count_t cnt = info.frames;
821 while (cnt) {
822 // libsndfile does the conversion for us (if needed)
823 int n = sf_readf_short(hFile, buffer, bufsize);
824 // write from buffer directly (physically) into .gig file
825 iter->gig_sample->Write(buffer, n);
826 cnt -= n;
827 }
828 delete[] buffer;
829 break;
830 }
831 case 24: {
832 int* srcbuf = new int[bufsize * info.channels];
833 uint8_t* dstbuf = new uint8_t[bufsize * 3 * info.channels];
834 sf_count_t cnt = info.frames;
835 while (cnt) {
836 // libsndfile returns 32 bits, convert to 24
837 int n = sf_readf_int(hFile, srcbuf, bufsize);
838 int j = 0;
839 for (int i = 0 ; i < n * info.channels ; i++) {
840 dstbuf[j++] = srcbuf[i] >> 8;
841 dstbuf[j++] = srcbuf[i] >> 16;
842 dstbuf[j++] = srcbuf[i] >> 24;
843 }
844 // write from buffer directly (physically) into .gig file
845 iter->gig_sample->Write(dstbuf, n);
846 cnt -= n;
847 }
848 delete[] srcbuf;
849 delete[] dstbuf;
850 break;
851 }
852 }
853 // cleanup
854 sf_close(hFile);
855 // on success we remove the sample from the import queue,
856 // otherwise keep it, maybe it works the next time ?
857 std::list<SampleImportItem>::iterator cur = iter;
858 ++iter;
859 m_SampleImportQueue.erase(cur);
860 } catch (std::string what) {
861 // remember the files that made trouble (and their cause)
862 if (error_files.size()) error_files += "\n";
863 error_files += (*iter).sample_path += " (" + what + ")";
864 ++iter;
865 }
866 }
867 // show error message box when some sample(s) could not be imported
868 if (error_files.size()) {
869 Glib::ustring txt = _("Could not import the following sample(s):\n") + error_files;
870 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
871 msg.run();
872 }
873 }
874
875 void MainWindow::on_action_file_properties()
876 {
877 propDialog.show();
878 propDialog.deiconify();
879 }
880
881 void MainWindow::on_action_help_about()
882 {
883 #ifdef ABOUT_DIALOG
884 Gtk::AboutDialog dialog;
885 dialog.set_version(VERSION);
886 dialog.set_copyright("Copyright (C) 2006,2007 Andreas Persson");
887 dialog.set_comments(
888 "Released under the GNU General Public License.\n"
889 "\n"
890 "Please notice that this is still a very young instrument editor. "
891 "So better backup your Gigasampler files before editing them with "
892 "this application.\n"
893 "\n"
894 "Please report bugs to: http://bugs.linuxsampler.org"
895 );
896 dialog.set_website("http://www.linuxsampler.org");
897 dialog.set_website_label("http://www.linuxsampler.org");
898 dialog.run();
899 #endif
900 }
901
902 PropDialog::PropDialog()
903 : table(2,1),
904 eName("Name"),
905 eCreationDate("Creation date"),
906 eComments("Comments"),
907 eProduct("Product"),
908 eCopyright("Copyright"),
909 eArtists("Artists"),
910 eGenre("Genre"),
911 eKeywords("Keywords"),
912 eEngineer("Engineer"),
913 eTechnician("Technician"),
914 eSoftware("Software"),
915 eMedium("Medium"),
916 eSource("Source"),
917 eSourceForm("Source form"),
918 eCommissioned("Commissioned"),
919 eSubject("Subject"),
920 quitButton(Gtk::Stock::CLOSE),
921 update_model(0)
922 {
923 set_title("File Properties");
924 eName.set_width_chars(50);
925
926 connect(eName, &DLS::Info::Name);
927 connect(eCreationDate, &DLS::Info::CreationDate);
928 connect(eComments, &DLS::Info::Comments);
929 connect(eProduct, &DLS::Info::Product);
930 connect(eCopyright, &DLS::Info::Copyright);
931 connect(eArtists, &DLS::Info::Artists);
932 connect(eGenre, &DLS::Info::Genre);
933 connect(eKeywords, &DLS::Info::Keywords);
934 connect(eEngineer, &DLS::Info::Engineer);
935 connect(eTechnician, &DLS::Info::Technician);
936 connect(eSoftware, &DLS::Info::Software);
937 connect(eMedium, &DLS::Info::Medium);
938 connect(eSource, &DLS::Info::Source);
939 connect(eSourceForm, &DLS::Info::SourceForm);
940 connect(eCommissioned, &DLS::Info::Commissioned);
941 connect(eSubject, &DLS::Info::Subject);
942
943 table.add(eName);
944 table.add(eCreationDate);
945 table.add(eComments);
946 table.add(eProduct);
947 table.add(eCopyright);
948 table.add(eArtists);
949 table.add(eGenre);
950 table.add(eKeywords);
951 table.add(eEngineer);
952 table.add(eTechnician);
953 table.add(eSoftware);
954 table.add(eMedium);
955 table.add(eSource);
956 table.add(eSourceForm);
957 table.add(eCommissioned);
958 table.add(eSubject);
959
960 table.set_col_spacings(5);
961 add(vbox);
962 table.set_border_width(5);
963 vbox.add(table);
964 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
965 buttonBox.set_layout(Gtk::BUTTONBOX_END);
966 buttonBox.set_border_width(5);
967 buttonBox.show();
968 buttonBox.pack_start(quitButton);
969 quitButton.set_flags(Gtk::CAN_DEFAULT);
970 quitButton.grab_focus();
971 quitButton.signal_clicked().connect(
972 sigc::mem_fun(*this, &PropDialog::hide));
973
974 quitButton.show();
975 vbox.show();
976 show_all_children();
977 }
978
979 void PropDialog::set_info(DLS::Info* info)
980 {
981 this->info = info;
982 update_model++;
983 eName.set_value(info->Name);
984 eCreationDate.set_value(info->CreationDate);
985 eComments.set_value(info->Comments);
986 eProduct.set_value(info->Product);
987 eCopyright.set_value(info->Copyright);
988 eArtists.set_value(info->Artists);
989 eGenre.set_value(info->Genre);
990 eKeywords.set_value(info->Keywords);
991 eEngineer.set_value(info->Engineer);
992 eTechnician.set_value(info->Technician);
993 eSoftware.set_value(info->Software);
994 eMedium.set_value(info->Medium);
995 eSource.set_value(info->Source);
996 eSourceForm.set_value(info->SourceForm);
997 eCommissioned.set_value(info->Commissioned);
998 eSubject.set_value(info->Subject);
999 update_model--;
1000 }
1001
1002 sigc::signal<void>& PropDialog::signal_info_changed()
1003 {
1004 return info_changed;
1005 }
1006
1007 void InstrumentProps::set_IsDrum(bool value)
1008 {
1009 instrument->IsDrum = value;
1010 }
1011
1012 void InstrumentProps::set_MIDIBank(uint16_t value)
1013 {
1014 instrument->MIDIBank = value;
1015 }
1016
1017 void InstrumentProps::set_MIDIProgram(uint32_t value)
1018 {
1019 instrument->MIDIProgram = value;
1020 }
1021
1022 void InstrumentProps::set_DimensionKeyRange_low(uint8_t value)
1023 {
1024 instrument->DimensionKeyRange.low = value;
1025 if (value > instrument->DimensionKeyRange.high) {
1026 eDimensionKeyRangeHigh.set_value(value);
1027 }
1028 }
1029
1030 void InstrumentProps::set_DimensionKeyRange_high(uint8_t value)
1031 {
1032 instrument->DimensionKeyRange.high = value;
1033 if (value < instrument->DimensionKeyRange.low) {
1034 eDimensionKeyRangeLow.set_value(value);
1035 }
1036 }
1037
1038 InstrumentProps::InstrumentProps()
1039 : table(2,1),
1040 quitButton(Gtk::Stock::CLOSE),
1041 eName("Name"),
1042 eIsDrum("Is drum"),
1043 eMIDIBank("MIDI bank", 0, 16383),
1044 eMIDIProgram("MIDI program"),
1045 eAttenuation("Attenuation", 0, 96, 0, 1),
1046 eGainPlus6("Gain +6dB", eAttenuation, -6),
1047 eEffectSend("Effect send", 0, 65535),
1048 eFineTune("Fine tune", -8400, 8400),
1049 ePitchbendRange("Pitchbend range", 0, 12),
1050 ePianoReleaseMode("Piano release mode"),
1051 eDimensionKeyRangeLow("Dimension key range low"),
1052 eDimensionKeyRangeHigh("Dimension key range high"),
1053 update_model(0)
1054 {
1055 set_title("Instrument Properties");
1056
1057 connect(eIsDrum, &InstrumentProps::set_IsDrum);
1058 connect(eMIDIBank, &InstrumentProps::set_MIDIBank);
1059 connect(eMIDIProgram, &InstrumentProps::set_MIDIProgram);
1060 connect(eAttenuation, &gig::Instrument::Attenuation);
1061 connect(eGainPlus6, &gig::Instrument::Attenuation);
1062 connect(eEffectSend, &gig::Instrument::EffectSend);
1063 connect(eFineTune, &gig::Instrument::FineTune);
1064 connect(ePitchbendRange, &gig::Instrument::PitchbendRange);
1065 connect(ePianoReleaseMode, &gig::Instrument::PianoReleaseMode);
1066 connect(eDimensionKeyRangeLow,
1067 &InstrumentProps::set_DimensionKeyRange_low);
1068 connect(eDimensionKeyRangeHigh,
1069 &InstrumentProps::set_DimensionKeyRange_high);
1070
1071 table.set_col_spacings(5);
1072
1073 table.add(eName);
1074 table.add(eIsDrum);
1075 table.add(eMIDIBank);
1076 table.add(eMIDIProgram);
1077 table.add(eAttenuation);
1078 table.add(eGainPlus6);
1079 table.add(eEffectSend);
1080 table.add(eFineTune);
1081 table.add(ePitchbendRange);
1082 table.add(ePianoReleaseMode);
1083 table.add(eDimensionKeyRangeLow);
1084 table.add(eDimensionKeyRangeHigh);
1085
1086 add(vbox);
1087 table.set_border_width(5);
1088 vbox.pack_start(table);
1089 table.show();
1090 vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
1091 buttonBox.set_layout(Gtk::BUTTONBOX_END);
1092 buttonBox.set_border_width(5);
1093 buttonBox.show();
1094 buttonBox.pack_start(quitButton);
1095 quitButton.set_flags(Gtk::CAN_DEFAULT);
1096 quitButton.grab_focus();
1097
1098 quitButton.signal_clicked().connect(
1099 sigc::mem_fun(*this, &InstrumentProps::hide));
1100
1101 quitButton.show();
1102 vbox.show();
1103 show_all_children();
1104 }
1105
1106 void InstrumentProps::set_instrument(gig::Instrument* instrument)
1107 {
1108 this->instrument = instrument;
1109
1110 update_model++;
1111 eName.set_value(instrument->pInfo->Name);
1112 eIsDrum.set_value(instrument->IsDrum);
1113 eMIDIBank.set_value(instrument->MIDIBank);
1114 eMIDIProgram.set_value(instrument->MIDIProgram);
1115 eAttenuation.set_value(instrument->Attenuation);
1116 eGainPlus6.set_value(instrument->Attenuation);
1117 eEffectSend.set_value(instrument->EffectSend);
1118 eFineTune.set_value(instrument->FineTune);
1119 ePitchbendRange.set_value(instrument->PitchbendRange);
1120 ePianoReleaseMode.set_value(instrument->PianoReleaseMode);
1121 eDimensionKeyRangeLow.set_value(instrument->DimensionKeyRange.low);
1122 eDimensionKeyRangeHigh.set_value(instrument->DimensionKeyRange.high);
1123 update_model--;
1124 }
1125
1126 sigc::signal<void>& InstrumentProps::signal_instrument_changed()
1127 {
1128 return instrument_changed;
1129 }
1130
1131 void MainWindow::file_changed()
1132 {
1133 if (file && !file_is_changed) {
1134 set_title("*" + get_title());
1135 file_is_changed = true;
1136 }
1137 }
1138
1139 void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument)
1140 {
1141 file = 0;
1142 set_file_is_shared(isSharedInstrument);
1143
1144 this->filename = filename ? filename : _("Unsaved Gig File");
1145 set_title(Glib::filename_display_basename(this->filename));
1146 file_has_name = filename;
1147 file_is_changed = false;
1148
1149 propDialog.set_info(gig->pInfo);
1150
1151 Gtk::MenuItem* instrument_menu =
1152 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument"));
1153
1154 int instrument_index = 0;
1155 Gtk::RadioMenuItem::Group instrument_group;
1156 for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ;
1157 instrument = gig->GetNextInstrument()) {
1158 Gtk::TreeModel::iterator iter = m_refTreeModel->append();
1159 Gtk::TreeModel::Row row = *iter;
1160 row[m_Columns.m_col_name] = instrument->pInfo->Name.c_str();
1161 row[m_Columns.m_col_instr] = instrument;
1162 // create a menu item for this instrument
1163 Gtk::RadioMenuItem* item =
1164 new Gtk::RadioMenuItem(instrument_group, instrument->pInfo->Name.c_str());
1165 instrument_menu->get_submenu()->append(*item);
1166 item->signal_activate().connect(
1167 sigc::bind(
1168 sigc::mem_fun(*this, &MainWindow::on_instrument_selection_change),
1169 instrument_index
1170 )
1171 );
1172 instrument_index++;
1173 }
1174 instrument_menu->show();
1175 instrument_menu->get_submenu()->show_all_children();
1176
1177 for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) {
1178 if (group->Name != "") {
1179 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
1180 Gtk::TreeModel::Row rowGroup = *iterGroup;
1181 rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str();
1182 rowGroup[m_SamplesModel.m_col_group] = group;
1183 rowGroup[m_SamplesModel.m_col_sample] = NULL;
1184 for (gig::Sample* sample = group->GetFirstSample();
1185 sample; sample = group->GetNextSample()) {
1186 Gtk::TreeModel::iterator iterSample =
1187 m_refSamplesTreeModel->append(rowGroup.children());
1188 Gtk::TreeModel::Row rowSample = *iterSample;
1189 rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str();
1190 rowSample[m_SamplesModel.m_col_sample] = sample;
1191 rowSample[m_SamplesModel.m_col_group] = NULL;
1192 }
1193 }
1194 }
1195
1196 file = gig;
1197
1198 // select the first instrument
1199 Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
1200 tree_sel_ref->select(Gtk::TreePath("0"));
1201 }
1202
1203 void MainWindow::show_instr_props()
1204 {
1205 gig::Instrument* instrument = get_instrument();
1206 if (instrument)
1207 {
1208 instrumentProps.set_instrument(instrument);
1209 instrumentProps.show();
1210 instrumentProps.deiconify();
1211 }
1212 }
1213
1214 void MainWindow::on_action_view_status_bar() {
1215 Gtk::CheckMenuItem* item =
1216 dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/Statusbar"));
1217 if (!item) {
1218 std::cerr << "/MenuBar/MenuView/Statusbar == NULL\n";
1219 return;
1220 }
1221 if (item->get_active()) m_StatusBar.show();
1222 else m_StatusBar.hide();
1223 }
1224
1225 void MainWindow::on_button_release(GdkEventButton* button)
1226 {
1227 if (button->type == GDK_2BUTTON_PRESS) {
1228 show_instr_props();
1229 } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
1230 popup_menu->popup(button->button, button->time);
1231 }
1232 }
1233
1234 void MainWindow::on_instrument_selection_change(int index) {
1235 m_RegionChooser.set_instrument(file->GetInstrument(index));
1236 }
1237
1238 void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) {
1239 if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
1240 Gtk::Menu* sample_popup =
1241 dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/SamplePopupMenu"));
1242 // update enabled/disabled state of sample popup items
1243 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1244 Gtk::TreeModel::iterator it = sel->get_selected();
1245 bool group_selected = false;
1246 bool sample_selected = false;
1247 if (it) {
1248 Gtk::TreeModel::Row row = *it;
1249 group_selected = row[m_SamplesModel.m_col_group];
1250 sample_selected = row[m_SamplesModel.m_col_sample];
1251 }
1252 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->
1253 set_sensitive(group_selected || sample_selected);
1254 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->
1255 set_sensitive(group_selected || sample_selected);
1256 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->
1257 set_sensitive(file);
1258 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->
1259 set_sensitive(group_selected || sample_selected);
1260 // show sample popup
1261 sample_popup->popup(button->button, button->time);
1262 }
1263 }
1264
1265 void MainWindow::on_action_add_instrument() {
1266 static int __instrument_indexer = 0;
1267 if (!file) return;
1268 gig::Instrument* instrument = file->AddInstrument();
1269 __instrument_indexer++;
1270 instrument->pInfo->Name =
1271 "Unnamed Instrument " + ToString(__instrument_indexer);
1272 // update instrument tree view
1273 Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append();
1274 Gtk::TreeModel::Row rowInstr = *iterInstr;
1275 rowInstr[m_Columns.m_col_name] = instrument->pInfo->Name.c_str();
1276 rowInstr[m_Columns.m_col_instr] = instrument;
1277 file_changed();
1278 }
1279
1280 void MainWindow::on_action_remove_instrument() {
1281 if (!file) return;
1282 if (file_is_shared) {
1283 Gtk::MessageDialog msg(
1284 *this,
1285 _("You cannot delete an instrument from this file, since it's "
1286 "currently used by the sampler."),
1287 false, Gtk::MESSAGE_INFO
1288 );
1289 msg.run();
1290 return;
1291 }
1292
1293 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
1294 Gtk::TreeModel::iterator it = sel->get_selected();
1295 if (it) {
1296 Gtk::TreeModel::Row row = *it;
1297 gig::Instrument* instr = row[m_Columns.m_col_instr];
1298 try {
1299 // remove instrument from the gig file
1300 if (instr) file->DeleteInstrument(instr);
1301 // remove respective row from instruments tree view
1302 m_refTreeModel->erase(it);
1303 file_changed();
1304 } catch (RIFF::Exception e) {
1305 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1306 msg.run();
1307 }
1308 }
1309 }
1310
1311 void MainWindow::on_action_sample_properties() {
1312 //TODO: show a dialog where the selected sample's properties can be edited
1313 Gtk::MessageDialog msg(
1314 *this, "Sorry, yet to be implemented!", false, Gtk::MESSAGE_INFO
1315 );
1316 msg.run();
1317 }
1318
1319 void MainWindow::on_action_add_group() {
1320 static int __sample_indexer = 0;
1321 if (!file) return;
1322 gig::Group* group = file->AddGroup();
1323 group->Name = "Unnamed Group";
1324 if (__sample_indexer) group->Name += " " + ToString(__sample_indexer);
1325 __sample_indexer++;
1326 // update sample tree view
1327 Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
1328 Gtk::TreeModel::Row rowGroup = *iterGroup;
1329 rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str();
1330 rowGroup[m_SamplesModel.m_col_sample] = NULL;
1331 rowGroup[m_SamplesModel.m_col_group] = group;
1332 file_changed();
1333 }
1334
1335 void MainWindow::on_action_add_sample() {
1336 if (!file) return;
1337 // get selected group
1338 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1339 Gtk::TreeModel::iterator it = sel->get_selected();
1340 if (!it) return;
1341 Gtk::TreeModel::Row row = *it;
1342 gig::Group* group = row[m_SamplesModel.m_col_group];
1343 if (!group) { // not a group, but a sample is selected (probably)
1344 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1345 if (!sample) return;
1346 it = row.parent(); // resolve parent (that is the sample's group)
1347 if (!it) return;
1348 row = *it;
1349 group = row[m_SamplesModel.m_col_group];
1350 if (!group) return;
1351 }
1352 // show 'browse for file' dialog
1353 Gtk::FileChooserDialog dialog(*this, _("Add Sample(s)"));
1354 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1355 dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
1356 dialog.set_select_multiple(true);
1357 Gtk::FileFilter soundfilter; // matches all file types supported by libsndfile
1358 const char* const supportedFileTypes[] = {
1359 "*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd",
1360 "*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF",
1361 "*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64",
1362 "*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK",
1363 "*.caf", "*.CAF", NULL
1364 };
1365 for (int i = 0; supportedFileTypes[i]; i++)
1366 soundfilter.add_pattern(supportedFileTypes[i]);
1367 soundfilter.set_name("Sound Files");
1368 Gtk::FileFilter allpassfilter; // matches every file
1369 allpassfilter.add_pattern("*.*");
1370 allpassfilter.set_name("All Files");
1371 dialog.add_filter(soundfilter);
1372 dialog.add_filter(allpassfilter);
1373 if (dialog.run() == Gtk::RESPONSE_OK) {
1374 Glib::ustring error_files;
1375 Glib::SListHandle<Glib::ustring> filenames = dialog.get_filenames();
1376 for (Glib::SListHandle<Glib::ustring>::iterator iter = filenames.begin();
1377 iter != filenames.end(); ++iter) {
1378 printf("Adding sample %s\n",(*iter).c_str());
1379 // use libsndfile to retrieve file informations
1380 SF_INFO info;
1381 info.format = 0;
1382 SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
1383 try {
1384 if (!hFile) throw std::string("could not open file");
1385 int bitdepth;
1386 switch (info.format & 0xff) {
1387 case SF_FORMAT_PCM_S8:
1388 case SF_FORMAT_PCM_16:
1389 case SF_FORMAT_PCM_U8:
1390 bitdepth = 16;
1391 break;
1392 case SF_FORMAT_PCM_24:
1393 case SF_FORMAT_PCM_32:
1394 case SF_FORMAT_FLOAT:
1395 case SF_FORMAT_DOUBLE:
1396 bitdepth = 24;
1397 break;
1398 default:
1399 sf_close(hFile); // close sound file
1400 throw std::string("format not supported"); // unsupported subformat (yet?)
1401 }
1402 // add a new sample to the .gig file
1403 gig::Sample* sample = file->AddSample();
1404 // file name without path
1405 Glib::ustring filename = Glib::filename_display_basename(*iter);
1406 // remove file extension if there is one
1407 for (int i = 0; supportedFileTypes[i]; i++) {
1408 if (Glib::str_has_suffix(filename, supportedFileTypes[i] + 1)) {
1409 filename.erase(filename.length() - strlen(supportedFileTypes[i] + 1));
1410 break;
1411 }
1412 }
1413 sample->pInfo->Name = filename;
1414 sample->Channels = info.channels;
1415 sample->BitDepth = bitdepth;
1416 sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
1417 sample->SamplesPerSecond = info.samplerate;
1418 sample->AverageBytesPerSecond = sample->FrameSize * sample->SamplesPerSecond;
1419 sample->BlockAlign = sample->FrameSize;
1420 sample->SamplesTotal = info.frames;
1421
1422 SF_INSTRUMENT instrument;
1423 if (sf_command(hFile, SFC_GET_INSTRUMENT,
1424 &instrument, sizeof(instrument)) != SF_FALSE)
1425 {
1426 sample->MIDIUnityNote = instrument.basenote;
1427
1428 #if HAVE_SF_INSTRUMENT_LOOPS
1429 if (instrument.loop_count && instrument.loops[0].mode != SF_LOOP_NONE) {
1430 sample->Loops = 1;
1431
1432 switch (instrument.loops[0].mode) {
1433 case SF_LOOP_FORWARD:
1434 sample->LoopType = gig::loop_type_normal;
1435 break;
1436 case SF_LOOP_BACKWARD:
1437 sample->LoopType = gig::loop_type_backward;
1438 break;
1439 case SF_LOOP_ALTERNATING:
1440 sample->LoopType = gig::loop_type_bidirectional;
1441 break;
1442 }
1443 sample->LoopStart = instrument.loops[0].start;
1444 sample->LoopEnd = instrument.loops[0].end;
1445 sample->LoopPlayCount = instrument.loops[0].count;
1446 sample->LoopSize = sample->LoopEnd - sample->LoopStart + 1;
1447 }
1448 #endif
1449 }
1450
1451 // schedule resizing the sample (which will be done
1452 // physically when File::Save() is called)
1453 sample->Resize(info.frames);
1454 // make sure sample is part of the selected group
1455 group->AddSample(sample);
1456 // schedule that physical resize and sample import
1457 // (data copying), performed when "Save" is requested
1458 SampleImportItem sched_item;
1459 sched_item.gig_sample = sample;
1460 sched_item.sample_path = *iter;
1461 m_SampleImportQueue.push_back(sched_item);
1462 // add sample to the tree view
1463 Gtk::TreeModel::iterator iterSample =
1464 m_refSamplesTreeModel->append(row.children());
1465 Gtk::TreeModel::Row rowSample = *iterSample;
1466 rowSample[m_SamplesModel.m_col_name] = filename;
1467 rowSample[m_SamplesModel.m_col_sample] = sample;
1468 rowSample[m_SamplesModel.m_col_group] = NULL;
1469 // close sound file
1470 sf_close(hFile);
1471 file_changed();
1472 } catch (std::string what) { // remember the files that made trouble (and their cause)
1473 if (error_files.size()) error_files += "\n";
1474 error_files += *iter += " (" + what + ")";
1475 }
1476 }
1477 // show error message box when some file(s) could not be opened / added
1478 if (error_files.size()) {
1479 Glib::ustring txt = _("Could not add the following sample(s):\n") + error_files;
1480 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1481 msg.run();
1482 }
1483 }
1484 }
1485
1486 void MainWindow::on_action_remove_sample() {
1487 if (!file) return;
1488 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1489 Gtk::TreeModel::iterator it = sel->get_selected();
1490 if (it) {
1491 Gtk::TreeModel::Row row = *it;
1492 gig::Group* group = row[m_SamplesModel.m_col_group];
1493 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1494 Glib::ustring name = row[m_SamplesModel.m_col_name];
1495 try {
1496 // remove group or sample from the gig file
1497 if (group) {
1498 // temporarily remember the samples that bolong to
1499 // that group (we need that to clean the queue)
1500 std::list<gig::Sample*> members;
1501 for (gig::Sample* pSample = group->GetFirstSample();
1502 pSample; pSample = group->GetNextSample()) {
1503 members.push_back(pSample);
1504 }
1505 // notify everybody that we're going to remove these samples
1506 samples_to_be_removed_signal.emit(members);
1507 // delete the group in the .gig file including the
1508 // samples that belong to the group
1509 file->DeleteGroup(group);
1510 // notify that we're done with removal
1511 samples_removed_signal.emit();
1512 // if sample(s) were just previously added, remove
1513 // them from the import queue
1514 for (std::list<gig::Sample*>::iterator member = members.begin();
1515 member != members.end(); ++member) {
1516 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1517 iter != m_SampleImportQueue.end(); ++iter) {
1518 if ((*iter).gig_sample == *member) {
1519 printf("Removing previously added sample '%s' from group '%s'\n",
1520 (*iter).sample_path.c_str(), name.c_str());
1521 m_SampleImportQueue.erase(iter);
1522 break;
1523 }
1524 }
1525 }
1526 file_changed();
1527 } else if (sample) {
1528 // notify everybody that we're going to remove this sample
1529 std::list<gig::Sample*> lsamples;
1530 lsamples.push_back(sample);
1531 samples_to_be_removed_signal.emit(lsamples);
1532 // remove sample from the .gig file
1533 file->DeleteSample(sample);
1534 // notify that we're done with removal
1535 samples_removed_signal.emit();
1536 // if sample was just previously added, remove it from
1537 // the import queue
1538 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1539 iter != m_SampleImportQueue.end(); ++iter) {
1540 if ((*iter).gig_sample == sample) {
1541 printf("Removing previously added sample '%s'\n",
1542 (*iter).sample_path.c_str());
1543 m_SampleImportQueue.erase(iter);
1544 break;
1545 }
1546 }
1547 dimreg_changed();
1548 file_changed();
1549 }
1550 // remove respective row(s) from samples tree view
1551 m_refSamplesTreeModel->erase(it);
1552 } catch (RIFF::Exception e) {
1553 // pretend we're done with removal (i.e. to avoid dead locks)
1554 samples_removed_signal.emit();
1555 // show error message
1556 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1557 msg.run();
1558 }
1559 }
1560 }
1561
1562 // For some reason drag_data_get gets called two times for each
1563 // drag'n'drop (at least when target is an Entry). This work-around
1564 // makes sure the code in drag_data_get and drop_drag_data_received is
1565 // only executed once, as drag_begin only gets called once.
1566 void MainWindow::on_sample_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
1567 {
1568 first_call_to_drag_data_get = true;
1569 }
1570
1571 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
1572 Gtk::SelectionData& selection_data, guint, guint)
1573 {
1574 if (!first_call_to_drag_data_get) return;
1575 first_call_to_drag_data_get = false;
1576
1577 // get selected sample
1578 gig::Sample* sample = NULL;
1579 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1580 Gtk::TreeModel::iterator it = sel->get_selected();
1581 if (it) {
1582 Gtk::TreeModel::Row row = *it;
1583 sample = row[m_SamplesModel.m_col_sample];
1584 }
1585 // pass the gig::Sample as pointer
1586 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
1587 sizeof(sample)/*length of data in bytes*/);
1588 }
1589
1590 void MainWindow::on_sample_label_drop_drag_data_received(
1591 const Glib::RefPtr<Gdk::DragContext>& context, int, int,
1592 const Gtk::SelectionData& selection_data, guint, guint time)
1593 {
1594 gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
1595
1596 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
1597 std::cout << "Drop received sample \"" <<
1598 sample->pInfo->Name << "\"" << std::endl;
1599 // drop success
1600 context->drop_reply(true, time);
1601
1602 //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
1603
1604 // notify everybody that we're going to alter the region
1605 gig::Region* region = m_RegionChooser.get_region();
1606 region_to_be_changed_signal.emit(region);
1607
1608 // find the samplechannel dimension
1609 gig::dimension_def_t* stereo_dimension = 0;
1610 for (int i = 0 ; i < region->Dimensions ; i++) {
1611 if (region->pDimensionDefinitions[i].dimension ==
1612 gig::dimension_samplechannel) {
1613 stereo_dimension = &region->pDimensionDefinitions[i];
1614 break;
1615 }
1616 }
1617 bool channels_changed = false;
1618 if (sample->Channels == 1 && stereo_dimension) {
1619 // remove the samplechannel dimension
1620 region->DeleteDimension(stereo_dimension);
1621 channels_changed = true;
1622 region_changed();
1623 }
1624 dimreg_edit.set_sample(sample);
1625
1626 if (sample->Channels == 2 && !stereo_dimension) {
1627 // add samplechannel dimension
1628 gig::dimension_def_t dim;
1629 dim.dimension = gig::dimension_samplechannel;
1630 dim.bits = 1;
1631 dim.zones = 2;
1632 region->AddDimension(&dim);
1633 channels_changed = true;
1634 region_changed();
1635 }
1636 if (channels_changed) {
1637 // unmap all samples with wrong number of channels
1638 // TODO: maybe there should be a warning dialog for this
1639 for (int i = 0 ; i < region->DimensionRegions ; i++) {
1640 gig::DimensionRegion* d = region->pDimensionRegions[i];
1641 if (d->pSample && d->pSample->Channels != sample->Channels) {
1642 gig::Sample* oldref = d->pSample;
1643 d->pSample = NULL;
1644 sample_ref_changed_signal.emit(oldref, NULL);
1645 }
1646 }
1647 }
1648
1649 // notify we're done with altering
1650 region_changed_signal.emit(region);
1651
1652 file_changed();
1653
1654 return;
1655 }
1656 // drop failed
1657 context->drop_reply(false, time);
1658 }
1659
1660 void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
1661 const Gtk::TreeModel::iterator& iter) {
1662 if (!iter) return;
1663 Gtk::TreeModel::Row row = *iter;
1664 Glib::ustring name = row[m_SamplesModel.m_col_name];
1665 gig::Group* group = row[m_SamplesModel.m_col_group];
1666 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1667 if (group) {
1668 if (group->Name != name) {
1669 group->Name = name;
1670 printf("group name changed\n");
1671 file_changed();
1672 }
1673 } else if (sample) {
1674 if (sample->pInfo->Name != name.raw()) {
1675 sample->pInfo->Name = name.raw();
1676 printf("sample name changed\n");
1677 file_changed();
1678 }
1679 }
1680 }
1681
1682 void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
1683 const Gtk::TreeModel::iterator& iter) {
1684 if (!iter) return;
1685 Gtk::TreeModel::Row row = *iter;
1686 Glib::ustring name = row[m_Columns.m_col_name];
1687 gig::Instrument* instrument = row[m_Columns.m_col_instr];
1688 if (instrument && instrument->pInfo->Name != name.raw()) {
1689 instrument->pInfo->Name = name.raw();
1690 file_changed();
1691 }
1692 }
1693
1694 void MainWindow::set_file_is_shared(bool b) {
1695 this->file_is_shared = b;
1696
1697 if (file_is_shared) {
1698 m_AttachedStateLabel.set_label(_("live-mode"));
1699 m_AttachedStateImage.set(
1700 Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
1701 );
1702 } else {
1703 m_AttachedStateLabel.set_label(_("stand-alone"));
1704 m_AttachedStateImage.set(
1705 Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
1706 );
1707 }
1708 }
1709
1710 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
1711 return file_structure_to_be_changed_signal;
1712 }
1713
1714 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
1715 return file_structure_changed_signal;
1716 }
1717
1718 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
1719 return samples_to_be_removed_signal;
1720 }
1721
1722 sigc::signal<void>& MainWindow::signal_samples_removed() {
1723 return samples_removed_signal;
1724 }
1725
1726 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
1727 return region_to_be_changed_signal;
1728 }
1729
1730 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
1731 return region_changed_signal;
1732 }
1733
1734 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
1735 return sample_ref_changed_signal;
1736 }
1737
1738 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
1739 return dimreg_to_be_changed_signal;
1740 }
1741
1742 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
1743 return dimreg_changed_signal;
1744 }
1745
1746 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
1747 return note_on_signal;
1748 }
1749
1750 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
1751 return note_off_signal;
1752 }

  ViewVC Help
Powered by ViewVC