Warning, file /plasma/libksysguard/formatter/Formatter.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     formatBootTimestamp is based on TimeUtil class:
0005     SPDX-FileCopyrightText: 2014 Gregor Mi <codestruct@posteo.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "Formatter.h"
0011 
0012 #include <KFormat>
0013 #include <KLocalizedString>
0014 
0015 #include <QFontMetrics>
0016 #include <QLocale>
0017 #include <QTime>
0018 
0019 #include <cmath>
0020 #include <ctime>
0021 
0022 #include <time.h>
0023 #include <unistd.h>
0024 
0025 #include "formatter_debug.h"
0026 
0027 namespace KSysGuard
0028 {
0029 // TODO: Is there a bit nicer way to handle formatting?
0030 
0031 static KLocalizedString unitFormat(Unit unit)
0032 {
0033     const static KLocalizedString B = ki18nc("Bytes unit symbol", "%1 B");
0034     const static KLocalizedString KiB = ki18nc("Kilobytes unit symbol", "%1 KiB");
0035     const static KLocalizedString MiB = ki18nc("Megabytes unit symbol", "%1 MiB");
0036     const static KLocalizedString GiB = ki18nc("Gigabytes unit symbol", "%1 GiB");
0037     const static KLocalizedString TiB = ki18nc("Terabytes unit symbol", "%1 TiB");
0038     const static KLocalizedString PiB = ki18nc("Petabytes unit symbol", "%1 PiB");
0039 
0040     const static KLocalizedString bps = ki18nc("Bytes per second unit symbol", "%1 B/s");
0041     const static KLocalizedString Kbps = ki18nc("Kilobytes per second unit symbol", "%1 KiB/s");
0042     const static KLocalizedString Mbps = ki18nc("Megabytes per second unit symbol", "%1 MiB/s");
0043     const static KLocalizedString Gbps = ki18nc("Gigabytes per second unit symbol", "%1 GiB/s");
0044     const static KLocalizedString Tbps = ki18nc("Terabytes per second unit symbol", "%1 TiB/s");
0045     const static KLocalizedString Pbps = ki18nc("Petabytes per second unit symbol", "%1 PiB/s");
0046 
0047     const static KLocalizedString bitsps = ki18nc("Bits per second unit symbol", "%1 bps");
0048     const static KLocalizedString Kbitsps = ki18nc("Kilobits per second unit symbol", "%1 Kbps");
0049     const static KLocalizedString Mbitsps = ki18nc("Megabits per second unit symbol", "%1 Mbps");
0050     const static KLocalizedString Gbitsps = ki18nc("Gigabits per second unit symbol", "%1 Gbps");
0051     const static KLocalizedString Tbitsps = ki18nc("Terabits per second unit symbol", "%1 Tbps");
0052     const static KLocalizedString Pbitsps = ki18nc("Petabits per second unit symbol", "%1 Pbps");
0053 
0054     const static KLocalizedString Hz = ki18nc("Hertz unit symbol", "%1 Hz");
0055     const static KLocalizedString kHz = ki18nc("Kilohertz unit symbol", "%1 kHz");
0056     const static KLocalizedString MHz = ki18nc("Megahertz unit symbol", "%1 MHz");
0057     const static KLocalizedString GHz = ki18nc("Gigahertz unit symbol", "%1 GHz");
0058     const static KLocalizedString THz = ki18nc("Terahertz unit symbol", "%1 THz");
0059     const static KLocalizedString PHz = ki18nc("Petahertz unit symbol", "%1 PHz");
0060 
0061     const static KLocalizedString percent = ki18nc("Percent unit", "%1%");
0062     const static KLocalizedString RPM = ki18nc("Revolutions per minute unit symbol", "%1 RPM");
0063     const static KLocalizedString C = ki18nc("Celsius unit symbol", "%1°C");
0064     const static KLocalizedString dBm = ki18nc("Decibels unit symbol", "%1 dBm");
0065     const static KLocalizedString s = ki18nc("Seconds unit symbol", "%1s");
0066     const static KLocalizedString V = ki18nc("Volts unit symbol", "%1 V");
0067     const static KLocalizedString W = ki18nc("Watts unit symbol", "%1 W");
0068     const static KLocalizedString Wh = ki18nc("Watt-hours unit symbol", "%1 Wh");
0069     const static KLocalizedString rate = ki18nc("Rate unit symbol", "%1 s⁻¹");
0070     const static KLocalizedString A = ki18nc("Ampere unit symbol", "%1 A");
0071     const static KLocalizedString unitless = ki18nc("Unitless", "%1");
0072 
0073     switch (unit) {
0074     case UnitByte:
0075         return B;
0076     case UnitKiloByte:
0077         return KiB;
0078     case UnitMegaByte:
0079         return MiB;
0080     case UnitGigaByte:
0081         return GiB;
0082     case UnitTeraByte:
0083         return TiB;
0084     case UnitPetaByte:
0085         return PiB;
0086 
0087     case UnitByteRate:
0088         return bps;
0089     case UnitKiloByteRate:
0090         return Kbps;
0091     case UnitMegaByteRate:
0092         return Mbps;
0093     case UnitGigaByteRate:
0094         return Gbps;
0095     case UnitTeraByteRate:
0096         return Tbps;
0097     case UnitPetaByteRate:
0098         return Pbps;
0099 
0100     case UnitBitRate:
0101         return bitsps;
0102     case UnitKiloBitRate:
0103         return Kbitsps;
0104     case UnitMegaBitRate:
0105         return Mbitsps;
0106     case UnitGigaBitRate:
0107         return Gbitsps;
0108     case UnitTeraBitRate:
0109         return Tbitsps;
0110     case UnitPetaBitRate:
0111         return Pbitsps;
0112 
0113     case UnitHertz:
0114         return Hz;
0115     case UnitKiloHertz:
0116         return kHz;
0117     case UnitMegaHertz:
0118         return MHz;
0119     case UnitGigaHertz:
0120         return GHz;
0121     case UnitTeraHertz:
0122         return THz;
0123     case UnitPetaHertz:
0124         return PHz;
0125 
0126     case UnitCelsius:
0127         return C;
0128     case UnitDecibelMilliWatts:
0129         return dBm;
0130     case UnitPercent:
0131         return percent;
0132     case UnitRate:
0133         return rate;
0134     case UnitRpm:
0135         return RPM;
0136     case UnitSecond:
0137         return s;
0138     case UnitVolt:
0139         return V;
0140     case UnitWatt:
0141         return W;
0142     case UnitWattHour:
0143         return Wh;
0144     case UnitAmpere:
0145         return A;
0146 
0147     default:
0148         return unitless;
0149     }
0150 }
0151 
0152 static int unitOrder(Unit unit)
0153 {
0154     switch (unit) {
0155     case UnitByte:
0156     case UnitKiloByte:
0157     case UnitMegaByte:
0158     case UnitGigaByte:
0159     case UnitTeraByte:
0160     case UnitPetaByte:
0161     case UnitByteRate:
0162     case UnitKiloByteRate:
0163     case UnitMegaByteRate:
0164     case UnitGigaByteRate:
0165     case UnitTeraByteRate:
0166     case UnitPetaByteRate:
0167     case UnitBitRate:
0168     case UnitKiloBitRate:
0169     case UnitMegaBitRate:
0170     case UnitGigaBitRate:
0171     case UnitTeraBitRate:
0172     case UnitPetaBitRate:
0173         return 1024;
0174 
0175     case UnitHertz:
0176     case UnitKiloHertz:
0177     case UnitMegaHertz:
0178     case UnitGigaHertz:
0179     case UnitTeraHertz:
0180     case UnitPetaHertz:
0181     case UnitWatt:
0182     case UnitWattHour:
0183     case UnitAmpere:
0184         return 1000;
0185 
0186     default:
0187         return 0;
0188     }
0189 }
0190 
0191 static Unit unitBase(Unit unit)
0192 {
0193     switch (unit) {
0194     case UnitByte:
0195     case UnitKiloByte:
0196     case UnitMegaByte:
0197     case UnitGigaByte:
0198     case UnitTeraByte:
0199     case UnitPetaByte:
0200         return UnitByte;
0201 
0202     case UnitByteRate:
0203     case UnitKiloByteRate:
0204     case UnitMegaByteRate:
0205     case UnitGigaByteRate:
0206     case UnitTeraByteRate:
0207     case UnitPetaByteRate:
0208         return UnitByteRate;
0209 
0210     case UnitBitRate:
0211     case UnitKiloBitRate:
0212     case UnitMegaBitRate:
0213     case UnitGigaBitRate:
0214     case UnitTeraBitRate:
0215     case UnitPetaBitRate:
0216         return UnitBitRate;
0217 
0218     case UnitHertz:
0219     case UnitKiloHertz:
0220     case UnitMegaHertz:
0221     case UnitGigaHertz:
0222     case UnitTeraHertz:
0223     case UnitPetaHertz:
0224         return UnitHertz;
0225 
0226     default:
0227         return unit;
0228     }
0229 }
0230 
0231 static Unit adjustedUnit(qreal value, Unit unit, MetricPrefix prefix)
0232 {
0233     const int order = unitOrder(unit);
0234     if (!order) {
0235         return unit;
0236     }
0237 
0238     const Unit baseUnit = unitBase(unit);
0239     const MetricPrefix basePrefix = MetricPrefix(unit - baseUnit);
0240 
0241     if (prefix == MetricPrefixAutoAdjust) {
0242         const qreal absoluteValue = value * std::pow(order, int(basePrefix));
0243         if (absoluteValue > 0) {
0244             const int targetPrefix = std::log2(absoluteValue) / std::log2(order);
0245             if (targetPrefix <= MetricPrefixLast) {
0246                 prefix = MetricPrefix(targetPrefix);
0247             }
0248         }
0249         if (prefix == MetricPrefixAutoAdjust) {
0250             prefix = basePrefix;
0251         }
0252     }
0253 
0254     const Unit newUnit = Unit(prefix + baseUnit);
0255     // If there is no prefixed unit (e.g. no UnitKiloWatt),
0256     // don't overflow into the following unrelated units.
0257     if (unitBase(newUnit) != baseUnit) {
0258         return unit;
0259     }
0260 
0261     return newUnit;
0262 }
0263 
0264 static QString formatNumber(const QVariant &value, Unit unit, MetricPrefix prefix, FormatOptions options)
0265 {
0266     qreal amount = value.toDouble();
0267 
0268     if (!options.testFlag(FormatOptionShowNull) && (qFuzzyIsNull(amount) || qIsNaN(amount))) {
0269         return QString();
0270     }
0271 
0272     const Unit adjusted = adjustedUnit(amount, unit, prefix);
0273     if (adjusted != unit) {
0274         amount /= std::pow(unitOrder(unit), adjusted - unit);
0275     }
0276 
0277     const int precision = (value.type() != QVariant::Double && adjusted <= unit) ? 0 : 1;
0278     const QString text = QLocale().toString(amount, 'f', precision);
0279 
0280     return unitFormat(adjusted).subs(text).toString();
0281 }
0282 
0283 static QString formatTime(const QVariant &value)
0284 {
0285     return KFormat().formatDuration(value.toLongLong() * 1000);
0286 }
0287 
0288 static QString formatTicks(const QVariant &value)
0289 {
0290     auto seconds = value.toLongLong() / sysconf(_SC_CLK_TCK);
0291     return KFormat().formatDuration(seconds * 1000);
0292 }
0293 
0294 static QString formatBootTimestamp(const QVariant &value)
0295 {
0296     timespec tp;
0297 #ifdef Q_OS_LINUX
0298     clock_gettime(CLOCK_BOOTTIME, &tp);
0299 #else
0300     clock_gettime(CLOCK_MONOTONIC, &tp);
0301 #endif
0302 
0303     const QDateTime systemBootTime = QDateTime::currentDateTime().addSecs(-tp.tv_sec);
0304 
0305     const qreal secondsSinceSystemBoot = value.toReal() / sysconf(_SC_CLK_TCK);
0306     const QDateTime absoluteTimeSinceBoot = systemBootTime.addSecs(secondsSinceSystemBoot);
0307 
0308     return KFormat().formatRelativeDateTime(absoluteTimeSinceBoot, QLocale::ShortFormat);
0309 }
0310 
0311 qreal Formatter::scaleDownFactor(const QVariant &value, Unit unit, MetricPrefix targetPrefix)
0312 {
0313     const Unit adjusted = adjustedUnit(value.toDouble(), unit, targetPrefix);
0314     if (adjusted == unit) {
0315         return 1;
0316     }
0317 
0318     return std::pow(unitOrder(unit), adjusted - unit);
0319 }
0320 
0321 KLocalizedString Formatter::localizedString(const QVariant &value, Unit unit, MetricPrefix targetPrefix)
0322 {
0323     const Unit adjusted = adjustedUnit(value.toDouble(), unit, targetPrefix);
0324     return unitFormat(adjusted);
0325 }
0326 
0327 QString Formatter::formatValue(const QVariant &value, Unit unit, MetricPrefix targetPrefix, FormatOptions options)
0328 {
0329     switch (unit) {
0330     case UnitByte:
0331     case UnitKiloByte:
0332     case UnitMegaByte:
0333     case UnitGigaByte:
0334     case UnitTeraByte:
0335     case UnitPetaByte:
0336     case UnitByteRate:
0337     case UnitKiloByteRate:
0338     case UnitMegaByteRate:
0339     case UnitGigaByteRate:
0340     case UnitTeraByteRate:
0341     case UnitPetaByteRate:
0342     case UnitBitRate:
0343     case UnitKiloBitRate:
0344     case UnitMegaBitRate:
0345     case UnitGigaBitRate:
0346     case UnitTeraBitRate:
0347     case UnitPetaBitRate:
0348     case UnitHertz:
0349     case UnitKiloHertz:
0350     case UnitMegaHertz:
0351     case UnitGigaHertz:
0352     case UnitTeraHertz:
0353     case UnitPetaHertz:
0354     case UnitPercent:
0355     case UnitRate:
0356     case UnitRpm:
0357     case UnitCelsius:
0358     case UnitDecibelMilliWatts:
0359     case UnitVolt:
0360     case UnitWatt:
0361     case UnitWattHour:
0362     case UnitSecond:
0363     case UnitAmpere:
0364         return formatNumber(value, unit, targetPrefix, options);
0365 
0366     case UnitBootTimestamp:
0367         return formatBootTimestamp(value);
0368     case UnitTime:
0369         return formatTime(value);
0370     case UnitNone:
0371         return formatNumber(value, unit, MetricPrefix::MetricPrefixUnity, options);
0372     case UnitTicks:
0373         return formatTicks(value);
0374 
0375     default:
0376         return value.toString();
0377     }
0378 }
0379 
0380 QString Formatter::symbol(Unit unit)
0381 {
0382     // TODO: Is it possible to avoid duplication of these symbols?
0383     switch (unit) {
0384     case UnitByte:
0385         return i18nc("Bytes unit symbol", "B");
0386     case UnitKiloByte:
0387         return i18nc("Kilobytes unit symbol", "KiB");
0388     case UnitMegaByte:
0389         return i18nc("Megabytes unit symbol", "MiB");
0390     case UnitGigaByte:
0391         return i18nc("Gigabytes unit symbol", "GiB");
0392     case UnitTeraByte:
0393         return i18nc("Terabytes unit symbol", "TiB");
0394     case UnitPetaByte:
0395         return i18nc("Petabytes unit symbol", "PiB");
0396 
0397     case UnitByteRate:
0398         return i18nc("Bytes per second unit symbol", "B/s");
0399     case UnitKiloByteRate:
0400         return i18nc("Kilobytes per second unit symbol", "KiB/s");
0401     case UnitMegaByteRate:
0402         return i18nc("Megabytes per second unit symbol", "MiB/s");
0403     case UnitGigaByteRate:
0404         return i18nc("Gigabytes per second unit symbol", "GiB/s");
0405     case UnitTeraByteRate:
0406         return i18nc("Terabytes per second unit symbol", "TiB/s");
0407     case UnitPetaByteRate:
0408         return i18nc("Petabytes per second unit symbol", "PiB/s");
0409 
0410     case UnitBitRate:
0411         return i18nc("Bits per second unit symbol", "bps");
0412     case UnitKiloBitRate:
0413         return i18nc("Kilobits per second unit symbol", "Kbps");
0414     case UnitMegaBitRate:
0415         return i18nc("Megabits per second unit symbol", "Mbps");
0416     case UnitGigaBitRate:
0417         return i18nc("Gigabits per second unit symbol", "Gbps");
0418     case UnitTeraBitRate:
0419         return i18nc("Terabits per second unit symbol", "Tbps");
0420     case UnitPetaBitRate:
0421         return i18nc("Petabits per second unit symbol", "Pbps");
0422 
0423     case UnitHertz:
0424         return i18nc("Hertz unit symbol", "Hz");
0425     case UnitKiloHertz:
0426         return i18nc("Kilohertz unit symbol", "kHz");
0427     case UnitMegaHertz:
0428         return i18nc("Megahertz unit symbol", "MHz");
0429     case UnitGigaHertz:
0430         return i18nc("Gigahertz unit symbol", "GHz");
0431     case UnitTeraHertz:
0432         return i18nc("Terahertz unit symbol", "THz");
0433     case UnitPetaHertz:
0434         return i18nc("Petahertz unit symbol", "PHz");
0435 
0436     case UnitPercent:
0437         return i18nc("Percent unit", "%");
0438     case UnitRpm:
0439         return i18nc("Revolutions per minute unit symbol", "RPM");
0440     case UnitCelsius:
0441         return i18nc("Celsius unit symbol", "°C");
0442     case UnitDecibelMilliWatts:
0443         return i18nc("Decibels unit symbol", "dBm");
0444     case UnitSecond:
0445         return i18nc("Seconds unit symbol", "s");
0446     case UnitVolt:
0447         return i18nc("Volts unit symbol", "V");
0448     case UnitWatt:
0449         return i18nc("Watts unit symbol", "W");
0450     case UnitWattHour:
0451         return i18nc("Watt-hours unit symbol", "Wh");
0452     case UnitRate:
0453         return i18nc("Rate unit symbol", "s⁻¹");
0454     case UnitAmpere:
0455         return i18nc("Ampere unit symbol", "A");
0456 
0457     default:
0458         return QString();
0459     }
0460 }
0461 
0462 qreal Formatter::maximumLength(Unit unit, const QFont &font)
0463 {
0464     auto order = unitOrder(unit);
0465 
0466     QString maximum;
0467     switch (unitBase(unit)) {
0468     case UnitByte:
0469         maximum = formatValue(order - 0.5, UnitMegaByte, MetricPrefixMega);
0470         break;
0471     case UnitByteRate:
0472         maximum = formatValue(order - 0.5, UnitMegaByteRate, MetricPrefixMega);
0473         break;
0474     case UnitBitRate:
0475         maximum = formatValue(order - 0.5, UnitMegaBitRate, MetricPrefixMega);
0476         break;
0477     case UnitHertz:
0478         maximum = formatValue(order - 0.5, UnitMegaHertz, MetricPrefixMega);
0479         break;
0480     case UnitPercent:
0481         maximum = formatValue(9999.9, UnitPercent);
0482         break;
0483     default:
0484         return -1.0;
0485     }
0486 
0487     auto metrics = QFontMetrics{font};
0488     return metrics.horizontalAdvance(maximum);
0489 }
0490 
0491 } // namespace KSysGuard