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

Plasma

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

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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