Initial commit
[tietoopcom] / src / TocUi / TocContactListWidget.cpp
1 /** \file TocContactListWidget.cpp
2  * \brief Implementation of TocContactListWidget class
3  * 
4  * Tieto Open Communicator - Client for the Telepathy communications framework.
5  * Copyright (c) 2010, Tieto Corporation
6  *
7  * All rights reserved.
8  * Redistribution and use in source and binary forms, with or without modification,
9  * are permitted provided that the following conditions are met:
10  *
11  *      Redistributions of source code must retain the above copyright notice,
12  *      this list of conditions and the following disclaimer.
13  *      Redistributions in binary form must reproduce the above copyright notice,
14  *      this list of conditions and the following disclaimer in the documentation
15  *      and/or other materials provided with the distribution.
16  *      Neither the name of the Tieto Corporation nor the names of its contributors 
17  *      may be used to endorse or promote products derived from this software without
18  *      specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  * 
30  */
31
32 #include <QMenu>
33 #include <QMouseEvent>
34 #include <QMessageBox>
35 #include <QPainter>
36 #include <QDebug>
37 #include <QCommonStyle>
38
39 #include "TocContactListWidget"
40 #include "IconProvider"
41 #include "uidefs.h"
42 #include "macros.h"
43
44 Q_DECLARE_METATYPE(TocContact*);
45
46 TocContactListWidget::TocContactListWidget(QWidget * parent)
47         : QListWidget(parent),
48         _editAction(tr("Edit"), 0),
49         _removeAction(tr("Remove"), 0)
50 {
51         setStyle(new QCommonStyle());
52         setFlow( QListView::LeftToRight );
53         setViewMode( QListView::IconMode );
54         setGridSize( QSize(109, 113) );
55         setIconSize(QSize(68,68));
56         setDragEnabled( false );
57         setVerticalScrollMode( QAbstractItemView::ScrollPerItem );
58         setWrapping( true );
59         setWordWrap( true );
60         setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
61         setResizeMode( QListView::Adjust );
62         QFont currentFont(font());
63         currentFont.setPixelSize(14);
64         setFont(currentFont);
65
66         // 'Add contact' item
67         (new QListWidgetItem(QIcon(":/addcontact.png"), tr("Add Contact"), this))->setFlags( Qt::ItemIsEnabled );
68
69         TocSettings *pSettings = TocSettings::getInstance();
70         
71         QList<TocContact*> *pContactsList = pSettings->contactList();
72         
73         IconProvider *pIconProvider = IconProvider::getInstance();
74         
75         foreach(TocContact* pContact, *pContactsList)
76         {
77                 if(!pContact->customIconPath.isEmpty())
78                         pIconProvider->setCustomIcon(pContact);
79
80                 QString displayedText;
81                 if(pContact->name.isEmpty())
82                         displayedText = pContact->uid;
83                 else
84                         displayedText = pContact->name;
85
86                 QListWidgetItem* pItem = new QListWidgetItem(displayedText, this);
87                 
88                 pItem->setData( Qt::DecorationRole, *(pIconProvider->matchIcon(pContact)) );
89                 pItem->setData( ContactRole, QVariant::fromValue(pContact));
90                 
91                 pItem->setFlags( Qt::ItemIsEnabled );
92                         
93                 addItem(pItem);
94         }
95         
96         // Load logo image
97         _logo = new QImage(":/tieto-logo.png");
98
99         sortItems(Alphabetical); // Sort items alphabetically
100
101         //CONNECT SIGNALS
102         CONNECT(&_editAction, SIGNAL(triggered(bool)), this, SLOT(onEditRequested()));
103         CONNECT(&_removeAction, SIGNAL(triggered(bool)), this, SLOT(onRemoveRequested()));
104         CONNECT(this, SIGNAL(logoColorChanged(QColor)), this, SLOT(changeLogoColor(QColor)));
105 }
106
107 TocContactListWidget::~TocContactListWidget() {
108         delete _logo;
109 }
110
111 void TocContactListWidget::onNewContactList(const TocContactList& contactList) {
112
113         QListWidgetItem* pAddContactItem = takeItem( 0 );
114         clear();
115
116         IconProvider *pIconProvider = IconProvider::getInstance();      
117         
118         foreach(TocContact* pContact, contactList)
119         {
120                 QString displayedText;
121                 if(pContact->name.isEmpty())
122                         displayedText = pContact->uid;
123                 else
124                         displayedText = pContact->name;
125
126                 QListWidgetItem* pNewItem = new QListWidgetItem(displayedText, this);
127
128                 pNewItem->setData(DecorationRole, *(pIconProvider->matchIcon(pContact)));
129                 pNewItem->setData(ContactRole, QVariant::fromValue(pContact));
130
131                 //make it not selectable
132                 pNewItem->setFlags( Qt::ItemIsEnabled );
133         }
134
135         insertItem(0, pAddContactItem);
136
137         sortItems(Alphabetical);
138 }
139
140 void TocContactListWidget::onContactPresenceUpdate(const QString& uid, Presence presence, const QString& description) {
141
142         IconProvider *pIconProvider = IconProvider::getInstance();
143
144         QListWidgetItem* pItem = findItem(uid);
145
146         if( !pItem )
147                 return;
148         
149         TocContact *pContact = pItem->data(ContactRole).value<TocContact*>();
150
151         pContact->presence = presence;
152         pContact->description = description;
153
154         pItem->setData(Qt::DecorationRole, *(pIconProvider->matchIcon(pContact)));
155 }
156
157 QString TocContactListWidget::getDisplayedName(const QString& uid) {
158         
159         QListWidgetItem* pItem = findItem(uid);
160         
161         if(pItem)
162                 return pItem->data(DisplayRole).toString();
163         
164         return QString();
165 }
166
167 void TocContactListWidget::clearStatuses() {
168
169         IconProvider *pIconProvider = IconProvider::getInstance();
170
171         for(int i = 1; i < count(); ++i) {
172
173                 QListWidgetItem* pItem = this->item(i);
174
175                 TocContact *pContact = pItem->data(ContactRole).value<TocContact*>();
176                 pContact->presence = Offline;
177 //              pContact->description = ""; // TODO: Clear description also? Some clients remember last descr
178
179                 pItem->setData(Qt::DecorationRole, *(pIconProvider->matchIcon(pContact)));
180         }
181 }
182
183 Q_DECLARE_METATYPE(QListWidgetItem*);
184
185
186 void TocContactListWidget::mouseDoubleClickEvent(QMouseEvent *event)
187 {
188         QPoint itemPos = event->pos();
189         
190         QListWidgetItem* pClickedItem = itemAt(itemPos);
191         if(!pClickedItem)
192                 return;
193
194         QListWidgetItem* pAddContactItem = item(0);
195
196         if(pAddContactItem == pClickedItem)
197                 emit contactInfoRequest(0);
198         else
199         {
200                 TocContact *pContact = pClickedItem->data(ContactRole).value<TocContact*>();
201                 QString uid = pContact->uid;
202                 QString name = pClickedItem->text(); // Using Text instead of pContact->name because the latter can be empty
203
204                 emit triggered(uid, name, pContact->presence);
205         }
206 }
207
208 void TocContactListWidget::contextMenuEvent(QContextMenuEvent *event)
209 {
210         QPoint itemPos = event->pos();
211         
212         QListWidgetItem* pClickedItem = itemAt(itemPos);
213                 
214         //if it applies to Add Contact then skip it
215         QListWidgetItem* pAddContactItem = item(0);
216         if(pClickedItem == pAddContactItem || pClickedItem == 0) return;
217                                 
218         //create menu
219         QMenu contextMenu(this);
220
221         contextMenu.setStyleSheet(
222                         "QMenu {"
223                                 "margin-left: 0px;"
224                                 "margin-top: 0px;"
225                                 "margin-right: 0px;"
226                                 "margin-bottom: 0px;"
227                                 "font: bold 16px;"
228                         "}"
229                         "QMenu::item {"
230                                 "margin-left: 0px;"
231                                 "margin-top: 0px;"
232                                 "margin-right: 0px;"
233                                 "margin-bottom: 0px;"
234                                 "padding: 0px 10px 0px 30px;"
235                                 "min-height: 60px;"
236                                 "min-width: 80px;"
237                                 "max-height: 60px;"
238                                 "border-radius: 5px;"
239                         "}");
240         
241         _editAction.setData(QVariant::fromValue(pClickedItem));
242         _removeAction.setData(QVariant::fromValue(pClickedItem));
243         
244         contextMenu.addAction(&_editAction);
245         contextMenu.addAction(&_removeAction);
246         
247         contextMenu.exec(mapToGlobal(itemPos));
248 }
249
250
251 void TocContactListWidget::onEditRequested() {
252
253         //extract data from sender
254         QObject* pObject = QObject::sender();
255         _pCurrentEditedItem = (static_cast<QAction*>(pObject))->data().value<QListWidgetItem*>();
256
257         _currentEditedContact = *(_pCurrentEditedItem->data(ContactRole).value<TocContact*>());
258
259         if(_pCurrentEditedItem)
260                 emit contactInfoRequest(_pCurrentEditedItem);
261 }
262
263 void TocContactListWidget::onRemoveRequested() {
264         
265         //extract data from sender
266         QObject* pObject = QObject::sender();
267         QListWidgetItem* pItem = (static_cast<QAction*>(pObject))->data().value<QListWidgetItem*>();
268         
269         if(!pItem) return;
270         
271         TocContact* pContact = pItem->data(ContactRole).value<TocContact*>();
272         QMessageBox::StandardButton clickedButton = QMessageBox::question(this, "TEC", tr("Are you sure you want to remove\ncontact: %1").arg(pContact->name), QMessageBox::Yes | QMessageBox::No);
273         if(clickedButton == QMessageBox::No) return;
274         
275         //Remove from list
276         if(takeItem(row(pItem)))
277         {
278                 //Removing went successfuly
279                 //Lets notify the engine
280                 QString uid = pItem->data(ContactRole).value<TocContact*>()->uid;
281                 
282                 emit removedContact(uid);
283         }
284         
285 }
286
287 void TocContactListWidget::commitAdd(QListWidgetItem* pNewItem) {
288         
289         TocContact *pNewContact = pNewItem->data(ContactRole).value<TocContact*>();
290         QString uid = pNewContact->uid;
291         
292         //ensure that item is not already present
293         if(findItem(uid))
294                 return;
295         
296         //add to list
297         addItem(pNewItem);
298
299         // sortItems now
300         sortItems(Alphabetical);
301         
302         //notify engine
303         emit addContact(pNewContact);
304 }
305
306 void TocContactListWidget::commitEdit(QListWidgetItem* pEditedItem) {
307
308         TocContact* pEditedContact = pEditedItem->data(ContactRole).value<TocContact*>();
309
310         QString newUid = pEditedContact->uid;
311         QString newName = pEditedContact->name;
312
313         QString oldUid = _currentEditedContact.uid;
314         QString oldName = _currentEditedContact.name;
315         
316         if( newUid != oldUid )//If Uid has changed ensure that item with such uid is not already present
317                 if( findItem(newUid, pEditedItem) ) {
318                         pEditedContact->uid = oldUid;
319                         return;
320                 }
321
322         if( newName != oldName )
323                 if(newName.isEmpty())
324                         emit displayedNameChanged( newUid, newUid );
325                 else
326                         emit displayedNameChanged( newUid, newName );
327
328         // sort items now
329         sortItems(Alphabetical);
330
331         IconProvider *pIconProvider = IconProvider::getInstance();
332         pEditedItem->setData(Qt::DecorationRole, *(pIconProvider->matchIcon(pEditedContact)));
333
334         emit editContact(pEditedContact, oldUid);
335 }
336
337 QListWidgetItem* TocContactListWidget::findItem(const QString& requestedUid, const QListWidgetItem* pExcludedItem) {
338         
339         QString uid;
340         
341         for(int i=1; i < count(); i++)
342         {
343                 QListWidgetItem* pRequestedItem = item(i);
344                 if( pExcludedItem == pRequestedItem )
345                         continue;
346
347                 uid = pRequestedItem->data(ContactRole).value<TocContact*>()->uid;
348                 
349                 if(uid == requestedUid)
350                         return pRequestedItem;
351         }
352                 
353         return 0;
354 }
355
356 void TocContactListWidget::sortItems(SortOrder order) {
357         
358         QListWidgetItem* pAddContactItem = 0;
359         
360         switch(order) {
361         
362                 case Alphabetical:
363                         pAddContactItem = takeItem( 0 );
364                         QListWidget::sortItems(Qt::AscendingOrder);
365                         insertItem(0, pAddContactItem);
366                         break;
367
368                 case ByStatus:
369                         // TODO: sort by status
370                         break;
371
372                 case None:
373                         // TODO: disable sorting and enable drag & drop events
374                         break;
375         }
376 }
377
378 void TocContactListWidget::paintEvent(QPaintEvent * event) {
379         Q_UNUSED(event);
380
381         QPainter painter(this->viewport());
382         painter.drawImage(this->width() - _logo->width(), this->height() - _logo->height(), *_logo);
383
384         QListWidget::paintEvent(event);
385 }
386
387 QColor TocContactListWidget::logoColor() const {
388         return _logoColor;
389 }
390
391 void TocContactListWidget::setLogoColor(QColor color) {
392         _logoColor = color;
393         emit logoColorChanged( _logoColor );
394 }
395
396 void TocContactListWidget::changeLogoColor(QColor color) {
397         QColor lColor;
398         QString colorName = color.name();
399
400         if( colorName == "#0000ff" )
401               lColor = LOGO_BLUE;
402         else if( colorName == "#add8e6" )
403               lColor = LOGO_LIGHT_BLUE;
404         else if( colorName == "#008000" )
405               lColor = LOGO_GREEN;
406         else if( colorName == "#ffa500" )
407               lColor = LOGO_ORANGE;
408         else if( colorName == "#ff0000" )
409               lColor = LOGO_RED;
410         else
411               lColor = LOGO_RED;
412
413         for(int i = 0; i < _logo->width(); ++i)
414                 for(int j = 0; j < _logo->height(); ++j)
415                          if(_logo->pixel(i, j) >> 24)
416                                 _logo->setPixel(i,j, lColor.rgb());
417 }