--- gigedit/trunk/src/gigedit/mainwindow.cpp 2016/04/15 13:45:12 2877 +++ gigedit/trunk/src/gigedit/mainwindow.cpp 2017/02/12 16:35:03 3109 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2016 Andreas Persson + * Copyright (C) 2006-2017 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 @@ -57,19 +57,25 @@ #include "ReferencesView.h" #include "../../gfx/status_attached.xpm" #include "../../gfx/status_detached.xpm" - +#include "gfx/builtinpix.h" MainWindow::MainWindow() : m_DimRegionChooser(*this), dimreg_label(_("Changes apply to:")), dimreg_all_regions(_("all regions")), dimreg_all_dimregs(_("all dimension splits")), - dimreg_stereo(_("both channels")) + dimreg_stereo(_("both channels")), + labelLegend(_("Legend:")), + labelNoSample(_(" No Sample")), + labelMissingSample(_(" Missing some Sample(s)")), + labelLooped(_(" Looped")), + labelSomeLoops(_(" Some Loop(s)")) { + loadBuiltInPix(); + // set_border_width(5); // set_default_size(400, 200); - add(m_VBox); // Handle selection @@ -103,6 +109,36 @@ dimreg_hbox.add(dimreg_stereo); dimreg_vbox.add(dimreg_edit); dimreg_vbox.pack_start(dimreg_hbox, Gtk::PACK_SHRINK); + { + legend_hbox.add(labelLegend); + + imageNoSample.set(redDot); + imageNoSample.set_alignment(Gtk::ALIGN_END); + labelNoSample.set_alignment(Gtk::ALIGN_START); + legend_hbox.add(imageNoSample); + legend_hbox.add(labelNoSample); + + imageMissingSample.set(yellowDot); + imageMissingSample.set_alignment(Gtk::ALIGN_END); + labelMissingSample.set_alignment(Gtk::ALIGN_START); + legend_hbox.add(imageMissingSample); + legend_hbox.add(labelMissingSample); + + imageLooped.set(blackLoop); + imageLooped.set_alignment(Gtk::ALIGN_END); + labelLooped.set_alignment(Gtk::ALIGN_START); + legend_hbox.add(imageLooped); + legend_hbox.add(labelLooped); + + imageSomeLoops.set(grayLoop); + imageSomeLoops.set_alignment(Gtk::ALIGN_END); + labelSomeLoops.set_alignment(Gtk::ALIGN_START); + legend_hbox.add(imageSomeLoops); + legend_hbox.add(labelSomeLoops); + + legend_hbox.show_all_children(); + } + dimreg_vbox.pack_start(legend_hbox, Gtk::PACK_SHRINK); m_HPaned.add2(dimreg_vbox); dimreg_label.set_tooltip_text(_("To automatically apply your changes above globally to the entire instrument, check all 3 check boxes on the right.")); @@ -192,6 +228,21 @@ actionGroup->add(toggle_action, sigc::mem_fun( *this, &MainWindow::on_action_view_status_bar)); + + toggle_action = + Gtk::ToggleAction::create("AutoRestoreWinDim", _("_Auto Restore Window Dimension")); + toggle_action->set_active(Settings::singleton()->autoRestoreWindowDimension); + actionGroup->add(toggle_action, + sigc::mem_fun( + *this, &MainWindow::on_auto_restore_win_dim)); + + toggle_action = + Gtk::ToggleAction::create("SaveWithTemporaryFile", _("Save with _temporary file")); + toggle_action->set_active(Settings::singleton()->saveWithTemporaryFile); + actionGroup->add(toggle_action, + sigc::mem_fun( + *this, &MainWindow::on_save_with_temporary_file)); + actionGroup->add( Gtk::Action::create("RefreshAll", _("_Refresh All")), sigc::mem_fun(*this, &MainWindow::on_action_refresh_all) @@ -364,6 +415,7 @@ " " " " " " + " " " " " " " " @@ -375,6 +427,7 @@ " " " " " " + " " " " " " " " @@ -462,6 +515,11 @@ } { Gtk::MenuItem* item = dynamic_cast( + uiManager->get_widget("/MenuBar/MenuView/AutoRestoreWinDim")); + item->set_tooltip_text(_("If checked, size and position of all windows will be saved and automatically restored next time.")); + } + { + 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.")); } @@ -499,14 +557,16 @@ // Create the Tree model: m_refTreeModel = Gtk::ListStore::create(m_Columns); m_TreeView.set_model(m_refTreeModel); + m_TreeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); 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) ); // Add the TreeView's view columns: - m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name); - m_TreeView.set_headers_visible(false); + m_TreeView.append_column(_("Nr"), m_Columns.m_col_nr); + m_TreeView.append_column_editable(_("Instrument"), m_Columns.m_col_name); + m_TreeView.set_headers_visible(true); // establish drag&drop within the instrument tree view, allowing to reorder // the sequence of instruments within the gig file @@ -529,6 +589,7 @@ // create samples treeview (including its data model) m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel); m_TreeViewSamples.set_model(m_refSamplesTreeModel); + m_TreeViewSamples.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); m_TreeViewSamples.set_tooltip_text(_("To actually use a sample, drag it from this list view to \"Sample\" -> \"Sample:\" on the region's settings pane on the right.\n\nRight click here for more actions on samples.")); // m_TreeViewSamples.set_reorderable(); m_TreeViewSamples.append_column_editable(_("Name"), m_SamplesModel.m_col_name); @@ -706,8 +767,9 @@ gig::Instrument* MainWindow::get_instrument() { gig::Instrument* instrument = 0; - Gtk::TreeModel::const_iterator it = - m_TreeView.get_selection()->get_selected(); + std::vector rows = m_TreeView.get_selection()->get_selected_rows(); + if (rows.empty()) return NULL; + Gtk::TreeModel::const_iterator it = m_refTreeModel->get_iter(rows[0]); if (it) { Gtk::TreeModel::Row row = *it; instrument = row[m_Columns.m_col_instr]; @@ -750,6 +812,10 @@ add_region_to_dimregs(region, stereo, all_dimregs); } } + + m_DimRegionChooser.setModifyAllRegions(all_regions); + m_DimRegionChooser.setModifyAllDimensionRegions(all_dimregs); + m_DimRegionChooser.setModifyBothChannels(stereo); } void MainWindow::dimreg_all_dimregs_toggled() @@ -767,13 +833,16 @@ void MainWindow::on_sel_change() { // select item in instrument menu - Gtk::TreeModel::iterator it = m_TreeView.get_selection()->get_selected(); - if (it) { - Gtk::TreePath path(it); - int index = path[0]; - const std::vector children = - instrument_menu->get_children(); - static_cast(children[index])->set_active(); + std::vector rows = m_TreeView.get_selection()->get_selected_rows(); + if (!rows.empty()) { + Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]); + if (it) { + Gtk::TreePath path(it); + int index = path[0]; + const std::vector children = + instrument_menu->get_children(); + static_cast(children[index])->set_active(); + } } m_RegionChooser.set_instrument(get_instrument()); @@ -798,6 +867,10 @@ progress_dispatcher(); } +#if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) +// make sure stack is 16-byte aligned for SSE instructions +__attribute__((force_align_arg_pointer)) +#endif void Loader::thread_function() { printf("thread_function self=%p\n", @@ -877,6 +950,10 @@ progress_dispatcher.emit(); } +#if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) +// make sure stack is 16-byte aligned for SSE instructions +__attribute__((force_align_arg_pointer)) +#endif void Saver::thread_function() { printf("thread_function self=%p\n", @@ -889,7 +966,34 @@ // if no filename was provided, that means "save", if filename was provided means "save as" if (filename.empty()) { - gig->Save(&progress); + if (!Settings::singleton()->saveWithTemporaryFile) { + // save directly over the existing .gig file + // (requires less disk space than solution below + // but may be slower) + gig->Save(&progress); + } else { + // save the file as separate temporary file first, + // then move the saved file over the old file + // (may result in performance speedup during save) + String tmpname = filename + ".TMP"; + gig->Save(tmpname, &progress); + #if defined(WIN32) + if (!DeleteFile(filename.c_str())) { + throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file)."); + } + #else // POSIX ... + if (unlink(filename.c_str())) { + throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file): " + String(strerror(errno))); + } + #endif + if (rename(tmpname.c_str(), filename.c_str())) { + #if defined(WIN32) + throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file)."); + #else + throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file): " + String(strerror(errno))); + #endif + } + } } else { gig->Save(filename, &progress); } @@ -1463,7 +1567,7 @@ dialog.set_name("Gigedit"); #endif dialog.set_version(VERSION); - dialog.set_copyright("Copyright (C) 2006-2016 Andreas Persson"); + dialog.set_copyright("Copyright (C) 2006-2017 Andreas Persson"); const std::string sComment = _("Built " __DATE__ "\nUsing ") + ::gig::libraryName() + " " + ::gig::libraryVersion() + "\n\n" + @@ -1758,12 +1862,14 @@ propDialog.set_info(gig->pInfo); instrument_name_connection.block(); + int index = 0; for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ; - instrument = gig->GetNextInstrument()) { + instrument = gig->GetNextInstrument(), ++index) { Glib::ustring name(gig_to_utf8(instrument->pInfo->Name)); Gtk::TreeModel::iterator iter = m_refTreeModel->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; @@ -1836,8 +1942,12 @@ { instrumentProps.signal_name_changed().clear(); - Gtk::TreeModel::const_iterator it = - m_TreeView.get_selection()->get_selected(); + std::vector rows = m_TreeView.get_selection()->get_selected_rows(); + if (rows.empty()) { + instrumentProps.hide(); + return false; + } + Gtk::TreeModel::const_iterator it = m_refTreeModel->get_iter(rows[0]); if (it) { Gtk::TreeModel::Row row = *it; gig::Instrument* instrument = row[m_Columns.m_col_instr]; @@ -1890,8 +2000,9 @@ void MainWindow::show_script_slots() { if (!file) return; // get selected instrument - Glib::RefPtr sel = m_TreeView.get_selection(); - Gtk::TreeModel::iterator it = sel->get_selected(); + 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]; @@ -1918,6 +2029,26 @@ else m_StatusBar.hide(); } +void MainWindow::on_auto_restore_win_dim() { + Gtk::CheckMenuItem* item = + dynamic_cast(uiManager->get_widget("/MenuBar/MenuView/AutoRestoreWinDim")); + if (!item) { + std::cerr << "/MenuBar/MenuView/AutoRestoreWinDim == NULL\n"; + return; + } + Settings::singleton()->autoRestoreWindowDimension = item->get_active(); +} + +void MainWindow::on_save_with_temporary_file() { + Gtk::CheckMenuItem* item = + dynamic_cast(uiManager->get_widget("/MenuBar/MenuSettings/SaveWithTemporaryFile")); + if (!item) { + std::cerr << "/MenuBar/MenuSettings/SaveWithTemporaryFile == NULL\n"; + return; + } + Settings::singleton()->saveWithTemporaryFile = item->get_active(); +} + bool MainWindow::is_copy_samples_unity_note_enabled() const { Gtk::CheckMenuItem* item = dynamic_cast(uiManager->get_widget("/MenuBar/MenuEdit/CopySampleUnity")); @@ -1991,11 +2122,12 @@ 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()->unselect_all(); m_TreeView.get_selection()->select(model->children()[i]); - Gtk::TreePath path( - m_TreeView.get_selection()->get_selected() - ); - m_TreeView.scroll_to_row(path); + std::vector rows = + m_TreeView.get_selection()->get_selected_rows(); + if (!rows.empty()) + m_TreeView.scroll_to_row(rows[0]); on_sel_change(); // the regular instrument selection change callback } } @@ -2012,11 +2144,12 @@ 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()->unselect_all(); m_TreeView.get_selection()->select(model->children()[i]); - Gtk::TreePath path( - m_TreeView.get_selection()->get_selected() - ); - m_TreeView.scroll_to_row(path); + std::vector rows = + m_TreeView.get_selection()->get_selected_rows(); + if (!rows.empty()) + m_TreeView.scroll_to_row(rows[0]); on_sel_change(); // the regular instrument selection change callback // select respective region in the region selector @@ -2042,11 +2175,12 @@ Gtk::TreeModel::Row rowSample = rowGroup.children()[s]; if (rowSample[m_SamplesModel.m_col_sample] == sample) { show_samples_tab(); + m_TreeViewSamples.get_selection()->unselect_all(); m_TreeViewSamples.get_selection()->select(rowGroup.children()[s]); - Gtk::TreePath path( - m_TreeViewSamples.get_selection()->get_selected() - ); - m_TreeViewSamples.scroll_to_row(path); + std::vector rows = + m_TreeViewSamples.get_selection()->get_selected_rows(); + if (rows.empty()) return; + m_TreeViewSamples.scroll_to_row(rows[0]); return; } } @@ -2055,43 +2189,64 @@ void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) { if (button->type == GDK_BUTTON_PRESS && button->button == 3) { + // by default if Ctrl keys is pressed down, then a mouse right-click + // does not select the respective row, so we must assure this + // programmatically ... + /*{ + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* pColumn = NULL; + int cellX, cellY; + bool bSuccess = m_TreeViewSamples.get_path_at_pos( + (int)button->x, (int)button->y, + path, pColumn, cellX, cellY + ); + if (bSuccess) { + if (m_TreeViewSamples.get_selection()->count_selected_rows() <= 0) { + printf("not selected !!!\n"); + m_TreeViewSamples.get_selection()->select(path); + } + } + }*/ + Gtk::Menu* sample_popup = dynamic_cast(uiManager->get_widget("/SamplePopupMenu")); // update enabled/disabled state of sample popup items Glib::RefPtr sel = m_TreeViewSamples.get_selection(); - Gtk::TreeModel::iterator it = sel->get_selected(); - bool group_selected = false; - bool sample_selected = false; - if (it) { + std::vector rows = sel->get_selected_rows(); + const int n = rows.size(); + int nGroups = 0; + int nSamples = 0; + for (int r = 0; r < n; ++r) { + Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[r]); + if (!it) continue; Gtk::TreeModel::Row row = *it; - group_selected = row[m_SamplesModel.m_col_group]; - sample_selected = row[m_SamplesModel.m_col_sample]; + if (row[m_SamplesModel.m_col_group]) nGroups++; + if (row[m_SamplesModel.m_col_sample]) nSamples++; } - - + dynamic_cast(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))-> - set_sensitive(group_selected || sample_selected); + set_sensitive(n == 1); dynamic_cast(uiManager->get_widget("/SamplePopupMenu/AddSample"))-> - set_sensitive(group_selected || sample_selected); + set_sensitive(n); dynamic_cast(uiManager->get_widget("/SamplePopupMenu/AddGroup"))-> set_sensitive(file); dynamic_cast(uiManager->get_widget("/SamplePopupMenu/ShowSampleRefs"))-> - set_sensitive(sample_selected); + set_sensitive(nSamples == 1); dynamic_cast(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))-> - set_sensitive(group_selected || sample_selected); + set_sensitive(n); // show sample popup sample_popup->popup(button->button, button->time); dynamic_cast(uiManager->get_widget("/MenuBar/MenuSample/SampleProperties"))-> - set_sensitive(group_selected || sample_selected); + set_sensitive(n == 1); dynamic_cast(uiManager->get_widget("/MenuBar/MenuSample/AddSample"))-> - set_sensitive(group_selected || sample_selected); + set_sensitive(n); dynamic_cast(uiManager->get_widget("/MenuBar/MenuSample/AddGroup"))-> set_sensitive(file); dynamic_cast(uiManager->get_widget("/MenuBar/MenuSample/ShowSampleRefs"))-> - set_sensitive(sample_selected); + set_sensitive(nSamples == 1); dynamic_cast(uiManager->get_widget("/MenuBar/MenuSample/RemoveSample"))-> - set_sensitive(group_selected || sample_selected); + set_sensitive(n); } } @@ -2170,6 +2325,7 @@ instrument_name_connection.block(); Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append(); Gtk::TreeModel::Row rowInstr = *iterInstr; + rowInstr[m_Columns.m_col_nr] = m_refTreeModel->children().size() - 1; rowInstr[m_Columns.m_col_name] = name; rowInstr[m_Columns.m_col_instr] = instrument; instrument_name_connection.unblock(); @@ -2198,19 +2354,23 @@ // retrieve the currently selected instrument // (being the original instrument to be duplicated) Glib::RefPtr sel = m_TreeView.get_selection(); - Gtk::TreeModel::iterator itSelection = sel->get_selected(); - if (!itSelection) return; - Gtk::TreeModel::Row row = *itSelection; - gig::Instrument* instrOrig = row[m_Columns.m_col_instr]; - if (!instrOrig) return; - - // duplicate the orginal instrument - gig::Instrument* instrNew = file->AddDuplicateInstrument(instrOrig); - instrNew->pInfo->Name = - instrOrig->pInfo->Name + - gig_from_utf8(Glib::ustring(" (") + _("Copy") + ")"); + 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]); + if (it) { + Gtk::TreeModel::Row row = *it; + gig::Instrument* instrOrig = row[m_Columns.m_col_instr]; + if (instrOrig) { + // duplicate the orginal instrument + gig::Instrument* instrNew = file->AddDuplicateInstrument(instrOrig); + instrNew->pInfo->Name = + instrOrig->pInfo->Name + + gig_from_utf8(Glib::ustring(" (") + _("Copy") + ")"); - add_instrument(instrNew); + add_instrument(instrNew); + } + } + } } void MainWindow::on_action_remove_instrument() { @@ -2227,8 +2387,10 @@ } Glib::RefPtr sel = m_TreeView.get_selection(); - Gtk::TreeModel::iterator it = sel->get_selected(); - if (it) { + 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]); + if (!it) continue; Gtk::TreeModel::Row row = *it; gig::Instrument* instr = row[m_Columns.m_col_instr]; try { @@ -2243,6 +2405,16 @@ // remove row from instruments tree view m_refTreeModel->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) + { + Gtk::TreeModel::Row row = *it; + row[m_Columns.m_col_nr] = index; + } + } #if GTKMM_MAJOR_VERSION < 3 // select another instrument (in gtk3 this is done @@ -2340,6 +2512,12 @@ 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(); @@ -2418,7 +2596,9 @@ // get selected group (and probably selected sample) Glib::RefPtr sel = m_TreeViewSamples.get_selection(); - Gtk::TreeModel::iterator it = sel->get_selected(); + std::vector rows = sel->get_selected_rows(); + if (rows.empty()) return; + Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]); if (!it) return; Gtk::TreeModel::Row row = *it; gig::Sample* sample = NULL; @@ -2701,8 +2881,10 @@ void MainWindow::on_action_remove_sample() { if (!file) return; Glib::RefPtr sel = m_TreeViewSamples.get_selection(); - Gtk::TreeModel::iterator it = sel->get_selected(); - if (it) { + std::vector rows = sel->get_selected_rows(); + for (int r = rows.size() - 1; r >= 0; --r) { + Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[r]); + if (!it) continue; Gtk::TreeModel::Row row = *it; gig::Group* group = row[m_SamplesModel.m_col_group]; gig::Sample* sample = row[m_SamplesModel.m_col_sample]; @@ -2881,10 +3063,13 @@ 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]; + std::vector rows = sel->get_selected_rows(); + if (!rows.empty()) { + Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[0]); + if (it) { + Gtk::TreeModel::Row row = *it; + src = row[m_Columns.m_col_instr]; + } } } if (!src) return; @@ -2939,10 +3124,13 @@ // get selected sample gig::Sample* sample = NULL; Glib::RefPtr sel = m_TreeViewSamples.get_selection(); - Gtk::TreeModel::iterator it = sel->get_selected(); - if (it) { - Gtk::TreeModel::Row row = *it; - sample = row[m_SamplesModel.m_col_sample]; + std::vector rows = sel->get_selected_rows(); + if (!rows.empty()) { + Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]); + if (it) { + Gtk::TreeModel::Row row = *it; + sample = row[m_SamplesModel.m_col_sample]; + } } // pass the gig::Sample as pointer selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample, @@ -3082,6 +3270,12 @@ 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(); @@ -3135,7 +3329,9 @@ void MainWindow::on_action_view_references() { Glib::RefPtr sel = m_TreeViewSamples.get_selection(); - Gtk::TreeModel::iterator it = sel->get_selected(); + std::vector rows = sel->get_selected_rows(); + if (rows.empty()) return; + Gtk::TreeModel::iterator it = m_refSamplesTreeModel->get_iter(rows[0]); if (!it) return; Gtk::TreeModel::Row row = *it; gig::Sample* sample = row[m_SamplesModel.m_col_sample];