File indexing completed on 2024-04-21 05:45:05

0001 /*
0002  *   Copyright (C) 2007 Ivan Cukic <ivan.cukic+kde@gmail.com>
0003  *   Copyright (C) 2008-2011 Daniel Nicoletti <dantti12@gmail.com>
0004  *
0005  *   This program is free software; you can redistribute it and/or modify
0006  *   it under the terms of the GNU Library/Lesser General Public License
0007  *   version 2, or (at your option) any later version, as published by the
0008  *   Free Software Foundation
0009  *
0010  *   This program is distributed in the hope that it will be useful,
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  *   GNU General Public License for more details
0014  *
0015  *   You should have received a copy of the GNU Library/Lesser General Public
0016  *   License along with this program; if not, write to the
0017  *   Free Software Foundation, Inc.,
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0019  */
0020 
0021 #include "ApplicationsDelegate.h"
0022 
0023 #include <QLoggingCategory>
0024 #include <KLocalizedString>
0025 #include <QPushButton>
0026 #include <QApplication>
0027 
0028 #include <QPainter>
0029 #include <QMouseEvent>
0030 
0031 #include "PackageModel.h"
0032 #include "PkIcons.h"
0033 
0034 #define UNIVERSAL_PADDING 4
0035 #define FADE_LENGTH 16
0036 
0037 using namespace PackageKit;
0038 
0039 ApplicationsDelegate::ApplicationsDelegate(QAbstractItemView *parent)
0040   : QStyledItemDelegate(parent),
0041     m_viewport(parent->viewport()),
0042     // loads it here to be faster when displaying items
0043     m_installIcon(QIcon::fromTheme(QLatin1String("go-down"))),
0044     m_installString(i18n("Install")),
0045     m_removeIcon(QIcon::fromTheme(QLatin1String("edit-delete"))),
0046     m_removeString(i18n("Remove")),
0047     m_undoIcon(QIcon::fromTheme(QLatin1String("edit-undo"))),
0048     m_undoString(i18n("Deselect")),
0049     m_checkedIcon(QIcon::fromTheme(QLatin1String("dialog-ok")))
0050 {
0051     m_viewport->setAttribute(Qt::WA_Hover, true);
0052     QPushButton button, button2;
0053     button.setText(m_installString);
0054     button.setIcon(m_installIcon);
0055     button2.setText(m_removeString);
0056     button2.setIcon(m_removeIcon);
0057     m_buttonSize = button.sizeHint();
0058     int width = qMax(button.sizeHint().width(), button2.sizeHint().width());
0059     button.setText(m_undoString);
0060     width = qMax(width, button2.sizeHint().width());
0061     m_buttonSize.setWidth(width);
0062     m_buttonIconSize = button.iconSize();
0063 }
0064 
0065 void ApplicationsDelegate::paint(QPainter *painter,
0066                         const QStyleOptionViewItem &option,
0067                         const QModelIndex &index) const
0068 {
0069     if (!index.isValid()) {
0070         return;
0071     }
0072 
0073     // Button height
0074     int btHeight = m_buttonSize.height() + UNIVERSAL_PADDING;
0075     if (index.column() == PackageModel::VersionCol ||
0076         index.column() == PackageModel::CurrentVersionCol ||
0077         index.column() == PackageModel::ArchCol ||
0078         index.column() == PackageModel::OriginCol ||
0079         index.column() == PackageModel::SizeCol) {
0080         QStyleOptionViewItem opt(option);
0081         if (opt.state & QStyle::State_HasFocus) {
0082             opt.state ^= QStyle::State_HasFocus;
0083         }
0084         painter->save();
0085         QStyledItemDelegate::paint(painter, opt, index);
0086         painter->restore();
0087         return;
0088     } else if (index.column() == PackageModel::NameCol) {
0089         bool leftToRight = (painter->layoutDirection() == Qt::LeftToRight);
0090         QStyleOptionViewItem opt1(option);
0091         if (opt1.state & QStyle::State_HasFocus) {
0092             opt1.state ^= QStyle::State_HasFocus;
0093         }
0094         QSize size = QStyledItemDelegate::sizeHint(opt1, index);
0095         if (leftToRight) {
0096             opt1.rect.setRight(size.width());
0097         } else {
0098             opt1.rect.setLeft(option.rect.left() + (option.rect.width() - size.width()));
0099         }
0100         painter->save();
0101         QStyledItemDelegate::paint(painter, opt1, index);
0102         painter->restore();
0103 
0104         // a new option for the summary
0105         QStyleOptionViewItem opt(option);
0106         if (leftToRight) {
0107             opt.rect.setLeft(size.width() + 1);
0108         } else {
0109             opt.rect.setRight(option.rect.left() + (option.rect.width() - size.width()) - 1);
0110         }
0111 
0112         QPixmap pixmap(opt.rect.size());
0113         pixmap.fill(Qt::transparent);
0114         QPainter p(&pixmap);
0115         if (!p.isActive()) {
0116             return; // TODO test if this fixes warnigns
0117         }
0118         p.translate(-opt.rect.topLeft());
0119 
0120         QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0121         opt.viewItemPosition = QStyleOptionViewItem::Middle;
0122         painter->save();
0123         style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
0124         painter->restore();
0125 
0126         int left = opt.rect.left();
0127         int width = opt.rect.width();
0128 
0129         QString pkgSummary = index.data(PackageModel::SummaryRole).toString();
0130         if (!pkgSummary.isEmpty()) {
0131             if (leftToRight) {
0132                 pkgSummary.prepend(QLatin1String("- "));
0133             } else {
0134                 pkgSummary.append(QLatin1String(" -"));
0135             }
0136         }
0137 
0138         // store the original opacity
0139         qreal opa = p.opacity();
0140         QStyleOptionViewItem local_option_normal(opt);
0141         QColor foregroundColor;
0142 
0143         p.setOpacity(0.75);
0144         if (opt.state.testFlag(QStyle::State_Selected)) {
0145             foregroundColor = opt.palette.color(QPalette::HighlightedText);
0146         } else {
0147             foregroundColor = opt.palette.color(QPalette::Text);
0148         }
0149 
0150         p.setFont(local_option_normal.font);
0151         p.setPen(foregroundColor);
0152         p.drawText(opt.rect, Qt::AlignVCenter | (leftToRight ? Qt::AlignLeft : Qt::AlignRight), pkgSummary);
0153         p.setOpacity(opa);
0154 
0155         QLinearGradient gradient;
0156         // Gradient part of the background - fading of the text at the end
0157         if (leftToRight) {
0158             gradient = QLinearGradient(left + width - UNIVERSAL_PADDING - FADE_LENGTH,
0159                                     0,
0160                                     left + width - UNIVERSAL_PADDING,
0161                                     0);
0162             gradient.setColorAt(0, Qt::white);
0163             gradient.setColorAt(1, Qt::transparent);
0164         } else {
0165             gradient = QLinearGradient(left + UNIVERSAL_PADDING,
0166                                     0,
0167                                     left + UNIVERSAL_PADDING + FADE_LENGTH,
0168                                     0);
0169             gradient.setColorAt(0, Qt::transparent);
0170             gradient.setColorAt(1, Qt::white);
0171         }
0172 
0173         QRect paintRect = opt.rect;
0174         p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
0175         p.fillRect(paintRect, gradient);
0176 
0177         if (leftToRight) {
0178             gradient.setStart(left + width
0179                     - (UNIVERSAL_PADDING) - FADE_LENGTH, 0);
0180             gradient.setFinalStop(left + width
0181                     - (UNIVERSAL_PADDING), 0);
0182         } else {
0183             gradient.setStart(left + UNIVERSAL_PADDING
0184                     + (UNIVERSAL_PADDING ), 0);
0185             gradient.setFinalStop(left + UNIVERSAL_PADDING
0186                     + (UNIVERSAL_PADDING) + FADE_LENGTH, 0);
0187         }
0188         paintRect.setHeight(btHeight);
0189         p.fillRect(paintRect, gradient);
0190         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
0191         p.end();
0192 
0193         painter->drawPixmap(opt.rect.topLeft(), pixmap);
0194         return;
0195     } else if (index.column() == PackageModel::ActionCol) {
0196         bool pkgChecked = index.data(PackageModel::CheckStateRole).toBool();
0197         QStyleOptionViewItem opt(option);
0198         if (opt.state & QStyle::State_HasFocus) {
0199             opt.state ^= QStyle::State_HasFocus;
0200         }
0201 
0202         // Do not draw if the line is not selected or the mouse is over it
0203         if (!(option.state & QStyle::State_MouseOver) &&
0204             !(option.state & QStyle::State_Selected) &&
0205             !pkgChecked) {
0206             QStyledItemDelegate::paint(painter, opt, index);
0207             return;
0208         }
0209 
0210         QStyledItemDelegate::paint(painter, opt, index);
0211         QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0212 
0213         QStyleOptionButton optBt;
0214         optBt.rect = option.rect;
0215 
0216         Transaction::Info info;
0217         info = index.data(PackageModel::InfoRole).value<Transaction::Info>();
0218         bool    pkgInstalled  = (info == Transaction::InfoInstalled ||
0219                                  info == Transaction::InfoCollectionInstalled);
0220 
0221         // Calculate the top of the button which is the item height - the button height size divided by 2
0222         // this give us a little value which is the top and bottom margin
0223         optBt.rect.setLeft(optBt.rect.left() + UNIVERSAL_PADDING / 2);
0224         optBt.rect.setTop(optBt.rect.top() + ((btHeight - m_buttonSize.height()) / 2));
0225         optBt.rect.setSize(m_buttonSize); // the width and height sizes of the button
0226 
0227         if (option.state & QStyle::State_Selected) {
0228             optBt.palette.setColor(QPalette::WindowText, option.palette.color(QPalette::HighlightedText));
0229         }
0230 
0231         optBt.features = QStyleOptionButton::Flat;
0232         optBt.iconSize = m_buttonIconSize;
0233         if (pkgChecked) {
0234             optBt.state |= QStyle::State_Sunken | QStyle::State_Active | QStyle::State_Enabled;;
0235         } else {
0236             if (option.state & QStyle::State_MouseOver) {
0237                 optBt.state |= QStyle::State_MouseOver;
0238             }
0239             optBt.state |= QStyle::State_Raised | QStyle::State_Active | QStyle::State_Enabled;
0240         }
0241         optBt.icon = pkgInstalled ? m_removeIcon   : m_installIcon;
0242         optBt.text = pkgInstalled ? m_removeString : m_installString;
0243 
0244         style->drawControl(QStyle::CE_PushButton, &optBt, painter);
0245         return;
0246     }
0247 }
0248 
0249 bool ApplicationsDelegate::insideButton(const QRect &rect, const QPoint &pos) const
0250 {
0251 //     kDebug() << rect << pos;
0252     if ((pos.x() >= rect.x() && (pos.x() <= rect.x() + rect.width())) &&
0253         (pos.y() >= rect.y() && (pos.y() <= rect.y() + rect.height()))) {
0254         return true;
0255     }
0256     return false;
0257 }
0258 
0259 bool ApplicationsDelegate::editorEvent(QEvent *event,
0260                                        QAbstractItemModel *model,
0261                                        const QStyleOptionViewItem &option,
0262                                        const QModelIndex &index)
0263 {
0264     bool setData = false;
0265     if (index.column() == PackageModel::ActionCol &&
0266         event->type() == QEvent::MouseButtonPress) {
0267         setData = true;
0268     }
0269 
0270     const QWidget *widget = nullptr;
0271     if (const QStyleOptionViewItem *v = qstyleoption_cast<const QStyleOptionViewItem *>(&option)) {
0272         widget = v->widget;
0273     }
0274 
0275     QStyle *style = widget ? widget->style() : QApplication::style();
0276 
0277     // make sure that we have the right event type
0278     if ((event->type() == QEvent::MouseButtonRelease)
0279         || (event->type() == QEvent::MouseButtonDblClick)) {
0280         QStyleOptionViewItem viewOpt(option);
0281         initStyleOption(&viewOpt, index);
0282         QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, widget);
0283         QMouseEvent *me = static_cast<QMouseEvent*>(event);
0284         if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos()))
0285             return false;
0286 
0287         // eat the double click events inside the check rect
0288         if (event->type() == QEvent::MouseButtonDblClick)
0289             return true;
0290 
0291         setData = true;
0292     } else if (event->type() == QEvent::KeyPress) {
0293         if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Space
0294          || static_cast<QKeyEvent*>(event)->key() == Qt::Key_Select) {
0295             setData = true;
0296         }
0297     }
0298 
0299     if (setData) {
0300         return model->setData(index,
0301                        !index.data(PackageModel::CheckStateRole).toBool(),
0302                        Qt::CheckStateRole);
0303     }
0304     return false;
0305 }
0306 
0307 void ApplicationsDelegate::setCheckable(bool checkable)
0308 {
0309     m_checkable = checkable;
0310 }
0311 
0312 QSize ApplicationsDelegate::sizeHint(const QStyleOptionViewItem &option,
0313                                      const QModelIndex &index) const
0314 {
0315     QSize size;
0316 //     kDebug() << index;
0317     if (index.column() == PackageModel::ActionCol) {
0318         size = m_buttonSize;
0319         size.rheight() += UNIVERSAL_PADDING;
0320         size.rwidth()  += UNIVERSAL_PADDING;
0321     } else {
0322         QFontMetrics metric = QFontMetrics(option.font);
0323         // Button size is always bigger than text (since it has text in it
0324         size.setHeight(m_buttonSize.height() + UNIVERSAL_PADDING);
0325         size.setWidth(metric.width(index.data().toString()));
0326         if (index.column() == PackageModel::NameCol) {
0327             if (m_checkable) {
0328                 const QStyle *style = QApplication::style();
0329                 QRect rect = style->subElementRect(QStyle::SE_CheckBoxIndicator, &option);
0330                 // Adds the icon size AND the checkbox size
0331                 // [ x ] (icon) Text
0332                 size.rwidth() += 4 * UNIVERSAL_PADDING + 46 + rect.width();
0333 //                return QStyledItemDelegate::sizeHint(option, index);
0334             } else {
0335                 // Adds the icon size
0336                 size.rwidth() += 3 * UNIVERSAL_PADDING + 44;
0337             }
0338         } else {
0339             size.rwidth() += 2 * UNIVERSAL_PADDING;
0340         }
0341     }
0342     return size;
0343 }
0344 
0345 #include "moc_ApplicationsDelegate.cpp"