Commit 952a7b22 authored by Simon Eugster's avatar Simon Eugster
Browse files

Merge branch 'feature/granjow/decimal-separator'

This MR includes the following changes for decimal point handling:

* MLT XML in project files is now always stored with "C" as LC_ALL, meaning that it will always use `.` as decimal separator. This means that new project files should not run into issues with `,` or another character as decimal separator.
* When loading an old project file which uses a different decimal separators, the values are converted:
  * Effect parameters are converted based on their type
  * Filter parameters are converted based on a list of known properties which need conversion
  * General properties in the document are also converted based on a list (e.g. all `frame_rate` properties)

Related: #78
Closes #713
parents b41fc1df ed32c5e7
Pipeline #25175 passed with stage
in 12 minutes and 18 seconds
......@@ -7,6 +7,32 @@
* [The KDE Frameworks][kf]
* [XMLGUI Technology][xmlgui-tut] (e.g. for the `kdenliveui.rc` file)
## Locale handling
Locales are important e.g. for formatting numbers in a way that the user is
familiar with. Some countries would write `12500.42` as `12.000,42`, for
example.
Since 20.08, Kdenlive follows the following rules:
* When parsing data (by Kdenlive or by other libraries like MLT), **the `C`
locale is used.** This is especially important for project files. The reason
is that for passing data between programs, the format has to be well-defined
and *not* depend on where the user happens to live.
* When presenting data to the user, the user’s locale is used.
MLT uses the C locale which is set by `setlocale()` and which must be set to
`C`. If it is set to e.g. `hu_HU.utf-8`, which uses `,` as decimal separator,
properties are converted to this format upon saving the project file, and the
project file ends up corrupted.
In Kdenlive, `QLocale` should only be used in one case: when data is shown to
the user or read from the user. Usually that is handled by Qt already. A
`QDoubleSpinBox`, for example, presents the double value in the user’s local
number format.
## Configuration
Named settings are stored in [`kdenlivesettings.kcfg`][sett]. To add a new
......
......@@ -9,6 +9,7 @@ include_directories(
set(kdenlive_render_SRCS
kdenlive_render.cpp
renderjob.cpp
../src/lib/localeHandling.cpp
)
add_executable(kdenlive_render ${kdenlive_render_SRCS})
......
......@@ -17,16 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "framework/mlt_version.h"
#include "../src/lib/localeHandling.h"
#include "mlt++/Mlt.h"
#include "renderjob.h"
#include <QApplication>
#include <QDir>
#include <QDomDocument>
#include <QString>
#include <QStringList>
#include <QObject>
#include <cstdio>
int main(int argc, char **argv)
{
......@@ -79,7 +75,12 @@ int main(int argc, char **argv)
#endif
args.removeFirst();
QDir baseFolder(target);
// After initialising the MLT factory, set the locale back from user default to C
// to ensure numbers are always serialised with . as decimal point.
Mlt::Factory::init();
LocaleHandling::resetLocale();
Mlt::Profile profile(profilePath.toUtf8().constData());
profile.set_explicit(1);
Mlt::Producer prod(profile, nullptr, playlist.toUtf8().constData());
......
......@@ -80,11 +80,6 @@ RenderJob::~RenderJob()
m_logfile.close();
}
void RenderJob::setLocale(const QString &locale)
{
qputenv("LC_NUMERIC", locale.toUtf8().constData());
}
void RenderJob::slotAbort(const QString &url)
{
if (m_dest == url) {
......
......@@ -35,7 +35,6 @@ class RenderJob : public QObject
public:
RenderJob(const QString &render, const QString &scenelist, const QString &target, int pid = -1, int in = -1, int out = -1, QObject *parent = nullptr);
~RenderJob();
void setLocale(const QString &locale);
public slots:
void start();
......
......@@ -85,7 +85,7 @@ ki18n_wrap_ui(kdenlive_UIS ${kdenlive_UIS})
qt5_wrap_cpp(kdenlive_MOC definitions.h)
set_property(SOURCE definitions.h PROPERTY SKIP_AUTOMOC ON)
add_library(kdenliveLib STATIC ${kdenlive_SRCS} ${kdenlive_UIS} ${kdenlive_MOC})
add_library(kdenliveLib STATIC ${kdenlive_SRCS} ${kdenlive_UIS} ${kdenlive_MOC} lib/localeHandling.cpp lib/localeHandling.h)
qt5_add_resources(kdenlive_extra_SRCS icons.qrc ui/resources.qrc uiresources.qrc)
## Icon for Windows and OSX
......
......@@ -38,13 +38,6 @@ template <typename AssetType> AbstractAssetsRepository<AssetType>::AbstractAsset
template <typename AssetType> void AbstractAssetsRepository<AssetType>::init()
{
// Warning: Mlt::Factory::init() resets the locale to the default system value, make sure we keep correct locale
#ifndef Q_OS_MAC
setlocale(LC_NUMERIC, nullptr);
#else
setlocale(LC_NUMERIC_MASK, nullptr);
#endif
// Parse blacklist
parseAssetList(assetBlackListPath(), m_blacklist);
......@@ -149,8 +142,6 @@ template <typename AssetType> bool AbstractAssetsRepository<AssetType>::parseInf
QDomElement eff = doc.createElement(QStringLiteral("effect"));
eff.setAttribute(QStringLiteral("tag"), id);
eff.setAttribute(QStringLiteral("id"), id);
QLocale locale;
////qCDebug(KDENLIVE_LOG)<<"Effect: "<<id;
Mlt::Properties param_props((mlt_properties)metadata->get_data("parameters"));
for (int j = 0; param_props.is_valid() && j < param_props.count(); ++j) {
......@@ -172,10 +163,10 @@ template <typename AssetType> bool AbstractAssetsRepository<AssetType>::parseInf
if (paramType == QLatin1String("float")) {
// Float must be converted using correct locale
if (paramdesc.get("maximum")) {
params.setAttribute(QStringLiteral("max"), locale.toString(paramdesc.get_double("maximum")));
params.setAttribute(QStringLiteral("max"), QString::number(paramdesc.get_double("maximum"), 'f'));
}
if (paramdesc.get("minimum")) {
params.setAttribute(QStringLiteral("min"), locale.toString(paramdesc.get_double("minimum")));
params.setAttribute(QStringLiteral("min"), QString::number(paramdesc.get_double("minimum"), 'f'));
}
} else {
if (paramdesc.get("maximum")) {
......@@ -221,12 +212,12 @@ template <typename AssetType> bool AbstractAssetsRepository<AssetType>::parseInf
if (paramType == QLatin1String("float")) {
// floats have to be converted using correct locale
if (paramdesc.get("default")) {
params.setAttribute(QStringLiteral("default"), locale.toString(paramdesc.get_double("default")));
params.setAttribute(QStringLiteral("default"), QString::number(paramdesc.get_double("default"), 'f'));
}
if (paramdesc.get("value")) {
params.setAttribute(QStringLiteral("value"), locale.toString(paramdesc.get_double("value")));
params.setAttribute(QStringLiteral("value"), QString::number(paramdesc.get_double("value"), 'f'));
} else {
params.setAttribute(QStringLiteral("value"), locale.toString(paramdesc.get_double("default")));
params.setAttribute(QStringLiteral("value"), QString::number(paramdesc.get_double("default"), 'f'));
}
} else {
if (paramdesc.get("default")) {
......@@ -259,7 +250,12 @@ template <typename AssetType> bool AbstractAssetsRepository<AssetType>::parseInf
doc.appendChild(eff);
res.xml = eff;
return true;
} else {
qDebug() << "Invalid title/identifier for " << assetId;
qDebug() << metadata->get("title") << "/" << metadata->get("identifier");
}
} else {
qDebug() << "Metadata for" << assetId << "is invalid.";
}
return false;
}
......
......@@ -442,8 +442,12 @@ QVariant KeyframeModel::data(const QModelIndex &index, int role) const
case NormalizedValueRole: {
if (m_paramType == ParamType::AnimatedRect) {
const QString &data = it->second.second.toString();
QLocale locale;
return locale.toDouble(data.section(QLatin1Char(' '), -1));
bool ok;
double converted = data.section(QLatin1Char(' '), -1).toDouble(&ok);
if (!ok) {
qDebug() << "QLocale: Could not convert animated rect opacity" << data;
}
return converted;
}
double val = it->second.second.toDouble();
if (auto ptr = m_model.lock()) {
......@@ -713,7 +717,6 @@ void KeyframeModel::parseAnimProperty(const QString &prop)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
QLocale locale;
disconnect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification);
removeAllKeyframes(undo, redo);
int in = 0;
......@@ -749,7 +752,7 @@ void KeyframeModel::parseAnimProperty(const QString &prop)
case ParamType::AnimatedRect: {
mlt_rect rect = mlt_prop.anim_get_rect("key", frame);
if (useOpacity) {
value = QVariant(QStringLiteral("%1 %2 %3 %4 %5").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h).arg(locale.toString(rect.o)));
value = QVariant(QStringLiteral("%1 %2 %3 %4 %5").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h).arg(rect.o, 0, 'f'));
} else {
value = QVariant(QStringLiteral("%1 %2 %3 %4").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h));
}
......@@ -782,7 +785,6 @@ void KeyframeModel::resetAnimProperty(const QString &prop)
removeAllKeyframes(undo, redo);
Mlt::Properties mlt_prop;
QLocale locale;
int in = 0;
bool useOpacity = true;
if (auto ptr = m_model.lock()) {
......@@ -812,7 +814,7 @@ void KeyframeModel::resetAnimProperty(const QString &prop)
case ParamType::AnimatedRect: {
mlt_rect rect = mlt_prop.anim_get_rect("key", frame);
if (useOpacity) {
value = QVariant(QStringLiteral("%1 %2 %3 %4 %5").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h).arg(locale.toString(rect.o)));
value = QVariant(QStringLiteral("%1 %2 %3 %4 %5").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h).arg(QString::number(rect.o, 'f')));
} else {
value = QVariant(QStringLiteral("%1 %2 %3 %4").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h));
}
......@@ -877,10 +879,8 @@ QVariant KeyframeModel::getInterpolatedValue(int p) const
QVariant KeyframeModel::updateInterpolated(const QVariant &interpValue, double val)
{
QStringList vals = interpValue.toString().split(QLatin1Char(' '));
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
if (!vals.isEmpty()) {
vals[vals.size() - 1] = locale.toString(val);
vals[vals.size() - 1] = QString::number(val, 'f');
}
return vals.join(QLatin1Char(' '));
}
......@@ -938,14 +938,13 @@ QVariant KeyframeModel::getInterpolatedValue(const GenTime &pos) const
return QVariant();
} else if (m_paramType == ParamType::AnimatedRect) {
if (!animData.isEmpty()) {
QLocale locale;
mlt_prop.set("key", animData.toUtf8().constData());
// This is a fake query to force the animation to be parsed
(void)mlt_prop.anim_get_double("key", 0, out);
mlt_rect rect = mlt_prop.anim_get_rect("key", pos.frames(pCore->getCurrentFps()));
QString res = QStringLiteral("%1 %2 %3 %4").arg((int)rect.x).arg((int)rect.y).arg((int)rect.w).arg((int)rect.h);
if (useOpacity) {
res.append(QStringLiteral(" %1").arg(locale.toString(rect.o)));
res.append(QStringLiteral(" %1").arg(QString::number(rect.o, 'f')));
}
return QVariant(res);
}
......
......@@ -33,8 +33,6 @@ AssetCommand::AssetCommand(const std::shared_ptr<AssetParameterModel> &model, co
, m_updateView(false)
, m_stamp(QTime::currentTime())
{
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
m_name = m_model->data(index, AssetParameterModel::NameRole).toString();
const QString id = model->getAssetId();
if (EffectsRepository::get()->exists(id)) {
......@@ -43,26 +41,25 @@ AssetCommand::AssetCommand(const std::shared_ptr<AssetParameterModel> &model, co
setText(i18n("Edit %1", TransitionsRepository::get()->getName(id)));
}
QVariant previousVal = m_model->data(index, AssetParameterModel::ValueRole);
m_oldValue = previousVal.type() == QVariant::Double ? locale.toString(previousVal.toDouble()) : previousVal.toString();
m_oldValue = previousVal.type() == previousVal.toString();
}
void AssetCommand::undo()
{
m_model->setParameter(m_name, m_oldValue, true, m_index);
}
// virtual
void AssetCommand::redo()
{
m_model->setParameter(m_name, m_value, m_updateView, m_index);
m_updateView = true;
}
// virtual
int AssetCommand::id() const
{
return 1;
}
// virtual
bool AssetCommand::mergeWith(const QUndoCommand *other)
{
if (other->id() != id() || static_cast<const AssetCommand *>(other)->m_index != m_index ||
......@@ -82,8 +79,6 @@ AssetMultiCommand::AssetMultiCommand(const std::shared_ptr<AssetParameterModel>
, m_updateView(false)
, m_stamp(QTime::currentTime())
{
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
qDebug()<<"CREATING MULTIPLE COMMAND!!!\nVALUES: "<<m_values;
m_name = m_model->data(indexes.first(), AssetParameterModel::NameRole).toString();
const QString id = model->getAssetId();
......@@ -94,7 +89,7 @@ AssetMultiCommand::AssetMultiCommand(const std::shared_ptr<AssetParameterModel>
}
for (QModelIndex ix : m_indexes) {
QVariant previousVal = m_model->data(ix, AssetParameterModel::ValueRole);
m_oldValues << (previousVal.type() == QVariant::Double ? locale.toString(previousVal.toDouble()) : previousVal.toString());
m_oldValues << previousVal.toString();
}
}
......
......@@ -29,11 +29,12 @@
#include <QDir>
#include <QJsonArray>
#include <QJsonObject>
#include <QLocale>
#include <QString>
#include <effects/effectsrepository.hpp>
#define DEBUG_LOCALE false
AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset, const QDomElement &assetXml, const QString &assetId, ObjectId ownerId,
QObject *parent)
const QString& originalDecimalPoint, QObject *parent)
: QAbstractListModel(parent)
, monitorId(ownerId.first == ObjectType::BinClip ? Kdenlive::ClipMonitor : Kdenlive::ProjectMonitor)
, m_assetId(assetId)
......@@ -42,17 +43,15 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
, m_keyframes(nullptr)
{
Q_ASSERT(m_asset->is_valid());
QDomNodeList nodeList = assetXml.elementsByTagName(QStringLiteral("parameter"));
QDomNodeList parameterNodes = assetXml.elementsByTagName(QStringLiteral("parameter"));
m_hideKeyframesByDefault = assetXml.hasAttribute(QStringLiteral("hideKeyframes"));
m_isAudio = assetXml.attribute(QStringLiteral("type")) == QLatin1String("audio");
bool needsLocaleConversion = false;
QChar separator, oldSeparator;
// Check locale, default effects xml has no LC_NUMERIC defined and always uses the C locale
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
if (assetXml.hasAttribute(QStringLiteral("LC_NUMERIC"))) {
QLocale effectLocale = QLocale(assetXml.attribute(QStringLiteral("LC_NUMERIC")));
QLocale effectLocale = QLocale(assetXml.attribute(QStringLiteral("LC_NUMERIC"))); // Check if effect has a special locale → probably OK
if (QLocale::c().decimalPoint() != effectLocale.decimalPoint()) {
needsLocaleConversion = true;
separator = QLocale::c().decimalPoint();
......@@ -60,11 +59,36 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
}
}
qDebug() << "XML parsing of " << assetId << ". found : " << nodeList.count();
for (int i = 0; i < nodeList.count(); ++i) {
QDomElement currentParameter = nodeList.item(i).toElement();
if (EffectsRepository::get()->exists(assetId)) {
qDebug() << "Asset " << assetId << " found in the repository. Description: " << EffectsRepository::get()->getDescription(assetId);
#if false
QString str;
QTextStream stream(&str);
EffectsRepository::get()->getXml(assetId).save(stream, 4);
qDebug() << "Asset XML: " << str;
#endif
} else {
qDebug() << "Asset not found in repo: " << assetId;
}
qDebug() << "XML parsing of " << assetId << ". found" << parameterNodes.count() << "parameters";
if (DEBUG_LOCALE) {
QString str;
QTextStream stream(&str);
assetXml.save(stream, 1);
qDebug() << "XML to parse: " << str;
}
bool fixDecimalPoint = !originalDecimalPoint.isEmpty();
if (fixDecimalPoint) {
qDebug() << "Original decimal point was different:" << originalDecimalPoint << "Values will be converted if required.";
}
for (int i = 0; i < parameterNodes.count(); ++i) {
QDomElement currentParameter = parameterNodes.item(i).toElement();
// Convert parameters if we need to
// Note: This is not directly related to the originalDecimalPoint parameter.
// Is it still required? Does it work correctly for non-number values (e.g. lists which contain commas)?
if (needsLocaleConversion) {
QDomNamedNodeMap attrs = currentParameter.attributes();
for (int k = 0; k < attrs.count(); ++k) {
......@@ -87,7 +111,8 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
currentRow.xml = currentParameter;
if (value.isEmpty()) {
QVariant defaultValue = parseAttribute(m_ownerId, QStringLiteral("default"), currentParameter);
value = defaultValue.type() == QVariant::Double ? locale.toString(defaultValue.toDouble()) : defaultValue.toString();
value = defaultValue.toString();
qDebug() << "QLocale: Default value is" << defaultValue << "parsed:" << value;
}
bool isFixed = (type == QLatin1String("fixed"));
if (isFixed) {
......@@ -105,11 +130,73 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
value.prepend(QStringLiteral("%1=").arg(pCore->getItemIn(m_ownerId)));
}
}
if (fixDecimalPoint) {
bool converted = true;
QString originalValue(value);
switch (currentRow.type) {
case ParamType::KeyframeParam:
case ParamType::Position:
// Fix values like <position>=1,5
value.replace(QRegExp(R"((=\d+),(\d+))"), "\\1.\\2");
break;
case ParamType::AnimatedRect:
// Fix values like <position>=50 20 1920 1080 0,75
value.replace(QRegExp(R"((=\d+ \d+ \d+ \d+ \d+),(\d+))"), "\\1.\\2");
break;
case ParamType::ColorWheel:
// Colour wheel has 3 separate properties: prop_r, prop_g and prop_b, always numbers
case ParamType::Double:
case ParamType::Hidden:
case ParamType::List:
// Despite its name, a list type parameter is a single value *chosen from* a list.
// If it contains a non-“.” decimal separator, it is very likely wrong.
// Fall-through, treat like Double
case ParamType::Bezier_spline:
value.replace(originalDecimalPoint, ".");
break;
case ParamType::Bool:
case ParamType::Color:
case ParamType::Fontfamily:
case ParamType::Keywords:
case ParamType::Readonly:
case ParamType::RestrictedAnim: // Fine because unsupported
case ParamType::Animated: // Fine because unsupported
case ParamType::Addedgeometry: // Fine because unsupported
case ParamType::Url:
// All fine
converted = false;
break;
case ParamType::Curve:
case ParamType::Geometry:
case ParamType::Switch:
case ParamType::Wipe:
// Pretty sure that those are fine
converted = false;
break;
case ParamType::Roto_spline: // Not sure because cannot test
case ParamType::Filterjob:
// Not sure if fine
converted = false;
break;
}
if (converted) {
if (value != originalValue) {
qDebug() << "Decimal point conversion: " << name << "converted from" << originalValue << "to" << value;
} else {
qDebug() << "Decimal point conversion: " << name << " is already ok: " << value;
}
} else {
qDebug() << "No fixing needed for" << name << "=" << value;
}
}
if (!name.isEmpty()) {
internalSetParameter(name, value);
// Keep track of param order
m_paramOrder.push_back(name);
}
if (isFixed) {
// fixed parameters are not displayed so we don't store them.
continue;
......@@ -128,6 +215,7 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
}
m_asset->set("effect", effectParam.join(QLatin1Char(' ')).toUtf8().constData());
}
qDebug() << "END parsing of " << assetId << ". Number of found parameters" << m_rows.size();
emit modelChanged();
}
......@@ -202,8 +290,6 @@ void AssetParameterModel::setParameter(const QString &name, int value, bool upda
void AssetParameterModel::internalSetParameter(const QString &name, const QString &paramValue, const QModelIndex &paramIndex)
{
Q_ASSERT(m_asset->is_valid());
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
// TODO: this does not really belong here, but I don't see another way to do it so that undo works
if (data(paramIndex, AssetParameterModel::TypeRole).value<ParamType>() == ParamType::Curve) {
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
......@@ -231,13 +317,7 @@ void AssetParameterModel::internalSetParameter(const QString &name, const QStrin
}
}
bool conversionSuccess = true;
double doubleValue = 0;
if (paramValue.simplified().contains(QLatin1Char(' '))) {
// Some locale interpret a space as thousands separator
conversionSuccess = false;
} else {
doubleValue = locale.toDouble(paramValue, &conversionSuccess);
}
double doubleValue = paramValue.toDouble(&conversionSuccess);
if (conversionSuccess) {
m_asset->set(name.toLatin1().constData(), doubleValue);
if (m_fixedParams.count(name) == 0) {
......@@ -247,7 +327,6 @@ void AssetParameterModel::internalSetParameter(const QString &name, const QStrin
}
} else {
m_asset->set(name.toLatin1().constData(), paramValue.toUtf8().constData());
qDebug() << " = = SET EFFECT PARAM: " << name << " = " << paramValue;
if (m_fixedParams.count(name) == 0) {
m_params[name].value = paramValue;
if (m_keyframes) {
......@@ -261,6 +340,7 @@ void AssetParameterModel::internalSetParameter(const QString &name, const QStrin
m_fixedParams[name] = paramValue;
}
}
qDebug() << " = = SET EFFECT PARAM: " << name << " = " << m_asset->get(name.toLatin1().constData());
}
void AssetParameterModel::setParameter(const QString &name, const QString &paramValue, bool update, const QModelIndex &paramIndex)
......@@ -555,9 +635,12 @@ QVariant AssetParameterModel::parseAttribute(const ObjectId &owner, const QStrin
if (attribute == QLatin1String("default")) {
return content.toDouble();
}
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
return locale.toDouble(content);
bool ok;
double converted = content.toDouble(&ok);
if (!ok) {
qDebug() << "QLocale: Could not load double parameter" << content;
}
return converted;
}
if (attribute == QLatin1String("default")) {
if (type == ParamType::RestrictedAnim) {
......@@ -571,11 +654,6 @@ QVariant AssetParameterModel::parseAttribute(const ObjectId &owner, const QStrin
return res;
}
return defaultValue.isNull() ? content : defaultValue;
} else if (type == ParamType::Bezier_spline) {
QLocale locale;
if (locale.decimalPoint() != QLocale::c().decimalPoint()) {
return content.replace(QLocale::c().decimalPoint(), locale.decimalPoint());
}
}
}
return content;
......@@ -833,8 +911,6 @@ const QVector<QPair<QString, QVariant>> AssetParameterModel::loadPreset(const QS
void AssetParameterModel::setParameters(const QVector<QPair<QString, QVariant>> &params, bool update)
{
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
ObjectType itemId;
if (!update) {
// Change itemId to NoItem to ensure we don't send any update like refreshProjectItem that would trigger monitor refreshes.
......@@ -842,11 +918,7 @@ void AssetParameterModel::setParameters(const QVector<QPair<QString, QVariant>>
m_ownerId.first = ObjectType::NoItem;
}
for (const auto &param : params) {
if (param.second.type() == QVariant::Double) {
setParameter(param.first, locale.toString(param.second.toDouble()), false);
} else {
setParameter(param.first, param.second.toString(), false);
}
setParameter(param.first, param.second.toString(), false);
}
if (m_keyframes) {
m_keyframes->refresh();
......
......@@ -42,12 +42,12 @@ class KeyframeModelList;
enum class ParamType {
Double,
List,
List, // Value can be chosen from a list of pre-defined ones
Bool,
Switch,
RestrictedAnim, // animated 1 dimensional param with linear support only
Animated,
AnimatedRect,
AnimatedRect, // Animated rects have X, Y, width, height, and opacity (in [0,1])
Geometry,
Addedgeometry,
KeyframeParam,
......@@ -71,7 +71,18 @@ class AssetParameterModel : public QAbstractListModel, public enable_shared_from
Q_OBJECT
public:
/**
*
* @param asset
* @param assetXml XML to parse, from project file
* @param assetId
* @param ownerId
* @param originalDecimalPoint If a decimal point other than “.” was used, try to replace all occurrences by a “.”
* so numbers are parsed correctly.
* @param parent
*/
explicit AssetParameterModel(std::unique_ptr<Mlt::Properties> asset, const QDomElement &assetXml, const QString &assetId, ObjectId ownerId,
const QString& originalDecimalPoint = QString(),
QObject *parent = nullptr);
~AssetParameterModel() override;
enum DataRoles {
......
......@@ -135,8 +135,6 @@ void AssetParameterView::setModel(const std::shared_ptr<AssetParameterModel> &mo
QVector<QPair<QString, QVariant>> AssetParameterView::getDefaultValues() const
{
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
QVector<QPair<QString, QVariant>> values;
for (int i = 0; i < m_model->rowCount(); ++i) {
QModelIndex index = m_model->index(i, 0);
......@@ -144,7 +142,7 @@ QVector<QPair<QString, QVariant>> AssetParameterView::getDefaultValues() const
auto type = m_model->data(index, AssetParameterModel::TypeRole).value<ParamType>();
QVariant defaultValue = m_model->data(index, AssetParameterModel::DefaultRole);
if (type == ParamType::KeyframeParam || type == ParamType::AnimatedRect) {