1
/***************************************************************************
2
* Copyright (C) 2009-2014 by Savoir-Faire Linux *
3
* Author : Emmanuel Lepage Valle <emmanuel.lepage@savoirfairelinux.com >*
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 3 of the License, or *
8
* (at your option) any later version. *
10
* This program 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 *
13
* GNU 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 "conferencedelegate.h"
21
#include <QtCore/QSize>
22
#include <QtGui/QPainter>
23
#include <QtGui/QPalette>
24
#include <QtGui/QTreeWidget>
25
#include <QtGui/QGraphicsEffect>
26
#include <QtGui/QGraphicsOpacityEffect>
27
#include <QtGui/QApplication>
30
#include <KStandardDirs>
34
#include "../widgets/categorizedtreeview.h"
36
#include "../lib/call.h"
39
ConferenceDelegate::ConferenceDelegate(CategorizedTreeView* widget,QPalette pal)
40
: QStyledItemDelegate(widget) , m_tree(widget) , m_Pal(pal),
41
m_LeftMargin(7),m_RightMargin(7),m_pCallDelegate(nullptr)
45
///Guess the size of the item
46
QSize ConferenceDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
48
QSize sh = QStyledItemDelegate::sizeHint(option, index);
50
//HACK make absolutely sure the editor (if any) is still required
51
if (m_tree->indexWidget(index)) {
52
Call* call = qvariant_cast<Call*>(index.data(Call::Role::Object));
53
if (call && call->state() != Call::State::DIALING) {
54
m_tree->closeEditor(m_tree->indexWidget(index),QAbstractItemDelegate::NoHint);
58
if (index.parent().isValid()) {
59
sh = m_pCallDelegate->sizeHint(option,index);
61
else if (!index.child(0,0).isValid()) {
62
sh = m_pCallDelegate->sizeHint(option,index);
65
sh.setHeight(categoryHeight(index,option,&m_Pal));
69
if (index.parent().isValid() && !index.parent().child(index.row()+1,0).isValid())
74
///Generate the rectangle
75
QRect ConferenceDelegate::fullCategoryRect(const QStyleOptionViewItem& option, const QModelIndex& index) const
77
QModelIndex i(index),old(index);
78
//BEGIN real sizeHint()
79
//Otherwise it would be called too often (thanks to valgrind)
80
const_cast<ConferenceDelegate*>(this)->m_SH = QStyledItemDelegate::sizeHint(option, index);
81
if (!index.parent().isValid() && index.child(0,0).isValid()) {
82
((QSize)m_SH).rheight() += 2 * m_LeftMargin;
84
((QSize)m_SH).rheight() += m_LeftMargin;
86
((QSize)m_SH).rwidth() += m_LeftMargin;
89
if (i.parent().isValid()) {
93
//Avoid repainting the category over and over (optimization)
94
//note: 0,0,0,0 is actually wrong, but it wont change anything for this use case
95
// if (i != old && old.row()>2)
96
// return QRect(0,0,0,0);
99
if (option.state & QStyle::State_Small) //Hack to know when this is a drag popup
102
r = m_tree->visualRect(i);
104
r.setLeft(m_LeftMargin);
105
r.setWidth(m_tree->viewport()->width() - m_LeftMargin - m_RightMargin);
108
const int childCount = m_tree->model()->rowCount(i);
109
if (m_tree->isExpanded(i) && childCount > 0) {
110
//There is a massive implact on CPU usage to have massive rect
111
r.setHeight(r.height() + sizeHint(option,i.child(0,0)).height()*childCount+2*15); //2*15 = margins
113
r.setTop(r.top() + m_LeftMargin);
118
///Paint the delegate
119
void ConferenceDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
121
Q_ASSERT(index.isValid());
123
QStyleOptionViewItem opt(option);
124
//BEGIN: draw toplevel items
125
if (!index.parent().isValid() && index.child(0,0).isValid()) {
126
const QRegion cl = painter->clipRegion();
127
painter->setClipRect(opt.rect);
128
opt.rect = fullCategoryRect(option, index);
129
drawCategory(index, 0, opt, painter,&m_Pal);
131
//Draw the conference icon and info
132
static const QPixmap* pxm = nullptr;
134
pxm = new QPixmap(KStandardDirs::locate("data","sflphone-client-kde/conf-small.png"));
135
painter->drawPixmap ( opt.rect.x()+5, opt.rect.y()+2, 24, 24, *pxm);
136
QFont font = painter->font();
139
static QColor baseColor = Qt::red;
140
/*static block*/if (baseColor == Qt::red) {
141
QColor textColor = QApplication::palette().text().color();
142
baseColor = QApplication::palette().base().color().name();
143
baseColor.setBlue (baseColor.blue() + (textColor.blue() -baseColor.blue()) *0.6);
144
baseColor.setRed (baseColor.red() + (textColor.red() -baseColor.red()) *0.6);
145
baseColor.setGreen(baseColor.green()+ (textColor.green()-baseColor.green())*0.6);
147
painter->setPen(baseColor);
148
painter->setFont(font);
149
painter->drawText(opt.rect.x()+33,opt.rect.y()+font.pointSize()+8,"Conference");
151
painter->setFont(font);
152
baseColor.setAlpha(150);
153
painter->setPen(baseColor);
154
baseColor.setAlpha(255);
155
static QFontMetrics* fm = nullptr;
157
fm = new QFontMetrics(painter->font());
159
const QString len = index.data(Call::Role::Length).toString();
160
painter->drawText(opt.rect.x()+opt.rect.width()-fm->width(len),opt.rect.y()+font.pointSize()+8,len);
161
painter->setClipRegion(cl);
165
if (!index.parent().parent().isValid()) {
166
opt.rect = fullCategoryRect(option, index);
167
const QRegion cl = painter->clipRegion();
168
QRect cr = option.rect;
169
if (index.column() == 0) {
170
if (m_tree->layoutDirection() == Qt::LeftToRight) {
173
cr.setRight(opt.rect.right());
176
painter->setClipRect(cr);
179
if (index.parent().isValid())
180
drawCategory(index, 0, opt, painter,&m_Pal);
182
painter->setClipRegion(cl);
183
painter->setRenderHint(QPainter::Antialiasing, false);
186
//END: draw background of category for all other items
188
QStyleOptionViewItem opt2(option);
189
if (index.parent().isValid())
190
opt2.rect.setWidth(opt2.rect.width()-15);
191
painter->setClipRect(option.rect);
193
//Check if it is the last item
194
if (index.parent().isValid() && !index.parent().child(index.row()+1,0).isValid()) {
195
opt2.rect.setHeight(opt2.rect.height()-15);
196
m_pCallDelegate->paint(painter,opt2,index);
198
else if (index.parent().isValid() || (!index.parent().isValid() && !index.child(0,0).isValid())) {
199
m_pCallDelegate->paint(painter,opt2,index);
202
if (index.parent().isValid() && !index.parent().child(index.row()+1,0).isValid()) {
203
// QStyleOptionViewItem opt5(option);
204
// opt5.rect.setLeft(leftMargin());
205
// opt5.rect.setWidth(m_tree->viewport()->width() - leftMargin() - rightMargin());
206
painter->setClipRect(opt.rect);
207
drawBoxBottom(index, 0, option, painter);
211
void ConferenceDelegate::drawCategory(const QModelIndex& index, int sortRole, const QStyleOption& option, QPainter* painter, const QPalette* pal) const
215
const QPalette* palette = (pal)?pal:&option.palette ;
216
painter->setRenderHint(QPainter::Antialiasing);
218
const QRect optRect = option.rect;
220
//BEGIN: decoration gradient
222
QPainterPath path(optRect.bottomLeft());
224
path.lineTo(QPoint(optRect.topLeft().x(), optRect.topLeft().y() - 3));
225
const QPointF topLeft(optRect.topLeft());
226
QRectF arc(topLeft, QSizeF(4, 4));
227
path.arcTo(arc, 180, -90);
228
path.lineTo(optRect.topRight());
229
path.lineTo(optRect.bottomRight());
230
path.lineTo(optRect.bottomLeft());
232
QColor window(palette->window().color());
233
const QColor base(palette->base().color());
235
window.setAlphaF(option.state & QStyle::State_Selected?0.9:0.9);
237
QColor window2(window);
238
window2.setAlphaF(option.state & QStyle::State_Selected?0.4:0.4);
240
QLinearGradient decoGradient1;
241
decoGradient1.setStart(optRect.topLeft());
242
decoGradient1.setFinalStop(optRect.bottomLeft());
243
decoGradient1.setColorAt(0, window);
244
decoGradient1.setColorAt(1, Qt::transparent);
246
QLinearGradient decoGradient2;
247
decoGradient2.setStart(optRect.topLeft());
248
decoGradient2.setFinalStop(optRect.topRight());
249
decoGradient2.setColorAt(0, window2);
250
decoGradient2.setColorAt(1, Qt::transparent);
252
painter->fillPath(path, decoGradient1);
253
painter->fillRect(optRect, decoGradient2);
255
//END: decoration gradient
258
QRect newOptRect(optRect);
260
newOptRect.translate(1, 1);
262
//BEGIN: inner top left corner
265
painter->setPen(palette->base().color());
266
const QPointF topLeft(newOptRect.topLeft());
267
QRectF arc = QRectF(topLeft, QSizeF(4, 4));
268
arc.translate(0.5, 0.5);
269
painter->drawArc(arc, 1440, 1440);
272
//END: inner top left corner
274
//BEGIN: inner left vertical line
276
QPoint start = newOptRect.topLeft();
277
QPoint verticalGradBottom = newOptRect.topLeft();
279
verticalGradBottom.ry() += newOptRect.height() - 3;
280
QLinearGradient gradient(start, verticalGradBottom);
281
gradient.setColorAt(0, palette->base().color());
282
gradient.setColorAt(1, Qt::transparent);
283
painter->fillRect(QRect(start, QSize(1, newOptRect.height() - 3)), gradient);
285
//END: inner left vertical line
287
//BEGIN: top inner horizontal line
289
QPoint start = newOptRect.topLeft();
290
QPoint horizontalGradTop = newOptRect.topLeft();
292
horizontalGradTop.rx() += newOptRect.width() - 3;
293
QLinearGradient gradient(start, horizontalGradTop);
294
gradient.setColorAt(0, palette->base().color());
295
gradient.setColorAt(1, Qt::transparent);
296
QSize rectSize = QSize(newOptRect.width() - 30, 1);
297
painter->fillRect(QRect(start, rectSize), gradient);
299
//END: top inner horizontal line
302
QColor outlineColor = palette->text().color();
303
outlineColor.setAlphaF(0.35);
305
//BEGIN: top left corner
308
painter->setPen(outlineColor);
310
const QPointF topLeft(optRect.topLeft());
311
arc = QRectF(topLeft, QSizeF(4, 4));
312
arc.translate(0.5, 0.5);
313
painter->drawArc(arc, 1440, 1440);
316
//END: top left corner
318
//BEGIN: top right corner
321
painter->setPen(outlineColor);
322
QPointF topRight(optRect.topRight());
324
QRectF arc = QRectF(topRight, QSizeF(4, 4));
325
arc.translate(-0.5, 0.5);
326
painter->drawArc(arc, 0, 1440);
329
//END: top right corner
331
//BEGIN: left vertical line
333
QPoint start = optRect.topLeft();
334
QPoint verticalGradBottom = optRect.topLeft();
336
verticalGradBottom.ry() += optRect.height() - 3 + 200;
337
painter->fillRect(QRect(start, QSize(1, optRect.height() - 21)), outlineColor);
339
//END: left vertical line
341
//BEGIN: right vertical line
343
QPoint start = optRect.topRight();
344
QPoint verticalGradBottom = optRect.topRight();
346
verticalGradBottom.ry() += optRect.height() - 3 + 200;
347
painter->fillRect(QRect(start, QSize(1, optRect.height() - 21)), outlineColor);
349
//END: right vertical line
351
//BEGIN: horizontal line
353
QPoint start = optRect.topLeft();
354
QPoint horizontalGradTop = optRect.topLeft();
356
horizontalGradTop.rx() += optRect.width() - 3;
357
QLinearGradient gradient(start, horizontalGradTop);
358
gradient.setColorAt(0, outlineColor);
359
gradient.setColorAt(1, outlineColor);
360
QSize rectSize = QSize(optRect.width() - 6, 1);
361
painter->fillRect(QRect(start, rectSize), gradient);
363
//END: horizontal line
366
///Draw the bottom border of the box
367
void ConferenceDelegate::drawBoxBottom(const QModelIndex &index, int sortRole, const QStyleOption &option, QPainter *painter,const QPalette* pal) const {
370
painter->setClipping(false);
371
const QPalette* palette = (pal)?pal:&option.palette ;
372
painter->setRenderHint(QPainter::Antialiasing);
373
QColor outlineColor = palette->text().color();
374
outlineColor.setAlphaF(0.35);
375
painter->setPen(outlineColor);
377
//BEGIN: bottom horizontal line
379
QPoint bl = option.rect.bottomLeft();
381
bl.setX(m_LeftMargin+3);
383
painter->fillRect(QRect(bl, QSize(option.rect.width()+4,1)), outlineColor);
385
//END: bottom horizontal line
387
//BEGIN: bottom right corner
390
QPointF br(option.rect.bottomRight());
393
arc = QRectF(br, QSizeF(4, 4));
394
arc.translate(0.5, 0.5);
395
painter->drawArc(arc, 4320, 1440);
397
//END: bottom right corner
399
//BEGIN: bottom left corner
402
QPointF br(option.rect.bottomRight());
404
br.setX(m_LeftMargin);
405
arc = QRectF(br, QSizeF(4, 4));
406
arc.translate(0.5, 0.5);
407
painter->drawArc(arc, 1440*2, 1440);
409
//END: bottom left corner
410
painter->setClipping(true);
413
///Return the height of the conference box
414
int ConferenceDelegate::categoryHeight(const QModelIndex &index, const QStyleOption &option,const QPalette* pal) const
419
QFont font(QApplication::font());
421
const QFontMetrics fontMetrics = QFontMetrics(font);
423
return fontMetrics.height() + 2 + 16 /* vertical spacing */;
426
///Draw the delegate when it is being dragged
427
QPixmap ConferenceDelegate::getDragPixmap(CategorizedTreeView* parent, const QModelIndex& index)
429
QStyleOptionViewItemV4 option;
430
option.locale = parent->locale();
431
option.widget = parent;
432
option.state = QStyle::State_Selected | QStyle::State_Enabled | QStyle::State_Active | QStyle::State_Small;
433
option.rect = QRect(0,0,parent->width()-(index.parent().isValid()?15:0),parent->height());
434
QSize size = parent->itemDelegate()->sizeHint(option,index);
435
QSize itemSize = size;
436
const int rowCount = parent->model()->rowCount(index);
437
for (int i=0;i<rowCount;i++) {
438
size.setHeight(size.height()+parent->itemDelegate()->sizeHint(option,index.child(i,0)).height());
440
if (index.parent().isValid() && index.model()->rowCount(index.parent()) -1 == index.row()) {
441
size = parent->itemDelegate()->sizeHint(option,index.parent().child(0,0));
445
QPixmap pixmap(parent->width()-(index.parent().isValid()?15:0),size.height());
446
QPainter customPainter(&pixmap);
447
customPainter.eraseRect(option.rect);
448
customPainter.setCompositionMode(QPainter::CompositionMode_Clear);
449
customPainter.fillRect(option.rect,QBrush(Qt::white));
450
customPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
453
option.rect = QRect(0,0,parent->width(),itemSize.height());
455
option.rect.setHeight(pixmap.height()-3);
456
parent->itemDelegate()->paint(&customPainter, option, index);
457
option.rect = QRect(0,0,parent->width(),itemSize.height());
460
for (int i=0;i<rowCount;i++) {
461
itemSize.setHeight(parent->itemDelegate()->sizeHint(option,index.child(i,0)).height());
462
option.rect = QRect(10,option.rect.y()+option.rect.height(),parent->width()-20,itemSize.height());
463
option.state = QStyle::State_Enabled | QStyle::State_Active | QStyle::State_Small;
464
parent->itemDelegate()->paint(&customPainter, option, index.child(i,0));
469
///Create an editor widget
470
QWidget* ConferenceDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
473
KLineEdit* ed = new KLineEdit(parent);
474
ed->setStyleSheet(QString("QLineEdit { background-color:transparent;border:0px;color:white;font-weight:bold;padding-left:%1 }").arg(option.rect.height()));
475
ed->setAutoFillBackground(false);
476
ed->setProperty("call",index.data(Call::Role::Object));
477
connect(ed,SIGNAL(textChanged(QString)),this,SLOT(slotTextChanged(QString)));
478
connect(ed,SIGNAL(returnPressed()),this,SLOT(slotReturnPressed()));
484
///Update line edit text when in dialing mode
485
void ConferenceDelegate::setEditorData(QWidget * editor, const QModelIndex & index ) const
487
KLineEdit* ed = qobject_cast<KLineEdit*>(editor);
489
ed->setText(index.data(Qt::EditRole).toString());
494
///Update call PhoneNumber when leaving edit mode
495
void ConferenceDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
497
KLineEdit* ed = qobject_cast<KLineEdit*>(editor);
498
if (index.data(Call::Role::CallState) != static_cast<int>(Call::State::DIALING)) {
499
ed->setVisible(false);
500
emit const_cast<ConferenceDelegate*>(this)->closeEditor(editor,NoHint);
504
model->setData(index,ed->text(),Qt::EditRole);
507
///Intercept KLineEdit show() call, to deselect the text (as it is selected automagically)
508
bool ConferenceDelegate::eventFilter(QObject *obj, QEvent *event)
511
if (event->type() == QEvent::Show) {
512
KLineEdit* ed = qobject_cast<KLineEdit*>(obj);
516
else if (event->type() == QEvent::MouseButtonDblClick || event->type() == QEvent::MouseButtonPress) {
517
KLineEdit* ed = qobject_cast<KLineEdit*>(obj);
519
QObject* obj= qvariant_cast<Call*>(ed->property("call"));
520
Call* call = nullptr;
522
call = qobject_cast<Call*>(obj);
524
if (event->type() == QEvent::MouseButtonDblClick)
525
call->performAction(Call::Action::ACCEPT);
526
else if (event->type() == QEvent::MouseButtonPress)
527
m_tree->selectionModel()->setCurrentIndex(CallModel::instance()->getIndex(call),QItemSelectionModel::SelectCurrent);
534
///Update the model text as soon as
535
void ConferenceDelegate::slotTextChanged(const QString& text)
537
KLineEdit* ed = qobject_cast<KLineEdit*>(QObject::sender());
540
QObject* obj= qvariant_cast<Call*>(ed->property("call"));
541
Call* call = nullptr;
543
call = qobject_cast<Call*>(obj);
544
if (call && call->state() != Call::State::DIALING) {
545
emit closeEditor(ed);
547
if (call && call->dialNumber() != text) {
548
if (text.left(text.size()-1) == call->dialNumber()) {
549
call->playDTMF(text.right(1));
551
call->setDialNumber(text);
554
emit closeEditor(ed);
558
emit closeEditor(ed);
562
void ConferenceDelegate::slotReturnPressed()
564
KLineEdit* ed = qobject_cast<KLineEdit*>(QObject::sender());
566
emit closeEditor(ed);