• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

libplasma

icon.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2007 by Aaron Seigo <aseigo@kde.org>
00003  *   Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
00004  *   Copyright 2007 by Matt Broadstone <mbroadst@gmail.com>
00005  *   Copyright 2006-2007 Fredrik Höglund <fredrik@kde.org>
00006  *   Copyright 2007 by Marco Martin <notmart@gmail.com>
00007  *   Copyright 2008 by Alexis Ménard <darktears31@gmail.com>
00008  *
00009  *   This program is free software; you can redistribute it and/or modify
00010  *   it under the terms of the GNU Library General Public License as
00011  *   published by the Free Software Foundation; either version 2, or
00012  *   (at your option) any later version.
00013  *
00014  *   This program is distributed in the hope that it will be useful,
00015  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *   GNU General Public License for more details
00018  *
00019  *   You should have received a copy of the GNU Library General Public
00020  *   License along with this program; if not, write to the
00021  *   Free Software Foundation, Inc.,
00022  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023  */
00024 
00025 #include "icon.h"
00026 #include "icon_p.h"
00027 
00028 #include <QAction>
00029 #include <QApplication>
00030 #include <QPainter>
00031 #include <QGraphicsSceneMouseEvent>
00032 #include <QGraphicsView>
00033 #include <QStyleOptionGraphicsItem>
00034 #include <QTextLayout>
00035 
00036 //#define BACKINGSTORE_BLUR_HACK
00037 
00038 #ifdef BACKINGSTORE_BLUR_HACK
00039 #include <private/qwindowsurface_p.h>
00040 #include "effects/blur.cpp"
00041 #endif
00042 
00043 #include <KGlobalSettings>
00044 #include <KIconEffect>
00045 #include <KIconLoader>
00046 #include <KIcon>
00047 #include <KUrl>
00048 #include <KRun>
00049 #include <KMimeType>
00050 #include <KDebug>
00051 #include <KColorScheme>
00052 
00053 #include <plasma/paintutils.h>
00054 #include <plasma/theme.h>
00055 
00056 #include "animator.h"
00057 #include "svg.h"
00058 
00059 namespace Plasma
00060 {
00061 
00062 IconPrivate::IconPrivate(Icon *i)
00063     : q(i),
00064       iconSvg(0),
00065       iconSize(48, 48),
00066       states(IconPrivate::NoState),
00067       orientation(Qt::Vertical),
00068       numDisplayLines(2),
00069       invertLayout(false),
00070       drawBg(false),
00071       action(0)
00072 {
00073     m_hoverAnimId = -1;
00074     m_hoverAlpha = 20/255;
00075 }
00076 
00077 IconPrivate::~IconPrivate()
00078 {
00079     qDeleteAll(cornerActions);
00080     delete iconSvg;
00081 }
00082 
00083 void Icon::readColors() 
00084 {
00085     d->textColor = Plasma::Theme::defaultTheme()->color(Theme::TextColor);
00086     d->shadowColor = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor);
00087 
00088 }
00089 
00090 IconAction::IconAction(Icon* icon, QAction *action)
00091     : m_icon(icon),
00092       m_action(action),
00093       m_hovered(false),
00094       m_pressed(false),
00095       m_visible(false),
00096       m_animationId(-1)
00097 {
00098 }
00099 
00100 void IconAction::show()
00101 {
00102     if (m_animationId) {
00103         Animator::self()->stopElementAnimation(m_animationId);
00104     }
00105 
00106     rebuildPixmap();
00107 
00108     m_animationId = Animator::self()->animateElement(m_icon, Animator::AppearAnimation);
00109     Animator::self()->setInitialPixmap(m_animationId, m_pixmap);
00110     m_visible = true;
00111 }
00112 
00113 void IconAction::hide()
00114 {
00115     if (m_animationId) {
00116         Animator::self()->stopElementAnimation(m_animationId);
00117     }
00118 
00119     rebuildPixmap();
00120 
00121     m_animationId = Animator::self()->animateElement(m_icon, Animator::DisappearAnimation);
00122     Animator::self()->setInitialPixmap(m_animationId, m_pixmap);
00123     m_visible = false;
00124 }
00125 
00126 bool IconAction::isVisible() const
00127 {
00128     return m_visible;
00129 }
00130 
00131 bool IconAction::isPressed() const
00132 {
00133     return m_pressed;
00134 }
00135 
00136 bool IconAction::isHovered() const
00137 {
00138     return m_hovered;
00139 }
00140 
00141 void IconAction::setSelected(bool selected)
00142 {
00143     m_selected = selected;
00144 }
00145 
00146 bool IconAction::isSelected() const
00147 {
00148     return m_selected;
00149 }
00150 
00151 void IconAction::setRect(const QRectF &rect)
00152 {
00153     m_rect = rect;
00154 }
00155 
00156 QRectF IconAction::rect() const
00157 {
00158     return m_rect;
00159 }
00160 
00161 void IconAction::rebuildPixmap()
00162 {
00163     // Determine proper QIcon mode based on selection status
00164     QIcon::Mode mode = QIcon::Normal;
00165     if (m_selected) {
00166         mode = QIcon::Selected;
00167     }
00168 
00169     // Draw everything
00170     m_pixmap = QPixmap(26, 26);
00171     m_pixmap.fill(Qt::transparent);
00172 
00173     int element = IconPrivate::Minibutton;
00174     if (m_pressed) {
00175         element = IconPrivate::MinibuttonPressed;
00176     } else if (m_hovered) {
00177         element = IconPrivate::MinibuttonHover;
00178     }
00179 
00180     QPainter painter(&m_pixmap);
00181     m_icon->drawActionButtonBase(&painter, m_pixmap.size(), element);
00182     m_action->icon().paint(&painter, 2, 2, 22, 22, Qt::AlignCenter, mode);
00183 }
00184 
00185 bool IconAction::event(QEvent::Type type, const QPointF &pos)
00186 {
00187     if (m_icon->size().width() < m_rect.width()*2.0 ||
00188         m_icon->size().height() < m_rect.height()*2.0) {
00189         return false;
00190     }
00191 
00192     switch (type) {
00193     case QEvent::GraphicsSceneMousePress: {
00194         setSelected(m_rect.contains(pos));
00195         return isSelected();
00196         }
00197         break;
00198 
00199     case QEvent::GraphicsSceneMouseMove: {
00200         bool wasSelected = isSelected();
00201         bool active = m_rect.contains(pos);
00202         setSelected(wasSelected && active);
00203         return (wasSelected != isSelected()) || active;
00204         }
00205         break;
00206 
00207     case QEvent::GraphicsSceneMouseRelease: {
00208         // kDebug() << "IconAction::event got a QEvent::MouseButtonRelease, " << isSelected();
00209         bool wasSelected = isSelected();
00210         setSelected(false);
00211         if (wasSelected) {
00212             m_action->trigger();
00213         }
00214 
00215         return wasSelected;
00216         }
00217         break;
00218 
00219     case QEvent::GraphicsSceneHoverEnter:
00220         m_pressed = false;
00221         m_hovered = true;
00222         break;
00223 
00224     case QEvent::GraphicsSceneHoverLeave:
00225         m_pressed = false;
00226         m_hovered = false;
00227         break;
00228 
00229     default:
00230             break;
00231     }
00232 
00233     return false;
00234 }
00235 
00236 int IconAction::animationId() const
00237 {
00238     return m_animationId;
00239 }
00240 
00241 QAction* IconAction::action() const
00242 {
00243     return m_action;
00244 }
00245 
00246 void IconAction::paint(QPainter *painter) const
00247 {
00248     if (m_icon->size().width() < m_rect.width()*2.0 ||
00249         m_icon->size().height() < m_rect.height()*2.0) {
00250         return;
00251     }
00252 
00253     QPixmap animPixmap = Animator::self()->currentPixmap(m_animationId);
00254 
00255     if (m_visible && animPixmap.isNull()) {
00256         painter->drawPixmap(m_rect.toRect(), m_pixmap);
00257     } else {
00258         painter->drawPixmap(m_rect.toRect(), animPixmap);
00259     }
00260 }
00261 
00262 Icon::Icon(QGraphicsItem *parent)
00263     : QGraphicsWidget(parent),
00264       d(new IconPrivate(this))
00265 {
00266     init();
00267 }
00268 
00269 Icon::Icon(const QString &text, QGraphicsItem *parent)
00270     : QGraphicsWidget(parent),
00271       d(new IconPrivate(this))
00272 {
00273     init();
00274     setText(text);
00275 }
00276 
00277 Icon::Icon(const QIcon &icon, const QString &text, QGraphicsItem *parent)
00278     : QGraphicsWidget(parent),
00279       d(new IconPrivate(this))
00280 {
00281     init();
00282     setText(text);
00283     setIcon(icon);
00284 }
00285 
00286 Icon::~Icon()
00287 {
00288     delete d;
00289 }
00290 
00291 void Icon::init()
00292 {
00293     readColors();
00294     connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), SLOT(readColors()));
00295 
00296     // setAcceptedMouseButtons(Qt::LeftButton);
00297     setAcceptsHoverEvents(true);
00298 
00299     int focusHMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
00300     int focusVMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
00301 
00302     // Margins for horizontal mode (list views, tree views, table views)
00303     d->setHorizontalMargin(IconPrivate::TextMargin, focusHMargin, focusVMargin);
00304     d->setHorizontalMargin(IconPrivate::IconMargin, focusHMargin, focusVMargin);
00305     d->setHorizontalMargin(IconPrivate::ItemMargin, 0, 0);
00306 
00307     // Margins for vertical mode (icon views)
00308     d->setVerticalMargin(IconPrivate::TextMargin, 6, 2);
00309     d->setVerticalMargin(IconPrivate::IconMargin, focusHMargin, focusVMargin);
00310     d->setVerticalMargin(IconPrivate::ItemMargin, 0, 0);
00311 
00312     d->setActiveMargins();
00313     d->currentSize = QSizeF(-1, -1);
00314     //setDrawStandardBackground(false);
00315 }
00316 
00317 void Icon::addIconAction(QAction *action)
00318 {
00319     int count = d->cornerActions.count();
00320     if (count > 3) {
00321         kDebug() << "no more room for more actions!";
00322     }
00323 
00324     IconAction* iconAction = new IconAction(this, action);
00325     d->cornerActions.append(iconAction);
00326     connect(action, SIGNAL(destroyed(QObject*)), this, SLOT(actionDestroyed(QObject*)));
00327 
00328     iconAction->setRect(d->actionRect((IconPrivate::ActionPosition)count));
00329 }
00330 
00331 void Icon::setAction(QAction *action)
00332 {
00333     if (d->action) {
00334         disconnect(d->action, 0, this, 0);
00335         disconnect(this, 0, d->action, 0);
00336     }
00337     d->action = action;
00338     if (action) {
00339         connect(action, SIGNAL(changed()), this, SLOT(syncToAction()));
00340         connect(this, SIGNAL(clicked()), action, SLOT(trigger()));
00341         d->syncToAction();
00342     }
00343 }
00344 
00345 QAction* Icon::action() const
00346 {
00347     return d->action;
00348 }
00349 
00350 void Icon::actionDestroyed(QObject* action)
00351 {
00352     QList<IconAction*>::iterator it = d->cornerActions.begin();
00353 
00354     while (it != d->cornerActions.end()) {
00355         if ((*it)->action() == action) {
00356             d->cornerActions.erase(it);
00357             break;
00358         }
00359     }
00360 
00361     update();   // redraw since an action has been deleted.
00362 }
00363 
00364 int Icon::numDisplayLines()
00365 {
00366     return d->numDisplayLines;
00367 }
00368 
00369 void Icon::setNumDisplayLines(int numLines)
00370 {
00371     if(numLines > d->maxDisplayLines) {
00372         d->numDisplayLines = d->maxDisplayLines;
00373     } else {
00374         d->numDisplayLines = numLines;
00375     }
00376 }
00377 
00378 void Icon::setDrawBackground(bool draw)
00379 {
00380     if (d->drawBg != draw) {
00381         d->drawBg = draw;
00382         update();
00383     }
00384 }
00385 
00386 bool Icon::drawBackground() const
00387 {
00388     return d->drawBg;
00389 }
00390 
00391 QPainterPath Icon::shape() const
00392 {
00393     if (d->currentSize.width() < 1) {
00394         return QGraphicsItem::shape();
00395     }
00396 
00397     return PaintUtils::roundedRectangle(QRectF(QPointF(0.0, 0.0), d->currentSize).adjusted(-2, -2, 2, 2), 10.0);
00398 }
00399 
00400 QSizeF IconPrivate::displaySizeHint(const QStyleOptionGraphicsItem *option, const qreal width) const
00401 {
00402     if (text.isEmpty() && infoText.isEmpty()) {
00403       return QSizeF( .0, .0 );
00404     }
00405     QString label = text;
00406     // const qreal maxWidth = (orientation == Qt::Vertical) ? iconSize.width() + 10 : 32757;
00407     // NOTE: find a way to use the other layoutText, it currently returns nominal width, when
00408     //       we actually need the actual width.
00409 
00410 
00411     qreal textWidth = width -
00412                       horizontalMargin[IconPrivate::TextMargin].left -
00413                       horizontalMargin[IconPrivate::TextMargin].right;
00414 
00415     //allow only five lines of text
00416     const qreal maxHeight = numDisplayLines*Plasma::Theme::defaultTheme()->fontMetrics().lineSpacing();
00417 
00418     // To compute the nominal size for the label + info, we'll just append
00419     // the information string to the label
00420     if (!infoText.isEmpty()) {
00421         label += QString(QChar::LineSeparator) + infoText;
00422     }
00423 
00424     QTextLayout layout;
00425     setLayoutOptions(layout, option);
00426     QSizeF size = layoutText(layout, option, label, QSizeF(textWidth, maxHeight));
00427 
00428     return addMargin(size, TextMargin);
00429 }
00430 
00431 void Icon::layoutIcons(const QStyleOptionGraphicsItem *option)
00432 {
00433     if (size() == d->currentSize) {
00434         return;
00435     }
00436 
00437     d->currentSize = size();
00438     d->setActiveMargins();
00439 
00440     //calculate icon size based on the available space
00441     qreal iconWidth;
00442 
00443     if (d->orientation == Qt::Vertical) {
00444         qreal heightAvail;
00445         //if there is text resize the icon in order to make room for the text
00446         if (!d->text.isEmpty() || !d->infoText.isEmpty()) {
00447             heightAvail = d->currentSize.height() -
00448                           d->displaySizeHint(option, d->currentSize.width()).height() -
00449                           d->verticalMargin[IconPrivate::TextMargin].top -
00450                           d->verticalMargin[IconPrivate::TextMargin].bottom;
00451             //never make a label higher than half the total height
00452             heightAvail = qMax(heightAvail, d->currentSize.height()/2);
00453         } else {
00454             heightAvail = d->currentSize.height();
00455         }
00456 
00457         //aspect ratio very "tall"
00458         if (d->currentSize.width() < heightAvail) {
00459             iconWidth = d->currentSize.width() -
00460                         d->horizontalMargin[IconPrivate::IconMargin].left -
00461                         d->horizontalMargin[IconPrivate::IconMargin].right;
00462         } else {
00463             iconWidth = heightAvail -
00464                         d->verticalMargin[IconPrivate::IconMargin].top -
00465                         d->verticalMargin[IconPrivate::IconMargin].bottom;
00466         }
00467     } else {
00468         //Horizontal layout
00469         QFontMetricsF fm(font());
00470 
00471         //if there is text resize the icon in order to make room for the text
00472         if (d->text.isEmpty() && d->infoText.isEmpty()) {
00473             // with no text, we just take up the whole geometry
00474             iconWidth = d->currentSize.width() -
00475                         d->horizontalMargin[IconPrivate::IconMargin].left -
00476                         d->horizontalMargin[IconPrivate::IconMargin].right;
00477         } else {
00478             iconWidth = d->currentSize.height() -
00479                         d->verticalMargin[IconPrivate::IconMargin].top -
00480                         d->verticalMargin[IconPrivate::IconMargin].bottom;
00481         }
00482     }
00483 
00484     d->iconSize = QSizeF(iconWidth, iconWidth);
00485 
00486     int count = 0;
00487     foreach (IconAction* iconAction, d->cornerActions) {
00488         iconAction->setRect(d->actionRect((IconPrivate::ActionPosition)count));
00489         ++count;
00490     }
00491 }
00492 
00493 void Icon::setSvg(const QString &svgFilePath, const QString &elementId)
00494 {
00495     if (!d->iconSvg) {
00496         d->iconSvg = new Plasma::Svg();
00497     }
00498 
00499     d->iconSvg->setImagePath(svgFilePath);
00500 
00501     d->iconSvgElement = elementId;
00502 }
00503 
00504 void Icon::hoverEffect(bool show)
00505 {
00506     if (show) {
00507         d->states |= IconPrivate::HoverState;
00508     }
00509 
00510     if (d->m_hoverAnimId == -1 && !d->drawBg) {
00511         // we do this only when we don't do the anim, because
00512         // this gets set at animation end when we are animating
00513         if (!show) {
00514             d->states &= ~IconPrivate::HoverState;
00515         }
00516 
00517         return;
00518     }
00519 
00520     d->m_fadeIn = show;
00521     const int FadeInDuration = 150;
00522 
00523     if (d->m_hoverAnimId != -1) {
00524         Animator::self()->stopCustomAnimation(d->m_hoverAnimId);
00525     }
00526     d->m_hoverAnimId = Animator::self()->customAnimation(40 / (1000 / FadeInDuration), FadeInDuration,
00527                                                          Animator::EaseOutCurve, this,
00528                                                          "hoverAnimationUpdate");
00529 }
00530 
00531 void Icon::hoverAnimationUpdate(qreal progress) 
00532 {
00533     if (d->m_fadeIn) {
00534         d->m_hoverAlpha = progress;
00535     } else {
00536         // If we mouse leaves before the fade in is done, fade out from where we were,
00537         // not from fully faded in
00538         qreal new_alpha = d->m_fadeIn ? progress : 1 - progress;
00539         d->m_hoverAlpha = qMin(new_alpha, d->m_hoverAlpha);
00540     }
00541     if (progress == 1) {
00542         d->m_hoverAnimId = -1;
00543     }
00544     if (!d->m_fadeIn && progress == 1 ) {
00545         d->states &= ~IconPrivate::HoverState;
00546     }
00547     update();
00548 }
00549 
00550 void IconPrivate::drawBackground(QPainter *painter, IconState state)
00551 {
00552     if (!drawBg) {
00553         return;
00554     }
00555 
00556     bool darkShadow = shadowColor.value() < 128;
00557     QColor shadow = shadowColor;
00558     QColor border = textColor;
00559 
00560     switch (state) {
00561         case IconPrivate::HoverState:
00562             shadow.setHsv(shadow.hue(),
00563                           shadow.saturation(),
00564                           shadow.value() + (int)(darkShadow ? 50 * m_hoverAlpha: -50 * m_hoverAlpha),
00565                           200 + (int)m_hoverAlpha * 55); // opacity
00566             break;
00567         case IconPrivate::PressedState:
00568             shadow.setHsv(shadow.hue(),
00569                           shadow.saturation(),
00570                           shadow.value() + (darkShadow?(int)(50*m_hoverAlpha):(int)(-50*m_hoverAlpha)),
00571                           204); //80% opacity
00572             break;
00573         default:
00574             break;
00575     }
00576     
00577     border.setAlphaF(0.3*m_hoverAlpha);
00578     shadow.setAlphaF(0.6*m_hoverAlpha);
00579 
00580     painter->save();
00581     painter->translate(0.5, 0.5);
00582     painter->setRenderHint(QPainter::Antialiasing);
00583     painter->setBrush(shadow);
00584     painter->setPen(QPen(border, 1));
00585     painter->drawPath(PaintUtils::roundedRectangle(QRectF(QPointF(1, 1), QSize((int)currentSize.width()-2, (int)currentSize.height()-2)), 5.0));
00586     painter->restore();
00587 }
00588 
00589 QPixmap IconPrivate::decoration(const QStyleOptionGraphicsItem *option, bool useHoverEffect)
00590 {
00591     QPixmap result;
00592 
00593     QIcon::Mode mode   = option->state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled;
00594     QIcon::State state = option->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
00595 
00596     if (iconSvg) {
00597         if (iconSvgPixmap.size() != iconSize.toSize()) {
00598             QImage img(iconSize.toSize(), QImage::Format_ARGB32_Premultiplied);
00599             {
00600                 img.fill(0);
00601                 QPainter p(&img);
00602                 iconSvg->resize(iconSize);
00603                 iconSvg->paint(&p, img.rect(), iconSvgElement);
00604             }
00605             iconSvgPixmap = QPixmap::fromImage(img);
00606         }
00607         result = iconSvgPixmap;
00608     } else {
00609         const QSize size = icon.actualSize(iconSize.toSize(), mode, state);
00610         result = icon.pixmap(size, mode, state);
00611     }
00612 
00613     // We disable the iconeffect here since we cannot get it into sync with
00614     // the fade animation. TODO: Enable it when animations are switched off
00615     if (!result.isNull() && useHoverEffect && !drawBg) {
00616         KIconEffect *effect = KIconLoader::global()->iconEffect();
00617         // Note that in KIconLoader terminology, active = hover.
00618         // We're assuming that the icon group is desktop/filemanager, since this
00619         // is KFileItemDelegate.
00620         if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) {
00621             result = effect->apply(result, KIconLoader::Desktop, KIconLoader::ActiveState);
00622         }
00623     }
00624 
00625     return result;
00626 }
00627 
00628 QPointF IconPrivate::iconPosition(const QStyleOptionGraphicsItem *option, const QPixmap &pixmap) const
00629 {
00630     const QRectF itemRect = subtractMargin(option->rect, IconPrivate::ItemMargin);
00631 
00632     // Compute the nominal decoration rectangle
00633     const QSizeF size = addMargin(iconSize, IconPrivate::IconMargin);
00634 
00635     Qt::LayoutDirection direction = iconDirection(option);
00636 
00637     //alignment depends from orientation and option->direction
00638     Qt::Alignment alignment;
00639     if (text.isEmpty() && infoText.isEmpty()) {
00640         alignment = Qt::AlignCenter;
00641     }else if (orientation == Qt::Vertical) {
00642         alignment = Qt::Alignment(Qt::AlignHCenter | Qt::AlignTop);
00643     //Horizontal
00644     }else{
00645         alignment = QStyle::visualAlignment(direction, Qt::Alignment(Qt::AlignLeft | Qt::AlignVCenter));
00646     }
00647 
00648     const QRect iconRect = QStyle::alignedRect(direction, alignment, size.toSize(), itemRect.toRect());
00649 
00650     // Position the pixmap in the center of the rectangle
00651     QRect pixmapRect = pixmap.rect();
00652     pixmapRect.moveCenter(iconRect.center());
00653 
00654     // add a gimmicky margin of 5px to y, TEMP TEMP TEMP
00655     // pixmapRect = pixmapRect.adjusted(0, 5, 0, 0);
00656 
00657     return QPointF(pixmapRect.topLeft());
00658 }
00659 
00660 QRectF IconPrivate::labelRectangle(const QStyleOptionGraphicsItem *option, const QPixmap &icon,
00661                                     const QString &string) const
00662 {
00663     Q_UNUSED(string)
00664 
00665     if (icon.isNull()) {
00666         return option->rect;
00667     }
00668 
00669     const QSizeF decoSize = addMargin(iconSize, IconPrivate::IconMargin);
00670     const QRectF itemRect = subtractMargin(option->rect, IconPrivate::ItemMargin);
00671     QRectF textArea(QPointF(0, 0), itemRect.size());
00672 
00673     if (orientation == Qt::Vertical) {
00674         textArea.setTop(decoSize.height() + 1);
00675     } else {
00676         //Horizontal
00677        textArea.setLeft(decoSize.width() + 1);
00678     }
00679 
00680     textArea.translate(itemRect.topLeft());
00681     return QRectF(QStyle::visualRect(iconDirection(option), option->rect, textArea.toRect()));
00682 }
00683 
00684 // Lays the text out in a rectangle no larger than constraints, eliding it as necessary
00685 QSizeF IconPrivate::layoutText(QTextLayout &layout, const QStyleOptionGraphicsItem *option,
00686                                 const QString &text, const QSizeF &constraints) const
00687 {
00688     const QSizeF size = layoutText(layout, text, constraints.width());
00689 
00690     if (size.width() > constraints.width() || size.height() > constraints.height())
00691     {
00692         const QString elided = elidedText(layout, option, constraints);
00693         return layoutText(layout, elided, constraints.width());
00694     }
00695 
00696     return size;
00697 }
00698 
00699 // Lays the text out in a rectangle no wider than maxWidth
00700 QSizeF IconPrivate::layoutText(QTextLayout &layout, const QString &text, qreal maxWidth) const
00701 {
00702     QFontMetricsF metrics(layout.font());
00703     qreal leading     = metrics.leading();
00704     qreal height      = 0.0;
00705     qreal widthUsed   = 0.0;
00706     QTextLine line;
00707 
00708     layout.setText(text);
00709 
00710     layout.beginLayout();
00711 
00712     while ((line = layout.createLine()).isValid())
00713     {
00714         line.setLineWidth(maxWidth);
00715         height += leading;
00716         line.setPosition(QPointF(0.0, height));
00717         height += line.height();
00718         widthUsed = qMax(widthUsed, line.naturalTextWidth());
00719     }
00720     layout.endLayout();
00721 
00722     return QSizeF(widthUsed, height);
00723 }
00724 
00725 // Elides the text in the layout, by iterating over each line in the layout, eliding
00726 // or word breaking the line if it's wider than the max width, and finally adding an
00727 // ellipses at the end of the last line, if there are more lines than will fit within
00728 // the vertical size constraints.
00729 QString IconPrivate::elidedText(QTextLayout &layout, const QStyleOptionGraphicsItem *option,
00730                                   const QSizeF &size) const
00731 {
00732     Q_UNUSED(option)
00733 
00734     QFontMetricsF metrics(layout.font());
00735     const QString text = layout.text();
00736     qreal maxWidth       = size.width();
00737     qreal maxHeight      = size.height();
00738     qreal height         = 0;
00739 
00740     // Elide each line that has already been laid out in the layout.
00741     QString elided;
00742     elided.reserve(text.length());
00743 
00744     for (int i = 0; i < layout.lineCount(); i++)
00745     {
00746         QTextLine line = layout.lineAt(i);
00747         int start  = line.textStart();
00748         int length = line.textLength();
00749 
00750         height += metrics.leading();
00751         if (height + line.height() + metrics.lineSpacing() > maxHeight)
00752         {
00753             // Unfortunately, if the line ends because of a line separator, elidedText() will be too
00754             // clever and keep adding lines until it finds one that's too wide.
00755             if (line.naturalTextWidth() < maxWidth && start+length > 0 && text[start + length - 1] == QChar::LineSeparator)
00756                 elided += text.mid(start, length - 1);
00757             else
00758                 elided += metrics.elidedText(text.mid(start), Qt::ElideRight, maxWidth);
00759             break;
00760         }
00761         else if (line.naturalTextWidth() > maxWidth)
00762             elided += metrics.elidedText(text.mid(start, length), Qt::ElideRight, maxWidth);
00763         else
00764             elided += text.mid(start, length);
00765 
00766         height += line.height();
00767     }
00768 
00769     return elided;
00770 }
00771 
00772 void IconPrivate::layoutTextItems(const QStyleOptionGraphicsItem *option,
00773                                     const QPixmap &icon, QTextLayout *labelLayout,
00774                                     QTextLayout *infoLayout, QRectF *textBoundingRect) const
00775 {
00776     bool showInformation = false;
00777 
00778     setLayoutOptions(*labelLayout, option);
00779 
00780     QFontMetricsF fm(labelLayout->font());
00781     const QRectF textArea = labelRectangle(option, icon, text);
00782     QRectF textRect = subtractMargin(textArea, IconPrivate::TextMargin);
00783 
00784     //kDebug() << this << "text area" << textArea << "text rect" << textRect;
00785     // Sizes and constraints for the different text parts
00786     QSizeF maxLabelSize = textRect.size();
00787     QSizeF maxInfoSize  = textRect.size();
00788     QSizeF labelSize;
00789     QSizeF infoSize;
00790 
00791     // If we have additional info text, and there's space for at least two lines of text,
00792     // adjust the max label size to make room for at least one line of the info text
00793     if (!infoText.isEmpty() && textRect.height() >= fm.lineSpacing() * 2)
00794     {
00795         infoLayout->setFont(labelLayout->font());
00796         infoLayout->setTextOption(labelLayout->textOption());
00797 
00798         maxLabelSize.rheight() -= fm.lineSpacing();
00799         showInformation = true;
00800     }
00801 
00802     // Lay out the label text, and adjust the max info size based on the label size
00803     labelSize = layoutText(*labelLayout, option, text, maxLabelSize);
00804     maxInfoSize.rheight() -= labelSize.height();
00805 
00806     // Lay out the info text
00807     if (showInformation) {
00808         infoSize = layoutText(*infoLayout, option, infoText, maxInfoSize);
00809     } else {
00810         infoSize = QSizeF(0, 0);
00811     }
00812     // Compute the bounding rect of the text
00813     const Qt::Alignment alignment = labelLayout->textOption().alignment();
00814     const QSizeF size(qMax(labelSize.width(), infoSize.width()), labelSize.height() + infoSize.height());
00815     *textBoundingRect = QStyle::alignedRect(iconDirection(option), alignment, size.toSize(), textRect.toRect());
00816 
00817     // Compute the positions where we should draw the layouts
00818     labelLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y()));
00819     infoLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y() + labelSize.height()));
00820     //kDebug() << "final position is" << labelLayout->position();
00821 }
00822 
00823 QBrush IconPrivate::foregroundBrush(const QStyleOptionGraphicsItem *option) const
00824 {
00825     const QPalette::ColorGroup group = option->state & QStyle::State_Enabled ?
00826             QPalette::Normal : QPalette::Disabled;
00827 
00828     // Always use the highlight color for selected items
00829     if (option->state & QStyle::State_Selected) {
00830         return option->palette.brush(group, QPalette::HighlightedText);
00831     }
00832     return option->palette.brush(group, QPalette::Text);
00833 }
00834 
00835 QBrush IconPrivate::backgroundBrush(const QStyleOptionGraphicsItem *option) const
00836 {
00837     const QPalette::ColorGroup group = option->state & QStyle::State_Enabled ?
00838             QPalette::Normal : QPalette::Disabled;
00839 
00840     QBrush background(Qt::NoBrush);
00841 
00842     // Always use the highlight color for selected items
00843     if (option->state & QStyle::State_Selected) {
00844         background = option->palette.brush(group, QPalette::Highlight);
00845     }
00846     return background;
00847 }
00848 
00849 void IconPrivate::drawTextItems(QPainter *painter, const QStyleOptionGraphicsItem *option,
00850                                   const QTextLayout &labelLayout, const QTextLayout &infoLayout) const
00851 {
00852     Q_UNUSED(option)
00853 
00854     painter->save();
00855     painter->setPen(textColor);
00856 
00857     // the translation prevents odd rounding errors in labelLayout.position()
00858     // when applied to the canvas
00859     painter->translate(0.5, 0.5);
00860 
00861     labelLayout.draw(painter, QPointF());
00862 
00863     if (!infoLayout.text().isEmpty()) {
00864         painter->setPen(textColor);
00865         infoLayout.draw(painter, QPointF());
00866     }
00867     painter->restore();
00868 }
00869 
00870 
00871 void Icon::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
00872 {
00873     Q_UNUSED(widget)
00874 
00875 #ifdef BACKINGSTORE_BLUR_HACK
00876      if (d->state == IconPrivate::HoverState && scene()) {
00877          QList<QGraphicsView*> views = scene()->views();
00878          if (views.count() > 0) {
00879              QPixmap* pix = static_cast<QPixmap*>(views[0]->windowSurface()->paintDevice());
00880              QImage image(boundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied);
00881              {
00882                  QPainter p(&image);
00883                  p.drawPixmap(image.rect(), *pix, sceneBoundingRect());
00884              }
00885              expblur<16,7>(image, 8);
00886              painter->drawImage(0, 0, image);
00887          }
00888      }
00889 #endif
00890 
00891     //Lay out the main icon and action icons
00892     layoutIcons(option);
00893 
00894     // Compute the metrics, and lay out the text items
00895     // ========================================================================
00896     IconPrivate::IconState state = IconPrivate::NoState;
00897     if (d->states & IconPrivate::ManualPressedState) {
00898         state = IconPrivate::PressedState;
00899     } else if (d->states & IconPrivate::PressedState) {
00900         if (d->states & IconPrivate::HoverState) {
00901             state = IconPrivate::PressedState;
00902         }
00903     } else if (d->states & IconPrivate::HoverState) {
00904         state = IconPrivate::HoverState;
00905     }
00906 
00907     QPixmap icon = d->decoration(option, state != IconPrivate::NoState);
00908     const QPointF iconPos = d->iconPosition(option, icon);
00909 
00910     d->drawBackground(painter, state);
00911 
00912     // draw icon
00913     if (!icon.isNull()) {
00914         painter->drawPixmap(iconPos, icon);
00915     }
00916 
00917     // Draw corner actions
00918     foreach (const IconAction *action, d->cornerActions) {
00919         if (action->animationId()) {
00920             action->paint(painter);
00921         }
00922     }
00923 
00924     // Draw text last because its overlayed
00925     QTextLayout labelLayout, infoLayout;
00926     QRectF textBoundingRect;
00927     d->layoutTextItems(option, icon, &labelLayout, &infoLayout, &textBoundingRect);
00928 
00929     QImage shadow(textBoundingRect.size().toSize()+QSize(4,4), QImage::Format_ARGB32_Premultiplied);
00930     shadow.fill(Qt::transparent);
00931     {
00932         QPainter buffPainter(&shadow);
00933         buffPainter.translate(-textBoundingRect.x(), -textBoundingRect.y());
00934         d->drawTextItems(&buffPainter, option, labelLayout, infoLayout);
00935     }
00936 
00937     QPoint shadowOffset = QPoint(1,2);
00938     if (d->shadowColor.value() > 128) {
00939         shadowOffset = QPoint(0,1);
00940     }
00941 
00942     PaintUtils::shadowBlur(shadow, 2, d->shadowColor);
00943     painter->drawImage(textBoundingRect.topLeft()+shadowOffset, shadow);
00944     d->drawTextItems(painter, option, labelLayout, infoLayout);
00945 }
00946 
00947 void Icon::drawActionButtonBase(QPainter* painter, const QSize &size, int element)
00948 {
00949     qreal radius = size.width()/2;
00950     QRadialGradient gradient(radius, radius, radius, radius, radius);
00951     int alpha;
00952 
00953     if (element == IconPrivate::MinibuttonPressed) {
00954         alpha = 255;
00955     } else if (element == IconPrivate::MinibuttonHover) {
00956         alpha = 200;
00957     } else {
00958         alpha = 160;
00959     }
00960     gradient.setColorAt(0, QColor::fromRgb(d->textColor.red(),
00961                                            d->textColor.green(),
00962                                            d->textColor.blue(), alpha));
00963     gradient.setColorAt(1, QColor::fromRgb(d->textColor.red(),
00964                                            d->textColor.green(),
00965                                            d->textColor.blue(), 0));
00966 
00967     painter->setBrush(gradient);
00968     painter->setPen(Qt::NoPen);
00969     painter->drawEllipse(QRectF(QPointF(.0, .0), size));
00970 }
00971 
00972 
00973 void Icon::setText(const QString& text)
00974 {
00975     d->text = text;
00976     // cause a relayout
00977     // FIXME: is invalidating d->currentSize really necessary?
00978     // FIXME: is d->currentSize really necessary at all?
00979     d->currentSize = QSizeF(-1, -1);
00980     resize(sizeFromIconSize(d->iconSize.width()));
00981 }
00982 
00983 QString Icon::text() const
00984 {
00985     return d->text;
00986 }
00987 
00988 void Icon::setInfoText(const QString& text)
00989 {
00990     d->infoText = text;
00991     // cause a relayout
00992     // FIXME: is invalidating d->currentSize really necessary?
00993     // FIXME: is d->currentSize really necessary at all?
00994     d->currentSize = QSizeF(-1, -1);
00995     resize(sizeFromIconSize(d->iconSize.width()));
00996 }
00997 
00998 QString Icon::infoText() const
00999 {
01000     return d->infoText;
01001 }
01002 
01003 QIcon Icon::icon() const
01004 {
01005     return d->icon;
01006 }
01007 
01008 void Icon::setIcon(const QString& icon)
01009 {
01010     if (icon.isEmpty()) {
01011         setIcon(QIcon());
01012         return;
01013     }
01014 
01015     setIcon(KIcon(icon));
01016 }
01017 
01018 void Icon::setIcon(const QIcon& icon)
01019 {
01020     d->icon = icon;
01021 }
01022 
01023 QSizeF Icon::iconSize() const
01024 {
01025     return d->iconSize;
01026 }
01027 
01028 bool Icon::isDown()
01029 {
01030     return d->states & IconPrivate::PressedState;
01031 }
01032 
01033 void Icon::mousePressEvent(QGraphicsSceneMouseEvent *event)
01034 {
01035     if (event->button() != Qt::LeftButton) {
01036         QGraphicsWidget::mousePressEvent(event);
01037         return;
01038     }
01039 
01040     d->states |= IconPrivate::PressedState;
01041     d->clickStartPos = scenePos();
01042 
01043     bool handled = false;
01044     foreach (IconAction *action, d->cornerActions) {
01045         handled = action->event(event->type(), event->pos());
01046         if (handled) {
01047             break;
01048         }
01049     }
01050 
01051     if (!handled && geometry().contains(event->pos())) {
01052         emit pressed(true);
01053     }
01054 
01055     update();
01056 }
01057 
01058 void Icon::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
01059 {
01060     if (~d->states & IconPrivate::PressedState) {
01061         QGraphicsWidget::mouseMoveEvent(event);
01062         return;
01063     }
01064 
01065     if (boundingRect().contains(event->pos())) {
01066         if (~d->states & IconPrivate::HoverState) {
01067             d->states |= IconPrivate::HoverState;
01068             update();
01069         }
01070     } else {
01071         if (d->states & IconPrivate::HoverState) {
01072             d->states &= ~IconPrivate::HoverState;
01073             update();
01074         }
01075     }
01076 }
01077 
01078 void Icon::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
01079 {
01080     if (~d->states & IconPrivate::PressedState) {
01081         QGraphicsWidget::mouseMoveEvent(event);
01082         return;
01083     }
01084 
01085     d->states &= ~IconPrivate::PressedState;
01086 
01087     //don't pass click when the mouse was moved
01088     bool handled = d->clickStartPos != scenePos();
01089     if (!handled) {
01090         foreach (IconAction *action, d->cornerActions) {
01091             if (action->event(event->type(), event->pos())) {
01092                 handled = true;
01093                 break;
01094             }
01095         }
01096     }
01097 
01098     if (!handled) {
01099         if (boundingRect().contains(event->pos())) {
01100             emit clicked();
01101             if (KGlobalSettings::singleClick()) {
01102                emit activated();
01103             }
01104         }
01105         emit pressed(false);
01106     }
01107 
01108     update();
01109 }
01110 
01111 void Icon::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
01112 {
01113     Q_UNUSED(event)
01114 
01115     emit doubleClicked();
01116     if (!KGlobalSettings::singleClick()) {
01117         emit activated();
01118     }
01119 }
01120 
01121 void Icon::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
01122 {
01123     foreach (IconAction *action, d->cornerActions) {
01124         action->show();
01125         action->event(event->type(), event->pos());
01126     }
01127     hoverEffect(true);
01128     update();
01129 
01130     QGraphicsWidget::hoverEnterEvent(event);
01131 }
01132 
01133 void Icon::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
01134 {
01135     foreach (IconAction *action, d->cornerActions) {
01136         action->hide();
01137         action->event(event->type(), event->pos());
01138     }
01139     // d->states &= ~IconPrivate::HoverState; // Will be set once progress is zero again ... 
01140     hoverEffect(false);
01141     update();
01142 
01143     QGraphicsWidget::hoverLeaveEvent(event);
01144 }
01145 
01146 void Icon::setPressed(bool pressed)
01147 {
01148     if (pressed) {
01149         d->states |= IconPrivate::ManualPressedState;
01150         d->states |= IconPrivate::PressedState;
01151     } else {
01152         d->states &= ~IconPrivate::ManualPressedState;
01153         d->states &= ~IconPrivate::PressedState;
01154     }
01155     update();
01156 }
01157 
01158 void Icon::setUnpressed()
01159 {
01160     setPressed(false);
01161 }
01162 
01163 void IconPrivate::syncToAction()
01164 {
01165     if (!action) {
01166         return;
01167     }
01168     //we don't get told *what* changed, just that something changed
01169     //so we update everything we care about
01170     q->setIcon(action->icon());
01171     q->setText(action->iconText());
01172     q->setEnabled(action->isEnabled());
01173     //TODO use action's tooltip too
01174 
01175     emit q->changed();
01176 }
01177 
01178 void Icon::setOrientation(Qt::Orientation orientation)
01179 {
01180     d->orientation = orientation;
01181     resize(sizeFromIconSize(d->iconSize.width()));
01182 }
01183 
01184 void Icon::invertLayout(bool invert)
01185 {
01186     d->invertLayout = invert;
01187 }
01188 
01189 bool Icon::invertedLayout() const
01190 {
01191     return d->invertLayout;
01192 }
01193 
01194 QSizeF Icon::sizeFromIconSize(const qreal iconWidth) const
01195 {
01196     if (d->text.isEmpty() && d->infoText.isEmpty()) {
01197         //no text, less calculations
01198         return d->addMargin(d->addMargin(QSizeF(iconWidth, iconWidth), IconPrivate::IconMargin),
01199                             IconPrivate::ItemMargin);
01200     }
01201 
01202     QFontMetricsF fm = Plasma::Theme::defaultTheme()->fontMetrics();
01203     qreal width = 0;
01204 
01205     if (d->orientation == Qt::Vertical) {
01206         // make room for at most 14 characters
01207         width = qMax(fm.width(d->text.left(12)),
01208                      fm.width(d->infoText.left(12))) +
01209                      fm.width("xx") +
01210                      d->horizontalMargin[IconPrivate::TextMargin].left +
01211                      d->horizontalMargin[IconPrivate::TextMargin].right;
01212 
01213         width = qMax(width,
01214                      iconWidth +
01215                      d->horizontalMargin[IconPrivate::IconMargin].left +
01216                      d->horizontalMargin[IconPrivate::IconMargin].right);
01217     } else {
01218         width = iconWidth +
01219                 d->horizontalMargin[IconPrivate::IconMargin].left +
01220                 d->horizontalMargin[IconPrivate::IconMargin].right +
01221                 qMax(fm.width(d->text), fm.width(d->infoText)) + fm.width("xx") +
01222                 d->horizontalMargin[IconPrivate::TextMargin].left +
01223                 d->horizontalMargin[IconPrivate::TextMargin].right;
01224     }
01225 
01226     qreal height;
01227     qreal textHeight;
01228 
01229     QStyleOptionGraphicsItem option;
01230     option.state = QStyle::State_None;
01231     option.rect = boundingRect().toRect();
01232     textHeight = d->displaySizeHint(&option, width).height();
01233 
01234     if (d->orientation == Qt::Vertical) {
01235         height = iconWidth + textHeight +
01236                  d->verticalMargin[IconPrivate::TextMargin].top +
01237                  d->verticalMargin[IconPrivate::TextMargin].bottom +
01238                  d->verticalMargin[IconPrivate::IconMargin].top +
01239                  d->verticalMargin[IconPrivate::IconMargin].bottom;
01240     } else {
01241         //Horizontal
01242         height = qMax(iconWidth +
01243                       d->verticalMargin[IconPrivate::IconMargin].top +
01244                       d->verticalMargin[IconPrivate::IconMargin].bottom,
01245                       textHeight +
01246                       d->verticalMargin[IconPrivate::TextMargin].top +
01247                       d->verticalMargin[IconPrivate::TextMargin].bottom);
01248     }
01249 
01250     return d->addMargin(QSizeF(width, height), IconPrivate::ItemMargin);
01251 }
01252 
01253 } // namespace Plasma
01254 
01255 #include "icon.moc"

libplasma

Skip menu "libplasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal