Formatter.cpp 11.5 KB
Newer Older
1
/*
2
    Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

    formatBootTimestamp is based on TimeUtil class:
    Copyright (C) 2014 Gregor Mi <codestruct@posteo.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

23
#include "Formatter.h"
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

#include <KLocalizedString>

#include <QLocale>
#include <QTime>

#include <cmath>

#ifdef Q_OS_OSX
#include <mach/clock.h>
#include <mach/mach.h>
#else
#include <ctime>
#endif

#include <unistd.h>

David Redondo's avatar
David Redondo committed
41 42
#include "formatter_debug.h"

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
namespace KSysGuard
{

// TODO: Is there a bit nicer way to handle formatting?

static KLocalizedString unitFormat(Unit unit)
{
    const static KLocalizedString B = ki18nc("Bytes unit symbol", "%1 B");
    const static KLocalizedString KiB = ki18nc("Kilobytes unit symbol", "%1 KiB");
    const static KLocalizedString MiB = ki18nc("Megabytes unit symbol", "%1 MiB");
    const static KLocalizedString GiB = ki18nc("Gigabytes unit symbol", "%1 GiB");
    const static KLocalizedString TiB = ki18nc("Terabytes unit symbol", "%1 TiB");
    const static KLocalizedString PiB = ki18nc("Petabytes unit symbol", "%1 PiB");

    const static KLocalizedString bps = ki18nc("Bytes per second unit symbol", "%1 B/s");
    const static KLocalizedString Kbps = ki18nc("Kilobytes per second unit symbol", "%1 KiB/s");
    const static KLocalizedString Mbps = ki18nc("Megabytes per second unit symbol", "%1 MiB/s");
    const static KLocalizedString Gbps = ki18nc("Gigabytes per second unit symbol", "%1 GiB/s");
    const static KLocalizedString Tbps = ki18nc("Gigabytes per second unit symbol", "%1 TiB/s");
    const static KLocalizedString Pbps = ki18nc("Gigabytes per second unit symbol", "%1 PiB/s");

    const static KLocalizedString Hz = ki18nc("Hertz unit symbol", "%1 Hz");
    const static KLocalizedString kHz = ki18nc("Kilohertz unit symbol", "%1 kHz");
    const static KLocalizedString MHz = ki18nc("Megahertz unit symbol", "%1 MHz");
    const static KLocalizedString GHz = ki18nc("Gigahertz unit symbol", "%1 GHz");
    const static KLocalizedString THz = ki18nc("Terahertz unit symbol", "%1 THz");
    const static KLocalizedString PHz = ki18nc("Petahertz unit symbol", "%1 PHz");

    const static KLocalizedString percent = ki18nc("Percent unit", "%1%");
    const static KLocalizedString RPM = ki18nc("Revolutions per minute unit symbol", "%1 RPM");
    const static KLocalizedString C = ki18nc("Celsius unit symbol", "%1°C");
    const static KLocalizedString dBm = ki18nc("Decibels unit symbol", "%1 dBm");
    const static KLocalizedString s = ki18nc("Seconds unit symbol", "%1s");
    const static KLocalizedString V = ki18nc("Volts unit symbol", "%1 V");
    const static KLocalizedString W = ki18nc("Watts unit symbol", "%1 W");
David Redondo's avatar
David Redondo committed
78
    const static KLocalizedString Wh = ki18nc("Watt-hours unit symbol", "%1 Wh");
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    const static KLocalizedString rate = ki18nc("Rate unit symbol", "%1 s⁻¹");
    const static KLocalizedString unitless = ki18nc("Unitless", "%1");

    switch (unit) {
    case UnitByte:
        return B;
    case UnitKiloByte:
        return KiB;
    case UnitMegaByte:
        return MiB;
    case UnitGigaByte:
        return GiB;
    case UnitTeraByte:
        return TiB;
    case UnitPetaByte:
        return PiB;

    case UnitByteRate:
        return bps;
    case UnitKiloByteRate:
        return Kbps;
    case UnitMegaByteRate:
        return Mbps;
    case UnitGigaByteRate:
        return Gbps;
    case UnitTeraByteRate:
        return Tbps;
    case UnitPetaByteRate:
        return Pbps;

    case UnitHertz:
        return Hz;
    case UnitKiloHertz:
        return kHz;
    case UnitMegaHertz:
        return MHz;
    case UnitGigaHertz:
        return GHz;
    case UnitTeraHertz:
        return THz;
    case UnitPetaHertz:
        return PHz;

    case UnitCelsius:
        return C;
    case UnitDecibelMilliWatts:
        return dBm;
    case UnitPercent:
        return percent;
    case UnitRate:
        return rate;
    case UnitRpm:
        return RPM;
    case UnitSecond:
        return s;
    case UnitVolt:
        return V;
    case UnitWatt:
        return W;
David Redondo's avatar
David Redondo committed
138 139
    case UnitWattHour:
        return Wh;
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

    default:
        return unitless;
    }
}

static int unitOrder(Unit unit)
{
    switch (unit) {
    case UnitByte:
    case UnitKiloByte:
    case UnitMegaByte:
    case UnitGigaByte:
    case UnitTeraByte:
    case UnitPetaByte:
    case UnitByteRate:
    case UnitKiloByteRate:
    case UnitMegaByteRate:
    case UnitGigaByteRate:
    case UnitTeraByteRate:
    case UnitPetaByteRate:
        return 1024;

    case UnitHertz:
    case UnitKiloHertz:
    case UnitMegaHertz:
    case UnitGigaHertz:
    case UnitTeraHertz:
    case UnitPetaHertz:
David Redondo's avatar
David Redondo committed
169 170
    case UnitWatt:
    case UnitWattHour:
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        return 1000;

    default:
        return 0;
    }
}

static Unit unitBase(Unit unit)
{
    switch (unit) {
    case UnitByte:
    case UnitKiloByte:
    case UnitMegaByte:
    case UnitGigaByte:
    case UnitTeraByte:
    case UnitPetaByte:
        return UnitByte;

    case UnitByteRate:
    case UnitKiloByteRate:
    case UnitMegaByteRate:
    case UnitGigaByteRate:
    case UnitTeraByteRate:
    case UnitPetaByteRate:
        return UnitByteRate;

    case UnitHertz:
    case UnitKiloHertz:
    case UnitMegaHertz:
    case UnitGigaHertz:
    case UnitTeraHertz:
    case UnitPetaHertz:
        return UnitHertz;

    default:
        return unit;
    }
}

static Unit adjustedUnit(qreal value, Unit unit, MetricPrefix prefix)
{
    const int order = unitOrder(unit);
    if (!order) {
        return unit;
    }

    const Unit baseUnit = unitBase(unit);
    const MetricPrefix basePrefix = MetricPrefix(unit - baseUnit);

    if (prefix == MetricPrefixAutoAdjust) {
        const qreal absoluteValue = value * std::pow(order, int(basePrefix));
        if (absoluteValue > 0) {
            const int targetPrefix = std::log2(absoluteValue) / std::log2(order);
            if (targetPrefix <= MetricPrefixLast) {
                prefix = MetricPrefix(targetPrefix);
            }
        }
        if (prefix == MetricPrefixAutoAdjust) {
            prefix = basePrefix;
        }
    }

    return Unit(prefix + baseUnit);
}

static QString formatNumber(const QVariant &value, Unit unit, MetricPrefix prefix, FormatOptions options)
{
    qreal amount = value.toDouble();

    if (!options.testFlag(FormatOptionShowNull) && qFuzzyIsNull(amount)) {
        return QString();
    }

    const Unit adjusted = adjustedUnit(amount, unit, prefix);
    if (adjusted != unit) {
        amount /= std::pow(unitOrder(unit), adjusted - unit);
    }

    const int precision = (value.type() != QVariant::Double && adjusted <= unit) ? 0 : 1;
    const QString text = QLocale().toString(amount, 'f', precision);

    return unitFormat(adjusted).subs(text).toString();
}

static QString formatTime(const QVariant &value)
{
    const qlonglong seconds = value.toLongLong();

    const QString minutesString = QString::number(seconds / 60);
    const QString secondsScring = QStringLiteral("%1").arg(seconds % 60, 2, 10, QLatin1Char('0'));

    return minutesString + QLatin1Char(':') + secondsScring;
}

qreal Formatter::scaleDownFactor(const QVariant &value, Unit unit, MetricPrefix targetPrefix)
{
    const Unit adjusted = adjustedUnit(value.toDouble(), unit, targetPrefix);
    if (adjusted == unit) {
        return 1;
    }

    return std::pow(unitOrder(unit), adjusted - unit);
}

KLocalizedString Formatter::localizedString(const QVariant &value, Unit unit, MetricPrefix targetPrefix)
{
    const Unit adjusted = adjustedUnit(value.toDouble(), unit, targetPrefix);
    return unitFormat(adjusted);
}

QString Formatter::formatValue(const QVariant &value, Unit unit, MetricPrefix targetPrefix, FormatOptions options)
{
    switch (unit) {
    case UnitByte:
    case UnitKiloByte:
    case UnitMegaByte:
    case UnitGigaByte:
    case UnitTeraByte:
    case UnitPetaByte:
    case UnitByteRate:
    case UnitKiloByteRate:
    case UnitMegaByteRate:
    case UnitGigaByteRate:
    case UnitTeraByteRate:
    case UnitPetaByteRate:
    case UnitHertz:
    case UnitKiloHertz:
    case UnitMegaHertz:
    case UnitGigaHertz:
    case UnitTeraHertz:
    case UnitPetaHertz:
    case UnitPercent:
    case UnitRate:
    case UnitRpm:
    case UnitCelsius:
    case UnitDecibelMilliWatts:
    case UnitVolt:
    case UnitWatt:
    case UnitSecond:
        return formatNumber(value, unit, targetPrefix, options);

    case UnitBootTimestamp:
David Redondo's avatar
David Redondo committed
313 314
        qCWarning(FORMATTER) << "UnitBootTimestamp is deprecated and is not formatted anymore";
        return value.toString();
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    case UnitTime:
        return formatTime(value);

    default:
        return value.toString();
    }
}

QString Formatter::symbol(Unit unit)
{
    // TODO: Is it possible to avoid duplication of these symbols?
    switch (unit) {
    case UnitByte:
        return i18nc("Bytes unit symbol", "B");
    case UnitKiloByte:
        return i18nc("Kilobytes unit symbol", "KiB");
    case UnitMegaByte:
        return i18nc("Megabytes unit symbol", "MiB");
    case UnitGigaByte:
        return i18nc("Gigabytes unit symbol", "GiB");
    case UnitTeraByte:
        return i18nc("Terabytes unit symbol", "TiB");
    case UnitPetaByte:
        return i18nc("Petabytes unit symbol", "PiB");

    case UnitByteRate:
        return i18nc("Bytes per second unit symbol", "B/s");
    case UnitKiloByteRate:
        return i18nc("Kilobytes per second unit symbol", "KiB/s");
    case UnitMegaByteRate:
        return i18nc("Megabytes per second unit symbol", "MiB/s");
    case UnitGigaByteRate:
        return i18nc("Gigabytes per second unit symbol", "GiB/s");
    case UnitTeraByteRate:
        return i18nc("Gigabytes per second unit symbol", "TiB/s");
    case UnitPetaByteRate:
        return i18nc("Gigabytes per second unit symbol", "PiB/s");

    case UnitHertz:
        return i18nc("Hertz unit symbol", "Hz");
    case UnitKiloHertz:
        return i18nc("Kilohertz unit symbol", "kHz");
    case UnitMegaHertz:
        return i18nc("Megahertz unit symbol", "MHz");
    case UnitGigaHertz:
        return i18nc("Gigahertz unit symbol", "GHz");
    case UnitTeraHertz:
        return i18nc("Terahertz unit symbol", "THz");
    case UnitPetaHertz:
        return i18nc("Petahertz unit symbol", "PHz");

    case UnitPercent:
        return i18nc("Percent unit", "%");
    case UnitRpm:
        return i18nc("Revolutions per minute unit symbol", "RPM");
    case UnitCelsius:
        return i18nc("Celsius unit symbol", "°C");
    case UnitDecibelMilliWatts:
        return i18nc("Decibels unit symbol", "dBm");
    case UnitSecond:
        return i18nc("Seconds unit symbol", "s");
    case UnitVolt:
        return i18nc("Volts unit symbol", "V");
    case UnitWatt:
        return i18nc("Watts unit symbol", "W");
David Redondo's avatar
David Redondo committed
380 381
    case UnitWattHour:
        return i18nc("Watt-hours unit symbol", "Wh");
382 383 384 385 386 387 388 389 390
    case UnitRate:
        return i18nc("Rate unit symbol", "s⁻¹");

    default:
        return QString();
    }
}

} // namespace KSysGuard