pane.cpp 34.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
    Copyright (c) 2009 Kevin Ottens <ervin@kde.org>

    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.
*/

19
#include "pane.h"
20

21 22
#include <KDE/KActionCollection>
#include <KDE/KActionMenu>
23 24 25
#include <KDE/KIcon>
#include <KDE/KLocale>
#include <KDE/KMenu>
26
#include <KDE/KXMLGUIClient>
27

28 29
#include <KToggleAction>

30
#include <QtCore/QAbstractItemModel>
31 32 33 34 35
#include <QAbstractProxyModel>
#include <QItemSelectionModel>
#include <QTabBar>
#include <QToolButton>
#include <QMouseEvent>
36
#include <QHeaderView>
37

38 39
#include <akonadi/etmviewstatesaver.h>

40 41
#include "storagemodel.h"
#include "widget.h"
42
#include "core/settings.h"
Laurent Montel's avatar
Laurent Montel committed
43
#include "core/manager.h"
44
#include <akonadi/kmime/messagestatus.h>
Volker Krause's avatar
Volker Krause committed
45 46
#include "core/model.h"

47 48 49 50 51 52
namespace MessageList
{

class Pane::Private
{
public:
Laurent Montel's avatar
Laurent Montel committed
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
    Private( Pane *owner )
        : q( owner ),
          mXmlGuiClient( 0 ),
          mActionMenu( 0 ),
          mModel( 0 ),
          mSelectionModel( 0 ),
          mPreSelectionMode( Core::PreSelectLastSelected ),
          mNewTabButton( 0 ),
          mCloseTabButton( 0 ),
          mCloseTabAction( 0 ),
          mActivateNextTabAction( 0 ),
          mActivatePreviousTabAction( 0 ),
          mMoveTabLeftAction( 0 ),
          mMoveTabRightAction( 0 ),
          mPreferEmptyTab( false ),
          mMaxTabCreated( 0 )
    { }

    void onSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected );
    void onNewTabClicked();
    void onCloseTabClicked();
    void activateTab();
    void closeTab( QWidget * );
    void onCurrentTabChanged();
    void onTabContextMenuRequest( const QPoint &pos );
    void activateNextTab();
    void activatePreviousTab();
    void moveTabLeft();
    void moveTabRight();
    void moveTabBackward();
    void moveTabForward();
    void changeQuicksearchVisibility(bool);
    void addActivateTabAction(int i);
    QItemSelection mapSelectionToSource( const QItemSelection &selection ) const;
    QItemSelection mapSelectionFromSource( const QItemSelection &selection ) const;
    void updateTabControls();

    Pane * const q;

    KXMLGUIClient *mXmlGuiClient;
    KActionMenu *mActionMenu;

    QAbstractItemModel *mModel;
    QItemSelectionModel *mSelectionModel;
    Core::PreSelectionMode mPreSelectionMode;

    QHash<Widget*, QItemSelectionModel*> mWidgetSelectionHash;
    QList<const QAbstractProxyModel*> mProxyStack;

    QToolButton *mNewTabButton;
    QToolButton *mCloseTabButton;
    KAction *mCloseTabAction;
    KAction *mActivateNextTabAction;
    KAction *mActivatePreviousTabAction;
    KAction *mMoveTabLeftAction;
    KAction *mMoveTabRightAction;
    bool mPreferEmptyTab;
    int mMaxTabCreated;
111 112 113 114
};

} // namespace MessageList

115
using namespace Akonadi;
116
using namespace MessageList;
117 118


Laurent Montel's avatar
Laurent Montel committed
119
Pane::Pane( bool restoreSession, QAbstractItemModel *model, QItemSelectionModel *selectionModel, QWidget *parent )
Laurent Montel's avatar
Laurent Montel committed
120
    : KTabWidget( parent ), d( new Private( this ) )
