void KisScalarKeyframeChannel::uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame)
{
    KisScalarKeyframeChannel *srcScalarChannel = dynamic_cast<KisScalarKeyframeChannel*>(srcChannel);
    KIS_ASSERT_RECOVER_RETURN(srcScalarChannel);

    KisKeyframeSP srcFrame = srcScalarChannel->keyframeAt(srcTime);
    KIS_ASSERT_RECOVER_RETURN(srcFrame);

    const qreal newValue = scalarValue(srcFrame);

    const int dstId = dstFrame->value();
    KIS_ASSERT_RECOVER_RETURN(m_d->values.contains(dstId));
    m_d->values[dstId] = newValue;
}
QVariant KisAnimationCurvesModel::data(const QModelIndex &index, int role) const
{
    KisAnimationCurve *curve = m_d->getCurveAt(index);

    if (curve) {
        KisScalarKeyframeChannel *channel = curve->channel();

        int time = index.column();
        KisKeyframeSP keyframe = channel->keyframeAt(time);

        switch (role) {
        case SpecialKeyframeExists:
            return !keyframe.isNull();
        case ScalarValueRole:
            return channel->interpolatedValue(time);
        case LeftTangentRole:
            return (keyframe.isNull()) ? QVariant() : keyframe->leftTangent();
        case RightTangentRole:
            return (keyframe.isNull()) ? QVariant() : keyframe->rightTangent();
        case InterpolationModeRole:
            return (keyframe.isNull()) ? QVariant() : keyframe->interpolationMode();
        case TangentsModeRole:
            return (keyframe.isNull()) ? QVariant() : keyframe->tangentsMode();
        case CurveColorRole:
            return curve->color();
        case CurveVisibleRole:
            return curve->visible();
        case PreviousKeyframeTime:
        {
            KisKeyframeSP active = channel->activeKeyframeAt(time);
            if (active.isNull()) return QVariant();
            if (active->time() < time) {
                return active->time();
            }
            KisKeyframeSP previous = channel->previousKeyframe(active);
            if (previous.isNull()) return QVariant();
            return previous->time();
        }
        case NextKeyframeTime:
        {
            KisKeyframeSP active = channel->activeKeyframeAt(time);
            if (active.isNull()) {
                KisKeyframeSP first = channel->firstKeyframe();
                if (!first.isNull() && first->time() > time) {
                    return first->time();
                }
                return QVariant();
            }
            KisKeyframeSP next = channel->nextKeyframe(active);
            if (next.isNull()) return QVariant();
            return next->time();
        }
        default:
            break;
        }
    }

    return KisTimeBasedItemModel::data(index, role);
}
bool KisAnimationCurvesModel::adjustKeyframes(const QModelIndexList &indexes, int timeOffset, qreal valueOffset)
{
    KUndo2Command *command = new KUndo2Command(
        kundo2_i18np("Adjust Keyframe",
                     "Adjust %1 Keyframes",
                     indexes.size()));

    if (timeOffset != 0) {
        bool ok = offsetFrames(indexes, QPoint(timeOffset, 0), false, command);
        if (!ok) return false;
    }

    Q_FOREACH(QModelIndex oldIndex, indexes) {
        KisScalarKeyframeChannel *channel = m_d->getCurveAt(oldIndex)->channel();
        KIS_ASSERT_RECOVER_RETURN_VALUE(channel, false);

        KisKeyframeSP keyframe = channel->keyframeAt(oldIndex.column() + timeOffset);
        KIS_ASSERT_RECOVER_RETURN_VALUE(!keyframe.isNull(), false);

        qreal currentValue = channel->scalarValue(keyframe);
        channel->setScalarValue(keyframe, currentValue + valueOffset, command);
    };
bool KisAnimationCurvesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid()) return false;
    KisScalarKeyframeChannel *channel = m_d->getCurveAt(index)->channel();
    KUndo2Command *command = m_d->undoCommand;

    switch (role) {
    case ScalarValueRole:
    {
        KisKeyframeSP keyframe = channel->keyframeAt(index.column());
        if (keyframe) {
            if (!command) command = new KUndo2Command(kundo2_i18n("Adjust keyframe"));
            channel->setScalarValue(keyframe, value.toReal(), command);
        } else {
            if (!command) command = new KUndo2Command(kundo2_i18n("Insert keyframe"));
            auto *addKeyframeCommand = new KisScalarKeyframeChannel::AddKeyframeCommand(
                channel, index.column(), value.toReal(), command);
            addKeyframeCommand->redo();
        }
    }
        break;
    case LeftTangentRole:
    case RightTangentRole:
    {
        KisKeyframeSP keyframe = channel->keyframeAt(index.column());
        if (!keyframe) return false;

        QPointF leftTangent = (role == LeftTangentRole ? value.toPointF() : keyframe->leftTangent());
        QPointF rightTangent = (role == RightTangentRole ? value.toPointF() : keyframe->rightTangent());

        if (!command) command = new KUndo2Command(kundo2_i18n("Adjust tangent"));
        channel->setInterpolationTangents(keyframe, keyframe->tangentsMode(), leftTangent, rightTangent, command);
    }
        break;
    case InterpolationModeRole:
    {
        KisKeyframeSP keyframe = channel->keyframeAt(index.column());
        if (!keyframe) return false;

        if (!command) command = new KUndo2Command(kundo2_i18n("Set interpolation mode"));
        channel->setInterpolationMode(keyframe, (KisKeyframe::InterpolationMode)value.toInt(), command);
    }
        break;
    case TangentsModeRole:
    {
        KisKeyframeSP keyframe = channel->keyframeAt(index.column());
        if (!keyframe) return false;

        KisKeyframe::InterpolationTangentsMode mode = (KisKeyframe::InterpolationTangentsMode)value.toInt();
        QPointF leftTangent = keyframe->leftTangent();
        QPointF rightTangent = keyframe->rightTangent();

        if (!command) command = new KUndo2Command(kundo2_i18n("Set interpolation mode"));
        channel->setInterpolationTangents(keyframe, mode, leftTangent, rightTangent, command);
    }
        break;
    default:
        return KisTimeBasedItemModel::setData(index, value, role);
    }

    if (command && !m_d->undoCommand) {
        image()->postExecutionUndoAdapter()->addCommand(toQShared(command));
    }

    return true;
}