File indexing completed on 2024-05-05 05:47:49
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 "ChangesDelegate.h" 0022 0023 #include <KIconLoader> 0024 #include <KLocalizedString> 0025 #include <QApplication> 0026 #include <QLoggingCategory> 0027 #include <QPushButton> 0028 #include <QTreeView> 0029 #include <QHeaderView> 0030 0031 #include <QPainter> 0032 0033 #include "PackageModel.h" 0034 #include "PkIcons.h" 0035 0036 #include <Transaction> 0037 0038 #define FAV_ICON_SIZE 24 0039 #define EMBLEM_ICON_SIZE 8 0040 #define UNIVERSAL_PADDING 4 0041 #define FADE_LENGTH 16 0042 #define MAIN_ICON_SIZE 32 0043 0044 Q_DECLARE_LOGGING_CATEGORY(APPER_LIB) 0045 0046 using namespace PackageKit; 0047 0048 ChangesDelegate::ChangesDelegate(QAbstractItemView *parent) : 0049 KExtendableItemDelegate(parent), 0050 m_viewport(parent->viewport()), 0051 // loads it here to be faster when displaying items 0052 m_packageIcon(QIcon::fromTheme(QLatin1String("package"))), 0053 m_collectionIcon(QIcon::fromTheme(QLatin1String("package-orign"))), 0054 m_installIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))), 0055 m_installString(i18n("Do not Install")), 0056 m_removeIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))), 0057 m_removeString(i18n("Do not Remove")), 0058 m_undoIcon(QIcon::fromTheme(QLatin1String("edit-undo"))), 0059 m_undoString(i18n("Deselect")), 0060 m_checkedIcon(QIcon::fromTheme(QLatin1String("dialog-ok-apply"))) 0061 { 0062 // maybe rename or copy it to package-available 0063 if (QApplication::isRightToLeft()) { 0064 setExtendPixmap(SmallIcon(QLatin1String("arrow-left"))); 0065 } else { 0066 setExtendPixmap(SmallIcon(QLatin1String("arrow-right"))); 0067 } 0068 setContractPixmap(SmallIcon(QLatin1String("arrow-down"))); 0069 // store the size of the extend pixmap to know how much we should move 0070 m_extendPixmapWidth = SmallIcon(QLatin1String("arrow-right")).size().width(); 0071 0072 QPushButton button, button2; 0073 button.setText(m_installString); 0074 button.setIcon(m_installIcon); 0075 button2.setText(m_removeString); 0076 button2.setIcon(m_removeIcon); 0077 m_buttonSize = button.sizeHint(); 0078 int width = qMax(button.sizeHint().width(), button2.sizeHint().width()); 0079 button.setText(m_undoString); 0080 width = qMax(width, button2.sizeHint().width()); 0081 m_buttonSize.setWidth(width); 0082 m_buttonIconSize = button.iconSize(); 0083 } 0084 0085 void ChangesDelegate::paint(QPainter *painter, 0086 const QStyleOptionViewItem &option, 0087 const QModelIndex &index) const 0088 { 0089 if (!index.isValid()) { 0090 return; 0091 } 0092 bool leftToRight = (painter->layoutDirection() == Qt::LeftToRight); 0093 0094 QStyleOptionViewItem opt(option); 0095 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); 0096 painter->save(); 0097 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); 0098 painter->restore(); 0099 0100 //grab the package from the index pointer 0101 QString pkgName = index.data(PackageModel::NameRole).toString(); 0102 QString pkgSummary = index.data(PackageModel::SummaryRole).toString(); 0103 QString pkgVersion = index.data(PackageModel::VersionRole).toString(); 0104 QString pkgArch = index.data(PackageModel::ArchRole).toString(); 0105 // QString pkgIconPath = index.data(PackageModel::IconPathRole).toString(); 0106 bool pkgChecked = index.data(PackageModel::CheckStateRole).toBool(); 0107 bool pkgCheckable = !index.data(Qt::CheckStateRole).isNull(); 0108 Transaction::Info info; 0109 info = index.data(PackageModel::InfoRole).value<Transaction::Info>(); 0110 bool pkgInstalled = (info == Transaction::InfoInstalled || 0111 info == Transaction::InfoCollectionInstalled); 0112 0113 bool pkgCollection = (info == Transaction::InfoCollectionInstalled || 0114 info == Transaction::InfoCollectionAvailable); 0115 0116 QIcon emblemIcon; 0117 if (pkgCheckable) { 0118 // update kind icon 0119 emblemIcon = index.data(PackageModel::IconRole).value<QIcon>(); 0120 } else { 0121 emblemIcon = m_checkedIcon; 0122 } 0123 0124 // pain the background (checkbox and the extender) 0125 if (m_extendPixmapWidth) { 0126 KExtendableItemDelegate::paint(painter, opt, index); 0127 } 0128 0129 int leftCount; 0130 if (leftToRight) { 0131 opt.rect.setLeft(option.rect.left() + m_extendPixmapWidth + UNIVERSAL_PADDING); 0132 leftCount = opt.rect.left() + UNIVERSAL_PADDING; 0133 } else { 0134 opt.rect.setRight(option.rect.right() - m_extendPixmapWidth - UNIVERSAL_PADDING); 0135 leftCount = opt.rect.width() - (UNIVERSAL_PADDING + MAIN_ICON_SIZE); 0136 } 0137 0138 int left = opt.rect.left(); 0139 int top = opt.rect.top(); 0140 int width = opt.rect.width(); 0141 0142 QStyleOptionButton optBt; 0143 optBt.rect = opt.rect; 0144 if (pkgCheckable) { 0145 optBt.rect = style->subElementRect(QStyle::SE_CheckBoxIndicator, &optBt); 0146 // Count the checkbox size 0147 if (leftToRight) { 0148 leftCount += optBt.rect.width(); 0149 } else { 0150 leftCount -= optBt.rect.width(); 0151 } 0152 } else if ((option.state & QStyle::State_MouseOver) || 0153 (option.state & QStyle::State_Selected) || 0154 !pkgChecked) { 0155 if (leftToRight) { 0156 optBt.rect.setLeft(left + width - (m_buttonSize.width() + UNIVERSAL_PADDING)); 0157 width -= m_buttonSize.width() + UNIVERSAL_PADDING; 0158 } else { 0159 optBt.rect.setLeft(left + UNIVERSAL_PADDING); 0160 left += m_buttonSize.width() + UNIVERSAL_PADDING; 0161 } 0162 // Calculate the top of the button which is the item height - the button height size divided by 2 0163 // this give us a little value which is the top and bottom margin 0164 optBt.rect.setTop(optBt.rect.top() + ((calcItemHeight(option) - m_buttonSize.height()) / 2)); 0165 optBt.rect.setSize(m_buttonSize); // the width and height sizes of the button 0166 optBt.features = QStyleOptionButton::Flat; 0167 optBt.iconSize = m_buttonIconSize; 0168 optBt.icon = pkgInstalled ? m_removeIcon : m_installIcon; 0169 optBt.text = pkgInstalled ? m_removeString : m_installString; 0170 if (pkgChecked) { 0171 optBt.state |= QStyle::State_Raised | QStyle::State_Active | QStyle::State_Enabled;; 0172 } else { 0173 if ((option.state & QStyle::State_MouseOver) && 0174 !(option.state & QStyle::State_Selected)) { 0175 optBt.state |= QStyle::State_MouseOver; 0176 } 0177 optBt.state |= QStyle::State_Sunken | QStyle::State_Active | QStyle::State_Enabled; 0178 } 0179 style->drawControl(QStyle::CE_PushButton, &optBt, painter); 0180 } 0181 0182 // QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent()); 0183 // QPoint pos = view->viewport()->mapFromGlobal(QCursor::pos()); 0184 // kDebug() << pos; 0185 0186 0187 // selects the mode to paint the icon based on the info field 0188 QIcon::Mode iconMode = QIcon::Normal; 0189 if (option.state & QStyle::State_MouseOver) { 0190 iconMode = QIcon::Active; 0191 } 0192 0193 QColor foregroundColor = (option.state.testFlag(QStyle::State_Selected))? 0194 option.palette.color(QPalette::HighlightedText):option.palette.color(QPalette::Text); 0195 0196 // Painting main column 0197 QStyleOptionViewItem local_option_title(option); 0198 QStyleOptionViewItem local_option_normal(option); 0199 0200 local_option_normal.font.setPointSize(local_option_normal.font.pointSize() - 1); 0201 0202 QPixmap pixmap(option.rect.size()); 0203 pixmap.fill(Qt::transparent); 0204 QPainter p(&pixmap); 0205 p.translate(-option.rect.topLeft()); 0206 0207 // Main icon 0208 QIcon icon; 0209 if (pkgCollection) { 0210 icon = m_collectionIcon; 0211 } else { 0212 icon = PkIcons::getIcon(index.data(PackageModel::IconRole).toString(), QString()); 0213 if (icon.isNull()) { 0214 icon = m_packageIcon; 0215 } 0216 } 0217 // if (pkgIconPath.isEmpty()) { 0218 // icon = pkgCollection ? m_collectionIcon : m_packageIcon; 0219 // } else { 0220 // icon = PkIcons::getIcon(pkgIconPath, "package"); 0221 // } 0222 0223 int iconSize = calcItemHeight(option) - 2 * UNIVERSAL_PADDING; 0224 icon.paint(&p, 0225 leftCount, 0226 top + UNIVERSAL_PADDING, 0227 iconSize, 0228 iconSize, 0229 Qt::AlignCenter, 0230 iconMode); 0231 0232 int textWidth; 0233 if (leftToRight) { 0234 // add the main icon 0235 leftCount += iconSize + UNIVERSAL_PADDING; 0236 textWidth = width - (leftCount - left); 0237 } else { 0238 leftCount -= UNIVERSAL_PADDING; 0239 textWidth = leftCount - left; 0240 leftCount = left; 0241 } 0242 0243 0244 // Painting 0245 0246 // Text 0247 const int itemHeight = calcItemHeight(option); 0248 0249 p.setPen(foregroundColor); 0250 // compose the top line 0251 // Collections does not have version and arch 0252 if (option.state & QStyle::State_MouseOver && !pkgCollection) { 0253 //! pkgName = pkgName + " - " + pkgVersion + (pkgArch.isNull() ? NULL : " (" + pkgArch + ')'); 0254 } 0255 0256 // draw the top line 0257 int topTextHeight = QFontInfo(local_option_title.font).pixelSize(); 0258 p.setFont(local_option_title.font); 0259 p.drawText(leftCount, 0260 top, 0261 textWidth, 0262 topTextHeight + UNIVERSAL_PADDING, 0263 Qt::AlignVCenter | Qt::AlignLeft, 0264 pkgName); 0265 0266 // draw the bottom line 0267 iconSize = topTextHeight + UNIVERSAL_PADDING; 0268 if (pkgCheckable || pkgInstalled) { 0269 emblemIcon.paint(&p, 0270 leftToRight ? leftCount : (textWidth + left) - iconSize, 0271 top + topTextHeight + UNIVERSAL_PADDING, 0272 iconSize, 0273 iconSize, 0274 Qt::AlignVCenter | Qt::AlignHCenter, 0275 iconMode); 0276 } 0277 0278 // store the original opacity 0279 qreal opa = p.opacity(); 0280 if (!(option.state & QStyle::State_MouseOver) && !(option.state & QStyle::State_Selected)) { 0281 p.setOpacity(opa / 2.5); 0282 } 0283 0284 p.setFont(local_option_normal.font); 0285 p.drawText(leftToRight ? leftCount + iconSize + UNIVERSAL_PADDING : left - UNIVERSAL_PADDING, 0286 top + itemHeight / 2, 0287 textWidth - iconSize, 0288 QFontInfo(local_option_normal.font).pixelSize() + UNIVERSAL_PADDING, 0289 Qt::AlignTop | Qt::AlignLeft, 0290 pkgSummary); 0291 p.setOpacity(opa); 0292 0293 QLinearGradient gradient; 0294 // Gradient part of the background - fading of the text at the end 0295 if (leftToRight) { 0296 gradient = QLinearGradient(left + width - UNIVERSAL_PADDING - FADE_LENGTH, 0297 0, 0298 left + width - UNIVERSAL_PADDING, 0299 0); 0300 gradient.setColorAt(0, Qt::white); 0301 gradient.setColorAt(1, Qt::transparent); 0302 } else { 0303 gradient = QLinearGradient(left + UNIVERSAL_PADDING, 0304 0, 0305 left + UNIVERSAL_PADDING + FADE_LENGTH, 0306 0); 0307 gradient.setColorAt(0, Qt::transparent); 0308 gradient.setColorAt(1, Qt::white); 0309 } 0310 0311 QRect paintRect = option.rect; 0312 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 0313 p.fillRect(paintRect, gradient); 0314 0315 if (leftToRight) { 0316 gradient.setStart(left + width 0317 - (UNIVERSAL_PADDING + EMBLEM_ICON_SIZE) - FADE_LENGTH, 0); 0318 gradient.setFinalStop(left + width 0319 - (UNIVERSAL_PADDING + EMBLEM_ICON_SIZE), 0); 0320 } else { 0321 gradient.setStart(left + UNIVERSAL_PADDING 0322 + (UNIVERSAL_PADDING + EMBLEM_ICON_SIZE), 0); 0323 gradient.setFinalStop(left + UNIVERSAL_PADDING 0324 + (UNIVERSAL_PADDING + EMBLEM_ICON_SIZE) + FADE_LENGTH, 0); 0325 } 0326 paintRect.setHeight(UNIVERSAL_PADDING + MAIN_ICON_SIZE / 2); 0327 p.fillRect(paintRect, gradient); 0328 p.setCompositionMode(QPainter::CompositionMode_SourceOver); 0329 p.end(); 0330 0331 painter->drawPixmap(option.rect.topLeft(), pixmap); 0332 } 0333 0334 int ChangesDelegate::calcItemHeight(const QStyleOptionViewItem &option) const 0335 { 0336 // Painting main column 0337 QStyleOptionViewItem local_option_title(option); 0338 QStyleOptionViewItem local_option_normal(option); 0339 0340 local_option_normal.font.setPointSize(local_option_normal.font.pointSize() - 1); 0341 0342 int textHeight = QFontInfo(local_option_title.font).pixelSize() + QFontInfo(local_option_normal.font).pixelSize(); 0343 return textHeight + 3 * UNIVERSAL_PADDING; 0344 } 0345 0346 bool ChangesDelegate::insideButton(const QRect &rect, const QPoint &pos) const 0347 { 0348 // kDebug() << rect << pos; 0349 if ((pos.x() >= rect.x() && (pos.x() <= rect.x() + rect.width())) && 0350 (pos.y() >= rect.y() && (pos.y() <= rect.y() + rect.height()))) { 0351 return true; 0352 } 0353 return false; 0354 } 0355 0356 bool ChangesDelegate::editorEvent(QEvent *event, 0357 QAbstractItemModel *model, 0358 const QStyleOptionViewItem &option, 0359 const QModelIndex &index) 0360 { 0361 Q_UNUSED(option) 0362 0363 if (event->type() == QEvent::MouseButtonRelease) { 0364 QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent()); 0365 QPoint point = m_viewport->mapFromGlobal(QCursor::pos()); 0366 QTreeView *tree = qobject_cast<QTreeView*>(parent()); 0367 if (tree) { 0368 point.ry() -= tree->header()->size().height(); 0369 } 0370 0371 bool leftToRight = QApplication::isLeftToRight(); 0372 QStyleOptionButton optBt; 0373 optBt.rect = option.rect; 0374 if (leftToRight) { 0375 optBt.rect.setLeft(option.rect.left() + option.rect.width() - (m_buttonSize.width() + UNIVERSAL_PADDING)); 0376 } else { 0377 optBt.rect.setLeft(option.rect.left() + UNIVERSAL_PADDING); 0378 } 0379 // Calculate the top of the button which is the item height - the button height size divided by 2 0380 // this give us a little value which is the top and bottom margin 0381 optBt.rect.setTop(optBt.rect.top() + ((calcItemHeight(option) - m_buttonSize.height()) / 2)); 0382 optBt.rect.setSize(m_buttonSize); 0383 0384 qCDebug(APPER_LIB) << point << option.rect.left() << option << insideButton(optBt.rect, point); 0385 // kDebug() << view->visualRect(index); 0386 if (insideButton(optBt.rect, point)) { 0387 return model->setData(index, 0388 !index.data(PackageModel::CheckStateRole).toBool(), 0389 Qt::CheckStateRole); 0390 } 0391 QRect rect = view->visualRect(index); 0392 if (QApplication::isRightToLeft()) { 0393 if ((rect.width() - point.x()) <= m_extendPixmapWidth) { 0394 emit showExtendItem(index); 0395 } 0396 } else if (point.x() <= m_extendPixmapWidth) { 0397 emit showExtendItem(index); 0398 } 0399 } 0400 0401 // We need move the option rect left because KExtendableItemDelegate 0402 // drew the extendPixmap 0403 QStyleOptionViewItem opt(option); 0404 if (QApplication::isRightToLeft()) { 0405 opt.rect.setRight(option.rect.right() - m_extendPixmapWidth); 0406 } else { 0407 opt.rect.setLeft(option.rect.left() + m_extendPixmapWidth); 0408 } 0409 // When the exterder is shown the height get compromised, 0410 // this makes sure the check box is always known 0411 opt.rect.setHeight(calcItemHeight(option)); 0412 return KExtendableItemDelegate::editorEvent(event, model, opt, index); 0413 } 0414 0415 void ChangesDelegate::setExtendPixmapWidth(int width) 0416 { 0417 m_extendPixmapWidth = width; 0418 } 0419 0420 void ChangesDelegate::setViewport(QWidget *viewport) 0421 { 0422 m_viewport = viewport; 0423 } 0424 0425 QSize ChangesDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index ) const 0426 { 0427 int width = (index.column() == 0) ? index.data(Qt::SizeHintRole).toSize().width() : FAV_ICON_SIZE + 2 * UNIVERSAL_PADDING; 0428 QSize ret(KExtendableItemDelegate::sizeHint(option, index)); 0429 // remove the default size of the index 0430 ret -= QStyledItemDelegate::sizeHint(option, index); 0431 0432 ret.rheight() += calcItemHeight(option); 0433 ret.rwidth() += width; 0434 0435 return ret; 0436 } 0437 0438 #include "moc_ChangesDelegate.cpp"