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

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 "compound_timeline_widget.hpp"
0008 #include "ui_compound_timeline_widget.h"
0009 
0010 #include <QMenu>
0011 #include <QScrollBar>
0012 #include <QClipboard>
0013 #include <QMimeData>
0014 #include <QSignalBlocker>
0015 #include <QActionGroup>
0016 
0017 #include "math/bezier/meta.hpp"
0018 #include "model/shapes/precomp_layer.hpp"
0019 #include "command/animation_commands.hpp"
0020 #include "command/undo_macro_guard.hpp"
0021 
0022 #include "item_models/property_model_full.hpp"
0023 #include "item_models/comp_filter_model.hpp"
0024 
0025 #include "glaxnimate_app.hpp"
0026 
0027 #include "style/property_delegate.hpp"
0028 #include "style/fixed_height_delegate.hpp"
0029 #include "widgets/dialogs/keyframe_editor_dialog.hpp"
0030 
0031 #ifndef Q_OS_ANDROID
0032     #include "widgets/menus/node_menu.hpp"
0033 #endif
0034 #include "widgets/menus/animated_property_menu.hpp"
0035 #include "widgets/timeline/keyframe_transition_data.hpp"
0036 
0037 using namespace glaxnimate::gui;
0038 using namespace glaxnimate;
0039 
0040 class CompoundTimelineWidget::Private
0041 {
0042 public:
0043     void setupUi(CompoundTimelineWidget* parent)
0044     {
0045         ui.setupUi(parent);
0046         comp_model.setSourceModel(&property_model);
0047 
0048         QPalette prop_pal = ui.properties->palette();
0049         prop_pal.setBrush(
0050             QPalette::Inactive,
0051             QPalette::Highlight,
0052             prop_pal.brush(QPalette::Active, QPalette::Highlight)
0053         );
0054         prop_pal.setBrush(
0055             QPalette::Inactive,
0056             QPalette::HighlightedText,
0057             prop_pal.brush(QPalette::Active, QPalette::HighlightedText)
0058         );
0059         ui.properties->setPalette(prop_pal);
0060 
0061         ui.properties->setModel(&comp_model);
0062         ui.timeline->set_model(&comp_model, &property_model, ui.properties);
0063         connect(&property_model, &QAbstractItemModel::dataChanged,
0064                 ui.properties->viewport(), (void (QWidget::*)())&QWidget::update);
0065 
0066         ui.properties->setItemDelegate(&fixed_height_delegate);
0067         ui.properties->setItemDelegateForColumn(item_models::PropertyModelFull::ColumnValue, &property_delegate);
0068         ui.properties->setItemDelegateForColumn(item_models::PropertyModelFull::ColumnColor, &color_delegate);
0069 
0070         ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnName, QHeaderView::ResizeToContents);
0071         ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnValue, QHeaderView::Stretch);
0072         ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnColor, QHeaderView::ResizeToContents);
0073         ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnLocked, QHeaderView::ResizeToContents);
0074         ui.properties->header()->setSectionResizeMode(item_models::PropertyModelFull::ColumnVisible, QHeaderView::ResizeToContents);
0075         ui.properties->header()->moveSection(item_models::PropertyModelFull::ColumnColor, 0);
0076         ui.properties->header()->moveSection(item_models::PropertyModelFull::ColumnVisible, 1);
0077         ui.properties->header()->moveSection(item_models::PropertyModelFull::ColumnLocked, 2);
0078 
0079         ui.properties->setUniformRowHeights(true);
0080         ui.properties->header()->setFixedHeight(ui.timeline->header_height());
0081         property_delegate.set_forced_height(ui.timeline->header_height());
0082         fixed_height_delegate.set_height(ui.timeline->row_height());
0083         ui.properties->setRootIsDecorated(true);
0084 
0085         ui.properties->verticalScrollBar()->installEventFilter(parent);
0086         ui.properties->verticalScrollBar()->setSingleStep(ui.scrollbar->singleStep());
0087         ui.properties->verticalScrollBar()->setPageStep(ui.scrollbar->pageStep());
0088         connect(ui.properties->verticalScrollBar(), &QScrollBar::valueChanged, ui.scrollbar, &QScrollBar::setValue);
0089         connect(ui.scrollbar, &QScrollBar::valueChanged, ui.properties->verticalScrollBar(), &QScrollBar::setValue);
0090         connect(ui.properties->verticalScrollBar(), &QScrollBar::rangeChanged, ui.scrollbar, &QScrollBar::setRange);
0091         connect(ui.scrollbar, &QScrollBar::valueChanged, parent, &CompoundTimelineWidget::on_scroll);
0092 
0093         connect(ui.timeline, &TimelineWidget::line_clicked, parent, &CompoundTimelineWidget::select_line);
0094         connect(ui.properties->selectionModel(), &QItemSelectionModel::currentChanged, parent, &CompoundTimelineWidget::select_index);
0095         connect(ui.properties->selectionModel(), &QItemSelectionModel::selectionChanged, parent, &CompoundTimelineWidget::_on_selection_changed);
0096 
0097         ui.action_add_keyframe->setIcon(QIcon::fromTheme("keyframe-add"));
0098         connect(ui.action_add_keyframe, &QAction::triggered, parent, &CompoundTimelineWidget::add_keyframe);
0099 
0100         connect(parent, &QWidget::customContextMenuRequested, parent, &CompoundTimelineWidget::custom_context_menu);
0101 
0102         setup_menu(parent);
0103 
0104     }
0105 
0106     void setup_menu(CompoundTimelineWidget* parent)
0107     {
0108         for ( int i = 0; i < KeyframeTransitionData::count; i++ )
0109         {
0110             auto data = KeyframeTransitionData::from_index(i, KeyframeTransitionData::Full);
0111             actions_enter[i].setIcon(data.icon());
0112             actions_enter[i].setActionGroup(&enter);
0113             actions_enter[i].setData(data.variant());
0114 
0115             actions_leave[i].setIcon(data.icon());
0116             actions_leave[i].setActionGroup(&exit);
0117             actions_leave[i].setData(data.variant());
0118         }
0119 
0120         menu_keyframe.addAction(&action_kf_remove);
0121         action_kf_remove.setIcon(QIcon::fromTheme("keyframe-remove"));
0122         connect(&action_kf_remove, &QAction::triggered, parent, &CompoundTimelineWidget::remove_keyframe);
0123 
0124 
0125         menu_keyframe.addAction(&action_kf_copy);
0126         action_kf_copy.setIcon(QIcon::fromTheme("keyframe-duplicate"));
0127         connect(&action_kf_copy, &QAction::triggered, parent, &CompoundTimelineWidget::copy_keyframe);
0128 
0129         action_kf_paste.setIcon(QIcon::fromTheme("edit-paste"));
0130         menu_keyframe.addAction(&action_kf_paste);
0131 
0132         menu_keyframe.addSeparator();
0133 
0134         menu_keyframe.addAction(&action_kf_remove_all);
0135         action_kf_remove_all.setIcon(QIcon::fromTheme("edit-clear-all"));
0136         connect(&action_kf_remove_all, &QAction::triggered, &menu_property, &AnimatedPropertyMenu::remove_all_keyframes);
0137 
0138         action_enter = menu_keyframe.addSeparator();
0139         for ( QAction* ac : enter.actions() )
0140         {
0141             ac->setCheckable(true);
0142             menu_keyframe.addAction(ac);
0143             connect(ac, &QAction::triggered, parent, &CompoundTimelineWidget::keyframe_action_enter);
0144         }
0145 
0146         action_exit = menu_keyframe.addSeparator();
0147         for ( QAction* ac : exit.actions() )
0148         {
0149             ac->setCheckable(true);
0150             menu_keyframe.addAction(ac);
0151             connect(ac, &QAction::triggered, parent, &CompoundTimelineWidget::keyframe_action_exit);
0152         }
0153 
0154         retranslate_menu();
0155     }
0156 
0157     void retranslate_menu()
0158     {
0159         action_enter->setText(i18n("Transition From Previous"));
0160         action_exit->setText(i18n("Transition To Next"));
0161 
0162 
0163         for ( int i = 0; i < KeyframeTransitionData::count; i++ )
0164         {
0165             if ( i == model::KeyframeTransition::Custom )
0166             {
0167                 actions_enter[i].setText(i18n("Custom..."));
0168                 actions_leave[i].setText(actions_enter[i].text());
0169             }
0170             else
0171             {
0172                 actions_leave[i].setText(KeyframeTransitionData::from_index(i, KeyframeTransitionData::Start).name);
0173                 actions_enter[i].setText(KeyframeTransitionData::from_index(i, KeyframeTransitionData::Finish).name);
0174             }
0175         }
0176 
0177         action_kf_remove.setText(i18n("Remove Keyframe"));
0178         action_kf_remove_all.setText(i18n("Clear Animations"));
0179 
0180         action_kf_copy.setText(i18n("Copy Keyframe"));
0181         action_kf_paste.setText(i18n("Paste Keyframe"));
0182     }
0183 
0184     void retranslateUi(CompoundTimelineWidget* parent)
0185     {
0186         ui.retranslateUi(parent);
0187         retranslate_menu();
0188     }
0189 
0190     void clear_menu_data()
0191     {
0192         menu_property.set_property(nullptr);
0193         menu_kf_enter = nullptr;
0194         menu_kf_exit = nullptr;
0195     }
0196 
0197     void keyframe_action(CompoundTimelineWidget* parent, model::KeyframeBase* keyframe, bool before_transition, QAction* action)
0198     {
0199 
0200         if ( !keyframe || !menu_property.property() )
0201             return;
0202 
0203         int index = menu_property.property()->keyframe_index(keyframe);
0204         if ( index == -1 )
0205             return;
0206 
0207         QVariant data = action->data();
0208         if ( data.isValid() && data.toInt() != model::KeyframeTransition::Custom )
0209         {
0210             menu_property.property()->object()->push_command(
0211                 new command::SetKeyframeTransition(
0212                     menu_property.property(), index, data.value<model::KeyframeTransition::Descriptive>(),
0213                     before_transition ? keyframe->transition().before() : keyframe->transition().after(),
0214                     before_transition
0215                 )
0216             );
0217         }
0218         else
0219         {
0220             KeyframeEditorDialog keyframe_editor(keyframe->transition(), parent);
0221             keyframe_editor.setWindowModality(Qt::ApplicationModal);
0222             if ( keyframe_editor.exec() )
0223             {
0224                 menu_property.property()->object()->push_command(
0225                     new command::SetKeyframeTransition(
0226                         menu_property.property(), index,
0227                         keyframe_editor.transition()
0228                     )
0229                 );
0230             }
0231         }
0232     }
0233 
0234     void toggle_paste()
0235     {
0236         action_kf_paste.setEnabled(menu_property.can_paste());
0237     }
0238 
0239     void emit_click(CompoundTimelineWidget* parent, QModelIndex index)
0240     {
0241         model::VisualNode* node = nullptr;
0242         do
0243         {
0244             node = property_model.visual_node(comp_model.mapToSource(index));
0245             index = index.parent();
0246         }
0247         while ( !node && index.isValid() );
0248 
0249         if ( node )
0250             Q_EMIT parent->current_node_changed(node);
0251     }
0252 
0253     model::VisualNode* index_node_or_parent(QModelIndex property_index)
0254     {
0255         while ( property_index.isValid() )
0256         {
0257             auto item = property_model.item(property_index);
0258             model::Object* obj = item.object;
0259             if ( !obj && item.property )
0260             {
0261                 obj = item.property->object();
0262                 if ( !obj )
0263                     return nullptr;
0264             }
0265 
0266             if ( auto visual = obj->cast<model::VisualNode>() )
0267                 return visual;
0268 
0269             if ( obj->is_instance<model::Asset>() )
0270                 return nullptr;
0271 
0272             property_index = property_index.parent();
0273         }
0274 
0275         return nullptr;
0276     }
0277 
0278 
0279     Ui_CompoundTimelineWidget ui;
0280     item_models::PropertyModelFull property_model;
0281     style::PropertyDelegate property_delegate;
0282     color_widgets::ColorDelegate color_delegate;
0283     style::FixedHeightDelegate fixed_height_delegate;
0284     item_models::CompFilterModel comp_model;
0285 
0286     AnimatedPropertyMenu menu_property;
0287 
0288     QAction* action_enter;
0289     QAction* action_exit;
0290     QAction action_kf_copy;
0291     QAction action_kf_paste;
0292     QAction action_kf_remove;
0293     QAction action_kf_remove_all;
0294 
0295     std::array<QAction, KeyframeTransitionData::count> actions_enter;
0296     std::array<QAction, KeyframeTransitionData::count> actions_leave;
0297 
0298     QMenu menu_keyframe;
0299     QActionGroup enter{&menu_keyframe};
0300     QActionGroup exit{&menu_keyframe};
0301 
0302     model::KeyframeBase* menu_kf_enter = nullptr;
0303     model::KeyframeBase* menu_kf_exit = nullptr;
0304 
0305     GlaxnimateWindow* window = nullptr;
0306 };
0307 
0308 CompoundTimelineWidget::CompoundTimelineWidget(QWidget* parent)
0309     : QWidget(parent), d(std::make_unique<Private>())
0310 {
0311     d->setupUi(this);
0312     connect(d->ui.tab_bar, &CompositionTabBar::switch_composition, this, &CompoundTimelineWidget::switch_composition);
0313     connect(&d->property_model, &QAbstractItemModel::rowsRemoved, this, &CompoundTimelineWidget::rows_removed);
0314 }
0315 
0316 
0317 CompoundTimelineWidget::~CompoundTimelineWidget() = default;
0318 
0319 void CompoundTimelineWidget::changeEvent ( QEvent* e )
0320 {
0321     QWidget::changeEvent(e);
0322     if ( e->type() == QEvent::LanguageChange)
0323     {
0324         d->retranslateUi(this);
0325     }
0326 }
0327 
0328 void CompoundTimelineWidget::set_composition(model::Composition* comp)
0329 {
0330     QSignalBlocker g(d->ui.tab_bar);
0331     d->ui.tab_bar->set_current_composition(comp);
0332     d->comp_model.set_composition(comp);
0333 
0334     on_scroll(d->ui.scrollbar->value());
0335 }
0336 
0337 void CompoundTimelineWidget::set_current_node(model::DocumentNode* node)
0338 {
0339     QModelIndex index = d->comp_model.mapFromSource(d->property_model.object_index(node));
0340     d->ui.properties->expand(index);
0341     d->ui.properties->setCurrentIndex(index);
0342     d->clear_menu_data();
0343 }
0344 
0345 void CompoundTimelineWidget::set_document(model::Document* document)
0346 {
0347     d->ui.timeline->set_document(document);
0348     d->property_model.set_document(document);
0349     d->clear_menu_data();
0350     d->ui.tab_bar->set_document(document);
0351     d->comp_model.set_composition(nullptr);
0352 }
0353 
0354 void CompoundTimelineWidget::clear_document()
0355 {
0356     set_document(nullptr);
0357 }
0358 
0359 void CompoundTimelineWidget::select_index(const QModelIndex& index)
0360 {
0361 //     d->ui.timeline->select(index);
0362 //     d->ui.properties->viewport()->update();
0363     d->emit_click(this, index);
0364 }
0365 
0366 void CompoundTimelineWidget::select_line(quintptr id, bool selected, bool replace_selection)
0367 {
0368     QModelIndex index = d->comp_model.mapFromSource(d->property_model.index_by_id(id));
0369     if ( selected )
0370     {
0371         d->ui.properties->selectionModel()->setCurrentIndex(
0372             index,
0373             QItemSelectionModel::Rows | QItemSelectionModel::Select |
0374             ( replace_selection ? QItemSelectionModel::Clear : QItemSelectionModel::NoUpdate )
0375         );
0376     }
0377     else
0378     {
0379         d->ui.properties->selectionModel()->select(
0380             index,
0381             QItemSelectionModel::Rows | QItemSelectionModel::Deselect
0382         );
0383     }
0384     d->ui.properties->viewport()->update();
0385     d->emit_click(this, index);
0386 }
0387 
0388 void CompoundTimelineWidget::custom_context_menu(const QPoint& p)
0389 {
0390     d->clear_menu_data();
0391 
0392     d->ui.timeline->keep_highlight();
0393 
0394     QPoint glob = static_cast<QWidget*>(sender())->mapToGlobal(p);
0395 
0396     item_models::PropertyModelFull::Item item;
0397     if ( d->ui.properties->rect().contains(p) )
0398     {
0399         item = d->property_model.item(d->comp_model.mapToSource(
0400             d->ui.properties->indexAt(
0401                 d->ui.properties->viewport()->mapFromGlobal(glob)
0402             )
0403         ));
0404     }
0405     else
0406     {
0407         std::tie(d->menu_kf_enter, d->menu_kf_exit) = d->ui.timeline->keyframe_at(
0408             d->ui.timeline->viewport()->mapFromGlobal(glob)
0409         );
0410 
0411         item = d->ui.timeline->item_at(
0412             d->ui.timeline->viewport()->mapFromGlobal(glob)
0413         );
0414     }
0415 
0416     if ( item.property && item.property->traits().flags & model::PropertyTraits::Animated )
0417         d->menu_property.set_property(static_cast<model::AnimatableBase*>(item.property));
0418 
0419     if ( d->menu_kf_exit )
0420     {
0421         d->enter.setEnabled(d->menu_kf_enter);
0422         if ( d->menu_kf_enter )
0423         {
0424             d->actions_enter[d->menu_kf_enter->transition().after_descriptive()].setChecked(true);
0425         }
0426 
0427         d->actions_leave[d->menu_kf_exit->transition().before_descriptive()].setChecked(true);
0428 
0429         d->toggle_paste();
0430         d->menu_keyframe.exec(glob);
0431     }
0432     else if ( d->menu_property.property() )
0433     {
0434         d->menu_property.refresh_actions();
0435         d->menu_property.exec(glob);
0436     }
0437 #ifndef MOBILE_UI
0438     else if ( auto dn = qobject_cast<model::DocumentNode*>(item.object) )
0439     {
0440         NodeMenu(dn, d->window, this).exec(glob);
0441     }
0442 #endif
0443 }
0444 
0445 void CompoundTimelineWidget::add_keyframe()
0446 {
0447     if ( !d->menu_property.property() )
0448         return;
0449 
0450     d->menu_property.property()->add_smooth_keyframe_undoable(
0451         d->ui.timeline->highlighted_time(), d->menu_property.property()->value()
0452     );
0453 }
0454 
0455 void CompoundTimelineWidget::on_scroll(int amount)
0456 {
0457     int scroll = -d->ui.timeline->header_height() + amount * d->ui.timeline->row_height();
0458     d->ui.timeline->verticalScrollBar()->setValue(scroll);
0459 }
0460 
0461 void CompoundTimelineWidget::keyframe_action_enter()
0462 {
0463     d->keyframe_action(this, d->menu_kf_enter, false, static_cast<QAction*>(sender()));
0464 }
0465 
0466 void CompoundTimelineWidget::keyframe_action_exit()
0467 {
0468     d->keyframe_action(this, d->menu_kf_exit, true, static_cast<QAction*>(sender()));
0469 }
0470 
0471 void CompoundTimelineWidget::remove_keyframe()
0472 {
0473     if ( !d->menu_kf_exit || !d->menu_property.property() )
0474         return;
0475 
0476     d->menu_property.property()->object()->document()->undo_stack().push(
0477         new command::RemoveKeyframeTime(d->menu_property.property(), d->menu_kf_exit->time())
0478     );
0479 }
0480 
0481 void CompoundTimelineWidget::load_state(const QByteArray& state)
0482 {
0483     d->ui.splitter->restoreState(state);
0484 }
0485 
0486 QByteArray CompoundTimelineWidget::save_state() const
0487 {
0488     return d->ui.splitter->saveState();
0489 }
0490 
0491 #ifndef MOBILE_UI
0492 void CompoundTimelineWidget::set_controller(GlaxnimateWindow* window)
0493 {
0494     d->window = window;
0495     d->menu_property.set_controller(window);
0496 }
0497 #endif
0498 
0499 void CompoundTimelineWidget::copy_keyframe()
0500 {
0501     if ( !d->menu_kf_exit || !d->menu_property.property() )
0502         return;
0503 
0504     QMimeData* data = new QMimeData;
0505     QByteArray encoded;
0506     QDataStream stream(&encoded, QIODevice::WriteOnly);
0507     stream << int(d->menu_property.property()->traits().type);
0508     stream << d->menu_kf_exit->value();
0509     data->setData("application/x.glaxnimate-keyframe", encoded);
0510     QGuiApplication::clipboard()->setMimeData(data);
0511 }
0512 
0513 
0514 void CompoundTimelineWidget::collapse_index(const QModelIndex& index)
0515 {
0516     d->ui.timeline->collapse(index);
0517 }
0518 
0519 void CompoundTimelineWidget::expand_index(const QModelIndex& index)
0520 {
0521     d->ui.timeline->expand(index);
0522 }
0523 
0524 
0525 void CompoundTimelineWidget::click_index ( const QModelIndex& index )
0526 {
0527     auto source_index = d->comp_model.mapToSource(index);
0528     if ( auto node = d->property_model.visual_node(source_index) )
0529     {
0530         if ( index.column() == item_models::PropertyModelFull::ColumnVisible )
0531             node->visible.set_undoable(!node->visible.get());
0532         else if ( index.column() == item_models::PropertyModelFull::ColumnLocked )
0533             node->locked.set_undoable(!node->locked.get());
0534     }
0535     else if ( auto anprop = d->property_model.animatable(source_index) )
0536     {
0537         if ( index.column() == item_models::PropertyModelFull::ColumnToggleKeyframe )
0538         {
0539             auto time = d->property_model.document()->current_time();
0540             if ( anprop->has_keyframe(time) )
0541             {
0542                 d->property_model.document()->push_command(new command::RemoveKeyframeTime(anprop, time));
0543             }
0544             else
0545             {
0546                 d->property_model.document()->push_command(new command::SetKeyframe(anprop, time, anprop->value(), true));
0547             }
0548         }
0549         else if ( index.column() == item_models::PropertyModelFull::ColumnPrevKeyframe )
0550         {
0551             if ( anprop->keyframe_count() < 2 )
0552                 return;
0553 
0554             auto time = d->property_model.document()->current_time();
0555             auto kfindex = anprop->keyframe_index(time);
0556             auto kf = anprop->keyframe(kfindex);
0557 
0558             if ( qFuzzyCompare(kf->time(), time) || kf->time() > time )
0559             {
0560                 kfindex -= 1;
0561             }
0562 
0563             if ( kfindex < 0 )
0564                 kfindex = anprop->keyframe_count() - 1;
0565 
0566             d->property_model.document()->set_current_time(anprop->keyframe(kfindex)->time());
0567         }
0568         else if ( index.column() == item_models::PropertyModelFull::ColumnNextKeyframe )
0569         {
0570             if ( anprop->keyframe_count() < 2 )
0571                 return;
0572 
0573             auto time = d->property_model.document()->current_time();
0574             auto kfindex = anprop->keyframe_index(time);
0575             auto kf = anprop->keyframe(kfindex);
0576 
0577             if ( qFuzzyCompare(kf->time(), time) || kf->time() < time )
0578             {
0579                 kfindex += 1;
0580             }
0581 
0582             if ( kfindex == anprop->keyframe_count() )
0583                 kfindex = 0;
0584 
0585             d->property_model.document()->set_current_time(anprop->keyframe(kfindex)->time());
0586         }
0587     }
0588 }
0589 
0590 void CompoundTimelineWidget::_on_selection_changed(const QItemSelection &selected, const QItemSelection &deselected)
0591 {
0592     std::vector<model::VisualNode*> selected_nodes;
0593     std::vector<model::VisualNode*> deselected_nodes;
0594 
0595     for ( const auto& index : selected.indexes() )
0596     {
0597         if ( auto node = d->index_node_or_parent(d->comp_model.mapToSource(index)) )
0598             selected_nodes.push_back(node);
0599     }
0600 
0601     for ( const auto& index : deselected.indexes() )
0602     {
0603         if ( auto node = d->index_node_or_parent(d->comp_model.mapToSource(index)) )
0604             deselected_nodes.push_back(node);
0605     }
0606 
0607     d->ui.timeline->select(selected, deselected);
0608 
0609     Q_EMIT selection_changed(selected_nodes, deselected_nodes);
0610 }
0611 
0612 QAbstractItemModel * CompoundTimelineWidget::raw_model() const
0613 {
0614     return &d->property_model;
0615 }
0616 
0617 QAbstractItemModel * CompoundTimelineWidget::filtered_model() const
0618 {
0619     return &d->comp_model;
0620 }
0621 
0622 TimelineWidget * CompoundTimelineWidget::timeline() const
0623 {
0624     return d->ui.timeline;
0625 }
0626 
0627 void CompoundTimelineWidget::rows_removed(const QModelIndex& index, int first, int last)
0628 {
0629     if ( d->property_model.document() )
0630     {
0631         for ( int i = first; i <= last; i++ )
0632         {
0633             auto id = d->property_model.index(i, 0, index).internalId();
0634             if ( id == d->comp_model.get_root_id() )
0635             {
0636                 set_composition(d->comp_model.composition());
0637                 return;
0638             }
0639         }
0640     }
0641 }
0642 
0643 bool CompoundTimelineWidget::eventFilter(QObject*, QEvent* event)
0644 {
0645     // For some reason scrolling on the tree view doesn't respect step size
0646     if ( event->type() == QEvent::Wheel )
0647     {
0648         QApplication::sendEvent(d->ui.scrollbar, event);
0649         return true;
0650     }
0651 
0652     return false;
0653 }
0654 
0655 void CompoundTimelineWidget::reset_view()
0656 {
0657     d->ui.timeline->reset_view();
0658 }
0659 
0660 void CompoundTimelineWidget::select(const std::vector<model::VisualNode *>& selected, const std::vector<model::VisualNode *>& deselected)
0661 {
0662     QItemSelection selected_indices;
0663     QItemSelection deselected_indices;
0664 
0665     for ( const auto& node : selected )
0666     {
0667         auto index = d->comp_model.mapFromSource(d->property_model.node_index(node));
0668         if ( index.isValid() )
0669             selected_indices.push_back(QItemSelectionRange(index));
0670     }
0671 
0672     for ( const auto& node : deselected )
0673     {
0674         auto index = d->comp_model.mapFromSource(d->property_model.node_index(node));
0675         if ( index.isValid() )
0676             deselected_indices.push_back(QItemSelectionRange(index));
0677     }
0678 
0679     d->ui.timeline->select(selected_indices, deselected_indices);
0680 
0681 
0682     d->ui.properties->selectionModel()->select(
0683         selected_indices,
0684         QItemSelectionModel::Rows | QItemSelectionModel::Select
0685     );
0686 
0687     d->ui.properties->selectionModel()->select(
0688         deselected_indices,
0689         QItemSelectionModel::Rows | QItemSelectionModel::Deselect
0690     );
0691 }
0692 
0693 model::DocumentNode * glaxnimate::gui::CompoundTimelineWidget::current_node() const
0694 {
0695     return d->index_node_or_parent(d->comp_model.mapToSource(d->ui.properties->currentIndex()));
0696 }
0697 
0698 QModelIndex glaxnimate::gui::CompoundTimelineWidget::current_index_raw() const
0699 {
0700     return d->comp_model.mapToSource(d->ui.properties->currentIndex());
0701 }
0702 
0703 QModelIndex glaxnimate::gui::CompoundTimelineWidget::current_index_filtered() const
0704 {
0705     return d->ui.properties->currentIndex();
0706 }