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

1 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     if (Glib::path_is_absolute(filename)) {
756     dialog.set_filename(filename);
757     } else if (current_dir != "") {
758     dialog.set_current_folder(current_dir);
759     }
760     dialog.set_current_name(Glib::filename_display_basename(filename));
761    
762 schoenebeck 1225 if (dialog.run() == Gtk::RESPONSE_OK) {
763 schoenebeck 1322 file_structure_to_be_changed_signal.emit(this->file);
764 schoenebeck 1225 try {
765 persson 1261 std::string filename = dialog.get_filename();
766     if (!Glib::str_has_suffix(filename, ".gig")) {
767     filename += ".gig";
768     }
769     printf("filename=%s\n", filename.c_str());
770     file->Save(filename);
771     this->filename = filename;
772     current_dir = Glib::path_get_dirname(filename);
773     set_title(Glib::filename_display_basename(filename));
774     file_has_name = true;
775     file_is_changed = false;
776 schoenebeck 1225 } catch (RIFF::Exception e) {
777 schoenebeck 1322 file_structure_changed_signal.emit(this->file);
778 schoenebeck 1382 Glib::ustring txt = _("Could not save file: ") + e.Message;
779 schoenebeck 1225 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
780     msg.run();
781 persson 1261 return false;
782 schoenebeck 1225 }
783     __import_queued_samples();
784 schoenebeck 1322 file_structure_changed_signal.emit(this->file);
785 persson 1261 return true;
786 schoenebeck 1225 }
787 persson 1261 return false;
788 schoenebeck 1225 }
789    
790     // actually write the sample(s)' data to the gig file
791     void MainWindow::__import_queued_samples() {
792     std::cout << "Starting sample import\n" << std::flush;
793     Glib::ustring error_files;
794     printf("Samples to import: %d\n", m_SampleImportQueue.size());
795     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
796     iter != m_SampleImportQueue.end(); ) {
797     printf("Importing sample %s\n",(*iter).sample_path.c_str());
798     SF_INFO info;
799     info.format = 0;
800     SNDFILE* hFile = sf_open((*iter).sample_path.c_str(), SFM_READ, &info);
801     try {
802     if (!hFile) throw std::string("could not open file");
803     // determine sample's bit depth
804     int bitdepth;
805     switch (info.format & 0xff) {
806     case SF_FORMAT_PCM_S8:
807     case SF_FORMAT_PCM_16:
808 persson 1265 case SF_FORMAT_PCM_U8:
809 schoenebeck 1225 bitdepth = 16;
810     break;
811     case SF_FORMAT_PCM_24:
812     case SF_FORMAT_PCM_32:
813     case SF_FORMAT_FLOAT:
814     case SF_FORMAT_DOUBLE:
815 persson 1265 bitdepth = 24;
816 schoenebeck 1225 break;
817     default:
818     sf_close(hFile); // close sound file
819     throw std::string("format not supported"); // unsupported subformat (yet?)
820     }
821 persson 1265
822     const int bufsize = 10000;
823 schoenebeck 1225 switch (bitdepth) {
824 persson 1265 case 16: {
825     short* buffer = new short[bufsize * info.channels];
826     sf_count_t cnt = info.frames;
827     while (cnt) {
828     // libsndfile does the conversion for us (if needed)
829     int n = sf_readf_short(hFile, buffer, bufsize);
830     // write from buffer directly (physically) into .gig file
831     iter->gig_sample->Write(buffer, n);
832     cnt -= n;
833     }
834     delete[] buffer;
835 schoenebeck 1225 break;
836 persson 1265 }
837     case 24: {
838     int* srcbuf = new int[bufsize * info.channels];
839     uint8_t* dstbuf = new uint8_t[bufsize * 3 * info.channels];
840     sf_count_t cnt = info.frames;
841     while (cnt) {
842     // libsndfile returns 32 bits, convert to 24
843     int n = sf_readf_int(hFile, srcbuf, bufsize);
844     int j = 0;
845     for (int i = 0 ; i < n * info.channels ; i++) {
846     dstbuf[j++] = srcbuf[i] >> 8;
847     dstbuf[j++] = srcbuf[i] >> 16;
848     dstbuf[j++] = srcbuf[i] >> 24;
849     }
850     // write from buffer directly (physically) into .gig file
851     iter->gig_sample->Write(dstbuf, n);
852     cnt -= n;
853     }
854     delete[] srcbuf;
855     delete[] dstbuf;
856 schoenebeck 1225 break;
857 persson 1265 }
858 schoenebeck 1225 }
859     // cleanup
860     sf_close(hFile);
861     // on success we remove the sample from the import queue,
862     // otherwise keep it, maybe it works the next time ?
863     std::list<SampleImportItem>::iterator cur = iter;
864     ++iter;
865     m_SampleImportQueue.erase(cur);
866     } catch (std::string what) {
867     // remember the files that made trouble (and their cause)
868     if (error_files.size()) error_files += "\n";
869     error_files += (*iter).sample_path += " (" + what + ")";
870     ++iter;
871     }
872     }
873     // show error message box when some sample(s) could not be imported
874     if (error_files.size()) {
875 schoenebeck 1382 Glib::ustring txt = _("Could not import the following sample(s):\n") + error_files;
876 schoenebeck 1225 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
877     msg.run();
878     }
879     }
880    
881     void MainWindow::on_action_file_properties()
882     {
883     propDialog.show();
884     propDialog.deiconify();
885     }
886    
887     void MainWindow::on_action_help_about()
888     {
889     #ifdef ABOUT_DIALOG
890     Gtk::AboutDialog dialog;
891     dialog.set_version(VERSION);
892 schoenebeck 1436 dialog.set_copyright("Copyright (C) 2006,2007 Andreas Persson");
893     dialog.set_comments(
894     "Released under the GNU General Public License.\n"
895     "\n"
896     "Please notice that this is still a very young instrument editor. "
897     "So better backup your Gigasampler files before editing them with "
898     "this application.\n"
899     "\n"
900     "Please report bugs to: http://bugs.linuxsampler.org"
901     );
902     dialog.set_website("http://www.linuxsampler.org");
903     dialog.set_website_label("http://www.linuxsampler.org");
904 schoenebeck 1225 dialog.run();
905     #endif
906     }
907    
908     PropDialog::PropDialog()
909 persson 1582 : table(2,1),
910     eName("Name"),
911     eCreationDate("Creation date"),
912     eComments("Comments"),
913     eProduct("Product"),
914     eCopyright("Copyright"),
915     eArtists("Artists"),
916     eGenre("Genre"),
917     eKeywords("Keywords"),
918     eEngineer("Engineer"),
919     eTechnician("Technician"),
920     eSoftware("Software"),
921     eMedium("Medium"),
922     eSource("Source"),
923     eSourceForm("Source form"),
924     eCommissioned("Commissioned"),
925     eSubject("Subject"),
926     quitButton(Gtk::Stock::CLOSE),
927     update_model(0)
928 schoenebeck 1225 {
929 persson 1582 set_title("File Properties");
930     eName.set_width_chars(50);
931    
932     connect(eName, &DLS::Info::Name);
933     connect(eCreationDate, &DLS::Info::CreationDate);
934     connect(eComments, &DLS::Info::Comments);
935     connect(eProduct, &DLS::Info::Product);
936     connect(eCopyright, &DLS::Info::Copyright);
937     connect(eArtists, &DLS::Info::Artists);
938     connect(eGenre, &DLS::Info::Genre);
939     connect(eKeywords, &DLS::Info::Keywords);
940     connect(eEngineer, &DLS::Info::Engineer);
941     connect(eTechnician, &DLS::Info::Technician);
942     connect(eSoftware, &DLS::Info::Software);
943     connect(eMedium, &DLS::Info::Medium);
944     connect(eSource, &DLS::Info::Source);
945     connect(eSourceForm, &DLS::Info::SourceForm);
946     connect(eCommissioned, &DLS::Info::Commissioned);
947     connect(eSubject, &DLS::Info::Subject);
948    
949     table.add(eName);
950     table.add(eCreationDate);
951     table.add(eComments);
952     table.add(eProduct);
953     table.add(eCopyright);
954     table.add(eArtists);
955     table.add(eGenre);
956     table.add(eKeywords);
957     table.add(eEngineer);
958     table.add(eTechnician);
959     table.add(eSoftware);
960     table.add(eMedium);
961     table.add(eSource);
962     table.add(eSourceForm);
963     table.add(eCommissioned);
964     table.add(eSubject);
965    
966 schoenebeck 1225 table.set_col_spacings(5);
967 persson 1582 add(vbox);
968     table.set_border_width(5);
969     vbox.add(table);
970     vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
971     buttonBox.set_layout(Gtk::BUTTONBOX_END);
972     buttonBox.set_border_width(5);
973     buttonBox.show();
974     buttonBox.pack_start(quitButton);
975     quitButton.set_flags(Gtk::CAN_DEFAULT);
976     quitButton.grab_focus();
977     quitButton.signal_clicked().connect(
978     sigc::mem_fun(*this, &PropDialog::hide));
979 schoenebeck 1225
980 persson 1582 quitButton.show();
981     vbox.show();
982 schoenebeck 1225 show_all_children();
983     }
984    
985     void PropDialog::set_info(DLS::Info* info)
986     {
987 persson 1582 this->info = info;
988     update_model++;
989     eName.set_value(info->Name);
990     eCreationDate.set_value(info->CreationDate);
991     eComments.set_value(info->Comments);
992     eProduct.set_value(info->Product);
993     eCopyright.set_value(info->Copyright);
994     eArtists.set_value(info->Artists);
995     eGenre.set_value(info->Genre);
996     eKeywords.set_value(info->Keywords);
997     eEngineer.set_value(info->Engineer);
998     eTechnician.set_value(info->Technician);
999     eSoftware.set_value(info->Software);
1000     eMedium.set_value(info->Medium);
1001     eSource.set_value(info->Source);
1002     eSourceForm.set_value(info->SourceForm);
1003     eCommissioned.set_value(info->Commissioned);
1004     eSubject.set_value(info->Subject);
1005     update_model--;
1006 schoenebeck 1225 }
1007    
1008 persson 1582 sigc::signal<void>& PropDialog::signal_info_changed()
1009     {
1010     return info_changed;
1011     }
1012    
1013 persson 1460 void InstrumentProps::set_IsDrum(bool value)
1014     {
1015     instrument->IsDrum = value;
1016     }
1017    
1018     void InstrumentProps::set_MIDIBank(uint16_t value)
1019     {
1020     instrument->MIDIBank = value;
1021     }
1022    
1023     void InstrumentProps::set_MIDIProgram(uint32_t value)
1024     {
1025     instrument->MIDIProgram = value;
1026     }
1027    
1028     void InstrumentProps::set_DimensionKeyRange_low(uint8_t value)
1029     {
1030     instrument->DimensionKeyRange.low = value;
1031     if (value > instrument->DimensionKeyRange.high) {
1032     eDimensionKeyRangeHigh.set_value(value);
1033     }
1034     }
1035    
1036     void InstrumentProps::set_DimensionKeyRange_high(uint8_t value)
1037     {
1038     instrument->DimensionKeyRange.high = value;
1039     if (value < instrument->DimensionKeyRange.low) {
1040     eDimensionKeyRangeLow.set_value(value);
1041     }
1042     }
1043    
1044 schoenebeck 1225 InstrumentProps::InstrumentProps()
1045     : table(2,1),
1046     quitButton(Gtk::Stock::CLOSE),
1047     eName("Name"),
1048 persson 1262 eIsDrum("Is drum"),
1049     eMIDIBank("MIDI bank", 0, 16383),
1050     eMIDIProgram("MIDI program"),
1051 schoenebeck 1225 eAttenuation("Attenuation", 0, 96, 0, 1),
1052     eGainPlus6("Gain +6dB", eAttenuation, -6),
1053 persson 1262 eEffectSend("Effect send", 0, 65535),
1054     eFineTune("Fine tune", -8400, 8400),
1055     ePitchbendRange("Pitchbend range", 0, 12),
1056     ePianoReleaseMode("Piano release mode"),
1057 schoenebeck 1656 eDimensionKeyRangeLow("Keyswitching range low"),
1058     eDimensionKeyRangeHigh("Keyswitching range high"),
1059 persson 1460 update_model(0)
1060 schoenebeck 1225 {
1061 persson 1582 set_title("Instrument Properties");
1062 schoenebeck 1225
1063 schoenebeck 1656 eDimensionKeyRangeLow.set_tip(
1064     _("start of the keyboard area which should switch the "
1065     "\"keyswitching\" dimension")
1066     );
1067     eDimensionKeyRangeHigh.set_tip(
1068     _("end of the keyboard area which should switch the "
1069     "\"keyswitching\" dimension")
1070     );
1071    
1072 persson 1460 connect(eIsDrum, &InstrumentProps::set_IsDrum);
1073     connect(eMIDIBank, &InstrumentProps::set_MIDIBank);
1074     connect(eMIDIProgram, &InstrumentProps::set_MIDIProgram);
1075     connect(eAttenuation, &gig::Instrument::Attenuation);
1076     connect(eGainPlus6, &gig::Instrument::Attenuation);
1077     connect(eEffectSend, &gig::Instrument::EffectSend);
1078     connect(eFineTune, &gig::Instrument::FineTune);
1079     connect(ePitchbendRange, &gig::Instrument::PitchbendRange);
1080     connect(ePianoReleaseMode, &gig::Instrument::PianoReleaseMode);
1081     connect(eDimensionKeyRangeLow,
1082     &InstrumentProps::set_DimensionKeyRange_low);
1083     connect(eDimensionKeyRangeHigh,
1084     &InstrumentProps::set_DimensionKeyRange_high);
1085    
1086 schoenebeck 1225 table.set_col_spacings(5);
1087    
1088 persson 1582 table.add(eName);
1089     table.add(eIsDrum);
1090     table.add(eMIDIBank);
1091     table.add(eMIDIProgram);
1092     table.add(eAttenuation);
1093     table.add(eGainPlus6);
1094     table.add(eEffectSend);
1095     table.add(eFineTune);
1096     table.add(ePitchbendRange);
1097     table.add(ePianoReleaseMode);
1098     table.add(eDimensionKeyRangeLow);
1099     table.add(eDimensionKeyRangeHigh);
1100 schoenebeck 1225
1101     add(vbox);
1102     table.set_border_width(5);
1103     vbox.pack_start(table);
1104     table.show();
1105     vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
1106     buttonBox.set_layout(Gtk::BUTTONBOX_END);
1107     buttonBox.set_border_width(5);
1108     buttonBox.show();
1109     buttonBox.pack_start(quitButton);
1110     quitButton.set_flags(Gtk::CAN_DEFAULT);
1111     quitButton.grab_focus();
1112    
1113     quitButton.signal_clicked().connect(
1114     sigc::mem_fun(*this, &InstrumentProps::hide));
1115    
1116     quitButton.show();
1117     vbox.show();
1118     show_all_children();
1119     }
1120    
1121     void InstrumentProps::set_instrument(gig::Instrument* instrument)
1122     {
1123 persson 1460 this->instrument = instrument;
1124    
1125     update_model++;
1126 persson 1582 eName.set_value(instrument->pInfo->Name);
1127 persson 1460 eIsDrum.set_value(instrument->IsDrum);
1128     eMIDIBank.set_value(instrument->MIDIBank);
1129     eMIDIProgram.set_value(instrument->MIDIProgram);
1130     eAttenuation.set_value(instrument->Attenuation);
1131     eGainPlus6.set_value(instrument->Attenuation);
1132     eEffectSend.set_value(instrument->EffectSend);
1133     eFineTune.set_value(instrument->FineTune);
1134     ePitchbendRange.set_value(instrument->PitchbendRange);
1135     ePianoReleaseMode.set_value(instrument->PianoReleaseMode);
1136     eDimensionKeyRangeLow.set_value(instrument->DimensionKeyRange.low);
1137     eDimensionKeyRangeHigh.set_value(instrument->DimensionKeyRange.high);
1138     update_model--;
1139 schoenebeck 1225 }
1140    
1141 schoenebeck 1339 sigc::signal<void>& InstrumentProps::signal_instrument_changed()
1142 schoenebeck 1225 {
1143 persson 1261 return instrument_changed;
1144     }
1145 schoenebeck 1225
1146 persson 1261 void MainWindow::file_changed()
1147     {
1148     if (file && !file_is_changed) {
1149     set_title("*" + get_title());
1150     file_is_changed = true;
1151 schoenebeck 1225 }
1152 persson 1261 }
1153 schoenebeck 1225
1154 schoenebeck 1382 void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument)
1155 persson 1261 {
1156     file = 0;
1157 schoenebeck 1411 set_file_is_shared(isSharedInstrument);
1158 persson 1261
1159     this->filename = filename ? filename : _("Unsaved Gig File");
1160     set_title(Glib::filename_display_basename(this->filename));
1161     file_has_name = filename;
1162     file_is_changed = false;
1163    
1164 schoenebeck 1225 propDialog.set_info(gig->pInfo);
1165    
1166     Gtk::MenuItem* instrument_menu =
1167     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument"));
1168    
1169     int instrument_index = 0;
1170     Gtk::RadioMenuItem::Group instrument_group;
1171     for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ;
1172     instrument = gig->GetNextInstrument()) {
1173     Gtk::TreeModel::iterator iter = m_refTreeModel->append();
1174     Gtk::TreeModel::Row row = *iter;
1175     row[m_Columns.m_col_name] = instrument->pInfo->Name.c_str();
1176     row[m_Columns.m_col_instr] = instrument;
1177     // create a menu item for this instrument
1178     Gtk::RadioMenuItem* item =
1179     new Gtk::RadioMenuItem(instrument_group, instrument->pInfo->Name.c_str());
1180     instrument_menu->get_submenu()->append(*item);
1181     item->signal_activate().connect(
1182     sigc::bind(
1183     sigc::mem_fun(*this, &MainWindow::on_instrument_selection_change),
1184     instrument_index
1185     )
1186     );
1187     instrument_index++;
1188     }
1189     instrument_menu->show();
1190     instrument_menu->get_submenu()->show_all_children();
1191    
1192     for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) {
1193     if (group->Name != "") {
1194     Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
1195     Gtk::TreeModel::Row rowGroup = *iterGroup;
1196     rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str();
1197     rowGroup[m_SamplesModel.m_col_group] = group;
1198     rowGroup[m_SamplesModel.m_col_sample] = NULL;
1199     for (gig::Sample* sample = group->GetFirstSample();
1200     sample; sample = group->GetNextSample()) {
1201     Gtk::TreeModel::iterator iterSample =
1202     m_refSamplesTreeModel->append(rowGroup.children());
1203     Gtk::TreeModel::Row rowSample = *iterSample;
1204     rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str();
1205     rowSample[m_SamplesModel.m_col_sample] = sample;
1206     rowSample[m_SamplesModel.m_col_group] = NULL;
1207     }
1208     }
1209     }
1210    
1211 persson 1261 file = gig;
1212    
1213 schoenebeck 1225 // select the first instrument
1214     Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
1215     tree_sel_ref->select(Gtk::TreePath("0"));
1216     }
1217    
1218     void MainWindow::show_instr_props()
1219     {
1220 persson 1533 gig::Instrument* instrument = get_instrument();
1221     if (instrument)
1222 schoenebeck 1225 {
1223 persson 1533 instrumentProps.set_instrument(instrument);
1224     instrumentProps.show();
1225     instrumentProps.deiconify();
1226 schoenebeck 1225 }
1227     }
1228    
1229 schoenebeck 1415 void MainWindow::on_action_view_status_bar() {
1230     Gtk::CheckMenuItem* item =
1231     dynamic_cast<Gtk::CheckMenuItem*>(uiManager->get_widget("/MenuBar/MenuView/Statusbar"));
1232     if (!item) {
1233     std::cerr << "/MenuBar/MenuView/Statusbar == NULL\n";
1234     return;
1235     }
1236     if (item->get_active()) m_StatusBar.show();
1237     else m_StatusBar.hide();
1238     }
1239    
1240 schoenebeck 1225 void MainWindow::on_button_release(GdkEventButton* button)
1241     {
1242     if (button->type == GDK_2BUTTON_PRESS) {
1243     show_instr_props();
1244     } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
1245     popup_menu->popup(button->button, button->time);
1246     }
1247     }
1248    
1249     void MainWindow::on_instrument_selection_change(int index) {
1250     m_RegionChooser.set_instrument(file->GetInstrument(index));
1251     }
1252    
1253     void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) {
1254     if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
1255     Gtk::Menu* sample_popup =
1256     dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/SamplePopupMenu"));
1257     // update enabled/disabled state of sample popup items
1258     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1259     Gtk::TreeModel::iterator it = sel->get_selected();
1260     bool group_selected = false;
1261     bool sample_selected = false;
1262     if (it) {
1263     Gtk::TreeModel::Row row = *it;
1264     group_selected = row[m_SamplesModel.m_col_group];
1265     sample_selected = row[m_SamplesModel.m_col_sample];
1266     }
1267     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->
1268     set_sensitive(group_selected || sample_selected);
1269     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->
1270     set_sensitive(group_selected || sample_selected);
1271     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->
1272     set_sensitive(file);
1273     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->
1274     set_sensitive(group_selected || sample_selected);
1275     // show sample popup
1276     sample_popup->popup(button->button, button->time);
1277     }
1278     }
1279    
1280     void MainWindow::on_action_add_instrument() {
1281     static int __instrument_indexer = 0;
1282     if (!file) return;
1283     gig::Instrument* instrument = file->AddInstrument();
1284     __instrument_indexer++;
1285     instrument->pInfo->Name =
1286     "Unnamed Instrument " + ToString(__instrument_indexer);
1287     // update instrument tree view
1288     Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append();
1289     Gtk::TreeModel::Row rowInstr = *iterInstr;
1290     rowInstr[m_Columns.m_col_name] = instrument->pInfo->Name.c_str();
1291     rowInstr[m_Columns.m_col_instr] = instrument;
1292 persson 1261 file_changed();
1293 schoenebeck 1225 }
1294    
1295     void MainWindow::on_action_remove_instrument() {
1296     if (!file) return;
1297 schoenebeck 1382 if (file_is_shared) {
1298     Gtk::MessageDialog msg(
1299     *this,
1300     _("You cannot delete an instrument from this file, since it's "
1301     "currently used by the sampler."),
1302     false, Gtk::MESSAGE_INFO
1303     );
1304     msg.run();
1305     return;
1306     }
1307    
1308 schoenebeck 1225 Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
1309     Gtk::TreeModel::iterator it = sel->get_selected();
1310     if (it) {
1311     Gtk::TreeModel::Row row = *it;
1312     gig::Instrument* instr = row[m_Columns.m_col_instr];
1313     try {
1314     // remove instrument from the gig file
1315     if (instr) file->DeleteInstrument(instr);
1316     // remove respective row from instruments tree view
1317     m_refTreeModel->erase(it);
1318 persson 1261 file_changed();
1319 schoenebeck 1225 } catch (RIFF::Exception e) {
1320     Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1321     msg.run();
1322     }
1323     }
1324     }
1325    
1326     void MainWindow::on_action_sample_properties() {
1327     //TODO: show a dialog where the selected sample's properties can be edited
1328     Gtk::MessageDialog msg(
1329     *this, "Sorry, yet to be implemented!", false, Gtk::MESSAGE_INFO
1330     );
1331     msg.run();
1332     }
1333    
1334     void MainWindow::on_action_add_group() {
1335     static int __sample_indexer = 0;
1336     if (!file) return;
1337     gig::Group* group = file->AddGroup();
1338     group->Name = "Unnamed Group";
1339     if (__sample_indexer) group->Name += " " + ToString(__sample_indexer);
1340     __sample_indexer++;
1341     // update sample tree view
1342     Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
1343     Gtk::TreeModel::Row rowGroup = *iterGroup;
1344     rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str();
1345     rowGroup[m_SamplesModel.m_col_sample] = NULL;
1346     rowGroup[m_SamplesModel.m_col_group] = group;
1347 persson 1261 file_changed();
1348 schoenebeck 1225 }
1349    
1350     void MainWindow::on_action_add_sample() {
1351     if (!file) return;
1352     // get selected group
1353     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1354     Gtk::TreeModel::iterator it = sel->get_selected();
1355     if (!it) return;
1356     Gtk::TreeModel::Row row = *it;
1357     gig::Group* group = row[m_SamplesModel.m_col_group];
1358     if (!group) { // not a group, but a sample is selected (probably)
1359     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1360     if (!sample) return;
1361     it = row.parent(); // resolve parent (that is the sample's group)
1362     if (!it) return;
1363     row = *it;
1364     group = row[m_SamplesModel.m_col_group];
1365     if (!group) return;
1366     }
1367     // show 'browse for file' dialog
1368     Gtk::FileChooserDialog dialog(*this, _("Add Sample(s)"));
1369     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1370     dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
1371     dialog.set_select_multiple(true);
1372     Gtk::FileFilter soundfilter; // matches all file types supported by libsndfile
1373 persson 1262 const char* const supportedFileTypes[] = {
1374 schoenebeck 1225 "*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd",
1375     "*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF",
1376     "*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64",
1377     "*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK",
1378     "*.caf", "*.CAF", NULL
1379     };
1380     for (int i = 0; supportedFileTypes[i]; i++)
1381     soundfilter.add_pattern(supportedFileTypes[i]);
1382     soundfilter.set_name("Sound Files");
1383     Gtk::FileFilter allpassfilter; // matches every file
1384     allpassfilter.add_pattern("*.*");
1385     allpassfilter.set_name("All Files");
1386     dialog.add_filter(soundfilter);
1387     dialog.add_filter(allpassfilter);
1388     if (dialog.run() == Gtk::RESPONSE_OK) {
1389     Glib::ustring error_files;
1390     Glib::SListHandle<Glib::ustring> filenames = dialog.get_filenames();
1391     for (Glib::SListHandle<Glib::ustring>::iterator iter = filenames.begin();
1392     iter != filenames.end(); ++iter) {
1393     printf("Adding sample %s\n",(*iter).c_str());
1394     // use libsndfile to retrieve file informations
1395     SF_INFO info;
1396     info.format = 0;
1397     SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
1398     try {
1399     if (!hFile) throw std::string("could not open file");
1400     int bitdepth;
1401     switch (info.format & 0xff) {
1402     case SF_FORMAT_PCM_S8:
1403     case SF_FORMAT_PCM_16:
1404 persson 1265 case SF_FORMAT_PCM_U8:
1405 schoenebeck 1225 bitdepth = 16;
1406     break;
1407     case SF_FORMAT_PCM_24:
1408     case SF_FORMAT_PCM_32:
1409     case SF_FORMAT_FLOAT:
1410     case SF_FORMAT_DOUBLE:
1411 persson 1265 bitdepth = 24;
1412 schoenebeck 1225 break;
1413     default:
1414     sf_close(hFile); // close sound file
1415     throw std::string("format not supported"); // unsupported subformat (yet?)
1416     }
1417     // add a new sample to the .gig file
1418     gig::Sample* sample = file->AddSample();
1419     // file name without path
1420 persson 1262 Glib::ustring filename = Glib::filename_display_basename(*iter);
1421     // remove file extension if there is one
1422     for (int i = 0; supportedFileTypes[i]; i++) {
1423     if (Glib::str_has_suffix(filename, supportedFileTypes[i] + 1)) {
1424     filename.erase(filename.length() - strlen(supportedFileTypes[i] + 1));
1425     break;
1426     }
1427     }
1428     sample->pInfo->Name = filename;
1429 schoenebeck 1225 sample->Channels = info.channels;
1430     sample->BitDepth = bitdepth;
1431     sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
1432     sample->SamplesPerSecond = info.samplerate;
1433 persson 1265 sample->AverageBytesPerSecond = sample->FrameSize * sample->SamplesPerSecond;
1434     sample->BlockAlign = sample->FrameSize;
1435     sample->SamplesTotal = info.frames;
1436    
1437     SF_INSTRUMENT instrument;
1438     if (sf_command(hFile, SFC_GET_INSTRUMENT,
1439     &instrument, sizeof(instrument)) != SF_FALSE)
1440     {
1441     sample->MIDIUnityNote = instrument.basenote;
1442    
1443 persson 1303 #if HAVE_SF_INSTRUMENT_LOOPS
1444 persson 1265 if (instrument.loop_count && instrument.loops[0].mode != SF_LOOP_NONE) {
1445     sample->Loops = 1;
1446    
1447     switch (instrument.loops[0].mode) {
1448     case SF_LOOP_FORWARD:
1449     sample->LoopType = gig::loop_type_normal;
1450     break;
1451     case SF_LOOP_BACKWARD:
1452     sample->LoopType = gig::loop_type_backward;
1453     break;
1454     case SF_LOOP_ALTERNATING:
1455     sample->LoopType = gig::loop_type_bidirectional;
1456     break;
1457     }
1458     sample->LoopStart = instrument.loops[0].start;
1459     sample->LoopEnd = instrument.loops[0].end;
1460     sample->LoopPlayCount = instrument.loops[0].count;
1461     sample->LoopSize = sample->LoopEnd - sample->LoopStart + 1;
1462     }
1463 persson 1303 #endif
1464 persson 1265 }
1465    
1466 schoenebeck 1225 // schedule resizing the sample (which will be done
1467     // physically when File::Save() is called)
1468     sample->Resize(info.frames);
1469     // make sure sample is part of the selected group
1470     group->AddSample(sample);
1471     // schedule that physical resize and sample import
1472     // (data copying), performed when "Save" is requested
1473     SampleImportItem sched_item;
1474     sched_item.gig_sample = sample;
1475     sched_item.sample_path = *iter;
1476     m_SampleImportQueue.push_back(sched_item);
1477     // add sample to the tree view
1478     Gtk::TreeModel::iterator iterSample =
1479     m_refSamplesTreeModel->append(row.children());
1480     Gtk::TreeModel::Row rowSample = *iterSample;
1481 persson 1262 rowSample[m_SamplesModel.m_col_name] = filename;
1482 schoenebeck 1225 rowSample[m_SamplesModel.m_col_sample] = sample;
1483     rowSample[m_SamplesModel.m_col_group] = NULL;
1484     // close sound file
1485     sf_close(hFile);
1486 persson 1261 file_changed();
1487 schoenebeck 1225 } catch (std::string what) { // remember the files that made trouble (and their cause)
1488     if (error_files.size()) error_files += "\n";
1489     error_files += *iter += " (" + what + ")";
1490     }
1491     }
1492     // show error message box when some file(s) could not be opened / added
1493     if (error_files.size()) {
1494 schoenebeck 1382 Glib::ustring txt = _("Could not add the following sample(s):\n") + error_files;
1495 schoenebeck 1225 Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1496     msg.run();
1497     }
1498     }
1499     }
1500    
1501 schoenebeck 1673 void MainWindow::on_action_replace_all_samples_in_all_groups()
1502     {
1503     if (!file) return;
1504     Gtk::FileChooserDialog dialog(*this, _("Select Folder"),
1505     Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
1506     Gtk::Label description(
1507     _("This is a very specific function. It tries to replace all samples "
1508     "in the current gig file by samples located in the directory chosen "
1509     "by you above.\n\n"
1510     "It works like this: For each sample in the gig file it tries to "
1511     "find a sample file in the selected directory with the same name as "
1512     "the sample in the gig file. Optionally you can add a filename "
1513     "postfix below, which will be added to the filename expected to be "
1514     "found. That is, assume you have a gig file with a sample called "
1515     "'Snare', if you enter '.wav' below (like it's done by default), it "
1516     "assumes to find a sample file called 'Snare.wav' and will replace "
1517     "the sample in the gig file accordingly. If you don't need such a "
1518     "postfix, blank the field below. Any gig sample where no "
1519     "appropriate sample file could be found, will be reported and left "
1520     "untouched.\n\n")
1521     );
1522     description.set_line_wrap(true);
1523     Gtk::HBox entryArea;
1524     Gtk::Label entryLabel( _("Add Filename Extension: "), Gtk::ALIGN_RIGHT);
1525     Gtk::Entry postfixEntryBox;
1526     postfixEntryBox.set_text(".wav");
1527     entryArea.pack_start(entryLabel);
1528     entryArea.pack_start(postfixEntryBox);
1529     dialog.get_vbox()->pack_start(description, Gtk::PACK_SHRINK);
1530     dialog.get_vbox()->pack_start(entryArea, Gtk::PACK_SHRINK);
1531     description.show();
1532     entryLabel.show();
1533     postfixEntryBox.show();
1534     entryArea.show();
1535     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1536     dialog.add_button(_("Select"), Gtk::RESPONSE_OK);
1537     dialog.set_select_multiple(false);
1538     // fix label width (because Gtk by design doesn't
1539     // know anything about the parent's size)
1540     #if 0 //FIXME: doesn't work
1541     int dialogW, dialogH, labelW, labelH;
1542     dialog.get_size_request(dialogW, dialogH);
1543     description.get_size_request(labelW, labelH);
1544     std::cout << "dialog(" << dialogW << "," << dialogH << ")\nlabel(" << labelW << "," << labelH << ")\n" << std::flush;
1545     description.set_size_request(dialogW, labelH);
1546     #endif
1547     if (dialog.run() == Gtk::RESPONSE_OK)
1548     {
1549     Glib::ustring error_files;
1550     Glib::ustring folder = dialog.get_filename();
1551     for (gig::Sample* sample = file->GetFirstSample();
1552     sample; sample = file->GetNextSample())
1553     {
1554     std::string filename =
1555     folder + G_DIR_SEPARATOR_S + sample->pInfo->Name +
1556     postfixEntryBox.get_text().raw();
1557     SF_INFO info;
1558     info.format = 0;
1559     SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &info);
1560     try
1561     {
1562     if (!hFile) throw std::string("could not open file");
1563     int bitdepth;
1564     switch (info.format & 0xff) {
1565     case SF_FORMAT_PCM_S8:
1566     case SF_FORMAT_PCM_16:
1567     case SF_FORMAT_PCM_U8:
1568     bitdepth = 16;
1569     break;
1570     case SF_FORMAT_PCM_24:
1571     case SF_FORMAT_PCM_32:
1572     case SF_FORMAT_FLOAT:
1573     case SF_FORMAT_DOUBLE:
1574     bitdepth = 24;
1575     break;
1576     default:
1577     sf_close(hFile);
1578     throw std::string("format not supported");
1579     }
1580     SampleImportItem sched_item;
1581     sched_item.gig_sample = sample;
1582     sched_item.sample_path = filename;
1583     m_SampleImportQueue.push_back(sched_item);
1584     sf_close(hFile);
1585     file_changed();
1586     }
1587     catch (std::string what)
1588     {
1589     if (error_files.size()) error_files += "\n";
1590     error_files += filename += " (" + what + ")";
1591     }
1592     }
1593     // show error message box when some file(s) could not be opened / added
1594     if (error_files.size()) {
1595     Glib::ustring txt =
1596     _("Could not replace the following sample(s):\n") + error_files;
1597     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1598     msg.run();
1599     }
1600     }
1601     }
1602    
1603 schoenebeck 1225 void MainWindow::on_action_remove_sample() {
1604     if (!file) return;
1605     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1606     Gtk::TreeModel::iterator it = sel->get_selected();
1607     if (it) {
1608     Gtk::TreeModel::Row row = *it;
1609     gig::Group* group = row[m_SamplesModel.m_col_group];
1610     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1611     Glib::ustring name = row[m_SamplesModel.m_col_name];
1612     try {
1613     // remove group or sample from the gig file
1614     if (group) {
1615     // temporarily remember the samples that bolong to
1616     // that group (we need that to clean the queue)
1617     std::list<gig::Sample*> members;
1618     for (gig::Sample* pSample = group->GetFirstSample();
1619     pSample; pSample = group->GetNextSample()) {
1620     members.push_back(pSample);
1621     }
1622 schoenebeck 1322 // notify everybody that we're going to remove these samples
1623     samples_to_be_removed_signal.emit(members);
1624 schoenebeck 1225 // delete the group in the .gig file including the
1625     // samples that belong to the group
1626     file->DeleteGroup(group);
1627 schoenebeck 1322 // notify that we're done with removal
1628     samples_removed_signal.emit();
1629 schoenebeck 1225 // if sample(s) were just previously added, remove
1630     // them from the import queue
1631     for (std::list<gig::Sample*>::iterator member = members.begin();
1632     member != members.end(); ++member) {
1633     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1634     iter != m_SampleImportQueue.end(); ++iter) {
1635     if ((*iter).gig_sample == *member) {
1636     printf("Removing previously added sample '%s' from group '%s'\n",
1637     (*iter).sample_path.c_str(), name.c_str());
1638     m_SampleImportQueue.erase(iter);
1639     break;
1640     }
1641     }
1642     }
1643 persson 1261 file_changed();
1644 schoenebeck 1225 } else if (sample) {
1645 schoenebeck 1322 // notify everybody that we're going to remove this sample
1646     std::list<gig::Sample*> lsamples;
1647     lsamples.push_back(sample);
1648     samples_to_be_removed_signal.emit(lsamples);
1649 schoenebeck 1225 // remove sample from the .gig file
1650     file->DeleteSample(sample);
1651 schoenebeck 1322 // notify that we're done with removal
1652     samples_removed_signal.emit();
1653 schoenebeck 1225 // if sample was just previously added, remove it from
1654     // the import queue
1655     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1656     iter != m_SampleImportQueue.end(); ++iter) {
1657     if ((*iter).gig_sample == sample) {
1658     printf("Removing previously added sample '%s'\n",
1659     (*iter).sample_path.c_str());
1660     m_SampleImportQueue.erase(iter);
1661     break;
1662     }
1663     }
1664 persson 1303 dimreg_changed();
1665 persson 1261 file_changed();
1666 schoenebeck 1225 }
1667     // remove respective row(s) from samples tree view
1668     m_refSamplesTreeModel->erase(it);
1669     } catch (RIFF::Exception e) {
1670 schoenebeck 1322 // pretend we're done with removal (i.e. to avoid dead locks)
1671     samples_removed_signal.emit();
1672     // show error message
1673 schoenebeck 1225 Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1674     msg.run();
1675     }
1676     }
1677     }
1678    
1679 persson 1303 // For some reason drag_data_get gets called two times for each
1680     // drag'n'drop (at least when target is an Entry). This work-around
1681     // makes sure the code in drag_data_get and drop_drag_data_received is
1682     // only executed once, as drag_begin only gets called once.
1683     void MainWindow::on_sample_treeview_drag_begin(const Glib::RefPtr<Gdk::DragContext>& context)
1684     {
1685     first_call_to_drag_data_get = true;
1686     }
1687    
1688 schoenebeck 1225 void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
1689     Gtk::SelectionData& selection_data, guint, guint)
1690     {
1691 persson 1303 if (!first_call_to_drag_data_get) return;
1692     first_call_to_drag_data_get = false;
1693    
1694 schoenebeck 1225 // get selected sample
1695     gig::Sample* sample = NULL;
1696     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1697     Gtk::TreeModel::iterator it = sel->get_selected();
1698     if (it) {
1699     Gtk::TreeModel::Row row = *it;
1700     sample = row[m_SamplesModel.m_col_sample];
1701     }
1702     // pass the gig::Sample as pointer
1703     selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
1704     sizeof(sample)/*length of data in bytes*/);
1705     }
1706    
1707     void MainWindow::on_sample_label_drop_drag_data_received(
1708     const Glib::RefPtr<Gdk::DragContext>& context, int, int,
1709     const Gtk::SelectionData& selection_data, guint, guint time)
1710     {
1711     gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
1712    
1713 persson 1265 if (sample && selection_data.get_length() == sizeof(gig::Sample*)) {
1714 persson 1303 std::cout << "Drop received sample \"" <<
1715     sample->pInfo->Name << "\"" << std::endl;
1716     // drop success
1717     context->drop_reply(true, time);
1718    
1719 schoenebeck 1322 //TODO: we should better move most of the following code to DimRegionEdit::set_sample()
1720    
1721     // notify everybody that we're going to alter the region
1722     gig::Region* region = m_RegionChooser.get_region();
1723     region_to_be_changed_signal.emit(region);
1724    
1725 persson 1303 // find the samplechannel dimension
1726     gig::dimension_def_t* stereo_dimension = 0;
1727     for (int i = 0 ; i < region->Dimensions ; i++) {
1728     if (region->pDimensionDefinitions[i].dimension ==
1729     gig::dimension_samplechannel) {
1730     stereo_dimension = &region->pDimensionDefinitions[i];
1731     break;
1732     }
1733 schoenebeck 1225 }
1734 persson 1303 bool channels_changed = false;
1735     if (sample->Channels == 1 && stereo_dimension) {
1736     // remove the samplechannel dimension
1737     region->DeleteDimension(stereo_dimension);
1738     channels_changed = true;
1739     region_changed();
1740     }
1741     dimreg_edit.set_sample(sample);
1742    
1743     if (sample->Channels == 2 && !stereo_dimension) {
1744     // add samplechannel dimension
1745     gig::dimension_def_t dim;
1746     dim.dimension = gig::dimension_samplechannel;
1747     dim.bits = 1;
1748     dim.zones = 2;
1749     region->AddDimension(&dim);
1750     channels_changed = true;
1751     region_changed();
1752     }
1753     if (channels_changed) {
1754     // unmap all samples with wrong number of channels
1755     // TODO: maybe there should be a warning dialog for this
1756     for (int i = 0 ; i < region->DimensionRegions ; i++) {
1757     gig::DimensionRegion* d = region->pDimensionRegions[i];
1758     if (d->pSample && d->pSample->Channels != sample->Channels) {
1759 schoenebeck 1322 gig::Sample* oldref = d->pSample;
1760     d->pSample = NULL;
1761     sample_ref_changed_signal.emit(oldref, NULL);
1762 persson 1303 }
1763     }
1764     }
1765    
1766 schoenebeck 1322 // notify we're done with altering
1767     region_changed_signal.emit(region);
1768    
1769 persson 1460 file_changed();
1770    
1771 persson 1303 return;
1772 schoenebeck 1225 }
1773     // drop failed
1774     context->drop_reply(false, time);
1775     }
1776    
1777     void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
1778     const Gtk::TreeModel::iterator& iter) {
1779     if (!iter) return;
1780     Gtk::TreeModel::Row row = *iter;
1781     Glib::ustring name = row[m_SamplesModel.m_col_name];
1782     gig::Group* group = row[m_SamplesModel.m_col_group];
1783     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1784     if (group) {
1785 persson 1261 if (group->Name != name) {
1786     group->Name = name;
1787     printf("group name changed\n");
1788     file_changed();
1789     }
1790 schoenebeck 1225 } else if (sample) {
1791 persson 1261 if (sample->pInfo->Name != name.raw()) {
1792     sample->pInfo->Name = name.raw();
1793     printf("sample name changed\n");
1794     file_changed();
1795     }
1796 schoenebeck 1225 }
1797     }
1798    
1799     void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
1800     const Gtk::TreeModel::iterator& iter) {
1801     if (!iter) return;
1802     Gtk::TreeModel::Row row = *iter;
1803     Glib::ustring name = row[m_Columns.m_col_name];
1804     gig::Instrument* instrument = row[m_Columns.m_col_instr];
1805 persson 1261 if (instrument && instrument->pInfo->Name != name.raw()) {
1806     instrument->pInfo->Name = name.raw();
1807     file_changed();
1808     }
1809 schoenebeck 1225 }
1810 schoenebeck 1322
1811 schoenebeck 1411 void MainWindow::set_file_is_shared(bool b) {
1812     this->file_is_shared = b;
1813    
1814     if (file_is_shared) {
1815     m_AttachedStateLabel.set_label(_("live-mode"));
1816     m_AttachedStateImage.set(
1817     Gdk::Pixbuf::create_from_xpm_data(status_attached_xpm)
1818     );
1819     } else {
1820     m_AttachedStateLabel.set_label(_("stand-alone"));
1821     m_AttachedStateImage.set(
1822     Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm)
1823     );
1824     }
1825     }
1826    
1827 schoenebeck 1339 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_to_be_changed() {
1828 schoenebeck 1322 return file_structure_to_be_changed_signal;
1829     }
1830    
1831 schoenebeck 1339 sigc::signal<void, gig::File*>& MainWindow::signal_file_structure_changed() {
1832 schoenebeck 1322 return file_structure_changed_signal;
1833     }
1834    
1835 schoenebeck 1339 sigc::signal<void, std::list<gig::Sample*> >& MainWindow::signal_samples_to_be_removed() {
1836 schoenebeck 1322 return samples_to_be_removed_signal;
1837     }
1838    
1839 schoenebeck 1339 sigc::signal<void>& MainWindow::signal_samples_removed() {
1840 schoenebeck 1322 return samples_removed_signal;
1841     }
1842    
1843 schoenebeck 1339 sigc::signal<void, gig::Region*>& MainWindow::signal_region_to_be_changed() {
1844 schoenebeck 1322 return region_to_be_changed_signal;
1845     }
1846    
1847 schoenebeck 1339 sigc::signal<void, gig::Region*>& MainWindow::signal_region_changed() {
1848 schoenebeck 1322 return region_changed_signal;
1849     }
1850    
1851 schoenebeck 1339 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& MainWindow::signal_sample_ref_changed() {
1852 schoenebeck 1322 return sample_ref_changed_signal;
1853     }
1854    
1855 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_to_be_changed() {
1856 schoenebeck 1322 return dimreg_to_be_changed_signal;
1857     }
1858    
1859 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& MainWindow::signal_dimreg_changed() {
1860 schoenebeck 1322 return dimreg_changed_signal;
1861     }
1862 schoenebeck 1654
1863     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_on() {
1864     return note_on_signal;
1865     }
1866    
1867     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_note_off() {
1868     return note_off_signal;
1869     }
1870 schoenebeck 1660
1871     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_hit() {
1872     return m_RegionChooser.signal_keyboard_key_hit();
1873     }
1874    
1875     sigc::signal<void, int/*key*/, int/*velocity*/>& MainWindow::signal_keyboard_key_released() {
1876     return m_RegionChooser.signal_keyboard_key_released();
1877     }

  ViewVC Help
Powered by ViewVC