/*** 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 TIMELINEWIDGET_H #define TIMELINEWIDGET_H #include #include #include #include "core.h" #include "node/block/transition/transition.h" #include "node/output/viewer/viewer.h" #include "node/project/serializer/serializer.h" #include "timeline/timelinecommon.h" #include "timelineandtrackview.h" #include "widget/slider/rationalslider.h" #include "widget/timebased/timebasedwidget.h" #include "widget/timelinewidget/timelinewidgetselections.h" #include "widget/timelinewidget/tool/import.h" #include "widget/timelinewidget/tool/tool.h" namespace olive { /** * @brief Full widget for working with TimelineOutput nodes * * Encapsulates TimelineViews, TimeRulers, and scrollbars for a complete widget to manipulate Timelines */ class TimelineWidget : public TimeBasedWidget { Q_OBJECT public: TimelineWidget(QWidget *parent = nullptr); virtual ~TimelineWidget() override; void Clear(); void SelectAll(); void DeselectAll(); void RippleToIn(); void RippleToOut(); void EditToIn(); void EditToOut(); void SplitAtPlayhead(); void DeleteSelected(bool ripple = false); void IncreaseTrackHeight(); void DecreaseTrackHeight(); void InsertFootageAtPlayhead(const QVector &footage); void OverwriteFootageAtPlayhead(const QVector &footage); void ToggleLinksOnSelected(); void AddDefaultTransitionsToSelected(); virtual bool CopySelected(bool cut) override; virtual bool Paste() override; void PasteInsert(); void DeleteInToOut(bool ripple); void ToggleSelectedEnabled(); void SetColorLabel(int index); void NudgeLeft(); void NudgeRight(); void MoveInToPlayhead(); void MoveOutToPlayhead(); void ShowSpeedDurationDialogForSelectedClips(); void RecordingCallback(const QString &filename, const TimeRange &time, const Track::Reference &track); void EnableRecordingOverlay(const TimelineCoordinate &coord); void DisableRecordingOverlay(); void AddTentativeSubtitleTrack(); void NestSelectedClips(); /** * @brief Timelines should always be connected to sequences */ Sequence *sequence() const { return static_cast(GetConnectedNode()); } const QVector &GetSelectedBlocks() const { return selected_blocks_; } QByteArray SaveSplitterState() const; void RestoreSplitterState(const QByteArray &state); static void ReplaceBlocksWithGaps(const QVector &blocks, bool remove_from_graph, MultiUndoCommand *command, bool handle_transitions = true); /** * @brief Retrieve the QGraphicsItem at a particular scene position * * Requires a float-based scene position. If you have a screen position, use GetScenePos() first to convert it to a * scene position */ Block *GetItemAtScenePos(const TimelineCoordinate &coord); void AddSelection(const TimeRange &time, const Track::Reference &track); void AddSelection(Block *item); void RemoveSelection(const TimeRange &time, const Track::Reference &track); void RemoveSelection(Block *item); const TimelineWidgetSelections &GetSelections() const { return selections_; } void SetSelections(const TimelineWidgetSelections &s, bool process_block_changes); Track *GetTrackFromReference(const Track::Reference &ref) const; void SetViewBeamCursor(const TimelineCoordinate &coord); void SetViewTransitionOverlay(ClipBlock *out, ClipBlock *in); const QVector &GetGhostItems() const { return ghost_items_; } void InsertGapsAt(const rational &time, const rational &length, MultiUndoCommand *command); void StartRubberBandSelect(const QPoint &global_cursor_start); void MoveRubberBandSelect(bool enable_selecting, bool select_links); void EndRubberBandSelect(); int GetTrackY(const Track::Reference &ref); int GetTrackHeight(const Track::Reference &ref); void AddGhost(TimelineViewGhostItem *ghost); void ClearGhosts(); bool HasGhosts() const { return !ghost_items_.isEmpty(); } bool IsBlockSelected(Block *b) const { return selected_blocks_.contains(b); } void SetBlockLinksSelected(ClipBlock *block, bool selected); void QueueScroll(int value); TimelineView *GetFirstTimelineView(); rational GetTimebaseForTrackType(Track::Type type); const QRect &GetRubberBandGeometry() const; /** * @brief Track blocks that have newly been selected (this is preferred over emitting BlocksSelected directly) * * TimelineWidget keeps track of which blocks are selected internally. Calling this function will * add to that list and emit a signal to other widgets that said blocks have been selected. * * @param selected_blocks * * The list of blocks to add to the internal selection list and signal. * * @param filter * * TRUE to automatically filter blocks that are already selected from the list. In most cases, * this is preferable and should only be set to FALSE if the list is guaranteed not to contain * already selected blocks (and therefore filtering can be skipped to save time). */ void SignalSelectedBlocks(QVector selected_blocks, bool filter = true); /** * @brief Track blocks that have been newly deselected */ void SignalDeselectedBlocks(const QVector &deselected_blocks); /** * @brief Convenience function to deselect all blocks and signal them */ void SignalDeselectedAllBlocks(); void Refresh() { UpdateViewports(); } MultiUndoCommand *TakeSubtitleSectionCommand() { // Copy pointer MultiUndoCommand *c = subtitle_show_command_; // Set to null subtitle_show_command_ = nullptr; subtitle_tentative_track_ = nullptr; // Return command return c; } class SetSelectionsCommand : public UndoCommand { public: SetSelectionsCommand(TimelineWidget *timeline, const TimelineWidgetSelections &now, const TimelineWidgetSelections &old, bool process_block_changes = true) : timeline_(timeline) , old_(old) , now_(now) , process_block_changes_(process_block_changes) { } virtual Project *GetRelevantProject() const override { return nullptr; } protected: virtual void redo() override { timeline_->SetSelections(now_, process_block_changes_); } virtual void undo() override { timeline_->SetSelections(old_, process_block_changes_); } private: TimelineWidget *timeline_; TimelineWidgetSelections old_; TimelineWidgetSelections now_; bool process_block_changes_; }; public slots: void ClearTentativeSubtitleTrack(); void RenameSelectedBlocks(); signals: void BlockSelectionChanged(const QVector &selected_blocks); void RequestCaptureStart(const TimeRange &time, const Track::Reference &track); void RevealViewerInFootageViewer(ViewerOutput *r, const TimeRange &range); void RevealViewerInProject(ViewerOutput *r); protected: virtual void resizeEvent(QResizeEvent *event) override; virtual void TimeChangedEvent(const rational &) override; virtual void TimebaseChangedEvent(const rational &) override; virtual void ScaleChangedEvent(const double &) override; virtual void ConnectNodeEvent(ViewerOutput *n) override; virtual void DisconnectNodeEvent(ViewerOutput *n) override; virtual const QVector *GetSnapBlocks() const override { return &added_blocks_; } protected slots: virtual void SendCatchUpScrollEvent() override; private: QVector GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode); void RippleTo(Timeline::MovementMode mode); void EditTo(Timeline::MovementMode mode); void UpdateViewports(const Track::Type &type = Track::kNone); bool PasteInternal(bool insert); TimelineAndTrackView *AddTimelineAndTrackView(Qt::Alignment alignment); QHash GenerateExistingPasteMap(const ProjectSerializer::Result &r); QRubberBand rubberband_; QVector rubberband_scene_pos_; TimelineWidgetSelections rubberband_old_selections_; QVector rubberband_now_selected_; bool rubberband_enable_selecting_; bool rubberband_select_links_; TimelineWidgetSelections selections_; TimelineTool *GetActiveTool(); QVector tools_; ImportTool *import_tool_; TimelineTool *active_tool_; QVector ghost_items_; QVector views_; RationalSlider *timecode_label_; QVector selected_blocks_; QVector added_blocks_; int deferred_scroll_value_; bool use_audio_time_units_; QSplitter *view_splitter_; MultiUndoCommand *subtitle_show_command_; Track *subtitle_tentative_track_; QTimer *signal_block_change_timer_; class SetSplitterSizesCommand : public UndoCommand { public: SetSplitterSizesCommand(QSplitter *splitter, const QList &sizes) : splitter_(splitter) , new_sizes_(sizes) { } virtual Project *GetRelevantProject() const override { return nullptr; } protected: virtual void redo() override; virtual void undo() override; private: QSplitter *splitter_; QList new_sizes_; QList old_sizes_; }; void CenterOn(qreal scene_pos); void UpdateViewTimebases(); void NudgeInternal(rational amount); void MoveToPlayheadInternal(bool out); private slots: void ViewMousePressed(TimelineViewMouseEvent *event); void ViewMouseMoved(TimelineViewMouseEvent *event); void ViewMouseReleased(TimelineViewMouseEvent *event); void ViewMouseDoubleClicked(TimelineViewMouseEvent *event); void ViewDragEntered(TimelineViewMouseEvent *event); void ViewDragMoved(TimelineViewMouseEvent *event); void ViewDragLeft(QDragLeaveEvent *event); void ViewDragDropped(TimelineViewMouseEvent *event); void AddBlock(Block *block); void RemoveBlock(Block *blocks); void AddTrack(Track *track); void RemoveTrack(Track *track); void TrackUpdated(); void BlockUpdated(); void UpdateHorizontalSplitters(); void UpdateTimecodeWidthFromSplitters(QSplitter *s); void ShowContextMenu(); void DeferredScrollAction(); void ShowSequenceDialog(); void SetUseAudioTimeUnits(bool use); void ToolChanged(); void AddableObjectChanged(); void SetViewWaveformsEnabled(bool e); void SetViewThumbnailsEnabled(QAction *action); void FrameRateChanged(); void SampleRateChanged(); void TrackIndexChanged(int old, int now); void SignalBlockSelectionChange(); void RevealInFootageViewer(); void RevealInProject(); void TrackAboutToBeDeleted(Track *track); void SetSelectedClipsAutocaching(bool e); void CacheClips(); void CacheClipsInOut(); void CacheDiscard(); void MulticamEnabledTriggered(bool e); void ForceUpdateRubberBand(); }; } #endif // TIMELINEWIDGET_H