--- gigedit/trunk/src/gigedit/CombineInstrumentsDialog.cpp 2017/05/22 18:58:46 3202 +++ gigedit/trunk/src/gigedit/CombineInstrumentsDialog.cpp 2019/01/02 16:39:20 3450 @@ -1,5 +1,5 @@ /* - Copyright (c) 2014-2017 Christian Schoenebeck + Copyright (c) 2014-2018 Christian Schoenebeck This file is part of "gigedit" and released under the terms of the GNU General Public License version 2. @@ -20,9 +20,12 @@ #include #include -#include +#if HAS_GTKMM_STOCK +# include +#endif #include #include +#include // for gtk_widget_modify_*() Glib::ustring dimTypeAsString(gig::dimension_t d); @@ -781,17 +784,43 @@ CombineInstrumentsDialog::CombineInstrumentsDialog(Gtk::Window& parent, gig::File* gig) : ManagedDialog(_("Combine Instruments"), parent, true), m_gig(gig), m_fileWasChanged(false), m_newCombinedInstrument(NULL), +#if HAS_GTKMM_STOCK m_cancelButton(Gtk::Stock::CANCEL), m_OKButton(Gtk::Stock::OK), - m_descriptionLabel(), m_tableDimCombo(2, 2), m_comboDimType(), +#else + m_cancelButton(_("_Cancel"), true), m_OKButton(_("_OK"), true), +#endif + m_descriptionLabel(), +#if USE_GTKMM_GRID + m_tableDimCombo(), +#else + m_tableDimCombo(2, 2), +#endif + m_comboDimType(), m_labelDimType(Glib::ustring(_("Combine by Dimension:")) + " ", Gtk::ALIGN_END) { + if (!Settings::singleton()->autoRestoreWindowDimension) { + set_default_size(500, 600); + set_position(Gtk::WIN_POS_MOUSE); + } + m_scrolledWindow.add(m_treeView); m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); +#if USE_GTKMM_BOX + get_content_area()->pack_start(m_descriptionLabel, Gtk::PACK_SHRINK); + get_content_area()->pack_start(m_tableDimCombo, Gtk::PACK_SHRINK); + get_content_area()->pack_start(m_scrolledWindow); + get_content_area()->pack_start(m_labelOrder, Gtk::PACK_SHRINK); + get_content_area()->pack_start(m_iconView, Gtk::PACK_SHRINK); + get_content_area()->pack_start(m_buttonBox, Gtk::PACK_SHRINK); +#else get_vbox()->pack_start(m_descriptionLabel, Gtk::PACK_SHRINK); get_vbox()->pack_start(m_tableDimCombo, Gtk::PACK_SHRINK); get_vbox()->pack_start(m_scrolledWindow); + get_vbox()->pack_start(m_labelOrder, Gtk::PACK_SHRINK); + get_vbox()->pack_start(m_iconView, Gtk::PACK_SHRINK); get_vbox()->pack_start(m_buttonBox, Gtk::PACK_SHRINK); +#endif #if GTKMM_MAJOR_VERSION >= 3 m_descriptionLabel.set_line_wrap(); @@ -835,8 +864,9 @@ "Use SHIFT + left click or CTRL + left click to select the instruments " "you want to combine." )); - m_treeView.append_column("Instrument", m_columns.m_col_name); - m_treeView.set_headers_visible(false); + m_treeView.append_column(_("Nr"), m_columns.m_col_index); + m_treeView.append_column(_("Instrument"), m_columns.m_col_name); + m_treeView.set_headers_visible(true); m_treeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); m_treeView.get_selection()->signal_changed().connect( sigc::mem_fun(*this, &CombineInstrumentsDialog::onSelectionChanged) @@ -861,12 +891,61 @@ Glib::ustring name(gig_to_utf8(instr->pInfo->Name)); Gtk::TreeModel::iterator iter = m_refTreeModel->append(); Gtk::TreeModel::Row row = *iter; + row[m_columns.m_col_index] = i; row[m_columns.m_col_name] = name; row[m_columns.m_col_instr] = instr; } + m_refOrderModel = Gtk::ListStore::create(m_orderColumns); + m_iconView.set_model(m_refOrderModel); + m_iconView.set_tooltip_text(_("Use drag & drop to change the order.")); + m_iconView.set_markup_column(1); + m_iconView.set_selection_mode(Gtk::SELECTION_SINGLE); + // force background to retain white also on selections + // (this also fixes a bug with GTK 2 which often causes visibility issue + // with the text of the selected item) + { +#if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2 + Gdk::Color white; +#else + Gdk::RGBA white; +#endif + white.set("#ffffff"); + GtkWidget* widget = (GtkWidget*) m_iconView.gobj(); +#if GTK_MAJOR_VERSION < 3 + gtk_widget_modify_base(widget, GTK_STATE_SELECTED, white.gobj()); + gtk_widget_modify_base(widget, GTK_STATE_ACTIVE, white.gobj()); + gtk_widget_modify_bg(widget, GTK_STATE_SELECTED, white.gobj()); + gtk_widget_modify_bg(widget, GTK_STATE_ACTIVE, white.gobj()); +#endif + } + + m_labelOrder.set_text(_("Order of the instruments to be combined:")); + + // 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_iconView.drag_source_set(drag_target_instrument); + m_iconView.drag_dest_set(drag_target_instrument); + m_iconView.signal_drag_begin().connect( + sigc::mem_fun(*this, &CombineInstrumentsDialog::on_order_drag_begin) + ); + m_iconView.signal_drag_data_get().connect( + sigc::mem_fun(*this, &CombineInstrumentsDialog::on_order_drag_data_get) + ); + m_iconView.signal_drag_data_received().connect( + sigc::mem_fun(*this, &CombineInstrumentsDialog::on_order_drop_drag_data_received) + ); + } + m_buttonBox.set_layout(Gtk::BUTTONBOX_END); +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) + m_buttonBox.set_margin(5); +#else m_buttonBox.set_border_width(5); +#endif m_buttonBox.pack_start(m_cancelButton, Gtk::PACK_SHRINK); m_buttonBox.pack_start(m_OKButton, Gtk::PACK_SHRINK); m_buttonBox.show(); @@ -883,7 +962,14 @@ sigc::mem_fun(*this, &CombineInstrumentsDialog::combineSelectedInstruments) ); +#if HAS_GTKMM_SHOW_ALL_CHILDREN show_all_children(); +#endif + + Settings::singleton()->showTooltips.get_proxy().signal_changed().connect( + sigc::mem_fun(*this, &CombineInstrumentsDialog::on_show_tooltips_changed) + ); + on_show_tooltips_changed(); // show a warning to user if he uses a .gig in v2 format if (gig->pVersion->major < 3) { @@ -896,20 +982,155 @@ Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_WARNING); msg.run(); } + + // OK button should have focus by default for quick combining with Return key + m_OKButton.grab_focus(); +} + +void CombineInstrumentsDialog::on_order_drag_begin(const Glib::RefPtr& context) +{ + printf("Drag begin\n"); + first_call_to_drag_data_get = true; +} + +void CombineInstrumentsDialog::on_order_drag_data_get(const Glib::RefPtr& context, + Gtk::SelectionData& selection_data, guint, guint) +{ + printf("Drag data get\n"); + if (!first_call_to_drag_data_get) return; + first_call_to_drag_data_get = false; + + // get selected source instrument + gig::Instrument* src = NULL; + { + std::vector rows = m_iconView.get_selected_items(); + if (!rows.empty()) { + Gtk::TreeModel::iterator it = m_refOrderModel->get_iter(rows[0]); + if (it) { + Gtk::TreeModel::Row row = *it; + src = row[m_orderColumns.m_col_instr]; + } + } + } + if (!src) { + printf("Drag data get: !src\n"); + return; + } + printf("src=%ld\n", (size_t)src); + + // 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 CombineInstrumentsDialog::on_order_drop_drag_data_received( + const Glib::RefPtr& context, int x, int y, + const Gtk::SelectionData& selection_data, guint, guint time) +{ + printf("Drag data received\n"); + if (&selection_data == NULL) { + printf("!selection_data\n"); + return; + } + if (!selection_data.get_data()) { + printf("selection_data.get_data() == NULL\n"); + return; + } + + gig::Instrument* src = *((gig::Instrument**) selection_data.get_data()); + if (!src || selection_data.get_length() != sizeof(gig::Instrument*)) { + printf("!src\n"); + return; + } + printf("src=%d\n", src); + + gig::Instrument* dst = NULL; + { + Gtk::TreeModel::Path path = m_iconView.get_path_at_pos(x, y); + if (!path) return; + + Gtk::TreeModel::iterator iter = m_refOrderModel->get_iter(path); + if (!iter) return; + Gtk::TreeModel::Row row = *iter; + dst = row[m_orderColumns.m_col_instr]; + } + if (!dst) { + printf("!dst\n"); + return; + } + + printf("dragdrop received src='%s' dst='%s'\n", src->pInfo->Name.c_str(), dst->pInfo->Name.c_str()); + + // swap the two items + typedef Gtk::TreeModel::Children Children; + Children children = m_refOrderModel->children(); + Children::iterator itSrc, itDst; + int i = 0, iSrc = -1, iDst = -1; + for (Children::iterator iter = children.begin(); + iter != children.end(); ++iter, ++i) + { + Gtk::TreeModel::Row row = *iter; + if (row[m_orderColumns.m_col_instr] == src) { + itSrc = iter; + iSrc = i; + } else if (row[m_orderColumns.m_col_instr] == dst) { + itDst = iter; + iDst = i; + } + } + if (itSrc && itDst) { + // swap elements + m_refOrderModel->iter_swap(itSrc, itDst); + // update markup + Gtk::TreeModel::Row rowSrc = *itSrc; + Gtk::TreeModel::Row rowDst = *itDst; + { + Glib::ustring name = rowSrc[m_orderColumns.m_col_name]; + Glib::ustring markup = + "" + ToString(iDst+1) + ".\n" + name + ""; + rowSrc[m_orderColumns.m_col_markup] = markup; + } + { + Glib::ustring name = rowDst[m_orderColumns.m_col_name]; + Glib::ustring markup = + "" + ToString(iSrc+1) + ".\n" + name + ""; + rowDst[m_orderColumns.m_col_markup] = markup; + } + } +} + +void CombineInstrumentsDialog::setSelectedInstruments(const std::set& instrumentIndeces) { + typedef Gtk::TreeModel::Children Children; + Children children = m_refTreeModel->children(); + for (Children::iterator iter = children.begin(); + iter != children.end(); ++iter) + { + Gtk::TreeModel::Row row = *iter; + int index = row[m_columns.m_col_index]; + if (instrumentIndeces.count(index)) + m_treeView.get_selection()->select(iter); + } + // hack: OK button lost focus after doing the above, it should have focus by default for quick combining with Return key + m_OKButton.grab_focus(); } void CombineInstrumentsDialog::combineSelectedInstruments() { std::vector instruments; - std::vector v = m_treeView.get_selection()->get_selected_rows(); - for (uint i = 0; i < v.size(); ++i) { - Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(v[i]); - Gtk::TreeModel::Row row = *it; - Glib::ustring name = row[m_columns.m_col_name]; - gig::Instrument* instrument = row[m_columns.m_col_instr]; - #if DEBUG_COMBINE_INSTRUMENTS - printf("Selection '%s' 0x%lx\n\n", name.c_str(), int64_t((void*)instrument)); - #endif - instruments.push_back(instrument); + { + typedef Gtk::TreeModel::Children Children; + int i = 0; + Children selection = m_refOrderModel->children(); + for (Children::iterator it = selection.begin(); + it != selection.end(); ++it, ++i) + { + Gtk::TreeModel::Row row = *it; + Glib::ustring name = row[m_orderColumns.m_col_name]; + gig::Instrument* instrument = row[m_orderColumns.m_col_instr]; + #if DEBUG_COMBINE_INSTRUMENTS + printf("Selection %d. '%s' %p\n\n", (i+1), name.c_str(), instrument)); + #endif + instruments.push_back(instrument); + } } g_warnings.clear(); @@ -926,7 +1147,7 @@ mainDimension = static_cast(iTypeID); } - // now start the actual cobination task ... + // now start the actual combination task ... combineInstruments(instruments, m_gig, m_newCombinedInstrument, mainDimension); } catch (RIFF::Exception e) {; Gtk::MessageDialog msg(*this, e.Message, false, Gtk::MESSAGE_ERROR); @@ -966,6 +1187,80 @@ void CombineInstrumentsDialog::onSelectionChanged() { std::vector v = m_treeView.get_selection()->get_selected_rows(); m_OKButton.set_sensitive(v.size() >= 2); + + typedef Gtk::TreeModel::Children Children; + + // update horizontal selection list (icon view) ... + + // remove items which are not part of the new selection anymore + { + Children allOrdered = m_refOrderModel->children(); + for (Children::iterator itOrder = allOrdered.begin(); + itOrder != allOrdered.end(); ++itOrder) + { + Gtk::TreeModel::Row rowOrder = *itOrder; + gig::Instrument* instr = rowOrder[m_orderColumns.m_col_instr]; + for (uint i = 0; i < v.size(); ++i) { + Gtk::TreeModel::iterator itSel = m_refTreeModel->get_iter(v[i]); + Gtk::TreeModel::Row rowSel = *itSel; + if (rowSel[m_columns.m_col_instr] == instr) + goto nextOrderedItem; + } + goto removeOrderedItem; + nextOrderedItem: + continue; + removeOrderedItem: + m_refOrderModel->erase(itOrder); + } + } + + // add items newly added to the selection + for (uint i = 0; i < v.size(); ++i) { + Gtk::TreeModel::iterator itSel = m_refTreeModel->get_iter(v[i]); + Gtk::TreeModel::Row rowSel = *itSel; + gig::Instrument* instr = rowSel[m_columns.m_col_instr]; + Children allOrdered = m_refOrderModel->children(); + for (Children::iterator itOrder = allOrdered.begin(); + itOrder != allOrdered.end(); ++itOrder) + { + Gtk::TreeModel::Row rowOrder = *itOrder; + if (rowOrder[m_orderColumns.m_col_instr] == instr) + goto nextSelectionItem; + } + goto addNewSelectionItem; + nextSelectionItem: + continue; + addNewSelectionItem: + Glib::ustring name = gig_to_utf8(instr->pInfo->Name); + Gtk::TreeModel::iterator iterOrder = m_refOrderModel->append(); + Gtk::TreeModel::Row rowOrder = *iterOrder; + rowOrder[m_orderColumns.m_col_name] = name; + rowOrder[m_orderColumns.m_col_instr] = instr; + } + + // update markup + { + int i = 0; + Children allOrdered = m_refOrderModel->children(); + for (Children::iterator itOrder = allOrdered.begin(); + itOrder != allOrdered.end(); ++itOrder, ++i) + { + Gtk::TreeModel::Row rowOrder = *itOrder; + Glib::ustring name = rowOrder[m_orderColumns.m_col_name]; + Glib::ustring markup = + "" + ToString(i+1) + ".\n" + name + ""; + rowOrder[m_orderColumns.m_col_markup] = markup; + } + } +} + +void CombineInstrumentsDialog::on_show_tooltips_changed() { + const bool b = Settings::singleton()->showTooltips; + + m_treeView.set_has_tooltip(b); + m_iconView.set_has_tooltip(b); + + set_has_tooltip(b); } bool CombineInstrumentsDialog::fileWasChanged() const {