/[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 1725 - (show annotations) (download)
Sat Apr 26 08:52:15 2008 UTC (15 years, 11 months ago) by persson
File size: 71393 byte(s)
* the "Add Samples" dialog now remembers current directory (patch by
  Devin Anderson, fixes #81)

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

  ViewVC Help
Powered by ViewVC