standardpanel.cpp 25.4 KB
Newer Older
1 2 3
/*****************************************************************************
 * standardpanel.cpp : The "standard" playlist panel : just a treeview
 ****************************************************************************
4
 * Copyright © 2000-2010 VideoLAN
5
 * $Id$
6 7
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          Jean-Baptiste Kempf <jb@videolan.org>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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.
 *****************************************************************************/
24

25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
28

29
#include "components/playlist/standardpanel.hpp"
Clément Stenac's avatar
Clément Stenac committed
30

31
#include "components/playlist/vlc_model.hpp"      /* VLCModel */
32
#include "components/playlist/playlist_model.hpp" /* PLModel */
33
#include "components/playlist/ml_model.hpp"       /* MLModel */
34 35
#include "components/playlist/views.hpp"          /* 3 views */
#include "components/playlist/selector.hpp"       /* PLSelector */
36
#include "util/customwidgets.hpp"                 /* PixmapAnimator */
37 38
#include "menus.hpp"                              /* Popup */
#include "input_manager.hpp"                      /* THEMIM */
39
#include "dialogs_provider.hpp"                   /* THEDP */
40 41
#include "dialogs/playlist.hpp"                   /* Playlist Dialog */
#include "dialogs/mediainfo.hpp"                  /* MediaInfoDialog */
42

43
#include <vlc_services_discovery.h>               /* SD_CMD_SEARCH */
44
#include <vlc_intf_strings.h>                     /* POP_ */
45

46 47 48 49 50 51
#define I_NEW_DIR \
    I_DIR_OR_FOLDER( N_("Create Directory"), N_( "Create Folder" ) )
#define I_NEW_DIR_NAME \
    I_DIR_OR_FOLDER( N_( "Enter name for new directory:" ), \
                     N_( "Enter name for new folder:" ) )

52
#include <QHeaderView>
53
#include <QMenu>
54
#include <QKeyEvent>
55
#include <QWheelEvent>
56
#include <QStackedLayout>
57
#include <QSignalMapper>
58
#include <QSettings>
59
#include <QStylePainter>
60 61 62
#include <QInputDialog>
#include <QDesktopServices>
#include <QUrl>
63
#include <QFont>
64

Clément Stenac's avatar
Clément Stenac committed
65
#include <assert.h>
Clément Stenac's avatar
Clément Stenac committed
66

67
StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
68
                                  intf_thread_t *_p_intf,
69
                                  playlist_item_t *p_root,
70
                                  PLSelector *_p_selector,
71 72
                                  PLModel *_p_model,
                                  MLModel *_p_plmodel)
73 74 75 76 77
                : QWidget( _parent ),
                  model( _p_model ),
                  mlmodel( _p_plmodel),
                  p_intf( _p_intf ),
                  p_selector( _p_selector )
78
{
79 80
    viewStack = new QStackedLayout( this );
    viewStack->setSpacing( 0 ); viewStack->setMargin( 0 );
81
    setMinimumWidth( 300 );
82

83 84 85 86
    iconView    = NULL;
    treeView    = NULL;
    listView    = NULL;
    picFlowView = NULL;
87

88 89
    currentRootIndexId  = -1;
    lastActivatedId     = -1;
Clément Stenac's avatar
Clément Stenac committed
90

91 92 93 94 95 96 97 98
    QList<QString> frames;
    frames << ":/util/wait1";
    frames << ":/util/wait2";
    frames << ":/util/wait3";
    frames << ":/util/wait4";
    spinnerAnimation = new PixmapAnimator( this, frames );
    CONNECT( spinnerAnimation, pixmapReady( const QPixmap & ), this, updateViewport() );

99
    /* Saved Settings */
100
    int i_savedViewMode = getSettings()->value( "Playlist/view-mode", TREE_VIEW ).toInt();
101 102
    i_zoom = getSettings()->value( "Playlist/zoom", 0 ).toInt();

103
    showView( i_savedViewMode );
Jakob Leben's avatar
Jakob Leben committed
104

105 106
    DCONNECT( THEMIM, leafBecameParent( int ),
              this, browseInto( int ) );
107

108
    CONNECT( model, currentIndexChanged( const QModelIndex& ),
109
             this, handleExpansion( const QModelIndex& ) );
110
    CONNECT( model, rootIndexChanged(), this, browseInto() );
111

112
    setRootItem( p_root, false );
113 114
}

