File indexing completed on 2024-04-21 03:49:52

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