~ubuntu-branches/ubuntu/quantal/kde-baseapps/quantal

« back to all changes in this revision

Viewing changes to dolphin/src/kitemviews/kfileitemlistwidget.cpp

  • Committer: Package Import Robot
  • Author(s): Philip Muškovac
  • Date: 2012-05-25 17:26:17 UTC
  • mfrom: (1.1.12)
  • Revision ID: package-import@ubuntu.com-20120525172617-xr9bzhlugldbflvq
Tags: 4:4.8.80a-0ubuntu1
* Merge with debian git, remainging changes:
  - keep breaks/replaces
  - keep symbol files
  - keep kubuntu patches
  - dolphin suggests and not recommends ruby
  - add kfind, konsole and kompare to dolphin suggests
* konq-plugins breaks/replaces kde-baseapps-data << 4:4.8.80

* Drop squence numbers from kubuntu patches 

* New upstream beta release
  - refresh kubuntu_fix_systemsettings_about_me.diff
  - add new doc images to {konqueror,dolphin}.install

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
#include "kfileitemlistwidget.h"
21
21
 
22
 
#include "kfileitemclipboard_p.h"
23
 
#include "kfileitemmodel.h"
24
 
#include "kitemlistview.h"
25
 
#include "kpixmapmodifier_p.h"
26
 
 
27
 
#include <KIcon>
28
 
#include <KIconEffect>
29
 
#include <KIconLoader>
 
22
#include <KDebug>
 
23
#include <KGlobal>
30
24
#include <KLocale>
31
 
#include <KStringHandler>
32
 
#include <KDebug>
33
 
 
34
 
#include <QFontMetricsF>
35
 
#include <QGraphicsSceneResizeEvent>
36
 
#include <QPainter>
37
 
#include <QStyleOption>
38
 
#include <QTextLayout>
39
 
#include <QTextLine>
40
 
 
41
 
// #define KFILEITEMLISTWIDGET_DEBUG
42
 
 
43
 
KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) :
44
 
    KItemListWidget(parent),
45
 
    m_isCut(false),
46
 
    m_isHidden(false),
47
 
    m_isExpandable(false),
48
 
    m_supportsItemExpanding(false),
49
 
    m_dirtyLayout(true),
50
 
    m_dirtyContent(true),
51
 
    m_dirtyContentRoles(),
52
 
    m_layout(IconsLayout),
53
 
    m_pixmapPos(),
54
 
    m_pixmap(),
55
 
    m_scaledPixmapSize(),
56
 
    m_iconRect(),
57
 
    m_hoverPixmap(),
58
 
    m_textPos(),
59
 
    m_text(),
60
 
    m_textRect(),
61
 
    m_sortedVisibleRoles(),
62
 
    m_expansionArea(),
63
 
    m_customTextColor(),
64
 
    m_additionalInfoTextColor(),
65
 
    m_overlay()
66
 
{
67
 
    for (int i = 0; i < TextIdCount; ++i) {
68
 
        m_text[i].setTextFormat(Qt::PlainText);
69
 
        m_text[i].setPerformanceHint(QStaticText::AggressiveCaching);
70
 
    }
71
 
}
72
 
 
73
 
KFileItemListWidget::~KFileItemListWidget()
74
 
{
75
 
}
76
 
 
77
 
void KFileItemListWidget::setLayout(Layout layout)
78
 
{
79
 
    if (m_layout != layout) {
80
 
        m_layout = layout;
81
 
        m_dirtyLayout = true;
82
 
        updateAdditionalInfoTextColor();
83
 
        update();
84
 
    }
85
 
}
86
 
 
87
 
KFileItemListWidget::Layout KFileItemListWidget::layout() const
88
 
{
89
 
    return m_layout;
90
 
}
91
 
 
92
 
void KFileItemListWidget::setSupportsItemExpanding(bool supportsItemExpanding)
93
 
{
94
 
    if (m_supportsItemExpanding != supportsItemExpanding) {
95
 
        m_supportsItemExpanding = supportsItemExpanding;
96
 
        m_dirtyLayout = true;
97
 
        update();
98
 
    }
99
 
}
100
 
 
101
 
bool KFileItemListWidget::supportsItemExpanding() const
102
 
{
103
 
    return m_supportsItemExpanding;
104
 
}
105
 
 
106
 
void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
107
 
