/[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 1679 - (show annotations) (download)
Tue Feb 12 14:13:46 2008 UTC (11 years, 10 months ago) by schoenebeck
File size: 71040 byte(s)
* in "Save As..." dialog set inital filename to "copy_of_<filename>" and
  show a warning message that saving to the same .gig file will corrupt
  the sample wave data
* added autoconf test for the recent virtual MIDI device support on LS
  side to prevent compile time errors when compiling gigedit against an
  old LS version

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

  ViewVC Help
Powered by ViewVC