/[svn]/gigedit/trunk/src/gigedit/mainwindow.cpp
ViewVC logotype

Annotation of /gigedit/trunk/src/gigedit/mainwindow.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1654 - (hide annotations) (download)
Wed Jan 30 02:20:48 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 64203 byte(s)
* first step to make the virtual keyboard interactive: active keys of the
  sampler (in live-mode only of course) are highlighted on the virtual
  keyboard - NOTE: yet inaccurate draw of the keys and this mechanism
  yet only works on the first gigedit invocation by the sampler process,
  so this still has to be fixed

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

  ViewVC Help
Powered by ViewVC