diff --git a/NEWS.md b/NEWS.md index 523d43f6e..3ac7d3283 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,6 +20,8 @@ * MKVToolNix GUI: header editor: a new column has been added to the tree view showing the status of the "track enabled" flag. The information is also shown on the track overview page on the right. Implements #3228. +* MKVToolNix GUI: header editor: users can now reorder tracks by dragging & + dropping. Implements #3227. ## Bug fixes diff --git a/src/mkvtoolnix-gui/header_editor/page_model.cpp b/src/mkvtoolnix-gui/header_editor/page_model.cpp index 312bbe6bf..38d7c89b2 100644 --- a/src/mkvtoolnix-gui/header_editor/page_model.cpp +++ b/src/mkvtoolnix-gui/header_editor/page_model.cpp @@ -8,6 +8,7 @@ #include "mkvtoolnix-gui/header_editor/page_base.h" #include "mkvtoolnix-gui/header_editor/page_model.h" #include "mkvtoolnix-gui/header_editor/top_level_page.h" +#include "mkvtoolnix-gui/header_editor/track_type_page.h" #include "mkvtoolnix-gui/util/model.h" namespace mtx::gui::HeaderEditor { @@ -25,6 +26,9 @@ PageModel::~PageModel() { PageBase * PageModel::selectedPage(QModelIndex const &idx) const { + if (!idx.isValid()) + return {}; + auto selectedItem = itemFromIndex(idx.sibling(idx.row(), 0)); if (!selectedItem) return {}; @@ -189,20 +193,20 @@ PageModel::canDropMimeData(QMimeData const *data, const { if ( !data || (Qt::MoveAction != action) - || !parent.isValid() || !m_lastSelectedIdx.isValid() || (0 > row)) return false; auto draggedPage = selectedPage(m_lastSelectedIdx); - if (!draggedPage || !dynamic_cast(draggedPage)) - return false; + auto parentPage = selectedPage(parent); - auto parentPage = selectedPage(parent); - if (!parentPage || !dynamic_cast(parentPage)) - return false; + if (dynamic_cast(draggedPage)) + return dynamic_cast(parentPage); - return true; + if (dynamic_cast(draggedPage)) + return !parentPage && (row > 0) && (row < rowCount()); + + return false; } bool @@ -214,13 +218,30 @@ PageModel::dropMimeData(QMimeData const *data, if (!canDropMimeData(data, action, row, column, parent)) return false; + auto draggedPage = selectedPage(m_lastSelectedIdx); + auto result = QStandardItemModel::dropMimeData(data, action, row, 0, parent); Util::requestAllItems(*this); - Q_EMIT attachmentsReordered(); + if (dynamic_cast(draggedPage)) + Q_EMIT attachmentsReordered(); + + else if (dynamic_cast(draggedPage)) + Q_EMIT tracksReordered(); return result; } +void +PageModel::rereadTopLevelPageIndexes() { + auto rootItem = invisibleRootItem(); + + for (int row = 0, numRows = rootItem->rowCount(); row < numRows; ++row) { + auto topLevelItem = rootItem->child(row); + auto pageId = topLevelItem->data(Util::HeaderEditorPageIdRole).value(); + m_pages[pageId]->m_pageIdx = topLevelItem->index(); + } +} + } diff --git a/src/mkvtoolnix-gui/header_editor/page_model.h b/src/mkvtoolnix-gui/header_editor/page_model.h index 2fcf0a49f..5a319cca0 100644 --- a/src/mkvtoolnix-gui/header_editor/page_model.h +++ b/src/mkvtoolnix-gui/header_editor/page_model.h @@ -48,6 +48,10 @@ public: Q_SIGNALS: void attachmentsReordered(); + void tracksReordered(); + +public Q_SLOTS: + void rereadTopLevelPageIndexes(); }; } diff --git a/src/mkvtoolnix-gui/header_editor/tab.cpp b/src/mkvtoolnix-gui/header_editor/tab.cpp index 90df34332..539802643 100644 --- a/src/mkvtoolnix-gui/header_editor/tab.cpp +++ b/src/mkvtoolnix-gui/header_editor/tab.cpp @@ -93,6 +93,7 @@ Tab::resetData() { m_eTracks.reset(); m_model->reset(); m_segmentinfoPage = nullptr; + m_tracksReordered = false; } void @@ -196,7 +197,7 @@ Tab::save() { tracksModified = true; } - if (!segmentinfoModified && !tracksModified && !attachmentsModified) { + if (!segmentinfoModified && !tracksModified && !attachmentsModified && !m_tracksReordered) { Util::MessageBox::information(this)->title(QY("File has not been modified")).text(QY("The header values have not been modified. There is nothing to save.")).exec(); return; } @@ -226,7 +227,9 @@ Tab::save() { } } - if (ok && tracksModified && m_eTracks) { + if (ok && m_eTracks && (tracksModified || m_tracksReordered)) { + updateTracksElementToMatchTrackOrder(); + auto result = m_analyzer->update_element(m_eTracks, true); if (kax_analyzer_c::uer_success != result) { Util::KaxAnalyzer::displayUpdateElementResult(this, result, QY("Saving the modified track headers failed.")); @@ -314,6 +317,13 @@ Tab::setupUi() { connect(m_replaceAttachmentContentAction, &QAction::triggered, [this]() { replaceAttachmentContent(false); }); connect(m_replaceAttachmentContentSetValuesAction, &QAction::triggered, [this]() { replaceAttachmentContent(true); }); connect(m_model, &PageModel::attachmentsReordered, [this]() { m_attachmentsPage->rereadChildren(*m_model); }); + connect(m_model, &PageModel::tracksReordered, this, &Tab::handleReorderedTracks); +} + +void +Tab::handleReorderedTracks() { + m_tracksReordered = true; + m_model->rereadTopLevelPageIndexes(); } void @@ -874,13 +884,15 @@ Tab::isClosingOrReloadingOkIfModified(ModifiedConfirmationMode mode) { return true; auto modifiedPage = hasBeenModified(); - if (!modifiedPage) + if (!modifiedPage && !m_tracksReordered) return true; auto tool = MainWindow::headerEditorTool(); MainWindow::get()->switchToTool(tool); tool->showTab(*this); - focusPage(modifiedPage); + + if (modifiedPage) + focusPage(modifiedPage); auto closing = mode == ModifiedConfirmationMode::Closing; auto text = closing ? QY("The file \"%1\" has been modified. Do you really want to close? All changes will be lost.") @@ -898,4 +910,15 @@ Tab::isClosingOrReloadingOkIfModified(ModifiedConfirmationMode mode) { return answer == QMessageBox::Yes; } +void +Tab::updateTracksElementToMatchTrackOrder() { + auto &tracks = static_cast(*m_eTracks); + + RemoveChildren(tracks); + + for (auto page : m_model->topLevelPages()) + if (dynamic_cast(page)) + tracks.PushElement(static_cast(*page).m_master); +} + } diff --git a/src/mkvtoolnix-gui/header_editor/tab.h b/src/mkvtoolnix-gui/header_editor/tab.h index 41a239301..4f9ed19a2 100644 --- a/src/mkvtoolnix-gui/header_editor/tab.h +++ b/src/mkvtoolnix-gui/header_editor/tab.h @@ -45,7 +45,7 @@ protected: PageModel *m_model; PageBase *m_segmentinfoPage{}; AttachmentsPage *m_attachmentsPage{}; - bool m_ignoreSelectionChanges{}; + bool m_ignoreSelectionChanges{}, m_tracksReordered{}; QMenu *m_treeContextMenu; QAction *m_expandAllAction, *m_collapseAllAction, *m_addAttachmentsAction, *m_removeAttachmentAction, *m_removeAllAttachmentsAction, *m_saveAttachmentContentAction; @@ -86,6 +86,7 @@ public Q_SLOTS: virtual void replaceAttachmentContent(bool deriveNameAndMimeType); virtual void handleDroppedFiles(QStringList const &fileNames, Qt::MouseButtons mouseButtons); virtual void focusPage(PageBase *page); + virtual void handleReorderedTracks(); protected: void setupUi(); @@ -107,6 +108,8 @@ protected: void pruneEmptyMastersForTrack(TrackTypePage &page); void pruneEmptyMastersForAllTracks(); + void updateTracksElementToMatchTrackOrder(); + public: static memory_cptr readFileData(QWidget *parent, QString const &fileName); };