121
{
Laurent Montel's avatar
Laurent Montel committed
122 123 124
    setDocumentMode( true );
    d->mModel = model;
    d->mSelectionModel = selectionModel;
125

Laurent Montel's avatar
Laurent Montel committed
126 127
    // Build the proxy stack
    const QAbstractProxyModel *proxyModel = qobject_cast<const QAbstractProxyModel*>( d->mSelectionModel->model() );
128

Laurent Montel's avatar
Laurent Montel committed
129 130 131 132
    while (proxyModel) {
        if (static_cast<const QAbstractItemModel*>(proxyModel) == d->mModel) {
            break;
        }
133

Laurent Montel's avatar
Laurent Montel committed
134 135
        d->mProxyStack << proxyModel;
        const QAbstractProxyModel *nextProxyModel = qobject_cast<const QAbstractProxyModel*>(proxyModel->sourceModel());
136

Laurent Montel's avatar
Laurent Montel committed
137 138 139 140 141 142 143
        if (!nextProxyModel) {
            // It's the final model in the chain, so it is necessarily the sourceModel.
            Q_ASSERT(qobject_cast<const QAbstractItemModel*>(proxyModel->sourceModel()) == d->mModel);
            break;
        }
        proxyModel = nextProxyModel;
    } // Proxy stack done
144

Laurent Montel's avatar
Laurent Montel committed
145 146 147 148
    d->mNewTabButton = new QToolButton( this );
    d->mNewTabButton->setIcon( KIcon( QLatin1String( "tab-new" ) ) );
    d->mNewTabButton->adjustSize();
    d->mNewTabButton->setToolTip( i18nc("@info:tooltip", "Open a new tab"));
149
#ifndef QT_NO_ACCESSIBILITY
Laurent Montel's avatar
Laurent Montel committed
150
    d->mNewTabButton->setAccessibleName( i18n( "New tab" ) );
151
#endif
Laurent Montel's avatar
Laurent Montel committed
152 153 154 155 156 157 158 159
    setCornerWidget( d->mNewTabButton, Qt::TopLeftCorner );
    connect( d->mNewTabButton, SIGNAL(clicked()),
             SLOT(onNewTabClicked()) );

    d->mCloseTabButton = new QToolButton( this );
    d->mCloseTabButton->setIcon( KIcon( QLatin1String( "tab-close" ) ) );
    d->mCloseTabButton->adjustSize();
    d->mCloseTabButton->setToolTip( i18nc("@info:tooltip", "Close the current tab"));
160
#ifndef QT_NO_ACCESSIBILITY
Laurent Montel's avatar
Laurent Montel committed
161
    d->mCloseTabButton->setAccessibleName( i18n( "Close tab" ) );
162
#endif
Laurent Montel's avatar
Laurent Montel committed
163 164 165
    setCornerWidget( d->mCloseTabButton, Qt::TopRightCorner );
    connect( d->mCloseTabButton, SIGNAL(clicked()),
             SLOT(onCloseTabClicked()) );
166

Laurent Montel's avatar
Laurent Montel committed
167 168
    setTabsClosable( Core::Settings::self()->tabsHaveCloseButton() );
    connect( this, SIGNAL(closeRequest(QWidget*)), SLOT(closeTab(QWidget*)) );
169

Laurent Montel's avatar
Laurent Montel committed
170 171
    readConfig(restoreSession);
    setMovable( true );
172

Laurent Montel's avatar
Laurent Montel committed
173 174 175 176
    connect( d->mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
             this, SLOT(onSelectionChanged(QItemSelection,QItemSelection)) );
    connect( this, SIGNAL(currentChanged(int)),
             this, SLOT(onCurrentTabChanged()) );
177

Laurent Montel's avatar
Laurent Montel committed
178 179 180
    setContextMenuPolicy( Qt::CustomContextMenu );
    connect( this, SIGNAL(customContextMenuRequested(QPoint)),
             this, SLOT(onTabContextMenuRequest(QPoint)) );
181

Laurent Montel's avatar
Laurent Montel committed
182 183
    connect( Core::Settings::self(), SIGNAL(configChanged()),
             this, SLOT(updateTabControls()) );
184

Laurent Montel's avatar
Laurent Montel committed
185 186
    connect( this, SIGNAL(mouseDoubleClick()),
             this, SLOT(createNewTab()) );
Allen Winter's avatar
Allen Winter committed
187

Laurent Montel's avatar
Laurent Montel committed
188 189 190
    connect( this, SIGNAL(mouseMiddleClick(QWidget*)),
             this, SLOT(closeTab(QWidget*)) );
    tabBar()->installEventFilter( this );
191 192 193 194
}

Pane::~Pane()
{
Laurent Montel's avatar
Laurent Montel committed
195 196
    writeConfig(true);
    delete d;
197 198
}

199 200
void Pane::Private::addActivateTabAction(int i)
{
Laurent Montel's avatar
Laurent Montel committed
201 202 203 204 205 206
    QString actionname;
    actionname.sprintf("activate_tab_%02d", i);
    KAction *action = new KAction( i18n("Activate Tab %1", i), q);
    action->setShortcut( QKeySequence( QString::fromLatin1( "Alt+%1" ).arg( i ) ) );
    mXmlGuiClient->actionCollection()->addAction( actionname, action );
    connect( action, SIGNAL(triggered(bool)),q, SLOT(activateTab()) );
207 208 209 210
}



211 212
void Pane::setXmlGuiClient( KXMLGUIClient *xmlGuiClient )
{
Laurent Montel's avatar
Laurent Montel committed
213
    d->mXmlGuiClient = xmlGuiClient;
214

Laurent Montel's avatar
Laurent Montel committed
215 216 217
    KToggleAction * const showHideQuicksearch = new KToggleAction( i18n( "Show Quick Search Bar" ), this );
    showHideQuicksearch->setShortcut( Qt::CTRL + Qt::Key_H );
    showHideQuicksearch->setChecked( Core::Settings::showQuickSearch() );
218

Laurent Montel's avatar
Laurent Montel committed
219 220
    d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "show_quick_search" ), showHideQuicksearch );
    connect( showHideQuicksearch, SIGNAL(triggered(bool)), this, SLOT(changeQuicksearchVisibility(bool)) );
221 222


Laurent Montel's avatar
Laurent Montel committed
223 224 225
    for ( int i=0; i<count(); ++i ) {
        Widget *w = qobject_cast<Widget *>( widget( i ) );
        w->setXmlGuiClient( d->mXmlGuiClient );
226
    }
227