{
108
 
    const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
109
 
 
110
 
    KItemListWidget::paint(painter, option, widget);
111
 
 
112
 
    if (!m_expansionArea.isEmpty()) {
113
 
        drawSiblingsInformation(painter);
114
 
    }
115
 
 
116
 
    const KItemListStyleOption& itemListStyleOption = styleOption();
117
 
    if (isHovered()) {
118
 
        // Blend the unhovered and hovered pixmap if the hovering
119
 
        // animation is ongoing
120
 
        if (hoverOpacity() < 1.0) {
121
 
            drawPixmap(painter, m_pixmap);
122
 
        }
123
 
 
124
 
        const qreal opacity = painter->opacity();
125
 
        painter->setOpacity(hoverOpacity() * opacity);
126
 
        drawPixmap(painter, m_hoverPixmap);
127
 
        painter->setOpacity(opacity);
128
 
    } else {
129
 
        drawPixmap(painter, m_pixmap);
130
 
    }
131
 
 
132
 
    painter->setFont(itemListStyleOption.font);
133
 
    painter->setPen(textColor());
134
 
    painter->drawStaticText(m_textPos[Name], m_text[Name]);
135
 
 
136
 
    bool clipAdditionalInfoBounds = false;
137
 
    if (m_supportsItemExpanding) {
138
 
        // Prevent a possible overlapping of the additional-information texts
139
 
        // with the icon. This can happen if the user has minimized the width
140
 
        // of the name-column to a very small value.
141
 
        const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.padding;
142
 
        if (m_textPos[Name + 1].x() < minX) {
143
 
            clipAdditionalInfoBounds = true;
144
 
            painter->save();
145
 
            painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip);
146
 
        }
147
 
    }
148
 
 
149
 
    painter->setPen(m_additionalInfoTextColor);
150
 
    painter->setFont(itemListStyleOption.font);
151
 
    for (int i = Name + 1; i < TextIdCount; ++i) {
152
 
        painter->drawStaticText(m_textPos[i], m_text[i]);
153
 
    }
154
 
 
155
 
    if (clipAdditionalInfoBounds) {
156
 
        painter->restore();
157
 
    }
158
 
 
159
 
#ifdef KFILEITEMLISTWIDGET_DEBUG
160
 
    painter->setBrush(Qt::NoBrush);
161
 
    painter->setPen(Qt::green);
162
 
    painter->drawRect(m_iconRect);
163
 
 
164
 
    painter->setPen(Qt::red);
165
 
    painter->drawText(QPointF(0, itemListStyleOption.fontMetrics.height()), QString::number(index()));
166
 
    painter->drawRect(rect());
167
 
#endif
168
 
}
169
 
 
170
 
QRectF KFileItemListWidget::iconRect() const
171
 
{
172
 
    const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
173
 
    return m_iconRect;
174
 
}
175
 
 
176
 
QRectF KFileItemListWidget::textRect() const
177
 
{
178
 
    const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
179
 
    return m_textRect;
180
 
}
181
 
 
182
 
QRectF KFileItemListWidget::expansionToggleRect() const
183
 
{
184
 
    const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
185
 
    return m_isExpandable ? m_expansionArea : QRectF();
186
 
}
187
 
 
188
 
QRectF KFileItemListWidget::selectionToggleRect() const
189
 
{
190
 
    const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
191
 
 
192
 
    const int iconHeight = styleOption().iconSize;
193
 
 
194
 
    int toggleSize = KIconLoader::SizeSmall;
195
 
    if (iconHeight >= KIconLoader::SizeEnormous) {
196
 
        toggleSize = KIconLoader::SizeMedium;
197
 
    } else if (iconHeight >= KIconLoader::SizeLarge) {
198
 
        toggleSize = KIconLoader::SizeSmallMedium;
199
 
    }
200
 
 
201
 
    QPointF pos = iconRect().topLeft();
202
 
 
203
 
    // If the selection toggle has a very small distance to the
204
 
    // widget borders, the size of the selection toggle will get
205
 
    // increased to prevent an accidental clicking of the item
206
 
    // when trying to hit the toggle.
207
 
    const int widgetHeight = size().height();
208
 
    const int widgetWidth = size().width();
209
 
    const int minMargin = 2;
210
 
 
211
 
    if (toggleSize + minMargin * 2 >= widgetHeight) {
212
 
        pos.rx() -= (widgetHeight - toggleSize) / 2;
213
 
        toggleSize = widgetHeight;
214
 
        pos.setY(0);
215
 
    }
216
 
    if (toggleSize + minMargin * 2 >= widgetWidth) {
217
 
        pos.ry() -= (widgetWidth - toggleSize) / 2;
218
 
        toggleSize = widgetWidth;
219
 
        pos.setX(0);
220
 
    }
221
 
 
222
 
    return QRectF(pos, QSizeF(toggleSize, toggleSize));
223
 
}
224
 
 
225
 
QString KFileItemListWidget::roleText(const QByteArray& role, const QHash<QByteArray, QVariant>& values)
 
25
#include <KIO/MetaData>
 
26
#include <QDateTime>
 
27
 
 
28
KFileItemListWidgetInformant::KFileItemListWidgetInformant() :
 
29
    KStandardItemListWidgetInformant()
 
30
{
 
31
}
 
32
 
 
33
KFileItemListWidgetInformant::~KFileItemListWidgetInformant()
 
34
{
 
35
}
 
36
 
 
37
QString KFileItemListWidgetInformant::roleText(const QByteArray& role,
 
38
                                               const QHash<QByteArray, QVariant>& values) const
