File indexing completed on 2025-01-05 04:47:40

0001 /*
0002   SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
0003   SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0004 
0005   SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "freeperiodmodel.h"
0009 
0010 #include <KFormat>
0011 #include <KLocalizedString>
0012 
0013 #include <QDateTime>
0014 #include <QLocale>
0015 #include <QTimeZone>
0016 
0017 using namespace CalendarSupport;
0018 
0019 FreePeriodModel::FreePeriodModel(QObject *parent)
0020     : QAbstractTableModel(parent)
0021 {
0022 }
0023 
0024 FreePeriodModel::~FreePeriodModel() = default;
0025 
0026 QVariant FreePeriodModel::data(const QModelIndex &index, int role) const
0027 {
0028     if (!index.isValid() || !hasIndex(index.row(), index.column())) {
0029         return {};
0030     }
0031 
0032     if (index.column() == 0) { // day
0033         switch (role) {
0034         case Qt::DisplayRole:
0035             return day(index.row());
0036         case Qt::ToolTipRole:
0037             return tooltipify(index.row());
0038         case FreePeriodModel::PeriodRole:
0039             return QVariant::fromValue(mPeriodList.at(index.row()));
0040         case Qt::TextAlignmentRole:
0041             return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);
0042         default:
0043             return {};
0044         }
0045     } else { // everything else
0046         switch (role) {
0047         case Qt::DisplayRole:
0048             return date(index.row());
0049         case Qt::ToolTipRole:
0050             return tooltipify(index.row());
0051         case FreePeriodModel::PeriodRole:
0052             return QVariant::fromValue(mPeriodList.at(index.row()));
0053         case Qt::TextAlignmentRole:
0054             return static_cast<int>(Qt::AlignLeft | Qt::AlignVCenter);
0055         default:
0056             return {};
0057         }
0058     }
0059 }
0060 
0061 int FreePeriodModel::rowCount(const QModelIndex &parent) const
0062 {
0063     if (!parent.isValid()) {
0064         return mPeriodList.size();
0065     }
0066     return 0;
0067 }
0068 
0069 int FreePeriodModel::columnCount(const QModelIndex &parent) const
0070 {
0071     Q_UNUSED(parent)
0072     return 2;
0073 }
0074 
0075 QVariant FreePeriodModel::headerData(int section, Qt::Orientation orientation, int role) const
0076 {
0077     return QAbstractItemModel::headerData(section, orientation, role);
0078 }
0079 
0080 void FreePeriodModel::slotNewFreePeriods(const KCalendarCore::Period::List &freePeriods)
0081 {
0082     beginResetModel();
0083     mPeriodList.clear();
0084     mPeriodList = splitPeriodsByDay(freePeriods);
0085     std::sort(mPeriodList.begin(), mPeriodList.end());
0086     endResetModel();
0087 }
0088 
0089 KCalendarCore::Period::List FreePeriodModel::splitPeriodsByDay(const KCalendarCore::Period::List &freePeriods)
0090 {
0091     KCalendarCore::Period::List splitList;
0092     for (const KCalendarCore::Period &period : freePeriods) {
0093         if (period.start().date() == period.end().date()) {
0094             splitList << period; // period occurs on the same day
0095             continue;
0096         }
0097 
0098         const int validPeriodSecs = 300; // 5 minutes
0099         KCalendarCore::Period tmpPeriod = period;
0100         while (tmpPeriod.start().date() != tmpPeriod.end().date()) {
0101             const QDateTime midnight(tmpPeriod.start().date(), QTime(23, 59, 59, 999), tmpPeriod.start().timeZone());
0102             KCalendarCore::Period firstPeriod(tmpPeriod.start(), midnight);
0103             KCalendarCore::Period secondPeriod(midnight.addMSecs(1), tmpPeriod.end());
0104             if (firstPeriod.duration().asSeconds() >= validPeriodSecs) {
0105                 splitList << firstPeriod;
0106             }
0107             tmpPeriod = secondPeriod;
0108         }
0109         if (tmpPeriod.duration().asSeconds() >= validPeriodSecs) {
0110             splitList << tmpPeriod;
0111         }
0112     }
0113 
0114     // Perform some jiggery pokery to remove duplicates
0115     std::sort(splitList.begin(), splitList.end());
0116     splitList.erase(std::unique(splitList.begin(), splitList.end()), splitList.end());
0117     return splitList;
0118 }
0119 
0120 QString FreePeriodModel::day(int index) const
0121 {
0122     KCalendarCore::Period period = mPeriodList.at(index);
0123     const QDate startDate = period.start().date();
0124     return ki18nc("@label Day of the week name, example: Monday,", "%1,")
0125         .subs(QLocale::system().dayName(startDate.dayOfWeek(), QLocale::LongFormat))
0126         .toString();
0127 }
0128 
0129 QString FreePeriodModel::date(int index) const
0130 {
0131     KCalendarCore::Period period = mPeriodList.at(index);
0132 
0133     const QDate startDate = period.start().date();
0134     const QString startTime = QLocale::system().toString(period.start().time(), QLocale::ShortFormat);
0135     const QString endTime = QLocale::system().toString(period.end().time(), QLocale::ShortFormat);
0136     const QString longMonthName = QLocale::system().monthName(startDate.month());
0137     return ki18nc(
0138                "@label A time period duration. It is preceded/followed (based on the "
0139                "orientation) by the name of the week, see the message above. "
0140                "example: 12 June, 8:00am to 9:30am",
0141                "%1 %2, %3 to %4")
0142         .subs(startDate.day())
0143         .subs(longMonthName)
0144         .subs(startTime)
0145         .subs(endTime)
0146         .toString();
0147 }
0148 
0149 QString FreePeriodModel::stringify(int index) const
0150 {
0151     KCalendarCore::Period period = mPeriodList.at(index);
0152 
0153     const QDate startDate = period.start().date();
0154     const QString startTime = QLocale().toString(period.start().time(), QLocale::ShortFormat);
0155     const QString endTime = QLocale().toString(period.end().time(), QLocale::ShortFormat);
0156     const QString longMonthName = QLocale::system().monthName(startDate.month(), QLocale::LongFormat);
0157     const QString dayofWeek = QLocale::system().dayName(startDate.dayOfWeek(), QLocale::LongFormat);
0158 
0159     return ki18nc(
0160                "@label A time period duration. KLocale is used to format the components. "
0161                "example: Monday, 12 June, 8:00am to 9:30am",
0162                "%1, %2 %3, %4 to %5")
0163         .subs(dayofWeek)
0164         .subs(startDate.day())
0165         .subs(longMonthName)
0166         .subs(startTime)
0167         .subs(endTime)
0168         .toString();
0169 }
0170 
0171 QString FreePeriodModel::tooltipify(int index) const
0172 {
0173     KCalendarCore::Period period = mPeriodList.at(index);
0174     unsigned long duration = period.duration().asSeconds() * 1000; // we want milliseconds
0175     QString toolTip = QStringLiteral("<qt>");
0176     toolTip += QLatin1StringView("<b>") + i18nc("@info:tooltip", "Free Period") + QLatin1StringView("</b>");
0177     toolTip += QLatin1StringView("<hr>");
0178     toolTip += QLatin1StringView("<i>") + i18nc("@info:tooltip period start time", "Start:") + QLatin1StringView("</i>&nbsp;");
0179     toolTip += QLocale().toString(period.start().toLocalTime(), QLocale::ShortFormat);
0180     toolTip += QLatin1StringView("<br>");
0181     toolTip += QLatin1StringView("<i>") + i18nc("@info:tooltip period end time", "End:") + QLatin1StringView("</i>&nbsp;");
0182     toolTip += QLocale().toString(period.end().toLocalTime(), QLocale::ShortFormat);
0183     toolTip += QLatin1StringView("<br>");
0184     toolTip += QLatin1StringView("<i>") + i18nc("@info:tooltip period duration", "Duration:") + QLatin1StringView("</i>&nbsp;");
0185     toolTip += KFormat().formatSpelloutDuration(duration);
0186     toolTip += QLatin1StringView("</qt>");
0187     return toolTip;
0188 }
0189 
0190 #include "moc_freeperiodmodel.cpp"