1
/****************************************************************************
2
* Copyright (C) 2009-2014 by Savoir-Faire Linux *
3
* Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
5
* This library is free software; you can redistribute it and/or *
6
* modify it under the terms of the GNU Lesser General Public *
7
* License as published by the Free Software Foundation; either *
8
* version 2.1 of the License, or (at your option) any later version. *
10
* This library is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13
* Lesser General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17
***************************************************************************/
18
#include "historydelegate.h"
21
#include <QtGui/QPainter>
22
#include <QtGui/QApplication>
23
#include <QtGui/QBitmap>
24
#include <QtGui/QSortFilterProxyModel>
25
#include <QtGui/QTreeView>
26
#include <QtCore/QFile>
27
#include <QtCore/QDebug>
30
#include <KColorScheme>
33
#include <KStandardDirs>
36
#include <lib/historymodel.h>
37
#include <lib/contact.h>
38
#include <lib/callmodel.h>
39
#include <lib/phonenumber.h>
40
#include "klib/kcfg_settings.h"
41
#include "widgets/playeroverlay.h"
42
#include "dialpaddelegate.h"
43
#include "../widgets/tips/ringingtip.h"
44
#include "klib/tipanimationwrapper.h"
45
#include "lib/visitors/pixmapmanipulationvisitor.h"
47
static const char* icnPath[4] = {
48
/* INCOMING */ ICON_HISTORY_INCOMING,
49
/* OUTGOING */ ICON_HISTORY_OUTGOING,
50
/* MISSED */ ICON_HISTORY_MISSED ,
55
#pragma GCC diagnostic ignored "-Wmissing-braces"
56
TypedStateMachine< const char* , Call::State > callStateIcons = {{ICON_INCOMING, ICON_RINGING, ICON_CURRENT, ICON_DIALING, ICON_HOLD, ICON_FAILURE, ICON_BUSY, ICON_TRANSFER, ICON_TRANSF_HOLD, "", "", ICON_CONFERENCE}};
58
HistoryDelegate::HistoryDelegate(QTreeView* parent) : QStyledItemDelegate(parent),m_pParent(parent),m_pDelegatedropoverlay(nullptr),m_AnimationWrapper(nullptr),m_pRingingTip(nullptr)
60
connect(CallModel::instance(),SIGNAL(callStateChanged(Call*,Call::State)),this,SLOT(slotStopRingingAnimation()));
63
QSize HistoryDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
66
const QSize sh = QStyledItemDelegate::sizeHint(option, index);
67
const QFontMetrics fm(QApplication::font());
68
int lineHeight = fm.height()+2;
70
const Call::State currentState = static_cast<Call::State>(index.data(Call::Role::CallState).toInt());
71
int minimumRowHeight = (currentState == Call::State::OVER)?48:(ConfigurationSkeleton::limitMinimumRowHeight()?ConfigurationSkeleton::minimumRowHeight():0);
72
if (currentState == Call::State::OVER)
75
rowCount = ConfigurationSkeleton::displayCallPeer()
76
+ ConfigurationSkeleton::displayCallNumber ()
77
+ ConfigurationSkeleton::displayCallSecure ()
78
+ ConfigurationSkeleton::displayCallCodec ()
79
+ ConfigurationSkeleton::displayCallOrganisation ()
80
+ ConfigurationSkeleton::displayCallDepartment ()
81
+ ConfigurationSkeleton::displayCallEmail ();
83
return QSize(sh.width(),((rowCount*lineHeight)<minimumRowHeight?minimumRowHeight:(rowCount*lineHeight)) + 4);
86
void HistoryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
88
Q_ASSERT(index.isValid());
90
const bool isBookmark = index.data(Call::Role::IsBookmark).toBool();
93
int iconHeight = option.rect.height() -4;
94
//Paint the "selected" or "hover" backgrounds
95
// if (option.state & QStyle::State_Selected || option.state & QStyle::State_MouseOver) {
96
QStyleOptionViewItem opt2 = option;
97
QPalette pal = option.palette;
98
pal.setBrush(QPalette::Text,QColor(0,0,0,0));
99
pal.setBrush(QPalette::HighlightedText,QColor(0,0,0,0));
101
QStyledItemDelegate::paint(painter,opt2,index);
104
painter->setPen(QApplication::palette().color(QPalette::Active,(option.state & QStyle::State_Selected)?QPalette::HighlightedText:QPalette::Text));
105
const Call::State currentState = (Call::State) index.data(Call::Role::CallState).toInt();
106
if (currentState == Call::State::HOLD)
107
painter->setOpacity(0.70);
109
const PhoneNumber* n = qvariant_cast<PhoneNumber*>(index.data(Call::Role::PhoneNu));
110
QPixmap pxm = n?PixmapManipulationVisitor::instance()->callPhoto(n,QSize(iconHeight+4,iconHeight+4),isBookmark).value<QPixmap>():QPixmap(QSize(iconHeight+4,iconHeight+4));
112
//Handle history with recording
113
if (index.data(Call::Role::HasRecording).toBool() && currentState == Call::State::OVER) {
114
QObject* obj= qvariant_cast<Call*>(index.data(Call::Role::Object));
115
Call* call = nullptr;
117
call = qobject_cast<Call*>(obj);
118
if (call && QFile::exists(call->recordingPath())) {
119
QPainter painter(&pxm);
120
QPixmap status(KStandardDirs::locate("data","sflphone-client-kde/voicemail.png"));
121
status=status.scaled(QSize(24,24));
122
painter.drawPixmap(pxm.width()-status.width(),pxm.height()-status.height(),status);
123
if (m_pParent && m_pParent->indexWidget(index) == nullptr) {
124
auto button = new PlayerOverlay(call,nullptr);
125
button->setCall(call);
126
m_pParent->setIndexWidget(index,button);
131
else if (!isBookmark && (index.data(Call::Role::Historystate).toInt() != (int)Call::LegacyHistoryState::NONE || currentState != Call::State::OVER) && ConfigurationSkeleton::displayHistoryStatus()) {
132
QPainter painter(&pxm);
133
QPixmap status((currentState==Call::State::OVER)?icnPath[index.data(Call::Role::Historystate).toInt()]:callStateIcons[currentState]);
134
if (!status.isNull()) {
135
status=status.scaled(QSize(24,24));
136
painter.drawPixmap(pxm.width()-status.width(),pxm.height()-status.height(),status);
139
if (currentState != Call::State::OVER && index.data(Call::Role::IsRecording).toBool()) {
140
const static QPixmap record(KStandardDirs::locate("data","sflphone-client-kde/record.png"));
144
painter->drawPixmap(option.rect.x()+option.rect.width()-record.width()-2,option.rect.y()+option.rect.height()-record.height()-2,record);
146
int x_offset((iconHeight-pxm.width())/2),y_offset((iconHeight-pxm.height())/2);
147
painter->drawPixmap(option.rect.x()+4+x_offset,option.rect.y()+y_offset+(option.rect.height()-iconHeight)/2,pxm);
149
QFont font = painter->font();
150
QFontMetrics fm(font);
151
int currentHeight = option.rect.y()+fm.height()+2;
152
//BEGIN history fields
153
if (currentState == Call::State::OVER || isBookmark) { //History/Bookmarks
154
const QPen textCol = (option.state & QStyle::State_Selected) ? Qt::white : QApplication::palette().color(QPalette::Disabled,QPalette::Text);
155
font.setWeight(QFont::DemiBold);
157
painter->setFont(font);
158
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Qt::DisplayRole).toString());
159
font.setWeight(QFont::Normal);
160
painter->setFont(font);
161
painter->setPen(textCol);
162
currentHeight +=fm.height();
164
//Draw INCOMING/OUTGOING/MISSED
166
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Call::Role::FormattedDate).toString());
167
currentHeight +=fm.height();
168
const static QPixmap* callPxm = nullptr;
170
callPxm = new QPixmap(KStandardDirs::locate("data","sflphone-client-kde/mini/call.png"));
171
painter->drawPixmap(option.rect.x()+15+iconHeight,currentHeight-12+(fm.height()-12),index.data(Call::Role::CategoryIcon).value<QPixmap>());
174
// // if (isTracked) {
176
// // painter->setPen(presentBrush);
178
// // painter->setPen(awayBrush);
181
painter->drawText(option.rect.x()+15+iconHeight+((!isBookmark)?12:0),currentHeight,index.data(Call::Role::Number).toString());
184
// painter->setPen(textCol);
186
currentHeight +=fm.height();
190
else { //Active calls
191
if (ConfigurationSkeleton::displayCallIcon()) {
194
if(ConfigurationSkeleton::displayCallPeer() && !(currentState == Call::State::DIALING || (option.state & QStyle::State_Editing))) {
196
painter->setFont(font);
197
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Qt::DisplayRole).toString());
199
painter->setFont(font);
200
currentHeight +=fm.height();
203
if (ConfigurationSkeleton::displayCallNumber() && !(currentState == Call::State::DIALING || (option.state & QStyle::State_Editing))) {
206
// painter->setPen(presentBrush);
208
// painter->setPen(awayBrush);
210
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Call::Role::Number).toString());
211
currentHeight +=fm.height();
214
if (ConfigurationSkeleton::displayCallSecure()) {
215
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Call::Role::Security).toString());
216
currentHeight +=fm.height();
219
if (ConfigurationSkeleton::displayCallCodec()) {
220
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Call::Role::Codec).toString());
221
currentHeight +=fm.height();
224
if (ConfigurationSkeleton::displayCallOrganisation()) {
225
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Call::Role::Organisation).toString());
226
currentHeight +=fm.height();
229
if (ConfigurationSkeleton::displayCallDepartment()) {
230
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Call::Role::Department).toString());
231
currentHeight +=fm.height();
234
if (ConfigurationSkeleton::displayCallEmail()) {
235
painter->drawText(option.rect.x()+15+iconHeight,currentHeight,index.data(Call::Role::Email).toString());
236
//currentHeight +=fm.height();
240
if (!index.parent().isValid() && currentState != Call::State::RINGING && currentState != Call::State::INCOMING){
241
const QString length = index.data(Call::Role::Length).toString();
242
const int lenLen = fm.width(length);
243
if (!length.isEmpty()) {
244
painter->drawText(option.rect.x()+option.rect.width()-lenLen-4,option.rect.y()+(option.rect.height()/2)+(fm.height()/4),length);
246
DialpadDelegate::paint(painter,option,index,lenLen);
248
else if (!isBookmark && ((currentState == Call::State::RINGING || currentState == Call::State::INCOMING) && index.model()->rowCount() > 1)) {
249
if (!m_AnimationWrapper) {
250
const_cast<HistoryDelegate*>(this)->m_AnimationWrapper = new TipAnimationWrapper();
251
const_cast<HistoryDelegate*>(this)->m_pRingingTip = new RingingTip();
252
m_AnimationWrapper->setTip(m_pRingingTip);
254
if (!m_pRingingTip->isVisible())
255
m_pRingingTip->setVisible(true);
257
painter->setRenderHint (QPainter::Antialiasing, true );
258
painter->setOpacity(0.066);
259
painter->drawImage(QRect(option.rect.x()+option.rect.width()-option.rect.height()-8,option.rect.y()+4,option.rect.height()-8,option.rect.height()-8),m_AnimationWrapper->currentImage());
263
//Display the dialpad if necesary
264
DialpadDelegate::paint(painter,option,index);
268
if (index.data(Call::Role::DropState).toInt() != 0) {
269
/*static*/ if (!m_pDelegatedropoverlay) {
270
const_cast<HistoryDelegate*>(this)->m_pDelegatedropoverlay = new DelegateDropOverlay((QObject*)this);
271
const_cast<HistoryDelegate*>(this)->callMap.insert(i18n("Conference") ,new DelegateDropOverlay::OverlayButton(new QImage(KStandardDirs::locate("data","sflphone-client-kde/confBlackWhite.png")),Call::DropAction::Conference));
272
const_cast<HistoryDelegate*>(this)->callMap.insert(i18n("Transfer") ,new DelegateDropOverlay::OverlayButton(new QImage(KStandardDirs::locate("data","sflphone-client-kde/transferarrow.png")),Call::DropAction::Transfer));
273
const_cast<HistoryDelegate*>(this)->historyMap.insert(i18n("Transfer") ,new DelegateDropOverlay::OverlayButton(new QImage(KStandardDirs::locate("data","sflphone-client-kde/transferarrow.png")),Call::DropAction::Transfer));
276
if (currentState == Call::State::OVER)
277
const_cast<HistoryDelegate*>(this)->m_pDelegatedropoverlay->setButtons(&const_cast<HistoryDelegate*>(this)->historyMap);
279
const_cast<HistoryDelegate*>(this)->m_pDelegatedropoverlay->setButtons(&const_cast<HistoryDelegate*>(this)->callMap);
280
m_pDelegatedropoverlay->paintEvent(painter, option, index);
285
if (currentState == Call::State::DIALING) {
286
m_pParent->edit(index);
292
void HistoryDelegate::slotStopRingingAnimation()
294
if (m_pRingingTip && m_pRingingTip->isVisible()) {
296
foreach(const Call* call,CallModel::instance()->getCallList()) {
297
found = (call->state() == Call::State::RINGING || call->state() == Call::State::INCOMING);
303
m_pRingingTip->setVisible(false);
307
HistoryDelegate::~HistoryDelegate()
309
if (m_AnimationWrapper)
310
delete m_AnimationWrapper;
312
delete m_pRingingTip;
313
if (m_pDelegatedropoverlay) {
314
delete m_pDelegatedropoverlay;
315
foreach (DelegateDropOverlay::OverlayButton* b, historyMap)
317
foreach (DelegateDropOverlay::OverlayButton* b, callMap)