Modified FriendListItem font metrics calculation.
[situare] / src / ui / friendlistitem.cpp
1 /*
2    Situare - A location system for Facebook
3    Copyright (C) 2010  Ixonos Plc. Authors:
4
5        Jussi Laitinen - jussi.laitinen@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 #include <QVBoxLayout>
23 #include <QPushButton>
24 #include <QPainter>
25 #include <QDebug>
26 #include <QPaintEvent>
27 #include <QLabel>
28 #include <QPixmap>
29 #include <QFormLayout>
30 #include <QSpacerItem>
31 #include <QStylePainter>
32 #include <math.h>
33
34 #include "friendlistitem.h"
35 #include "../user/user.h"
36 #include "imagebutton.h"
37 #include "../common.h"
38
39 const int ITEM_WIDTH = 368;
40 const int ICON_HEIGHT = 24;     ///< Icon height
41 const int ICON_WIDTH = 24;       ///< Icon width
42 const int IMAGE_HEIGHT = 64; ///< Friend image height
43 const int IMAGE_WIDTH = 64;  ///< Friend image width
44 const int ITEM_MIN_HEIGHT = 141; ///< Minimum height for item
45 const int MARGIN = 5;   ///< Icon margin
46 const int MOUSE_PRESS_AREA_WIDTH = 20;  ///< Area width for item height toggling
47 const int MOUSE_PRESS_AREA_HEIGHT = 20; ///< Area height for item height toggling
48
49 /**
50 * @var NAME_LABEL_MAX_WIDTH
51 *
52 * @brief Name label's maximum width
53 */
54 const int NAME_LABEL_MAX_WIDTH = ITEM_WIDTH - 3*MARGIN - IMAGE_WIDTH;
55
56 /**
57 * @var LABEL_MAX_WIDTH
58 *
59 * @brief All label's maximum width
60 */
61 const int LABEL_MAX_WIDTH = ITEM_WIDTH - 3 * MARGIN - IMAGE_WIDTH - MARGIN - ICON_WIDTH - MARGIN;
62
63 const int WALK_DISTANCE = 5;        ///< Walk distance limit for distance icon
64 const int CAR_DISTANCE = 500;       ///< Car distance limit for distance icon
65 const int AEROPLANE_DISTANCE = 5000;///< Aeroplane distance limit for distance icon
66
67 FriendListItem::FriendListItem()
68     : m_expanded(false)
69     , m_user(0)
70 {
71     qDebug() << __PRETTY_FUNCTION__;
72
73     setData(Qt::SizeHintRole, QSize(ITEM_WIDTH, ITEM_MIN_HEIGHT));
74 }
75
76 void FriendListItem::calculateTextRects()
77 {
78     QPixmap p = QPixmap(10, 10);
79     QPainter painter(&p);
80     painter.setFont(NOKIA_FONT_SMALL);
81     QFontMetrics smallFontMetrics = painter.fontMetrics();
82
83     QRect distanceRect = smallFontMetrics.boundingRect(m_distanceText);
84     QRect statusTextRect = smallFontMetrics.boundingRect(m_user->note());
85     QRect updatedRect = smallFontMetrics.boundingRect(m_user->timestamp());
86     QRect locationRect = smallFontMetrics.boundingRect(m_user->address());
87
88
89     qDebug() << m_shortenedStatusText;
90
91     int statusTextFactor = statusTextRect.width() / LABEL_MAX_WIDTH;
92     statusTextFactor++;
93     m_statusTextRect = QRect(0, 0, LABEL_MAX_WIDTH, 24 * statusTextFactor);
94     int updatedFactor = updatedRect.width() / LABEL_MAX_WIDTH;
95     updatedFactor++;
96     m_updatedRect = QRect(0, 0, LABEL_MAX_WIDTH, 24 * updatedFactor);
97     int locationFactor = locationRect.width() / LABEL_MAX_WIDTH;
98     locationFactor++;
99     m_locationRect = QRect(0, 0, LABEL_MAX_WIDTH, 24 * locationFactor);
100
101     qDebug() << statusTextRect;
102     qDebug() << updatedRect;
103     qDebug() << locationRect;
104
105     qDebug() << m_statusTextRect;
106     qDebug() << m_updatedRect;
107     qDebug() << m_locationRect;
108
109     qWarning() << statusTextRect.width() << statusTextRect.height();
110     qWarning() << m_statusTextRect.width() << m_statusTextRect.height();
111
112     m_expandedHeight = ITEM_MIN_HEIGHT + ((statusTextFactor + updatedFactor + locationFactor - 3) *
113                                           ICON_HEIGHT);
114
115     setData(Qt::SizeHintRole + 1, distanceRect);
116 }
117
118 QPointF FriendListItem::coordinates()
119 {
120     qWarning() << __PRETTY_FUNCTION__;
121
122     return m_user->coordinates();
123 }
124
125 void FriendListItem::setId(const QString &id)
126 {
127     qDebug() << __PRETTY_FUNCTION__;
128
129     ListItem::setId(id);
130 }
131
132 QString FriendListItem::id() const
133 {
134     return ListItem::id();
135 }
136
137 void FriendListItem::setUserData(User *user)
138 {
139     qDebug() << __PRETTY_FUNCTION__;
140
141     if(user) {
142         m_user = user;
143
144         setId(m_user->userId());
145
146         if (!m_user->profileImage().isNull())
147             setData(Qt::DecorationRole, m_user->profileImage());
148
149         QString unit;
150         double value;
151         m_user->distance(value, unit);
152         m_distanceText = QString::number(value) + " " + unit;
153         setData(Qt::UserRole + 3, m_distanceText);
154         setDistanceIcon(value, unit);
155
156         shortenTexts();
157         calculateTextRects();
158         setText(false);
159     }
160 }
161
162 void FriendListItem::setDistanceIcon(double value, const QString &unit)
163 {
164     QPixmap distanceImage;
165
166     if ((unit == "m") || (value < WALK_DISTANCE))
167         distanceImage.load(":/res/images/walk_icon_gray.png");
168     else if (value < CAR_DISTANCE)
169         distanceImage.load(":/res/images/car_icon_gray.png");
170     else if (value < AEROPLANE_DISTANCE)
171         distanceImage.load(":/res/images/aeroplane_icon_gray.png");
172     else
173         distanceImage.load(":/res/images/rocket_icon_gray.png");
174
175     qDebug() << __PRETTY_FUNCTION__ << distanceImage.isNull();
176
177     setData(Qt::UserRole + 4, distanceImage);
178 }
179
180 void FriendListItem::shortenTexts()
181 {
182     qDebug() << __PRETTY_FUNCTION__;
183
184     QPixmap p = QPixmap(10, 10);
185
186     QPainter painter(&p);
187     painter.setFont(NOKIA_FONT_NORMAL);
188     QFontMetrics nameLabelMetrics = painter.fontMetrics();
189     painter.setFont(NOKIA_FONT_SMALL);
190     QFontMetrics otherLabelsMetrics = painter.fontMetrics();
191
192     QString name = m_user->name();
193     QString updated = m_user->timestamp();
194     QString statusText = m_user->note();
195     QString location = m_user->address();
196
197     int nameIndex = name.indexOf('\n');
198     int updatedIndex = updated.indexOf('\n');
199     int statusTextIndex = statusText.indexOf('\n');
200     int locationIndex = location.indexOf('\n');
201
202     if (nameIndex > 0) {
203         name.truncate(nameIndex);
204         name.append("...");
205     }
206     if (updatedIndex > 0) {
207         updated.truncate(updatedIndex);
208         updated.append("...");
209     }
210     if (statusTextIndex > 0) {
211         statusText.truncate(statusTextIndex);
212         statusText.append("...");
213     }
214     if (locationIndex > 0) {
215         location.truncate(locationIndex);
216         location.append("...");
217     }
218
219     int distanceLabelWidth = otherLabelsMetrics.width(m_distanceText) + 5;
220     m_shortenedName = nameLabelMetrics.elidedText(name, Qt::ElideRight, NAME_LABEL_MAX_WIDTH
221                                                   - distanceLabelWidth);
222     m_shortenedStatusText = otherLabelsMetrics.elidedText(statusText, Qt::ElideRight,
223                                                           LABEL_MAX_WIDTH);
224     m_shortenedUpdated = otherLabelsMetrics.elidedText(updated, Qt::ElideRight, LABEL_MAX_WIDTH);
225     m_shortenedLocation = otherLabelsMetrics.elidedText(location, Qt::ElideRight, LABEL_MAX_WIDTH);
226
227     setData(Qt::SizeHintRole + 4, QRect(0, 0, distanceLabelWidth, 24));
228
229     qWarning() << m_shortenedStatusText;
230 }
231
232 QString FriendListItem::elideText(int width, const QString &text)
233 {
234     QFontMetrics otherLabelsMetrics = QFontMetrics(NOKIA_FONT_SMALL);
235     int widthSum = 0;
236     QString t = text;
237
238     for (int i = 0; i < text.size(); ++i) {
239         QChar c = text.at(i);
240
241         widthSum += otherLabelsMetrics.width(c);
242
243         if (widthSum > width) {
244             t.truncate(i - 3);
245             t.append("...");
246             break;
247         }
248     }
249
250     return t;
251 }
252
253 void FriendListItem::setText(bool expanded)
254 {
255     qDebug() << __PRETTY_FUNCTION__;
256
257     setData(Qt::DisplayRole, m_shortenedName);
258
259     if (expanded) {
260         setData(Qt::UserRole, m_user->note());
261         setData(Qt::UserRole + 1, m_user->timestamp());
262         setData(Qt::UserRole + 2, m_user->address());
263
264         setData(Qt::SizeHintRole + 2, m_statusTextRect);
265         setData(Qt::SizeHintRole + 3, m_updatedRect);
266         setData(Qt::SizeHintRole + 4, m_locationRect);
267     }
268     else {
269         setData(Qt::UserRole, m_shortenedStatusText);
270         setData(Qt::UserRole + 1, m_shortenedUpdated);
271         setData(Qt::UserRole + 2, m_shortenedLocation);
272
273         setData(Qt::SizeHintRole + 2, QRect(0, 0, LABEL_MAX_WIDTH, ICON_HEIGHT));
274         setData(Qt::SizeHintRole + 3, QRect(0, 0, LABEL_MAX_WIDTH, ICON_HEIGHT));
275         setData(Qt::SizeHintRole + 4, QRect(0, 0, LABEL_MAX_WIDTH, ICON_HEIGHT));
276     }
277 }
278
279 void FriendListItem::toggleHeight()
280 {
281     if (m_expanded) {
282         m_expanded = false;
283         setText(false);
284         setData(Qt::SizeHintRole, QSize(ITEM_WIDTH, ITEM_MIN_HEIGHT));
285     }
286     else {
287         m_expanded = true;
288         setText(true);
289         setData(Qt::SizeHintRole, QSize(ITEM_WIDTH, m_expandedHeight));
290     }
291 }
292
293 void FriendListItem::setExpanded(bool expanded)
294 {
295     m_expanded = expanded;
296
297     if (m_expanded) {
298         setText(true);
299         setData(Qt::SizeHintRole, QSize(ITEM_WIDTH, m_expandedHeight));
300     }
301     else {
302         setText(false);
303         setData(Qt::SizeHintRole, QSize(ITEM_WIDTH, ITEM_MIN_HEIGHT));
304     }
305 }