You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1558 lines
49 KiB

/******************************************************************************
*
* Copyright 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
*
* This program is free softhisare; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Softhisare Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Softhisare
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*******************************************************************************/
#include "messagelistview/core/themeeditor.h"
#include "messagelistview/core/theme.h"
#include "messagelistview/core/groupheaderitem.h"
#include "messagelistview/core/messageitem.h"
#include "messagelistview/core/modelinvariantrowmapper.h"
#include "messagelistview/core/manager.h"
#include "messagelistview/core/comboboxutils.h"
#include <libkdepim/messagestatus.h>
#include <QActionGroup>
#include <QCheckBox>
#include <QCursor>
#include <QDrag>
#include <QGridLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
#include <QMouseEvent>
#include <QTreeWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QPixmap>
#include <QPushButton>
#include <QSpinBox>
#include <QStringList>
#include <KColorDialog>
#include <KComboBox>
#include <KLineEdit>
#include <KLocale>
#include <KFontDialog>
#include <KMenu>
#include <KIconLoader>
#include <KTextEdit>
#include <time.h> // for time_t
namespace KMail
{
namespace MessageListView
{
namespace Core
{
static const char * gThemeContentItemTypeDndMimeDataFormat = "application/x-kmail-messagelistview-theme-contentitem-type";
ThemeColumnPropertiesDialog::ThemeColumnPropertiesDialog( QWidget * parent, Theme::Column * column, const QString &title )
: KDialog( parent ), mColumn( column )
{
//setAttribute( Qt::WA_DeleteOnClose );
setWindowModality( Qt::ApplicationModal ); // FIXME: Sure ?
setButtons( Ok | Cancel );
setWindowTitle( title );
QWidget * base = new QWidget( this );
setMainWidget( base );
QGridLayout * g = new QGridLayout( base );
QLabel * l;
l = new QLabel( i18nc( "@label:textbox Property name", "Name:" ), base );
g->addWidget( l, 0, 0 );
mNameEdit = new KLineEdit( base );
mNameEdit->setToolTip( i18n( "The label that will be displayed in the column header." ) );
g->addWidget( mNameEdit, 0, 1 );
l = new QLabel( i18n( "Header click sorts messages:" ), base );
g->addWidget( l, 1, 0 );
mMessageSortingCombo = new KComboBox( base );
mMessageSortingCombo->setToolTip( i18n( "The sorting order that clicking on this column header will switch to." ) );
g->addWidget( mMessageSortingCombo, 1, 1 );
mVisibleByDefaultCheck = new QCheckBox( i18n( "Visible by default" ), base );
mVisibleByDefaultCheck->setToolTip( i18n( "Check this if this column should be visible when the theme is selected." ) );
g->addWidget( mVisibleByDefaultCheck, 2, 1 );
mIsSenderOrReceiverCheck = new QCheckBox( i18n( "Contains \"Sender or Receiver\" field" ), base );
mIsSenderOrReceiverCheck->setToolTip( i18n( "Check this if this column label should be updated depending on the folder \"inbound\"/\"outbound\" type." ) );
g->addWidget( mIsSenderOrReceiverCheck, 3, 1 );
g->setColumnStretch( 1, 1 );
g->setRowStretch( 10, 1 );
connect( this, SIGNAL( okClicked() ),
SLOT( slotOkButtonClicked() ) );
// Display the current settings
mNameEdit->setText( mColumn->label() );
mVisibleByDefaultCheck->setChecked( mColumn->visibleByDefault() );
mIsSenderOrReceiverCheck->setChecked( mColumn->isSenderOrReceiver() );
ComboBoxUtils::fillIntegerOptionCombo( mMessageSortingCombo, SortOrder::enumerateMessageSortingOptions( Aggregation::PerfectReferencesAndSubject ) );
ComboBoxUtils::setIntegerOptionComboValue( mMessageSortingCombo, mColumn->messageSorting() );
}
void ThemeColumnPropertiesDialog::slotOkButtonClicked()
{
QString text = mNameEdit->text();
if ( text.isEmpty() )
text = i18n( "Unnamed Column" );
mColumn->setLabel( text );
mColumn->setVisibleByDefault( mVisibleByDefaultCheck->isChecked() );
mColumn->setIsSenderOrReceiver( mIsSenderOrReceiverCheck->isChecked() );
mColumn->setMessageSorting(
static_cast< SortOrder::MessageSorting >(
ComboBoxUtils::getIntegerOptionComboValue( mMessageSortingCombo, SortOrder::NoMessageSorting )
)
);
accept();
}
ThemeContentItemSourceLabel::ThemeContentItemSourceLabel( QWidget * parent, Theme::ContentItem::Type type )
: QLabel( parent ), mType( type )
{
setFrameStyle( QFrame::StyledPanel | QFrame::Raised );
}
ThemeContentItemSourceLabel::~ThemeContentItemSourceLabel()
{
}
void ThemeContentItemSourceLabel::mousePressEvent( QMouseEvent * e )
{
if ( e->button() == Qt::LeftButton )
mMousePressPoint = e->pos();
}
void ThemeContentItemSourceLabel::mouseMoveEvent( QMouseEvent * e )
{
if ( e->buttons() & Qt::LeftButton )
{
QPoint diff = mMousePressPoint - e->pos();
if ( diff.manhattanLength() > 4 )
startDrag();
}
}
void ThemeContentItemSourceLabel::startDrag()
{
//QPixmap pix = QPixmap::grabWidget( this );
//QPixmap alpha( pix.width(), pix.height() );
//alpha.fill(0x0f0f0f0f);
//pix.setAlphaChannel( alpha ); // <-- this crashes... no alpha for dragged pixmap :(
QMimeData * data = new QMimeData();
QByteArray arry;
arry.resize( sizeof( Theme::ContentItem::Type ) );
*( ( Theme::ContentItem::Type * ) arry.data() ) = mType;
data->setData( gThemeContentItemTypeDndMimeDataFormat, arry );
QDrag * drag = new QDrag( this );
drag->setMimeData( data );
//drag->setPixmap( pix );
//drag->setHotSpot( mapFromGlobal( QCursor::pos() ) );
drag->exec( Qt::CopyAction, Qt::CopyAction );
}
ThemePreviewDelegate::ThemePreviewDelegate( QAbstractItemView * parent, QPaintDevice * paintDevice )
: ThemeDelegate( parent, paintDevice )
{
mRowMapper = new ModelInvariantRowMapper();
mSampleGroupHeaderItem = new GroupHeaderItem( i18n( "Message Group" ) );
mSampleGroupHeaderItem->setDate( time( 0 ) );
mSampleGroupHeaderItem->setMaxDate( time( 0 ) + 31337 );
mSampleGroupHeaderItem->setSenderOrReceiver( i18n( "Sender/Receiver" ) );
mSampleGroupHeaderItem->setSubject( i18n( "Very long subject very long subject very long subject very long subject very long subject very long" ) );
mSampleMessageItem = new MessageItem();
mSampleMessageItem->setDate( time( 0 ) );
mSampleMessageItem->setSize( 0x31337 );
mSampleMessageItem->setMaxDate( time( 0 ) + 31337 );
mSampleMessageItem->setSenderOrReceiver( i18n( "Sender/Receiver" ) );
mSampleMessageItem->setSender( i18n( "Sender" ) );
mSampleMessageItem->setReceiver( i18n( "Receiver" ) );
mSampleMessageItem->setSubject( i18n( "Very long subject very long subject very long subject very long subject very long subject very long" ) );
mSampleMessageItem->setSignatureState( MessageItem::FullySigned );
mSampleMessageItem->setEncryptionState( MessageItem::FullyEncrypted );
QList< MessageItem::Tag * > * list = new QList< MessageItem::Tag * >();
list->append( new MessageItem::Tag( SmallIcon( "feed-subscribe" ), i18n( "Sample Tag 1" ), QString() ) );
list->append( new MessageItem::Tag( SmallIcon( "feed-subscribe" ), i18n( "Sample Tag 2" ), QString() ) );
list->append( new MessageItem::Tag( SmallIcon( "feed-subscribe" ), i18n( "Sample Tag 3" ), QString() ) );
mSampleMessageItem->setTagList( list );
mRowMapper->createModelInvariantIndex( 0, mSampleMessageItem );
mSampleGroupHeaderItem->rawAppendChildItem( mSampleMessageItem );
mSampleMessageItem->setParent( mSampleGroupHeaderItem );
KPIM::MessageStatus stat;
stat.fromQInt32( 0x7fffffff );
stat.setQueued( false );
stat.setSent( false );
stat.setNew();
stat.setSpam( true );
stat.setWatched( true );
//stat.setHasAttachment( false );
mSampleMessageItem->setStatus( stat );
}
ThemePreviewDelegate::~ThemePreviewDelegate()
{
delete mSampleGroupHeaderItem;
//delete mSampleMessageItem; (deleted by the parent)
delete mRowMapper;
}
Item * ThemePreviewDelegate::itemFromIndex( const QModelIndex &index ) const
{
if ( index.parent().isValid() )
return mSampleMessageItem;
return mSampleGroupHeaderItem;
}
ThemePreviewWidget::ThemePreviewWidget( QWidget * parent )
: QTreeWidget( parent )
{
mSelectedThemeContentItem = 0;
mSelectedThemeColumn = 0;
mFirstShow = true;
mDelegate = new ThemePreviewDelegate( this, viewport() );
setItemDelegate( mDelegate );
setRootIsDecorated( false );
viewport()->setAcceptDrops( true );
header()->setContextMenuPolicy( Qt::CustomContextMenu ); // make sure it's true
connect( header(), SIGNAL( customContextMenuRequested( const QPoint& ) ),
SLOT( slotHeaderContextMenuRequested( const QPoint& ) ) );
mGroupHeaderSampleItem = new QTreeWidgetItem( this );
mGroupHeaderSampleItem->setText( 0, QString() );
mGroupHeaderSampleItem->setFlags( Qt::ItemIsEnabled );
QTreeWidgetItem * m = new QTreeWidgetItem( mGroupHeaderSampleItem );
m->setText( 0, QString() );
mGroupHeaderSampleItem->setExpanded( true );
}
ThemePreviewWidget::~ThemePreviewWidget()
{
}
QSize ThemePreviewWidget::sizeHint() const
{
return QSize( 350, 180 );
}
void ThemePreviewWidget::applyThemeColumnWidths()
{
if ( !mTheme )
return;
const QList< Theme::Column * > & columns = mTheme->columns();
if ( columns.count() < 1 )
{
viewport()->update(); // trigger a repaint
return;
}
// Now we want to distribute the available width on all the columns.
// The algorithm used here is very similar to the one used in View::applyThemeColumns().
// It just takes care of ALL the columns instead of the visible ones.
QList< Theme::Column * >::ConstIterator it;
// Gather size hints for all sections.
int idx = 0;
int totalVisibleWidthHint = 0;
for ( it = columns.begin(); it != columns.end(); ++it )
{
totalVisibleWidthHint += mDelegate->sizeHintForItemTypeAndColumn( Item::Message, idx ).width();
idx++;
}
if ( totalVisibleWidthHint < 16 )
totalVisibleWidthHint = 16; // be reasonable
// Now we can compute proportional widths.
idx = 0;
QList< int > realWidths;
int totalVisibleWidth = 0;
for ( it = columns.begin(); it != columns.end(); ++it )
{
int hintWidth = mDelegate->sizeHintForItemTypeAndColumn( Item::Message, idx ).width();
int realWidth;
if ( ( *it )->containsTextItems() )
{
// the column contains text items, it should get more space
realWidth = ( ( hintWidth * viewport()->width() ) / totalVisibleWidthHint ) - 2; // -2 is heuristic
if ( realWidth < ( hintWidth + 2 ) )
realWidth = hintWidth + 2; // can't be less
} else {
// the column contains no text items, it should get just a little bit more than its sizeHint().
realWidth = hintWidth + 2;
}
realWidths.append( realWidth );
totalVisibleWidth += realWidth;
idx++;
}
idx = 0;
totalVisibleWidth += 4; // account for some view's border
if ( totalVisibleWidth < viewport()->width() )
{
// give the additional space to the text columns
// also give more space to the first ones and less space to the last ones
int available = viewport()->width() - totalVisibleWidth;
for ( it = columns.begin(); it != columns.end(); ++it )
{
if ( ( ( *it )->visibleByDefault() || ( idx == 0 ) ) && ( *it )->containsTextItems() )
{
// give more space to this column
available >>= 1; // eat half of the available space
realWidths[ idx ] += available; // and give it to this column
}
idx++;
}
// if any space is still available, give it to the first column
if ( available )
realWidths[ 0 ] += available;
}
idx = 0;
// We're ready.
// Assign widths. Hide the sections that are not visible by default, show the other ones.
for ( it = columns.begin(); it != columns.end(); ++it )
{
header()->resizeSection( idx, realWidths[ idx ] );
idx++;
}
#if 0
if( mTheme->viewHeaderPolicy() == Theme::NeverShowHeader )
header()->hide();
else
header()->show();
#endif
}
void ThemePreviewWidget::setTheme( Theme * theme )
{
bool themeChanged = theme != mTheme;
mSelectedThemeContentItem = 0;
mThemeSelectedContentItemRect = QRect();
mDropIndicatorPoint1 = QPoint();
mDropIndicatorPoint2 = QPoint();
mTheme = theme;
mDelegate->setTheme( theme );
mGroupHeaderSampleItem->setExpanded( true );
const QList< Theme::Column * > & columns = mTheme->columns();
setColumnCount( columns.count() );
QStringList headerLabels;
for( QList< Theme::Column * >::ConstIterator it = columns.constBegin(); it != columns.constEnd(); ++it )
{
QString label = ( *it )->label();
if ( ( *it )->visibleByDefault() )
label += QString( " (%1)" ).arg( i18nc( "Indicates whether or not a header label is visible", "Visible") );
headerLabels.append( label );
}
setHeaderLabels( headerLabels );
if ( themeChanged )
applyThemeColumnWidths();
viewport()->update(); // trigger a repaint
}
void ThemePreviewWidget::internalHandleDragEnterEvent( QDragEnterEvent * e )
{
e->ignore();
if ( !e->mimeData() )
return;
if ( !e->mimeData()->hasFormat( gThemeContentItemTypeDndMimeDataFormat ) )
return;
e->accept();
}
void ThemePreviewWidget::showEvent( QShowEvent * e )
{
QTreeWidget::showEvent( e );
if ( mFirstShow )
{
// Make sure we re-apply column widths the first time we're shown.
// The first "apply" call was made while the widget was still hidden and
// almost surely had wrong sizes.
applyThemeColumnWidths();
mFirstShow = false;
}
}
void ThemePreviewWidget::dragEnterEvent( QDragEnterEvent * e )
{
internalHandleDragEnterEvent( e );
mThemeSelectedContentItemRect = QRect();
viewport()->update(); // trigger a repaint
}
void ThemePreviewWidget::internalHandleDragMoveEvent( QDragMoveEvent * e )
{
e->ignore();
if ( !e->mimeData() )
return;
if ( !e->mimeData()->hasFormat( gThemeContentItemTypeDndMimeDataFormat ) )
return;
QByteArray arry = e->mimeData()->data( gThemeContentItemTypeDndMimeDataFormat );
if ( arry.size() != sizeof( Theme::ContentItem::Type ) )
return; // ugh
Theme::ContentItem::Type type = *( ( Theme::ContentItem::Type * ) arry.data() );
if ( !computeContentItemInsertPosition( e->pos(), type ) )
return;
e->accept();
}
void ThemePreviewWidget::dragMoveEvent( QDragMoveEvent * e )
{
internalHandleDragMoveEvent( e );
mThemeSelectedContentItemRect = QRect();
viewport()->update(); // trigger a repaint
}
void ThemePreviewWidget::dropEvent( QDropEvent * e )
{
mDropIndicatorPoint1 = mDropIndicatorPoint2;
e->ignore();
if ( !e->mimeData() )
return;
if ( !e->mimeData()->hasFormat( gThemeContentItemTypeDndMimeDataFormat ) )
return;
QByteArray arry = e->mimeData()->data( gThemeContentItemTypeDndMimeDataFormat );
if ( arry.size() != sizeof( Theme::ContentItem::Type ) )
return; // ugh
Theme::ContentItem::Type type = *( ( Theme::ContentItem::Type * ) arry.data() );
if ( !computeContentItemInsertPosition( e->pos(), type ) )
{
viewport()->update();
return;
}
Theme::Row * row = 0;
switch ( mRowInsertPosition )
{
case AboveRow:
row = new Theme::Row();
if ( mDelegate->hitItem()->type() == Item::Message )
const_cast< Theme::Column * >( mDelegate->hitColumn() )->insertMessageRow( mDelegate->hitRowIndex(), row );
else
const_cast< Theme::Column * >( mDelegate->hitColumn() )->insertGroupHeaderRow( mDelegate->hitRowIndex(), row );
break;
case InsideRow:
row = const_cast< Theme::Row * >( mDelegate->hitRow() );
break;
case BelowRow:
row = new Theme::Row();
if ( mDelegate->hitItem()->type() == Item::Message )
const_cast< Theme::Column * >( mDelegate->hitColumn() )->insertMessageRow( mDelegate->hitRowIndex()+1, row );
else
const_cast< Theme::Column * >( mDelegate->hitColumn() )->insertGroupHeaderRow( mDelegate->hitRowIndex()+1, row );
break;
}
if ( !row )
return;
Theme::ContentItem * ci = new Theme::ContentItem( type );
if ( ci->canBeDisabled() )
{
if ( ci->isClickable() )
ci->setSoftenByBlendingWhenDisabled( true ); // default to softened
else
ci->setHideWhenDisabled( true ); // default to hidden
}
int idx;
switch( mItemInsertPosition )
{
case OnLeftOfItem:
if ( !mDelegate->hitContentItem() )
{
// bleah
delete ci;
return;
}
idx = mDelegate->hitContentItemRight() ? \
row->rightItems().indexOf( const_cast< Theme::ContentItem * >( mDelegate->hitContentItem() ) ) : \
row->leftItems().indexOf( const_cast< Theme::ContentItem * >( mDelegate->hitContentItem() ) );
if ( idx < 0 )
{
// bleah
delete ci;
return;
}
if ( mDelegate->hitContentItemRight() )
row->insertRightItem( idx+1, ci );
else
row->insertLeftItem( idx, ci );
break;
case OnRightOfItem:
if ( !mDelegate->hitContentItem() )
{
// bleah
delete ci;
return;
}
idx = mDelegate->hitContentItemRight() ? \
row->rightItems().indexOf( const_cast< Theme::ContentItem * >( mDelegate->hitContentItem() ) ) : \
row->leftItems().indexOf( const_cast< Theme::ContentItem * >( mDelegate->hitContentItem() ) );
if ( idx < 0 )
{
// bleah
delete ci;
return;
}
if ( mDelegate->hitContentItemRight() )
row->insertRightItem( idx, ci );
else
row->insertLeftItem( idx+1, ci );
break;
case AsLastLeftItem:
row->addLeftItem( ci );
break;
case AsLastRightItem:
row->addRightItem( ci );
break;
case AsFirstLeftItem:
row->insertLeftItem( 0, ci );
break;
case AsFirstRightItem:
row->insertRightItem( 0, ci );
break;
default: // should never happen
row->addRightItem( ci );
break;
}
e->acceptProposedAction();
mThemeSelectedContentItemRect = QRect();
mDropIndicatorPoint1 = mDropIndicatorPoint2;
mSelectedThemeContentItem = 0;
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
bool ThemePreviewWidget::computeContentItemInsertPosition( const QPoint &pos, Theme::ContentItem::Type type )
{
mDropIndicatorPoint1 = mDropIndicatorPoint2; // this marks the position as invalid
if ( !mDelegate->hitTest( pos, false ) )
return false;
if ( !mDelegate->hitRow() )
return false;
if ( mDelegate->hitRowIsMessageRow() )
{
if ( !Theme::ContentItem::applicableToMessageItems( type ) )
return false;
} else {
if ( !Theme::ContentItem::applicableToGroupHeaderItems( type ) )
return false;
}
QRect rowRect = mDelegate->hitRowRect();
if ( pos.y() < rowRect.top() + 3 )
{
// above a row
mRowInsertPosition = AboveRow;
if ( pos.x() < ( rowRect.left() + ( rowRect.width() / 2 ) ) )
{
mDropIndicatorPoint1 = rowRect.topLeft();
mItemInsertPosition = AsLastLeftItem;
} else {
mDropIndicatorPoint1 = rowRect.topRight();
mItemInsertPosition = AsLastRightItem;
}
mDropIndicatorPoint2 = QPoint( rowRect.left() + ( rowRect.width() / 2 ), rowRect.top() );
return true;
}
if ( pos.y() > rowRect.bottom() - 3 )
{
// below a row
mRowInsertPosition = BelowRow;
if ( pos.x() < ( rowRect.left() + ( rowRect.width() / 2 ) ) )
{
mDropIndicatorPoint1 = rowRect.bottomLeft();
mItemInsertPosition = AsLastLeftItem;
} else {
mDropIndicatorPoint1 = rowRect.bottomRight();
mItemInsertPosition = AsLastRightItem;
}
mDropIndicatorPoint2 = QPoint( rowRect.left() + ( rowRect.width() / 2 ), rowRect.bottom() );
return true;
}
mRowInsertPosition = InsideRow;
if ( !mDelegate->hitContentItem() )
{
// didn't hit anything... probably no items in the row
if ( pos.x() < ( rowRect.left() + ( rowRect.width() / 2 ) ) )
{
mItemInsertPosition = AsLastLeftItem;
mDropIndicatorPoint1 = QPoint( rowRect.left(), rowRect.top() );
mDropIndicatorPoint2 = QPoint( rowRect.left(), rowRect.bottom() );
} else {
mItemInsertPosition = AsLastRightItem;
mDropIndicatorPoint1 = QPoint( rowRect.right(), rowRect.top() );
mDropIndicatorPoint2 = QPoint( rowRect.right(), rowRect.bottom() );
}
return true;
}
// hit something, maybe inexactly
QRect itemRect = mDelegate->hitContentItemRect();
if ( !itemRect.contains( pos ) )
{
// inexact hit: outside an item
if ( pos.x() > itemRect.right() )
{
// right side of an item
if ( mDelegate->hitRow()->rightItems().count() < 1 )
{
// between the last left item and the right side
if ( pos.x() > ( itemRect.right() + ( ( rowRect.right() - itemRect.right() ) / 2 ) ) )
{
// first/last right item
mItemInsertPosition = AsFirstRightItem;
mDropIndicatorPoint1 = rowRect.topRight();
mDropIndicatorPoint2 = rowRect.bottomRight();
}
return true;
}
// either there were some right items (so the theme delegate knows that the reported item is the closest)
// or there were no right items but the position is closest to the left item than the right row end
mItemInsertPosition = OnRightOfItem;
mDropIndicatorPoint1 = itemRect.topRight();
mDropIndicatorPoint2 = itemRect.bottomRight();
return true;
}
// left side of an item
if ( mDelegate->hitRow()->leftItems().count() < 1 )
{
// between the left side and the leftmost right item
if ( pos.x() < ( itemRect.left() - ( ( itemRect.left() - rowRect.left() ) / 2 ) ) )
{
mItemInsertPosition = AsFirstLeftItem;
mDropIndicatorPoint1 = rowRect.topLeft();
mDropIndicatorPoint2 = rowRect.bottomLeft();
return true;
}
}
mItemInsertPosition = OnLeftOfItem;
mDropIndicatorPoint1 = itemRect.topLeft();
mDropIndicatorPoint2 = itemRect.bottomLeft();
return true;
}
// exact hit
if ( pos.x() < ( itemRect.left() + ( itemRect.width() / 2 ) ) )
{
// left side
mItemInsertPosition = OnLeftOfItem;
mDropIndicatorPoint1 = itemRect.topLeft();
mDropIndicatorPoint2 = itemRect.bottomLeft();
return true;
}
// right side
mItemInsertPosition = OnRightOfItem;
mDropIndicatorPoint1 = itemRect.topRight();
mDropIndicatorPoint2 = itemRect.bottomRight();
return true;
}
void ThemePreviewWidget::mouseMoveEvent( QMouseEvent * e )
{
if ( ! ( mSelectedThemeContentItem && ( e->buttons() & Qt::LeftButton ) ) )
{
QTreeWidget::mouseMoveEvent( e );
return;
}
if ( mSelectedThemeContentItem != mDelegate->hitContentItem() )
{
QTreeWidget::mouseMoveEvent( e );
return; // ugh.. something weird happened
}
// starting a drag ?
QPoint diff = e->pos() - mMouseDownPoint;
if ( diff.manhattanLength() <= 4 )
{
QTreeWidget::mouseMoveEvent( e );
return; // ugh.. something weird happened
}
// startin a drag
QMimeData * data = new QMimeData();
QByteArray arry;
arry.resize( sizeof( Theme::ContentItem::Type ) );
*( ( Theme::ContentItem::Type * ) arry.data() ) = mSelectedThemeContentItem->type();
data->setData( gThemeContentItemTypeDndMimeDataFormat, arry );
QDrag * drag = new QDrag( this );
drag->setMimeData( data );
// remove the Theme::ContentItem from the Theme
if ( mDelegate->hitContentItemRight() )
const_cast< Theme::Row * >( mDelegate->hitRow() )->removeRightItem( mSelectedThemeContentItem );
else
const_cast< Theme::Row * >( mDelegate->hitRow() )->removeLeftItem( mSelectedThemeContentItem );
delete mSelectedThemeContentItem;
if ( mDelegate->hitRow()->rightItems().isEmpty() && mDelegate->hitRow()->leftItems().isEmpty() )
{
if ( mDelegate->hitItem()->type() == Item::Message )
{
if ( mDelegate->hitColumn()->messageRows().count() > 1 )
{
const_cast< Theme::Column * >( mDelegate->hitColumn() )->removeMessageRow( const_cast< Theme::Row * >( mDelegate->hitRow() ) );
delete mDelegate->hitRow();
}
} else {
if ( mDelegate->hitColumn()->groupHeaderRows().count() > 1 )
{
const_cast< Theme::Column * >( mDelegate->hitColumn() )->removeGroupHeaderRow( const_cast< Theme::Row * >( mDelegate->hitRow() ) );
delete mDelegate->hitRow();
}
}
}
mSelectedThemeContentItem = 0;
mThemeSelectedContentItemRect = QRect();
mDropIndicatorPoint1 = mDropIndicatorPoint2;
setTheme( mTheme ); // this will reset theme cache and trigger a global update
// and do drag
drag->exec( Qt::CopyAction, Qt::CopyAction );
}
void ThemePreviewWidget::mousePressEvent( QMouseEvent * e )
{
mMouseDownPoint = e->pos();
if ( mDelegate->hitTest( mMouseDownPoint ) )
{
mSelectedThemeContentItem = const_cast< Theme::ContentItem * >( mDelegate->hitContentItem() );
mThemeSelectedContentItemRect = mSelectedThemeContentItem ? mDelegate->hitContentItemRect() : QRect();
} else {
mSelectedThemeContentItem = 0;
mThemeSelectedContentItemRect = QRect();
}
QTreeWidget::mousePressEvent( e );
viewport()->update();
if ( e->button() == Qt::RightButton )
{
KMenu menu;
if ( mSelectedThemeContentItem )
{
menu.addTitle( Theme::ContentItem::description( mSelectedThemeContentItem->type() ) );
if ( mSelectedThemeContentItem->displaysText() )
{
QAction * act;
act = menu.addAction( i18nc( "@action:inmenu soften the text color", "Soften" ) );
act->setCheckable( true );
act->setChecked( mSelectedThemeContentItem->softenByBlending() );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( slotSoftenActionTriggered( bool ) ) );
KMenu * childmenu = new KMenu( &menu );
QActionGroup * grp = new QActionGroup( childmenu );
act = childmenu->addAction( i18nc("@action:inmenu Font setting", "Default") );
act->setData( QVariant( static_cast< int >( 0 ) ) );
act->setCheckable( true );
act->setChecked( !mSelectedThemeContentItem->useCustomFont() );
grp->addAction( act );
act = childmenu->addAction( i18nc("@action:inmenu Font setting", "Custom...") );
act->setData( QVariant( static_cast< int >( Theme::ContentItem::UseCustomFont ) ) );
act->setCheckable( true );
act->setChecked( mSelectedThemeContentItem->useCustomFont() );
grp->addAction( act );
// We would like the group to be exclusive, but then the "Custom..." action
// will not be triggered if activated multiple times in a row... well, we'll have to live with checkboxes instead of radios.
grp->setExclusive( false );
connect( childmenu, SIGNAL( triggered( QAction * ) ),
SLOT( slotFontMenuTriggered( QAction * ) ) );
menu.addMenu( childmenu )->setText( i18n( "Font" ) );
}
if ( mSelectedThemeContentItem->canUseCustomColor() )
{
KMenu * childmenu = new KMenu( &menu );
QActionGroup * grp = new QActionGroup( childmenu );
QAction * act;
act = childmenu->addAction( i18nc("@action:inmenu Foreground color setting", "Default") );
act->setData( QVariant( static_cast< int >( 0 ) ) );
act->setCheckable( true );
act->setChecked( !mSelectedThemeContentItem->useCustomColor() );
grp->addAction( act );
act = childmenu->addAction( i18nc("@action:inmenu Foreground color setting", "Custom...") );
act->setData( QVariant( static_cast< int >( Theme::ContentItem::UseCustomColor ) ) );
act->setCheckable( true );
act->setChecked( mSelectedThemeContentItem->useCustomColor() );
grp->addAction( act );
// We would like the group to be exclusive, but then the "Custom..." action
// will not be triggered if activated multiple times in a row... well, we'll have to live with checkboxes instead of radios.
grp->setExclusive( false );
connect( childmenu, SIGNAL( triggered( QAction * ) ),
SLOT( slotForegroundColorMenuTriggered( QAction * ) ) );
menu.addMenu( childmenu )->setText( i18n( "Foreground Color" ) );
}
if ( mSelectedThemeContentItem->canBeDisabled() )
{
KMenu * childmenu = new KMenu( &menu );
QActionGroup * grp = new QActionGroup( childmenu );
QAction * act;
act = childmenu->addAction( i18nc("Hide a mark if the mail does not have the attribute, e.g. Important mark on a non important mail", "Hide") );
act->setData( QVariant( static_cast< int >( Theme::ContentItem::HideWhenDisabled ) ) );
act->setCheckable( true );
act->setChecked( mSelectedThemeContentItem->hideWhenDisabled() );
grp->addAction( act );
act = childmenu->addAction( i18nc("Keep a empty space in the list if the mail does not have the attribute, e.g. Important mark on a non important mail", "Keep Empty Space") );
act->setData( QVariant( static_cast< int >( 0 ) ) );
act->setCheckable( true );
act->setChecked( ! ( mSelectedThemeContentItem->softenByBlendingWhenDisabled() || mSelectedThemeContentItem->hideWhenDisabled() ) );
grp->addAction( act );
act = childmenu->addAction( i18nc("Show the icon softened in the list if the mail does not have the attribute, e.g. Important mark on a non important mail", "Keep Softened Icon") );
act->setData( QVariant( static_cast< int >( Theme::ContentItem::SoftenByBlendingWhenDisabled ) ) );
act->setCheckable( true );
act->setChecked( mSelectedThemeContentItem->softenByBlendingWhenDisabled() );
grp->addAction( act );
connect( childmenu, SIGNAL( triggered( QAction * ) ),
SLOT( slotDisabledFlagsMenuTriggered( QAction * ) ) );
menu.addMenu( childmenu )->setText( i18n( "When Disabled" ) );
}
}
if ( mDelegate->hitItem() )
{
if ( mDelegate->hitItem()->type() == Item::GroupHeader )
{
menu.addTitle( i18n( "Group Header" ) );
// Background color (mode) submenu
KMenu * childmenu = new KMenu( &menu );
QActionGroup * grp = new QActionGroup( childmenu );
QAction * act;
act = childmenu->addAction( i18nc("@action:inmenu Group header background color setting", "None") );
act->setData( QVariant( static_cast< int >( Theme::Transparent ) ) );
act->setCheckable( true );
act->setChecked( mTheme->groupHeaderBackgroundMode() == Theme::Transparent );
grp->addAction( act );
act = childmenu->addAction( i18nc("@action:inmenu Group header background color setting", "Automatic") );
act->setData( QVariant( static_cast< int >( Theme::AutoColor ) ) );
act->setCheckable( true );
act->setChecked( mTheme->groupHeaderBackgroundMode() == Theme::AutoColor );
grp->addAction( act );
act = childmenu->addAction( i18nc("@action:inmenu Group header background color setting", "Custom...") );
act->setData( QVariant( static_cast< int >( Theme::CustomColor ) ) );
act->setCheckable( true );
act->setChecked( mTheme->groupHeaderBackgroundMode() == Theme::CustomColor );
grp->addAction( act );
// We would like the group to be exclusive, but then the "Custom..." action
// will not be triggered if activated multiple times in a row... well, we'll have to live with checkboxes instead of radios.
grp->setExclusive( false );
connect( childmenu, SIGNAL( triggered( QAction * ) ),
SLOT( slotGroupHeaderBackgroundModeMenuTriggered( QAction * ) ) );
menu.addMenu( childmenu )->setText( i18n( "Background Color" ) );
// Background style submenu
childmenu = new KMenu( &menu );
grp = new QActionGroup( childmenu );
QList< QPair< QString, int > > styles = Theme::enumerateGroupHeaderBackgroundStyles();
for ( QList< QPair< QString, int > >::ConstIterator it = styles.constBegin(); it != styles.constEnd(); ++it )
{
act = childmenu->addAction( ( *it ).first );
act->setData( QVariant( ( *it ).second ) );
act->setCheckable( true );
act->setChecked( mTheme->groupHeaderBackgroundStyle() == static_cast< Theme::GroupHeaderBackgroundStyle >( ( *it ).second ) );
grp->addAction( act );
}
connect( childmenu, SIGNAL( triggered( QAction * ) ),
SLOT( slotGroupHeaderBackgroundStyleMenuTriggered( QAction * ) ) );
act = menu.addMenu( childmenu );
act->setText( i18n( "Background Style" ) );
if ( mTheme->groupHeaderBackgroundMode() == Theme::Transparent )
act->setEnabled( false );
}
}
if ( menu.isEmpty() )
return;
menu.exec( viewport()->mapToGlobal( e->pos() ) );
}
}
void ThemePreviewWidget::slotDisabledFlagsMenuTriggered( QAction * act )
{
if ( !mSelectedThemeContentItem )
return;
bool ok;
int flags = act->data().toInt( &ok );
if ( !ok )
return;
mSelectedThemeContentItem->setHideWhenDisabled( flags == Theme::ContentItem::HideWhenDisabled );
mSelectedThemeContentItem->setSoftenByBlendingWhenDisabled( flags == Theme::ContentItem::SoftenByBlendingWhenDisabled );
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
void ThemePreviewWidget::slotForegroundColorMenuTriggered( QAction * act )
{
if ( !mSelectedThemeContentItem )
return;
bool ok;
int flag = act->data().toInt( &ok );
if ( !ok )
return;
if ( flag == 0 )
{
mSelectedThemeContentItem->setUseCustomColor( false );
setTheme( mTheme ); // this will reset theme cache and trigger a global update
return;
}
QColor clr;
int result = KColorDialog::getColor( clr, mSelectedThemeContentItem->customColor(), this );
if ( result != KColorDialog::Accepted )
return;
mSelectedThemeContentItem->setCustomColor( clr );
mSelectedThemeContentItem->setUseCustomColor( true );
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
void ThemePreviewWidget::slotSoftenActionTriggered( bool )
{
if ( !mSelectedThemeContentItem )
return;
mSelectedThemeContentItem->setSoftenByBlending( !mSelectedThemeContentItem->softenByBlending() );
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
void ThemePreviewWidget::slotFontMenuTriggered( QAction * act )
{
if ( !mSelectedThemeContentItem )
return;
bool ok;
int flag = act->data().toInt( &ok );
if ( !ok )
return;
if ( flag == 0 )
{
mSelectedThemeContentItem->setUseCustomFont( false );
setTheme( mTheme ); // this will reset theme cache and trigger a global update
return;
}
QFont f = mSelectedThemeContentItem->font();
if ( KFontDialog::getFont( f ) != KFontDialog::Accepted )
return;
mSelectedThemeContentItem->setFont( f );
mSelectedThemeContentItem->setUseCustomFont( true );
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
void ThemePreviewWidget::slotGroupHeaderBackgroundModeMenuTriggered( QAction * act )
{
bool ok;
Theme::GroupHeaderBackgroundMode mode = static_cast< Theme::GroupHeaderBackgroundMode >( act->data().toInt( &ok ) );
if ( !ok )
return;
switch( mode )
{
case Theme::Transparent:
mTheme->setGroupHeaderBackgroundMode( Theme::Transparent );
break;
case Theme::AutoColor:
mTheme->setGroupHeaderBackgroundMode( Theme::AutoColor );
break;
case Theme::CustomColor:
{
QColor clr;
int result = KColorDialog::getColor( clr, mTheme->groupHeaderBackgroundColor(), this );
if ( result != KColorDialog::Accepted )
return;
mTheme->setGroupHeaderBackgroundMode( Theme::CustomColor );
mTheme->setGroupHeaderBackgroundColor( clr );
}
break;
}
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
void ThemePreviewWidget::slotGroupHeaderBackgroundStyleMenuTriggered( QAction * act )
{
bool ok;
Theme::GroupHeaderBackgroundStyle mode = static_cast< Theme::GroupHeaderBackgroundStyle >( act->data().toInt( &ok ) );
if ( !ok )
return;
mTheme->setGroupHeaderBackgroundStyle( mode );
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
void ThemePreviewWidget::paintEvent( QPaintEvent * e )
{
QTreeWidget::paintEvent( e );
if (
mThemeSelectedContentItemRect.isValid() ||
( mDropIndicatorPoint1 != mDropIndicatorPoint2 )
)
{
QPainter painter( viewport() );
if ( mThemeSelectedContentItemRect.isValid() )
{
painter.setPen( QPen( Qt::black ) );
painter.drawRect( mThemeSelectedContentItemRect );
}
if ( mDropIndicatorPoint1 != mDropIndicatorPoint2 )
{
painter.setPen( QPen( Qt::black, 3 ) );
painter.drawLine( mDropIndicatorPoint1, mDropIndicatorPoint2 );
}
}
}
void ThemePreviewWidget::slotHeaderContextMenuRequested( const QPoint &pos )
{
QTreeWidgetItem * hitem = headerItem();
if ( !hitem )
return; // ooops
int col = header()->logicalIndexAt( pos );
if ( col < 0 )
return;
if ( col >= mTheme->columns().count() )
return;
mSelectedThemeColumn = mTheme->column( col );
if ( !mSelectedThemeColumn )
return;
KMenu menu;
menu.addTitle( mSelectedThemeColumn->label() );
QAction * act;
act = menu.addAction( i18n( "Column Properties" ) );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( slotColumnProperties() ) );
act = menu.addAction( i18n( "Add Column..." ) );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( slotAddColumn() ) );
act = menu.addAction( i18n( "Delete Column" ) );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( slotDeleteColumn() ) );
act->setEnabled( col > 0 );
menu.exec( header()->mapToGlobal( pos ) );
}
void ThemePreviewWidget::slotAddColumn()
{
int newColumnIndex = mTheme->columns().count();
if ( mSelectedThemeColumn )
{
newColumnIndex = mTheme->columns().indexOf( mSelectedThemeColumn );
if ( newColumnIndex < 0 )
newColumnIndex = mTheme->columns().count();
else
newColumnIndex++;
}
mSelectedThemeColumn = new Theme::Column();
mSelectedThemeColumn->setLabel( i18n( "New Column" ) );
mSelectedThemeColumn->setVisibleByDefault( true );
mSelectedThemeColumn->addMessageRow( new Theme::Row() );
mSelectedThemeColumn->addGroupHeaderRow( new Theme::Row() );
ThemeColumnPropertiesDialog * dlg = new ThemeColumnPropertiesDialog( this, mSelectedThemeColumn, i18n( "Add New Column" ) );
if ( dlg->exec() == QDialog::Accepted )
{
mTheme->insertColumn( newColumnIndex, mSelectedThemeColumn );
mSelectedThemeContentItem = 0;
mThemeSelectedContentItemRect = QRect();
mDropIndicatorPoint1 = mDropIndicatorPoint2;
setTheme( mTheme ); // this will reset theme cache and trigger a global update
} else {
delete mSelectedThemeColumn;
mSelectedThemeColumn = 0;
}
delete dlg;
}
void ThemePreviewWidget::slotColumnProperties()
{
if ( !mSelectedThemeColumn )
return;
ThemeColumnPropertiesDialog * dlg = new ThemeColumnPropertiesDialog( this, mSelectedThemeColumn, i18n( "Column Properties" ) );
if ( dlg->exec() == QDialog::Accepted )
{
mSelectedThemeContentItem = 0;
mThemeSelectedContentItemRect = QRect();
mDropIndicatorPoint1 = mDropIndicatorPoint2;
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
delete dlg;
}
void ThemePreviewWidget::slotDeleteColumn()
{
if ( !mSelectedThemeColumn )
return;
int idx = mTheme->columns().indexOf( mSelectedThemeColumn );
if ( idx < 1 ) // first column can't be deleted
return;
mTheme->removeColumn( mSelectedThemeColumn );
delete mSelectedThemeColumn;
mSelectedThemeColumn = 0;
mSelectedThemeContentItem = 0;
mThemeSelectedContentItemRect = QRect();
mDropIndicatorPoint1 = mDropIndicatorPoint2;
setTheme( mTheme ); // this will reset theme cache and trigger a global update
}
ThemeEditor::ThemeEditor( QWidget *parent )
: OptionSetEditor( parent )
{
mCurrentTheme = 0;
// Appearance tab
QWidget * tab = new QWidget( this );
addTab( tab, i18n( "Appearance" ) );
QGridLayout * tabg = new QGridLayout( tab );
QGroupBox * gb = new QGroupBox( i18n( "Content Items" ), tab );
tabg->addWidget( gb, 0, 0 );
QGridLayout * gblayout = new QGridLayout( gb );
ThemeContentItemSourceLabel * cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::Subject );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 0 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::Date );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 1, 0 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::Size );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 2, 0 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::Sender );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 1 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::Receiver );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 1, 1 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::SenderOrReceiver );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 2, 1 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::MostRecentDate );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 2 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::TagList );
cil->setText( Theme::ContentItem::description( cil->type() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 1, 2 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::CombinedReadRepliedStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageRepliedAndForwarded() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 3 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::ReadStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageNew() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 1, 3 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::RepliedStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageReplied() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 2, 3 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::AttachmentStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageAttachment() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 4 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::EncryptionStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageFullyEncrypted() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 1, 4 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::SignatureStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageFullySigned() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 2, 4 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::ActionItemStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageActionItem() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 5 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::ImportantStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageImportant() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 6 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::SpamHamStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageSpam() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 1, 6 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::WatchedIgnoredStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapMessageWatched() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 2, 6 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::ExpandedStateIcon );
cil->setPixmap( *( Manager::instance()->pixmapShowMore() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 0, 7 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::VerticalLine );
cil->setPixmap( *( Manager::instance()->pixmapVerticalLine() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 1, 7 );
cil = new ThemeContentItemSourceLabel( gb, Theme::ContentItem::HorizontalSpacer );
cil->setPixmap( *( Manager::instance()->pixmapHorizontalSpacer() ) );
cil->setToolTip( Theme::ContentItem::description( cil->type() ) );
gblayout->addWidget( cil, 2, 7 );
mPreviewWidget = new ThemePreviewWidget( tab );
tabg->addWidget( mPreviewWidget, 1, 0 );
QLabel * l = new QLabel( tab );
l->setText( i18n( "Right click on the header to add or modify columns. Drag the content items and drop them on the columns in order to compose your theme. Right click on the items inside the view for more options." ) );
l->setWordWrap( true );
l->setAlignment( Qt::AlignCenter );
tabg->addWidget( l, 2, 0 );
tabg->setRowStretch( 1, 1 );
// Advanced tab
tab = new QWidget( this );
addTab( tab, i18nc( "@title:tab Advanced theme settings", "Advanced" ) );
tabg = new QGridLayout( tab );
l = new QLabel( i18n( "Header:" ), tab );
tabg->addWidget( l, 0, 0 );
mViewHeaderPolicyCombo = new KComboBox( tab );
tabg->addWidget( mViewHeaderPolicyCombo, 0, 1 );
l = new QLabel( i18n( "Icon size:" ), tab );
tabg->addWidget( l, 1, 0 );
mIconSizeSpinBox = new QSpinBox( tab );
mIconSizeSpinBox->setMinimum( 8 );
mIconSizeSpinBox->setMaximum( 64 );
mIconSizeSpinBox->setSuffix( ' ' + i18nc( "suffix in a spinbox", "Pixels" ) );
QObject::connect(
mIconSizeSpinBox, SIGNAL( valueChanged( int ) ),
this, SLOT( slotIconSizeSpinBoxValueChanged( int ) )
);
tabg->addWidget( mIconSizeSpinBox, 1, 1 );
tabg->setColumnStretch( 1, 1 );
tabg->setRowStretch( 2, 1 );
}
ThemeEditor::~ThemeEditor()
{
}
void ThemeEditor::editTheme( Theme *set )
{
mCurrentTheme = set;
if ( !mCurrentTheme )
{
setEnabled( false );
return;
}
setEnabled( true );
nameEdit()->setText( set->name() );
descriptionEdit()->setPlainText( set->description() );
mPreviewWidget->setTheme( set );
fillViewHeaderPolicyCombo();
ComboBoxUtils::setIntegerOptionComboValue( mViewHeaderPolicyCombo, (int)mCurrentTheme->viewHeaderPolicy() );
mIconSizeSpinBox->setValue( set->iconSize() );
}
void ThemeEditor::commit()
{
if ( !mCurrentTheme )
return;
mCurrentTheme->setName( nameEdit()->text() );
mCurrentTheme->setDescription( descriptionEdit()->toPlainText() );
mCurrentTheme->setViewHeaderPolicy(
(Theme::ViewHeaderPolicy)ComboBoxUtils::getIntegerOptionComboValue( mViewHeaderPolicyCombo, 0 )
);
mCurrentTheme->setIconSize( mIconSizeSpinBox->value() );
// other settings are already committed to this theme
}
void ThemeEditor::fillViewHeaderPolicyCombo()
{
ComboBoxUtils::fillIntegerOptionCombo(
mViewHeaderPolicyCombo,
Theme::enumerateViewHeaderPolicyOptions()
);
}
void ThemeEditor::slotNameEditTextEdited( const QString &newName )
{
if( !mCurrentTheme )
return;
mCurrentTheme->setName( newName );
emit themeNameChanged();
}
void ThemeEditor::slotIconSizeSpinBoxValueChanged( int val )
{
if( !mCurrentTheme )
return;
mCurrentTheme->setIconSize( val );
mPreviewWidget->setTheme( mCurrentTheme ); // will trigger a cache reset and a view update
}
} // namespace Core
} // namespace MessageListView
} // namespace KMail