File indexing completed on 2025-02-02 04:11:30

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 "animated_property_menu.hpp"
0008 
0009 #include <QClipboard>
0010 #include <QMimeData>
0011 
0012 #include "widgets/dialogs/follow_path_dialog.hpp"
0013 #include "command/animation_commands.hpp"
0014 #include "command/undo_macro_guard.hpp"
0015 #include "glaxnimate_app.hpp"
0016 #include "widgets/dialogs/glaxnimate_window.hpp"
0017 
0018 using namespace glaxnimate::gui;
0019 
0020 class glaxnimate::gui::AnimatedPropertyMenu::Private
0021 {
0022 public:
0023     Private(AnimatedPropertyMenu* parent)
0024     {
0025         parent->setIcon(QIcon::fromTheme("label"));
0026         action_title = parent->addSeparator();
0027 
0028         parent->addAction(&action_kf_paste);
0029         action_kf_paste.setIcon(QIcon::fromTheme("edit-paste"));
0030         connect(&action_kf_paste, &QAction::triggered, parent, &AnimatedPropertyMenu::paste_keyframe);
0031 
0032         parent->addAction(&action_add_keyframe);
0033         action_add_keyframe.setIcon(QIcon::fromTheme("keyframe-add"));
0034         connect(&action_add_keyframe, &QAction::triggered, parent, &AnimatedPropertyMenu::add_keyframe);
0035 
0036         parent->addAction(&action_remove_keyframe);
0037         action_remove_keyframe.setIcon(QIcon::fromTheme("keyframe-remove"));
0038         connect(&action_remove_keyframe, &QAction::triggered, parent, &AnimatedPropertyMenu::remove_keyframe);
0039 
0040         action_remove_all_keyframes.setIcon(QIcon::fromTheme("edit-clear-all"));
0041         connect(&action_remove_all_keyframes, &QAction::triggered, parent, &AnimatedPropertyMenu::remove_all_keyframes);
0042         parent->addAction(&action_remove_all_keyframes);
0043 
0044         parent->addSeparator();
0045 
0046         action_kf_loop.setIcon(QIcon::fromTheme("media-repeat-all"));
0047         connect(&action_kf_loop, &QAction::triggered, parent, &AnimatedPropertyMenu::loop_keyframes);
0048         parent->addAction(&action_kf_loop);
0049 
0050         action_follow_path.setIcon(QIcon::fromTheme("draw-bezier-curves"));
0051         connect(&action_follow_path, &QAction::triggered, parent, &AnimatedPropertyMenu::follow_path);
0052         parent->addAction(&action_follow_path);
0053         action_follow_path.setVisible(false);
0054 
0055         retranslate_menu();
0056     }
0057 
0058     void retranslate_menu()
0059     {
0060         action_remove_all_keyframes.setText(i18n("Clear Animations"));
0061         action_kf_paste.setText(i18n("Paste Keyframe"));
0062         action_kf_loop.setText(i18n("Loop Animation"));
0063         action_follow_path.setText(i18n("Follow Path..."));
0064         action_add_keyframe.setText(i18n("Add Keyframe"));
0065         action_remove_keyframe.setText(i18n("Remove Keyframe"));
0066     }
0067 
0068     model::Composition* comp()
0069     {
0070         if ( auto shape = property->object()->cast<model::ShapeElement>() )
0071             return shape->owner_composition();
0072 
0073         return window->current_composition();
0074     }
0075 
0076     model::AnimatableBase* property = nullptr;
0077     QAction* action_title;
0078     QAction action_kf_loop;
0079     QAction action_kf_paste;
0080     QAction action_follow_path;
0081     QAction action_add_keyframe;
0082     QAction action_remove_keyframe;
0083     QAction action_remove_all_keyframes;
0084     SelectionManager* window = nullptr;
0085 };
0086 
0087 glaxnimate::gui::AnimatedPropertyMenu::AnimatedPropertyMenu(QWidget* parent)
0088     : QMenu(parent), d(std::make_unique<Private>(this))
0089 {
0090 }
0091 
0092 glaxnimate::gui::AnimatedPropertyMenu::~AnimatedPropertyMenu() = default;
0093 
0094 void glaxnimate::gui::AnimatedPropertyMenu::paste_keyframe()
0095 {
0096     if ( !d->property )
0097         return;
0098 
0099     const QMimeData* data = QGuiApplication::clipboard()->mimeData();
0100     if ( !data->hasFormat("application/x.glaxnimate-keyframe") )
0101         return;
0102 
0103     QByteArray encoded = data->data("application/x.glaxnimate-keyframe");
0104     QDataStream stream(&encoded, QIODevice::ReadOnly);
0105     int type = model::PropertyTraits::Unknown;
0106     stream >> type;
0107     if ( type != d->property->traits().type )
0108         return;
0109 
0110     QVariant value;
0111     stream >> value;
0112 
0113     d->property->object()->push_command(
0114         new command::SetKeyframe(d->property, d->property->time(), value, true)
0115     );
0116 }
0117 
0118 void glaxnimate::gui::AnimatedPropertyMenu::loop_keyframes()
0119 {
0120     if ( !d->property || d->property->keyframe_count() < 1 )
0121         return;
0122 
0123     d->property->object()->push_command(new command::SetKeyframe(
0124         d->property,
0125         d->comp()->animation->last_frame.get(),
0126         d->property->keyframe(0)->value(),
0127         true
0128     ));
0129 }
0130 
0131 void glaxnimate::gui::AnimatedPropertyMenu::follow_path()
0132 {
0133     if ( d->property && d->property->traits().type == model::PropertyTraits::Point )
0134     {
0135         auto prop = static_cast<model::AnimatedProperty<QPointF>*>(d->property);
0136         FollowPathDialog(prop, d->comp(), d->window->model(), parentWidget()).exec();
0137     }
0138 }
0139 
0140 void glaxnimate::gui::AnimatedPropertyMenu::remove_all_keyframes()
0141 {
0142     if ( !d->property )
0143         return;
0144 
0145     d->property->clear_keyframes_undoable();
0146 }
0147 
0148 void glaxnimate::gui::AnimatedPropertyMenu::set_controller(glaxnimate::gui::SelectionManager* window)
0149 {
0150     d->window = window;
0151 }
0152 
0153 void glaxnimate::gui::AnimatedPropertyMenu::changeEvent(QEvent* e)
0154 {
0155     QWidget::changeEvent(e);
0156     if ( e->type() == QEvent::LanguageChange)
0157     {
0158         d->retranslate_menu();
0159     }
0160 }
0161 
0162 void glaxnimate::gui::AnimatedPropertyMenu::set_property(model::AnimatableBase* property)
0163 {
0164     d->property = property;
0165     if ( property )
0166     {
0167         setTitle(d->property->name());
0168         d->action_title->setText(d->property->name());
0169         d->action_follow_path.setVisible(property->traits().type == model::PropertyTraits::Point);
0170         refresh_actions();
0171     }
0172 }
0173 
0174 void glaxnimate::gui::AnimatedPropertyMenu::refresh_actions()
0175 {
0176     if ( d->property )
0177     {
0178         bool has_kf = d->property->has_keyframe(d->property->time());
0179         d->action_remove_keyframe.setEnabled(has_kf);
0180         d->action_remove_all_keyframes.setEnabled(d->property->keyframe_count() > 0);
0181         d->action_kf_loop.setEnabled(d->property->keyframe_count() > 0);
0182         d->action_kf_paste.setEnabled(can_paste());
0183     }
0184 }
0185 
0186 
0187 glaxnimate::model::AnimatableBase * glaxnimate::gui::AnimatedPropertyMenu::property() const
0188 {
0189     return d->property;
0190 }
0191 
0192 bool glaxnimate::gui::AnimatedPropertyMenu::can_paste() const
0193 {
0194     const QMimeData* data = QGuiApplication::clipboard()->mimeData();
0195     if ( d->property && data->hasFormat("application/x.glaxnimate-keyframe") )
0196     {
0197         QByteArray encoded = data->data("application/x.glaxnimate-keyframe");
0198         QDataStream stream(&encoded, QIODevice::ReadOnly);
0199         int type = model::PropertyTraits::Unknown;
0200         stream >> type;
0201         return type == d->property->traits().type;
0202     }
0203 
0204     return false;
0205 }
0206 
0207 
0208 void glaxnimate::gui::AnimatedPropertyMenu::add_keyframe()
0209 {
0210     if ( d->property )
0211         d->property->add_smooth_keyframe_undoable(d->property->time(), d->property->value());
0212 }
0213 
0214 void glaxnimate::gui::AnimatedPropertyMenu::remove_keyframe()
0215 {
0216     if ( d->property )
0217         d->property->object()->push_command(
0218             new command::RemoveKeyframeTime(d->property, d->property->time())
0219         );
0220 }
0221