Marble

PluginItemDelegate.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2009 Bastian Holst <[email protected]>
4 //
5 
6 // Self
7 #include "PluginItemDelegate.h"
8 
9 // Marble
10 #include "RenderPluginModel.h"
11 #include "MarbleDebug.h"
12 
13 // Qt
14 #include <QEvent>
15 #include <QSize>
16 #include <QVariant>
17 #include <QAbstractItemView>
18 #include <QMouseEvent>
19 #include <QStandardItemModel>
20 #include <QApplication>
21 #include <QPainter>
22 
23 using namespace Marble;
24 /* TRANSLATOR Marble::PluginItemDelegate */
25 
26 const QSize iconSize( 16, 16 );
27 
28 PluginItemDelegate::PluginItemDelegate( QAbstractItemView *view, QObject * parent )
29  : QAbstractItemDelegate( parent )
30 {
31  // Enable mouse tracking of itemview makes it possible to find when the mouse if moved
32  // without pressed buttons.
33  view->setMouseTracking( true );
34 }
35 
36 PluginItemDelegate::~PluginItemDelegate()
37 {
38 }
39 
40 void PluginItemDelegate::paint( QPainter *painter,
41  const QStyleOptionViewItem& option,
42  const QModelIndex& index ) const
43 {
44  Q_ASSERT( index.isValid() );
45  QRect rect = option.rect;
46  QStyle *style = QApplication::style();
47 
48  painter->save();
49 
50  // Drawing the background
51  QStyleOption background = option;
52  style->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );
53 
54  painter->translate( rect.topLeft() );
55 
56  // rect is now represented in item coordinates
57  rect.moveTopLeft( QPoint( 0, 0 ) );
58  // The point at the top left of the available drawing area.
59  QPoint topLeft( 0, 0 );
60  // The point at the top right of the available drawing area.
61  QPoint topRight( rect.topRight() );
62 
63  QRect nameRect = rect;
64 
65  // Painting the checkbox
66  QStyleOptionButton checkBox = checkboxOption( option, index, topLeft.x(), Qt::AlignLeft );
67  painter->save();
68  style->drawControl( QStyle::CE_CheckBox, &checkBox, painter );
69  painter->restore();
70 
71  nameRect.setLeft( checkBox.rect.right() + 1 );
72 
73  // Painting the About Button
74  QStyleOptionButton button = buttonOption( option, index, PluginItemDelegate::About,
75  topRight.x(), Qt::AlignRight );
76  style->drawControl( QStyle::CE_PushButton, &button, painter );
77  topRight -= QPoint( button.rect.width(), 0 );
78 
79  // Painting the Configure Button
80  if ( index.data( RenderPluginModel::ConfigurationDialogAvailable ).toBool() ) {
81  QStyleOptionButton button = buttonOption( option, index, PluginItemDelegate::Configure,
82  topRight.x(), Qt::AlignRight );
83  style->drawControl( QStyle::CE_PushButton, &button, painter );
84  topRight -= QPoint( button.rect.width(), 0 );
85 
86  nameRect.setRight( button.rect.left() -1 );
87  }
88 
89  // Painting the Icon
90  const QIcon icon = index.data( Qt::DecorationRole ).value<QIcon>();
91  const QPixmap iconPixmap = icon.pixmap(16, 16);
92 
93  nameRect.moveBottom( nameRect.bottom()+5 );
94  style->drawItemPixmap( painter,
95  nameRect,
97  iconPixmap );
98 
99  nameRect.setLeft( nameRect.left() + 16 + 5 );
100  nameRect.moveBottom( nameRect.bottom()-5 );
101 
102  // Painting the Name string
104 
105  style->drawItemText( painter,
106  nameRect,
108  option.palette,
109  true,
110  name );
111 
112  painter->restore();
113 }
114 
115 QSize PluginItemDelegate::sizeHint( const QStyleOptionViewItem& option,
116  const QModelIndex & index ) const
117 {
118  QSize size;
119 
120  QStyleOptionViewItem opt = option;
121  opt.rect = QRect( 0, 0, 0, 0 );
122  QVector<QSize> elementSize;
123  elementSize.reserve(4);
124  QStyleOptionButton checkBox = checkboxOption( opt, index );
125  elementSize.append( checkBox.rect.size() );
126  QStyleOptionButton aboutButton = buttonOption( opt, index, PluginItemDelegate::About );
127  elementSize.append( aboutButton.rect.size() );
128  QStyleOptionButton configButton = buttonOption( opt, index, PluginItemDelegate::Configure );
129  elementSize.append( configButton.rect.size() );
130  elementSize.append( nameSize( index ) );
131 
132  for( const QSize& buttonSize: elementSize ) {
133  if( buttonSize.height() > size.height() )
134  size.setHeight( buttonSize.height() );
135  size.setWidth( size.width() + buttonSize.width() );
136  }
137 
138  return size;
139 }
140 
141 void PluginItemDelegate::setAboutIcon( const QIcon& icon )
142 {
143  m_aboutIcon = icon;
144 }
145 
146 void PluginItemDelegate::setConfigIcon( const QIcon& icon )
147 {
148  m_configIcon = icon;
149 }
150 
151 bool PluginItemDelegate::editorEvent( QEvent *event,
152  QAbstractItemModel *model,
153  const QStyleOptionViewItem &option,
154  const QModelIndex &index )
155 {
156  Q_ASSERT(event);
157  Q_ASSERT(model);
158 
159  if ( ( event->type() == QEvent::MouseButtonRelease )
160  || ( event->type() == QEvent::MouseButtonDblClick )
161  || ( event->type() == QEvent::MouseButtonPress )
162  || ( event->type() == QEvent::MouseMove ) )
163  {
164  QMouseEvent *me = static_cast<QMouseEvent*>(event);
165  QPoint mousePosition = me->pos() - option.rect.topLeft();
166 
167  if ( ( event->type() == QEvent::MouseMove )
168  && !( me->buttons() & Qt::LeftButton ) )
169  {
170  // If the mouse moves around and no left button is pressed, no pushbutton is pressed
171  // and no other event will be successful.
172  m_aboutPressedIndex = QModelIndex();
173  m_configPressedIndex = QModelIndex();
174  return true;
175  }
176 
177  // Handle checkbox
178  QRect checkRect = checkboxOption( option, index, 0, Qt::AlignLeft ).rect;
179  if ( checkRect.contains( mousePosition )
180  && ( ( event->type() == QEvent::MouseButtonDblClick )
181  || ( event->type() == QEvent::MouseButtonRelease ) ) )
182  {
183  // make sure that the item is checkable
184  Qt::ItemFlags flags = model->flags(index);
185  if ( !( flags & Qt::ItemIsUserCheckable ) || !( option.state & QStyle::State_Enabled )
186  || !( flags & Qt::ItemIsEnabled ) )
187  return false;
188 
189  // make sure that we have a check state
190  QVariant checkValue = index.data( Qt::CheckStateRole );
191  if ( !checkValue.isValid() )
192  return false;
193 
194  // eat the double click events inside the check rect
195  if ( event->type() == QEvent::MouseButtonDblClick )
196  return true;
197 
198  Qt::CheckState state = ( static_cast<Qt::CheckState>( checkValue.toInt() ) == Qt::Checked
200  return model->setData(index, state, Qt::CheckStateRole);
201  }
202 
203  if ( ( event->type() == QEvent::MouseMove )
204  && !( me->buttons() & Qt::LeftButton ) )
205  {
206  m_aboutPressedIndex = QModelIndex();
207  m_configPressedIndex = QModelIndex();
208  return true;
209  }
210 
211  QPoint topRight = option.rect.topRight();
212 
213  // Handle aboutButton
214  {
215  QRect aboutRect = buttonOption( option,
216  index,
217  PluginItemDelegate::About,
218  topRight.x(),
219  Qt::AlignRight ).rect;
220  if ( aboutRect.contains( mousePosition ) ) {
221  if ( event->type() == QEvent::MouseButtonDblClick )
222  return true;
223  if ( event->type() == QEvent::MouseButtonPress ) {
224  m_aboutPressedIndex = index;
225  m_configPressedIndex = QModelIndex();
226  return true;
227  }
228  if ( event->type() == QEvent::MouseButtonRelease ) {
229  m_aboutPressedIndex = QModelIndex();
230  m_configPressedIndex = QModelIndex();
231  emit aboutPluginClicked( index );
232  return true;
233  }
234  if ( event->type() == QEvent::MouseMove ) {
235  if ( me->buttons() & Qt::LeftButton ) {
236  m_aboutPressedIndex = index;
237  m_configPressedIndex = QModelIndex();
238  return true;
239  }
240  else {
241  m_aboutPressedIndex = QModelIndex();
242  m_configPressedIndex = QModelIndex();
243  return true;
244  }
245  }
246  }
247  else {
248  // If the mouse is on the item and the mouse isn't above the button.
249  // no about button is pressed.
250  m_aboutPressedIndex = QModelIndex();
251  }
252  topRight -= QPoint( aboutRect.width(), 0 );
253  }
254 
255  // Handle configButton
256  // make sure we have config button
257  if ( index.data( RenderPluginModel::ConfigurationDialogAvailable ).toBool() ) {
258  QRect configRect = buttonOption( option,
259  index,
260  PluginItemDelegate::Configure,
261  topRight.x(),
262  Qt::AlignRight ).rect;
263  if( configRect.contains( mousePosition ) ) {
264  if ( event->type() == QEvent::MouseButtonDblClick )
265  return true;
266 
267  if ( event->type() == QEvent::MouseButtonPress ) {
268  m_aboutPressedIndex = QModelIndex();
269  m_configPressedIndex = index;
270  return true;
271  }
272  if ( event->type() == QEvent::MouseButtonRelease ) {
273  m_aboutPressedIndex = QModelIndex();
274  m_configPressedIndex = QModelIndex();
275  emit configPluginClicked( index );
276  return true;
277  }
278  if ( event->type() == QEvent::MouseMove ) {
279  if ( me->buttons() & Qt::LeftButton ) {
280  m_aboutPressedIndex = QModelIndex();
281  m_configPressedIndex = index;
282  return true;
283  }
284  else {
285  m_aboutPressedIndex = QModelIndex();
286  m_configPressedIndex = QModelIndex();
287  return true;
288  }
289  }
290  }
291  else {
292  // If the mouse is on the item and the mouse isn't above the button.
293  // no config button is pressed.
294  m_configPressedIndex = QModelIndex();
295  }
296 
297  topRight -= QPoint( configRect.width(), 0 );
298  }
299  else {
300  // If we don't have an config dialog shown and the mouse is over this item,
301  // no config button is pressed.
302  m_configPressedIndex = QModelIndex();
303  }
304  }
305 
306  return false;
307 }
308 
309 QStyleOptionButton PluginItemDelegate::checkboxOption( const QStyleOptionViewItem& option,
310  const QModelIndex& index,
311  int position,
312  Qt::AlignmentFlag alignment )
313 {
314  QStyleOptionButton styleOptionButton;
315  if ( index.data( Qt::CheckStateRole ).toBool() )
316  styleOptionButton.state = option.state | QStyle::State_On;
317  else
318  styleOptionButton.state = option.state | QStyle::State_Off;
320  if ( size.isEmpty() ) {
321  // A checkbox has definitely a size != 0
322  styleOptionButton.rect.setSize( QSize( 22, 22 ) );
323  }
324  else {
325  styleOptionButton.rect.setSize( QSize( size.width(), size.height() ) );
326  }
327  styleOptionButton.rect = alignRect( styleOptionButton.rect, option.rect, position, alignment );
328  return styleOptionButton;
329 }
330 
331 QStyleOptionButton PluginItemDelegate::buttonOption( const QStyleOptionViewItem& option,
332  const QModelIndex& index,
333  PluginItemDelegate::ButtonType type,
334  int position,
335  Qt::AlignmentFlag alignment ) const
336 {
337  QStyleOptionButton buttonOption;
338  buttonOption.state = option.state;
339  buttonOption.state &= ~QStyle::State_HasFocus;
340 
341  buttonOption.rect.setTopLeft( QPoint( 0, 0 ) );
342  buttonOption.palette = option.palette;
343  buttonOption.features = QStyleOptionButton::None;
344 
345  QSize contentSize;
346  if ( type == PluginItemDelegate::About ) {
347  if ( m_aboutIcon.isNull() ) {
348  buttonOption.text = tr( "About" );
349  contentSize = buttonOption.fontMetrics.size( 0, buttonOption.text ) + QSize( 4, 4 );
350  }
351  else {
352  buttonOption.icon = m_aboutIcon;
353  buttonOption.iconSize = iconSize;
354  contentSize = iconSize;
355  }
356 
357  if ( m_aboutPressedIndex == index ) {
358  buttonOption.state |= QStyle::State_Sunken;
359  }
360  }
361  else if ( type == PluginItemDelegate::Configure ) {
362  if ( m_configIcon.isNull() ) {
363  buttonOption.text = tr( "Configure" );
364  contentSize = buttonOption.fontMetrics.size( 0, buttonOption.text ) + QSize( 4, 4 );
365  }
366  else {
367  buttonOption.icon = m_configIcon;
368  buttonOption.iconSize = iconSize;
369  contentSize = iconSize;
370  }
371  if ( m_configPressedIndex == index ) {
372  buttonOption.state |= QStyle::State_Sunken;
373  }
374  }
375 
377  &buttonOption,
378  contentSize );
379  buttonOption.rect.setSize( buttonSize );
380  buttonOption.rect = alignRect( buttonOption.rect, option.rect, position, alignment );
381  return buttonOption;
382 }
383 
384 QSize PluginItemDelegate::nameSize( const QModelIndex& index )
385 {
387  // FIXME: QApplication::fontMetrics() doesn't work for non-application fonts
388  QSize nameSize( QApplication::fontMetrics().size( 0, name ) );
389  return nameSize;
390 }
391 
392 QRect PluginItemDelegate::alignRect( const QRect& object,
393  const QRect& frame,
394  int position,
395  Qt::AlignmentFlag alignment )
396 {
397  QRect rect = object;
398 
399  rect.setTopLeft( QPoint( 0, 0 ) );
400  // Moves the object to the middle of the item.
401  if ( rect.height() < frame.height() ) {
402  rect.moveTop( ( frame.height() - rect.height() ) / 2 );
403  }
404 
405  if ( alignment & Qt::AlignLeft ) {
406  rect.moveLeft( position );
407  }
408  else if ( alignment & Qt::AlignRight ) {
409  rect.moveRight( position );
410  }
411 
412  return rect;
413 }
414 
415 
416 #include "moc_PluginItemDelegate.cpp"
QPoint pos() const const
AlignLeft
void moveTopLeft(const QPoint &position)
bool isValid() const const
virtual void drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
DecorationRole
MouseButtonRelease
QPoint topLeft() const const
void moveBottom(int y)
virtual void drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, const QPixmap &pixmap) const const
T value() const const
bool isEmpty() const const
void append(const T &value)
QFontMetrics fontMetrics()
int width() const const
int x() const const
int width() const const
bool contains(const QRect &rectangle, bool proper) const const
LeftButton
int left() const const
void setWidth(int width)
void setRight(int x)
QVariant data(int role) const const
int bottom() const const
typedef ItemFlags
int height() const const
Qt::MouseButtons buttons() const const
void setTopLeft(const QPoint &position)
void moveTop(int y)
int toInt(bool *ok) const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
virtual void drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole) const const
Binds a QML item to a specific geodetic location in screen coordinates.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
bool isValid() const const
void reserve(int size)
bool toBool() const const
void setMouseTracking(bool enable)
virtual QSize sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const const=0
int height() const const
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
QPoint topRight() const const
virtual void drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
const char * name(StandardAction id)
void translate(const QPointF &offset)
void restore()
void save()
CheckState
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
void moveLeft(int x)
void setLeft(int x)
void setHeight(int height)
QStyle * style()
PE_PanelItemViewItem
void moveRight(int x)
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Sep 27 2023 04:09:07 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.