Laurent Montel's avatar
Laurent Montel committed
228 229 230 231 232 233 234 235
    // Setup "View->Message List" actions.
    if ( xmlGuiClient ) {
        if ( d->mActionMenu ) {
            d->mXmlGuiClient->actionCollection()->removeAction( d->mActionMenu );
        }
        d->mActionMenu = new KActionMenu( KIcon(), i18n( "Message List" ), this );
        d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "view_message_list" ), d->mActionMenu );
        MessageList::Util::fillViewMenu( d->mActionMenu->menu(), this );
Laurent Montel's avatar
Laurent Montel committed
236

Laurent Montel's avatar
Laurent Montel committed
237
        d->mActionMenu->addSeparator();
Laurent Montel's avatar
Laurent Montel committed
238

Laurent Montel's avatar
Laurent Montel committed
239 240 241 242 243 244 245 246 247 248
        KAction *action = new KAction( i18n("Create New Tab"), this );
        action->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_O ) );
        d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "create_new_tab" ), action );
        connect( action, SIGNAL(triggered(bool)), SLOT(onNewTabClicked()) );
        d->mActionMenu->addAction( action );

        d->mMaxTabCreated = count();
        for (int i=1;i<10 && i<=count();++i) {
            d->addActivateTabAction(i);
        }
249 250


Laurent Montel's avatar
Laurent Montel committed
251 252 253 254 255 256
        d->mCloseTabAction = new KAction( i18n("Close Tab"), this );
        d->mCloseTabAction->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_W ) );
        d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "close_current_tab" ), d->mCloseTabAction );
        connect( d->mCloseTabAction, SIGNAL(triggered(bool)), SLOT(onCloseTabClicked()) );
        d->mActionMenu->addAction( d->mCloseTabAction );
        d->mCloseTabAction->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
257

Laurent Montel's avatar
Laurent Montel committed
258 259 260 261
        d->mActivateNextTabAction = new KAction( i18n("Activate Next Tab"),this );
        d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "activate_next_tab" ), d->mActivateNextTabAction );
        d->mActivateNextTabAction->setEnabled( false );
        connect( d->mActivateNextTabAction, SIGNAL(triggered(bool)), SLOT(activateNextTab()) );
262

Laurent Montel's avatar
Laurent Montel committed
263 264 265 266
        d->mActivatePreviousTabAction = new KAction( i18n("Activate Previous Tab"),this );
        d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "activate_previous_tab" ), d->mActivatePreviousTabAction );
        d->mActivatePreviousTabAction->setEnabled( false );
        connect( d->mActivatePreviousTabAction, SIGNAL(triggered(bool)), SLOT(activatePreviousTab()) );
267 268


Laurent Montel's avatar
Laurent Montel committed
269 270 271 272
        d->mMoveTabLeftAction = new KAction( i18n("Move Tab Left"),this );
        d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "move_tab_left" ), d->mMoveTabLeftAction );
        d->mMoveTabLeftAction->setEnabled( false );
        connect( d->mMoveTabLeftAction, SIGNAL(triggered(bool)), SLOT(moveTabLeft()) );
273

Laurent Montel's avatar
Laurent Montel committed
274 275 276 277 278
        d->mMoveTabRightAction = new KAction( i18n("Move Tab Right"),this );
        d->mXmlGuiClient->actionCollection()->addAction( QLatin1String( "move_tab_right" ), d->mMoveTabRightAction );
        d->mMoveTabRightAction->setEnabled( false );
        connect( d->mMoveTabRightAction, SIGNAL(triggered(bool)), SLOT(moveTabRight()) );
    }
279 280
}

281
bool Pane::selectNextMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter,
282 283 284
                                  MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour,
                                  bool centerItem,
                                  bool loop )
285
{
Laurent Montel's avatar
Laurent Montel committed
286
    Widget *w = static_cast<Widget*>( currentWidget() );
287

Laurent Montel's avatar
Laurent Montel committed
288 289 290
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return true;
Volker Krause's avatar
Volker Krause committed
291

Laurent Montel's avatar
Laurent Montel committed
292 293 294 295
        return w->selectNextMessageItem( messageTypeFilter, existingSelectionBehaviour, centerItem, loop );
    } else {
        return false;
    }
296 297 298
}

bool Pane::selectPreviousMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter,
299 300 301
                                      MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour,
                                      bool centerItem,
                                      bool loop )
302
{
Laurent Montel's avatar
Laurent Montel committed
303
    Widget *w = static_cast<Widget*>( currentWidget() );
304

Laurent Montel's avatar
Laurent Montel committed
305 306 307
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return true;
Volker Krause's avatar
Volker Krause committed
308

Laurent Montel's avatar
Laurent Montel committed
309 310 311 312
        return w->selectPreviousMessageItem( messageTypeFilter, existingSelectionBehaviour, centerItem, loop );
    } else {
        return false;
    }
313 314 315 316
}

bool Pane::focusNextMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop )
{
Laurent Montel's avatar
Laurent Montel committed
317
    Widget *w = static_cast<Widget*>( currentWidget() );
318

Laurent Montel's avatar
Laurent Montel committed
319 320 321
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return true;
Volker Krause's avatar
Volker Krause committed
322

Laurent Montel's avatar
Laurent Montel committed
323 324 325 326
        return w->focusNextMessageItem( messageTypeFilter, centerItem, loop );
    } else {
        return false;
    }
