/*** Olive - Non-Linear Video Editor Copyright (C) 2022 Olive Team 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 "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(); 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(); /** * @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(); signals: void BlockSelectionChanged(const QVector& selected_blocks); void RequestCaptureStart(const TimeRange &time, const Track::Reference &track); void RevealViewerInProject(ViewerOutput *r); protected: virtual void resizeEvent(QResizeEvent *event) override; virtual void TimebaseChangedEvent(const rational &) override; virtual void TimeChangedEvent(const rational &time) 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_; } 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); QVector GetBlocksInGlobalRect(const QPoint &p1, const QPoint &p2); bool PasteInternal(bool insert); TimelineAndTrackView *AddTimelineAndTrackView(Qt::Alignment alignment); QPoint drag_origin_; QRubberBand rubberband_; TimelineWidgetSelections rubberband_old_selections_; QVector rubberband_now_selected_; 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 SetViewTime(const rational &time); void ToolChanged(); void AddableObjectChanged(); void SetViewWaveformsEnabled(bool e); void FrameRateChanged(); void SampleRateChanged(); void TrackIndexChanged(int old, int now); void SetScrollZoomsByDefaultOnAllViews(bool e); void SignalBlockSelectionChange(); void RevealInProject(); void RenameSelectedBlocks(); void TrackAboutToBeDeleted(Track *track); }; } #endif // TIMELINEWIDGET_H