Commit f07ea0f6 authored by Simon Eugster's avatar Simon Eugster

Fix some decimal separator issues while upgrading

Properties are converted to C locale on-the-fly.

Related: #713
parent cf951dbc
......@@ -17,15 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "../src/lib/localeHandling.h"
#include "mlt++/Mlt.h"
#include "renderjob.h"
#include <QtGlobal>
#include <QApplication>
#include <QDir>
#include <QDomDocument>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QObject>
#include <QtGlobal>
#include <cstdio>
int main(int argc, char **argv)
......@@ -83,8 +84,7 @@ int main(int argc, char **argv)
// 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();
std::setlocale(LC_ALL, "C");
::qputenv("LC_ALL", "C");
LocaleHandling::resetLocale();
Mlt::Profile profile(profilePath.toUtf8().constData());
profile.set_explicit(1);
......
......@@ -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
......
This diff is collapsed.
......@@ -25,6 +25,7 @@
#include <QMap>
#include <QUrl>
#include <QtCore/QLocale>
class DocumentValidator
{
......@@ -43,6 +44,7 @@ private:
bool m_modified;
/** @brief Upgrade from a previous Kdenlive document version. */
bool upgrade(double version, const double currentVersion);
bool upgradeTo100(const QLocale &documentLocale);
/** @brief Pass producer properties from previous Kdenlive versions. */
void updateProducerInfo(const QDomElement &prod, const QDomElement &source);
/** @brief Make sur we don't have orphaned producers (that are not in Bin). */
......
......@@ -303,10 +303,16 @@ const QByteArray KdenliveDoc::getProjectXml()
{
const QByteArray result = m_document.toString().toUtf8();
// We don't need the xml data anymore, throw away
// TODO This is a getter – should not have any side effects! Fix or rename!
m_document.clear();
qDebug() << "Project XML: " << result;
return result;
}
QString KdenliveDoc::getLcNumeric() {
return m_document.documentElement().attribute("LC_NUMERIC");
}
QDomDocument KdenliveDoc::createEmptyDocument(int videotracks, int audiotracks)
{
QList<TrackInfo> tracks;
......@@ -1261,8 +1267,8 @@ QMap<QString, QString> KdenliveDoc::documentProperties()
m_projectFolder + QLatin1Char('/') + m_documentProperties.value(QStringLiteral("documentid")));
}
m_documentProperties.insert(QStringLiteral("profile"), pCore->getCurrentProfile()->path());
if (!m_documentProperties.contains(QStringLiteral("decimalPoint"))) {
m_documentProperties.insert(QStringLiteral("decimalPoint"), QLocale().decimalPoint());
if (m_documentProperties.contains(QStringLiteral("decimalPoint"))) {
m_documentProperties.remove(QStringLiteral("decimalPoint"));
}
return m_documentProperties;
}
......
......@@ -63,6 +63,7 @@ public:
friend class LoadJob;
/** @brief Get current document's producer. */
const QByteArray getProjectXml();
QString getLcNumeric();
double fps() const;
int width() const;
int height() const;
......
/*
Copyright (C) 2020 Simon A. Eugster <simon.eu@gmail.com>
This file is part of kdenlive. See www.kdenlive.org.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
#include "localeHandling.h"
#include <QtCore/QDebug>
#include <QtCore/QList>
auto LocaleHandling::setLocale(const QString &lcName) -> QString
{
QString newLocale;
QList<QString> localesToTest;
localesToTest << lcName << lcName + ".utf-8" << lcName + ".UTF-8" << lcName + ".utf8" << lcName + ".UTF8";
for (const auto &locale : localesToTest) {
auto *result = std::setlocale(LC_ALL, locale.toStdString().c_str());
if (result != nullptr) {
::qputenv("LC_ALL", locale.toStdString().c_str());
newLocale = locale;
break;
}
}
if (newLocale.isEmpty()) {
resetLocale();
}
return newLocale;
}
void LocaleHandling::resetLocale()
{
std::setlocale(LC_ALL, "C");
::qputenv("LC_ALL", "C");
qDebug() << "LC_ALL reset to C";
}
QPair<QLocale, LocaleHandling::MatchType> LocaleHandling::getQLocaleForDecimalPoint(const QString &requestedLocale, const QString &decimalPoint)
{
// Parse installed locales to find one matching
const QList<QLocale> list = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale().script(), QLocale::AnyCountry);
QLocale matching = QLocale::c();
QLocale locale;
MatchType matchType = MatchType::NoMatch;
for (const QLocale &loc : list) {
if (loc.name().startsWith(requestedLocale)) {
if (loc.decimalPoint() == decimalPoint) {
locale = loc;
matchType = MatchType::Exact;
}
}
}
if (matchType == MatchType::NoMatch) {
for (const QLocale &loc : list) {
if (loc.decimalPoint() == decimalPoint) {
locale = loc;
matchType = MatchType::DecimalOnly;
}
}
}
return QPair<QLocale, LocaleHandling::MatchType>(locale, matchType);
}
/*
Copyright (C) 2020 Simon A. Eugster <simon.eu@gmail.com>
This file is part of kdenlive. See www.kdenlive.org.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
#ifndef KDENLIVE_LOCALEHANDLING_H
#define KDENLIVE_LOCALEHANDLING_H
#include <QtCore/QLocale>
#include <QtCore/QString>
class LocaleHandling
{
public:
enum class MatchType { Exact = 0, DecimalOnly = 1, NoMatch = 2 };
/**
* Set LC_ALL to the desired locale.
* The function also tries variants of .utf-8 appendixes if setting the plain locale fails.
* @return The locale which was set, or an empty string if no locale could be set (e.g. not installed on the system).
*/
static QString setLocale(const QString &lcName);
static QPair<QLocale, LocaleHandling::MatchType> getQLocaleForDecimalPoint(const QString &requestedLocale, const QString &decimalPoint);
/**
* Reset LC_ALL to "C".
* This is used for MLT to ensure that numbers are always serialised the same way
* regardless of the user's locale; many locales use , or another character as decimal point.
*/
static void resetLocale();
};
#endif // KDENLIVE_LOCALEHANDLING_H
......@@ -15,16 +15,16 @@ the Free Software Foundation, either version 3 of the License, or
#include "mainwindow.h"
#include "mlt_config.h"
#include <KUrlRequesterDialog>
#include <config-kdenlive.h>
#include <klocalizedstring.h>
#include <QtConcurrent>
#include "kdenlive_debug.h"
#include <QFile>
#include <QStandardPaths>
#include <framework/mlt_log.h>
#include <lib/localeHandling.h>
#include <mlt++/MltFactory.h>
#include <mlt++/MltRepository.h>
#include <framework/mlt_log.h>
static void mlt_log_handler(void *service, int mlt_level, const char *format, va_list args)
{
......@@ -89,9 +89,10 @@ MltConnection::MltConnection(const QString &mltPath)
// After initialising the MLT factory, set the locale back from user default to C
// to ensure numbers are always serialised with . as decimal point.
m_repository = std::unique_ptr<Mlt::Repository>(Mlt::Factory::init());
std::setlocale(LC_ALL, "C");
::qputenv("LC_ALL", "C");
qDebug() << "LC_ALL set to C after initialising MLT";
LocaleHandling::resetLocale();
auto locale = strdup(std::setlocale(LC_ALL, nullptr));
qDebug() << "NEW LC_ALL" << locale;
locateMeltAndProfilesPath(mltPath);
......
......@@ -38,6 +38,7 @@
#include "profiles/profilemodel.hpp"
#include "timeline2/view/qml/timelineitems.h"
#include <mlt++/Mlt.h>
#include <lib/localeHandling.h>
#ifndef GL_UNPACK_ROW_LENGTH
#ifdef GL_UNPACK_ROW_LENGTH_EXT
......@@ -1365,6 +1366,7 @@ void GLWidget::resetConsumer(bool fullReset)
const QString GLWidget::sceneList(const QString &root, const QString &fullPath)
{
LocaleHandling::resetLocale();
QString playlist;
qCDebug(KDENLIVE_LOG) << " * * *Setting document xml root: " << root;
Mlt::Consumer xmlConsumer(pCore->getCurrentProfile()->profile(), "xml", fullPath.isEmpty() ? "kdenlive_playlist" : fullPath.toUtf8().constData());
......
......@@ -51,6 +51,7 @@ the Free Software Foundation, either version 3 of the License, or
#include <QProgressDialog>
#include <QTimeZone>
#include <audiomixer/mixermanager.hpp>
#include <lib/localeHandling.h>
static QString getProjectNameFilters(bool ark=true) {
auto filter = i18n("Kdenlive project (*.kdenlive)");
......@@ -217,7 +218,6 @@ void ProjectManager::newFile(QString profileName, bool showProjectSettings)
}
bool openBackup;
m_notesPlugin->clear();
documentProperties.insert(QStringLiteral("decimalPoint"), QLocale().decimalPoint());
KdenliveDoc *doc = new KdenliveDoc(QUrl(), projectFolder, pCore->window()->m_commandStack, profileName, documentProperties, documentMetadata, projectTracks, audioChannels, &openBackup, pCore->window());
doc->m_autosave = new KAutoSaveFile(startFile, doc);
ThumbnailCache::get()->clearCache();
......@@ -857,15 +857,34 @@ bool ProjectManager::updateTimeline(int pos, int scrollPos)
{
Q_UNUSED(scrollPos);
pCore->jobManager()->slotCancelJobs();
/*qDebug() << "Loading xml"<<m_project->getProjectXml().constData();
QFile file("/tmp/data.xml");
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << m_project->getProjectXml() << endl;
}*/
pCore->window()->getMainTimeline()->loading = true;
pCore->window()->slotSwitchTimelineZone(m_project->getDocumentProperty(QStringLiteral("enableTimelineZone")).toInt() == 1);
auto lcNumericCategory = m_project->getLcNumeric();
if (lcNumericCategory.isEmpty() || lcNumericCategory == "C") {
// Default locale is C. All fine, no number format issues to expect.
} else {
qDebug() << "Document uses the locale " << lcNumericCategory << ", switching locale for loading the document";
QString newLocale = LocaleHandling::setLocale(lcNumericCategory);
if (newLocale.isEmpty()) {
qDebug() << "Could not switch locale. Is it installed?";
auto res =
KMessageBox::warningYesNo(qApp->activeWindow(), i18n("This project file uses the locale %1 but it is not installed on the system. Load anyway? "
"Warning: Loaded project may be corrupted or cause a crash.",
lcNumericCategory));
if (res == KMessageBox::No) {
newFile(false);
return false;
} else {
qDebug() << "WARNING: Loading project with locale " << lcNumericCategory << " which is not found on the system.";
}
} else {
qDebug() << "Locale successfully switched to " << newLocale;
}
}
QScopedPointer<Mlt::Producer> xmlProd(new Mlt::Producer(pCore->getCurrentProfile()->profile(), "xml-string", m_project->getProjectXml().constData()));
Mlt::Service s(*xmlProd);
Mlt::Tractor tractor(s);
if (tractor.count() == 0) {
......@@ -907,13 +926,16 @@ bool ProjectManager::updateTimeline(int pos, int scrollPos)
pCore->monitorManager()->projectMonitor()->setProducer(m_mainTimelineModel->producer(), pos);
pCore->monitorManager()->projectMonitor()->adjustRulerSize(m_mainTimelineModel->duration() - 1, m_project->getGuideModel());
pCore->window()->getMainTimeline()->controller()->setZone(m_project->zone(), false);
//pCore->window()->getMainTimeline()->controller()->setTargetTracks(m_project->targetTracks());
pCore->window()->getMainTimeline()->controller()->setScrollPos(m_project->getDocumentProperty(QStringLiteral("scrollPos")).toInt());
int activeTrackPosition = m_project->getDocumentProperty(QStringLiteral("activeTrack"), QString::number( - 1)).toInt();
if (activeTrackPosition > -1 && activeTrackPosition < m_mainTimelineModel->getTracksCount()) {
pCore->window()->getMainTimeline()->controller()->setActiveTrack(m_mainTimelineModel->getTrackIndexFromPosition(activeTrackPosition));
}
m_mainTimelineModel->setUndoStack(m_project->commandStack());
// Reset locale to C to ensure numbers are serialised correctly
LocaleHandling::resetLocale();
return true;
}
......
......@@ -180,9 +180,9 @@ private slots:
signals:
void docOpened(KdenliveDoc *document);
// void projectOpened(Project *project);
protected:
/** @brief Update the timeline according to the MLT XML */
bool updateTimeline(int pos = -1, int scrollPos = -1);
private:
......
......@@ -19,10 +19,11 @@
***************************************************************************/
#include "mltpreview.h"
#include "../src/lib/localeHandling.h"
#include <QtGlobal>
#include <QImage>
#include <QVarLengthArray>
#include <QtGlobal>
#include <QDebug>
#include <krandomsequence.h>
......@@ -39,9 +40,7 @@ MltPreview::MltPreview()
// 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();
std::setlocale(LC_ALL, "C");
::qputenv("LC_ALL", "C");
qDebug() << "LC_ALL set to C after initialising MLT";
LocaleHandling::resetLocale();
}
MltPreview::~MltPreview()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment