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