115 116 117
StandardPLPanel::~StandardPLPanel()
{
    getSettings()->beginGroup("Playlist");
118 119
    if( treeView )
        getSettings()->setValue( "headerStateV2", treeView->header()->saveState() );
120
    getSettings()->setValue( "view-mode", currentViewIndex() );
121
    getSettings()->setValue( "zoom", i_zoom );
122 123 124
    getSettings()->endGroup();
}

125
/* Unused anymore, but might be useful, like in right-click menu */
126 127
void StandardPLPanel::gotoPlayingItem()
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
128
    currentView->scrollTo( model->currentIndex() );
129 130
}

131
void StandardPLPanel::handleExpansion( const QModelIndex& index )
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
132
{
Ilkka Ollakka's avatar
Ilkka Ollakka committed
133
    assert( currentView );
134 135
    if( currentRootIndexId != -1 && currentRootIndexId != model->itemId( index.parent() ) )
        browseInto( index.parent() );
136
    currentView->scrollTo( index );
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
137 138
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
139 140 141 142
void StandardPLPanel::popupPlView( const QPoint &point )
{
    QModelIndex index = currentView->indexAt( point );
    QPoint globalPoint = currentView->viewport()->mapToGlobal( point );
143
    QItemSelectionModel *selection = currentView->selectionModel();
144
    QModelIndexList list = selection->selectedRows();
145

146
    if( !popup( index, globalPoint, list ) )
147
        VLCMenuBar::PopupMenu( p_intf, true );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
148 149
}

150 151 152 153 154 155
/*********** Popup *********/
bool StandardPLPanel::popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &selectionlist )
{
    VLCModel *model = qobject_cast<VLCModel*>(currentView->model());
    QModelIndexList callerAsList;
    callerAsList << ( index.isValid() ? index : QModelIndex() );
156
    popupIndex = index; /* suitable for modal only */
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174

#define ADD_MENU_ENTRY( icon, title, act, data ) \
    action = menu.addAction( icon, title ); \
    container.action = act; \
    container.indexes = data; \
    action->setData( QVariant::fromValue( container ) )

    /* */
    QMenu menu;
    QAction *action;
    PLModel::actionsContainerType container;

    /* Play/Stream/Info static actions */
    if( index.isValid() )
    {
        ADD_MENU_ENTRY( QIcon( ":/menu/play" ), qtr(I_POP_PLAY),
                        container.ACTION_PLAY, callerAsList );

175 176
        menu.addAction( QIcon( ":/menu/stream" ), qtr(I_POP_STREAM),
                        this, SLOT( popupStream() ) );
177

178 179
        menu.addAction( QIcon(), qtr(I_POP_SAVE),
                        this, SLOT( popupSave() ) );
180

181 182
        menu.addAction( QIcon( ":/menu/info" ), qtr(I_POP_INFO),
                        this, SLOT( popupInfoDialog() ) );
183 184 185 186

        menu.addSeparator();

        if( model->getURI( index ).startsWith( "file://" ) )
187 188
            menu.addAction( QIcon( ":/type/folder-grey" ), qtr(I_POP_EXPLORE),
                            this, SLOT( popupExplore() ) );
189 190 191 192 193 194 195 196
    }

    /* In PL or ML, allow to add a file/folder */
    if( model->canEdit() )
    {
        QIcon addIcon( ":/buttons/playlist/playlist_add" );

        if( model->isTree() )
197 198
            menu.addAction( addIcon, qtr(I_POP_NEWFOLDER),
                            this, SLOT( popupPromptAndCreateNode() ) );
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

        menu.addSeparator();
        if( model->isCurrentItem( model->rootIndex(), PLModel::IN_PLAYLIST ) )
        {
            menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simplePLAppendDialog()) );
            menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( PLAppendDir()) );
            menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( PLAppendDialog()) );
        }
        else if( model->isCurrentItem( model->rootIndex(), PLModel::IN_MEDIALIBRARY ) )
        {
            menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simpleMLAppendDialog()) );
            menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( MLAppendDir() ) );
            menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( MLAppendDialog() ) );
        }
    }

    if( index.isValid() )
    {
        if( !model->isCurrentItem( model->rootIndex(), PLModel::IN_PLAYLIST ) )
        {
219
            ADD_MENU_ENTRY( QIcon(), qtr(I_PL_ADDPL),
220 221 222 223 224 225 226 227 228 229 230 231 232 233
                            container.ACTION_ADDTOPLAYLIST, selectionlist );
        }
    }

    menu.addSeparator();

    /* Item removal */
    if( index.isValid() )
    {
        ADD_MENU_ENTRY( QIcon( ":/buttons/playlist/playlist_remove" ), qtr(I_POP_DEL),
                        container.ACTION_REMOVE, selectionlist );
    }

    if( model->canEdit() ) {
234
        menu.addAction( QIcon( ":/toolbar/clear" ), qtr("Clear the playlist"),
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
                        model, SLOT( clearPlaylist() ) );
    }

    menu.addSeparator();

    /* Playlist sorting */
    QMenu *sortingMenu = new QMenu( qtr( "Sort by" ) );
    /* Choose what columns to show in sorting menu, not sure if this should be configurable*/
    QList<int> sortingColumns;
    sortingColumns << COLUMN_TITLE << COLUMN_ARTIST << COLUMN_ALBUM << COLUMN_TRACK_NUMBER << COLUMN_URI;
    container.action = container.ACTION_SORT;
    container.indexes = callerAsList;
    foreach( int Column, sortingColumns )
    {
        action = sortingMenu->addAction( qfu( psz_column_title( Column ) ) + " " + qtr("Ascending") );
        container.column = model->columnFromMeta(Column) + 1;
        action->setData( QVariant::fromValue( container ) );

        action = sortingMenu->addAction( qfu( psz_column_title( Column ) ) + " " + qtr("Descending") );
        container.column = -1 * (model->columnFromMeta(Column)+1);
        action->setData( QVariant::fromValue( container ) );
    }
    menu.addMenu( sortingMenu );

    /* Zoom */
    QMenu *zoomMenu = new QMenu( qtr( "Display size" ) );
