Changeset View
Changeset View
Standalone View
Standalone View
processcore/formatter.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com> | ||||
3 | | ||||
4 | formatBootTimestamp is based on TimeUtil class: | ||||
5 | Copyright (C) 2014 Gregor Mi <codestruct@posteo.org> | ||||
6 | | ||||
7 | This library is free software; you can redistribute it and/or | ||||
8 | modify it under the terms of the GNU Library General Public | ||||
9 | License as published by the Free Software Foundation; either | ||||
10 | version 2 of the License, or (at your option) any later version. | ||||
11 | | ||||
12 | This library is distributed in the hope that it will be useful, | ||||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
15 | Library General Public License for more details. | ||||
16 | | ||||
17 | You should have received a copy of the GNU Library General Public License | ||||
18 | along with this library; see the file COPYING.LIB. If not, write to | ||||
19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
20 | Boston, MA 02110-1301, USA. | ||||
21 | */ | ||||
22 | | ||||
23 | #include "formatter.h" | ||||
24 | | ||||
25 | #include <KLocalizedString> | ||||
26 | | ||||
27 | #include <QLocale> | ||||
28 | #include <QTime> | ||||
29 | | ||||
30 | #include <cmath> | ||||
31 | | ||||
32 | #ifdef Q_OS_OSX | ||||
33 | #include <mach/clock.h> | ||||
34 | #include <mach/mach.h> | ||||
35 | #else | ||||
36 | #include <ctime> | ||||
37 | #endif | ||||
38 | | ||||
39 | #include <unistd.h> | ||||
40 | | ||||
41 | namespace KSysGuard | ||||
42 | { | ||||
43 | | ||||
44 | // TODO: Is there a bit nicer way to handle formatting? | ||||
45 | | ||||
46 | static KLocalizedString unitFormat(Unit unit) | ||||
47 | { | ||||
48 | const static KLocalizedString B = ki18nc("Bytes unit symbol", "%1 B"); | ||||
49 | const static KLocalizedString KiB = ki18nc("Kilobytes unit symbol", "%1 KiB"); | ||||
50 | const static KLocalizedString MiB = ki18nc("Megabytes unit symbol", "%1 MiB"); | ||||
51 | const static KLocalizedString GiB = ki18nc("Gigabytes unit symbol", "%1 GiB"); | ||||
52 | const static KLocalizedString TiB = ki18nc("Terabytes unit symbol", "%1 TiB"); | ||||
53 | const static KLocalizedString PiB = ki18nc("Petabytes unit symbol", "%1 PiB"); | ||||
54 | | ||||
55 | const static KLocalizedString bps = ki18nc("Bytes per second unit symbol", "%1 B/s"); | ||||
56 | const static KLocalizedString Kbps = ki18nc("Kilobytes per second unit symbol", "%1 KiB/s"); | ||||
57 | const static KLocalizedString Mbps = ki18nc("Megabytes per second unit symbol", "%1 MiB/s"); | ||||
58 | const static KLocalizedString Gbps = ki18nc("Gigabytes per second unit symbol", "%1 GiB/s"); | ||||
59 | const static KLocalizedString Tbps = ki18nc("Gigabytes per second unit symbol", "%1 TiB/s"); | ||||
60 | const static KLocalizedString Pbps = ki18nc("Gigabytes per second unit symbol", "%1 PiB/s"); | ||||
61 | | ||||
62 | const static KLocalizedString Hz = ki18nc("Hertz unit symbol", "%1 Hz"); | ||||
63 | const static KLocalizedString kHz = ki18nc("Kilohertz unit symbol", "%1 kHz"); | ||||
64 | const static KLocalizedString MHz = ki18nc("Megahertz unit symbol", "%1 MHz"); | ||||
65 | const static KLocalizedString GHz = ki18nc("Gigahertz unit symbol", "%1 GHz"); | ||||
66 | const static KLocalizedString THz = ki18nc("Terahertz unit symbol", "%1 THz"); | ||||
67 | const static KLocalizedString PHz = ki18nc("Petahertz unit symbol", "%1 PHz"); | ||||
68 | | ||||
69 | const static KLocalizedString percent = ki18nc("Percent unit", "%1%"); | ||||
70 | const static KLocalizedString RPM = ki18nc("Revolutions per minute unit symbol", "%1 RPM"); | ||||
71 | const static KLocalizedString C = ki18nc("Celsius unit symbol", "%1°C"); | ||||
72 | const static KLocalizedString dBm = ki18nc("Decibels unit symbol", "%1 dBm"); | ||||
73 | const static KLocalizedString s = ki18nc("Seconds unit symbol", "%1s"); | ||||
74 | const static KLocalizedString V = ki18nc("Volts unit symbol", "%1 V"); | ||||
75 | const static KLocalizedString W = ki18nc("Watts unit symbol", "%1 W"); | ||||
76 | const static KLocalizedString rate = ki18nc("Rate unit symbol", "%1 s⁻¹"); | ||||
77 | const static KLocalizedString unitless = ki18nc("Unitless", "%1"); | ||||
78 | | ||||
79 | switch (unit) { | ||||
80 | case UnitByte: | ||||
81 | return B; | ||||
82 | case UnitKiloByte: | ||||
83 | return KiB; | ||||
84 | case UnitMegaByte: | ||||
85 | return MiB; | ||||
86 | case UnitGigaByte: | ||||
87 | return GiB; | ||||
88 | case UnitTeraByte: | ||||
89 | return TiB; | ||||
90 | case UnitPetaByte: | ||||
91 | return PiB; | ||||
92 | | ||||
93 | case UnitByteRate: | ||||
94 | return bps; | ||||
95 | case UnitKiloByteRate: | ||||
96 | return Kbps; | ||||
97 | case UnitMegaByteRate: | ||||
98 | return Mbps; | ||||
99 | case UnitGigaByteRate: | ||||
100 | return Gbps; | ||||
101 | case UnitTeraByteRate: | ||||
102 | return Tbps; | ||||
103 | case UnitPetaByteRate: | ||||
104 | return Pbps; | ||||
105 | | ||||
106 | case UnitHertz: | ||||
107 | return Hz; | ||||
108 | case UnitKiloHertz: | ||||
109 | return kHz; | ||||
110 | case UnitMegaHertz: | ||||
111 | return MHz; | ||||
112 | case UnitGigaHertz: | ||||
113 | return GHz; | ||||
114 | case UnitTeraHertz: | ||||
115 | return THz; | ||||
116 | case UnitPetaHertz: | ||||
117 | return PHz; | ||||
118 | | ||||
119 | case UnitCelsius: | ||||
120 | return C; | ||||
121 | case UnitDecibelMilliWatts: | ||||
122 | return dBm; | ||||
123 | case UnitPercent: | ||||
124 | return percent; | ||||
125 | case UnitRate: | ||||
126 | return rate; | ||||
127 | case UnitRpm: | ||||
128 | return RPM; | ||||
129 | case UnitSecond: | ||||
130 | return s; | ||||
131 | case UnitVolt: | ||||
132 | return V; | ||||
133 | case UnitWatt: | ||||
134 | return W; | ||||
135 | | ||||
136 | default: | ||||
137 | return unitless; | ||||
138 | } | ||||
139 | } | ||||
140 | | ||||
141 | static int unitOrder(Unit unit) | ||||
142 | { | ||||
143 | switch (unit) { | ||||
144 | case UnitByte: | ||||
145 | case UnitKiloByte: | ||||
146 | case UnitMegaByte: | ||||
147 | case UnitGigaByte: | ||||
148 | case UnitTeraByte: | ||||
149 | case UnitPetaByte: | ||||
150 | case UnitByteRate: | ||||
151 | case UnitKiloByteRate: | ||||
152 | case UnitMegaByteRate: | ||||
153 | case UnitGigaByteRate: | ||||
154 | case UnitTeraByteRate: | ||||
155 | case UnitPetaByteRate: | ||||
156 | return 1024; | ||||
157 | | ||||
158 | case UnitHertz: | ||||
159 | case UnitKiloHertz: | ||||
160 | case UnitMegaHertz: | ||||
161 | case UnitGigaHertz: | ||||
162 | case UnitTeraHertz: | ||||
163 | case UnitPetaHertz: | ||||
164 | return 1000; | ||||
165 | | ||||
166 | default: | ||||
167 | return 0; | ||||
168 | } | ||||
169 | } | ||||
170 | | ||||
171 | static Unit unitBase(Unit unit) | ||||
172 | { | ||||
173 | switch (unit) { | ||||
174 | case UnitByte: | ||||
175 | case UnitKiloByte: | ||||
176 | case UnitMegaByte: | ||||
177 | case UnitGigaByte: | ||||
178 | case UnitTeraByte: | ||||
179 | case UnitPetaByte: | ||||
180 | return UnitByte; | ||||
181 | | ||||
182 | case UnitByteRate: | ||||
183 | case UnitKiloByteRate: | ||||
184 | case UnitMegaByteRate: | ||||
185 | case UnitGigaByteRate: | ||||
186 | case UnitTeraByteRate: | ||||
187 | case UnitPetaByteRate: | ||||
188 | return UnitByteRate; | ||||
189 | | ||||
190 | case UnitHertz: | ||||
191 | case UnitKiloHertz: | ||||
192 | case UnitMegaHertz: | ||||
193 | case UnitGigaHertz: | ||||
194 | case UnitTeraHertz: | ||||
195 | case UnitPetaHertz: | ||||
196 | return UnitHertz; | ||||
197 | | ||||
198 | default: | ||||
199 | return unit; | ||||
200 | } | ||||
201 | } | ||||
202 | | ||||
203 | static Unit adjustedUnit(qreal value, Unit unit, MetricPrefix prefix) | ||||
204 | { | ||||
205 | const int order = unitOrder(unit); | ||||
206 | if (!order) { | ||||
207 | return unit; | ||||
208 | } | ||||
209 | | ||||
210 | const Unit baseUnit = unitBase(unit); | ||||
211 | const MetricPrefix basePrefix = MetricPrefix(unit - baseUnit); | ||||
212 | | ||||
213 | if (prefix == MetricPrefixAutoAdjust) { | ||||
214 | const qreal absoluteValue = value * std::pow(order, int(basePrefix)); | ||||
215 | if (absoluteValue > 0) { | ||||
216 | const int targetPrefix = std::log2(absoluteValue) / std::log2(order); | ||||
217 | if (targetPrefix <= MetricPrefixLast) { | ||||
218 | prefix = MetricPrefix(targetPrefix); | ||||
219 | } | ||||
220 | } | ||||
221 | if (prefix == MetricPrefixAutoAdjust) { | ||||
222 | prefix = basePrefix; | ||||
223 | } | ||||
224 | } | ||||
225 | | ||||
226 | return Unit(prefix + baseUnit); | ||||
227 | } | ||||
228 | | ||||
229 | static QString formatNumber(const QVariant &value, Unit unit, MetricPrefix prefix, FormatOptions options) | ||||
230 | { | ||||
231 | qreal amount = value.toDouble(); | ||||
232 | | ||||
233 | if (!options.testFlag(FormatOptionShowNull) && qFuzzyIsNull(amount)) { | ||||
234 | return QString(); | ||||
235 | } | ||||
236 | | ||||
237 | const Unit adjusted = adjustedUnit(amount, unit, prefix); | ||||
238 | if (adjusted != unit) { | ||||
239 | amount /= std::pow(unitOrder(unit), adjusted - unit); | ||||
240 | } | ||||
241 | | ||||
242 | const int precision = (value.type() != QVariant::Double && adjusted <= unit) ? 0 : 1; | ||||
243 | const QString text = QLocale().toString(amount, 'f', precision); | ||||
244 | | ||||
245 | return unitFormat(adjusted).subs(text).toString(); | ||||
246 | } | ||||
247 | | ||||
248 | static QString formatTime(const QVariant &value) | ||||
249 | { | ||||
250 | const qlonglong seconds = value.toLongLong(); | ||||
251 | | ||||
252 | const QString minutesString = QString::number(seconds / 60); | ||||
253 | const QString secondsScring = QStringLiteral("%1").arg(seconds % 60, 2, 10, QLatin1Char('0')); | ||||
254 | | ||||
255 | return minutesString + QLatin1Char(':') + secondsScring; | ||||
256 | } | ||||
257 | | ||||
258 | static QString formatBootTimestamp(const QVariant &value, FormatOptions options) | ||||
259 | { | ||||
260 | const qlonglong clockTicksSinceSystemBoot = value.toLongLong(); | ||||
261 | const QDateTime now = QDateTime::currentDateTime(); | ||||
262 | | ||||
263 | #ifdef Q_OS_OSX | ||||
264 | clock_serv_t cclock; | ||||
265 | mach_timespec_t tp; | ||||
266 | | ||||
267 | host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); | ||||
268 | clock_get_time(cclock, &tp); | ||||
269 | mach_port_deallocate(mach_task_self(), cclock); | ||||
270 | #else | ||||
271 | timespec tp; | ||||
272 | | ||||
273 | clock_gettime(CLOCK_MONOTONIC, &tp); | ||||
274 | #endif | ||||
275 | const QDateTime systemBootTime = now.addSecs(-tp.tv_sec); | ||||
276 | | ||||
277 | const long clockTicksPerSecond = sysconf(_SC_CLK_TCK); | ||||
278 | const qreal secondsSinceSystemBoot = qreal(clockTicksSinceSystemBoot) / clockTicksPerSecond; | ||||
279 | const QDateTime absoluteStartTime = systemBootTime.addSecs(secondsSinceSystemBoot); | ||||
280 | | ||||
281 | if (!options.testFlag(FormatOptionAgo)) { | ||||
282 | return QLocale().toString(absoluteStartTime); | ||||
283 | } | ||||
284 | | ||||
285 | const qint64 totalSeconds = absoluteStartTime.secsTo(now); | ||||
286 | const qint64 totalMinutes = totalSeconds / 60.0; | ||||
287 | const qint64 totalHours = totalSeconds / 60.0 / 60.0; | ||||
288 | const qint64 totalDays = totalSeconds / 60.0 / 60.0 / 24.0; | ||||
289 | | ||||
290 | if (!totalMinutes) { | ||||
291 | return i18nc("contains a abbreviated time unit: (s)econds", "%1s ago", totalSeconds); | ||||
292 | } | ||||
293 | | ||||
294 | if (!totalHours) { | ||||
295 | const int seconds = totalSeconds - totalMinutes * 60; | ||||
296 | return i18nc("contains abbreviated time units: (m)inutes and (s)econds", "%1m %2s ago", | ||||
297 | totalMinutes, seconds); | ||||
298 | } | ||||
299 | | ||||
300 | if (!totalDays) { | ||||
301 | const int seconds = totalSeconds - totalMinutes * 60; | ||||
302 | const int minutes = totalMinutes - totalHours * 60; | ||||
303 | return i18nc("contains abbreviated time units: (h)ours, (m)inutes and (s)econds)", | ||||
304 | "%1h %2m %3s ago", totalHours, minutes, seconds); | ||||
305 | } | ||||
306 | | ||||
307 | const int minutes = totalMinutes - totalHours * 60; | ||||
308 | const int hours = totalHours - totalDays * 24; | ||||
309 | return i18ncp("contains also abbreviated time units: (h)ours and (m)inutes", | ||||
310 | "%1 day %2h %3m ago", "%1 days %2h %3m ago", totalDays, hours, minutes); | ||||
311 | } | ||||
312 | | ||||
313 | qreal Formatter::scaleDownFactor(const QVariant &value, Unit unit, MetricPrefix targetPrefix) | ||||
314 | { | ||||
315 | const Unit adjusted = adjustedUnit(value.toDouble(), unit, targetPrefix); | ||||
316 | if (adjusted == unit) { | ||||
317 | return 1; | ||||
318 | } | ||||
319 | | ||||
320 | return std::pow(unitOrder(unit), adjusted - unit); | ||||
321 | } | ||||
322 | | ||||
323 | KLocalizedString Formatter::localizedString(const QVariant &value, Unit unit, MetricPrefix targetPrefix) | ||||
324 | { | ||||
325 | const Unit adjusted = adjustedUnit(value.toDouble(), unit, targetPrefix); | ||||
326 | return unitFormat(adjusted); | ||||
327 | } | ||||
328 | | ||||
329 | QString Formatter::formatValue(const QVariant &value, Unit unit, MetricPrefix targetPrefix, FormatOptions options) | ||||
330 | { | ||||
331 | switch (unit) { | ||||
332 | case UnitByte: | ||||
333 | case UnitKiloByte: | ||||
334 | case UnitMegaByte: | ||||
335 | case UnitGigaByte: | ||||
336 | case UnitTeraByte: | ||||
337 | case UnitPetaByte: | ||||
338 | case UnitByteRate: | ||||
339 | case UnitKiloByteRate: | ||||
340 | case UnitMegaByteRate: | ||||
341 | case UnitGigaByteRate: | ||||
342 | case UnitTeraByteRate: | ||||
343 | case UnitPetaByteRate: | ||||
344 | case UnitHertz: | ||||
345 | case UnitKiloHertz: | ||||
346 | case UnitMegaHertz: | ||||
347 | case UnitGigaHertz: | ||||
348 | case UnitTeraHertz: | ||||
349 | case UnitPetaHertz: | ||||
350 | case UnitPercent: | ||||
351 | case UnitRate: | ||||
352 | case UnitRpm: | ||||
353 | case UnitCelsius: | ||||
354 | case UnitDecibelMilliWatts: | ||||
355 | case UnitVolt: | ||||
356 | case UnitWatt: | ||||
357 | case UnitSecond: | ||||
358 | return formatNumber(value, unit, targetPrefix, options); | ||||
359 | | ||||
360 | case UnitBootTimestamp: | ||||
361 | return formatBootTimestamp(value, options); | ||||
362 | case UnitTime: | ||||
363 | return formatTime(value); | ||||
364 | | ||||
365 | default: | ||||
366 | return value.toString(); | ||||
367 | } | ||||
368 | } | ||||
369 | | ||||
370 | QString Formatter::symbol(Unit unit) | ||||
371 | { | ||||
372 | // TODO: Is it possible to avoid duplication of these symbols? | ||||
373 | switch (unit) { | ||||
374 | case UnitByte: | ||||
375 | return i18nc("Bytes unit symbol", "B"); | ||||
376 | case UnitKiloByte: | ||||
377 | return i18nc("Kilobytes unit symbol", "KiB"); | ||||
378 | case UnitMegaByte: | ||||
379 | return i18nc("Megabytes unit symbol", "MiB"); | ||||
380 | case UnitGigaByte: | ||||
381 | return i18nc("Gigabytes unit symbol", "GiB"); | ||||
382 | case UnitTeraByte: | ||||
383 | return i18nc("Terabytes unit symbol", "TiB"); | ||||
384 | case UnitPetaByte: | ||||
385 | return i18nc("Petabytes unit symbol", "PiB"); | ||||
386 | | ||||
387 | case UnitByteRate: | ||||
388 | return i18nc("Bytes per second unit symbol", "B/s"); | ||||
389 | case UnitKiloByteRate: | ||||
390 | return i18nc("Kilobytes per second unit symbol", "KiB/s"); | ||||
391 | case UnitMegaByteRate: | ||||
392 | return i18nc("Megabytes per second unit symbol", "MiB/s"); | ||||
393 | case UnitGigaByteRate: | ||||
394 | return i18nc("Gigabytes per second unit symbol", "GiB/s"); | ||||
395 | case UnitTeraByteRate: | ||||
396 | return i18nc("Gigabytes per second unit symbol", "TiB/s"); | ||||
397 | case UnitPetaByteRate: | ||||
398 | return i18nc("Gigabytes per second unit symbol", "PiB/s"); | ||||
399 | | ||||
400 | case UnitHertz: | ||||
401 | return i18nc("Hertz unit symbol", "Hz"); | ||||
402 | case UnitKiloHertz: | ||||
403 | return i18nc("Kilohertz unit symbol", "kHz"); | ||||
404 | case UnitMegaHertz: | ||||
405 | return i18nc("Megahertz unit symbol", "MHz"); | ||||
406 | case UnitGigaHertz: | ||||
407 | return i18nc("Gigahertz unit symbol", "GHz"); | ||||
408 | case UnitTeraHertz: | ||||
409 | return i18nc("Terahertz unit symbol", "THz"); | ||||
410 | case UnitPetaHertz: | ||||
411 | return i18nc("Petahertz unit symbol", "PHz"); | ||||
412 | | ||||
413 | case UnitPercent: | ||||
414 | return i18nc("Percent unit", "%"); | ||||
415 | case UnitRpm: | ||||
416 | return i18nc("Revolutions per minute unit symbol", "RPM"); | ||||
417 | case UnitCelsius: | ||||
418 | return i18nc("Celsius unit symbol", "°C"); | ||||
419 | case UnitDecibelMilliWatts: | ||||
420 | return i18nc("Decibels unit symbol", "dBm"); | ||||
421 | case UnitSecond: | ||||
422 | return i18nc("Seconds unit symbol", "s"); | ||||
423 | case UnitVolt: | ||||
424 | return i18nc("Volts unit symbol", "V"); | ||||
425 | case UnitWatt: | ||||
426 | return i18nc("Watts unit symbol", "W"); | ||||
427 | case UnitRate: | ||||
428 | return i18nc("Rate unit symbol", "s⁻¹"); | ||||
429 | | ||||
430 | default: | ||||
431 | return QString(); | ||||
432 | } | ||||
433 | } | ||||
434 | | ||||
435 | } // namespace KSysGuard |