User data optimization. Only changed profile images will be downloaded, except
[situare] / src / map / frienditemshandler.cpp
1 /*
2     Situare - A location system for Facebook
3     Copyright (C) 2010  Ixonos Plc. Authors:
4
5         Sami Rämö - sami.ramo@ixonos.com
6         Henri Lampela - henri.lampela@ixonos.com
7
8     Situare is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License
10     version 2 as published by the Free Software Foundation.
11
12     Situare is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with Situare; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
20     USA.
21 */
22
23
24 #include <QDebug>
25
26 #include "friendgroupitem.h"
27 #include "friendlocationitem.h"
28 #include "mapcommon.h"
29 #include "mapengine.h"
30 #include "mapscene.h"
31 #include "user/user.h"
32
33 #include "frienditemshandler.h"
34
35 FriendItemsHandler::FriendItemsHandler(MapScene *mapScene, QObject *parent)
36     : QObject(parent),
37       m_mapScene(mapScene),
38       m_zoomLevel(0)
39 {
40     qDebug() << __PRETTY_FUNCTION__;
41 }
42
43 void FriendItemsHandler::addFriendItem(User *friendData)
44 {
45     qDebug() << __PRETTY_FUNCTION__;
46
47     FriendLocationItem *item = new FriendLocationItem(friendData->userId());
48
49     item->setProfileImage(friendData->profileImage(), friendData->profileImageUrl());
50     item->setPos(MapEngine::convertLatLonToSceneCoordinate(friendData->coordinates()));
51     m_friendItems.append(item);
52     m_mapScene->addItem(item);
53
54     connect(item, SIGNAL(locationItemClicked(QList<QString>)),
55             this, SIGNAL(locationItemClicked(QList<QString>)));
56 }
57
58 void FriendItemsHandler::checkAllFriendsForCollidingFriends()
59 {
60     qDebug() << __PRETTY_FUNCTION__;
61
62     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
63     while (iter != m_friendItems.end()) {
64         // check only friends which are not part of group already
65         if (!(*iter)->isPartOfGroup()) {
66             checkFriendForCollidingFriends(*iter);
67         }
68         iter++;
69     }
70 }
71
72 void FriendItemsHandler::checkAllGroupsForCollidingFriends()
73 {
74     qDebug() << __PRETTY_FUNCTION__;
75
76     // loop through all groups
77     QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
78     while (iter != m_friendGroupItems.end()) {
79         checkGroupForCollidingFriends(*iter);
80         iter++;
81     }
82 }
83
84 void FriendItemsHandler::checkFriendForCollidingFriends(FriendLocationItem *item)
85 {
86     // checkGroupsForCollisions() is used for checking if groups collide with another
87     // groups or friend items, so this method doesn't have to check against groups
88
89     qDebug() << __PRETTY_FUNCTION__;
90
91     FriendGroupItem *group = 0;
92
93     // loop through all friend items
94     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
95     while (iter != m_friendItems.end()) {
96         // but don't check myself and friends which are already part of a group
97         if (item != *iter && !(*iter)->isPartOfGroup()) {
98             if (collides(item, *iter)) {
99                 if (!group) {
100                     group = new FriendGroupItem(item);
101                     m_mapScene->addItem(group);
102                     m_friendGroupItems.append(group);
103
104                     connect(group, SIGNAL(locationItemClicked(QList<QString>)),
105                             this, SIGNAL(locationItemClicked(QList<QString>)));
106                 }
107                 group->joinFriend(*iter);
108             }
109         }
110         iter++;
111     }
112 }
113
114 void FriendItemsHandler::checkGroupForCollidingFriends(FriendGroupItem *group)
115 {
116     qDebug() << __PRETTY_FUNCTION__;
117
118     // loop through all friend items
119     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
120     while (iter != m_friendItems.end()) {
121         // but don't check friends which are already part of a group
122         if (!(*iter)->isPartOfGroup()) {
123             if (collides(group, *iter)) {
124                 group->joinFriend(*iter);
125             }
126         }
127         iter++;
128     }
129 }
130
131 void FriendItemsHandler::checkGroupForCollidingGroups(FriendGroupItem *group)
132 {
133     qDebug() << __PRETTY_FUNCTION__;
134
135     // loop through all groups
136     QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
137     while (iter != m_friendGroupItems.end()) {
138         // but don't check myself
139         if (group != *iter) {
140             if (collides(group, *iter)) {
141                 (*iter)->mergeWithGroup(group);
142                 m_mapScene->removeItem(*iter);
143                 delete *iter;
144                 iter = m_friendGroupItems.erase(iter);
145             }
146             else {
147                 iter++;
148             }
149         }
150         else {
151             iter++;
152         }
153     }
154 }
155
156 bool FriendItemsHandler::collides(BaseLocationItem *item1, BaseLocationItem *item2)
157 {
158     QRect rect = item1->sceneTransformedBoundingRect(m_zoomLevel);
159
160     if (rect.intersects(item2->sceneTransformedBoundingRect(m_zoomLevel)))
161         return true;
162
163     if (rect.left() < MAP_MIN_PIXEL_X) {
164         QRect translated = rect.translated(MAP_PIXELS_X, 0);
165         if (translated.intersects(item2->sceneTransformedBoundingRect(m_zoomLevel)))
166             return true;
167     }
168
169     if (rect.right() > MAP_MAX_PIXEL_X) {
170         QRect translated = rect.translated(-MAP_PIXELS_X, 0);
171         if (translated.intersects(item2->sceneTransformedBoundingRect(m_zoomLevel)))
172             return true;
173     }
174
175     return false;
176 }
177
178 void FriendItemsHandler::deleteFriendItem(FriendLocationItem *item)
179 {
180     qDebug() << __PRETTY_FUNCTION__;
181
182     dropFriendFromAllGroups(item);
183     m_mapScene->removeItem(item);
184     delete item;
185 }
186
187 void FriendItemsHandler::dropFriendFromAllGroups(FriendLocationItem *item)
188 {
189     qDebug() << __PRETTY_FUNCTION__;
190
191     foreach (FriendGroupItem *group, m_friendGroupItems) {
192         group->dropFriend(item);
193     }
194 }
195
196 void FriendItemsHandler::dropOutOfGroupFriends()
197 {
198     qDebug() << __PRETTY_FUNCTION__;
199
200     // loop through all group items and drop friends which doesn't collide anymore
201     // delete group if possible
202     foreach (FriendGroupItem *group, m_friendGroupItems) {
203         if (group->dropFriends(m_zoomLevel)) {
204             m_friendGroupItems.removeAll(group);
205             m_mapScene->removeItem(group);
206             delete group;
207         }
208     }
209 }
210
211 void FriendItemsHandler::friendImageReady(User *user)
212 {
213    qDebug() << __PRETTY_FUNCTION__;
214
215    foreach (FriendLocationItem *friendItem, m_friendItems) {
216        if (user->userId() == friendItem->userId()) {
217            friendItem->setProfileImage(user->profileImage(), user->profileImageUrl());
218            break;
219        }
220    }
221 }
222
223 void FriendItemsHandler::friendListUpdated(QList<User *> &friendsList)
224 {
225     qDebug() << __PRETTY_FUNCTION__;
226
227     // loop through friend items and find matching friend data. If matching data
228     // is not found then remove item
229     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
230     while (iter != m_friendItems.end()) {
231         bool found = false;
232         foreach (User * friendData, friendsList) {
233             if (friendData->userId() == (*iter)->userId()) {
234                 found = true;
235                 break;
236             }
237         }
238         if (!found) {
239             // data for friend item was not found so item must be deleted
240             deleteFriendItem(*iter);
241             iter = m_friendItems.erase(iter);
242         }
243         else {
244             iter++;
245         }
246     }
247
248     // loop through new friend data, find matching friend items and update them, or add new items
249     // if old items are not found
250     foreach (User * friendData, friendsList) {
251         bool found = false;
252         foreach (FriendLocationItem *friendItem, m_friendItems) {
253             if (friendData->userId() == friendItem->userId()) {
254                 // friend item was found so update the data
255                 updateFriendItem(friendItem, friendData);
256                 found = true;
257                 break;
258             }
259         }
260         if (!found) {
261             // friend item was not found so new item must be added
262             addFriendItem(friendData);
263         }
264     }
265
266     refactorFriendItems(m_zoomLevel);
267 }
268
269 void FriendItemsHandler::mergeCollidingGroups()
270 {
271     qDebug() << __PRETTY_FUNCTION__;
272
273     // loop through all groups
274     QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
275     while (iter != m_friendGroupItems.end()) {
276         checkGroupForCollidingGroups(*iter);
277         iter++;
278     }
279 }
280
281 void FriendItemsHandler::refactorFriendItems(int zoomLevel)
282 {
283     qDebug() << __PRETTY_FUNCTION__;
284
285     m_zoomLevel = zoomLevel;
286
287     for (int repeat = 2; repeat > 0; repeat--) {
288         mergeCollidingGroups();
289         dropOutOfGroupFriends();
290         checkAllGroupsForCollidingFriends();
291         checkAllFriendsForCollidingFriends();
292     }
293 }
294
295 void FriendItemsHandler::updateFriendItem(FriendLocationItem *friendItem, User *friendData)
296 {
297     qDebug() << __PRETTY_FUNCTION__;
298
299     // update position
300     QPoint newPosition = MapEngine::convertLatLonToSceneCoordinate(friendData->coordinates());
301     if (friendItem->pos().toPoint() != newPosition)
302         friendItem->setPos(newPosition);
303
304     // update image
305     if (friendItem->profileImageUrl() != friendData->profileImageUrl())
306         friendItem->setProfileImage(friendData->profileImage(), friendData->profileImageUrl());
307 }