261 262
    zoomMenu->addAction( qtr( "Increase" ), this, SLOT( increaseZoom() ) );
    zoomMenu->addAction( qtr( "Decrease" ), this, SLOT( decreaseZoom() ) );
263 264 265 266
    menu.addMenu( zoomMenu );

    CONNECT( &menu, triggered( QAction * ), model, actionSlot( QAction * ) );

267 268 269 270 271 272 273 274 275 276 277
    menu.addMenu( StandardPLPanel::viewSelectionMenu( this ) );

    /* Display and forward the result */
    if( !menu.isEmpty() )
    {
        menu.exec( point ); return true;
    }
    else return false;

#undef ADD_MENU_ENTRY
}
278

279 280 281 282 283
QMenu* StandardPLPanel::viewSelectionMenu( StandardPLPanel *panel )
{
    QMenu *viewMenu = new QMenu( qtr( "Playlist View Mode" ) );
    QSignalMapper *viewSelectionMapper = new QSignalMapper( viewMenu );
    CONNECT( viewSelectionMapper, mapped( int ), panel, showView( int ) );
284

285
    QActionGroup *viewGroup = new QActionGroup( viewMenu );
286 287 288 289 290
# define MAX_VIEW StandardPLPanel::VIEW_COUNT
    for( int i = 0; i < MAX_VIEW; i++ )
    {
        QAction *action = viewMenu->addAction( viewNames[i] );
        action->setCheckable( true );
291
        viewGroup->addAction( action );
292 293
        viewSelectionMapper->setMapping( action, i );
        CONNECT( action, triggered(), viewSelectionMapper, map() );
294
        if( panel->currentViewIndex() == i )
295 296
            action->setChecked( true );
    }
297
    return viewMenu;
298 299
}

300
void StandardPLPanel::popupSelectColumn( QPoint )
301
{
302
    QMenu menu;
303
    assert( treeView );
304

305
    /* We do not offer the option to hide index 0 column, or
306 307
     * QTreeView will behave weird */
    for( int i = 1 << 1, j = 1; i < COLUMN_END; i <<= 1, j++ )
308
    {
309
        QAction* option = menu.addAction( qfu( psz_column_title( i ) ) );
310
        option->setCheckable( true );
311
        option->setChecked( !treeView->isColumnHidden( j ) );
312 313
        selectColumnsSigMapper->setMapping( option, j );
        CONNECT( option, triggered(), selectColumnsSigMapper, map() );
314
    }
315
    menu.exec( QCursor::pos() );
316 317
}

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
void StandardPLPanel::popupPromptAndCreateNode()
{
    bool ok;
    QString name = QInputDialog::getText( PlaylistDialog::getInstance( p_intf ),
        qtr( I_NEW_DIR ), qtr( I_NEW_DIR_NAME ),
        QLineEdit::Normal, QString(), &ok);
    if ( !ok ) return;
    qobject_cast<VLCModel *>(currentView->model())->createNode( popupIndex, name );
}

