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