/*** Olive - Non-Linear Video Editor Copyright (C) 2022 Olive Team Modifications Copyright (C) 2025 mikesolar This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #ifndef OAK_TIMELINEMARKER_H #define OAK_TIMELINEMARKER_H #include #include #include #include #include #include "undo/undocommand.h" using namespace olive::core; namespace olive { class TimelineMarker : public QObject { Q_OBJECT public: TimelineMarker(QObject *parent = nullptr); TimelineMarker(int color, const TimeRange &time, const QString &name = QString(), QObject *parent = nullptr); const TimeRange &time() const { return time_; } void set_time(const TimeRange &time); void set_time(const Rational &time); bool has_sibling_at_time(const Rational &t) const; const QString &name() const { return name_; } void set_name(const QString &name); int color() const { return color_; } void set_color(int c); static int get_marker_height(const QFontMetrics &fm); QRect draw(QPainter *p, const QPoint &pt, int max_right, double scale, bool selected); bool load(QXmlStreamReader *reader); void save(QXmlStreamWriter *writer) const; signals: void time_changed(const TimeRange &time); void name_changed(const QString &name); void color_changed(int c); private: TimeRange time_; QString name_; int color_; }; class TimelineMarkerList : public QObject { Q_OBJECT public: TimelineMarkerList(QObject *parent = nullptr) : QObject(parent) { } inline bool empty() const { return markers_.empty(); } inline std::vector::iterator begin() { return markers_.begin(); } inline std::vector::iterator end() { return markers_.end(); } inline std::vector::const_iterator cbegin() const { return markers_.cbegin(); } inline std::vector::const_iterator cend() const { return markers_.cend(); } inline TimelineMarker *back() const { return markers_.back(); } inline TimelineMarker *front() const { return markers_.front(); } inline size_t size() const { return markers_.size(); } bool load(QXmlStreamReader *reader); void save(QXmlStreamWriter *writer) const; TimelineMarker *get_marker_at_time(const Rational &t) const { for (auto it = markers_.cbegin(); it != markers_.cend(); it++) { TimelineMarker *m = *it; if (m->time().in() == t) { return m; } } return nullptr; } TimelineMarker *get_closest_marker_to_time(const Rational &t) const { TimelineMarker *closest = nullptr; for (auto it = markers_.cbegin(); it != markers_.cend(); it++) { TimelineMarker *m = *it; Rational this_diff = qAbs(m->time().in() - t); if (closest) { Rational stored_diff = qAbs(closest->time().in() - t); if (this_diff > stored_diff) { // Since the list is organized by time, if the diff increases, assume we are only going // to move further away from here and there's no need to check break; } } closest = m; } return closest; } signals: void marker_added(TimelineMarker *marker); void marker_removed(TimelineMarker *marker); void marker_modified(TimelineMarker *marker); protected: virtual void childEvent(QChildEvent *e) override; private: void insert_into_list(TimelineMarker *m); bool remove_from_list(TimelineMarker *m); std::vector markers_; private slots: void handle_marker_modification(); void handle_marker_time_change(); }; class MarkerAddCommand : public UndoCommand { public: MarkerAddCommand(TimelineMarkerList *marker_list, const TimeRange &range, const QString &name, int color); MarkerAddCommand(TimelineMarkerList *marker_list, TimelineMarker *marker); virtual Project *get_relevant_project() const override; protected: virtual void redo() override; virtual void undo() override; private: TimelineMarkerList *marker_list_; TimelineMarker *added_marker_; QObject memory_manager_; }; class MarkerRemoveCommand : public UndoCommand { public: MarkerRemoveCommand(TimelineMarker *marker); virtual Project *get_relevant_project() const override; protected: virtual void redo() override; virtual void undo() override; private: TimelineMarker *marker_; QObject *marker_list_; QObject memory_manager_; }; class MarkerChangeColorCommand : public UndoCommand { public: MarkerChangeColorCommand(TimelineMarker *marker, int new_color); virtual Project *get_relevant_project() const override; protected: virtual void redo() override; virtual void undo() override; private: TimelineMarker *marker_; int old_color_; int new_color_; }; class MarkerChangeNameCommand : public UndoCommand { public: MarkerChangeNameCommand(TimelineMarker *marker, QString name); virtual Project *get_relevant_project() const override; protected: virtual void redo() override; virtual void undo() override; private: TimelineMarker *marker_; QString old_name_; QString new_name_; }; class MarkerChangeTimeCommand : public UndoCommand { public: MarkerChangeTimeCommand(TimelineMarker *marker, const TimeRange &time, const TimeRange &old_time); MarkerChangeTimeCommand(TimelineMarker *marker, const TimeRange &time) : MarkerChangeTimeCommand(marker, time, marker->time()) { } virtual Project *get_relevant_project() const override; protected: virtual void redo() override; virtual void undo() override; private: TimelineMarker *marker_; TimeRange old_time_; TimeRange new_time_; }; } #endif // OAK_TIMELINEMARKER_H