app/TimeHistogram.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "TimeHistogram.h" | ||
| 2 | |||
| 3 | #include <QRegularExpression> | ||
| 4 | #include <QTime> | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | |||
| 8 | namespace TimeHistogram { | ||
| 9 | |||
| 10 | namespace { | ||
| 11 | |||
| 12 | 130946 | QDate weekStartOf(const QDate &date, Qt::DayOfWeek weekStart) { | |
| 13 | 130946 | return date.addDays(-((date.dayOfWeek() - int(weekStart) + 7) % 7)); | |
| 14 | } | ||
| 15 | |||
| 16 |
3/3✓ Branch 2 → 3 taken 88496 times.
✓ Branch 3 → 4 taken 88496 times.
✓ Branch 4 → 5 taken 88496 times.
|
88496 | QDate monthStart(const QDate &date) { return QDate(date.year(), date.month(), 1); } |
| 17 |
3/3✓ Branch 2 → 3 taken 38755 times.
✓ Branch 3 → 4 taken 38755 times.
✓ Branch 4 → 5 taken 38755 times.
|
38755 | QDate monthEnd(const QDate &date) { return monthStart(date).addMonths(1).addDays(-1); } |
| 18 | |||
| 19 | 23237 | bool wholeYear(const Range &range) { | |
| 20 |
8/8✓ Branch 2 → 3 taken 23237 times.
✓ Branch 3 → 4 taken 23237 times.
✓ Branch 5 → 6 taken 2383 times.
✓ Branch 5 → 11 taken 20854 times.
✓ Branch 6 → 7 taken 2383 times.
✓ Branch 7 → 8 taken 2383 times.
✓ Branch 9 → 10 taken 672 times.
✓ Branch 9 → 11 taken 1711 times.
|
23237 | return range.first == QDate(range.first.year(), 1, 1) && range.last == QDate(range.first.year(), 12, 31); |
| 21 | } | ||
| 22 | 22967 | bool wholeMonth(const Range &range) { | |
| 23 |
6/6✓ Branch 2 → 3 taken 22967 times.
✓ Branch 4 → 5 taken 11981 times.
✓ Branch 4 → 9 taken 10986 times.
✓ Branch 5 → 6 taken 11981 times.
✓ Branch 7 → 8 taken 10970 times.
✓ Branch 7 → 9 taken 1011 times.
|
22967 | return range.first == monthStart(range.first) && range.last == monthEnd(range.first); |
| 24 | } | ||
| 25 | 1417 | bool sameMonth(const Range &range) { | |
| 26 |
4/4✓ Branch 4 → 5 taken 947 times.
✓ Branch 4 → 9 taken 470 times.
✓ Branch 7 → 8 taken 695 times.
✓ Branch 7 → 9 taken 252 times.
|
1417 | return range.first.year() == range.last.year() && range.first.month() == range.last.month(); |
| 27 | } | ||
| 28 | |||
| 29 | 542531 | Range clip(const Range &range, const Range &bounds) { | |
| 30 | 542531 | return {std::max(range.first, bounds.first), std::min(range.last, bounds.last)}; | |
| 31 | } | ||
| 32 | |||
| 33 | // The locale's long date format without weekday and year ("d MMMM", | ||
| 34 | // "d. MMMM", "MMMM d"): day and month in the locale's order and words. | ||
| 35 | 25294 | QString dayMonthPattern(const QLocale &locale) { | |
| 36 |
1/1✓ Branch 2 → 3 taken 25294 times.
|
25294 | QString pattern = locale.dateFormat(QLocale::LongFormat); |
| 37 |
2/2✓ Branch 7 → 8 taken 25294 times.
✓ Branch 8 → 9 taken 25294 times.
|
25294 | pattern.remove(QRegularExpression(QStringLiteral("d{3,4}[,.]?\\s*"))); |
| 38 |
2/2✓ Branch 16 → 17 taken 25294 times.
✓ Branch 17 → 18 taken 25294 times.
|
25294 | pattern.remove(QRegularExpression(QStringLiteral("[,\\s]*y+[.]?"))); |
| 39 |
2/2✓ Branch 25 → 26 taken 25294 times.
✓ Branch 26 → 27 taken 25294 times.
|
25294 | pattern.remove(QRegularExpression(QStringLiteral("\\s*'[^']*'\\s*$"))); |
| 40 |
1/1✓ Branch 30 → 31 taken 25294 times.
|
25294 | pattern = pattern.trimmed(); |
| 41 |
2/4✓ Branch 35 → 36 taken 25294 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 25294 times.
✗ Branch 37 → 38 not taken.
|
25294 | if (pattern.endsWith(QLatin1Char(','))) pattern.chop(1); |
| 42 |
5/10✓ Branch 40 → 41 taken 25294 times.
✓ Branch 41 → 42 taken 25294 times.
✗ Branch 41 → 47 not taken.
✓ Branch 44 → 45 taken 25294 times.
✓ Branch 45 → 46 taken 25294 times.
✗ Branch 45 → 47 not taken.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 25294 times.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 91 not taken.
|
75882 | return pattern.contains(QLatin1Char('M')) && pattern.contains(QLatin1Char('d')) ? pattern : QStringLiteral("d MMMM"); |
| 43 | 25294 | } | |
| 44 | |||
| 45 | 12126 | bool monthFirst(const QLocale &locale) { | |
| 46 |
1/1✓ Branch 2 → 3 taken 12126 times.
|
12126 | const QString pattern = dayMonthPattern(locale); |
| 47 | 24252 | return pattern.indexOf(QLatin1Char('M')) < pattern.indexOf(QLatin1Char('d')); | |
| 48 | 12126 | } | |
| 49 | |||
| 50 | // The day-number part of the day-month pattern ("d" or "d."). | ||
| 51 | 1574 | QString dayPattern(const QLocale &locale) { | |
| 52 |
1/1✓ Branch 2 → 3 taken 1574 times.
|
1574 | const QString pattern = dayMonthPattern(locale); |
| 53 |
7/7✓ Branch 6 → 7 taken 1574 times.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 11 taken 1572 times.
✓ Branch 15 → 16 taken 1572 times.
✓ Branch 15 → 17 taken 2 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 1572 times.
|
4722 | return pattern.contains(QStringLiteral("d.")) ? QStringLiteral("d.") : QStringLiteral("d"); |
| 54 | 1574 | } | |
| 55 | |||
| 56 | 11997 | QString withYear(const QString &text, int year, const QLocale &locale) { | |
| 57 |
10/14✓ Branch 2 → 3 taken 11997 times.
✓ Branch 3 → 4 taken 11997 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 8 taken 11995 times.
✓ Branch 11 → 12 taken 11997 times.
✓ Branch 12 → 13 taken 11997 times.
✓ Branch 16 → 17 taken 11995 times.
✓ Branch 16 → 18 taken 2 times.
✓ Branch 18 → 19 taken 2 times.
✓ Branch 18 → 20 taken 11995 times.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
|
23994 | return text + (monthFirst(locale) ? QStringLiteral(", ") : QStringLiteral(" ")) + QString::number(year); |
| 58 | } | ||
| 59 | |||
| 60 | 10808 | QString monthLabel(const QDate &date, const QLocale &locale, bool year) { | |
| 61 |
2/2✓ Branch 2 → 3 taken 10808 times.
✓ Branch 3 → 4 taken 10808 times.
|
10808 | const QString name = locale.standaloneMonthName(date.month(), QLocale::LongFormat); |
| 62 |
10/14✓ Branch 4 → 5 taken 10660 times.
✓ Branch 4 → 11 taken 148 times.
✓ Branch 5 → 6 taken 10660 times.
✓ Branch 6 → 7 taken 10660 times.
✓ Branch 9 → 10 taken 10660 times.
✓ Branch 10 → 12 taken 10660 times.
✓ Branch 13 → 14 taken 10660 times.
✓ Branch 13 → 15 taken 148 times.
✓ Branch 15 → 16 taken 10660 times.
✓ Branch 15 → 17 taken 148 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
|
32424 | return year ? name + QLatin1Char(' ') + QString::number(date.year()) : name; |
| 63 | 10808 | } | |
| 64 | |||
| 65 | // "10–16 March" (day-first locales) or "March 10–16"; across months | ||
| 66 | // "28 Feb – 6 Mar". The year is appended when asked for. | ||
| 67 | 588 | QString spanLabel(const Range &range, const QLocale &locale, bool year) { | |
| 68 |
1/1✓ Branch 2 → 3 taken 588 times.
|
588 | const QString pattern = dayMonthPattern(locale); |
| 69 | 588 | QString text; | |
| 70 |
3/3✓ Branch 3 → 4 taken 588 times.
✓ Branch 4 → 5 taken 129 times.
✓ Branch 4 → 54 taken 459 times.
|
588 | if (sameMonth(range)) { |
| 71 |
1/1✓ Branch 5 → 6 taken 129 times.
|
129 | text = monthFirst(locale) |
| 72 |
19/33✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 17 taken 127 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 14 → 15 taken 2 times.
✓ Branch 15 → 16 taken 2 times.
✓ Branch 16 → 25 taken 2 times.
✓ Branch 24 → 25 taken 127 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 41 → 43 taken 127 times.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 45 taken 127 times.
✓ Branch 45 → 46 taken 2 times.
✓ Branch 45 → 47 taken 127 times.
✓ Branch 47 → 48 taken 2 times.
✓ Branch 47 → 49 taken 127 times.
✓ Branch 49 → 50 taken 2 times.
✓ Branch 49 → 51 taken 127 times.
✓ Branch 51 → 52 taken 2 times.
✓ Branch 51 → 53 taken 127 times.
✗ Branch 144 → 145 not taken.
✗ Branch 144 → 146 not taken.
✗ Branch 148 → 149 not taken.
✗ Branch 148 → 150 not taken.
✗ Branch 152 → 153 not taken.
✗ Branch 152 → 154 not taken.
✗ Branch 156 → 157 not taken.
✗ Branch 156 → 158 not taken.
✗ Branch 160 → 161 not taken.
✗ Branch 160 → 162 not taken.
✗ Branch 164 → 165 not taken.
✗ Branch 164 → 166 not taken.
✗ Branch 168 → 169 not taken.
✗ Branch 168 → 170 not taken.
|
645 | ? locale.toString(range.first, pattern) + QStringLiteral("–") + locale.toString(range.last, QStringLiteral("d")) |
| 73 |
18/30✓ Branch 17 → 18 taken 127 times.
✓ Branch 21 → 22 taken 127 times.
✓ Branch 22 → 23 taken 127 times.
✓ Branch 23 → 24 taken 127 times.
✓ Branch 27 → 28 taken 127 times.
✓ Branch 27 → 29 taken 2 times.
✓ Branch 29 → 30 taken 127 times.
✓ Branch 29 → 31 taken 2 times.
✓ Branch 31 → 32 taken 127 times.
✓ Branch 31 → 33 taken 2 times.
✓ Branch 33 → 34 taken 127 times.
✓ Branch 33 → 35 taken 2 times.
✓ Branch 35 → 36 taken 127 times.
✓ Branch 35 → 37 taken 2 times.
✓ Branch 37 → 38 taken 127 times.
✓ Branch 37 → 39 taken 2 times.
✓ Branch 39 → 40 taken 2 times.
✓ Branch 39 → 41 taken 127 times.
✗ Branch 120 → 121 not taken.
✗ Branch 120 → 122 not taken.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 126 not taken.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 130 not taken.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 138 not taken.
✗ Branch 140 → 141 not taken.
✗ Branch 140 → 142 not taken.
|
512 | : locale.toString(range.first, dayPattern(locale)) + QStringLiteral("–") + locale.toString(range.last, pattern); |
| 74 | } else { | ||
| 75 |
1/1✓ Branch 61 → 62 taken 459 times.
|
1377 | const QString shortPattern = QString(pattern).replace(QStringLiteral("MMMM"), QStringLiteral("MMM")); |
| 76 |
4/4✓ Branch 68 → 69 taken 459 times.
✓ Branch 72 → 73 taken 459 times.
✓ Branch 73 → 74 taken 459 times.
✓ Branch 74 → 75 taken 459 times.
|
918 | text = locale.toString(range.first, shortPattern) + QStringLiteral(" – ") + locale.toString(range.last, shortPattern); |
| 77 |
4/4✓ Branch 82 → 83 taken 459 times.
✓ Branch 83 → 84 taken 459 times.
✓ Branch 84 → 85 taken 442 times.
✓ Branch 84 → 105 taken 17 times.
|
459 | if (range.first.year() != range.last.year()) |
| 78 |
4/4✓ Branch 91 → 92 taken 442 times.
✓ Branch 92 → 93 taken 442 times.
✓ Branch 93 → 94 taken 442 times.
✓ Branch 94 → 95 taken 442 times.
|
884 | return withYear(locale.toString(range.first, shortPattern), range.first.year(), locale) + QStringLiteral(" – ") + |
| 79 |
4/4✓ Branch 85 → 86 taken 442 times.
✓ Branch 86 → 87 taken 442 times.
✓ Branch 87 → 88 taken 442 times.
✓ Branch 95 → 96 taken 442 times.
|
1326 | withYear(locale.toString(range.last, shortPattern), range.last.year(), locale); |
| 80 | 459 | } | |
| 81 |
4/4✓ Branch 111 → 112 taken 108 times.
✓ Branch 111 → 114 taken 38 times.
✓ Branch 112 → 113 taken 108 times.
✓ Branch 113 → 115 taken 108 times.
|
146 | return year ? withYear(text, range.last.year(), locale) : text; |
| 82 | 588 | } | |
| 83 | |||
| 84 | 11006 | QString dayLabel(const QDate &date, const QLocale &locale, bool year) { | |
| 85 |
4/4✓ Branch 2 → 3 taken 11006 times.
✓ Branch 3 → 4 taken 11006 times.
✓ Branch 7 → 8 taken 11006 times.
✓ Branch 8 → 9 taken 11006 times.
|
22012 | const QString text = locale.toString(date, QStringLiteral("ddd ")) + locale.toString(date, dayMonthPattern(locale)); |
| 86 |
4/4✓ Branch 14 → 15 taken 11005 times.
✓ Branch 14 → 17 taken 1 time.
✓ Branch 15 → 16 taken 11005 times.
✓ Branch 16 → 18 taken 11005 times.
|
22012 | return year ? withYear(text, date.year(), locale) : text; |
| 87 | 11006 | } | |
| 88 | |||
| 89 | // Shortest label of a breadcrumb level below `parent` (the year is implied | ||
| 90 | // by a year level above it). | ||
| 91 | 446 | QString levelLabel(const Range &range, const Range &parent, const QLocale &locale) { | |
| 92 |
4/6✓ Branch 3 → 4 taken 187 times.
✓ Branch 3 → 11 taken 259 times.
✓ Branch 6 → 7 taken 187 times.
✗ Branch 6 → 11 not taken.
✓ Branch 9 → 10 taken 187 times.
✗ Branch 9 → 11 not taken.
|
446 | const bool yearShown = parent.isValid() && parent.first.year() == range.first.year() && parent.last.year() == range.last.year(); |
| 93 |
2/2✓ Branch 13 → 14 taken 232 times.
✓ Branch 13 → 16 taken 214 times.
|
446 | if (wholeYear(range)) return QString::number(range.first.year()); |
| 94 |
2/2✓ Branch 17 → 18 taken 148 times.
✓ Branch 17 → 19 taken 66 times.
|
214 | if (wholeMonth(range)) return monthLabel(range.first, locale, !yearShown); |
| 95 |
2/2✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 22 taken 65 times.
|
66 | if (range.first == range.last) return dayLabel(range.first, locale, !yearShown); |
| 96 | 65 | return spanLabel(range, locale, !yearShown); | |
| 97 | } | ||
| 98 | |||
| 99 | 602 | QList<int> tickSteps(Unit unit) { | |
| 100 |
4/6✓ Branch 2 → 3 taken 23 times.
✓ Branch 2 → 4 taken 416 times.
✓ Branch 2 → 5 taken 4 times.
✓ Branch 2 → 6 taken 159 times.
✗ Branch 2 → 7 not taken.
✗ Branch 2 → 8 not taken.
|
602 | switch (unit) { |
| 101 | 23 | case Unit::Year: return {1, 2, 5, 10, 20, 50}; | |
| 102 | 416 | case Unit::Month: return {1, 2, 3, 6, 12}; | |
| 103 | 4 | case Unit::Week: return {1, 2, 4, 8, 13, 26}; | |
| 104 | 159 | case Unit::Day: return {1, 2, 7, 14, 28}; | |
| 105 | ✗ | case Unit::Hour: return {1, 2, 3, 6, 12}; | |
| 106 | } | ||
| 107 | ✗ | return {1}; | |
| 108 | } | ||
| 109 | |||
| 110 | } // namespace | ||
| 111 | |||
| 112 | 1128 | int maxBucketsForWidth(qreal plotWidth) { | |
| 113 |
1/1✓ Branch 2 → 3 taken 1128 times.
|
1128 | return std::clamp(int(std::floor(plotWidth / MinimumBarPixels)), 1, MaximumBuckets); |
| 114 | } | ||
| 115 | |||
| 116 | 2889 | int bucketCount(const Range &range, Unit unit, Qt::DayOfWeek weekStart) { | |
| 117 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2889 times.
|
2889 | if (!range.isValid()) return 0; |
| 118 |
4/6✓ Branch 5 → 6 taken 40 times.
✓ Branch 5 → 9 taken 898 times.
✓ Branch 5 → 14 taken 913 times.
✓ Branch 5 → 19 taken 1038 times.
✗ Branch 5 → 21 not taken.
✗ Branch 5 → 23 not taken.
|
2889 | switch (unit) { |
| 119 | 40 | case Unit::Year: return range.last.year() - range.first.year() + 1; | |
| 120 | 898 | case Unit::Month: | |
| 121 | 898 | return (range.last.year() * 12 + range.last.month()) - (range.first.year() * 12 + range.first.month()) + 1; | |
| 122 | 913 | case Unit::Week: | |
| 123 |
3/3✓ Branch 14 → 15 taken 913 times.
✓ Branch 15 → 16 taken 913 times.
✓ Branch 16 → 17 taken 913 times.
|
913 | return int(weekStartOf(range.first, weekStart).daysTo(weekStartOf(range.last, weekStart)) / 7) + 1; |
| 124 | 1038 | case Unit::Day: return int(range.days()); | |
| 125 | ✗ | case Unit::Hour: return int(range.days()) * 24; | |
| 126 | } | ||
| 127 | ✗ | return 0; | |
| 128 | } | ||
| 129 | |||
| 130 | 1138 | Unit chooseUnit(const Range &range, int maxBuckets, Qt::DayOfWeek weekStart) { | |
| 131 |
5/6✓ Branch 3 → 4 taken 1138 times.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 140 times.
✓ Branch 5 → 7 taken 998 times.
✓ Branch 8 → 9 taken 140 times.
✓ Branch 8 → 10 taken 998 times.
|
1138 | if (range.isValid() && range.days() == 1) return Unit::Hour; |
| 132 |
2/2✓ Branch 16 → 12 taken 2728 times.
✓ Branch 16 → 17 taken 66 times.
|
2794 | for (const Unit unit : {Unit::Day, Unit::Week, Unit::Month}) |
| 133 |
3/3✓ Branch 12 → 13 taken 2728 times.
✓ Branch 13 → 14 taken 932 times.
✓ Branch 13 → 15 taken 1796 times.
|
2728 | if (bucketCount(range, unit, weekStart) <= maxBuckets) return unit; |
| 134 | 66 | return Unit::Year; | |
| 135 | } | ||
| 136 | |||
| 137 | 555694 | Range unitRange(const QDate &date, Unit unit, Qt::DayOfWeek weekStart) { | |
| 138 |
4/5✓ Branch 2 → 3 taken 1502 times.
✓ Branch 2 → 7 taken 26370 times.
✓ Branch 2 → 9 taken 64560 times.
✓ Branch 2 → 14 taken 463262 times.
✗ Branch 2 → 15 not taken.
|
555694 | switch (unit) { |
| 139 | 1502 | case Unit::Year: return {QDate(date.year(), 1, 1), QDate(date.year(), 12, 31)}; | |
| 140 | 26370 | case Unit::Month: return {monthStart(date), monthEnd(date)}; | |
| 141 |
2/2✓ Branch 10 → 11 taken 64560 times.
✓ Branch 11 → 12 taken 64560 times.
|
64560 | case Unit::Week: return {weekStartOf(date, weekStart), weekStartOf(date, weekStart).addDays(6)}; |
| 142 | 463262 | case Unit::Day: | |
| 143 | 463262 | case Unit::Hour: return {date, date}; | |
| 144 | } | ||
| 145 | ✗ | return {date, date}; | |
| 146 | } | ||
| 147 | |||
| 148 | 552 | Range spanForHits(const QDate &firstHit, const QDate &lastHit, int maxBuckets, Qt::DayOfWeek weekStart) { | |
| 149 | 552 | const Range hits{std::min(firstHit, lastHit), std::max(firstHit, lastHit)}; | |
| 150 |
3/3✓ Branch 4 → 5 taken 552 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 551 times.
|
552 | if (!hits.isValid()) return {}; |
| 151 |
1/1✓ Branch 7 → 8 taken 551 times.
|
551 | const Unit unit = chooseUnit(hits, maxBuckets, weekStart); |
| 152 |
4/4✓ Branch 8 → 9 taken 471 times.
✓ Branch 8 → 10 taken 80 times.
✓ Branch 9 → 10 taken 25 times.
✓ Branch 9 → 11 taken 446 times.
|
551 | if (unit == Unit::Hour || unit == Unit::Day) return hits; |
| 153 |
2/2✓ Branch 11 → 12 taken 446 times.
✓ Branch 12 → 13 taken 446 times.
|
446 | return {unitRange(hits.first, unit, weekStart).first, unitRange(hits.last, unit, weekStart).last}; |
| 154 | } | ||
| 155 | |||
| 156 | 1256 | QList<Bucket> buckets(const QMap<QDate, qint64> &dayCounts, const Range &range, Unit unit, | |
| 157 | Qt::DayOfWeek weekStart) { | ||
| 158 | 1256 | QList<Bucket> result; | |
| 159 |
4/7✓ Branch 2 → 3 taken 1256 times.
✓ Branch 3 → 4 taken 1256 times.
✗ Branch 3 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1256 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1256 times.
|
1256 | if (!range.isValid() || unit == Unit::Hour) return result; |
| 160 | 1256 | QDate cursor = range.first; | |
| 161 |
2/2✓ Branch 31 → 10 taken 542531 times.
✓ Branch 31 → 32 taken 1256 times.
|
543787 | while (cursor <= range.last) { |
| 162 | 542531 | Bucket bucket; | |
| 163 |
2/2✓ Branch 10 → 11 taken 542531 times.
✓ Branch 11 → 12 taken 542531 times.
|
542531 | bucket.range = clip(unitRange(cursor, unit, weekStart), range); |
| 164 |
8/8✓ Branch 12 → 13 taken 542531 times.
✓ Branch 16 → 17 taken 792124 times.
✓ Branch 18 → 19 taken 785236 times.
✓ Branch 18 → 24 taken 6888 times.
✓ Branch 22 → 23 taken 249593 times.
✓ Branch 22 → 24 taken 535643 times.
✓ Branch 25 → 14 taken 249593 times.
✓ Branch 25 → 26 taken 542531 times.
|
792124 | for (auto it = dayCounts.lowerBound(bucket.range.first); it != dayCounts.cend() && it.key() <= bucket.range.last; ++it) |
| 165 | 249593 | bucket.count += it.value(); | |
| 166 |
1/1✓ Branch 26 → 27 taken 542531 times.
|
542531 | result.append(bucket); |
| 167 |
1/1✓ Branch 27 → 28 taken 542531 times.
|
542531 | cursor = bucket.range.last.addDays(1); |
| 168 | } | ||
| 169 | 1256 | return result; | |
| 170 | ✗ | } | |
| 171 | |||
| 172 | 2 | QList<Bucket> hourBuckets(const QDate &day, const QVector<qint64> &hourCounts) { | |
| 173 | 2 | QList<Bucket> result; | |
| 174 |
2/2✓ Branch 10 → 3 taken 48 times.
✓ Branch 10 → 11 taken 2 times.
|
50 | for (int hour = 0; hour < 24; ++hour) |
| 175 |
2/3✓ Branch 4 → 5 taken 48 times.
✗ Branch 4 → 7 not taken.
✓ Branch 8 → 9 taken 48 times.
|
48 | result.append({{day, day}, hour, hour < hourCounts.size() ? hourCounts.at(hour) : 0}); |
| 176 | 2 | return result; | |
| 177 | ✗ | } | |
| 178 | |||
| 179 | 657 | qint64 total(const QList<Bucket> &buckets) { | |
| 180 | 657 | qint64 sum = 0; | |
| 181 |
2/2✓ Branch 7 → 4 taken 525473 times.
✓ Branch 7 → 8 taken 657 times.
|
526130 | for (const Bucket &bucket : buckets) sum += bucket.count; |
| 182 | 657 | return sum; | |
| 183 | } | ||
| 184 | |||
| 185 | 6 | Range brush(const QList<Bucket> &buckets, int a, int b) { | |
| 186 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 5 times.
|
6 | if (buckets.isEmpty()) return {}; |
| 187 |
1/1✓ Branch 7 → 8 taken 5 times.
|
5 | const int first = std::clamp(std::min(a, b), 0, int(buckets.size()) - 1); |
| 188 |
1/1✓ Branch 10 → 11 taken 5 times.
|
5 | const int last = std::clamp(std::max(a, b), 0, int(buckets.size()) - 1); |
| 189 | 5 | return {buckets.at(first).range.first, buckets.at(last).range.last}; | |
| 190 | } | ||
| 191 | |||
| 192 | 829 | Range parentRange(const Range &range) { | |
| 193 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 829 times.
|
829 | if (!range.isValid()) return {}; |
| 194 |
6/6✓ Branch 6 → 7 taken 566 times.
✓ Branch 6 → 10 taken 263 times.
✓ Branch 8 → 9 taken 404 times.
✓ Branch 8 → 10 taken 162 times.
✓ Branch 11 → 12 taken 404 times.
✓ Branch 11 → 14 taken 425 times.
|
829 | if (sameMonth(range) && !wholeMonth(range)) return {monthStart(range.first), monthEnd(range.first)}; |
| 195 |
6/6✓ Branch 16 → 17 taken 397 times.
✓ Branch 16 → 20 taken 28 times.
✓ Branch 18 → 19 taken 164 times.
✓ Branch 18 → 20 taken 233 times.
✓ Branch 21 → 22 taken 164 times.
✓ Branch 21 → 26 taken 261 times.
|
425 | if (range.first.year() == range.last.year() && !wholeYear(range)) |
| 196 | 164 | return {QDate(range.first.year(), 1, 1), QDate(range.first.year(), 12, 31)}; | |
| 197 | 261 | return {}; | |
| 198 | } | ||
| 199 | |||
| 200 | 2269 | QList<Level> breadcrumb(const Range &range, const QLocale &locale, const QString &allTimeLabel) { | |
| 201 | 2269 | QList<Range> chain; | |
| 202 |
5/5✓ Branch 3 → 4 taken 446 times.
✓ Branch 4 → 5 taken 446 times.
✓ Branch 5 → 6 taken 2715 times.
✓ Branch 6 → 3 taken 446 times.
✓ Branch 6 → 7 taken 2269 times.
|
2715 | for (Range level = range; level.isValid(); level = parentRange(level)) chain.prepend(level); |
| 203 |
2/4✓ Branch 10 → 11 taken 2269 times.
✓ Branch 10 → 12 taken 2269 times.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
|
4538 | QList<Level> levels{{Range{}, allTimeLabel}}; |
| 204 | 2269 | Range parent; | |
| 205 |
4/4✓ Branch 12 → 13 taken 2269 times.
✓ Branch 13 → 14 taken 2269 times.
✓ Branch 21 → 15 taken 446 times.
✓ Branch 21 → 22 taken 2269 times.
|
2715 | for (const Range &level : chain) { |
| 206 |
1/1✓ Branch 17 → 18 taken 446 times.
|
446 | levels.append({level, levelLabel(level, parent, locale)}); |
| 207 | 446 | parent = level; | |
| 208 | } | ||
| 209 | 2269 | return levels; | |
| 210 | 4984 | } | |
| 211 | |||
| 212 | 22394 | QString rangeLabel(const Range &range, const QLocale &locale) { | |
| 213 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 22394 times.
|
22394 | if (!range.isValid()) return {}; |
| 214 |
2/2✓ Branch 6 → 7 taken 207 times.
✓ Branch 6 → 9 taken 22187 times.
|
22394 | if (wholeYear(range)) return QString::number(range.first.year()); |
| 215 |
2/2✓ Branch 10 → 11 taken 10660 times.
✓ Branch 10 → 12 taken 11527 times.
|
22187 | if (wholeMonth(range)) return monthLabel(range.first, locale, true); |
| 216 |
2/2✓ Branch 13 → 14 taken 11004 times.
✓ Branch 13 → 15 taken 523 times.
|
11527 | if (range.first == range.last) return dayLabel(range.first, locale, true); |
| 217 | 523 | return spanLabel(range, locale, true); | |
| 218 | } | ||
| 219 | |||
| 220 | 9353 | QString bucketLabel(const Bucket &bucket, Unit unit, const QLocale &locale) { | |
| 221 |
2/2✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 18 taken 9352 times.
|
9353 | if (unit == Unit::Hour) |
| 222 |
2/2✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
|
2 | return dayLabel(bucket.range.first, locale, true) + QStringLiteral(", ") + |
| 223 |
3/3✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
|
3 | locale.toString(QTime(bucket.hour, 0), QLocale::ShortFormat); |
| 224 | 9352 | return rangeLabel(bucket.range, locale); | |
| 225 | } | ||
| 226 | |||
| 227 | 6815 | QString axisLabel(const Bucket &bucket, Unit unit, const QLocale &locale) { | |
| 228 |
3/5✓ Branch 2 → 3 taken 163 times.
✓ Branch 2 → 5 taken 5205 times.
✓ Branch 2 → 7 taken 1447 times.
✗ Branch 2 → 12 not taken.
✗ Branch 2 → 15 not taken.
|
6815 | switch (unit) { |
| 229 | 163 | case Unit::Year: return QString::number(bucket.range.first.year()); | |
| 230 | 5205 | case Unit::Month: return locale.standaloneMonthName(bucket.range.first.month(), QLocale::ShortFormat); | |
| 231 | 1447 | case Unit::Week: | |
| 232 |
2/2✓ Branch 7 → 8 taken 1447 times.
✓ Branch 8 → 9 taken 1447 times.
|
1447 | case Unit::Day: return locale.toString(bucket.range.first, dayPattern(locale)); |
| 233 | ✗ | case Unit::Hour: return locale.toString(QTime(bucket.hour, 0), QLocale::ShortFormat); | |
| 234 | } | ||
| 235 | ✗ | return {}; | |
| 236 | } | ||
| 237 | |||
| 238 | namespace { | ||
| 239 | // Month ticks carry the year where it starts and on the first tick ("Jan | ||
| 240 | // 2021", "Apr 2020"), so two "Jan" are never ambiguous. | ||
| 241 | 6814 | QString tickLabel(const Bucket &bucket, Unit unit, const QLocale &locale, bool first) { | |
| 242 |
1/1✓ Branch 2 → 3 taken 6814 times.
|
6814 | const QString label = axisLabel(bucket, unit, locale); |
| 243 |
9/9✓ Branch 3 → 4 taken 5204 times.
✓ Branch 3 → 8 taken 1610 times.
✓ Branch 4 → 5 taken 4273 times.
✓ Branch 4 → 7 taken 931 times.
✓ Branch 5 → 6 taken 4273 times.
✓ Branch 6 → 7 taken 148 times.
✓ Branch 6 → 8 taken 4125 times.
✓ Branch 9 → 10 taken 1079 times.
✓ Branch 9 → 20 taken 5735 times.
|
6814 | if (unit == Unit::Month && (first || bucket.range.first.month() == 1)) |
| 244 |
4/4✓ Branch 10 → 11 taken 1079 times.
✓ Branch 11 → 12 taken 1079 times.
✓ Branch 14 → 15 taken 1079 times.
✓ Branch 15 → 16 taken 1079 times.
|
1079 | return label + QLatin1Char(' ') + QString::number(bucket.range.first.year()); |
| 245 | 5735 | return label; | |
| 246 | 6814 | } | |
| 247 | } // namespace | ||
| 248 | |||
| 249 | 602 | QList<Tick> ticks(const QList<Bucket> &buckets, Unit unit, const QLocale &locale, qreal bucketWidth, | |
| 250 | const std::function<qreal(const QString &)> &textWidth, qreal gap) { | ||
| 251 | 1164 | const auto labelled = [&](int step) { | |
| 252 | 1164 | QList<Tick> result; | |
| 253 | 1164 | qreal previousRight = -1e9; | |
| 254 |
2/2✓ Branch 19 → 3 taken 6814 times.
✓ Branch 19 → 20 taken 602 times.
|
7416 | for (int i = 0; i < buckets.size(); i += step) { |
| 255 |
1/1✓ Branch 4 → 5 taken 6814 times.
|
6814 | const QString label = tickLabel(buckets.at(i), unit, locale, i == 0); |
| 256 |
1/1✓ Branch 5 → 6 taken 6814 times.
|
6814 | const qreal width = textWidth(label); |
| 257 | 6814 | const qreal left = (i + 0.5) * bucketWidth - width / 2; | |
| 258 |
2/2✓ Branch 6 → 7 taken 562 times.
✓ Branch 6 → 9 taken 6252 times.
|
6814 | if (left < previousRight + gap) return QList<Tick>{}; |
| 259 |
1/1✓ Branch 10 → 11 taken 6252 times.
|
6252 | result.append({i, label}); |
| 260 | 6252 | previousRight = left + width; | |
| 261 | 6814 | } | |
| 262 | 602 | return result; | |
| 263 | 7416 | }; | |
| 264 |
4/5✓ Branch 2 → 3 taken 602 times.
✓ Branch 3 → 4 taken 602 times.
✓ Branch 4 → 5 taken 602 times.
✓ Branch 23 → 6 taken 1164 times.
✗ Branch 23 → 24 not taken.
|
1164 | for (const int step : tickSteps(unit)) { |
| 265 |
1/1✓ Branch 7 → 8 taken 1164 times.
|
1164 | const auto result = labelled(step); |
| 266 |
6/6✓ Branch 9 → 10 taken 563 times.
✓ Branch 9 → 12 taken 601 times.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 562 times.
✓ Branch 14 → 15 taken 602 times.
✓ Branch 14 → 16 taken 562 times.
|
1164 | if (!result.isEmpty() || buckets.isEmpty()) return result; |
| 267 | 1164 | } | |
| 268 | ✗ | return buckets.isEmpty() ? QList<Tick>{} : QList<Tick>{{0, tickLabel(buckets.first(), unit, locale, true)}}; | |
| 269 | ✗ | } | |
| 270 | |||
| 271 | } // namespace TimeHistogram | ||
| 272 |