Quantum GIS API Documentation
1.7.4
|
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 }