]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemlistwidget.cpp
Bring back the selection-markers
[dolphin.git] / src / kitemviews / kfileitemlistwidget.cpp
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include "kfileitemlistwidget.h"
21
22 #include "kfileitemmodel.h"
23 #include "kitemlistview.h"
24 #include "kpixmapmodifier_p.h"
25
26 #include <KIcon>
27 #include <KIconEffect>
28 #include <KIconLoader>
29 #include <KLocale>
30 #include <KStringHandler>
31 #include <KDebug>
32
33 #include <QFontMetricsF>
34 #include <QGraphicsSceneResizeEvent>
35 #include <QPainter>
36 #include <QStyleOption>
37 #include <QTextLayout>
38 #include <QTextLine>
39
40 //#define KFILEITEMLISTWIDGET_DEBUG
41
42 KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) :
43 KItemListWidget(parent),
44 m_isDir(false),
45 m_dirtyLayout(true),
46 m_dirtyContent(true),
47 m_dirtyContentRoles(),
48 m_layout(IconsLayout),
49 m_pixmapPos(),
50 m_pixmap(),
51 m_scaledPixmapSize(),
52 m_hoverPixmapRect(),
53 m_hoverPixmap(),
54 m_textPos(),
55 m_text(),
56 m_textRect(),
57 m_sortedVisibleRoles(),
58 m_expansionArea(),
59 m_customTextColor(),
60 m_additionalInfoTextColor(),
61 m_overlay()
62 {
63 for (int i = 0; i < TextIdCount; ++i) {
64 m_text[i].setTextFormat(Qt::PlainText);
65 m_text[i].setPerformanceHint(QStaticText::AggressiveCaching);
66 }
67 }
68
69 KFileItemListWidget::~KFileItemListWidget()
70 {
71 }
72
73 void KFileItemListWidget::setLayout(Layout layout)
74 {
75 if (m_layout != layout) {
76 m_layout = layout;
77 m_dirtyLayout = true;
78 update();
79 }
80 }
81
82 KFileItemListWidget::Layout KFileItemListWidget::layout() const
83 {
84 return m_layout;
85 }
86
87 void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
88 {
89 KItemListWidget::paint(painter, option, widget);
90
91 const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
92
93 // Draw expansion toggle '>' or 'V'
94 if (m_isDir && !m_expansionArea.isEmpty()) {
95 QStyleOption arrowOption;
96 arrowOption.rect = m_expansionArea.toRect();
97 const QStyle::PrimitiveElement arrow = data()["isExpanded"].toBool()
98 ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight;
99 style()->drawPrimitive(arrow, &arrowOption, painter);
100 }
101
102 const KItemListStyleOption& itemListStyleOption = styleOption();
103 if (isHovered()) {
104 // Blend the unhovered and hovered pixmap if the hovering
105 // animation is ongoing
106 if (hoverOpacity() < 1.0) {
107 drawPixmap(painter, m_pixmap);
108 }
109
110 const qreal opacity = painter->opacity();
111 painter->setOpacity(hoverOpacity() * opacity);
112 drawPixmap(painter, m_hoverPixmap);
113 painter->setOpacity(opacity);
114 } else {
115 drawPixmap(painter, m_pixmap);
116 }
117
118 painter->setFont(itemListStyleOption.font);
119 painter->setPen(textColor());
120 painter->drawStaticText(m_textPos[Name], m_text[Name]);
121
122 bool clipAdditionalInfoBounds = false;
123 if (m_layout == DetailsLayout) {
124 // Prevent a possible overlapping of the additional-information texts
125 // with the icon. This can happen if the user has minimized the width
126 // of the name-column to a very small value.
127 const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.margin;
128 if (m_textPos[Name + 1].x() < minX) {
129 clipAdditionalInfoBounds = true;
130 painter->save();
131 painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip);
132 }
133 }
134
135 painter->setPen(m_additionalInfoTextColor);
136 painter->setFont(itemListStyleOption.font);
137 for (int i = Name + 1; i < TextIdCount; ++i) {
138 painter->drawStaticText(m_textPos[i], m_text[i]);
139 }
140
141 if (clipAdditionalInfoBounds) {
142 painter->restore();
143 }
144
145 #ifdef KFILEITEMLISTWIDGET_DEBUG
146 painter->setPen(Qt::red);
147 painter->setBrush(Qt::NoBrush);
148 painter->drawText(QPointF(0, itemListStyleOption.fontMetrics.height()), QString::number(index()));
149 painter->drawRect(rect());
150 #endif
151 }
152
153 QRectF KFileItemListWidget::iconRect() const
154 {
155 const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
156
157 QRectF bounds = m_hoverPixmapRect;
158 const qreal margin = styleOption().margin;
159 bounds.adjust(-margin, -margin, margin, margin);
160 return bounds;
161 }
162
163 QRectF KFileItemListWidget::textRect() const
164 {
165 const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
166 return m_textRect;
167 }
168
169 QRectF KFileItemListWidget::expansionToggleRect() const
170 {
171 const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
172 return m_isDir ? m_expansionArea : QRectF();
173 }
174
175 QRectF KFileItemListWidget::selectionToggleRect() const
176 {
177 const_cast<KFileItemListWidget*>(this)->triggerCacheRefreshing();
178
179 const int iconHeight = m_pixmap.height();
180
181 int toggleSize = KIconLoader::SizeSmall;
182 if (iconHeight >= KIconLoader::SizeEnormous) {
183 toggleSize = KIconLoader::SizeMedium;
184 } else if (iconHeight >= KIconLoader::SizeLarge) {
185 toggleSize = KIconLoader::SizeSmallMedium;
186 }
187
188 return QRectF(m_pixmapPos, QSizeF(toggleSize, toggleSize));
189 }
190
191 QString KFileItemListWidget::roleText(const QByteArray& role, const QHash<QByteArray, QVariant>& values)
192 {
193 QString text;
194 const QVariant roleValue = values.value(role);
195
196 switch (roleTextId(role)) {
197 case Name:
198 case Permissions:
199 case Owner:
200 case Group:
201 case Type:
202 case Destination:
203 case Path:
204 text = roleValue.toString();
205 break;
206
207 case Size: {
208 if (values.value("isDir").toBool()) {
209 // The item represents a directory. Show the number of sub directories
210 // instead of the file size of the directory.
211 if (roleValue.isNull()) {
212 text = i18nc("@item:intable", "Unknown");
213 } else {
214 const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
215 text = i18ncp("@item:intable", "%1 item", "%1 items", size);
216 }
217 } else {
218 // Show the size in kilobytes (always round up)
219 const KLocale* locale = KGlobal::locale();
220 const int roundInc = (locale->binaryUnitDialect() == KLocale::MetricBinaryDialect) ? 499 : 511;
221 const KIO::filesize_t size = roleValue.value<KIO::filesize_t>() + roundInc;
222 text = locale->formatByteSize(size, 0, KLocale::DefaultBinaryDialect, KLocale::UnitKiloByte);
223 }
224 break;
225 }
226
227 case Date: {
228 const QDateTime dateTime = roleValue.toDateTime();
229 text = KGlobal::locale()->formatDateTime(dateTime);
230 break;
231 }
232
233 default:
234 Q_ASSERT(false);
235 break;
236 }
237
238 return text;
239 }
240
241 void KFileItemListWidget::invalidateCache()
242 {
243 m_dirtyLayout = true;
244 m_dirtyContent = true;
245 }
246
247 void KFileItemListWidget::refreshCache()
248 {
249 }
250
251 void KFileItemListWidget::setTextColor(const QColor& color)
252 {
253 if (color != m_customTextColor) {
254 m_customTextColor = color;
255 updateAdditionalInfoTextColor();
256 update();
257 }
258 }
259
260 QColor KFileItemListWidget::textColor() const
261 {
262 return m_customTextColor.isValid() ? m_customTextColor : styleOption().palette.text().color();
263 }
264
265 void KFileItemListWidget::setOverlay(const QPixmap& overlay)
266 {
267 m_overlay = overlay;
268 m_dirtyContent = true;
269 update();
270 }
271
272 QPixmap KFileItemListWidget::overlay() const
273 {
274 return m_overlay;
275 }
276
277 void KFileItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
278 const QSet<QByteArray>& roles)
279 {
280 KItemListWidget::dataChanged(current, roles);
281 m_dirtyContent = true;
282
283 QSet<QByteArray> dirtyRoles;
284 if (roles.isEmpty()) {
285 dirtyRoles = visibleRoles().toSet();
286 dirtyRoles.insert("iconPixmap");
287 dirtyRoles.insert("iconName");
288 } else {
289 dirtyRoles = roles;
290 }
291
292 QSetIterator<QByteArray> it(dirtyRoles);
293 while (it.hasNext()) {
294 const QByteArray& role = it.next();
295 m_dirtyContentRoles.insert(role);
296 }
297 }
298
299 void KFileItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
300 const QList<QByteArray>& previous)
301 {
302 KItemListWidget::visibleRolesChanged(current, previous);
303 m_sortedVisibleRoles = current;
304 m_dirtyLayout = true;
305 }
306
307 void KFileItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
308 const QHash<QByteArray, QSizeF>& previous)
309 {
310 KItemListWidget::visibleRolesSizesChanged(current, previous);
311 m_dirtyLayout = true;
312 }
313
314 void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
315 const KItemListStyleOption& previous)
316 {
317 KItemListWidget::styleOptionChanged(current, previous);
318 updateAdditionalInfoTextColor();
319 m_dirtyLayout = true;
320 }
321
322 void KFileItemListWidget::hoveredChanged(bool hovered)
323 {
324 Q_UNUSED(hovered);
325 m_dirtyLayout = true;
326 }
327
328 void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
329 {
330 KItemListWidget::resizeEvent(event);
331 m_dirtyLayout = true;
332 }
333
334 void KFileItemListWidget::triggerCacheRefreshing()
335 {
336 if ((!m_dirtyContent && !m_dirtyLayout) || index() < 0) {
337 return;
338 }
339
340 refreshCache();
341
342 m_isDir = data()["isDir"].toBool();
343
344 updateExpansionArea();
345 updateTextsCache();
346 updatePixmapCache();
347
348 m_dirtyLayout = false;
349 m_dirtyContent = false;
350 m_dirtyContentRoles.clear();
351 }
352
353 void KFileItemListWidget::updateExpansionArea()
354 {
355 if (m_layout == DetailsLayout) {
356 const QHash<QByteArray, QVariant> values = data();
357 Q_ASSERT(values.contains("expansionLevel"));
358 const KItemListStyleOption& option = styleOption();
359 const int expansionLevel = values.value("expansionLevel", 0).toInt();
360
361 const qreal widgetHeight = size().height();
362 const qreal expansionLevelSize = KIconLoader::SizeSmall;
363 const qreal x = option.margin + expansionLevel * widgetHeight;
364 const qreal y = (widgetHeight - expansionLevelSize) / 2;
365 m_expansionArea = QRectF(x, y, expansionLevelSize, expansionLevelSize);
366 } else {
367 m_expansionArea = QRectF();
368 }
369 }
370
371 void KFileItemListWidget::updatePixmapCache()
372 {
373 // Precondition: Requires already updated m_textPos values to calculate
374 // the remaining height when the alignment is vertical.
375
376 const bool iconOnTop = (m_layout == IconsLayout);
377 const KItemListStyleOption& option = styleOption();
378 const int iconHeight = option.iconSize;
379
380 const QHash<QByteArray, QVariant> values = data();
381 const QSizeF widgetSize = size();
382
383 int scaledIconHeight = 0;
384 if (iconOnTop) {
385 scaledIconHeight = static_cast<int>(m_textPos[Name].y() - 3 * option.margin);
386 } else {
387 const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1;
388 const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height();
389 scaledIconHeight = (requiredTextHeight < iconHeight) ? widgetSize.height() - 2 * option.margin : iconHeight;
390 }
391
392 bool updatePixmap = (iconHeight != m_pixmap.height());
393 if (!updatePixmap && m_dirtyContent) {
394 updatePixmap = m_dirtyContentRoles.isEmpty()
395 || m_dirtyContentRoles.contains("iconPixmap")
396 || m_dirtyContentRoles.contains("iconName");
397 }
398
399 if (updatePixmap) {
400 m_pixmap = values["iconPixmap"].value<QPixmap>();
401 if (m_pixmap.isNull()) {
402 // Use the icon that fits to the MIME-type
403 QString iconName = values["iconName"].toString();
404 if (iconName.isEmpty()) {
405 // The icon-name has not been not resolved by KFileItemModelRolesUpdater,
406 // use a generic icon as fallback
407 iconName = QLatin1String("unknown");
408 }
409 m_pixmap = pixmapForIcon(iconName, iconHeight);
410 m_hoverPixmapRect.setSize(m_pixmap.size());
411 } else if (m_pixmap.size() != QSize(iconHeight, iconHeight)) {
412 // A custom pixmap has been applied. Assure that the pixmap
413 // is scaled to the available size.
414 const bool scale = m_pixmap.width() > iconHeight || m_pixmap.height() > iconHeight ||
415 (m_pixmap.width() < iconHeight && m_pixmap.height() < iconHeight);
416 if (scale) {
417 KPixmapModifier::scale(m_pixmap, QSize(iconHeight, iconHeight));
418 }
419 m_hoverPixmapRect.setSize(m_pixmap.size());
420
421 // To simplify the handling of scaling the original pixmap
422 // will be embedded into a square pixmap.
423 QPixmap squarePixmap(iconHeight, iconHeight);
424 squarePixmap.fill(Qt::transparent);
425
426 QPainter painter(&squarePixmap);
427 if (iconOnTop) {
428 const int x = (iconHeight - m_pixmap.width()) / 2; // Center horizontally
429 const int y = iconHeight - m_pixmap.height(); // Align on bottom
430 painter.drawPixmap(x, y, m_pixmap);
431 } else {
432 const int x = iconHeight - m_pixmap.width(); // Align right
433 const int y = (iconHeight - m_pixmap.height()) / 2; // Center vertically
434 painter.drawPixmap(x, y, m_pixmap);
435 }
436
437 m_pixmap = squarePixmap;
438 } else {
439 m_hoverPixmapRect.setSize(m_pixmap.size());
440 }
441
442 Q_ASSERT(m_pixmap.height() == iconHeight);
443 }
444 if (!m_overlay.isNull()) {
445 QPainter painter(&m_pixmap);
446 painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay);
447 }
448
449 m_scaledPixmapSize = QSize(scaledIconHeight, scaledIconHeight);
450
451 if (iconOnTop) {
452 m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2);
453 } else {
454 m_pixmapPos.setX(m_textPos[Name].x() - 2 * option.margin - scaledIconHeight);
455 }
456 m_pixmapPos.setY(option.margin);
457
458 // Center the hover rectangle horizontally and align it on bottom
459 const qreal x = m_pixmapPos.x() + (m_scaledPixmapSize.width() - m_hoverPixmapRect.width()) / 2.0;
460 const qreal y = m_pixmapPos.y() + m_scaledPixmapSize.height() - m_hoverPixmapRect.height();
461 m_hoverPixmapRect.moveTopLeft(QPointF(x, y));
462
463 // Prepare the pixmap that is used when the item gets hovered
464 if (isHovered()) {
465 m_hoverPixmap = m_pixmap;
466 KIconEffect* effect = KIconLoader::global()->iconEffect();
467 // In the KIconLoader terminology, active = hover.
468 if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) {
469 m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState);
470 } else {
471 m_hoverPixmap = m_pixmap;
472 }
473 } else if (hoverOpacity() <= 0.0) {
474 // No hover animation is ongoing. Clear m_hoverPixmap to save memory.
475 m_hoverPixmap = QPixmap();
476 }
477 }
478
479 void KFileItemListWidget::updateTextsCache()
480 {
481 QTextOption textOption;
482 switch (m_layout) {
483 case IconsLayout:
484 textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
485 textOption.setAlignment(Qt::AlignHCenter);
486 break;
487 case CompactLayout:
488 case DetailsLayout:
489 textOption.setAlignment(Qt::AlignLeft);
490 textOption.setWrapMode(QTextOption::NoWrap);
491 break;
492 default:
493 Q_ASSERT(false);
494 break;
495 }
496
497 for (int i = 0; i < TextIdCount; ++i) {
498 m_text[i].setText(QString());
499 m_text[i].setTextOption(textOption);
500 }
501
502 switch (m_layout) {
503 case IconsLayout: updateIconsLayoutTextCache(); break;
504 case CompactLayout: updateCompactLayoutTextCache(); break;
505 case DetailsLayout: updateDetailsLayoutTextCache(); break;
506 default: Q_ASSERT(false); break;
507 }
508 }
509
510 void KFileItemListWidget::updateIconsLayoutTextCache()
511 {
512 // +------+
513 // | Icon |
514 // +------+
515 //
516 // Name role that
517 // might get wrapped above
518 // several lines.
519 // Additional role 1
520 // Additional role 2
521
522 const QHash<QByteArray, QVariant> values = data();
523
524 const KItemListStyleOption& option = styleOption();
525 const qreal maxWidth = size().width() - 2 * option.margin;
526 const qreal widgetHeight = size().height();
527 const qreal fontHeight = option.fontMetrics.height();
528
529 // Initialize properties for the "name" role. It will be used as anchor
530 // for initializing the position of the other roles.
531 m_text[Name].setText(KStringHandler::preProcessWrap(values["name"].toString()));
532
533 // Calculate the number of lines required for the name and the required width
534 int textLinesCountForName = 0;
535 qreal requiredWidthForName = 0;
536 QTextLine line;
537
538 QTextLayout layout(m_text[Name].text(), option.font);
539 layout.setTextOption(m_text[Name].textOption());
540 layout.beginLayout();
541 while ((line = layout.createLine()).isValid()) {
542 line.setLineWidth(maxWidth);
543 requiredWidthForName = qMax(requiredWidthForName, line.naturalTextWidth());
544 ++textLinesCountForName;
545 }
546 layout.endLayout();
547
548 // Use one line for each additional information
549 int textLinesCount = textLinesCountForName;
550 const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
551 textLinesCount += additionalRolesCount;
552
553 m_text[Name].setTextWidth(maxWidth);
554 m_textPos[Name] = QPointF(option.margin, widgetHeight - textLinesCount * fontHeight - option.margin);
555 m_textRect = QRectF(option.margin + (maxWidth - requiredWidthForName) / 2,
556 m_textPos[Name].y(),
557 requiredWidthForName,
558 m_text[Name].size().height());
559
560 // Calculate the position for each additional information
561 qreal y = m_textPos[Name].y() + textLinesCountForName * fontHeight;
562 foreach (const QByteArray& role, m_sortedVisibleRoles) {
563 const TextId textId = roleTextId(role);
564 if (textId == Name) {
565 continue;
566 }
567
568 const QString text = roleText(role, values);
569 m_text[textId].setText(text);
570
571 qreal requiredWidth = 0;
572
573 QTextLayout layout(text, option.font);
574 layout.setTextOption(m_text[textId].textOption());
575 layout.beginLayout();
576 QTextLine textLine = layout.createLine();
577 if (textLine.isValid()) {
578 textLine.setLineWidth(maxWidth);
579 requiredWidth = textLine.naturalTextWidth();
580 if (textLine.textLength() < text.length()) {
581 // TODO: QFontMetrics::elidedText() works different regarding the given width
582 // in comparison to QTextLine::setLineWidth(). It might happen that the text does
583 // not get elided although it does not fit into the given width. As workaround
584 // the margin is substracted.
585 const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth - option.margin);
586 m_text[textId].setText(elidedText);
587 }
588 }
589 layout.endLayout();
590
591 m_textPos[textId] = QPointF(option.margin, y);
592 m_text[textId].setTextWidth(maxWidth);
593
594 const QRectF textRect(option.margin + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight);
595 m_textRect |= textRect;
596
597 y += fontHeight;
598 }
599
600 // Add a margin to the text rectangle
601 const qreal margin = option.margin;
602 m_textRect.adjust(-margin, -margin, margin, margin);
603 }
604
605 void KFileItemListWidget::updateCompactLayoutTextCache()
606 {
607 // +------+ Name role
608 // | Icon | Additional role 1
609 // +------+ Additional role 2
610
611 const QHash<QByteArray, QVariant> values = data();
612
613 const KItemListStyleOption& option = styleOption();
614 const qreal widgetHeight = size().height();
615 const qreal fontHeight = option.fontMetrics.height();
616 const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * fontHeight;
617 const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.margin : option.iconSize;
618
619 qreal maximumRequiredTextWidth = 0;
620 const qreal x = option.margin * 3 + scaledIconSize;
621 qreal y = (widgetHeight - textLinesHeight) / 2;
622 const qreal maxWidth = size().width() - x - option.margin;
623 foreach (const QByteArray& role, m_sortedVisibleRoles) {
624 const TextId textId = roleTextId(role);
625
626 const QString text = roleText(role, values);
627 m_text[textId].setText(text);
628
629 qreal requiredWidth = option.fontMetrics.width(text);
630 if (requiredWidth > maxWidth) {
631 requiredWidth = maxWidth;
632 const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
633 m_text[textId].setText(elidedText);
634 }
635
636 m_textPos[textId] = QPointF(x, y);
637 m_text[textId].setTextWidth(maxWidth);
638
639 maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth);
640
641 y += fontHeight;
642 }
643
644 m_textRect = QRectF(x - option.margin, 0, maximumRequiredTextWidth + 2 * option.margin, widgetHeight);
645 }
646
647 void KFileItemListWidget::updateDetailsLayoutTextCache()
648 {
649 // Precondition: Requires already updated m_expansionArea
650 // to determine the left position.
651
652 // +------+
653 // | Icon | Name role Additional role 1 Additional role 2
654 // +------+
655 m_textRect = QRectF();
656
657 const KItemListStyleOption& option = styleOption();
658 const QHash<QByteArray, QVariant> values = data();
659
660 const qreal widgetHeight = size().height();
661 const int scaledIconSize = widgetHeight - 2 * option.margin;
662 const int fontHeight = option.fontMetrics.height();
663
664 const qreal columnMargin = option.margin * 3;
665 const qreal firstColumnInc = m_expansionArea.right() + option.margin * 2 + scaledIconSize;
666 qreal x = firstColumnInc;
667 const qreal y = qMax(qreal(option.margin), (widgetHeight - fontHeight) / 2);
668
669 foreach (const QByteArray& role, m_sortedVisibleRoles) {
670 const TextId textId = roleTextId(role);
671
672 QString text = roleText(role, values);
673
674 // Elide the text in case it does not fit into the available column-width
675 qreal requiredWidth = option.fontMetrics.width(text);
676 const qreal columnWidth = visibleRolesSizes().value(role, QSizeF(0, 0)).width();
677 qreal availableTextWidth = columnWidth - 2 * columnMargin;
678 if (textId == Name) {
679 availableTextWidth -= firstColumnInc;
680 }
681
682 if (requiredWidth > availableTextWidth) {
683 text = option.fontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth);
684 requiredWidth = option.fontMetrics.width(text);
685 }
686
687 m_text[textId].setText(text);
688 m_textPos[textId] = QPointF(x + columnMargin, y);
689 x += columnWidth;
690
691 switch (textId) {
692 case Name: {
693 m_textRect = QRectF(m_textPos[textId].x() - option.margin, 0,
694 requiredWidth + 2 * option.margin, size().height());
695
696 // The column after the name should always be aligned on the same x-position independent
697 // from the expansion-level shown in the name column
698 x -= firstColumnInc;
699 break;
700 }
701 case Size:
702 // The values for the size should be right aligned
703 m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * columnMargin;
704 break;
705
706 default:
707 break;
708 }
709 }
710 }
711
712 void KFileItemListWidget::updateAdditionalInfoTextColor()
713 {
714 // For the color of the additional info the inactive text color
715 // is not used as this might lead to unreadable text for some color schemes. Instead
716 // the text color is slightly mixed with the background color.
717 const QColor c1 = textColor();
718 const QColor c2 = styleOption().palette.base().color();
719 const int p1 = 70;
720 const int p2 = 100 - p1;
721 m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100,
722 (c1.green() * p1 + c2.green() * p2) / 100,
723 (c1.blue() * p1 + c2.blue() * p2) / 100);
724 }
725
726 void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
727 {
728 const bool isHiddenItem = m_text[Name].text().startsWith(QLatin1Char('.'));
729 qreal opacity;
730 if (isHiddenItem) {
731 opacity = painter->opacity();
732 painter->setOpacity(opacity * 0.3);
733 }
734
735 if (m_scaledPixmapSize != pixmap.size()) {
736 QPixmap scaledPixmap = pixmap;
737 KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize);
738 painter->drawPixmap(m_pixmapPos, scaledPixmap);
739
740 #ifdef KFILEITEMLISTWIDGET_DEBUG
741 painter->setPen(Qt::green);
742 painter->drawRect(QRectF(m_pixmapPos, QSizeF(scaledPixmap.size())));
743 #endif
744 } else {
745 painter->drawPixmap(m_pixmapPos, pixmap);
746 }
747
748 if (isHiddenItem) {
749 painter->setOpacity(opacity);
750 }
751 }
752
753 QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size)
754 {
755 const KIcon icon(name);
756
757 int requestedSize;
758 if (size <= KIconLoader::SizeSmall) {
759 requestedSize = KIconLoader::SizeSmall;
760 } else if (size <= KIconLoader::SizeSmallMedium) {
761 requestedSize = KIconLoader::SizeSmallMedium;
762 } else if (size <= KIconLoader::SizeMedium) {
763 requestedSize = KIconLoader::SizeMedium;
764 } else if (size <= KIconLoader::SizeLarge) {
765 requestedSize = KIconLoader::SizeLarge;
766 } else if (size <= KIconLoader::SizeHuge) {
767 requestedSize = KIconLoader::SizeHuge;
768 } else if (size <= KIconLoader::SizeEnormous) {
769 requestedSize = KIconLoader::SizeEnormous;
770 } else if (size <= KIconLoader::SizeEnormous * 2) {
771 requestedSize = KIconLoader::SizeEnormous * 2;
772 } else {
773 requestedSize = size;
774 }
775
776 QPixmap pixmap = icon.pixmap(requestedSize, requestedSize);
777 if (requestedSize != size) {
778 KPixmapModifier::scale(pixmap, QSize(size, size));
779 }
780
781 return pixmap;
782 }
783
784 KFileItemListWidget::TextId KFileItemListWidget::roleTextId(const QByteArray& role)
785 {
786 static QHash<QByteArray, TextId> rolesHash;
787 if (rolesHash.isEmpty()) {
788 rolesHash.insert("name", Name);
789 rolesHash.insert("size", Size);
790 rolesHash.insert("date", Date);
791 rolesHash.insert("permissions", Permissions);
792 rolesHash.insert("owner", Owner);
793 rolesHash.insert("group", Group);
794 rolesHash.insert("type", Type);
795 rolesHash.insert("destination", Destination);
796 rolesHash.insert("path", Path);
797 }
798
799 return rolesHash.value(role);
800 }
801
802 #include "kfileitemlistwidget.moc"