@@ -104,6 +104,10 @@ void Config::SetDefaults()
|
||||
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
|
||||
|
||||
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
||||
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
||||
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1)));
|
||||
|
||||
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
|
||||
|
||||
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt, ColorCoding::kRed);
|
||||
|
||||
@@ -86,6 +86,11 @@ public:
|
||||
|
||||
virtual void MoveOutToPlayhead() override;
|
||||
|
||||
void AddDefaultTransitionsToSelected()
|
||||
{
|
||||
timeline_widget()->AddDefaultTransitionsToSelected();
|
||||
}
|
||||
|
||||
void ShowSpeedDurationDialogForSelectedClips()
|
||||
{
|
||||
timeline_widget()->ShowSpeedDurationDialogForSelectedClips();
|
||||
|
||||
@@ -291,7 +291,7 @@ void MenuShared::NestTriggered()
|
||||
|
||||
void MenuShared::DefaultTransitionTriggered()
|
||||
{
|
||||
qDebug() << "FIXME: Stub";
|
||||
PanelManager::instance()->MostRecentlyFocused<TimelinePanel>()->AddDefaultTransitionsToSelected();
|
||||
}
|
||||
|
||||
void MenuShared::TimecodeDisplayTriggered()
|
||||
|
||||
@@ -568,6 +568,22 @@ void TimelineWidget::ToggleLinksOnSelected()
|
||||
Core::instance()->undo_stack()->push(new NodeLinkManyCommand(blocks, link));
|
||||
}
|
||||
|
||||
void TimelineWidget::AddDefaultTransitionsToSelected()
|
||||
{
|
||||
QVector<ClipBlock*> blocks;
|
||||
|
||||
foreach (Block* item, GetSelectedBlocks()) {
|
||||
// Only clips can be linked
|
||||
if (ClipBlock *clip = dynamic_cast<ClipBlock*>(item)) {
|
||||
blocks.append(clip);
|
||||
}
|
||||
}
|
||||
|
||||
if (!blocks.isEmpty()) {
|
||||
Core::instance()->undo_stack()->push(new TimelineAddDefaultTransitionCommand(blocks, timebase()));
|
||||
}
|
||||
}
|
||||
|
||||
bool TimelineWidget::CopySelected(bool cut)
|
||||
{
|
||||
if (super::CopySelected(cut)) {
|
||||
|
||||
@@ -79,6 +79,8 @@ public:
|
||||
|
||||
void ToggleLinksOnSelected();
|
||||
|
||||
void AddDefaultTransitionsToSelected();
|
||||
|
||||
virtual bool CopySelected(bool cut) override;
|
||||
|
||||
virtual bool Paste() override;
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/math/math/math.h"
|
||||
#include "node/math/merge/merge.h"
|
||||
#include "timelineundocommon.h"
|
||||
#include "widget/timelinewidget/undo/timelineundotrack.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -287,8 +289,8 @@ void TrackListInsertGaps::prepare()
|
||||
QVector<Block*> blocks_to_append_gap_to;
|
||||
QVector<Track*> tracks_to_append_gap_to;
|
||||
|
||||
foreach (Track* track, working_tracks_) {
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
for (Track* track : qAsConst(working_tracks_)) {
|
||||
for (Block* b : track->Blocks()) {
|
||||
if (dynamic_cast<GapBlock*>(b) && b->in() <= point_ && b->out() >= point_) {
|
||||
// Found a gap at the location
|
||||
gaps_to_extend_.append(b);
|
||||
@@ -542,4 +544,130 @@ void TimelineRemoveTrackCommand::undo()
|
||||
remove_command_->undo_now();
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::prepare()
|
||||
{
|
||||
for (auto it=clips_.cbegin(); it!=clips_.cend(); it++) {
|
||||
ClipBlock *c = *it;
|
||||
|
||||
// Handle in transition
|
||||
if (clips_.contains(static_cast<ClipBlock*>(c->previous()))) {
|
||||
// Do nothing, assume this will be handled by a dual transition from that clip
|
||||
} else if (dynamic_cast<GapBlock*>(c->previous()) || !c->previous()) {
|
||||
// Create in transition
|
||||
AddTransition(c, kIn);
|
||||
}
|
||||
|
||||
// Handle out transition
|
||||
if (clips_.contains(static_cast<ClipBlock*>(c->next()))) {
|
||||
AddTransition(c, kOutDual);
|
||||
} else if (dynamic_cast<GapBlock*>(c->next()) || !c->next()) {
|
||||
// Create out transition
|
||||
AddTransition(c, kOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::AddTransition(ClipBlock *c, CreateTransitionMode mode)
|
||||
{
|
||||
if (Track *t = c->track()) {
|
||||
Node *p = nullptr;
|
||||
if (t->type() == Track::kVideo) {
|
||||
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultVideoTransition").toString());
|
||||
} else if (t->type() == Track::kAudio) {
|
||||
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultAudioTransition").toString());
|
||||
}
|
||||
|
||||
rational transition_length = OLIVE_CONFIG("DefaultTransitionLength").value<rational>();
|
||||
|
||||
// Resize original clip
|
||||
switch (mode) {
|
||||
case kIn:
|
||||
ValidateTransitionLength(c, transition_length);
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(c, transition_length, false);
|
||||
}
|
||||
break;
|
||||
case kOut:
|
||||
ValidateTransitionLength(c, transition_length);
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(c, transition_length, true);
|
||||
}
|
||||
break;
|
||||
case kOutDual:
|
||||
{
|
||||
rational half_length = transition_length / 2;
|
||||
|
||||
ValidateTransitionLength(static_cast<ClipBlock*>(c->next()), half_length);
|
||||
ValidateTransitionLength(c, half_length);
|
||||
|
||||
transition_length = half_length * 2;
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(static_cast<ClipBlock*>(c->next()), half_length, false);
|
||||
AdjustClipLength(c, half_length, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (transition_length > 0) {
|
||||
if (TransitionBlock *transition = dynamic_cast<TransitionBlock*>(p)) {
|
||||
transition->set_length_and_media_out(transition_length);
|
||||
|
||||
// Add transition
|
||||
commands_.append(new NodeAddCommand(c->parent(), transition));
|
||||
|
||||
// Insert block
|
||||
Block *insert_after;
|
||||
switch (mode) {
|
||||
case kIn:
|
||||
insert_after = c->previous();
|
||||
break;
|
||||
case kOut:
|
||||
case kOutDual:
|
||||
insert_after = c;
|
||||
break;
|
||||
}
|
||||
commands_.append(new TrackInsertBlockAfterCommand(c->track(), transition, insert_after));
|
||||
|
||||
// Connect
|
||||
switch (mode) {
|
||||
case kIn:
|
||||
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kInBlockInput)));
|
||||
break;
|
||||
case kOutDual:
|
||||
commands_.append(new NodeEdgeAddCommand(c->next(), NodeInput(transition, TransitionBlock::kInBlockInput)));
|
||||
/* fall through */
|
||||
case kOut:
|
||||
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kOutBlockInput)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out)
|
||||
{
|
||||
rational cur_len = lengths_.value(c, c->length());
|
||||
rational new_len = cur_len - transition_length;
|
||||
if (out) {
|
||||
commands_.append(new BlockResizeCommand(c, new_len));
|
||||
} else {
|
||||
commands_.append(new BlockResizeWithMediaInCommand(c, new_len));
|
||||
}
|
||||
lengths_.insert(c, new_len);
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::ValidateTransitionLength(ClipBlock *c, rational &transition_length)
|
||||
{
|
||||
rational cur_len = lengths_.value(c, c->length());
|
||||
rational half_cur_len = cur_len/2;
|
||||
if (transition_length >= half_cur_len) {
|
||||
transition_length = half_cur_len - timebase_;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -358,6 +358,61 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class TimelineAddDefaultTransitionCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TimelineAddDefaultTransitionCommand(const QVector<ClipBlock*> &clips, const rational &timebase) :
|
||||
clips_(clips),
|
||||
timebase_(timebase)
|
||||
{}
|
||||
|
||||
virtual ~TimelineAddDefaultTransitionCommand() override
|
||||
{
|
||||
qDeleteAll(commands_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return clips_.empty() ? nullptr : clips_.first()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
|
||||
(*it)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
|
||||
(*it)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum CreateTransitionMode {
|
||||
kIn,
|
||||
kOut,
|
||||
kOutDual
|
||||
};
|
||||
|
||||
void AddTransition(ClipBlock *c, CreateTransitionMode mode);
|
||||
void AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out);
|
||||
void ValidateTransitionLength(ClipBlock *c, rational &transition_length);
|
||||
|
||||
|
||||
QVector<ClipBlock*> clips_;
|
||||
rational timebase_;
|
||||
QVector<UndoCommand*> commands_;
|
||||
|
||||
QHash<ClipBlock*, rational> lengths_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOGENERAL_H
|
||||
|
||||
Reference in New Issue
Block a user