/[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 1679 - (hide annotations) (download)
Tue Feb 12 14:13:46 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 71040 byte(s)
* in "Save As..." dialog set inital filename to "copy_of_<filename>" and
  show a warning message that saving to the same .gig file will corrupt
  the sample wave data
* added autoconf test for the recent virtual MIDI device support on LS
  side to prevent compile time errors when compiling gigedit against an
  old LS version

1 schoenebeck 1225 /*
2 schoenebeck 1654 * Copyright (C) 2006 - 2008 Andreas Persson
3 schoenebeck 1225 *
4     * This program is free software; you can redistribute it and/or
5     * modify it under the terms of the GNU General Public License as
6     * published by the Free Software Foundation; either version 2, or (at
7     * your option) any later version.
8     *
9     * This program is distributed in the hope that it will be useful, but
10     * WITHOUT ANY WARRANTY; without even the implied warranty of
11     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12     * General Public License for more details.
13     *
14     * You should have received a copy of the GNU General Public License
15     * along with program; see the file COPYING. If not, write to the Free
16     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17     * 02110-1301 USA.
18     */
19    
20     #include <iostream>
21    
22     #include <gtkmm/filechooserdialog.h>
23     #include <gtkmm/messagedialog.h>
24     #include <gtkmm/stock.h>
25     #include <gtkmm/targetentry.h>
26     #include <gtkmm/main.h>
27 schoenebeck 1415 #include <gtkmm/toggleaction.h>
28 schoenebeck 1225
29 schoenebeck 1396 #include "global.h"
30    
31 persson 1261 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
32 schoenebeck 1225 #define ABOUT_DIALOG
33     #include <gtkmm/aboutdialog.h>
34     #endif
35    
36 persson 1303 #if (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION < 6) || GLIBMM_MAJOR_VERSION < 2
37     namespace Glib {
38     Glib::ustring filename_display_basename(const std::string& filename)
39     {
40     gchar* gstr = g_path_get_basename(filename.c_str());
41     Glib::ustring str(gstr);
42     g_free(gstr);
43     return Glib::filename_to_utf8(str);
44     }
45     }
46     #endif
47    
48 schoenebeck 1225 #include <stdio.h>
49     #include <sndfile.h>
50    
51     #include "mainwindow.h"
52    
53 schoenebeck 1411 #include "../../gfx/status_attached.xpm"
54     #include "../../gfx/status_detached.xpm"
55    
56 schoenebeck 1225 template<class T> inline std::string ToString(T o) {
57     std::stringstream ss;
58     ss << o;
59     return ss.str();
60     }
61    
62 persson 1582 Table::Table(int x, int y) : Gtk::Table(x, y), rowno(0) { }
63    
64     void Table::add(BoolEntry& boolentry)
65     {
66     attach(boolentry.widget, 0, 2, rowno, rowno + 1,
67     Gtk::FILL, Gtk::SHRINK);
68     rowno++;
69     }
70    
71     void Table::add(BoolEntryPlus6& boolentry)
72     {
73     attach(boolentry.widget, 0, 2, rowno, rowno + 1,
74     Gtk::FILL, Gtk::SHRINK);
75     rowno++;
76     }
77    
78     void Table::add(LabelWidget& prop)
79     {
80     attach(prop.label, 1, 2, rowno, rowno + 1,
81     Gtk::FILL, Gtk::SHRINK);
82     attach(prop.widget, 2, 3, rowno, rowno + 1,
83     Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK);
84     rowno++;
85     }
86    
87 persson 1533 MainWindow::MainWindow() :
88     dimreg_label(_("Changes apply to:")),
89     dimreg_all_regions(_("all regions")),
90     dimreg_all_dimregs(_("all dimension splits")),
91     dimreg_stereo(_("both channels"))
92 schoenebeck 1225 {
93     // set_border_width(5);
94     // set_default_size(400, 200);
95    
96    
97     add(m_VBox);
98    
99     // Handle selection
100     Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
101     tree_sel_ref->signal_changed().connect(
102     sigc::mem_fun(*this, &MainWindow::on_sel_change));
103    
104     // m_TreeView.set_reorderable();
105    
106     m_TreeView.signal_button_press_event().connect_notify(
107     sigc::mem_fun(*this, &MainWindow::on_button_release));
108    
109     // Add the TreeView tab, inside a ScrolledWindow, with the button underneath:
110     m_ScrolledWindow.add(m_TreeView);
111     // m_ScrolledWindow.set_size_request(200, 600);
112     m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
113    
114     m_ScrolledWindowSamples.add(m_TreeViewSamples);
115     m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
116    
117    
118     m_TreeViewNotebook.set_size_request(300);
119    
120     m_HPaned.add1(m_TreeViewNotebook);
121 persson 1533 dimreg_hbox.add(dimreg_label);
122     dimreg_hbox.add(dimreg_all_regions);
123     dimreg_hbox.add(dimreg_all_dimregs);
124     dimreg_stereo.set_active();
125     dimreg_hbox.add(dimreg_stereo);
126     dimreg_vbox.add(dimreg_edit);
127 persson 1582 dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK);
128 persson 1533 m_HPaned.add2(dimreg_vbox);
129 schoenebeck 1225
130    
131     m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples");
132     m_TreeViewNotebook.append_page(m_ScrolledWindow, "Instruments");
133    
134    
135     actionGroup = Gtk::ActionGroup::create();
136    
137     actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
138     actionGroup->add(Gtk::Action::create("New", Gtk::Stock::NEW),
139     sigc::mem_fun(
140     *this, &MainWindow::on_action_file_new));
141     Glib::RefPtr<Gtk::Action> action =
142     Gtk::Action::create("Open", Gtk::Stock::OPEN);
143     action->property_label() = action->property_label() + "...";
144     actionGroup->add(action,
145     sigc::mem_fun(
146     *this, &MainWindow::on_action_file_open));
147     actionGroup->add(Gtk::Action::create("Save", Gtk::Stock::SAVE),
148     sigc::mem_fun(
149     *this, &MainWindow::on_action_file_save));
150     action = Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS);
151     action->property_label() = action->property_label() + "...";
152     actionGroup->add(action,
153 persson 1261 Gtk::AccelKey("<shift><control>s"),
154 schoenebeck 1225 sigc::mem_fun(
155 persson 1261 *this, &MainWindow::on_action_file_save_as));
156 schoenebeck 1225 actionGroup->add(Gtk::Action::create("Properties",
157     Gtk::Stock::PROPERTIES),
158     sigc::mem_fun(
159     *this, &MainWindow::on_action_file_properties));
160     actionGroup->add(Gtk::Action::create("InstrProperties",
161     Gtk::Stock::PROPERTIES),
162     sigc::mem_fun(
163     *this, &MainWindow::show_instr_props));
164     actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT),
165     sigc::mem_fun(
166 persson 1261 *this, &MainWindow::on_action_quit));
167 schoenebeck 1225 actionGroup->add(Gtk::Action::create("MenuInstrument", _("_Instrument")));
168    
169 schoenebeck 1415 actionGroup->add(Gtk::Action::create("MenuView", _("_View")));
170     Glib::RefPtr<Gtk::ToggleAction> toggle_action =
171     Gtk::ToggleAction::create("Statusbar", _("_Statusbar"));
172     toggle_action->set_active(true);
173     actionGroup->add(toggle_action,
174     sigc::mem_fun(
175     *this, &MainWindow::on_action_view_status_bar));
176    
177 schoenebeck 1225 action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP);
178     actionGroup->add(Gtk::Action::create("MenuHelp",
179     action->property_label()));
180     #ifdef ABOUT_DIALOG
181     actionGroup->add(Gtk::Action::create("About", Gtk::Stock::ABOUT),
182     sigc::mem_fun(
183     *this, &MainWindow::on_action_help_about));
184     #endif
185     actionGroup->add(
186     Gtk::Action::create("AddInstrument", _("Add _Instrument")),
187     sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
188     );
189     actionGroup->add(
190     Gtk::Action::create("RemoveInstrument", Gtk::Stock::REMOVE),
191     sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
192     );
193    
194     // sample right-click popup actions
195     actionGroup->add(
196     Gtk::Action::create("SampleProperties", Gtk::Stock::PROPERTIES),
197     sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
198     );
199     actionGroup->add(
200     Gtk::Action::create("AddGroup", _("Add _Group")),
201     sigc::mem_fun(*this, &MainWindow::on_action_add_group)
202     );
203     actionGroup->add(
204     Gtk::Action::create("AddSample", _("Add _Sample(s)")),
205     sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
206     );
207     actionGroup->add(
208     Gtk::Action::create("RemoveSample", Gtk::Stock::REMOVE),
209     sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
210     );
211 schoenebeck 1673 actionGroup->add(
212     Gtk::Action::create("ReplaceAllSamplesInAllGroups", _("Replace All Samples In All Groups")),
213     sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups)
214     );
215 schoenebeck 1225
216     uiManager = Gtk::UIManager::create();
217     uiManager->insert_action_group(actionGroup);
218 persson 1261 add_accel_group(uiManager->get_accel_group());
219 schoenebeck 1225
220     Glib::ustring ui_info =
221     "<ui>"
222     " <menubar name='MenuBar'>"
223     " <menu action='MenuFile'>"
224     " <menuitem action='New'/>"
225     " <menuitem action='Open'/>"
226     " <separator/>"
227     " <menuitem action='Save'/>"
228     " <menuitem action='SaveAs'/>"
229     " <separator/>"
230     " <menuitem action='Properties'/>"
231     " <separator/>"
232     " <menuitem action='Quit'/>"
233     " </menu>"
234     " <menu action='MenuInstrument'>"
235     " </menu>"
236 schoenebeck 1415 " <menu action='MenuView'>"
237     " <menuitem action='Statusbar'/>"
238     " </menu>"
239 schoenebeck 1225 #ifdef ABOUT_DIALOG
240     " <menu action='MenuHelp'>"
241     " <menuitem action='About'/>"
242     " </menu>"
243     #endif
244     " </menubar>"
245     " <popup name='PopupMenu'>"
246     " <menuitem action='InstrProperties'/>"
247     " <menuitem action='AddInstrument'/>"
248     " <separator/>"
249     " <menuitem action='RemoveInstrument'/>"
250     " </popup>"
251     " <popup name='SamplePopupMenu'>"
252     " <menuitem action='SampleProperties'/>"
253     " <menuitem action='AddGroup'/>"
254     " <menuitem action='AddSample'/>"
255 schoenebeck 1673 " <menuitem action='ReplaceAllSamplesInAllGroups' />"
256 schoenebeck 1225 " <separator/>"
257     " <menuitem action='RemoveSample'/>"
258     " </popup>"
259     "</ui>";
260     uiManager->add_ui_from_string(ui_info);
261    
262     popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
263    
264     Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar");
265     m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
266     m_VBox.pack_start(m_HPaned);
267     m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
268 schoenebeck 1661 m_VBox.pack_start(m_RegionChooser.m_VirtKeybPropsBox, Gtk::PACK_SHRINK);
269 schoenebeck 1225 m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
270 schoenebeck 1411 m_VBox.pack_start(m_StatusBar, Gtk::PACK_SHRINK);
271 schoenebeck 1225
272 schoenebeck 1411 // Status Bar:
273     m_StatusBar.pack_start(m_AttachedStateLabel, Gtk::PACK_SHRINK);
274     m_StatusBar.pack_start(m_AttachedStateImage, Gtk::PACK_SHRINK);
275     m_StatusBar.show();
276    
277 persson 1261 m_RegionChooser.signal_region_selected().connect(
278 schoenebeck 1225 sigc::mem_fun(*this, &MainWindow::region_changed) );
279 persson 1261 m_DimRegionChooser.signal_dimregion_selected().connect(
280 schoenebeck 1225 sigc::mem_fun(*this, &MainWindow::dimreg_changed) );
281    
282    
283     // Create the Tree model:
284     m_refTreeModel = Gtk::ListStore::create(m_Columns);
285     m_TreeView.set_model(m_refTreeModel);
286     m_refTreeModel->signal_row_changed().connect(
287     sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
288     );
289    
290     // Add the TreeView's view columns:
291     m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name);
292     m_TreeView.set_headers_visible(false);
293    
294     // create samples treeview (including its data model)
295     m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
296     m_TreeViewSamples.set_model(m_refSamplesTreeModel);
297     // m_TreeViewSamples.set_reorderable();
298     m_TreeViewSamples.append_column_editable("Samples", m_SamplesModel.m_col_name);
299     m_TreeViewSamples.set_headers_visible(false);
300     m_TreeViewSamples.signal_button_press_event().connect_notify(
301     sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
302     );
303     m_refSamplesTreeModel->signal_row_changed().connect(
304     sigc::mem_fun(*this, &MainWindow::sample_name_changed)
305     );
306    
307     // establish drag&drop between samples tree view and dimension region 'Sample' text entry
308     std::list<Gtk::TargetEntry> drag_target_gig_sample;
309     drag_target_gig_sample.push_back( Gtk::TargetEntry("gig::Sample") );
310     m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
311 persson 1303 m_TreeViewSamples.signal_drag_begin().connect(
312     sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_begin)
313     );
314 schoenebeck 1225 m_TreeViewSamples.signal_drag_data_get().connect(
315     sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get)
316     );
317     dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample);
318     dimreg_edit.wSample->signal_drag_data_received().connect(
319     sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
320     );
321 persson 1261 dimreg_edit.signal_dimreg_changed().connect(
322 schoenebeck 1322 sigc::hide(sigc::mem_fun(*this, &MainWindow::file_changed)));
323 persson 1261 m_RegionChooser.signal_instrument_changed().connect(
324     sigc::mem_fun(*this, &MainWindow::file_changed));
325     m_DimRegionChooser.signal_region_changed().connect(
326     sigc::mem_fun(*this, &MainWindow::file_changed));
327     instrumentProps.signal_instrument_changed().connect(
328     sigc::mem_fun(*this, &MainWindow::file_changed));
329 persson 1582 propDialog.signal_info_changed().connect(
330     sigc::mem_fun(*this, &MainWindow::file_changed));
331 schoenebeck 1322
332     dimreg_edit.signal_dimreg_to_be_changed().connect(
333     dimreg_to_be_changed_signal.make_slot());
334     dimreg_edit.signal_dimreg_changed().connect(
335     dimreg_changed_signal.make_slot());
336     dimreg_edit.signal_sample_ref_changed().connect(
337     sample_ref_changed_signal.make_slot());
338    
339     m_RegionChooser.signal_instrument_struct_to_be_changed().connect(
340     sigc::hide(
341     sigc::bind(
342     file_structure_to_be_changed_signal.make_slot(),
343     sigc::ref(this->file)
344     )
345     )
346     );
347     m_RegionChooser.signal_instrument_struct_changed().connect(
348     sigc::hide(
349     sigc::bind(
350     file_structure_changed_signal.make_slot(),
351     sigc::ref(this->file)
352     )
353     )
354     );
355     m_RegionChooser.signal_region_to_be_changed().connect(
356     region_to_be_changed_signal.make_slot());
357     m_RegionChooser.signal_region_changed_signal().connect(
358     region_changed_signal.make_slot());
359    
360 schoenebeck 1654 note_on_signal.connect(
361     sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_on_event));
362     note_off_signal.connect(
363     sigc::mem_fun(m_RegionChooser, &RegionChooser::on_note_off_event));
364    
365 persson 1533 dimreg_all_regions.signal_toggled().connect(
366     sigc::mem_fun(*this, &MainWindow::update_dimregs));
367     dimreg_all_dimregs.signal_toggled().connect(
368     sigc::mem_fun(*this, &MainWindow::dimreg_all_dimregs_toggled));
369     dimreg_stereo.signal_toggled().connect(
370     sigc::mem_fun(*this, &MainWindow::update_dimregs));
371    
372 schoenebeck 1225 file = 0;
373 persson 1261 file_is_changed = false;
374 schoenebeck 1411 set_file_is_shared(false);
375 schoenebeck 1225
376     show_all_children();
377 schoenebeck 1300
378     // start with a new gig file by default
379     on_action_file_new();
380 schoenebeck 1225 }
381    
382     MainWindow::~MainWindow()
383     {
384     }
385    
386 persson 1261 bool MainWindow::on_delete_event(GdkEventAny* event)
387     {
388 schoenebeck 1382 return !file_is_shared && file_is_changed && !close_confirmation_dialog();
389 persson 1261 }
390    
391     void MainWindow::on_action_quit()
392     {
393 schoenebeck 1382 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
394 persson 1261 hide();
395     }
396    
397 schoenebeck 1225 void MainWindow::region_changed()
398     {
399     m_DimRegionChooser.set_region(m_RegionChooser.get_region());
400     }
401    
402 persson 1533 gig::Instrument* MainWindow::get_instrument()
403 schoenebeck 1225 {
404 persson 1533 gig::Instrument* instrument = 0;
405 schoenebeck 1225 Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
406    
407     Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();
408     if (it) {
409     Gtk::TreeModel::Row row = *it;
410 persson 1533 instrument = row[m_Columns.m_col_instr];
411     }
412     return instrument;
413     }
414 schoenebeck 1225
415 persson 1533 void MainWindow::add_region_to_dimregs(gig::Region* region, bool stereo, bool all_dimregs)
416     {
417     if (all_dimregs) {
418     for (int i = 0 ; i < region->DimensionRegions ; i++) {
419     if (region->pDimensionRegions[i]) {
420     dimreg_edit.dimregs.insert(region->pDimensionRegions[i]);
421     }
422     }
423 schoenebeck 1225 } else {
424 persson 1533 m_DimRegionChooser.get_dimregions(region, stereo, dimreg_edit.dimregs);
425 schoenebeck 1225 }
426     }
427    
428 persson 1533 void MainWindow::update_dimregs()
429     {
430     dimreg_edit.dimregs.clear();
431     bool all_regions = dimreg_all_regions.get_active();
432     bool stereo = dimreg_stereo.get_active();
433     bool all_dimregs = dimreg_all_dimregs.get_active();
434    
435     if (all_regions) {
436     gig::Instrument* instrument = get_instrument();
437     if (instrument) {
438     for (gig::Region* region = instrument->GetFirstRegion() ;
439     region ;
440     region = instrument->GetNextRegion()) {
441     add_region_to_dimregs(region, stereo, all_dimregs);
442     }
443     }
444     } else {
445     gig::Region* region = m_RegionChooser.get_region();
446     if (region) {
447     add_region_to_dimregs(region, stereo, all_dimregs);
448     }
449     }
450     }
451    
452     void MainWindow::dimreg_all_dimregs_toggled()
453     {
454     dimreg_stereo.set_sensitive(!dimreg_all_dimregs.get_active());
455     update_dimregs();
456     }
457    
458     void MainWindow::dimreg_changed()
459     {
460     update_dimregs();
461     dimreg_edit.set_dim_region(m_DimRegionChooser.get_dimregion());
462     }
463    
464     void MainWindow::on_sel_change()
465     {
466     m_RegionChooser.set_instrument(get_instrument());
467     }
468    
469 schoenebeck 1225 void loader_progress_callback(gig::progress_t* progress)
470     {
471     Loader* loader = static_cast<Loader*>(progress->custom);
472     loader->progress_callback(progress->factor);
473     }
474    
475     void Loader::progress_callback(float fraction)
476     {
477     {
478     Glib::Mutex::Lock lock(progressMutex);
479     progress = fraction;
480     }
481     progress_dispatcher();
482     }
483    
484     void Loader::thread_function()
485     {
486     printf("thread_function self=%x\n", Glib::Thread::self());
487     printf("Start %s\n", filename);
488     RIFF::File* riff = new RIFF::File(filename);
489     gig = new gig::File(riff);
490     gig::progress_t progress;
491     progress.callback = loader_progress_callback;
492     progress.custom = this;
493    
494     gig->GetInstrument(0, &progress);
495     printf("End\n");
496     finished_dispatcher();
497     }
498    
499     Loader::Loader(const char* filename)
500     : thread(0), filename(filename)
501     {
502     }
503    
504     void Loader::launch()
505     {
506     thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true);
507     printf("launch thread=%x\n", thread);
508     }
509    
510     float Loader::get_progress()
511     {
512     float res;
513     {
514     Glib::Mutex::Lock lock(progressMutex);
515     res = progress;
516     }
517     return res;
518     }
519    
520     Glib::Dispatcher& Loader::signal_progress()
521     {
522     return progress_dispatcher;
523     }
524    
525     Glib::Dispatcher& Loader::signal_finished()
526     {
527     return finished_dispatcher;
528     }
529    
530     LoadDialog::LoadDialog(const Glib::ustring& title, Gtk::Window& parent)
531     : Gtk::Dialog(title, parent, true)
532     {
533     get_vbox()->pack_start(progressBar);
534     show_all_children();
535     }
536    
537     // Clear all GUI elements / controls. This method is typically called
538     // before a new .gig file is to be created or to be loaded.
539     void MainWindow::__clear() {
540     // remove all entries from "Instrument" menu
541     Gtk::MenuItem* instrument_menu =
542     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument"));
543     instrument_menu->hide();
544     for (int i = 0; i < instrument_menu->get_submenu()->items().size(); i++) {
545     delete &instrument_menu->get_submenu()->items()[i];
546     }
547     instrument_menu->get_submenu()->items().clear();
548     // forget all samples that ought to be imported
549     m_SampleImportQueue.clear();
550     // clear the samples and instruments tree views
551     m_refTreeModel->clear();
552     m_refSamplesTreeModel->clear();
553     // free libgig's gig::File instance
554 schoenebeck 1382 if (file && !file_is_shared) delete file;
555     file = NULL;
556 schoenebeck 1411 set_file_is_shared(false);
557 schoenebeck 1225 }
558    
559     void MainWindow::on_action_file_new()
560     {
561 schoenebeck 1382 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
562 persson 1261
563 schoenebeck 1382 if (file_is_shared && !leaving_shared_mode_dialog()) return;
564    
565 schoenebeck 1225 // clear all GUI elements
566     __clear();
567     // create a new .gig file (virtually yet)
568     gig::File* pFile = new gig::File;
569     // already add one new instrument by default
570     gig::Instrument* pInstrument = pFile->AddInstrument();
571     pInstrument->pInfo->Name = "Unnamed Instrument";
572     // update GUI with that new gig::File
573 persson 1261 load_gig(pFile, 0 /*no file name yet*/);
574 schoenebeck 1225 }
575    
576 persson 1261 bool MainWindow::close_confirmation_dialog()
577     {
578     gchar* msg = g_strdup_printf(_("Save changes to \"%s\" before closing?"),
579     Glib::filename_display_basename(filename).c_str());
580     Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
581     g_free(msg);
582 persson 1303 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
583 persson 1261 dialog.set_secondary_text(_("If you close without saving, your changes will be lost."));
584 persson 1303 #endif
585 persson 1261 dialog.add_button(_("Close _Without Saving"), Gtk::RESPONSE_NO);
586     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
587     dialog.add_button(file_has_name ? Gtk::Stock::SAVE : Gtk::Stock::SAVE_AS, Gtk::RESPONSE_YES);
588     dialog.set_default_response(Gtk::RESPONSE_YES);
589     int response = dialog.run();
590 persson 1303 dialog.hide();
591 persson 1261 if (response == Gtk::RESPONSE_YES) return file_save();
592     return response != Gtk::RESPONSE_CANCEL;
593     }
594    
595 schoenebeck 1382 bool MainWindow::leaving_shared_mode_dialog() {
596     Glib::ustring msg = _("Detach from sampler and proceed working stand-alone?");
597     Gtk::MessageDialog dialog(*this, msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE);
598     #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6) || GTKMM_MAJOR_VERSION > 2
599     dialog.set_secondary_text(
600     _("If you proceed to work on another instrument file, it won't be "
601     "used by the sampler until you tell the sampler explicitly to "
602     "load it.")
603     );
604     #endif
605     dialog.add_button(_("_Yes, Detach"), Gtk::RESPONSE_YES);
606     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
607     dialog.set_default_response(Gtk::RESPONSE_CANCEL);
608     int response = dialog.run();
609     dialog.hide();
610     return response == Gtk::RESPONSE_YES;
611     }
612    
613 schoenebeck 1225 void MainWindow::on_action_file_open()
614     {
615 schoenebeck 1382 if (!file_is_shared && file_is_changed && !close_confirmation_dialog()) return;
616 persson 1261
617 schoenebeck 1382 if (file_is_shared && !leaving_shared_mode_dialog()) return;
618    
619 schoenebeck 1225 Gtk::FileChooserDialog dialog(*this, _("Open file"));
620     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
621     dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
622 persson 1261 dialog.set_default_response(Gtk::RESPONSE_OK);
623 schoenebeck 1225 Gtk::FileFilter filter;
624     filter.add_pattern("*.gig");
625     dialog.set_filter(filter);
626 persson 1261 if (current_dir != "") {
627     dialog.set_current_folder(current_dir);
628     }
629 schoenebeck 1225 if (dialog.run() == Gtk::RESPONSE_OK) {
630 persson 1261 std::string filename = dialog.get_filename();
631     printf("filename=%s\n", filename.c_str());
632 schoenebeck 1225 printf("on_action_file_open self=%x\n", Glib::Thread::self());
633 persson 1261 load_file(filename.c_str());
634     current_dir = Glib::path_get_dirname(filename);
635 schoenebeck 1225 }
636     }
637    
638     void MainWindow::load_file(const char* name)
639     {
640 persson 1303 __clear();
641 schoenebeck 1225 load_dialog = new LoadDialog("Loading...", *this);
642     load_dialog->show_all();
643     loader = new Loader(strdup(name));
644     loader->signal_progress().connect(
645     sigc::mem_fun(*this, &MainWindow::on_loader_progress));
646     loader->signal_finished().connect(
647     sigc::mem_fun(*this, &MainWindow::on_loader_finished));
648     loader->launch();
649     }
650    
651     void MainWindow::load_instrument(gig::Instrument* instr) {
652     if (!instr) {
653     Glib::ustring txt = "Provided instrument is NULL!\n";
654     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
655     msg.run();
656     Gtk::Main::quit();
657     }
658 schoenebeck 1328 // clear all GUI elements
659     __clear();
660     // load the instrument
661 schoenebeck 1225 gig::File* pFile = (gig::File*) instr->GetParent();
662 schoenebeck 1382 load_gig(pFile, 0 /*file name*/, true /*shared instrument*/);
663 schoenebeck 1225 //TODO: automatically select the given instrument
664     }
665    
666     void MainWindow::on_loader_progress()
667     {
668     load_dialog->set_fraction(loader->get_progress());
669     }
670    
671     void MainWindow::on_loader_finished()
672     {
673     printf("Loader finished!\n");
674     printf("on_loader_finished self=%x\n", Glib::Thread::self());
675     load_gig(loader->gig, loader->filename);
676     load_dialog->hide();
677     }
678    
679     void MainWindow::on_action_file_save()
680     {
681 persson 1261 file_save();
682     }
683    
684 persson 1303 bool MainWindow::check_if_savable()
685     {
686     if (!file) return false;
687    
688     if (!file->GetFirstSample()) {
689     Gtk::MessageDialog(*this, _("The file could not be saved "
690     "because it contains no samples"),
691     false, Gtk::MESSAGE_ERROR).run();
692     return false;
693     }
694    
695     for (gig::Instrument* instrument = file->GetFirstInstrument() ; instrument ;
696     instrument = file->GetNextInstrument()) {
697     if (!instrument->GetFirstRegion()) {
698     Gtk::MessageDialog(*this, _("The file could not be saved "
699     "because there are instruments "
700     "that have no regions"),
701     false, Gtk::MESSAGE_ERROR).run();
702     return false;
703     }
704     }
705     return true;
706     }
707    
708 persson 1261 bool MainWindow::file_save()
709     {
710 persson 1303 if (!check_if_savable()) return false;
711 schoenebeck 1382 if (!file_is_shared && !file_has_name) return file_save_as();
712 persson 1261
713 schoenebeck 1225 std::cout << "Saving file\n" << std::flush;
714 schoenebeck 1322 file_structure_to_be_changed_signal.emit(this->file);
715 schoenebeck 1225 try {
716     file->Save();
717 persson 1261 if (file_is_changed) {
718     set_title(get_title().substr(1));
719     file_is_changed = false;
720     }
721 schoenebeck 1225 } catch (RIFF::Exception e) {
722 schoenebeck 1322 file_structure_changed_signal.emit(this->file);
723 schoenebeck 1382 Glib::ustring txt = _("Could not save file: ") + e.Message;
724 schoenebeck 1225 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
725     msg.run();
726 persson 1261 return false;
727 schoenebeck 1225 }
728     std::cout << "Saving file done\n" << std::flush;
729     __import_queued_samples();
730 schoenebeck 1322 file_structure_changed_signal.emit(this->file);
731 persson 1261 return true;
732 schoenebeck 1225 }
733    
734     void MainWindow::on_action_file_save_as()
735     {
736 persson 1303 if (!check_if_savable()) return;
737 persson 1261 file_save_as();
738     }
739    
740     bool MainWindow::file_save_as()
741     {
742     Gtk::FileChooserDialog dialog(*this, _("Save as"), Gtk::FILE_CHOOSER_ACTION_SAVE);
743 schoenebeck 1225 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
744     dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
745 persson 1261 dialog.set_default_response(Gtk::RESPONSE_OK);
746    
747     #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 8) || GTKMM_MAJOR_VERSION > 2
748     dialog.set_do_overwrite_confirmation();
749     // TODO: an overwrite dialog for gtkmm < 2.8
750     #endif
751 schoenebeck 1225 Gtk::FileFilter filter;
752     filter.add_pattern("*.gig");
753     dialog.set_filter(filter);
754 persson 1261
755 schoenebeck 1679 // set initial dir and filename of the Save As dialog
756     // and prepare that initial filename as a copy of the gig
757     {
758     std::string basename = Glib::path_get_basename(filename);
759     std::string dir = Glib::path_get_dirname(filename);
760     basename = std::string("copy_of_") + basename;
761     Glib::ustring copyFileName = Glib::build_filename(dir, basename);
762     if (Glib::path_is_absolute(filename)) {
763     dialog.set_filename(copyFileName);
764     } else {
765     if (current_dir != "") dialog.set_current_folder(current_dir);
766     }
767     dialog.set_current_name(Glib::filename_display_basename(copyFileName));
768 persson 1261 }
769    
770 schoenebeck 1679 // show warning in the dialog
771     Gtk::HBox descriptionArea;
772     descriptionArea.set_spacing(15);
773     Gtk::Image warningIcon(Gtk::Stock::DIALOG_WARNING, Gtk::IconSize(Gtk::ICON_SIZE_DIALOG));
774     descriptionArea.pack_start(warningIcon, Gtk::PACK_SHRINK);
775     warningIcon.show();
776     Gtk::Label description;
777     description.set_markup(
778     _("\n<b>CAUTION:</b> You <b>MUST</b> use the "
779     "<span style=\"italic\">\"Save\"</span> dialog instead of "
780     "<span style=\"italic\">\"Save As...\"</span> if you want to save "
781     "to the same .gig file. Using "
782     "<span style=\"italic\">\"Save As...\"</span> for writing to the "
783     "same .gig file will end up in corrupted sample wave data!\n")
784     );
785     description.set_line_wrap(true);
786     descriptionArea.pack_start(description, Gtk::PACK_SHRINK);
787     description.show();
788     dialog.get_vbox()->pack_start(descriptionArea, Gtk::PACK_SHRINK);
789     descriptionArea.show();
790    
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     current_dir = Glib::path_get_dirname(filename);
802     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     if (dialog.run() == Gtk::RESPONSE_OK) {
1418     Glib::ustring error_files;
1419     Glib::SListHandle<Glib::ustring> filenames = dialog.get_filenames();
1420     for (Glib::SListHandle<Glib::ustring>::iterator iter = filenames.begin();
1421     iter != filenames.end(); ++iter) {
1422     printf("Adding sample %s\n",(*iter).c_str());
1423     // use libsndfile to retrieve file informations
1424     SF_INFO info;
1425     info.format = 0;
1426     SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
1427     try {
1428     if (!hFile) throw std::string("could not open file");
1429     int bitdepth;
1430     switch (info.format & 0xff) {
1431     case SF_FORMAT_PCM_S8:
1432     case SF_FORMAT_PCM_16:
1433 persson 1265 case SF_FORMAT_PCM_U8:
1434 schoenebeck 1225 bitdepth = 16;
1435     break;
1436     case SF_FORMAT_PCM_24:
1437     case SF_FORMAT_PCM_32:
1438     case SF_FORMAT_FLOAT:
1439     case SF_FORMAT_DOUBLE:
1440 persson 1265 bitdepth = 24;
1441 schoenebeck 1225 break;
1442     default:
1443     sf_close(hFile); // close sound file
1444     throw std::string("format not supported"); // unsupported subformat (yet?)
1445     }
1446     // add a new sample to the .gig file
1447     gig::Sample* sample = file->AddSample();
1448     // file name without path
1449 persson 1262 Glib::ustring filename = Glib::filename_display_basename(*iter);
1450     // remove file extension if there is one
1451     for (int i = 0; supportedFileTypes[i]; i++) {
1452     if (Glib::str_has_suffix(filename, supportedFileTypes[i] + 1)) {
1453     filename.erase(filename.length() - strlen(supportedFileTypes[i] + 1));
1454     break;
1455     }
1456     }
1457     sample->pInfo->Name = filename;
1458 schoenebeck 1225 sample->Channels = info.channels;
1459     sample->BitDepth = bitdepth;
1460     sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
1461     sample->SamplesPerSecond = info.samplerate;
1462 persson 1265 sample->AverageBytesPerSecond = sample->FrameSize * sample->SamplesPerSecond;
1463     sample->BlockAlign = sample->FrameSize;
1464     sample->SamplesTotal = info.frames;
1465    
1466     SF_INSTRUMENT instrument;
1467     if (sf_command(hFile, SFC_GET_INSTRUMENT,
1468     &instrument, sizeof(instrument)) != SF_FALSE)
1469     {
1470     sample->MIDIUnityNote = instrument.basenote;
1471    
1472 persson 1303 #if HAVE_SF_INSTRUMENT_LOOPS
1473 persson 1265 if (instrument.loop_count && instrument.loops[0].mode != SF_LOOP_NONE) {
1474     sample->Loops = 1;
1475    
1476     switch (instrument.loops[0].mode) {
1477     case SF_LOOP_FORWARD:
1478     sample->LoopType = gig::loop_type_normal;
1479     break;
1480     case SF_LOOP_BACKWARD:
1481     sample->LoopType = gig::loop_type_backward;
1482     break;
1483     case SF_LOOP_ALTERNATING:
1484     sample->LoopType = gig::loop_type_bidirectional;
1485     break;
1486     }
1487     sample->LoopStart = instrument.loops[0].start;
1488     sample->LoopEnd = instrument.loops[0].end;
1489     sample->LoopPlayCount = instrument.loops[0].count;
1490     sample->LoopSize = sample->LoopEnd - sample->LoopStart + 1;
1491     }
1492 persson 1303 #endif
1493 persson 1265 }
1494    
1495 schoenebeck 1225 // schedule resizing the sample (which will be done
1496     // physically when File::Save() is called)
1497     sample->Resize(info.frames);
1498     // make sure sample is part of the selected group
1499     group->AddSample(sample);
1500     // schedule that physical resize and sample import
1501     // (data copying), performed when "Save" is requested
1502     SampleImportItem sched_item;
1503     sched_item.gig_sample = sample;
1504     sched_item.sample_path = *iter;
1505     m_SampleImportQueue.push_back(sched_item);
1506     // add sample to the tree view
1507     Gtk::TreeModel::iterator iterSample =
1508     m_refSamplesTreeModel->append(row.children());
1509     Gtk::TreeModel::Row rowSample = *iterSample;
1510 persson 1262 rowSample[m_SamplesModel.m_col_name] = filename;
1511 schoenebeck 1225 rowSample[m_SamplesModel.m_col_sample] = sample;
1512     rowSample[m_SamplesModel.m_col_group] = NULL;
1513     // close sound file
1514     sf_close(hFile);
1515 persson 1261 file_changed();
1516 schoenebeck 1225 } catch (std::string what) { // remember the files that made trouble (and their cause)
1517     if (error_files.size()) error_files += "\n";
1518     error_files += *iter += " (" + what + ")";
1519     }
1520     }
1521     // show error message box when some file(s) could not be opened / added
1522     if (error_files.size()) {
1523 schoenebeck 1382 Glib::ustring txt = _("Could not add the following sample(s):\n") + error_files;
1524 schoenebeck 1225 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1525     msg.run();
1526     }
1527     }
1528     }
1529    
1530 schoenebeck 1673 void MainWindow::on_action_replace_all_samples_in_all_groups()
1531     {
1532     if (!file) return;
1533     Gtk::FileChooserDialog dialog(*this, _("Select Folder"),
1534     Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
1535     Gtk::Label description(
1536     _("This is a very specific function. It tries to replace all samples "
1537     "in the current gig file by samples located in the directory chosen "
1538     "by you above.\n\n"
1539     "It works like this: For each sample in the gig file it tries to "
1540     "find a sample file in the selected directory with the same name as "
1541     "the sample in the gig file. Optionally you can add a filename "
1542     "postfix below, which will be added to the filename expected to be "
1543     "found. That is, assume you have a gig file with a sample called "
1544     "'Snare', if you enter '.wav' below (like it's done by default), it "
1545     "assumes to find a sample file called 'Snare.wav' and will replace "
1546     "the sample in the gig file accordingly. If you don't need such a "
1547     "postfix, blank the field below. Any gig sample where no "
1548     "appropriate sample file could be found, will be reported and left "
1549     "untouched.\n\n")
1550     );
1551     description.set_line_wrap(true);
1552     Gtk::HBox entryArea;
1553     Gtk::Label entryLabel( _("Add Filename Extension: "), Gtk::ALIGN_RIGHT);
1554     Gtk::Entry postfixEntryBox;
1555     postfixEntryBox.set_text(".wav");
1556     entryArea.pack_start(entryLabel);
1557     entryArea.pack_start(postfixEntryBox);
1558     dialog.get_vbox()->pack_start(description, Gtk::PACK_SHRINK);
1559     dialog.get_vbox()->pack_start(entryArea, Gtk::PACK_SHRINK);
1560     description.show();
1561     entryLabel.show();
1562     postfixEntryBox.show();
1563     entryArea.show();
1564     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1565     dialog.add_button(_("Select"), Gtk::RESPONSE_OK);
1566     dialog.set_select_multiple(false);
1567     // fix label width (because Gtk by design doesn't
1568     // know anything about the parent's size)
1569     #if 0 //FIXME: doesn't work
1570     int dialogW, dialogH, labelW, labelH;
1571     dialog.get_size_request(dialogW, dialogH);
1572     description.get_size_request(labelW, labelH);
1573     std::cout << "dialog(" << dialogW << "," << dialogH << ")\nlabel(" << labelW << "," << labelH << ")\n" << std::flush;
1574     description.set_size_request(dialogW, labelH);
1575     #endif
1576     if (dialog.run() == Gtk::RESPONSE_OK)
1577     {
1578     Glib::ustring error_files;
1579     Glib::ustring folder = dialog.get_filename();
1580     for (gig::Sample* sample = file->GetFirstSample();
1581     sample; sample = file->GetNextSample())
1582     {
1583     std::string filename =
1584     folder + G_DIR_SEPARATOR_S + sample->pInfo->Name +
1585     postfixEntryBox.get_text().raw();
1586     SF_INFO info;
1587     info.format = 0;
1588     SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &info);
1589     try
1590     {
1591     if (!hFile) throw std::string("could not open file");
1592     int bitdepth;
1593     switch (info.format & 0xff) {
1594     case SF_FORMAT_PCM_S8:
1595     case SF_FORMAT_PCM_16:
1596     case SF_FORMAT_PCM_U8:
1597     bitdepth = 16;
1598     break;
1599     case SF_FORMAT_PCM_24:
1600     case SF_FORMAT_PCM_32:
1601     case SF_FORMAT_FLOAT:
1602     case SF_FORMAT_DOUBLE:
1603     bitdepth = 24;
1604     break;
1605     default:
1606     sf_close(hFile);
1607     throw std::string("format not supported");
1608     }
1609     SampleImportItem sched_item;
1610     sched_item.gig_sample = sample;
1611     sched_item.sample_path = filename;
1612     m_SampleImportQueue.push_back(sched_item);
1613     sf_close(hFile);
1614     file_changed();
1615     }
1616     catch (std::string what)
1617     {
1618     if (error_files.size()) error_files += "\n";
1619     error_files += filename += " (" + what + ")";
1620     }
1621     }
1622     // show error message box when some file(s) could not be opened / added
1623     if (error_files.size()) {
1624     Glib::ustring txt =
1625     _("Could not replace the following sample(s):\n") + error_files;
1626     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1627     msg.run();
1628     }
1629     }
1630     }
1631    
1632 schoenebeck 1225 void MainWindow::on_action_remove_sample() {
1633     if (!file) return;
1634     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1635     Gtk::TreeModel::iterator it = sel->get_selected();
1636     if (it) {
1637     Gtk::TreeModel::Row row = *it;
1638     gig::Group* group = row[m_SamplesModel.m_col_group];
1639     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1640     Glib::ustring name = row[m_SamplesModel.m_col_name];
1641     try {
1642     // remove group or sample from the gig file
1643     if (group) {
1644     // temporarily remember the samples that bolong to
1645     // that group (we need that to clean the queue)
1646     std::list<gig::Sample*> members;
1647     for (gig::Sample* pSample = group->GetFirstSample();
1648     pSample; pSample = group->GetNextSample()) {
1649     members.push_back(pSample);
1650     }
1651 schoenebeck 1322 // notify everybody that we're going to remove these samples
1652     samples_to_be_removed_signal.emit(members);
1653 schoenebeck 1225 // delete the group in the .gig file including the
1654     // samples that belong to the group
1655     file->DeleteGroup(group);
1656 schoenebeck 1322 // notify that we're done with removal
1657     samples_removed_signal.emit();
1658 schoenebeck 1225 // if sample(s) were just previously added, remove
1659     // them from the import queue
1660     for (std::list<gig::Sample*>::iterator member = members.begin();
1661     member != members.end(); ++member) {
1662     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1663     iter != m_SampleImportQueue.end(); ++iter) {
1664     if ((*iter).gig_sample == *member) {
1665     printf("Removing previously added sample '%s' from group '%s'\n",
1666     (*iter).sample_path.c_str(), name.c_str());
1667     m_SampleImportQueue.erase(iter);
1668     break;
1669     }
1670     }
1671     }
1672 persson 1261 file_changed();
1673 schoenebeck 1225 } else if (sample) {
1674 schoenebeck 1322 // notify everybody that we're going to remove this sample
1675     std::list<gig::Sample*> lsamples;
1676     lsamples.push_back(sample);
1677     samples_to_be_removed_signal.emit(lsamples);
1678 schoenebeck 1225 // remove sample from the .gig file
1679     file->DeleteSample(sample);
1680 schoenebeck 1322 // notify that we're done with removal
1681     samples_removed_signal.emit();
1682 schoenebeck 1225 // if sample was just previously added, remove it from
1683     // the import queue
1684     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1685     iter != m_SampleImportQueue.end(); ++iter) {
1686     if ((*iter).gig_sample == sample) {
1687     printf("Removing previously added sample '%s'\n",
1688     (*iter).sample_path.c_str());
1689     m_SampleImportQueue.erase(iter);
1690     break;
1691     }
1692     }
1693 persson 1303 dimreg_changed();
1694 persson 1261 file_changed();
1695 schoenebeck 1225 }
1696     // remove respective row(s) from samples tree view
1697     m_refSamplesTreeModel->erase(it);
1698     } catch (RIFF::Exception e) {
1699 schoenebeck 1322 // pretend we're done with removal (i.e. to avoid dead locks)
1700     samples_removed_signal.emit();
1701     // show error message
1702 schoenebeck 1225 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1703     msg.run();
1704     }
1705     }
1706     }
1707    
1708 persson 1303 // For some reason drag_data_get gets called two times for each
1709     // drag'n'drop (at least when target is an Entry). This work-around
1710     // makes sure the code in drag_data_get and drop_drag_data_received is
1711     // only executed once, as drag_begin only gets called once.
1712     void MainWindow::on_sample_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
1713     {
1714     first_call_to_drag_data_get = true;
1715     }
1716    
1717 schoenebeck 1225 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
1718     Gtk::SelectionData& selection_data, guint, guint)
1719     {
1720 persson 1303 if (!first_call_to_drag_data_get) return;
1721     first_call_to_drag_data_get = false;
1722    
1723 schoenebeck 1225 // get selected sample
1724     gig::Sample* sample = NULL;
1725     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1726     Gtk::TreeModel::iterator it = sel->get_selected();
1727     if (it) {
1728     Gtk::TreeModel::Row row = *it;
1729     sample = row[m_SamplesModel.m_col_sample];
1730     }
1731     // pass the gig::Sample as pointer
1732     selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
1733     sizeof(sample)/*length of data in bytes*/);
1734     }
1735    
1736     void MainWindow::on_sample_label_drop_drag_data_received(
1737     const Glib::RefPtr<Gdk::DragContext>& context, int, int,
1738     const Gtk::SelectionData& selection_data, guint, guint time)
1739     {
1740     gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
1741    
1742 persson 1265 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
1743 persson 1303 std::cout << "Drop received sample \"" <<
1744     sample->pInfo->Name << "\"" << std::endl;
1745     // drop success
1746     context->drop_reply(true, time);
1747    
1748 schoenebeck 1322 //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
1749    
1750     // notify everybody that we're going to alter the region
1751     gig::Region* region = m_RegionChooser.get_region();
1752     region_to_be_changed_signal.emit(region);
1753    
1754 persson 1303 // find the samplechannel dimension
1755     gig::dimension_def_t* stereo_dimension = 0;
1756     for (int i = 0 ; i < region->Dimensions ; i++) {
1757     if (region->pDimensionDefinitions[i].dimension ==
1758     gig::dimension_samplechannel) {
1759     stereo_dimension = &region->pDimensionDefinitions[i];
1760     break;
1761     }
1762 schoenebeck 1225 }
1763 persson 1303 bool channels_changed = false;
1764     if (sample->Channels == 1 && stereo_dimension) {
1765     // remove the samplechannel dimension
1766     region->DeleteDimension(stereo_dimension);
1767     channels_changed = true;
1768     region_changed();
1769     }
1770     dimreg_edit.set_sample(sample);
1771    
1772     if (sample->Channels == 2 && !stereo_dimension) {
1773     // add samplechannel dimension
1774     gig::dimension_def_t dim;
1775     dim.dimension = gig::dimension_samplechannel;
1776     dim.bits = 1;
1777     dim.zones = 2;
1778     region->AddDimension(&dim);
1779     channels_changed = true;
1780     region_changed();
1781     }
1782     if (channels_changed) {
1783     // unmap all samples with wrong number of channels
1784     // TODO: maybe there should be a warning dialog for this
1785     for (int i = 0 ; i < region->DimensionRegions ; i++) {
1786     gig::DimensionRegion* d = region->pDimensionRegions[i];
1787     if (d->pSample && d->pSample->Channels != sample->Channels) {
1788 schoenebeck 1322 gig::Sample* oldref = d->pSample;
1789     d->pSample = NULL;
1790     sample_ref_changed_signal.emit(oldref, NULL);
1791 persson 1303 }
1792     }
1793     }
1794    
1795 schoenebeck 1322 // notify we're done with altering
1796     region_changed_signal.emit(region);
1797    
1798 persson 1460 file_changed();
1799    
1800 persson 1303 return;
1801 schoenebeck 1225 }
1802     // drop failed
1803     context->drop_reply(false, time);
1804     }
1805    
1806     void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
1807     const Gtk::TreeModel::iterator& iter) {
1808     if (!iter) return;
1809     Gtk::TreeModel::Row row = *iter;
1810     Glib::ustring name = row[m_SamplesModel.m_col_name];
1811     gig::Group* group = row[m_SamplesModel.m_col_group];
1812     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1813     if (group) {
1814 persson 1261 if (group->Name != name) {
1815     group->Name = name;
1816     printf("group name changed\n");
1817     file_changed();
1818     }
1819 schoenebeck 1225 } else if (sample) {
1820 persson 1261 if (sample->pInfo->Name != name.raw()) {
1821     sample->pInfo->Name = name.raw();
1822     printf("sample name changed\n");
1823     file_changed();
1824     }
1825 schoenebeck 1225 }
1826     }
1827    
1828     void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
1829     const Gtk::TreeModel::iterator& iter) {
1830     if (!iter) return;
1831     Gtk::TreeModel::Row row = *iter;
1832     Glib::ustring name = row[m_Columns.m_col_name];
1833     gig::Instrument* instrument = row[m_Columns.m_col_instr];
1834 persson 1261 if (instrument && instrument->pInfo->Name != name.raw()) {
1835     instrument->pInfo->Name = name.raw();
1836     file_changed();
1837     }
1838 schoenebeck 1225 }
1839 schoenebeck 1322
1840 schoenebeck 1411 void MainWindow::set_file_is_shared(bool b) {
1841     this->file_is_shared = b;
1842    
1843     if (file_is_shared) {
1844     m_AttachedStateLabel.set_label(_("live-mode"));
1845     m_AttachedStateImage.set(
1846     Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
1847     );
1848     } else {
1849     m_AttachedStateLabel.set_label(_("stand-alone"));
1850     m_AttachedStateImage.set(
1851     Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
1852     );
1853     }
1854     }
1855    
1856 schoenebeck 1339 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
1857 schoenebeck 1322 return file_structure_to_be_changed_signal;
1858     }
1859    
1860 schoenebeck 1339 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
1861 schoenebeck 1322 return file_structure_changed_signal;
1862     }
1863    
1864 schoenebeck 1339 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
1865 schoenebeck 1322 return samples_to_be_removed_signal;
1866     }
1867    
1868 schoenebeck 1339 sigc::signal<void>& MainWindow::signal_samples_removed() {
1869 schoenebeck 1322 return samples_removed_signal;
1870     }
1871    
1872 schoenebeck 1339 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
1873 schoenebeck 1322 return region_to_be_changed_signal;
1874     }
1875    
1876 schoenebeck 1339 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
1877 schoenebeck 1322 return region_changed_signal;
1878     }
1879    
1880 schoenebeck 1339 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
1881 schoenebeck 1322 return sample_ref_changed_signal;
1882     }
1883    
1884 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
1885 schoenebeck 1322 return dimreg_to_be_changed_signal;
1886     }
1887    
1888 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
1889 schoenebeck 1322 return dimreg_changed_signal;
1890     }
1891 schoenebeck 1654
1892     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
1893     return note_on_signal;
1894     }
1895    
1896     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
1897     return note_off_signal;
1898     }
1899 schoenebeck 1660
1900     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_hit() {
1901     return m_RegionChooser.signal_keyboard_key_hit();
1902     }
1903    
1904     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_released() {
1905     return m_RegionChooser.signal_keyboard_key_released();
1906     }

  ViewVC Help
Powered by ViewVC