bool
SourceFileModel::dropSourceFiles(QMimeData const *data,
                                 Qt::DropAction action,
                                 int row,
                                 QModelIndex const &parent) {
  if (action != Qt::MoveAction)
    return QAbstractItemModel::dropMimeData(data, action, row, 0, parent);

  auto encoded = data->data(mtx::gui::MimeTypes::MergeSourceFileModelItem);
  QDataStream stream{&encoded, QIODevice::ReadOnly};

  while (!stream.atEnd()) {
    quint64 value;
    stream >> value;
    auto sourceFile = m_sourceFileMap[value];
    auto sourceIdx  = indexFromSourceFile(sourceFile.get());

    if (!sourceIdx.isValid())
      continue;

    auto sourceParent     = sourceIdx.parent();
    auto sourceParentItem = sourceParent.isValid() ? itemFromIndex(sourceParent) : invisibleRootItem();
    auto rowItems         = sourceParentItem->takeRow(sourceIdx.row());

    if (!parent.isValid()) {
      if ((sourceParent == parent) && (sourceIdx.row() < row))
        --row;

      invisibleRootItem()->insertRow(row, rowItems);
      ++row;

    } else {
      auto parentFile = fromIndex(parent);
      Q_ASSERT(parentFile);

      if (sourceFile->isAdditionalPart())
        row = std::min(row, parentFile->m_additionalParts.size());
      else
        row = std::max(row, parentFile->m_additionalParts.size());

      if ((sourceParent == parent) && (sourceIdx.row() < row))
        --row;

      itemFromIndex(parent)->insertRow(row, rowItems);
      ++row;
    }

    updateSourceFileLists();
  }

  return false;
}
bool
SourceFileModel::dropSourceFiles(QMimeData const *data,
                                 Qt::DropAction action,
                                 int row,
                                 QModelIndex const &parent) {
  if (action != Qt::MoveAction)
    return QAbstractItemModel::dropMimeData(data, action, row, 0, parent);

  mxinfo(boost::format("dropMimeData row %1% parent %2%/%3%\n") % row % parent.row() % parent.column());

  auto encoded = data->data(MIME_TYPE);
  QDataStream stream{&encoded, QIODevice::ReadOnly};

  while (!stream.atEnd()) {
    quint64 value;
    stream >> value;
    auto sourceFile = m_sourceFileMap[value];
    auto sourceIdx  = indexFromSourceFile(sourceFile.get());

    if (!sourceIdx.isValid())
      continue;

    mxinfo(boost::format("  val %|1$08x| ptr %2% idx %3%/%4%\n") % value % sourceFile.get() % sourceIdx.row() % sourceIdx.column());

    auto sourceParent     = sourceIdx.parent();
    auto sourceParentItem = sourceParent.isValid() ? itemFromIndex(sourceParent) : invisibleRootItem();
    auto priorRow         = row;
    auto rowItems         = sourceParentItem->takeRow(sourceIdx.row());

    if (!parent.isValid()) {
      if ((sourceParent == parent) && (sourceIdx.row() < row))
        --row;

      mxinfo(boost::format("tried moving case 1 from %1% row %2% to new parent %3% row %4% new row %5%\n") % dumpIdx(sourceIdx.parent()) % sourceIdx.row() % dumpIdx(parent) % priorRow % (row + 1));

      invisibleRootItem()->insertRow(row, rowItems);
      ++row;

    } else {
      auto parentFile = fromIndex(parent);
      Q_ASSERT(parentFile);

      if (sourceFile->isAdditionalPart())
        row = std::min(row, parentFile->m_additionalParts.size());
      else
        row = std::max(row, parentFile->m_additionalParts.size());

      if ((sourceParent == parent) && (sourceIdx.row() < row))
        --row;

      mxinfo(boost::format("tried moving case 2 from %1% row %2% to new parent %3% row %4% new row %5%\n") % dumpIdx(sourceIdx.parent()) % sourceIdx.row() % dumpIdx(parent) % priorRow % (row + 1));

      itemFromIndex(parent)->insertRow(row, rowItems);
      ++row;
    }

    updateSourceFileLists();
  }

  return false;
}
void
SourceFileModel::moveSourceFilesUpOrDown(QList<SourceFile *> files,
                                         bool up) {
  sortSourceFiles(files, !up);

  // qDebug() << "move up?" << up << "files" << files;

  auto couldNotBeMoved = QHash<SourceFile *, bool>{};
  auto isSelected      = QHash<SourceFile *, bool>{};
  auto const direction = up ? -1 : +1;
  auto const topRows   = rowCount();

  for (auto const &file : files) {
    isSelected[file] = true;

    if (!file->isRegular() && isSelected[file->m_appendedTo])
      continue;

    auto idx = indexFromSourceFile(file);
    Q_ASSERT(idx.isValid());

    auto targetRow = idx.row() + direction;
    if (couldNotBeMoved[fromIndex(idx.sibling(targetRow, 0)).get()]) {
      couldNotBeMoved[file] = true;
      continue;
    }

    if (file->isRegular()) {
      if (!((0 <= targetRow) && (targetRow < topRows))) {
        couldNotBeMoved[file] = true;
        continue;
      }

      // qDebug() << "top level: would like to move" << idx.row() << "to" << targetRow;

      insertRow(targetRow, takeRow(idx.row()));

      continue;
    }

    auto parentItem                   = itemFromIndex(idx.parent());
    auto const appendedAdditionalRows = countAppendedAndAdditionalParts(parentItem);
    auto const additionalPartsRows    = appendedAdditionalRows.first;
    auto const appendedRows           = appendedAdditionalRows.second;
    auto const lowerLimit             = (file->isAdditionalPart() ? 0 : additionalPartsRows);
    auto const upperLimit             = (file->isAdditionalPart() ? 0 : appendedRows) +  additionalPartsRows;

    if ((lowerLimit <= targetRow) && (targetRow < upperLimit)) {
      // qDebug() << "appended level normal: would like to move" << idx.row() << "to" << targetRow;

      parentItem->insertRow(targetRow, parentItem->takeRow(idx.row()));
      continue;
    }

    auto parentIdx = parentItem->index();
    Q_ASSERT(parentIdx.isValid());

    auto newParentRow = parentIdx.row() + direction;
    if ((0 > newParentRow) || (rowCount() <= newParentRow)) {
      // qDebug() << "appended, cannot move further";
      couldNotBeMoved[file] = true;
      continue;
    }

    auto newParent        = fromIndex(index(newParentRow, 0));
    auto newParentItem    = itemFromIndex(index(newParentRow, 0));
    auto rowItems         = parentItem->takeRow(idx.row());
    auto newParentNumbers = countAppendedAndAdditionalParts(newParentItem);
    targetRow             = up  && file->isAdditionalPart() ? newParentNumbers.first
                          : up                              ? newParentNumbers.first + newParentNumbers.second
                          : !up && file->isAdditionalPart() ? 0
                          :                                   newParentNumbers.first;

    Q_ASSERT(!!newParent);

    // qDebug() << "appended level cross: would like to move" << idx.row() << "from" << file->m_appendedTo << "to" << newParent.get() << "as" << targetRow;

    newParentItem->insertRow(targetRow, rowItems);
    file->m_appendedTo = newParent.get();
  }

  updateSourceFileLists();
}