/[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 1673 - (show annotations) (download)
Wed Feb 6 22:08:29 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 69657 byte(s)
* added "replace all samples" function to samples popup menu
  (patch by Anders Dahnielson, a bit extended, fixes #73)

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

  ViewVC Help
Powered by ViewVC