327 328 329 330
}

bool Pane::focusPreviousMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop )
{
Laurent Montel's avatar
Laurent Montel committed
331
    Widget *w = static_cast<Widget*>( currentWidget() );
332

Laurent Montel's avatar
Laurent Montel committed
333 334 335
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return true;
Volker Krause's avatar
Volker Krause committed
336

Laurent Montel's avatar
Laurent Montel committed
337 338 339 340
        return w->focusPreviousMessageItem( messageTypeFilter, centerItem, loop );
    } else {
        return false;
    }
341 342 343 344
}

void Pane::selectFocusedMessageItem( bool centerItem )
{
Laurent Montel's avatar
Laurent Montel committed
345
    Widget *w = static_cast<Widget*>( currentWidget() );
346

Laurent Montel's avatar
Laurent Montel committed
347 348 349
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return;
Volker Krause's avatar
Volker Krause committed
350

Laurent Montel's avatar
Laurent Montel committed
351 352
        w->selectFocusedMessageItem( centerItem );
    }
353 354 355 356
}

bool Pane::selectFirstMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem )
{
Laurent Montel's avatar
Laurent Montel committed
357
    Widget *w = static_cast<Widget*>( currentWidget() );
358

Laurent Montel's avatar
Laurent Montel committed
359 360 361
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return true;
Volker Krause's avatar
Volker Krause committed
362

Laurent Montel's avatar
Laurent Montel committed
363 364 365 366
        return w->selectFirstMessageItem( messageTypeFilter, centerItem );
    } else {
        return false;
    }
367 368 369 370
}

bool Pane::selectLastMessageItem(Core::MessageTypeFilter messageTypeFilter, bool centerItem)
{
Laurent Montel's avatar
Laurent Montel committed
371
    Widget *w = static_cast<Widget*>( currentWidget() );
372

Laurent Montel's avatar
Laurent Montel committed
373 374 375
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return true;
376

Laurent Montel's avatar
Laurent Montel committed
377 378 379 380
        return w->selectLastMessageItem( messageTypeFilter, centerItem );
    } else {
        return false;
    }
381 382 383 384
}

void Pane::selectAll()
{
Laurent Montel's avatar
Laurent Montel committed
385
    Widget *w = static_cast<Widget*>( currentWidget() );
386

Laurent Montel's avatar
Laurent Montel committed
387 388 389
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return;
Volker Krause's avatar
Volker Krause committed
390

Laurent Montel's avatar
Laurent Montel committed
391 392
        w->selectAll();
    }
393 394
}

Laurent Montel's avatar
Laurent Montel committed
395 396 397

void Pane::setCurrentThreadExpanded( bool expand )
{
Laurent Montel's avatar
Laurent Montel committed
398
    Widget *w = static_cast<Widget*>( currentWidget() );
Laurent Montel's avatar
Laurent Montel committed
399

Laurent Montel's avatar
Laurent Montel committed
400 401 402
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return;
Volker Krause's avatar
Volker Krause committed
403

Laurent Montel's avatar
Laurent Montel committed
404 405
        w->setCurrentThreadExpanded(expand );
    }
Laurent Montel's avatar
Laurent Montel committed
406 407 408 409
}

void Pane::setAllThreadsExpanded( bool expand )
{
Laurent Montel's avatar
Laurent Montel committed
410
    Widget *w = static_cast<Widget*>( currentWidget() );
Laurent Montel's avatar
Laurent Montel committed
411

Laurent Montel's avatar
Laurent Montel committed
412 413 414
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return;
Volker Krause's avatar
Volker Krause committed
415

Laurent Montel's avatar
Laurent Montel committed
416 417
        w->setAllThreadsExpanded( expand );
    }
Laurent Montel's avatar
Laurent Montel committed
418 419 420 421
}

void Pane::setAllGroupsExpanded( bool expand )
{
Laurent Montel's avatar
Laurent Montel committed
422
    Widget *w = static_cast<Widget*>( currentWidget() );
Laurent Montel's avatar
Laurent Montel committed
423

Laurent Montel's avatar
Laurent Montel committed
424 425 426
    if ( w ) {
        if ( w->view()->model()->isLoading() )
            return;
Volker Krause's avatar
Volker Krause committed
427

Laurent Montel's avatar
Laurent Montel committed
428 429
        w->setAllGroupsExpanded(expand);
    }
Laurent Montel's avatar
Laurent Montel committed
430 431
}

432
void Pane::focusQuickSearch(const QString &selectedText)
433
{
Laurent Montel's avatar
Laurent Montel committed
434
    Widget *w = static_cast<Widget*>( currentWidget() );
435

Laurent Montel's avatar
Laurent Montel committed
436
    if ( w ) {
437
        w->focusQuickSearch(selectedText);
Laurent Montel's avatar
Laurent Montel committed
438
    }
439
}
Laurent Montel's avatar
Laurent Montel committed
440

