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"