226
39
{
227
40
    QString text;
228
41
    const QVariant roleValue = values.value(role);
229
42
 
230
 
    switch (roleTextId(role)) {
231
 
    case Name:
232
 
    case Permissions:
233
 
    case Owner:
234
 
    case Group:
235
 
    case Type:
236
 
    case Destination:
237
 
    case Path:
238
 
        text = roleValue.toString();
239
 
        break;
 
43
    // Implementation note: In case if more roles require a custom handling
 
44
    // use a hash + switch for a linear runtime.
240
45
 
241
 
    case Size: {
 
46
    if (role == "size") {
242
47
        if (values.value("isDir").toBool()) {
243
48
            // The item represents a directory. Show the number of sub directories
244
49
            // instead of the file size of the directory.
254
59
            const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
255
60
            text = KGlobal::locale()->formatByteSize(size);
256
61
        }
257
 
        break;
258
 
    }
259
 
 
260
 
    case Date: {
 
62
    } else if (role == "date") {
261
63
        const QDateTime dateTime = roleValue.toDateTime();
262
64
        text = KGlobal::locale()->formatDateTime(dateTime);
263
 
        break;
264
 
    }
265
 
 
266
 
    default:
267
 
        Q_ASSERT(false);
268
 
        break;
 
65
    } else {
 
66
        text = KStandardItemListWidgetInformant::roleText(role, values);
269
67
    }
270
68
 
271
69
    return text;
272
70
}
273
71
 
274
 
void KFileItemListWidget::invalidateCache()
275
 
{
276
 
    m_dirtyLayout = true;
277
 
    m_dirtyContent = true;
278
 
}
279
 
 
280
 
void KFileItemListWidget::refreshCache()
281
 
{
282
 
}
283
 
 
284
 
void KFileItemListWidget::setTextColor(const QColor& color)
285
 
{
286
 
    if (color != m_customTextColor) {
287
 
        m_customTextColor = color;
288
 
        updateAdditionalInfoTextColor();
289
 
        update();
290
 
    }
291
 
}
292
 
 
293
 
QColor KFileItemListWidget::textColor() const
294
 
{
295
 
    if (m_customTextColor.isValid() && !isSelected()) {
296
 
        return m_customTextColor;
297
 
    }
298
 
 
299
 
    const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
300
 
    const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : QPalette::Text;
301
 
    return styleOption().palette.brush(group, role).color();
302
 
}
303
 
 
304
 
void KFileItemListWidget::setOverlay(const QPixmap& overlay)
305
 
{
306
 
    m_overlay = overlay;
307
 
    m_dirtyContent = true;
308
 
    update();
309
 
}
310
 
 
311
 
QPixmap KFileItemListWidget::overlay() const
312
 
{
313
 
    return m_overlay;
314
 
}
315
 
 
316
 
void KFileItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
317
 
                                      const QSet<QByteArray>& roles)
318
 
{
319
 
    Q_UNUSED(current);
320
 
 
321
 
    m_dirtyContent = true;
322
 
 
323
 
    QSet<QByteArray> dirtyRoles;
324
 
    if (roles.isEmpty()) {
325
 
        dirtyRoles = visibleRoles().toSet();
326
 
        dirtyRoles.insert("iconPixmap");
327
 
        dirtyRoles.insert("iconName");
328
 
    } else {
329
 
        dirtyRoles = roles;
330
 
    }
331
 
 
332
 
    QSetIterator<QByteArray> it(dirtyRoles);
333
 
    while (it.hasNext()) {
334
 
        const QByteArray& role = it.next();
335
 
        m_dirtyContentRoles.insert(role);
336
 
    }
337
 
}
338
 
 
339
 
void KFileItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
340
 
                                              const QList<QByteArray>& previous)
341
 
{
342
 
    Q_UNUSED(previous);
343
 
    m_sortedVisibleRoles = current;
344
 
    m_dirtyLayout = true;
345
 
}
346
 
 
347
 
void KFileItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
348
 
                                                   const QHash<QByteArray, QSizeF>& previous)
349
 
{
350
 
    Q_UNUSED(current);
351
 
    Q_UNUSED(previous);
352
 
    m_dirtyLayout = true;
353
 
}
354
 
 
355
 
void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
356
 
                                             const KItemListStyleOption& previous)
357
 
{
358
 
    Q_UNUSED(current);
359
 
    Q_UNUSED(previous);
360
 
    updateAdditionalInfoTextColor();
361
 
    m_dirtyLayout = true;
362
 
}
363
 
 
364
 
void KFileItemListWidget::hoveredChanged(bool hovered)
365
 
{
366
 
    Q_UNUSED(hovered);
367
 
    m_dirtyLayout = true;
368
 
}
369
 
 
370
 
void KFileItemListWidget::selectedChanged(bool selected)
371
 
{
372
 
    Q_UNUSED(selected);
373
 
    updateAdditionalInfoTextColor();
374
 
}
375
 
 
376
 
void KFileItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous)
377
 
{
378
 
    Q_UNUSED(current);
379
 
    Q_UNUSED(previous);
380
 
    m_dirtyLayout = true;
381
 
}
382
 
 
383
 
 
384
 
void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
385
 
{
386
 
    KItemListWidget::resizeEvent(event);
387
 
    m_dirtyLayout = true;
388
 
}
389
 
 
390
 
void KFileItemListWidget::showEvent(QShowEvent* event)
391
 