441 442
void Pane::setQuickSearchClickMessage(const QString &msg)
{
Laurent Montel's avatar
Laurent Montel committed
443
    Widget *w = static_cast<Widget*>( currentWidget() );
444

Laurent Montel's avatar
Laurent Montel committed
445 446 447
    if ( w ) {
        w->setQuickSearchClickMessage(msg);
    }
448 449 450
}


451
void Pane::Private::onSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
452
{
Laurent Montel's avatar
Laurent Montel committed
453 454
    if ( mPreferEmptyTab ) {
        q->createNewTab();
Laurent Montel's avatar
Laurent Montel committed
455
    }
456

Laurent Montel's avatar
Laurent Montel committed
457 458
    Widget *w = static_cast<Widget*>( q->currentWidget() );
    QItemSelectionModel * s = mWidgetSelectionHash[w];
459

Laurent Montel's avatar
Laurent Montel committed
460 461 462 463 464 465 466 467
    w->saveCurrentSelection();

    // Deselect old before we select new - so that the messagelist can clear first.
    s->select( mapSelectionToSource( deselected ), QItemSelectionModel::Deselect );
    if ( s->selection().isEmpty() ) {
        w->view()->model()->setPreSelectionMode( mPreSelectionMode );
    }
    s->select( mapSelectionToSource( selected ), QItemSelectionModel::Select );
468

Laurent Montel's avatar
Laurent Montel committed
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
    QString label;
    QIcon icon;
    QString toolTip;
    foreach ( const QModelIndex &index, s->selectedRows() ) {
        label+= index.data( Qt::DisplayRole ).toString()+QLatin1String( ", " );
    }
    label.chop( 2 );

    if ( label.isEmpty() ) {
        label = i18nc( "@title:tab Empty messagelist", "Empty" );
        icon = QIcon();
    } else if ( s->selectedRows().size()==1 ) {
        icon = s->selectedRows().first().data( Qt::DecorationRole ).value<QIcon>();
        QModelIndex idx = s->selectedRows().first().parent();
        toolTip = label;
        while ( idx != QModelIndex() ) {
            toolTip = idx.data().toString() + QLatin1Char( '/' ) + toolTip;
            idx = idx.parent();
        }
    } else {
        icon = KIcon( QLatin1String( "folder" ) );
    }

    const int index = q->indexOf( w );
    q->setTabText( index, label );
    q->setTabIcon( index, icon );
    q->setTabToolTip( index, toolTip);
    if ( mPreferEmptyTab ) {
        disconnect( mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
                    q, SLOT(onSelectionChanged(QItemSelection,QItemSelection)) );

        mSelectionModel->select( mapSelectionFromSource( s->selection() ),
                                 QItemSelectionModel::ClearAndSelect );

        connect( mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
                 q, SLOT(onSelectionChanged(QItemSelection,QItemSelection)) );

    }
507 508
}

509 510
void Pane::Private::activateTab()
{
Laurent Montel's avatar
Laurent Montel committed
511
    q->tabBar()->setCurrentIndex( q->sender()->objectName().right( 2 ).toInt() -1 );
512 513
}

514 515
void Pane::Private::moveTabRight()
{
Laurent Montel's avatar
Laurent Montel committed
516 517 518 519 520 521 522
    const int numberOfTab = q->tabBar()->count();
    if( numberOfTab == 1 )
        return;
    if ( QApplication::isRightToLeft() )
        moveTabForward();
    else
        moveTabBackward();
523 524 525 526 527

}

void Pane::Private::moveTabLeft()
{
Laurent Montel's avatar
Laurent Montel committed
528 529 530 531 532 533 534
    const int numberOfTab = q->tabBar()->count();
    if( numberOfTab == 1 )
        return;
    if ( QApplication::isRightToLeft() )
        moveTabBackward();
    else
        moveTabForward();
535 536 537 538 539

}

void Pane::Private::moveTabForward()
{
Laurent Montel's avatar
Laurent Montel committed
540 541 542 543
    const int currentIndex = q->tabBar()->currentIndex();
    if ( currentIndex == q->tabBar()->count()-1 )
        return;
    q->tabBar()->moveTab( currentIndex, currentIndex+1 );
544 545 546 547
}

void Pane::Private::moveTabBackward()
{
Laurent Montel's avatar
Laurent Montel committed
548 549 550 551
    const int currentIndex = q->tabBar()->currentIndex();
    if ( currentIndex == 0 )
        return;
    q->tabBar()->moveTab( currentIndex, currentIndex-1 );
552 553
}

554 555
void Pane::Private::activateNextTab()
{
Laurent Montel's avatar
Laurent Montel committed
556 557 558
    const int numberOfTab = q->tabBar()->count();
    if( numberOfTab == 1 )
        return;
559

Laurent Montel's avatar
Laurent Montel committed
560
    int indexTab = ( q->tabBar()->currentIndex() + 1 );
561

Laurent Montel's avatar
Laurent Montel committed
562 563
    if( indexTab == numberOfTab )
        indexTab = 0;
Allen Winter's avatar
Allen Winter committed
564

Laurent Montel's avatar
Laurent Montel committed
565
    q->tabBar()->setCurrentIndex( indexTab );
566 567 568 569
}

