1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** You may use this file under the terms of the BSD license as follows: |
10 | ** |
11 | ** "Redistribution and use in source and binary forms, with or without |
12 | ** modification, are permitted provided that the following conditions are |
13 | ** met: |
14 | ** * Redistributions of source code must retain the above copyright |
15 | ** notice, this list of conditions and the following disclaimer. |
16 | ** * Redistributions in binary form must reproduce the above copyright |
17 | ** notice, this list of conditions and the following disclaimer in |
18 | ** the documentation and/or other materials provided with the |
19 | ** distribution. |
20 | ** * Neither the name of The Qt Company Ltd nor the names of its |
21 | ** contributors may be used to endorse or promote products derived |
22 | ** from this software without specific prior written permission. |
23 | ** |
24 | ** |
25 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
26 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
27 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
28 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
29 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
30 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
31 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
32 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
33 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
34 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
35 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include <QtWidgets> |
42 | #include <QtOrganizer/qorganizer.h> |
43 | #include <QtOrganizer/qorganizerabstractrequest.h> |
44 | |
45 | #include "monthpage.h" |
46 | #include "calendardemo.h" |
47 | |
48 | QTORGANIZER_USE_NAMESPACE |
49 | |
50 | Q_DECLARE_METATYPE(QOrganizerItem) |
51 | |
52 | MonthPage::MonthPage(QWidget *parent) |
53 | :QWidget(parent), |
54 | m_manager(0), |
55 | m_calendarWidget(0), |
56 | m_dateLabel(0), |
57 | m_itemList(0), |
58 | m_ignoreShowDayPageOnceFlag(false) |
59 | { |
60 | // Create widgets |
61 | QFormLayout *mainlayout = new QFormLayout(this); |
62 | |
63 | m_managerComboBox = new QComboBox(this); |
64 | foreach (const QString& manager, QOrganizerManager::availableManagers()) { |
65 | if (manager != "invalid" && manager != "skeleton" ) |
66 | m_managerComboBox->addItem(atext: manager); |
67 | } |
68 | mainlayout->addRow(labelText: "Backend:" , field: m_managerComboBox); |
69 | connect(sender: m_managerComboBox, SIGNAL(currentIndexChanged(const QString &)), receiver: this, SLOT(backendChanged(const QString &))); |
70 | |
71 | m_dateLabel = new QLabel(this); |
72 | m_dateLabel->setAlignment(Qt::AlignCenter); |
73 | mainlayout->addRow(widget: m_dateLabel); |
74 | |
75 | m_calendarWidget = new QCalendarWidget(this); |
76 | m_calendarWidget->setGridVisible(true); |
77 | m_calendarWidget->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames); |
78 | m_calendarWidget->setNavigationBarVisible(false); |
79 | mainlayout->addRow(widget: m_calendarWidget); |
80 | connect(sender: m_calendarWidget, SIGNAL(selectionChanged()), receiver: this, SLOT(refreshDayItems())); |
81 | connect(sender: m_calendarWidget, SIGNAL(currentPageChanged(int, int)), receiver: this, SLOT(currentMonthChanged())); |
82 | connect(sender: m_calendarWidget, SIGNAL(activated(QDate)), receiver: this, SLOT(dayDoubleClicked(QDate))); |
83 | |
84 | m_itemList = new QListWidget(this); |
85 | m_itemList->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Expanding); |
86 | m_itemList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
87 | m_itemList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
88 | m_itemList->setFocusPolicy(Qt::NoFocus); |
89 | mainlayout->addRow(widget: m_itemList); |
90 | connect(sender: m_itemList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), receiver: this, SLOT(itemDoubleClicked(QListWidgetItem*))); |
91 | |
92 | setLayout(mainlayout); |
93 | } |
94 | |
95 | // This is separate from the constructor so we can do it after connecting the signals |
96 | void MonthPage::init() |
97 | { |
98 | backendChanged(managerName: m_managerComboBox->currentText()); |
99 | currentMonthChanged(); |
100 | } |
101 | |
102 | MonthPage::~MonthPage() |
103 | { |
104 | |
105 | } |
106 | |
107 | void MonthPage::backendChanged(const QString &managerName) |
108 | { |
109 | // Nothing to change |
110 | if (m_manager && m_manager->managerName() == managerName) |
111 | return; |
112 | |
113 | // Try creating a new manager |
114 | QMap<QString, QString> parameters; |
115 | QOrganizerManager* newManager = new QOrganizerManager(managerName, parameters, this); |
116 | if (!newManager || newManager->error()) { |
117 | QMessageBox::information(parent: this, title: tr(s: "Failed!" ), text: QString("Failed to create manager" )); |
118 | delete newManager; |
119 | m_managerComboBox->setCurrentIndex(m_managerComboBox->findText(text: m_manager->managerName())); |
120 | } else { |
121 | // Success: Replace the old one |
122 | delete m_manager; |
123 | m_manager = newManager; |
124 | emit managerChanged(manager: m_manager); |
125 | refresh(); |
126 | } |
127 | } |
128 | |
129 | void MonthPage::editItem() |
130 | { |
131 | QListWidgetItem *listItem = m_itemList->currentItem(); |
132 | if (!listItem) |
133 | return; |
134 | |
135 | QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value<QOrganizerItem>(); |
136 | if (!organizerItem.isEmpty()) |
137 | emit showEditPage(item: organizerItem); |
138 | } |
139 | |
140 | void MonthPage::removeItem() |
141 | { |
142 | QListWidgetItem *listItem = m_itemList->currentItem(); |
143 | if (!listItem) |
144 | return; |
145 | |
146 | QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value<QOrganizerItem>(); |
147 | if (organizerItem.isEmpty()) |
148 | return; |
149 | |
150 | m_manager->removeItem(itemId: organizerItem.id()); |
151 | refresh(); |
152 | } |
153 | |
154 | void MonthPage::refresh() |
155 | { |
156 | // fetch all current months items |
157 | QDate start(m_calendarWidget->yearShown(), m_calendarWidget->monthShown(), 1); |
158 | QDate end = start.addDays(days: start.daysInMonth()); |
159 | QDateTime startDateTime(start, QTime(0, 0, 0, 0)); |
160 | QDateTime endDateTime(end, QTime(23, 59, 59, 0)); |
161 | // Remove all previous formatting |
162 | for (int i=0; i<start.daysInMonth(); i++) { |
163 | QTextCharFormat cf = m_calendarWidget->dateTextFormat(date: start.addDays(days: i)); |
164 | cf.setFontItalic(false); |
165 | cf.setFontUnderline(false); |
166 | cf.clearBackground(); |
167 | m_calendarWidget->setDateTextFormat(date: start.addDays(days: i), format: cf); |
168 | } |
169 | |
170 | // Underline current date |
171 | QTextCharFormat cf = m_calendarWidget->dateTextFormat(date: QDate::currentDate()); |
172 | cf.setFontUnderline(true); |
173 | m_calendarWidget->setDateTextFormat(date: QDate::currentDate(), format: cf); |
174 | |
175 | // TODO: switch to item instances when theres a backed |
176 | QList<QOrganizerItem> items = m_manager->items(startDateTime, endDateTime); |
177 | |
178 | // Get dates for all items |
179 | QList<QDate> dates; |
180 | foreach (const QOrganizerItem &item, items) |
181 | { |
182 | QDate startDate; |
183 | QDate endDate; |
184 | |
185 | QOrganizerEventTime eventTime = item.detail(detailType: QOrganizerItemDetail::TypeEventTime); |
186 | if (!eventTime.isEmpty()) { |
187 | startDate = eventTime.startDateTime().date(); |
188 | endDate = eventTime.endDateTime().date(); |
189 | } else { |
190 | QOrganizerTodoTime todoTime = item.detail(detailType: QOrganizerItemDetail::TypeTodoTime); |
191 | if (!todoTime.isEmpty()) { |
192 | startDate = todoTime.startDateTime().date(); |
193 | endDate = todoTime.dueDateTime().date(); |
194 | } else { |
195 | QOrganizerJournalTime journalTime = item.detail(detailType: QOrganizerItemDetail::TypeJournalTime); |
196 | if (!journalTime.isEmpty()) |
197 | startDate = endDate = journalTime.entryDateTime().date(); |
198 | } |
199 | } |
200 | |
201 | if (!startDate.isNull()) { |
202 | while (startDate <= endDate) { |
203 | dates << startDate; |
204 | startDate = startDate.addDays(days: 1); |
205 | } |
206 | } |
207 | } |
208 | |
209 | // Mark all dates which has events. |
210 | QBrush brush; |
211 | brush.setColor(Qt::green); |
212 | foreach (QDate date, dates) { |
213 | if (date == QDate()) |
214 | continue; |
215 | QTextCharFormat cf = m_calendarWidget->dateTextFormat(date); |
216 | cf.setFontItalic(true); // TODO: find a better way to mark dates |
217 | cf.setBackground(brush); |
218 | m_calendarWidget->setDateTextFormat(date, format: cf); |
219 | } |
220 | |
221 | // As the day item list is not showed do not refresh |
222 | // the day items to improve performance |
223 | refreshDayItems(); |
224 | } |
225 | |
226 | void MonthPage::refreshDayItems() |
227 | { |
228 | QDate selectedDate = m_calendarWidget->selectedDate(); |
229 | emit currentDayChanged(date: selectedDate); |
230 | |
231 | QDateTime startOfDay(selectedDate, QTime(0, 0, 0)); |
232 | QDateTime endOfDay(selectedDate, QTime(23, 59, 59)); |
233 | // Clear items shown |
234 | m_itemList->clear(); |
235 | |
236 | // Find all items for today |
237 | QList<QOrganizerItem> items = m_manager->items(startDateTime: startOfDay, endDateTime: endOfDay); |
238 | |
239 | foreach (const QOrganizerItem &item, items) |
240 | { |
241 | QOrganizerEventTime eventTime = item.detail(detailType: QOrganizerItemDetail::TypeEventTime); |
242 | if (!eventTime.isEmpty()) { |
243 | QString time = eventTime.startDateTime().time().toString(format: "hh:mm" ); |
244 | QListWidgetItem* listItem = new QListWidgetItem(); |
245 | listItem->setText(QString("Event:%1-%2" ).arg(a: time).arg(a: item.displayLabel())); |
246 | QVariant data = QVariant::fromValue<QOrganizerItem>(value: item); |
247 | listItem->setData(ORGANIZER_ITEM_ROLE, value: data); |
248 | m_itemList->addItem(aitem: listItem); |
249 | } |
250 | |
251 | QOrganizerTodoTime todoTime = item.detail(detailType: QOrganizerItemDetail::TypeTodoTime); |
252 | if (!todoTime.isEmpty()) { |
253 | QString time = todoTime.startDateTime().time().toString(format: "hh:mm" ); |
254 | QListWidgetItem* listItem = new QListWidgetItem(); |
255 | listItem->setText(QString("Todo:%1-%2" ).arg(a: time).arg(a: item.displayLabel())); |
256 | QVariant data = QVariant::fromValue<QOrganizerItem>(value: item); |
257 | listItem->setData(ORGANIZER_ITEM_ROLE, value: data); |
258 | m_itemList->addItem(aitem: listItem); |
259 | } |
260 | |
261 | QOrganizerJournalTime journalTime = item.detail(detailType: QOrganizerItemDetail::TypeJournalTime); |
262 | if (!journalTime.isEmpty()) { |
263 | QString time = journalTime.entryDateTime().time().toString(format: "hh:mm" ); |
264 | QListWidgetItem* listItem = new QListWidgetItem(); |
265 | listItem->setText(QString("Journal:%1-%2" ).arg(a: time).arg(a: item.displayLabel())); |
266 | QVariant data = QVariant::fromValue<QOrganizerItem>(value: item); |
267 | listItem->setData(ORGANIZER_ITEM_ROLE, value: data); |
268 | m_itemList->addItem(aitem: listItem); |
269 | } |
270 | |
271 | // TODO: other item types |
272 | // TODO: icons for event/todo/journal would be nice |
273 | } |
274 | if (m_itemList->count() == 0) |
275 | m_itemList->addItem(label: "(no entries)" ); |
276 | } |
277 | |
278 | |
279 | void MonthPage::currentMonthChanged() |
280 | { |
281 | int month = m_calendarWidget->monthShown(); |
282 | int year = m_calendarWidget->yearShown(); |
283 | m_dateLabel->setText(QString("%1 %2" ).arg(a: QDate::longMonthName(month)).arg(a: year)); |
284 | refresh(); |
285 | m_ignoreShowDayPageOnceFlag = true; |
286 | } |
287 | |
288 | void MonthPage::dayDoubleClicked(QDate date) |
289 | { |
290 | if (!m_ignoreShowDayPageOnceFlag) |
291 | emit showDayPage(date); |
292 | else |
293 | m_ignoreShowDayPageOnceFlag = false; |
294 | } |
295 | |
296 | void MonthPage::openDay() |
297 | { |
298 | emit showDayPage(date: m_calendarWidget->selectedDate()); |
299 | } |
300 | |
301 | void MonthPage::itemDoubleClicked(QListWidgetItem *listItem) |
302 | { |
303 | if (!listItem) |
304 | return; |
305 | |
306 | QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value<QOrganizerItem>(); |
307 | if (!organizerItem.isEmpty()) |
308 | emit showEditPage(item: organizerItem); |
309 | } |
310 | |
311 | void MonthPage::showEvent(QShowEvent *event) |
312 | { |
313 | window()->setWindowTitle("Calendar Demo" ); |
314 | QWidget::showEvent(event); |
315 | } |
316 | |