void StandardPLPanel::popupInfoDialog()
{
    if( popupIndex.isValid() )
    {
        VLCModel *model = qobject_cast<VLCModel *>(currentView->model());
        input_item_t* p_input = model->getInputItem( popupIndex );
        MediaInfoDialog *mid = new MediaInfoDialog( p_intf, p_input );
        mid->setParent( PlaylistDialog::getInstance( p_intf ),
                        Qt::Dialog );
        mid->show();
    }
}

void StandardPLPanel::popupExplore()
{
    VLCModel *model = qobject_cast<VLCModel *>(currentView->model());
    QString uri = model->getURI( popupIndex );
    char *path = NULL;

    if( ! uri.isEmpty() )
348
        path = make_path( uri.toLatin1().constData() );
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374

    if( path == NULL )
        return;

    QDesktopServices::openUrl(
                QUrl::fromLocalFile( QFileInfo( qfu( path ) ).absolutePath() ) );

    free( path );
}

void StandardPLPanel::popupStream()
{
    VLCModel *model = qobject_cast<VLCModel *>(currentView->model());
    QString uri = model->getURI( popupIndex );
    if ( ! uri.isEmpty() )
        THEDP->streamingDialog( NULL, uri, false );
}

void StandardPLPanel::popupSave()
{
    VLCModel *model = qobject_cast<VLCModel *>(currentView->model());
    QString uri = model->getURI( popupIndex );
    if ( ! uri.isEmpty() )
        THEDP->streamingDialog( NULL, uri );
}

375
void StandardPLPanel::toggleColumnShown( int i )
376
{
377
    treeView->setColumnHidden( i, !treeView->isColumnHidden( i ) );
378
}
379

380
/* Search in the playlist */
381
void StandardPLPanel::search( const QString& searchText )
Clément Stenac's avatar
Clément Stenac committed
382
{
383 384
    int type;
    QString name;
385 386 387 388
    bool can_search;
    p_selector->getCurrentItemInfos( &type, &can_search, &name );

    if( type != SD_TYPE || !can_search )
389
    {
390 391 392
        bool flat = ( currentView == iconView ||
                      currentView == listView ||
                      currentView == picFlowView );
393 394 395 396
        model->search( searchText,
                       flat ? currentView->rootIndex() : QModelIndex(),
                       !flat );
    }
397 398
}

399
void StandardPLPanel::searchDelayed( const QString& searchText )
400 401 402
{
    int type;
    QString name;
403 404
    bool can_search;
    p_selector->getCurrentItemInfos( &type, &can_search, &name );
405

406
    if( type == SD_TYPE && can_search )
407
    {
408
        if( !name.isEmpty() && !searchText.isEmpty() )
409 410
            playlist_ServicesDiscoveryControl( THEPL, qtu( name ), SD_CMD_SEARCH,
                                              qtu( searchText ) );
411
    }
Clément Stenac's avatar
Clément Stenac committed
412 413
}

414 415
/* Set the root of the new Playlist */
/* This activated by the selector selection */
416
void StandardPLPanel::setRootItem( playlist_item_t *p_item, bool b )
417
{
418
#ifdef MEDIA_LIBRARY
419 420 421 422 423 424
    if( b )
    {
        msg_Dbg( p_intf, "Setting the SQL ML" );
        currentView->setModel( mlmodel );
    }
    else
425 426
#else
    Q_UNUSED( b );
427
#endif
428 429 430 431 432
    {
        if( currentView->model() != model )
            currentView->setModel( model );
        model->rebuild( p_item );
    }
433 434
}

