/[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 1714 - (show annotations) (download)
Thu Mar 6 20:50:04 2008 UTC (11 years ago) by persson
File size: 71059 byte(s)
* fixed compilation with gcc 4.3

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

  ViewVC Help
Powered by ViewVC