--- gigedit/trunk/src/gigedit/mainwindow.cpp 2019/02/16 19:56:56 3472 +++ gigedit/trunk/src/gigedit/mainwindow.cpp 2020/08/14 11:54:49 3811 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2019 Andreas Persson + * Copyright (C) 2006-2020 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 @@ -96,9 +96,9 @@ if (!Settings::singleton()->autoRestoreWindowDimension) { #if GTKMM_MAJOR_VERSION >= 3 - set_default_size(895, 600); + set_default_size(1010, -1); #else - set_default_size(800, 600); + set_default_size(915, -1); #endif set_position(Gtk::WIN_POS_CENTER); } @@ -106,21 +106,21 @@ add(m_VBox); // Handle selection - m_TreeView.get_selection()->signal_changed().connect( + m_TreeViewInstruments.get_selection()->signal_changed().connect( sigc::mem_fun(*this, &MainWindow::on_sel_change)); - // m_TreeView.set_reorderable(); + // m_TreeViewInstruments.set_reorderable(); #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION > 91 || (GTKMM_MINOR_VERSION == 91 && GTKMM_MICRO_VERSION >= 2))) // GTKMM >= 3.91.2 - m_TreeView.signal_button_press_event().connect( + m_TreeViewInstruments.signal_button_press_event().connect( sigc::mem_fun(*this, &MainWindow::on_button_release)); #else - m_TreeView.signal_button_press_event().connect_notify( + m_TreeViewInstruments.signal_button_press_event().connect_notify( sigc::mem_fun(*this, &MainWindow::on_button_release)); #endif // Add the TreeView tab, inside a ScrolledWindow, with the button underneath: - m_ScrolledWindow.add(m_TreeView); + m_ScrolledWindow.add(m_TreeViewInstruments); // m_ScrolledWindow.set_size_request(200, 600); m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); @@ -451,6 +451,12 @@ m_actionGroup->add_action_bool("Statusbar", sigc::mem_fun(*this, &MainWindow::on_action_view_status_bar), true); m_actionToggleRestoreWinDim = m_actionGroup->add_action_bool("AutoRestoreWinDim", sigc::mem_fun(*this, &MainWindow::on_auto_restore_win_dim), Settings::singleton()->autoRestoreWindowDimension); + m_actionInstrDoubleClickOpensProps = + m_actionGroup->add_action_bool( + "OpenInstrPropsByDoubleClick", + sigc::mem_fun(*this, &MainWindow::on_instr_double_click_opens_props), + Settings::singleton()->instrumentDoubleClickOpensProps + ); m_actionToggleShowTooltips = m_actionGroup->add_action_bool( "ShowTooltips", sigc::mem_fun(*this, &MainWindow::on_action_show_tooltips), Settings::singleton()->showTooltips @@ -478,6 +484,13 @@ *this, &MainWindow::on_auto_restore_win_dim)); toggle_action = + Gtk::ToggleAction::create("OpenInstrPropsByDoubleClick", _("Instrument Properties by Double Click")); + toggle_action->set_active(Settings::singleton()->instrumentDoubleClickOpensProps); + actionGroup->add(toggle_action, + sigc::mem_fun( + *this, &MainWindow::on_instr_double_click_opens_props)); + + toggle_action = Gtk::ToggleAction::create("ShowTooltips", _("Tooltips for Beginners")); toggle_action->set_active(Settings::singleton()->showTooltips); actionGroup->add( @@ -509,6 +522,9 @@ "DupInstrument", sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument) ); m_actionGroup->add_action( + "MoveInstrument", sigc::mem_fun(*this, &MainWindow::on_action_move_instr) + ); + m_actionGroup->add_action( "CombInstruments", sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments) ); m_actionGroup->add_action( @@ -530,6 +546,11 @@ sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument) ); actionGroup->add( + Gtk::Action::create("MoveInstrument", _("Move _Instrument To ...")), + Gtk::AccelKey(GDK_KEY_i, primaryModifierKey), + sigc::mem_fun(*this, &MainWindow::on_action_move_instr) + ); + actionGroup->add( Gtk::Action::create("CombInstruments", _("_Combine Instruments ...")), Gtk::AccelKey(GDK_KEY_j, primaryModifierKey), sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments) @@ -885,6 +906,10 @@ " Duplicate Instrument" " AppMenu.DupInstrument" " " + " " + " Move Instrument To ..." + " AppMenu.MoveInstrument" + " " " " " Combine Instrument" " AppMenu.CombInstruments" @@ -935,6 +960,10 @@ " Auto restore Window Dimensions" " AppMenu.AutoRestoreWinDim" " " + " " + " Instrument Properties by Double Click" + " AppMenu.OpenInstrPropsByDoubleClick" + " " " " "
" " " @@ -1010,6 +1039,10 @@ " Duplicate Instrument" " AppMenu.DupInstrument" " " + " " + " Move Instrument To ..." + " AppMenu.MoveInstrument" + " " " " " Combine Instruments" " AppMenu.CombInstruments" @@ -1148,6 +1181,7 @@ " " " " " " + " " " " " " " " @@ -1163,6 +1197,7 @@ " " " " " " + " " " " " " " " @@ -1186,6 +1221,7 @@ " " " " " " + " " " " " " " " @@ -1286,6 +1322,11 @@ } { Gtk::MenuItem* item = dynamic_cast( + uiManager->get_widget("/MenuBar/MenuView/OpenInstrPropsByDoubleClick")); + item->set_tooltip_text(_("If checked, double clicking an instrument opens its properties dialog.")); + } + { + 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.")); } @@ -1342,42 +1383,42 @@ // Create the Tree model: - m_refTreeModel = Gtk::ListStore::create(m_Columns); - m_refTreeModelFilter = Gtk::TreeModelFilter::create(m_refTreeModel); - m_refTreeModelFilter->set_visible_func( + m_refInstrumentsTreeModel = Gtk::ListStore::create(m_InstrumentsModel); + m_refInstrumentsModelFilter = Gtk::TreeModelFilter::create(m_refInstrumentsTreeModel); + m_refInstrumentsModelFilter->set_visible_func( sigc::mem_fun(*this, &MainWindow::instrument_row_visible) ); - m_TreeView.set_model(m_refTreeModelFilter); + m_TreeViewInstruments.set_model(m_refInstrumentsModelFilter); - m_TreeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); - m_TreeView.set_has_tooltip(true); - m_TreeView.signal_query_tooltip().connect( + m_TreeViewInstruments.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); + m_TreeViewInstruments.set_has_tooltip(true); + m_TreeViewInstruments.signal_query_tooltip().connect( sigc::mem_fun(*this, &MainWindow::onQueryTreeViewTooltip) ); - instrument_name_connection = m_refTreeModel->signal_row_changed().connect( + instrument_name_connection = m_refInstrumentsTreeModel->signal_row_changed().connect( sigc::mem_fun(*this, &MainWindow::instrument_name_changed) ); // Add the TreeView's view columns: - m_TreeView.append_column(_("Nr"), m_Columns.m_col_nr); - m_TreeView.append_column_editable(_("Instrument"), m_Columns.m_col_name); - m_TreeView.append_column(_("Scripts"), m_Columns.m_col_scripts); - m_TreeView.set_headers_visible(true); + m_TreeViewInstruments.append_column(_("Nr"), m_InstrumentsModel.m_col_nr); + m_TreeViewInstruments.append_column_editable(_("Instrument"), m_InstrumentsModel.m_col_name); + m_TreeViewInstruments.append_column(_("Scripts"), m_InstrumentsModel.m_col_scripts); + m_TreeViewInstruments.set_headers_visible(true); // 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( + m_TreeViewInstruments.drag_source_set(drag_target_instrument); + m_TreeViewInstruments.drag_dest_set(drag_target_instrument); + m_TreeViewInstruments.signal_drag_begin().connect( sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_begin) ); - m_TreeView.signal_drag_data_get().connect( + m_TreeViewInstruments.signal_drag_data_get().connect( sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drag_data_get) ); - m_TreeView.signal_drag_data_received().connect( + m_TreeViewInstruments.signal_drag_data_received().connect( sigc::mem_fun(*this, &MainWindow::on_instruments_treeview_drop_drag_data_received) ); } @@ -1484,7 +1525,9 @@ sigc::mem_fun(*this, &MainWindow::file_changed)); instrumentProps.signal_changed().connect( sigc::mem_fun(*this, &MainWindow::file_changed)); - propDialog.signal_changed().connect( + sampleProps.signal_changed().connect( + sigc::mem_fun(*this, &MainWindow::file_changed)); + fileProps.signal_changed().connect( sigc::mem_fun(*this, &MainWindow::file_changed)); midiRules.signal_changed().connect( sigc::mem_fun(*this, &MainWindow::file_changed)); @@ -1506,6 +1549,35 @@ sigc::mem_fun(*this, &MainWindow::select_sample) ); + dimreg_edit.editScriptSlotsButton.signal_clicked().connect( + sigc::mem_fun(*this, &MainWindow::show_script_slots) + ); + // simply sending the same signal (pair) to the sampler on 'patch' variable + // changes as the already existing signal (pair) when the user edits the + // script's source code, because the sampler would reload the source code + // and the 'patch' variables from the instrument on this signal anyway + dimreg_edit.scriptVars.signal_vars_to_be_changed.connect( + [this](gig::Instrument* instr) { + for (int i = 0; i < instr->ScriptSlotCount(); ++i) { + gig::Script* script = instr->GetScriptOfSlot(i); + signal_script_to_be_changed.emit(script); + } + } + ); + dimreg_edit.scriptVars.signal_vars_changed.connect( + [this](gig::Instrument* instr) { + for (int i = 0; i < instr->ScriptSlotCount(); ++i) { + gig::Script* script = instr->GetScriptOfSlot(i); + signal_script_changed.emit(script); + } + } + ); + dimreg_edit.scriptVars.signal_edit_script.connect( + [this](gig::Script* script) { + editScript(script); + } + ); + m_RegionChooser.signal_instrument_struct_to_be_changed().connect( sigc::hide( sigc::bind( @@ -1548,7 +1620,7 @@ sigc::mem_fun(*this, &MainWindow::update_dimregs)); m_searchText.signal_changed().connect( - sigc::mem_fun(*m_refTreeModelFilter.operator->(), &Gtk::TreeModelFilter::refilter) + sigc::mem_fun(*m_refInstrumentsModelFilter.operator->(), &Gtk::TreeModelFilter::refilter) ); file = 0; @@ -1792,14 +1864,21 @@ gig::Instrument* MainWindow::get_instrument() { - gig::Instrument* instrument = 0; - std::vector rows = m_TreeView.get_selection()->get_selected_rows(); + gig::Instrument* instrument = NULL; + + // get indeces of visual selection + std::vector rows = m_TreeViewInstruments.get_selection()->get_selected_rows(); if (rows.empty()) return NULL; + + // convert index of visual selection (i.e. if filtered) to index of model + Gtk::TreeModel::Path path = m_refInstrumentsModelFilter->convert_path_to_child_path(rows[0]); + if (!path) return NULL; + //NOTE: was const_iterator before, which did not compile with GTKMM4 development branch, probably going to be fixed before final GTKMM4 release though. - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]); + Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->get_iter(path); if (it) { Gtk::TreeModel::Row row = *it; - instrument = row[m_Columns.m_col_instr]; + instrument = row[m_InstrumentsModel.m_col_instr]; } return instrument; } @@ -1864,9 +1943,11 @@ { #if !USE_GTKMM_BUILDER // select item in instrument menu - std::vector rows = m_TreeView.get_selection()->get_selected_rows(); + std::vector rows = m_TreeViewInstruments.get_selection()->get_selected_rows(); if (!rows.empty()) { - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]); + // convert index of visual selection (i.e. if filtered) to index of model + Gtk::TreeModel::Path row = m_refInstrumentsModelFilter->convert_path_to_child_path(rows[0]); + Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->get_iter(row); if (it) { Gtk::TreePath path(it); int index = path[0]; @@ -1879,7 +1960,10 @@ updateScriptListOfMenu(); - m_RegionChooser.set_instrument(get_instrument()); + gig::Instrument* instr = get_instrument(); + + m_RegionChooser.set_instrument(instr); + dimreg_edit.scriptVars.setInstrument(instr, true/*force update*/); if (Settings::singleton()->syncSamplerInstrumentSelection) { switch_sampler_instrument_signal.emit(get_instrument()); @@ -2071,7 +2155,7 @@ // forget all samples that ought to be imported m_SampleImportQueue.clear(); // clear the samples and instruments tree views - m_refTreeModel->clear(); + m_refInstrumentsTreeModel->clear(); m_refSamplesTreeModel->clear(); m_refScriptsTreeModel->clear(); #if !USE_GTKMM_BUILDER @@ -2088,7 +2172,7 @@ void MainWindow::__refreshEntireGUI() { // clear the samples and instruments tree views - m_refTreeModel->clear(); + m_refInstrumentsTreeModel->clear(); m_refSamplesTreeModel->clear(); m_refScriptsTreeModel->clear(); #if !USE_GTKMM_BUILDER @@ -2205,6 +2289,7 @@ dialog.set_current_folder(current_gig_dir); } if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); std::string filename = dialog.get_filename(); printf("filename=%s\n", filename.c_str()); #ifdef GLIB_THREADS @@ -2260,10 +2345,10 @@ { if (instrument == instr) { // select item in "instruments" tree view - m_TreeView.get_selection()->select(Gtk::TreePath(ToString(i))); + m_TreeViewInstruments.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))); + m_TreeViewInstruments.scroll_to_row(Gtk::TreePath(ToString(i))); #if !USE_GTKMM_BUILDER // select item in instrument menu { @@ -2473,6 +2558,7 @@ #endif if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); std::string filename = dialog.get_filename(); if (!Glib::str_has_suffix(filename, ".gig")) { filename += ".gig"; @@ -2601,8 +2687,8 @@ void MainWindow::on_action_file_properties() { - propDialog.show(); - propDialog.deiconify(); + fileProps.show(); + fileProps.deiconify(); } void MainWindow::on_action_warn_user_on_extensions() { @@ -2626,7 +2712,7 @@ dimreg_stereo.set_has_tooltip(b); // Not doing this here, we let onQueryTreeViewTooltip() handle this per cell - //m_TreeView.set_has_tooltip(b); + //m_TreeViewInstruments.set_has_tooltip(b); m_TreeViewSamples.set_has_tooltip(b); m_TreeViewScripts.set_has_tooltip(b); @@ -2664,16 +2750,16 @@ "backup your Gigasampler/GigaStudio files before editing them with " "this application.\n" "\n" - "Please report bugs to: http://bugs.linuxsampler.org" + "Please report bugs to: https://bugs.linuxsampler.org" ); dialog.set_comments(sComment.c_str()); - dialog.set_website("http://www.linuxsampler.org"); - dialog.set_website_label("http://www.linuxsampler.org"); + dialog.set_website("https://www.linuxsampler.org"); + dialog.set_website_label("https://www.linuxsampler.org"); dialog.set_position(Gtk::WIN_POS_CENTER); dialog.run(); } -PropDialog::PropDialog() +FilePropDialog::FilePropDialog() : eFileFormat(_("File Format")), eName(_("Name")), eCreationDate(_("Creation date")), @@ -2707,7 +2793,7 @@ set_title(_("File Properties")); eName.set_width_chars(50); - connect(eFileFormat, &PropDialog::set_FileFormat); + connect(eFileFormat, &FilePropDialog::set_FileFormat); connect(eName, &DLS::Info::Name); connect(eCreationDate, &DLS::Info::CreationDate); connect(eComments, &DLS::Info::Comments); @@ -2768,7 +2854,7 @@ quitButton.set_can_default(); quitButton.grab_focus(); quitButton.signal_clicked().connect( - sigc::mem_fun(*this, &PropDialog::hide)); + sigc::mem_fun(*this, &FilePropDialog::hide)); quitButton.show(); vbox.show(); @@ -2777,7 +2863,7 @@ #endif } -void PropDialog::set_file(gig::File* file) +void FilePropDialog::set_file(gig::File* file) { m_file = file; update(file->pInfo); @@ -2803,7 +2889,7 @@ update_model--; } -void PropDialog::set_FileFormat(int value) +void FilePropDialog::set_FileFormat(int value) { m_file->pVersion->major = value; } @@ -2847,14 +2933,30 @@ eIsDrum(_("Is drum")), eMIDIBank(_("MIDI bank"), 0, 16383), eMIDIProgram(_("MIDI program")), - eAttenuation(_("Attenuation"), 0, 96, 0, 1), - eGainPlus6(_("Gain +6dB"), eAttenuation, -6), + eAttenuation(_("Attenuation (dB)"), -96, +96, 0, 1), eEffectSend(_("Effect send"), 0, 65535), eFineTune(_("Fine tune"), -8400, 8400), - ePitchbendRange(_("Pitchbend range"), 0, 48), + ePitchbendRange(_("Pitchbend range (halftones)"), 0, 48), ePianoReleaseMode(_("Piano release mode")), eDimensionKeyRangeLow(_("Keyswitching range low")), - eDimensionKeyRangeHigh(_("Keyswitching range high")) + eDimensionKeyRangeHigh(_("Keyswitching range high")), + table2(2,1), + eName2(_("Name")), + eCreationDate(_("Creation date")), + eComments(_("Comments")), + eProduct(_("Product")), + eCopyright(_("Copyright")), + eArtists(_("Artists")), + eGenre(_("Genre")), + eKeywords(_("Keywords")), + eEngineer(_("Engineer")), + eTechnician(_("Technician")), + eSoftware(_("Software")), + eMedium(_("Medium")), + eSource(_("Source")), + eSourceForm(_("Source form")), + eCommissioned(_("Commissioned")), + eSubject(_("Subject")) { if (!Settings::singleton()->autoRestoreWindowDimension) { //set_default_size(470, 390); @@ -2863,6 +2965,9 @@ set_title(_("Instrument Properties")); + tabs.append_page(vbox[1], _("Settings")); + tabs.append_page(vbox[2], _("Info")); + eDimensionKeyRangeLow.set_tip( _("start of the keyboard area which should switch the " "\"keyswitching\" dimension") @@ -2877,7 +2982,6 @@ connect(eMIDIBank, &InstrumentProps::set_MIDIBank); connect(eMIDIProgram, &InstrumentProps::set_MIDIProgram); connect(eAttenuation, &gig::Instrument::Attenuation); - connect(eGainPlus6, &gig::Instrument::Attenuation); connect(eEffectSend, &gig::Instrument::EffectSend); connect(eFineTune, &gig::Instrument::FineTune); connect(ePitchbendRange, &gig::Instrument::PitchbendRange); @@ -2887,18 +2991,64 @@ eName.signal_value_changed().connect(sig_name_changed.make_slot()); + connect(eName2, &InstrumentProps::set_Name); + connectLambda(eCreationDate, [this](gig::String s) { + m->pInfo->CreationDate = s; + }); + connectLambda(eComments, [this](gig::String s) { + m->pInfo->Comments = s; + }); + connectLambda(eProduct, [this](gig::String s) { + m->pInfo->Product = s; + }); + connectLambda(eCopyright, [this](gig::String s) { + m->pInfo->Copyright = s; + }); + connectLambda(eArtists, [this](gig::String s) { + m->pInfo->Artists = s; + }); + connectLambda(eGenre, [this](gig::String s) { + m->pInfo->Genre = s; + }); + connectLambda(eKeywords, [this](gig::String s) { + m->pInfo->Keywords = s; + }); + connectLambda(eEngineer, [this](gig::String s) { + m->pInfo->Engineer = s; + }); + connectLambda(eTechnician, [this](gig::String s) { + m->pInfo->Technician = s; + }); + connectLambda(eSoftware, [this](gig::String s) { + m->pInfo->Software = s; + }); + connectLambda(eMedium, [this](gig::String s) { + m->pInfo->Medium = s; + }); + connectLambda(eSource, [this](gig::String s) { + m->pInfo->Source = s; + }); + connectLambda(eSourceForm, [this](gig::String s) { + m->pInfo->SourceForm = s; + }); + connectLambda(eCommissioned, [this](gig::String s) { + m->pInfo->Commissioned = s; + }); + connectLambda(eSubject, [this](gig::String s) { + m->pInfo->Subject = s; + }); + + // tab 1 #if USE_GTKMM_GRID table.set_column_spacing(5); #else table.set_col_spacings(5); #endif - table.add(eName); table.add(eIsDrum); table.add(eMIDIBank); table.add(eMIDIProgram); table.add(eAttenuation); - table.add(eGainPlus6); table.add(eEffectSend); table.add(eFineTune); table.add(ePitchbendRange); @@ -2906,15 +3056,41 @@ table.add(eDimensionKeyRangeLow); table.add(eDimensionKeyRangeHigh); - add(vbox); + // tab 2 +#if USE_GTKMM_GRID + table2.set_column_spacing(5); +#else + table2.set_col_spacings(5); +#endif + table2.add(eName2); + table2.add(eCreationDate); + table2.add(eComments); + table2.add(eProduct); + table2.add(eCopyright); + table2.add(eArtists); + table2.add(eGenre); + table2.add(eKeywords); + table2.add(eEngineer); + table2.add(eTechnician); + table2.add(eSoftware); + table2.add(eMedium); + table2.add(eSource); + table2.add(eSourceForm); + table2.add(eCommissioned); + table2.add(eSubject); + + add(vbox[0]); #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) table.set_margin(5); #else table.set_border_width(5); #endif - vbox.pack_start(table); + vbox[1].pack_start(table); + vbox[2].pack_start(table2); table.show(); - vbox.pack_start(buttonBox, Gtk::PACK_SHRINK); + table2.show(); + vbox[0].pack_start(tabs); + vbox[0].pack_start(buttonBox, Gtk::PACK_SHRINK); buttonBox.set_layout(Gtk::BUTTONBOX_END); #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) buttonBox.set_margin(5); @@ -2930,7 +3106,7 @@ sigc::mem_fun(*this, &InstrumentProps::hide)); quitButton.show(); - vbox.show(); + vbox[0].show(); #if HAS_GTKMM_SHOW_ALL_CHILDREN show_all_children(); #endif @@ -2941,10 +3117,318 @@ update(instrument); update_model++; + + // tab 1 eName.set_value(instrument->pInfo->Name); eIsDrum.set_value(instrument->IsDrum); eMIDIBank.set_value(instrument->MIDIBank); eMIDIProgram.set_value(instrument->MIDIProgram); + // tab 2 + eName2.set_value(instrument->pInfo->Name); + eCreationDate.set_value(instrument->pInfo->CreationDate); + eComments.set_value(instrument->pInfo->Comments); + eProduct.set_value(instrument->pInfo->Product); + eCopyright.set_value(instrument->pInfo->Copyright); + eArtists.set_value(instrument->pInfo->Artists); + eGenre.set_value(instrument->pInfo->Genre); + eKeywords.set_value(instrument->pInfo->Keywords); + eEngineer.set_value(instrument->pInfo->Engineer); + eTechnician.set_value(instrument->pInfo->Technician); + eSoftware.set_value(instrument->pInfo->Software); + eMedium.set_value(instrument->pInfo->Medium); + eSource.set_value(instrument->pInfo->Source); + eSourceForm.set_value(instrument->pInfo->SourceForm); + eCommissioned.set_value(instrument->pInfo->Commissioned); + eSubject.set_value(instrument->pInfo->Subject); + + update_model--; +} + + +SampleProps::SampleProps() : +#if HAS_GTKMM_STOCK + quitButton(Gtk::Stock::CLOSE), +#else + quitButton(_("_Close")), +#endif + table(2,1), + eName(_("Name")), + eUnityNote(_("Unity Note")), + eSampleGroup(_("Sample Group")), + eSampleFormatInfo(_("Sample Format")), + eSampleID("Sample ID"), + eChecksum("Wave Data CRC-32"), + eLoopsCount(_("Loops"), 0, 1), // we might support more than 1 loop in future + eLoopStart(_("Loop start position"), 0, 9999999), + eLoopLength(_("Loop size"), 0, 9999999), + eLoopType(_("Loop type")), + eLoopPlayCount(_("Playback count")), + table2(2,1), + eName2(_("Name")), + eCreationDate(_("Creation date")), + eComments(_("Comments")), + eProduct(_("Product")), + eCopyright(_("Copyright")), + eArtists(_("Artists")), + eGenre(_("Genre")), + eKeywords(_("Keywords")), + eEngineer(_("Engineer")), + eTechnician(_("Technician")), + eSoftware(_("Software")), + eMedium(_("Medium")), + eSource(_("Source")), + eSourceForm(_("Source form")), + eCommissioned(_("Commissioned")), + eSubject(_("Subject")) +{ + if (!Settings::singleton()->autoRestoreWindowDimension) { + //set_default_size(470, 390); + set_position(Gtk::WIN_POS_MOUSE); + } + + set_title(_("Sample Properties")); + + tabs.append_page(vbox[1], _("Settings")); + tabs.append_page(vbox[2], _("Info")); + + connect(eName, &SampleProps::set_Name); + connect(eUnityNote, &gig::Sample::MIDIUnityNote); + connect(eLoopsCount, &gig::Sample::Loops); + connectLambda(eLoopStart, [this](uint32_t start){ + m->LoopStart = start; + m->LoopEnd = start + m->LoopSize; + }); + connectLambda(eLoopLength, [this](uint32_t length){ + m->LoopSize = length; + m->LoopEnd = m->LoopStart + length; + }); + { + const char* choices[] = { _("normal"), _("bidirectional"), _("backward"), 0 }; + static const gig::loop_type_t values[] = { + gig::loop_type_normal, + gig::loop_type_bidirectional, + gig::loop_type_backward + }; + eLoopType.set_choices(choices, values); + } + connect(eLoopType, &gig::Sample::LoopType); + connect(eLoopPlayCount, &gig::Sample::LoopPlayCount); + + eName.signal_value_changed().connect(sig_name_changed.make_slot()); + + connect(eName2, &SampleProps::set_Name); + connectLambda(eCreationDate, [this](gig::String s) { + m->pInfo->CreationDate = s; + }); + connectLambda(eComments, [this](gig::String s) { + m->pInfo->Comments = s; + }); + connectLambda(eProduct, [this](gig::String s) { + m->pInfo->Product = s; + }); + connectLambda(eCopyright, [this](gig::String s) { + m->pInfo->Copyright = s; + }); + connectLambda(eArtists, [this](gig::String s) { + m->pInfo->Artists = s; + }); + connectLambda(eGenre, [this](gig::String s) { + m->pInfo->Genre = s; + }); + connectLambda(eKeywords, [this](gig::String s) { + m->pInfo->Keywords = s; + }); + connectLambda(eEngineer, [this](gig::String s) { + m->pInfo->Engineer = s; + }); + connectLambda(eTechnician, [this](gig::String s) { + m->pInfo->Technician = s; + }); + connectLambda(eSoftware, [this](gig::String s) { + m->pInfo->Software = s; + }); + connectLambda(eMedium, [this](gig::String s) { + m->pInfo->Medium = s; + }); + connectLambda(eSource, [this](gig::String s) { + m->pInfo->Source = s; + }); + connectLambda(eSourceForm, [this](gig::String s) { + m->pInfo->SourceForm = s; + }); + connectLambda(eCommissioned, [this](gig::String s) { + m->pInfo->Commissioned = s; + }); + connectLambda(eSubject, [this](gig::String s) { + m->pInfo->Subject = s; + }); + + // tab 1 +#if USE_GTKMM_GRID + table.set_column_spacing(5); +#else + table.set_col_spacings(5); +#endif + table.add(eName); + table.add(eUnityNote); + table.add(eSampleGroup); + table.add(eSampleFormatInfo); + table.add(eSampleID); + table.add(eChecksum); + table.add(eLoopsCount); + table.add(eLoopStart); + table.add(eLoopLength); + table.add(eLoopType); + table.add(eLoopPlayCount); + + // tab 2 +#if USE_GTKMM_GRID + table2.set_column_spacing(5); +#else + table2.set_col_spacings(5); +#endif + table2.add(eName2); + table2.add(eCreationDate); + table2.add(eComments); + table2.add(eProduct); + table2.add(eCopyright); + table2.add(eArtists); + table2.add(eGenre); + table2.add(eKeywords); + table2.add(eEngineer); + table2.add(eTechnician); + table2.add(eSoftware); + table2.add(eMedium); + table2.add(eSource); + table2.add(eSourceForm); + table2.add(eCommissioned); + table2.add(eSubject); + + add(vbox[0]); +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) + table.set_margin(5); +#else + table.set_border_width(5); +#endif + vbox[1].pack_start(table); + vbox[2].pack_start(table2); + table.show(); + table2.show(); + vbox[0].pack_start(tabs); + vbox[0].pack_start(buttonBox, Gtk::PACK_SHRINK); + buttonBox.set_layout(Gtk::BUTTONBOX_END); +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) + buttonBox.set_margin(5); +#else + buttonBox.set_border_width(5); +#endif + buttonBox.show(); + buttonBox.pack_start(quitButton); + quitButton.set_can_default(); + quitButton.grab_focus(); + + quitButton.signal_clicked().connect( + sigc::mem_fun(*this, &SampleProps::hide)); + + quitButton.show(); + vbox[0].show(); +#if HAS_GTKMM_SHOW_ALL_CHILDREN + show_all_children(); +#endif +} + +void SampleProps::set_sample(gig::Sample* sample) +{ + update(sample); + + update_model++; + + // tab 1 + eName.set_value(sample->pInfo->Name); + eUnityNote.set_value(sample->MIDIUnityNote); + // show sample group name + { + Glib::ustring s = "---"; + if (sample && sample->GetGroup()) + s = sample->GetGroup()->Name; + eSampleGroup.text.set_text(s); + } + // assemble sample format info string + { + Glib::ustring s; + if (sample) { + switch (sample->Channels) { + case 1: s = _("Mono"); break; + case 2: s = _("Stereo"); break; + default: + s = ToString(sample->Channels) + _(" audio channels"); + break; + } + s += " " + ToString(sample->BitDepth) + " Bits"; + s += " " + ToString(sample->SamplesPerSecond/1000) + "." + + ToString((sample->SamplesPerSecond%1000)/100) + " kHz"; + } else { + s = _("No sample assigned to this dimension region."); + } + eSampleFormatInfo.text.set_text(s); + } + // generate sample's memory address pointer string + { + Glib::ustring s; + if (sample) { + char buf[64] = {}; + snprintf(buf, sizeof(buf), "%p", sample); + s = buf; + } else { + s = "---"; + } + eSampleID.text.set_text(s); + } + // generate raw wave form data CRC-32 checksum string + { + Glib::ustring s = "---"; + if (sample) { + char buf[64] = {}; + snprintf(buf, sizeof(buf), "%x", sample->GetWaveDataCRC32Checksum()); + s = buf; + } + eChecksum.text.set_text(s); + } + eLoopsCount.set_value(sample->Loops); + eLoopStart.set_value(sample->LoopStart); + eLoopLength.set_value(sample->LoopSize); + eLoopType.set_value(sample->LoopType); + eLoopPlayCount.set_value(sample->LoopPlayCount); + // tab 2 + eName2.set_value(sample->pInfo->Name); + eCreationDate.set_value(sample->pInfo->CreationDate); + eComments.set_value(sample->pInfo->Comments); + eProduct.set_value(sample->pInfo->Product); + eCopyright.set_value(sample->pInfo->Copyright); + eArtists.set_value(sample->pInfo->Artists); + eGenre.set_value(sample->pInfo->Genre); + eKeywords.set_value(sample->pInfo->Keywords); + eEngineer.set_value(sample->pInfo->Engineer); + eTechnician.set_value(sample->pInfo->Technician); + eSoftware.set_value(sample->pInfo->Software); + eMedium.set_value(sample->pInfo->Medium); + eSource.set_value(sample->pInfo->Source); + eSourceForm.set_value(sample->pInfo->SourceForm); + eCommissioned.set_value(sample->pInfo->Commissioned); + eSubject.set_value(sample->pInfo->Subject); + + update_model--; +} + +void SampleProps::set_Name(const gig::String& name) +{ + m->pInfo->Name = name; +} + +void SampleProps::update_name() +{ + update_model++; + eName.set_value(m->pInfo->Name); update_model--; } @@ -2980,7 +3464,7 @@ bool MainWindow::onQueryTreeViewTooltip(int x, int y, bool keyboardTip, const Glib::RefPtr& tooltip) { Gtk::TreeModel::iterator iter; - if (!m_TreeView.get_tooltip_context_iter(x, y, keyboardTip, iter)) { + if (!m_TreeViewInstruments.get_tooltip_context_iter(x, y, keyboardTip, iter)) { return false; } Gtk::TreeModel::Path path(iter); @@ -2990,13 +3474,13 @@ { Gtk::TreeModel::Path path; // unused int cellX, cellY; // unused - m_TreeView.get_path_at_pos(x, y, path, pointedColumn, cellX, cellY); + m_TreeViewInstruments.get_path_at_pos(x, y, path, pointedColumn, cellX, cellY); } - Gtk::TreeViewColumn* scriptsColumn = m_TreeView.get_column(2); + Gtk::TreeViewColumn* scriptsColumn = m_TreeViewInstruments.get_column(2); if (pointedColumn == scriptsColumn) { // mouse hovers scripts column ... // show the script(s) assigned to the hovered instrument as tooltip - tooltip->set_markup( row[m_Columns.m_col_tooltip] ); - m_TreeView.set_tooltip_cell(tooltip, &path, scriptsColumn, NULL); + tooltip->set_markup( row[m_InstrumentsModel.m_col_tooltip] ); + m_TreeViewInstruments.set_tooltip_cell(tooltip, &path, scriptsColumn, NULL); } else { // if beginners' tooltips is disabled then don't show the following one if (!Settings::singleton()->showTooltips) @@ -3006,7 +3490,7 @@ "Right click here for actions on instruments & MIDI Rules. " "Drag & drop to change the order of instruments." )); - m_TreeView.set_tooltip_cell(tooltip, &path, pointedColumn, NULL); + m_TreeViewInstruments.set_tooltip_cell(tooltip, &path, pointedColumn, NULL); } return true; } @@ -3040,7 +3524,7 @@ file_has_name = filename; file_is_changed = false; - propDialog.set_file(gig); + fileProps.set_file(gig); instrument_name_connection.block(); int index = 0; @@ -3049,13 +3533,13 @@ Glib::ustring name(gig_to_utf8(instrument->pInfo->Name)); const int iScriptSlots = instrument->ScriptSlotCount(); - Gtk::TreeModel::iterator iter = m_refTreeModel->append(); + Gtk::TreeModel::iterator iter = m_refInstrumentsTreeModel->append(); Gtk::TreeModel::Row row = *iter; - row[m_Columns.m_col_nr] = index; - row[m_Columns.m_col_name] = name; - row[m_Columns.m_col_instr] = instrument; - row[m_Columns.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; - row[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); + row[m_InstrumentsModel.m_col_nr] = index; + row[m_InstrumentsModel.m_col_name] = name; + row[m_InstrumentsModel.m_col_instr] = instrument; + row[m_InstrumentsModel.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; + row[m_InstrumentsModel.m_col_tooltip] = scriptTooltipFor(instrument, index); #if !USE_GTKMM_BUILDER add_instrument_to_menu(name); @@ -3117,7 +3601,7 @@ file = gig; // select the first instrument - m_TreeView.get_selection()->select(Gtk::TreePath("0")); + m_TreeViewInstruments.get_selection()->select(Gtk::TreePath("0")); instr_props_set_instrument(); gig::Instrument* instrument = get_instrument(); @@ -3130,16 +3614,21 @@ { instrumentProps.signal_name_changed().clear(); - std::vector rows = m_TreeView.get_selection()->get_selected_rows(); + // get visual selection + std::vector rows = m_TreeViewInstruments.get_selection()->get_selected_rows(); if (rows.empty()) { instrumentProps.hide(); return false; } + + // convert index of visual selection (i.e. if filtered) to index of model + Gtk::TreeModel::Path path = m_refInstrumentsModelFilter->convert_path_to_child_path(rows[0]); + //NOTE: was const_iterator before, which did not compile with GTKMM4 development branch, probably going to be fixed before final GTKMM4 release though. - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]); + Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->get_iter(path); if (it) { Gtk::TreeModel::Row row = *it; - gig::Instrument* instrument = row[m_Columns.m_col_instr]; + gig::Instrument* instrument = row[m_InstrumentsModel.m_col_instr]; instrumentProps.set_instrument(instrument); @@ -3168,15 +3657,69 @@ void MainWindow::instr_name_changed_by_instr_props(Gtk::TreeModel::iterator& it) { Gtk::TreeModel::Row row = *it; - Glib::ustring name = row[m_Columns.m_col_name]; + Glib::ustring name = row[m_InstrumentsModel.m_col_name]; - gig::Instrument* instrument = row[m_Columns.m_col_instr]; + gig::Instrument* instrument = row[m_InstrumentsModel.m_col_instr]; Glib::ustring gigname(gig_to_utf8(instrument->pInfo->Name)); if (gigname != name) { Gtk::TreeModel::Path path(*it); const int index = path[0]; - row[m_Columns.m_col_name] = gigname; - row[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); + row[m_InstrumentsModel.m_col_name] = gigname; + row[m_InstrumentsModel.m_col_tooltip] = scriptTooltipFor(instrument, index); + } +} + +bool MainWindow::sample_props_set_sample() +{ + sampleProps.signal_name_changed().clear(); + + std::vector rows = m_TreeViewSamples.get_selection()->get_selected_rows(); + if (rows.empty()) { + sampleProps.hide(); + return false; + } + //NOTE: was const_iterator before, which did not compile with GTKMM4 development branch, probably going to be fixed before final GTKMM4 release though. + Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]); + if (it) { + Gtk::TreeModel::Row row = *it; + gig::Sample* sample = row[m_SamplesModel.m_col_sample]; + + sampleProps.set_sample(sample); + + // make sure sample tree is updated when user changes the + // sample name in sample properties window + sampleProps.signal_name_changed().connect( + sigc::bind( + sigc::mem_fun(*this, + &MainWindow::sample_name_changed_by_sample_props + ), it + ) + ); + } else { + sampleProps.hide(); + } + //NOTE: explicit boolean cast required for GTKMM4 development branch here + return it ? true : false; +} + +void MainWindow::show_sample_props() +{ + if (sample_props_set_sample()) { + sampleProps.show(); + sampleProps.deiconify(); + } +} + +void MainWindow::sample_name_changed_by_sample_props(Gtk::TreeModel::iterator& it) +{ + Gtk::TreeModel::Row row = *it; + Glib::ustring name = row[m_SamplesModel.m_col_name]; + + gig::Sample* sample = row[m_SamplesModel.m_col_sample]; + Glib::ustring gigname(gig_to_utf8(sample->pInfo->Name)); + if (gigname != name) { + Gtk::TreeModel::Path path(*it); + row[m_SamplesModel.m_col_name] = gigname; } } @@ -3192,13 +3735,9 @@ void MainWindow::show_script_slots() { if (!file) return; + // get selected instrument - std::vector rows = m_TreeView.get_selection()->get_selected_rows(); - if (rows.empty()) return; - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]); - if (!it) return; - Gtk::TreeModel::Row row = *it; - gig::Instrument* instrument = row[m_Columns.m_col_instr]; + gig::Instrument* instrument = get_instrument(); if (!instrument) return; ScriptSlots* window = new ScriptSlots; @@ -3214,20 +3753,23 @@ if (!pInstrument) return; const int iScriptSlots = pInstrument->ScriptSlotCount(); - //NOTE: This is a big mess! Sometimes GTK requires m_TreeView.get_model(), here we need m_refTreeModelFilter->get_model(), otherwise accessing children below causes an error! - //Glib::RefPtr model = m_TreeView.get_model(); - Glib::RefPtr model = m_refTreeModelFilter->get_model(); + //NOTE: This is a big mess! Sometimes GTK requires m_TreeViewInstruments.get_model(), here we need m_refInstrumentsModelFilter->get_model(), otherwise accessing children below causes an error! + //Glib::RefPtr model = m_TreeViewInstruments.get_model(); + Glib::RefPtr model = m_refInstrumentsModelFilter->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) continue; - row[m_Columns.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; - row[m_Columns.m_col_tooltip] = scriptTooltipFor(pInstrument, i); + if (row[m_InstrumentsModel.m_col_instr] != pInstrument) continue; + row[m_InstrumentsModel.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; + row[m_InstrumentsModel.m_col_tooltip] = scriptTooltipFor(pInstrument, i); break; } // causes the sampler to reload the instrument with the new script on_sel_change(); + + // force script 'patch' variables editor ("Script" tab) to be refreshed + dimreg_edit.scriptVars.setInstrument(pInstrument, true/*force update*/); } void MainWindow::assignScript(gig::Script* pScript) { @@ -3308,6 +3850,25 @@ #endif } +void MainWindow::on_instr_double_click_opens_props() { +#if USE_GLIB_ACTION + bool active = false; + m_actionInstrDoubleClickOpensProps->get_state(active); + // for some reason toggle state does not change automatically + active = !active; + m_actionInstrDoubleClickOpensProps->change_state(active); + Settings::singleton()->instrumentDoubleClickOpensProps = active; +#else + Gtk::CheckMenuItem* item = + dynamic_cast(uiManager->get_widget("/MenuBar/MenuView/OpenInstrPropsByDoubleClick")); + if (!item) { + std::cerr << "/MenuBar/MenuView/OpenInstrPropsByDoubleClick == NULL\n"; + return; + } + Settings::singleton()->instrumentDoubleClickOpensProps = item->get_active(); +#endif +} + void MainWindow::on_save_with_temporary_file() { #if USE_GLIB_ACTION bool active = false; @@ -3382,7 +3943,8 @@ void MainWindow::on_button_release(GdkEventButton* button) { #endif if (button->type == GDK_2BUTTON_PRESS) { - show_instr_props(); + if (Settings::singleton()->instrumentDoubleClickOpensProps) + show_instr_props(); } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) { // gig v2 files have no midi rules const bool bEnabled = !(file->pVersion && file->pVersion->major == 2); @@ -3414,7 +3976,7 @@ find(children.begin(), children.end(), item); if (it != children.end()) { int index = it - children.begin(); - m_TreeView.get_selection()->select(Gtk::TreePath(ToString(index))); + m_TreeViewInstruments.get_selection()->select(Gtk::TreePath(ToString(index))); m_RegionChooser.set_instrument(file->GetInstrument(index)); } @@ -3422,30 +3984,90 @@ } #endif +void MainWindow::on_action_move_instr() { + gig::Instrument* instr = get_instrument(); + if (!instr) return; + + int currentIndex = getIndexOf(instr); + + Gtk::Dialog dialog(_("Move Instrument"), true /*modal*/); +#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 + Gtk::Adjustment adjustment( + currentIndex, + 0 /*min*/, file->CountInstruments() - 1 /*max*/ + ); + Gtk::SpinButton spinBox(adjustment); +#else + Gtk::SpinButton spinBox( + Gtk::Adjustment::create( + currentIndex, + 0 /*min*/, file->CountInstruments() - 1 /*max*/ + ) + ); +#endif +#if USE_GTKMM_BOX + dialog.get_content_area()->pack_start(spinBox); +#else + dialog.get_vbox()->pack_start(spinBox); +#endif +#if HAS_GTKMM_STOCK + Gtk::Button* okButton = dialog.add_button(Gtk::Stock::OK, 0); + dialog.add_button(Gtk::Stock::CANCEL, 1); +#else + Gtk::Button* okButton = dialog.add_button(_("_OK"), 0); + dialog.add_button(_("_Cancel"), 1); +#endif + okButton->set_sensitive(false); + // show the dialog at a reasonable screen position + dialog.set_position(Gtk::WIN_POS_MOUSE); + // only enable the 'OK' button if entered new index is not instrument's + // current index already + spinBox.signal_value_changed().connect([&]{ + okButton->set_sensitive( spinBox.get_value_as_int() != currentIndex ); + }); + // usability acceleration: if user hits enter key on the text entry field + // then auto trigger the 'OK' button + spinBox.signal_activate().connect([&]{ + if (okButton->get_sensitive()) + okButton->clicked(); + }); +#if HAS_GTKMM_SHOW_ALL_CHILDREN + dialog.show_all_children(); +#endif + if (!dialog.run()) { // 'OK' selected ... + int newIndex = spinBox.get_value_as_int(); + printf("MOVE TO %d\n", newIndex); + gig::Instrument* dst = file->GetInstrument(newIndex); + instr->MoveTo(dst); + __refreshEntireGUI(); + select_instrument(instr); + } +} + void MainWindow::select_instrument(gig::Instrument* instrument) { if (!instrument) return; - //NOTE: This is a big mess! Sometimes GTK requires m_refTreeModelFilter->get_model(), here we need m_TreeView.get_model(), otherwise treeview selection below causes an error! - Glib::RefPtr model = m_TreeView.get_model(); - //Glib::RefPtr model = m_refTreeModelFilter->get_model(); + //NOTE: This is a big mess! Sometimes GTK requires m_refInstrumentsModelFilter->get_model(), here we need m_TreeViewInstruments.get_model(), otherwise treeview selection below causes an error! + Glib::RefPtr model = m_TreeViewInstruments.get_model(); + //Glib::RefPtr model = m_refInstrumentsModelFilter->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) { + if (row[m_InstrumentsModel.m_col_instr] == instrument) { // select and show the respective instrument in the list view show_intruments_tab(); - m_TreeView.get_selection()->unselect_all(); + m_TreeViewInstruments.get_selection()->unselect_all(); #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) auto iterSel = model->children()[i].get_iter(); - m_TreeView.get_selection()->select(iterSel); + m_TreeViewInstruments.get_selection()->select(iterSel); #else - m_TreeView.get_selection()->select(model->children()[i]); + m_TreeViewInstruments.get_selection()->select(model->children()[i]); #endif std::vector rows = - m_TreeView.get_selection()->get_selected_rows(); + m_TreeViewInstruments.get_selection()->get_selected_rows(); if (!rows.empty()) - m_TreeView.scroll_to_row(rows[0]); + m_TreeViewInstruments.scroll_to_row(rows[0]); on_sel_change(); // the regular instrument selection change callback } } @@ -3456,26 +4078,26 @@ gig::Region* pRegion = (gig::Region*) dimRgn->GetParent(); gig::Instrument* pInstrument = (gig::Instrument*) pRegion->GetParent(); - //NOTE: This is a big mess! Sometimes GTK requires m_refTreeModelFilter->get_model(), here we need m_TreeView.get_model(), otherwise treeview selection below causes an error! - Glib::RefPtr model = m_TreeView.get_model(); - //Glib::RefPtr model = m_refTreeModelFilter->get_model(); + //NOTE: This is a big mess! Sometimes GTK requires m_refInstrumentsModelFilter->get_model(), here we need m_TreeViewInstruments.get_model(), otherwise treeview selection below causes an error! + Glib::RefPtr model = m_TreeViewInstruments.get_model(); + //Glib::RefPtr model = m_refInstrumentsModelFilter->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) { + if (row[m_InstrumentsModel.m_col_instr] == pInstrument) { // select and show the respective instrument in the list view show_intruments_tab(); - m_TreeView.get_selection()->unselect_all(); + m_TreeViewInstruments.get_selection()->unselect_all(); #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) auto iterSel = model->children()[i].get_iter(); - m_TreeView.get_selection()->select(iterSel); + m_TreeViewInstruments.get_selection()->select(iterSel); #else - m_TreeView.get_selection()->select(model->children()[i]); + m_TreeViewInstruments.get_selection()->select(model->children()[i]); #endif std::vector rows = - m_TreeView.get_selection()->get_selected_rows(); + m_TreeViewInstruments.get_selection()->get_selected_rows(); if (!rows.empty()) - m_TreeView.scroll_to_row(rows[0]); + m_TreeViewInstruments.scroll_to_row(rows[0]); on_sel_change(); // the regular instrument selection change callback // select respective region in the region selector @@ -3760,14 +4382,15 @@ // update instrument tree view instrument_name_connection.block(); - Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append(); + Gtk::TreeModel::iterator iterInstr = m_refInstrumentsTreeModel->append(); Gtk::TreeModel::Row rowInstr = *iterInstr; - const int index = m_refTreeModel->children().size() - 1; - rowInstr[m_Columns.m_col_nr] = index; - rowInstr[m_Columns.m_col_name] = name; - rowInstr[m_Columns.m_col_instr] = instrument; - rowInstr[m_Columns.m_col_scripts] = ""; - rowInstr[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); + const int index = m_refInstrumentsTreeModel->children().size() - 1; + const int iScriptSlots = instrument->ScriptSlotCount(); + rowInstr[m_InstrumentsModel.m_col_nr] = index; + rowInstr[m_InstrumentsModel.m_col_name] = name; + rowInstr[m_InstrumentsModel.m_col_instr] = instrument; + rowInstr[m_InstrumentsModel.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; + rowInstr[m_InstrumentsModel.m_col_tooltip] = scriptTooltipFor(instrument, index); instrument_name_connection.unblock(); #if !USE_GTKMM_BUILDER @@ -3793,13 +4416,15 @@ // retrieve the currently selected instrument // (being the original instrument to be duplicated) - Glib::RefPtr sel = m_TreeView.get_selection(); + Glib::RefPtr sel = m_TreeViewInstruments.get_selection(); std::vector rows = sel->get_selected_rows(); for (int r = 0; r < rows.size(); ++r) { - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]); + // convert index of visual selection (i.e. if filtered) to index of model + Gtk::TreeModel::Path path = m_refInstrumentsModelFilter->convert_path_to_child_path(rows[r]); + Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->get_iter(path); if (it) { Gtk::TreeModel::Row row = *it; - gig::Instrument* instrOrig = row[m_Columns.m_col_instr]; + gig::Instrument* instrOrig = row[m_InstrumentsModel.m_col_instr]; if (instrOrig) { // duplicate the orginal instrument gig::Instrument* instrNew = file->AddDuplicateInstrument(instrOrig); @@ -3826,13 +4451,13 @@ return; } - Glib::RefPtr sel = m_TreeView.get_selection(); + Glib::RefPtr sel = m_TreeViewInstruments.get_selection(); std::vector rows = sel->get_selected_rows(); for (int r = rows.size() - 1; r >= 0; --r) { - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]); + Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->get_iter(rows[r]); if (!it) continue; Gtk::TreeModel::Row row = *it; - gig::Instrument* instr = row[m_Columns.m_col_instr]; + gig::Instrument* instr = row[m_InstrumentsModel.m_col_instr]; try { Gtk::TreePath path(it); int index = path[0]; @@ -3846,28 +4471,28 @@ #endif // remove row from instruments tree view - m_refTreeModel->erase(it); + m_refInstrumentsTreeModel->erase(it); // update "Nr" column of all instrument rows { int index = 0; - for (Gtk::TreeModel::iterator it = m_refTreeModel->children().begin(); - it != m_refTreeModel->children().end(); ++it, ++index) + for (Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->children().begin(); + it != m_refInstrumentsTreeModel->children().end(); ++it, ++index) { Gtk::TreeModel::Row row = *it; - gig::Instrument* instrument = row[m_Columns.m_col_instr]; - row[m_Columns.m_col_nr] = index; - row[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); + gig::Instrument* instrument = row[m_InstrumentsModel.m_col_instr]; + row[m_InstrumentsModel.m_col_nr] = index; + row[m_InstrumentsModel.m_col_tooltip] = scriptTooltipFor(instrument, index); } } #if GTKMM_MAJOR_VERSION < 3 // select another instrument (in gtk3 this is done // automatically) - if (!m_refTreeModel->children().empty()) { - if (index == m_refTreeModel->children().size()) { + if (!m_refInstrumentsTreeModel->children().empty()) { + if (index == m_refInstrumentsTreeModel->children().size()) { index--; } - m_TreeView.get_selection()->select( + m_TreeViewInstruments.get_selection()->select( Gtk::TreePath(ToString(index))); } #endif @@ -3886,11 +4511,7 @@ } void MainWindow::on_action_sample_properties() { - //TODO: show a dialog where the selected sample's properties can be edited - Gtk::MessageDialog msg( - *this, _("Sorry, yet to be implemented!"), false, Gtk::MESSAGE_INFO - ); - msg.run(); + show_sample_props(); } void MainWindow::on_action_add_script_group() { @@ -3953,15 +4574,22 @@ if (!it) return; Gtk::TreeModel::Row row = *it; gig::Script* script = row[m_ScriptsModel.m_col_script]; - if (!script) return; + editScript(script); +} +void MainWindow::editScript(gig::Script* script) { + if (!script) return; ScriptEditor* editor = new ScriptEditor; editor->signal_script_to_be_changed.connect( signal_script_to_be_changed.make_slot() ); - editor->signal_script_changed.connect( - signal_script_changed.make_slot() - ); + editor->signal_script_changed.connect([this](gig::Script* script) { + // signal to sampler (which will reload the script due to this) + signal_script_changed.emit(script); + // force script 'patch' variables editor ("Script" tab) to be refreshed + gig::Instrument* instr = get_instrument(); + dimreg_edit.scriptVars.setInstrument(instr, true/*force update*/); + }); editor->setScript(script); //editor->reparent(*this); editor->show(); @@ -4109,6 +4737,7 @@ dialog.set_current_folder(current_sample_dir); } if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); current_sample_dir = dialog.get_current_folder(); Glib::ustring error_files; std::vector filenames = dialog.get_filenames(); @@ -4289,6 +4918,7 @@ } if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); current_sample_dir = dialog.get_current_folder(); Glib::ustring error_files; std::string folder = dialog.get_filename(); @@ -4514,13 +5144,13 @@ // get selected source instrument gig::Instrument* src = NULL; { - Glib::RefPtr sel = m_TreeView.get_selection(); + Glib::RefPtr sel = m_TreeViewInstruments.get_selection(); std::vector rows = sel->get_selected_rows(); if (!rows.empty()) { - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]); + Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->get_iter(rows[0]); if (it) { Gtk::TreeModel::Row row = *it; - src = row[m_Columns.m_col_instr]; + src = row[m_InstrumentsModel.m_col_instr]; } } } @@ -4542,13 +5172,13 @@ gig::Instrument* dst = NULL; { Gtk::TreeModel::Path path; - const bool found = m_TreeView.get_path_at_pos(x, y, path); + const bool found = m_TreeViewInstruments.get_path_at_pos(x, y, path); if (!found) return; - Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path); + Gtk::TreeModel::iterator iter = m_refInstrumentsTreeModel->get_iter(path); if (!iter) return; Gtk::TreeModel::Row row = *iter; - dst = row[m_Columns.m_col_instr]; + dst = row[m_InstrumentsModel.m_col_instr]; } if (!dst) return; @@ -4687,6 +5317,10 @@ file_changed(); } } + // change name in the sample properties window + if (sampleProps.get_sample() == sample && sample) { + sampleProps.set_sample(sample); + } } void MainWindow::script_name_changed(const Gtk::TreeModel::Path& path, @@ -4719,25 +5353,14 @@ 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->signal_script_to_be_changed.connect( - signal_script_to_be_changed.make_slot() - ); - editor->signal_script_changed.connect( - signal_script_changed.make_slot() - ); - editor->setScript(script); - //editor->reparent(*this); - editor->show(); + editScript(script); } void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter) { if (!iter) return; Gtk::TreeModel::Row row = *iter; - Glib::ustring name = row[m_Columns.m_col_name]; + Glib::ustring name = row[m_InstrumentsModel.m_col_name]; #if !USE_GTKMM_BUILDER // change name in instrument menu @@ -4755,7 +5378,7 @@ #endif // change name in gig - gig::Instrument* instrument = row[m_Columns.m_col_instr]; + gig::Instrument* instrument = row[m_InstrumentsModel.m_col_instr]; gig::String gigname(gig_from_utf8(name)); if (instrument && instrument->pInfo->Name != gigname) { instrument->pInfo->Name = gigname; @@ -4783,7 +5406,7 @@ #else Gtk::TreeModel::Row row = *iter; #endif - Glib::ustring name = row[m_Columns.m_col_name]; + Glib::ustring name = row[m_InstrumentsModel.m_col_name]; name = name.lowercase(); std::vector tokens = Glib::Regex::split_simple(" ", pattern); @@ -4801,13 +5424,13 @@ // list view as pre-selection std::set indeces; { - Glib::RefPtr sel = m_TreeView.get_selection(); + Glib::RefPtr sel = m_TreeViewInstruments.get_selection(); std::vector rows = sel->get_selected_rows(); for (int r = 0; r < rows.size(); ++r) { - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]); + Gtk::TreeModel::iterator it = m_refInstrumentsTreeModel->get_iter(rows[r]); if (it) { Gtk::TreeModel::Row row = *it; - int index = row[m_Columns.m_col_nr]; + int index = row[m_InstrumentsModel.m_col_nr]; indeces.insert(index); } } @@ -5017,6 +5640,7 @@ #endif if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); #ifdef GLIB_THREADS printf("on_action_merge_files self=%p\n", static_cast(Glib::Threads::Thread::self()));