Incidenceeditor

visualfreebusywidget.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
3 SPDX-FileCopyrightText: 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "visualfreebusywidget.h"
9using namespace Qt::Literals::StringLiterals;
10
11#include "freebusyganttproxymodel.h"
12#include <CalendarSupport/FreeBusyItemModel>
13
14#include <KGanttAbstractRowController>
15#include <KGanttDateTimeGrid>
16#include <KGanttGraphicsView>
17#include <KGanttView>
18
19#include "incidenceeditor_debug.h"
20#include <KLocalizedString>
21#include <QComboBox>
22
23#include <QHeaderView>
24#include <QLabel>
25#include <QPointer>
26#include <QPushButton>
27#include <QScrollBar>
28#include <QSplitter>
29#include <QTreeView>
30#include <QVBoxLayout>
31
32using namespace IncidenceEditorNG;
33
34namespace IncidenceEditorNG
35{
36class RowController : public KGantt::AbstractRowController
37{
38private:
39 static const int ROW_HEIGHT;
40 QPointer<QAbstractItemModel> m_model;
41
42public:
43 RowController()
44 {
45 mRowHeight = 20;
46 }
47
48 void setModel(QAbstractItemModel *model)
49 {
50 m_model = model;
51 }
52
53 [[nodiscard]] int headerHeight() const override
54 {
55 return 2 * mRowHeight + 10;
56 }
57
58 [[nodiscard]] bool isRowVisible(const QModelIndex &) const override
59 {
60 return true;
61 }
62
63 [[nodiscard]] bool isRowExpanded(const QModelIndex &) const override
64 {
65 return false;
66 }
67
68 [[nodiscard]] KGantt::Span rowGeometry(const QModelIndex &idx) const override
69 {
70 return KGantt::Span(idx.row() * mRowHeight, mRowHeight);
71 }
72
73 [[nodiscard]] int maximumItemHeight() const override
74 {
75 return mRowHeight / 2;
76 }
77
78 [[nodiscard]] int totalHeight() const override
79 {
80 return m_model->rowCount() * mRowHeight;
81 }
82
83 [[nodiscard]] QModelIndex indexAt(int height) const override
84 {
85 return m_model->index(height / mRowHeight, 0);
86 }
87
88 [[nodiscard]] QModelIndex indexBelow(const QModelIndex &idx) const override
89 {
90 if (!idx.isValid()) {
91 return QModelIndex();
92 }
93 return idx.model()->index(idx.row() + 1, idx.column(), idx.parent());
94 }
95
96 [[nodiscard]] QModelIndex indexAbove(const QModelIndex &idx) const override
97 {
98 if (!idx.isValid()) {
99 return QModelIndex();
100 }
101 return idx.model()->index(idx.row() - 1, idx.column(), idx.parent());
102 }
103
104 void setRowHeight(int height)
105 {
106 mRowHeight = height;
107 }
108
109private:
110 int mRowHeight;
111};
112
113class GanttHeaderView : public QHeaderView
114{
115public:
116 explicit GanttHeaderView(QWidget *parent = nullptr)
118 {
119 }
120
121 [[nodiscard]] QSize sizeHint() const override
122 {
123 QSize s = QHeaderView::sizeHint();
124 s.rheight() *= 2;
125 return s;
126 }
127};
128}
129
130VisualFreeBusyWidget::VisualFreeBusyWidget(CalendarSupport::FreeBusyItemModel *model, int spacing, QWidget *parent)
131 : QWidget(parent)
132{
133 auto topLayout = new QVBoxLayout(this);
134 topLayout->setSpacing(spacing);
135
136 // The control panel for the gantt widget
137 QBoxLayout *controlLayout = new QHBoxLayout();
138 controlLayout->setSpacing(topLayout->spacing());
139 topLayout->addItem(controlLayout);
140
141 auto label = new QLabel(i18nc("@label", "Scale: "), this);
142 controlLayout->addWidget(label);
143
144 mScaleCombo = new QComboBox(this);
145 mScaleCombo->setToolTip(i18nc("@info:tooltip", "Set the Gantt chart zoom level"));
146 mScaleCombo->setWhatsThis(xi18nc("@info:whatsthis",
147 "Select the Gantt chart zoom level from one of the following:<nl/>"
148 "'Hour' shows a range of several hours,<nl/>"
149 "'Day' shows a range of a few days,<nl/>"
150 "'Week' shows a range of a few weeks,<nl/>"
151 "and 'Month' shows a range of a few months,<nl/>"
152 "while 'Automatic' selects the range most "
153 "appropriate for the current event or to-do."));
154 mScaleCombo->addItem(i18nc("@item:inlistbox range in hours", "Hour"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleHour));
155 mScaleCombo->addItem(i18nc("@item:inlistbox range in days", "Day"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleDay));
156 mScaleCombo->addItem(i18nc("@item:inlistbox range in weeks", "Week"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleWeek));
157 mScaleCombo->addItem(i18nc("@item:inlistbox range in months", "Month"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleMonth));
158 mScaleCombo->addItem(i18nc("@item:inlistbox range is computed automatically", "Automatic"), QVariant::fromValue<int>(KGantt::DateTimeGrid::ScaleAuto));
159 mScaleCombo->setCurrentIndex(0); // start with "hour"
160 connect(mScaleCombo, &QComboBox::activated, this, &VisualFreeBusyWidget::slotScaleChanged);
161 controlLayout->addWidget(mScaleCombo);
162
163 auto button = new QPushButton(i18nc("@action:button", "Center on Start"), this);
164 button->setToolTip(i18nc("@info:tooltip", "Center the Gantt chart on the event start date and time"));
165 button->setWhatsThis(i18nc("@info:whatsthis",
166 "Click this button to center the Gantt chart on the start "
167 "time and day of this event."));
168 connect(button, &QPushButton::clicked, this, &VisualFreeBusyWidget::slotCenterOnStart);
169 controlLayout->addWidget(button);
170
171 controlLayout->addStretch(1);
172
173 button = new QPushButton(i18nc("@action:button", "Pick Date"), this);
174 button->setToolTip(i18nc("@info:tooltip",
175 "Move the event to a date and time when all "
176 "attendees are available"));
177 button->setWhatsThis(i18nc("@info:whatsthis",
178 "Click this button to move the event to a date "
179 "and time when all the attendees have time "
180 "available in their Free/Busy lists."));
181 button->setEnabled(false);
182 connect(button, &QPushButton::clicked, this, &VisualFreeBusyWidget::slotPickDate);
183 controlLayout->addWidget(button);
184
185 controlLayout->addStretch(1);
186
187 button = new QPushButton(i18nc("@action:button reload freebusy data", "Reload"), this);
188 button->setToolTip(i18nc("@info:tooltip", "Reload Free/Busy data for all attendees"));
189 button->setWhatsThis(i18nc("@info:whatsthis",
190 "Pressing this button will cause the Free/Busy data for all "
191 "attendees to be reloaded from their corresponding servers."));
192 controlLayout->addWidget(button);
193 connect(button, &QPushButton::clicked, this, &VisualFreeBusyWidget::manualReload);
194
195 auto splitter = new QSplitter(Qt::Horizontal, this);
196 connect(splitter, &QSplitter::splitterMoved, this, &VisualFreeBusyWidget::splitterMoved);
197 mLeftView = new QTreeView(this);
198 mLeftView->setModel(model);
199 mLeftView->setHeader(new GanttHeaderView);
200 mLeftView->header()->setStretchLastSection(true);
201 mLeftView->setToolTip(i18nc("@info:tooltip", "Shows the tree list of all data"));
202 mLeftView->setWhatsThis(i18nc("@info:whatsthis", "Shows the tree list of all data"));
203 mLeftView->setRootIsDecorated(false);
204 mLeftView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
205 mLeftView->setContextMenuPolicy(Qt::CustomContextMenu);
206 mGanttGraphicsView = new KGantt::GraphicsView(this);
207 mGanttGraphicsView->setObjectName("mGanttGraphicsView"_L1);
208 mGanttGraphicsView->setToolTip(i18nc("@info:tooltip", "Shows the Free/Busy status of all attendees"));
209 mGanttGraphicsView->setWhatsThis(i18nc("@info:whatsthis",
210 "Shows the Free/Busy status of all attendees. "
211 "Double-clicking on an attendee's entry in the "
212 "list will allow you to enter the location of "
213 "their Free/Busy Information."));
214 mModel = new FreeBusyGanttProxyModel(this);
215 mModel->setSourceModel(model);
216
217 mRowController = new RowController;
218 mRowController->setRowHeight(fontMetrics().height()); // TODO: detect
219
220 mRowController->setModel(mModel);
221 mGanttGraphicsView->setRowController(mRowController);
222
223 mGanttGrid = new KGantt::DateTimeGrid;
224 mGanttGrid->setScale(KGantt::DateTimeGrid::ScaleHour);
225 mGanttGrid->setDayWidth(800);
226 mGanttGrid->setRowSeparators(true);
227 mGanttGraphicsView->setGrid(mGanttGrid);
228 mGanttGraphicsView->setModel(mModel);
229 mGanttGraphicsView->viewport()->setFixedWidth(800 * 30);
230
231 splitter->addWidget(mLeftView);
232 splitter->addWidget(mGanttGraphicsView);
233
234 topLayout->addWidget(splitter);
235 topLayout->setStretchFactor(splitter, 100);
236
237 // Initially, show 15 days back and forth
238 // set start to even hours, i.e. to 12:AM 0 Min 0 Sec
239 const QDateTime horizonStart = QDateTime(QDateTime::currentDateTime().addDays(-15).date().startOfDay());
240 mGanttGrid->setStartDateTime(horizonStart);
241
242 connect(mLeftView, &QTreeView::customContextMenuRequested, this, &VisualFreeBusyWidget::showAttendeeStatusMenu);
243}
244
245VisualFreeBusyWidget::~VisualFreeBusyWidget()
246{
247}
248
249void VisualFreeBusyWidget::showAttendeeStatusMenu()
250{
251}
252
253void VisualFreeBusyWidget::slotCenterOnStart()
254{
255 auto grid = static_cast<KGantt::DateTimeGrid *>(mGanttGraphicsView->grid());
256 int daysTo = grid->startDateTime().daysTo(mDtStart);
257 mGanttGraphicsView->horizontalScrollBar()->setValue(daysTo * 800);
258}
259
260void VisualFreeBusyWidget::slotIntervalColorRectangleMoved(const QDateTime &start, const QDateTime &end)
261{
262 mDtStart = start;
263 mDtEnd = end;
264 Q_EMIT dateTimesChanged(start, end);
265}
266
267/*!
268 This slot is called when the user clicks the "Pick a date" button.
269*/
270void VisualFreeBusyWidget::slotPickDate()
271{
272}
273
274void VisualFreeBusyWidget::slotScaleChanged(int newScale)
275{
276 const QVariant var = mScaleCombo->itemData(newScale);
277 Q_ASSERT(var.isValid());
278
279 int value = var.toInt();
280 mGanttGrid->setScale((KGantt::DateTimeGrid::Scale)value);
281}
282
283void VisualFreeBusyWidget::slotUpdateIncidenceStartEnd(const QDateTime &dtFrom, const QDateTime &dtTo)
284{
285 mDtStart = dtFrom;
286 mDtEnd = dtTo;
287 QDateTime horizonStart = QDateTime(dtFrom.addDays(-15).date().startOfDay());
288
289 auto grid = static_cast<KGantt::DateTimeGrid *>(mGanttGraphicsView->grid());
290 grid->setStartDateTime(horizonStart);
291 slotCenterOnStart();
292 mGanttGrid->setStartDateTime(horizonStart);
293}
294
295void VisualFreeBusyWidget::slotZoomToTime()
296{
297#if 0
298 mGanttGraphicsView->zoomToFit();
299#else
300 qCDebug(INCIDENCEEDITOR_LOG) << "Disabled code, port to KDGantt2";
301#endif
302}
303
304void VisualFreeBusyWidget::splitterMoved()
305{
306}
307
308#include "moc_visualfreebusywidget.cpp"
This is a private proxy model, that wraps the free busy data exposed by the FreeBusyItemModel for use...
void setScale(Scale s)
Q_SCRIPTABLE Q_NOREPLY void start()
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString label(StandardShortcut id)
const QList< QKeySequence > & end()
void clicked(bool checked)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
void addStretch(int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
virtual void setSpacing(int spacing) override
void activated(int index)
QDateTime startOfDay() const const
QDateTime addDays(qint64 ndays) const const
QDateTime currentDateTime()
QDate date() const const
QHeaderView(Qt::Orientation orientation, QWidget *parent)
virtual QSize sizeHint() const const override
int column() const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
int row() const const
Q_EMITQ_EMIT
QObject * parent() const const
int & rheight()
void splitterMoved(int pos, int index)
CustomContextMenu
Horizontal
ScrollBarAlwaysOff
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVariant fromValue(T &&value)
bool isValid() const const
int toInt(bool *ok) const const
QWidget(QWidget *parent, Qt::WindowFlags f)
void customContextMenuRequested(const QPoint &pos)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:54:48 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.