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

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();
}
}
......
This diff is collapsed.
......@@ -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) {
QString val = type == ParamType::KeyframeParam ? locale.toString(defaultValue.toDouble()) : defaultValue.toString();
QString val = defaultValue.toString();
if (!val.contains(QLatin1Char('='))) {
val.prepend(QStringLiteral("%1=").arg(m_model->data(index, AssetParameterModel::ParentInRole).toInt()));
defaultValue = QVariant(val);
......
......@@ -1070,8 +1070,6 @@ const QMap<QString, QString> AnimationWidget::getAnimation()
int pos;
mlt_keyframe_type type;
QString key;
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
QStringList result;
int duration = m_outPoint;
for (int j = 0; j < m_animController.key_count(); ++j) {
......@@ -1092,7 +1090,7 @@ const QMap<QString, QString> AnimationWidget::getAnimation()
key.append(QStringLiteral("="));
break;
}
key.append(locale.toString(val));
key.append(QString::number(val, 'f'));
result << key;
}
animationResults.insert(i.key(), result.join(QLatin1Char(';')));
......
......@@ -17,9 +17,6 @@
***************************************************************************/
#include "cubicbezierspline.h"
#include <QLocale>
#include <QStringList>
#include <QVector>
#include <cmath>
/** @brief For sorting a Bezier spline. Whether a is before b. */
......@@ -44,7 +41,6 @@ CubicBezierSpline &CubicBezierSpline::operator=(const CubicBezierSpline &spline)
void CubicBezierSpline::fromString(const QString &spline)
{
m_points.clear();
QLocale locale;
const QStringList bpoints = spline.split(QLatin1Char('|'));
for (const QString &bpoint : bpoints) {
const QStringList points = bpoint.split(QLatin1Char('#'));
......@@ -52,7 +48,7 @@ void CubicBezierSpline::fromString(const QString &spline)
for (const QString &point : points) {
const QStringList xy = point.split(QLatin1Char(';'));
if (xy.count() == 2) {
values.append(QPointF(locale.toDouble(xy.at(0)), locale.toDouble(xy.at(1))));
values.append(QPointF(xy.at(0).toDouble(), xy.at(1).toDouble()));
}
}
if (values.count() == 3) {
......@@ -67,12 +63,11 @@ void CubicBezierSpline::fromString(const QString &spline)
QString CubicBezierSpline::toString() const
{
QStringList spline;
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
for (const BPoint &p : m_points) {
spline << QStringLiteral("%1;%2#%3;%4#%5;%6")
.arg(locale.toString(p.h1.x()), locale.toString(p.h1.y()), locale.toString(p.p.x()), locale.toString(p.p.y()), locale.toString(p.h2.x()),
locale.toString(p.h2.y()));
.arg(QString::number(p.h1.x(), 'f'), QString::number(p.h1.y(), 'f'), QString::number(p.p.x(), 'f'),
QString::number(p.p.y(), 'f'), QString::number(p.h2.x(), 'f'),
QString::number(p.h2.y(), 'f'));
}
return spline.join(QLatin1Char('|'));
}
......
......@@ -407,7 +407,6 @@ template <typename CurveWidget_t> void CurveParamWidget<CurveWidget_t>::slotRefr
{
if (m_model->data(m_index, AssetParameterModel::TypeRole).template value<ParamType>() == ParamType::Curve) {
QList<QPointF> points;
QLocale locale;
// Rounding gives really weird results. (int) (10 * 0.3) gives 2! So for now, add 0.5 to get correct result
int number = m_model->data(m_index, AssetParameterModel::Enum3Role).toDouble() * 10 + 0.5;
int start = m_model->data(m_index, AssetParameterModel::MinRole).toInt();
......
......@@ -32,15 +32,13 @@ DoubleParamWidget::DoubleParamWidget(std::shared_ptr<AssetParameterModel> model,
m_lay = new QVBoxLayout(this);
m_lay->setContentsMargins(0, 0, 0, 0);
m_lay->setSpacing(0);
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
// Retrieve parameters from the model
QString name = m_model->data(m_index, Qt::DisplayRole).toString();
double value = locale.toDouble(m_model->data(m_index, AssetParameterModel::ValueRole).toString());
double value = m_model->data(m_index, AssetParameterModel::ValueRole).toDouble();
double min = m_model->data(m_index, AssetParameterModel::MinRole).toDouble();
double max = m_model->data(m_index, AssetParameterModel::MaxRole).toDouble();
double defaultValue = locale.toDouble(m_model->data(m_index, AssetParameterModel::DefaultRole).toString());
double defaultValue = m_model->data(m_index, AssetParameterModel::DefaultRole).toDouble();
QString comment = m_model->data(m_index, AssetParameterModel::CommentRole).toString();
QString suffix = m_model->data(m_index, AssetParameterModel::SuffixRole).toString();
int decimals = m_model->data(m_index, AssetParameterModel::DecimalsRole).toInt();
......@@ -51,16 +49,14 @@ DoubleParamWidget::DoubleParamWidget(std::shared_ptr<AssetParameterModel> model,
setMinimumHeight(m_doubleWidget->height());
// Connect signal
connect(m_doubleWidget, &DoubleWidget::valueChanged, [this, locale](double val) { emit valueChanged(m_index, locale.toString(val), true); });
connect(m_doubleWidget, &DoubleWidget::valueChanged, [this](double val) { emit valueChanged(m_index, QString::number(val, 'f'), true); });
slotRefresh();
}
void DoubleParamWidget::slotRefresh()
{
QSignalBlocker bk(m_doubleWidget);
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
double value = locale.toDouble(m_model->data(m_index, AssetParameterModel::ValueRole).toString());
double value = m_model->data(m_index, AssetParameterModel::ValueRole).toDouble();
m_doubleWidget->setValue(value);
}
......
......@@ -113,13 +113,11 @@ void KeyframeEdit::addParameter(QModelIndex index, int activeKeyframe)
keyframe_list->blockSignals(true);
// Retrieve parameters from the model
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
QString name = m_model->data(index, Qt::DisplayRole).toString();
double value = 0; // locale.toDouble(m_model->data(index, AssetParameterModel::ValueRole).toString());
double min = m_model->data(index, AssetParameterModel::MinRole).toDouble();
double max = m_model->data(index, AssetParameterModel::MaxRole).toDouble();
double defaultValue = locale.toDouble(m_model->data(index, AssetParameterModel::DefaultRole).toString());
double defaultValue = m_model->data(index, AssetParameterModel::DefaultRole).toDouble();
QString comment = m_model->data(index, AssetParameterModel::CommentRole).toString();
QString suffix = m_model->data(index, AssetParameterModel::SuffixRole).toString();
int decimals = m_model->data(index, AssetParameterModel::DecimalsRole).toInt();
......@@ -131,14 +129,6 @@ void KeyframeEdit::addParameter(QModelIndex index, int activeKeyframe)
DoubleWidget *doubleparam = new DoubleWidget(name, value, min, max, m_model->data(index, AssetParameterModel::FactorRole).toDouble(), defaultValue, comment,
-1, suffix, decimals, m_model->data(index, AssetParameterModel::OddRole).toBool(), this);
/*DoubleParameterWidget *doubleparam = new DoubleParameterWidget(
paramName, 0, m_params.at(columnId).attribute(QStringLiteral("min")).toDouble(), m_params.at(columnId).attribute(QStringLiteral("max")).toDouble(),
m_params.at(columnId).attribute(QStringLiteral("default")).toDouble(), comment, columnId, m_params.at(columnId).attribute(QStringLiteral("suffix")),
m_params.at(columnId).attribute(QStringLiteral("decimals")).toInt(), false, this);*/
// Connect signal
// connect(doubleparam, &DoubleWidget::valueChanged, [this, locale, index](double value) { emit valueChanged(index, locale.toString(value)); });
connect(doubleparam, &DoubleWidget::valueChanged, this, &KeyframeEdit::slotAdjustKeyframeValue);
connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
// connect(doubleparam, SIGNAL(setInTimeline(int)), this, SLOT(slotUpdateVisibleParameter(int)));
......@@ -451,7 +441,7 @@ void KeyframeEdit::slotAdjustKeyframeInfo(bool seek)
m_position->setRange(min, max, true);
m_position->setPosition(getPos(item->row()));
m_position->blockSignals(false);
QLocale locale;
QLocale locale; // Used to convert user input → OK
for (int col = 0; col < keyframe_list->columnCount(); ++col) {
DoubleWidget *doubleparam = static_cast<DoubleWidget *>(m_slidersLayout->itemAtPosition(col, 0)->widget());
......
......@@ -646,7 +646,7 @@ void KeyframeImport::importSelectedData()
Fun redo = []() { return true; };
// Geometry target
int finalAlign = m_alignCombo->currentIndex();
QLocale locale;
QLocale locale; // Import from clipboard – OK to use locale here?
locale.setNumberOptions(QLocale::OmitGroupSeparator);
for (const auto &ix : m_indexes) {
// update keyframes in other indexes
......
......@@ -237,8 +237,7 @@ void KeyframeWidget::slotRefreshParams()
if (vals.count() >= 4) {
rect = QRect(vals.at(0).toInt(), vals.at(1).toInt(), vals.at(2).toInt(), vals.at(3).toInt());
if (vals.count() > 4) {
QLocale locale;
opacity = locale.toDouble(vals.at(4));
opacity = vals.at(4).toDouble();
}
}
((GeometryWidget *)w.second)->setValue(rect, opacity);
......@@ -329,8 +328,6 @@ void KeyframeWidget::resetKeyframes()
void KeyframeWidget::addParameter(const QPersistentModelIndex &index)
{
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
// Retrieve parameters from the model
QString name = m_model->data(index, Qt::DisplayRole).toString();
QString comment = m_model->data(index, AssetParameterModel::CommentRole).toString();
......@@ -351,7 +348,7 @@ void KeyframeWidget::addParameter(const QPersistentModelIndex &index)
if (vals.count() > 3) {
rect = QRect(vals.at(0).toInt(), vals.at(1).toInt(), vals.at(2).toInt(), vals.at(3).toInt());
if (vals.count() > 4) {
opacity = locale.toDouble(vals.at(4));
opacity = vals.at(4).toDouble();
}
}
// qtblend uses an opacity value in the (0-1) range, while older geometry effects use (0-100)
......@@ -385,11 +382,11 @@ void KeyframeWidget::addParameter(const QPersistentModelIndex &index)
}
}
double value = m_keyframes->getInterpolatedValue(getPosition(), index).toDouble();
double min = locale.toDouble(m_model->data(index, AssetParameterModel::MinRole).toString());
double max = locale.toDouble(m_model->data(index, AssetParameterModel::MaxRole).toString());
double min = m_model->data(index, AssetParameterModel::MinRole).toDouble();
double max = m_model->data(index, AssetParameterModel::MaxRole).toDouble();
double defaultValue = m_model->data(index, AssetParameterModel::DefaultRole).toDouble();
int decimals = m_model->data(index, AssetParameterModel::DecimalsRole).toInt();
double factor = locale.toDouble(m_model->data(index, AssetParameterModel::FactorRole).toString());
double factor = m_model->data(index, AssetParameterModel::FactorRole).toDouble();
factor = qFuzzyIsNull(factor) ? 1 : factor;
auto doubleWidget = new DoubleWidget(name, value, min, max, factor, defaultValue, comment, -1, suffix, decimals, m_model->data(index, AssetParameterModel::OddRole).toBool(), this);
connect(doubleWidget, &DoubleWidget::valueChanged,
......
......@@ -128,16 +128,8 @@ void ListParamWidget::slotRefresh()
if (names.count() != values.count()) {
names = values;
}
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
for (int i = 0; i < names.count(); i++) {
QString val = values.at(i);
bool ok;
double num = val.toDouble(&ok);
if (ok) {
val = locale.toString(num);
}
m_list->addItem(names.at(i), val);
m_list->addItem(names.at(i), values.at(i));
}
if (!value.isEmpty()) {
int ix = m_list->findData(value);
......
......@@ -34,7 +34,6 @@ LumaLiftGainParam::LumaLiftGainParam(std::shared_ptr<AssetParameterModel> model,
{
m_flowLayout = new FlowLayout(this, 2, 2, 2);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_locale.setNumberOptions(QLocale::OmitGroupSeparator);
m_lift = new ColorWheel(QStringLiteral("lift"), i18n("Lift"), NegQColor(), this);
m_lift->setFactorDefaultZero(LIFT_FACTOR, 0, 0.5);
connect(m_lift, &ColorWheel::colorChange, this, &LumaLiftGainParam::liftChanged);
......@@ -60,19 +59,19 @@ LumaLiftGainParam::LumaLiftGainParam(std::shared_ptr<AssetParameterModel> model,
connect(this, &LumaLiftGainParam::liftChanged, [this, indexes]() {
NegQColor liftColor = m_lift->color();
QList <QModelIndex> ixes{indexes.value(QStringLiteral("lift_r")),indexes.value(QStringLiteral("lift_g")), indexes.value(QStringLiteral("lift_b"))};
QStringList values {m_locale.toString(liftColor.redF()), m_locale.toString(liftColor.greenF()), m_locale.toString(liftColor.blueF())};
QStringList values {QString::number(liftColor.redF(), 'f'), QString::number(liftColor.greenF(), 'f'), QString::number(liftColor.blueF(), 'f')};
emit valuesChanged(ixes, values, true);
});
connect(this, &LumaLiftGainParam::gammaChanged, [this, indexes]() {
NegQColor gammaColor = m_gamma->color();
QList <QModelIndex> ixes{indexes.value(QStringLiteral("gamma_r")),indexes.value(QStringLiteral("gamma_g")), indexes.value(QStringLiteral("gamma_b"))};
QStringList values {m_locale.toString(gammaColor.redF() * GAMMA_FACTOR), m_locale.toString(gammaColor.greenF() * GAMMA_FACTOR), m_locale.toString(gammaColor.blueF() * GAMMA_FACTOR)};
QStringList values {QString::number(gammaColor.redF() * GAMMA_FACTOR, 'f'), QString::number(gammaColor.greenF() * GAMMA_FACTOR, 'f'), QString::number(gammaColor.blueF() * GAMMA_FACTOR, 'f')};
emit valuesChanged(ixes, values, true);
});
connect(this, &LumaLiftGainParam::gainChanged, [this, indexes]() {
NegQColor gainColor = m_gain->color();
QList <QModelIndex> ixes{indexes.value(QStringLiteral("gain_r")),indexes.value(QStringLiteral("gain_g")), indexes.value(QStringLiteral("gain_b"))};
QStringList values {m_locale.toString(gainColor.redF() * GAIN_FACTOR), m_locale.toString(gainColor.greenF() * GAIN_FACTOR), m_locale.toString(gainColor.blueF() * GAIN_FACTOR)};
QStringList values {QString::number(gainColor.redF() * GAIN_FACTOR, 'f'), QString::number(gainColor.greenF() * GAIN_FACTOR, 'f'), QString::number(gainColor.blueF() * GAIN_FACTOR, 'f')};
emit valuesChanged(ixes, values, true);
});
}
......@@ -103,7 +102,7 @@ void LumaLiftGainParam::updateEffect(QDomElement &effect)
}
if (values.contains(pa.attribute(QStringLiteral("name")))) {
pa.setAttribute(QStringLiteral("value"), (int)(values.value(pa.attribute(QStringLiteral("name"))) *
m_locale.toDouble(pa.attribute(QStringLiteral("factor"), QStringLiteral("1")))));
pa.attribute(QStringLiteral("factor"), QStringLiteral("1")).toDouble()));
}
}
}
......@@ -124,7 +123,7 @@ void LumaLiftGainParam::slotRefresh()
for (int i = 0; i < m_model->rowCount(); ++i) {
QModelIndex local_index = m_model->index(i, 0);
QString name = m_model->data(local_index, AssetParameterModel::NameRole).toString();
double val = m_locale.toDouble(m_model->data(local_index, AssetParameterModel::ValueRole).toString());