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"