void Pane::Private::activatePreviousTab()
{
Laurent Montel's avatar
Laurent Montel committed
570 571 572
    const int numberOfTab = q->tabBar()->count();
    if( numberOfTab == 1 )
        return;
573

Laurent Montel's avatar
Laurent Montel committed
574
    int indexTab = ( q->tabBar()->currentIndex() - 1 );
575

Laurent Montel's avatar
Laurent Montel committed
576 577
    if( indexTab == -1 )
        indexTab = numberOfTab - 1;
578

Laurent Montel's avatar
Laurent Montel committed
579
    q->tabBar()->setCurrentIndex( indexTab );
580 581
}

582
void Pane::Private::onNewTabClicked()
583
{
Laurent Montel's avatar
Laurent Montel committed
584
    q->createNewTab();
585 586
}

587
void Pane::Private::onCloseTabClicked()
588
{
Laurent Montel's avatar
Laurent Montel committed
589
    closeTab( q->currentWidget() );
590 591 592 593
}

void Pane::Private::closeTab( QWidget *w )
{
Laurent Montel's avatar
Laurent Montel committed
594 595 596
    if ( !w || (q->count() < 2) ) {
        return;
    }
597

Laurent Montel's avatar
Laurent Montel committed
598 599
    delete w;
    updateTabControls();
600 601
}

602

603
void Pane::Private::changeQuicksearchVisibility(bool show)
604
{
Laurent Montel's avatar
Laurent Montel committed
605
    for ( int i=0; i<q->count(); ++i ) {
Laurent Montel's avatar
Laurent Montel committed
606 607
        Widget *w = qobject_cast<Widget *>( q->widget( i ) );
        w->changeQuicksearchVisibility(show);
608 609 610 611
    }
}


612 613
bool Pane::eventFilter( QObject *object, QEvent *event )
{
Laurent Montel's avatar
Laurent Montel committed
614 615 616 617 618
    if ( event->type() == QEvent::MouseButtonPress ) {
        QMouseEvent * const mouseEvent = static_cast<QMouseEvent *>( event );
        if ( mouseEvent->button() == Qt::MidButton ) {
            return true;
        }
619
    }
Laurent Montel's avatar
Laurent Montel committed
620
    return KTabWidget::eventFilter( object, event );
621 622
}

623
void Pane::Private::onCurrentTabChanged()
624
{
Laurent Montel's avatar
Laurent Montel committed
625
    emit q->currentTabChanged();
626

Laurent Montel's avatar
Laurent Montel committed
627
    Widget *w = static_cast<Widget*>( q->currentWidget() );
Allen Winter's avatar
Allen Winter committed
628

Laurent Montel's avatar
Laurent Montel committed
629
    QItemSelectionModel *s = mWidgetSelectionHash[w];
630

Laurent Montel's avatar
Laurent Montel committed
631 632
    disconnect( mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
                q, SLOT(onSelectionChanged(QItemSelection,QItemSelection)) );
633

Laurent Montel's avatar
Laurent Montel committed
634 635
    mSelectionModel->select( mapSelectionFromSource( s->selection() ),
                             QItemSelectionModel::ClearAndSelect );
636

Laurent Montel's avatar
Laurent Montel committed
637 638
    connect( mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
             q, SLOT(onSelectionChanged(QItemSelection,QItemSelection)) );
639 640
}

641
void Pane::Private::onTabContextMenuRequest( const QPoint &pos )
642
{
Laurent Montel's avatar
Laurent Montel committed
643 644
    QTabBar *bar = q->tabBar();
    if ( q->count() <= 1 ) return;
645

Laurent Montel's avatar
Laurent Montel committed
646 647
    const int indexBar = bar->tabAt( bar->mapFrom( q, pos ) );
    if ( indexBar == -1 ) return;
648

Laurent Montel's avatar
Laurent Montel committed
649 650
    Widget *w = qobject_cast<Widget *>( q->widget( indexBar ) );
    if ( !w ) return;
651

Laurent Montel's avatar
Laurent Montel committed
652
    KMenu menu( q );
653

654

Laurent Montel's avatar
Laurent Montel committed
655 656
    QAction *closeTabAction = menu.addAction( i18nc( "@action:inmenu", "Close Tab" ) );
    closeTabAction->setIcon( KIcon( QLatin1String( "tab-close" ) ) );
657

Laurent Montel's avatar
Laurent Montel committed
658 659
    QAction *allOther = menu.addAction( i18nc("@action:inmenu", "Close All Other Tabs" ) );
    allOther->setIcon( KIcon( QLatin1String( "tab-close-other" ) ) );
660

Laurent Montel's avatar
Laurent Montel committed
661
    QAction *action = menu.exec( q->mapToGlobal( pos ) );
662

Laurent Montel's avatar
Laurent Montel committed
663 664 665
    if ( action == allOther ) { // Close all other tabs
        QList<Widget *> widgets;
        const int index = q->indexOf( w );
666

Laurent Montel's avatar
Laurent Montel committed
667 668
        for ( int i=0; i<q->count(); ++i ) {
            if ( i==index) continue; // Skip the current one
669

Laurent Montel's avatar
Laurent Montel committed
670 671 672
            Widget *other = qobject_cast<Widget *>( q->widget( i ) );
            widgets << other;
        }
673

Laurent Montel's avatar
Laurent Montel committed
674 675 676
        foreach ( Widget *other, widgets ) {
            delete other;
        }
677

Laurent Montel's avatar
Laurent Montel committed
678 679 680 681
        updateTabControls();
    } else if (action == closeTabAction) {
        closeTab(q->widget(indexBar));
    }
682 683
}

