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

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 <QDebug>
0029 #include <QPainter>
0030 #include <QTextLayout>
0031 
0032 const int KEXILISTVIEW_VERTICAL_MARGIN = 10;
0033 const int KEXILISTVIEW_HORIZONTAL_MARGIN = 12;
0034 
0035 KexiListView::KexiListView(QWidget *parent)
0036     : KexiListView(UseDefaultDelegate, parent)
0037 {
0038 }
0039 
0040 KexiListView::KexiListView(UseDelegate useDelegate, QWidget *parent)
0041  : QListView(parent)
0042 {
0043     setViewMode(QListView::ListMode);
0044     setMovement(QListView::Static);
0045     setVerticalScrollMode(QListView::ScrollPerPixel);
0046     if (useDelegate == UseDefaultDelegate) {
0047         setItemDelegate(new KexiListViewDelegate(this));
0048     }
0049 }
0050 
0051 KexiListView::~KexiListView()
0052 {
0053 }
0054 
0055 void KexiListView::setModel(QAbstractItemModel *model)
0056 {
0057     /*
0058       KPageListViewProxy *proxy = new KPageListViewProxy( this );
0059       proxy->setSourceModel( model );
0060       proxy->rebuildMap();
0061 
0062       connect( model, SIGNAL(layoutChanged()), proxy, SLOT(rebuildMap()) );
0063     */
0064     connect(model, SIGNAL(layoutChanged()), this, SLOT(updateWidth()));
0065 
0066 //  QListView::setModel( proxy );
0067     QListView::setModel(model);
0068 
0069     // Set our own selection model, which won't allow our current selection to be cleared
0070     setSelectionModel(new KexiListViewSelectionModel(model, this));
0071 
0072     updateWidth();
0073 }
0074 
0075 void KexiListView::updateWidth()
0076 {
0077     if (!model()) {
0078         return;
0079     }
0080 
0081     int rows = model()->rowCount();
0082 
0083     int width = 0;
0084     for (int i = 0; i < rows; ++i) {
0085         width = qMax(width, sizeHintForIndex(model()->index(i, 0)).width());
0086     }
0087 
0088     setFixedWidth(width + KEXILISTVIEW_HORIZONTAL_MARGIN * 2);
0089 }
0090 
0091 // ----
0092 
0093 KexiListViewDelegate::KexiListViewDelegate(QObject *parent)
0094     : QAbstractItemDelegate(parent)
0095 {
0096 }
0097 
0098 static int layoutText(QTextLayout *layout, int maxWidth)
0099 {
0100     qreal height = 0;
0101     int textWidth = 0;
0102     layout->beginLayout();
0103     while (true) {
0104         QTextLine line = layout->createLine();
0105         if (!line.isValid()) {
0106             break;
0107         }
0108         line.setLineWidth(maxWidth);
0109         line.setPosition(QPointF(0, height));
0110         height += line.height();
0111         textWidth = qMax(textWidth, qRound(line.naturalTextWidth() + 0.5));
0112     }
0113     layout->endLayout();
0114     return textWidth;
0115 }
0116 
0117 void KexiListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
0118                                  const QModelIndex &index) const
0119 {
0120     if (!index.isValid()) {
0121         return;
0122     }
0123 
0124     QStyleOptionViewItem opt(option);
0125     opt.showDecorationSelected = true;
0126     const QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0127     paint(painter, *style, &opt, index);
0128 }
0129 
0130 void KexiListViewDelegate::paint(QPainter *painter, const QStyle &style,
0131                                  QStyleOptionViewItem *option,
0132                                  const QModelIndex &index) const
0133 {
0134     int iconSize = style.pixelMetric(QStyle::PM_IconViewIconSize);
0135     const QString text = index.model()->data(index, Qt::DisplayRole).toString();
0136     const QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
0137     QIcon::Mode iconMode;
0138     if (option->state & QStyle::State_Enabled && option->state & QStyle::State_Selected) {
0139         iconMode = QIcon::Selected;
0140     } else {
0141         iconMode = (option->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled;
0142     }
0143     const QPixmap pixmap = icon.pixmap(iconSize, iconSize, iconMode);
0144     QFontMetrics fm = painter->fontMetrics();
0145     int wp = pixmap.width() / pixmap.devicePixelRatio();
0146     int hp = pixmap.height() / pixmap.devicePixelRatio();
0147 
0148     QTextLayout iconTextLayout(text, option->font);
0149     QTextOption textOption(Qt::AlignHCenter);
0150     iconTextLayout.setTextOption(textOption);
0151     int maxWidth = qMax(3 * wp, 8 * fm.height());
0152     layoutText(&iconTextLayout, maxWidth);
0153 
0154     QPen pen = painter->pen();
0155     QPalette::ColorGroup cg;
0156     if (option->state & QStyle::State_Enabled) {
0157         cg = (option->state & QStyle::State_Active) ? QPalette::Normal : QPalette::Inactive;
0158     } else {
0159         cg = QPalette::Disabled;
0160         option->state &= ~QStyle::State_MouseOver;
0161     }
0162     //qDebug() << hex << int(option->state) << text << int(iconMode) << cg;
0163 
0164     style.drawPrimitive(QStyle::PE_PanelItemViewItem, option, painter, option->widget);
0165     if (option->state & QStyle::State_Selected) {
0166         painter->setPen(option->palette.color(cg, QPalette::HighlightedText));
0167     } else {
0168         painter->setPen(option->palette.color(cg, QPalette::Text));
0169     }
0170 
0171     painter->drawPixmap(option->rect.x() + (option->rect.width() / 2)
0172                         - (wp / 2), option->rect.y() + KEXILISTVIEW_VERTICAL_MARGIN,
0173                         pixmap);
0174     if (!text.isEmpty()) {
0175         iconTextLayout.draw(painter,
0176             QPoint(option->rect.x() + (option->rect.width() / 2)
0177                    - (maxWidth / 2), option->rect.y() + hp + KEXILISTVIEW_VERTICAL_MARGIN + 2));
0178     }
0179 
0180     painter->setPen(pen);
0181     drawFocus(painter, *option, option->rect);
0182 }
0183 
0184 QSize KexiListViewDelegate::sizeHint(const QStyleOptionViewItem &option,
0185                                      const QModelIndex &index) const
0186 {
0187     if (!index.isValid()) {
0188         return QSize(0, 0);
0189     }
0190 
0191     QStyleOptionViewItem opt(option);
0192     opt.showDecorationSelected = true;
0193     QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0194 
0195     int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize);
0196     const QString text = index.model()->data(index, Qt::DisplayRole).toString();
0197     const QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
0198     const QPixmap pixmap = icon.pixmap(iconSize, iconSize);
0199 
0200     QFontMetrics fm = option.fontMetrics;
0201     int gap = 0; //fm.height();
0202     int wp = pixmap.width() / pixmap.devicePixelRatio();
0203     int hp = pixmap.height() / pixmap.devicePixelRatio();
0204 
0205     if (hp == 0) {
0206         /**
0207          * No pixmap loaded yet, we'll use the default icon size in this case.
0208          */
0209         hp = iconSize;
0210         wp = iconSize;
0211     }
0212 
0213     QTextLayout iconTextLayout(text, option.font);
0214     int wt = layoutText(&iconTextLayout, qMax(3 * wp, 8 * fm.height()));
0215     int ht = iconTextLayout.boundingRect().height();
0216 
0217     int width, height;
0218     if (text.isEmpty()) {
0219         height = hp;
0220     } else {
0221         height = hp + ht + 2 * KEXILISTVIEW_VERTICAL_MARGIN;
0222     }
0223 
0224     width = qMax(wt, wp) + gap;
0225 
0226     return QSize(width, height);
0227 }
0228 
0229 void KexiListViewDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option,
0230                                      const QRect &rect) const
0231 {
0232     if (option.state & QStyle::State_HasFocus) {
0233         QStyleOptionFocusRect o;
0234         o.QStyleOption::operator=(option);
0235         o.rect = rect;
0236         o.state |= QStyle::State_KeyboardFocusChange;
0237         QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
0238                                   ? QPalette::Normal : QPalette::Disabled;
0239         o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
0240                             ? QPalette::Highlight : QPalette::Background);
0241         QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
0242     }
0243 }
0244 
0245 // ----
0246 
0247 KexiListViewSelectionModel::KexiListViewSelectionModel(QAbstractItemModel *model, QObject *parent)
0248     : QItemSelectionModel(model, parent)
0249 {
0250 }
0251 
0252 void KexiListViewSelectionModel::clear()
0253 {
0254     // Don't allow the current selection to be cleared
0255 }
0256 
0257 void KexiListViewSelectionModel::select(const QModelIndex &index,
0258                                         QItemSelectionModel::SelectionFlags command)
0259 {
0260     // Don't allow the current selection to be cleared
0261     if (!index.isValid() && (command & Clear || command & Deselect)) {
0262         return;
0263     }
0264     QItemSelectionModel::select(index, command);
0265 }
0266 
0267 void KexiListViewSelectionModel::select(const QItemSelection &selection,
0268                                         QItemSelectionModel::SelectionFlags command)
0269 {
0270     // Don't allow the current selection to be cleared
0271     if (!selection.count() && (command & QItemSelectionModel::Clear)) {
0272         return;
0273     }
0274     QItemSelectionModel::select(selection, command);
0275 }