00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <QtDebug>
00021 #include <QApplication>
00022 #include <QKeyEvent>
00023 #include <QLineEdit>
00024 #include <QModelIndex>
00025 #include <QMouseEvent>
00026 #include <QPainter>
00027 #include <QPointer>
00028 #include <QStyleOptionViewItem>
00029 #include "KoDocumentSectionModel.h"
00030 #include "KoDocumentSectionToolTip.h"
00031 #include "KoDocumentSectionView.h"
00032 #include "KoDocumentSectionDelegate.h"
00033
00034 class KoDocumentSectionDelegate::Private
00035 {
00036 public:
00037 KoDocumentSectionView *view;
00038 QPointer<QWidget> edit;
00039 KoDocumentSectionToolTip tip;
00040 static const int margin = 1;
00041 Private(): view( 0 ), edit( 0 ) { }
00042 };
00043
00044 KoDocumentSectionDelegate::KoDocumentSectionDelegate( KoDocumentSectionView *view, QObject *parent )
00045 : super( parent )
00046 , d( new Private )
00047 {
00048 d->view = view;
00049 view->setItemDelegate( this );
00050 QApplication::instance()->installEventFilter( this );
00051 }
00052
00053 KoDocumentSectionDelegate::~KoDocumentSectionDelegate()
00054 {
00055 delete d;
00056 }
00057
00058 QSize KoDocumentSectionDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
00059 {
00060 switch( d->view->displayMode() )
00061 {
00062 case View::ThumbnailMode:
00063 {
00064 const int height = thumbnailHeight( option, index ) + textBoxHeight( option ) + d->margin * 2;
00065 return QSize( availableWidth( index ), height );
00066 }
00067 case View::DetailedMode:
00068 return QSize( option.rect.width(),
00069 textBoxHeight( option ) + option.decorationSize.height() + d->margin );
00070 case View::MinimalMode:
00071 return QSize( option.rect.width(), textBoxHeight( option ) );
00072 default: return option.rect.size();
00073 }
00074 }
00075
00076 void KoDocumentSectionDelegate::paint( QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &index ) const
00077 {
00078 p->save();
00079 {
00080 QStyleOptionViewItem option = getOptions( o, index );
00081
00082 p->setFont( option.font );
00083
00084 if( option.state & QStyle::State_Selected )
00085 p->fillRect( option.rect, option.palette.highlight() );
00086
00087 drawText( p, option, index );
00088 drawIcons( p, option, index );
00089 drawThumbnail( p, option, index );
00090 drawDecoration( p, option, index );
00091 }
00092 p->restore();
00093 }
00094
00095 bool KoDocumentSectionDelegate::editorEvent( QEvent *e, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index )
00096 {
00097 if( e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick )
00098 {
00099 QMouseEvent *me = static_cast<QMouseEvent*>( e );
00100 if( me->button() != Qt::LeftButton )
00101 {
00102 d->view->setCurrentIndex( index );
00103 return false;
00104 }
00105
00106 const QRect ir = iconsRect( option, index ).translated( option.rect.topLeft() ),
00107 tr = textRect( option, index ).translated( option.rect.topLeft() );
00108
00109 if( ir.isValid() && ir.contains( me->pos() ) )
00110 {
00111 const int iconWidth = option.decorationSize.width();
00112 int x = me->pos().x() - ir.left();
00113 if( x % ( iconWidth + d->margin ) < iconWidth )
00114 {
00115 Model::PropertyList lp = index.data( Model::PropertiesRole ).value<Model::PropertyList>();
00116 int p = -1;
00117 for( int i = 0, n = lp.count(); i < n; ++i )
00118 {
00119 if( lp[i].isMutable )
00120 x -= iconWidth + d->margin;
00121 p += 1;
00122 if( x < 0 )
00123 break;
00124 }
00125 lp[p].state = !lp[p].state.toBool();
00126 model->setData( index, QVariant::fromValue( lp ), Model::PropertiesRole );
00127 }
00128 return true;
00129 }
00130
00131 else if( tr.isValid() && tr.contains( me->pos() ) && ( option.state & QStyle::State_HasFocus ) )
00132 {
00133 d->view->edit( index );
00134 return true;
00135 }
00136
00137 if( !( me->modifiers() & Qt::ControlModifier) && !( me->modifiers() & Qt::ShiftModifier ) )
00138 d->view->setCurrentIndex( index );
00139 }
00140
00141 else if( e->type() == QEvent::ToolTip )
00142 {
00143 QHelpEvent *he = static_cast<QHelpEvent*>( e );
00144 d->tip.showTip( d->view, he->pos(), option, index );
00145 return true;
00146 }
00147
00148 else if( e->type() == QEvent::Leave )
00149 {
00150 d->tip.hide();
00151 }
00152
00153 return false;
00154 }
00155
00156 QWidget *KoDocumentSectionDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem&, const QModelIndex& ) const
00157 {
00158 d->edit = new QLineEdit( parent );
00159 d->edit->installEventFilter( const_cast<KoDocumentSectionDelegate*>( this ) );
00160 return d->edit;
00161 }
00162
00163 void KoDocumentSectionDelegate::setEditorData( QWidget *widget, const QModelIndex &index ) const
00164 {
00165 QLineEdit *edit = qobject_cast<QLineEdit*>( widget );
00166 Q_ASSERT( edit );
00167
00168 edit->setText( index.data( Qt::DisplayRole ).toString() );
00169 }
00170
00171 void KoDocumentSectionDelegate::setModelData( QWidget *widget, QAbstractItemModel *model, const QModelIndex &index ) const
00172 {
00173 QLineEdit *edit = qobject_cast<QLineEdit*>( widget );
00174 Q_ASSERT( edit );
00175
00176 model->setData( index, edit->text(), Qt::DisplayRole );
00177 }
00178
00179 void KoDocumentSectionDelegate::updateEditorGeometry( QWidget *widget, const QStyleOptionViewItem &option, const QModelIndex &index ) const
00180 {
00181 widget->setGeometry( textRect( option, index ).translated( option.rect.topLeft() ) );
00182 }
00183
00184
00185
00186
00187
00188 bool KoDocumentSectionDelegate::eventFilter( QObject *object, QEvent *event )
00189 {
00190 if( event->type() == QEvent::MouseButtonPress && d->edit )
00191 {
00192 QMouseEvent *me = static_cast<QMouseEvent*>( event );
00193 if( !QRect( d->edit->mapToGlobal( QPoint() ), d->edit->size() ).contains( me->globalPos() ) )
00194 emit closeEditor( d->edit );
00195 }
00196
00197 QLineEdit *edit = qobject_cast<QLineEdit*>( object );
00198 if( edit && edit == d->edit && event->type() == QEvent::KeyPress )
00199 {
00200 QKeyEvent *ke = static_cast<QKeyEvent*>( event );
00201 switch( ke->key() )
00202 {
00203 case Qt::Key_Escape:
00204 emit closeEditor( edit );
00205 return true;
00206 case Qt::Key_Tab:
00207 emit commitData( edit );
00208 emit closeEditor( edit, EditNextItem );
00209 return true;
00210 case Qt::Key_Backtab:
00211 emit commitData( edit );
00212 emit closeEditor( edit, EditPreviousItem );
00213 return true;
00214 case Qt::Key_Return:
00215 case Qt::Key_Enter:
00216 emit commitData( edit );
00217 emit closeEditor( edit );
00218 return true;
00219 default: break;
00220 }
00221 }
00222
00223 return super::eventFilter( object, event );
00224 }
00225
00226
00227
00228
00229
00230 QStyleOptionViewItem KoDocumentSectionDelegate::getOptions( const QStyleOptionViewItem &o, const QModelIndex &index )
00231 {
00232 QStyleOptionViewItem option = o;
00233 QVariant v = index.data( Qt::FontRole );
00234 if( v.isValid() )
00235 {
00236 option.font = v.value<QFont>();
00237 option.fontMetrics = QFontMetrics( option.font );
00238 }
00239 v = index.data( Qt::TextAlignmentRole );
00240 if( v.isValid() )
00241 option.displayAlignment = QFlag( v.toInt() );
00242 v = index.data( Qt::TextColorRole );
00243 if( v.isValid() )
00244 option.palette.setColor( QPalette::Text, v.value<QColor>() );
00245 v = index.data( Qt::BackgroundColorRole );
00246 if( v.isValid() )
00247 option.palette.setColor( QPalette::Background, v.value<QColor>() );
00248
00249 return option;
00250 }
00251
00252 int KoDocumentSectionDelegate::thumbnailHeight( const QStyleOptionViewItem &option, const QModelIndex &index ) const
00253 {
00254 const QSize size = index.data( Qt::SizeHintRole ).toSize();
00255 int width = option.rect.width();
00256 if( !option.rect.isValid() )
00257 width = availableWidth( index );
00258 if( size.width() <= width )
00259 return size.height();
00260 else
00261 return int( width / ( double( size.width() ) / size.height() ) );
00262 }
00263
00264 int KoDocumentSectionDelegate::availableWidth( const QModelIndex &index ) const
00265
00266 {
00267 int dis = 0;
00268 if( d->view->rootIndex().isValid() )
00269 {
00270 QModelIndex i = index;
00271 while( i.isValid() && i != d->view->rootIndex() )
00272 {
00273 i = i.parent();
00274 dis++;
00275 }
00276 Q_ASSERT( i.isValid() );
00277 }
00278 if( d->view->rootIsDecorated() )
00279 dis++;
00280 const int indent = dis * d->view->indentation();
00281 return d->view->columnWidth( 0 ) - indent;
00282 }
00283
00284 int KoDocumentSectionDelegate::textBoxHeight( const QStyleOptionViewItem &option ) const
00285 {
00286 return qMax( option.fontMetrics.height(), option.decorationSize.height() );
00287 }
00288
00289 QRect KoDocumentSectionDelegate::textRect( const QStyleOptionViewItem &option, const QModelIndex &index ) const
00290 {
00291 if( d->view->displayMode() == View::ThumbnailMode )
00292 {
00293 const QRect r = decorationRect( option, index );
00294 const int left = r.right() + d->margin;
00295 return QRect( left, r.top(), option.rect.width() - left, textBoxHeight( option ) );
00296 }
00297 else
00298 {
00299 static QFont f;
00300 static int minbearing = 1337 + 666;
00301 if( minbearing == 2003 || f != option.font )
00302 {
00303 f = option.font;
00304 minbearing = option.fontMetrics.minLeftBearing() + option.fontMetrics.minRightBearing();
00305 }
00306
00307 int indent = decorationRect( option, index ).right() + d->margin;
00308
00309 const int width = ( d->view->displayMode() == View::DetailedMode
00310 ? option.rect.width()
00311 : iconsRect( option, index ).left() )
00312 - indent - d->margin + minbearing;
00313
00314 return QRect( indent, 0, width, textBoxHeight( option ) );
00315 }
00316 }
00317
00318 QRect KoDocumentSectionDelegate::iconsRect( const QStyleOptionViewItem &option, const QModelIndex &index ) const
00319 {
00320 if( d->view->displayMode() == View::ThumbnailMode )
00321 return QRect();
00322
00323 Model::PropertyList lp = index.data( Model::PropertiesRole ).value<Model::PropertyList>();
00324 int propscount = 0;
00325 for( int i = 0, n = lp.count(); i < n; ++i )
00326 if( lp[i].isMutable )
00327 propscount++;
00328
00329 const int iconswidth = propscount * option.decorationSize.width() + (propscount - 1) * d->margin;
00330
00331 const int x = d->view->displayMode() == View::DetailedMode ? thumbnailRect( option, index ).right() + d->margin : option.rect.width() - iconswidth;
00332 const int y = d->view->displayMode() == View::DetailedMode ? textBoxHeight( option ) + d->margin : 0;
00333
00334 return QRect( x, y, iconswidth, option.decorationSize.height() );
00335 }
00336
00337 QRect KoDocumentSectionDelegate::thumbnailRect( const QStyleOptionViewItem &option, const QModelIndex &index ) const
00338 {
00339 if( d->view->displayMode() == View::ThumbnailMode )
00340 return QRect( 0, 0, option.rect.width(), thumbnailHeight( option, index ) );
00341 else
00342 return QRect( 0, 0, option.rect.height(), option.rect.height() );
00343 }
00344
00345 QRect KoDocumentSectionDelegate::decorationRect( const QStyleOptionViewItem &option, const QModelIndex &index ) const
00346 {
00347 int width = option.decorationSize.width();
00348 if( index.data( Qt::DecorationRole ).value<QIcon>().isNull() )
00349 width = 0;
00350 switch( d->view->displayMode() )
00351 {
00352 case View::ThumbnailMode:
00353 {
00354 QFont font = option.font;
00355 if( index.data( Model::ActiveRole ).toBool() )
00356 font.setBold( !font.bold() );
00357 const QFontMetrics metrics( font );
00358 const int totalwidth = metrics.width( index.data( Qt::DisplayRole ).toString() ) + width + d->margin;
00359 int left;
00360 if( totalwidth < option.rect.width() )
00361 left = ( option.rect.width() - totalwidth ) / 2;
00362 else
00363 left = 0;
00364 return QRect( left, thumbnailRect( option, index ).bottom() + d->margin, width, textBoxHeight( option ) );
00365 }
00366 case View::DetailedMode:
00367 case View::MinimalMode:
00368 {
00369 const int left = thumbnailRect( option, index ).right() + d->margin;
00370 return QRect( left, 0, width, textBoxHeight( option ) );
00371 }
00372 default: return QRect();
00373 }
00374 }
00375
00376 void KoDocumentSectionDelegate::drawText( QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index ) const
00377 {
00378 const QRect r = textRect( option, index ).translated( option.rect.topLeft() );
00379
00380 p->save();
00381 {
00382 p->setClipRect( r );
00383 p->translate( r.left(), r.top() );
00384 p->setPen( ( option.state & QStyle::State_Selected )
00385 ? option.palette.highlightedText().color()
00386 : option.palette.text().color() );
00387
00388 if( index.data( Model::ActiveRole ).toBool() )
00389 {
00390 QFont f = p->font();
00391 f.setBold( !f.bold() );
00392 p->setFont( f );
00393 }
00394
00395 const QString text = index.data( Qt::DisplayRole ).toString();
00396 const QString elided = elidedText( p->fontMetrics(), r.width(), Qt::ElideRight, text );
00397 p->drawText( d->margin, 0, r.width(), r.height(), Qt::AlignLeft | Qt::AlignTop, elided );
00398 }
00399 p->restore();
00400 }
00401
00402 void KoDocumentSectionDelegate::drawIcons( QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index ) const
00403 {
00404 const QRect r = iconsRect( option, index ).translated( option.rect.topLeft() );
00405
00406 p->save();
00407 {
00408 p->setClipRect( r );
00409 p->translate( r.left(), r.top() );
00410 int x = 0;
00411 Model::PropertyList lp = index.data( Model::PropertiesRole ).value<Model::PropertyList>();
00412 for( int i = 0, n = lp.count(); i < n; ++i )
00413 if( lp[i].isMutable )
00414 {
00415 QIcon icon = lp[i].state.toBool() ? lp[i].onIcon : lp[i].offIcon;
00416 p->drawPixmap( x, 0, icon.pixmap( option.decorationSize ) );
00417 x += option.decorationSize.width() + d->margin;
00418 }
00419 }
00420 p->restore();
00421 }
00422
00423 void KoDocumentSectionDelegate::drawThumbnail( QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index ) const
00424 {
00425 const QRect r = thumbnailRect( option, index ).translated( option.rect.topLeft() );
00426
00427 p->save();
00428 {
00429 p->setClipRect( r );
00430 const double myratio = double( r.width() ) / r.height();
00431 const double thumbratio = index.data( Model::AspectRatioRole ).toDouble();
00432 const int s = ( myratio > thumbratio ) ? r.height() : r.width();
00433
00434 const QImage i = index.data( int( Model::BeginThumbnailRole ) + s ).value<QImage>();
00435 QPoint offset;
00436 offset.setX( r.width()/2 - i.width()/2 );
00437 offset.setY( r.height()/2 - i.height()/2 );
00438
00439 p->drawImage( r.topLeft() + offset, i );
00440 }
00441 p->restore();
00442 }
00443
00444 void KoDocumentSectionDelegate::drawDecoration( QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index ) const
00445 {
00446 const QRect r = decorationRect( option, index ).translated( option.rect.topLeft() );
00447
00448 p->save();
00449 {
00450 p->setClipRect( r );
00451 p->translate( r.topLeft() );
00452 if( !index.data( Qt::DecorationRole ).value<QIcon>().isNull() )
00453 p->drawPixmap( 0, 0, index.data( Qt::DecorationRole ).value<QIcon>().pixmap( option.decorationSize ) );
00454 }
00455 p->restore();
00456 }
00457
00458
00459
00460 #include "KoDocumentSectionDelegate.moc"