Quantum GIS API Documentation  1.7.4
src/gui/qgsdetaileditemdelegate.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002      qgsdetailedlistwidget.cpp  -  A rich QItemDelegate subclass
00003                              -------------------
00004     begin                : Sat May 17 2008
00005     copyright            : (C) 2008 Tim Sutton
00006     email                : tim@linfiniti.com
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 /* $Id:$ */
00018 
00019 #include "qgsdetaileditemdelegate.h"
00020 #include "qgsdetaileditemwidget.h"
00021 #include "qgsdetaileditemdata.h"
00022 #include <QPainter>
00023 #include <QFont>
00024 #include <QFontMetrics>
00025 #include <QStyleOptionViewItem>
00026 #include <QModelIndex>
00027 #include <QCheckBox>
00028 #include <QLinearGradient>
00029 QgsDetailedItemDelegate::QgsDetailedItemDelegate( QObject * parent ) :
00030     QAbstractItemDelegate( parent ),
00031     mpWidget( new QgsDetailedItemWidget() ),
00032     mpCheckBox( new QCheckBox() )
00033 
00034 {
00035   //mpWidget->setFixedHeight(80);
00036   mpCheckBox->resize( mpCheckBox->sizeHint().height(), mpCheckBox->sizeHint().height() );
00037   setVerticalSpacing( 3 );
00038   setHorizontalSpacing( 5 );
00039 }
00040 
00041 QgsDetailedItemDelegate::~QgsDetailedItemDelegate()
00042 {
00043   delete mpCheckBox;
00044   delete mpWidget;
00045 }
00046 
00047 void QgsDetailedItemDelegate::paint( QPainter * thepPainter,
00048                                      const QStyleOptionViewItem & theOption,
00049                                      const QModelIndex & theIndex ) const
00050 {
00051   // After painting we need to restore the painter to its original state
00052   thepPainter->save();
00053   if ( qVariantCanConvert<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) ) )
00054   {
00055     QgsDetailedItemData myData =
00056       qVariantValue<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) );
00057     if ( myData.isRenderedAsWidget() )
00058     {
00059       paintAsWidget( thepPainter, theOption, myData );
00060     }
00061     else //render by manually painting
00062     {
00063       paintManually( thepPainter, theOption, myData );
00064     }
00065   } //can convert item data
00066   thepPainter->restore();
00067 }
00068 
00069 
00070 
00071 QSize QgsDetailedItemDelegate::sizeHint(
00072   const QStyleOptionViewItem & theOption,
00073   const QModelIndex & theIndex ) const
00074 {
00075   if ( qVariantCanConvert<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) ) )
00076   {
00077     QgsDetailedItemData myData =
00078       qVariantValue<QgsDetailedItemData>( theIndex.data( Qt::UserRole ) );
00079     if ( myData.isRenderedAsWidget() )
00080     {
00081       return QSize( 378, mpWidget->height() );
00082     }
00083     else // fall back to hand calculated & hand drawn item
00084     {
00085       //for some reason itmes are non selectable if using rect.width() on osx and win
00086       return QSize( 50, height( theOption, myData ) );
00087       //return QSize(theOption.rect.width(), myHeight + myVerticalSpacer);
00088     }
00089   }
00090   else //cant convert to qgsdetaileditemdata
00091   {
00092     return QSize( 50, 50 ); //fallback
00093   }
00094 }
00095 
00096 void QgsDetailedItemDelegate::paintManually( QPainter * thepPainter,
00097     const QStyleOptionViewItem & theOption,
00098     const QgsDetailedItemData theData ) const
00099 {
00100   //
00101   // Get the strings and check box properties
00102   //
00103   //bool myCheckState = theIndex.model()->data(theIndex, Qt::CheckStateRole).toBool();
00104   mpCheckBox->setChecked( theData.isChecked() );
00105   mpCheckBox->setEnabled( theData.isEnabled() );
00106   QPixmap myCbxPixmap( mpCheckBox->size() );
00107   mpCheckBox->render( &myCbxPixmap ); //we will draw this onto the widget further down
00108 
00109   //
00110   // Calculate the widget height and other metrics
00111   //
00112 
00113   QFontMetrics myTitleMetrics( titleFont( theOption ) );
00114   QFontMetrics myDetailMetrics( detailFont( theOption ) );
00115   int myTextStartX = theOption.rect.x() + horizontalSpacing();
00116   int myTextStartY = theOption.rect.y() + verticalSpacing();
00117   int myHeight = myTitleMetrics.height() + verticalSpacing();
00118 
00119   //
00120   // Draw the item background with a gradient if its highlighted
00121   //
00122   if ( theOption.state & QStyle::State_Selected )
00123   {
00124     drawHighlight( theOption, thepPainter, height( theOption, theData ) );
00125     thepPainter->setPen( theOption.palette.highlightedText().color() );
00126   }
00127   else
00128   {
00129     thepPainter->setPen( theOption.palette.text().color() );
00130   }
00131 
00132 
00133   //
00134   // Draw the checkbox
00135   //
00136   if ( theData.isCheckable() )
00137   {
00138     thepPainter->drawPixmap( theOption.rect.x(),
00139                              theOption.rect.y() + mpCheckBox->height(),
00140                              myCbxPixmap );
00141     myTextStartX = theOption.rect.x() + myCbxPixmap.width() + horizontalSpacing();
00142   }
00143   //
00144   // Draw the decoration (pixmap)
00145   //
00146   bool myIconFlag = false;
00147   QPixmap myDecoPixmap = theData.icon();
00148   if ( !myDecoPixmap.isNull() )
00149   {
00150     int iconWidth = 32, iconHeight = 32;
00151 
00152     if ( myDecoPixmap.width() <= iconWidth && myDecoPixmap.height() <= iconHeight )
00153     {
00154       // the pixmap has reasonable size
00155       int offsetX = 0, offsetY = 0;
00156       if ( myDecoPixmap.width() < iconWidth )
00157         offsetX = ( iconWidth - myDecoPixmap.width() ) / 2;
00158       if ( myDecoPixmap.height() < iconHeight )
00159         offsetY = ( iconHeight - myDecoPixmap.height() ) / 2;
00160 
00161       thepPainter->drawPixmap( myTextStartX + offsetX,
00162                                myTextStartY + offsetY,
00163                                myDecoPixmap );
00164     }
00165     else
00166     {
00167       // shrink the pixmap, it's too big
00168       thepPainter->drawPixmap( myTextStartX, myTextStartY, iconWidth, iconHeight, myDecoPixmap );
00169     }
00170 
00171     myTextStartX += iconWidth + horizontalSpacing();
00172   }
00173   //
00174   // Draw the title
00175   //
00176   myTextStartY += myHeight / 2;
00177   thepPainter->setFont( titleFont( theOption ) );
00178   thepPainter->drawText( myTextStartX,
00179                          myTextStartY,
00180                          theData.title() );
00181   //
00182   // Draw the description with word wrapping if needed
00183   //
00184   thepPainter->setFont( detailFont( theOption ) ); //return to original font set by client
00185   if ( myIconFlag )
00186   {
00187     myTextStartY += verticalSpacing();
00188   }
00189   else
00190   {
00191     myTextStartY +=  myDetailMetrics.height() + verticalSpacing();
00192   }
00193   QStringList myList =
00194     wordWrap( theData.detail(), myDetailMetrics, theOption.rect.width() - myTextStartX );
00195   QStringListIterator myLineWrapIterator( myList );
00196   while ( myLineWrapIterator.hasNext() )
00197   {
00198     QString myLine = myLineWrapIterator.next();
00199     thepPainter->drawText( myTextStartX,
00200                            myTextStartY,
00201                            myLine );
00202     myTextStartY += myDetailMetrics.height() - verticalSpacing();
00203   }
00204 } //render by manual painting
00205 
00206 
00207 void QgsDetailedItemDelegate::paintAsWidget( QPainter * thepPainter,
00208     const QStyleOptionViewItem & theOption,
00209     const QgsDetailedItemData theData ) const
00210 {
00211 
00212   mpWidget->setChecked( theData.isChecked() );
00213   mpWidget->setData( theData );
00214   mpWidget->resize( theOption.rect.width(), mpWidget->height() );
00215   mpWidget->setAutoFillBackground( true );
00216   //mpWidget->setAttribute(Qt::WA_OpaquePaintEvent);
00217   mpWidget->repaint();
00218   if ( theOption.state & QStyle::State_Selected )
00219   {
00220     drawHighlight( theOption, thepPainter, height( theOption, theData ) );
00221   }
00222   QPixmap myPixmap = QPixmap::grabWidget( mpWidget );
00223   thepPainter->drawPixmap( theOption.rect.x(),
00224                            theOption.rect.y(),
00225                            myPixmap );
00226 }//render as widget
00227 
00228 void QgsDetailedItemDelegate::drawHighlight( const QStyleOptionViewItem &theOption,
00229     QPainter * thepPainter,
00230     int theHeight ) const
00231 {
00232   QColor myColor1 = theOption.palette.highlight().color();
00233   QColor myColor2 = myColor1;
00234   myColor2 = myColor2.lighter( 110 ); //10% lighter
00235   QLinearGradient myGradient( QPointF( 0, theOption.rect.y() ),
00236                               QPointF( 0, theOption.rect.y() + theHeight ) );
00237   myGradient.setColorAt( 0, myColor1 );
00238   myGradient.setColorAt( 0.1, myColor2 );
00239   myGradient.setColorAt( 0.5, myColor1 );
00240   myGradient.setColorAt( 0.9, myColor2 );
00241   myGradient.setColorAt( 1, myColor2 );
00242   thepPainter->fillRect( theOption.rect, QBrush( myGradient ) );
00243 }
00244 
00245 int QgsDetailedItemDelegate::height( const QStyleOptionViewItem & theOption,
00246                                      const QgsDetailedItemData theData ) const
00247 {
00248   QFontMetrics myTitleMetrics( titleFont( theOption ) );
00249   QFontMetrics myDetailMetrics( detailFont( theOption ) );
00250   //we don't word wrap the title so its easy to measure
00251   int myHeight = myTitleMetrics.height() + verticalSpacing();
00252   //the detail needs to be measured though
00253   QStringList myList = wordWrap( theData.detail(),
00254                                  myDetailMetrics,
00255                                  theOption.rect.width() - ( mpCheckBox->width() + horizontalSpacing() ) );
00256   myHeight += ( myList.count() + 1 ) * ( myDetailMetrics.height() - verticalSpacing() );
00257   return myHeight;
00258 }
00259 
00260 
00261 QFont QgsDetailedItemDelegate::detailFont( const QStyleOptionViewItem &theOption ) const
00262 {
00263   QFont myFont = theOption.font;
00264   return myFont;
00265 }
00266 
00267 QFont QgsDetailedItemDelegate::titleFont( const QStyleOptionViewItem &theOption ) const
00268 {
00269   QFont myTitleFont = detailFont( theOption );
00270   myTitleFont.setBold( true );
00271   myTitleFont.setPointSize( myTitleFont.pointSize() );
00272   return myTitleFont;
00273 }
00274 
00275 
00276 QStringList QgsDetailedItemDelegate::wordWrap( QString theString,
00277     QFontMetrics theMetrics,
00278     int theWidth ) const
00279 {
00280   if ( theString.isEmpty() ) return QStringList();
00281   if ( 50 >= theWidth ) return QStringList() << theString;
00282   //QString myDebug = QString("Word wrapping: %1 into %2 pixels").arg(theString).arg(theWidth);
00283   //qDebug(myDebug.toLocal8Bit());
00284   //iterate the string
00285   QStringList myList;
00286   QString myCumulativeLine = "";
00287   QString myStringToPreviousSpace = "";
00288   int myPreviousSpacePos = 0;
00289   for ( int i = 0; i < theString.count(); ++i )
00290   {
00291     QChar myChar = theString.at( i );
00292     if ( myChar == QChar( ' ' ) )
00293     {
00294       myStringToPreviousSpace = myCumulativeLine;
00295       myPreviousSpacePos = i;
00296     }
00297     myCumulativeLine += myChar;
00298     if ( theMetrics.width( myCumulativeLine ) >= theWidth )
00299     {
00300       //time to wrap
00301       //@todo deal with long strings that have no spaces
00302       //forcing a break at current pos...
00303       myList << myStringToPreviousSpace.trimmed();
00304       i = myPreviousSpacePos;
00305       myStringToPreviousSpace = "";
00306       myCumulativeLine = "";
00307     }
00308   }//end of i loop
00309   //add whatever is left in the string to the list
00310   if ( !myCumulativeLine.trimmed().isEmpty() )
00311   {
00312     myList << myCumulativeLine.trimmed();
00313   }
00314 
00315   //qDebug("Wrapped legend entry:");
00316   //qDebug(theString);
00317   //qDebug(myList.join("\n").toLocal8Bit());
00318   return myList;
00319 
00320 }
00321 
00322 
00323 
00324 int QgsDetailedItemDelegate::verticalSpacing() const
00325 {
00326   return mVerticalSpacing;
00327 }
00328 
00329 
00330 void QgsDetailedItemDelegate::setVerticalSpacing( int theValue )
00331 {
00332   mVerticalSpacing = theValue;
00333 }
00334 
00335 
00336 int QgsDetailedItemDelegate::horizontalSpacing() const
00337 {
00338   return mHorizontalSpacing;
00339 }
00340 
00341 
00342 void QgsDetailedItemDelegate::setHorizontalSpacing( int theValue )
00343 {
00344   mHorizontalSpacing = theValue;
00345 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines