/[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 1799 - (show annotations) (download)
Sat Dec 6 13:49:26 2008 UTC (11 years, 5 months ago) by persson
File size: 70817 byte(s)
* minor gui fix: made multi line lables expand to use full width

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 #include "wrapLabel.hh"
30
31 #include "global.h"
32
33 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
34 #define ABOUT_DIALOG
35 #include <gtkmm/aboutdialog.h>
36 #endif
37
38 #if (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION < 6) || GLIBMM_MAJOR_VERSION < 2
39 namespace Glib {
40 Glib::ustring filename_display_basename(const std::string& filename)
41 {
42 gchar* gstr = g_path_get_basename(filename.c_str());
43 Glib::ustring str(gstr);
44 g_free(gstr);
45 return Glib::filename_to_utf8(str);
46 }
47 }
48 #endif
49
50 #include <stdio.h>
51 #include <sndfile.h>
52
53 #include "mainwindow.h"
54
55 #include "../../gfx/status_attached.xpm"
56 #include "../../gfx/status_detached.xpm"
57
58 template<class T> inline std::string ToString(T o) {
59 std::stringstream ss;
60 ss << o;
61 return ss.str();
62 }
63
64 Table::Table(int x, int y) : Gtk::Table(x, y), rowno(0) { }
65
66 void Table::add(BoolEntry& boolentry)
67 {
68 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
69 Gtk::FILL, Gtk::SHRINK);
70 rowno++;
71 }
72
73 void Table::add(BoolEntryPlus6& boolentry)
74 {
75 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
76 Gtk::FILL, Gtk::SHRINK);
77 rowno++;
78 }
79
80 void Table::add(LabelWidget& prop)
81 {
82 attach(prop.label, 1, 2, rowno, rowno + 1,
83 Gtk::FILL, Gtk::SHRINK);
84 attach(prop.widget, 2, 3, rowno, rowno + 1,
85 Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK);
86 rowno++;
87 }
88
89 MainWindow::MainWindow() :
90 dimreg_label(_("Changes apply to:")),
91 dimreg_all_regions(_("all regions")),
92 dimreg_all_dimregs(_("all dimension splits")),
93 dimreg_stereo(_("both channels"))
94 {
95 // set_border_width(5);
96 // set_default_size(400, 200);
97
98
99 add(m_VBox);
100
101 // Handle selection
102 Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
103 tree_sel_ref->signal_changed().connect(
104 sigc::mem_fun(*this, &MainWindow::on_sel_change));
105
106 // m_TreeView.set_reorderable();
107
108 m_TreeView.signal_button_press_event().connect_notify(
109 sigc::mem_fun(*this, &MainWindow::on_button_release));
110
111 // Add the TreeView tab, inside a ScrolledWindow, with the button underneath:
112 m_ScrolledWindow.add(m_TreeView);
113 // m_ScrolledWindow.set_size_request(200, 600);
114 m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
115
116 m_ScrolledWindowSamples.add(m_TreeViewSamples);
117 m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
118
119
120 m_TreeViewNotebook.set_size_request(300);
121
122 m_HPaned.add1(m_TreeViewNotebook);
123 dimreg_hbox.add(dimreg_label);
124 dimreg_hbox.add(dimreg_all_regions);
125 dimreg_hbox.add(dimreg_all_dimregs);
126 dimreg_stereo.set_active();
127 dimreg_hbox.add(dimreg_stereo);
128 dimreg_vbox.add(dimreg_edit);
129 dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
130 m_HPaned.add2(dimreg_vbox);
131
132
133 m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples");
134 m_TreeViewNotebook.append_page(m_ScrolledWindow, "Instruments");
135
136
137 actionGroup = Gtk::ActionGroup::create();
138
139 actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
140 actionGroup->add(Gtk::Action::create("New", Gtk::Stock::NEW),
141 sigc::mem_fun(
142 *this, &MainWindow::on_action_file_new));
143 Glib::RefPtr<Gtk::Action> action =
144 Gtk::Action::create("Open", Gtk::Stock::OPEN);
145 action->property_label() = action->property_label() + "...";
146 actionGroup->add(action,
147 sigc::mem_fun(
148 *this, &MainWindow::on_action_file_open));
149 actionGroup->add(Gtk::Action::create("Save", Gtk::Stock::SAVE),
150 sigc::mem_fun(
151 *this, &MainWindow::on_action_file_save));
152 action = Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS);
153 action->property_label() = action->property_label() + "...";
154 actionGroup->add(action,
155 Gtk::AccelKey("<shift><control>s"),
156 sigc::mem_fun(
157 *this, &MainWindow::on_action_file_save_as));
158 actionGroup->add(Gtk::Action::create("Properties",
159 Gtk::Stock::PROPERTIES),
160 sigc::mem_fun(
161 *this, &MainWindow::on_action_file_properties));
162 actionGroup->add(Gtk::Action::create("InstrProperties",
163 Gtk::Stock::PROPERTIES),
164 sigc::mem_fun(
165 *this, &MainWindow::show_instr_props));
166 actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT),
167 sigc::mem_fun(
168 *this, &MainWindow::on_action_quit));
169 actionGroup->add(Gtk::Action::create("MenuInstrument", _("_Instrument")));
170
171 actionGroup->add(Gtk::Action::create("MenuView", _("_View")));
172 Glib::RefPtr<Gtk::ToggleAction> toggle_action =
173 Gtk::ToggleAction::create("Statusbar", _("_Statusbar"));
174 toggle_action->set_active(true);
175 actionGroup->add(toggle_action,
176 sigc::mem_fun(
177 *this, &MainWindow::on_action_view_status_bar));
178
179 action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP);
180 actionGroup->add(Gtk::Action::create("MenuHelp",
181 action->property_label()));
182 #ifdef ABOUT_DIALOG
183 actionGroup->add(Gtk::Action::create("About", Gtk::Stock::ABOUT),
184 sigc::mem_fun(
185 *this, &MainWindow::on_action_help_about));
186 #endif
187 actionGroup->add(
188 Gtk::Action::create("AddInstrument", _("Add _Instrument")),
189 sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
190 );
191 actionGroup->add(
192 Gtk::Action::create("RemoveInstrument", Gtk::Stock::REMOVE),
193 sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
194 );
195
196 // sample right-click popup actions
197 actionGroup->add(
198 Gtk::Action::create("SampleProperties", Gtk::Stock::PROPERTIES),
199 sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
200 );
201 actionGroup->add(
202 Gtk::Action::create("AddGroup", _("Add _Group")),
203 sigc::mem_fun(*this, &MainWindow::on_action_add_group)
204 );
205 actionGroup->add(
206 Gtk::Action::create("AddSample", _("Add _Sample(s)...")),
207 sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
208 );
209 actionGroup->add(
210 Gtk::Action::create("RemoveSample", Gtk::Stock::REMOVE),
211 sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
212 );
213 actionGroup->add(
214 Gtk::Action::create("ReplaceAllSamplesInAllGroups",
215 _("Replace All Samples in All Groups...")),
216 sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups)
217 );
218
219 uiManager = Gtk::UIManager::create();
220 uiManager->insert_action_group(actionGroup);
221 add_accel_group(uiManager->get_accel_group());
222
223 Glib::ustring ui_info =
224 "<ui>"
225 " <menubar name='MenuBar'>"
226 " <menu action='MenuFile'>"
227 " <menuitem action='New'/>"
228 " <menuitem action='Open'/>"
229 " <separator/>"
230 " <menuitem action='Save'/>"
231 " <menuitem action='SaveAs'/>"
232 " <separator/>"
233 " <menuitem action='Properties'/>"
234 " <separator/>"
235 " <menuitem action='Quit'/>"
236 " </menu>"
237 " <menu action='MenuInstrument'>"
238 " </menu>"
239 " <menu action='MenuView'>"
240 " <menuitem action='Statusbar'/>"
241 " </menu>"
242 #ifdef ABOUT_DIALOG
243 " <menu action='MenuHelp'>"
244 " <menuitem action='About'/>"
245 " </menu>"
246 #endif
247 " </menubar>"
248 " <popup name='PopupMenu'>"
249 " <menuitem action='InstrProperties'/>"
250 " <menuitem action='AddInstrument'/>"
251 " <separator/>"
252 " <menuitem action='RemoveInstrument'/>"
253 " </popup>"
254 " <popup name='SamplePopupMenu'>"
255 " <menuitem action='SampleProperties'/>"
256 " <menuitem action='AddGroup'/>"
257 " <menuitem action='AddSample'/>"
258 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
259 " <separator/>"
260 " <menuitem action='RemoveSample'/>"
261 " </popup>"
262 "</ui>";
263 uiManager->add_ui_from_string(ui_info);
264
265 popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
266
267 Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar");
268 m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
269 m_VBox.pack_start(m_HPaned);
270 m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
271 m_VBox.pack_start(m_RegionChooser.m_VirtKeybPropsBox, Gtk::PACK_SHRINK);
272 m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
273 m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
274
275 // Status Bar:
276 m_StatusBar.pack_start(m_AttachedStateLabel, Gtk::PACK_SHRINK);
277 m_StatusBar.pack_start(m_AttachedStateImage, Gtk::PACK_SHRINK);
278 m_StatusBar.show();
279
280 m_RegionChooser.signal_region_selected().connect(
281 sigc::mem_fun(*this, &MainWindow::region_changed) );
282 m_DimRegionChooser.signal_dimregion_selected().connect(
283 sigc::mem_fun(*this, &MainWindow::dimreg_changed) );
284
285
286 // Create the Tree model:
287 m_refTreeModel = Gtk::ListStore::create(m_Columns);
288 m_TreeView.set_model(m_refTreeModel);
289 m_refTreeModel->signal_row_changed().connect(
290 sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
291 );
292
293 // Add the TreeView's view columns:
294 m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name);
295 m_TreeView.set_headers_visible(false);
296
297 // create samples treeview (including its data model)
298 m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
299 m_TreeViewSamples.set_model(m_refSamplesTreeModel);
300 // m_TreeViewSamples.set_reorderable();
301 m_TreeViewSamples.append_column_editable("Samples", m_SamplesModel.m_col_name);
302 m_TreeViewSamples.set_headers_visible(false);
303 m_TreeViewSamples.signal_button_press_event().connect_notify(
304 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
305 );
306 m_refSamplesTreeModel->signal_row_changed().connect(
307 sigc::mem_fun(*this, &MainWindow::sample_name_changed)
308 );
309
310 // establish drag&drop between samples tree view and dimension region 'Sample' text entry
311 std::list<Gtk::TargetEntry> drag_target_gig_sample;
312 drag_target_gig_sample.push_back( Gtk::TargetEntry("gig::Sample") );
313 m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
314 m_TreeViewSamples.signal_drag_begin().connect(
315 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_begin)
316 );
317 m_TreeViewSamples.signal_drag_data_get().connect(
318 sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get)
319 );
320 dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample);
321 dimreg_edit.wSample->signal_drag_data_received().connect(
322 sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
323 );
324 dimreg_edit.signal_dimreg_changed().connect(
325 sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
326 m_RegionChooser.signal_instrument_changed().connect(
327 sigc::mem_fun(*this, &MainWindow::file_changed));
328 m_DimRegionChooser.signal_region_changed().connect(
329 sigc::mem_fun(*this, &MainWindow::file_changed));
330 instrumentProps.signal_instrument_changed().connect(
331 sigc::mem_fun(*this, &MainWindow::file_changed));
332 propDialog.signal_info_changed().connect(
333 sigc::mem_fun(*this, &MainWindow::file_changed));
334
335 dimreg_edit.signal_dimreg_to_be_changed().connect(
336 dimreg_to_be_changed_signal.make_slot());
337 dimreg_edit.signal_dimreg_changed().connect(
338 dimreg_changed_signal.make_slot());
339 dimreg_edit.signal_sample_ref_changed().connect(
340 sample_ref_changed_signal.make_slot());
341
342 m_RegionChooser.signal_instrument_struct_to_be_changed().connect(
343 sigc::hide(
344 sigc::bind(
345 file_structure_to_be_changed_signal.make_slot(),
346 sigc::ref(this->file)
347 )
348 )
349 );
350 m_RegionChooser.signal_instrument_struct_changed().connect(
351 sigc::hide(
352 sigc::bind(
353 file_structure_changed_signal.make_slot(),
354 sigc::ref(this->file)
355 )
356 )
357 );
358 m_RegionChooser.signal_region_to_be_changed().connect(
359 region_to_be_changed_signal.make_slot());
360 m_RegionChooser.signal_region_changed_signal().connect(
361 region_changed_signal.make_slot());
362
363 note_on_signal.connect(
364 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_on_event));
365 note_off_signal.connect(
366 sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_off_event));
367
368 dimreg_all_regions.signal_toggled().connect(
369 sigc::mem_fun(*this, &MainWindow::update_dimregs));
370 dimreg_all_dimregs.signal_toggled().connect(
371 sigc::mem_fun(*this, &MainWindow::dimreg_all_dimregs_toggled));
372 dimreg_stereo.signal_toggled().connect(
373 sigc::mem_fun(*this, &MainWindow::update_dimregs));
374
375 file = 0;
376 file_is_changed = false;
377 set_file_is_shared(false);
378
379 show_all_children();
380
381 // start with a new gig file by default
382 on_action_file_new();
383 }
384
385 MainWindow::~MainWindow()
386 {
387 }
388
389 bool MainWindow::on_delete_event(GdkEventAny* event)
390 {
391 return !file_is_shared && file_is_changed && !close_confirmation_dialog();
392 }
393
394 void MainWindow::on_action_quit()
395 {
396 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
397 hide();
398 }
399
400 void MainWindow::region_changed()
401 {
402 m_DimRegionChooser.set_region(m_RegionChooser.get_region());
403 }
404
405 gig::Instrument* MainWindow::get_instrument()
406 {
407 gig::Instrument* instrument = 0;
408 Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
409
410 Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();
411 if (it) {
412 Gtk::TreeModel::Row row = *it;
413 instrument = row[m_Columns.m_col_instr];
414 }
415 return instrument;
416 }
417
418 void MainWindow::add_region_to_dimregs(gig::Region* region, bool stereo, bool all_dimregs)
419 {
420 if (all_dimregs) {
421 for (int i = 0 ; i < region->DimensionRegions ; i++) {
422 if (region->pDimensionRegions[i]) {
423 dimreg_edit.dimregs.insert(region->pDimensionRegions[i]);
424 }
425 }
426 } else {
427 m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
428 }
429 }
430
431 void MainWindow::update_dimregs()
432 {
433 dimreg_edit.dimregs.clear();
434 bool all_regions = dimreg_all_regions.get_active();
435 bool stereo = dimreg_stereo.get_active();
436 bool all_dimregs = dimreg_all_dimregs.get_active();
437
438 if (all_regions) {
439 gig::Instrument* instrument = get_instrument();
440 if (instrument) {
441 for (gig::Region* region = instrument->GetFirstRegion() ;
442 region ;
443 region = instrument->GetNextRegion()) {
444 add_region_to_dimregs(region, stereo, all_dimregs);
445 }
446 }
447 } else {
448 gig::Region* region = m_RegionChooser.get_region();
449 if (region) {
450 add_region_to_dimregs(region, stereo, all_dimregs);
451 }
452 }
453 }
454
455 void MainWindow::dimreg_all_dimregs_toggled()
456 {
457 dimreg_stereo.set_sensitive(!dimreg_all_dimregs.get_active());
458 update_dimregs();
459 }
460
461 void MainWindow::dimreg_changed()
462 {
463 update_dimregs();
464 dimreg_edit.set_dim_region(m_DimRegionChooser.get_dimregion());
465 }
466
467 void MainWindow::on_sel_change()
468 {
469 m_RegionChooser.set_instrument(get_instrument());
470 }
471
472 void loader_progress_callback(gig::progress_t* progress)
473 {
474 Loader* loader = static_cast<Loader*>(progress->custom);
475 loader->progress_callback(progress->factor);
476 }
477
478 void Loader::progress_callback(float fraction)
479 {
480 {
481 Glib::Mutex::Lock lock(progressMutex);
482 progress = fraction;
483 }
484 progress_dispatcher();
485 }
486
487 void Loader::thread_function()
488 {
489 printf("thread_function self=%x\n", Glib::Thread::self());
490 printf("Start %s\n", filename);
491 RIFF::File* riff = new RIFF::File(filename);
492 gig = new gig::File(riff);
493 gig::progress_t progress;
494 progress.callback = loader_progress_callback;
495 progress.custom = this;
496
497 gig->GetInstrument(0, &progress);
498 printf("End\n");
499 finished_dispatcher();
500 }
501
502 Loader::Loader(const char* filename)
503 : thread(0), filename(filename)
504 {
505 }
506
507 void Loader::launch()
508 {
509 thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true);
510 printf("launch thread=%x\n", thread);
511 }
512
513 float Loader::get_progress()
514 {
515 float res;
516 {
517 Glib::Mutex::Lock lock(progressMutex);
518 res = progress;
519 }
520 return res;
521 }
522
523 Glib::Dispatcher& Loader::signal_progress()
524 {
525 return progress_dispatcher;
526 }
527
528 Glib::Dispatcher& Loader::signal_finished()
529 {
530 return finished_dispatcher;
531 }
532
533 LoadDialog::LoadDialog(const Glib::ustring& title, Gtk::Window& parent)
534 : Gtk::Dialog(title, parent, true)
535 {
536 get_vbox()->pack_start(progressBar);
537 show_all_children();
538 }
539
540 // Clear all GUI elements / controls. This method is typically called
541 // before a new .gig file is to be created or to be loaded.
542 void MainWindow::__clear() {
543 // remove all entries from "Instrument" menu
544 Gtk::MenuItem* instrument_menu =
545 dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument"));
546 instrument_menu->hide();
547 for (int i = 0; i < instrument_menu->get_submenu()->items().size(); i++) {
548 delete &instrument_menu->get_submenu()->items()[i];
549 }
550 instrument_menu->get_submenu()->items().clear();
551 // forget all samples that ought to be imported
552 m_SampleImportQueue.clear();
553 // clear the samples and instruments tree views
554 m_refTreeModel->clear();
555 m_refSamplesTreeModel->clear();
556 // free libgig's gig::File instance
557 if (file && !file_is_shared) delete file;
558 file = NULL;
559 set_file_is_shared(false);
560 }
561
562 void MainWindow::on_action_file_new()
563 {
564 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
565
566 if (file_is_shared && !leaving_shared_mode_dialog()) return;
567
568 // clear all GUI elements
569 __clear();
570 // create a new .gig file (virtually yet)
571 gig::File* pFile = new gig::File;
572 // already add one new instrument by default
573 gig::Instrument* pInstrument = pFile->AddInstrument();
574 pInstrument->pInfo->Name = "Unnamed Instrument";
575 // update GUI with that new gig::File
576 load_gig(pFile, 0 /*no file name yet*/);
577 }
578
579 bool MainWindow::close_confirmation_dialog()
580 {
581 gchar* msg = g_strdup_printf(_("Save changes to \"%s\" before closing?"),
582 Glib::filename_display_basename(filename).c_str());
583 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
584 g_free(msg);
585 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
586 dialog.set_secondary_text(_("If you close without saving, your changes will be lost."));
587 #endif
588 dialog.add_button(_("Close _Without Saving"), Gtk::RESPONSE_NO);
589 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
590 dialog.add_button(file_has_name ? Gtk::Stock::SAVE : Gtk::Stock::SAVE_AS, Gtk::RESPONSE_YES);
591 dialog.set_default_response(Gtk::RESPONSE_YES);
592 int response = dialog.run();
593 dialog.hide();
594 if (response == Gtk::RESPONSE_YES) return file_save();
595 return response != Gtk::RESPONSE_CANCEL;
596 }
597
598 bool MainWindow::leaving_shared_mode_dialog() {
599 Glib::ustring msg = _("Detach from sampler and proceed working stand-alone?");
600 Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
601 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
602 dialog.set_secondary_text(
603 _("If you proceed to work on another instrument file, it won't be "
604 "used by the sampler until you tell the sampler explicitly to "
605 "load it.")
606 );
607 #endif
608 dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
609 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
610 dialog.set_default_response(Gtk::RESPONSE_CANCEL);
611 int response = dialog.run();
612 dialog.hide();
613 return response == Gtk::RESPONSE_YES;
614 }
615
616 void MainWindow::on_action_file_open()
617 {
618 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
619
620 if (file_is_shared && !leaving_shared_mode_dialog()) return;
621
622 Gtk::FileChooserDialog dialog(*this, _("Open file"));
623 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
624 dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
625 dialog.set_default_response(Gtk::RESPONSE_OK);
626 Gtk::FileFilter filter;
627 filter.add_pattern("*.gig");
628 dialog.set_filter(filter);
629 if (current_gig_dir != "") {
630 dialog.set_current_folder(current_gig_dir);
631 }
632 if (dialog.run() == Gtk::RESPONSE_OK) {
633 std::string filename = dialog.get_filename();
634 printf("filename=%s\n", filename.c_str());
635 printf("on_action_file_open self=%x\n", Glib::Thread::self());
636 load_file(filename.c_str());
637 current_gig_dir = Glib::path_get_dirname(filename);
638 }
639 }
640
641 void MainWindow::load_file(const char* name)
642 {
643 __clear();
644 load_dialog = new LoadDialog("Loading...", *this);
645 load_dialog->show_all();
646 loader = new Loader(strdup(name));
647 loader->signal_progress().connect(
648 sigc::mem_fun(*this, &MainWindow::on_loader_progress));
649 loader->signal_finished().connect(
650 sigc::mem_fun(*this, &MainWindow::on_loader_finished));
651 loader->launch();
652 }
653
654 void MainWindow::load_instrument(gig::Instrument* instr) {
655 if (!instr) {
656 Glib::ustring txt = "Provided instrument is NULL!\n";
657 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
658 msg.run();
659 Gtk::Main::quit();
660 }
661 // clear all GUI elements
662 __clear();
663 // load the instrument
664 gig::File* pFile = (gig::File*) instr->GetParent();
665 load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
666 //TODO: automatically select the given instrument
667 }
668
669 void MainWindow::on_loader_progress()
670 {
671 load_dialog->set_fraction(loader->get_progress());
672 }
673
674 void MainWindow::on_loader_finished()
675 {
676 printf("Loader finished!\n");
677 printf("on_loader_finished self=%x\n", Glib::Thread::self());
678 load_gig(loader->gig, loader->filename);
679 load_dialog->hide();
680 }
681
682 void MainWindow::on_action_file_save()
683 {
684 file_save();
685 }
686
687 bool MainWindow::check_if_savable()
688 {
689 if (!file) return false;
690
691 if (!file->GetFirstSample()) {
692 Gtk::MessageDialog(*this, _("The file could not be saved "
693 "because it contains no samples"),
694 false, Gtk::MESSAGE_ERROR).run();
695 return false;
696 }
697
698 for (gig::Instrument* instrument = file->GetFirstInstrument() ; instrument ;
699 instrument = file->GetNextInstrument()) {
700 if (!instrument->GetFirstRegion()) {
701 Gtk::MessageDialog(*this, _("The file could not be saved "
702 "because there are instruments "
703 "that have no regions"),
704 false, Gtk::MESSAGE_ERROR).run();
705 return false;
706 }
707 }
708 return true;
709 }
710
711 bool MainWindow::file_save()
712 {
713 if (!check_if_savable()) return false;
714 if (!file_is_shared && !file_has_name) return file_save_as();
715
716 std::cout << "Saving file\n" << std::flush;
717 file_structure_to_be_changed_signal.emit(this->file);
718 try {
719 file->Save();
720 if (file_is_changed) {
721 set_title(get_title().substr(1));
722 file_is_changed = false;
723 }
724 } catch (RIFF::Exception e) {
725 file_structure_changed_signal.emit(this->file);
726 Glib::ustring txt = _("Could not save file: ") + e.Message;
727 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
728 msg.run();
729 return false;
730 }
731 std::cout << "Saving file done\n" << std::flush;
732 __import_queued_samples();
733 file_structure_changed_signal.emit(this->file);
734 return true;
735 }
736
737 void MainWindow::on_action_file_save_as()
738 {
739 if (!check_if_savable()) return;
740 file_save_as();
741 }
742
743 bool MainWindow::file_save_as()
744 {
745 Gtk::FileChooserDialog dialog(*this, _("Save as"), Gtk::FILE_CHOOSER_ACTION_SAVE);
746 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
747 dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
748 dialog.set_default_response(Gtk::RESPONSE_OK);
749
750 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 8) || GTKMM_MAJOR_VERSION > 2
751 dialog.set_do_overwrite_confirmation();
752 // TODO: an overwrite dialog for gtkmm < 2.8
753 #endif
754 Gtk::FileFilter filter;
755 filter.add_pattern("*.gig");
756 dialog.set_filter(filter);
757
758 // set initial dir and filename of the Save As dialog
759 // and prepare that initial filename as a copy of the gig
760 {
761 std::string basename = Glib::path_get_basename(filename);
762 std::string dir = Glib::path_get_dirname(filename);
763 basename = std::string("copy_of_") + basename;
764 Glib::ustring copyFileName = Glib::build_filename(dir, basename);
765 if (Glib::path_is_absolute(filename)) {
766 dialog.set_filename(copyFileName);
767 } else {
768 if (current_gig_dir != "") dialog.set_current_folder(current_gig_dir);
769 }
770 dialog.set_current_name(Glib::filename_display_basename(copyFileName));
771 }
772
773 // show warning in the dialog
774 Gtk::HBox descriptionArea;
775 descriptionArea.set_spacing(15);
776 Gtk::Image warningIcon(Gtk::Stock::DIALOG_WARNING, Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
777 descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
778 view::WrapLabel description;
779 description.set_markup(
780 _("\n<b>CAUTION:</b> You <b>MUST</b> use the "
781 "<span style=\"italic\">\"Save\"</span> dialog instead of "
782 "<span style=\"italic\">\"Save As...\"</span> if you want to save "
783 "to the same .gig file. Using "
784 "<span style=\"italic\">\"Save As...\"</span> for writing to the "
785 "same .gig file will end up in corrupted sample wave data!\n")
786 );
787 descriptionArea.pack_start(description);
788 dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
789 descriptionArea.show_all();
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_gig_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 (current_sample_dir != "") {
1418 dialog.set_current_folder(current_sample_dir);
1419 }
1420 if (dialog.run() == Gtk::RESPONSE_OK) {
1421 current_sample_dir = dialog.get_current_folder();
1422 Glib::ustring error_files;
1423 Glib::SListHandle<Glib::ustring> filenames = dialog.get_filenames();
1424 for (Glib::SListHandle<Glib::ustring>::iterator iter = filenames.begin();
1425 iter != filenames.end(); ++iter) {
1426 printf("Adding sample %s\n",(*iter).c_str());
1427 // use libsndfile to retrieve file informations
1428 SF_INFO info;
1429 info.format = 0;
1430 SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
1431 try {
1432 if (!hFile) throw std::string("could not open file");
1433 int bitdepth;
1434 switch (info.format & 0xff) {
1435 case SF_FORMAT_PCM_S8:
1436 case SF_FORMAT_PCM_16:
1437 case SF_FORMAT_PCM_U8:
1438 bitdepth = 16;
1439 break;
1440 case SF_FORMAT_PCM_24:
1441 case SF_FORMAT_PCM_32:
1442 case SF_FORMAT_FLOAT:
1443 case SF_FORMAT_DOUBLE:
1444 bitdepth = 24;
1445 break;
1446 default:
1447 sf_close(hFile); // close sound file
1448 throw std::string("format not supported"); // unsupported subformat (yet?)
1449 }
1450 // add a new sample to the .gig file
1451 gig::Sample* sample = file->AddSample();
1452 // file name without path
1453 Glib::ustring filename = Glib::filename_display_basename(*iter);
1454 // remove file extension if there is one
1455 for (int i = 0; supportedFileTypes[i]; i++) {
1456 if (Glib::str_has_suffix(filename, supportedFileTypes[i] + 1)) {
1457 filename.erase(filename.length() - strlen(supportedFileTypes[i] + 1));
1458 break;
1459 }
1460 }
1461 sample->pInfo->Name = filename;
1462 sample->Channels = info.channels;
1463 sample->BitDepth = bitdepth;
1464 sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
1465 sample->SamplesPerSecond = info.samplerate;
1466 sample->AverageBytesPerSecond = sample->FrameSize * sample->SamplesPerSecond;
1467 sample->BlockAlign = sample->FrameSize;
1468 sample->SamplesTotal = info.frames;
1469
1470 SF_INSTRUMENT instrument;
1471 if (sf_command(hFile, SFC_GET_INSTRUMENT,
1472 &instrument, sizeof(instrument)) != SF_FALSE)
1473 {
1474 sample->MIDIUnityNote = instrument.basenote;
1475
1476 #if HAVE_SF_INSTRUMENT_LOOPS
1477 if (instrument.loop_count && instrument.loops[0].mode != SF_LOOP_NONE) {
1478 sample->Loops = 1;
1479
1480 switch (instrument.loops[0].mode) {
1481 case SF_LOOP_FORWARD:
1482 sample->LoopType = gig::loop_type_normal;
1483 break;
1484 case SF_LOOP_BACKWARD:
1485 sample->LoopType = gig::loop_type_backward;
1486 break;
1487 case SF_LOOP_ALTERNATING:
1488 sample->LoopType = gig::loop_type_bidirectional;
1489 break;
1490 }
1491 sample->LoopStart = instrument.loops[0].start;
1492 sample->LoopEnd = instrument.loops[0].end;
1493 sample->LoopPlayCount = instrument.loops[0].count;
1494 sample->LoopSize = sample->LoopEnd - sample->LoopStart + 1;
1495 }
1496 #endif
1497 }
1498
1499 // schedule resizing the sample (which will be done
1500 // physically when File::Save() is called)
1501 sample->Resize(info.frames);
1502 // make sure sample is part of the selected group
1503 group->AddSample(sample);
1504 // schedule that physical resize and sample import
1505 // (data copying), performed when "Save" is requested
1506 SampleImportItem sched_item;
1507 sched_item.gig_sample = sample;
1508 sched_item.sample_path = *iter;
1509 m_SampleImportQueue.push_back(sched_item);
1510 // add sample to the tree view
1511 Gtk::TreeModel::iterator iterSample =
1512 m_refSamplesTreeModel->append(row.children());
1513 Gtk::TreeModel::Row rowSample = *iterSample;
1514 rowSample[m_SamplesModel.m_col_name] = filename;
1515 rowSample[m_SamplesModel.m_col_sample] = sample;
1516 rowSample[m_SamplesModel.m_col_group] = NULL;
1517 // close sound file
1518 sf_close(hFile);
1519 file_changed();
1520 } catch (std::string what) { // remember the files that made trouble (and their cause)
1521 if (error_files.size()) error_files += "\n";
1522 error_files += *iter += " (" + what + ")";
1523 }
1524 }
1525 // show error message box when some file(s) could not be opened / added
1526 if (error_files.size()) {
1527 Glib::ustring txt = _("Could not add the following sample(s):\n") + error_files;
1528 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1529 msg.run();
1530 }
1531 }
1532 }
1533
1534 void MainWindow::on_action_replace_all_samples_in_all_groups()
1535 {
1536 if (!file) return;
1537 Gtk::FileChooserDialog dialog(*this, _("Select Folder"),
1538 Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
1539 view::WrapLabel description(
1540 _("This is a very specific function. It tries to replace all samples "
1541 "in the current gig file by samples located in the chosen "
1542 "directory.\n\n"
1543 "It works like this: For each sample in the gig file, it tries to "
1544 "find a sample file in the selected directory with the same name as "
1545 "the sample in the gig file. Optionally, you can add a filename "
1546 "extension below, which will be added to the filename expected to be "
1547 "found. That is, assume you have a gig file with a sample called "
1548 "'Snare', if you enter '.wav' below (like it's done by default), it "
1549 "expects to find a sample file called 'Snare.wav' and will replace "
1550 "the sample in the gig file accordingly. If you don't need an "
1551 "extension, blank the field below. Any gig sample where no "
1552 "appropriate sample file could be found will be reported and left "
1553 "untouched.\n")
1554 );
1555 Gtk::HBox entryArea;
1556 Gtk::Label entryLabel( _("Add filename extension: "), Gtk::ALIGN_RIGHT);
1557 Gtk::Entry postfixEntryBox;
1558 postfixEntryBox.set_text(".wav");
1559 entryArea.pack_start(entryLabel);
1560 entryArea.pack_start(postfixEntryBox);
1561 dialog.get_vbox()->pack_start(description, Gtk::PACK_SHRINK);
1562 dialog.get_vbox()->pack_start(entryArea, Gtk::PACK_SHRINK);
1563 description.show();
1564 entryArea.show_all();
1565 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1566 dialog.add_button(_("Select"), Gtk::RESPONSE_OK);
1567 dialog.set_select_multiple(false);
1568 if (current_sample_dir != "") {
1569 dialog.set_current_folder(current_sample_dir);
1570 }
1571 if (dialog.run() == Gtk::RESPONSE_OK)
1572 {
1573 current_sample_dir = dialog.get_current_folder();
1574 Glib::ustring error_files;
1575 Glib::ustring folder = dialog.get_filename();
1576 for (gig::Sample* sample = file->GetFirstSample();
1577 sample; sample = file->GetNextSample())
1578 {
1579 std::string filename =
1580 folder + G_DIR_SEPARATOR_S + sample->pInfo->Name +
1581 postfixEntryBox.get_text().raw();
1582 SF_INFO info;
1583 info.format = 0;
1584 SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &info);
1585 try
1586 {
1587 if (!hFile) throw std::string("could not open file");
1588 int bitdepth;
1589 switch (info.format & 0xff) {
1590 case SF_FORMAT_PCM_S8:
1591 case SF_FORMAT_PCM_16:
1592 case SF_FORMAT_PCM_U8:
1593 bitdepth = 16;
1594 break;
1595 case SF_FORMAT_PCM_24:
1596 case SF_FORMAT_PCM_32:
1597 case SF_FORMAT_FLOAT:
1598 case SF_FORMAT_DOUBLE:
1599 bitdepth = 24;
1600 break;
1601 default:
1602 sf_close(hFile);
1603 throw std::string("format not supported");
1604 }
1605 SampleImportItem sched_item;
1606 sched_item.gig_sample = sample;
1607 sched_item.sample_path = filename;
1608 m_SampleImportQueue.push_back(sched_item);
1609 sf_close(hFile);
1610 file_changed();
1611 }
1612 catch (std::string what)
1613 {
1614 if (error_files.size()) error_files += "\n";
1615 error_files += filename += " (" + what + ")";
1616 }
1617 }
1618 // show error message box when some file(s) could not be opened / added
1619 if (error_files.size()) {
1620 Glib::ustring txt =
1621 _("Could not replace the following sample(s):\n") + error_files;
1622 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1623 msg.run();
1624 }
1625 }
1626 }
1627
1628 void MainWindow::on_action_remove_sample() {
1629 if (!file) return;
1630 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1631 Gtk::TreeModel::iterator it = sel->get_selected();
1632 if (it) {
1633 Gtk::TreeModel::Row row = *it;
1634 gig::Group* group = row[m_SamplesModel.m_col_group];
1635 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1636 Glib::ustring name = row[m_SamplesModel.m_col_name];
1637 try {
1638 // remove group or sample from the gig file
1639 if (group) {
1640 // temporarily remember the samples that bolong to
1641 // that group (we need that to clean the queue)
1642 std::list<gig::Sample*> members;
1643 for (gig::Sample* pSample = group->GetFirstSample();
1644 pSample; pSample = group->GetNextSample()) {
1645 members.push_back(pSample);
1646 }
1647 // notify everybody that we're going to remove these samples
1648 samples_to_be_removed_signal.emit(members);
1649 // delete the group in the .gig file including the
1650 // samples that belong to the group
1651 file->DeleteGroup(group);
1652 // notify that we're done with removal
1653 samples_removed_signal.emit();
1654 // if sample(s) were just previously added, remove
1655 // them from the import queue
1656 for (std::list<gig::Sample*>::iterator member = members.begin();
1657 member != members.end(); ++member) {
1658 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1659 iter != m_SampleImportQueue.end(); ++iter) {
1660 if ((*iter).gig_sample == *member) {
1661 printf("Removing previously added sample '%s' from group '%s'\n",
1662 (*iter).sample_path.c_str(), name.c_str());
1663 m_SampleImportQueue.erase(iter);
1664 break;
1665 }
1666 }
1667 }
1668 file_changed();
1669 } else if (sample) {
1670 // notify everybody that we're going to remove this sample
1671 std::list<gig::Sample*> lsamples;
1672 lsamples.push_back(sample);
1673 samples_to_be_removed_signal.emit(lsamples);
1674 // remove sample from the .gig file
1675 file->DeleteSample(sample);
1676 // notify that we're done with removal
1677 samples_removed_signal.emit();
1678 // if sample was just previously added, remove it from
1679 // the import queue
1680 for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1681 iter != m_SampleImportQueue.end(); ++iter) {
1682 if ((*iter).gig_sample == sample) {
1683 printf("Removing previously added sample '%s'\n",
1684 (*iter).sample_path.c_str());
1685 m_SampleImportQueue.erase(iter);
1686 break;
1687 }
1688 }
1689 dimreg_changed();
1690 file_changed();
1691 }
1692 // remove respective row(s) from samples tree view
1693 m_refSamplesTreeModel->erase(it);
1694 } catch (RIFF::Exception e) {
1695 // pretend we're done with removal (i.e. to avoid dead locks)
1696 samples_removed_signal.emit();
1697 // show error message
1698 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1699 msg.run();
1700 }
1701 }
1702 }
1703
1704 // For some reason drag_data_get gets called two times for each
1705 // drag'n'drop (at least when target is an Entry). This work-around
1706 // makes sure the code in drag_data_get and drop_drag_data_received is
1707 // only executed once, as drag_begin only gets called once.
1708 void MainWindow::on_sample_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
1709 {
1710 first_call_to_drag_data_get = true;
1711 }
1712
1713 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
1714 Gtk::SelectionData& selection_data, guint, guint)
1715 {
1716 if (!first_call_to_drag_data_get) return;
1717 first_call_to_drag_data_get = false;
1718
1719 // get selected sample
1720 gig::Sample* sample = NULL;
1721 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1722 Gtk::TreeModel::iterator it = sel->get_selected();
1723 if (it) {
1724 Gtk::TreeModel::Row row = *it;
1725 sample = row[m_SamplesModel.m_col_sample];
1726 }
1727 // pass the gig::Sample as pointer
1728 selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
1729 sizeof(sample)/*length of data in bytes*/);
1730 }
1731
1732 void MainWindow::on_sample_label_drop_drag_data_received(
1733 const Glib::RefPtr<Gdk::DragContext>& context, int, int,
1734 const Gtk::SelectionData& selection_data, guint, guint time)
1735 {
1736 gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
1737
1738 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
1739 std::cout << "Drop received sample \"" <<
1740 sample->pInfo->Name << "\"" << std::endl;
1741 // drop success
1742 context->drop_reply(true, time);
1743
1744 //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
1745
1746 // notify everybody that we're going to alter the region
1747 gig::Region* region = m_RegionChooser.get_region();
1748 region_to_be_changed_signal.emit(region);
1749
1750 // find the samplechannel dimension
1751 gig::dimension_def_t* stereo_dimension = 0;
1752 for (int i = 0 ; i < region->Dimensions ; i++) {
1753 if (region->pDimensionDefinitions[i].dimension ==
1754 gig::dimension_samplechannel) {
1755 stereo_dimension = &region->pDimensionDefinitions[i];
1756 break;
1757 }
1758 }
1759 bool channels_changed = false;
1760 if (sample->Channels == 1 && stereo_dimension) {
1761 // remove the samplechannel dimension
1762 region->DeleteDimension(stereo_dimension);
1763 channels_changed = true;
1764 region_changed();
1765 }
1766 dimreg_edit.set_sample(sample);
1767
1768 if (sample->Channels == 2 && !stereo_dimension) {
1769 // add samplechannel dimension
1770 gig::dimension_def_t dim;
1771 dim.dimension = gig::dimension_samplechannel;
1772 dim.bits = 1;
1773 dim.zones = 2;
1774 region->AddDimension(&dim);
1775 channels_changed = true;
1776 region_changed();
1777 }
1778 if (channels_changed) {
1779 // unmap all samples with wrong number of channels
1780 // TODO: maybe there should be a warning dialog for this
1781 for (int i = 0 ; i < region->DimensionRegions ; i++) {
1782 gig::DimensionRegion* d = region->pDimensionRegions[i];
1783 if (d->pSample && d->pSample->Channels != sample->Channels) {
1784 gig::Sample* oldref = d->pSample;
1785 d->pSample = NULL;
1786 sample_ref_changed_signal.emit(oldref, NULL);
1787 }
1788 }
1789 }
1790
1791 // notify we're done with altering
1792 region_changed_signal.emit(region);
1793
1794 file_changed();
1795
1796 return;
1797 }
1798 // drop failed
1799 context->drop_reply(false, time);
1800 }
1801
1802 void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
1803 const Gtk::TreeModel::iterator& iter) {
1804 if (!iter) return;
1805 Gtk::TreeModel::Row row = *iter;
1806 Glib::ustring name = row[m_SamplesModel.m_col_name];
1807 gig::Group* group = row[m_SamplesModel.m_col_group];
1808 gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1809 if (group) {
1810 if (group->Name != name) {
1811 group->Name = name;
1812 printf("group name changed\n");
1813 file_changed();
1814 }
1815 } else if (sample) {
1816 if (sample->pInfo->Name != name.raw()) {
1817 sample->pInfo->Name = name.raw();
1818 printf("sample name changed\n");
1819 file_changed();
1820 }
1821 }
1822 }
1823
1824 void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
1825 const Gtk::TreeModel::iterator& iter) {
1826 if (!iter) return;
1827 Gtk::TreeModel::Row row = *iter;
1828 Glib::ustring name = row[m_Columns.m_col_name];
1829 gig::Instrument* instrument = row[m_Columns.m_col_instr];
1830 if (instrument && instrument->pInfo->Name != name.raw()) {
1831 instrument->pInfo->Name = name.raw();
1832 file_changed();
1833 }
1834 }
1835
1836 void MainWindow::set_file_is_shared(bool b) {
1837 this->file_is_shared = b;
1838
1839 if (file_is_shared) {
1840 m_AttachedStateLabel.set_label(_("live-mode"));
1841 m_AttachedStateImage.set(
1842 Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
1843 );
1844 } else {
1845 m_AttachedStateLabel.set_label(_("stand-alone"));
1846 m_AttachedStateImage.set(
1847 Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
1848 );
1849 }
1850 }
1851
1852 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
1853 return file_structure_to_be_changed_signal;
1854 }
1855
1856 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
1857 return file_structure_changed_signal;
1858 }
1859
1860 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
1861 return samples_to_be_removed_signal;
1862 }
1863
1864 sigc::signal<void>& MainWindow::signal_samples_removed() {
1865 return samples_removed_signal;
1866 }
1867
1868 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
1869 return region_to_be_changed_signal;
1870 }
1871
1872 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
1873 return region_changed_signal;
1874 }
1875
1876 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
1877 return sample_ref_changed_signal;
1878 }
1879
1880 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
1881 return dimreg_to_be_changed_signal;
1882 }
1883
1884 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
1885 return dimreg_changed_signal;
1886 }
1887
1888 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
1889 return note_on_signal;
1890 }
1891
1892 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
1893 return note_off_signal;
1894 }
1895
1896 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_hit() {
1897 return m_RegionChooser.signal_keyboard_key_hit();
1898 }
1899
1900 sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_released() {
1901 return m_RegionChooser.signal_keyboard_key_released();
1902 }

  ViewVC Help
Powered by ViewVC