{
392
 
    KItemListWidget::showEvent(event);
393
 
 
394
 
    // Listen to changes of the clipboard to mark the item as cut/uncut
395
 
    KFileItemClipboard* clipboard = KFileItemClipboard::instance();
396
 
 
397
 
    const KUrl itemUrl = data().value("url").value<KUrl>();
398
 
    m_isCut = clipboard->isCut(itemUrl);
399
 
 
400
 
    connect(clipboard, SIGNAL(cutItemsChanged()),
401
 
            this, SLOT(slotCutItemsChanged()));
402
 
}
403
 
 
404
 
void KFileItemListWidget::hideEvent(QHideEvent* event)
405
 
{
406
 
    disconnect(KFileItemClipboard::instance(), SIGNAL(cutItemsChanged()),
407
 
               this, SLOT(slotCutItemsChanged()));
408
 
 
409
 
    KItemListWidget::hideEvent(event);
410
 
}
411
 
 
412
 
void KFileItemListWidget::slotCutItemsChanged()
413
 
{
414
 
    const KUrl itemUrl = data().value("url").value<KUrl>();
415
 
    const bool isCut = KFileItemClipboard::instance()->isCut(itemUrl);
416
 
    if (m_isCut != isCut) {
417
 
        m_isCut = isCut;
418
 
        m_pixmap = QPixmap();
419
 
        m_dirtyContent = true;
420
 
        update();
421
 
    }
422
 
}
423
 
 
424
 
void KFileItemListWidget::triggerCacheRefreshing()
425
 
{
426
 
    if ((!m_dirtyContent && !m_dirtyLayout) || index() < 0) {
427
 
        return;
428
 
    }
429
 
 
430
 
    refreshCache();
431
 
 
432
 
    const QHash<QByteArray, QVariant> values = data();
433
 
    m_isExpandable = m_supportsItemExpanding && values["isExpandable"].toBool();
434
 
    m_isHidden = values["name"].toString().startsWith(QLatin1Char('.'));
435
 
 
436
 
    updateExpansionArea();
437
 
    updateTextsCache();
438
 
    updatePixmapCache();
439
 
 
440
 
    m_dirtyLayout = false;
441
 
    m_dirtyContent = false;
442
 
    m_dirtyContentRoles.clear();
443
 
}
444
 
 
445
 
void KFileItemListWidget::updateExpansionArea()
446
 
{
447
 
    if (m_supportsItemExpanding) {
448
 
        const QHash<QByteArray, QVariant> values = data();
449
 
        Q_ASSERT(values.contains("expandedParentsCount"));
450
 
        const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
451
 
        if (expandedParentsCount >= 0) {
452
 
            const qreal widgetHeight = size().height();
453
 
            const qreal inc = (widgetHeight - KIconLoader::SizeSmall) / 2;
454
 
            const qreal x = expandedParentsCount * widgetHeight + inc;
455
 
            const qreal y = inc;
456
 
            m_expansionArea = QRectF(x, y, KIconLoader::SizeSmall, KIconLoader::SizeSmall);
457
 
            return;
458
 
        }
459
 
    }
460
 
 
461
 
    m_expansionArea = QRectF();
462
 
}
463
 
 
464
 
void KFileItemListWidget::updatePixmapCache()
465
 
{
466
 
    // Precondition: Requires already updated m_textPos values to calculate
467
 
    // the remaining height when the alignment is vertical.
468
 
 
469
 
    const QSizeF widgetSize = size();
470
 
    const bool iconOnTop = (m_layout == IconsLayout);
471
 
    const KItemListStyleOption& option = styleOption();
472
 
    const qreal padding = option.padding;
473
 
 
474
 
    const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : option.iconSize;
475
 
    const int maxIconHeight = option.iconSize;
476
 
 
477
 
    const QHash<QByteArray, QVariant> values = data();
478
 
 
479
 
    bool updatePixmap = (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight);
480
 
    if (!updatePixmap && m_dirtyContent) {
481
 
        updatePixmap = m_dirtyContentRoles.isEmpty()
482
 
                       || m_dirtyContentRoles.contains("iconPixmap")
483
 
                       || m_dirtyContentRoles.contains("iconName")
484
 
                       || m_dirtyContentRoles.contains("iconOverlays");
485
 
    }
486
 
 
487
 
    if (updatePixmap) {
488
 
        m_pixmap = values["iconPixmap"].value<QPixmap>();
489
 
        if (m_pixmap.isNull()) {
490
 
            // Use the icon that fits to the MIME-type
491
 
            QString iconName = values["iconName"].toString();
492
 
            if (iconName.isEmpty()) {
493
 
                // The icon-name has not been not resolved by KFileItemModelRolesUpdater,
494
 
                // use a generic icon as fallback
495
 
                iconName = QLatin1String("unknown");
496
 
            }
497
 
            m_pixmap = pixmapForIcon(iconName, maxIconHeight);
498
 
        } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) {
499
 
            // A custom pixmap has been applied. Assure that the pixmap
500
 
            // is scaled to the maximum available size.
501
 
            KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight));
502
 
        }
503
 
 
504
 
        const QStringList overlays = values["iconOverlays"].toStringList();
505
 
 
506
 
        // Strangely KFileItem::overlays() returns empty string-values, so