435 436
void StandardPLPanel::browseInto( const QModelIndex &index )
{
437
    if( currentView == iconView || currentView == listView || currentView == picFlowView )
438
    {
439

440
        currentView->setRootIndex( index );
441 442 443 444 445 446 447 448 449 450 451

        /* When going toward root in LocationBar, scroll to the item
           that was previously as root */
        QModelIndex newIndex = model->index(currentRootIndexId,0);
        while( newIndex.isValid() && (newIndex.parent() != index) )
            newIndex = newIndex.parent();
        if( newIndex.isValid() )
            currentView->scrollTo( newIndex );

        /* Store new rootindexid*/
        currentRootIndexId = model->itemId( index );
452
        model->ensureArtRequested( index );
453 454
    }

455
    emit viewChanged( index );
456 457
}

458
void StandardPLPanel::browseInto()
459
{
460 461 462
    browseInto( (currentRootIndexId != -1 && currentView != treeView) ?
                 model->index( currentRootIndexId, 0 ) :
                 QModelIndex() );
463 464
}

465
void StandardPLPanel::wheelEvent( QWheelEvent *e )
Clément Stenac's avatar
Clément Stenac committed
466
{
467 468 469 470 471 472 473
    if( e->modifiers() & Qt::ControlModifier ) {
        int numSteps = e->delta() / 8 / 15;
        if( numSteps > 0)
            increaseZoom();
        else if( numSteps < 0)
            decreaseZoom();
    }
474 475 476 477
    // Accept this event in order to prevent unwanted volume up/down changes
    e->accept();
}

478
bool StandardPLPanel::eventFilter ( QObject *obj, QEvent * event )
479 480
{
    if (event->type() == QEvent::KeyPress)
Clément Stenac's avatar
Clément Stenac committed
481
    {
482 483
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        if( keyEvent->key() == Qt::Key_Delete ||
484
            keyEvent->key() == Qt::Key_Backspace )
485 486 487 488
        {
            deleteSelection();
            return true;
        }
Clément Stenac's avatar
Clément Stenac committed
489
    }
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
    else if ( event->type() == QEvent::Paint )
    {/* Warn! Don't filter events from anything else than views ! */
        if ( model->rowCount() == 0 && p_selector->getCurrentItemCategory() == PL_ITEM_TYPE )
        {
            QWidget *viewport = qobject_cast<QWidget *>( obj );
            QStylePainter painter( viewport );
            QPixmap dropzone(":/dropzone");
            QRect rect = viewport->geometry();
            QSize size = rect.size() / 2 - dropzone.size() / 2;
            rect.adjust( 0, size.height(), 0 , 0 );
            painter.drawItemPixmap( rect, Qt::AlignHCenter, dropzone );
            /* now select the zone just below the drop zone and let Qt center
               the text by itself */
            rect.adjust( 0, dropzone.size().height() + 10, 0, 0 );
            rect.setRight( viewport->geometry().width() );
            rect.setLeft( 0 );
            painter.drawItemText( rect,
                                  Qt::AlignHCenter,
                                  palette(),
                                  true,
Christoph Miebach's avatar
Typo  
Christoph Miebach committed
510
                                  qtr("Playlist is currently empty.\n"
511
                                      "Drop a file here or select a "
Christoph Miebach's avatar
Typo  
Christoph Miebach committed
512
                                      "media source from the left."),
513 514
                                  QPalette::Text );
        }
515 516 517 518 519 520 521 522 523 524 525 526 527 528
        else if ( spinnerAnimation->state() == PixmapAnimator::Running )
        {
            if ( currentView->model()->rowCount() )
                spinnerAnimation->stop(); /* Trick until SD emits events */
            else
            {
                QWidget *viewport = qobject_cast<QWidget *>( obj );
                QStylePainter painter( viewport );
                QPixmap *spinner = spinnerAnimation->getPixmap();
                QPoint point = viewport->geometry().center();
                point -= QPoint( spinner->size().width() / 2, spinner->size().height() / 2 );
                painter.drawPixmap( point, *spinner );
            }
        }
529
    }
530
    return false;
Clément Stenac's avatar
Clément Stenac committed
531 532 533 534
}

void StandardPLPanel::deleteSelection()
{
535
    QItemSelectionModel *selection = currentView->selectionModel();
Clément Stenac's avatar
Clément Stenac committed
536 537 538 539
    QModelIndexList list = selection->selectedIndexes();
    model->doDelete( list );
}

