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;
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 {
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...
QDateTime startDateTime() const
void setStartDateTime(const QDateTime &dt)
void setScale(Scale s)
AbstractGrid * grid() const
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
QScrollBar * horizontalScrollBar() const const
void setValue(int)
void addStretch(int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
virtual void setSpacing(int spacing) override
void activated(int index)
QVariant itemData(int index, int role) const const
QDateTime startOfDay() const const
QDateTime addDays(qint64 ndays) const const
QDateTime currentDateTime()
QDate date() const const
qint64 daysTo(const QDateTime &other) const const
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
void customContextMenuRequested(const QPoint &pos)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:16:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.