1 /*
2 * SPDX-FileCopyrightText: 2014 Sandro Knauß <knauss@kolabsys.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "attendeetablemodel.h"
8
9 #include <KEmailAddress>
10
11 #include <KLocalizedString>
12
13 using namespace IncidenceEditorNG;
14
AttendeeTableModel(QObject * parent)15 AttendeeTableModel::AttendeeTableModel(QObject *parent)
16 : QAbstractTableModel(parent)
17 {
18 }
19
rowCount(const QModelIndex &) const20 int AttendeeTableModel::rowCount(const QModelIndex & /*parent*/) const
21 {
22 return mAttendeeList.count();
23 }
24
columnCount(const QModelIndex &) const25 int AttendeeTableModel::columnCount(const QModelIndex & /*parent*/) const
26 {
27 return 8;
28 }
29
flags(const QModelIndex & index) const30 Qt::ItemFlags AttendeeTableModel::flags(const QModelIndex &index) const
31 {
32 if (!index.isValid()) {
33 return Qt::ItemIsEnabled;
34 }
35 if (index.column() == Available || index.column() == Name || index.column() == Email) { // Available is read only
36 return QAbstractTableModel::flags(index);
37 } else {
38 return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
39 }
40 }
41
data(const QModelIndex & index,int role) const42 QVariant AttendeeTableModel::data(const QModelIndex &index, int role) const
43 {
44 if (!index.isValid()) {
45 return QVariant();
46 }
47
48 if (index.row() >= mAttendeeList.size()) {
49 return QVariant();
50 }
51
52 const KCalendarCore::Attendee attendee = mAttendeeList[index.row()];
53 if (role == Qt::DisplayRole || role == Qt::EditRole) {
54 switch (index.column()) {
55 case Role:
56 return attendee.role();
57 case FullName:
58 return attendee.fullName();
59 case Available: {
60 AvailableStatus available = mAttendeeAvailable[index.row()];
61 if (role == Qt::DisplayRole) {
62 switch (available) {
63 case Free:
64 return i18n("Free");
65 case Busy:
66 return i18n("Busy");
67 case Accepted:
68 return i18n("Accepted");
69 case Unknown:
70 return i18n("Unknown");
71 default:
72 return i18n("Unknown");
73 }
74 } else {
75 return available;
76 }
77 }
78 case Status:
79 return attendee.status();
80 case CuType:
81 return attendee.cuType();
82 case Response:
83 return attendee.RSVP();
84 case Name:
85 return attendee.name();
86 case Email:
87 return attendee.email();
88 }
89 }
90 if (role == AttendeeRole) {
91 return QVariant::fromValue(attendee);
92 }
93 return QVariant();
94 }
95
setData(const QModelIndex & index,const QVariant & value,int role)96 bool AttendeeTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
97 {
98 QString email;
99 QString name;
100 if (index.isValid() && role == Qt::EditRole) {
101 KCalendarCore::Attendee &attendee = mAttendeeList[index.row()];// clazy:exclude=detaching-member
102 switch (index.column()) {
103 case Role:
104 attendee.setRole(static_cast<KCalendarCore::Attendee::Role>(value.toInt()));
105 break;
106 case FullName:
107 if (mRemoveEmptyLines && value.toString().trimmed().isEmpty()) {
108 // Do not remove last empty line if mKeepEmpty==true
109 // (only works if initially there is only one empty line)
110 if (!mKeepEmpty || !(attendee.name().isEmpty() && attendee.email().isEmpty())) {
111 removeRows(index.row(), 1);
112 return true;
113 }
114 }
115 KEmailAddress::extractEmailAddressAndName(value.toString(), email, name);
116 attendee.setName(name);
117 attendee.setEmail(email);
118
119 addEmptyAttendee();
120 break;
121 case Available:
122 mAttendeeAvailable[index.row()] = static_cast<AvailableStatus>(value.toInt());
123 break;
124 case Status:
125 attendee.setStatus(static_cast<KCalendarCore::Attendee::PartStat>(value.toInt()));
126 break;
127 case CuType:
128 attendee.setCuType(static_cast<KCalendarCore::Attendee::CuType>(value.toInt()));
129 break;
130 case Response:
131 attendee.setRSVP(value.toBool());
132 break;
133 default:
134 return false;
135 }
136 Q_EMIT dataChanged(index, index);
137 return true;
138 }
139 return false;
140 }
141
headerData(int section,Qt::Orientation orientation,int role) const142 QVariant AttendeeTableModel::headerData(int section, Qt::Orientation orientation, int role) const
143 {
144 if (role != Qt::DisplayRole) {
145 return QVariant();
146 }
147
148 if (orientation == Qt::Horizontal) {
149 switch (section) {
150 case Role:
151 return i18nc("vCard attendee role", "Role");
152 case FullName:
153 return i18nc("Attendees (name+emailaddress)", "Name");
154 case Available:
155 return i18nc("Is attendee available for incidence", "Available");
156 case Status:
157 return i18nc("Status of attendee in an incidence (accepted, declined, delegated, ...)", "Status");
158 case CuType:
159 return i18nc("Type of calendar user (vCard attribute)", "User Type");
160 case Response:
161 return i18nc("Has attendee to respond to the invitation", "Response");
162 case Name:
163 return i18nc("Attendee name", "Name");
164 case Email:
165 return i18nc("Attendee email", "Email");
166 }
167 }
168
169 return QVariant();
170 }
171
insertRows(int position,int rows,const QModelIndex & parent)172 bool AttendeeTableModel::insertRows(int position, int rows, const QModelIndex &parent)
173 {
174 beginInsertRows(parent, position, position + rows - 1);
175
176 for (int row = 0; row < rows; ++row) {
177 KCalendarCore::Attendee attendee(QLatin1String(""), QLatin1String(""));
178 mAttendeeList.insert(position, attendee);
179 mAttendeeAvailable.insert(mAttendeeAvailable.begin() + position, AvailableStatus{});
180 }
181
182 endInsertRows();
183 return true;
184 }
185
removeRows(int position,int rows,const QModelIndex & parent)186 bool AttendeeTableModel::removeRows(int position, int rows, const QModelIndex &parent)
187 {
188 beginRemoveRows(parent, position, position + rows - 1);
189
190 for (int row = 0; row < rows; ++row) {
191 mAttendeeAvailable.erase(mAttendeeAvailable.begin() + position);
192 mAttendeeList.remove(position);
193 }
194
195 endRemoveRows();
196 return true;
197 }
198
insertAttendee(int position,const KCalendarCore::Attendee & attendee)199 bool AttendeeTableModel::insertAttendee(int position, const KCalendarCore::Attendee &attendee)
200 {
201 beginInsertRows(QModelIndex(), position, position);
202 mAttendeeList.insert(position, attendee);
203 mAttendeeAvailable.insert(mAttendeeAvailable.begin() + position, AvailableStatus{});
204 endInsertRows();
205
206 addEmptyAttendee();
207
208 return true;
209 }
210
setAttendees(const KCalendarCore::Attendee::List & attendees)211 void AttendeeTableModel::setAttendees(const KCalendarCore::Attendee::List &attendees)
212 {
213 Q_EMIT layoutAboutToBeChanged();
214
215 mAttendeeList = attendees;
216 mAttendeeAvailable.clear();
217 mAttendeeAvailable.resize(attendees.size());
218
219 addEmptyAttendee();
220
221 Q_EMIT layoutChanged();
222 }
223
attendees() const224 KCalendarCore::Attendee::List AttendeeTableModel::attendees() const
225 {
226 return mAttendeeList;
227 }
228
addEmptyAttendee()229 void AttendeeTableModel::addEmptyAttendee()
230 {
231 if (mKeepEmpty) {
232 bool create = true;
233 for (const KCalendarCore::Attendee &attendee : std::as_const(mAttendeeList)) {
234 if (attendee.fullName().isEmpty()) {
235 create = false;
236 break;
237 }
238 }
239
240 if (create) {
241 insertRows(rowCount(), 1);
242 }
243 }
244 }
245
keepEmpty() const246 bool AttendeeTableModel::keepEmpty() const
247 {
248 return mKeepEmpty;
249 }
250
setKeepEmpty(bool keepEmpty)251 void AttendeeTableModel::setKeepEmpty(bool keepEmpty)
252 {
253 if (keepEmpty != mKeepEmpty) {
254 mKeepEmpty = keepEmpty;
255 addEmptyAttendee();
256 }
257 }
258
removeEmptyLines() const259 bool AttendeeTableModel::removeEmptyLines() const
260 {
261 return mRemoveEmptyLines;
262 }
263
setRemoveEmptyLines(bool removeEmptyLines)264 void AttendeeTableModel::setRemoveEmptyLines(bool removeEmptyLines)
265 {
266 mRemoveEmptyLines = removeEmptyLines;
267 }
268
ResourceFilterProxyModel(QObject * parent)269 ResourceFilterProxyModel::ResourceFilterProxyModel(QObject *parent)
270 : QSortFilterProxyModel(parent)
271 {
272 }
273
filterAcceptsRow(int sourceRow,const QModelIndex & sourceParent) const274 bool ResourceFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
275 {
276 const QModelIndex cuTypeIndex = sourceModel()->index(sourceRow, AttendeeTableModel::CuType, sourceParent);
277 KCalendarCore::Attendee::CuType cuType = static_cast<KCalendarCore::Attendee::CuType>(sourceModel()->data(cuTypeIndex).toUInt());
278
279 return cuType == KCalendarCore::Attendee::Resource || cuType == KCalendarCore::Attendee::Room;
280 }
281
AttendeeFilterProxyModel(QObject * parent)282 AttendeeFilterProxyModel::AttendeeFilterProxyModel(QObject *parent)
283 : QSortFilterProxyModel(parent)
284 {
285 }
286
filterAcceptsRow(int sourceRow,const QModelIndex & sourceParent) const287 bool AttendeeFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
288 {
289 const QModelIndex cuTypeIndex = sourceModel()->index(sourceRow, AttendeeTableModel::CuType, sourceParent);
290 KCalendarCore::Attendee::CuType cuType = static_cast<KCalendarCore::Attendee::CuType>(sourceModel()->data(cuTypeIndex).toUInt());
291
292 return !(cuType == KCalendarCore::Attendee::Resource || cuType == KCalendarCore::Attendee::Room);
293 }
294