684 685
MessageList::StorageModel *Pane::createStorageModel( QAbstractItemModel *model, QItemSelectionModel *selectionModel, QObject *parent )
{
Laurent Montel's avatar
Laurent Montel committed
686
    return new MessageList::StorageModel( model, selectionModel, parent );
687 688
}

689
void Pane::setCurrentFolder( const Akonadi::Collection &collection, bool, Core::PreSelectionMode preSelectionMode, const QString &overrideLabel )
690
{
Laurent Montel's avatar
Laurent Montel committed
691 692 693 694 695 696 697 698 699 700 701
    d->mPreSelectionMode = preSelectionMode;
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w ) {
        w->setCurrentFolder( collection );
        QItemSelectionModel *s = d->mWidgetSelectionHash[w];
        MessageList::StorageModel *m = createStorageModel( d->mModel, s, w );
        w->setStorageModel( m, preSelectionMode );
        if ( !overrideLabel.isEmpty() ) {
            int index = indexOf( w );
            setTabText( index, overrideLabel );
        }
Laurent Montel's avatar
Laurent Montel committed
702
    }
703 704
}

Laurent Montel's avatar
Laurent Montel committed
705
void Pane::updateTabIconText( const Akonadi::Collection &collection, const QString&label, const QIcon& icon )
706
{
Laurent Montel's avatar
Laurent Montel committed
707 708 709 710 711 712 713 714
    for ( int i=0; i<count(); ++i ) {
        Widget *w = qobject_cast<Widget *>( widget( i ) );
        if ( w->currentCollection() == collection )
        {
            const int index = indexOf( w );
            setTabText( index, label );
            setTabIcon( index, icon );
        }
715 716 717
    }
}

718
QItemSelectionModel *Pane::createNewTab()
719
{
Laurent Montel's avatar
Laurent Montel committed
720 721
    Widget * w = new Widget( this );
    w->setXmlGuiClient( d->mXmlGuiClient );
722

Laurent Montel's avatar
Laurent Montel committed
723
    addTab( w, i18nc( "@title:tab Empty messagelist", "Empty" ) );
724

Laurent Montel's avatar
Laurent Montel committed
725 726 727 728 729
    if(d->mXmlGuiClient && count() < 10) {
        if(d->mMaxTabCreated < count() ) {
            d->mMaxTabCreated = count();
            d->addActivateTabAction(d->mMaxTabCreated);
        }
730 731
    }

Laurent Montel's avatar
Laurent Montel committed
732 733 734
    QItemSelectionModel *s = new QItemSelectionModel( d->mModel, w );
    MessageList::StorageModel *m = createStorageModel( d->mModel, s, w );
    w->setStorageModel( m );
735

Laurent Montel's avatar
Laurent Montel committed
736
    d->mWidgetSelectionHash[w] = s;
Kevin Ottens's avatar
Kevin Ottens committed
737

Laurent Montel's avatar
Laurent Montel committed
738 739 740 741 742 743 744 745
    connect( w, SIGNAL(messageSelected(Akonadi::Item)),
             this, SIGNAL(messageSelected(Akonadi::Item)) );
    connect( w, SIGNAL(messageActivated(Akonadi::Item)),
             this, SIGNAL(messageActivated(Akonadi::Item)) );
    connect( w, SIGNAL(selectionChanged()),
             this, SIGNAL(selectionChanged()) );
    connect( w, SIGNAL(messageStatusChangeRequest(Akonadi::Item,Akonadi::MessageStatus,Akonadi::MessageStatus)),
             this, SIGNAL(messageStatusChangeRequest(Akonadi::Item,Akonadi::MessageStatus,Akonadi::MessageStatus)) );
Kevin Ottens's avatar
Kevin Ottens committed
746

Laurent Montel's avatar
Laurent Montel committed
747 748
    connect( w, SIGNAL(statusMessage(QString)),
             this, SIGNAL(statusMessage(QString)) );
749

Laurent Montel's avatar
Laurent Montel committed
750 751 752
    d->updateTabControls();
    setCurrentWidget( w );
    return s;
753 754
}

755
QItemSelection Pane::Private::mapSelectionToSource( const QItemSelection &selection ) const
756
{
Laurent Montel's avatar
Laurent Montel committed
757
    QItemSelection result = selection;
758

Laurent Montel's avatar
Laurent Montel committed
759 760 761
    foreach ( const QAbstractProxyModel *proxy, mProxyStack ) {
        result = proxy->mapSelectionToSource( result );
    }
762

Laurent Montel's avatar
Laurent Montel committed
763
    return result;
764 765
}

