1
/***************************************************************************
2
* Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> *
3
* Copyright (C) 2008 by Alexis Ménard <darktears31@gmail.com> *
4
* Copyright (C) 2008 by Marco Martin <notmart@gmail.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 2 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, write to the *
18
* Free Software Foundation, Inc., *
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
20
***************************************************************************/
23
#include "abstracttaskitem.h"
26
#include <QApplication>
28
#include <QGraphicsLinearLayout>
29
#include <QGraphicsSceneContextMenuEvent>
30
#include <QGraphicsView>
31
#include <QStyleOptionGraphicsItem>
32
#include <QTextLayout>
34
#include <QVarLengthArray>
35
#include <QPropertyAnimation>
41
#include <KAuthorized>
42
#include <KColorUtils>
44
#include <KGlobalSettings>
46
#include <KIconEffect>
47
#include <KIconLoader>
51
#include <Plasma/Containment>
52
#include <Plasma/Corona>
53
#include <Plasma/FrameSvg>
54
#include <Plasma/PaintUtils>
55
#include <Plasma/Theme>
56
#include <Plasma/ToolTipManager>
57
#include <Plasma/WindowEffects>
59
#include <taskmanager/task.h>
60
#include <taskmanager/taskmanager.h>
61
#include <taskmanager/taskgroup.h>
64
#include "taskgroupitem.h"
66
static const int HOVER_EFFECT_TIMEOUT = 900;
68
AbstractTaskItem::AbstractTaskItem(QGraphicsWidget *parent, Tasks *applet)
69
: QGraphicsWidget(parent),
73
m_backgroundFadeAnim(0),
75
m_backgroundPrefix("normal"),
77
m_updateGeometryTimerId(0),
79
m_hoverEffectTimerId(0),
80
m_attentionTimerId(0),
84
m_layoutAnimationLock(false),
85
m_firstGeometryUpdate(false)
87
m_layoutAnimation = new QPropertyAnimation(this, "animationPos", this);
88
m_layoutAnimation->setEasingCurve(QEasingCurve::InOutQuad);
89
m_layoutAnimation->setDuration(250);
91
setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
92
setAcceptsHoverEvents(true);
94
setFocusPolicy(Qt::StrongFocus);
95
setFlag(QGraphicsItem::ItemIsFocusable);
98
connect(applet->itemBackground(), SIGNAL(repaintNeeded()), this, SLOT(syncActiveRect()));
99
connect(applet, SIGNAL(settingsChanged()), this, SLOT(checkSettings()));
102
QSize AbstractTaskItem::basicPreferredSize() const
104
QFontMetrics fm(KGlobalSettings::taskbarFont());
105
QSize mSize = fm.size(0, "M");
106
const int iconsize = KIconLoader::SizeSmall;
108
//the 4 should be the default spacing between layout items, is there a way to fetch it without hardcoding?
109
// in small panels, we'll reduce the spacing a bit so it's easier to cramp the text in and still get two rows
110
int topMargin = m_applet->itemTopMargin();
111
int bottomMargin = m_applet->itemBottomMargin();
113
//if this item is out of the applet rect must be in a popup
114
//const bool inPopup = (m_applet->mapToScene(m_applet->rect()).boundingRect().contains(mapToScene(rect()).boundingRect()))
116
if (m_applet->size().height() < 44) {
119
} else if (m_applet->size().height() < 64) {
120
topMargin = qMax(1, topMargin/2);
121
bottomMargin = qMax(1, bottomMargin/2);
124
//kDebug() << (QObject*)this;
125
return QSize(mSize.width()*12 + m_applet->itemLeftMargin() + m_applet->itemRightMargin() + KIconLoader::SizeSmall,
126
qMax(mSize.height(), iconsize) + topMargin + bottomMargin);
129
void AbstractTaskItem::setPreferredOffscreenSize()
131
QFontMetrics fm(KGlobalSettings::taskbarFont());
132
QSize mSize = fm.size(0, "M");
133
int iconsize = KIconLoader::SizeSmall;
134
int topMargin = m_applet->offscreenTopMargin();
135
int bottomMargin = m_applet->offscreenBottomMargin();
136
int rightMargin = m_applet->offscreenRightMargin();
137
int leftMargin = m_applet->offscreenLeftMargin();
139
//kDebug() << (QObject*)this;
140
QSizeF s(mSize.width() * 12 + leftMargin + rightMargin + KIconLoader::SizeSmall,
141
qMax(mSize.height(), iconsize) + topMargin + bottomMargin);
145
void AbstractTaskItem::setPreferredOnscreenSize()
147
setPreferredSize(basicPreferredSize());
150
AbstractTaskItem::~AbstractTaskItem()
152
stopWindowHoverEffect();
153
emit destroyed(this);
154
Plasma::ToolTipManager::self()->unregisterWidget(this);
157
void AbstractTaskItem::checkSettings()
159
TaskGroupItem *group = qobject_cast<TaskGroupItem *>(this);
161
if (m_applet->showToolTip() && (!group || group->collapsed())) {
164
Plasma::ToolTipManager::self()->unregisterWidget(this);
168
void AbstractTaskItem::clearToolTip()
170
Plasma::ToolTipContent data;
171
data.setInstantPopup(true);
173
Plasma::ToolTipManager::self()->setContent(this, data);
176
void AbstractTaskItem::clearAbstractItem()
181
void AbstractTaskItem::textChanged()
183
m_cachedShadow = QPixmap();
186
QString AbstractTaskItem::text() const
188
if (m_abstractItem) {
189
return m_abstractItem->name();
191
kDebug() << "no abstract item?";
197
QIcon AbstractTaskItem::icon() const
199
if (m_abstractItem) {
200
return m_abstractItem->icon();
206
void AbstractTaskItem::setTaskFlags(const TaskFlags flags)
208
if (((m_flags & TaskWantsAttention) != 0) != ((flags & TaskWantsAttention) != 0)) {
209
//kDebug() << "task attention state changed" << m_attentionTimerId;
211
if (flags & TaskWantsAttention) {
212
m_applet->needsVisualFocus(true);
213
// start attention getting
214
if (!m_attentionTimerId) {
215
m_attentionTimerId = startTimer(500);
218
m_applet->needsVisualFocus(false);
219
if (m_attentionTimerId) {
220
killTimer(m_attentionTimerId);
221
m_attentionTimerId = 0;
228
QString newBackground;
229
if (m_flags & TaskIsMinimized) {
230
newBackground = "minimized";
231
} else if (m_flags & TaskHasFocus) {
232
newBackground = "focus";
234
newBackground = "normal";
237
if (newBackground != m_backgroundPrefix) {
238
fadeBackground(newBackground, 250);
242
void AbstractTaskItem::fadeBackground(const QString &newBackground, int duration)
244
TaskGroupItem *group = qobject_cast<TaskGroupItem*>(this);
245
if (group && !group->collapsed()) {
249
m_oldBackgroundPrefix = m_backgroundPrefix;
250
m_backgroundPrefix = newBackground;
252
if (m_oldBackgroundPrefix.isEmpty()) {
255
if (!m_backgroundFadeAnim) {
256
m_backgroundFadeAnim = new QPropertyAnimation(this);
257
m_backgroundFadeAnim->setDuration(duration);
258
m_backgroundFadeAnim->setEasingCurve(QEasingCurve::InQuad);
259
m_backgroundFadeAnim->setPropertyName("backgroundFadeAlpha");
260
m_backgroundFadeAnim->setTargetObject(this);
261
m_backgroundFadeAnim->setStartValue(0);
262
m_backgroundFadeAnim->setEndValue(1);
265
m_backgroundFadeAnim->start();
269
AbstractTaskItem::TaskFlags AbstractTaskItem::taskFlags() const
274
void AbstractTaskItem::toolTipAboutToShow()
276
if (m_applet->showToolTip()) {
278
connect(Plasma::ToolTipManager::self(),
279
SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
280
this, SLOT(activateWindow(WId,Qt::MouseButtons)));
286
void AbstractTaskItem::toolTipHidden()
289
disconnect(Plasma::ToolTipManager::self(),
290
SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
291
this, SLOT(activateWindow(WId,Qt::MouseButtons)));
294
void AbstractTaskItem::activateWindow(WId id, Qt::MouseButtons buttons)
296
if (buttons & Qt::LeftButton) {
298
AbstractTaskItem *item = parentGroup()->taskItemForWId(id);
306
void AbstractTaskItem::queueUpdate()
308
if (m_updateTimerId || m_attentionTimerId) {
312
if (m_lastUpdate.elapsed() < 100) {
313
m_updateTimerId = startTimer(100);
318
m_lastUpdate.restart();
321
void AbstractTaskItem::focusInEvent(QFocusEvent *event)
325
setTaskFlags(m_flags | TaskHasFocus);
329
void AbstractTaskItem::focusOutEvent(QFocusEvent *event)
333
setTaskFlags(m_flags & ~TaskHasFocus);
337
void AbstractTaskItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
340
fadeBackground("hover", 250);
341
QGraphicsWidget *w = parentWidget();
342
if (w && this != m_applet->rootGroupItem()) {
343
if (m_hoverEffectTimerId) {
344
killTimer(m_hoverEffectTimerId);
345
m_hoverEffectTimerId = 0;
348
m_hoverEffectTimerId = startTimer(HOVER_EFFECT_TIMEOUT);
352
void AbstractTaskItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
356
stopWindowHoverEffect();
358
QString backgroundPrefix;
359
if (m_flags & TaskWantsAttention) {
360
backgroundPrefix = "attention";
361
} else if (m_flags & TaskIsMinimized) {
362
backgroundPrefix = "minimized";
363
} else if (m_flags & TaskHasFocus) {
364
backgroundPrefix = "focus";
366
backgroundPrefix = "normal";
369
fadeBackground(backgroundPrefix, 150);
372
void AbstractTaskItem::stopWindowHoverEffect()
374
if (m_hoverEffectTimerId) {
375
killTimer(m_hoverEffectTimerId);
376
m_hoverEffectTimerId = 0;
379
if (m_lastViewId && m_applet->highlightWindows()) {
380
Plasma::WindowEffects::highlightWindows(m_lastViewId, QList<WId>());
384
void AbstractTaskItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
386
if (event->button() == Qt::LeftButton && boundingRect().contains(event->pos())) {
391
void AbstractTaskItem::mousePressEvent(QGraphicsSceneMouseEvent *)
396
void AbstractTaskItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
399
if (QPoint(event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() < QApplication::startDragDistance()) {
401
} //Wait a bit before starting drag
403
/* if((m_applet->taskSortOrder() != Tasks::NoSorting) && (m_applet->taskSortOrder() != Tasks::GroupSorting)){ //FIXME check somhow if drag is allowed
408
data.resize(sizeof(AbstractTaskItem*));
409
AbstractTaskItem *selfPtr = this;
410
memcpy(data.data(), &selfPtr, sizeof(AbstractTaskItem*));
412
QMimeData* mimeData = new QMimeData();
413
setAdditionalMimeData(mimeData);
415
if (mimeData->formats().isEmpty()) {
420
QDrag *drag = new QDrag(event->widget());
421
drag->setMimeData(mimeData);
422
drag->setPixmap(icon().pixmap(20));
423
// drag->setDragCursor( set the correct cursor //TODO
427
void AbstractTaskItem::timerEvent(QTimerEvent *event)
429
if (event->timerId() == m_activateTimerId) {
430
killTimer(m_activateTimerId);
431
m_activateTimerId = 0;
435
} else if (event->timerId() == m_updateGeometryTimerId) {
436
killTimer(m_updateGeometryTimerId);
437
m_updateGeometryTimerId = 0;
438
m_firstGeometryUpdate = true;
439
publishIconGeometry();
440
} else if (event->timerId() == m_updateTimerId) {
441
killTimer(m_updateTimerId);
444
} else if (event->timerId() == m_attentionTimerId) {
446
if (m_attentionTicks > 6) {
447
killTimer(m_attentionTimerId);
448
m_attentionTimerId = 0;
449
m_attentionTicks = 0;
452
if (m_attentionTicks % 2 == 0) {
453
fadeBackground("attention", 200);
455
fadeBackground("normal", 250);
457
} else if (event->timerId() == m_hoverEffectTimerId) {
458
killTimer(m_hoverEffectTimerId);
459
m_hoverEffectTimerId = 0;
460
if (!isUnderMouse()) {
467
if (m_abstractItem && m_abstractItem->itemType() == TaskManager::GroupItemType) {
468
TaskManager::TaskGroup *group = qobject_cast<TaskManager::TaskGroup *>(m_abstractItem);
471
TaskGroupItem *groupItem = qobject_cast<TaskGroupItem *>(this);
472
if (groupItem && groupItem->popupDialog()) {
473
kDebug() << "adding" << groupItem->popupDialog()->winId();
474
windows.append(groupItem->popupDialog()->winId());
477
foreach (AbstractGroupableItem *item, group->members()) {
478
if (item->itemType() == TaskManager::TaskItemType) {
479
TaskManager::TaskItem *taskItem = qobject_cast<TaskManager::TaskItem *>(item);
480
if (taskItem && taskItem->task()) {
481
windows.append(taskItem->task()->window());
483
} //TODO: if taskgroup, recurse through sub-groups?
487
WindowTaskItem *windowTaskItem = qobject_cast<WindowTaskItem *>(this);
488
if (windowTaskItem && windowTaskItem->parent()) {
489
TaskGroupItem *groupItem = qobject_cast<TaskGroupItem *>(windowTaskItem->parent());
490
if (groupItem && groupItem->popupDialog()) {
491
windows.append(groupItem->popupDialog()->winId());
495
TaskManager::TaskItem *taskItem = qobject_cast<TaskManager::TaskItem *>(m_abstractItem);
496
if (taskItem && taskItem->task()) {
497
windows.append(taskItem->task()->window());
501
stopWindowHoverEffect();
502
QGraphicsView *view = m_applet->view();
503
if (view && m_applet->highlightWindows()) {
504
m_lastViewId = view->winId();
505
Plasma::WindowEffects::highlightWindows(m_lastViewId, windows);
509
QGraphicsWidget::timerEvent(event);
513
void AbstractTaskItem::paint(QPainter *painter,
514
const QStyleOptionGraphicsItem *option,
517
if (!m_abstractItem) {
520
//kDebug() << "painting" << (QObject*)this << text();
521
painter->setRenderHint(QPainter::Antialiasing);
523
if (m_abstractItem->itemType() != TaskManager::LauncherItemType) { //Launchers have no frame
525
drawBackground(painter, option, widget);
528
// draw icon and text
529
drawTask(painter, option, widget);
532
void AbstractTaskItem::syncActiveRect()
534
m_cachedShadow = QPixmap();
535
Plasma::FrameSvg *itemBackground = m_applet->itemBackground();
536
itemBackground->setElementPrefix("normal");
538
qreal left, top, right, bottom;
539
itemBackground->getMargins(left, top, right, bottom);
541
itemBackground->setElementPrefix("focus");
542
qreal activeLeft, activeTop, activeRight, activeBottom;
543
itemBackground->getMargins(activeLeft, activeTop, activeRight, activeBottom);
545
m_activeRect = QRectF(QPointF(0, 0), size());
546
m_activeRect.adjust(left - activeLeft, top - activeTop,
547
-(right - activeRight), -(bottom - activeBottom));
549
itemBackground->setElementPrefix(m_backgroundPrefix);
551
// check to see if there is enough room!
552
QFontMetrics fm(font());
553
const int minimumWidth = left + 8 + IconTextSpacing + right;
554
m_showText = (size().width() >= fm.width("M") * 6 + minimumWidth);
558
void AbstractTaskItem::resizeEvent(QGraphicsSceneResizeEvent *event)
561
resizeBackground(event->newSize().toSize());
564
void AbstractTaskItem::resizeBackground(const QSize &size)
566
Plasma::FrameSvg *itemBackground = m_applet->itemBackground();
568
itemBackground->setElementPrefix("focus");
569
m_applet->resizeItemBackground(size);
570
itemBackground->setElementPrefix("normal");
571
m_applet->resizeItemBackground(size);
572
itemBackground->setElementPrefix("minimized");
573
m_applet->resizeItemBackground(size);
574
itemBackground->setElementPrefix("attention");
575
m_applet->resizeItemBackground(size);
576
itemBackground->setElementPrefix("hover");
577
m_applet->resizeItemBackground(size);
580
itemBackground->setElementPrefix(m_backgroundPrefix);
583
void AbstractTaskItem::drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
585
// Do not paint with invalid sizes, the happens when the layout is being initialized
586
if (!option->rect.isValid()) {
590
/*FIXME -could be done more elegant with caching in tasks in a qhash <size,svg>.
591
-do not use size() directly because this introduces the blackline syndrome.
592
-This line is only needed when we have different items in the taskbar because of an expanded group for example. otherwise the resizing in the resizeEvent is sufficient
594
Plasma::FrameSvg *itemBackground = m_applet->itemBackground();
596
if (~option->state & QStyle::State_Sunken &&
597
(!m_backgroundFadeAnim || m_backgroundFadeAnim->state() != QAbstractAnimation::Running)) {
598
itemBackground->setElementPrefix(m_backgroundPrefix);
599
//since a single framesvg is shared between all tasks, we could have to resize it even if there wasn't a resizeevent
600
if (size().toSize() != itemBackground->frameSize()) {
601
resizeBackground(size().toSize());
604
if (itemBackground->frameSize() == m_activeRect.size().toSize()) {
605
itemBackground->paintFrame(painter, m_activeRect.topLeft());
607
itemBackground->paintFrame(painter);
609
//itemBackground->paintFrame(painter, backgroundPosition);
613
itemBackground->setElementPrefix(m_oldBackgroundPrefix);
614
//since a single framesvg is shared between all tasks, we could have to resize it even if there wasn't a resizeevent
615
if (size().toSize() != itemBackground->frameSize()) {
616
resizeBackground(size().toSize());
619
QPixmap oldBackground;
621
if (option->state & QStyle::State_Sunken) {
622
oldBackground = QPixmap(m_activeRect.size().toSize());
623
oldBackground.fill(Qt::transparent);
626
oldBackground = itemBackground->framePixmap();
629
itemBackground->setElementPrefix(m_backgroundPrefix);
630
//since a single framesvg is shared between all tasks, we could have to resize it even if there wasn't a resizeevent
631
if (size().toSize() != itemBackground->frameSize()) {
632
resizeBackground(size().toSize());
635
QPixmap result = Plasma::PaintUtils::transition(oldBackground, itemBackground->framePixmap(), m_alpha);
637
if (result.size() == m_activeRect.size().toSize()) {
638
painter->drawPixmap(m_activeRect.topLeft(), result);
640
painter->drawPixmap(QPoint(0,0), result);
644
void AbstractTaskItem::drawTask(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
648
QRectF bounds = boundingRect();
650
if (m_abstractItem->itemType() != TaskManager::LauncherItemType) {
651
bounds = bounds.adjusted(m_applet->itemLeftMargin(), m_applet->itemTopMargin(), -m_applet->itemRightMargin(), -m_applet->itemBottomMargin());
653
bounds = bounds.adjusted(5,5,-5,-5);
656
WindowTaskItem *window = qobject_cast<WindowTaskItem *>(this);
657
QGraphicsWidget *busyWidget;
658
busyWidget = window ? window->busyWidget() : 0;
659
const QRectF iconR = iconRect(bounds);
662
busyWidget->setGeometry(iconR);
666
kDebug() << bool(option->state & QStyle::State_MouseOver) << m_backgroundFadeAnim <<
667
(m_backgroundFadeAnim ? m_backgroundFadeAnim->state() : QAbstractAnimation::Stopped);*/
668
const bool fadingBg = m_backgroundFadeAnim && m_backgroundFadeAnim->state() == QAbstractAnimation::Running;
669
if ((!fadingBg && !(option->state & QStyle::State_MouseOver)) ||
670
(m_oldBackgroundPrefix != "hover" && m_backgroundPrefix != "hover")) {
671
// QIcon::paint does some alignment work and can lead to funny
672
// things when icon().size() != iconR.toRect().size()
673
QPixmap result = icon().pixmap(iconR.toRect().size());
674
painter->drawPixmap(iconR.topLeft(), result);
676
KIconEffect *effect = KIconLoader::global()->iconEffect();
677
QPixmap result = icon().pixmap(iconR.toRect().size());
679
if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) {
680
if (qFuzzyCompare(qreal(1.0), m_alpha)) {
681
result = effect->apply(result, KIconLoader::Desktop, KIconLoader::ActiveState);
683
result = Plasma::PaintUtils::transition(result,
684
effect->apply(result, KIconLoader::Desktop,
685
KIconLoader::ActiveState), m_backgroundPrefix != "hover" ? 1 - m_alpha : m_alpha);
689
painter->drawPixmap(iconR.topLeft(), result);
693
painter->setPen(QPen(textColor(), 1.0));
695
if (m_abstractItem->itemType() != TaskManager::LauncherItemType) {
697
QRect rect = textRect(bounds).toRect();
698
if (rect.height() > 20) {
699
rect.adjust(2, 2, -2, -2); // Create a text margin
702
layout.setFont(KGlobalSettings::taskbarFont());
703
layout.setTextOption(textOption());
705
layoutText(layout, text(), rect.size());
706
drawTextLayout(painter, layout, rect);
709
TaskGroupItem *groupItem = qobject_cast<TaskGroupItem *>(this);
711
QFont font(KGlobalSettings::smallestReadableFont());
712
QFontMetrics fm(font);
713
QRectF rect(expanderRect(bounds));
715
Plasma::FrameSvg *itemBackground = m_applet->itemBackground();
717
if (itemBackground->hasElement(expanderElement())) {
718
QSizeF arrowSize(itemBackground->elementSize(expanderElement()));
719
QRectF arrowRect(rect.center()-QPointF(arrowSize.width()/2, arrowSize.height()+fm.xHeight()/2), arrowSize);
720
itemBackground->paint(painter, arrowRect, expanderElement());
722
painter->setFont(font);
723
rect.setTop(arrowRect.bottom());
724
painter->drawText(rect, Qt::AlignHCenter|Qt::AlignTop, QString::number(groupItem->count()));
726
painter->setFont(font);
727
painter->drawText(rect, Qt::AlignCenter, QString::number(groupItem->count()));
733
QTextOption AbstractTaskItem::textOption() const
735
Qt::LayoutDirection direction = QApplication::layoutDirection();
736
Qt::Alignment alignment = QStyle::visualAlignment(direction, Qt::AlignLeft | Qt::AlignVCenter);
739
option.setTextDirection(direction);
740
option.setAlignment(alignment);
745
QSize AbstractTaskItem::layoutText(QTextLayout &layout, const QString &text,
746
const QSize &constraints) const
748
QFontMetrics metrics(layout.font());
749
int leading = metrics.leading();
751
int maxWidth = constraints.width();
753
int lineSpacing = metrics.lineSpacing();
756
layout.setText(text);
758
layout.beginLayout();
759
while ((line = layout.createLine()).isValid()) {
762
// Make the last line that will fit infinitely long.
763
// drawTextLayout() will handle this by fading the line out
764
// if it won't fit in the constraints.
765
if (height + 2 * lineSpacing > constraints.height()) {
766
line.setPosition(QPoint(0, height));
770
line.setLineWidth(maxWidth);
771
line.setPosition(QPoint(0, height));
773
height += int(line.height());
774
widthUsed = int(qMax(qreal(widthUsed), line.naturalTextWidth()));
778
return QSize(widthUsed, height);
781
void AbstractTaskItem::drawTextLayout(QPainter *painter, const QTextLayout &layout, const QRect &rect)
783
if (rect.width() < 1 || rect.height() < 1) {
787
QPixmap pixmap(rect.size());
788
pixmap.fill(Qt::transparent);
791
p.setPen(painter->pen());
793
// Create the alpha gradient for the fade out effect
794
QLinearGradient alphaGradient(0, 0, 1, 0);
795
alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
796
if (layout.textOption().textDirection() == Qt::LeftToRight)
798
alphaGradient.setColorAt(0, QColor(0, 0, 0, 255));
799
alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
802
alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
803
alphaGradient.setColorAt(1, QColor(0, 0, 0, 255));
806
QFontMetrics fm(layout.font());
807
int textHeight = layout.lineCount() * fm.lineSpacing();
809
QPointF position(0, (rect.height() - textHeight) / 2 + (fm.tightBoundingRect("M").height() - fm.xHeight())/2);
810
QList<QRect> fadeRects;
813
// Draw each line in the layout
814
for (int i = 0; i < layout.lineCount(); i++)
816
QTextLine line = layout.lineAt(i);
817
line.draw(&p, position);
819
// Add a fade out rect to the list if the line is too long
820
if (line.naturalTextWidth() > rect.width())
822
int x = int(qMin(line.naturalTextWidth(), (qreal)pixmap.width())) - fadeWidth;
823
int y = int(line.position().y() + position.y());
824
QRect r = QStyle::visualRect(layout.textOption().textDirection(), pixmap.rect(),
825
QRect(x, y, fadeWidth, int(line.height())));
830
// Reduce the alpha in each fade out rect using the alpha gradient
831
if (!fadeRects.isEmpty())
833
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
834
foreach (const QRect &rect, fadeRects) {
835
p.fillRect(rect, alphaGradient);
843
if (qGray(textColor().rgb()) > 192) {
844
shadowColor = Qt::black;
846
shadowColor = Qt::white;
849
if (m_cachedShadow.isNull()) {
850
QImage shadow = pixmap.toImage();
851
Plasma::PaintUtils::shadowBlur(shadow, 2, shadowColor);
852
m_cachedShadow = QPixmap(shadow.size());
853
m_cachedShadow.fill(Qt::transparent);
854
QPainter buffPainter(&m_cachedShadow);
855
buffPainter.drawImage(QPoint(0,0), shadow);
858
if (shadowColor == Qt::white) {
859
painter->drawPixmap(rect.topLeft(), m_cachedShadow);
861
painter->drawPixmap(rect.topLeft() + QPoint(1,2), m_cachedShadow);
863
painter->drawPixmap(rect.topLeft(), pixmap);
867
qreal AbstractTaskItem::backgroundFadeAlpha() const
872
void AbstractTaskItem::setBackgroundFadeAlpha(qreal progress)
878
bool AbstractTaskItem::shouldIgnoreDragEvent(QGraphicsSceneDragDropEvent *event)
880
if (event->mimeData()->hasFormat(TaskManager::Task::mimetype()) ||
881
event->mimeData()->hasFormat(TaskManager::Task::groupMimetype())) {
885
if (event->mimeData()->hasFormat("text/uri-list")) {
886
// we want to check if we have executables; if so, then we treat it as a possible
887
// drop for a launcher
888
const KUrl::List uris = KUrl::List::fromMimeData(event->mimeData());
889
if (!uris.isEmpty()) {
890
foreach (const QUrl &uri, uris) {
892
if (url.isLocalFile()) {
893
const QString path = url.toLocalFile();
894
QFileInfo info(path);
895
if (info.isDir() || !info.isExecutable()) {
909
void AbstractTaskItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
911
if (shouldIgnoreDragEvent(event)) {
918
if (!m_activateTimerId) {
919
m_activateTimerId = startTimer(500);
923
void AbstractTaskItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
927
// restart the timer so that activate() is only called after the mouse
929
if (m_activateTimerId) {
930
killTimer(m_activateTimerId);
931
m_activateTimerId = startTimer(500);
935
void AbstractTaskItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
939
if (m_activateTimerId) {
940
killTimer(m_activateTimerId);
941
m_activateTimerId = 0;
945
QRect AbstractTaskItem::iconGeometry() const
947
if (!scene() || !boundingRect().isValid()) {
951
QGraphicsView *parentView = 0;
952
QGraphicsView *possibleParentView = 0;
953
// The following was taken from Plasma::Applet, it doesn't make sense to make the item an applet, and this was the easiest way around it.
954
foreach (QGraphicsView *view, scene()->views()) {
955
if (view->sceneRect().intersects(sceneBoundingRect()) ||
956
view->sceneRect().contains(scenePos())) {
957
if (view->isActiveWindow()) {
961
possibleParentView = view;
967
parentView = possibleParentView;
974
QRect rect = parentView->mapFromScene(mapToScene(boundingRect())).boundingRect().adjusted(0, 0, 1, 1);
975
rect.moveTopLeft(parentView->mapToGlobal(rect.topLeft()));
979
void AbstractTaskItem::publishIconGeometry() const
983
void AbstractTaskItem::publishIconGeometry(const QRect &rect) const
988
void AbstractTaskItem::setAnimationPos(const QPointF &pos)
990
m_layoutAnimationLock = true;
992
m_layoutAnimationLock = false;
995
QPointF AbstractTaskItem::animationPos() const
1000
void AbstractTaskItem::setGeometry(const QRectF& geometry)
1002
if (geometry == QGraphicsWidget::geometry()) {
1006
QPointF oldPos = pos();
1008
if (m_lastGeometryUpdate.elapsed() < 500) {
1009
if (m_updateGeometryTimerId) {
1010
killTimer(m_updateGeometryTimerId);
1011
m_updateGeometryTimerId = 0;
1014
m_updateGeometryTimerId = startTimer(500 - m_lastGeometryUpdate.elapsed());
1016
publishIconGeometry();
1017
m_lastGeometryUpdate.restart();
1020
//TODO:remove when we will have proper animated layouts
1021
if (m_firstGeometryUpdate && !m_layoutAnimationLock) {
1022
QRectF animStartGeom(oldPos, geometry.size());
1023
QGraphicsWidget::setGeometry(animStartGeom);
1025
if (m_layoutAnimation->state() == QAbstractAnimation::Running) {
1026
m_layoutAnimation->stop();
1029
m_layoutAnimation->setEndValue(geometry.topLeft());
1030
m_layoutAnimation->start();
1032
QGraphicsWidget::setGeometry(geometry);
1036
QRectF AbstractTaskItem::iconRect(const QRectF &b)
1039
const int right = bounds.right();
1042
//leave enough space for the text. useful in vertical panel
1043
bounds.setWidth(qMax(bounds.width() / 3, qMin(minimumSize().height(), bounds.width())));
1046
//restore right position if the layout is RTL
1047
if (QApplication::layoutDirection() == Qt::RightToLeft) {
1048
bounds.moveRight(right);
1051
QSize iconSize = icon().actualSize(bounds.size().toSize());
1053
if (iconSize.width() == iconSize.height()) {
1054
if (iconSize.width() > KIconLoader::SizeSmall && iconSize.width() < KIconLoader::SizeSmallMedium) {
1055
iconSize = QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
1056
} else if (iconSize.width() > KIconLoader::SizeSmallMedium && iconSize.width() < KIconLoader::SizeMedium) {
1057
iconSize = QSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium);
1058
} else if (iconSize.width() > KIconLoader::SizeMedium && iconSize.width() < KIconLoader::SizeLarge) {
1059
iconSize = QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium);
1063
if (iconSize != m_lastIconSize) {
1064
m_cachedShadow = QPixmap();
1066
m_lastIconSize = iconSize;
1067
return QStyle::alignedRect(QApplication::layoutDirection(),
1068
(m_showText ? Qt::AlignLeft : Qt::AlignCenter) | Qt::AlignVCenter,
1069
iconSize, bounds.toRect());
1072
QRectF AbstractTaskItem::expanderRect(const QRectF &bounds)
1074
const TaskGroupItem *groupItem = qobject_cast<const TaskGroupItem *>(this);
1079
QFontMetrics fm(KGlobalSettings::smallestReadableFont());
1080
Plasma::FrameSvg *itemBackground = m_applet->itemBackground();
1082
QSize expanderSize(qMax(fm.width(QString::number(groupItem->count())),
1083
itemBackground->elementSize(expanderElement()).width()),
1086
return QStyle::alignedRect(QApplication::layoutDirection(), Qt::AlignRight | Qt::AlignVCenter,
1087
expanderSize, bounds.toRect());
1090
QRectF AbstractTaskItem::textRect(const QRectF &bounds)
1092
QSize size(bounds.size().toSize());
1093
QRectF effectiveBounds(bounds);
1095
size.rwidth() -= int(iconRect(bounds).width()) + qMax(0, IconTextSpacing - 2);
1096
if (!isWindowItem()) {
1097
size.rwidth() -= int(expanderRect(bounds).width()) + qMax(0, IconTextSpacing - 2);
1099
if (QApplication::layoutDirection() == Qt::RightToLeft) {
1100
effectiveBounds.setLeft(expanderRect(bounds).right());
1102
effectiveBounds.setRight(expanderRect(bounds).left());
1106
return QStyle::alignedRect(QApplication::layoutDirection(), Qt::AlignRight | Qt::AlignVCenter,
1107
size, effectiveBounds.toRect());
1110
QColor AbstractTaskItem::textColor() const
1114
Plasma::Theme *theme = Plasma::Theme::defaultTheme();
1116
if ((m_oldBackgroundPrefix == "attention" || m_backgroundPrefix == "attention") &&
1117
m_applet->itemBackground()->hasElement("hint-attention-button-color")) {
1118
bool animatingBg = m_backgroundFadeAnim && m_backgroundFadeAnim->state() == QAbstractAnimation::Running;
1120
if (m_oldBackgroundPrefix == "attention") {
1126
color = KColorUtils::mix(theme->color(Plasma::Theme::TextColor),
1127
theme->color(Plasma::Theme::ButtonTextColor), bias);
1128
} else if (m_backgroundPrefix != "attention") {
1129
color = theme->color(Plasma::Theme::TextColor);
1131
color = theme->color(Plasma::Theme::ButtonTextColor);
1134
color = theme->color(Plasma::Theme::TextColor);
1137
if (m_flags & TaskIsMinimized) {
1138
color.setAlphaF(0.85);
1144
QString AbstractTaskItem::expanderElement() const
1146
switch (m_applet->location()) {
1147
case Plasma::TopEdge:
1148
return "group-expander-top";
1149
case Plasma::RightEdge:
1150
return "group-expander-right";
1151
case Plasma::LeftEdge:
1152
return "group-expander-left";
1153
case Plasma::BottomEdge:
1155
return "group-expander-bottom";
1160
bool AbstractTaskItem::isGroupMember(const TaskGroupItem *group) const
1162
if (!m_abstractItem || !group) {
1163
kDebug() <<"no task";
1167
return m_abstractItem->isGroupMember(group->group());
1171
bool AbstractTaskItem::isGrouped() const
1173
if (!m_abstractItem) {
1174
kDebug() <<"no item";
1178
return m_abstractItem->isGrouped();
1181
TaskGroupItem * AbstractTaskItem::parentGroup() const
1183
TaskGroupItem *group = qobject_cast<TaskGroupItem*>(parentWidget());
1185
//lucky case: directly in a group
1190
//in a popup or a popup's popup?
1191
QObject *candidate = parentWidget();
1194
group = qobject_cast<TaskGroupItem*>(candidate);
1195
candidate = candidate->parent();
1204
TaskManager::AbstractGroupableItem * AbstractTaskItem::abstractItem()
1206
return m_abstractItem;
1209
#include "abstracttaskitem.moc"