507
 
        // we need to check first whether an overlay must be drawn at all.
508
 
        // It is more efficient to do it here, as KIconLoader::drawOverlays()
509
 
        // assumes that an overlay will be drawn and has some additional
510
 
        // setup time.
511
 
        foreach (const QString& overlay, overlays) {
512
 
            if (!overlay.isEmpty()) {
513
 
                // There is at least one overlay, draw all overlays above m_pixmap
514
 
                // and cancel the check
515
 
                KIconLoader::global()->drawOverlays(overlays, m_pixmap, KIconLoader::Desktop);
516
 
                break;
517
 
            }
518
 
        }
519
 
 
520
 
        if (m_isCut) {
521
 
            applyCutEffect(m_pixmap);
522
 
        }
523
 
 
524
 
        if (m_isHidden) {
525
 
            applyHiddenEffect(m_pixmap);
526
 
        }
527
 
    }
528
 
 
529
 
    if (!m_overlay.isNull()) {
530
 
        QPainter painter(&m_pixmap);
531
 
        painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay);
532
 
    }
533
 
 
534
 
    int scaledIconSize = 0;
535
 
    if (iconOnTop) {
536
 
        scaledIconSize = static_cast<int>(m_textPos[Name].y() - 2 * padding);
537
 
    } else {
538
 
        const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1;
539
 
        const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height();
540
 
        scaledIconSize = (requiredTextHeight < maxIconHeight) ?
541
 
                           widgetSize.height() - 2 * padding : maxIconHeight;
542
 
    }
543
 
 
544
 
    const int maxScaledIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : scaledIconSize;
545
 
    const int maxScaledIconHeight = scaledIconSize;
546
 
 
547
 
    m_scaledPixmapSize = m_pixmap.size();
548
 
    m_scaledPixmapSize.scale(maxScaledIconWidth, maxScaledIconHeight, Qt::KeepAspectRatio);
549
 
 
550
 
    if (iconOnTop) {
551
 
        // Center horizontally and align on bottom within the icon-area
552
 
        m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2);
553
 
        m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height());
554
 
    } else {
555
 
        // Center horizontally and vertically within the icon-area
556
 
        m_pixmapPos.setX(m_textPos[Name].x() - 2 * padding
557
 
                         - (scaledIconSize + m_scaledPixmapSize.width()) / 2);
558
 
        m_pixmapPos.setY(padding
559
 
                         + (scaledIconSize - m_scaledPixmapSize.height()) / 2);
560
 
    }
561
 
 
562
 
    m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize));
563
 
 
564
 
    // Prepare the pixmap that is used when the item gets hovered
565
 
    if (isHovered()) {
566
 
        m_hoverPixmap = m_pixmap;
567
 
        KIconEffect* effect = KIconLoader::global()->iconEffect();
568
 
        // In the KIconLoader terminology, active = hover.
569
 
        if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) {
570
 
            m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState);
571
 
        } else {
572
 
            m_hoverPixmap = m_pixmap;
573
 
        }
574
 
    } else if (hoverOpacity() <= 0.0) {
575
 
        // No hover animation is ongoing. Clear m_hoverPixmap to save memory.
576
 
        m_hoverPixmap = QPixmap();
577
 
    }
578
 
}
579
 
 
580
 
void KFileItemListWidget::updateTextsCache()
581
 
{
582
 
    QTextOption textOption;
583
 
    switch (m_layout) {
584
 
    case IconsLayout:
585
 
        textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
586
 
        textOption.setAlignment(Qt::AlignHCenter);
587
 
        break;
588
 
    case CompactLayout:
589
 
    case DetailsLayout:
590
 
        textOption.setAlignment(Qt::AlignLeft);
591
 
        textOption.setWrapMode(QTextOption::NoWrap);
592
 
        break;
593
 
    default:
594
 
        Q_ASSERT(false);
595
 
        break;
596
 
    }
597
 
 
598
 
    for (int i = 0; i < TextIdCount; ++i) {
599
 
        m_text[i].setText(QString());
600
 
        m_text[i].setTextOption(textOption);
601
 
    }
602
 
 
603
 
    switch (m_layout) {
604
 
    case IconsLayout:   updateIconsLayoutTextCache(); break;
605
 
    case CompactLayout: updateCompactLayoutTextCache(); break;
606
 
    case DetailsLayout: updateDetailsLayoutTextCache(); break;
607
 
    default: Q_ASSERT(false); break;
608
 
    }
609
 
}
610
 
 
611
 
void KFileItemListWidget::updateIconsLayoutTextCache()
612
 
