/[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 1725 - (hide annotations) (download)
Sat Apr 26 08:52:15 2008 UTC (15 years, 11 months ago) by persson
File size: 71393 byte(s)
* the "Add Samples" dialog now remembers current directory (patch by
  Devin Anderson, fixes #81)

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

  ViewVC Help
Powered by ViewVC