540 541 542 543 544 545
void StandardPLPanel::createIconView()
{
    iconView = new PlIconView( model, this );
    iconView->setContextMenuPolicy( Qt::CustomContextMenu );
    CONNECT( iconView, customContextMenuRequested( const QPoint & ),
             this, popupPlView( const QPoint & ) );
546 547
    CONNECT( iconView, activated( const QModelIndex & ),
             this, activate( const QModelIndex & ) );
548
    iconView->installEventFilter( this );
549
    iconView->viewport()->installEventFilter( this );
550
    viewStack->addWidget( iconView );
551 552
}

Jakob Leben's avatar
Jakob Leben committed
553 554 555 556 557 558 559 560
void StandardPLPanel::createListView()
{
    listView = new PlListView( model, this );
    listView->setContextMenuPolicy( Qt::CustomContextMenu );
    CONNECT( listView, customContextMenuRequested( const QPoint & ),
             this, popupPlView( const QPoint & ) );
    CONNECT( listView, activated( const QModelIndex & ),
             this, activate( const QModelIndex & ) );
561
    listView->installEventFilter( this );
562
    listView->viewport()->installEventFilter( this );
563
    viewStack->addWidget( listView );
Jakob Leben's avatar
Jakob Leben committed
564 565
}

566 567 568 569 570 571 572 573 574 575 576 577
void StandardPLPanel::createCoverView()
{
    picFlowView = new PicFlowView( model, this );
    picFlowView->setContextMenuPolicy( Qt::CustomContextMenu );
    CONNECT( picFlowView, customContextMenuRequested( const QPoint & ),
             this, popupPlView( const QPoint & ) );
    CONNECT( picFlowView, activated( const QModelIndex & ),
             this, activate( const QModelIndex & ) );
    viewStack->addWidget( picFlowView );
    picFlowView->installEventFilter( this );
}

578 579 580
void StandardPLPanel::createTreeView()
{
    /* Create and configure the QTreeView */
581
    treeView = new PlTreeView( model, this );
582

583 584
    /* setModel after setSortingEnabled(true), or the model will sort immediately! */

585 586
    /* Connections for the TreeView */
    CONNECT( treeView, activated( const QModelIndex& ),
587
             this, activate( const QModelIndex& ) );
588 589 590 591
    CONNECT( treeView->header(), customContextMenuRequested( const QPoint & ),
             this, popupSelectColumn( QPoint ) );
    CONNECT( treeView, customContextMenuRequested( const QPoint & ),
             this, popupPlView( const QPoint & ) );
592
    treeView->installEventFilter( this );
593
    treeView->viewport()->installEventFilter( this );
594 595 596 597 598 599

    /* SignalMapper for columns */
    selectColumnsSigMapper = new QSignalMapper( this );
    CONNECT( selectColumnsSigMapper, mapped( int ),
             this, toggleColumnShown( int ) );

600
    viewStack->addWidget( treeView );
601 602
}

603 604
void StandardPLPanel::updateZoom( int i )
{
605 606
    if ( i < 5 - QApplication::font().pointSize() ) return;
    if ( i > 3 + QApplication::font().pointSize() ) return;
607 608 609 610 611 612 613 614 615 616
    i_zoom = i;
#define A_ZOOM( view ) \
    if ( view ) \
    qobject_cast<AbstractPlViewItemDelegate*>( view->itemDelegate() )->setZoom( i_zoom )
    /* Can't iterate as picflow & tree aren't using custom delegate */
    A_ZOOM( iconView );
    A_ZOOM( listView );
#undef A_ZOOM
}

617 618
void StandardPLPanel::changeModel( bool b_ml )
{
619
#ifdef MEDIA_LIBRARY
620 621 622 623 624 625 626
    VLCModel *mod;
    if( b_ml )
        mod = mlmodel;
    else
        mod = model;
    if( currentView->model() != mod )
        currentView->setModel( mod );
627
#else
628
    Q_UNUSED( b_ml );
629 630
    if( currentView->model() != model )
        currentView->setModel( model );
631
#endif
632 633
}