{
613
 
    //      +------+
614
 
    //      | Icon |
615
 
    //      +------+
616
 
    //
617
 
    //    Name role that
618
 
    // might get wrapped above
619
 
    //    several lines.
620
 
    //  Additional role 1
621
 
    //  Additional role 2
622
 
 
623
 
    const QHash<QByteArray, QVariant> values = data();
624
 
 
625
 
    const KItemListStyleOption& option = styleOption();
626
 
    const qreal padding = option.padding;
627
 
    const qreal maxWidth = size().width() - 2 * padding;
628
 
    const qreal widgetHeight = size().height();
629
 
    const qreal fontHeight = option.fontMetrics.height();
630
 
 
631
 
    // Initialize properties for the "name" role. It will be used as anchor
632
 
    // for initializing the position of the other roles.
633
 
    m_text[Name].setText(KStringHandler::preProcessWrap(values["name"].toString()));
634
 
 
635
 
    // Calculate the number of lines required for the name and the required width
636
 
    int textLinesCountForName = 0;
637
 
    qreal requiredWidthForName = 0;
638
 
    QTextLine line;
639
 
 
640
 
    QTextLayout layout(m_text[Name].text(), option.font);
641
 
    layout.setTextOption(m_text[Name].textOption());
642
 
    layout.beginLayout();
643
 
    while ((line = layout.createLine()).isValid()) {
644
 
        line.setLineWidth(maxWidth);
645
 
        requiredWidthForName = qMax(requiredWidthForName, line.naturalTextWidth());
646
 
        ++textLinesCountForName;
647
 
    }
648
 
    layout.endLayout();
649
 
 
650
 
    // Use one line for each additional information
651
 
    int textLinesCount = textLinesCountForName;
652
 
    const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
653
 
    textLinesCount += additionalRolesCount;
654
 
 
655
 
    m_text[Name].setTextWidth(maxWidth);
656
 
    m_textPos[Name] = QPointF(padding, widgetHeight - textLinesCount * fontHeight - padding);
657
 
    m_textRect = QRectF(padding + (maxWidth - requiredWidthForName) / 2,
658
 
                        m_textPos[Name].y(),
659
 
                        requiredWidthForName,
660
 
                        textLinesCountForName * fontHeight);
661
 
 
662
 
    // Calculate the position for each additional information
663
 
    qreal y = m_textPos[Name].y() + textLinesCountForName * fontHeight;
664
 
    foreach (const QByteArray& role, m_sortedVisibleRoles) {
665
 
        const TextId textId = roleTextId(role);
666
 
        if (textId == Name) {
667
 
            continue;
668
 
        }
669
 
 
670
 
        const QString text = roleText(role, values);
671
 
        m_text[textId].setText(text);
672
 
 
673
 
        qreal requiredWidth = 0;
674
 
 
675
 
        QTextLayout layout(text, option.font);
676
 
        layout.setTextOption(m_text[textId].textOption());
677
 
        layout.beginLayout();
678
 
        QTextLine textLine = layout.createLine();
679
 
        if (textLine.isValid()) {
680
 
            textLine.setLineWidth(maxWidth);
681
 
            requiredWidth = textLine.naturalTextWidth();
682
 
            if (textLine.textLength() < text.length()) {
683
 
                // TODO: QFontMetrics::elidedText() works different regarding the given width
684
 
                // in comparison to QTextLine::setLineWidth(). It might happen that the text does
685
 
                // not get elided although it does not fit into the given width. As workaround
686
 
                // the padding is substracted.
687
 
                const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth - padding);
688
 
                m_text[textId].setText(elidedText);
689
 
            }
690
 
        }
691
 
        layout.endLayout();
692
 
 
693
 
        m_textPos[textId] = QPointF(padding, y);
694
 
        m_text[textId].setTextWidth(maxWidth);
695
 
 
696
 
        const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight);
697
 
        m_textRect |= textRect;
698
 
 
699
 
        y += fontHeight;
700
 
    }
701
 
 
702
 
    // Add a padding to the text rectangle
703
 
    m_textRect.adjust(-padding, -padding, padding, padding);
704
 
}
705
 
 
706
 
void KFileItemListWidget::updateCompactLayoutTextCache()
707
 
{
708
 
    // +------+  Name role
709
 
    // | Icon |  Additional role 1
710
 
    // +------+  Additional role 2
711
 
 
712
 
    const QHash<QByteArray, QVariant> values = data();
713
 
 
714
 
    const KItemListStyleOption& option = styleOption();
715
 
    const qreal widgetHeight = size().height();
716
 
    const qreal fontHeight = option.fontMetrics.height();
717
 
    const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * fontHeight;
718
 
    const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.padding : option.iconSize;
719
 
 
720
 
    qreal maximumRequiredTextWidth = 0;
721
 
    const qreal x = option.padding * 3 + scaledIconSize;
722
 
    qreal y = (widgetHeight - textLinesHeight) / 2;
723
 
    const qreal maxWidth = size().width() - x - option.padding;
724
 
    foreach (const QByteArray& role, m_sortedVisibleRoles) {
725
 
        const TextId textId = roleTextId(role);
726
 
 
727
 
        const QString text = roleText(role, values);
728
 
        m_text[textId].setText(text);
729
 
 
730
 
        qreal requiredWidth = option.fontMetrics.width(text);
731
 
        if (requiredWidth > maxWidth) {
732
 
            requiredWidth = maxWidth;
733
 
            const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
734
 
            m_text[textId].setText(elidedText);
735
 
        }
736
 
 
737
 
        m_textPos[textId] = QPointF(x, y);
738
 
        m_text[textId].setTextWidth(maxWidth);
739
 
 
740
 
        maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth);
741
 
 
742
 
        y += fontHeight;
743
 
    }
