compile
[kdegraphics.git] / okular / ui / sidebar.cpp
blob86c84590ae44c4db1631ebc8c0f60a6cae4be4ac
1 /***************************************************************************
2 * Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 ***************************************************************************/
10 #include "sidebar.h"
12 #include <qabstractitemdelegate.h>
13 #include <qaction.h>
14 #include <qapplication.h>
15 #include <qevent.h>
16 #include <qfont.h>
17 #include <qfontmetrics.h>
18 #include <qlabel.h>
19 #include <qlayout.h>
20 #include <qlist.h>
21 #include <qlistwidget.h>
22 #include <qpainter.h>
23 #include <qscrollbar.h>
24 #include <qsplitter.h>
25 #include <qstackedwidget.h>
27 #include <kiconloader.h>
28 #include <klocale.h>
29 #include <kmenu.h>
31 #include "settings.h"
33 static const int SidebarItemType = QListWidgetItem::UserType + 1;
35 /* List item representing a sidebar entry. */
36 class SidebarItem : public QListWidgetItem
38 public:
39 SidebarItem( QWidget* w, const QIcon &icon, const QString &text )
40 : QListWidgetItem( 0, SidebarItemType ),
41 m_widget( w )
43 setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
44 setIcon( icon );
45 setText( text );
46 setToolTip( text );
49 QWidget* widget() const
51 return m_widget;
54 private:
55 QWidget *m_widget;
59 /* A simple delegate to paint the icon of each item */
60 #define ITEM_MARGIN_LEFT 5
61 #define ITEM_MARGIN_TOP 5
62 #define ITEM_MARGIN_RIGHT 5
63 #define ITEM_MARGIN_BOTTOM 5
64 #define ITEM_PADDING 5
66 class SidebarDelegate : public QAbstractItemDelegate
68 public:
69 SidebarDelegate( QObject *parent = 0 );
70 ~SidebarDelegate();
72 void setShowText( bool show );
73 bool isTextShown() const;
75 // from QAbstractItemDelegate
76 void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
77 QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const;
79 private:
80 bool m_showText;
83 SidebarDelegate::SidebarDelegate( QObject *parent )
84 : QAbstractItemDelegate( parent ), m_showText( true )
88 SidebarDelegate::~SidebarDelegate()
92 void SidebarDelegate::setShowText( bool show )
94 m_showText = show;
97 bool SidebarDelegate::isTextShown() const
99 return m_showText;
102 void SidebarDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
104 QBrush backBrush;
105 QColor foreColor;
106 bool disabled = false;
107 bool hover = false;
108 if ( !( option.state & QStyle::State_Enabled ) )
110 backBrush = option.palette.brush( QPalette::Disabled, QPalette::Base );
111 foreColor = option.palette.color( QPalette::Disabled, QPalette::Text );
112 disabled = true;
114 else if ( option.state & ( QStyle::State_HasFocus | QStyle::State_Selected ) )
116 backBrush = option.palette.brush( QPalette::Highlight );
117 foreColor = option.palette.color( QPalette::HighlightedText );
119 else if ( option.state & QStyle::State_MouseOver )
121 backBrush = option.palette.color( QPalette::Highlight ).light( 115 );
122 foreColor = option.palette.color( QPalette::HighlightedText );
123 hover = true;
125 else /*if ( option.state & QStyle::State_Enabled )*/
127 backBrush = option.palette.brush( QPalette::Base );
128 foreColor = option.palette.color( QPalette::Text );
130 QStyle *style = QApplication::style();
131 QStyleOptionViewItemV4 opt( option );
132 // KStyle provides an "hover highlight" effect for free;
133 // but we want that for non-KStyle-based styles too
134 if ( !style->inherits( "KStyle" ) && hover )
136 Qt::BrushStyle bs = opt.backgroundBrush.style();
137 if ( bs > Qt::NoBrush && bs < Qt::TexturePattern )
138 opt.backgroundBrush = opt.backgroundBrush.color().light( 115 );
139 else
140 opt.backgroundBrush = backBrush;
142 painter->save();
143 style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, 0 );
144 painter->restore();
145 QIcon icon = index.data( Qt::DecorationRole ).value< QIcon >();
146 if ( !icon.isNull() )
148 QPoint iconpos(
149 ( option.rect.width() - option.decorationSize.width() ) / 2,
150 ITEM_MARGIN_TOP
152 iconpos += option.rect.topLeft();
153 QIcon::Mode iconmode = disabled ? QIcon::Disabled : QIcon::Normal;
154 painter->drawPixmap( iconpos, icon.pixmap( option.decorationSize, iconmode ) );
157 if ( m_showText )
159 QString text = index.data( Qt::DisplayRole ).toString();
160 QRect fontBoundaries = QFontMetrics( option.font ).boundingRect( text );
161 QPoint textPos(
162 ITEM_MARGIN_LEFT + ( option.rect.width() - ITEM_MARGIN_LEFT - ITEM_MARGIN_RIGHT - fontBoundaries.width() ) / 2,
163 ITEM_MARGIN_TOP + option.decorationSize.height() + ITEM_PADDING
165 fontBoundaries.translate( -fontBoundaries.topLeft() );
166 fontBoundaries.translate( textPos );
167 fontBoundaries.translate( option.rect.topLeft() );
168 painter->setPen( foreColor );
169 painter->drawText( fontBoundaries, Qt::AlignCenter, text );
173 QSize SidebarDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
175 QSize baseSize( option.decorationSize.width(), option.decorationSize.height() );
176 if ( m_showText )
178 QRect fontBoundaries = QFontMetrics( option.font ).boundingRect( index.data( Qt::DisplayRole ).toString() );
179 baseSize.setWidth( qMax( fontBoundaries.width(), baseSize.width() ) );
180 baseSize.setHeight( baseSize.height() + fontBoundaries.height() + ITEM_PADDING );
182 return baseSize + QSize( ITEM_MARGIN_LEFT + ITEM_MARGIN_RIGHT, ITEM_MARGIN_TOP + ITEM_MARGIN_BOTTOM );
186 /* A custom list widget that ignores the events for disabled items */
187 class SidebarListWidget : public QListWidget
189 public:
190 SidebarListWidget( QWidget *parent = 0 );
191 ~SidebarListWidget();
193 protected:
194 // from QListWidget
195 void mouseDoubleClickEvent( QMouseEvent *event );
196 void mouseMoveEvent( QMouseEvent *event );
197 void mousePressEvent( QMouseEvent *event );
198 void mouseReleaseEvent( QMouseEvent *event );
200 QModelIndex moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers );
203 SidebarListWidget::SidebarListWidget( QWidget *parent )
204 : QListWidget( parent )
208 SidebarListWidget::~SidebarListWidget()
212 void SidebarListWidget::mouseDoubleClickEvent( QMouseEvent *event )
214 QModelIndex index = indexAt( event->pos() );
215 if ( index.isValid() && !( index.flags() & Qt::ItemIsSelectable ) )
216 return;
218 QListWidget::mouseDoubleClickEvent( event );
221 void SidebarListWidget::mouseMoveEvent( QMouseEvent *event )
223 QModelIndex index = indexAt( event->pos() );
224 if ( index.isValid() && !( index.flags() & Qt::ItemIsSelectable ) )
225 return;
227 QListWidget::mouseMoveEvent( event );
230 void SidebarListWidget::mousePressEvent( QMouseEvent *event )
232 QModelIndex index = indexAt( event->pos() );
233 if ( index.isValid() && !( index.flags() & Qt::ItemIsSelectable ) )
234 return;
236 QListWidget::mousePressEvent( event );
239 void SidebarListWidget::mouseReleaseEvent( QMouseEvent *event )
241 QModelIndex index = indexAt( event->pos() );
242 if ( index.isValid() && !( index.flags() & Qt::ItemIsSelectable ) )
243 return;
245 QListWidget::mouseReleaseEvent( event );
248 QModelIndex SidebarListWidget::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
250 Q_UNUSED( modifiers )
251 QModelIndex oldindex = currentIndex();
252 QModelIndex newindex = oldindex;
253 switch ( cursorAction )
255 case MoveUp:
256 case MovePrevious:
258 int row = oldindex.row() - 1;
259 while ( row > -1 && !( model()->index( row, 0 ).flags() & Qt::ItemIsSelectable ) ) --row;
260 if ( row > -1 )
261 newindex = model()->index( row, 0 );
262 break;
264 case MoveDown:
265 case MoveNext:
267 int row = oldindex.row() + 1;
268 int max = model()->rowCount();
269 while ( row < max && !( model()->index( row, 0 ).flags() & Qt::ItemIsSelectable ) ) ++row;
270 if ( row < max )
271 newindex = model()->index( row, 0 );
272 break;
274 case MoveHome:
275 case MovePageUp:
277 int row = 0;
278 while ( row < oldindex.row() && !( model()->index( row, 0 ).flags() & Qt::ItemIsSelectable ) ) ++row;
279 if ( row < oldindex.row() )
280 newindex = model()->index( row, 0 );
281 break;
283 case MoveEnd:
284 case MovePageDown:
286 int row = model()->rowCount() - 1;
287 while ( row > oldindex.row() && !( model()->index( row, 0 ).flags() & Qt::ItemIsSelectable ) ) --row;
288 if ( row > oldindex.row() )
289 newindex = model()->index( row, 0 );
290 break;
292 // no navigation possible for these
293 case MoveLeft:
294 case MoveRight:
295 break;
298 // dirty hack to change item when the key cursor changes item
299 if ( oldindex != newindex )
301 emit itemClicked( itemFromIndex( newindex ) );
303 return newindex;
307 /* Private storage. */
308 class Sidebar::Private
310 public:
311 Private()
312 : sideWidget( 0 ), bottomWidget( 0 ), splitterSizesSet( false ),
313 itemsHeight( 0 )
317 void adjustListSize( bool recalc, bool expand = true );
319 SidebarListWidget *list;
320 QSplitter *splitter;
321 QStackedWidget *stack;
322 QWidget *sideContainer;
323 QLabel *sideTitle;
324 QVBoxLayout *vlay;
325 QWidget *sideWidget;
326 QWidget *bottomWidget;
327 QList< SidebarItem* > pages;
328 bool splitterSizesSet;
329 int itemsHeight;
330 SidebarDelegate *sideDelegate;
333 void Sidebar::Private::adjustListSize( bool recalc, bool expand )
335 QRect bottomElemRect(
336 QPoint( 0, 0 ),
337 list->sizeHintForIndex( list->model()->index( list->count() - 1, 0 ) )
339 if ( recalc )
341 int w = 0;
342 for ( int i = 0; i < list->count(); ++i )
344 QSize s = list->sizeHintForIndex( list->model()->index( i, 0 ) );
345 if ( s.width() > w )
346 w = s.width();
348 bottomElemRect.setWidth( w );
350 bottomElemRect.translate( 0, bottomElemRect.height() * ( list->count() - 1 ) );
351 itemsHeight = bottomElemRect.height() * list->count();
352 list->setMinimumHeight( itemsHeight + list->frameWidth() * 2 );
353 int curWidth = list->minimumWidth();
354 int newWidth = expand
355 ? qMax( bottomElemRect.width() + list->frameWidth() * 2, curWidth )
356 : qMin( bottomElemRect.width() + list->frameWidth() * 2, curWidth );
357 list->setFixedWidth( newWidth );
361 Sidebar::Sidebar( QWidget *parent )
362 : QWidget( parent ), d( new Private )
364 QHBoxLayout *mainlay = new QHBoxLayout( this );
365 mainlay->setMargin( 0 );
366 mainlay->setSpacing( 0 );
368 d->list = new SidebarListWidget( this );
369 mainlay->addWidget( d->list );
370 d->list->setMouseTracking( true );
371 d->list->viewport()->setAttribute( Qt::WA_Hover );
372 d->sideDelegate = new SidebarDelegate( d->list );
373 d->sideDelegate->setShowText( Okular::Settings::sidebarShowText() );
374 d->list->setItemDelegate( d->sideDelegate );
375 d->list->setUniformItemSizes( true );
376 d->list->setSelectionMode( QAbstractItemView::SingleSelection );
377 int iconsize = Okular::Settings::sidebarIconSize();
378 d->list->setIconSize( QSize( iconsize, iconsize ) );
379 d->list->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
380 d->list->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
381 d->list->setContextMenuPolicy( Qt::CustomContextMenu );
382 QPalette pal = d->list->palette();
383 pal.setBrush( QPalette::Base, pal.brush( QPalette::Window ) );
384 d->list->setPalette( pal );
386 d->splitter = new QSplitter( this );
387 mainlay->addWidget( d->splitter );
388 d->splitter->setOpaqueResize( true );
389 d->splitter->setChildrenCollapsible( false );
391 d->sideContainer = new QWidget( d->splitter );
392 d->sideContainer->setMinimumWidth( 90 );
393 d->sideContainer->setMaximumWidth( 300 );
394 d->vlay = new QVBoxLayout( d->sideContainer );
395 d->vlay->setMargin( 0 );
397 d->sideTitle = new QLabel( d->sideContainer );
398 d->vlay->addWidget( d->sideTitle );
399 QFont tf = d->sideTitle->font();
400 tf.setBold( true );
401 d->sideTitle->setFont( tf );
402 d->sideTitle->setMargin( 3 );
403 d->sideTitle->setIndent( 3 );
405 d->stack = new QStackedWidget( d->sideContainer );
406 d->vlay->addWidget( d->stack );
407 d->sideContainer->hide();
409 connect( d->list, SIGNAL( itemClicked( QListWidgetItem* ) ), this, SLOT( itemClicked( QListWidgetItem* ) ) );
410 connect( d->list, SIGNAL( customContextMenuRequested( const QPoint & ) ),
411 this, SLOT( listContextMenu( const QPoint & ) ) );
412 connect( d->splitter, SIGNAL( splitterMoved( int, int ) ), this, SLOT( splitterMoved( int, int ) ) );
415 Sidebar::~Sidebar()
417 delete d;
420 int Sidebar::addItem( QWidget *widget, const QIcon &icon, const QString &text )
422 if ( !widget )
423 return -1;
425 SidebarItem *newitem = new SidebarItem( widget, icon, text );
426 d->list->addItem( newitem );
427 d->pages.append( newitem );
428 widget->setParent( d->stack );
429 d->stack->addWidget( widget );
430 // updating the minimum height of the icon view, so all are visible with no scrolling
431 d->adjustListSize( false, true );
432 return d->pages.count() - 1;
435 void Sidebar::setMainWidget( QWidget *widget )
437 delete d->sideWidget;
438 d->sideWidget = widget;
439 if ( d->sideWidget )
441 // setting the splitter as parent for the widget automatically plugs it
442 // into the splitter, neat!
443 d->sideWidget->setParent( d->splitter );
445 if ( !d->splitterSizesSet )
447 QList<int> splitterSizes = Okular::Settings::splitterSizes();
448 if ( !splitterSizes.count() )
450 // the first time use 1/10 for the panel and 9/10 for the pageView
451 splitterSizes.push_back( 50 );
452 splitterSizes.push_back( 500 );
454 d->splitter->setSizes( splitterSizes );
455 d->splitterSizesSet = true;
460 void Sidebar::setBottomWidget( QWidget *widget )
462 delete d->bottomWidget;
463 d->bottomWidget = widget;
464 if ( d->bottomWidget )
466 d->bottomWidget->setParent( this );
467 d->vlay->addWidget( d->bottomWidget );
471 void Sidebar::setItemEnabled( int index, bool enabled )
473 if ( index < 0 || index >= d->pages.count() )
474 return;
476 Qt::ItemFlags f = d->pages.at( index )->flags();
477 if ( enabled )
479 f |= Qt::ItemIsEnabled;
480 f |= Qt::ItemIsSelectable;
482 else
484 f &= ~Qt::ItemIsEnabled;
485 f &= ~Qt::ItemIsSelectable;
487 d->pages.at( index )->setFlags( f );
489 if ( !enabled && index == currentIndex() )
490 // find an enabled item, and select that one
491 for ( int i = 0; i < d->pages.count(); ++i )
492 if ( d->pages.at(i)->flags() & Qt::ItemIsEnabled )
494 setCurrentIndex( i );
495 break;
499 bool Sidebar::isItemEnabled( int index ) const
501 if ( index < 0 || index >= d->pages.count() )
502 return false;
504 Qt::ItemFlags f = d->pages.at( index )->flags();
505 return ( f & Qt::ItemIsEnabled ) == Qt::ItemIsEnabled;
508 void Sidebar::setCurrentIndex( int index )
510 if ( index < 0 || index >= d->pages.count() || !isItemEnabled( index ) )
511 return;
513 itemClicked( d->pages.at( index ) );
514 QModelIndex modelindex = d->list->model()->index( index, 0 );
515 d->list->setCurrentIndex( modelindex );
516 d->list->selectionModel()->select( modelindex, QItemSelectionModel::ClearAndSelect );
519 int Sidebar::currentIndex() const
521 return d->list->currentRow();
524 void Sidebar::setSidebarVisibility( bool visible )
526 if ( visible != d->list->isHidden() )
527 return;
529 static bool sideWasVisible = !d->sideContainer->isHidden();
531 d->list->setHidden( !visible );
532 if ( visible )
534 d->sideContainer->setHidden( !sideWasVisible );
535 sideWasVisible = true;
537 else
539 sideWasVisible = !d->sideContainer->isHidden();
540 d->sideContainer->setHidden( true );
544 bool Sidebar::isSidebarVisible() const
546 return !d->sideContainer->isHidden();
549 void Sidebar::itemClicked( QListWidgetItem *item )
551 if ( !item )
552 return;
554 SidebarItem* sbItem = dynamic_cast< SidebarItem* >( item );
555 if ( !sbItem )
556 return;
558 if ( sbItem->widget() == d->stack->currentWidget() )
560 if ( d->sideContainer->isVisible() )
562 d->list->selectionModel()->clear();
563 d->sideContainer->hide();
565 else
567 d->sideContainer->show();
570 else
572 if ( d->sideContainer->isHidden() )
573 d->sideContainer->show();
574 d->stack->setCurrentWidget( sbItem->widget() );
575 d->sideTitle->setText( sbItem->toolTip() );
579 void Sidebar::splitterMoved( int /*pos*/, int index )
581 // if the side panel has been resized, save splitter sizes
582 if ( index == 1 )
583 saveSplitterSize();
586 void Sidebar::saveSplitterSize() const
588 Okular::Settings::setSplitterSizes( d->splitter->sizes() );
589 Okular::Settings::self()->writeConfig();
592 void Sidebar::listContextMenu( const QPoint &pos )
594 KMenu menu( this );
595 menu.addTitle( i18n( "Okular" ) );
596 QAction *showTextAct = menu.addAction( i18n( "Show Text" ) );
597 showTextAct->setCheckable( true );
598 showTextAct->setChecked( d->sideDelegate->isTextShown() );
599 connect( showTextAct, SIGNAL( toggled( bool ) ), this, SLOT( showTextToggled( bool ) ) );
600 menu.addSeparator();
601 QActionGroup *sizeGroup = new QActionGroup( &menu );
602 int curSize = d->list->iconSize().width();
603 #define ADD_SIZE_ACTION( text, _itssize ) \
605 const int itssize = static_cast< int >( _itssize ); \
606 QAction *sizeAct = menu.addAction( text ); \
607 sizeAct->setCheckable( true ); \
608 sizeAct->setData( qVariantFromValue( itssize ) ); \
609 sizeAct->setChecked( itssize == curSize ); \
610 sizeGroup->addAction( sizeAct ); \
612 ADD_SIZE_ACTION( i18n( "Small Icons" ), KIconLoader::SizeSmallMedium )
613 ADD_SIZE_ACTION( i18n( "Normal Icons" ), KIconLoader::SizeMedium )
614 ADD_SIZE_ACTION( i18n( "Large Icons" ), KIconLoader::SizeLarge )
615 #undef ADD_SIZE_ACTION
616 connect( sizeGroup, SIGNAL( triggered( QAction* ) ), this, SLOT( iconSizeChanged( QAction* ) ) );
617 menu.exec( mapToGlobal( pos ) );
620 void Sidebar::showTextToggled( bool on )
622 d->sideDelegate->setShowText( on );
623 d->adjustListSize( true, on );
624 d->list->reset();
625 d->list->update();
626 Okular::Settings::setSidebarShowText( on );
627 Okular::Settings::self()->writeConfig();
630 void Sidebar::iconSizeChanged( QAction *action )
632 int size = action->data().toInt();
633 int oldSize = d->list->iconSize().width();
634 d->list->setIconSize( QSize( size, size ) );
635 d->adjustListSize( true, size > oldSize );
636 d->list->reset();
637 d->list->update();
638 Okular::Settings::setSidebarIconSize( size );
639 Okular::Settings::self()->writeConfig();
642 #include "sidebar.moc"