File indexing completed on 2025-01-05 03:59:31

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2009 Bastian Holst <bastianholst@gmx.de>
0004 //
0005 
0006 // Self
0007 #include "PluginItemDelegate.h"
0008 
0009 // Qt
0010 #include <QEvent>
0011 #include <QSize>
0012 #include <QVariant>
0013 #include <QAbstractItemView>
0014 #include <QMouseEvent>
0015 #include <QStandardItemModel>
0016 #include <QApplication>
0017 #include <QPainter>
0018 
0019 #include <klocalizedstring.h>
0020 
0021 // Marble
0022 #include "RenderPluginModel.h"
0023 
0024 #include "digikam_debug.h"
0025 
0026 using namespace Marble;
0027 
0028 const QSize iconSize( 16, 16 );
0029 
0030 PluginItemDelegate::PluginItemDelegate( QAbstractItemView *view, QObject * parent )
0031     : QAbstractItemDelegate( parent )
0032 {
0033     // Enable mouse tracking of itemview makes it possible to find when the mouse if moved
0034     // without pressed buttons.
0035     view->setMouseTracking( true );
0036 }
0037 
0038 PluginItemDelegate::~PluginItemDelegate()
0039 {
0040 }
0041 
0042 void PluginItemDelegate::paint( QPainter *painter,
0043                                 const QStyleOptionViewItem& option,
0044                                 const QModelIndex& index ) const
0045 {
0046     Q_ASSERT( index.isValid() );
0047     QRect rect = option.rect;
0048     QStyle *style = QApplication::style();
0049 
0050     painter->save();
0051 
0052     // Drawing the background
0053     QStyleOption background = option;
0054     style->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );
0055 
0056     painter->translate( rect.topLeft() );
0057 
0058     // rect is now represented in item coordinates
0059     rect.moveTopLeft( QPoint( 0, 0 ) );
0060     // The point at the top left of the available drawing area.
0061     QPoint topLeft( 0, 0 );
0062     // The point at the top right of the available drawing area.
0063     QPoint topRight( rect.topRight() );
0064 
0065     QRect nameRect = rect;
0066 
0067     // Painting the checkbox
0068     QStyleOptionButton checkBox = checkboxOption( option, index, topLeft.x(), Qt::AlignLeft );
0069     painter->save();
0070     style->drawControl( QStyle::CE_CheckBox, &checkBox, painter );
0071     painter->restore();
0072 
0073     nameRect.setLeft( checkBox.rect.right() + 1 );
0074 
0075     // Painting the About Button
0076     QStyleOptionButton button = buttonOption( option, index, PluginItemDelegate::About,
0077                                               topRight.x(), Qt::AlignRight );
0078     style->drawControl( QStyle::CE_PushButton, &button, painter );
0079     topRight -= QPoint( button.rect.width(), 0 );
0080 
0081     // Painting the Configure Button
0082     if ( index.data( RenderPluginModel::ConfigurationDialogAvailable ).toBool() ) {
0083         QStyleOptionButton button = buttonOption( option, index, PluginItemDelegate::Configure,
0084                                                   topRight.x(), Qt::AlignRight );
0085         style->drawControl( QStyle::CE_PushButton, &button, painter );
0086         topRight -= QPoint( button.rect.width(), 0 );
0087 
0088         nameRect.setRight( button.rect.left() -1 );
0089     }
0090 
0091     // Painting the Icon
0092     const QIcon icon = index.data( Qt::DecorationRole ).value<QIcon>();
0093     const QPixmap iconPixmap = icon.pixmap(16, 16);
0094 
0095     nameRect.moveBottom( nameRect.bottom()+5 );
0096     style->drawItemPixmap( painter,
0097                            nameRect,
0098                            Qt::AlignLeft,
0099                            iconPixmap );
0100 
0101     nameRect.setLeft( nameRect.left() + 16 + 5 );
0102     nameRect.moveBottom( nameRect.bottom()-5 );
0103 
0104     // Painting the Name string
0105     QString name = index.data( Qt::DisplayRole ).toString();
0106 
0107     style->drawItemText( painter,
0108                          nameRect,
0109                          Qt::AlignLeft | Qt::AlignVCenter,
0110                          option.palette,
0111                          true,
0112                          name );
0113 
0114     painter->restore();
0115 }
0116 
0117 QSize PluginItemDelegate::sizeHint( const QStyleOptionViewItem& option,
0118                                     const QModelIndex & index ) const
0119 {
0120     QSize size;
0121 
0122     QStyleOptionViewItem opt = option;
0123     opt.rect = QRect( 0, 0, 0, 0 );
0124     QVector<QSize> elementSize;
0125     elementSize.reserve(4);
0126     QStyleOptionButton checkBox = checkboxOption( opt, index );
0127     elementSize.append( checkBox.rect.size() );
0128     QStyleOptionButton aboutButton = buttonOption( opt, index, PluginItemDelegate::About );
0129     elementSize.append( aboutButton.rect.size() );
0130     QStyleOptionButton configButton = buttonOption( opt, index, PluginItemDelegate::Configure );
0131     elementSize.append( configButton.rect.size() );
0132     elementSize.append( nameSize( index ) );
0133 
0134     for( const QSize& buttonSize: elementSize ) {
0135         if( buttonSize.height() > size.height() )
0136             size.setHeight( buttonSize.height() );
0137         size.setWidth( size.width() + buttonSize.width() );
0138     }
0139 
0140     return size;
0141 }
0142 
0143 void PluginItemDelegate::setAboutIcon( const QIcon& icon )
0144 {
0145     m_aboutIcon = icon;
0146 }
0147 
0148 void PluginItemDelegate::setConfigIcon( const QIcon& icon )
0149 {
0150     m_configIcon = icon;
0151 }
0152 
0153 bool PluginItemDelegate::editorEvent( QEvent *event,
0154                                       QAbstractItemModel *model,
0155                                       const QStyleOptionViewItem &option,
0156                                       const QModelIndex &index )
0157 {
0158     Q_ASSERT(event);
0159     Q_ASSERT(model);
0160 
0161     if ( ( event->type() == QEvent::MouseButtonRelease )
0162          || ( event->type() == QEvent::MouseButtonDblClick )
0163          || ( event->type() == QEvent::MouseButtonPress )
0164          || ( event->type() == QEvent::MouseMove ) )
0165     {
0166         QMouseEvent *me = static_cast<QMouseEvent*>(event);
0167         QPoint mousePosition = me->pos() - option.rect.topLeft();
0168 
0169         if ( ( event->type() == QEvent::MouseMove )
0170              && !( me->buttons() & Qt::LeftButton ) )
0171         {
0172             // If the mouse moves around and no left button is pressed, no pushbutton is pressed
0173             // and no other event will be successful.
0174             m_aboutPressedIndex = QModelIndex();
0175             m_configPressedIndex = QModelIndex();
0176             return true;
0177         }
0178 
0179         // Handle checkbox
0180         QRect checkRect = checkboxOption( option, index, 0, Qt::AlignLeft ).rect;
0181         if ( checkRect.contains( mousePosition )
0182              && ( ( event->type() == QEvent::MouseButtonDblClick )
0183                    || ( event->type() == QEvent::MouseButtonRelease ) ) )
0184         {
0185             // make sure that the item is checkable
0186             Qt::ItemFlags flags = model->flags(index);
0187             if ( !( flags & Qt::ItemIsUserCheckable ) || !( option.state & QStyle::State_Enabled )
0188                 || !( flags & Qt::ItemIsEnabled ) )
0189                 return false;
0190 
0191             // make sure that we have a check state
0192             QVariant checkValue = index.data( Qt::CheckStateRole );
0193             if ( !checkValue.isValid() )
0194                 return false;
0195 
0196             // eat the double click events inside the check rect
0197             if ( event->type() == QEvent::MouseButtonDblClick )
0198                 return true;
0199 
0200             Qt::CheckState state = ( static_cast<Qt::CheckState>( checkValue.toInt() ) == Qt::Checked
0201                                      ? Qt::Unchecked : Qt::Checked );
0202             return model->setData(index, state, Qt::CheckStateRole);
0203         }
0204 
0205         if ( ( event->type() == QEvent::MouseMove )
0206              && !( me->buttons() & Qt::LeftButton ) )
0207         {
0208             m_aboutPressedIndex = QModelIndex();
0209             m_configPressedIndex = QModelIndex();
0210             return true;
0211         }
0212 
0213         QPoint topRight = option.rect.topRight();
0214 
0215         // Handle aboutButton
0216         {
0217             QRect aboutRect = buttonOption( option,
0218                                             index,
0219                                             PluginItemDelegate::About,
0220                                             topRight.x(),
0221                                             Qt::AlignRight ).rect;
0222             if ( aboutRect.contains( mousePosition ) ) {
0223                 if ( event->type() == QEvent::MouseButtonDblClick )
0224                     return true;
0225                 if ( event->type() == QEvent::MouseButtonPress ) {
0226                     m_aboutPressedIndex = index;
0227                     m_configPressedIndex = QModelIndex();
0228                     return true;
0229                 }
0230                 if ( event->type() == QEvent::MouseButtonRelease ) {
0231                     m_aboutPressedIndex = QModelIndex();
0232                     m_configPressedIndex = QModelIndex();
0233                     Q_EMIT aboutPluginClicked( index );
0234                     return true;
0235                 }
0236                 if ( event->type() == QEvent::MouseMove ) {
0237                     if ( me->buttons() & Qt::LeftButton ) {
0238                         m_aboutPressedIndex = index;
0239                         m_configPressedIndex = QModelIndex();
0240                         return true;
0241                     }
0242                     else {
0243                         m_aboutPressedIndex = QModelIndex();
0244                         m_configPressedIndex = QModelIndex();
0245                         return true;
0246                     }
0247                 }
0248             }
0249             else {
0250                 // If the mouse is on the item and the mouse isn't above the button.
0251                 // no about button is pressed.
0252                 m_aboutPressedIndex = QModelIndex();
0253             }
0254             topRight -= QPoint( aboutRect.width(), 0 );
0255         }
0256 
0257         // Handle configButton
0258         // make sure we have config button
0259         if ( index.data( RenderPluginModel::ConfigurationDialogAvailable ).toBool() ) {
0260             QRect configRect = buttonOption( option,
0261                                              index,
0262                                              PluginItemDelegate::Configure,
0263                                              topRight.x(),
0264                                              Qt::AlignRight ).rect;
0265             if( configRect.contains( mousePosition ) ) {
0266                 if ( event->type() == QEvent::MouseButtonDblClick )
0267                     return true;
0268 
0269                 if ( event->type() == QEvent::MouseButtonPress ) {
0270                     m_aboutPressedIndex = QModelIndex();
0271                     m_configPressedIndex = index;
0272                     return true;
0273                 }
0274                 if ( event->type() == QEvent::MouseButtonRelease ) {
0275                     m_aboutPressedIndex = QModelIndex();
0276                     m_configPressedIndex = QModelIndex();
0277                     Q_EMIT configPluginClicked( index );
0278                     return true;
0279                 }
0280                 if ( event->type() == QEvent::MouseMove ) {
0281                     if ( me->buttons() & Qt::LeftButton ) {
0282                         m_aboutPressedIndex = QModelIndex();
0283                         m_configPressedIndex = index;
0284                         return true;
0285                     }
0286                     else {
0287                         m_aboutPressedIndex = QModelIndex();
0288                         m_configPressedIndex = QModelIndex();
0289                         return true;
0290                     }
0291                 }
0292             }
0293             else {
0294                 // If the mouse is on the item and the mouse isn't above the button.
0295                 // no config button is pressed.
0296                 m_configPressedIndex = QModelIndex();
0297             }
0298 
0299             topRight -= QPoint( configRect.width(), 0 );
0300         }
0301         else {
0302             // If we don't have an config dialog shown and the mouse is over this item,
0303             // no config button is pressed.
0304             m_configPressedIndex = QModelIndex();
0305         }
0306     }
0307 
0308     return false;
0309 }
0310 
0311 QStyleOptionButton PluginItemDelegate::checkboxOption( const QStyleOptionViewItem& option,
0312                                                        const QModelIndex& index,
0313                                                        int position,
0314                                                        Qt::AlignmentFlag alignment )
0315 {
0316     QStyleOptionButton styleOptionButton;
0317     if ( index.data( Qt::CheckStateRole ).toBool() )
0318         styleOptionButton.state = option.state | QStyle::State_On;
0319     else
0320         styleOptionButton.state = option.state | QStyle::State_Off;
0321     QSize size = QApplication::style()->sizeFromContents( QStyle::CT_CheckBox, &option, QSize() );
0322     if ( size.isEmpty() ) {
0323         // A checkbox has definitely a size != 0
0324         styleOptionButton.rect.setSize( QSize( 22, 22 ) );
0325     }
0326     else {
0327         styleOptionButton.rect.setSize( QSize( size.width(), size.height() ) );
0328     }
0329     styleOptionButton.rect = alignRect( styleOptionButton.rect, option.rect, position, alignment );
0330     return styleOptionButton;
0331 }
0332 
0333 QStyleOptionButton PluginItemDelegate::buttonOption( const QStyleOptionViewItem& option,
0334                                                      const QModelIndex& index,
0335                                                      PluginItemDelegate::ButtonType type,
0336                                                      int position,
0337                                                      Qt::AlignmentFlag alignment ) const
0338 {
0339     QStyleOptionButton buttonOption;
0340     buttonOption.state = option.state;
0341     buttonOption.state &= ~QStyle::State_HasFocus;
0342 
0343     buttonOption.rect.setTopLeft( QPoint( 0, 0 ) );
0344     buttonOption.palette = option.palette;
0345     buttonOption.features = QStyleOptionButton::None;
0346 
0347     QSize contentSize;
0348     if ( type == PluginItemDelegate::About ) {
0349         if ( m_aboutIcon.isNull() ) {
0350             buttonOption.text = i18n( "About" );
0351             contentSize = buttonOption.fontMetrics.size( 0, buttonOption.text ) + QSize( 4, 4 );
0352         }
0353         else {
0354             buttonOption.icon = m_aboutIcon;
0355             buttonOption.iconSize = iconSize;
0356             contentSize = iconSize;
0357         }
0358 
0359         if ( m_aboutPressedIndex == index ) {
0360             buttonOption.state |= QStyle::State_Sunken;
0361         }
0362     }
0363     else if ( type == PluginItemDelegate::Configure ) {
0364         if ( m_configIcon.isNull() ) {
0365             buttonOption.text = i18n( "Configure" );
0366             contentSize = buttonOption.fontMetrics.size( 0, buttonOption.text ) + QSize( 4, 4 );
0367         }
0368         else {
0369             buttonOption.icon = m_configIcon;
0370             buttonOption.iconSize = iconSize;
0371             contentSize = iconSize;
0372         }
0373         if ( m_configPressedIndex == index ) {
0374             buttonOption.state |= QStyle::State_Sunken;
0375         }
0376     }
0377 
0378     QSize buttonSize = QApplication::style()->sizeFromContents( QStyle::CT_PushButton,
0379                                                                 &buttonOption,
0380                                                                 contentSize );
0381     buttonOption.rect.setSize( buttonSize );
0382     buttonOption.rect = alignRect( buttonOption.rect, option.rect, position, alignment );
0383     return buttonOption;
0384 }
0385 
0386 QSize PluginItemDelegate::nameSize( const QModelIndex& index )
0387 {
0388     QString name = index.data( Qt::DisplayRole ).toString();
0389     // FIXME: QApplication::fontMetrics() doesn't work for non-application fonts
0390     QSize nameSize( QFontMetrics(qApp->font()).size( 0, name ) );
0391     return nameSize;
0392 }
0393 
0394 QRect PluginItemDelegate::alignRect( const QRect& object,
0395                                      const QRect& frame,
0396                                      int position,
0397                                      Qt::AlignmentFlag alignment )
0398 {
0399     QRect rect = object;
0400 
0401     rect.setTopLeft( QPoint( 0, 0 ) );
0402     // Moves the object to the middle of the item.
0403     if ( rect.height() < frame.height() ) {
0404         rect.moveTop( ( frame.height() - rect.height() ) / 2 );
0405     }
0406 
0407     if ( alignment & Qt::AlignLeft ) {
0408         rect.moveLeft( position );
0409     }
0410     else if ( alignment & Qt::AlignRight ) {
0411         rect.moveRight( position );
0412     }
0413 
0414     return rect;
0415 }
0416 
0417 
0418 #include "moc_PluginItemDelegate.cpp"