]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/playlist/standardpanel.cpp
Qt: toAscii has been deprecated
[vlc] / modules / gui / qt4 / components / playlist / standardpanel.cpp
1 /*****************************************************************************
2  * standardpanel.cpp : The "standard" playlist panel : just a treeview
3  ****************************************************************************
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  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include "components/playlist/standardpanel.hpp"
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 #include "components/playlist/views.hpp"          /* 3 views */
35 #include "components/playlist/selector.hpp"       /* PLSelector */
36 #include "util/customwidgets.hpp"                 /* PixmapAnimator */
37 #include "menus.hpp"                              /* Popup */
38 #include "input_manager.hpp"                      /* THEMIM */
39 #include "dialogs_provider.hpp"                   /* THEDP */
40 #include "recents.hpp"                            /* RecentMRL */
41 #include "dialogs/playlist.hpp"                   /* Playlist Dialog */
42 #include "dialogs/mediainfo.hpp"                  /* MediaInfoDialog */
43 #include "util/qt_dirs.hpp"
44
45 #include <vlc_services_discovery.h>               /* SD_CMD_SEARCH */
46 #include <vlc_intf_strings.h>                     /* POP_ */
47
48 #define I_NEW_DIR \
49     I_DIR_OR_FOLDER( N_("Create Directory"), N_( "Create Folder" ) )
50 #define I_NEW_DIR_NAME \
51     I_DIR_OR_FOLDER( N_( "Enter name for new directory:" ), \
52                      N_( "Enter name for new folder:" ) )
53
54 #include <QHeaderView>
55 #include <QMenu>
56 #include <QKeyEvent>
57 #include <QWheelEvent>
58 #include <QStackedLayout>
59 #include <QSignalMapper>
60 #include <QSettings>
61 #include <QStylePainter>
62 #include <QInputDialog>
63 #include <QDesktopServices>
64 #include <QUrl>
65 #include <QFont>
66
67 #include <assert.h>
68
69 /* local helper */
70 inline QModelIndex popupIndex( QAbstractItemView *view );
71
72 StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
73                                   intf_thread_t *_p_intf,
74                                   playlist_item_t *p_root,
75                                   PLSelector *_p_selector,
76                                   VLCProxyModel *_p_model )
77                 : QWidget( _parent ),
78                   model( _p_model ),
79                   p_intf( _p_intf ),
80                   p_selector( _p_selector )
81 {
82     viewStack = new QStackedLayout( this );
83     viewStack->setSpacing( 0 ); viewStack->setMargin( 0 );
84     setMinimumWidth( 300 );
85
86     iconView    = NULL;
87     treeView    = NULL;
88     listView    = NULL;
89     picFlowView = NULL;
90
91     currentRootIndexPLId  = -1;
92     lastActivatedPLItemId     = -1;
93
94     QList<QString> frames;
95     frames << ":/util/wait1";
96     frames << ":/util/wait2";
97     frames << ":/util/wait3";
98     frames << ":/util/wait4";
99     spinnerAnimation = new PixmapAnimator( this, frames );
100     CONNECT( spinnerAnimation, pixmapReady( const QPixmap & ), this, updateViewport() );
101
102     /* Saved Settings */
103     int i_savedViewMode = getSettings()->value( "Playlist/view-mode", TREE_VIEW ).toInt();
104     i_zoom = getSettings()->value( "Playlist/zoom", 0 ).toInt();
105
106     showView( i_savedViewMode );
107
108     DCONNECT( THEMIM, leafBecameParent( int ),
109               this, browseInto( int ) );
110
111     CONNECT( model->sigs, currentIndexChanged( const QModelIndex& ),
112              this, handleExpansion( const QModelIndex& ) );
113     CONNECT( model->sigs, rootIndexChanged(), this, browseInto() );
114
115     setRootItem( p_root, false );
116 }
117
118 StandardPLPanel::~StandardPLPanel()
119 {
120     getSettings()->beginGroup("Playlist");
121     if( treeView )
122         getSettings()->setValue( "headerStateV2", treeView->header()->saveState() );
123     getSettings()->setValue( "view-mode", currentViewIndex() );
124     getSettings()->setValue( "zoom", i_zoom );
125     getSettings()->endGroup();
126 }
127
128 /* Unused anymore, but might be useful, like in right-click menu */
129 void StandardPLPanel::gotoPlayingItem()
130 {
131     currentView->scrollTo( model->currentIndex() );
132 }
133
134 void StandardPLPanel::handleExpansion( const QModelIndex& index )
135 {
136     assert( currentView );
137     if( currentRootIndexPLId != -1 && currentRootIndexPLId != model->itemId( index.parent(), PLAYLIST_ID ) )
138         browseInto( index.parent() );
139     currentView->scrollTo( index );
140 }
141
142 void StandardPLPanel::popupPlView( const QPoint &point )
143 {
144     QPoint globalPoint = currentView->viewport()->mapToGlobal( point );
145     QModelIndex index = currentView->indexAt( point );
146     if ( !index.isValid() )
147     {
148         currentView->clearSelection();
149     }
150     else if ( ! currentView->selectionModel()->selectedIndexes().contains( index ) )
151     {
152         currentView->selectionModel()->select( index, QItemSelectionModel::Select );
153     }
154
155     if( !popup( globalPoint ) ) VLCMenuBar::PopupMenu( p_intf, true );
156 }
157
158 /*********** Popup *********/
159 bool StandardPLPanel::popup( const QPoint &point )
160 {
161     QModelIndex index = popupIndex( currentView ); /* index for menu logic only. Do not store.*/
162     VLCProxyModel *model = qobject_cast<VLCProxyModel *>(currentView->model());
163
164 #define ADD_MENU_ENTRY( icon, title, act ) \
165     if ( model->isSupportedAction( act, index ) )\
166     {\
167     action = menu.addAction( icon, title ); \
168     container.action = act; \
169     action->setData( QVariant::fromValue( container ) );\
170     }
171
172     /* */
173     QMenu menu;
174     QAction *action;
175     VLCModelSubInterface::actionsContainerType container;
176
177     /* Play/Stream/Info static actions */
178
179     ADD_MENU_ENTRY( QIcon( ":/menu/play" ), qtr(I_POP_PLAY),
180                     VLCModelSubInterface::ACTION_PLAY )
181
182     ADD_MENU_ENTRY( QIcon( ":/menu/stream" ), qtr(I_POP_STREAM),
183                     VLCModelSubInterface::ACTION_STREAM )
184
185     ADD_MENU_ENTRY( QIcon(), qtr(I_POP_SAVE),
186                     VLCModelSubInterface::ACTION_SAVE );
187
188     ADD_MENU_ENTRY( QIcon( ":/menu/info" ), qtr(I_POP_INFO),
189                     VLCModelSubInterface::ACTION_INFO );
190
191     menu.addSeparator();
192
193     ADD_MENU_ENTRY( QIcon( ":/type/folder-grey" ), qtr(I_POP_EXPLORE),
194                     VLCModelSubInterface::ACTION_EXPLORE );
195
196     QIcon addIcon( ":/buttons/playlist/playlist_add" );
197
198     ADD_MENU_ENTRY( addIcon, qtr(I_POP_NEWFOLDER),
199                     VLCModelSubInterface::ACTION_CREATENODE )
200
201     menu.addSeparator();
202     /* In PL or ML, allow to add a file/folder */
203     ADD_MENU_ENTRY( addIcon, qtr(I_PL_ADDF),
204                     VLCModelSubInterface::ACTION_ENQUEUEFILE )
205
206     ADD_MENU_ENTRY( addIcon, qtr(I_PL_ADDDIR),
207                     VLCModelSubInterface::ACTION_ENQUEUEDIR )
208
209     ADD_MENU_ENTRY( addIcon, qtr(I_OP_ADVOP),
210                     VLCModelSubInterface::ACTION_ENQUEUEGENERIC )
211
212     ADD_MENU_ENTRY( QIcon(), qtr(I_PL_ADDPL),
213                     VLCModelSubInterface::ACTION_ADDTOPLAYLIST );
214
215     menu.addSeparator();
216
217     /* Item removal */
218
219     ADD_MENU_ENTRY( QIcon( ":/buttons/playlist/playlist_remove" ), qtr(I_POP_DEL),
220                     VLCModelSubInterface::ACTION_REMOVE );
221
222     ADD_MENU_ENTRY( QIcon( ":/toolbar/clear" ), qtr("Clear the playlist"),
223                     VLCModelSubInterface::ACTION_CLEAR );
224
225     menu.addSeparator();
226
227     /* Playlist sorting */
228     if ( model->isSupportedAction( VLCModelSubInterface::ACTION_SORT, index ) )
229     {
230         QMenu *sortingMenu = new QMenu( qtr( "Sort by" ) );
231         /* Choose what columns to show in sorting menu, not sure if this should be configurable*/
232         QList<int> sortingColumns;
233         sortingColumns << COLUMN_TITLE << COLUMN_ARTIST << COLUMN_ALBUM << COLUMN_TRACK_NUMBER << COLUMN_URI;
234         container.action = VLCModelSubInterface::ACTION_SORT;
235         foreach( int Column, sortingColumns )
236         {
237             action = sortingMenu->addAction( qfu( psz_column_title( Column ) ) + " " + qtr("Ascending") );
238             container.column = model->columnFromMeta(Column) + 1;
239             action->setData( QVariant::fromValue( container ) );
240
241             action = sortingMenu->addAction( qfu( psz_column_title( Column ) ) + " " + qtr("Descending") );
242             container.column = -1 * (model->columnFromMeta(Column)+1);
243             action->setData( QVariant::fromValue( container ) );
244         }
245         menu.addMenu( sortingMenu );
246     }
247
248     /* Zoom */
249     QMenu *zoomMenu = new QMenu( qtr( "Display size" ) );
250     zoomMenu->addAction( qtr( "Increase" ), this, SLOT( increaseZoom() ) );
251     zoomMenu->addAction( qtr( "Decrease" ), this, SLOT( decreaseZoom() ) );
252     menu.addMenu( zoomMenu );
253
254     CONNECT( &menu, triggered( QAction * ), this, popupAction( QAction * ) );
255
256     menu.addMenu( StandardPLPanel::viewSelectionMenu( this ) );
257
258     /* Display and forward the result */
259     if( !menu.isEmpty() )
260     {
261         menu.exec( point ); return true;
262     }
263     else return false;
264
265 #undef ADD_MENU_ENTRY
266 }
267
268 void StandardPLPanel::popupAction( QAction *action )
269 {
270     VLCProxyModel *model = qobject_cast<VLCProxyModel *>(currentView->model());
271     VLCModelSubInterface::actionsContainerType a =
272             action->data().value<VLCModelSubInterface::actionsContainerType>();
273     QModelIndexList list = currentView->selectionModel()->selectedRows();
274     QModelIndex index = popupIndex( currentView );
275     char *path = NULL;
276     OpenDialog *dialog;
277     QString temp;
278     QStringList uris;
279     bool ok;
280
281     /* first try to complete actions requiring missing parameters thru UI dialogs */
282     switch( a.action )
283     {
284     case VLCModelSubInterface::ACTION_INFO:
285         /* locally handled only */
286         if( index.isValid() )
287         {
288             input_item_t* p_input = model->getInputItem( index );
289             MediaInfoDialog *mid = new MediaInfoDialog( p_intf, p_input );
290             mid->setParent( PlaylistDialog::getInstance( p_intf ),
291                             Qt::Dialog );
292             mid->show();
293         }
294         break;
295
296     case VLCModelSubInterface::ACTION_EXPLORE:
297         /* locally handled only */
298         temp = model->getURI( index );
299         if( ! temp.isEmpty() ) path = make_path( temp.toLatin1().constData() );
300         if( path == NULL ) return;
301         QDesktopServices::openUrl(
302                     QUrl::fromLocalFile( QFileInfo( qfu( path ) ).absolutePath() ) );
303         free( path );
304         break;
305
306     case VLCModelSubInterface::ACTION_STREAM:
307         /* locally handled only */
308         temp = model->getURI( index );
309         if ( ! temp.isEmpty() )
310             THEDP->streamingDialog( NULL, temp, false );
311         break;
312
313     case VLCModelSubInterface::ACTION_SAVE:
314         /* locally handled only */
315         temp = model->getURI( index );
316         if ( ! temp.isEmpty() )
317             THEDP->streamingDialog( NULL, temp );
318         break;
319
320     case VLCModelSubInterface::ACTION_CREATENODE:
321         temp = QInputDialog::getText( PlaylistDialog::getInstance( p_intf ),
322             qtr( I_NEW_DIR ), qtr( I_NEW_DIR_NAME ),
323             QLineEdit::Normal, QString(), &ok);
324         if ( !ok ) return;
325         model->createNode( index, temp );
326         break;
327
328     case VLCModelSubInterface::ACTION_ENQUEUEFILE:
329         uris = THEDP->showSimpleOpen();
330         if ( uris.isEmpty() ) return;
331         uris.sort();
332         foreach( const QString &file, uris )
333             a.uris << qtu( toURI( toNativeSeparators( file ) ) );
334         action->setData( QVariant::fromValue( a ) );
335         if ( model->action( action, list ) )
336             foreach( const QString &file, a.uris )
337                 RecentsMRL::getInstance( p_intf )->addRecent( file );
338         break;
339
340     case VLCModelSubInterface::ACTION_ENQUEUEDIR:
341         temp = THEDP->getDirectoryDialog();
342         if ( temp.isEmpty() ) return;
343         a.uris << temp;
344         action->setData( QVariant::fromValue( a ) );
345         model->action( action, list );
346         break;
347
348     case VLCModelSubInterface::ACTION_ENQUEUEGENERIC:
349         dialog = OpenDialog::getInstance( this, p_intf, false, SELECT, true, true );
350         dialog->showTab( OPEN_FILE_TAB );
351         dialog->exec(); /* make it modal */
352         a.uris = dialog->getMRLs( false );
353         a.options = dialog->getOptions();
354         if ( a.uris.isEmpty() ) return;
355         action->setData( QVariant::fromValue( a ) );
356         if ( model->action( action, list ) )
357             foreach( const QString &file, a.uris )
358                 RecentsMRL::getInstance( p_intf )->addRecent( file );
359         break;
360
361     default:
362         model->action( action, list );
363     }
364 }
365
366 QMenu* StandardPLPanel::viewSelectionMenu( StandardPLPanel *panel )
367 {
368     QMenu *viewMenu = new QMenu( qtr( "Playlist View Mode" ) );
369     QSignalMapper *viewSelectionMapper = new QSignalMapper( viewMenu );
370     CONNECT( viewSelectionMapper, mapped( int ), panel, showView( int ) );
371
372     QActionGroup *viewGroup = new QActionGroup( viewMenu );
373 # define MAX_VIEW StandardPLPanel::VIEW_COUNT
374     for( int i = 0; i < MAX_VIEW; i++ )
375     {
376         QAction *action = viewMenu->addAction( viewNames[i] );
377         action->setCheckable( true );
378         viewGroup->addAction( action );
379         viewSelectionMapper->setMapping( action, i );
380         CONNECT( action, triggered(), viewSelectionMapper, map() );
381         if( panel->currentViewIndex() == i )
382             action->setChecked( true );
383     }
384     return viewMenu;
385 }
386
387 inline QModelIndex popupIndex( QAbstractItemView *view )
388 {
389     QModelIndexList list = view->selectionModel()->selectedIndexes();
390     if ( list.isEmpty() )
391         return QModelIndex();
392     else
393         return list.first();
394 }
395
396 void StandardPLPanel::popupSelectColumn( QPoint )
397 {
398     QMenu menu;
399     assert( treeView );
400
401     /* We do not offer the option to hide index 0 column, or
402      * QTreeView will behave weird */
403     for( int i = 1 << 1, j = 1; i < COLUMN_END; i <<= 1, j++ )
404     {
405         QAction* option = menu.addAction( qfu( psz_column_title( i ) ) );
406         option->setCheckable( true );
407         option->setChecked( !treeView->isColumnHidden( j ) );
408         selectColumnsSigMapper->setMapping( option, j );
409         CONNECT( option, triggered(), selectColumnsSigMapper, map() );
410     }
411     menu.exec( QCursor::pos() );
412 }
413
414 void StandardPLPanel::toggleColumnShown( int i )
415 {
416     treeView->setColumnHidden( i, !treeView->isColumnHidden( i ) );
417 }
418
419 /* Search in the playlist */
420 void StandardPLPanel::search( const QString& searchText )
421 {
422     int type;
423     QString name;
424     bool can_search;
425     p_selector->getCurrentItemInfos( &type, &can_search, &name );
426
427     if( type != SD_TYPE || !can_search )
428     {
429         bool flat = ( currentView == iconView ||
430                       currentView == listView ||
431                       currentView == picFlowView );
432         model->filter( searchText,
433                        flat ? currentView->rootIndex() : QModelIndex(),
434                        !flat );
435     }
436 }
437
438 void StandardPLPanel::searchDelayed( const QString& searchText )
439 {
440     int type;
441     QString name;
442     bool can_search;
443     p_selector->getCurrentItemInfos( &type, &can_search, &name );
444
445     if( type == SD_TYPE && can_search )
446     {
447         if( !name.isEmpty() && !searchText.isEmpty() )
448             playlist_ServicesDiscoveryControl( THEPL, qtu( name ), SD_CMD_SEARCH,
449                                               qtu( searchText ) );
450     }
451 }
452
453 /* Set the root of the new Playlist */
454 /* This activated by the selector selection */
455 void StandardPLPanel::setRootItem( playlist_item_t *p_item, bool b )
456 {
457 #ifdef SQL_MEDIA_LIBRARY
458     if( b )
459     {
460         msg_Dbg( p_intf, "Setting the SQL ML" );
461         if ( model->switchToModel( VLCProxyModel::SQLML_MODEL ) )
462             currentView->setModel( model );
463     }
464     else
465 #else
466     Q_UNUSED( b );
467 #endif
468     {
469         if ( model->switchToModel( VLCProxyModel::PL_MODEL ) )
470             model->rebuild( p_item );
471     }
472 }
473
474 void StandardPLPanel::browseInto( const QModelIndex &index )
475 {
476     if( currentView == iconView || currentView == listView || currentView == picFlowView )
477     {
478
479         currentView->setRootIndex( index );
480
481         /* When going toward root in LocationBar, scroll to the item
482            that was previously as root */
483         QModelIndex newIndex = model->indexByPLID(currentRootIndexPLId,0);
484         while( newIndex.isValid() && (newIndex.parent() != index) )
485             newIndex = newIndex.parent();
486         if( newIndex.isValid() )
487             currentView->scrollTo( newIndex );
488
489         /* Store new rootindexid*/
490         currentRootIndexPLId = model->itemId( index, PLAYLIST_ID );
491
492         model->ensureArtRequested( index );
493     }
494
495     emit viewChanged( index );
496 }
497
498 void StandardPLPanel::browseInto()
499 {
500     browseInto( (currentRootIndexPLId != -1 && currentView != treeView) ?
501                  model->indexByPLID( currentRootIndexPLId, 0 ) :
502                  QModelIndex() );
503 }
504
505 void StandardPLPanel::wheelEvent( QWheelEvent *e )
506 {
507     if( e->modifiers() & Qt::ControlModifier ) {
508         int numSteps = e->delta() / 8 / 15;
509         if( numSteps > 0)
510             increaseZoom();
511         else if( numSteps < 0)
512             decreaseZoom();
513     }
514     // Accept this event in order to prevent unwanted volume up/down changes
515     e->accept();
516 }
517
518 bool StandardPLPanel::eventFilter ( QObject *obj, QEvent * event )
519 {
520     if (event->type() == QEvent::KeyPress)
521     {
522         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
523         if( keyEvent->key() == Qt::Key_Delete ||
524             keyEvent->key() == Qt::Key_Backspace )
525         {
526             deleteSelection();
527             return true;
528         }
529     }
530     else if ( event->type() == QEvent::Paint )
531     {/* Warn! Don't filter events from anything else than views ! */
532         if ( model->rowCount() == 0 && p_selector->getCurrentItemCategory() == PL_ITEM_TYPE )
533         {
534             QWidget *viewport = qobject_cast<QWidget *>( obj );
535             QStylePainter painter( viewport );
536             QPixmap dropzone(":/dropzone");
537             QRect rect = viewport->geometry();
538             QSize size = rect.size() / 2 - dropzone.size() / 2;
539             rect.adjust( 0, size.height(), 0 , 0 );
540             painter.drawItemPixmap( rect, Qt::AlignHCenter, dropzone );
541             /* now select the zone just below the drop zone and let Qt center
542                the text by itself */
543             rect.adjust( 0, dropzone.size().height() + 10, 0, 0 );
544             rect.setRight( viewport->geometry().width() );
545             rect.setLeft( 0 );
546             painter.drawItemText( rect,
547                                   Qt::AlignHCenter,
548                                   palette(),
549                                   true,
550                                   qtr("Playlist is currently empty.\n"
551                                       "Drop a file here or select a "
552                                       "media source from the left."),
553                                   QPalette::Text );
554         }
555         else if ( spinnerAnimation->state() == PixmapAnimator::Running )
556         {
557             if ( currentView->model()->rowCount() )
558                 spinnerAnimation->stop(); /* Trick until SD emits events */
559             else
560             {
561                 QWidget *viewport = qobject_cast<QWidget *>( obj );
562                 QStylePainter painter( viewport );
563                 QPixmap *spinner = spinnerAnimation->getPixmap();
564                 QPoint point = viewport->geometry().center();
565                 point -= QPoint( spinner->size().width() / 2, spinner->size().height() / 2 );
566                 painter.drawPixmap( point, *spinner );
567             }
568         }
569     }
570     return false;
571 }
572
573 void StandardPLPanel::deleteSelection()
574 {
575     QModelIndexList list = currentView->selectionModel()->selectedIndexes();
576     model->doDelete( list );
577 }
578
579 void StandardPLPanel::createIconView()
580 {
581     iconView = new PlIconView( model, this );
582     iconView->setContextMenuPolicy( Qt::CustomContextMenu );
583     CONNECT( iconView, customContextMenuRequested( const QPoint & ),
584              this, popupPlView( const QPoint & ) );
585     CONNECT( iconView, activated( const QModelIndex & ),
586              this, activate( const QModelIndex & ) );
587     iconView->installEventFilter( this );
588     iconView->viewport()->installEventFilter( this );
589     viewStack->addWidget( iconView );
590 }
591
592 void StandardPLPanel::createListView()
593 {
594     listView = new PlListView( model, this );
595     listView->setContextMenuPolicy( Qt::CustomContextMenu );
596     CONNECT( listView, customContextMenuRequested( const QPoint & ),
597              this, popupPlView( const QPoint & ) );
598     CONNECT( listView, activated( const QModelIndex & ),
599              this, activate( const QModelIndex & ) );
600     listView->installEventFilter( this );
601     listView->viewport()->installEventFilter( this );
602     viewStack->addWidget( listView );
603 }
604
605 void StandardPLPanel::createCoverView()
606 {
607     picFlowView = new PicFlowView( model, this );
608     picFlowView->setContextMenuPolicy( Qt::CustomContextMenu );
609     CONNECT( picFlowView, customContextMenuRequested( const QPoint & ),
610              this, popupPlView( const QPoint & ) );
611     CONNECT( picFlowView, activated( const QModelIndex & ),
612              this, activate( const QModelIndex & ) );
613     viewStack->addWidget( picFlowView );
614     picFlowView->installEventFilter( this );
615 }
616
617 void StandardPLPanel::createTreeView()
618 {
619     /* Create and configure the QTreeView */
620     treeView = new PlTreeView( model, this );
621
622     /* setModel after setSortingEnabled(true), or the model will sort immediately! */
623
624     /* Connections for the TreeView */
625     CONNECT( treeView, activated( const QModelIndex& ),
626              this, activate( const QModelIndex& ) );
627     CONNECT( treeView->header(), customContextMenuRequested( const QPoint & ),
628              this, popupSelectColumn( QPoint ) );
629     CONNECT( treeView, customContextMenuRequested( const QPoint & ),
630              this, popupPlView( const QPoint & ) );
631     treeView->installEventFilter( this );
632     treeView->viewport()->installEventFilter( this );
633
634     /* SignalMapper for columns */
635     selectColumnsSigMapper = new QSignalMapper( this );
636     CONNECT( selectColumnsSigMapper, mapped( int ),
637              this, toggleColumnShown( int ) );
638
639     viewStack->addWidget( treeView );
640 }
641
642 void StandardPLPanel::updateZoom( int i )
643 {
644     if ( i < 5 - QApplication::font().pointSize() ) return;
645     if ( i > 3 + QApplication::font().pointSize() ) return;
646     i_zoom = i;
647 #define A_ZOOM( view ) \
648     if ( view ) \
649     qobject_cast<AbstractPlViewItemDelegate*>( view->itemDelegate() )->setZoom( i_zoom )
650     /* Can't iterate as picflow & tree aren't using custom delegate */
651     A_ZOOM( iconView );
652     A_ZOOM( listView );
653 #undef A_ZOOM
654 }
655
656 void StandardPLPanel::showView( int i_view )
657 {
658     bool b_treeViewCreated = false;
659
660     switch( i_view )
661     {
662     case ICON_VIEW:
663     {
664         if( iconView == NULL )
665             createIconView();
666         currentView = iconView;
667         break;
668     }
669     case LIST_VIEW:
670     {
671         if( listView == NULL )
672             createListView();
673         currentView = listView;
674         break;
675     }
676     case PICTUREFLOW_VIEW:
677     {
678         if( picFlowView == NULL )
679             createCoverView();
680         currentView = picFlowView;
681         break;
682     }
683     default:
684     case TREE_VIEW:
685     {
686         if( treeView == NULL )
687         {
688             createTreeView();
689             b_treeViewCreated = true;
690         }
691         currentView = treeView;
692         break;
693     }
694     }
695
696     currentView->setModel( model );
697
698     /* Restoring the header Columns must come after changeModel */
699     if( b_treeViewCreated )
700     {
701         assert( treeView );
702         if( getSettings()->contains( "Playlist/headerStateV2" ) )
703         {
704             treeView->header()->restoreState(getSettings()
705                     ->value( "Playlist/headerStateV2" ).toByteArray() );
706             /* if there is allready stuff in playlist, we don't sort it and we reset
707                sorting */
708             if( model->rowCount() )
709             {
710                 treeView->header()->setSortIndicator( -1 , Qt::AscendingOrder );
711             }
712         }
713         else
714         {
715             for( int m = 1, c = 0; m != COLUMN_END; m <<= 1, c++ )
716             {
717                 treeView->setColumnHidden( c, !( m & COLUMN_DEFAULT ) );
718                 if( m == COLUMN_TITLE ) treeView->header()->resizeSection( c, 200 );
719                 else if( m == COLUMN_DURATION ) treeView->header()->resizeSection( c, 80 );
720             }
721         }
722     }
723
724     updateZoom( i_zoom );
725     viewStack->setCurrentWidget( currentView );
726     browseInto();
727     gotoPlayingItem();
728 }
729
730 void StandardPLPanel::setWaiting( bool b )
731 {
732     if ( b )
733     {
734         spinnerAnimation->setLoopCount( 20 ); /* Trick until SD emits an event */
735         spinnerAnimation->start();
736     }
737     else
738         spinnerAnimation->stop();
739 }
740
741 void StandardPLPanel::updateViewport()
742 {
743     /* A single update on parent widget won't work */
744     currentView->viewport()->repaint();
745 }
746
747 int StandardPLPanel::currentViewIndex() const
748 {
749     if( currentView == treeView )
750         return TREE_VIEW;
751     else if( currentView == iconView )
752         return ICON_VIEW;
753     else if( currentView == listView )
754         return LIST_VIEW;
755     else
756         return PICTUREFLOW_VIEW;
757 }
758
759 void StandardPLPanel::cycleViews()
760 {
761     if( currentView == iconView )
762         showView( TREE_VIEW );
763     else if( currentView == treeView )
764         showView( LIST_VIEW );
765     else if( currentView == listView )
766 #ifndef NDEBUG
767         showView( PICTUREFLOW_VIEW  );
768     else if( currentView == picFlowView )
769 #endif
770         showView( ICON_VIEW );
771     else
772         assert( 0 );
773 }
774
775 void StandardPLPanel::activate( const QModelIndex &index )
776 {
777     if( currentView->model() == model )
778     {
779         /* If we are not a leaf node */
780         if( !index.data( VLCModelSubInterface::IsLeafNodeRole ).toBool() )
781         {
782             if( currentView != treeView )
783                 browseInto( index );
784         }
785         else
786         {
787             playlist_Lock( THEPL );
788             playlist_item_t *p_item = playlist_ItemGetById( THEPL, model->itemId( index, PLAYLIST_ID ) );
789             if ( p_item )
790             {
791                 p_item->i_flags |= PLAYLIST_SUBITEM_STOP_FLAG;
792                 lastActivatedPLItemId = p_item->i_id;
793             }
794             playlist_Unlock( THEPL );
795             if ( p_item && index.isValid() )
796                 model->activateItem( index );
797         }
798     }
799 }
800
801 void StandardPLPanel::browseInto( int i_pl_item_id )
802 {
803     if( i_pl_item_id != lastActivatedPLItemId ) return;
804
805     QModelIndex index = model->indexByPLID( i_pl_item_id, 0 );
806
807     if( currentView == treeView )
808         treeView->setExpanded( index, true );
809     else
810         browseInto( index );
811
812     lastActivatedPLItemId = -1;
813 }