766
QItemSelection Pane::Private::mapSelectionFromSource( const QItemSelection &selection ) const
767
{
Laurent Montel's avatar
Laurent Montel committed
768
    QItemSelection result = selection;
769

Laurent Montel's avatar
Laurent Montel committed
770
    typedef QList<const QAbstractProxyModel*>::ConstIterator Iterator;
771

Laurent Montel's avatar
Laurent Montel committed
772 773 774 775
    for ( Iterator it = mProxyStack.end()-1; it!=mProxyStack.begin(); --it ) {
        result = (*it)->mapSelectionFromSource( result );
    }
    result = mProxyStack.first()->mapSelectionFromSource( result );
776

Laurent Montel's avatar
Laurent Montel committed
777
    return result;
778 779
}

780
void Pane::Private::updateTabControls()
781
{
Laurent Montel's avatar
Laurent Montel committed
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
    const bool enableAction = ( q->count()>1 );
    if (mCloseTabButton)
        mCloseTabButton->setEnabled( enableAction );
    if ( mCloseTabAction )
        mCloseTabAction->setEnabled( enableAction );
    if ( mActivatePreviousTabAction )
        mActivatePreviousTabAction->setEnabled( enableAction );
    if ( mActivateNextTabAction )
        mActivateNextTabAction->setEnabled( enableAction );
    if ( mMoveTabRightAction )
        mMoveTabRightAction->setEnabled( enableAction );
    if ( mMoveTabLeftAction )
        mMoveTabLeftAction->setEnabled( enableAction );

    if ( Core::Settings::self()->autoHideTabBarWithSingleTab() ) {
        q->tabBar()->setVisible( enableAction );
    } else {
        q->tabBar()->setVisible( true );
    }

    const bool hasCloseButton(Core::Settings::self()->tabsHaveCloseButton());
    q->setTabsClosable( hasCloseButton );
    if( hasCloseButton ) {
        const int numberOfTab(q->count());
        if( numberOfTab ==1) {
            q->tabBar()->tabButton(0, QTabBar::RightSide)->setEnabled(false);
        } else if(numberOfTab > 1) {
            q->tabBar()->tabButton(0, QTabBar::RightSide)->setEnabled(true);
        }
811
    }
812
}
813

814
Item Pane::currentItem() const
815
{
Laurent Montel's avatar
Laurent Montel committed
816
    Widget *w = static_cast<Widget*>( currentWidget() );
817

Laurent Montel's avatar
Laurent Montel committed
818 819 820
    if ( w == 0 ) {
        return Item();
    }
821

Laurent Montel's avatar
Laurent Montel committed
822
    return w->currentItem();
823 824
}

825
KMime::Message::Ptr Pane::currentMessage() const
826
{
Laurent Montel's avatar
Laurent Montel committed
827
    Widget *w = static_cast<Widget*>( currentWidget() );
828

Laurent Montel's avatar
Laurent Montel committed
829 830 831
    if ( w == 0 ) {
        return KMime::Message::Ptr();
    }
832

Laurent Montel's avatar
Laurent Montel committed
833
    return w->currentMessage();
834 835
}

836 837
QList<KMime::Message::Ptr > Pane::selectionAsMessageList( bool includeCollapsedChildren ) const
{
Laurent Montel's avatar
Laurent Montel committed
838 839 840 841 842
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w == 0 ) {
        return QList<KMime::Message::Ptr>();
    }
    return w->selectionAsMessageList( includeCollapsedChildren );
843 844 845 846
}

QList<Akonadi::Item> Pane::selectionAsMessageItemList( bool includeCollapsedChildren ) const
{
Laurent Montel's avatar
Laurent Montel committed
847 848 849 850 851
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w == 0 ) {
        return QList<Akonadi::Item>();
    }
    return w->selectionAsMessageItemList( includeCollapsedChildren );
852 853
}

854 855
QList<Akonadi::Item::Id> Pane::selectionAsListMessageId( bool includeCollapsedChildren ) const
{
Laurent Montel's avatar
Laurent Montel committed
856 857 858 859 860
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w == 0 ) {
        return QList<Akonadi::Item::Id>();
    }
    return w->selectionAsListMessageId( includeCollapsedChildren );
861 862 863
}


864 865
QVector<qlonglong> Pane::selectionAsMessageItemListId( bool includeCollapsedChildren ) const
{
Laurent Montel's avatar
Laurent Montel committed
866 867 868 869 870
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w == 0 ) {
        return QVector<qlonglong>();
    }
    return w->selectionAsMessageItemListId( includeCollapsedChildren );
871 872
}

873 874 875

QList<Akonadi::Item> Pane::currentThreadAsMessageList() const
{
Laurent Montel's avatar
Laurent Montel committed
876 877 878 879 880
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w == 0 ) {
        return QList<Akonadi::Item>();
    }
    return w->currentThreadAsMessageList();
881 882
}

883 884
QList<Akonadi::Item> Pane::itemListFromPersistentSet( MessageList::Core::MessageItemSetReference ref )
{
Laurent Montel's avatar
Laurent Montel committed
885 886 887 888 889
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w ) {
        return w->itemListFromPersistentSet(ref);
    }
    return QList<Akonadi::Item>();
890 891 892 893
}

void Pane::deletePersistentSet( MessageList::Core::MessageItemSetReference ref )
{
Laurent Montel's avatar
Laurent Montel committed
894 895 896 897
    Widget *w = static_cast<Widget*>( currentWidget() );
    if ( w ) {
        w->deletePersistentSet( ref );
    }
Laurent Montel's avatar