Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgsdetaileditemdelegate.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdetailedlistwidget.cpp - A rich QItemDelegate subclass
3  -------------------
4  begin : Sat May 17 2008
5  copyright : (C) 2008 Tim Sutton
6  email : tim@linfiniti.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 /* $Id:$ */
18 
20 #include "qgsdetaileditemwidget.h"
21 #include "qgsdetaileditemdata.h"
22 #include <QPainter>
23 #include <QFont>
24 #include <QFontMetrics>
25 #include <QStyleOptionViewItem>
26 #include <QModelIndex>
27 #include <QCheckBox>
28 #include <QLinearGradient>
30  QAbstractItemDelegate( parent ),
31  mpWidget( new QgsDetailedItemWidget() ),
32  mpCheckBox( new QCheckBox() )
33 
34 {
35  //mpWidget->setFixedHeight(80);
36  mpCheckBox->resize( mpCheckBox->sizeHint().height(), mpCheckBox->sizeHint().height() );
37  setVerticalSpacing( 3 );
39 }
40 
42 {
43  delete mpCheckBox;
44  delete mpWidget;
45 }
46 
47 void QgsDetailedItemDelegate::paint( QPainter * thepPainter,
48  const QStyleOptionViewItem & theOption,
49  const QModelIndex & theIndex ) const
50 {
51  // After painting we need to restore the painter to its original state
52  thepPainter->save();
53  if ( qVariantCanConvert<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) ) )
54  {
55  QgsDetailedItemData myData =
56  qVariantValue<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) );
57  if ( myData.isRenderedAsWidget() )
58  {
59  paintAsWidget( thepPainter, theOption, myData );
60  }
61  else //render by manually painting
62  {
63  paintManually( thepPainter, theOption, myData );
64  }
65  } //can convert item data
66  thepPainter->restore();
67 }
68 
69 
70 
72  const QStyleOptionViewItem & theOption,
73  const QModelIndex & theIndex ) const
74 {
75  if ( qVariantCanConvert<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) ) )
76  {
77  QgsDetailedItemData myData =
78  qVariantValue<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) );
79  if ( myData.isRenderedAsWidget() )
80  {
81  return QSize( 378, mpWidget->height() );
82  }
83  else // fall back to hand calculated & hand drawn item
84  {
85  //for some reason itmes are non selectable if using rect.width() on osx and win
86  return QSize( 50, height( theOption, myData ) );
87  //return QSize(theOption.rect.width(), myHeight + myVerticalSpacer);
88  }
89  }
90  else //cant convert to qgsdetaileditemdata
91  {
92  return QSize( 50, 50 ); //fallback
93  }
94 }
95 
96 void QgsDetailedItemDelegate::paintManually( QPainter * thepPainter,
97  const QStyleOptionViewItem & theOption,
98  const QgsDetailedItemData theData ) const
99 {
100  //
101  // Get the strings and check box properties
102  //
103  //bool myCheckState = theIndex.model()->data(theIndex, Qt::CheckStateRole).toBool();
104  mpCheckBox->setChecked( theData.isChecked() );
105  mpCheckBox->setEnabled( theData.isEnabled() );
106  QPixmap myCbxPixmap( mpCheckBox->size() );
107  mpCheckBox->render( &myCbxPixmap ); //we will draw this onto the widget further down
108 
109  //
110  // Calculate the widget height and other metrics
111  //
112 
113  QFontMetrics myTitleMetrics( titleFont( theOption ) );
114  QFontMetrics myDetailMetrics( detailFont( theOption ) );
115  int myTextStartX = theOption.rect.x() + horizontalSpacing();
116  int myTextStartY = theOption.rect.y() + verticalSpacing();
117  int myHeight = myTitleMetrics.height() + verticalSpacing();
118 
119  //
120  // Draw the item background with a gradient if its highlighted
121  //
122  if ( theOption.state & QStyle::State_Selected )
123  {
124  drawHighlight( theOption, thepPainter, height( theOption, theData ) );
125  thepPainter->setPen( theOption.palette.highlightedText().color() );
126  }
127  else
128  {
129  thepPainter->setPen( theOption.palette.text().color() );
130  }
131 
132 
133  //
134  // Draw the checkbox
135  //
136  if ( theData.isCheckable() )
137  {
138  thepPainter->drawPixmap( theOption.rect.x(),
139  theOption.rect.y() + mpCheckBox->height(),
140  myCbxPixmap );
141  myTextStartX = theOption.rect.x() + myCbxPixmap.width() + horizontalSpacing();
142  }
143  //
144  // Draw the decoration (pixmap)
145  //
146  bool myIconFlag = false;
147  QPixmap myDecoPixmap = theData.icon();
148  if ( !myDecoPixmap.isNull() )
149  {
150  int iconWidth = 32, iconHeight = 32;
151 
152  if ( myDecoPixmap.width() <= iconWidth && myDecoPixmap.height() <= iconHeight )
153  {
154  // the pixmap has reasonable size
155  int offsetX = 0, offsetY = 0;
156  if ( myDecoPixmap.width() < iconWidth )
157  offsetX = ( iconWidth - myDecoPixmap.width() ) / 2;
158  if ( myDecoPixmap.height() < iconHeight )
159  offsetY = ( iconHeight - myDecoPixmap.height() ) / 2;
160 
161  thepPainter->drawPixmap( myTextStartX + offsetX,
162  myTextStartY + offsetY,
163  myDecoPixmap );
164  }
165  else
166  {
167  // shrink the pixmap, it's too big
168  thepPainter->drawPixmap( myTextStartX, myTextStartY, iconWidth, iconHeight, myDecoPixmap );
169  }
170 
171  myTextStartX += iconWidth + horizontalSpacing();
172  }
173  //
174  // Draw the title
175  //
176  myTextStartY += myHeight / 2;
177  thepPainter->setFont( titleFont( theOption ) );
178  thepPainter->drawText( myTextStartX,
179  myTextStartY,
180  theData.title() );
181  //
182  // Draw the description with word wrapping if needed
183  //
184  thepPainter->setFont( detailFont( theOption ) ); //return to original font set by client
185  if ( myIconFlag )
186  {
187  myTextStartY += verticalSpacing();
188  }
189  else
190  {
191  myTextStartY += myDetailMetrics.height() + verticalSpacing();
192  }
193  QStringList myList =
194  wordWrap( theData.detail(), myDetailMetrics, theOption.rect.width() - myTextStartX );
195  QStringListIterator myLineWrapIterator( myList );
196  while ( myLineWrapIterator.hasNext() )
197  {
198  QString myLine = myLineWrapIterator.next();
199  thepPainter->drawText( myTextStartX,
200  myTextStartY,
201  myLine );
202  myTextStartY += myDetailMetrics.height() - verticalSpacing();
203  }
204 } //render by manual painting
205 
206 
207 void QgsDetailedItemDelegate::paintAsWidget( QPainter * thepPainter,
208  const QStyleOptionViewItem & theOption,
209  const QgsDetailedItemData theData ) const
210 {
211 
212  mpWidget->setChecked( theData.isChecked() );
213  mpWidget->setData( theData );
214  mpWidget->resize( theOption.rect.width(), mpWidget->height() );
215  mpWidget->setAutoFillBackground( true );
216  //mpWidget->setAttribute(Qt::WA_OpaquePaintEvent);
217  mpWidget->repaint();
218  if ( theOption.state & QStyle::State_Selected )
219  {
220  drawHighlight( theOption, thepPainter, height( theOption, theData ) );
221  }
222  QPixmap myPixmap = QPixmap::grabWidget( mpWidget );
223  thepPainter->drawPixmap( theOption.rect.x(),
224  theOption.rect.y(),
225  myPixmap );
226 }//render as widget
227 
228 void QgsDetailedItemDelegate::drawHighlight( const QStyleOptionViewItem &theOption,
229  QPainter * thepPainter,
230  int theHeight ) const
231 {
232  QColor myColor1 = theOption.palette.highlight().color();
233  QColor myColor2 = myColor1;
234  myColor2 = myColor2.lighter( 110 ); //10% lighter
235  QLinearGradient myGradient( QPointF( 0, theOption.rect.y() ),
236  QPointF( 0, theOption.rect.y() + theHeight ) );
237  myGradient.setColorAt( 0, myColor1 );
238  myGradient.setColorAt( 0.1, myColor2 );
239  myGradient.setColorAt( 0.5, myColor1 );
240  myGradient.setColorAt( 0.9, myColor2 );
241  myGradient.setColorAt( 1, myColor2 );
242  thepPainter->fillRect( theOption.rect, QBrush( myGradient ) );
243 }
244 
245 int QgsDetailedItemDelegate::height( const QStyleOptionViewItem & theOption,
246  const QgsDetailedItemData theData ) const
247 {
248  QFontMetrics myTitleMetrics( titleFont( theOption ) );
249  QFontMetrics myDetailMetrics( detailFont( theOption ) );
250  //we don't word wrap the title so its easy to measure
251  int myHeight = myTitleMetrics.height() + verticalSpacing();
252  //the detail needs to be measured though
253  QStringList myList = wordWrap( theData.detail(),
254  myDetailMetrics,
255  theOption.rect.width() - ( mpCheckBox->width() + horizontalSpacing() ) );
256  myHeight += ( myList.count() + 1 ) * ( myDetailMetrics.height() - verticalSpacing() );
257  return myHeight;
258 }
259 
260 
261 QFont QgsDetailedItemDelegate::detailFont( const QStyleOptionViewItem &theOption ) const
262 {
263  QFont myFont = theOption.font;
264  return myFont;
265 }
266 
267 QFont QgsDetailedItemDelegate::titleFont( const QStyleOptionViewItem &theOption ) const
268 {
269  QFont myTitleFont = detailFont( theOption );
270  myTitleFont.setBold( true );
271  myTitleFont.setPointSize( myTitleFont.pointSize() );
272  return myTitleFont;
273 }
274 
275 
276 QStringList QgsDetailedItemDelegate::wordWrap( QString theString,
277  QFontMetrics theMetrics,
278  int theWidth ) const
279 {
280  if ( theString.isEmpty() ) return QStringList();
281  if ( 50 >= theWidth ) return QStringList() << theString;
282  //QString myDebug = QString("Word wrapping: %1 into %2 pixels").arg(theString).arg(theWidth);
283  //qDebug(myDebug.toLocal8Bit());
284  //iterate the string
285  QStringList myList;
286  QString myCumulativeLine = "";
287  QString myStringToPreviousSpace = "";
288  int myPreviousSpacePos = 0;
289  for ( int i = 0; i < theString.count(); ++i )
290  {
291  QChar myChar = theString.at( i );
292  if ( myChar == QChar( ' ' ) )
293  {
294  myStringToPreviousSpace = myCumulativeLine;
295  myPreviousSpacePos = i;
296  }
297  myCumulativeLine += myChar;
298  if ( theMetrics.width( myCumulativeLine ) >= theWidth )
299  {
300  //time to wrap
301  //@todo deal with long strings that have no spaces
302  //forcing a break at current pos...
303  myList << myStringToPreviousSpace.trimmed();
304  i = myPreviousSpacePos;
305  myStringToPreviousSpace = "";
306  myCumulativeLine = "";
307  }
308  }//end of i loop
309  //add whatever is left in the string to the list
310  if ( !myCumulativeLine.trimmed().isEmpty() )
311  {
312  myList << myCumulativeLine.trimmed();
313  }
314 
315  //qDebug("Wrapped legend entry:");
316  //qDebug(theString);
317  //qDebug(myList.join("\n").toLocal8Bit());
318  return myList;
319 
320 }
321 
322 
323 
325 {
326  return mVerticalSpacing;
327 }
328 
329 
331 {
332  mVerticalSpacing = theValue;
333 }
334 
335 
337 {
338  return mHorizontalSpacing;
339 }
340 
341 
343 {
344  mHorizontalSpacing = theValue;
345 }