--- gigedit/trunk/src/gigedit/CombineInstrumentsDialog.cpp 2014/05/24 06:44:39 2579 +++ gigedit/trunk/src/gigedit/CombineInstrumentsDialog.cpp 2019/01/27 10:07:56 3456 @@ -1,16 +1,16 @@ /* - Copyright (c) 2014 Christian Schoenebeck + Copyright (c) 2014-2019 Christian Schoenebeck This file is part of "gigedit" and released under the terms of the GNU General Public License version 2. */ +#include "global.h" #include "CombineInstrumentsDialog.h" // enable this for debug messages being printed while combining the instruments #define DEBUG_COMBINE_INSTRUMENTS 0 -#include "global.h" #include "compat.h" #include @@ -20,11 +20,13 @@ #include #include -#include +#if HAS_GTKMM_STOCK +# include +#endif #include #include +#include // for gtk_widget_modify_*() -Glib::ustring gig_to_utf8(const gig::String& gig_string); Glib::ustring dimTypeAsString(gig::dimension_t d); typedef std::vector< std::pair > OrderedRegionGroup; @@ -34,8 +36,6 @@ typedef std::vector DimensionZones; typedef std::map Dimensions; -typedef std::map DimensionCase; - typedef std::map DimensionRegionUpperLimits; typedef std::set Warnings; @@ -97,7 +97,7 @@ * found with a range member point >= iStart */ static int findLowestRegionPoint(std::vector& instruments, int iStart) { - DLS::range_t searchRange = { iStart, 127 }; + DLS::range_t searchRange = { uint16_t(iStart), 127 }; int result = -1; for (uint i = 0; i < instruments.size(); ++i) { gig::Instrument* instr = instruments[i]; @@ -119,7 +119,7 @@ * with a range end >= iStart */ static int findFirstRegionEnd(std::vector& instruments, int iStart) { - DLS::range_t searchRange = { iStart, 127 }; + DLS::range_t searchRange = { uint16_t(iStart), 127 }; int result = -1; for (uint i = 0; i < instruments.size(); ++i) { gig::Instrument* instr = instruments[i]; @@ -195,7 +195,7 @@ iStart = findLowestRegionPoint(instruments, iStart); if (iStart < 0) break; const int iEnd = findFirstRegionEnd(instruments, iStart); - DLS::range_t range = { iStart, iEnd }; + DLS::range_t range = { uint16_t(iStart), uint16_t(iEnd) }; intersections.push_back(range); iStart = iEnd + 1; } @@ -266,7 +266,7 @@ itNums != it->second.end(); ++itNums) { const int iUpperLimit = *itNums; - DLS::range_t range = { iLow, iUpperLimit }; + DLS::range_t range = { uint16_t(iLow), uint16_t(iUpperLimit) }; dims[type].push_back(range); iLow = iUpperLimit + 1; } @@ -275,13 +275,6 @@ return dims; } -inline int getDimensionIndex(gig::dimension_t type, gig::Region* rgn) { - for (uint i = 0; i < rgn->Dimensions; ++i) - if (rgn->pDimensionDefinitions[i].dimension == type) - return i; - return -1; -} - static void fillDimValues(uint* values/*[8]*/, DimensionCase dimCase, gig::Region* rgn, bool bShouldHaveAllDimensionsPassed) { #if DEBUG_COMBINE_INSTRUMENTS printf("dimvalues = { "); @@ -323,23 +316,6 @@ } } -/** - * Returns the sum of all bits of all dimensions defined before the given - * dimensions (@a type). This allows to access cases of that particular - * dimension directly. - * - * @param type - dimension that shall be used - * @param rgn - parent region of that dimension - */ -inline int baseBits(gig::dimension_t type, gig::Region* rgn) { - int previousBits = 0; - for (uint i = 0; i < rgn->Dimensions; ++i) { - if (rgn->pDimensionDefinitions[i].dimension == type) break; - previousBits += rgn->pDimensionDefinitions[i].bits; - } - return previousBits; -} - inline int dimensionRegionIndex(gig::DimensionRegion* dimRgn) { gig::Region* rgn = dimRgn->GetParent(); int sz = sizeof(rgn->pDimensionRegions) / sizeof(gig::DimensionRegion*); @@ -372,6 +348,7 @@ const gig::dimension_def_t& def = rgn->pDimensionDefinitions[iDimension]; int iDimRgn = dimensionRegionIndex(dimRgn); int iBaseBits = baseBits(type, rgn); + assert(iBaseBits >= 0); int mask = ~(((1 << def.bits) - 1) << iBaseBits); #if DEBUG_COMBINE_INSTRUMENTS @@ -383,7 +360,7 @@ gig::DimensionRegion* dimRgn2 = rgn->pDimensionRegions[ (iDimRgn & mask) | ( z << iBaseBits) ]; int iHigh = dimRgn2->DimensionUpperLimits[iDimension]; - DLS::range_t range = { iLow, iHigh}; + DLS::range_t range = { uint16_t(iLow), uint16_t(iHigh) }; #if DEBUG_COMBINE_INSTRUMENTS printf("%d..%d, ", iLow, iHigh); fflush(stdout); @@ -469,7 +446,7 @@ #if DEBUG_COMBINE_INSTRUMENTS printf("dst "); fflush(stdout); #endif - fillDimValues(dstDimValues, dstDimCase, outRgn, true); + fillDimValues(dstDimValues, dstDimCase, outRgn, false); gig::DimensionRegion* srcDimRgn = inRgn->GetDimensionRegionByValue(srcDimValues); gig::DimensionRegion* dstDimRgn = outRgn->GetDimensionRegionByValue(dstDimValues); #if DEBUG_COMBINE_INSTRUMENTS @@ -505,7 +482,7 @@ printf("dst velocity value = %d\n", dstDimCase[gig::dimension_velocity]); printf("dst refilled "); fflush(stdout); #endif - fillDimValues(dstDimValues, dstDimCase, outRgn, true); + fillDimValues(dstDimValues, dstDimCase, outRgn, false); dstDimRgn = outRgn->GetDimensionRegionByValue(dstDimValues); #if DEBUG_COMBINE_INSTRUMENTS printf("reselected dstDimRgn=%lx\n", (uint64_t)dstDimRgn); @@ -660,7 +637,7 @@ iTotalZones += (def) ? def->zones : 1; } #if DEBUG_COMBINE_INSTRUMENTS - printf("Required total zones: %d\n", iTotalZones); + printf("Required total zones: %d, vertical regions: %d\n", iTotalZones, itGroup->second.size()); #endif // create all required dimensions for this output region @@ -722,6 +699,8 @@ #if DEBUG_COMBINE_INSTRUMENTS std::cout << "OK" << std::endl << std::flush; #endif + } else { + dims.erase(mainDimension); } // for the next task we need to have the current RegionGroup to be @@ -803,16 +782,45 @@ // class 'CombineInstrumentsDialog' CombineInstrumentsDialog::CombineInstrumentsDialog(Gtk::Window& parent, gig::File* gig) - : Gtk::Dialog(_("Combine Instruments"), parent, true), + : 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_treeView); + 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(); @@ -856,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) @@ -882,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(); @@ -904,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) { @@ -917,20 +982,151 @@ 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.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=%ld\n", (size_t)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(); @@ -947,7 +1143,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); @@ -987,6 +1183,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 {