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.
3043 lines
87 KiB
3043 lines
87 KiB
/****************************************************************************** |
|
* |
|
* Copyright (c) 2007 Volker Krause <vkrause@kde.org> |
|
* Copyright (c) 2008 Szymon Tomasz Stefanek <pragma@kvirc.net> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software 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 Software |
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
* |
|
*****************************************************************************/ |
|
|
|
#include "folderview.h" |
|
|
|
#include <libkdepim/maillistdrag.h> // KPIM::MailList |
|
#include <libkdepim/broadcaststatus.h> // KPIM::BroadcastStatus |
|
|
|
#include "kmaccount.h" |
|
#include "kmfolder.h" |
|
#include "kmfolderdir.h" |
|
#include "kmfoldermgr.h" |
|
#include "kmfoldercachedimap.h" |
|
#include "kmkernel.h" |
|
#include "kmmainwidget.h" |
|
#include "kmailicalifaceimpl.h" |
|
|
|
#include "accountmanager.h" |
|
#include "acljobs.h" |
|
#include "expirypropertiesdialog.h" |
|
#include "favoritefolderview.h" // ugly, derived class: should be moved from here :/ |
|
#include "mainfolderview.h" // ugly, derived class: should be moved from here :/ |
|
#include "foldershortcutdialog.h" |
|
#include "folderstorage.h" |
|
#include "messagecopyhelper.h" |
|
#include "newfolderdialog.h" |
|
#include "util.h" |
|
|
|
#include <klocale.h> |
|
#include <kdebug.h> |
|
#include <kicon.h> |
|
#include <kconfiggroup.h> |
|
#include <kglobalsettings.h> |
|
#include <kmenu.h> |
|
#include <kmessagebox.h> |
|
#include <kapplication.h> |
|
#include <kcolorscheme.h> |
|
#include <kconfig.h> |
|
#include <kiconloader.h> |
|
|
|
#include <QActionGroup> |
|
#include <QContextMenuEvent> |
|
#include <QDrag> |
|
#include <QDragEnterEvent> |
|
#include <QDragMoveEvent> |
|
#include <QDropEvent> |
|
#include <QFontMetrics> |
|
#include <QHash> |
|
#include <QHeaderView> |
|
#include <QHelpEvent> |
|
#include <QMimeData> |
|
#include <QMouseEvent> |
|
#include <QPainter> |
|
#include <QScrollBar> |
|
#include <QTime> |
|
#include <QTimer> |
|
#include <QToolTip> |
|
#include <QVariant> |
|
|
|
// TODO: Use QDockWidget for this ? |
|
|
|
// TODO: Option to show / hide inactive folders "on-the-fly". That is, hide folders |
|
// which don't have unread messages, aren't close to quota etc... |
|
|
|
// Timeout after that we execute a delayed slotUpdateCounts() (milliseconds) |
|
static const int gLazyUpdateCountsTimeout = 500; |
|
// The maximum time that we can spend "at once" inside updateCounts() (milliseconds) |
|
static const int gUpdateCountsProcessingInterval = 200; |
|
// The time in msecs an user must wait over a parent item to get it automatically expanded (dnd) |
|
static const int gDNDAutoExpandDelay = 500; |
|
|
|
|
|
namespace KMail |
|
{ |
|
|
|
|
|
|
|
// The one and only dragged folder list |
|
static QList< QPointer< KMFolder > > gDraggedFolderList; |
|
|
|
QString DraggedFolderList::mimeDataType() |
|
{ |
|
return QString( "application/x-kmail-folder-list" ); |
|
} |
|
|
|
void DraggedFolderList::set( QList< QPointer< KMFolder > > &list ) |
|
{ |
|
gDraggedFolderList = list; |
|
} |
|
|
|
void DraggedFolderList::clear() |
|
{ |
|
gDraggedFolderList.clear(); |
|
} |
|
|
|
QList< QPointer< KMFolder > > DraggedFolderList::get() |
|
{ |
|
return gDraggedFolderList; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
FolderViewManager::FolderViewManager( KMMainWidget *, const char *name ) |
|
: QObject( 0 ) |
|
{ |
|
setObjectName( name ); |
|
} |
|
|
|
void FolderViewManager::attachView( FolderView *view ) |
|
{ |
|
mFolderViews.removeAll( view ); // make sure we don't store it twice (should never happen tough) |
|
mFolderViews.append( view ); |
|
} |
|
|
|
void FolderViewManager::detachView( FolderView *view ) |
|
{ |
|
mFolderViews.removeAll( view ); |
|
} |
|
|
|
void FolderViewManager::viewFolderActivated( FolderView *view, KMFolder *fld, bool middleButton ) |
|
{ |
|
for ( QList<FolderView *>::Iterator it = mFolderViews.begin(); it != mFolderViews.end(); ++it ) |
|
{ |
|
if ( ( *it ) != view ) |
|
( *it )->notifyFolderActivated( fld ); |
|
} |
|
|
|
emit folderActivated( fld ); |
|
emit folderActivated( fld, middleButton ); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
FolderView::FolderView( KMMainWidget *mainWidget, FolderViewManager *manager, QWidget *parent, const QString &configPrefix, const char *name ) |
|
: KPIM::FolderTreeWidget( parent, name ), |
|
mMainWidget( mainWidget ), |
|
mManager( manager ), |
|
mConfigPrefix( configPrefix ), |
|
mToolTipDisplayPolicy( DisplayAlways ), |
|
mSortingPolicy( SortByCurrentColumn ), |
|
mDnDMoveTargetItem( 0 ), |
|
mDnDSortReferenceItem( 0 ) |
|
{ |
|
mManager->attachView( this ); |
|
|
|
mUpdateCountsTimer = new QTimer( this ); |
|
|
|
connect( mUpdateCountsTimer, SIGNAL( timeout() ), |
|
SLOT( slotUpdateCounts() ) ); |
|
|
|
setDragEnabled( true ); |
|
|
|
// Disable auto-expanding, because it will actually do auto-collapsing, too, |
|
// which makes manually reordering folders unusable. |
|
// Trolltech issue N220934 |
|
//setAutoExpandDelay( gDNDAutoExpandDelay ); |
|
setAutoExpandDelay( -1 ); |
|
|
|
setSelectionMode( ExtendedSelection ); |
|
|
|
setDropIndicatorShown( false ); |
|
|
|
header()->setMinimumSectionSize( 30 ); // empiric :) |
|
header()->setDefaultSectionSize( 30 ); // empiric :) |
|
|
|
setIconSize( QSize( 22, 22 ) ); |
|
|
|
addLabelColumn( i18n( "Folder" ) ); |
|
addUnreadColumn( i18nc( "@title:column Column showing the number of unread email messages.", |
|
"Unread" ) ); |
|
addTotalColumn( i18nc( "@title:column Column showing the total number of messages", |
|
"Total" ) ); |
|
addDataSizeColumn( i18nc( "@title:column Size of the folder.", "Size" ) ); |
|
|
|
header()->setResizeMode( LabelColumn, QHeaderView::Stretch ); |
|
header()->setResizeMode( UnreadColumn, QHeaderView::ResizeToContents ); |
|
header()->setResizeMode( TotalColumn, QHeaderView::ResizeToContents ); |
|
header()->setResizeMode( DataSizeColumn, QHeaderView::ResizeToContents ); |
|
|
|
connect( this, SIGNAL( columnVisibilityChanged( int ) ), |
|
SLOT( slotColumnVisibilityChanged( int ) ) ); |
|
|
|
// Connect local folder manager |
|
connect( kmkernel->folderMgr(), SIGNAL( changed() ), |
|
SLOT( slotReload() ) ); |
|
connect( kmkernel->folderMgr(), SIGNAL( folderRemoved( KMFolder * ) ), |
|
SLOT( slotFolderRemoved( KMFolder * ) ) ); |
|
connect( kmkernel->folderMgr(), SIGNAL( folderMoveOrCopyOperationFinished() ), |
|
SLOT( slotFolderMoveOrCopyOperationFinished() ) ); |
|
|
|
// Connect the IMAP folder manager |
|
connect( kmkernel->imapFolderMgr(), SIGNAL( changed() ), |
|
SLOT( slotReload() ) ); |
|
connect( kmkernel->imapFolderMgr(), SIGNAL( folderRemoved( KMFolder * ) ), |
|
SLOT( slotFolderRemoved( KMFolder * ) ) ); |
|
|
|
// Connect the Cached Imap folder manager |
|
connect( kmkernel->dimapFolderMgr(), SIGNAL( changed() ), |
|
SLOT( slotReload() ) ); |
|
connect( kmkernel->dimapFolderMgr(), SIGNAL( folderRemoved( KMFolder * ) ), |
|
SLOT( slotFolderRemoved( KMFolder * ) ) ); |
|
|
|
// Connect the Search virtual folder manager |
|
connect( kmkernel->searchFolderMgr(), SIGNAL( changed() ), |
|
SLOT( slotReload() ) ); |
|
connect( kmkernel->searchFolderMgr(), SIGNAL( folderRemoved( KMFolder * ) ), |
|
SLOT( slotFolderRemoved( KMFolder * ) ) ); |
|
|
|
// Track account removals |
|
connect( kmkernel->acctMgr(), SIGNAL( accountRemoved( KMAccount * ) ), |
|
SLOT( slotAccountRemoved( KMAccount * ) ) ); |
|
|
|
// Track folder expands and collapses (dynamic tree updates) |
|
connect( this, SIGNAL( itemExpanded( QTreeWidgetItem * ) ), |
|
SLOT( slotFolderExpanded( QTreeWidgetItem * ) ) ); |
|
connect( this, SIGNAL( itemCollapsed( QTreeWidgetItem * ) ), |
|
SLOT( slotFolderCollapsed( QTreeWidgetItem * ) ) ); |
|
|
|
connect( this, SIGNAL( itemClicked ( QTreeWidgetItem *, int ) ), |
|
SLOT( slotItemClicked ( QTreeWidgetItem *, int ) ) ); |
|
} |
|
|
|
FolderView::~FolderView() |
|
{ |
|
// We don't use mManager here since we want to be really sure |
|
// that KMMainWidget didn't delete it yet (may happen at shutdown). |
|
if ( mainWidget()->folderViewManager() ) |
|
mainWidget()->folderViewManager()->detachView( this ); |
|
} |
|
|
|
void FolderView::readConfig() |
|
{ |
|
readColorConfig(); |
|
|
|
// Custom/System font support |
|
KConfigGroup conf( KMKernel::config(), "Fonts" ); |
|
if (!conf.readEntry( "defaultFonts", true ) ) |
|
setFont( conf.readEntry("folder-font", KGlobalSettings::generalFont() ) ); |
|
else |
|
setFont( KGlobalSettings::generalFont() ); |
|
|
|
if ( !restoreLayout( KMKernel::config(), "Geometry", mConfigPrefix + "Layout" ) ) |
|
{ |
|
// hide all the columns but the first (the default) |
|
setColumnHidden( DataSizeColumn, true ); |
|
setColumnHidden( UnreadColumn, true ); |
|
setColumnHidden( TotalColumn, true ); |
|
// default sort order is ascending |
|
header()->setSortIndicator( LabelColumn, Qt::AscendingOrder ); |
|
} |
|
|
|
KConfigGroup myGroup( KMKernel::config(), mConfigPrefix ); |
|
int iIconSize = myGroup.readEntry( "IconSize", iconSize().width() ); |
|
if ( iIconSize < 16 || iIconSize > 32 ) |
|
iIconSize = 22; |
|
setIconSize( QSize( iIconSize, iIconSize ) ); |
|
|
|
setToolTipDisplayPolicy( ( ToolTipDisplayPolicy )myGroup.readEntry( "ToolTipDisplayPolicy", ( int ) toolTipDisplayPolicy() ) ); |
|
setSortingPolicy( ( SortingPolicy )myGroup.readEntry( "SortingPolicy", ( int ) sortingPolicy() ) ); |
|
|
|
// we don't restore item states here: we'll do it in reload() instead. |
|
} |
|
|
|
void FolderView::writeConfig() |
|
{ |
|
saveItemStates(); |
|
saveLayout( KMKernel::config(), "Geometry", mConfigPrefix + "Layout" ); |
|
|
|
KConfigGroup myGroup( KMKernel::config(), mConfigPrefix ); |
|
myGroup.writeEntry( "IconSize", iconSize().width() ); |
|
myGroup.writeEntry( "ToolTipDisplayPolicy", ( int ) toolTipDisplayPolicy() ); |
|
myGroup.writeEntry( "SortingPolicy", ( int ) sortingPolicy() ); |
|
} |
|
|
|
void FolderView::setSortingPolicy( SortingPolicy policy ) |
|
{ |
|
mSortingPolicy = policy; |
|
switch ( mSortingPolicy ) |
|
{ |
|
case SortByCurrentColumn: |
|
header()->setClickable( true ); |
|
header()->setSortIndicatorShown( true ); |
|
setSortingEnabled( true ); |
|
break; |
|
case SortByDragAndDropKey: |
|
header()->setClickable( false ); |
|
header()->setSortIndicatorShown( false ); |
|
setSortingEnabled( false ); |
|
sortByColumn( LabelColumn, Qt::AscendingOrder ); |
|
fixSortingKeysForChildren( invisibleRootItem() ); |
|
break; |
|
default: |
|
// should never happen |
|
break; |
|
} |
|
} |
|
|
|
void FolderView::saveItemStates() |
|
{ |
|
FolderViewItem * fvi; |
|
|
|
QTreeWidgetItemIterator it( this ); |
|
while ( ( fvi = static_cast< FolderViewItem * >( *it ) ) ) |
|
{ |
|
QString configGroupName = fvi->configGroupName(); |
|
if ( !configGroupName.isEmpty() ) |
|
{ |
|
KConfigGroup conf( KMKernel::config(), configGroupName ); |
|
|
|
conf.writeEntry( mConfigPrefix + "ItemIsExpanded", fvi->isExpanded() ); |
|
conf.writeEntry( mConfigPrefix + "ItemIsHidden", fvi->isHidden() ); |
|
conf.writeEntry( mConfigPrefix + "ItemIsSelected", fvi->isSelected() ); |
|
conf.writeEntry( mConfigPrefix + "ItemDnDSortingKey", fvi->dndSortingKey() ); |
|
} |
|
++it; |
|
} |
|
} |
|
|
|
void FolderView::restoreItemStates() |
|
{ |
|
// This function attempts to restore the state of the items stored |
|
// in the configuration file. State includes "Expanded" state, "Hidden" |
|
// state and selection. |
|
|
|
// At the time of writing QTreeWidgetIterator can't be used to loop on items |
|
// while setting properties on them. In particular setExpanded()/setHidden() |
|
// or setSelected() (lost no time to investigate which one exactly) mess |
|
// up the iteration pointer and we end up traversing twice some parts |
|
// of the tree while not traversing at all some other. |
|
|
|
// This is why we use a QHash here. |
|
QHash< QString, FolderViewItem * > itemHash; |
|
|
|
QTreeWidgetItemIterator it( this ); |
|
FolderViewItem * fvi; |
|
|
|
while ( ( fvi = static_cast< FolderViewItem * >( *it ) ) ) |
|
{ |
|
QString configGroupName = fvi->configGroupName(); |
|
if ( !configGroupName.isEmpty() ) |
|
itemHash.insert( configGroupName, fvi ); |
|
++it; |
|
} |
|
|
|
QHash< QString, FolderViewItem * >::Iterator mit; |
|
|
|
for( mit = itemHash.begin(); mit != itemHash.end(); ++mit ) |
|
{ |
|
fvi = mit.value(); |
|
|
|
KConfigGroup conf( KMKernel::config(), mit.key() ); |
|
|
|
fvi->setExpanded( conf.readEntry( mConfigPrefix + "ItemIsExpanded", false ) ); |
|
fvi->setHidden( conf.readEntry( mConfigPrefix + "ItemIsHidden", false ) ); |
|
fvi->setSelected( conf.readEntry( mConfigPrefix + "ItemIsSelected", false ) ); |
|
fvi->setDndSortingKey( conf.readEntry( mConfigPrefix + "ItemDnDSortingKey", -1 ) ); |
|
} |
|
} |
|
|
|
void FolderView::cleanupConfigFile() |
|
{ |
|
// This is invoked by KMMainWidget to cleanup the configuration |
|
// file from stale entries. |
|
|
|
if ( topLevelItemCount() == 0 ) |
|
return; // just in case reload wasn't called before |
|
|
|
KConfig* config = KMKernel::config(); |
|
FolderViewItem * fvi; |
|
QHash<QString,bool> folderMap; |
|
|
|
// Gather a map of currently displayed folders |
|
QTreeWidgetItemIterator it(this); |
|
while ( ( fvi = static_cast< FolderViewItem * >( *it ) ) ) |
|
{ |
|
QString configGroupName = fvi->configGroupName(); |
|
if ( !configGroupName.isEmpty() ) |
|
folderMap.insert( configGroupName, true ); |
|
++it; |
|
} |
|
|
|
// Loop through the config groups and try to kill the ones |
|
// that are not present in the map above |
|
QStringList groupList = config->groupList(); |
|
|
|
for ( QStringList::Iterator grpIt = groupList.begin(); grpIt != groupList.end(); ++grpIt) |
|
{ |
|
if ( !( *grpIt ).startsWith( KMFOLDER_CONFIG_GROUP_NAME_PREFIX ) ) |
|
continue; |
|
|
|
if ( !folderMap.contains( *grpIt ) ) |
|
{ |
|
// Folder not displayed in the tree |
|
// Check if it's a special hidden folder... |
|
QString folderId = ( *grpIt ).mid( KMFOLDER_CONFIG_GROUP_NAME_PREFIX_LEN ); |
|
|
|
KMFolder* folder = kmkernel->findFolderById( folderId ); |
|
if ( folder ) { |
|
// The folder, in fact, exists. |
|
if ( |
|
kmkernel->iCalIface().hideResourceFolder( folder ) || |
|
kmkernel->iCalIface().hideResourceAccountRoot( folder ) |
|
) |
|
continue; // hidden IMAP resource folder, don't delete info |
|
} |
|
|
|
// Nope.. it either doesn't exist at all or it's not displayed |
|
// for some reason (may happen ?)... kill the group. |
|
config->deleteGroup( *grpIt, KConfig::Localized ); |
|
} |
|
} |
|
} |
|
|
|
void FolderView::slotColumnVisibilityChanged( int logicalIndex ) |
|
{ |
|
// This is called when one of our columns is hidden or shown either |
|
// by the means of the header popup menu or programmatically. |
|
|
|
bool nowVisible = !isColumnHidden( logicalIndex ); |
|
|
|
switch( logicalIndex ) |
|
{ |
|
case LabelColumn: |
|
// Ugh.. should never happen :/ |
|
break; |
|
case UnreadColumn: |
|
case TotalColumn: |
|
case DataSizeColumn: |
|
reload( nowVisible ); |
|
break; |
|
} |
|
|
|
// OK: what follows is totally empiric.. but it looks nice ;) |
|
|
|
// A shown column usually causes the horizontal scrollbar to appear. |
|
// The user is then forced to readjust the size of the column manually, |
|
// sometimes with non trivial operations (try it: #ifdef this code and see :D). |
|
// This tends to be very annoying thus a section relayout is a good idea. |
|
|
|
// We do our own resizing since none of the defaults provided by Qt 4.4 seem to |
|
// be nice enough. QHeaderView::Interactive and QHeaderView::Fixed are clearly |
|
// useless here. QHeaderView::Stretch makes all the columns the same size (which looks ugly) |
|
// and QHeaderView::ResizeToContents often will cause the scrollbar to appear again. |
|
|
|
// If you feel afraid when mantaining the following code, be aware |
|
// that nothing really relies on it... |
|
|
|
if ( nowVisible && ( !isColumnHidden( LabelColumn ) ) ) |
|
{ |
|
// A new column appeared and the "LabelColumn" (the long one) is actually visible. |
|
int count = header()->count(); |
|
int totalw = 0; |
|
int labelw = 180; |
|
|
|
// Compute the actual total width and find out the width of the label column |
|
for ( int i = 0; i < count ; i++ ) |
|
{ |
|
if ( !isColumnHidden( i ) ) |
|
{ |
|
int size = header()->sectionSize( i ); |
|
totalw += size; |
|
if ( i == LabelColumn ) |
|
labelw = size; |
|
} |
|
} |
|
|
|
int vwidth = viewport()->width(); |
|
|
|
// If the total width extends beyond the view or the label column is really too small |
|
// then perform a nice proportional re-layout. |
|
if ( ( totalw > vwidth ) || ( labelw < 180 ) ) |
|
{ |
|
// all the sections but the "LabelColumn" get 70 pixels (yes, this is heuristic) |
|
for ( int c = 0; c < count ; c++ ) |
|
{ |
|
if ( !isColumnHidden( c ) && ( c != LabelColumn ) ) |
|
{ |
|
header()->resizeSection( c, 70 ); |
|
vwidth -= 70; |
|
} |
|
} |
|
// then the LabelSection gets the remaining space (but at least 180 pixels) |
|
header()->resizeSection( LabelColumn, vwidth >= 180 ? vwidth : 180 ); |
|
} |
|
} // else less space is needed and the view should look OK without resizing. |
|
|
|
emit columnsChanged(); |
|
} |
|
|
|
void FolderView::mousePressEvent( QMouseEvent * e ) |
|
{ |
|
mLastButtonPressedWasMiddle = e->button() == Qt::MidButton; |
|
|
|
KPIM::FolderTreeWidget::mousePressEvent( e ); |
|
} |
|
|
|
void FolderView::slotItemClicked( QTreeWidgetItem *item, int column ) |
|
{ |
|
Q_UNUSED( column ); |
|
FolderViewItem *folderItem = dynamic_cast<FolderViewItem*>( item ); |
|
Q_ASSERT( folderItem ); |
|
bool multiSelection = ( QApplication::keyboardModifiers() & Qt::ControlModifier ) || |
|
( QApplication::keyboardModifiers() & Qt::ShiftModifier ); |
|
if ( folderItem && !multiSelection ) |
|
activateItem( folderItem, false, mLastButtonPressedWasMiddle ); |
|
} |
|
|
|
void FolderView::slotReload() |
|
{ |
|
// This is called mainly because folders have been added/deleted: update the tree |
|
reload( false ); |
|
} |
|
|
|
void FolderView::slotDelayedReload() |
|
{ |
|
QTimer::singleShot( 0, this, SLOT( slotReload() ) ); |
|
} |
|
|
|
void FolderView::reloadInternal( QTreeWidgetItem *item ) |
|
{ |
|
// The recursive part of reload(). |
|
// Makes connections on every item. |
|
// Scans the tree from bottom to top by updating the counts (from cache) |
|
|
|
// This is also very likely to be faster than QTreeWidgetIterator. |
|
|
|
int cc = item->childCount(); |
|
int i = 0; |
|
while ( i < cc ) |
|
{ |
|
FolderViewItem *fvi = static_cast< FolderViewItem * >( item->child( i ) ); |
|
|
|
if ( fvi ) |
|
{ |
|
reloadInternal( fvi ); |
|
|
|
fvi->updateCounts(); |
|
|
|
KMFolder *fld = fvi->folder(); |
|
|
|
if ( fld ) |
|
{ |
|
Util::reconnectSignalSlotPair( fld, SIGNAL( iconsChanged() ), fvi, SLOT( slotIconsChanged() ) ); |
|
Util::reconnectSignalSlotPair( fld, SIGNAL( nameChanged() ), fvi, SLOT( slotNameChanged() ) ); |
|
Util::reconnectSignalSlotPair( fld, SIGNAL( noContentChanged() ), fvi, SLOT( slotNoContentChanged() ) ); |
|
|
|
// we want to be noticed of changes to update the unread/total columns |
|
Util::reconnectSignalSlotPair( fld, SIGNAL( msgAdded( KMFolder *, quint32 ) ), fvi, SLOT( slotFolderCountsChanged( KMFolder * ) ) ); |
|
Util::reconnectSignalSlotPair( fld, SIGNAL( msgRemoved( KMFolder * ) ), fvi, SLOT( slotFolderCountsChanged( KMFolder * ) ) ); |
|
Util::reconnectSignalSlotPair( fld, SIGNAL( numUnreadMsgsChanged( KMFolder * ) ), fvi, SLOT( slotFolderCountsChanged( KMFolder * ) ) ); |
|
Util::reconnectSignalSlotPair( fld, SIGNAL( folderSizeChanged( KMFolder * ) ), fvi, SLOT( slotFolderCountsChanged( KMFolder * ) ) ); |
|
|
|
Util::reconnectSignalSlotPair( fld, SIGNAL( shortcutChanged( KMFolder * ) ), mMainWidget, SLOT( slotShortcutChanged( KMFolder * ) ) ); |
|
} |
|
} |
|
i++; |
|
} |
|
|
|
} |
|
|
|
void FolderView::reload( bool openFoldersForUpdate ) |
|
{ |
|
// Clears and re-fills the tree. |
|
|
|
// This code attempts to be a "generic" reload |
|
// routine for all the folder views. |
|
|
|
// The basic idea is that we need to loop on all the folder |
|
// trees exposed by the folder managers. On each node |
|
// we create a view item. |
|
|
|
// The trees themselves are not consistent and a couple of |
|
// them need a fake root to be added. This function takes |
|
// care of that. |
|
|
|
// The magic differences between all the views are added |
|
// in the createItem() method which is a pure virtual and |
|
// must be overridden by the child classes. |
|
|
|
// createItem() is passed the informations about the folder |
|
// we want to create a view item for and it's position in |
|
// the hierarchy. The child view is then allowed to mess |
|
// up a bit by either skipping the item creation (favorite folder |
|
// view does that), by modifying the position in the hierarchy |
|
// or by changing the item displayable properties (such as the label). |
|
|
|
setUpdatesEnabled( false ); |
|
setSortingEnabled( false ); // disable sorting while inserting items |
|
|
|
// remember the scroll bar positions so we can restore them later |
|
int oldLeft = horizontalScrollBar()->value(); |
|
int oldTop = verticalScrollBar()->value(); |
|
|
|
// save the current item states, if any |
|
saveItemStates(); |
|
|
|
// clean up the view |
|
clear(); |
|
|
|
// construct the (fake) root of the local folders |
|
FolderViewItem *root = createItem( |
|
0, i18n( "Local Folders" ), |
|
0, FolderViewItem::Local, FolderViewItem::Root |
|
); |
|
// note that root MIGHT be 0 here (if the view filtered it out) |
|
addDirectory( false, &( kmkernel->folderMgr()->dir() ), root ); |
|
|
|
// each imap-account creates it's own root |
|
addDirectory( true, &( kmkernel->imapFolderMgr()->dir() ), 0); |
|
|
|
// each dimap-account creates it's own root |
|
addDirectory( true, &( kmkernel->dimapFolderMgr()->dir() ), 0); |
|
|
|
// construct the root of the search folder hierarchy: |
|
root = createItem( |
|
0, i18n( "Searches" ), |
|
0, FolderViewItem::Search, FolderViewItem::Root |
|
); |
|
// note that root MIGHT be 0 here (if the view filtered it out) |
|
addDirectory( false, &( kmkernel->searchFolderMgr()->dir() ), root ); |
|
|
|
// Do connections on all the items |
|
reloadInternal( invisibleRootItem() ); |
|
|
|
// Trigger a complete count update, if requested |
|
if ( openFoldersForUpdate ) |
|
{ |
|
// We open all folders to update the count |
|
for( QTreeWidgetItemIterator itr( this ); QTreeWidgetItem * twitem = (*itr); ++itr ) |
|
{ |
|
FolderViewItem *fvi = static_cast< FolderViewItem * >( twitem ); |
|
|
|
if ( !fvi->folder() ) |
|
continue; |
|
|
|
fvi->setCountsDirty( true ); |
|
} |
|
|
|
triggerImmediateUpdateCounts(); // this will trigger after a zero timeout. |
|
} |
|
|
|
// restore the item states that we have saved earlier |
|
restoreItemStates(); |
|
|
|
// restore scrollbars |
|
horizontalScrollBar()->setValue( oldLeft ); |
|
verticalScrollBar()->setValue( oldTop ); |
|
|
|
// fix up sorting, if needed and re-enable the updates |
|
setUpdatesEnabled( true ); |
|
if ( sortingPolicy() == SortByCurrentColumn ) |
|
setSortingEnabled( true ); |
|
else { |
|
sortByColumn( LabelColumn, Qt::AscendingOrder ); |
|
fixSortingKeysForChildren( invisibleRootItem() ); |
|
} |
|
} |
|
|
|
void FolderView::fixSortingKeysForChildren( QTreeWidgetItem *item ) |
|
{ |
|
int cc = item->childCount(); |
|
int idx = 0; |
|
while ( idx < cc ) |
|
{ |
|
FolderViewItem *fvi = static_cast<FolderViewItem *>( item->child( idx ) ); |
|
if ( fvi ) |
|
{ |
|
fvi->setDndSortingKey( idx ); |
|
fixSortingKeysForChildren( fvi ); |
|
} |
|
++idx; |
|
} |
|
} |
|
|
|
void FolderView::addDirectory( bool createRoots, KMFolderDir * fdir, FolderViewItem *parent ) |
|
{ |
|
if ( !fdir ) |
|
return; // should never happen, but well.... |
|
|
|
QList<KMFolderNode*>::const_iterator it; |
|
|
|
for ( it = fdir->constBegin(); it != fdir->constEnd() ; ++it ) |
|
{ |
|
KMFolderNode * node = *it; |
|
if ( !node ) |
|
continue; |
|
|
|
if ( node->isDir() ) |
|
continue; |
|
|
|
KMFolder * folder = static_cast<KMFolder*>(node); |
|
FolderViewItem * fvi = 0; |
|
|
|
if ( createRoots ) |
|
{ |
|
// create new root-item, but only if this is not the root of a |
|
// "groupware folders only" account |
|
if ( kmkernel->iCalIface().hideResourceAccountRoot( folder ) ) |
|
continue; |
|
// it needs a folder e.g. to save it's state (open/close) |
|
fvi = createItem( |
|
parent, folder->label(), folder, |
|
FolderViewItem::protocolByFolder( folder ), |
|
FolderViewItem::Root |
|
); |
|
// note that root MIGHT be 0 here (if the view filtered it out) |
|
if ( fvi ) |
|
fvi->setChildIndicatorPolicy( QTreeWidgetItem::ShowIndicator ); |
|
|
|
// add child-folders |
|
if ( folder->child() ) |
|
addDirectory( false, folder->child(), fvi ); |
|
|
|
} else { |
|
// hide local inbox if unused (that is if option enabled, no messages, no accounts) |
|
if ( ( kmkernel->inboxFolder() == folder ) && hideLocalInbox() ) |
|
{ |
|
// make sure we're reloaded when the precondition for hiding the local inbox changes |
|
Util::reconnectSignalSlotPair( kmkernel->inboxFolder(), SIGNAL( msgAdded( KMFolder*, quint32 ) ), |
|
this, SLOT( slotReload() ) ); |
|
continue; // skip child creation |
|
} |
|
|
|
// create new child |
|
fvi = createItem( |
|
parent, folder->label(), folder, |
|
FolderViewItem::protocolByFolder( folder ), |
|
FolderViewItem::folderTypeByFolder( folder ) |
|
); |
|
// set folders explicitly to exandable when they have children |
|
// this way we can do a listing for IMAP folders when the user expands them |
|
// even when the child folders are not created yet |
|
if ( fvi && ( folder->storage()->hasChildren() == FolderStorage::HasChildren ) ) |
|
fvi->setChildIndicatorPolicy( QTreeWidgetItem::ShowIndicator ); |
|
|
|
// add child-folders |
|
if ( folder->child() ) |
|
addDirectory( false, folder->child(), fvi ); |
|
|
|
if ( fvi ) |
|
{ |
|
// Check if this is an IMAP resource folder or a no-content parent only |
|
// containing groupware folders |
|
if ( |
|
fvi && |
|
( kmkernel->iCalIface().hideResourceFolder( folder ) || folder->noContent() ) && |
|
( fvi->childCount() == 0 ) |
|
) |
|
{ |
|
delete fvi; |
|
// still, it might change in the future, so we better check the change signals |
|
connect ( folder, SIGNAL( noContentChanged() ), SLOT( slotDelayedReload() ) ); |
|
continue; |
|
} |
|
|
|
connect( fvi, SIGNAL( iconChanged( FolderViewItem * ) ), |
|
this, SIGNAL( iconChanged( FolderViewItem * ) ) ); |
|
connect( fvi, SIGNAL( nameChanged( FolderViewItem * ) ), |
|
this, SIGNAL( nameChanged( FolderViewItem * ) ) ); |
|
} |
|
} |
|
} // for-end |
|
} |
|
|
|
void FolderView::triggerLazyUpdateCounts() |
|
{ |
|
// Called to trigger a delayed update of the counts of the folders marked with countsDirty(). |
|
if ( mUpdateCountsTimer->isActive() ) |
|
return; // update already pending |
|
mUpdateCountsTimer->start( gLazyUpdateCountsTimeout ); |
|
} |
|
|
|
void FolderView::triggerImmediateUpdateCounts() |
|
{ |
|
if ( mUpdateCountsTimer->isActive() ) |
|
{ |
|
if ( mUpdateCountsTimer->interval() == 0 ) |
|
return; // immediate update already pending |
|
// need to restart |
|
mUpdateCountsTimer->stop(); |
|
} |
|
mUpdateCountsTimer->start( 0 ); |
|
} |
|
|
|
|
|
FolderView::UpdateCountsResult FolderView::updateCountsForChildren( QTreeWidgetItem *it, const QTime &tStart ) |
|
{ |
|
// Update counts for the children of it, recursively: first go to the children |
|
// of the children and if any were dirty, mark as dirty the direct child too. |
|
// Break (and resume later) if running for too long. |
|
|
|
int cc = it->childCount(); |
|
int i = 0; |
|
bool childrenWereDirty = false; |
|
int elapsed; |
|
|
|
while ( i < cc ) |
|
{ |
|
FolderViewItem *fvi = static_cast< FolderViewItem *>( it->child( i ) ); |
|
i++; |
|
if ( !fvi ) |
|
continue; |
|
|
|
// go down to the children |
|
switch ( updateCountsForChildren( fvi, tStart ) ) |
|
{ |
|
case OperationCompletedDirty: |
|
// some children were dirty |
|
fvi->setCountsDirty( false ); // propagate dirty flag to this item |
|
childrenWereDirty = true; // and propagate up |
|
|
|
// At least one item was dirty and has been updated. |
|
// We can check the timeout now and eventually interrupt the operation. |
|
// Checking that at least one item was updated ensures that we will |
|
// not end up in endless loops trying to update and never making progress. |
|
elapsed = tStart.msecsTo( QTime::currentTime() ); |
|
if ( ( elapsed > gUpdateCountsProcessingInterval ) || ( elapsed < 0 ) ) |
|
return OperationInterrupted; // it's taking too long, will continue later. |
|
break; |
|
case OperationCompletedClean: |
|
// no children were dirty |
|
break; |
|
case OperationInterrupted: |
|
// the operation was interrupted due to a timeout: propagate up |
|
return OperationInterrupted; |
|
break; |
|
} |
|
|
|
// update this item, if needed |
|
if ( fvi->countsDirty() ) |
|
{ |
|
childrenWereDirty = true; |
|
fvi->updateCounts(); |
|
|
|
// maybe this took time, check again, unless this is the last item |
|
if ( i < cc ) |
|
{ |
|
elapsed = tStart.msecsTo( QTime::currentTime() ); |
|
if ( ( elapsed > gUpdateCountsProcessingInterval ) || ( elapsed < 0 ) ) |
|
return OperationInterrupted; // it's taking too long, will continue later. |
|
} |
|
} |
|
} |
|
|
|
return childrenWereDirty ? OperationCompletedDirty : OperationCompletedClean; |
|
} |
|
|
|
|
|
void FolderView::slotUpdateCounts() |
|
{ |
|
// This is connected to the mUpdateCountsTimer timeout() signal. |
|
|
|
// We attempt to update as more items as possible but also avoid |
|
// blocking the UI for too much. We do the job in timed chunks. |
|
// If an update takes too long, we ask a timer to call us in a while |
|
// and jump out to let the application handle some events. |
|
|
|
// FIXME: Update visible items first. Children should come before parents too. |
|
|
|
mUpdateCountsTimer->stop(); |
|
|
|
QTime tStart = QTime::currentTime(); |
|
|
|
// We scan the children starting from the leafs and going up |
|
// This ensures coherent children counts |
|
if ( updateCountsForChildren( invisibleRootItem(), tStart ) == OperationInterrupted ) |
|
{ |
|
// the operation has been interrupted due to a timeout, restart it in a while |
|
mUpdateCountsTimer->start( 0 ); |
|
} |
|
|
|
viewport()->update(); |
|
} |
|
|
|
void FolderView::mouseReleaseEvent( QMouseEvent *e ) |
|
{ |
|
FolderViewItem * fvi = dynamic_cast<FolderViewItem *>( currentItem() ); |
|
|
|
// When the middle button is used on a folder with a mailing list |
|
// start composing a message to that mailing list too |
|
|
|
// FIXME: Middle click is also used to open the folder in a new tab! |
|
|
|
if ( |
|
fvi && |
|
fvi->folder() && |
|
(e->button() == Qt::MidButton) && |
|
fvi->folder()->isMailingListEnabled() |
|
) |
|
{ |
|
KMCommand *command = new KMMailingListPostCommand( this, fvi->folder() ); |
|
command->start(); |
|
} |
|
|
|
KPIM::FolderTreeWidget::mouseReleaseEvent( e ); |
|
} |
|
|
|
|
|
void FolderView::slotFolderRemoved( KMFolder *folder ) |
|
{ |
|
FolderViewItem * fvi = static_cast< FolderViewItem * >( findItemByFolder( folder ) ); |
|
if ( !fvi ) |
|
return; |
|
// (in)sanity check |
|
if ( fvi->folder() != folder) |
|
return; // insane... |
|
|
|
if ( ( (QTreeWidgetItem *)fvi ) == currentItem() ) |
|
{ |
|
QTreeWidgetItem * newcurrent = itemAbove( fvi ); |
|
if ( !newcurrent ) |
|
newcurrent = itemBelow( fvi ); |
|
|
|
FolderViewItem * ncfvi = static_cast< FolderViewItem * >( newcurrent ); |
|
|
|
activateItem( ncfvi ); |
|
} |
|
|
|
delete fvi; |
|
updateCopyActions(); |
|
} |
|
|
|
void FolderView::setCurrentFolder( KMFolder *folder ) |
|
{ |
|
FolderViewItem * fvi; |
|
|
|
if ( !folder ) |
|
fvi = static_cast< FolderViewItem * >( |
|
findItemByProperties( |
|
KPIM::FolderTreeWidgetItem::Local, |
|
KPIM::FolderTreeWidgetItem::Root |
|
) |
|
); // activate Local Root |
|
else |
|
fvi = static_cast< FolderViewItem * >( |
|
findItemByFolder( |
|
folder |
|
) |
|
); // activate the item with the specified folder |
|
|
|
if ( !fvi ) |
|
{ |
|
clearSelection(); |
|
return; |
|
} |
|
|
|
activateItem( fvi ); |
|
} |
|
|
|
void FolderView::activateItem( FolderViewItem *fvi, bool keepSelection, bool middleButton ) |
|
{ |
|
activateItemInternal( fvi, keepSelection, true, middleButton ); |
|
} |
|
|
|
void FolderView::activateItemInternal( FolderViewItem *fvi, bool keepSelection, bool notifyManager, bool middleButton ) |
|
{ |
|
if ( !fvi ) |
|
return; // nothing to do: we keep everything as it is |
|
|
|
KMFolder* folder = fvi->folder(); |
|
|
|
if ( fvi->isHidden() ) |
|
fvi->setHidden( false ); |
|
|
|
if ( !keepSelection ) |
|
clearSelection(); |
|
if ( currentItem() != ( ( QTreeWidgetItem * ) fvi ) ) |
|
setCurrentItem( fvi ); |
|
if ( !keepSelection && !fvi->isSelected() ) |
|
fvi->setSelected( true ); |
|
|
|
scrollToItem( fvi ); |
|
|
|
if ( notifyManager ) |
|
mManager->viewFolderActivated( this, folder, middleButton ); |
|
|
|
if ( folder ) |
|
{ |
|
// update counts up to top-level parent |
|
while ( fvi ) |
|
{ |
|
if ( !fvi->updateCounts() ) |
|
break; // nothing has changed at this level: parents can't change |
|
fvi = static_cast< FolderViewItem * >( static_cast< QTreeWidgetItem *>( fvi )->parent() ); |
|
} |
|
|
|
} |
|
|
|
updateCopyActions(); |
|
} |
|
|
|
void FolderView::notifyFolderActivated( KMFolder *folder ) |
|
{ |
|
FolderViewItem * fvi; |
|
|
|
if ( !folder ) |
|
fvi = static_cast< FolderViewItem * >( |
|
findItemByProperties( |
|
KPIM::FolderTreeWidgetItem::Local, |
|
KPIM::FolderTreeWidgetItem::Root |
|
) |
|
); // activate Local Root |
|
else |
|
fvi = static_cast< FolderViewItem * >( |
|
findItemByFolder( |
|
folder |
|
) |
|
); // activate the item with the specified folder |
|
|
|
if ( !fvi ) |
|
{ |
|
clearSelection(); |
|
return; |
|
} |
|
|
|
activateItemInternal( fvi, false, false, false ); |
|
} |
|
|
|
void FolderView::slotAddToFavorites() |
|
{ |
|
KMail::FavoriteFolderView *favView = mMainWidget->favoriteFolderView(); |
|
if ( !favView ) |
|
return; // ugh :/ |
|
foreach ( const QPointer<KMFolder> &f, selectedFolders() ) |
|
favView->addFolder( f ); |
|
} |
|
|
|
void FolderView::updateCopyActions() |
|
{ |
|
QAction *copy = mMainWidget->action("copy_folder"); |
|
QAction *cut = mMainWidget->action("cut_folder"); |
|
QAction *paste = mMainWidget->action("paste_folder"); |
|
|
|
QList< QPointer< KMFolder > > selected = selectedFolders(); |
|
|
|
if ( selected.isEmpty() ) |
|
{ |
|
copy->setEnabled( false ); |
|
cut->setEnabled( false ); |
|
|
|
} else { |
|
|
|
bool bCanMove = true; |
|
for( QList< QPointer< KMFolder > >::Iterator it = selected.begin(); it != selected.end(); ++it ) |
|
{ |
|
if ( !( *it ) ) |
|
{ |
|
// yes, it can happen |
|
bCanMove = false; // stay safe |
|
break; |
|
} |
|
|
|
if( !( *it )->isMoveable() ) |
|
{ |
|
bCanMove = false; |
|
break; |
|
} |
|
} |
|
|
|
copy->setEnabled( true ); |
|
cut->setEnabled( bCanMove ); |
|
} |
|
|
|
paste->setEnabled( !mFolderClipboard.isEmpty() ); |
|
} |
|
|
|
bool FolderView::event( QEvent *e ) |
|
{ |
|
// We catch ToolTip events and pass everything else |
|
|
|
if( e->type() != QEvent::ToolTip ) |
|
return KPIM::FolderTreeWidget::event( e ); |
|
|
|
QHelpEvent * he = dynamic_cast< QHelpEvent * >( e ); |
|
if ( !he ) |
|
return true; // eh ? |
|
|
|
if ( mToolTipDisplayPolicy == DisplayNever ) |
|
return true; // never display tooltips |
|
|
|
QPoint pnt = viewport()->mapFromGlobal( mapToGlobal( he->pos() ) ); |
|
|
|
if ( pnt.y() < 0 ) |
|
return true; // don't display the tooltip for items hidden under the header |
|
|
|
QTreeWidgetItem * it = itemAt( pnt ); |
|
if ( !it ) |
|
return true; // may be |
|
|
|
FolderViewItem * fvi = static_cast< FolderViewItem * >( it ); |
|
|
|
if ( ( mToolTipDisplayPolicy == DisplayWhenTextElided ) && ( !fvi->labelTextElided() ) ) |
|
return true; // don't display if text not elided |
|
|
|
QString bckColor = palette().color( QPalette::ToolTipBase ).name(); |
|
QString txtColor = palette().color( QPalette::ToolTipText ).name(); |
|
|
|
QString tip = QString::fromLatin1( |
|
"<table width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">" |
|
); |
|
|
|
tip += QString::fromLatin1( |
|
"<tr>" \ |
|
"<td bgcolor=\"%1\" colspan=\"2\" align=\"left\" valign=\"middle\">" \ |
|
"<div style=\"color: %2; font-weight: bold;\">" \ |
|
"%3" \ |
|
"</div>" \ |
|
"</td>" \ |
|
"</tr>" |
|
).arg( txtColor ).arg( bckColor ).arg( fvi->labelText() ); |
|
|
|
|
|
tip += QString::fromLatin1( |
|
"<tr>" \ |
|
"<td align=\"left\" valign=\"top\">" |
|
); |
|
|
|
if ( KMFolder * fld = fvi->folder() ) |
|
{ |
|
if ( !fld->noContent() ) |
|
{ |
|
tip += QString::fromLatin1( |
|
"<strong>%1</strong>: %2<br>" \ |
|
"<strong>%3</strong>: %4<br><br>" |
|
).arg( i18n("Total Messages") ).arg( fld->count( !fld->isOpened() ) ) |
|
.arg( i18n("Unread Messages") ).arg( fld->countUnread() ); |
|
|
|
if ( KMFolderCachedImap * imap = dynamic_cast< KMFolderCachedImap * >( fld->storage() ) ) |
|
{ |
|
QuotaInfo info = imap->quotaInfo(); |
|
if ( info.isValid() && !info.isEmpty() ) |
|
tip += QString( "<strong>%1</strong>: %2" ).arg( i18n( "Quota" ) ).arg( info.toString() ); |
|
} |
|
|
|
if ( fld->storage()->folderSize() >= 0 ) |
|
tip += QString::fromLatin1( |
|
"<strong>%1</strong>: %2<br>" |
|
).arg( i18n("Storage Size") ).arg( KIO::convertSize( (KIO::filesize_t)( fld->storage()->folderSize() ) ) ); |
|
|
|
fvi->setCountsDirty( true ); // make sure the counts in the view get updated |
|
triggerLazyUpdateCounts(); |
|
} |
|
|
|
if (fvi->childrenDataSize() >= 0 ) |
|
tip += QString::fromLatin1( |
|
"<strong>%1</strong>: %2<br>" |
|
).arg( i18n("Subfolder Storage Size") ).arg( KIO::convertSize( (KIO::filesize_t)( fvi->childrenDataSize() ) ) ); |
|
|
|
// FIXME prettyUrl() ? |
|
|
|
/* |
|
This makes the tooltip too big :/ |
|
QString location = fld->location(); |
|
if ( !location.isEmpty() ) |
|
{ |
|
tip += QString::fromLatin1( |
|
"<strong>%1</strong>: <nobr>%2</nobr><br>" |
|
).arg( i18n("Path") ).arg( fontMetrics().elidedText( location, Qt::ElideRight, 250 ) ); |
|
} |
|
*/ |
|
} |
|
|
|
int icon_sizes[] = { /* 128, 64, 48, */ 32, 22 }; |
|
|
|
QString iconPath; |
|
|
|
for ( int i = 0; i < 2; i++ ) |
|
{ |
|
iconPath = KIconLoader::global()->iconPath( fvi->normalIcon(), -icon_sizes[ i ], true ); |
|
if ( !iconPath.isEmpty() ) |
|
break; |
|
} |
|
|
|
if ( iconPath.isEmpty() ) |
|
iconPath = KIconLoader::global()->iconPath( "folder", -32, false ); |
|
|
|
tip += QString::fromLatin1( |
|
"</td>" \ |
|
"<td align=\"right\" valign=\"top\">" \ |
|
"<table border=\"0\"><tr><td width=\"32\" height=\"32\" align=\"center\" valign=\"middle\">" |
|
"<img src=\"%1\">" \ |
|
"</td></tr></table>" \ |
|
"</td>" \ |
|
"</tr>" \ |
|
) .arg( iconPath ); |
|
|
|
|
|
tip += QString::fromLatin1( |
|
"</table>" |
|
); |
|
|
|
QToolTip::showText( he->globalPos(), tip, viewport(), visualItemRect( it ) ); |
|
|
|
return true; |
|
} |
|
|
|
bool FolderView::fillHeaderContextMenu( KMenu * menu, const QPoint &clickPoint ) |
|
{ |
|
if ( !KPIM::FolderTreeWidget::fillHeaderContextMenu( menu, clickPoint ) ) |
|
return false; |
|
|
|
QAction *act; |
|
|
|
menu->addTitle( i18n( "Icon Size" ) ); |
|
|
|
static int icon_sizes[] = { 16, 22, 32 /*, 48, 64, 128 */ }; |
|
|
|
QActionGroup *grp = new QActionGroup( menu ); |
|
|
|
for ( int i = 0; i < (int)( sizeof( icon_sizes ) / sizeof( int ) ); i++ ) |
|
{ |
|
act = menu->addAction( QString("%1x%2").arg( icon_sizes[ i ] ).arg( icon_sizes[ i ] ) ); |
|
act->setCheckable( true ); |
|
grp->addAction( act ); |
|
if ( iconSize().width() == icon_sizes[ i ] ) |
|
act->setChecked( true ); |
|
act->setData( QVariant( icon_sizes[ i ] ) ); |
|
|
|
connect( act, SIGNAL( triggered( bool ) ), |
|
SLOT( slotHeaderContextMenuChangeIconSize( bool ) ) ); |
|
} |
|
|
|
menu->addTitle( i18n( "Display Tooltips" ) ); |
|
|
|
grp = new QActionGroup( menu ); |
|
|
|
act = menu->addAction( i18n("Always") ); |
|
act->setCheckable( true ); |
|
grp->addAction( act ); |
|
act->setChecked( mToolTipDisplayPolicy == DisplayAlways ); |
|
act->setData( QVariant( (int)DisplayAlways ) ); |
|
connect( act, SIGNAL( triggered( bool ) ), |
|
SLOT( slotHeaderContextMenuChangeToolTipDisplayPolicy( bool ) ) ); |
|
act = menu->addAction( i18n("When Text Obscured") ); |
|
act->setCheckable( true ); |
|
grp->addAction( act ); |
|
act->setChecked( mToolTipDisplayPolicy == DisplayWhenTextElided ); |
|
act->setData( QVariant( (int)DisplayWhenTextElided ) ); |
|
connect( act, SIGNAL( triggered( bool ) ), |
|
SLOT( slotHeaderContextMenuChangeToolTipDisplayPolicy( bool ) ) ); |
|
|
|
act = menu->addAction( i18n("Never") ); |
|
act->setCheckable( true ); |
|
grp->addAction( act ); |
|
act->setChecked( mToolTipDisplayPolicy == DisplayNever ); |
|
act->setData( QVariant( (int)DisplayNever ) ); |
|
connect( act, SIGNAL( triggered( bool ) ), |
|
SLOT( slotHeaderContextMenuChangeToolTipDisplayPolicy( bool ) ) ); |
|
|
|
menu->addTitle( i18n( "Sort Items" ) ); |
|
|
|
grp = new QActionGroup( menu ); |
|
|
|
act = menu->addAction( i18n("Automatically, by Current Column") ); |
|
act->setCheckable( true ); |
|
grp->addAction( act ); |
|
act->setChecked( mSortingPolicy == SortByCurrentColumn ); |
|
act->setData( QVariant( (int)SortByCurrentColumn ) ); |
|
connect( act, SIGNAL( triggered( bool ) ), |
|
SLOT( slotHeaderContextMenuChangeSortingPolicy( bool ) ) ); |
|
|
|
act = menu->addAction( i18n("Manually, by Drag And Drop") ); |
|
act->setCheckable( true ); |
|
grp->addAction( act ); |
|
act->setChecked( mSortingPolicy == SortByDragAndDropKey ); |
|
act->setData( QVariant( (int)SortByDragAndDropKey ) ); |
|
connect( act, SIGNAL( triggered( bool ) ), |
|
SLOT( slotHeaderContextMenuChangeSortingPolicy( bool ) ) ); |
|
|
|
return true; |
|
} |
|
|
|
void FolderView::slotHeaderContextMenuChangeSortingPolicy( bool ) |
|
{ |
|
QAction *act = dynamic_cast< QAction * >( sender() ); |
|
if ( !act ) |
|
return; |
|
|
|
QVariant data = act->data(); |
|
|
|
bool ok; |
|
int policy = data.toInt( &ok ); |
|
if ( !ok ) |
|
return; |
|
|
|
setSortingPolicy( ( SortingPolicy )policy ); |
|
} |
|
|
|
void FolderView::slotHeaderContextMenuChangeToolTipDisplayPolicy( bool ) |
|
{ |
|
QAction *act = dynamic_cast< QAction * >( sender() ); |
|
if ( !act ) |
|
return; |
|
|
|
QVariant data = act->data(); |
|
|
|
bool ok; |
|
int policy = data.toInt( &ok ); |
|
if ( !ok ) |
|
return; |
|
|
|
setToolTipDisplayPolicy( ( ToolTipDisplayPolicy )policy ); |
|
} |
|
|
|
void FolderView::slotHeaderContextMenuChangeIconSize( bool ) |
|
{ |
|
QAction *act = dynamic_cast< QAction * >( sender() ); |
|
if ( !act ) |
|
return; |
|
|
|
QVariant data = act->data(); |
|
|
|
bool ok; |
|
int size = data.toInt( &ok ); |
|
if ( !ok ) |
|
return; |
|
|
|
setUpdatesEnabled( false ); |
|
setIconSize( QSize( size, size ) ); |
|
reload(); |
|
setUpdatesEnabled( true ); |
|
} |
|
|
|
// |
|
// (Contextual) popup menu machinery |
|
// |
|
|
|
void FolderView::fillContextMenuAccountRelatedActions( KMenu *menu, FolderViewItem *item, bool multiSelection ) |
|
{ |
|
if ( multiSelection ) |
|
return; // no multiSelection Account Related actions |
|
|
|
KMFolder *folder = item->folder(); |
|
|
|
if ( !folder ) |
|
return; // no actions for items without folders at this time |
|
|
|
// Send Mail / Check Mail (Account Related) |
|
|
|
// outbox specific, but there it's the most used action |
|
// If the current item is the outbox and it has messages inside, allow sending them |
|
if ( ( folder == kmkernel->outboxFolder() ) && ( folder->count() > 0 ) ) |
|
menu->addAction( mMainWidget->action( "send_queued" ) ); |
|
|
|
if ( ( folder->folderType() == KMFolderTypeImap ) || ( folder->folderType() == KMFolderTypeCachedImap ) ) |
|
{ |
|
if ( item->folderType() == FolderViewItem::Root ) |
|
{ |
|
// check mail in imap/dimap root folder |
|
menu->addAction( |
|
KIcon( "mail-receive" ), i18n( "Check &Mail" ), |
|
this, SLOT( slotCheckMail() ) |
|
); |
|
} else { |
|
// check mail in imap/dimap subfolder |
|
if ( !folder->noContent() ) |
|
menu->addAction( mMainWidget->action( "refresh_folder" ) ); |
|
} |
|
|
|
menu->addAction( |
|
KIcon( "folder-bookmarks" ), |
|
i18n( "Subscription..." ), mMainWidget, |
|
SLOT( slotSubscriptionDialog() ) |
|
); |
|
|
|
menu->addAction( |
|
KIcon( "folder-bookmarks" ), |
|
i18n( "Local Subscription..." ), mMainWidget, |
|
SLOT( slotLocalSubscriptionDialog() ) |
|
); |
|
} |
|
} |
|
|
|
void FolderView::fillContextMenuMessageRelatedActions( KMenu *menu, FolderViewItem *item, bool multiSelection ) |
|
{ |
|
if ( multiSelection ) |
|
return; // no multiSelection Account Related actions |
|
|
|
KMFolder *folder = item->folder(); |
|
|
|
if ( !folder ) |
|
return; // no actions without a folder at this time |
|
|
|
// Message Related Actions |
|
|
|
if ( folder->isMailingListEnabled() ) |
|
menu->addAction( mMainWidget->action("post_message") ); |
|
|
|
if ( !folder->noContent() ) |
|
{ |
|
// search messages |
|
menu->addAction( mMainWidget->action( "search_messages" ) ); |
|
// mark all messages as read |
|
menu->addAction( mMainWidget->action( "mark_all_as_read" ) ); |
|
// move all messages to trash |
|
menu->addAction( mMainWidget->action( "empty" ) ); |
|
} |
|
} |
|
|
|
void FolderView::fillContextMenuTreeStructureRelatedActions( KMenu */*menu*/, FolderViewItem */*item*/, bool /*multiSelection*/ ) |
|
{ |
|
// this is overridden in derived classes |
|
} |
|
|
|
void FolderView::fillContextMenuViewStructureRelatedActions( KMenu */*menu*/, FolderViewItem */*item*/, bool /*multiSelection*/ ) |
|
{ |
|
// this is overridden in derived classes |
|
} |
|
|
|
void FolderView::fillContextMenuFolderServiceRelatedActions( KMenu *menu, FolderViewItem *item, bool multiSelection ) |
|
{ |
|
if ( multiSelection ) |
|
return; // no folder service actions for multifolder at this time |
|
|
|
KMFolder *folder = item->folder(); |
|
|
|
if ( !folder ) |
|
{ |
|
// local root or searches |
|
menu->addAction( mMainWidget->action( "compact_all_folders" ) ); |
|
menu->addAction( mMainWidget->action( "expire_all_folders" ) ); |
|
} |
|
|
|
if ( !folder ) |
|
return; // no more folder service actions for non-foldered items (notably for local root and searches) |
|
|
|
// Folder "Service" Actions |
|
|
|
if ( !folder->noContent() && ( ( folder->folderType() == KMFolderTypeImap ) || ( folder->folderType() == KMFolderTypeCachedImap ) ) ) |
|
{ |
|
if ( folder->folderType() == KMFolderTypeImap ) |
|
menu->addAction( |
|
KIcon( "view-refresh" ), i18n( "Refresh Folder List" ), |
|
this, SLOT( slotResetFolderList() ) |
|
); |
|
} |
|
|
|
if ( !folder->noContent() ) |
|
menu->addAction( mMainWidget->action( "compact" ) ); |
|
|
|
if ( folder->folderType() == KMFolderTypeMaildir ) |
|
menu->addAction( mMainWidget->action( "troubleshoot_maildir" ) ); // rebuild index |
|
|
|
if ( folder->folderType() == KMFolderTypeCachedImap ) |
|
{ |
|
KMFolderCachedImap * ifolder = static_cast<KMFolderCachedImap*>( folder->storage() ); |
|
menu->addAction( |
|
KIcon( "plasmagik" ), i18n( "&Troubleshoot IMAP Cache..." ), |
|
ifolder, SLOT( slotTroubleshoot() ) |
|
); |
|
} |
|
|
|
if ( item->folderType() != FolderViewItem::Root ) // non account level folder |
|
{ |
|
// FIXME: A very similar action is also used by KMMainWidget (mFolderShortCutCommandAction) |
|
menu->addAction( |
|
KIcon("configure-shortcuts"), i18n("&Assign Shortcut..."), |
|
item, SLOT( slotAssignShortcut() ) |
|
); |
|
|
|
if ( !folder->noContent() ) |
|
menu->addAction( i18n("Expire..."), item, SLOT( slotShowExpiryProperties() ) ); |
|
|
|
// properties |
|
menu->addAction( mMainWidget->action("modify") ); |
|
|
|
// FIXME: Couldn't we show the account edit dialog for imap roots ? |
|
} |
|
} |
|
|
|
void FolderView::fillContextMenuNoItem( KMenu *menu ) |
|
{ |
|
Q_UNUSED( menu ); |
|
// this is overridden in derived classes |
|
} |
|
|
|
// A little helper function: Adds a separator to the menu if it has more items |
|
// than lastActionCount. Returns the number of new items. |
|
static int insertSeparatorIfNeeded( int lastActionCount, QMenu *menu ) |
|
{ |
|
if ( lastActionCount < menu->actions().count() ) |
|
menu->insertSeparator( menu->actions().at( lastActionCount ) ); |
|
return menu->actions().count(); |
|
} |
|
|
|
void FolderView::contextMenuEvent( QContextMenuEvent *e ) |
|
{ |
|
// |
|
// Build the item's contextual menu. |
|
// |
|
// This code __attempts__ to favor some sort of logical order in the menu entries. |
|
// |
|
|
|
FolderViewItem * fvi = static_cast<FolderViewItem *>( itemAt( e->pos() ) ); |
|
|
|
KMenu * menu = new KMenu(); |
|
if ( fvi ) { |
|
KMFolder *folder = fvi->folder(); |
|
|
|
// make sure the folder is activated (so actions will get updated) |
|
// but keep the selection (so multifolder is possible) |
|
Q_ASSERT( mMainWidget ); |
|
if ( mMainWidget->activeFolder() != folder ) |
|
activateItem( fvi, true ); |
|
|
|
bool multiSelection = selectedFolders().count() > 1; |
|
|
|
// First of all, the title |
|
if ( multiSelection ) |
|
{ |
|
// multiple folders selected: add only actions that can handle all of them |
|
menu->addTitle( i18n( "Multiple Folders" ) ); |
|
} else { |
|
// single (or no) folder selected |
|
if ( folder ) |
|
menu->addTitle( folder->label() ); |
|
else |
|
menu->addTitle( fvi->text( LabelColumn ) ); |
|
} |
|
|
|
fillContextMenuAccountRelatedActions( menu, fvi, multiSelection ); |
|
|
|
// keep track of the number of actions so we can add separators in the right place, if needed |
|
int lastActionCount = menu->actions().count(); |
|
|
|
fillContextMenuMessageRelatedActions( menu, fvi, multiSelection ); |
|
lastActionCount = insertSeparatorIfNeeded( lastActionCount, menu ); |
|
|
|
fillContextMenuTreeStructureRelatedActions( menu, fvi, multiSelection ); |
|
lastActionCount = insertSeparatorIfNeeded( lastActionCount, menu ); |
|
|
|
fillContextMenuViewStructureRelatedActions( menu, fvi, multiSelection ); |
|
lastActionCount = insertSeparatorIfNeeded( lastActionCount, menu ); |
|
|
|
fillContextMenuFolderServiceRelatedActions( menu, fvi, multiSelection ); |
|
lastActionCount = insertSeparatorIfNeeded( lastActionCount, menu ); |
|
} |
|
else |
|
fillContextMenuNoItem( menu ); |
|
|
|
if ( !menu->isEmpty() ) { |
|
kmkernel->setContextMenuShown( true ); |
|
menu->exec( e->globalPos() , 0 ); |
|
kmkernel->setContextMenuShown( false ); |
|
} |
|
|
|
delete menu; |
|
} |
|
|
|
KMFolder * FolderView::currentFolder() const |
|
{ |
|
FolderViewItem * fvi = static_cast<FolderViewItem *>( currentItem() ); |
|
if ( fvi ) |
|
return fvi->folder(); |
|
return 0; |
|
} |
|
|
|
QString FolderView::currentItemFullPath() const |
|
{ |
|
QString ret; |
|
FolderViewItem * fvi = static_cast<FolderViewItem *>( currentItem() ); |
|
if ( !fvi ) |
|
return ret; |
|
ret = fvi->labelText(); |
|
fvi = static_cast< FolderViewItem * >( static_cast< QTreeWidgetItem * >( fvi )->parent() ); |
|
while ( fvi ) |
|
{ |
|
ret.prepend( "/" ); |
|
ret.prepend( fvi->labelText() ); |
|
fvi = static_cast< FolderViewItem * >( static_cast< QTreeWidgetItem * >( fvi )->parent() ); |
|
} |
|
return ret; |
|
} |
|
|
|
|
|
QList< QPointer< KMFolder > > FolderView::selectedFolders() |
|
{ |
|
QList< QPointer< KMFolder > > rv; |
|
|
|
for ( QTreeWidgetItemIterator it( this ) ; *it ; ++it ) |
|
{ |
|
FolderViewItem * fvi = static_cast<FolderViewItem *>( *it ); |
|
if ( fvi->isSelected() && fvi->folder() ) |
|
rv.append( fvi->folder() ); |
|
} |
|
|
|
return rv; |
|
} |
|
|
|
// FIXME: Does it *really* belong to here ? |
|
void FolderView::moveOrCopyFolders( const QList<QPointer<KMFolder> > &sources, KMFolder* destination, bool move ) |
|
{ |
|
// Disable drag during copy operation since it prevents from many crashes |
|
setDragEnabled( false ); |
|
|
|
if ( ! moveOrCopyFoldersInternal( sources, destination, move ) ) |
|
setDragEnabled( true ); // make sure we re-enable dragging if the above op fails |
|
// else drag will be re-enabled in slotFolderMoveOrCopyOperationFinished(). |
|
} |
|
|
|
bool FolderView::moveOrCopyFoldersInternal( const QList<QPointer<KMFolder> > &sources, KMFolder* destination, bool move ) |
|
{ |
|
KMFolderDir *parent = &(kmkernel->folderMgr()->dir()); |
|
if ( destination ) |
|
parent = destination->createChildFolder(); |
|
|
|
QStringList sourceFolderNames; |
|
|
|
// check if move/copy is possible at all |
|
for ( QList<QPointer<KMFolder> >::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it ) |
|
{ |
|
KMFolder* source = *it; |
|
|
|
// check if folder with same name already exits |
|
QString sourceFolderName; |
|
if ( source ) |
|
sourceFolderName = source->label(); |
|
|
|
if ( parent && |
|
( parent->hasNamedFolder( sourceFolderName ) || |
|
sourceFolderNames.contains( sourceFolderName ) ) ) |
|
{ |
|
KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> here because a folder with the same name already exists.</qt>", sourceFolderName ) ); |
|
return false; |
|
} |
|
sourceFolderNames.append( sourceFolderName ); |
|
|
|
// don't move/copy a folder that's still not completely moved/copied |
|
KMFolder *f = source; |
|
while ( f ) |
|
{ |
|
if ( f->moveInProgress() ) |
|
{ |
|
KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> because it is not completely copied itself.</qt>", sourceFolderName ) ); |
|
return false; |
|
} |
|
if ( f->parent() ) |
|
f = f->parent()->owner(); |
|
} |
|
|
|
QString message = i18n( "<qt>Cannot move or copy folder <b>%1</b> into a subfolder below itself.</qt>", sourceFolderName ); |
|
|
|
KMFolderDir* folderDir = parent; |
|
// check that the folder can be moved |
|
if ( source && source->child() ) |
|
{ |
|
while ( folderDir && ( folderDir != &kmkernel->folderMgr()->dir() ) && |
|
( folderDir != source->parent() ) ) |
|
{ |
|
if ( folderDir->indexOf( source ) != -1 ) |
|
{ |
|
KMessageBox::error( this, message ); |
|
return false; |
|
} |
|
folderDir = folderDir->parent(); |
|
} |
|
} |
|
|
|
if( source && source->child() && parent && |
|
( parent->path().indexOf( source->child()->path() + '/' ) == 0 ) ) { |
|
KMessageBox::error( this, message ); |
|
return false; |
|
} |
|
|
|
if( source && source->child() |
|
&& ( parent == source->child() ) ) { |
|
KMessageBox::error( this, message ); |
|
return false; |
|
} |
|
} |
|
|
|
// check if the source folders are independent of each other |
|
for ( QList<QPointer<KMFolder> >::ConstIterator it = sources.constBegin(); move && it != sources.constEnd(); ++it ) { |
|
KMFolderDir *parentDir = (*it)->child(); |
|
if ( !parentDir ) |
|
continue; |
|
for ( QList<QPointer<KMFolder> >::ConstIterator it2 = sources.constBegin(); it2 != sources.constEnd(); ++it2 ) { |
|
if ( *it == *it2 ) |
|
continue; |
|
KMFolderDir *childDir = (*it2)->parent(); |
|
do { |
|
if ( parentDir == childDir || parentDir->indexOf( childDir->owner() ) != -1 ) { |
|
KMessageBox::error( this, i18n("Moving the selected folders is not possible") ); |
|
return false; |
|
} |
|
childDir = childDir->parent(); |
|
} |
|
while ( childDir && childDir != &kmkernel->folderMgr()->dir() ); |
|
} |
|
} |
|
|
|
// de-select moved source folders (can cause crash due to unGetMsg() in KMHeaders) |
|
if ( move ) |
|
clearSelection(); |
|
|
|
// do the actual move/copy |
|
for ( QList<QPointer<KMFolder> >::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it ) |
|
{ |
|
KMFolder* source = *it; |
|
if ( move ) |
|
kmkernel->folderMgr()->moveFolder( source, parent ); |
|
else |
|
kmkernel->folderMgr()->copyFolder( source, parent ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void FolderView::slotFolderMoveOrCopyOperationFinished() |
|
{ |
|
setDragEnabled( true ); |
|
} |
|
|
|
void FolderView::slotCheckMail() |
|
{ |
|
KMFolder * folder = currentFolder(); |
|
|
|
if ( !folder ) |
|
return; |
|
|
|
if ( folder->storage() ) |
|
{ |
|
if ( KMAccount *acct = folder->storage()->account() ) |
|
kmkernel->acctMgr()->singleCheckMail( acct, true ); |
|
} |
|
} |
|
|
|
void FolderView::slotNewMessageToMailingList() |
|
{ |
|
KMFolder * folder = currentFolder(); |
|
|
|
if ( !folder ) |
|
return; |
|
|
|
KMCommand *command = new KMMailingListPostCommand( this, folder ); |
|
command->start(); |
|
} |
|
|
|
void FolderView::slotAccountRemoved( KMAccount * ) |
|
{ |
|
FolderViewItem * fvi = static_cast< FolderViewItem * >( firstItem() ); |
|
activateItem( fvi ); |
|
} |
|
|
|
// little static helper |
|
static bool folderHasCreateRights( const KMFolder *folder ) |
|
{ |
|
bool createRights = true; // we don't have acls for local folders yet |
|
if ( folder && folder->folderType() == KMFolderTypeImap ) { |
|
const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() ); |
|
createRights = imapFolder->userRights() == 0 || // hack, we should get the acls |
|
( imapFolder->userRights() > 0 && ( imapFolder->userRights() & KMail::ACLJobs::Create ) ); |
|
} else if ( folder && folder->folderType() == KMFolderTypeCachedImap ) { |
|
const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() ); |
|
createRights = dimapFolder->userRights() == 0 || |
|
( dimapFolder->userRights() > 0 && ( dimapFolder->userRights() & KMail::ACLJobs::Create ) ); |
|
} |
|
return createRights; |
|
} |
|
|
|
void FolderView::addChildFolder( KMFolder *fld, QWidget *par ) |
|
{ |
|
// Create a subfolder. |
|
// Requires creating the appropriate subdirectory and show a dialog |
|
if ( fld ) |
|
{ |
|
if ( !fld->createChildFolder() ) |
|
return; |
|
if ( !folderHasCreateRights( fld ) ) |
|
{ |
|
// FIXME: change this message to "Cannot create folder under ..." or similar |
|
const QString message = i18n( "<qt>Cannot create folder <b>%1</b> because of insufficient " |
|
"permissions on the server. If you think you should be able to create " |
|
"subfolders here, ask your administrator to grant you rights to do so." |
|
"</qt> " , fld->label() ); |
|
KMessageBox::error( par, message ); |
|
return; |
|
} |
|
} |
|
|
|
( new KMail::NewFolderDialog( par, fld ) )->show(); |
|
} |
|
|
|
|
|
void FolderView::slotAddChildFolder() |
|
{ |
|
addChildFolder( currentFolder(), this ); |
|
} |
|
|
|
void FolderView::slotCopyFolder() |
|
{ |
|
mFolderClipboard = selectedFolders(); |
|
mFolderClipboardMode = CopyFolders; |
|
updateCopyActions(); |
|
} |
|
|
|
void FolderView::slotCutFolder() |
|
{ |
|
mFolderClipboard = selectedFolders(); |
|
mFolderClipboardMode = CutFolders; |
|
updateCopyActions(); |
|
} |
|
|
|
void FolderView::slotPasteFolder() |
|
{ |
|
if ( mFolderClipboard.isEmpty() ) |
|
return; |
|
|
|
KMFolder *fld = currentFolder(); |
|
if ( !fld ) |
|
return; |
|
|
|
if ( mFolderClipboard.contains( fld ) ) |
|
mFolderClipboard.removeAll( fld ); |
|
|
|
if ( mFolderClipboard.isEmpty() ) |
|
return; |
|
|
|
moveOrCopyFolders( mFolderClipboard, fld, mFolderClipboardMode == CutFolders ); |
|
|
|
updateCopyActions(); |
|
} |
|
|
|
bool FolderView::selectFolderIfUnread(FolderViewItem *fvi, bool confirm) |
|
{ |
|
if ( |
|
!( |
|
fvi && fvi->folder() && |
|
( !fvi->folder()->ignoreNewMail() ) && |
|
( fvi->folder()->countUnread() > 0 ) |
|
) |
|
) |
|
return false; |
|
|
|
// yes: folder has unread messages. |
|
|
|
// Don't change into the trash or outbox folders. |
|
if ( |
|
fvi->folderType() == FolderViewItem::Trash || |
|
fvi->folderType() == FolderViewItem::Outbox |
|
) |
|
return false; |
|
|
|
if ( !confirm ) |
|
{ |
|
activateItem( fvi ); |
|
return true; // OK |
|
} |
|
|
|
// Need confirmation. |
|
|
|
// Skip drafts, sent mail and templates as well, when reading mail with the |
|
// space bar - but not when changing into the next folder with unread mail |
|
// via ctrl+ or ctrl- so we do this only if (confirm == true), which means |
|
// we are doing readOn. |
|
|
|
if ( |
|
fvi->folderType() == FolderViewItem::Drafts || |
|
fvi->folderType() == FolderViewItem::Templates || |
|
fvi->folderType() == FolderViewItem::SentMail |
|
) |
|
return false; |
|
|
|
// warn user that going to next folder - but keep track of |
|
// whether he wishes to be notified again in "AskNextFolder" |
|
// parameter (kept in the config file for kmail) |
|
if ( |
|
KMessageBox::questionYesNo( |
|
this, |
|
i18n( "<qt>Go to the next unread message in folder <b>%1</b>?</qt>" , fvi->folder()->label() ), |
|
i18n( "Go to Next Unread Message" ), |
|
KGuiItem( i18n( "Go To" ) ), |
|
KGuiItem( i18n( "Do Not Go To" ) ), // defaults |
|
"AskNextFolder", |
|
false |
|
) == KMessageBox::No |
|
) |
|
return true; // assume selected (do not continue looping) |
|
|
|
activateItem( fvi ); |
|
|
|
// FIXME: Which is the purpose of this signal ? Doesn't folderSelected() suffice ? |
|
//emit folderSelectedUnread( fvi->folder() ); |
|
|
|
return true; |
|
} |
|
|
|
void FolderView::focusItem( QTreeWidgetItem *item ) |
|
{ |
|
if( !item ) |
|
return; |
|
scrollToItem( item ); |
|
setFocus(); |
|
setCurrentItem( item, 0, QItemSelectionModel::NoUpdate ); |
|
} |
|
|
|
void FolderView::slotFocusNextFolder() |
|
{ |
|
if ( topLevelItemCount() < 1 ) |
|
return; |
|
|
|
QTreeWidgetItem * item = currentItem(); |
|
if ( item ) { |
|
QTreeWidgetItem *below = itemBelow( item ); |
|
focusItem( below ); |
|
} |
|
} |
|
|
|
void FolderView::slotFocusPrevFolder() |
|
{ |
|
if ( topLevelItemCount() < 1 ) |
|
return; |
|
|
|
QTreeWidgetItem * item = currentItem(); |
|
if ( item ) { |
|
QTreeWidgetItem *above = itemAbove( item ); |
|
focusItem( above ); |
|
} |
|
} |
|
|
|
void FolderView::slotSelectFocusedFolder() |
|
{ |
|
FolderViewItem *folderItem = dynamic_cast<FolderViewItem *>( currentItem() ); |
|
if ( !folderItem ) |
|
return; |
|
activateItem( folderItem ); |
|
} |
|
|
|
// |
|
// Methods for navigating folders with the keyboard |
|
// |
|
|
|
bool FolderView::selectNextUnreadFolder( bool confirm ) |
|
{ |
|
// first try from the current item (if any) to the end |
|
|
|
QTreeWidgetItem * current = currentItem(); |
|
|
|
if ( current ) |
|
{ |
|
QTreeWidgetItemIterator it( current, QTreeWidgetItemIterator::NotHidden ); |
|
++it; // skip current |
|
|
|
while ( *it ) |
|
{ |
|
bool folderSelected = selectFolderIfUnread( static_cast< FolderViewItem * >( *it ), |
|
confirm ); |
|
if ( folderSelected ) |
|
return true; |
|
++it; |
|
} |
|
} |
|
|
|
// didn't find a folder to select: try from the beginning up to current |
|
|
|
QTreeWidgetItem * begin = firstItem(); |
|
|
|
if ( begin ) |
|
{ |
|
QTreeWidgetItemIterator it( begin, QTreeWidgetItemIterator::NotHidden ); |
|
|
|
while ( *it ) |
|
{ |
|
if ( *it == current ) |
|
return false; // reached current |
|
|
|
bool folderSelected = selectFolderIfUnread( static_cast< FolderViewItem * >( *it ), |
|
confirm ); |
|
if ( folderSelected ) |
|
return true; |
|
++it; |
|
} |
|
} |
|
|
|
// No folder selected when we reach here |
|
return false; |
|
} |
|
|
|
bool FolderView::selectPrevUnreadFolder() |
|
{ |
|
// first try from the current item (if any) to the beginning |
|
|
|
QTreeWidgetItem * current = currentItem(); |
|
|
|
if ( current ) |
|
{ |
|
QTreeWidgetItemIterator it( current, QTreeWidgetItemIterator::NotHidden ); |
|
--it; // skip current |
|
|
|
while ( *it ) |
|
{ |
|
bool folderSelected = selectFolderIfUnread( static_cast< FolderViewItem * >( *it ), |
|
false /* don't confirm*/ ); |
|
if ( folderSelected ) |
|
return true; |
|
--it; |
|
} |
|
} |
|
|
|
// didn't find a folder to select: try from the end up to current |
|
|
|
QTreeWidgetItem * end = lastItem(); |
|
|
|
if ( end ) |
|
{ |
|
QTreeWidgetItemIterator it( end, QTreeWidgetItemIterator::NotHidden ); |
|
|
|
while ( *it ) |
|
{ |
|
if ( *it == current ) |
|
return false; // reached current |
|
|
|
bool folderSelected = selectFolderIfUnread( static_cast< FolderViewItem * >( *it ), |
|
false /* don't confirm*/ ); |
|
if ( folderSelected ) |
|
return true; |
|
--it; |
|
} |
|
} |
|
|
|
// No folder selected when we reach here |
|
return false; |
|
} |
|
|
|
void FolderView::readColorConfig() |
|
{ |
|
KConfigGroup cg = KMKernel::config()->group( "Reader" ); |
|
|
|
// Custom/System color support |
|
if ( !cg.readEntry( "defaultColors", true ) ) |
|
{ |
|
setUnreadCountColor( cg.readEntry( "UnreadMessage", QColor( Qt::blue ) ) ); |
|
setCloseToQuotaWarningColor( cg.readEntry( "CloseToQuotaColor", QColor( Qt::red ) ) ); |
|
} else { |
|
setUnreadCountColor( Qt::blue ); |
|
setCloseToQuotaWarningColor( Qt::blue ); |
|
} |
|
} |
|
|
|
bool FolderView::hideLocalInbox() const |
|
{ |
|
if ( !GlobalSettings::self()->hideLocalInbox() ) |
|
return false; |
|
KMFolder *localInbox = kmkernel->inboxFolder(); |
|
assert( localInbox ); |
|
// check if it is empty |
|
KMFolderOpener openInbox( localInbox, "FolderView" ); |
|
if ( localInbox->count() > 0 ) |
|
return false; |
|
// check if it has subfolders |
|
if ( localInbox->child() && !localInbox->child()->isEmpty() ) |
|
return false; |
|
// check if it is an account target |
|
if ( localInbox->hasAccounts() ) |
|
return false; |
|
return true; |
|
} |
|
|
|
void FolderView::handleMailListDrop(QDropEvent * event, KMFolder *destination ) |
|
{ |
|
KPIM::MailList list = KPIM::MailList::fromMimeData( event->mimeData() ); |
|
if ( list.isEmpty() ) { |
|
kWarning() << "Could not decode drag data!"; |
|
} else { |
|
QList<uint> serNums = MessageCopyHelper::serNumListFromMailList( list ); |
|
int action; |
|
if ( MessageCopyHelper::inReadOnlyFolder( serNums ) ) |
|
action = DragCopy; |
|
else |
|
action = dragMode(); |
|
if ( action == DragCopy || action == DragMove ) { |
|
new MessageCopyHelper( serNums, destination, action == DragMove, this ); |
|
} |
|
} |
|
} |
|
|
|
FolderViewItem * FolderView::findItemByFolder( const KMFolder *folder ) |
|
{ |
|
QTreeWidgetItemIterator it( this ); |
|
while( FolderViewItem * fvi = dynamic_cast<FolderViewItem *>( *it ) ) |
|
{ |
|
if ( fvi->folder() == folder ) |
|
return fvi; |
|
++it; |
|
} |
|
return 0; |
|
} |
|
|
|
FolderViewItem * FolderView::findItemByProperties( KPIM::FolderTreeWidgetItem::Protocol proto, KPIM::FolderTreeWidgetItem::FolderType type ) |
|
{ |
|
QTreeWidgetItemIterator it( this ); |
|
while( FolderViewItem * fvi = dynamic_cast<FolderViewItem *>( *it ) ) |
|
{ |
|
if ( ( fvi->protocol() == proto ) && ( fvi->folderType() == type ) ) |
|
return fvi; |
|
++it; |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// DND Related stuff |
|
// |
|
|
|
void FolderView::setDropIndicatorData( FolderViewItem * moveTargetItem, FolderViewItem *sortReferenceItem, DropInsertPosition sortReferencePosition) |
|
{ |
|
mDnDMoveTargetItem = moveTargetItem; |
|
mDnDSortReferenceItem = sortReferenceItem; |
|
mDnDSortReferencePosition = sortReferencePosition; |
|
} |
|
|
|
void FolderView::paintEvent( QPaintEvent *e ) |
|
{ |
|
FolderTreeWidget::paintEvent( e ); |
|
|
|
if ( !( mDnDMoveTargetItem || mDnDSortReferenceItem ) ) |
|
return; |
|
|
|
QPainter p( viewport() ); |
|
|
|
if ( mDnDMoveTargetItem ) |
|
{ |
|
QRect r = visualItemRect( mDnDMoveTargetItem ); |
|
if ( r.isValid() ) |
|
{ |
|
p.setPen( QPen( palette().brush( QPalette::Text ), 3 ) ); |
|
p.drawRect( r ); |
|
} |
|
} |
|
|
|
if ( mDnDSortReferenceItem ) |
|
{ |
|
QRect r = visualItemRect( mDnDSortReferenceItem ); |
|
if ( r.isValid() ) |
|
{ |
|
p.setPen( QPen( palette().brush( QPalette::Text ), 3 ) ); |
|
if ( mDnDSortReferencePosition == AboveReference ) |
|
p.drawLine( r.left(), r.top(), r.right(), r.top() ); |
|
else |
|
p.drawLine( r.left(), r.bottom(), r.right(), r.bottom() ); |
|
} |
|
} |
|
} |
|
|
|
|
|
FolderView::DragMode FolderView::dragMode( bool alwaysAsk, bool canMove ) |
|
{ |
|
DragMode action = DragCancel; |
|
int keybstate = kapp->keyboardModifiers(); |
|
if ( keybstate & Qt::CTRL ) |
|
{ |
|
action = DragCopy; |
|
} else if ( keybstate & Qt::SHIFT ) |
|
{ |
|
action = DragMove; |
|
} else { |
|
// FIXME: anybody sets this option to false ? |
|
if ( GlobalSettings::self()->showPopupAfterDnD() || alwaysAsk ) |
|
{ |
|
KMenu menu; |
|
QAction *moveAction = menu.addAction( KIcon( "go-jump"), i18n( "&Move Here" ) ); |
|
if ( ! canMove ) |
|
moveAction->setEnabled( false ); // keep it there, but disable |
|
QAction *copyAction = menu.addAction( KIcon( "edit-copy" ), i18n( "&Copy Here" ) ); |
|
menu.addSeparator(); |
|
menu.addAction( KIcon( "dialog-cancel" ), i18n( "C&ancel" ) ); |
|
|
|
QAction *menuChoice = menu.exec( QCursor::pos() ); |
|
if ( menuChoice == moveAction ) |
|
action = canMove ? DragMove : DragCopy; |
|
else if ( menuChoice == copyAction ) |
|
action = DragCopy; |
|
else |
|
action = DragCancel; |
|
} else |
|
action = canMove ? DragMove : DragCopy; |
|
} |
|
return action; |
|
} |
|
|
|
void FolderView::buildDragItemList( QTreeWidgetItem *item, QList< FolderViewItem * > &lDragItemList ) |
|
{ |
|
if ( !item ) |
|
return; |
|
|
|
if ( item->isSelected() ) |
|
{ |
|
if ( item != invisibleRootItem() ) // sanity check |
|
{ |
|
lDragItemList.append( static_cast< FolderViewItem * >( item ) ); |
|
return; |
|
} |
|
} |
|
|
|
int cc = item->childCount(); |
|
int i = 0; |
|
while ( i < cc ) |
|
{ |
|
QTreeWidgetItem * c = item->child( i ); |
|
if ( c ) |
|
buildDragItemList( c, lDragItemList ); |
|
i++; |
|
} |
|
} |
|
|
|
|
|
void FolderView::startDrag( Qt::DropActions ) |
|
{ |
|
// We don't use selectedFolders() here since: |
|
// - We want a coherent set of dragged folders: if dragging a parent |
|
// don't drag the children (since the children are contained in parent) |
|
// - We eventually want to start drag also for items without a folder |
|
// (so they can be sorted by dnd) |
|
|
|
QList< FolderViewItem * > lDraggedItems; |
|
buildDragItemList( invisibleRootItem(), lDraggedItems ); |
|
|
|
if ( lDraggedItems.isEmpty() ) |
|
return; // nothing to drag |
|
|
|
// By the way, unselect the items that aren't really dragged |
|
setUpdatesEnabled( false ); |
|
clearSelection(); |
|
|
|
QList< QPointer< KMFolder > > lDraggedFolders; |
|
for ( QList< FolderViewItem * >::Iterator it = lDraggedItems.begin(); it != lDraggedItems.end(); ++it ) |
|
{ |
|
if ( ( *it )->folder() ) |
|
lDraggedFolders.append( ( *it )->folder() ); |
|
( *it )->setSelected( true ); |
|
} |
|
|
|
setUpdatesEnabled( true ); |
|
|
|
viewport()->update(); |
|
|
|
QDrag *drag = new QDrag(this); |
|
QMimeData *mimeData = new QMimeData(); |
|
QByteArray dummy( 1, 'x' ); |
|
|
|
mimeData->setData( DraggedFolderList::mimeDataType(), dummy ); |
|
|
|
drag->setMimeData( mimeData ); |
|
drag->setPixmap( SmallIcon( "folder" ) ); |
|
// We use a trick to force Qt to display two different cursors depending |
|
// on our actions (even if they are not actually copy or move) |
|
drag->setDragCursor( QCursor( Qt::SplitVCursor ).pixmap(), Qt::MoveAction ); |
|
|
|
|
|
DraggedFolderList::set( lDraggedFolders ); |
|
|
|
drag->exec( Qt::CopyAction | Qt::MoveAction ); |
|
|
|
DraggedFolderList::clear(); |
|
} |
|
|
|
|
|
QStringList FolderView::mimeTypes() const |
|
{ |
|
QStringList ret; |
|
ret.append( DraggedFolderList::mimeDataType() ); |
|
ret.append( KPIM::MailList::mimeDataType() ); |
|
return ret; |
|
} |
|
|
|
Qt::DropActions FolderView::supportedDropActions() const |
|
{ |
|
return Qt::MoveAction | Qt::CopyAction; |
|
} |
|
|
|
void FolderView::dragEnterEvent( QDragEnterEvent *e ) |
|
{ |
|
if ( |
|
e->mimeData()->hasFormat( DraggedFolderList::mimeDataType() ) || |
|
e->mimeData()->hasFormat( KPIM::MailList::mimeDataType() ) |
|
) |
|
{ |
|
// show all the items (we might want to drop anywhere) |
|
QTreeWidgetItemIterator it( this ); |
|
while ( *it ) |
|
{ |
|
if ( ( *it )->isHidden() ) |
|
( *it )->setHidden( false ); |
|
++it; |
|
} |
|
|
|
e->acceptProposedAction(); |
|
} else |
|
e->ignore(); |
|
} |
|
|
|
|
|
|
|
bool FolderView::acceptDropCopyOrMoveMessages( FolderViewItem *item ) |
|
{ |
|
if ( !item ) |
|
return false; // obviously |
|
|
|
if ( item->protocol() == FolderViewItem::Search ) |
|
return false; // nothing can be dragged into search folders |
|
|
|
KMFolder *fld = item->folder(); |
|
|
|
if ( !fld ) |
|
return false; // no folder to drop into |
|
|
|
if ( |
|
fld->moveInProgress() || |
|
fld->isReadOnly() || |
|
(fld->noContent() && item->childCount() == 0) || |
|
(fld->noContent() && item->isExpanded()) |
|
) |
|
return false; // content can't be added to folder |
|
|
|
return true; |
|
} |
|
|
|
void FolderView::handleMessagesDropEvent( QDropEvent *e ) |
|
{ |
|
FolderViewItem * item = static_cast<FolderViewItem *>( itemAt( e->pos() ) ); |
|
if ( !item ) |
|
return; |
|
|
|
if ( !acceptDropCopyOrMoveMessages( item ) ) |
|
return; // hum.. can't accept it |
|
|
|
int action; |
|
if ( mainWidget()->folder() && !mainWidget()->folder()->canDeleteMessages() ) |
|
action = DragCopy; |
|
else |
|
action = dragMode(); |
|
|
|
e->setDropAction( Qt::CopyAction ); |
|
e->accept(); |
|
|
|
// KMHeaders does copy/move itself |
|
if ( action == DragMove && item->folder() ) |
|
emit folderDrop( item->folder() ); |
|
else if ( action == DragCopy && item->folder() ) |
|
emit folderDropCopy( item->folder() ); |
|
else |
|
handleMailListDrop( e, item->folder() ); |
|
} |
|
|
|
void FolderView::handleMessagesDragMoveEvent( QDragMoveEvent *e ) |
|
{ |
|
FolderViewItem * item = static_cast<FolderViewItem *>( itemAt( e->pos() ) ); |
|
|
|
// dragging messages |
|
if ( item && acceptDropCopyOrMoveMessages( item ) ) |
|
{ |
|
e->accept( visualItemRect( item ) ); |
|
KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Copy or Move Messages to %1", item->labelText() ) ); |
|
setDropIndicatorData( item, 0 ); |
|
} else { |
|
e->ignore( visualItemRect( item ) ); |
|
KPIM::BroadcastStatus::instance()->setStatusMsg( "" ); |
|
setDropIndicatorData( 0, 0 ); |
|
} |
|
} |
|
|
|
void FolderView::handleFoldersDragMoveEvent( QDragMoveEvent *e ) |
|
{ |
|
e->ignore(); |
|
} |
|
|
|
void FolderView::handleFoldersDropEvent( QDropEvent *e ) |
|
{ |
|
e->ignore(); |
|
} |
|
|
|
void FolderView::dragMoveEvent( QDragMoveEvent *e ) |
|
{ |
|
// Something is actually being dragged over our widget |
|
|
|
// First of all call the base class implementation in order to |
|
// handle scrolling and item expanding |
|
FolderTreeWidget::dragMoveEvent( e ); |
|
|
|
if ( e->mimeData()->hasFormat( DraggedFolderList::mimeDataType() ) ) |
|
handleFoldersDragMoveEvent( e ); |
|
else if ( e->mimeData()->hasFormat( KPIM::MailList::mimeDataType() ) ) |
|
handleMessagesDragMoveEvent( e ); |
|
else |
|
e->ignore(); |
|
|
|
viewport()->update(); // trigger a repaint |
|
} |
|
|
|
void FolderView::dropEvent( QDropEvent *e ) |
|
{ |
|
if ( e->mimeData()->hasFormat( DraggedFolderList::mimeDataType() ) ) |
|
handleFoldersDropEvent( e ); |
|
else if ( e->mimeData()->hasFormat( KPIM::MailList::mimeDataType() ) ) |
|
handleMessagesDropEvent( e ); |
|
else e->ignore(); // ignore by default |
|
|
|
mDnDMoveTargetItem = 0; |
|
mDnDSortReferenceItem = 0; |
|
viewport()->update(); // trigger a repaint |
|
} |
|
|
|
void FolderView::dragLeaveEvent( QDragLeaveEvent * ) |
|
{ |
|
mDnDMoveTargetItem = 0; |
|
mDnDSortReferenceItem = 0; |
|
viewport()->update(); // trigger a repaint |
|
} |
|
|
|
void FolderView::slotFolderExpanded( QTreeWidgetItem *item ) |
|
{ |
|
FolderViewItem * fvi = static_cast<FolderViewItem *>( item ); |
|
if ( !fvi || !fvi->folder() || !fvi->folder()->storage() ) |
|
return; |
|
|
|
fvi->setDataSize( fvi->folder()->storage()->folderSize() ); |
|
|
|
if( fvi->folder()->folderType() != KMFolderTypeImap ) |
|
return; // nothing more to do |
|
|
|
KMFolderImap *folder = static_cast<KMFolderImap*>( fvi->folder()->storage() ); |
|
|
|
// if we should list all folders we limit this to the root folder |
|
if ( |
|
!folder->account() || |
|
( |
|
( !folder->account()->listOnlyOpenFolders() ) && |
|
static_cast<QTreeWidgetItem *>( fvi )->parent() |
|
) |
|
) |
|
return; |
|
|
|
if ( folder->getSubfolderState() != KMFolderImap::imapNoInformation ) |
|
return; |
|
|
|
// the tree will be reloaded after that |
|
bool success = folder->listDirectory(); |
|
if (!success) |
|
fvi->setExpanded( false ); // failed to list the directory |
|
|
|
if ( ( fvi->childCount() == 0 ) && static_cast<QTreeWidgetItem *>( fvi )->parent() ) |
|
fvi->setChildIndicatorPolicy( QTreeWidgetItem::DontShowIndicatorWhenChildless ); // no children after listing |
|
} |
|
|
|
void FolderView::slotFolderCollapsed( QTreeWidgetItem* item ) |
|
{ |
|
FolderViewItem * fvi = static_cast<FolderViewItem *>( item ); |
|
if ( !fvi ) |
|
return; // sanity check |
|
|
|
resetFolderList( fvi, false ); |
|
} |
|
|
|
void FolderView::createFolderList( QStringList *folderNames, QList< QPointer< KMFolder > > *folders, int flags ) |
|
{ |
|
// Clear the buffers unless explicitly forbidden |
|
if ( !( flags & AppendListing ) ) |
|
{ |
|
if ( folderNames ) |
|
folderNames->clear(); |
|
if ( folders ) |
|
folders->clear(); |
|
} |
|
|
|
// iterate |
|
for ( QTreeWidgetItemIterator it( this ); *it; ++it ) |
|
{ |
|
FolderViewItem *fvi = static_cast< FolderViewItem * >( *it ); |
|
|
|
if ( !fvi->folder() ) |
|
continue; |
|
|
|
switch( fvi->folder()->folderType() ) |
|
{ |
|
case KMFolderTypeImap: |
|
if( !( flags & IncludeImapFolders ) ) |
|
continue; |
|
break; |
|
case KMFolderTypeCachedImap: |
|
if( !( flags & IncludeCachedImapFolders ) ) |
|
continue; |
|
break; |
|
case KMFolderTypeMbox: |
|
if( !( flags & IncludeLocalFolders ) ) |
|
continue; |
|
break; |
|
case KMFolderTypeMaildir: |
|
if( !( flags & IncludeLocalFolders ) ) |
|
continue; |
|
break; |
|
case KMFolderTypeSearch: |
|
if( !( flags & IncludeSearchFolders ) ) |
|
continue; |
|
break; |
|
case KMFolderTypeUnknown: |
|
if( !( flags & IncludeUnknownFolders ) ) |
|
continue; |
|
break; |
|
} |
|
if ( flags & SkipFoldersWithNoContent ) |
|
{ |
|
if ( fvi->folder()->noContent() ) |
|
continue; |
|
} |
|
if ( flags & SkipFoldersWithNoChildren ) |
|
{ |
|
if ( fvi->folder()->noChildren() ) |
|
continue; |
|
} |
|
|
|
if ( folderNames ) |
|
{ |
|
if ( flags & IncludeFolderIndentation ) |
|
{ |
|
// compute depths |
|
int depth = 0; |
|
QTreeWidgetItem *father = ((QTreeWidgetItem *)fvi)->parent(); |
|
while ( father ) |
|
{ |
|
father = father->parent(); |
|
depth++; |
|
} |
|
QString prefix; |
|
prefix.fill( ' ', 2 * depth ); |
|
folderNames->append( prefix + fvi->labelText() ); |
|
} else { |
|
folderNames->append( fvi->labelText() ); |
|
} |
|
} |
|
|
|
if ( folders ) |
|
folders->append(fvi->folder()); |
|
} |
|
} |
|
|
|
void FolderView::resetFolderList( FolderViewItem *item, bool startList ) |
|
{ |
|
if ( !item ) |
|
item = static_cast< FolderViewItem * >( currentItem() ); |
|
if ( !item ) |
|
return; |
|
if ( !item->folder() ) |
|
return; |
|
if ( item->folder()->folderType() != KMFolderTypeImap ) |
|
return; |
|
|
|
KMFolderImap *folder = static_cast< KMFolderImap * >( item->folder()->storage() ); |
|
folder->setSubfolderState( KMFolderImap::imapNoInformation ); |
|
if ( startList ) |
|
folder->listDirectory(); |
|
} |
|
|
|
void FolderView::slotResetFolderList() |
|
{ |
|
resetFolderList( 0, false ); |
|
} |
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// FolderViewItem |
|
// |
|
|
|
FolderViewItem::FolderViewItem( |
|
FolderView *parent, |
|
const QString & name, |
|
KMFolder *folder, |
|
FolderViewItem::Protocol protocol, |
|
FolderViewItem::FolderType type |
|
) |
|
: QObject( 0 ), |
|
KPIM::FolderTreeWidgetItem( parent, name, protocol, type ), |
|
mFolder( folder ), mFlags( 0 ) |
|
{ |
|
setObjectName( name ); |
|
setIcon( FolderView::LabelColumn, KIcon( normalIcon() ) ); |
|
} |
|
|
|
FolderViewItem::FolderViewItem( |
|
FolderViewItem *parent, |
|
const QString & name, |
|
KMFolder *folder, |
|
FolderViewItem::Protocol protocol, |
|
FolderViewItem::FolderType type |
|
) |
|
: QObject( 0 ), |
|
KPIM::FolderTreeWidgetItem( parent, name, protocol, type ), |
|
mFolder( folder ), mFlags( 0 ) |
|
{ |
|
setObjectName( name ); |
|
setIcon( FolderView::LabelColumn, KIcon( normalIcon() ) ); |
|
} |
|
|
|
QString FolderViewItem::normalIcon() const |
|
{ |
|
QString icon; |
|
|
|
KMFolder * fld = folder(); |
|
|
|
// Roots |
|
if ( folderType() == Root ) |
|
{ |
|
switch ( protocol() ) |
|
{ |
|
case Imap: |
|
case CachedImap: |
|
case News: |
|
icon = "network-server"; |
|
break; |
|
case Search: |
|
icon = "system-search"; |
|
break; |
|
default: |
|
icon = "folder"; |
|
break; |
|
} |
|
} else { |
|
// special folders |
|
switch ( folderType() ) |
|
{ |
|
case Inbox: |
|
icon = "mail-folder-inbox"; |
|
break; |
|
case Outbox: |
|
icon = "mail-folder-outbox"; |
|
break; |
|
case SentMail: |
|
icon = "mail-folder-sent"; |
|
break; |
|
case Trash: |
|
icon = "user-trash"; |
|
break; |
|
case Drafts: |
|
icon = "document-properties"; |
|
break; |
|
case Templates: |
|
icon = "document-new"; |
|
break; |
|
default: |
|
icon = kmkernel->iCalIface().folderPixmap( folderType() ); |
|
break; |
|
} |
|
|
|
// non-root search folders |
|
if ( protocol() == Search ) |
|
icon = "edit-find-mail"; |
|
|
|
// empty folders with no associated content |
|
if ( fld && fld->noContent() ) |
|
icon = "folder-grey"; |
|
} |
|
|
|
// allow icon customisation by the folder itself |
|
if ( fld && fld->useCustomIcons() ) |
|
icon = fld->normalIconPath(); |
|
|
|
// fallback |
|
if ( icon.isEmpty() ) |
|
icon = "folder"; |
|
|
|
return icon; |
|
} |
|
|
|
QString FolderViewItem::unreadIcon() const |
|
{ |
|
KMFolder * fld = folder(); |
|
|
|
QString icon; |
|
|
|
if ( |
|
!fld || ( folderType() == Root ) || fld->isSystemFolder() || |
|
kmkernel->folderIsTrash( fld ) || kmkernel->folderIsTemplates( fld ) || |
|
kmkernel->folderIsDraftOrOutbox( fld ) |
|
) |
|
icon = normalIcon(); |
|
|
|
if ( fld && fld->useCustomIcons() ) |
|
{ |
|
icon = fld->unreadIconPath(); |
|
if ( icon.isEmpty() ) |
|
icon = fld->normalIconPath(); |
|
} |
|
|
|
if ( icon.isEmpty() ) |
|
{ |
|
if ( fld && fld->noContent() ) |
|
icon = "folder-open-grey"; |
|
else { |
|
icon = kmkernel->iCalIface().folderPixmap( folderType() ); |
|
if ( icon.isEmpty() ) |
|
icon = "folder-open"; |
|
} |
|
} |
|
|
|
return icon; |
|
} |
|
|
|
QString FolderViewItem::configGroupName() const |
|
{ |
|
KMFolder * fld = folder(); |
|
|
|
if ( fld ) |
|
return fld->configGroupName(); |
|
|
|
if ( folderType() != Root ) |
|
return QString(); |
|
|
|
if ( protocol() == Local ) // local root item |
|
return "Folder_local_root"; // FIXME: Why we're using a different prefix here ? Folder-Virtual-Local-Root wouldn't be nice ? |
|
|
|
if ( protocol() == Search ) |
|
return "Folder_search"; // FIXME: Why we're using a different prefix here ? Folder-Virtual-Search wouldn't be nice ? |
|
|
|
return QString(); |
|
} |
|
|
|
void FolderViewItem::slotIconsChanged() |
|
{ |
|
// This slot is connected to the folder()'s iconsChanged() signal. |
|
KMFolder *fld = folder(); |
|
|
|
if ( !fld ) |
|
return; // should never happen.. but well |
|
|
|
KPIM::FolderTreeWidgetItem::FolderType newType = folderType(); |
|
if ( kmkernel->iCalIface().isResourceFolder( fld ) ) |
|
newType = kmkernel->iCalIface().folderType( fld ); |
|
|
|
// reload the folder tree if the type changed, needed because of the |
|
// various type-dependent folder hiding options |
|
if ( folderType() != newType ) |
|
static_cast<FolderView*>( treeWidget() )->slotDelayedReload(); |
|
setFolderType( newType ); |
|
|
|
if ( unreadCount() > 0 ) |
|
setIcon( FolderView::LabelColumn, KIcon( unreadIcon() ) ); |
|
else |
|
setIcon( FolderView::LabelColumn, KIcon( normalIcon() ) ); |
|
|
|
emit iconChanged( this ); |
|
} |
|
|
|
void FolderViewItem::slotNameChanged() |
|
{ |
|
// This slot is connected to the folder()'s nameChanged() signal. |
|
KMFolder *fld = folder(); |
|
|
|
if ( !fld ) |
|
return; // should never happen.. but well |
|
|
|
setLabelText( fld->label() ); |
|
|
|
emit nameChanged( this ); |
|
} |
|
|
|
void FolderViewItem::slotNoContentChanged() |
|
{ |
|
// reload the folder tree if the no content state changed, needed because |
|
// we hide no-content folders if their child nodes are hidden |
|
QTimer::singleShot( 0, static_cast<FolderView*>( treeWidget() ), |
|
SLOT( slotRreload() ) ); |
|
} |
|
|
|
void FolderViewItem::slotFolderCountsChanged( KMFolder * ) |
|
{ |
|
// This slot is connected to several folder() signals related |
|
// to unread, total and size count changes. |
|
// We mark this item as "counts dirty" and trigger |
|
// a delayed update in the tree widget. |
|
|
|
if ( mFlags & CountsDirty ) |
|
return; // already marked as dirty (this means that an update in the view is pending) |
|
mFlags |= CountsDirty; |
|
|
|
FolderView * tree = dynamic_cast< FolderView *>( treeWidget() ); |
|
if ( !tree ) |
|
return; // hum.. should never happen |
|
tree->triggerLazyUpdateCounts(); |
|
} |
|
|
|
|
|
bool FolderViewItem::updateCounts() |
|
{ |
|
// This is usually called by the FolderView in response |
|
// to a delayed trigger (see slotFolderCountsChanged()) |
|
bool openFolder = mFlags & OpenFolderForCountUpdate; |
|
|
|
// Clear dirty mark |
|
mFlags &= ~( CountsDirty | OpenFolderForCountUpdate ); |
|
|
|
// sanity check |
|
FolderView * tree = dynamic_cast< FolderView *>( treeWidget() ); |
|
if ( !tree ) |
|
return false; // hum.. should never happen |
|
|
|
KMFolder * fld = folder(); |
|
|
|
if ( fld && openFolder ) |
|
{ |
|
if ( fld->isOpened() ) |
|
openFolder = false; // already open, no need to close it |
|
else |
|
fld->open( "updatecount" ); // shouldn't we check the error code ? |
|
} |
|
|
|
// We will force text updates if the children counts have changed |
|
bool someCountsChanged = false; |
|
|
|
if ( childCount() > 0 ) |
|
someCountsChanged = updateChildrenCounts(); |
|
|
|
if ( fld && !fld->noContent() ) |
|
{ |
|
int count = fld->countUnread(); |
|
if ( someCountsChanged || ( count != unreadCount() ) ) |
|
{ |
|
someCountsChanged = true; |
|
adjustUnreadCount( count ); |
|
} |
|
|
|
if ( tree->totalColumnVisible() ) |
|
{ |
|
// get the cached count if the folder is not open |
|
count = fld->count( !fld->isOpened() ); |
|
if ( someCountsChanged || ( count != totalCount() ) ) |
|
{ |
|
someCountsChanged = true; |
|
setTotalCount( count ); |
|
} |
|
} |
|
|
|
if ( tree->dataSizeColumnVisible() ) |
|
{ |
|
qint64 size = fld->storage()->folderSize(); |
|
if ( someCountsChanged || ( size != dataSize() ) ) |
|
{ |
|
someCountsChanged = true; |
|
setDataSize( size ); |
|
} |
|
} |
|
|
|
if ( fld && ( isCloseToQuota() != fld->storage()->isCloseToQuota() ) ) |
|
setIsCloseToQuota( fld->storage()->isCloseToQuota() ); |
|
|
|
if ( openFolder ) // we opened it |
|
fld->close( "updatecount" ); |
|
|
|
} else { |
|
|
|
if ( someCountsChanged || ( unreadCount() != 0 ) ) |
|
{ |
|
someCountsChanged = true; |
|
adjustUnreadCount( 0 ); |
|
} |
|
if ( ( someCountsChanged || ( totalCount() != 0 ) ) && tree->totalColumnVisible() ) |
|
{ |
|
someCountsChanged = true; |
|
setTotalCount( 0 ); |
|
} |
|
if ( ( someCountsChanged || ( dataSize() != -1 ) ) && tree->dataSizeColumnVisible() ) |
|
{ |
|
someCountsChanged = true; |
|
setDataSize( -1 ); |
|
} |
|
if ( isCloseToQuota() ) |
|
setIsCloseToQuota( false ); |
|
} |
|
|
|
// tell the kernel that one of the counts has changed |
|
kmkernel->messageCountChanged(); // FIXME: does this really need to be triggered from here ? |
|
|
|
return someCountsChanged; |
|
} |
|
|
|
void FolderViewItem::adjustUnreadCount( int newUnreadCount ) |
|
{ |
|
int oldUnreadCount = unreadCount(); |
|
|
|
// adjust the icons if the folder is now newly unread or |
|
// now newly not-unread |
|
if ( newUnreadCount != 0 && oldUnreadCount == 0 ) |
|
setIcon( FolderView::LabelColumn, KIcon( unreadIcon() ) ); |
|
if ( oldUnreadCount != 0 && newUnreadCount == 0 ) |
|
setIcon( FolderView::LabelColumn, KIcon( normalIcon() ) ); |
|
|
|
setUnreadCount( newUnreadCount ); |
|
} |
|
|
|
void FolderViewItem::slotShowExpiryProperties() |
|
{ |
|
if ( !folder() ) |
|
return; |
|
|
|
FolderView * fv = dynamic_cast< FolderView * >( treeWidget() ); |
|
if ( !fv ) |
|
return; |
|
|
|
MainFolderView *mfv = fv->mainWidget()->mainFolderView(); |
|
if ( !mfv ) |
|
return; |
|
|
|
KMail::ExpiryPropertiesDialog *dlg = new KMail::ExpiryPropertiesDialog( mfv, folder() ); |
|
dlg->show(); |
|
} |
|
|
|
void FolderViewItem::slotAssignShortcut() |
|
{ |
|
if ( !folder() ) |
|
return; |
|
|
|
KMail::FolderShortcutDialog *dlg = new KMail::FolderShortcutDialog( folder(), kmkernel->getKMMainWidget(), treeWidget() ); |
|
dlg->exec(); |
|
} |
|
|
|
bool FolderViewItem::operator < ( const QTreeWidgetItem &other ) const |
|
{ |
|
FolderView * w = static_cast< FolderView * >( treeWidget() ); |
|
const FolderViewItem * oitem = static_cast< const FolderViewItem * >( &other ); |
|
|
|
if ( w->sortingPolicy() == FolderView::SortByDragAndDropKey ) |
|
return mDnDSortingKey < oitem->dndSortingKey(); |
|
|
|
int sortCol = w->sortColumn(); |
|
if ( sortCol < 0 ) |
|
return true; // just "yes" :D |
|
|
|
if ( sortCol == FolderView::UnreadColumn ) |
|
return unreadCount() < oitem->unreadCount(); |
|
if ( sortCol == FolderView::TotalColumn ) |
|
return totalCount() < oitem->totalCount(); |
|
if ( sortCol == FolderView::DataSizeColumn ) |
|
return dataSize() < oitem->dataSize(); |
|
if ( sortCol == FolderView::LabelColumn ) |
|
{ |
|
// Special sorting based on the item type |
|
int thisProto = ( int ) protocol(); |
|
int thatProto = ( int ) oitem->protocol(); |
|
if ( thisProto < thatProto ) |
|
return true; |
|
if ( thisProto > thatProto ) |
|
return false; |
|
|
|
int thisType = ( int ) folderType(); |
|
int thatType = ( int ) oitem->folderType(); |
|
if ( thisType < thatType ) |
|
return true; |
|
if ( thisType > thatType ) |
|
return false; |
|
|
|
// and finally compare by name |
|
return labelText() < oitem->labelText(); |
|
} |
|
|
|
return text(sortCol) < other.text(sortCol); |
|
} |
|
|
|
|
|
KPIM::FolderTreeWidgetItem::Protocol FolderViewItem::protocolByFolder( KMFolder *fld ) |
|
{ |
|
if ( !fld ) |
|
return NONE; // sanity check |
|
|
|
switch ( fld->folderType() ) |
|
{ |
|
case KMFolderTypeImap: |
|
return Imap; |
|
case KMFolderTypeCachedImap: |
|
return CachedImap; |
|
case KMFolderTypeMbox: |
|
case KMFolderTypeMaildir: |
|
return Local; |
|
case KMFolderTypeSearch: |
|
return Search; |
|
default: // make gcc happy |
|
break; |
|
} |
|
|
|
return NONE; |
|
} |
|
|
|
KPIM::FolderTreeWidgetItem::FolderType FolderViewItem::folderTypeByFolder( KMFolder *fld ) |
|
{ |
|
if ( !fld ) |
|
return Other; // sanity check |
|
|
|
if ( fld == kmkernel->inboxFolder() ) |
|
return Inbox; |
|
|
|
if ( kmkernel->folderIsDraftOrOutbox( fld ) ) |
|
return ( fld == kmkernel->outboxFolder() ) ? Outbox : Drafts; |
|
|
|
if ( kmkernel->folderIsSentMailFolder( fld ) ) |
|
return SentMail; |
|
|
|
if ( kmkernel->folderIsTrash( fld ) ) |
|
return Trash; |
|
|
|
if ( kmkernel->folderIsTemplates( fld ) ) |
|
return Templates; |
|
|
|
if( kmkernel->iCalIface().isResourceFolder( fld ) ) |
|
return kmkernel->iCalIface().folderType( fld ); |
|
|
|
// System folders on dimap or imap which are not resource folders are |
|
// inboxes. Urgs. |
|
if ( |
|
fld->isSystemFolder() && |
|
!kmkernel->iCalIface().isResourceFolder( fld ) && |
|
( fld->folderType() == KMFolderTypeImap || fld->folderType() == KMFolderTypeCachedImap ) |
|
) |
|
return Inbox; |
|
|
|
return Other; // dunno what is this |
|
} |
|
|
|
} // namespace KMail |
|
|
|
|
|
#include "folderview.moc"
|
|
|