--- gigedit/trunk/src/gigedit/mainwindow.cpp 2014/06/12 16:12:55 2627 +++ gigedit/trunk/src/gigedit/mainwindow.cpp 2015/01/20 18:48:15 2715 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2014 Andreas Persson + * Copyright (C) 2006-2015 Andreas Persson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -217,6 +217,14 @@ sigc::mem_fun(*this, &MainWindow::on_action_warn_user_on_extensions) ); + toggle_action = + Gtk::ToggleAction::create("SyncSamplerInstrumentSelection", _("Synchronize sampler's instrument selection")); + toggle_action->set_active(Settings::singleton()->syncSamplerInstrumentSelection); + actionGroup->add( + toggle_action, + sigc::mem_fun(*this, &MainWindow::on_action_sync_sampler_instrument_selection) + ); + actionGroup->add(Gtk::Action::create("MenuTools", _("_Tools"))); @@ -253,6 +261,11 @@ sigc::mem_fun(*this, &MainWindow::on_action_view_references) ); actionGroup->add( + Gtk::Action::create("ReplaceSample", + _("Replace Sample...")), + sigc::mem_fun(*this, &MainWindow::on_action_replace_sample) + ); + actionGroup->add( Gtk::Action::create("ReplaceAllSamplesInAllGroups", _("Replace All Samples in All Groups...")), sigc::mem_fun(*this, &MainWindow::on_action_replace_all_samples_in_all_groups) @@ -304,6 +317,7 @@ " " " " " " + " " " " " " " " @@ -336,6 +350,7 @@ " " " " " " + " " " " " " " " @@ -355,6 +370,7 @@ " " " " " " + " " " " " " " " @@ -397,6 +413,11 @@ } { Gtk::MenuItem* item = dynamic_cast( + uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection")); + item->set_tooltip_text(_("If checked, the sampler's current instrument will automatically be switched whenever another instrument was selected in gigedit (only available in live-mode).")); + } + { + Gtk::MenuItem* item = dynamic_cast( uiManager->get_widget("/MenuBar/MenuTools/CombineInstruments")); item->set_tooltip_text(_("Create combi sounds out of individual sounds of this .gig file.")); } @@ -434,7 +455,7 @@ // Create the Tree model: m_refTreeModel = Gtk::ListStore::create(m_Columns); m_TreeView.set_model(m_refTreeModel); - m_TreeView.set_tooltip_text(_("Right click here for actions on instruments & MIDI Rules.")); + m_TreeView.set_tooltip_text(_("Right click here for actions on instruments & MIDI Rules. Drag & drop to change the order of instruments.")); instrument_name_connection = m_refTreeModel->signal_row_changed().connect( sigc::mem_fun(*this, &MainWindow::instrument_name_changed) ); @@ -442,6 +463,24 @@ // Add the TreeView's view columns: m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name); m_TreeView.set_headers_visible(false); + + // establish drag&drop within the instrument tree view, allowing to reorder + // the sequence of instruments within the gig file + { + std::vector drag_target_instrument; + drag_target_instrument.push_back(Gtk::TargetEntry("gig::Instrument")); + m_TreeView.drag_source_set(drag_target_instrument); + m_TreeView.drag_dest_set(drag_target_instrument); + m_TreeView.signal_drag_begin().connect( + sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_begin) + ); + m_TreeView.signal_drag_data_get().connect( + sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_data_get) + ); + m_TreeView.signal_drag_data_received().connect( + sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drop_drag_data_received) + ); + } // create samples treeview (including its data model) m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel); @@ -453,7 +492,7 @@ { Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(0); Gtk::CellRendererText* cellrenderer = - dynamic_cast(column->get_first_cell_renderer()); + dynamic_cast(column->get_first_cell()); column->add_attribute( cellrenderer->property_foreground(), m_SamplesModel.m_color ); @@ -461,7 +500,7 @@ { Gtk::TreeViewColumn* column = m_TreeViewSamples.get_column(1); Gtk::CellRendererText* cellrenderer = - dynamic_cast(column->get_first_cell_renderer()); + dynamic_cast(column->get_first_cell()); column->add_attribute( cellrenderer->property_foreground(), m_SamplesModel.m_color ); @@ -478,6 +517,8 @@ m_refScriptsTreeModel = ScriptsTreeStore::create(m_ScriptsModel); m_TreeViewScripts.set_model(m_refScriptsTreeModel); m_TreeViewScripts.set_tooltip_text(_( + "Use CTRL + double click for editing a script." + "\n\n" "Note: instrument scripts are a LinuxSampler extension of the gig " "format. This feature will not work with the GigaStudio software!" )); @@ -487,6 +528,10 @@ m_TreeViewScripts.signal_button_press_event().connect_notify( sigc::mem_fun(*this, &MainWindow::on_script_treeview_button_release) ); + //FIXME: why the heck does this double click signal_row_activated() only fire while CTRL key is pressed ? + m_TreeViewScripts.signal_row_activated().connect( + sigc::mem_fun(*this, &MainWindow::script_double_clicked) + ); m_refScriptsTreeModel->signal_row_changed().connect( sigc::mem_fun(*this, &MainWindow::script_name_changed) ); @@ -542,6 +587,10 @@ sigc::mem_fun(*this, &MainWindow::on_samples_to_be_removed) ); + dimreg_edit.signal_select_sample().connect( + sigc::mem_fun(*this, &MainWindow::select_sample) + ); + m_RegionChooser.signal_instrument_struct_to_be_changed().connect( sigc::hide( sigc::bind( @@ -682,6 +731,10 @@ } m_RegionChooser.set_instrument(get_instrument()); + + if (Settings::singleton()->syncSamplerInstrumentSelection) { + switch_sampler_instrument_signal.emit(get_instrument()); + } } void loader_progress_callback(gig::progress_t* progress) @@ -702,20 +755,28 @@ void Loader::thread_function() { printf("thread_function self=%x\n", Glib::Threads::Thread::self()); - printf("Start %s\n", filename); - RIFF::File* riff = new RIFF::File(filename); - gig = new gig::File(riff); - gig::progress_t progress; - progress.callback = loader_progress_callback; - progress.custom = this; - - gig->GetInstrument(0, &progress); - printf("End\n"); - finished_dispatcher(); + printf("Start %s\n", filename.c_str()); + try { + RIFF::File* riff = new RIFF::File(filename); + gig = new gig::File(riff); + gig::progress_t progress; + progress.callback = loader_progress_callback; + progress.custom = this; + + gig->GetInstrument(0, &progress); + printf("End\n"); + finished_dispatcher(); + } catch (RIFF::Exception e) { + error_message = e.Message; + error_dispatcher.emit(); + } catch (...) { + error_message = _("Unknown exception occurred"); + error_dispatcher.emit(); + } } Loader::Loader(const char* filename) - : filename(filename), thread(0) + : filename(filename), thread(0), progress(0.f) { } @@ -749,11 +810,99 @@ return finished_dispatcher; } -LoadDialog::LoadDialog(const Glib::ustring& title, Gtk::Window& parent) +Glib::Dispatcher& Loader::signal_error() +{ + return error_dispatcher; +} + +void saver_progress_callback(gig::progress_t* progress) +{ + Saver* saver = static_cast(progress->custom); + saver->progress_callback(progress->factor); +} + +void Saver::progress_callback(float fraction) +{ + { + Glib::Threads::Mutex::Lock lock(progressMutex); + progress = fraction; + } + progress_dispatcher.emit(); +} + +void Saver::thread_function() +{ + printf("thread_function self=%x\n", Glib::Threads::Thread::self()); + printf("Start %s\n", filename.c_str()); + try { + gig::progress_t progress; + progress.callback = saver_progress_callback; + progress.custom = this; + + // if no filename was provided, that means "save", if filename was provided means "save as" + if (filename.empty()) { + gig->Save(&progress); + } else { + gig->Save(filename, &progress); + } + + printf("End\n"); + finished_dispatcher.emit(); + } catch (RIFF::Exception e) { + error_message = e.Message; + error_dispatcher.emit(); + } catch (...) { + error_message = _("Unknown exception occurred"); + error_dispatcher.emit(); + } +} + +Saver::Saver(gig::File* file, Glib::ustring filename) + : gig(file), filename(filename), thread(0), progress(0.f) +{ +} + +void Saver::launch() +{ +#ifdef OLD_THREADS + thread = Glib::Thread::create(sigc::mem_fun(*this, &Saver::thread_function), true); +#else + thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Saver::thread_function)); +#endif + printf("launch thread=%x\n", thread); +} + +float Saver::get_progress() +{ + float res; + { + Glib::Threads::Mutex::Lock lock(progressMutex); + res = progress; + } + return res; +} + +Glib::Dispatcher& Saver::signal_progress() +{ + return progress_dispatcher; +} + +Glib::Dispatcher& Saver::signal_finished() +{ + return finished_dispatcher; +} + +Glib::Dispatcher& Saver::signal_error() +{ + return error_dispatcher; +} + +ProgressDialog::ProgressDialog(const Glib::ustring& title, Gtk::Window& parent) : Gtk::Dialog(title, parent, true) { get_vbox()->pack_start(progressBar); show_all_children(); + resize(600,50); } // Clear all GUI elements / controls. This method is typically called @@ -822,8 +971,23 @@ dialog.set_default_response(Gtk::RESPONSE_YES); int response = dialog.run(); dialog.hide(); - if (response == Gtk::RESPONSE_YES) return file_save(); - return response != Gtk::RESPONSE_CANCEL; + + // user decided to exit app without saving + if (response == Gtk::RESPONSE_NO) return true; + + // user cancelled dialog, thus don't close app + if (response == Gtk::RESPONSE_CANCEL) return false; + + // TODO: the following return valid is disabled and hard coded instead for + // now, due to the fact that saving with progress bar is now implemented + // asynchronously, as a result the app does not close automatically anymore + // after saving the file has completed + // + // if (response == Gtk::RESPONSE_YES) return file_save(); + // return response != Gtk::RESPONSE_CANCEL; + // + if (response == Gtk::RESPONSE_YES) file_save(); + return false; // always prevent closing the app for now (see comment above) } bool MainWindow::leaving_shared_mode_dialog() { @@ -874,13 +1038,20 @@ void MainWindow::load_file(const char* name) { __clear(); - load_dialog = new LoadDialog(_("Loading..."), *this); - load_dialog->show_all(); - loader = new Loader(strdup(name)); + + progress_dialog = new ProgressDialog( //FIXME: memory leak! + _("Loading") + Glib::ustring(" '") + + Glib::filename_display_basename(name) + "' ...", + *this + ); + progress_dialog->show_all(); + loader = new Loader(name); //FIXME: memory leak! loader->signal_progress().connect( sigc::mem_fun(*this, &MainWindow::on_loader_progress)); loader->signal_finished().connect( sigc::mem_fun(*this, &MainWindow::on_loader_finished)); + loader->signal_error().connect( + sigc::mem_fun(*this, &MainWindow::on_loader_error)); loader->launch(); } @@ -896,20 +1067,49 @@ // load the instrument gig::File* pFile = (gig::File*) instr->GetParent(); load_gig(pFile, 0 /*file name*/, true /*shared instrument*/); - //TODO: automatically select the given instrument + // automatically select the given instrument + int i = 0; + for (gig::Instrument* instrument = pFile->GetFirstInstrument(); instrument; + instrument = pFile->GetNextInstrument(), ++i) + { + if (instrument == instr) { + // select item in "instruments" tree view + m_TreeView.get_selection()->select(Gtk::TreePath(ToString(i))); + // make sure the selected item in the "instruments" tree view is + // visible (scroll to it) + m_TreeView.scroll_to_row(Gtk::TreePath(ToString(i))); + // select item in instrument menu + { + const std::vector children = + instrument_menu->get_children(); + static_cast(children[i])->set_active(); + } + // update region chooser and dimension region chooser + m_RegionChooser.set_instrument(instr); + break; + } + } } void MainWindow::on_loader_progress() { - load_dialog->set_fraction(loader->get_progress()); + progress_dialog->set_fraction(loader->get_progress()); } void MainWindow::on_loader_finished() { printf("Loader finished!\n"); printf("on_loader_finished self=%x\n", Glib::Threads::Thread::self()); - load_gig(loader->gig, loader->filename); - load_dialog->hide(); + load_gig(loader->gig, loader->filename.c_str()); + progress_dialog->hide(); +} + +void MainWindow::on_loader_error() +{ + Glib::ustring txt = _("Could not load file: ") + loader->error_message; + Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); + msg.run(); + progress_dialog->hide(); } void MainWindow::on_action_file_save() @@ -948,23 +1148,54 @@ std::cout << "Saving file\n" << std::flush; file_structure_to_be_changed_signal.emit(this->file); - try { - file->Save(); - if (file_is_changed) { - set_title(get_title().substr(1)); - file_is_changed = false; - } - } catch (RIFF::Exception e) { - file_structure_changed_signal.emit(this->file); - Glib::ustring txt = _("Could not save file: ") + e.Message; - Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); - msg.run(); - return false; - } - std::cout << "Saving file done\n" << std::flush; + + progress_dialog = new ProgressDialog( //FIXME: memory leak! + _("Saving") + Glib::ustring(" '") + + Glib::filename_display_basename(this->filename) + "' ...", + *this + ); + progress_dialog->show_all(); + saver = new Saver(this->file); //FIXME: memory leak! + saver->signal_progress().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_progress)); + saver->signal_finished().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_finished)); + saver->signal_error().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_error)); + saver->launch(); + + return true; +} + +void MainWindow::on_saver_progress() +{ + progress_dialog->set_fraction(saver->get_progress()); +} + +void MainWindow::on_saver_error() +{ + file_structure_changed_signal.emit(this->file); + Glib::ustring txt = _("Could not save file: ") + saver->error_message; + Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); + msg.run(); +} + +void MainWindow::on_saver_finished() +{ + this->file = saver->gig; + this->filename = saver->filename; + current_gig_dir = Glib::path_get_dirname(filename); + set_title(Glib::filename_display_basename(filename)); + file_has_name = true; + file_is_changed = false; + std::cout << "Saving file done. Importing queued samples now ...\n" << std::flush; __import_queued_samples(); + std::cout << "Importing queued samples done.\n" << std::flush; + file_structure_changed_signal.emit(this->file); - return true; + + __refreshEntireGUI(); + progress_dialog->hide(); } void MainWindow::on_action_file_save_as() @@ -1029,28 +1260,28 @@ descriptionArea.show_all(); if (dialog.run() == Gtk::RESPONSE_OK) { - file_structure_to_be_changed_signal.emit(this->file); - try { - std::string filename = dialog.get_filename(); - if (!Glib::str_has_suffix(filename, ".gig")) { - filename += ".gig"; - } - printf("filename=%s\n", filename.c_str()); - file->Save(filename); - this->filename = filename; - current_gig_dir = Glib::path_get_dirname(filename); - set_title(Glib::filename_display_basename(filename)); - file_has_name = true; - file_is_changed = false; - } catch (RIFF::Exception e) { - file_structure_changed_signal.emit(this->file); - Glib::ustring txt = _("Could not save file: ") + e.Message; - Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); - msg.run(); - return false; + std::string filename = dialog.get_filename(); + if (!Glib::str_has_suffix(filename, ".gig")) { + filename += ".gig"; } - __import_queued_samples(); - file_structure_changed_signal.emit(this->file); + printf("filename=%s\n", filename.c_str()); + + progress_dialog = new ProgressDialog( //FIXME: memory leak! + _("Saving") + Glib::ustring(" '") + + Glib::filename_display_basename(filename) + "' ...", + *this + ); + progress_dialog->show_all(); + + saver = new Saver(file, filename); //FIXME: memory leak! + saver->signal_progress().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_progress)); + saver->signal_finished().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_finished)); + saver->signal_error().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_error)); + saver->launch(); + return true; } return false; @@ -1161,6 +1392,11 @@ !Settings::singleton()->warnUserOnExtensions; } +void MainWindow::on_action_sync_sampler_instrument_selection() { + Settings::singleton()->syncSamplerInstrumentSelection = + !Settings::singleton()->syncSamplerInstrumentSelection; +} + void MainWindow::on_action_help_about() { Gtk::AboutDialog dialog; @@ -1170,7 +1406,7 @@ dialog.set_name("Gigedit"); #endif dialog.set_version(VERSION); - dialog.set_copyright("Copyright (C) 2006-2014 Andreas Persson"); + dialog.set_copyright("Copyright (C) 2006-2015 Andreas Persson"); const std::string sComment = _("Built " __DATE__ "\nUsing ") + ::gig::libraryName() + " " + ::gig::libraryVersion() + "\n\n" + @@ -1682,6 +1918,77 @@ } } +void MainWindow::select_instrument(gig::Instrument* instrument) { + if (!instrument) return; + + Glib::RefPtr model = m_TreeView.get_model(); + for (int i = 0; i < model->children().size(); ++i) { + Gtk::TreeModel::Row row = model->children()[i]; + if (row[m_Columns.m_col_instr] == instrument) { + // select and show the respective instrument in the list view + show_intruments_tab(); + m_TreeView.get_selection()->select(model->children()[i]); + Gtk::TreePath path( + m_TreeView.get_selection()->get_selected() + ); + m_TreeView.scroll_to_row(path); + on_sel_change(); // the regular instrument selection change callback + } + } +} + +/// Returns true if requested dimension region was successfully selected and scrolled to in the list view, false on error. +bool MainWindow::select_dimension_region(gig::DimensionRegion* dimRgn) { + gig::Region* pRegion = (gig::Region*) dimRgn->GetParent(); + gig::Instrument* pInstrument = (gig::Instrument*) pRegion->GetParent(); + + Glib::RefPtr model = m_TreeView.get_model(); + for (int i = 0; i < model->children().size(); ++i) { + Gtk::TreeModel::Row row = model->children()[i]; + if (row[m_Columns.m_col_instr] == pInstrument) { + // select and show the respective instrument in the list view + show_intruments_tab(); + m_TreeView.get_selection()->select(model->children()[i]); + Gtk::TreePath path( + m_TreeView.get_selection()->get_selected() + ); + m_TreeView.scroll_to_row(path); + on_sel_change(); // the regular instrument selection change callback + + // select respective region in the region selector + m_RegionChooser.set_region(pRegion); + + // select and show the respective dimension region in the editor + //update_dimregs(); + if (!m_DimRegionChooser.select_dimregion(dimRgn)) return false; + //dimreg_edit.set_dim_region(dimRgn); + + return true; + } + } + + return false; +} + +void MainWindow::select_sample(gig::Sample* sample) { + Glib::RefPtr model = m_TreeViewSamples.get_model(); + for (int g = 0; g < model->children().size(); ++g) { + Gtk::TreeModel::Row rowGroup = model->children()[g]; + for (int s = 0; s < rowGroup.children().size(); ++s) { + Gtk::TreeModel::Row rowSample = rowGroup.children()[s]; + if (rowSample[m_SamplesModel.m_col_sample] == sample) { + show_samples_tab(); + m_TreeViewSamples.get_selection()->select(rowGroup.children()[s]); + Gtk::TreePath path( + m_TreeViewSamples.get_selection()->get_selected() + ); + m_TreeViewSamples.scroll_to_row(path); + return; + } + } + } +} + void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) { if (button->type == GDK_BUTTON_PRESS && button->button == 3) { Gtk::Menu* sample_popup = @@ -2034,28 +2341,40 @@ file_changed(); } +void MainWindow::on_action_replace_sample() { + add_or_replace_sample(true); +} + void MainWindow::on_action_add_sample() { + add_or_replace_sample(false); +} + +void MainWindow::add_or_replace_sample(bool replace) { if (!file) return; - // get selected group + + // get selected group (and probably selected sample) Glib::RefPtr sel = m_TreeViewSamples.get_selection(); Gtk::TreeModel::iterator it = sel->get_selected(); if (!it) return; Gtk::TreeModel::Row row = *it; + gig::Sample* sample = NULL; gig::Group* group = row[m_SamplesModel.m_col_group]; if (!group) { // not a group, but a sample is selected (probably) - gig::Sample* sample = row[m_SamplesModel.m_col_sample]; - if (!sample) return; + if (replace) sample = row[m_SamplesModel.m_col_sample]; + if (!row[m_SamplesModel.m_col_sample]) return; it = row.parent(); // resolve parent (that is the sample's group) if (!it) return; - row = *it; - group = row[m_SamplesModel.m_col_group]; + if (!replace) row = *it; + group = (*it)[m_SamplesModel.m_col_group]; if (!group) return; } + if (replace && !sample) return; + // show 'browse for file' dialog - Gtk::FileChooserDialog dialog(*this, _("Add Sample(s)")); + Gtk::FileChooserDialog dialog(*this, replace ? _("Replace Sample with") : _("Add Sample(s)")); dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); - dialog.set_select_multiple(true); + dialog.set_select_multiple(!replace); // allow multi audio file selection only when adding new samples, does not make sense when replacing a specific sample // matches all file types supported by libsndfile #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 @@ -2126,8 +2445,8 @@ sf_close(hFile); // close sound file throw std::string(_("format not supported")); // unsupported subformat (yet?) } - // add a new sample to the .gig file - gig::Sample* sample = file->AddSample(); + // add a new sample to the .gig file (if adding is requested actually) + if (!replace) sample = file->AddSample(); // file name without path Glib::ustring filename = Glib::filename_display_basename(*iter); // remove file extension if there is one @@ -2178,7 +2497,7 @@ // physically when File::Save() is called) sample->Resize(info.frames); // make sure sample is part of the selected group - group->AddSample(sample); + if (!replace) group->AddSample(sample); // schedule that physical resize and sample import // (data copying), performed when "Save" is requested SampleImportItem sched_item; @@ -2186,13 +2505,17 @@ sched_item.sample_path = *iter; m_SampleImportQueue.push_back(sched_item); // add sample to the tree view - Gtk::TreeModel::iterator iterSample = - m_refSamplesTreeModel->append(row.children()); - Gtk::TreeModel::Row rowSample = *iterSample; - rowSample[m_SamplesModel.m_col_name] = - gig_to_utf8(sample->pInfo->Name); - rowSample[m_SamplesModel.m_col_sample] = sample; - rowSample[m_SamplesModel.m_col_group] = NULL; + if (replace) { + row[m_SamplesModel.m_col_name] = gig_to_utf8(sample->pInfo->Name); + } else { + Gtk::TreeModel::iterator iterSample = + m_refSamplesTreeModel->append(row.children()); + Gtk::TreeModel::Row rowSample = *iterSample; + rowSample[m_SamplesModel.m_col_name] = + gig_to_utf8(sample->pInfo->Name); + rowSample[m_SamplesModel.m_col_sample] = sample; + rowSample[m_SamplesModel.m_col_group] = NULL; + } // close sound file sf_close(hFile); file_changed(); @@ -2203,7 +2526,11 @@ } // show error message box when some file(s) could not be opened / added if (!error_files.empty()) { - Glib::ustring txt = _("Could not add the following sample(s):\n") + error_files; + Glib::ustring txt = + (replace + ? _("Failed to replace sample with:\n") + : _("Could not add the following sample(s):\n")) + + error_files; Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); msg.run(); } @@ -2413,6 +2740,62 @@ sizeof(script)/*length of data in bytes*/); } +// see comment on on_sample_treeview_drag_begin() +void MainWindow::on_instruments_treeview_drag_begin(const Glib::RefPtr& context) +{ + first_call_to_drag_data_get = true; +} + +void MainWindow::on_instruments_treeview_drag_data_get(const Glib::RefPtr&, + Gtk::SelectionData& selection_data, guint, guint) +{ + if (!first_call_to_drag_data_get) return; + first_call_to_drag_data_get = false; + + // get selected source instrument + gig::Instrument* src = NULL; + { + Glib::RefPtr sel = m_TreeView.get_selection(); + Gtk::TreeModel::iterator it = sel->get_selected(); + if (it) { + Gtk::TreeModel::Row row = *it; + src = row[m_Columns.m_col_instr]; + } + } + if (!src) return; + + // pass the source gig::Instrument as pointer + selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&src, + sizeof(src)/*length of data in bytes*/); +} + +void MainWindow::on_instruments_treeview_drop_drag_data_received( + const Glib::RefPtr& context, int x, int y, + const Gtk::SelectionData& selection_data, guint, guint time) +{ + gig::Instrument* src = *((gig::Instrument**) selection_data.get_data()); + if (!src || selection_data.get_length() != sizeof(gig::Instrument*)) + return; + + gig::Instrument* dst = NULL; + { + Gtk::TreeModel::Path path; + const bool found = m_TreeView.get_path_at_pos(x, y, path); + if (!found) return; + + Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path); + if (!iter) return; + Gtk::TreeModel::Row row = *iter; + dst = row[m_Columns.m_col_instr]; + } + if (!dst) return; + + //printf("dragdrop received src=%s dst=%s\n", src->pInfo->Name.c_str(), dst->pInfo->Name.c_str()); + src->MoveTo(dst); + __refreshEntireGUI(); + select_instrument(src); +} + // For some reason drag_data_get gets called two times for each // drag'n'drop (at least when target is an Entry). This work-around // makes sure the code in drag_data_get and drop_drag_data_received is @@ -2564,6 +2947,21 @@ } } +void MainWindow::script_double_clicked(const Gtk::TreeModel::Path& path, + Gtk::TreeViewColumn* column) +{ + Gtk::TreeModel::iterator iter = m_refScriptsTreeModel->get_iter(path); + if (!iter) return; + Gtk::TreeModel::Row row = *iter; + gig::Script* script = row[m_ScriptsModel.m_col_script]; + if (!script) return; + + ScriptEditor* editor = new ScriptEditor; + editor->setScript(script); + //editor->reparent(*this); + editor->show(); +} + void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter) { if (!iter) return; @@ -2620,6 +3018,9 @@ ReferencesView* d = new ReferencesView(*this); d->setSample(sample); + d->dimension_region_selected.connect( + sigc::mem_fun(*this, &MainWindow::select_dimension_region) + ); d->show_all(); d->resize(500, 400); d->run(); @@ -2693,8 +3094,27 @@ ); } - // Note: requires that this file already has a filename ! - this->file->Save(); + // Finally save gig file persistently to disk ... + //NOTE: requires that this gig file already has a filename ! + { + std::cout << "Saving file\n" << std::flush; + file_structure_to_be_changed_signal.emit(this->file); + + progress_dialog = new ProgressDialog( //FIXME: memory leak! + _("Saving") + Glib::ustring(" '") + + Glib::filename_display_basename(this->filename) + "' ...", + *this + ); + progress_dialog->show_all(); + saver = new Saver(this->file); //FIXME: memory leak! + saver->signal_progress().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_progress)); + saver->signal_finished().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_finished)); + saver->signal_error().connect( + sigc::mem_fun(*this, &MainWindow::on_saver_error)); + saver->launch(); + } } void MainWindow::on_action_merge_files() { @@ -2784,6 +3204,12 @@ Gdk::Pixbuf::create_from_xpm_data(status_detached_xpm) ); } + + { + Gtk::MenuItem* item = dynamic_cast( + uiManager->get_widget("/MenuBar/MenuSettings/SyncSamplerInstrumentSelection")); + if (item) item->set_sensitive(b); + } } void MainWindow::on_sample_ref_count_incremented(gig::Sample* sample, int offset) { @@ -2812,7 +3238,7 @@ // just in case a new sample is added later with exactly the same memory // address, which would lead to incorrect refcount if not deleted here for (std::list::const_iterator it = samples.begin(); - it != samples.end(); it != samples.end()) + it != samples.end(); ++it) { sample_ref_count.erase(*it); } @@ -2885,3 +3311,7 @@ sigc::signal& MainWindow::signal_keyboard_key_released() { return m_RegionChooser.signal_keyboard_key_released(); } + +sigc::signal& MainWindow::signal_switch_sampler_instrument() { + return switch_sampler_instrument_signal; +}