diff --git a/runners/converter/converterrunner.cpp b/runners/converter/converterrunner.cpp index ebff7acf7..327aab327 100644 --- a/runners/converter/converterrunner.cpp +++ b/runners/converter/converterrunner.cpp @@ -1,261 +1,267 @@ /* * Copyright (C) 2007,2008 Petri Damstén * Copyright (C) 2020 Alexander Lohnau * * 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 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "converterrunner.h" #include #include #include #include #include #include ConverterRunner::ConverterRunner(QObject *parent, const QVariantList &args) : Plasma::AbstractRunner(parent, args) { setObjectName(QStringLiteral("Converter")); //can not ignore commands: we have things like m4 setIgnoredTypes(Plasma::RunnerContext::Directory | Plasma::RunnerContext::File | Plasma::RunnerContext::NetworkLocation); const QString description = i18n("Converts the value of :q: when :q: is made up of " "\"value unit [>, to, as, in] unit\". You can use the " "Unit converter applet to find all available units."); addSyntax(Plasma::RunnerSyntax(QStringLiteral(":q:"), description)); } void ConverterRunner::init() { valueRegex = QRegularExpression(QStringLiteral("^([0-9,./+-]+)")); const QStringList conversionWords = i18nc("list of words that can used as amount of 'unit1' [in|to|as] 'unit2'", "in;to;as").split(QLatin1Char(';')); QString conversionRegex; for (const auto &word: conversionWords) { conversionRegex.append(QLatin1Char(' ') + word + QStringLiteral(" |")); } conversionRegex.append(QStringLiteral(" ?> ?")); unitSeperatorRegex = QRegularExpression(conversionRegex); valueRegex.optimize(); unitSeperatorRegex.optimize(); insertCompatibleUnits(); addAction(copyActionId, QIcon::fromTheme(QStringLiteral("edit-copy")), QStringLiteral("Copy number")); addAction(copyUnitActionId, QIcon::fromTheme(QStringLiteral("edit-copy")), QStringLiteral("Copy unit and number")); actionList = {action(copyActionId), action(copyUnitActionId)}; } ConverterRunner::~ConverterRunner() = default; void ConverterRunner::match(Plasma::RunnerContext &context) { const QString term = context.query(); if (term.size() < 2 || !context.isValid()) { return; } const QRegularExpressionMatch valueRegexMatch = valueRegex.match(context.query()); if (!valueRegexMatch.hasMatch()) { return; } const QString inputValueString = valueRegexMatch.captured(1); // Get the different units by splitting up the query with the regex - QStringList unitStrings = context.query().simplified().remove(valueRegex).split(unitSeperatorRegex); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + QStringList unitStrings = context.query().simplified().remove(valueRegex) + .split(unitSeperatorRegex, QString::SkipEmptyParts); +#else + QStringList unitStrings = context.query().simplified().remove(valueRegex) + .split(unitSeperatorRegex, Qt::SkipEmptyParts); +#endif if (unitStrings.isEmpty()) { return; } // Check if unit is valid, otherwise check for the value in the compatibleUnits map QString inputUnitString = unitStrings.first().simplified(); KUnitConversion::UnitCategory inputCategory = converter.categoryForUnit(inputUnitString); if (inputCategory.id() == KUnitConversion::InvalidCategory) { inputUnitString = compatibleUnits.value(inputUnitString.toUpper()); if (inputUnitString.isEmpty()) { return; } inputCategory = converter.categoryForUnit(inputUnitString); if (inputCategory.id() == KUnitConversion::InvalidCategory) { return; } } QString outputUnitString; if (unitStrings.size() == 2) { outputUnitString = unitStrings.at(1).simplified(); } const KUnitConversion::Unit inputUnit = inputCategory.unit(inputUnitString); const QList outputUnits = createResultUnits(outputUnitString, inputCategory); const auto numberDataPair = getValidatedNumberValue(inputValueString); // Return on invalid user input if (!numberDataPair.first) { return; } const double numberValue = numberDataPair.second; QList matches; for (const KUnitConversion::Unit &outputUnit: outputUnits) { KUnitConversion::Value outputValue = inputCategory.convert( KUnitConversion::Value(numberValue, inputUnit), outputUnit); if (!outputValue.isValid() || inputUnit == outputUnit) { continue; } Plasma::QueryMatch match(this); match.setType(Plasma::QueryMatch::InformationalMatch); match.setIconName(QStringLiteral("accessories-calculator")); if (outputUnit.categoryId() == KUnitConversion::CurrencyCategory) { outputValue.round(2); match.setText(QStringLiteral("%1 (%2)").arg(outputValue.toString(0, 'f', 2), outputUnit.symbol())); } else { match.setText(QStringLiteral("%1 (%2)").arg(outputValue.toString(), outputUnit.symbol())); } match.setData(outputValue.number()); match.setRelevance(1.0 - std::abs(std::log10(outputValue.number())) / 50.0); matches.append(match); } context.addMatches(matches); } QList ConverterRunner::actionsForMatch(const Plasma::QueryMatch &match) { Q_UNUSED(match) return actionList; } void ConverterRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) { Q_UNUSED(context) if (match.selectedAction() == action(copyActionId)) { QGuiApplication::clipboard()->setText(match.data().toString()); } else { QGuiApplication::clipboard()->setText(match.text().split(QLatin1String(" (")).first()); } } QPair ConverterRunner::stringToDouble(const QStringRef &value) { bool ok; double numberValue = locale.toDouble(value, &ok); if (!ok) { numberValue = value.toDouble(&ok); } return {ok, numberValue}; } QPair ConverterRunner::getValidatedNumberValue(const QString &value) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) const auto fractionParts = value.splitRef(QLatin1Char('/'), QString::SkipEmptyParts); #else const auto fractionParts = value.splitRef(QLatin1Char('/'), Qt::SkipEmptyParts); #endif if (fractionParts.isEmpty() || fractionParts.count() > 2) { return {false, 0}; } if (fractionParts.count() == 2) { const QPair doubleFirstResults = stringToDouble(fractionParts.first()); if (!doubleFirstResults.first) { return {false, 0}; } const QPair doubleSecondResult = stringToDouble(fractionParts.last()); if (!doubleSecondResult.first || qFuzzyIsNull(doubleSecondResult.second)) { return {false, 0}; } return {true, doubleFirstResults.second / doubleSecondResult.second}; } else if (fractionParts.count() == 1) { const QPair doubleResult = stringToDouble(fractionParts.first()); if (!doubleResult.first) { return {false, 0}; } return {true, doubleResult.second}; } else { return {true, 0}; } } QList ConverterRunner::createResultUnits(QString &outputUnitString, const KUnitConversion::UnitCategory &category) { QList units; if (!outputUnitString.isEmpty()) { KUnitConversion::Unit outputUnit = category.unit(outputUnitString); if (!outputUnit.isNull() && outputUnit.isValid()) { units.append(outputUnit); } else { // Autocompletion for the target units outputUnitString = outputUnitString.toUpper(); for (const auto &unitStringKey: compatibleUnits.keys()) { if (unitStringKey.startsWith(outputUnitString)) { outputUnit = category.unit(compatibleUnits.value(unitStringKey)); if (!units.contains(outputUnit)) { units << outputUnit; } } } } } else { units = category.mostCommonUnits(); // suggest converting to the user's local currency if (category.id() == KUnitConversion::CurrencyCategory) { const QString ¤cyIsoCode = QLocale().currencySymbol(QLocale::CurrencyIsoCode); const KUnitConversion::Unit localCurrency = category.unit(currencyIsoCode); if (localCurrency.isValid() && !units.contains(localCurrency)) { units << localCurrency; } } } return units; } void ConverterRunner::insertCompatibleUnits() { // Add all currency symbols to the map, if their ISO code is supported by backend const QList allLocales = QLocale::matchingLocales( QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); KUnitConversion::UnitCategory currencyCategory = converter.category(QStringLiteral("Currency")); const QStringList availableISOCodes = currencyCategory.allUnits(); QRegularExpression hasCurrencyRegex = QRegularExpression(QStringLiteral("\\p{Sc}")); hasCurrencyRegex.optimize(); for (const auto ¤cyLocale: allLocales) { const QString symbol = currencyLocale.currencySymbol(QLocale::CurrencySymbol); const QString isoCode = currencyLocale.currencySymbol(QLocale::CurrencyIsoCode); if (isoCode.isEmpty() || !symbol.contains(hasCurrencyRegex)) { continue; } if (availableISOCodes.contains(isoCode)) { compatibleUnits.insert(symbol.toUpper(), isoCode); } } // Add all units as uppercase in the map for (const auto &category: converter.categories()) { for (const auto &unit: category.allUnits()) { compatibleUnits.insert(unit.toUpper(), unit); } } }