mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
620 lines
16 KiB
C++
620 lines
16 KiB
C++
/*
|
|
This file is part of the KDE Libraries
|
|
|
|
Copyright (C) 2006 Tobias Koenig (tokoe@kde.org)
|
|
Copyright (C) 2007 Rafael Fernández López (ereslibre@kde.org)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "kpageview_p.h"
|
|
|
|
#include <QtGui/QApplication>
|
|
#include <QtGui/QHeaderView>
|
|
#include <QtGui/QPainter>
|
|
#include <QtGui/QTextLayout>
|
|
#include <QtGui/QVBoxLayout>
|
|
|
|
#include <kdialog.h>
|
|
#include <kiconloader.h>
|
|
#include <kglobalsettings.h>
|
|
|
|
using namespace KDEPrivate;
|
|
|
|
/**
|
|
* KPagePlainView
|
|
*/
|
|
|
|
KPagePlainView::KPagePlainView( QWidget *parent )
|
|
: QAbstractItemView( parent )
|
|
{
|
|
hide();
|
|
}
|
|
|
|
QModelIndex KPagePlainView::indexAt( const QPoint& ) const
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
void KPagePlainView::scrollTo( const QModelIndex&, ScrollHint )
|
|
{
|
|
}
|
|
|
|
QRect KPagePlainView::visualRect( const QModelIndex& ) const
|
|
{
|
|
return QRect();
|
|
}
|
|
|
|
QModelIndex KPagePlainView::moveCursor( QAbstractItemView::CursorAction, Qt::KeyboardModifiers )
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
int KPagePlainView::horizontalOffset() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int KPagePlainView::verticalOffset() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool KPagePlainView::isIndexHidden( const QModelIndex& ) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void KPagePlainView::setSelection( const QRect&, QFlags<QItemSelectionModel::SelectionFlag> )
|
|
{
|
|
}
|
|
|
|
QRegion KPagePlainView::visualRegionForSelection( const QItemSelection& ) const
|
|
{
|
|
return QRegion();
|
|
}
|
|
|
|
|
|
/**
|
|
* KPageListView
|
|
*/
|
|
|
|
KPageListView::KPageListView( QWidget *parent )
|
|
: QListView( parent )
|
|
{
|
|
setViewMode( QListView::ListMode );
|
|
setMovement( QListView::Static );
|
|
setVerticalScrollMode( QListView::ScrollPerPixel );
|
|
|
|
QFont boldFont( font() );
|
|
boldFont.setBold( true );
|
|
setFont( boldFont );
|
|
|
|
setItemDelegate( new KPageListViewDelegate( this ) );
|
|
}
|
|
|
|
KPageListView::~KPageListView()
|
|
{
|
|
}
|
|
|
|
void KPageListView::setModel( QAbstractItemModel *model )
|
|
{
|
|
/*
|
|
KPageListViewProxy *proxy = new KPageListViewProxy( this );
|
|
proxy->setSourceModel( model );
|
|
proxy->rebuildMap();
|
|
|
|
connect( model, SIGNAL(layoutChanged()), proxy, SLOT(rebuildMap()) );
|
|
*/
|
|
connect( model, SIGNAL(layoutChanged()), this, SLOT(updateWidth()) );
|
|
|
|
// QListView::setModel( proxy );
|
|
QListView::setModel( model );
|
|
|
|
// Set our own selection model, which won't allow our current selection to be cleared
|
|
setSelectionModel( new KDEPrivate::SelectionModel( model, this ) );
|
|
|
|
updateWidth();
|
|
}
|
|
|
|
void KPageListView::updateWidth()
|
|
{
|
|
if ( !model() )
|
|
return;
|
|
|
|
int rows = model()->rowCount();
|
|
|
|
int width = 0;
|
|
for ( int i = 0; i < rows; ++i )
|
|
width = qMax( width, sizeHintForIndex( model()->index( i, 0 ) ).width() );
|
|
|
|
setFixedWidth( width + 25 );
|
|
}
|
|
|
|
|
|
/**
|
|
* KPageTreeView
|
|
*/
|
|
|
|
KPageTreeView::KPageTreeView( QWidget *parent )
|
|
: QTreeView( parent )
|
|
{
|
|
header()->hide();
|
|
}
|
|
|
|
void KPageTreeView::setModel( QAbstractItemModel *model )
|
|
{
|
|
connect( model, SIGNAL(layoutChanged()), this, SLOT(updateWidth()) );
|
|
|
|
QTreeView::setModel( model );
|
|
|
|
// Set our own selection model, which won't allow our current selection to be cleared
|
|
setSelectionModel( new KDEPrivate::SelectionModel( model, this ) );
|
|
|
|
updateWidth();
|
|
}
|
|
|
|
void KPageTreeView::updateWidth()
|
|
{
|
|
if ( !model() )
|
|
return;
|
|
|
|
int columns = model()->columnCount();
|
|
|
|
expandItems();
|
|
|
|
int width = 0;
|
|
for ( int i = 0; i < columns; ++i ) {
|
|
resizeColumnToContents( i );
|
|
width = qMax( width, sizeHintForColumn( i ) );
|
|
}
|
|
|
|
setFixedWidth( width + 25 );
|
|
}
|
|
|
|
void KPageTreeView::expandItems( const QModelIndex &index )
|
|
{
|
|
setExpanded( index, true );
|
|
|
|
const int count = model()->rowCount( index );
|
|
for ( int i = 0; i < count; ++i )
|
|
expandItems( model()->index( i, 0, index ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* KPageTabbedView
|
|
*/
|
|
|
|
KPageTabbedView::KPageTabbedView( QWidget *parent )
|
|
: QAbstractItemView( parent )
|
|
{
|
|
// hide the viewport of the QAbstractScrollArea
|
|
const QList<QWidget*> list = findChildren<QWidget*>();
|
|
for ( int i = 0; i < list.count(); ++i )
|
|
list[ i ]->hide();
|
|
|
|
setFrameShape( NoFrame );
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout( this );
|
|
layout->setMargin( 0 );
|
|
|
|
mTabWidget = new QTabWidget( this );
|
|
connect( mTabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentPageChanged(int)) );
|
|
|
|
layout->addWidget( mTabWidget );
|
|
}
|
|
|
|
KPageTabbedView::~KPageTabbedView()
|
|
{
|
|
if (model()) {
|
|
for ( int i = 0; i < mTabWidget->count(); ++i ) {
|
|
QWidget *page = qvariant_cast<QWidget*>( model()->data( model()->index( i, 0 ), KPageView::WidgetRole ) );
|
|
|
|
if (page) {
|
|
page->setVisible(false);
|
|
page->setParent(0); // reparent our children before they are deleted
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void KPageTabbedView::setModel( QAbstractItemModel *model )
|
|
{
|
|
QAbstractItemView::setModel( model );
|
|
|
|
connect( model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged()) );
|
|
|
|
layoutChanged();
|
|
}
|
|
|
|
QModelIndex KPageTabbedView::indexAt( const QPoint& ) const
|
|
{
|
|
if ( model() )
|
|
return model()->index( 0, 0 );
|
|
else
|
|
return QModelIndex();
|
|
}
|
|
|
|
void KPageTabbedView::scrollTo( const QModelIndex &index, ScrollHint )
|
|
{
|
|
if ( !index.isValid() )
|
|
return;
|
|
|
|
mTabWidget->setCurrentIndex( index.row() );
|
|
}
|
|
|
|
QRect KPageTabbedView::visualRect( const QModelIndex& ) const
|
|
{
|
|
return QRect();
|
|
}
|
|
|
|
QSize KPageTabbedView::minimumSizeHint() const
|
|
{
|
|
return mTabWidget->minimumSizeHint();
|
|
}
|
|
|
|
QModelIndex KPageTabbedView::moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers)
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
int KPageTabbedView::horizontalOffset() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int KPageTabbedView::verticalOffset() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool KPageTabbedView::isIndexHidden( const QModelIndex &index ) const
|
|
{
|
|
return ( mTabWidget->currentIndex() != index.row() );
|
|
}
|
|
|
|
void KPageTabbedView::setSelection( const QRect&, QFlags<QItemSelectionModel::SelectionFlag> )
|
|
{
|
|
}
|
|
|
|
QRegion KPageTabbedView::visualRegionForSelection( const QItemSelection& ) const
|
|
{
|
|
return QRegion();
|
|
}
|
|
|
|
void KPageTabbedView::currentPageChanged( int index )
|
|
{
|
|
if ( !model() )
|
|
return;
|
|
|
|
QModelIndex modelIndex = model()->index( index, 0 );
|
|
|
|
selectionModel()->setCurrentIndex( modelIndex, QItemSelectionModel::ClearAndSelect );
|
|
}
|
|
|
|
void KPageTabbedView::layoutChanged()
|
|
{
|
|
// save old position
|
|
int pos = mTabWidget->currentIndex();
|
|
|
|
// clear tab bar
|
|
int count = mTabWidget->count();
|
|
for ( int i = 0; i < count; ++i ) {
|
|
mTabWidget->removeTab( 0 );
|
|
}
|
|
|
|
if ( !model() )
|
|
return;
|
|
|
|
// add new tabs
|
|
for ( int i = 0; i < model()->rowCount(); ++i ) {
|
|
const QString title = model()->data( model()->index( i, 0 ) ).toString();
|
|
const QIcon icon = model()->data( model()->index( i, 0 ), Qt::DecorationRole ).value<QIcon>();
|
|
QWidget *page = qvariant_cast<QWidget*>( model()->data( model()->index( i, 0 ), KPageView::WidgetRole ) );
|
|
if (page) {
|
|
QWidget *widget = new QWidget(this);
|
|
QVBoxLayout *layout = new QVBoxLayout(widget);
|
|
widget->setLayout(layout);
|
|
layout->addWidget(page);
|
|
page->setVisible(true);
|
|
mTabWidget->addTab( widget, icon, title );
|
|
}
|
|
}
|
|
|
|
mTabWidget->setCurrentIndex( pos );
|
|
}
|
|
|
|
void KPageTabbedView::dataChanged( const QModelIndex &index, const QModelIndex& )
|
|
{
|
|
if ( !index.isValid() )
|
|
return;
|
|
|
|
if ( index.row() < 0 || index.row() >= mTabWidget->count() )
|
|
return;
|
|
|
|
const QString title = model()->data( index ).toString();
|
|
const QIcon icon = model()->data( index, Qt::DecorationRole ).value<QIcon>();
|
|
|
|
mTabWidget->setTabText( index.row(), title );
|
|
mTabWidget->setTabIcon( index.row(), icon );
|
|
}
|
|
|
|
/**
|
|
* KPageListViewDelegate
|
|
*/
|
|
|
|
KPageListViewDelegate::KPageListViewDelegate( QObject *parent )
|
|
: QAbstractItemDelegate( parent )
|
|
{
|
|
mIconSize = KIconLoader::global()->currentSize( KIconLoader::Dialog );
|
|
|
|
connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)), this, SLOT(iconSettingsChanged(int)) );
|
|
}
|
|
|
|
void KPageListViewDelegate::iconSettingsChanged( int group )
|
|
{
|
|
if ( group == KIconLoader::Dialog ) {
|
|
const int iconSize = KIconLoader::global()->currentSize( KIconLoader::Dialog );
|
|
if ( mIconSize != iconSize ) {
|
|
mIconSize = iconSize;
|
|
emit sizeHintChanged( QModelIndex() );
|
|
}
|
|
}
|
|
}
|
|
|
|
static int layoutText(QTextLayout *layout, int maxWidth)
|
|
{
|
|
qreal height = 0;
|
|
int textWidth = 0;
|
|
layout->beginLayout();
|
|
while (true) {
|
|
QTextLine line = layout->createLine();
|
|
if (!line.isValid()) {
|
|
break;
|
|
}
|
|
line.setLineWidth(maxWidth);
|
|
line.setPosition(QPointF(0, height));
|
|
height += line.height();
|
|
textWidth = qMax(textWidth, qRound(line.naturalTextWidth() + 0.5));
|
|
}
|
|
layout->endLayout();
|
|
return textWidth;
|
|
}
|
|
|
|
void KPageListViewDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
|
{
|
|
if ( !index.isValid() )
|
|
return;
|
|
|
|
const QString text = index.model()->data( index, Qt::DisplayRole ).toString();
|
|
const QIcon icon = index.model()->data( index, Qt::DecorationRole ).value<QIcon>();
|
|
const QPixmap pixmap = icon.pixmap( mIconSize, mIconSize );
|
|
|
|
QFontMetrics fm = painter->fontMetrics();
|
|
int wp = pixmap.width();
|
|
int hp = pixmap.height();
|
|
|
|
QTextLayout iconTextLayout( text, option.font );
|
|
QTextOption textOption( Qt::AlignHCenter );
|
|
iconTextLayout.setTextOption( textOption );
|
|
int maxWidth = qMax( 3 * wp, 8 * fm.height() );
|
|
layoutText( &iconTextLayout, maxWidth );
|
|
|
|
QPen pen = painter->pen();
|
|
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
|
|
? QPalette::Normal : QPalette::Disabled;
|
|
if ( cg == QPalette::Normal && !(option.state & QStyle::State_Active) )
|
|
cg = QPalette::Inactive;
|
|
|
|
QStyleOptionViewItem opt(option);
|
|
opt.showDecorationSelected = true;
|
|
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
|
|
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
|
|
if ( option.state & QStyle::State_Selected ) {
|
|
painter->setPen( option.palette.color( cg, QPalette::HighlightedText ) );
|
|
} else {
|
|
painter->setPen( option.palette.color( cg, QPalette::Text ) );
|
|
}
|
|
|
|
painter->drawPixmap( option.rect.x() + (option.rect.width()/2)-(wp/2), option.rect.y() + 5, pixmap );
|
|
if ( !text.isEmpty() )
|
|
iconTextLayout.draw( painter, QPoint( option.rect.x() + (option.rect.width()/2)-(maxWidth/2), option.rect.y() + hp+7 ) );
|
|
|
|
painter->setPen( pen );
|
|
|
|
drawFocus( painter, option, option.rect );
|
|
}
|
|
|
|
QSize KPageListViewDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
|
{
|
|
if ( !index.isValid() )
|
|
return QSize( 0, 0 );
|
|
|
|
const QString text = index.model()->data( index, Qt::DisplayRole ).toString();
|
|
const QIcon icon = index.model()->data( index, Qt::DecorationRole ).value<QIcon>();
|
|
const QPixmap pixmap = icon.pixmap( mIconSize, mIconSize );
|
|
|
|
QFontMetrics fm = option.fontMetrics;
|
|
int gap = fm.height();
|
|
int wp = pixmap.width();
|
|
int hp = pixmap.height();
|
|
|
|
if ( hp == 0 ) {
|
|
/**
|
|
* No pixmap loaded yet, we'll use the default icon size in this case.
|
|
*/
|
|
hp = mIconSize;
|
|
wp = mIconSize;
|
|
}
|
|
|
|
QTextLayout iconTextLayout( text, option.font );
|
|
int wt = layoutText( &iconTextLayout, qMax( 3 * wp, 8 * fm.height() ) );
|
|
int ht = iconTextLayout.boundingRect().height();
|
|
|
|
int width, height;
|
|
if ( text.isEmpty() )
|
|
height = hp;
|
|
else
|
|
height = hp + ht + 10;
|
|
|
|
width = qMax( wt, wp ) + gap;
|
|
|
|
return QSize( width, height );
|
|
}
|
|
|
|
void KPageListViewDelegate::drawFocus( QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect ) const
|
|
{
|
|
if (option.state & QStyle::State_HasFocus) {
|
|
QStyleOptionFocusRect o;
|
|
o.QStyleOption::operator=(option);
|
|
o.rect = rect;
|
|
o.state |= QStyle::State_KeyboardFocusChange;
|
|
QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
|
|
? QPalette::Normal : QPalette::Disabled;
|
|
o.backgroundColor = option.palette.color( cg, (option.state & QStyle::State_Selected)
|
|
? QPalette::Highlight : QPalette::Background );
|
|
QApplication::style()->drawPrimitive( QStyle::PE_FrameFocusRect, &o, painter );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* KPageListViewProxy
|
|
*/
|
|
|
|
KPageListViewProxy::KPageListViewProxy( QObject *parent )
|
|
: QAbstractProxyModel( parent )
|
|
{
|
|
}
|
|
|
|
KPageListViewProxy::~KPageListViewProxy()
|
|
{
|
|
}
|
|
|
|
int KPageListViewProxy::rowCount( const QModelIndex& ) const
|
|
{
|
|
return mList.count();
|
|
}
|
|
|
|
int KPageListViewProxy::columnCount( const QModelIndex& ) const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
QModelIndex KPageListViewProxy::index( int row, int column, const QModelIndex& ) const
|
|
{
|
|
if ( column > 1 || row >= mList.count() )
|
|
return QModelIndex();
|
|
else
|
|
return createIndex( row, column, mList[ row ].internalPointer() );
|
|
}
|
|
|
|
QModelIndex KPageListViewProxy::parent( const QModelIndex& ) const
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
QVariant KPageListViewProxy::data( const QModelIndex &index, int role ) const
|
|
{
|
|
if ( !index.isValid() )
|
|
return QVariant();
|
|
|
|
if ( index.row() >= mList.count() )
|
|
return QVariant();
|
|
|
|
return sourceModel()->data( mList[ index.row() ], role );
|
|
}
|
|
|
|
QModelIndex KPageListViewProxy::mapFromSource( const QModelIndex &index ) const
|
|
{
|
|
if ( !index.isValid() )
|
|
return QModelIndex();
|
|
|
|
for ( int i = 0; i < mList.count(); ++i ) {
|
|
if ( mList[ i ] == index )
|
|
return createIndex( i, 0, index.internalPointer() );
|
|
}
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
QModelIndex KPageListViewProxy::mapToSource( const QModelIndex &index ) const
|
|
{
|
|
if ( !index.isValid() )
|
|
return QModelIndex();
|
|
|
|
return mList[ index.row() ];
|
|
}
|
|
|
|
void KPageListViewProxy::rebuildMap()
|
|
{
|
|
mList.clear();
|
|
|
|
const QAbstractItemModel *model = sourceModel();
|
|
if ( !model )
|
|
return;
|
|
|
|
for ( int i = 0; i < model->rowCount(); ++i ) {
|
|
addMapEntry( model->index( i, 0 ) );
|
|
}
|
|
|
|
for ( int i = 0; i < mList.count(); ++i )
|
|
qDebug( "%d:0 -> %d:%d", i, mList[ i ].row(), mList[ i ].column() );
|
|
|
|
emit layoutChanged();
|
|
}
|
|
|
|
void KPageListViewProxy::addMapEntry( const QModelIndex &index )
|
|
{
|
|
if ( sourceModel()->rowCount( index ) == 0 ) {
|
|
mList.append( index );
|
|
} else {
|
|
const int count = sourceModel()->rowCount( index );
|
|
for ( int i = 0; i < count; ++i )
|
|
addMapEntry( sourceModel()->index( i, 0, index ) );
|
|
}
|
|
}
|
|
|
|
SelectionModel::SelectionModel( QAbstractItemModel *model, QObject *parent )
|
|
: QItemSelectionModel( model, parent )
|
|
{
|
|
}
|
|
|
|
void SelectionModel::clear()
|
|
{
|
|
// Don't allow the current selection to be cleared
|
|
}
|
|
|
|
void SelectionModel::select( const QModelIndex &index, QItemSelectionModel::SelectionFlags command )
|
|
{
|
|
// Don't allow the current selection to be cleared
|
|
if ( !index.isValid() && ( command & QItemSelectionModel::Clear ) ) {
|
|
return;
|
|
}
|
|
QItemSelectionModel::select( index, command );
|
|
}
|
|
|
|
void SelectionModel::select( const QItemSelection &selection, QItemSelectionModel::SelectionFlags command )
|
|
{
|
|
// Don't allow the current selection to be cleared
|
|
if ( !selection.count() && ( command & QItemSelectionModel::Clear ) ) {
|
|
return;
|
|
}
|
|
QItemSelectionModel::select( selection, command );
|
|
}
|
|
|
|
#include "moc_kpageview_p.cpp"
|