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

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

  ViewVC Help
Powered by ViewVC