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