fix: bug-fix sweep across node, audio, render, plugin subsystems
Node core: - MathNode/TrigonometryNode combo strings realigned with Operation enums - mathbase scalar/vector operand pick no longer uses bitwise type checks - NodeSetPositionAndDependenciesRecursively moves dependencies again - RemoveAllKeyframes undo actually restores keyframes - NodeGroup GetInputName null-deref guard, passthrough ids use input id - NodeValueTable::Has is an exact type match; tag fallback only for empty tags; kStrCombo/kPushButton get data type names - delete_all_keyframes no longer loops forever on unparented keyframes; keyframe-load failures propagate; rational interpolation falls back to double; OpacityEffect no longer leaks its internal MathNode Audio/footage: - AudioVisualWaveform: GetSummaryFromTime underflow OOB read, TrimIn prepend length bookkeeping, OverwriteSums source channel indexing - PanNode inserts the pan value into the sample job (keyframed pan works); OutputParamsChanged is emitted on device change; PortAudio device indices are validated before Pa_GetDeviceInfo - Footage: AdjustTimeByLoopMode no longer hangs/UBs on degenerate lengths, GetStreamIndex bounds-checked, CheckFootage clears stale state on missing files, failed probes are not cached, FootageDescription::Load requires its own root element Render/track: - ViewerOutput pushes the tagged samples value; TrackList disconnects the track-height lambda; GetTrackFromReference validity check - RenderManager dummy backend: null-initialized threads, guarded decoder-cache/timer paths; Renderer::Destroy releases color cache shaders/textures; unknown dynamic backends no longer alias to oakgl - SharedMemoryRegion POSIX attach validates segment size; ReadMessage skips blank lines instead of failing; GC counter clamped; IsRenderingCustomRange implemented; TimeOffsetNode gets a true inverse OutputTimeAdjustment; zero-speed clips return the held frame Plugin/nodes: - OliveClip: stored default region of definition is honored, on-demand images are cached; OliveHost sets host identity properties and logs instead of showing modal dialogs offscreen; Plugin.h dead decls gone - DespillNode guards graph-less use with Rec.709 fallback; description typos fixed (despill, swirl); mosaic applies when only one axis matches; Windows-only Project filename separator test fixed
This commit is contained in:
@@ -90,8 +90,9 @@ void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
table->Push(NodeValue(NodeValue::kSamples, samples, this));
|
||||
} else {
|
||||
// Requires job
|
||||
table->Push(NodeValue::kSamples,
|
||||
SampleJob(globals.time(), kSamplesInput, value),
|
||||
SampleJob job(globals.time(), kSamplesInput, value);
|
||||
job.Insert(kPanningInput, value);
|
||||
table->Push(NodeValue::kSamples, QVariant::fromValue(job),
|
||||
this);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -250,8 +250,8 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const
|
||||
|
||||
double speed_value = speed();
|
||||
if (qIsNull(speed_value)) {
|
||||
// I don't know what to return here yet...
|
||||
sequence_time = rational::NaN;
|
||||
// Speed zero holds the frame at the in point, so map to that frame
|
||||
sequence_time = media_in();
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
// Divide time
|
||||
sequence_time =
|
||||
|
||||
@@ -71,7 +71,7 @@ QVector<Node::CategoryID> SwirlDistortNode::Category() const
|
||||
|
||||
QString SwirlDistortNode::Description() const
|
||||
{
|
||||
return tr("Distorts an image along a sine wave.");
|
||||
return tr("Distorts an image by swirling it around a center point.");
|
||||
}
|
||||
|
||||
void SwirlDistortNode::Retranslate()
|
||||
|
||||
@@ -32,6 +32,7 @@ const QString OpacityEffect::kValueInput = QStringLiteral("opacity_in");
|
||||
OpacityEffect::OpacityEffect()
|
||||
{
|
||||
MathNode *math = new MathNode();
|
||||
math->setParent(this);
|
||||
|
||||
math->SetOperation(MathNode::kOpMultiply);
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ void MosaicFilterNode::Value(const NodeValueRow &value,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr texture = value[kTextureInput].toTexture()) {
|
||||
if (texture && value[kHorizInput].toInt() != texture->width() &&
|
||||
value[kVertInput].toInt() != texture->height()) {
|
||||
if (texture && (value[kHorizInput].toInt() != texture->width() ||
|
||||
value[kVertInput].toInt() != texture->height())) {
|
||||
ShaderJob job(value);
|
||||
|
||||
// Mipmapping makes this look weird, so we just use bilinear for finding the color of each block
|
||||
|
||||
@@ -262,7 +262,7 @@ QString NodeGroup::AddInputPassthrough(const NodeInput &input,
|
||||
id = input.input();
|
||||
int i = 2;
|
||||
while (HasInputWithID(id)) {
|
||||
id = QStringLiteral("%1_%2").arg(input.name(), QString::number(i));
|
||||
id = QStringLiteral("%1_%2").arg(input.input(), QString::number(i));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
@@ -334,6 +334,9 @@ QString NodeGroup::GetInputName(const QString &id) const
|
||||
|
||||
// Call GetInputName of passed through node, which may be another group
|
||||
NodeInput pass = GetInputFromID(id);
|
||||
if (!pass.IsValid()) {
|
||||
return QString();
|
||||
}
|
||||
return pass.node()->GetInputName(pass.input());
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "common/lerp.h"
|
||||
#include "common/tohex.h"
|
||||
#include "node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -289,11 +290,21 @@ void NodeInputImmediate::remove_keyframe(NodeKeyframe *key)
|
||||
void NodeInputImmediate::delete_all_keyframes(QObject *parent)
|
||||
{
|
||||
for (NodeKeyframeTrack &track : keyframe_tracks_) {
|
||||
while (!track.isEmpty()) {
|
||||
// Iterate over a copy of the track, since the keyframes may be removed
|
||||
// from it as we go
|
||||
const NodeKeyframeTrack copy = track;
|
||||
|
||||
for (NodeKeyframe *key : copy) {
|
||||
if (!dynamic_cast<Node *>(key->QObject::parent())) {
|
||||
// Keyframe isn't parented to a node, so reparenting/deleting it
|
||||
// won't remove it from the track automatically
|
||||
remove_keyframe(key);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
track.first()->setParent(parent);
|
||||
key->setParent(parent);
|
||||
} else {
|
||||
delete track.first();
|
||||
delete key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ QVector<Node::CategoryID> DespillNode::Category() const
|
||||
|
||||
QString DespillNode::Description() const
|
||||
{
|
||||
return tr("Selection of simple depsill operations");
|
||||
return tr("Selection of simple despill operations");
|
||||
}
|
||||
|
||||
void DespillNode::Retranslate()
|
||||
@@ -95,7 +95,13 @@ void DespillNode::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
|
||||
// Set luma coefficients
|
||||
double luma_coeffs[3] = { 0.0f, 0.0f, 0.0f };
|
||||
project()->color_manager()->GetDefaultLumaCoefs(luma_coeffs);
|
||||
if (project() && project()->color_manager()) {
|
||||
project()->color_manager()->GetDefaultLumaCoefs(luma_coeffs);
|
||||
} else {
|
||||
luma_coeffs[0] = 0.2126;
|
||||
luma_coeffs[1] = 0.7152;
|
||||
luma_coeffs[2] = 0.0722;
|
||||
}
|
||||
job.Insert(
|
||||
QStringLiteral("luma_coeffs"),
|
||||
NodeValue(NodeValue::kVec3,
|
||||
|
||||
@@ -85,7 +85,6 @@ void MathNode::Retranslate()
|
||||
GetOperationName(kOpSubtract),
|
||||
GetOperationName(kOpMultiply),
|
||||
GetOperationName(kOpDivide),
|
||||
QString(),
|
||||
GetOperationName(kOpPower) };
|
||||
|
||||
SetComboBoxStrings(kMethodIn, operations);
|
||||
|
||||
@@ -323,11 +323,14 @@ void MathNodeBase::ValueInternal(
|
||||
QVector4D vec = (NodeValue::type_is_vector(val_a.type()) ?
|
||||
RetrieveVector(val_a) :
|
||||
RetrieveVector(val_b));
|
||||
float number =
|
||||
RetrieveNumber((val_a.type() & NodeValue::kMatrix) ? val_b : val_a);
|
||||
float number = RetrieveNumber(NodeValue::type_is_vector(val_a.type()) ?
|
||||
val_b :
|
||||
val_a);
|
||||
|
||||
// Only multiply and divide are valid operations
|
||||
PushVector(output, val_a.type(),
|
||||
PushVector(output,
|
||||
NodeValue::type_is_vector(val_a.type()) ? val_a.type() :
|
||||
val_b.type(),
|
||||
PerformMultDiv<QVector4D, float>(operation, vec, number));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -64,11 +64,9 @@ void TrigonometryNode::Retranslate()
|
||||
QStringList strings = { tr("Sine"),
|
||||
tr("Cosine"),
|
||||
tr("Tangent"),
|
||||
QString(),
|
||||
tr("Inverse Sine"),
|
||||
tr("Inverse Cosine"),
|
||||
tr("Inverse Tangent"),
|
||||
QString(),
|
||||
tr("Hyperbolic Sine"),
|
||||
tr("Hyperbolic Cosine"),
|
||||
tr("Hyperbolic Tangent") };
|
||||
|
||||
+17
-3
@@ -497,8 +497,19 @@ QVariant Node::GetSplitValueAtTimeOnTrack(const QString &input,
|
||||
|
||||
double before_val, after_val, interpolated;
|
||||
if (type == NodeValue::kRational) {
|
||||
before_val = before->value().value<rational>().toDouble();
|
||||
after_val = after->value().value<rational>().toDouble();
|
||||
// Keys for rational inputs usually hold rationals, but may
|
||||
// hold plain doubles, in which case we convert to rational
|
||||
// first to preserve the value
|
||||
before_val = (before->value().canConvert<rational>() ?
|
||||
before->value().value<rational>() :
|
||||
rational::fromDouble(
|
||||
before->value().toDouble()))
|
||||
.toDouble();
|
||||
after_val = (after->value().canConvert<rational>() ?
|
||||
after->value().value<rational>() :
|
||||
rational::fromDouble(
|
||||
after->value().toDouble()))
|
||||
.toDouble();
|
||||
} else {
|
||||
before_val = before->value().toDouble();
|
||||
after_val = after->value().toDouble();
|
||||
@@ -1724,7 +1735,10 @@ bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input,
|
||||
key->set_element(element);
|
||||
key->set_track(track);
|
||||
|
||||
key->load(reader, data_type);
|
||||
if (!key->load(reader, data_type)) {
|
||||
delete key;
|
||||
return false;
|
||||
}
|
||||
key->setParent(this);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
|
||||
@@ -109,7 +109,7 @@ void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(
|
||||
{
|
||||
Node::Position pos = context_->GetNodePositionDataInContext(node);
|
||||
pos += diff;
|
||||
commands_.append(new NodeSetPositionCommand(node_, context_, pos));
|
||||
commands_.append(new NodeSetPositionCommand(node, context_, pos));
|
||||
|
||||
for (auto it = node->input_connections().cbegin();
|
||||
it != node->input_connections().cend(); it++) {
|
||||
@@ -610,6 +610,10 @@ void NodeImmediateRemoveAllKeyframesCommand::prepare()
|
||||
for (const NodeKeyframeTrack &track : immediate_->keyframe_tracks()) {
|
||||
keys_.append(track);
|
||||
}
|
||||
|
||||
if (!keys_.isEmpty()) {
|
||||
node_ = keys_.first()->parent();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeImmediateRemoveAllKeyframesCommand::redo()
|
||||
@@ -622,7 +626,7 @@ void NodeImmediateRemoveAllKeyframesCommand::redo()
|
||||
void NodeImmediateRemoveAllKeyframesCommand::undo()
|
||||
{
|
||||
for (auto it = keys_.crbegin(); it != keys_.crend(); it++) {
|
||||
(*it)->setParent(&memory_manager_);
|
||||
(*it)->setParent(node_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -823,6 +823,7 @@ class NodeImmediateRemoveAllKeyframesCommand : public UndoCommand {
|
||||
public:
|
||||
NodeImmediateRemoveAllKeyframesCommand(NodeInputImmediate *immediate)
|
||||
: immediate_(immediate)
|
||||
, node_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -841,6 +842,8 @@ protected:
|
||||
private:
|
||||
NodeInputImmediate *immediate_;
|
||||
|
||||
Node *node_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
QVector<NodeKeyframe *> keys_;
|
||||
|
||||
@@ -85,10 +85,11 @@ void TrackList::TrackConnected(Node *node, int element)
|
||||
|
||||
connect(track, &Track::TrackLengthChanged, this,
|
||||
&TrackList::UpdateTotalLength);
|
||||
connect(track, &Track::TrackHeightChanged, this, [this]() {
|
||||
Track *t = static_cast<Track *>(sender());
|
||||
emit TrackHeightChanged(t, t->GetTrackHeightInPixels());
|
||||
});
|
||||
track_height_connections_.insert(
|
||||
track, connect(track, &Track::TrackHeightChanged, this, [this]() {
|
||||
Track *t = static_cast<Track *>(sender());
|
||||
emit TrackHeightChanged(t, t->GetTrackHeightInPixels());
|
||||
}));
|
||||
|
||||
track->set_type(type_);
|
||||
track->set_sequence(parent());
|
||||
@@ -134,6 +135,7 @@ void TrackList::TrackDisconnected(Node *node, int element)
|
||||
|
||||
disconnect(track, &Track::TrackLengthChanged, this,
|
||||
&TrackList::UpdateTotalLength);
|
||||
disconnect(track_height_connections_.take(track));
|
||||
|
||||
emit TrackListChanged();
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef TRACKLIST_H
|
||||
#define TRACKLIST_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
#include "node/output/track/track.h"
|
||||
@@ -113,6 +114,11 @@ private:
|
||||
QVector<Track *> track_cache_;
|
||||
QVector<int> track_array_indexes_;
|
||||
|
||||
/**
|
||||
* @brief Stored TrackHeightChanged connections so they can be disconnected again
|
||||
*/
|
||||
QHash<Track *, QMetaObject::Connection> track_height_connections_;
|
||||
|
||||
QString track_input_;
|
||||
|
||||
rational total_length_;
|
||||
|
||||
@@ -460,7 +460,7 @@ void ViewerOutput::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
if (HasInputWithID(kSamplesInput)) {
|
||||
NodeValue repush = value[kSamplesInput];
|
||||
repush.set_tag(Track::Reference(Track::kAudio, 0).ToString());
|
||||
table->Push(value[kSamplesInput]);
|
||||
table->Push(repush);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,6 @@ public:
|
||||
QString SubCategory() const override;
|
||||
QString Description() const override;
|
||||
|
||||
void AddPushButton();
|
||||
void AddPage();
|
||||
|
||||
Node *copy() const override;
|
||||
/**
|
||||
* @brief The main processing function
|
||||
|
||||
@@ -182,11 +182,20 @@ int Footage::GetStreamIndex(Track::Type type, int index) const
|
||||
{
|
||||
switch (type) {
|
||||
case Track::kVideo:
|
||||
return GetVideoParams(index).stream_index();
|
||||
if (index >= 0 && index < GetVideoStreamCount()) {
|
||||
return GetVideoParams(index).stream_index();
|
||||
}
|
||||
break;
|
||||
case Track::kAudio:
|
||||
return GetAudioParams(index).stream_index();
|
||||
if (index >= 0 && index < GetAudioStreamCount()) {
|
||||
return GetAudioParams(index).stream_index();
|
||||
}
|
||||
break;
|
||||
case Track::kSubtitle:
|
||||
return GetSubtitleParams(index).stream_index();
|
||||
if (index >= 0 && index < GetSubtitleStreamCount()) {
|
||||
return GetSubtitleParams(index).stream_index();
|
||||
}
|
||||
break;
|
||||
case Track::kNone:
|
||||
case Track::kCount:
|
||||
break;
|
||||
@@ -476,18 +485,29 @@ rational Footage::AdjustTimeByLoopMode(rational time, LoopMode loop_mode,
|
||||
time = rational::NaN;
|
||||
break;
|
||||
case LoopMode::kLoopModeClamp:
|
||||
// Clamp footage time to length
|
||||
time = std::clamp(time, rational(0), length - timebase);
|
||||
if (length < timebase) {
|
||||
// No full frame fits in the range, so there is nothing to clamp to
|
||||
time = rational::NaN;
|
||||
} else {
|
||||
// Clamp footage time to length
|
||||
time = std::clamp(time, rational(0), length - timebase);
|
||||
}
|
||||
break;
|
||||
case LoopMode::kLoopModeLoop:
|
||||
// Loop footage time around job length
|
||||
do {
|
||||
if (time >= length) {
|
||||
time -= length;
|
||||
} else {
|
||||
time += length;
|
||||
}
|
||||
} while (TimeIsOutOfBounds(time, length));
|
||||
if (length <= 0) {
|
||||
// Cannot loop around an empty range
|
||||
time = rational::NaN;
|
||||
} else {
|
||||
// Loop footage time around job length
|
||||
do {
|
||||
if (time >= length) {
|
||||
time -= length;
|
||||
} else {
|
||||
time += length;
|
||||
}
|
||||
} while (TimeIsOutOfBounds(time, length));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,7 +817,10 @@ void Footage::Reprobe()
|
||||
}
|
||||
|
||||
if (!cancelled_ || !cancelled_->HeardCancel()) {
|
||||
if (!footage_info.Save(meta_cache_file)) {
|
||||
// Only cache successful probes; caching a failed probe
|
||||
// would make every future load re-use the invalid metadata
|
||||
if (footage_info.IsValid() &&
|
||||
!footage_info.Save(meta_cache_file)) {
|
||||
qWarning()
|
||||
<< "Failed to save stream cache, footage will have to be re-probed";
|
||||
}
|
||||
@@ -898,6 +921,7 @@ void Footage::CheckFootage()
|
||||
|
||||
if (current_file_timestamp != timestamp()) {
|
||||
// File has changed!
|
||||
Clear();
|
||||
Reprobe();
|
||||
InvalidateAll(kFilenameInput);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,11 @@ bool FootageDescription::Load(const QString &filename)
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
QXmlStreamReader reader(&file);
|
||||
|
||||
bool found_streamcache = false;
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("streamcache")) {
|
||||
found_streamcache = true;
|
||||
// Default to first version of metadata (which wasn't versioned at all)
|
||||
unsigned version = 1;
|
||||
|
||||
@@ -126,7 +129,8 @@ bool FootageDescription::Load(const QString &filename)
|
||||
qWarning() << "Failed to load footage description for" << filename
|
||||
<< reader.errorString();
|
||||
} else {
|
||||
return true;
|
||||
// Only accept files whose root element is the one Save() writes
|
||||
return found_streamcache;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,9 @@ public:
|
||||
|
||||
Track *GetTrackFromReference(const Track::Reference &track_ref) const
|
||||
{
|
||||
if (track_ref.type() < 0 || track_ref.type() >= track_lists_.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return track_lists_.at(track_ref.type())->GetTrackAt(track_ref.index());
|
||||
}
|
||||
|
||||
|
||||
@@ -120,9 +120,11 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project,
|
||||
if (attr.name() ==
|
||||
QStringLiteral("version")) { // 230220+ projects
|
||||
version = attr.value().toUInt();
|
||||
} else if (reader->name() ==
|
||||
} else if (attr.name() ==
|
||||
QStringLiteral("url")) { // 230220+ projects
|
||||
project->SetSavedURL(attr.value().toString());
|
||||
if (project) {
|
||||
project->SetSavedURL(attr.value().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,14 +66,14 @@ TimeRange
|
||||
TimeOffsetNode::OutputTimeAdjustment(const QString &input, int element,
|
||||
const TimeRange &input_time) const
|
||||
{
|
||||
/*if (input == kInputInput) {
|
||||
rational target_time = GetValueAtTime(kTimeInput, input_time.in()).value<rational>();
|
||||
|
||||
return TimeRange(target_time, target_time + input_time.length());
|
||||
} else {
|
||||
return super::OutputTimeAdjustment(input, element, input_time);
|
||||
}*/
|
||||
return super::OutputTimeAdjustment(input, element, input_time);
|
||||
if (input == kInputInput) {
|
||||
// The inverse of InputTimeAdjustment(): times at the input are mapped
|
||||
// back to the output by subtracting the offset again
|
||||
return TimeRange(GetRemappedOutputTime(input_time.in()),
|
||||
GetRemappedOutputTime(input_time.out()));
|
||||
} else {
|
||||
return super::OutputTimeAdjustment(input, element, input_time);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeOffsetNode::Value(const NodeValueRow &value,
|
||||
@@ -88,4 +88,9 @@ rational TimeOffsetNode::GetRemappedTime(const rational &input) const
|
||||
return input + GetValueAtTime(kTimeInput, input).value<rational>();
|
||||
}
|
||||
|
||||
rational TimeOffsetNode::GetRemappedOutputTime(const rational &input) const
|
||||
{
|
||||
return input - GetValueAtTime(kTimeInput, input).value<rational>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
|
||||
private:
|
||||
rational GetRemappedTime(const rational &input) const;
|
||||
rational GetRemappedOutputTime(const rational &input) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+11
-6
@@ -261,6 +261,8 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
|
||||
case kInt:
|
||||
case kCombo:
|
||||
return QCoreApplication::translate("NodeValue", "Integer");
|
||||
case kStrCombo:
|
||||
return QCoreApplication::translate("NodeValue", "String Combo");
|
||||
case kFloat:
|
||||
return QCoreApplication::translate("NodeValue", "Float");
|
||||
case kRational:
|
||||
@@ -297,6 +299,8 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
|
||||
return QCoreApplication::translate("NodeValue", "Subtitle Parameters");
|
||||
case kBinary:
|
||||
return QCoreApplication::translate("NodeValue", "Binary");
|
||||
case kPushButton:
|
||||
return QCoreApplication::translate("NodeValue", "Push Button");
|
||||
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
@@ -314,6 +318,8 @@ QString NodeValue::GetDataTypeName(Type type)
|
||||
return QStringLiteral("int");
|
||||
case kCombo:
|
||||
return QStringLiteral("combo");
|
||||
case kStrCombo:
|
||||
return QStringLiteral("strcombo");
|
||||
case kFloat:
|
||||
return QStringLiteral("float");
|
||||
case kRational:
|
||||
@@ -350,6 +356,8 @@ QString NodeValue::GetDataTypeName(Type type)
|
||||
return QStringLiteral("sparam");
|
||||
case kBinary:
|
||||
return QStringLiteral("binary");
|
||||
case kPushButton:
|
||||
return QStringLiteral("pushbutton");
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
@@ -399,7 +407,7 @@ bool NodeValueTable::Has(NodeValue::Type type) const
|
||||
for (int i = values_.size() - 1; i >= 0; i--) {
|
||||
const NodeValue &v = values_.at(i);
|
||||
|
||||
if (v.type() & type) {
|
||||
if (v.type() == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -463,12 +471,9 @@ int NodeValueTable::GetValueIndex(const QVector<NodeValue::Type> &types,
|
||||
for (int i = values_.size() - 1; i >= 0; i--) {
|
||||
const NodeValue &v = values_.at(i);
|
||||
|
||||
if (types.contains(v.type())) {
|
||||
if (types.contains(v.type()) && (tag.isEmpty() || tag == v.tag())) {
|
||||
index = i;
|
||||
|
||||
if (tag.isEmpty() || tag == v.tag()) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user