Added connections from FriendGroupItem to MapEngine. Added
[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     connect(item, SIGNAL(friendItemClicked(QString)),
53             this, SIGNAL(friendItemClicked(QString)));
54 }
55
56 void FriendItemsHandler::checkAllFriendsForCollidingFriends()
57 {
58     qDebug() << __PRETTY_FUNCTION__;
59
60     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
61     while (iter != m_friendItems.end()) {
62         // check only friends which are not part of group already
63         if (!(*iter)->isPartOfGroup()) {
64             checkFriendForCollidingFriends(*iter);
65         }
66         iter++;
67     }
68 }
69
70 void FriendItemsHandler::checkAllGroupsForCollidingFriends()
71 {
72     qDebug() << __PRETTY_FUNCTION__;
73
74     // loop through all groups
75     QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
76     while (iter != m_friendGroupItems.end()) {
77         checkGroupForCollidingFriends(*iter);
78         iter++;
79     }
80 }
81
82 void FriendItemsHandler::checkFriendForCollidingFriends(FriendLocationItem *item)
83 {
84     // checkGroupsForCollisions() is used for checking if groups collide with another
85     // groups or friend items, so this method doesn't have to check against groups
86
87     qDebug() << __PRETTY_FUNCTION__;
88
89     FriendGroupItem *group = 0;
90     QRect itemSceneRect = item->sceneTransformedBoundingRect(m_zoomLevel);
91
92     // loop through all friend items
93     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
94     while (iter != m_friendItems.end()) {
95         // but don't check myself and friends which are already part of a group
96         if (item != *iter && !(*iter)->isPartOfGroup()) {
97             if (itemSceneRect.intersects((*iter)->sceneTransformedBoundingRect(m_zoomLevel))) {
98                 if (!group) {
99                     group = new FriendGroupItem(item);
100                     m_mapScene->addItem(group);
101                     m_friendGroupItems.append(group);
102
103                     connect(group, SIGNAL(friendGroupItemClicked(QList<QString>)),
104                             this, SIGNAL(friendGroupItemClicked(QList<QString>)));
105                 }
106                 group->joinFriend(*iter);
107             }
108         }
109         iter++;
110     }
111 }
112
113 void FriendItemsHandler::checkGroupForCollidingFriends(FriendGroupItem *group)
114 {
115     qDebug() << __PRETTY_FUNCTION__;
116
117     QRect groupSceneRect = group->sceneTransformedBoundingRect(m_zoomLevel);
118
119     // loop through all friend items
120     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
121     while (iter != m_friendItems.end()) {
122         // but don't check friends which are already part of a group
123         if (!(*iter)->isPartOfGroup()) {
124             if (groupSceneRect.intersects((*iter)->sceneTransformedBoundingRect(m_zoomLevel))) {
125                 group->joinFriend(*iter);
126             }
127         }
128         iter++;
129     }
130 }
131
132 void FriendItemsHandler::checkGroupForCollidingGroups(FriendGroupItem *group)
133 {
134     qDebug() << __PRETTY_FUNCTION__;
135
136     QRect groupSceneRect = group->sceneTransformedBoundingRect(m_zoomLevel);
137
138     // loop through all groups
139     QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
140     while (iter != m_friendGroupItems.end()) {
141         // but don't check myself
142         if (group != *iter) {
143             if (groupSceneRect.intersects((*iter)->sceneTransformedBoundingRect(m_zoomLevel))) {
144                 (*iter)->mergeWithGroup(group);
145                 m_mapScene->removeItem(*iter);
146                 delete *iter;
147                 iter = m_friendGroupItems.erase(iter);
148             }
149             else {
150                 iter++;
151             }
152         }
153         else {
154             iter++;
155         }
156     }
157 }
158
159 void FriendItemsHandler::mergeCollidingGroups()
160 {
161     qDebug() << __PRETTY_FUNCTION__;
162
163     // loop through all groups
164     QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
165     while (iter != m_friendGroupItems.end()) {
166         checkGroupForCollidingGroups(*iter);
167         iter++;
168     }
169 }
170
171 void FriendItemsHandler::deleteFriendItem(FriendLocationItem *item)
172 {
173     qDebug() << __PRETTY_FUNCTION__;
174
175     disconnect(item, SIGNAL(friendItemClicked(QString)),
176             this, SIGNAL(friendItemClicked(QString)));
177
178     dropFriendFromAllGroups(item);
179     m_mapScene->removeItem(item);
180     delete item;
181 }
182
183 void FriendItemsHandler::dropFriendFromAllGroups(FriendLocationItem *item)
184 {
185     qDebug() << __PRETTY_FUNCTION__;
186
187     foreach (FriendGroupItem *group, m_friendGroupItems) {
188         group->dropFriend(item);
189     }
190 }
191
192 void FriendItemsHandler::dropOutOfGroupFriends()
193 {
194     qDebug() << __PRETTY_FUNCTION__;
195
196     // loop through all group items and drop friends which doesn't collide anymore
197     // delete group if possible
198     foreach (FriendGroupItem *group, m_friendGroupItems) {
199         if (group->dropFriends(m_zoomLevel)) {
200             m_friendGroupItems.removeAll(group);
201             m_mapScene->removeItem(group);
202             delete group;
203         }
204     }
205 }
206
207 void FriendItemsHandler::friendListUpdated(QList<User *> &friendsList)
208 {
209     qDebug() << __PRETTY_FUNCTION__;
210
211     // loop through friend items and find matching friend data. If matching data
212     // is not found then remove item
213     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
214     while (iter != m_friendItems.end()) {
215         bool found = false;
216         foreach (User * friendData, friendsList) {
217             if (friendData->userId() == (*iter)->userId()) {
218                 found = true;
219                 break;
220             }
221         }
222         if (!found) {
223             // data for friend item was not found so item must be deleted
224             deleteFriendItem(*iter);
225             iter = m_friendItems.erase(iter);
226         }
227         else {
228             iter++;
229         }
230     }
231
232     // loop through new friend data, find matching friend items and update them, or add new items
233     // if old items are not found
234     foreach (User * friendData, friendsList) {
235         bool found = false;
236         foreach (FriendLocationItem *friendItem, m_friendItems) {
237             if (friendData->userId() == friendItem->userId()) {
238                 // friend item was found so update the data
239                 updateFriendItem(friendItem, friendData);
240                 found = true;
241                 break;
242             }
243         }
244         if (!found) {
245             // friend item was not found so new item must be added
246             addFriendItem(friendData);
247         }
248     }
249
250     refactorFriendItems(m_zoomLevel);
251 }
252
253 void FriendItemsHandler::refactorFriendItems(int zoomLevel)
254 {
255     qDebug() << __PRETTY_FUNCTION__;
256
257     m_zoomLevel = zoomLevel;
258
259     mergeCollidingGroups();
260     dropOutOfGroupFriends();
261     checkAllGroupsForCollidingFriends();
262     checkAllFriendsForCollidingFriends();
263 }
264
265 void FriendItemsHandler::updateFriendItem(FriendLocationItem *friendItem, User *friendData)
266 {
267     qDebug() << __PRETTY_FUNCTION__;
268
269     // update position
270     QPoint newPosition = MapEngine::convertLatLonToSceneCoordinate(friendData->coordinates());
271     if (friendItem->pos().toPoint() != newPosition)
272         friendItem->setPos(newPosition);
273
274     // update image
275     if (friendItem->profileImageUrl() != friendData->profileImageUrl())
276         friendItem->setProfileImage(friendData->profileImage(), friendData->profileImageUrl());
277 }