Added distance icons to FriendListItem.
[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     dropFriendFromAllGroups(item);
176     m_mapScene->removeItem(item);
177     delete item;
178 }
179
180 void FriendItemsHandler::dropFriendFromAllGroups(FriendLocationItem *item)
181 {
182     qDebug() << __PRETTY_FUNCTION__;
183
184     foreach (FriendGroupItem *group, m_friendGroupItems) {
185         group->dropFriend(item);
186     }
187 }
188
189 void FriendItemsHandler::dropOutOfGroupFriends()
190 {
191     qDebug() << __PRETTY_FUNCTION__;
192
193     // loop through all group items and drop friends which doesn't collide anymore
194     // delete group if possible
195     foreach (FriendGroupItem *group, m_friendGroupItems) {
196         if (group->dropFriends(m_zoomLevel)) {
197             m_friendGroupItems.removeAll(group);
198             m_mapScene->removeItem(group);
199             delete group;
200         }
201     }
202 }
203
204 void FriendItemsHandler::friendListUpdated(QList<User *> &friendsList)
205 {
206     qDebug() << __PRETTY_FUNCTION__;
207
208     // loop through friend items and find matching friend data. If matching data
209     // is not found then remove item
210     QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
211     while (iter != m_friendItems.end()) {
212         bool found = false;
213         foreach (User * friendData, friendsList) {
214             if (friendData->userId() == (*iter)->userId()) {
215                 found = true;
216                 break;
217             }
218         }
219         if (!found) {
220             // data for friend item was not found so item must be deleted
221             deleteFriendItem(*iter);
222             iter = m_friendItems.erase(iter);
223         }
224         else {
225             iter++;
226         }
227     }
228
229     // loop through new friend data, find matching friend items and update them, or add new items
230     // if old items are not found
231     foreach (User * friendData, friendsList) {
232         bool found = false;
233         foreach (FriendLocationItem *friendItem, m_friendItems) {
234             if (friendData->userId() == friendItem->userId()) {
235                 // friend item was found so update the data
236                 updateFriendItem(friendItem, friendData);
237                 found = true;
238                 break;
239             }
240         }
241         if (!found) {
242             // friend item was not found so new item must be added
243             addFriendItem(friendData);
244         }
245     }
246
247     refactorFriendItems(m_zoomLevel);
248 }
249
250 void FriendItemsHandler::refactorFriendItems(int zoomLevel)
251 {
252     qDebug() << __PRETTY_FUNCTION__;
253
254     m_zoomLevel = zoomLevel;
255
256     mergeCollidingGroups();
257     dropOutOfGroupFriends();
258     checkAllGroupsForCollidingFriends();
259     checkAllFriendsForCollidingFriends();
260 }
261
262 void FriendItemsHandler::updateFriendItem(FriendLocationItem *friendItem, User *friendData)
263 {
264     qDebug() << __PRETTY_FUNCTION__;
265
266     // update position
267     QPoint newPosition = MapEngine::convertLatLonToSceneCoordinate(friendData->coordinates());
268     if (friendItem->pos().toPoint() != newPosition)
269         friendItem->setPos(newPosition);
270
271     // update image
272     if (friendItem->profileImageUrl() != friendData->profileImageUrl())
273         friendItem->setProfileImage(friendData->profileImage(), friendData->profileImageUrl());
274 }