bump version after release
[toast/confclerk.git] / src / mvc / eventmodel.cpp
1 /*
2  * Copyright (C) 2010 Ixonos Plc.
3  * Copyright (C) 2011-2017 Philipp Spitzer, gregor herrmann, Stefan Stahl
4  *
5  * This file is part of ConfClerk.
6  *
7  * ConfClerk is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation, either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * ConfClerk is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * ConfClerk.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 #include "eventmodel.h"
21 #include "conference.h"
22 #include "track.h"
23 #include "room.h"
24
25 const QString EventModel::COMMA_SEPARATOR = ", ";
26
27 EventModel::EventModel()
28 { }
29
30
31 void EventModel::Group::setTitle(const QList<Event>& mEvents) {
32     QDateTime startTime = mEvents.at(mFirstEventIndex).start();
33     QDateTime endTime(startTime);
34     for (int i = mFirstEventIndex; i != mFirstEventIndex + mChildCount; ++i) {
35         endTime = qMax(mEvents.at(i).start().addSecs(mEvents.at(i).duration()), endTime);
36     }
37     mTitle = QString("%1 - %2").arg(startTime.toString("HH:mm")).arg(endTime.toString("HH:mm"));
38 }
39
40
41 // We want to group the events into "time slots/time groups" that
42 // should start at full hours and have the duration of either
43 // one hour or (if less than 3 events are in one time slot)
44 // multiple of one hour.
45 void EventModel::createTimeGroups()
46 {
47     beginResetModel();
48
49     mGroups.clear();
50     mParents.clear();
51     if (mEvents.empty()) return;
52
53     const int minTimeSpan = 3600; // one hour // minimum duration of a group in seconds
54     const int minChildCount = 3;  // minimum number of events in one group
55
56     QDateTime groupStartDateTime(mEvents.first().start().date(), QTime(mEvents.first().start().time().hour(), 0), mEvents.first().start().timeSpec());
57     QDateTime groupEndDateTime = groupStartDateTime.addSecs(mEvents.first().duration());
58     mGroups << EventModel::Group("", 0);
59     int timeSpan = minTimeSpan;
60
61     for (int i = 0; i != mEvents.count(); ++i) {
62         QDateTime eventStartDateTime = mEvents.at(i).start();
63         QDateTime eventEndDateTime = eventStartDateTime.addSecs(mEvents.at(i).duration());
64
65         if (eventStartDateTime >= groupStartDateTime.addSecs(timeSpan)) {
66             // a new group could be necessary
67             if (mGroups.last().mChildCount < minChildCount) {
68                 // too few events in the group => no new group
69                 // except a gap in time would occur that is longer than minTimeSpan
70                 QDateTime prevEventStartDateTime = mEvents.at(i).start();
71                 if (i > 0 && qMax(prevEventStartDateTime.addSecs(mEvents.at(i-1).duration()), groupEndDateTime).secsTo(eventStartDateTime) < minTimeSpan) {
72                     timeSpan += minTimeSpan;
73                     --i;
74                     continue; // repeat with the same event
75                 }
76             }
77
78             // a new group is necessary
79             mGroups.last().setTitle(mEvents);
80             groupStartDateTime = groupStartDateTime.addSecs(timeSpan);
81             groupEndDateTime = groupStartDateTime.addSecs(mEvents.at(i).duration());
82             mGroups << EventModel::Group("", i);
83             timeSpan = minTimeSpan;
84         }
85
86         // insert event into current group
87         mParents[mEvents.at(i).id()] = mGroups.count() - 1;
88         mGroups.last().mChildCount += 1;
89         groupEndDateTime = qMax(eventEndDateTime, groupEndDateTime);
90     }
91
92     // the last group needs a title as well
93     mGroups.last().setTitle(mEvents);
94
95     endResetModel();
96 }
97
98 void EventModel::createTrackGroups() {
99     mGroups.clear();
100     mParents.clear();
101     if (mEvents.empty())
102     {
103         return;
104     }
105     int trackId = mEvents.first().trackId();
106
107     mGroups << EventModel::Group(Track::retrieveTrackName(trackId), 0);
108     int nextTrackId = trackId;
109
110     for (int i=0; i<mEvents.count(); i++)
111     {
112         trackId = mEvents.at(i).trackId();
113         if (nextTrackId != trackId)
114         {
115             mGroups.last().mChildCount = i - mGroups.last().mFirstEventIndex;
116             mGroups << EventModel::Group(Track::retrieveTrackName(trackId), i);
117             nextTrackId = trackId;
118         }
119         // add parent-child relation
120         mParents[mEvents.at(i).id()] = mGroups.count() - 1;
121     }
122     mGroups.last().mChildCount = mEvents.count() - mGroups.last().mFirstEventIndex;
123 }
124
125 void EventModel::createRoomGroups()
126 {
127     mGroups.clear();
128     mParents.clear();
129     if (mEvents.empty())
130     {
131         return;
132     }
133     int roomId = mEvents.first().roomId();
134
135     mGroups << EventModel::Group(Room::retrieveRoomName(roomId), 0);
136     int nextRoomId = roomId;
137
138     QList<Event>::iterator event = mEvents.begin();
139     int i = 0;
140     while (event != mEvents.end())
141     {
142         roomId = event->roomId();
143         if (nextRoomId != roomId)
144         {
145             mGroups.last().mChildCount = i - mGroups.last().mFirstEventIndex;
146             mGroups << EventModel::Group(Room::retrieveRoomName(roomId), i);
147             nextRoomId = roomId;
148         }
149         mParents[event->id()] = mGroups.count() - 1;
150         event++;
151         i++;
152     }
153     mGroups.last().mChildCount = mEvents.count() - mGroups.last().mFirstEventIndex;
154 }
155
156 QVariant EventModel::data(const QModelIndex& index, int role) const
157 {
158     if (index.isValid() && role == Qt::DisplayRole)
159     {
160         if (index.internalId() == 0)
161         {
162             return mGroups.at(index.row()).mTitle;
163         }
164         else //event data
165         {
166             return static_cast<Event*>(index.internalPointer())->id();
167         }
168     }
169
170     return QVariant();
171 }
172
173 QModelIndex EventModel::index(int row, int column, const QModelIndex& parent) const
174 {
175     // TODO: add checks for out of range rows
176
177     if (!parent.isValid())
178     {
179         return createIndex(row, column);
180     }
181     else if (parent.internalId() == 0)
182     {
183         const Group& group = mGroups.at(parent.row());
184         Event* event = const_cast<Event*>(&mEvents.at(row + group.mFirstEventIndex));
185         return createIndex(row, column, reinterpret_cast<void*>(event));
186     }
187     else
188     {
189         return QModelIndex();
190     }
191 }
192
193 QModelIndex EventModel::parent(const QModelIndex & index) const
194 {
195     if (index.isValid())
196     {
197         if (index.internalId() == 0)
198         {
199             return QModelIndex();
200         }
201
202         Event * event = static_cast<Event*>(index.internalPointer());
203
204         return createIndex(mParents[event->id()], 0);
205     }
206
207     return QModelIndex();
208 }
209
210 int EventModel::columnCount(const QModelIndex & parent) const
211 {
212     Q_UNUSED(parent);
213     return 1;
214 }
215
216 int EventModel::rowCount (const QModelIndex & parent) const
217 {
218     if (!parent.isValid())
219     {
220         return mGroups.count();
221     }
222
223     if (parent.internalId() == 0)
224     {
225         return mGroups.at(parent.row()).mChildCount;
226     }
227
228     return 0;
229 }
230
231 void EventModel::clearModel()
232 {
233     beginResetModel();
234     mGroups.clear();
235     mEvents.clear();
236     mParents.clear();
237     endResetModel();
238 }
239
240
241 void EventModel::loadEvents(const QDate &aDate, int aConferenceId) {
242     clearModel();
243     mEvents = Event::getByDate(QDate(aDate.year(), aDate.month(), aDate.day()), aConferenceId, "start, duration");
244     createTimeGroups();
245 }
246
247
248 void EventModel::loadFavEvents(const QDate &aDate, int aConferenceId) {
249     clearModel();
250     mEvents = Event::getFavByDate(QDate(aDate.year(), aDate.month(), aDate.day()), aConferenceId);
251     createTimeGroups();
252 }
253
254
255 int EventModel::loadSearchResultEvents(const QDate &aDate, int aConferenceId) {
256     clearModel();
257     try {
258         mEvents = Event::getSearchResultByDate(QDate(aDate.year(), aDate.month(), aDate.day()), aConferenceId, "start, duration");
259     }
260     catch( OrmException &e  ){
261         qDebug() << "Event::getSearchResultByDate failed: " << e.text();
262     }
263     catch(...){
264         qDebug() << "Event::getSearchResultByDate failed";
265     }
266
267     createTimeGroups();
268
269     return mEvents.count();
270 }
271
272
273 void EventModel::loadEventsByTrack(const QDate &aDate, int aConferenceId) {
274     clearModel();
275     mEvents = Event::getByDate(QDate(aDate.year(), aDate.month(), aDate.day()), aConferenceId, "xid_track, start, duration");
276     createTrackGroups();
277 }
278
279
280 void EventModel::loadEventsByRoom(const QDate &aDate, int aConferenceId) {
281     clearModel();
282     mEvents = Event::getByDateAndRoom(QDate(aDate.year(), aDate.month(), aDate.day()), aConferenceId);
283     createRoomGroups();
284 }
285
286
287 void EventModel::loadConflictEvents(int aEventId, int aConferenceId) {
288     clearModel();
289     mEvents = Event::conflictEvents(aEventId, aConferenceId);
290     createTimeGroups();
291 }
292
293
294 void EventModel::updateModel(int aEventId)
295 {
296     for(int i=0; i<mEvents.count(); i++)
297     {
298         if(mEvents[i].id() == aEventId)
299             mEvents[i] = Event::getById(aEventId,Conference::activeConference());
300     }
301
302     // find the ModelIndex for given aEventId
303     for(int i=0; i<mGroups.count(); i++)
304     {
305         QModelIndex groupIndex = index(i,0,QModelIndex());
306         for(int j=0; j<mGroups[i].mChildCount; j++)
307         {
308             QModelIndex eventIndex = index(j,0,groupIndex);
309             if(static_cast<Event*>(eventIndex.internalPointer())->id() == aEventId)
310             {
311                 emit(dataChanged(groupIndex,groupIndex));
312                 emit(dataChanged(eventIndex,eventIndex));
313             }
314         }
315     }
316 }
317