744
 
 
745
 
    m_textRect = QRectF(x - option.padding, 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight);
746
 
}
747
 
 
748
 
void KFileItemListWidget::updateDetailsLayoutTextCache()
749
 
{
750
 
    // Precondition: Requires already updated m_expansionArea
751
 
    // to determine the left position.
752
 
 
753
 
    // +------+
754
 
    // | Icon |  Name role   Additional role 1   Additional role 2
755
 
    // +------+
756
 
    m_textRect = QRectF();
757
 
 
758
 
    const KItemListStyleOption& option = styleOption();
759
 
    const QHash<QByteArray, QVariant> values = data();
760
 
 
761
 
    const qreal widgetHeight = size().height();
762
 
    const int scaledIconSize = widgetHeight - 2 * option.padding;
763
 
    const int fontHeight = option.fontMetrics.height();
764
 
 
765
 
    const qreal columnPadding = option.padding * 3;
766
 
    qreal firstColumnInc = scaledIconSize;
767
 
    if (m_supportsItemExpanding) {
768
 
        firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2;
769
 
    } else {
770
 
        firstColumnInc += option.padding;
771
 
    }
772
 
 
773
 
    qreal x = firstColumnInc;
774
 
    const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2);
775
 
 
776
 
    foreach (const QByteArray& role, m_sortedVisibleRoles) {
777
 
        const TextId textId = roleTextId(role);
778
 
 
779
 
        QString text = roleText(role, values);
780
 
 
781
 
        // Elide the text in case it does not fit into the available column-width
782
 
        qreal requiredWidth = option.fontMetrics.width(text);
783
 
        const qreal columnWidth = visibleRolesSizes().value(role, QSizeF(0, 0)).width();
784
 
        qreal availableTextWidth = columnWidth - 2 * columnPadding;
785
 
        if (textId == Name) {
786
 
            availableTextWidth -= firstColumnInc;
787
 
        }
788
 
 
789
 
        if (requiredWidth > availableTextWidth) {
790
 
            text = option.fontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth);
791
 
            requiredWidth = option.fontMetrics.width(text);
792
 
        }
793
 
 
794
 
        m_text[textId].setText(text);
795
 
        m_textPos[textId] = QPointF(x + columnPadding, y);
796
 
        x += columnWidth;
797
 
 
798
 
        switch (textId) {
799
 
        case Name: {
800
 
            const qreal textWidth = option.extendedSelectionRegion
801
 
                                    ? size().width() - m_textPos[textId].x()
802
 
                                    : requiredWidth + 2 * option.padding;
803
 
            m_textRect = QRectF(m_textPos[textId].x() - option.padding, 0,
804
 
                                textWidth, size().height());
805
 
 
806
 
            // The column after the name should always be aligned on the same x-position independent
807
 
            // from the expansion-level shown in the name column
808
 
            x -= firstColumnInc;
809
 
            break;
810
 
        }
811
 
        case Size:
812
 
            // The values for the size should be right aligned
813
 
            m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * columnPadding;
814
 
            break;
815
 
 
816
 
        default:
817
 
            break;
818
 
        }
819
 
    }
820
 
}
821
 
 
822
 
void KFileItemListWidget::updateAdditionalInfoTextColor()
823
 
{
824
 
    QColor c1;
825
 
    if (m_customTextColor.isValid()) {
826
 
        c1 = m_customTextColor;
827
 
    } else if (isSelected() && m_layout != DetailsLayout) {
828
 
        c1 = styleOption().palette.highlightedText().color();
829
 
    } else {
830
 
        c1 = styleOption().palette.text().color();
831
 
    }
832
 
 
833
 
    // For the color of the additional info the inactive text color
834
 
    // is not used as this might lead to unreadable text for some color schemes. Instead
835
 
    // the text color c1 is slightly mixed with the background color.
836
 
    const QColor c2 = styleOption().palette.base().color();
837
 
    const int p1 = 70;
838
 
    const int p2 = 100 - p1;
839
 
    m_additionalInfoTextColor = QColor((c1.red()   * p1 + c2.red()   * p2) / 100,
840
 
                                       (c1.green() * p1 + c2.green() * p2) / 100,
841
 
                                       (c1.blue()  * p1 + c2.blue()  * p2) / 100);
842
 
}
843
 
 
844
 
void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
845
 
{
846
 
    if (m_scaledPixmapSize != pixmap.size()) {
847
 
        QPixmap scaledPixmap = pixmap;
848
 
        KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize);
849
 
        painter->drawPixmap(m_pixmapPos, scaledPixmap);
850
 
 
851
 
#ifdef KFILEITEMLISTWIDGET_DEBUG
852
 
        painter->setPen(Qt::blue);
853
 
        painter->drawRect(QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize)));
854
 
#endif
855
 
    } else {
856
 
        painter->drawPixmap(m_pixmapPos, pixmap);
857
 
    }
858
 
}
859
 
 
860
 
void KFileItemListWidget::drawSiblingsInformation(QPainter* painter)
861
 
