1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2007-2011 Licq developers
4  *
5  * Licq is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Licq is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #ifndef CONTACTLISTMODEL_H
21 #define CONTACTLISTMODEL_H
22 
23 #include <QAbstractItemModel>
24 #include <QList>
25 
26 #include <licq/userid.h>
27 
28 namespace Licq
29 {
30 class User;
31 }
32 
33 // Allow UserId to be used in QVariant and QSet
34 Q_DECLARE_METATYPE(Licq::UserId)
35 uint qHash(const Licq::UserId& userId);
36 
37 namespace LicqQtGui
38 {
39 // Internal data classes for ContactList
40 class ContactUserData;
41 class ContactUser;
42 class ContactBar;
43 class ContactGroup;
44 
45 /**
46  * A QT-model representation of the licq contact list
47  *
48  * The contact list is presented as a two level tree structure with the groups
49  * as top level items and contacts located in the groups.
50  * Each group also contains separator bars as items together with the contacts.
51  * Special groups like "Invisible", "Online Notify" and "All Users" are present
52  * as normal groups but with special group IDs.
53  *
54  * Before this model can be used by a view a proxy model is needed to sort the
55  * list and to hide the special groups, ignored contacts and offline contacts
56  * depending on the view mode.
57  * The separator bars also needs to be sorted with the users for correct
58  * placement in the view or hidden if they are not used.
59  *
60  * To present a view with a custom list of contacts use a proxy model that
61  * filters items from the "All Users" special group.
62  *
63  * The classes holding the actual data for the contact list is located in
64  * contactlistdata.h as they are internal to the model and does not need to be
65  * visible to other classes.
66  */
67 class ContactListModel : public QAbstractItemModel
68 {
69   Q_OBJECT
70 
71 public:
72 
73   /**
74    * Roles added for non visual data needed to use the contact list
75    */
76   enum DataRole
77   {
78     ItemTypeRole = Qt::UserRole,        // Type of item (one of enum ItemType)
79     NameRole,                           // Item name (alias for UserItems)
80     SortPrefixRole,                     // Primary sort index (UserItems only)
81     SortRole,                           // Sort index (secondary index for UserItems, only index for GroupItems)
82     UnreadEventsRole,                   // Number of unread events
83     EventTypeRole,                      // Type of event (UserItems only)
84     GroupIdRole,                        // Id for groups, parent groups for other items
85     SubGroupRole,                       // Sub group type (one of enum SubGroupType) (UserItems and BarItems only)
86     UserCountRole,                      // Number of users in this group (GroupItems and BarItems only)
87     UserIdRole,                         // Id for user (UserItems only)
88     AccountIdRole,                      // Account id for user (UserItems only)
89     PpidRole,                           // Protocol id for user (UserItems only)
90     StatusRole,                         // Contact status (UserItems only)
91     ExtendedStatusRole,                 // Various status flags needed by the delegate (UserItems only)
92     UserIconRole,                       // User picture for use as icon (UserItems only)
93     CarAnimationRole,                   // Auto response read animation counter (UserItems only)
94     OnlineAnimationRole,                // Online animation counter (UserItems only)
95     EventAnimationRole,                 // Unread event animation counter (UserItems only)
96     VisibilityRole,                     // Item should always be visible
97   };
98 
99   /**
100    * Contact list item types (returned for ItemTypeRole)
101    */
102   enum ItemType
103   {
104     InvalidItem = 0,
105     GroupItem,
106     BarItem,
107     UserItem
108   };
109 
110   /**
111    * Separator bar types (returned for SubGroupRole)
112    */
113   enum SubGroupType
114   {
115     OnlineSubGroup = 0,
116     OfflineSubGroup,
117     NotInListSubGroup,
118   };
119 
120   /**
121    * Bit values for flags in ExtendedStatusRole data
122    * Mainly used for extended icons in delegate
123    */
124   enum ExtendedStatusBit
125   {
126     PhoneStatusBit = 0,
127     CellularStatusBit,
128     BirthdayStatusBit,
129     InvisibleStatusBit,
130     GpgKeyStatusBit,
131     GpgKeyEnabledStatusBit,
132     PhoneFollowMeActiveStatusBit,
133     PhoneFollowMeBusyStatusBit,
134     IcqPhoneActiveStatusBit,
135     IcqPhoneBusyStatusBit,
136     SharedFilesStatusBit,
137     TypingStatusBit,
138     SecureStatusBit,
139     CustomArStatusBit,
140     IgnoreStatusBit,
141     OnlineNotifyStatusBit,
142     NotInListStatusBit,
143     InvisibleListStatusBit,
144     VisibleListStatusBit,
145     NewUserStatusBit,
146     AwaitingAuthStatusBit,
147   };
148   static const unsigned long PhoneStatus                = 1 << PhoneStatusBit;
149   static const unsigned long CellularStatus             = 1 << CellularStatusBit;
150   static const unsigned long BirthdayStatus             = 1 << BirthdayStatusBit;
151   static const unsigned long InvisibleStatus            = 1 << InvisibleStatusBit;
152   static const unsigned long GpgKeyStatus               = 1 << GpgKeyStatusBit;
153   static const unsigned long GpgKeyEnabledStatus        = 1 << GpgKeyEnabledStatusBit;
154   static const unsigned long PhoneFollowMeActiveStatus  = 1 << PhoneFollowMeActiveStatusBit;
155   static const unsigned long PhoneFollowMeBusyStatus    = 1 << PhoneFollowMeBusyStatusBit;
156   static const unsigned long IcqPhoneActiveStatus       = 1 << IcqPhoneActiveStatusBit;
157   static const unsigned long IcqPhoneBusyStatus         = 1 << IcqPhoneBusyStatusBit;
158   static const unsigned long SharedFilesStatus          = 1 << SharedFilesStatusBit;
159   static const unsigned long TypingStatus               = 1 << TypingStatusBit;
160   static const unsigned long SecureStatus               = 1 << SecureStatusBit;
161   static const unsigned long CustomArStatus             = 1 << CustomArStatusBit;
162   static const unsigned long IgnoreStatus               = 1 << IgnoreStatusBit;
163   static const unsigned long OnlineNotifyStatus         = 1 << OnlineNotifyStatusBit;
164   static const unsigned long NotInListStatus            = 1 << NotInListStatusBit;
165   static const unsigned long InvisibleListStatus        = 1 << InvisibleListStatusBit;
166   static const unsigned long VisibleListStatus          = 1 << VisibleListStatusBit;
167   static const unsigned long NewUserStatus              = 1 << NewUserStatusBit;
168   static const unsigned long AwaitingAuthStatus         = 1 << AwaitingAuthStatusBit;
169 
170   // Constants for system groups
171   static const int SystemGroupOffset = 1000;
172   static const int OtherUsersGroupId = 0;
173 
174   // "Normal" system groups
175   // Note: These constants are used by Config::Contactlist and written to config file
176   //       Changing existing numbers will make old config files be read wrong
177   static const int OnlineNotifyGroupId                  = SystemGroupOffset + 0;
178   static const int VisibleListGroupId                   = SystemGroupOffset + 1;
179   static const int InvisibleListGroupId                 = SystemGroupOffset + 2;
180   static const int IgnoreListGroupId                    = SystemGroupOffset + 3;
181   static const int NewUsersGroupId                      = SystemGroupOffset + 4;
182   static const int AwaitingAuthGroupId                  = SystemGroupOffset + 5;
183   static const int NumSystemGroups                      = 6;
184   static const int LastSystemGroup                      = SystemGroupOffset + NumSystemGroups - 1;
185   static const int MostUsersGroupId                     = SystemGroupOffset + 100;
186 
187   // Not real group but need unique id in menus
188   static const int AllGroupsGroupId                     = SystemGroupOffset + 101;
189 
190   // Really all users. MostUsers above doesn't include Ignored users
191   static const int AllUsersGroupId                      = SystemGroupOffset + 200;
192 
193   /**
194    * Get display name for system groups
195    *
196    * @param groupId Id of a system group
197    * @return Name of group
198    */
199   static QString systemGroupName(int groupId);
200 
201   /**
202    * Constructor
203    * Will get the current list from the daemon and connect to the signal manager for updates.
204    *
205    * @param parent Parent object
206    */
207   ContactListModel(QObject* parent = 0);
208 
209   /**
210    * Destructor
211    */
212   virtual ~ContactListModel();
213 
214   /**
215    * Add a user to the contact list
216    *
217    * @param licqUser The user to add
218    */
219   void addUser(const Licq::User* licqUser);
220 
221   /**
222    * Remove a user from the contact list
223    *
224    * @param userId Licq user id
225    */
226   void removeUser(const Licq::UserId& userId);
227 
228   /**
229    * Get a model index for a group or user that other components can use.
230    *
231    * @param row Group index or user index within a group
232    * @param column A valid column for the model
233    * @param parent An existing group to get a user or an invalid model index to get a group
234    * @return A model index for the requested group or user
235    */
236   virtual QModelIndex index(int row, int column, const QModelIndex& parent) const;
237 
238   /**
239    * Get the parent of a model index
240    *
241    * @param index An index for a group or user
242    * @return A group index if a user is provided or an invalid index if a group is provided
243    */
244   virtual QModelIndex parent(const QModelIndex& index) const;
245 
246   /**
247    * Get the number of groups or users in a group
248    *
249    * @param parent An index for a group or an invalid index
250    * @return The number of users in the group or the number of groups
251    */
252   virtual int rowCount(const QModelIndex& parent) const;
253 
254   /**
255    * Get the number of columns for a user or a group
256    *
257    * @param parent An index in the model
258    * @return The number of columns
259    */
260   virtual int columnCount(const QModelIndex& parent = QModelIndex()) const;
261 
262   /**
263    * Get data for a user or a group
264    *
265    * @param index An index of a user or a group
266    * @param role The qt role to get data for
267    * @return The data for the given item and role
268    */
269   virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
270 
271   /**
272    * Get item flags for an item
273    *
274    * @param index An index of a user or group
275    * @return The item flags for the user or group
276    */
277   virtual Qt::ItemFlags flags(const QModelIndex& index) const;
278 
279   /**
280    * Get header titles for the contact list
281    *
282    * @param section A column in the list
283    * @param orientation Specify the horizontal or vertical header
284    * @param role The qt role to get data for
285    * @return Header data for the given column and role
286    */
287   virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
288 
289   /**
290    * Set item data
291    *
292    * @param index Index for the item to update
293    * @param value Value to set
294    * @param role Role to update
295    */
296   virtual bool setData(const QModelIndex& index, const QVariant& value, int role = ContactListModel::NameRole);
297 
298   /**
299    * Get index for a specific user
300    *
301    * @param userId Licq user id
302    * @param column The column to return an index for
303    * @return An index for the given user and column from the "All Users" group
304    */
305   QModelIndex userIndex(const Licq::UserId& userId, int column) const;
306 
307   /**
308    * Get index for a group to use as root item for a view
309    *
310    * @param id Id of the group
311    * @return An index for the group or an invalid index if the group does not exist
312    */
313   QModelIndex groupIndex(int id) const;
314 
315   /**
316    * Get index for the All Users group
317    *
318    * @return Index for "All Users" group
319    */
allUsersGroupIndex()320   QModelIndex allUsersGroupIndex() const
321   { return groupIndex(AllUsersGroupId); }
322 
323   /**
324    * Convenience function to get name of a group
325    *
326    * @param groupId Id of user group or system group
327    * @return Name of group
328    */
329   QString groupName(int groupId) const;
330 
331 public slots:
332   /**
333    * The daemon list has changed
334    *
335    * @param subSignal Sub signal telling what the change was
336    * @param argument Additional data, usage depend on sub signal type
337    * @param userId Id for affected user, if applicable
338    */
339   void listUpdated(unsigned long subSignal, int argument, const Licq::UserId& userId);
340 
341   /**
342    * The data for a user has changed in the daemon
343    *
344    * @param userId Id for affected user
345    * @param subSignal Sub signal telling what the change was
346    * @param argument Additional data, usage depend on sub signal type
347    */
348   void userUpdated(const Licq::UserId& userId, unsigned long subSignal, int argument);
349 
350   /**
351    * Reload the entire contact list from the daemon
352    */
353   void reloadAll();
354 
355 private slots:
356   /**
357    * Update everything related to GUI configuration
358    */
359   void configUpdated();
360 
361   /**
362    * The model data for a user has changed
363    * Will send a dataChanged signal for the user in all groups it is present
364    *
365    * @param user The user data object that has changed
366    */
367   void userDataChanged(const ContactUserData* user);
368 
369   /**
370    * The model data for a group has changed
371    * Will send a dataChanged signal for the group
372    *
373    * @param group The group object that has changed
374    */
375   void groupDataChanged(ContactGroup* group);
376 
377   /**
378    * The model data for a bar has changed
379    * Will send a dataChanged signal for the bar
380    *
381    * @param bar The bar object that has changed
382    * @param row The row of the bar in it's group
383    */
384   void barDataChanged(ContactBar* bar, int row);
385 
386   /**
387    * A group is about to add a user
388    * Will send beginInsertRow signal for the user
389    *
390    * @param group The group that's adding the user
391    * @param row The row the new user will have in the group
392    */
393   void groupBeginInsert(ContactGroup* group, int row);
394 
395   /**
396    * A group has finished adding a user
397    * Will send endInsertRow signal
398    */
399   void groupEndInsert();
400 
401   /**
402    * A group is about to remove a user
403    * Will send beginRemoveRow signal for the user
404    *
405    * @param group The group that's removing the user
406    * @param row The row of the user about to be removed
407    */
408   void groupBeginRemove(ContactGroup* group, int row);
409 
410   /**
411    * A group has finished removing a user
412    * Will send endRemoveRow signal
413    */
414   void groupEndRemove();
415 
416   /**
417    * Update the user membership in all groups
418    * This is triggered by the user data object when its group membership (may) have changed
419    *
420    * @param user The model user to update groups for
421    * @param licqUser The daemon user to get group membership from
422    */
423   void updateUserGroups(ContactUserData* user, const Licq::User* licqUser);
424 
425 private:
426   /**
427    * Connect signals for a newly created group object
428    *
429    * @param group Group object to connect signals from
430    */
431   void connectGroup(ContactGroup* group);
432 
433   /**
434    * Get the user object that represents an licq contact
435    *
436    * @param userId Licq user id
437    * @return The user object or NULL if it was not found
438    */
439   ContactUserData* findUser(const Licq::UserId& userId) const;
440 
441   /**
442    * Check if a user is member of a group and add/remove the user to/from the group if needed
443    *
444    * @param user The user to update
445    * @param group The group to check
446    * @param shouldBeMember True to add the user if missing or false to remove if member
447    */
448   void updateUserGroup(ContactUserData* user, ContactGroup* group, bool shouldBeMember);
449 
450   /**
451    * Get model row for a group
452    *
453    * @param group A group object
454    * @return Row in model or -1 if not found
455    */
456   int groupRow(ContactGroup* group) const;
457 
458   QList<ContactGroup*> myGroups;
459   ContactGroup* myAllUsersGroup;
460   QList<ContactUserData*> myUsers;
461   int myColumnCount;
462   bool myBlockUpdates;
463 };
464 
465 extern ContactListModel* gGuiContactList;
466 
467 } // namespace LicqQtGui
468 
469 #endif
470