File indexing completed on 2024-12-15 04:01:00

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "animation_commands.hpp"
0008 
0009 #include "model/document.hpp"
0010 
0011 glaxnimate::command::SetKeyframe::SetKeyframe(
0012     model::AnimatableBase* prop,
0013     model::FrameTime time,
0014     const QVariant& value,
0015     bool commit,
0016     bool force_insert
0017 ) : Parent(i18n("Update %1 keyframe at %2", prop->name(), time), commit),
0018     prop(prop),
0019     time(time),
0020     before(prop->value(time)),
0021     after(value),
0022     had_before(prop->has_keyframe(time) && !force_insert),
0023     force_insert(force_insert)
0024 {}
0025 
0026 void glaxnimate::command::SetKeyframe::undo()
0027 {
0028     if ( had_before )
0029         prop->set_keyframe(time, before);
0030     else
0031         prop->remove_keyframe_at_time(time);
0032 
0033     if ( insert_index > 0 )
0034         prop->keyframe(insert_index-1)->set_transition(trans_before);
0035 }
0036 
0037 void glaxnimate::command::SetKeyframe::redo()
0038 {
0039     if ( !calculated )
0040     {
0041         auto mid = prop->mid_transition(time);
0042         model::AnimatableBase::SetKeyframeInfo info;
0043         auto kf = prop->set_keyframe(time, after, &info, force_insert);
0044         if ( kf && info.insertion && info.index > 0 && info.index + 1 < prop->keyframe_count() )
0045         {
0046             if ( mid.type != model::AnimatableBase::MidTransition::Middle )
0047             {
0048                 insert_index = -1;
0049             }
0050             else
0051             {
0052                 insert_index = info.index;
0053 
0054                 auto kf_before = prop->keyframe(info.index - 1);
0055                 trans_before = kf_before->transition();
0056 
0057                 left = mid.from_previous;
0058                 right = mid.to_next;
0059             }
0060         }
0061     }
0062     else
0063     {
0064         prop->set_keyframe(time, after, nullptr, force_insert);
0065     }
0066 
0067     if ( insert_index > 0 )
0068     {
0069         prop->keyframe(insert_index-1)->set_transition(left);
0070         prop->keyframe(insert_index)->set_transition(right);
0071     }
0072 
0073 }
0074 
0075 bool glaxnimate::command::SetKeyframe::merge_with(const SetKeyframe& other)
0076 {
0077     if ( other.prop != prop )
0078         return false;
0079     after = other.after;
0080     return true;
0081 }
0082 
0083 glaxnimate::command::RemoveKeyframeTime::RemoveKeyframeTime(
0084     model::AnimatableBase* prop,
0085     model::FrameTime time
0086 ) : QUndoCommand(i18n("Remove %1 keyframe at %2", prop->name(), time)),
0087     prop(prop),
0088     time(time),
0089     index(prop->keyframe_index(time)),
0090     before(prop->keyframe(index)->value())
0091 {
0092     if ( index > 0 )
0093     {
0094         prev_transition_after = prev_transition_before = prop->keyframe(index-1)->transition();
0095         if ( !prev_transition_after.hold() )
0096             prev_transition_after.set_after(prop->keyframe(index)->transition().after());
0097     }
0098 }
0099 
0100 void glaxnimate::command::RemoveKeyframeTime::undo()
0101 {
0102     prop->set_keyframe(time, before);
0103     if ( index > 0 )
0104         prop->keyframe(index-1)->set_transition(prev_transition_before);
0105 }
0106 
0107 void glaxnimate::command::RemoveKeyframeTime::redo()
0108 {
0109     if ( index > 0 )
0110         prop->keyframe(index-1)->set_transition(prev_transition_after);
0111     prop->remove_keyframe(index);
0112 }
0113 
0114 glaxnimate::command::RemoveKeyframeIndex::RemoveKeyframeIndex(
0115     model::AnimatableBase* prop,
0116     int index
0117 ) : QUndoCommand(i18n("Remove %1 keyframe %2", prop->name(), index)),
0118     prop(prop),
0119     index(index),
0120     time(prop->keyframe(index)->time()),
0121     before(prop->keyframe(index)->value())
0122 {
0123     if ( index > 0 )
0124     {
0125         prev_transition_after = prev_transition_before = prop->keyframe(index-1)->transition();
0126         if ( !prev_transition_after.hold() )
0127             prev_transition_after.set_after(prop->keyframe(index)->transition().after());
0128     }
0129 }
0130 
0131 void glaxnimate::command::RemoveKeyframeIndex::undo()
0132 {
0133     prop->set_keyframe(time, before, nullptr, true);
0134     if ( index > 0 )
0135         prop->keyframe(index-1)->set_transition(prev_transition_before);
0136 
0137 }
0138 
0139 void glaxnimate::command::RemoveKeyframeIndex::redo()
0140 {
0141     if ( index > 0 )
0142         prop->keyframe(index-1)->set_transition(prev_transition_after);
0143     prop->remove_keyframe(index);
0144 }
0145 
0146 glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated(model::AnimatableBase* prop, QVariant after, bool commit)
0147     : SetMultipleAnimated(
0148         auto_name(prop),
0149         {prop},
0150         {},
0151         {after},
0152         commit
0153     )
0154 {}
0155 
0156 glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated(
0157     const QString& name,
0158     const std::vector<model::AnimatableBase*>& props,
0159     const QVariantList& before,
0160     const QVariantList& after,
0161     bool commit
0162 )
0163     : Parent(name, commit),
0164     props(props),
0165     before(before),
0166     after(after),
0167     keyframe_after(props[0]->object()->document()->record_to_keyframe()),
0168     time(props[0]->time())
0169 {
0170     bool add_before = before.empty();
0171 
0172     for ( auto prop : props )
0173     {
0174         if ( add_before )
0175             this->before.push_back(prop->value());
0176         keyframe_before.push_back(prop->has_keyframe(time));
0177         add_0.push_back(time != 0 && !prop->animated() && prop->object()->document()->record_to_keyframe());
0178     }
0179 }
0180 
0181 
0182 glaxnimate::command::SetMultipleAnimated::SetMultipleAnimated(const QString& name, bool commit)
0183     : Parent(name, commit)
0184 {
0185 }
0186 
0187 void glaxnimate::command::SetMultipleAnimated::push_property(model::AnimatableBase* prop, const QVariant& after_val)
0188 {
0189     keyframe_after = prop->object()->document()->record_to_keyframe();
0190     time = prop->time();
0191     int insert = props.size();
0192     props.push_back(prop);
0193     before.insert(before.begin() + insert, prop->value());
0194     after.insert(after.begin() + insert, after_val);
0195     keyframe_before.push_back(prop->has_keyframe(time));
0196     add_0.push_back(!prop->animated() && prop->object()->document()->record_to_keyframe());
0197 }
0198 
0199 void glaxnimate::command::SetMultipleAnimated::push_property_not_animated(model::BaseProperty* prop, const QVariant& after_val)
0200 {
0201     props_not_animated.push_back(prop);
0202     before.push_back(prop->value());
0203     after.push_back(after_val);
0204 }
0205 
0206 void glaxnimate::command::SetMultipleAnimated::undo()
0207 {
0208     for ( int i = 0; i < int(props.size()); i++ )
0209     {
0210         auto prop = props[i];
0211 
0212         if ( add_0[i] )
0213             prop->remove_keyframe_at_time(0);
0214 
0215         if ( keyframe_after )
0216         {
0217             if ( keyframe_before[i] )
0218             {
0219                 prop->set_keyframe(time, before[i]);
0220             }
0221             else
0222             {
0223                 prop->remove_keyframe_at_time(time);
0224                 prop->set_value(before[i]);
0225             }
0226         }
0227         else
0228         {
0229             if ( keyframe_before[i] )
0230                 prop->set_keyframe(time, before[i]);
0231             else if ( !prop->animated() || prop->time() == time )
0232                 prop->set_value(before[i]);
0233         }
0234 
0235     }
0236 
0237     for ( int i = 0; i < int(props_not_animated.size()); i++ )
0238     {
0239         props_not_animated[i]->set_value(before[i+props.size()]);
0240     }
0241 }
0242 
0243 void glaxnimate::command::SetMultipleAnimated::redo()
0244 {
0245     for ( int i = 0; i < int(props.size()); i++ )
0246     {
0247         auto prop = props[i];
0248 
0249         if ( add_0[i] )
0250             prop->set_keyframe(0, before[i]);
0251 
0252         if ( keyframe_after )
0253             prop->set_keyframe(time, after[i]);
0254         else if ( !prop->animated() || prop->time() == time )
0255             prop->set_value(after[i]);
0256     }
0257 
0258     for ( int i = 0; i < int(props_not_animated.size()); i++ )
0259     {
0260         props_not_animated[i]->set_value(after[i+props.size()]);
0261     }
0262 }
0263 
0264 
0265 bool glaxnimate::command::SetMultipleAnimated::merge_with(const SetMultipleAnimated& other)
0266 {
0267     if ( other.props.size() != props.size() || keyframe_after != other.keyframe_after ||
0268         time != other.time || other.props_not_animated.size() != props_not_animated.size())
0269         return false;
0270 
0271     for ( int i = 0; i < int(props.size()); i++ )
0272         if ( props[i] != other.props[i] )
0273             return false;
0274 
0275     for ( int i = 0; i < int(props_not_animated.size()); i++ )
0276         if ( props_not_animated[i] != other.props_not_animated[i] )
0277             return false;
0278 
0279     after = other.after;
0280     return true;
0281 }
0282 
0283 QString glaxnimate::command::SetMultipleAnimated::auto_name(model::AnimatableBase* prop)
0284 {
0285     bool key_before = prop->has_keyframe(prop->time());
0286     bool key_after = prop->object()->document()->record_to_keyframe();
0287 
0288     if ( key_after && !key_before )
0289         return i18n("Add keyframe for %1 at %2", prop->name(), prop->time());
0290 
0291     if ( key_before )
0292         return i18n("Update %1 at %2", prop->name(), prop->time());
0293 
0294     return i18n("Update %1", prop->name());
0295 }
0296 
0297 bool glaxnimate::command::SetMultipleAnimated::empty() const
0298 {
0299     return props.empty() && props_not_animated.empty();
0300 }
0301 
0302 glaxnimate::command::SetKeyframeTransition::SetKeyframeTransition(
0303         model::AnimatableBase* prop,
0304         int keyframe_index,
0305         const model::KeyframeTransition& transition
0306     )
0307 : QUndoCommand(i18n("Update keyframe transition")),
0308     prop(prop),
0309     keyframe_index(keyframe_index),
0310     undo_value(keyframe()->transition()),
0311     redo_value(transition)
0312 {
0313 }
0314 
0315 glaxnimate::command::SetKeyframeTransition::SetKeyframeTransition(
0316     model::AnimatableBase* prop,
0317     int keyframe_index,
0318     model::KeyframeTransition::Descriptive desc,
0319     const QPointF& point,
0320     bool before_transition
0321 ) : SetKeyframeTransition(prop, keyframe_index, prop->keyframe(keyframe_index)->transition())
0322 {
0323     if ( desc == model::KeyframeTransition::Custom )
0324     {
0325         if ( before_transition )
0326             redo_value.set_before(point);
0327         else
0328             redo_value.set_after(point);
0329     }
0330     else
0331     {
0332         if ( before_transition )
0333             redo_value.set_before_descriptive(desc);
0334         else
0335             redo_value.set_after_descriptive(desc);
0336     }
0337 }
0338 
0339 void glaxnimate::command::SetKeyframeTransition::undo()
0340 {
0341     keyframe()->set_transition(undo_value);
0342 }
0343 
0344 void glaxnimate::command::SetKeyframeTransition::redo()
0345 {
0346     keyframe()->set_transition(redo_value);
0347 }
0348 
0349 glaxnimate::model::KeyframeBase* glaxnimate::command::SetKeyframeTransition::keyframe() const
0350 {
0351     return prop->keyframe(keyframe_index);
0352 }
0353 
0354 glaxnimate::command::MoveKeyframe::MoveKeyframe(
0355     model::AnimatableBase* prop,
0356     int keyframe_index,
0357     model::FrameTime time_after
0358 ) : QUndoCommand(i18n("Move keyframe")),
0359     prop(prop),
0360     keyframe_index_before(keyframe_index),
0361     time_before(prop->keyframe(keyframe_index)->time()),
0362     time_after(time_after)
0363 {}
0364 
0365 void glaxnimate::command::MoveKeyframe::undo()
0366 {
0367     prop->move_keyframe(keyframe_index_after, time_before);
0368 }
0369 
0370 void glaxnimate::command::MoveKeyframe::redo()
0371 {
0372     keyframe_index_after = prop->move_keyframe(keyframe_index_before, time_after);
0373 }
0374 
0375 int glaxnimate::command::MoveKeyframe::redo_index() const
0376 {
0377     return keyframe_index_after;
0378 }
0379 
0380 glaxnimate::command::RemoveAllKeyframes::RemoveAllKeyframes(model::AnimatableBase* prop, QVariant after)
0381     : QUndoCommand(i18n("Remove animations from %1", prop->name())),
0382       prop(prop),
0383       before(prop->value()),
0384       after(std::move(after))
0385 {
0386     int count = prop->keyframe_count();
0387     keyframes.reserve(count);
0388     for ( int i = 0; i < count; i++ )
0389     {
0390         auto kf = prop->keyframe(i);
0391         keyframes.push_back({
0392             kf->time(),
0393             kf->value(),
0394             kf->transition()
0395         });
0396     }
0397 }
0398 
0399 void glaxnimate::command::RemoveAllKeyframes::redo()
0400 {
0401     prop->clear_keyframes();
0402     prop->set_value(after);
0403 }
0404 
0405 void glaxnimate::command::RemoveAllKeyframes::undo()
0406 {
0407     for ( const auto& kf : keyframes )
0408     {
0409         prop->set_keyframe(kf.time, kf.value, nullptr, true)->set_transition(kf.transition);
0410     }
0411     prop->set_time(prop->time());
0412     prop->set_value(before);
0413 }
0414 
0415 
0416 glaxnimate::command::SetPositionBezier::SetPositionBezier(
0417     model::detail::AnimatedPropertyPosition* prop,
0418     math::bezier::Bezier after,
0419     bool commit,
0420     const QString& name
0421 )
0422     : SetPositionBezier(prop, prop->bezier(), std::move(after), commit, name)
0423 {
0424 }
0425 
0426 glaxnimate::command::SetPositionBezier::SetPositionBezier(
0427     model::detail::AnimatedPropertyPosition* prop,
0428     math::bezier::Bezier before,
0429     math::bezier::Bezier after,
0430     bool commit,
0431     const QString& name
0432 ) : Parent(name.isEmpty() ? i18n("Update animation path") : name, commit),
0433     property(prop),
0434     before(std::move(before)),
0435     after(std::move(after))
0436 {
0437 }
0438 
0439 bool glaxnimate::command::SetPositionBezier::merge_with(const glaxnimate::command::SetPositionBezier& other)
0440 {
0441     return property == other.property;
0442 }
0443 
0444 void glaxnimate::command::SetPositionBezier::undo()
0445 {
0446     property->set_bezier(before);
0447 }
0448 
0449 void glaxnimate::command::SetPositionBezier::redo()
0450 {
0451     property->set_bezier(after);
0452 }