/[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 1225 - (hide annotations) (download)
Sun Jun 10 10:56:11 2007 UTC (16 years, 9 months ago) by schoenebeck
File size: 42797 byte(s)
moved gigedit sources from src/ -> src/gigedit/

1 schoenebeck 1225 /*
2     * Copyright (C) 2006, 2007 Andreas Persson
3     *
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 <libintl.h>
21     #include <iostream>
22    
23     #include <gtkmm/filechooserdialog.h>
24     #include <gtkmm/messagedialog.h>
25     #include <gtkmm/stock.h>
26     #include <gtkmm/targetentry.h>
27     #include <gtkmm/main.h>
28    
29     #if GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6
30     #define ABOUT_DIALOG
31     #include <gtkmm/aboutdialog.h>
32     #endif
33    
34     #include <stdio.h>
35     #include <sndfile.h>
36    
37     #include "mainwindow.h"
38    
39     #define _(String) gettext(String)
40    
41     template<class T> inline std::string ToString(T o) {
42     std::stringstream ss;
43     ss << o;
44     return ss.str();
45     }
46    
47     MainWindow::MainWindow()
48     {
49     // set_border_width(5);
50     // set_default_size(400, 200);
51    
52    
53     add(m_VBox);
54    
55     // Handle selection
56     Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
57     tree_sel_ref->signal_changed().connect(
58     sigc::mem_fun(*this, &MainWindow::on_sel_change));
59    
60     // m_TreeView.set_reorderable();
61    
62     m_TreeView.signal_button_press_event().connect_notify(
63     sigc::mem_fun(*this, &MainWindow::on_button_release));
64    
65     // Add the TreeView tab, inside a ScrolledWindow, with the button underneath:
66     m_ScrolledWindow.add(m_TreeView);
67     // m_ScrolledWindow.set_size_request(200, 600);
68     m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
69    
70     m_ScrolledWindowSamples.add(m_TreeViewSamples);
71     m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
72    
73    
74     m_TreeViewNotebook.set_size_request(300);
75    
76     m_HPaned.add1(m_TreeViewNotebook);
77     m_HPaned.add2(dimreg_edit);
78    
79    
80     m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples");
81     m_TreeViewNotebook.append_page(m_ScrolledWindow, "Instruments");
82    
83    
84     actionGroup = Gtk::ActionGroup::create();
85    
86     actionGroup->add(Gtk::Action::create("MenuFile", _("_File")));
87     actionGroup->add(Gtk::Action::create("New", Gtk::Stock::NEW),
88     sigc::mem_fun(
89     *this, &MainWindow::on_action_file_new));
90     Glib::RefPtr<Gtk::Action> action =
91     Gtk::Action::create("Open", Gtk::Stock::OPEN);
92     action->property_label() = action->property_label() + "...";
93     actionGroup->add(action,
94     sigc::mem_fun(
95     *this, &MainWindow::on_action_file_open));
96     actionGroup->add(Gtk::Action::create("Save", Gtk::Stock::SAVE),
97     sigc::mem_fun(
98     *this, &MainWindow::on_action_file_save));
99     action = Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS);
100     action->property_label() = action->property_label() + "...";
101     actionGroup->add(action,
102     *(new Gtk::AccelKey("<shift><control>s")),
103     sigc::mem_fun(
104     *this, &MainWindow::on_action_file_save_as)
105     );
106     actionGroup->add(Gtk::Action::create("Properties",
107     Gtk::Stock::PROPERTIES),
108     sigc::mem_fun(
109     *this, &MainWindow::on_action_file_properties));
110     actionGroup->add(Gtk::Action::create("InstrProperties",
111     Gtk::Stock::PROPERTIES),
112     sigc::mem_fun(
113     *this, &MainWindow::show_instr_props));
114     actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT),
115     sigc::mem_fun(
116     *this, &MainWindow::hide));
117     actionGroup->add(Gtk::Action::create("MenuInstrument", _("_Instrument")));
118    
119     action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP);
120     actionGroup->add(Gtk::Action::create("MenuHelp",
121     action->property_label()));
122     #ifdef ABOUT_DIALOG
123     actionGroup->add(Gtk::Action::create("About", Gtk::Stock::ABOUT),
124     sigc::mem_fun(
125     *this, &MainWindow::on_action_help_about));
126     #endif
127     actionGroup->add(
128     Gtk::Action::create("AddInstrument", _("Add _Instrument")),
129     sigc::mem_fun(*this, &MainWindow::on_action_add_instrument)
130     );
131     actionGroup->add(
132     Gtk::Action::create("RemoveInstrument", Gtk::Stock::REMOVE),
133     sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument)
134     );
135    
136     // sample right-click popup actions
137     actionGroup->add(
138     Gtk::Action::create("SampleProperties", Gtk::Stock::PROPERTIES),
139     sigc::mem_fun(*this, &MainWindow::on_action_sample_properties)
140     );
141     actionGroup->add(
142     Gtk::Action::create("AddGroup", _("Add _Group")),
143     sigc::mem_fun(*this, &MainWindow::on_action_add_group)
144     );
145     actionGroup->add(
146     Gtk::Action::create("AddSample", _("Add _Sample(s)")),
147     sigc::mem_fun(*this, &MainWindow::on_action_add_sample)
148     );
149     actionGroup->add(
150     Gtk::Action::create("RemoveSample", Gtk::Stock::REMOVE),
151     sigc::mem_fun(*this, &MainWindow::on_action_remove_sample)
152     );
153    
154     uiManager = Gtk::UIManager::create();
155     uiManager->insert_action_group(actionGroup);
156     // add_accel_group(uiManager->get_accel_group());
157    
158     Glib::ustring ui_info =
159     "<ui>"
160     " <menubar name='MenuBar'>"
161     " <menu action='MenuFile'>"
162     " <menuitem action='New'/>"
163     " <menuitem action='Open'/>"
164     " <separator/>"
165     " <menuitem action='Save'/>"
166     " <menuitem action='SaveAs'/>"
167     " <separator/>"
168     " <menuitem action='Properties'/>"
169     " <separator/>"
170     " <menuitem action='Quit'/>"
171     " </menu>"
172     " <menu action='MenuInstrument'>"
173     " </menu>"
174     #ifdef ABOUT_DIALOG
175     " <menu action='MenuHelp'>"
176     " <menuitem action='About'/>"
177     " </menu>"
178     #endif
179     " </menubar>"
180     " <popup name='PopupMenu'>"
181     " <menuitem action='InstrProperties'/>"
182     " <menuitem action='AddInstrument'/>"
183     " <separator/>"
184     " <menuitem action='RemoveInstrument'/>"
185     " </popup>"
186     " <popup name='SamplePopupMenu'>"
187     " <menuitem action='SampleProperties'/>"
188     " <menuitem action='AddGroup'/>"
189     " <menuitem action='AddSample'/>"
190     " <separator/>"
191     " <menuitem action='RemoveSample'/>"
192     " </popup>"
193     "</ui>";
194     uiManager->add_ui_from_string(ui_info);
195    
196     popup_menu = dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/PopupMenu"));
197    
198     Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar");
199     m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK);
200     m_VBox.pack_start(m_HPaned);
201     m_VBox.pack_start(m_RegionChooser, Gtk::PACK_SHRINK);
202     m_VBox.pack_start(m_DimRegionChooser, Gtk::PACK_SHRINK);
203    
204     m_RegionChooser.signal_sel_changed().connect(
205     sigc::mem_fun(*this, &MainWindow::region_changed) );
206     m_DimRegionChooser.signal_sel_changed().connect(
207     sigc::mem_fun(*this, &MainWindow::dimreg_changed) );
208    
209    
210     // Create the Tree model:
211     m_refTreeModel = Gtk::ListStore::create(m_Columns);
212     m_TreeView.set_model(m_refTreeModel);
213     m_refTreeModel->signal_row_changed().connect(
214     sigc::mem_fun(*this, &MainWindow::instrument_name_changed)
215     );
216    
217     // Add the TreeView's view columns:
218     m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name);
219     m_TreeView.set_headers_visible(false);
220    
221     // create samples treeview (including its data model)
222     m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel);
223     m_TreeViewSamples.set_model(m_refSamplesTreeModel);
224     // m_TreeViewSamples.set_reorderable();
225     m_TreeViewSamples.append_column_editable("Samples", m_SamplesModel.m_col_name);
226     m_TreeViewSamples.set_headers_visible(false);
227     m_TreeViewSamples.signal_button_press_event().connect_notify(
228     sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release)
229     );
230     m_refSamplesTreeModel->signal_row_changed().connect(
231     sigc::mem_fun(*this, &MainWindow::sample_name_changed)
232     );
233    
234     // establish drag&drop between samples tree view and dimension region 'Sample' text entry
235     std::list<Gtk::TargetEntry> drag_target_gig_sample;
236     drag_target_gig_sample.push_back( Gtk::TargetEntry("gig::Sample") );
237     m_TreeViewSamples.drag_source_set(drag_target_gig_sample);
238     m_TreeViewSamples.signal_drag_data_get().connect(
239     sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get)
240     );
241     dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample);
242     dimreg_edit.wSample->signal_drag_data_received().connect(
243     sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received)
244     );
245    
246     file = 0;
247    
248     show_all_children();
249     }
250    
251     MainWindow::~MainWindow()
252     {
253     }
254    
255     void MainWindow::region_changed()
256     {
257     m_DimRegionChooser.set_region(m_RegionChooser.get_region());
258     }
259    
260     void MainWindow::dimreg_changed()
261     {
262     dimreg_edit.set_dim_region(m_DimRegionChooser.get_dimregion());
263     }
264    
265     void MainWindow::on_sel_change()
266     {
267     Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
268    
269     Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();
270     if (it) {
271     Gtk::TreeModel::Row row = *it;
272     std::cout << row[m_Columns.m_col_name] << std::endl;
273    
274     m_RegionChooser.set_instrument(row[m_Columns.m_col_instr]);
275     } else {
276     m_RegionChooser.set_instrument(0);
277     }
278     }
279    
280     void loader_progress_callback(gig::progress_t* progress)
281     {
282     Loader* loader = static_cast<Loader*>(progress->custom);
283     loader->progress_callback(progress->factor);
284     }
285    
286     void Loader::progress_callback(float fraction)
287     {
288     {
289     Glib::Mutex::Lock lock(progressMutex);
290     progress = fraction;
291     }
292     progress_dispatcher();
293     }
294    
295     void Loader::thread_function()
296     {
297     printf("thread_function self=%x\n", Glib::Thread::self());
298     printf("Start %s\n", filename);
299     RIFF::File* riff = new RIFF::File(filename);
300     gig = new gig::File(riff);
301     gig::progress_t progress;
302     progress.callback = loader_progress_callback;
303     progress.custom = this;
304    
305     gig->GetInstrument(0, &progress);
306     printf("End\n");
307     finished_dispatcher();
308     }
309    
310     Loader::Loader(const char* filename)
311     : thread(0), filename(filename)
312     {
313     }
314    
315     void Loader::launch()
316     {
317     thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true);
318     printf("launch thread=%x\n", thread);
319     }
320    
321     float Loader::get_progress()
322     {
323     float res;
324     {
325     Glib::Mutex::Lock lock(progressMutex);
326     res = progress;
327     }
328     return res;
329     }
330    
331     Glib::Dispatcher& Loader::signal_progress()
332     {
333     return progress_dispatcher;
334     }
335    
336     Glib::Dispatcher& Loader::signal_finished()
337     {
338     return finished_dispatcher;
339     }
340    
341     LoadDialog::LoadDialog(const Glib::ustring& title, Gtk::Window& parent)
342     : Gtk::Dialog(title, parent, true)
343     {
344     get_vbox()->pack_start(progressBar);
345     show_all_children();
346     }
347    
348     // Clear all GUI elements / controls. This method is typically called
349     // before a new .gig file is to be created or to be loaded.
350     void MainWindow::__clear() {
351     // remove all entries from "Instrument" menu
352     Gtk::MenuItem* instrument_menu =
353     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument"));
354     instrument_menu->hide();
355     for (int i = 0; i < instrument_menu->get_submenu()->items().size(); i++) {
356     delete &instrument_menu->get_submenu()->items()[i];
357     }
358     instrument_menu->get_submenu()->items().clear();
359     // forget all samples that ought to be imported
360     m_SampleImportQueue.clear();
361     // clear the samples and instruments tree views
362     m_refTreeModel->clear();
363     m_refSamplesTreeModel->clear();
364     // free libgig's gig::File instance
365     if (file) {
366     delete file;
367     file = NULL;
368     }
369     }
370    
371     void MainWindow::on_action_file_new()
372     {
373     // clear all GUI elements
374     __clear();
375     // create a new .gig file (virtually yet)
376     gig::File* pFile = new gig::File;
377     // already add one new instrument by default
378     gig::Instrument* pInstrument = pFile->AddInstrument();
379     pInstrument->pInfo->Name = "Unnamed Instrument";
380     // update GUI with that new gig::File
381     load_gig(pFile, NULL /*no file name yet*/);
382     }
383    
384     void MainWindow::on_action_file_open()
385     {
386     Gtk::FileChooserDialog dialog(*this, _("Open file"));
387     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
388     dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
389     Gtk::FileFilter filter;
390     filter.add_pattern("*.gig");
391     dialog.set_filter(filter);
392     if (dialog.run() == Gtk::RESPONSE_OK) {
393     printf("filename=%s\n", dialog.get_filename().c_str());
394     __clear();
395     printf("on_action_file_open self=%x\n", Glib::Thread::self());
396     load_file(dialog.get_filename().c_str());
397     }
398     }
399    
400     void MainWindow::load_file(const char* name)
401     {
402     load_dialog = new LoadDialog("Loading...", *this);
403     load_dialog->show_all();
404     loader = new Loader(strdup(name));
405     loader->signal_progress().connect(
406     sigc::mem_fun(*this, &MainWindow::on_loader_progress));
407     loader->signal_finished().connect(
408     sigc::mem_fun(*this, &MainWindow::on_loader_finished));
409     loader->launch();
410     }
411    
412     void MainWindow::load_instrument(gig::Instrument* instr) {
413     if (!instr) {
414     Glib::ustring txt = "Provided instrument is NULL!\n";
415     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
416     msg.run();
417     Gtk::Main::quit();
418     }
419     gig::File* pFile = (gig::File*) instr->GetParent();
420     load_gig(pFile, NULL /*file name*/);
421     //TODO: automatically select the given instrument
422     }
423    
424     void MainWindow::on_loader_progress()
425     {
426     load_dialog->set_fraction(loader->get_progress());
427     }
428    
429     void MainWindow::on_loader_finished()
430     {
431     printf("Loader finished!\n");
432     printf("on_loader_finished self=%x\n", Glib::Thread::self());
433     load_gig(loader->gig, loader->filename);
434     load_dialog->hide();
435     }
436    
437     void MainWindow::on_action_file_save()
438     {
439     if (!file) return;
440     std::cout << "Saving file\n" << std::flush;
441     try {
442     file->Save();
443     } catch (RIFF::Exception e) {
444     Glib::ustring txt = "Could not save file: " + e.Message;
445     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
446     msg.run();
447     return;
448     }
449     std::cout << "Saving file done\n" << std::flush;
450     __import_queued_samples();
451     }
452    
453     void MainWindow::on_action_file_save_as()
454     {
455     if (!file) return;
456     Gtk::FileChooserDialog dialog(*this, "Open", Gtk::FILE_CHOOSER_ACTION_SAVE);
457     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
458     dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
459     Gtk::FileFilter filter;
460     filter.add_pattern("*.gig");
461     dialog.set_filter(filter);
462     if (dialog.run() == Gtk::RESPONSE_OK) {
463     printf("filename=%s\n", dialog.get_filename().c_str());
464     try {
465     file->Save(dialog.get_filename());
466     } catch (RIFF::Exception e) {
467     Glib::ustring txt = "Could not save file: " + e.Message;
468     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
469     msg.run();
470     return;
471     }
472     __import_queued_samples();
473     }
474     }
475    
476     // actually write the sample(s)' data to the gig file
477     void MainWindow::__import_queued_samples() {
478     std::cout << "Starting sample import\n" << std::flush;
479     Glib::ustring error_files;
480     printf("Samples to import: %d\n", m_SampleImportQueue.size());
481     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
482     iter != m_SampleImportQueue.end(); ) {
483     printf("Importing sample %s\n",(*iter).sample_path.c_str());
484     SF_INFO info;
485     info.format = 0;
486     SNDFILE* hFile = sf_open((*iter).sample_path.c_str(), SFM_READ, &info);
487     try {
488     if (!hFile) throw std::string("could not open file");
489     // determine sample's bit depth
490     int bitdepth;
491     switch (info.format & 0xff) {
492     case SF_FORMAT_PCM_S8:
493     bitdepth = 16; // we simply convert to 16 bit for now
494     break;
495     case SF_FORMAT_PCM_16:
496     bitdepth = 16;
497     break;
498     case SF_FORMAT_PCM_24:
499     bitdepth = 32; // we simply convert to 32 bit for now
500     break;
501     case SF_FORMAT_PCM_32:
502     bitdepth = 32;
503     break;
504     case SF_FORMAT_PCM_U8:
505     bitdepth = 16; // we simply convert to 16 bit for now
506     break;
507     case SF_FORMAT_FLOAT:
508     bitdepth = 32;
509     break;
510     case SF_FORMAT_DOUBLE:
511     bitdepth = 32; // I guess we will always truncate this to 32 bit
512     break;
513     default:
514     sf_close(hFile); // close sound file
515     throw std::string("format not supported"); // unsupported subformat (yet?)
516     }
517     // allocate appropriate copy buffer (TODO: for now we copy
518     // it in one piece, might be tough for very long samples)
519     // and copy sample data into buffer
520     int8_t* buffer = NULL;
521     switch (bitdepth) {
522     case 16:
523     buffer = new int8_t[2 * info.channels * info.frames];
524     // libsndfile does the conversion for us (if needed)
525     sf_readf_short(hFile, (short*) buffer, info.frames);
526     break;
527     case 32:
528     buffer = new int8_t[4 * info.channels * info.frames];
529     // libsndfile does the conversion for us (if needed)
530     sf_readf_int(hFile, (int*) buffer, info.frames);
531     break;
532     }
533     // write from buffer directly (physically) into .gig file
534     (*iter).gig_sample->Write(buffer, info.frames);
535     // cleanup
536     sf_close(hFile);
537     delete[] buffer;
538     // on success we remove the sample from the import queue,
539     // otherwise keep it, maybe it works the next time ?
540     std::list<SampleImportItem>::iterator cur = iter;
541     ++iter;
542     m_SampleImportQueue.erase(cur);
543     } catch (std::string what) {
544     // remember the files that made trouble (and their cause)
545     if (error_files.size()) error_files += "\n";
546     error_files += (*iter).sample_path += " (" + what + ")";
547     ++iter;
548     }
549     }
550     // show error message box when some sample(s) could not be imported
551     if (error_files.size()) {
552     Glib::ustring txt = "Could not import the following sample(s):\n" + error_files;
553     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
554     msg.run();
555     }
556     }
557    
558     void MainWindow::on_action_file_properties()
559     {
560     propDialog.show();
561     propDialog.deiconify();
562     }
563    
564     void MainWindow::on_action_help_about()
565     {
566     #ifdef ABOUT_DIALOG
567     Gtk::AboutDialog dialog;
568     dialog.set_version(VERSION);
569     dialog.run();
570     #endif
571     }
572    
573     PropDialog::PropDialog()
574     : table(2,1)
575     {
576     table.set_col_spacings(5);
577     const char* propLabels[] = {
578     "Name:",
579     "CreationDate:",
580     "Comments:", // TODO: multiline
581     "Product:",
582     "Copyright:",
583     "Artists:",
584     "Genre:",
585     "Keywords:",
586     "Engineer:",
587     "Technician:",
588     "Software:", // TODO: readonly
589     "Medium:",
590     "Source:",
591     "SourceForm:",
592     "Commissioned:",
593     "Subject:"
594     };
595     for (int i = 0 ; i < sizeof(propLabels) / sizeof(char*) ; i++) {
596     label[i].set_text(propLabels[i]);
597     label[i].set_alignment(Gtk::ALIGN_LEFT);
598     table.attach(label[i], 0, 1, i, i + 1, Gtk::FILL, Gtk::SHRINK);
599     table.attach(entry[i], 1, 2, i, i + 1, Gtk::FILL | Gtk::EXPAND,
600     Gtk::SHRINK);
601     }
602    
603     add(table);
604     // add_button(Gtk::Stock::CANCEL, 0);
605     // add_button(Gtk::Stock::OK, 1);
606     show_all_children();
607     }
608    
609     void PropDialog::set_info(DLS::Info* info)
610     {
611     entry[0].set_text(info->Name);
612     entry[1].set_text(info->CreationDate);
613     entry[2].set_text(Glib::convert(info->Comments, "UTF-8", "ISO-8859-1"));
614     entry[3].set_text(info->Product);
615     entry[4].set_text(info->Copyright);
616     entry[5].set_text(info->Artists);
617     entry[6].set_text(info->Genre);
618     entry[7].set_text(info->Keywords);
619     entry[8].set_text(info->Engineer);
620     entry[9].set_text(info->Technician);
621     entry[10].set_text(info->Software);
622     entry[11].set_text(info->Medium);
623     entry[12].set_text(info->Source);
624     entry[13].set_text(info->SourceForm);
625     entry[14].set_text(info->Commissioned);
626     entry[15].set_text(info->Subject);
627     }
628    
629     void InstrumentProps::add_prop(LabelWidget& prop)
630     {
631     table.attach(prop.label, 0, 1, rowno, rowno + 1,
632     Gtk::FILL, Gtk::SHRINK);
633     table.attach(prop.widget, 1, 2, rowno, rowno + 1,
634     Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
635     rowno++;
636     }
637    
638     InstrumentProps::InstrumentProps()
639     : table(2,1),
640     quitButton(Gtk::Stock::CLOSE),
641     eName("Name"),
642     eIsDrum("IsDrum"),
643     eMIDIBank("MIDIBank", 0, 16383),
644     eMIDIProgram("MIDIProgram"),
645     eAttenuation("Attenuation", 0, 96, 0, 1),
646     eGainPlus6("Gain +6dB", eAttenuation, -6),
647     eEffectSend("EffectSend", 0, 65535),
648     eFineTune("FineTune", -8400, 8400),
649     ePitchbendRange("PitchbendRange", 0, 12),
650     ePianoReleaseMode("PianoReleaseMode"),
651     eDimensionKeyRangeLow("DimensionKeyRangeLow"),
652     eDimensionKeyRangeHigh("DimensionKeyRangeHigh")
653     {
654     set_title("Instrument properties");
655    
656     rowno = 0;
657     table.set_col_spacings(5);
658    
659     add_prop(eName);
660     add_prop(eIsDrum);
661     add_prop(eMIDIBank);
662     add_prop(eMIDIProgram);
663     add_prop(eAttenuation);
664     add_prop(eGainPlus6);
665     add_prop(eEffectSend);
666     add_prop(eFineTune);
667     add_prop(ePitchbendRange);
668     add_prop(ePianoReleaseMode);
669     add_prop(eDimensionKeyRangeLow);
670     add_prop(eDimensionKeyRangeHigh);
671    
672     eDimensionKeyRangeLow.signal_value_changed().connect(
673     sigc::mem_fun(*this, &InstrumentProps::key_range_low_changed));
674     eDimensionKeyRangeHigh.signal_value_changed().connect(
675     sigc::mem_fun(*this, &InstrumentProps::key_range_high_changed));
676    
677     add(vbox);
678     table.set_border_width(5);
679     vbox.pack_start(table);
680     table.show();
681     vbox.pack_start(buttonBox, Gtk::PACK_SHRINK);
682     buttonBox.set_layout(Gtk::BUTTONBOX_END);
683     buttonBox.set_border_width(5);
684     buttonBox.show();
685     buttonBox.pack_start(quitButton);
686     quitButton.set_flags(Gtk::CAN_DEFAULT);
687     quitButton.grab_focus();
688    
689     quitButton.signal_clicked().connect(
690     sigc::mem_fun(*this, &InstrumentProps::hide));
691    
692     quitButton.show();
693     vbox.show();
694     show_all_children();
695     }
696    
697     void InstrumentProps::set_instrument(gig::Instrument* instrument)
698     {
699     update_gui = false;
700     eName.set_ptr(&instrument->pInfo->Name);
701     eIsDrum.set_ptr(&instrument->IsDrum);
702     eMIDIBank.set_ptr(&instrument->MIDIBank);
703     eMIDIProgram.set_ptr(&instrument->MIDIProgram);
704     eAttenuation.set_ptr(&instrument->Attenuation);
705     eGainPlus6.set_ptr(&instrument->Attenuation);
706     eEffectSend.set_ptr(&instrument->EffectSend);
707     eFineTune.set_ptr(&instrument->FineTune);
708     ePitchbendRange.set_ptr(&instrument->PitchbendRange);
709     ePianoReleaseMode.set_ptr(&instrument->PianoReleaseMode);
710     eDimensionKeyRangeLow.set_ptr(&instrument->DimensionKeyRange.low);
711     eDimensionKeyRangeHigh.set_ptr(&instrument->DimensionKeyRange.high);
712     update_gui = true;
713     }
714    
715     void InstrumentProps::key_range_low_changed()
716     {
717     double l = eDimensionKeyRangeLow.get_value();
718     double h = eDimensionKeyRangeHigh.get_value();
719     if (h < l) eDimensionKeyRangeHigh.set_value(l);
720     }
721    
722     void InstrumentProps::key_range_high_changed()
723     {
724     double l = eDimensionKeyRangeLow.get_value();
725     double h = eDimensionKeyRangeHigh.get_value();
726     if (h < l) eDimensionKeyRangeLow.set_value(h);
727     }
728    
729     void MainWindow::load_gig(gig::File* gig, const char* filename)
730     {
731     file = gig;
732    
733     if (filename) {
734     const char *basename = strrchr(filename, '/');
735     basename = basename ? basename + 1 : filename;
736     set_title(basename);
737     } else {
738     set_title("unnamed");
739     }
740    
741     propDialog.set_info(gig->pInfo);
742    
743     Gtk::MenuItem* instrument_menu =
744     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument"));
745    
746     int instrument_index = 0;
747     Gtk::RadioMenuItem::Group instrument_group;
748     for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ;
749     instrument = gig->GetNextInstrument()) {
750     Gtk::TreeModel::iterator iter = m_refTreeModel->append();
751     Gtk::TreeModel::Row row = *iter;
752     row[m_Columns.m_col_name] = instrument->pInfo->Name.c_str();
753     row[m_Columns.m_col_instr] = instrument;
754     // create a menu item for this instrument
755     Gtk::RadioMenuItem* item =
756     new Gtk::RadioMenuItem(instrument_group, instrument->pInfo->Name.c_str());
757     instrument_menu->get_submenu()->append(*item);
758     item->signal_activate().connect(
759     sigc::bind(
760     sigc::mem_fun(*this, &MainWindow::on_instrument_selection_change),
761     instrument_index
762     )
763     );
764     instrument_index++;
765     }
766     instrument_menu->show();
767     instrument_menu->get_submenu()->show_all_children();
768    
769     for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) {
770     if (group->Name != "") {
771     Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
772     Gtk::TreeModel::Row rowGroup = *iterGroup;
773     rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str();
774     rowGroup[m_SamplesModel.m_col_group] = group;
775     rowGroup[m_SamplesModel.m_col_sample] = NULL;
776     for (gig::Sample* sample = group->GetFirstSample();
777     sample; sample = group->GetNextSample()) {
778     Gtk::TreeModel::iterator iterSample =
779     m_refSamplesTreeModel->append(rowGroup.children());
780     Gtk::TreeModel::Row rowSample = *iterSample;
781     rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str();
782     rowSample[m_SamplesModel.m_col_sample] = sample;
783     rowSample[m_SamplesModel.m_col_group] = NULL;
784     }
785     }
786     }
787    
788     // select the first instrument
789     Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
790     tree_sel_ref->select(Gtk::TreePath("0"));
791     }
792    
793     void MainWindow::show_instr_props()
794     {
795     Glib::RefPtr<Gtk::TreeSelection> tree_sel_ref = m_TreeView.get_selection();
796     Gtk::TreeModel::iterator it = tree_sel_ref->get_selected();
797     if (it)
798     {
799     Gtk::TreeModel::Row row = *it;
800     if (row[m_Columns.m_col_instr])
801     {
802     instrumentProps.set_instrument(row[m_Columns.m_col_instr]);
803     instrumentProps.show();
804     instrumentProps.deiconify();
805     }
806     }
807     }
808    
809     void MainWindow::on_button_release(GdkEventButton* button)
810     {
811     if (button->type == GDK_2BUTTON_PRESS) {
812     show_instr_props();
813     } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
814     popup_menu->popup(button->button, button->time);
815     }
816     }
817    
818     void MainWindow::on_instrument_selection_change(int index) {
819     m_RegionChooser.set_instrument(file->GetInstrument(index));
820     }
821    
822     void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) {
823     if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
824     Gtk::Menu* sample_popup =
825     dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/SamplePopupMenu"));
826     // update enabled/disabled state of sample popup items
827     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
828     Gtk::TreeModel::iterator it = sel->get_selected();
829     bool group_selected = false;
830     bool sample_selected = false;
831     if (it) {
832     Gtk::TreeModel::Row row = *it;
833     group_selected = row[m_SamplesModel.m_col_group];
834     sample_selected = row[m_SamplesModel.m_col_sample];
835     }
836     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->
837     set_sensitive(group_selected || sample_selected);
838     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->
839     set_sensitive(group_selected || sample_selected);
840     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->
841     set_sensitive(file);
842     dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->
843     set_sensitive(group_selected || sample_selected);
844     // show sample popup
845     sample_popup->popup(button->button, button->time);
846     }
847     }
848    
849     void MainWindow::on_action_add_instrument() {
850     static int __instrument_indexer = 0;
851     if (!file) return;
852     gig::Instrument* instrument = file->AddInstrument();
853     __instrument_indexer++;
854     instrument->pInfo->Name =
855     "Unnamed Instrument " + ToString(__instrument_indexer);
856     // update instrument tree view
857     Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append();
858     Gtk::TreeModel::Row rowInstr = *iterInstr;
859     rowInstr[m_Columns.m_col_name] = instrument->pInfo->Name.c_str();
860     rowInstr[m_Columns.m_col_instr] = instrument;
861     }
862    
863     void MainWindow::on_action_remove_instrument() {
864     if (!file) return;
865     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeView.get_selection();
866     Gtk::TreeModel::iterator it = sel->get_selected();
867     if (it) {
868     Gtk::TreeModel::Row row = *it;
869     gig::Instrument* instr = row[m_Columns.m_col_instr];
870     try {
871     // remove instrument from the gig file
872     if (instr) file->DeleteInstrument(instr);
873     // remove respective row from instruments tree view
874     m_refTreeModel->erase(it);
875     } catch (RIFF::Exception e) {
876     Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
877     msg.run();
878     }
879     }
880     }
881    
882     void MainWindow::on_action_sample_properties() {
883     //TODO: show a dialog where the selected sample's properties can be edited
884     Gtk::MessageDialog msg(
885     *this, "Sorry, yet to be implemented!", false, Gtk::MESSAGE_INFO
886     );
887     msg.run();
888     }
889    
890     void MainWindow::on_action_add_group() {
891     static int __sample_indexer = 0;
892     if (!file) return;
893     gig::Group* group = file->AddGroup();
894     group->Name = "Unnamed Group";
895     if (__sample_indexer) group->Name += " " + ToString(__sample_indexer);
896     __sample_indexer++;
897     // update sample tree view
898     Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append();
899     Gtk::TreeModel::Row rowGroup = *iterGroup;
900     rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str();
901     rowGroup[m_SamplesModel.m_col_sample] = NULL;
902     rowGroup[m_SamplesModel.m_col_group] = group;
903     }
904    
905     void MainWindow::on_action_add_sample() {
906     if (!file) return;
907     // get selected group
908     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
909     Gtk::TreeModel::iterator it = sel->get_selected();
910     if (!it) return;
911     Gtk::TreeModel::Row row = *it;
912     gig::Group* group = row[m_SamplesModel.m_col_group];
913     if (!group) { // not a group, but a sample is selected (probably)
914     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
915     if (!sample) return;
916     it = row.parent(); // resolve parent (that is the sample's group)
917     if (!it) return;
918     row = *it;
919     group = row[m_SamplesModel.m_col_group];
920     if (!group) return;
921     }
922     // show 'browse for file' dialog
923     Gtk::FileChooserDialog dialog(*this, _("Add Sample(s)"));
924     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
925     dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
926     dialog.set_select_multiple(true);
927     Gtk::FileFilter soundfilter; // matches all file types supported by libsndfile
928     const char* supportedFileTypes[] = {
929     "*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd",
930     "*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF",
931     "*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64",
932     "*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK",
933     "*.caf", "*.CAF", NULL
934     };
935     for (int i = 0; supportedFileTypes[i]; i++)
936     soundfilter.add_pattern(supportedFileTypes[i]);
937     soundfilter.set_name("Sound Files");
938     Gtk::FileFilter allpassfilter; // matches every file
939     allpassfilter.add_pattern("*.*");
940     allpassfilter.set_name("All Files");
941     dialog.add_filter(soundfilter);
942     dialog.add_filter(allpassfilter);
943     if (dialog.run() == Gtk::RESPONSE_OK) {
944     Glib::ustring error_files;
945     Glib::SListHandle<Glib::ustring> filenames = dialog.get_filenames();
946     for (Glib::SListHandle<Glib::ustring>::iterator iter = filenames.begin();
947     iter != filenames.end(); ++iter) {
948     printf("Adding sample %s\n",(*iter).c_str());
949     // use libsndfile to retrieve file informations
950     SF_INFO info;
951     info.format = 0;
952     SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info);
953     try {
954     if (!hFile) throw std::string("could not open file");
955     int bitdepth;
956     switch (info.format & 0xff) {
957     case SF_FORMAT_PCM_S8:
958     bitdepth = 16; // we simply convert to 16 bit for now
959     break;
960     case SF_FORMAT_PCM_16:
961     bitdepth = 16;
962     break;
963     case SF_FORMAT_PCM_24:
964     bitdepth = 32; // we simply convert to 32 bit for now
965     break;
966     case SF_FORMAT_PCM_32:
967     bitdepth = 32;
968     break;
969     case SF_FORMAT_PCM_U8:
970     bitdepth = 16; // we simply convert to 16 bit for now
971     break;
972     case SF_FORMAT_FLOAT:
973     bitdepth = 32;
974     break;
975     case SF_FORMAT_DOUBLE:
976     bitdepth = 32; // I guess we will always truncate this to 32 bit
977     break;
978     default:
979     sf_close(hFile); // close sound file
980     throw std::string("format not supported"); // unsupported subformat (yet?)
981     }
982     // add a new sample to the .gig file
983     gig::Sample* sample = file->AddSample();
984     // file name without path
985     sample->pInfo->Name = (*iter).substr((*iter).rfind('/') + 1).raw();
986     sample->Channels = info.channels;
987     sample->BitDepth = bitdepth;
988     sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels;
989     sample->SamplesPerSecond = info.samplerate;
990     // schedule resizing the sample (which will be done
991     // physically when File::Save() is called)
992     sample->Resize(info.frames);
993     // make sure sample is part of the selected group
994     group->AddSample(sample);
995     // schedule that physical resize and sample import
996     // (data copying), performed when "Save" is requested
997     SampleImportItem sched_item;
998     sched_item.gig_sample = sample;
999     sched_item.sample_path = *iter;
1000     m_SampleImportQueue.push_back(sched_item);
1001     // add sample to the tree view
1002     Gtk::TreeModel::iterator iterSample =
1003     m_refSamplesTreeModel->append(row.children());
1004     Gtk::TreeModel::Row rowSample = *iterSample;
1005     rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str();
1006     rowSample[m_SamplesModel.m_col_sample] = sample;
1007     rowSample[m_SamplesModel.m_col_group] = NULL;
1008     // close sound file
1009     sf_close(hFile);
1010     } catch (std::string what) { // remember the files that made trouble (and their cause)
1011     if (error_files.size()) error_files += "\n";
1012     error_files += *iter += " (" + what + ")";
1013     }
1014     }
1015     // show error message box when some file(s) could not be opened / added
1016     if (error_files.size()) {
1017     Glib::ustring txt = "Could not add the following sample(s):\n" + error_files;
1018     Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR);
1019     msg.run();
1020     }
1021     }
1022     }
1023    
1024     void MainWindow::on_action_remove_sample() {
1025     if (!file) return;
1026     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1027     Gtk::TreeModel::iterator it = sel->get_selected();
1028     if (it) {
1029     Gtk::TreeModel::Row row = *it;
1030     gig::Group* group = row[m_SamplesModel.m_col_group];
1031     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1032     Glib::ustring name = row[m_SamplesModel.m_col_name];
1033     try {
1034     // remove group or sample from the gig file
1035     if (group) {
1036     // temporarily remember the samples that bolong to
1037     // that group (we need that to clean the queue)
1038     std::list<gig::Sample*> members;
1039     for (gig::Sample* pSample = group->GetFirstSample();
1040     pSample; pSample = group->GetNextSample()) {
1041     members.push_back(pSample);
1042     }
1043     // delete the group in the .gig file including the
1044     // samples that belong to the group
1045     file->DeleteGroup(group);
1046     // if sample(s) were just previously added, remove
1047     // them from the import queue
1048     for (std::list<gig::Sample*>::iterator member = members.begin();
1049     member != members.end(); ++member) {
1050     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1051     iter != m_SampleImportQueue.end(); ++iter) {
1052     if ((*iter).gig_sample == *member) {
1053     printf("Removing previously added sample '%s' from group '%s'\n",
1054     (*iter).sample_path.c_str(), name.c_str());
1055     m_SampleImportQueue.erase(iter);
1056     break;
1057     }
1058     }
1059     }
1060     } else if (sample) {
1061     // remove sample from the .gig file
1062     file->DeleteSample(sample);
1063     // if sample was just previously added, remove it from
1064     // the import queue
1065     for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin();
1066     iter != m_SampleImportQueue.end(); ++iter) {
1067     if ((*iter).gig_sample == sample) {
1068     printf("Removing previously added sample '%s'\n",
1069     (*iter).sample_path.c_str());
1070     m_SampleImportQueue.erase(iter);
1071     break;
1072     }
1073     }
1074     }
1075     // remove respective row(s) from samples tree view
1076     m_refSamplesTreeModel->erase(it);
1077     } catch (RIFF::Exception e) {
1078     Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR);
1079     msg.run();
1080     }
1081     }
1082     }
1083    
1084     void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&,
1085     Gtk::SelectionData& selection_data, guint, guint)
1086     {
1087     // get selected sample
1088     gig::Sample* sample = NULL;
1089     Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection();
1090     Gtk::TreeModel::iterator it = sel->get_selected();
1091     if (it) {
1092     Gtk::TreeModel::Row row = *it;
1093     sample = row[m_SamplesModel.m_col_sample];
1094     }
1095     // pass the gig::Sample as pointer
1096     selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample,
1097     sizeof(sample)/*length of data in bytes*/);
1098     }
1099    
1100     void MainWindow::on_sample_label_drop_drag_data_received(
1101     const Glib::RefPtr<Gdk::DragContext>& context, int, int,
1102     const Gtk::SelectionData& selection_data, guint, guint time)
1103     {
1104     gig::DimensionRegion* dimregion = m_DimRegionChooser.get_dimregion();
1105     gig::Sample* sample = *((gig::Sample**) selection_data.get_data());
1106    
1107     if (sample && dimregion && selection_data.get_length() == sizeof(gig::Sample*)) {
1108     if (sample != dimregion->pSample) {
1109     dimregion->pSample = sample;
1110     dimreg_edit.wSample->set_text(dimregion->pSample->pInfo->Name.c_str());
1111     std::cout << "Drop received sample \"" <<
1112     dimregion->pSample->pInfo->Name.c_str() << "\"" << std::endl;
1113     // drop success
1114     context->drop_reply(true, time);
1115     return;
1116     }
1117     }
1118     // drop failed
1119     context->drop_reply(false, time);
1120     }
1121    
1122     void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path,
1123     const Gtk::TreeModel::iterator& iter) {
1124     if (!iter) return;
1125     Gtk::TreeModel::Row row = *iter;
1126     Glib::ustring name = row[m_SamplesModel.m_col_name];
1127     gig::Group* group = row[m_SamplesModel.m_col_group];
1128     gig::Sample* sample = row[m_SamplesModel.m_col_sample];
1129     if (group) {
1130     group->Name = name;
1131     } else if (sample) {
1132     sample->pInfo->Name = name.raw();
1133     }
1134     }
1135    
1136     void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path,
1137     const Gtk::TreeModel::iterator& iter) {
1138     if (!iter) return;
1139     Gtk::TreeModel::Row row = *iter;
1140     Glib::ustring name = row[m_Columns.m_col_name];
1141     gig::Instrument* instrument = row[m_Columns.m_col_instr];
1142     if (instrument) instrument->pInfo->Name = name.raw();
1143     }

  ViewVC Help
Powered by ViewVC