{
862
 
    const int siblingSize = size().height();
863
 
    const int x = (m_expansionArea.left() + m_expansionArea.right() - siblingSize) / 2;
864
 
    QRect siblingRect(x, 0, siblingSize, siblingSize);
865
 
 
866
 
    QStyleOption option;
867
 
    bool isItemSibling = true;
868
 
 
869
 
    const QBitArray siblings = siblingsInformation();
870
 
    for (int i = siblings.count() - 1; i >= 0; --i) {
871
 
        option.rect = siblingRect;
872
 
        option.state = siblings.at(i) ? QStyle::State_Sibling : QStyle::State_None;
873
 
 
874
 
        if (isItemSibling) {
875
 
            option.state |= QStyle::State_Item;
876
 
            if (m_isExpandable) {
877
 
                option.state |= QStyle::State_Children;
878
 
            }
879
 
            if (data()["isExpanded"].toBool()) {
880
 
                option.state |= QStyle::State_Open;
881
 
            }
882
 
            isItemSibling = false;
883
 
        }
884
 
 
885
 
        style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter);
886
 
 
887
 
        siblingRect.translate(-siblingRect.width(), 0);
888
 
    }
889
 
}
890
 
 
891
 
QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size)
892
 
{
893
 
    const KIcon icon(name);
894
 
 
895
 
    int requestedSize;
896
 
    if (size <= KIconLoader::SizeSmall) {
897
 
        requestedSize = KIconLoader::SizeSmall;
898
 
    } else if (size <= KIconLoader::SizeSmallMedium) {
899
 
        requestedSize = KIconLoader::SizeSmallMedium;
900
 
    } else if (size <= KIconLoader::SizeMedium) {
901
 
        requestedSize = KIconLoader::SizeMedium;
902
 
    } else if (size <= KIconLoader::SizeLarge) {
903
 
        requestedSize = KIconLoader::SizeLarge;
904
 
    } else if (size <= KIconLoader::SizeHuge) {
905
 
        requestedSize = KIconLoader::SizeHuge;
906
 
    } else if (size <= KIconLoader::SizeEnormous) {
907
 
        requestedSize = KIconLoader::SizeEnormous;
908
 
    } else if (size <= KIconLoader::SizeEnormous * 2) {
909
 
        requestedSize = KIconLoader::SizeEnormous * 2;
910
 
    } else {
911
 
        requestedSize = size;
912
 
    }
913
 
 
914
 
    QPixmap pixmap = icon.pixmap(requestedSize, requestedSize);
915
 
    if (requestedSize != size) {
916
 
        KPixmapModifier::scale(pixmap, QSize(size, size));
917
 
    }
918
 
 
919
 
    return pixmap;
920
 
}
921
 
 
922
 
KFileItemListWidget::TextId KFileItemListWidget::roleTextId(const QByteArray& role)
923
 
{
924
 
    static QHash<QByteArray, TextId> rolesHash;
925
 
    if (rolesHash.isEmpty()) {
926
 
        rolesHash.insert("name", Name);
927
 
        rolesHash.insert("size", Size);
928
 
        rolesHash.insert("date", Date);
929
 
        rolesHash.insert("permissions", Permissions);
930
 
        rolesHash.insert("owner", Owner);
931
 
        rolesHash.insert("group", Group);
932
 
        rolesHash.insert("type", Type);
933
 
        rolesHash.insert("destination", Destination);
934
 
        rolesHash.insert("path", Path);
935
 
    }
936
 
 
937
 
    return rolesHash.value(role);
938
 
}
939
 
 
940
 
void KFileItemListWidget::applyCutEffect(QPixmap& pixmap)
941
 
{
942
 
    KIconEffect* effect = KIconLoader::global()->iconEffect();
943
 
    pixmap = effect->apply(pixmap, KIconLoader::Desktop, KIconLoader::DisabledState);
944
 
}
945
 
 
946
 
void KFileItemListWidget::applyHiddenEffect(QPixmap& pixmap)
947
 
{
948
 
    KIconEffect::semiTransparent(pixmap);
 
72
KFileItemListWidget::KFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
 
73
    KStandardItemListWidget(informant, parent)
 
74
{
 
75
}
 
76
 
 
77
KFileItemListWidget::~KFileItemListWidget()
 
78
{
 
79
}
 
80
 
 
81
KItemListWidgetInformant* KFileItemListWidget::createInformant()
 
82
{
 
83
    return new KFileItemListWidgetInformant();
 
84
}
 
85
 
 
86
bool KFileItemListWidget::isRoleRightAligned(const QByteArray& role) const
 
87
{
 
88
    return role == "size";
 
89
}
 
90
 
 
91
bool KFileItemListWidget::isHidden() const
 
92
{
 
93
    return data().value("text").toString().startsWith(QLatin1Char('.'));
 
94
}
 
95
 
 
96
QFont KFileItemListWidget::customizedFont(const QFont& baseFont) const
 
97
{
 
98
    // The customized font should be italic if the file is a symbolic link.
 
99
    QFont font(baseFont);
 
100
    font.setItalic(data().value("isLink").toBool());
 
101
    return font;
949
102
}
950
103
 
951
104
#include "kfileitemlistwidget.moc"