File indexing completed on 2024-05-12 16:40:55

0001 /* This file is part of the KDE project
0002    Copyright (C) 2016 Jarosław Staniek <staniek@kde.org>
0003 
0004    Forked from kwidgetsaddons/src/kpageview_p.cpp:
0005    Copyright (C) 2006 Tobias Koenig (tokoe@kde.org)
0006    Copyright (C) 2007 Rafael Fernández López (ereslibre@kde.org)
0007 
0008    This library is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU Library General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 
0013    This library is distributed in the hope that it will be useful,
0014    but WITHOUT ANY WARRANTY; without even the implied warranty of
0015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016    Library General Public License for more details.
0017 
0018    You should have received a copy of the GNU Library General Public License
0019    along with this library; see the file COPYING.LIB.  If not, write to
0020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "KexiListView.h"
0025 #include "KexiListView_p.h"
0026 
0027 #include <QApplication>
0028 #include <QPainter>
0029 #include <QTextLayout>
0030 
0031 const int KEXILISTVIEW_VERTICAL_MARGIN = 10;
0032 const int KEXILISTVIEW_HORIZONTAL_MARGIN = 12;
0033 
0034 KexiListView::KexiListView(QWidget *parent)
0035     : QListView(parent)
0036 {
0037     setViewMode(QListView::ListMode);
0038     setMovement(QListView::Static);
0039     setVerticalScrollMode(QListView::ScrollPerPixel);
0040     setItemDelegate(new KexiListViewDelegate(this));
0041 }
0042 
0043 KexiListView::~KexiListView()
0044 {
0045 }
0046 
0047 void KexiListView::setModel(QAbstractItemModel *model)
0048 {
0049     /*
0050       KPageListViewProxy *proxy = new KPageListViewProxy( this );
0051       proxy->setSourceModel( model );
0052       proxy->rebuildMap();
0053 
0054       connect( model, SIGNAL(layoutChanged()), proxy, SLOT(rebuildMap()) );
0055     */
0056     connect(model, SIGNAL(layoutChanged()), this, SLOT(updateWidth()));
0057 
0058 //  QListView::setModel( proxy );
0059     QListView::setModel(model);
0060 
0061     // Set our own selection model, which won't allow our current selection to be cleared
0062     setSelectionModel(new KexiListViewSelectionModel(model, this));
0063 
0064     updateWidth();
0065 }
0066 
0067 void KexiListView::updateWidth()
0068 {
0069     if (!model()) {
0070         return;
0071     }
0072 
0073     int rows = model()->rowCount();
0074 
0075     int width = 0;
0076     for (int i = 0; i < rows; ++i) {
0077         width = qMax(width, sizeHintForIndex(model()->index(i, 0)).width());
0078     }
0079 
0080     setFixedWidth(width + KEXILISTVIEW_HORIZONTAL_MARGIN * 2);
0081 }
0082 
0083 // ----
0084 
0085 KexiListViewDelegate::KexiListViewDelegate(QObject *parent)
0086     : QAbstractItemDelegate(parent)
0087 {
0088 }
0089 
0090 static int layoutText(QTextLayout *layout, int maxWidth)
0091 {
0092     qreal height = 0;
0093     int textWidth = 0;
0094     layout->beginLayout();
0095     while (true) {
0096         QTextLine line = layout->createLine();
0097         if (!line.isValid()) {
0098             break;
0099         }
0100         line.setLineWidth(maxWidth);
0101         line.setPosition(QPointF(0, height));
0102         height += line.height();
0103         textWidth = qMax(textWidth, qRound(line.naturalTextWidth() + 0.5));
0104     }
0105     layout->endLayout();
0106     return textWidth;
0107 }
0108 
0109 void KexiListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
0110                                  const QModelIndex &index) const
0111 {
0112     if (!index.isValid()) {
0113         return;
0114     }
0115 
0116     QStyleOptionViewItem opt(option);
0117     opt.showDecorationSelected = true;
0118     QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0119 
0120     int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize);
0121     const QString text = index.model()->data(index, Qt::DisplayRole).toString();
0122     const QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
0123     const QPixmap pixmap = icon.pixmap(iconSize, iconSize,
0124         (option.state & QStyle::State_Selected) ? QIcon::Selected : QIcon::Normal);
0125 
0126     QFontMetrics fm = painter->fontMetrics();
0127     int wp = pixmap.width() / pixmap.devicePixelRatio();
0128     int hp = pixmap.height() / pixmap.devicePixelRatio();
0129 
0130     QTextLayout iconTextLayout(text, option.font);
0131     QTextOption textOption(Qt::AlignHCenter);
0132     iconTextLayout.setTextOption(textOption);
0133     int maxWidth = qMax(3 * wp, 8 * fm.height());
0134     layoutText(&iconTextLayout, maxWidth);
0135 
0136     QPen pen = painter->pen();
0137     QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
0138                               ? QPalette::Normal : QPalette::Disabled;
0139     if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) {
0140         cg = QPalette::Inactive;
0141     }
0142 
0143     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
0144     if (option.state & QStyle::State_Selected) {
0145         painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
0146     } else {
0147         painter->setPen(option.palette.color(cg, QPalette::Text));
0148     }
0149 
0150     painter->drawPixmap(option.rect.x() + (option.rect.width() / 2)
0151                         - (wp / 2), option.rect.y() + KEXILISTVIEW_VERTICAL_MARGIN,
0152                         pixmap);
0153     if (!text.isEmpty()) {
0154         iconTextLayout.draw(painter,
0155             QPoint(option.rect.x() + (option.rect.width() / 2)
0156                    - (maxWidth / 2), option.rect.y() + hp + KEXILISTVIEW_VERTICAL_MARGIN + 2));
0157     }
0158 
0159     painter->setPen(pen);
0160 
0161     drawFocus(painter, option, option.rect);
0162 }
0163 
0164 QSize KexiListViewDelegate::sizeHint(const QStyleOptionViewItem &option,
0165                                      const QModelIndex &index) const
0166 {
0167     if (!index.isValid()) {
0168         return QSize(0, 0);
0169     }
0170 
0171     QStyleOptionViewItem opt(option);
0172     opt.showDecorationSelected = true;
0173     QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0174 
0175     int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize);
0176     const QString text = index.model()->data(index, Qt::DisplayRole).toString();
0177     const QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
0178     const QPixmap pixmap = icon.pixmap(iconSize, iconSize);
0179 
0180     QFontMetrics fm = option.fontMetrics;
0181     int gap = 0; //fm.height();
0182     int wp = pixmap.width() / pixmap.devicePixelRatio();
0183     int hp = pixmap.height() / pixmap.devicePixelRatio();
0184 
0185     if (hp == 0) {
0186         /**
0187          * No pixmap loaded yet, we'll use the default icon size in this case.
0188          */
0189         hp = iconSize;
0190         wp = iconSize;
0191     }
0192 
0193     QTextLayout iconTextLayout(text, option.font);
0194     int wt = layoutText(&iconTextLayout, qMax(3 * wp, 8 * fm.height()));
0195     int ht = iconTextLayout.boundingRect().height();
0196 
0197     int width, height;
0198     if (text.isEmpty()) {
0199         height = hp;
0200     } else {
0201         height = hp + ht + 2 * KEXILISTVIEW_VERTICAL_MARGIN;
0202     }
0203 
0204     width = qMax(wt, wp) + gap;
0205 
0206     return QSize(width, height);
0207 }
0208 
0209 void KexiListViewDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option,
0210                                      const QRect &rect) const
0211 {
0212     if (option.state & QStyle::State_HasFocus) {
0213         QStyleOptionFocusRect o;
0214         o.QStyleOption::operator=(option);
0215         o.rect = rect;
0216         o.state |= QStyle::State_KeyboardFocusChange;
0217         QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
0218                                   ? QPalette::Normal : QPalette::Disabled;
0219         o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
0220                             ? QPalette::Highlight : QPalette::Background);
0221         QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
0222     }
0223 }
0224 
0225 // ----
0226 
0227 KexiListViewSelectionModel::KexiListViewSelectionModel(QAbstractItemModel *model, QObject *parent)
0228     : QItemSelectionModel(model, parent)
0229 {
0230 }
0231 
0232 void KexiListViewSelectionModel::clear()
0233 {
0234     // Don't allow the current selection to be cleared
0235 }
0236 
0237 void KexiListViewSelectionModel::select(const QModelIndex &index,
0238                                         QItemSelectionModel::SelectionFlags command)
0239 {
0240     // Don't allow the current selection to be cleared
0241     if (!index.isValid() && (command & QItemSelectionModel::Clear)) {
0242         return;
0243     }
0244     QItemSelectionModel::select(index, command);
0245 }
0246 
0247 void KexiListViewSelectionModel::select(const QItemSelection &selection,
0248                                         QItemSelectionModel::SelectionFlags command)
0249 {
0250     // Don't allow the current selection to be cleared
0251     if (!selection.count() && (command & QItemSelectionModel::Clear)) {
0252         return;
0253     }
0254     QItemSelectionModel::select(selection, command);
0255 }