634
void StandardPLPanel::showView( int i_view )
635
{
636
    bool b_treeViewCreated = false;
637

638 639 640
    switch( i_view )
    {
    case ICON_VIEW:
641
    {
642
        if( iconView == NULL )
643
            createIconView();
644
        currentView = iconView;
Jakob Leben's avatar
Jakob Leben committed
645 646 647 648 649 650 651
        break;
    }
    case LIST_VIEW:
    {
        if( listView == NULL )
            createListView();
        currentView = listView;
652
        break;
653
    }
654 655 656 657 658 659 660
    case PICTUREFLOW_VIEW:
    {
        if( picFlowView == NULL )
            createCoverView();
        currentView = picFlowView;
        break;
    }
661 662 663 664
    default:
    case TREE_VIEW:
    {
        if( treeView == NULL )
665
        {
666
            createTreeView();
667 668
            b_treeViewCreated = true;
        }
669 670 671
        currentView = treeView;
        break;
    }
672
    }
673

674 675
    changeModel( false );

676 677 678 679 680 681 682 683
    /* Restoring the header Columns must come after changeModel */
    if( b_treeViewCreated )
    {
        assert( treeView );
        if( getSettings()->contains( "Playlist/headerStateV2" ) )
        {
            treeView->header()->restoreState(getSettings()
                    ->value( "Playlist/headerStateV2" ).toByteArray() );
684 685 686 687 688 689
            /* if there is allready stuff in playlist, we don't sort it and we reset
               sorting */
            if( model->rowCount() )
            {
                treeView->header()->setSortIndicator( -1 , Qt::AscendingOrder );
            }
690 691 692 693 694 695 696 697 698 699 700 701
        }
        else
        {
            for( int m = 1, c = 0; m != COLUMN_END; m <<= 1, c++ )
            {
                treeView->setColumnHidden( c, !( m & COLUMN_DEFAULT ) );
                if( m == COLUMN_TITLE ) treeView->header()->resizeSection( c, 200 );
                else if( m == COLUMN_DURATION ) treeView->header()->resizeSection( c, 80 );
            }
        }
    }

702
    updateZoom( i_zoom );
703
    viewStack->setCurrentWidget( currentView );
704
    browseInto();
705
    gotoPlayingItem();
706
}
707

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
void StandardPLPanel::setWaiting( bool b )
{
    if ( b )
    {
        spinnerAnimation->setLoopCount( 20 ); /* Trick until SD emits an event */
        spinnerAnimation->start();
    }
    else
        spinnerAnimation->stop();
}

void StandardPLPanel::updateViewport()
{
    /* A single update on parent widget won't work */
    currentView->viewport()->repaint();
}

725
int StandardPLPanel::currentViewIndex() const
726 727 728 729 730 731 732
{
    if( currentView == treeView )
        return TREE_VIEW;
    else if( currentView == iconView )
        return ICON_VIEW;
    else if( currentView == listView )
        return LIST_VIEW;
733 734
    else
        return PICTUREFLOW_VIEW;
735 736
}

737 738 739 740 741
void StandardPLPanel::cycleViews()
{
    if( currentView == iconView )
        showView( TREE_VIEW );
    else if( currentView == treeView )
Jakob Leben's avatar
Jakob Leben committed
742 743
        showView( LIST_VIEW );
    else if( currentView == listView )
744
#ifndef NDEBUG
745 746
        showView( PICTUREFLOW_VIEW  );
    else if( currentView == picFlowView )
747
#endif
748 749 750 751 752
        showView( ICON_VIEW );
    else
        assert( 0 );
}

753 754
void StandardPLPanel::activate( const QModelIndex &index )
{
755
    if( currentView->model() == model )
756
    {
757 758 759 760 761 762 763 764 765 766 767
        /* If we are not a leaf node */
        if( !index.data( PLModel::IsLeafNodeRole ).toBool() )
        {
            if( currentView != treeView )
                browseInto( index );
        }
        else
        {
            playlist_Lock( THEPL );
            playlist_item_t *p_item = playlist_ItemGetById( THEPL, model->itemId( index ) );
            p_item->i_flags |= PLAYLIST_SUBITEM_STOP_FLAG;
768
            lastActivatedId = p_item->i_id;
769 770 771
            playlist_Unlock( THEPL );
            model->activateItem( index );
        }
772 773
    }
}
774

775
void StandardPLPanel::browseInto( int i_id )
776
{
777
    if( i_id != lastActivatedId ) return;
778

779
    QModelIndex index = model->index( i_id, 0 );
780

781
    if( currentView == treeView )
782
        treeView->setExpanded( index, true );
783 784
    else
        browseInto( index );
785

786
    lastActivatedId = -1;
787
}