]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemlistwidget.cpp
Improve group-header layout
[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 QString KFileItemListWidget::roleText(const QByteArray& role, const QHash<QByteArray, QVariant>& values)
176 {
177 QString text;
178 const QVariant roleValue = values.value(role);
179
180 switch (roleTextId(role)) {
181 case Name:
182 case Permissions:
183 case Owner:
184 case Group:
185 case Type:
186 case Destination:
187 case Path:
188 text = roleValue.toString();
189 break;
190
191 case Size: {
192 if (values.value("isDir").toBool()) {
193 // The item represents a directory. Show the number of sub directories
194 // instead of the file size of the directory.
195 if (!roleValue.isNull()) {
196 const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
197 text = i18ncp("@item:intable", "%1 item", "%1 items", size);
198 }
199 } else {
200 const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
201 text = KIO::convertSize(size);
202 }
203 break;
204 }
205
206 case Date: {
207 const QDateTime dateTime = roleValue.toDateTime();
208 text = KGlobal::locale()->formatDateTime(dateTime);
209 break;
210 }
211
212 default:
213 Q_ASSERT(false);
214 break;
215 }
216
217 return text;
218 }
219
220 void KFileItemListWidget::invalidateCache()
221 {
222 m_dirtyLayout = true;
223 m_dirtyContent = true;
224 }
225
226 void KFileItemListWidget::refreshCache()
227 {
228 }
229
230 void KFileItemListWidget::setTextColor(const QColor& color)
231 {
232 if (color != m_customTextColor) {
233 m_customTextColor = color;
234 updateAdditionalInfoTextColor();
235 update();
236 }
237 }
238
239 QColor KFileItemListWidget::textColor() const
240 {
241 return m_customTextColor.isValid() ? m_customTextColor : styleOption().palette.text().color();
242 }
243
244 void KFileItemListWidget::setOverlay(const QPixmap& overlay)
245 {
246 m_overlay = overlay;
247 m_dirtyContent = true;
248 update();
249 }
250
251 QPixmap KFileItemListWidget::overlay() const
252 {
253 return m_overlay;
254 }
255
256 void KFileItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
257 const QSet<QByteArray>& roles)
258 {
259 KItemListWidget::dataChanged(current, roles);
260 m_dirtyContent = true;
261
262 QSet<QByteArray> dirtyRoles;
263 if (roles.isEmpty()) {
264 dirtyRoles = visibleRoles().toSet();
265 dirtyRoles.insert("iconPixmap");
266 dirtyRoles.insert("iconName");
267 } else {
268 dirtyRoles = roles;
269 }
270
271 QSetIterator<QByteArray> it(dirtyRoles);
272 while (it.hasNext()) {
273 const QByteArray& role = it.next();
274 m_dirtyContentRoles.insert(role);
275 }
276 }
277
278 void KFileItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
279 const QList<QByteArray>& previous)
280 {
281 KItemListWidget::visibleRolesChanged(current, previous);
282 m_sortedVisibleRoles = current;
283 m_dirtyLayout = true;
284 }
285
286 void KFileItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
287 const QHash<QByteArray, QSizeF>& previous)
288 {
289 KItemListWidget::visibleRolesSizesChanged(current, previous);
290 m_dirtyLayout = true;
291 }
292
293 void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
294 const KItemListStyleOption& previous)
295 {
296 KItemListWidget::styleOptionChanged(current, previous);
297 updateAdditionalInfoTextColor();
298 m_dirtyLayout = true;
299 }
300
301 void KFileItemListWidget::hoveredChanged(bool hovered)
302 {
303 Q_UNUSED(hovered);
304 m_dirtyLayout = true;
305 }
306
307 void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
308 {
309 KItemListWidget::resizeEvent(event);
310 m_dirtyLayout = true;
311 }
312
313 void KFileItemListWidget::triggerCacheRefreshing()
314 {
315 if ((!m_dirtyContent && !m_dirtyLayout) || index() < 0) {
316 return;
317 }
318
319 refreshCache();
320
321 m_isDir = data()["isDir"].toBool();
322
323 updateExpansionArea();
324 updateTextsCache();
325 updatePixmapCache();
326
327 m_dirtyLayout = false;
328 m_dirtyContent = false;
329 m_dirtyContentRoles.clear();
330 }
331
332 void KFileItemListWidget::updateExpansionArea()
333 {
334 if (m_layout == DetailsLayout) {
335 const QHash<QByteArray, QVariant> values = data();
336 Q_ASSERT(values.contains("expansionLevel"));
337 const KItemListStyleOption& option = styleOption();
338 const int expansionLevel = values.value("expansionLevel", 0).toInt();
339
340 const qreal widgetHeight = size().height();
341 const qreal expansionLevelSize = KIconLoader::SizeSmall;
342 const qreal x = option.margin + expansionLevel * widgetHeight;
343 const qreal y = (widgetHeight - expansionLevelSize) / 2;
344 m_expansionArea = QRectF(x, y, expansionLevelSize, expansionLevelSize);
345 } else {
346 m_expansionArea = QRectF();
347 }
348 }
349
350 void KFileItemListWidget::updatePixmapCache()
351 {
352 // Precondition: Requires already updated m_textPos values to calculate
353 // the remaining height when the alignment is vertical.
354
355 const bool iconOnTop = (m_layout == IconsLayout);
356 const KItemListStyleOption& option = styleOption();
357 const int iconHeight = option.iconSize;
358
359 const QHash<QByteArray, QVariant> values = data();
360 const QSizeF widgetSize = size();
361
362 int scaledIconHeight = 0;
363 if (iconOnTop) {
364 scaledIconHeight = static_cast<int>(m_textPos[Name].y() - 3 * option.margin);
365 } else {
366 const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1;
367 const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height();
368 scaledIconHeight = (requiredTextHeight < iconHeight) ? widgetSize.height() - 2 * option.margin : iconHeight;
369 }
370
371 bool updatePixmap = (iconHeight != m_pixmap.height());
372 if (!updatePixmap && m_dirtyContent) {
373 updatePixmap = m_dirtyContentRoles.isEmpty()
374 || m_dirtyContentRoles.contains("iconPixmap")
375 || m_dirtyContentRoles.contains("iconName");
376 }
377
378 if (updatePixmap) {
379 m_pixmap = values["iconPixmap"].value<QPixmap>();
380 if (m_pixmap.isNull()) {
381 // Use the icon that fits to the MIME-type
382 QString iconName = values["iconName"].toString();
383 if (iconName.isEmpty()) {
384 // The icon-name has not been not resolved by KFileItemModelRolesUpdater,
385 // use a generic icon as fallback
386 iconName = QLatin1String("unknown");
387 }
388 m_pixmap = pixmapForIcon(iconName, iconHeight);
389 m_hoverPixmapRect.setSize(m_pixmap.size());
390 } else if (m_pixmap.size() != QSize(iconHeight, iconHeight)) {
391 // A custom pixmap has been applied. Assure that the pixmap
392 // is scaled to the available size.
393 const bool scale = m_pixmap.width() > iconHeight || m_pixmap.height() > iconHeight ||
394 (m_pixmap.width() < iconHeight && m_pixmap.height() < iconHeight);
395 if (scale) {
396 KPixmapModifier::scale(m_pixmap, QSize(iconHeight, iconHeight));
397 }
398 m_hoverPixmapRect.setSize(m_pixmap.size());
399
400 // To simplify the handling of scaling the original pixmap
401 // will be embedded into a square pixmap.
402 QPixmap squarePixmap(iconHeight, iconHeight);
403 squarePixmap.fill(Qt::transparent);
404
405 QPainter painter(&squarePixmap);
406 if (iconOnTop) {
407 const int x = (iconHeight - m_pixmap.width()) / 2; // Center horizontally
408 const int y = iconHeight - m_pixmap.height(); // Align on bottom
409 painter.drawPixmap(x, y, m_pixmap);
410 } else {
411 const int x = iconHeight - m_pixmap.width(); // Align right
412 const int y = (iconHeight - m_pixmap.height()) / 2; // Center vertically
413 painter.drawPixmap(x, y, m_pixmap);
414 }
415
416 m_pixmap = squarePixmap;
417 } else {
418 m_hoverPixmapRect.setSize(m_pixmap.size());
419 }
420
421 Q_ASSERT(m_pixmap.height() == iconHeight);
422 }
423 if (!m_overlay.isNull()) {
424 QPainter painter(&m_pixmap);
425 painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay);
426 }
427
428 m_scaledPixmapSize = QSize(scaledIconHeight, scaledIconHeight);
429
430 if (iconOnTop) {
431 m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2);
432 } else {
433 m_pixmapPos.setX(m_textPos[Name].x() - 2 * option.margin - scaledIconHeight);
434 }
435 m_pixmapPos.setY(option.margin);
436
437 // Center the hover rectangle horizontally and align it on bottom
438 const qreal x = m_pixmapPos.x() + (m_scaledPixmapSize.width() - m_hoverPixmapRect.width()) / 2.0;
439 const qreal y = m_pixmapPos.y() + m_scaledPixmapSize.height() - m_hoverPixmapRect.height();
440 m_hoverPixmapRect.moveTopLeft(QPointF(x, y));
441
442 // Prepare the pixmap that is used when the item gets hovered
443 if (isHovered()) {
444 m_hoverPixmap = m_pixmap;
445 KIconEffect* effect = KIconLoader::global()->iconEffect();
446 // In the KIconLoader terminology, active = hover.
447 if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) {
448 m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState);
449 } else {
450 m_hoverPixmap = m_pixmap;
451 }
452 } else if (hoverOpacity() <= 0.0) {
453 // No hover animation is ongoing. Clear m_hoverPixmap to save memory.
454 m_hoverPixmap = QPixmap();
455 }
456 }
457
458 void KFileItemListWidget::updateTextsCache()
459 {
460 QTextOption textOption;
461 switch (m_layout) {
462 case IconsLayout:
463 textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
464 textOption.setAlignment(Qt::AlignHCenter);
465 break;
466 case CompactLayout:
467 case DetailsLayout:
468 textOption.setAlignment(Qt::AlignLeft);
469 textOption.setWrapMode(QTextOption::NoWrap);
470 break;
471 default:
472 Q_ASSERT(false);
473 break;
474 }
475
476 for (int i = 0; i < TextIdCount; ++i) {
477 m_text[i].setText(QString());
478 m_text[i].setTextOption(textOption);
479 }
480
481 switch (m_layout) {
482 case IconsLayout: updateIconsLayoutTextCache(); break;
483 case CompactLayout: updateCompactLayoutTextCache(); break;
484 case DetailsLayout: updateDetailsLayoutTextCache(); break;
485 default: Q_ASSERT(false); break;
486 }
487 }
488
489 void KFileItemListWidget::updateIconsLayoutTextCache()
490 {
491 // +------+
492 // | Icon |
493 // +------+
494 //
495 // Name role that
496 // might get wrapped above
497 // several lines.
498 // Additional role 1
499 // Additional role 2
500
501 const QHash<QByteArray, QVariant> values = data();
502
503 const KItemListStyleOption& option = styleOption();
504 const qreal maxWidth = size().width() - 2 * option.margin;
505 const qreal widgetHeight = size().height();
506 const qreal fontHeight = option.fontMetrics.height();
507
508 // Initialize properties for the "name" role. It will be used as anchor
509 // for initializing the position of the other roles.
510 m_text[Name].setText(KStringHandler::preProcessWrap(values["name"].toString()));
511
512 // Calculate the number of lines required for the name and the required width
513 int textLinesCountForName = 0;
514 qreal requiredWidthForName = 0;
515 QTextLine line;
516
517 QTextLayout layout(m_text[Name].text(), option.font);
518 layout.setTextOption(m_text[Name].textOption());
519 layout.beginLayout();
520 while ((line = layout.createLine()).isValid()) {
521 line.setLineWidth(maxWidth);
522 requiredWidthForName = qMax(requiredWidthForName, line.naturalTextWidth());
523 ++textLinesCountForName;
524 }
525 layout.endLayout();
526
527 // Use one line for each additional information
528 int textLinesCount = textLinesCountForName;
529 const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
530 textLinesCount += additionalRolesCount;
531
532 m_text[Name].setTextWidth(maxWidth);
533 m_textPos[Name] = QPointF(option.margin, widgetHeight - textLinesCount * fontHeight - option.margin);
534 m_textRect = QRectF(option.margin + (maxWidth - requiredWidthForName) / 2,
535 m_textPos[Name].y(),
536 requiredWidthForName,
537 m_text[Name].size().height());
538
539 // Calculate the position for each additional information
540 qreal y = m_textPos[Name].y() + textLinesCountForName * fontHeight;
541 foreach (const QByteArray& role, m_sortedVisibleRoles) {
542 const TextId textId = roleTextId(role);
543 if (textId == Name) {
544 continue;
545 }
546
547 const QString text = roleText(role, values);
548 m_text[textId].setText(text);
549
550 qreal requiredWidth = 0;
551
552 QTextLayout layout(text, option.font);
553 layout.setTextOption(m_text[textId].textOption());
554 layout.beginLayout();
555 QTextLine textLine = layout.createLine();
556 if (textLine.isValid()) {
557 textLine.setLineWidth(maxWidth);
558 requiredWidth = textLine.naturalTextWidth();
559 if (textLine.textLength() < text.length()) {
560 // TODO: QFontMetrics::elidedText() works different regarding the given width
561 // in comparison to QTextLine::setLineWidth(). It might happen that the text does
562 // not get elided although it does not fit into the given width. As workaround
563 // the margin is substracted.
564 const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth - option.margin);
565 m_text[textId].setText(elidedText);
566 }
567 }
568 layout.endLayout();
569
570 m_textPos[textId] = QPointF(option.margin, y);
571 m_text[textId].setTextWidth(maxWidth);
572
573 const QRectF textRect(option.margin + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight);
574 m_textRect |= textRect;
575
576 y += fontHeight;
577 }
578
579 // Add a margin to the text rectangle
580 const qreal margin = option.margin;
581 m_textRect.adjust(-margin, -margin, margin, margin);
582 }
583
584 void KFileItemListWidget::updateCompactLayoutTextCache()
585 {
586 // +------+ Name role
587 // | Icon | Additional role 1
588 // +------+ Additional role 2
589
590 const QHash<QByteArray, QVariant> values = data();
591
592 const KItemListStyleOption& option = styleOption();
593 const qreal widgetHeight = size().height();
594 const qreal fontHeight = option.fontMetrics.height();
595 const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * fontHeight;
596 const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.margin : option.iconSize;
597
598 qreal maximumRequiredTextWidth = 0;
599 const qreal x = option.margin * 3 + scaledIconSize;
600 qreal y = (widgetHeight - textLinesHeight) / 2;
601 const qreal maxWidth = size().width() - x - option.margin;
602 foreach (const QByteArray& role, m_sortedVisibleRoles) {
603 const TextId textId = roleTextId(role);
604
605 const QString text = roleText(role, values);
606 m_text[textId].setText(text);
607
608 qreal requiredWidth = option.fontMetrics.width(text);
609 if (requiredWidth > maxWidth) {
610 requiredWidth = maxWidth;
611 const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
612 m_text[textId].setText(elidedText);
613 }
614
615 m_textPos[textId] = QPointF(x, y);
616 m_text[textId].setTextWidth(maxWidth);
617
618 maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth);
619
620 y += fontHeight;
621 }
622
623 m_textRect = QRectF(x - option.margin, 0, maximumRequiredTextWidth + 2 * option.margin, widgetHeight);
624 }
625
626 void KFileItemListWidget::updateDetailsLayoutTextCache()
627 {
628 // Precondition: Requires already updated m_expansionArea
629 // to determine the left position.
630
631 // +------+
632 // | Icon | Name role Additional role 1 Additional role 2
633 // +------+
634 m_textRect = QRectF();
635
636 const KItemListStyleOption& option = styleOption();
637 const QHash<QByteArray, QVariant> values = data();
638
639 const qreal widgetHeight = size().height();
640 const int scaledIconSize = widgetHeight - 2 * option.margin;
641 const int fontHeight = option.fontMetrics.height();
642
643 const qreal columnMargin = option.margin * 3;
644 const qreal firstColumnInc = m_expansionArea.right() + option.margin * 2 + scaledIconSize;
645 qreal x = firstColumnInc;
646 const qreal y = qMax(qreal(option.margin), (widgetHeight - fontHeight) / 2);
647
648 foreach (const QByteArray& role, m_sortedVisibleRoles) {
649 const TextId textId = roleTextId(role);
650
651 QString text = roleText(role, values);
652
653 // Elide the text in case it does not fit into the available column-width
654 qreal requiredWidth = option.fontMetrics.width(text);
655 const qreal columnWidth = visibleRolesSizes().value(role, QSizeF(0, 0)).width();
656 qreal availableTextWidth = columnWidth - 2 * columnMargin;
657 if (textId == Name) {
658 availableTextWidth -= firstColumnInc;
659 }
660
661 if (requiredWidth > availableTextWidth) {
662 text = option.fontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth);
663 requiredWidth = option.fontMetrics.width(text);
664 }
665
666 m_text[textId].setText(text);
667 m_textPos[textId] = QPointF(x + columnMargin, y);
668 x += columnWidth;
669
670 switch (textId) {
671 case Name: {
672 m_textRect = QRectF(m_textPos[textId].x() - option.margin, 0,
673 requiredWidth + 2 * option.margin, size().height());
674
675 // The column after the name should always be aligned on the same x-position independent
676 // from the expansion-level shown in the name column
677 x -= firstColumnInc;
678 break;
679 }
680 case Size:
681 // The values for the size should be right aligned
682 m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * columnMargin;
683 break;
684
685 default:
686 break;
687 }
688 }
689 }
690
691 void KFileItemListWidget::updateAdditionalInfoTextColor()
692 {
693 // For the color of the additional info the inactive text color
694 // is not used as this might lead to unreadable text for some color schemes. Instead
695 // the text color is slightly mixed with the background color.
696 const QColor c1 = textColor();
697 const QColor c2 = styleOption().palette.background().color();
698 const int p1 = 70;
699 const int p2 = 100 - p1;
700 m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100,
701 (c1.green() * p1 + c2.green() * p2) / 100,
702 (c1.blue() * p1 + c2.blue() * p2) / 100);
703 }
704
705 void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
706 {
707 const bool isHiddenItem = m_text[Name].text().startsWith(QLatin1Char('.'));
708 qreal opacity;
709 if (isHiddenItem) {
710 opacity = painter->opacity();
711 painter->setOpacity(opacity * 0.3);
712 }
713
714 if (m_scaledPixmapSize != pixmap.size()) {
715 QPixmap scaledPixmap = pixmap;
716 KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize);
717 painter->drawPixmap(m_pixmapPos, scaledPixmap);
718
719 #ifdef KFILEITEMLISTWIDGET_DEBUG
720 painter->setPen(Qt::green);
721 painter->drawRect(QRectF(m_pixmapPos, QSizeF(scaledPixmap.size())));
722 #endif
723 } else {
724 painter->drawPixmap(m_pixmapPos, pixmap);
725 }
726
727 if (isHiddenItem) {
728 painter->setOpacity(opacity);
729 }
730 }
731
732 QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size)
733 {
734 const KIcon icon(name);
735
736 int requestedSize;
737 if (size <= KIconLoader::SizeSmall) {
738 requestedSize = KIconLoader::SizeSmall;
739 } else if (size <= KIconLoader::SizeSmallMedium) {
740 requestedSize = KIconLoader::SizeSmallMedium;
741 } else if (size <= KIconLoader::SizeMedium) {
742 requestedSize = KIconLoader::SizeMedium;
743 } else if (size <= KIconLoader::SizeLarge) {
744 requestedSize = KIconLoader::SizeLarge;
745 } else if (size <= KIconLoader::SizeHuge) {
746 requestedSize = KIconLoader::SizeHuge;
747 } else if (size <= KIconLoader::SizeEnormous) {
748 requestedSize = KIconLoader::SizeEnormous;
749 } else if (size <= KIconLoader::SizeEnormous * 2) {
750 requestedSize = KIconLoader::SizeEnormous * 2;
751 } else {
752 requestedSize = size;
753 }
754
755 QPixmap pixmap = icon.pixmap(requestedSize, requestedSize);
756 if (requestedSize != size) {
757 KPixmapModifier::scale(pixmap, QSize(size, size));
758 }
759
760 return pixmap;
761 }
762
763 KFileItemListWidget::TextId KFileItemListWidget::roleTextId(const QByteArray& role)
764 {
765 static QHash<QByteArray, TextId> rolesHash;
766 if (rolesHash.isEmpty()) {
767 rolesHash.insert("name", Name);
768 rolesHash.insert("size", Size);
769 rolesHash.insert("date", Date);
770 rolesHash.insert("permissions", Permissions);
771 rolesHash.insert("owner", Owner);
772 rolesHash.insert("group", Group);
773 rolesHash.insert("type", Type);
774 rolesHash.insert("destination", Destination);
775 rolesHash.insert("path", Path);
776 }
777
778 return rolesHash.value(role);
779 }
780
781 #include "kfileitemlistwidget.moc"