french -> French
[kdepim.git] / knode / foldertreewidget.cpp
blob4fc9ae27546798b49541349eec75f9ee4c25c15c
1 /******************************************************************************
3 * This file is part of libkdepim.
5 * Copyright (C) 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
22 *****************************************************************************/
24 #include "foldertreewidget.h"
26 #include <kio/global.h> // for KIO::filesize_t and related functions
27 #include <klineedit.h>
28 #include <klocale.h>
30 #include <QHeaderView>
31 #include <QDropEvent>
32 #include <QMimeData>
33 #include <QStyledItemDelegate>
34 #include <QStyle>
35 #include <QPainter>
36 #include <QFontMetrics>
37 #include <QApplication>
39 #define FOLDERTREEWIDGETITEM_TYPE ( QTreeWidgetItem::UserType + 0xcafe )
42 namespace KPIM {
44 #define ITEM_LABEL_RIGHT_MARGIN 2
45 #define ITEM_LABEL_TO_UNREADCOUNT_SPACING 2
47 class FolderTreeWidgetItemLabelColumnDelegate : public QStyledItemDelegate
49 protected:
50 FolderTreeWidget *mFolderTreeWidget;
52 public:
53 FolderTreeWidgetItemLabelColumnDelegate( FolderTreeWidget *parent )
54 : QStyledItemDelegate( parent ), mFolderTreeWidget( parent ) {}
56 ~FolderTreeWidgetItemLabelColumnDelegate()
57 {};
59 virtual QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
61 QStyleOptionViewItemV4 opt = option;
62 initStyleOption( &opt, index );
64 opt.text = "X"; // fake a text (so the height is computed correctly)
65 opt.features |= QStyleOptionViewItemV2::HasDisplay;
67 return mFolderTreeWidget->style()->sizeFromContents( QStyle::CT_ItemViewItem, &opt, QSize(), mFolderTreeWidget );
70 virtual void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
73 // The equilibrism in this function attempts to rely on QStyle for most of the job.
75 QStyleOptionViewItemV4 opt = option;
76 initStyleOption( &opt, index );
78 opt.text = ""; // draw no text for me, please.. I'll do it in a while
80 QStyle * style = mFolderTreeWidget->style();
82 style->drawControl( QStyle::CE_ItemViewItem, &opt, painter, mFolderTreeWidget );
84 // FIXME: this is plain ugly :D
85 // OTOH seaching the item by model index is extremely expensive when compared to a cast.
86 // We're also using static_cast<> instead of dynamic_cast<> to avoid performance loss:
87 // we actually trust the developer to add only real FolderTreeWidgetItems to this widget.
88 FolderTreeWidgetItem * item = static_cast<FolderTreeWidgetItem *>( index.internalPointer() );
89 if ( !item )
90 return; // ugh :/
92 // draw the labelText().
93 QRect textRect = style->subElementRect( QStyle::SE_ItemViewItemText, &opt, mFolderTreeWidget );
94 textRect.setWidth( textRect.width() - ITEM_LABEL_RIGHT_MARGIN );
96 // keeping indipendent state variables is faster than saving and restoring the whole painter state
97 QPen oldPen = painter->pen();
98 QFont oldFont = painter->font();
100 if ( item->isCloseToQuota() && ( !( opt.state & QStyle::State_Selected ) ) )
102 painter->setPen( QPen( mFolderTreeWidget->closeToQuotaWarningColor(), 0 ) );
103 } else {
104 QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
106 if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active))
107 cg = QPalette::Inactive;
109 QPalette::ColorRole cr = ( opt.state & QStyle::State_Selected ) ? QPalette::HighlightedText : QPalette::Text;
110 painter->setPen( QPen( opt.palette.brush( cg, cr ), 0 ) );
113 int unread = item->unreadCount();
114 int childUnread = item->childrenUnreadCount();
115 bool displayChildUnread = ( childUnread > 0 ) && ( !item->isExpanded() );
117 if ( ( unread > 0 ) || displayChildUnread )
119 // font is bold for sure (unread stuff in folder)
120 QFont f = opt.font;
121 f.setBold( true );
122 painter->setFont( f );
124 QFontMetrics fm( painter->font() ); // use the bold font metrics
126 if (
127 ( mFolderTreeWidget->unreadColumnIndex() == -1 ) ||
128 mFolderTreeWidget->isColumnHidden( mFolderTreeWidget->unreadColumnIndex() )
131 // we need to paint the unread count too
132 QString unreadText;
134 if ( unread > 0 )
136 if ( displayChildUnread )
137 unreadText = QString("(%1 + %2)").arg( unread ).arg( childUnread );
138 else
139 unreadText = QString("(%1)").arg( unread );
140 } else
141 unreadText = QString("(0 + %1)").arg( childUnread );
143 // compute the potential width so we know if elision has to be performed
145 int unreadWidth = fm.width( unreadText );
146 int labelWidth = fm.width( item->labelText() );
147 int maxWidth = labelWidth + ITEM_LABEL_TO_UNREADCOUNT_SPACING + unreadWidth;
149 QString label;
150 if ( maxWidth > textRect.width() )
152 // must elide
153 label = item->elidedLabelText( fm, textRect.width() - ( ITEM_LABEL_TO_UNREADCOUNT_SPACING + unreadWidth ) );
154 labelWidth = fm.width( label );
156 // the condition inside this call is an optimization (it's faster than simply label != item->labelText())
157 item->setLabelTextElided( ( label.length() != item->labelText().length() ) || ( label != item->labelText() ) );
158 } else {
159 label = item->labelText();
160 // no elision needed
161 item->setLabelTextElided( false );
164 painter->drawText( textRect, Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, label );
166 if ( QApplication::isLeftToRight() )
167 textRect.setLeft( textRect.left() + labelWidth + ITEM_LABEL_TO_UNREADCOUNT_SPACING );
168 else
169 textRect.setRight( textRect.right() - labelWidth - ITEM_LABEL_TO_UNREADCOUNT_SPACING );
171 if ( !( opt.state & QStyle::State_Selected ) ) {
172 painter->setPen( QPen( mFolderTreeWidget->unreadCountColor(), 0 ) );
174 painter->drawText( textRect, Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, unreadText );
175 } else {
176 // got unread messages: bold font but no special text tricks
177 QString label = item->elidedLabelText( fm, textRect.width() );
179 // the condition inside this call is an optimization (it's faster than simply label != item->labelText())
180 item->setLabelTextElided( ( label.length() != item->labelText().length() ) || ( label != item->labelText() ) );
182 painter->drawText( textRect, Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, label );
184 } else {
185 // no unread messages: normal font, no text tricks
186 QString label = item->elidedLabelText( opt.fontMetrics, textRect.width() );
188 // the condition inside this call is an optimization (it's faster than simply label != item->labelText())
189 item->setLabelTextElided( ( label.length() != item->labelText().length() ) || ( label != item->labelText() ) );
191 painter->setFont( opt.font );
192 painter->drawText( textRect, Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, label );
195 painter->setPen( oldPen );
196 painter->setFont( oldFont );
199 virtual void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
201 QStyledItemDelegate::setModelData( editor, model, index );
202 FolderTreeWidgetItem *item = static_cast<FolderTreeWidgetItem *>( index.internalPointer() );
203 if ( item ) {
204 // test if old text != new text
205 if( item->labelText() != item->text( mFolderTreeWidget->labelColumnIndex() ) ) {
206 item->setLabelText( item->text( mFolderTreeWidget->labelColumnIndex() ) );
207 mFolderTreeWidget->emitRenamed( item );
216 FolderTreeWidget::FolderTreeWidget( QWidget *parent , const char *name )
217 : KPIM::TreeWidget( parent , name ),
218 mUnreadCountColor( Qt::blue ), mCloseToQuotaWarningColor( Qt::red )
220 mLabelColumnIndex = -1;
221 mUnreadColumnIndex = -1;
222 mTotalColumnIndex = -1;
223 mDataSizeColumnIndex = -1;
225 setAlternatingRowColors( true );
226 setAcceptDrops( true );
227 setAllColumnsShowFocus( true );
228 setRootIsDecorated( true );
229 setSortingEnabled( true );
230 setUniformRowHeights( true ); // optimize please
231 //setAnimated( true ); // this slows down everything a lot
233 header()->setSortIndicatorShown( true );
234 header()->setClickable( true );
235 //setSelectionMode( Extended );
237 connect( this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
238 SLOT(updateExpandedState(QTreeWidgetItem*)) );
239 connect( this, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
240 SLOT(updateExpandedState(QTreeWidgetItem*)) );
244 int FolderTreeWidget::addLabelColumn( const QString &headerLabel )
246 if ( mLabelColumnIndex == -1 )
248 mLabelColumnIndex = addColumn( headerLabel );
249 setItemDelegateForColumn( mLabelColumnIndex, new FolderTreeWidgetItemLabelColumnDelegate( this ) );
251 return mLabelColumnIndex;
254 bool FolderTreeWidget::labelColumnVisible() const
256 if ( mLabelColumnIndex == -1 )
257 return false;
258 return !isColumnHidden( mLabelColumnIndex );
261 int FolderTreeWidget::addTotalColumn( const QString &headerLabel )
263 if ( mTotalColumnIndex == -1 )
264 mTotalColumnIndex = addColumn( headerLabel , Qt::AlignRight );
265 return mTotalColumnIndex;
268 bool FolderTreeWidget::totalColumnVisible() const
270 if ( mTotalColumnIndex == -1 )
271 return false;
272 return !isColumnHidden( mTotalColumnIndex );
275 int FolderTreeWidget::addUnreadColumn( const QString &headerLabel )
277 if ( mUnreadColumnIndex == -1 )
278 mUnreadColumnIndex = addColumn( headerLabel , Qt::AlignRight );
279 return mUnreadColumnIndex;
282 bool FolderTreeWidget::unreadColumnVisible() const
284 if ( mUnreadColumnIndex == -1 )
285 return false;
286 return !isColumnHidden( mUnreadColumnIndex );
289 int FolderTreeWidget::addDataSizeColumn( const QString &headerLabel )
291 if ( mDataSizeColumnIndex == -1 )
292 mDataSizeColumnIndex = addColumn( headerLabel , Qt::AlignRight );
293 return mDataSizeColumnIndex;
296 bool FolderTreeWidget::dataSizeColumnVisible() const
298 if ( mDataSizeColumnIndex == -1 )
299 return false;
300 return !isColumnHidden( mDataSizeColumnIndex );
303 void FolderTreeWidget::updateColumnForItem( FolderTreeWidgetItem * item, int columnIndex )
305 update( indexFromItem( item, columnIndex ) );
308 void FolderTreeWidget::updateExpandedState( QTreeWidgetItem *item )
310 FolderTreeWidgetItem *folderItem = dynamic_cast<FolderTreeWidgetItem*>( item );
311 Q_ASSERT( folderItem );
312 if ( folderItem )
313 folderItem->updateExpandedState();
316 void FolderTreeWidget::setCloseToQuotaWarningColor( const QColor &clr )
318 mCloseToQuotaWarningColor = clr;
319 update();
322 void FolderTreeWidget::setUnreadCountColor( const QColor &clr )
324 mUnreadCountColor = clr;
325 update();
328 void FolderTreeWidget::emitRenamed( QTreeWidgetItem *item )
330 emit renamed( item );
334 FolderTreeWidgetItem::FolderTreeWidgetItem(
335 FolderTreeWidget *parent,
336 const QString &label,
337 Protocol protocol,
338 FolderType folderType
339 ) : QTreeWidgetItem( parent, FOLDERTREEWIDGETITEM_TYPE ),
340 mProtocol( protocol ), mFolderType( folderType ), mLabelText( label ),
341 mTotalCount( 0 ), mUnreadCount( 0 ), mDataSize( -1 ), mIsCloseToQuota( 0 ),
342 mLabelTextElided( 0 ), mChildrenTotalCount( 0 ), mChildrenUnreadCount( 0 ),
343 mChildrenDataSize( -1 ), mAlwaysDisplayCounts( false )
345 setText( parent->labelColumnIndex(), label );
348 FolderTreeWidgetItem::FolderTreeWidgetItem(
349 FolderTreeWidgetItem *parent,
350 const QString &label,
351 Protocol protocol,
352 FolderType folderType
353 ) : QTreeWidgetItem( parent, FOLDERTREEWIDGETITEM_TYPE ),
354 mProtocol( protocol ), mFolderType( folderType ), mLabelText( label ),
355 mTotalCount( 0 ), mUnreadCount( 0 ), mDataSize( -1 ), mIsCloseToQuota( 0 ),
356 mLabelTextElided( 0 ), mChildrenTotalCount( 0 ), mChildrenUnreadCount( 0 ),
357 mChildrenDataSize( -1 ), mAlwaysDisplayCounts( false )
359 FolderTreeWidget * tree = dynamic_cast< FolderTreeWidget * >( treeWidget() );
360 if ( tree )
361 setText( tree->labelColumnIndex(), label );
364 QString FolderTreeWidgetItem::protocolDescription() const
366 switch( mProtocol )
368 case Local:
369 return i18n( "Local" );
370 break;
371 case Imap:
372 return i18n( "IMAP" );
373 break;
374 case CachedImap:
375 return i18n( "Cached IMAP" );
376 break;
377 case News:
378 return i18n( "News" );
379 break;
380 case Search:
381 return i18n( "Search" );
382 break;
383 case NONE:
384 return i18n( "None" );
385 break;
386 default:
387 break;
390 return i18n( "Unknown" );
393 bool FolderTreeWidgetItem::updateChildrenCounts()
395 int cc = childCount();
397 if ( cc < 1 )
398 return false;
400 int idx = 0;
403 int oldTotal = mChildrenTotalCount;
404 int oldUnread = mChildrenUnreadCount;
405 qint64 oldSize = mChildrenDataSize;
407 mChildrenTotalCount = 0;
408 mChildrenUnreadCount = 0;
409 mChildrenDataSize = 0;
411 bool gotValidDataSize = false;
413 while ( idx < cc )
415 FolderTreeWidgetItem *it = static_cast< FolderTreeWidgetItem * >( QTreeWidgetItem::child( idx ) );
416 mChildrenTotalCount += it->totalCount() + it->childrenTotalCount();
417 mChildrenUnreadCount += it->unreadCount() + it->childrenUnreadCount();
418 if ( it->dataSize() >= 0)
420 gotValidDataSize = true;
421 mChildrenDataSize += it->dataSize();
423 if ( it->childrenDataSize() >= 0 )
425 gotValidDataSize = true;
426 mChildrenDataSize += it->childrenDataSize();
428 idx++;
431 if ( !gotValidDataSize )
432 mChildrenDataSize = -1; // keep it invald
434 return ( oldTotal != mChildrenTotalCount ) ||
435 ( oldUnread != mChildrenUnreadCount ) ||
436 ( oldSize != mChildrenDataSize );
439 void FolderTreeWidgetItem::setLabelText( const QString &label )
441 mLabelText = label;
442 // We set the text of the item so QStyle will compute the height correctly
443 FolderTreeWidget * tree = dynamic_cast< FolderTreeWidget * >( treeWidget() );
444 Q_ASSERT ( tree );
445 int idx = tree->labelColumnIndex();
446 if ( idx >= 0 )
448 setText( idx, label );
449 setTextAlignment( idx, Qt::AlignRight );
453 void FolderTreeWidgetItem::setUnreadCount( int unreadCount )
455 mUnreadCount = unreadCount;
456 int unreadCountToDisplay = unreadCount;
458 if ( mChildrenUnreadCount > 0 && !isExpanded() )
459 unreadCountToDisplay += mChildrenUnreadCount;
461 FolderTreeWidget * tree = dynamic_cast< FolderTreeWidget * >( treeWidget() );
462 if ( tree )
464 int idx = tree->unreadColumnIndex();
465 if ( idx >= 0 )
467 if ( ( parent() || mAlwaysDisplayCounts || !isExpanded() ) && unreadCountToDisplay > 0 )
468 setText( idx, QString::number( unreadCountToDisplay ) );
469 else
470 setText( idx, QString() );
472 setTextAlignment( idx, Qt::AlignRight );
476 void FolderTreeWidgetItem::setTotalCount( int totalCount )
478 mTotalCount = totalCount;
479 int totalCountToDisplay = totalCount;
481 if ( mChildrenTotalCount > 0 && !isExpanded() )
482 totalCountToDisplay += mChildrenTotalCount;
484 FolderTreeWidget * tree = dynamic_cast< FolderTreeWidget * >( treeWidget() );
485 if( tree ) {
486 int idx = tree->totalColumnIndex();
487 if ( idx >= 0 )
489 // FIXME: Why the "parent()" logic is hardwired here ?
490 if ( parent() || mAlwaysDisplayCounts || !isExpanded() )
491 setText( idx, QString::number( totalCountToDisplay ) );
492 else
493 setText( idx, QString() );
494 setTextAlignment( idx, Qt::AlignRight );
499 void FolderTreeWidgetItem::setDataSize( qint64 dataSize )
501 mDataSize = dataSize;
503 QString txt;
504 QString sizeText = KIO::convertSize( mDataSize >= 0 ? (KIO::filesize_t)mDataSize : (KIO::filesize_t)0 );
505 QString childSizeText = KIO::convertSize( (KIO::filesize_t)mChildrenDataSize );
507 // A top level item, they all have size 0
508 // FIXME: Why this logic is hardwired here ?
509 if ( !parent() && !mAlwaysDisplayCounts ) {
510 if ( mChildrenDataSize >= 0 && !isExpanded() )
511 txt = childSizeText;
512 else
513 txt = QString();
516 // Not a top level item (or always displays counts)
517 else if ( ( mDataSize >= 0 ) || ( mChildrenDataSize >= 0 ) ) {
518 txt = sizeText;
519 if ( !isExpanded() ) {
520 if ( mChildrenDataSize >= 0 )
521 txt += " + " + childSizeText;
524 else
525 txt = "-";
527 FolderTreeWidget * tree = dynamic_cast< FolderTreeWidget * >( treeWidget() );
528 if( tree ) {
529 int idx = tree->dataSizeColumnIndex();
530 if ( idx >= 0 )
532 setText( idx, txt );
533 setTextAlignment( idx, Qt::AlignRight );
538 void FolderTreeWidgetItem::updateExpandedState()
540 // Just trigger an update of the column text, it will all be handled thaere.
541 setDataSize( mDataSize );
542 setTotalCount( mTotalCount );
543 setUnreadCount( mUnreadCount );
546 void FolderTreeWidgetItem::updateColumn( int columnIndex )
548 FolderTreeWidget * tree = dynamic_cast< FolderTreeWidget * >( treeWidget() );
549 if ( tree ) {
550 tree->updateColumnForItem( this, columnIndex );
551 if ( columnIndex!=tree->labelColumnIndex() && !tree->unreadColumnVisible() && tree->labelColumnVisible() ) {
552 updateColumn( tree->labelColumnIndex() );
557 void FolderTreeWidgetItem::setIsCloseToQuota( bool closeToQuota )
559 if ( ( mIsCloseToQuota == 1 ) == closeToQuota )
560 return;
561 mIsCloseToQuota = closeToQuota ? 1 : 0;
563 FolderTreeWidget * tree = dynamic_cast< FolderTreeWidget * >( treeWidget() );
564 if ( tree && tree->labelColumnVisible() )
565 updateColumn( tree->labelColumnIndex() );
569 bool FolderTreeWidgetItem::operator < ( const QTreeWidgetItem &other ) const
571 // FIXME: Sort by children counts too ?
573 int sortCol = treeWidget()->sortColumn();
574 if ( sortCol < 0 )
575 return true; // just "yes" :D
577 FolderTreeWidget * w = dynamic_cast< FolderTreeWidget * >( treeWidget() );
578 if ( w )
580 // sanity check
581 if ( dynamic_cast<const FolderTreeWidgetItem*>( &other ) )
583 const FolderTreeWidgetItem * oitem = dynamic_cast< const FolderTreeWidgetItem * >( &other );
584 if ( oitem )
586 if ( sortCol == w->unreadColumnIndex() )
587 return mUnreadCount < oitem->unreadCount();
588 if ( sortCol == w->totalColumnIndex() )
589 return mTotalCount < oitem->totalCount();
590 if ( sortCol == w->dataSizeColumnIndex() )
591 return mDataSize < oitem->dataSize();
592 if ( sortCol == w->labelColumnIndex() )
594 // Special sorting based on the item type
595 int thisProto = ( int ) mProtocol;
596 int thatProto = ( int ) oitem->protocol();
597 if ( thisProto < thatProto )
598 return true;
599 if ( thisProto > thatProto )
600 return false;
602 int thisType = ( int ) mFolderType;
603 int thatType = ( int ) oitem->folderType();
604 if ( thisType < thatType )
605 return true;
606 if ( thisType > thatType )
607 return false;
609 // and finally compare by name
610 return mLabelText.toLower() < oitem->labelText().toLower();
616 return text(sortCol) < other.text(sortCol);
619 QString FolderTreeWidgetItem::elidedLabelText( const QFontMetrics &metrics, unsigned int width ) const
621 return metrics.elidedText( labelText(), Qt::ElideRight, width );