Merge branch 'master' of https://github.com/olive-editor/olive
This commit is contained in:
@@ -100,6 +100,10 @@ endif()
|
||||
if (WIN32)
|
||||
# Set Windows application icon
|
||||
target_sources(olive-editor PRIVATE packaging/windows/resources.rc)
|
||||
|
||||
set_target_properties(olive-editor PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
elseif(APPLE)
|
||||
# Set Mac application icon
|
||||
set(OLIVE_ICON packaging/macos/olive.icns)
|
||||
|
||||
@@ -58,4 +58,18 @@ int QtUtils::MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString &
|
||||
return b.exec();
|
||||
}
|
||||
|
||||
QDateTime QtUtils::GetCreationDate(const QFileInfo &info)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
return info.created();
|
||||
#else
|
||||
return info.birthTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
|
||||
{
|
||||
return dt.toString(Qt::TextDate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
#include <QFontMetrics>
|
||||
#include <QFrame>
|
||||
#include <QMessageBox>
|
||||
@@ -54,6 +56,10 @@ public:
|
||||
|
||||
static int MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||
|
||||
static QString GetFormattedDateTime(const QDateTime &dt);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -202,6 +202,13 @@ void TimeRangeList::remove(const TimeRange &remove)
|
||||
util_remove(&array_, remove);
|
||||
}
|
||||
|
||||
void TimeRangeList::remove(const TimeRangeList &list)
|
||||
{
|
||||
for (const TimeRange &r : list) {
|
||||
remove(r);
|
||||
}
|
||||
}
|
||||
|
||||
bool TimeRangeList::contains(const TimeRange &range, bool in_inclusive, bool out_inclusive) const
|
||||
{
|
||||
for (int i=0;i<size();i++) {
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
void insert(TimeRange range_to_add);
|
||||
|
||||
void remove(const TimeRange& remove);
|
||||
void remove(const TimeRangeList &list);
|
||||
|
||||
template <typename T>
|
||||
static void util_remove(QVector<T> *list, const TimeRange &remove)
|
||||
@@ -148,6 +149,16 @@ public:
|
||||
return array_.constEnd();
|
||||
}
|
||||
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
const TimeRange& first() const
|
||||
{
|
||||
return array_.first();
|
||||
|
||||
@@ -136,6 +136,7 @@ void SequenceDialog::accept()
|
||||
sequence_->SetVideoParams(video_params);
|
||||
sequence_->SetAudioParams(audio_params);
|
||||
sequence_->SetLabel(name_field_->text());
|
||||
sequence_->SetAutoCacheEnabled(parameter_tab_->GetSelectedPreviewAutoCache());
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
|
||||
@@ -215,6 +215,20 @@ void Block::LinkChangeEvent()
|
||||
}
|
||||
}
|
||||
|
||||
bool Block::HashPassthrough(const QString &input, const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const
|
||||
{
|
||||
if (IsInputConnected(input)) {
|
||||
rational t = InputTimeAdjustment(input, -1, TimeRange(time, time)).in();
|
||||
|
||||
NodeOutput out = GetConnectedOutput(input);
|
||||
out.node()->Hash(out.output(), hash, t, video_params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Block::set_length_internal(const rational &length)
|
||||
{
|
||||
SetStandardValue(kLengthInput, QVariant::fromValue(length));
|
||||
|
||||
@@ -179,6 +179,8 @@ protected:
|
||||
|
||||
virtual void LinkChangeEvent() override;
|
||||
|
||||
bool HashPassthrough(const QString &input, const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const;
|
||||
|
||||
Block* previous_;
|
||||
Block* next_;
|
||||
|
||||
|
||||
@@ -117,14 +117,7 @@ void ClipBlock::Retranslate()
|
||||
|
||||
void ClipBlock::Hash(const QString &out, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const
|
||||
{
|
||||
Q_UNUSED(out)
|
||||
|
||||
if (IsInputConnected(kBufferIn)) {
|
||||
rational t = InputTimeAdjustment(kBufferIn, -1, TimeRange(time, time)).in();
|
||||
|
||||
NodeOutput output = GetConnectedOutput(kBufferIn);
|
||||
output.node()->Hash(output.output(), hash, t, video_params);
|
||||
}
|
||||
HashPassthrough(kBufferIn, out, hash, time, video_params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,16 +121,18 @@ double TransitionBlock::GetInProgress(const double &time) const
|
||||
|
||||
void TransitionBlock::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const
|
||||
{
|
||||
Node::Hash(output, hash, time, video_params);
|
||||
if (HashPassthrough(kInBlockInput, output, hash, time, video_params) || HashPassthrough(kOutBlockInput, output, hash, time, video_params)) {
|
||||
HashAddNodeSignature(hash, output);
|
||||
|
||||
double time_dbl = time.toDouble();
|
||||
double all_prog = GetTotalProgress(time_dbl);
|
||||
double in_prog = GetInProgress(time_dbl);
|
||||
double out_prog = GetOutProgress(time_dbl);
|
||||
|
||||
hash.addData(reinterpret_cast<const char*>(&all_prog), sizeof(double));
|
||||
hash.addData(reinterpret_cast<const char*>(&in_prog), sizeof(double));
|
||||
hash.addData(reinterpret_cast<const char*>(&out_prog), sizeof(double));
|
||||
hash.addData(reinterpret_cast<const char*>(&all_prog), sizeof(all_prog));
|
||||
hash.addData(reinterpret_cast<const char*>(&in_prog), sizeof(in_prog));
|
||||
hash.addData(reinterpret_cast<const char*>(&out_prog), sizeof(out_prog));
|
||||
}
|
||||
}
|
||||
|
||||
double TransitionBlock::GetInternalTransitionTime(const double &time) const
|
||||
@@ -217,7 +219,7 @@ NodeValueTable TransitionBlock::Value(const QString &output, NodeValueDatabase &
|
||||
|
||||
void TransitionBlock::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
TimeRange r;
|
||||
TimeRange r = range;
|
||||
|
||||
if (from == kOutBlockInput || from == kInBlockInput) {
|
||||
Block *n = dynamic_cast<Block*>(GetConnectedNode(from));
|
||||
@@ -283,7 +285,7 @@ void TransitionBlock::InputDisconnectedEvent(const QString &input, int element,
|
||||
|
||||
if (input == kOutBlockInput) {
|
||||
if (connected_out_block_) {
|
||||
connected_out_block_->set_in_transition(nullptr);
|
||||
connected_out_block_->set_out_transition(nullptr);
|
||||
connected_out_block_ = nullptr;
|
||||
}
|
||||
} else if (input == kInBlockInput) {
|
||||
|
||||
@@ -335,7 +335,7 @@ void TransformDistortNode::Hash(const QString &output, QCryptographicHash &hash,
|
||||
|
||||
if (!matrix.isIdentity()) {
|
||||
// Add fingerprint
|
||||
hash.addData(id().toUtf8());
|
||||
HashAddNodeSignature(hash, output);
|
||||
hash.addData(reinterpret_cast<const char*>(&matrix), sizeof(matrix));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rati
|
||||
|
||||
if (!passthrough_base && !passthrough_blend) {
|
||||
// This merge will actually do something so we add a fingerprint
|
||||
hash.addData(id().toUtf8());
|
||||
HashAddNodeSignature(hash, output);
|
||||
}
|
||||
|
||||
if (!passthrough_base) {
|
||||
|
||||
+7
-4
@@ -1241,6 +1241,12 @@ bool Node::AreLinked(Node *a, Node *b)
|
||||
return a->links_.contains(b);
|
||||
}
|
||||
|
||||
void Node::HashAddNodeSignature(QCryptographicHash &hash, const QString &output) const
|
||||
{
|
||||
hash.addData(id().toUtf8());
|
||||
hash.addData(output.toUtf8());
|
||||
}
|
||||
|
||||
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, Node::InputFlags flags, int index)
|
||||
{
|
||||
if (id.isEmpty()) {
|
||||
@@ -1434,11 +1440,8 @@ void Node::SetLabel(const QString &s)
|
||||
|
||||
void Node::Hash(const QString &output, QCryptographicHash &hash, const rational& time, const VideoParams &video_params) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
// Add this Node's ID and output being used
|
||||
hash.addData(id().toUtf8());
|
||||
hash.addData(output.toUtf8());
|
||||
HashAddNodeSignature(hash, output);
|
||||
|
||||
auto inputs = inputs_for_output(output);
|
||||
foreach (const QString& input, inputs) {
|
||||
|
||||
@@ -183,6 +183,9 @@ public:
|
||||
|
||||
virtual QString duration() const {return QString();}
|
||||
|
||||
virtual qint64 creation_time() const {return 0;}
|
||||
virtual qint64 mod_time() const {return 0;}
|
||||
|
||||
virtual QString rate() const {return QString();}
|
||||
|
||||
const QVector<QString>& inputs() const
|
||||
@@ -849,6 +852,8 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
void HashAddNodeSignature(QCryptographicHash &hash, const QString &output) const;
|
||||
|
||||
void InsertInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags, int index);
|
||||
|
||||
void PrependInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags = InputFlags(kInputFlagNormal))
|
||||
|
||||
@@ -62,22 +62,30 @@ public:
|
||||
|
||||
static rational TransformTimeForBlock(const Block* block, const rational& time)
|
||||
{
|
||||
if (time == RATIONAL_MAX || time == RATIONAL_MIN) {
|
||||
return time;
|
||||
}
|
||||
|
||||
return time - block->in();
|
||||
}
|
||||
|
||||
static TimeRange TransformRangeForBlock(const Block* block, const TimeRange& range)
|
||||
{
|
||||
return range - block->in();
|
||||
return TimeRange(TransformTimeForBlock(block, range.in()), TransformTimeForBlock(block, range.out()));
|
||||
}
|
||||
|
||||
static rational TransformTimeFromBlock(const Block* block, const rational& time)
|
||||
{
|
||||
if (time == RATIONAL_MAX || time == RATIONAL_MIN) {
|
||||
return time;
|
||||
}
|
||||
|
||||
return time + block->in();
|
||||
}
|
||||
|
||||
static TimeRange TransformRangeFromBlock(const Block* block, const TimeRange& range)
|
||||
{
|
||||
return range + block->in();
|
||||
return TimeRange(TransformTimeFromBlock(block, range.in()), TransformTimeFromBlock(block, range.out()));
|
||||
}
|
||||
|
||||
const double& GetTrackHeight() const;
|
||||
|
||||
@@ -104,10 +104,14 @@ public:
|
||||
|
||||
foreach (Node* node, item_children_) {
|
||||
T* cast_test = dynamic_cast<T*>(node);
|
||||
|
||||
if (cast_test) {
|
||||
list.append(cast_test);
|
||||
}
|
||||
|
||||
Folder *folder_test = dynamic_cast<Folder*>(node);
|
||||
if (folder_test) {
|
||||
list.append(folder_test->ListChildrenOfType<T>());
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "codec/decoder.h"
|
||||
#include "common/clamp.h"
|
||||
#include "common/filefunctions.h"
|
||||
#include "common/qtutils.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
@@ -483,6 +484,28 @@ rational Footage::AdjustTimeByLoopMode(rational time, Footage::LoopMode loop_mod
|
||||
return time;
|
||||
}
|
||||
|
||||
qint64 Footage::creation_time() const
|
||||
{
|
||||
QFileInfo info(filename());
|
||||
|
||||
if (info.exists()) {
|
||||
return QtUtils::GetCreationDate(info).toSecsSinceEpoch();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 Footage::mod_time() const
|
||||
{
|
||||
QFileInfo info(filename());
|
||||
|
||||
if (info.exists()) {
|
||||
return info.lastModified().toSecsSinceEpoch();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Footage::UpdateTooltip()
|
||||
{
|
||||
if (valid_) {
|
||||
|
||||
@@ -193,6 +193,9 @@ public:
|
||||
|
||||
static rational AdjustTimeByLoopMode(rational time, LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase);
|
||||
|
||||
virtual qint64 creation_time() const override;
|
||||
virtual qint64 mod_time() const override;
|
||||
|
||||
static const QString kFilenameInput;
|
||||
static const QString kLoopModeInput;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QMimeData>
|
||||
#include <QUrl>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "core.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
@@ -34,10 +35,6 @@ ProjectViewModel::ProjectViewModel(QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
project_(nullptr)
|
||||
{
|
||||
// FIXME: make this configurable
|
||||
columns_.append(kName);
|
||||
columns_.append(kDuration);
|
||||
columns_.append(kRate);
|
||||
}
|
||||
|
||||
Project *ProjectViewModel::project() const
|
||||
@@ -124,17 +121,18 @@ int ProjectViewModel::columnCount(const QModelIndex &parent) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
return columns_.size();
|
||||
return kColumnCount;
|
||||
}
|
||||
|
||||
QVariant ProjectViewModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
Node* internal_item = GetItemObjectFromIndex(index);
|
||||
|
||||
ColumnType column_type = columns_.at(index.column());
|
||||
ColumnType column_type = static_cast<ColumnType>(index.column());
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case kInnerTextRole:
|
||||
{
|
||||
// Standard text role
|
||||
|
||||
@@ -145,6 +143,30 @@ QVariant ProjectViewModel::data(const QModelIndex &index, int role) const
|
||||
return internal_item->duration();
|
||||
case kRate:
|
||||
return internal_item->rate();
|
||||
case kLastModified:
|
||||
case kCreatedTime:
|
||||
{
|
||||
qint64 using_time = (column_type == kLastModified) ? internal_item->mod_time() : internal_item->creation_time();
|
||||
|
||||
if (using_time == 0) {
|
||||
// 0 is the null value, return nothing
|
||||
break;
|
||||
}
|
||||
|
||||
QVariant ret;
|
||||
|
||||
if (role == kInnerTextRole) {
|
||||
// Use time value directly for correct sorting
|
||||
ret = using_time;
|
||||
} else {
|
||||
// Display role, format to a human readable string
|
||||
ret = QtUtils::GetFormattedDateTime(QDateTime::fromSecsSinceEpoch(using_time));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
case kColumnCount:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -166,7 +188,7 @@ QVariant ProjectViewModel::headerData(int section, Qt::Orientation orientation,
|
||||
// Check if we need text data (DisplayRole) and orientation is horizontal
|
||||
// FIXME I'm not 100% sure what happens if the orientation is vertical/if that check is necessary
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
ColumnType column_type = columns_.at(section);
|
||||
ColumnType column_type = static_cast<ColumnType>(section);
|
||||
|
||||
// Return the name based on the column's current type
|
||||
switch (column_type) {
|
||||
@@ -176,6 +198,12 @@ QVariant ProjectViewModel::headerData(int section, Qt::Orientation orientation,
|
||||
return tr("Duration");
|
||||
case kRate:
|
||||
return tr("Rate");
|
||||
case kLastModified:
|
||||
return tr("Modified");
|
||||
case kCreatedTime:
|
||||
return tr("Created");
|
||||
case kColumnCount:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +222,7 @@ bool ProjectViewModel::hasChildren(const QModelIndex &parent) const
|
||||
bool ProjectViewModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
// The name is editable
|
||||
if (index.isValid() && columns_.at(index.column()) == kName && role == Qt::EditRole) {
|
||||
if (index.isValid() && index.column() == kName && role == Qt::EditRole) {
|
||||
Node* item = GetItemObjectFromIndex(index);
|
||||
|
||||
QString new_name = value.toString();
|
||||
@@ -233,7 +261,7 @@ Qt::ItemFlags ProjectViewModel::flags(const QModelIndex &index) const
|
||||
}
|
||||
|
||||
// If the column is the kName column, that means it's editable
|
||||
if (columns_.at(index.column()) == kName) {
|
||||
if (index.column() == kName) {
|
||||
f |= Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,20 @@ public:
|
||||
kDuration,
|
||||
|
||||
/// Media rate (frame rate for video, sample rate for audio)
|
||||
kRate
|
||||
kRate,
|
||||
|
||||
/// Last modified time (for footage/files)
|
||||
kLastModified,
|
||||
|
||||
/// Creation time (for footage/files)
|
||||
kCreatedTime,
|
||||
|
||||
/// Count
|
||||
kColumnCount
|
||||
};
|
||||
|
||||
static const int kInnerTextRole = Qt::UserRole + 1;
|
||||
|
||||
/**
|
||||
* @brief ProjectViewModel Constructor
|
||||
*
|
||||
@@ -137,8 +148,6 @@ private:
|
||||
|
||||
Project* project_;
|
||||
|
||||
QVector<ColumnType> columns_;
|
||||
|
||||
private slots:
|
||||
void FolderBeginInsertItem(Node *n, int insert_index);
|
||||
|
||||
|
||||
@@ -38,67 +38,67 @@ TimelinePanel::TimelinePanel(QWidget *parent) :
|
||||
|
||||
void TimelinePanel::SplitAtPlayhead()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->SplitAtPlayhead();
|
||||
timeline_widget()->SplitAtPlayhead();
|
||||
}
|
||||
|
||||
QByteArray TimelinePanel::SaveSplitterState() const
|
||||
{
|
||||
return static_cast<TimelineWidget*>(GetTimeBasedWidget())->SaveSplitterState();
|
||||
return timeline_widget()->SaveSplitterState();
|
||||
}
|
||||
|
||||
void TimelinePanel::RestoreSplitterState(const QByteArray &state)
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->RestoreSplitterState(state);
|
||||
timeline_widget()->RestoreSplitterState(state);
|
||||
}
|
||||
|
||||
void TimelinePanel::SelectAll()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->SelectAll();
|
||||
timeline_widget()->SelectAll();
|
||||
}
|
||||
|
||||
void TimelinePanel::DeselectAll()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->DeselectAll();
|
||||
timeline_widget()->DeselectAll();
|
||||
}
|
||||
|
||||
void TimelinePanel::RippleToIn()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->RippleToIn();
|
||||
timeline_widget()->RippleToIn();
|
||||
}
|
||||
|
||||
void TimelinePanel::RippleToOut()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->RippleToOut();
|
||||
timeline_widget()->RippleToOut();
|
||||
}
|
||||
|
||||
void TimelinePanel::EditToIn()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->EditToIn();
|
||||
timeline_widget()->EditToIn();
|
||||
}
|
||||
|
||||
void TimelinePanel::EditToOut()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->EditToOut();
|
||||
timeline_widget()->EditToOut();
|
||||
}
|
||||
|
||||
void TimelinePanel::DeleteSelected()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->DeleteSelected(false);
|
||||
timeline_widget()->DeleteSelected(false);
|
||||
}
|
||||
|
||||
void TimelinePanel::RippleDelete()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->DeleteSelected(true);
|
||||
timeline_widget()->DeleteSelected(true);
|
||||
}
|
||||
|
||||
void TimelinePanel::IncreaseTrackHeight()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->IncreaseTrackHeight();
|
||||
timeline_widget()->IncreaseTrackHeight();
|
||||
}
|
||||
|
||||
void TimelinePanel::DecreaseTrackHeight()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->DecreaseTrackHeight();
|
||||
timeline_widget()->DecreaseTrackHeight();
|
||||
}
|
||||
|
||||
void TimelinePanel::Insert()
|
||||
@@ -121,57 +121,67 @@ void TimelinePanel::Overwrite()
|
||||
|
||||
void TimelinePanel::ToggleLinks()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->ToggleLinksOnSelected();
|
||||
timeline_widget()->ToggleLinksOnSelected();
|
||||
}
|
||||
|
||||
void TimelinePanel::CutSelected()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->CopySelected(true);
|
||||
timeline_widget()->CopySelected(true);
|
||||
}
|
||||
|
||||
void TimelinePanel::CopySelected()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->CopySelected(false);
|
||||
timeline_widget()->CopySelected(false);
|
||||
}
|
||||
|
||||
void TimelinePanel::Paste()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->Paste(false);
|
||||
timeline_widget()->Paste(false);
|
||||
}
|
||||
|
||||
void TimelinePanel::PasteInsert()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->Paste(true);
|
||||
timeline_widget()->Paste(true);
|
||||
}
|
||||
|
||||
void TimelinePanel::DeleteInToOut()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->DeleteInToOut(false);
|
||||
timeline_widget()->DeleteInToOut(false);
|
||||
}
|
||||
|
||||
void TimelinePanel::RippleDeleteInToOut()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->DeleteInToOut(true);
|
||||
timeline_widget()->DeleteInToOut(true);
|
||||
}
|
||||
|
||||
void TimelinePanel::ToggleSelectedEnabled()
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->ToggleSelectedEnabled();
|
||||
timeline_widget()->ToggleSelectedEnabled();
|
||||
}
|
||||
|
||||
void TimelinePanel::SetColorLabel(int index)
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->SetColorLabel(index);
|
||||
timeline_widget()->SetColorLabel(index);
|
||||
}
|
||||
|
||||
void TimelinePanel::NudgeLeft()
|
||||
{
|
||||
timeline_widget()->NudgeLeft();
|
||||
}
|
||||
|
||||
void TimelinePanel::NudgeRight()
|
||||
{
|
||||
timeline_widget()->NudgeRight();
|
||||
}
|
||||
|
||||
void TimelinePanel::InsertFootageAtPlayhead(const QVector<ViewerOutput *> &footage)
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->InsertFootageAtPlayhead(footage);
|
||||
timeline_widget()->InsertFootageAtPlayhead(footage);
|
||||
}
|
||||
|
||||
void TimelinePanel::OverwriteFootageAtPlayhead(const QVector<ViewerOutput *> &footage)
|
||||
{
|
||||
static_cast<TimelineWidget*>(GetTimeBasedWidget())->OverwriteFootageAtPlayhead(footage);
|
||||
timeline_widget()->OverwriteFootageAtPlayhead(footage);
|
||||
}
|
||||
|
||||
void TimelinePanel::Retranslate()
|
||||
|
||||
@@ -35,6 +35,11 @@ class TimelinePanel : public TimeBasedPanel
|
||||
public:
|
||||
TimelinePanel(QWidget* parent);
|
||||
|
||||
inline TimelineWidget *timeline_widget() const
|
||||
{
|
||||
return static_cast<TimelineWidget*>(GetTimeBasedWidget());
|
||||
}
|
||||
|
||||
void SplitAtPlayhead();
|
||||
|
||||
QByteArray SaveSplitterState() const;
|
||||
@@ -83,6 +88,10 @@ public:
|
||||
|
||||
virtual void SetColorLabel(int index) override;
|
||||
|
||||
virtual void NudgeLeft() override;
|
||||
|
||||
virtual void NudgeRight() override;
|
||||
|
||||
void InsertFootageAtPlayhead(const QVector<ViewerOutput *> &footage);
|
||||
|
||||
void OverwriteFootageAtPlayhead(const QVector<ViewerOutput *> &footage);
|
||||
|
||||
@@ -170,6 +170,10 @@ public:
|
||||
|
||||
virtual void SetColorLabel(int){}
|
||||
|
||||
virtual void NudgeLeft(){}
|
||||
|
||||
virtual void NudgeRight(){}
|
||||
|
||||
signals:
|
||||
void CloseRequested();
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ ProjectExplorer::ProjectExplorer(QWidget *parent) :
|
||||
|
||||
// Set up sort filter proxy model
|
||||
sort_model_.setSourceModel(&model_);
|
||||
sort_model_.setSortRole(ProjectViewModel::kInnerTextRole);
|
||||
|
||||
// Add tree view to stacked widget
|
||||
tree_view_ = new ProjectExplorerTreeView(stacked_widget_);
|
||||
|
||||
@@ -504,7 +504,7 @@ void TimelineWidget::DeleteSelected(bool ripple)
|
||||
ReplaceBlocksWithGaps(clips_to_delete, true, command);
|
||||
|
||||
// Remove all selections
|
||||
command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections()));
|
||||
command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections(), false));
|
||||
|
||||
// Insert ripple command now that it's all cleaned up gaps
|
||||
if (ripple) {
|
||||
@@ -724,6 +724,20 @@ void TimelineWidget::SetColorLabel(int index)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::NudgeLeft()
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
NudgeInternal(-timebase());
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::NudgeRight()
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
NudgeInternal(timebase());
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational &insert_length, MultiUndoCommand *command)
|
||||
{
|
||||
for (int i=0;i<Track::kCount;i++) {
|
||||
@@ -1145,6 +1159,23 @@ void TimelineWidget::UpdateViewTimebases()
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::NudgeInternal(const rational &amount)
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
command->add_child(new TrackReplaceBlockWithGapCommand(b->track(), b, false));
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(b->track()->type()), b->track()->Index(), b, b->in() + amount));
|
||||
}
|
||||
|
||||
// Nudge selections
|
||||
TimelineWidgetSelections new_sel = GetSelections();
|
||||
new_sel.ShiftTime(amount);
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(this, new_sel, GetSelections(), true));
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void TimelineWidget::SetViewBeamCursor(const TimelineCoordinate &coord)
|
||||
{
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
@@ -1428,6 +1459,31 @@ QVector<Block *> TimelineWidget::GetBlocksInGlobalRect(const QPoint &p1, const Q
|
||||
return blocks_in_rect;
|
||||
}
|
||||
|
||||
QVector<Block *> TimelineWidget::GetBlocksInSelection(const TimelineWidgetSelections &sel)
|
||||
{
|
||||
QVector<Block *> blocks;
|
||||
|
||||
for (auto it=sel.cbegin(); it!=sel.cend(); it++) {
|
||||
const Track::Reference &ref = it.key();
|
||||
const TimeRangeList &list = it.value();
|
||||
|
||||
for (const TimeRange &r : list) {
|
||||
Track *track = GetTrackFromReference(ref);
|
||||
if (track) {
|
||||
for (Block *b : track->Blocks()) {
|
||||
if (r.Contains(b->range())) {
|
||||
blocks.append(b);
|
||||
} else if (b->in() >= r.out()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
void TimelineWidget::HideSnaps()
|
||||
{
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
@@ -1474,7 +1530,7 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin
|
||||
QVector<Block*> items_in_rubberband = GetBlocksInGlobalRect(drag_origin_, rubberband_now);
|
||||
|
||||
// Reset selection to whatever it was before
|
||||
SetSelections(rubberband_old_selections_);
|
||||
SetSelections(rubberband_old_selections_, false);
|
||||
|
||||
// Add any blocks in rubberband
|
||||
rubberband_now_selected_.clear();
|
||||
@@ -1544,8 +1600,13 @@ void TimelineWidget::RemoveSelection(Block *item)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::SetSelections(const TimelineWidgetSelections &s)
|
||||
void TimelineWidget::SetSelections(const TimelineWidgetSelections &s, bool process_block_changes)
|
||||
{
|
||||
if (process_block_changes) {
|
||||
SignalDeselectedBlocks(GetBlocksInSelection(selections_.Subtracted(s)));
|
||||
SignalSelectedBlocks(GetBlocksInSelection(s.Subtracted(selections_)));
|
||||
}
|
||||
|
||||
selections_ = s;
|
||||
|
||||
UpdateViewports();
|
||||
|
||||
@@ -91,6 +91,10 @@ public:
|
||||
|
||||
void SetColorLabel(int index);
|
||||
|
||||
void NudgeLeft();
|
||||
|
||||
void NudgeRight();
|
||||
|
||||
/**
|
||||
* @brief Timelines should always be connected to sequences
|
||||
*/
|
||||
@@ -133,7 +137,7 @@ public:
|
||||
return selections_;
|
||||
}
|
||||
|
||||
void SetSelections(const TimelineWidgetSelections &s);
|
||||
void SetSelections(const TimelineWidgetSelections &s, bool process_block_changes);
|
||||
|
||||
Track* GetTrackFromReference(const Track::Reference& ref) const;
|
||||
|
||||
@@ -208,10 +212,11 @@ public:
|
||||
|
||||
class SetSelectionsCommand : public UndoCommand {
|
||||
public:
|
||||
SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old) :
|
||||
SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old, bool process_block_changes) :
|
||||
timeline_(timeline),
|
||||
old_(old),
|
||||
now_(now)
|
||||
now_(now),
|
||||
process_block_changes_(process_block_changes)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -220,18 +225,19 @@ public:
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
timeline_->SetSelections(now_);
|
||||
timeline_->SetSelections(now_, process_block_changes_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
timeline_->SetSelections(old_);
|
||||
timeline_->SetSelections(old_, process_block_changes_);
|
||||
}
|
||||
|
||||
private:
|
||||
TimelineWidget* timeline_;
|
||||
TimelineWidgetSelections old_;
|
||||
TimelineWidgetSelections now_;
|
||||
bool process_block_changes_;
|
||||
|
||||
};
|
||||
|
||||
@@ -271,6 +277,8 @@ private:
|
||||
|
||||
QVector<Block*> GetBlocksInGlobalRect(const QPoint &p1, const QPoint &p2);
|
||||
|
||||
QVector<Block*> GetBlocksInSelection(const TimelineWidgetSelections &sel);
|
||||
|
||||
QPoint drag_origin_;
|
||||
|
||||
QRubberBand rubberband_;
|
||||
@@ -307,6 +315,8 @@ private:
|
||||
|
||||
void UpdateViewTimebases();
|
||||
|
||||
void NudgeInternal(const rational &amount);
|
||||
|
||||
private slots:
|
||||
void ViewMousePressed(TimelineViewMouseEvent* event);
|
||||
void ViewMouseMoved(TimelineViewMouseEvent* event);
|
||||
|
||||
@@ -68,4 +68,17 @@ void TimelineWidgetSelections::TrimOut(const rational &diff)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidgetSelections::Subtract(const TimelineWidgetSelections &selections)
|
||||
{
|
||||
for (auto it=selections.cbegin(); it!=selections.cend(); it++) {
|
||||
const Track::Reference &track = it.key();
|
||||
const TimeRangeList &their_list = it.value();
|
||||
|
||||
if (this->contains(track)) {
|
||||
TimeRangeList &our_list = (*this)[it.key()];
|
||||
our_list.remove(their_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,6 +41,15 @@ public:
|
||||
|
||||
void TrimOut(const rational& diff);
|
||||
|
||||
void Subtract(const TimelineWidgetSelections &selections);
|
||||
|
||||
TimelineWidgetSelections Subtracted(const TimelineWidgetSelections &selections) const
|
||||
{
|
||||
TimelineWidgetSelections copy = *this;
|
||||
copy.Subtract(selections);
|
||||
return copy;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ void EditTool::MouseMove(TimelineViewMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
parent()->SetSelections(start_selections_);
|
||||
parent()->SetSelections(start_selections_, false);
|
||||
parent()->AddSelection(TimeRange(start_coord_.GetFrame(), end_frame),
|
||||
start_coord_.GetTrack());
|
||||
} else {
|
||||
@@ -73,6 +73,10 @@ void EditTool::MouseMove(TimelineViewMouseEvent *event)
|
||||
|
||||
void EditTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
{
|
||||
auto current_sel = parent()->GetSelections();
|
||||
parent()->SetSelections(start_selections_, false);
|
||||
parent()->SetSelections(current_sel, true);
|
||||
|
||||
dragging_ = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -574,7 +574,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
} else {
|
||||
new_sel.TrimOut(reference_ghost->GetOutAdjustment());
|
||||
}
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections()));
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,7 +620,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
TimelineWidgetSelections new_sel = parent()->GetSelections();
|
||||
new_sel.ShiftTime(blocks_moving.first().ghost->GetInAdjustment());
|
||||
new_sel.ShiftTracks(drag_track_type_, blocks_moving.first().ghost->GetTrackAdjustment());
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections()));
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), false));
|
||||
}
|
||||
|
||||
if (!blocks_sliding.isEmpty()) {
|
||||
@@ -681,7 +681,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
// Adjust selections
|
||||
TimelineWidgetSelections new_sel = parent()->GetSelections();
|
||||
new_sel.ShiftTime(movement);
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections()));
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ void RippleTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
} else {
|
||||
new_sel.TrimOut(reference_ghost->GetOutAdjustment());
|
||||
}
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections()));
|
||||
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), false));
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,7 @@ void TransitionTool::HoverMove(TimelineViewMouseEvent *event)
|
||||
{
|
||||
ClipBlock *primary = nullptr;
|
||||
ClipBlock *secondary = nullptr;
|
||||
Timeline::MovementMode trim_mode;
|
||||
Timeline::MovementMode trim_mode = Timeline::kNone;
|
||||
rational transition_start_point;
|
||||
|
||||
GetBlocksAtCoord(event->GetCoordinates(), &primary, &secondary, &trim_mode, &transition_start_point);
|
||||
|
||||
@@ -108,6 +108,9 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
edit_edit_to_in_item_ = edit_menu_->AddItem("edittoin", this, &MainMenu::EditToInTriggered, "Ctrl+Alt+Q");
|
||||
edit_edit_to_out_item_ = edit_menu_->AddItem("edittoout", this, &MainMenu::EditToOutTriggered, "Ctrl+Alt+W");
|
||||
edit_menu_->addSeparator();
|
||||
edit_nudge_left_item_ = edit_menu_->AddItem("nudgeleft", this, &MainMenu::NudgeLeftTriggered, "Alt+Left");
|
||||
edit_nudge_right_item_ = edit_menu_->AddItem("nudgeright", this, &MainMenu::NudgeRightTriggered, "Alt+Right");
|
||||
edit_menu_->addSeparator();
|
||||
MenuShared::instance()->AddItemsForInOutMenu(edit_menu_);
|
||||
edit_delete_inout_item_ = edit_menu_->AddItem("deleteinout", this, &MainMenu::DeleteInOutTriggered, ";");
|
||||
edit_ripple_delete_inout_item_ = edit_menu_->AddItem("rippledeleteinout", this, &MainMenu::RippleDeleteInOutTriggered, "'");
|
||||
@@ -537,6 +540,16 @@ void MainMenu::EditToOutTriggered()
|
||||
PanelManager::instance()->CurrentlyFocused()->EditToOut();
|
||||
}
|
||||
|
||||
void MainMenu::NudgeLeftTriggered()
|
||||
{
|
||||
PanelManager::instance()->CurrentlyFocused()->NudgeLeft();
|
||||
}
|
||||
|
||||
void MainMenu::NudgeRightTriggered()
|
||||
{
|
||||
PanelManager::instance()->CurrentlyFocused()->NudgeRight();
|
||||
}
|
||||
|
||||
void MainMenu::ActionSearchTriggered()
|
||||
{
|
||||
ActionSearch as(parentWidget());
|
||||
@@ -662,6 +675,8 @@ void MainMenu::Retranslate()
|
||||
edit_ripple_to_out_item_->setText(tr("Ripple to Out Point"));
|
||||
edit_edit_to_in_item_->setText(tr("Edit to In Point"));
|
||||
edit_edit_to_out_item_->setText(tr("Edit to Out Point"));
|
||||
edit_nudge_left_item_->setText(tr("Nudge Left"));
|
||||
edit_nudge_right_item_->setText(tr("Nudge Right"));
|
||||
edit_delete_inout_item_->setText(tr("Delete In/Out Point"));
|
||||
edit_ripple_delete_inout_item_->setText(tr("Ripple Delete In/Out Point"));
|
||||
edit_set_marker_item_->setText(tr("Set/Edit Marker"));
|
||||
|
||||
@@ -154,6 +154,9 @@ private slots:
|
||||
void EditToInTriggered();
|
||||
void EditToOutTriggered();
|
||||
|
||||
void NudgeLeftTriggered();
|
||||
void NudgeRightTriggered();
|
||||
|
||||
void ActionSearchTriggered();
|
||||
|
||||
void ShuttleLeftTriggered();
|
||||
@@ -218,6 +221,8 @@ private:
|
||||
QAction* edit_ripple_to_out_item_;
|
||||
QAction* edit_edit_to_in_item_;
|
||||
QAction* edit_edit_to_out_item_;
|
||||
QAction* edit_nudge_left_item_;
|
||||
QAction* edit_nudge_right_item_;
|
||||
QAction* edit_delete_inout_item_;
|
||||
QAction* edit_ripple_delete_inout_item_;
|
||||
QAction* edit_set_marker_item_;
|
||||
|
||||
Reference in New Issue
Block a user