1
/*****************************************************************************
2
* Copyright (C) 2009-2013 by Savoir-Faire Linux *
3
* Author : Mathieu Leduc-Hamel <mathieu.leduc-hamel@savoirfairelinux.com> *
4
* Emmanuel Lepage Valle <emmanuel.lepage@savoirfairelinux.com > *
6
* This program is free software; you can redistribute it and/or modify *
7
* it under the terms of the GNU General Public License as published by *
8
* the Free Software Foundation; either version 3 of the License, or *
9
* (at your option) any later version. *
11
* This program 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. *
16
* You should have received a copy of the GNU General Public License *
17
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
18
****************************************************************************/
21
#include "calltreeitem.h"
24
#include <QtCore/QStringList>
25
#include <QtCore/QMimeData>
26
#include <QtCore/QTimer>
27
#include <QtGui/QPainter>
28
#include <QtGui/QClipboard>
29
#include <QtGui/QApplication>
30
#include <QtGui/QWidget>
31
#include <QtGui/QLabel>
32
#include <QtGui/QSpacerItem>
33
#include <QtGui/QHBoxLayout>
34
#include <QtGui/QVBoxLayout>
35
#include <QtGui/QDragEnterEvent>
36
#include <QtGui/QDragMoveEvent>
37
#include <QtGui/QDragLeaveEvent>
38
#include <QtGui/QPushButton>
39
#include <QtGui/QTreeWidgetItem>
40
#include <QtGui/QFontMetrics>
41
#include <QtGui/QPalette>
42
#include <QtGui/QBitmap>
43
#include <QtGui/QGraphicsOpacityEffect>
49
#include <KStandardDirs>
53
#include "lib/sflphone_const.h"
54
#include "lib/contact.h"
56
#include "klib/configurationskeleton.h"
59
#include "klib/akonadibackend.h"
60
#include "widgets/translucentbuttons.h"
64
const char * CallTreeItem::callStateIcons[12] = {ICON_INCOMING, ICON_RINGING, ICON_CURRENT, ICON_DIALING, ICON_HOLD, ICON_FAILURE, ICON_BUSY, ICON_TRANSFER, ICON_TRANSF_HOLD, "", "", ICON_CONFERENCE};
67
CallTreeItem::CallTreeItem(QWidget *parent)
68
: QWidget(parent), m_pItemCall(0), m_Init(false),m_pBtnConf(0), m_pBtnTrans(0),m_pTimer(0),m_pPeerL(0),m_pIconL(0),m_pCallNumberL(0),m_pSecureL(0),m_pCodecL(0),m_pHistoryPeerL(0)
69
, m_pTransferPrefixL(0),m_pTransferNumberL(0),m_pElapsedL(0),m_Height(0),m_pContact(0),m_pDepartment(0),m_pOrganisation(0),m_pEmail(0),m_IsDragged(false),m_CurrentStyle(-1)
70
, m_pCallNumberLE(nullptr)
72
setContentsMargins(0,0,0,0);
73
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
74
connect(AkonadiBackend::getInstance(),SIGNAL(collectionChanged()),this,SLOT(updated()));
78
CallTreeItem::~CallTreeItem()
80
if (m_pIconL) delete m_pIconL ;
81
if (m_pPeerL) delete m_pPeerL ;
82
if (m_pCallNumberL) delete m_pCallNumberL ;
83
if (m_pCallNumberLE) delete m_pCallNumberLE ;
84
if (m_pTransferPrefixL) delete m_pTransferPrefixL ;
85
if (m_pTransferNumberL) delete m_pTransferNumberL ;
86
if (m_pCodecL) delete m_pCodecL ;
87
if (m_pSecureL) delete m_pSecureL ;
88
if (m_pHistoryPeerL) delete m_pHistoryPeerL ;
89
if (m_pElapsedL) delete m_pElapsedL ;
90
if (m_pTimer) delete m_pTimer ;
93
///Hack to render a proxy delegate of the widget when it is dragged
94
void CallTreeItem::setDragged(bool drag)
99
///Better support alternative color scheme
100
void CallTreeItem::setTextColor(int style)
102
if (m_CurrentStyle != style || m_CurrentStyle == -1) {
103
QString color = palette().text().color().name();
104
if (style & QStyle::State_Selected) {
105
color = palette().highlightedText().color().name();
109
m_pPeerL->setStyleSheet("color:"+color);
112
if (m_pCallNumberL) {
113
m_pCallNumberL->setStyleSheet("color:"+color);
115
if (m_pTransferPrefixL) {
116
m_pTransferPrefixL->setStyleSheet("color:"+color);
118
if (m_pTransferNumberL) {
119
m_pTransferNumberL->setStyleSheet("color:"+color);
122
m_pCodecL->setStyleSheet("color:"+color);
125
m_pSecureL->setStyleSheet("color:"+color);
127
if (m_pHistoryPeerL) {
128
m_pHistoryPeerL->setStyleSheet("color:"+color);
131
m_pElapsedL->setStyleSheet("color:"+color);
134
m_pDepartment->setStyleSheet("color:"+color);
136
if (m_pOrganisation) {
137
m_pOrganisation->setStyleSheet("color:"+color);
140
m_pEmail->setStyleSheet("color:"+color);
142
m_CurrentStyle = style;
146
/*****************************************************************************
150
****************************************************************************/
152
///Return the call item
153
Call* CallTreeItem::call() const
158
///Return the real size hint
159
QSize CallTreeItem::sizeHint () const
162
if (m_pItemCall && !m_pItemCall->isConference()) {
164
QFontMetrics fm(m_pPeerL->font());
165
height += fm.height();
167
if ( m_pCallNumberL ) {
168
QFontMetrics fm(m_pCallNumberL->font());
169
height += fm.height();
172
QFontMetrics fm(m_pSecureL->font());
173
height += fm.height();
176
QFontMetrics fm(m_pCodecL->font());
177
height += fm.height();
179
if ( m_pDepartment ) {
180
QFontMetrics fm(m_pDepartment->font());
181
height += fm.height();
183
if ( m_pOrganisation) {
184
QFontMetrics fm(m_pOrganisation->font());
185
height += fm.height();
188
QFontMetrics fm(m_pEmail->font());
189
height += fm.height();
192
// else if (m_pItemCall && m_pItemCall->isConference()) {
199
if (ConfigurationSkeleton::limitMinimumRowHeight() && height < (uint)ConfigurationSkeleton::minimumRowHeight() && !(m_pItemCall && m_pItemCall->isConference())) {
200
height = (uint)ConfigurationSkeleton::minimumRowHeight()-11;
203
if (height != m_Height) {
204
((CallTreeItem*)this)->m_Height=height;
205
if (m_pIconL && height) {
206
m_pIconL->setMinimumSize(m_Height,m_Height);
207
m_pIconL->setMaximumSize(m_Height,m_Height);
208
m_pIconL->resize(m_Height,m_Height);
209
((CallTreeItem*)this)->updated();
212
return QSize(0,height);
215
///Hack to use a proxy painter when dragging
216
bool CallTreeItem::isDragged()
221
/*****************************************************************************
225
****************************************************************************/
227
///Apply rounder mask
228
QPixmap& CallTreeItem::applyMask(QPixmap& pxm)
230
QRect pxRect = pxm.rect();
231
QBitmap mask(pxRect.size());
232
QPainter customPainter(&mask);
233
customPainter.setRenderHint(QPainter::Antialiasing, true);
234
customPainter.fillRect(pxRect,"white");
235
customPainter.setBackground(QColor("black"));
236
customPainter.setBrush(QColor("black"));
237
customPainter.drawRoundedRect(pxRect,5,5);
243
void CallTreeItem::setCall(Call *call)
249
setAcceptDrops(true);
251
if (m_pItemCall->isConference()) {
253
QColor textColor = palette().text().color();
254
QColor baseColor = palette().base().color().name();
255
baseColor.setBlue (baseColor.blue() + (textColor.blue() -baseColor.blue()) *0.6);
256
baseColor.setRed (baseColor.red() + (textColor.red() -baseColor.red()) *0.6);
257
baseColor.setGreen(baseColor.green()+ (textColor.green()-baseColor.green())*0.6);
259
m_pHistoryPeerL = new QLabel( i18n("<b>Conference</b>"),this );
260
m_pIconL = new QLabel( " ",this );
261
QHBoxLayout* mainLayout = new QHBoxLayout();
262
mainLayout->setAlignment ( Qt::AlignLeft | Qt::AlignVCenter );
263
m_pHistoryPeerL->setStyleSheet("color:"+baseColor.name());
265
m_pIconL->setAlignment ( Qt::AlignLeft | Qt::AlignVCenter );
267
mainLayout->addWidget( m_pIconL );
268
mainLayout->addWidget( m_pHistoryPeerL );
270
setLayout(mainLayout);
273
m_pIconL->setVisible(true);
274
m_pIconL->setStyleSheet("margin-left:7px;");
275
m_pHistoryPeerL->setVisible(true);
279
m_pTransferPrefixL = new QLabel(i18n("Transfer to:"));
280
m_pTransferNumberL = new QLabel(" ");
281
m_pElapsedL = new QLabel(" ");
282
QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
284
m_pTransferPrefixL->setVisible(false);
285
m_pTransferNumberL->setVisible(false);
287
QHBoxLayout* mainLayout = new QHBoxLayout();
288
mainLayout->setContentsMargins ( 3, 1, 2, 1);
289
mainLayout->setAlignment ( Qt::AlignLeft | Qt::AlignVCenter );
291
m_pBtnConf = new TranslucentButtons(this);
292
m_pBtnConf->setVisible(false);
293
m_pBtnConf->setParent(this);
294
m_pBtnConf->setText(i18n("Conference"));
295
m_pBtnConf->setPixmap(new QImage(KStandardDirs::locate("data","sflphone-client-kde/confBlackWhite.png")));
296
connect(m_pBtnConf,SIGNAL(dataDropped(QMimeData*)),this,SLOT(conversationEvent(QMimeData*)));
298
m_pBtnTrans = new TranslucentButtons(this);
299
m_pBtnTrans->setText(i18n("Transfer"));
300
m_pBtnTrans->setVisible(false);
301
m_pBtnTrans->setPixmap(new QImage(KStandardDirs::locate("data","sflphone-client-kde/transferarraw.png")));
302
connect(m_pBtnTrans,SIGNAL(dataDropped(QMimeData*)),this,SLOT(transferEvent(QMimeData*)));
304
m_pElapsedL->setStyleSheet("margin-right:5px;");
306
mainLayout->setSpacing(4);
307
QVBoxLayout* descr = new QVBoxLayout();
308
QHBoxLayout* transfer = new QHBoxLayout();
309
transfer->setContentsMargins(0,0,0,0);
310
descr->setMargin (1);
311
descr->setSpacing (1);
312
transfer->setMargin (0);
313
transfer->setSpacing(0);
314
descr->setContentsMargins(0,0,0,0);
315
descr->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
317
if (ConfigurationSkeleton::displayCallIcon()) {
318
m_pIconL = new QLabel(" ");
319
mainLayout->addWidget(m_pIconL);
322
if(ConfigurationSkeleton::displayCallPeer()/*&& ! m_pItemCall->getPeerName().isEmpty()*/) {
323
m_pPeerL = new QLabel(" ");
324
m_pPeerL->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
325
m_pPeerL->setText(m_pItemCall->getPeerName());
326
if (m_pItemCall->getPeerName().isEmpty()) {
327
m_pPeerL->setVisible(true);
329
descr->addWidget(m_pPeerL);
332
if (ConfigurationSkeleton::displayCallNumber()) {
333
m_pCallNumberL = new QLabel(m_pItemCall->getPeerPhoneNumber());
334
descr->addWidget(m_pCallNumberL);
335
m_pCallNumberLE = new KLineEdit(this);
336
m_pCallNumberLE->setStyleSheet("background-color:transparent;border:0px;");
337
m_pCallNumberLE->setVisible(false);
338
descr->addWidget(m_pCallNumberLE);
339
int pos = m_pCallNumberLE->cursorPosition();
340
m_pCallNumberLE->setText(m_pItemCall->getCallNumber());
341
m_pCallNumberLE->setCursorPosition(pos);
342
connect(m_pCallNumberLE,SIGNAL(textChanged(QString)),this,SLOT(numberChanged(QString)));
345
if (ConfigurationSkeleton::displayCallSecure()) {
346
m_pSecureL = new QLabel(" ",this);
347
descr->addWidget(m_pSecureL);
350
if (ConfigurationSkeleton::displayCallCodec()) {
351
m_pCodecL = new QLabel(" ",this);
352
descr->addWidget(m_pCodecL);
355
if (ConfigurationSkeleton::displayCallOrganisation()) {
356
m_pOrganisation = new QLabel(" ",this);
357
descr->addWidget(m_pOrganisation);
360
if (ConfigurationSkeleton::displayCallDepartment()) {
361
m_pDepartment = new QLabel(" ",this);
362
descr->addWidget(m_pDepartment);
365
if (ConfigurationSkeleton::displayCallEmail()) {
366
m_pEmail = new QLabel(" ",this);
367
descr->addWidget(m_pEmail);
370
transfer->addWidget(m_pTransferPrefixL);
371
transfer->addWidget(m_pTransferNumberL);
372
descr->addLayout(transfer);
373
descr->addItem(verticalSpacer);
374
mainLayout->addLayout(descr);
375
mainLayout->addWidget(m_pElapsedL);
377
QVBoxLayout* mainLayout2 = new QVBoxLayout();
378
mainLayout2->setContentsMargins(0,0,0,0);
379
mainLayout2->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
380
mainLayout2->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
381
mainLayout2->addItem(mainLayout);
382
mainLayout2->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
383
setLayout(mainLayout2);
385
connect(m_pItemCall, SIGNAL(changed()), this, SLOT(updated()));
391
void CallTreeItem::updated()
393
call_state state = m_pItemCall->getState();
394
bool recording = m_pItemCall->getRecording();
396
m_pContact = m_pItemCall->getContact();
400
m_pPeerL->setText("<b>"+m_pContact->getFormattedName()+"</b>");
401
m_pPeerL->setVisible(true);
403
if (m_pOrganisation) {
404
m_pOrganisation->setText(m_pContact->getOrganization());
407
m_pDepartment->setText(m_pContact->getDepartment());
410
m_pEmail->setText(m_pContact->getPreferredEmail());
414
if( m_pPeerL && ! m_pItemCall->getPeerName().trimmed().isEmpty()) {
415
m_pPeerL->setText("<b>"+m_pItemCall->getPeerName()+"</b>");
416
m_pPeerL->setVisible(true);
418
else if (m_pPeerL && !(state == CALL_STATE_RINGING || state == CALL_STATE_DIALING)) {
419
m_pPeerL->setText(i18n("<b>Unknown</b>"));
420
m_pPeerL->setVisible(true);
423
m_pPeerL->setText(QString());
424
m_pPeerL->setVisible(false);
428
if(state != CALL_STATE_OVER) {
429
if(m_pIconL && state == CALL_STATE_CURRENT && recording) {
430
if (m_pContact && !m_pItemCall->isConference() && m_pContact->getPhoto()) {
431
QPixmap pxm = (*m_pContact->getPhoto()).scaled(QSize(m_Height,m_Height));
433
QPainter painter(&pxm);
434
QPixmap status(ICON_CURRENT_REC);
435
painter.drawPixmap(pxm.width()-status.width(),pxm.height()-status.height(),status);
436
m_pIconL->setPixmap(pxm);
438
else if (!m_pItemCall->isConference()) {
439
m_pIconL->setPixmap(QPixmap(ICON_CURRENT_REC));
443
QString str = QString(callStateIcons[state]);
444
if (m_pContact && !m_pItemCall->isConference() && m_pContact->getPhoto()) {
445
QPixmap pxm = (*m_pContact->getPhoto()).scaled(QSize(m_Height,m_Height));
447
QPainter painter(&pxm);
449
painter.drawPixmap(pxm.width()-status.width(),pxm.height()-status.height(),status);
450
m_pIconL->setPixmap(pxm);
452
else if (!m_pItemCall->isConference()) {
453
m_pIconL->setPixmap(QPixmap(str));
457
if (m_pIconL && m_pItemCall->isConference()) {
458
m_pIconL->setPixmap(QPixmap(KStandardDirs::locate("data","sflphone-client-kde/conf-small.png")));
461
bool transfer = state == CALL_STATE_TRANSFERRED || state == CALL_STATE_TRANSF_HOLD;
462
if (m_pTransferPrefixL && m_pTransferNumberL) {
463
m_pTransferPrefixL->setVisible(transfer);
464
m_pTransferNumberL->setVisible(transfer);
467
if(!transfer && m_pTransferNumberL) {
468
m_pTransferNumberL->setText("");
470
if (m_pTransferNumberL)
471
m_pTransferNumberL->setText(m_pItemCall->getTransferNumber());
474
m_pCallNumberL->setText(m_pItemCall->getPeerPhoneNumber());
476
if(m_pCallNumberLE && state == CALL_STATE_DIALING) {
477
int pos = m_pCallNumberLE->cursorPosition();
478
if (m_pCallNumberLE->text()!=m_pItemCall->getCallNumber())
479
m_pCallNumberLE->setText(m_pItemCall->getCallNumber());
480
m_pCallNumberLE->setCursorPosition(pos);
481
m_pCallNumberLE->setVisible(true);
482
m_pCallNumberL->setVisible(false);
486
m_pCallNumberLE->setVisible(false);
488
m_pCallNumberL->setVisible(true);
490
m_pCodecL->setText("Codec: "+m_pItemCall->getCurrentCodecName());
491
if (m_pSecureL && m_pItemCall->isSecure())
492
m_pSecureL->setText("⚷");
496
//kDebug() << "Updating item of call of state OVER. Doing nothing.";
498
if (state == CALL_STATE_TRANSFERRED || state == CALL_STATE_TRANSF_HOLD) {
499
kDebug() << "Transferring";
500
emit askTransfer(m_pItemCall);
505
if (state == CALL_STATE_HOLD && m_pItemCall && !m_pItemCall->isConference()) {
506
QGraphicsOpacityEffect* opacityEffect = new QGraphicsOpacityEffect;
507
setGraphicsEffect(opacityEffect);
510
setGraphicsEffect(nullptr);
513
//Start/Stop the elapsed time label
514
if ((state == CALL_STATE_CURRENT || state == CALL_STATE_HOLD || state == CALL_STATE_TRANSFERRED) && !m_pTimer) {
515
m_pTimer = new QTimer(this);
516
m_pTimer->setInterval(1000);
517
connect(m_pTimer,SIGNAL(timeout()),this,SLOT(incrementTimer()));
520
else if (m_pTimer && (state == CALL_STATE_OVER || state == CALL_STATE_ERROR || state == CALL_STATE_FAILURE )) {
525
///Support the user case where the user want to edit the number after entering it
526
void CallTreeItem::numberChanged(QString number)
528
if (m_pItemCall->getCallNumber() != number) {
529
m_pItemCall->setCallNumber(number);
534
/*****************************************************************************
538
****************************************************************************/
540
///Called when a drag and drop occur while the item have not been dropped yet
541
void CallTreeItem::dragEnterEvent ( QDragEnterEvent *e )
543
kDebug() << "Drag enter";
544
if (SFLPhone::model()->getIndex(this) && SFLPhone::model()->getIndex(this)->parent() &&
545
SFLPhone::model()->getIndex(e->mimeData()->data( MIME_CALLID))->parent() &&
546
SFLPhone::model()->getIndex(this)->parent() == SFLPhone::model()->getIndex(e->mimeData()->data( MIME_CALLID))->parent() &&
547
e->mimeData()->data( MIME_CALLID) != SFLPhone::model()->getCall(this)->getCallId()) {
548
m_pBtnTrans->setVisible(true);
549
emit showChilds(this);
553
else if (e->mimeData()->hasFormat( MIME_CALLID) && m_pBtnTrans && (e->mimeData()->data( MIME_CALLID) != m_pItemCall->getCallId())) {
554
m_pBtnConf->setVisible(true);
555
m_pBtnTrans->setVisible(true);
556
emit showChilds(this);
564
///The cursor move on a potential drag event
565
void CallTreeItem::dragMoveEvent ( QDragMoveEvent *e )
567
QPoint pos = e->pos();
568
m_pBtnConf->setHoverState (pos.x() < rect().width()/2);
569
m_pBtnTrans->setHoverState(pos.x() > rect().width()/2);
574
///A potential drag event is cancelled
575
void CallTreeItem::dragLeaveEvent ( QDragLeaveEvent *e )
577
QTimer::singleShot(500, this, SLOT(hide()));
578
kDebug() << "Drag leave";
583
///Something is being dropped
584
void CallTreeItem::dropEvent(QDropEvent *e)
586
kDebug() << "Drop accepted";
587
QTimer::singleShot(500, this, SLOT(hide()));
589
if (e->pos().x() < rect().width()/2) {
590
emit conversationDropEvent(m_pItemCall,(QMimeData*)e->mimeData());
593
emit transferDropEvent(m_pItemCall,(QMimeData*)e->mimeData());
595
//emit dataDropped((QMimeData*)e->mimeData());
598
void CallTreeItem::resizeEvent ( QResizeEvent *e )
601
m_pBtnConf->setMinimumSize ( width()/2-15,height()-4 );
602
m_pBtnConf->setMaximumSize ( width()/2-15,height()-4 );
603
m_pBtnTrans->setMinimumSize( width()/2-15,height()-4 );
604
m_pBtnTrans->setMaximumSize( width()/2-15,height()-4 );
605
m_pBtnTrans->move ( width()/2+10,m_pBtnTrans->y()+2);
606
m_pBtnConf->move ( 10,m_pBtnConf->y()+2 );
612
void CallTreeItem::mouseDoubleClickEvent(QMouseEvent *e )
614
if (m_pItemCall && m_pItemCall->isConference() && m_pItemCall->getState() == CALL_STATE_CONFERENCE_HOLD) {
616
m_pItemCall->actionPerformed(CALL_ACTION_HOLD);
623
///Called when a call is dropped on transfer
624
void CallTreeItem::transferEvent(QMimeData* data)
626
emit transferDropEvent(m_pItemCall,data);
629
///Called when a call is dropped on conversation
630
void CallTreeItem::conversationEvent(QMimeData* data)
632
kDebug() << "Proxying conversation mime";
633
emit conversationDropEvent(m_pItemCall,data);
637
void CallTreeItem::copy()
639
kDebug() << "Copying contact";
640
QMimeData* mimeData = new QMimeData();
641
QString numbers(m_pItemCall->getPeerName()+": "+m_pItemCall->getPeerPhoneNumber());
642
QString numbersHtml("<b>"+m_pItemCall->getPeerName()+"</b><br />"+m_pItemCall->getPeerPhoneNumber());
643
mimeData->setData( MIME_CALLID , m_pItemCall->getCallId().toUtf8() );
644
mimeData->setData( "text/plain" , numbers.toUtf8() );
645
mimeData->setData( "text/html" , numbersHtml.toUtf8() );
646
QClipboard* clipboard = QApplication::clipboard();
647
clipboard->setMimeData(mimeData);
650
///Called when the overlay need to be hidden
651
void CallTreeItem::hide()
654
m_pBtnConf->setVisible (false);
655
m_pBtnTrans->setVisible(false);
659
///Increment the current call elapsed time label
660
void CallTreeItem::incrementTimer()
662
int nsec = QDateTime::fromTime_t(m_pItemCall->getStartTimeStamp().toInt()).secsTo( QDateTime::currentDateTime () );
664
m_pElapsedL->setText(QString("%1:%2:%3 ").arg((nsec%(3600*24))/3600).arg(((nsec%(3600*24))%3600)/60,2,10,QChar('0')).arg(((nsec%(3600*24))%3600)%60,2,10,QChar('0')));
666
m_pElapsedL->setText(QString("%1:%2 ").arg(nsec/60,2,10,QChar('0')).arg(nsec%60,2,10,QChar('0')));