File indexing completed on 2024-04-28 05:11:38

0001 /*
0002   SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
0003   SPDX-FileCopyrightText: 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0004 
0005   SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "visualfreebusywidget.h"
0009 #include "freebusyganttproxymodel.h"
0010 #include <CalendarSupport/FreeBusyItemModel>
0011 
0012 #include <KGanttAbstractRowController>
0013 #include <KGanttDateTimeGrid>
0014 #include <KGanttGraphicsView>
0015 #include <KGanttView>
0016 
0017 #include "incidenceeditor_debug.h"
0018 #include <KLocalizedString>
0019 #include <QComboBox>
0020 
0021 #include <QHeaderView>
0022 #include <QLabel>
0023 #include <QPointer>
0024 #include <QPushButton>
0025 #include <QScrollBar>
0026 #include <QSplitter>
0027 #include <QTreeView>
0028 #include <QVBoxLayout>
0029 
0030 using namespace IncidenceEditorNG;
0031 
0032 namespace IncidenceEditorNG
0033 {
0034 class RowController : public KGantt::AbstractRowController
0035 {
0036 private:
0037     static const int ROW_HEIGHT;
0038     QPointer<QAbstractItemModel> m_model;
0039 
0040 public:
0041     RowController()
0042     {
0043         mRowHeight = 20;
0044     }
0045 
0046     void setModel(QAbstractItemModel *model)
0047     {
0048         m_model = model;
0049     }
0050 
0051     [[nodiscard]] int headerHeight() const override
0052     {
0053         return 2 * mRowHeight + 10;
0054     }
0055 
0056     [[nodiscard]] bool isRowVisible(const QModelIndex &) const override
0057     {
0058         return true;
0059     }
0060 
0061     [[nodiscard]] bool isRowExpanded(const QModelIndex &) const override
0062     {
0063         return false;
0064     }
0065 
0066     [[nodiscard]] KGantt::Span rowGeometry(const QModelIndex &idx) const override
0067     {
0068         return KGantt::Span(idx.row() * mRowHeight, mRowHeight);
0069     }
0070 
0071     [[nodiscard]] int maximumItemHeight() const override
0072     {
0073         return mRowHeight / 2;
0074     }
0075 
0076     [[nodiscard]] int totalHeight() const override
0077     {
0078         return m_model->rowCount() * mRowHeight;
0079     }
0080 
0081     [[nodiscard]] QModelIndex indexAt(int height) const override
0082     {
0083         return m_model->index(height / mRowHeight, 0);
0084     }
0085 
0086     [[nodiscard]] QModelIndex indexBelow(const QModelIndex &idx) const override
0087     {
0088         if (!idx.isValid()) {
0089             return QModelIndex();
0090         }
0091         return idx.model()->index(idx.row() + 1, idx.column(), idx.parent());
0092     }
0093 
0094     [[nodiscard]] QModelIndex indexAbove(const QModelIndex &idx) const override
0095     {
0096         if (!idx.isValid()) {
0097             return QModelIndex();
0098         }
0099         return idx.model()->index(idx.row() - 1, idx.column(), idx.parent());
0100     }
0101 
0102     void setRowHeight(int height)
0103     {
0104         mRowHeight = height;
0105     }
0106 
0107 private:
0108     int mRowHeight;
0109 };
0110 
0111 class GanttHeaderView : public QHeaderView
0112 {
0113 public:
0114     explicit GanttHeaderView(QWidget *parent = nullptr)
0115         : QHeaderView(Qt::Horizontal, parent)
0116     {
0117     }
0118 
0119     [[nodiscard]] QSize sizeHint() const override
0120     {
0121         QSize s = QHeaderView::sizeHint();
0122         s.rheight() *= 2;
0123         return s;
0124     }
0125 };
0126 }
0127 
0128 VisualFreeBusyWidget::VisualFreeBusyWidget(CalendarSupport::FreeBusyItemModel *model, int spacing, QWidget *parent)
0129     : QWidget(parent)
0130 {
0131     auto topLayout = new QVBoxLayout(this);
0132     topLayout->setSpacing(spacing);
0133 
0134     // The control panel for the gantt widget
0135     QBoxLayout *controlLayout = new QHBoxLayout();
0136     controlLayout->setSpacing(topLayout->spacing());
0137     topLayout->addItem(controlLayout);
0138 
0139     auto label = new QLabel(i18nc("@label", "Scale: "), this);
0140     controlLayout->addWidget(label);
0141 
0142     mScaleCombo = new QComboBox(this);
0143     mScaleCombo->setToolTip(i18nc("@info:tooltip", "Set the Gantt chart zoom level"));
0144     mScaleCombo->setWhatsThis(xi18nc("@info:whatsthis",
0145                                      "Select the Gantt chart zoom level from one of the following:<nl/>"
0146                                      "'Hour' shows a range of several hours,<nl/>"
0147                                      "'Day' shows a range of a few days,<nl/>"
0148                                      "'Week' shows a range of a few weeks,<nl/>"
0149                                      "and 'Month' shows a range of a few months,<nl/>"
0150                                      "while 'Automatic' selects the range most "
0151                                      "appropriate for the current event or to-do."));
0152     mScaleCombo->addItem(i18nc("@item:inlistbox range in hours", "Hour"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleHour));
0153     mScaleCombo->addItem(i18nc("@item:inlistbox range in days", "Day"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleDay));
0154     mScaleCombo->addItem(i18nc("@item:inlistbox range in weeks", "Week"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleWeek));
0155     mScaleCombo->addItem(i18nc("@item:inlistbox range in months", "Month"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleMonth));
0156     mScaleCombo->addItem(i18nc("@item:inlistbox range is computed automatically", "Automatic"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleAuto));
0157     mScaleCombo->setCurrentIndex(0); // start with "hour"
0158     connect(mScaleCombo, &QComboBox::activated, this, &VisualFreeBusyWidget::slotScaleChanged);
0159     controlLayout->addWidget(mScaleCombo);
0160 
0161     auto button = new QPushButton(i18nc("@action:button", "Center on Start"), this);
0162     button->setToolTip(i18nc("@info:tooltip", "Center the Gantt chart on the event start date and time"));
0163     button->setWhatsThis(i18nc("@info:whatsthis",
0164                                "Click this button to center the Gantt chart on the start "
0165                                "time and day of this event."));
0166     connect(button, &QPushButton::clicked, this, &VisualFreeBusyWidget::slotCenterOnStart);
0167     controlLayout->addWidget(button);
0168 
0169     controlLayout->addStretch(1);
0170 
0171     button = new QPushButton(i18nc("@action:button", "Pick Date"), this);
0172     button->setToolTip(i18nc("@info:tooltip",
0173                              "Move the event to a date and time when all "
0174                              "attendees are available"));
0175     button->setWhatsThis(i18nc("@info:whatsthis",
0176                                "Click this button to move the event to a date "
0177                                "and time when all the attendees have time "
0178                                "available in their Free/Busy lists."));
0179     button->setEnabled(false);
0180     connect(button, &QPushButton::clicked, this, &VisualFreeBusyWidget::slotPickDate);
0181     controlLayout->addWidget(button);
0182 
0183     controlLayout->addStretch(1);
0184 
0185     button = new QPushButton(i18nc("@action:button reload freebusy data", "Reload"), this);
0186     button->setToolTip(i18nc("@info:tooltip", "Reload Free/Busy data for all attendees"));
0187     button->setWhatsThis(i18nc("@info:whatsthis",
0188                                "Pressing this button will cause the Free/Busy data for all "
0189                                "attendees to be reloaded from their corresponding servers."));
0190     controlLayout->addWidget(button);
0191     connect(button, &QPushButton::clicked, this, &VisualFreeBusyWidget::manualReload);
0192 
0193     auto splitter = new QSplitter(Qt::Horizontal, this);
0194     connect(splitter, &QSplitter::splitterMoved, this, &VisualFreeBusyWidget::splitterMoved);
0195     mLeftView = new QTreeView(this);
0196     mLeftView->setModel(model);
0197     mLeftView->setHeader(new GanttHeaderView);
0198     mLeftView->header()->setStretchLastSection(true);
0199     mLeftView->setToolTip(i18nc("@info:tooltip", "Shows the tree list of all data"));
0200     mLeftView->setWhatsThis(i18nc("@info:whatsthis", "Shows the tree list of all data"));
0201     mLeftView->setRootIsDecorated(false);
0202     mLeftView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0203     mLeftView->setContextMenuPolicy(Qt::CustomContextMenu);
0204     mGanttGraphicsView = new KGantt::GraphicsView(this);
0205     mGanttGraphicsView->setObjectName(QLatin1StringView("mGanttGraphicsView"));
0206     mGanttGraphicsView->setToolTip(i18nc("@info:tooltip", "Shows the Free/Busy status of all attendees"));
0207     mGanttGraphicsView->setWhatsThis(i18nc("@info:whatsthis",
0208                                            "Shows the Free/Busy status of all attendees. "
0209                                            "Double-clicking on an attendee's entry in the "
0210                                            "list will allow you to enter the location of "
0211                                            "their Free/Busy Information."));
0212     mModel = new FreeBusyGanttProxyModel(this);
0213     mModel->setSourceModel(model);
0214 
0215     mRowController = new RowController;
0216     mRowController->setRowHeight(fontMetrics().height()); // TODO: detect
0217 
0218     mRowController->setModel(mModel);
0219     mGanttGraphicsView->setRowController(mRowController);
0220 
0221     mGanttGrid = new KGantt::DateTimeGrid;
0222     mGanttGrid->setScale(KGantt::DateTimeGrid::ScaleHour);
0223     mGanttGrid->setDayWidth(800);
0224     mGanttGrid->setRowSeparators(true);
0225     mGanttGraphicsView->setGrid(mGanttGrid);
0226     mGanttGraphicsView->setModel(mModel);
0227     mGanttGraphicsView->viewport()->setFixedWidth(800 * 30);
0228 
0229     splitter->addWidget(mLeftView);
0230     splitter->addWidget(mGanttGraphicsView);
0231 
0232     topLayout->addWidget(splitter);
0233     topLayout->setStretchFactor(splitter, 100);
0234 
0235     // Initially, show 15 days back and forth
0236     // set start to even hours, i.e. to 12:AM 0 Min 0 Sec
0237     const QDateTime horizonStart = QDateTime(QDateTime::currentDateTime().addDays(-15).date().startOfDay());
0238     mGanttGrid->setStartDateTime(horizonStart);
0239 
0240     connect(mLeftView, &QTreeView::customContextMenuRequested, this, &VisualFreeBusyWidget::showAttendeeStatusMenu);
0241 }
0242 
0243 VisualFreeBusyWidget::~VisualFreeBusyWidget()
0244 {
0245 }
0246 
0247 void VisualFreeBusyWidget::showAttendeeStatusMenu()
0248 {
0249 }
0250 
0251 void VisualFreeBusyWidget::slotCenterOnStart()
0252 {
0253     auto grid = static_cast<KGantt::DateTimeGrid *>(mGanttGraphicsView->grid());
0254     int daysTo = grid->startDateTime().daysTo(mDtStart);
0255     mGanttGraphicsView->horizontalScrollBar()->setValue(daysTo * 800);
0256 }
0257 
0258 void VisualFreeBusyWidget::slotIntervalColorRectangleMoved(const QDateTime &start, const QDateTime &end)
0259 {
0260     mDtStart = start;
0261     mDtEnd = end;
0262     Q_EMIT dateTimesChanged(start, end);
0263 }
0264 
0265 /*!
0266   This slot is called when the user clicks the "Pick a date" button.
0267 */
0268 void VisualFreeBusyWidget::slotPickDate()
0269 {
0270 }
0271 
0272 void VisualFreeBusyWidget::slotScaleChanged(int newScale)
0273 {
0274     const QVariant var = mScaleCombo->itemData(newScale);
0275     Q_ASSERT(var.isValid());
0276 
0277     int value = var.toInt();
0278     mGanttGrid->setScale((KGantt::DateTimeGrid::Scale)value);
0279 }
0280 
0281 void VisualFreeBusyWidget::slotUpdateIncidenceStartEnd(const QDateTime &dtFrom, const QDateTime &dtTo)
0282 {
0283     mDtStart = dtFrom;
0284     mDtEnd = dtTo;
0285     QDateTime horizonStart = QDateTime(dtFrom.addDays(-15).date().startOfDay());
0286 
0287     auto grid = static_cast<KGantt::DateTimeGrid *>(mGanttGraphicsView->grid());
0288     grid->setStartDateTime(horizonStart);
0289     slotCenterOnStart();
0290     mGanttGrid->setStartDateTime(horizonStart);
0291 }
0292 
0293 void VisualFreeBusyWidget::slotZoomToTime()
0294 {
0295 #if 0
0296     mGanttGraphicsView->zoomToFit();
0297 #else
0298     qCDebug(INCIDENCEEDITOR_LOG) << "Disabled code, port to KDGantt2";
0299 #endif
0300 }
0301 
0302 void VisualFreeBusyWidget::splitterMoved()
0303 {
0304 }
0305 
0306 #include "moc_visualfreebusywidget.cpp"