1 /*****************************************************************************
2 * standardpanel.cpp : The "standard" playlist panel : just a treeview
3 ****************************************************************************
4 * Copyright © 2000-2010 VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Jean-Baptiste Kempf <jb@videolan.org>
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.
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.
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 *****************************************************************************/
29 #include "components/playlist/standardpanel.hpp"
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 "menus.hpp" /* Popup */
37 #include "input_manager.hpp" /* THEMIM */
39 #include "sorting.h" /* Columns order */
41 #include <vlc_services_discovery.h> /* SD_CMD_SEARCH */
43 #include <QHeaderView>
44 #include <QModelIndexList>
47 #include <QWheelEvent>
48 #include <QStackedLayout>
49 #include <QSignalMapper>
54 StandardPLPanel::StandardPLPanel( PlaylistWidget
*_parent
,
55 intf_thread_t
*_p_intf
,
56 playlist_item_t
*p_root
,
57 PLSelector
*_p_selector
,
60 : QWidget( _parent
), p_intf( _p_intf
),
61 p_selector( _p_selector
), model( _p_model
),
64 viewStack
= new QStackedLayout( this );
65 viewStack
->setSpacing( 0 ); viewStack
->setMargin( 0 );
66 setMinimumWidth( 300 );
73 currentRootIndexId
= -1;
77 getSettings()->beginGroup("Playlist");
78 int i_savedViewMode
= getSettings()->value( "view-mode", TREE_VIEW
).toInt();
79 getSettings()->endGroup();
80 /* Limit the saved value to a possible one inside [0, VIEW_COUNT[ */
81 if(i_savedViewMode
< 0 || i_savedViewMode
>= VIEW_COUNT
)
84 showView( i_savedViewMode
);
86 DCONNECT( THEMIM
, leafBecameParent( input_item_t
*),
87 this, browseInto( input_item_t
* ) );
89 CONNECT( model
, currentChanged( const QModelIndex
& ),
90 this, handleExpansion( const QModelIndex
& ) );
91 CONNECT( model
, rootChanged(), this, handleRootChange() );
93 setRoot( p_root
, false );
96 StandardPLPanel::~StandardPLPanel()
98 getSettings()->beginGroup("Playlist");
100 getSettings()->setValue( "headerStateV2", treeView
->header()->saveState() );
101 if( currentView
== treeView
)
102 getSettings()->setValue( "view-mode", TREE_VIEW
);
103 else if( currentView
== listView
)
104 getSettings()->setValue( "view-mode", LIST_VIEW
);
105 else if( currentView
== iconView
)
106 getSettings()->setValue( "view-mode", ICON_VIEW
);
107 else if( currentView
== picFlowView
)
108 getSettings()->setValue( "view-mode", PICTUREFLOW_VIEW
);
109 getSettings()->endGroup();
112 /* Unused anymore, but might be useful, like in right-click menu */
113 void StandardPLPanel::gotoPlayingItem()
115 currentView
->scrollTo( model
->currentIndex() );
118 void StandardPLPanel::handleExpansion( const QModelIndex
& index
)
120 assert( currentView
);
121 if( currentRootIndexId
!= -1 && currentRootIndexId
!= model
->itemId( index
.parent() ) )
122 browseInto( index
.parent() );
123 currentView
->scrollTo( index
);
126 void StandardPLPanel::handleRootChange()
131 void StandardPLPanel::popupPlView( const QPoint
&point
)
133 QModelIndex index
= currentView
->indexAt( point
);
134 QPoint globalPoint
= currentView
->viewport()->mapToGlobal( point
);
135 QItemSelectionModel
*selection
= currentView
->selectionModel();
136 QModelIndexList list
= selection
->selectedIndexes();
138 if( !model
->popup( index
, globalPoint
, list
) )
139 QVLCMenu::PopupMenu( p_intf
, true );
142 void StandardPLPanel::popupSelectColumn( QPoint pos
)
147 /* We do not offer the option to hide index 0 column, or
148 * QTreeView will behave weird */
150 for( i
= 1 << 1, j
= 1; i
< COLUMN_END
; i
<<= 1, j
++ )
152 QAction
* option
= menu
.addAction( qfu( psz_column_title( i
) ) );
153 option
->setCheckable( true );
154 option
->setChecked( !treeView
->isColumnHidden( j
) );
155 selectColumnsSigMapper
->setMapping( option
, j
);
156 CONNECT( option
, triggered(), selectColumnsSigMapper
, map() );
158 menu
.exec( QCursor::pos() );
161 void StandardPLPanel::toggleColumnShown( int i
)
163 treeView
->setColumnHidden( i
, !treeView
->isColumnHidden( i
) );
166 /* Search in the playlist */
167 void StandardPLPanel::search( const QString
& searchText
)
171 p_selector
->getCurrentSelectedItem( &type
, &name
);
172 if( type
!= SD_TYPE
)
174 bool flat
= currentView
== iconView
|| currentView
== listView
|| currentView
== picFlowView
;
175 model
->search( searchText
,
176 flat
? currentView
->rootIndex() : QModelIndex(),
181 void StandardPLPanel::searchDelayed( const QString
& searchText
)
185 p_selector
->getCurrentSelectedItem( &type
, &name
);
187 if( type
== SD_TYPE
)
189 if( !name
.isEmpty() && !searchText
.isEmpty() )
190 playlist_ServicesDiscoveryControl( THEPL
, qtu( name
), SD_CMD_SEARCH
, qtu( searchText
) );
194 /* Set the root of the new Playlist */
195 /* This activated by the selector selection */
196 void StandardPLPanel::setRoot( playlist_item_t
*p_item
, bool b
)
201 msg_Dbg( p_intf
, "Setting the SQL ML" );
202 currentView
->setModel( mlmodel
);
207 msg_Dbg( p_intf
, "Normal PL/ML or SD" );
208 if( currentView
->model() != model
)
209 currentView
->setModel( model
);
210 model
->rebuild( p_item
);
214 void StandardPLPanel::browseInto( const QModelIndex
&index
)
216 if( currentView
== iconView
|| currentView
== listView
|| currentView
== picFlowView
)
218 currentRootIndexId
= model
->itemId( index
);
219 currentView
->setRootIndex( index
);
222 emit
viewChanged( index
);
225 void StandardPLPanel::browseInto( )
227 browseInto( currentRootIndexId
!= -1 && currentView
!= treeView
?
228 model
->index( currentRootIndexId
, 0 ) :
232 void StandardPLPanel::wheelEvent( QWheelEvent
*e
)
234 // Accept this event in order to prevent unwanted volume up/down changes
238 bool StandardPLPanel::eventFilter ( QObject
* watched
, QEvent
* event
)
240 if (event
->type() == QEvent::KeyPress
)
242 QKeyEvent
*keyEvent
= static_cast<QKeyEvent
*>(event
);
243 if( keyEvent
->key() == Qt::Key_Delete
||
244 keyEvent
->key() == Qt::Key_Backspace
)
253 void StandardPLPanel::deleteSelection()
255 QItemSelectionModel
*selection
= currentView
->selectionModel();
256 QModelIndexList list
= selection
->selectedIndexes();
257 model
->doDelete( list
);
260 void StandardPLPanel::createIconView()
262 iconView
= new PlIconView( model
, this );
263 iconView
->setContextMenuPolicy( Qt::CustomContextMenu
);
264 CONNECT( iconView
, customContextMenuRequested( const QPoint
& ),
265 this, popupPlView( const QPoint
& ) );
266 CONNECT( iconView
, activated( const QModelIndex
& ),
267 this, activate( const QModelIndex
& ) );
268 iconView
->installEventFilter( this );
269 viewStack
->addWidget( iconView
);
272 void StandardPLPanel::createListView()
274 listView
= new PlListView( model
, this );
275 listView
->setContextMenuPolicy( Qt::CustomContextMenu
);
276 CONNECT( listView
, customContextMenuRequested( const QPoint
& ),
277 this, popupPlView( const QPoint
& ) );
278 CONNECT( listView
, activated( const QModelIndex
& ),
279 this, activate( const QModelIndex
& ) );
280 listView
->installEventFilter( this );
281 viewStack
->addWidget( listView
);
284 void StandardPLPanel::createCoverView()
286 picFlowView
= new PicFlowView( model
, this );
287 picFlowView
->setContextMenuPolicy( Qt::CustomContextMenu
);
288 CONNECT( picFlowView
, customContextMenuRequested( const QPoint
& ),
289 this, popupPlView( const QPoint
& ) );
290 CONNECT( picFlowView
, activated( const QModelIndex
& ),
291 this, activate( const QModelIndex
& ) );
292 viewStack
->addWidget( picFlowView
);
293 picFlowView
->installEventFilter( this );
296 void StandardPLPanel::createTreeView()
298 /* Create and configure the QTreeView */
299 treeView
= new PlTreeView
;
301 treeView
->setIconSize( QSize( 20, 20 ) );
302 treeView
->setAlternatingRowColors( true );
303 treeView
->setAnimated( true );
304 treeView
->setUniformRowHeights( true );
305 treeView
->setSortingEnabled( true );
306 treeView
->setAttribute( Qt::WA_MacShowFocusRect
, false );
307 treeView
->header()->setSortIndicator( -1 , Qt::AscendingOrder
);
308 treeView
->header()->setSortIndicatorShown( true );
309 treeView
->header()->setClickable( true );
310 treeView
->header()->setContextMenuPolicy( Qt::CustomContextMenu
);
312 treeView
->setSelectionBehavior( QAbstractItemView::SelectRows
);
313 treeView
->setSelectionMode( QAbstractItemView::ExtendedSelection
);
314 treeView
->setDragEnabled( true );
315 treeView
->setAcceptDrops( true );
316 treeView
->setDropIndicatorShown( true );
317 treeView
->setContextMenuPolicy( Qt::CustomContextMenu
);
319 /* setModel after setSortingEnabled(true), or the model will sort immediately! */
321 getSettings()->beginGroup("Playlist");
323 if( getSettings()->contains( "headerStateV2" ) )
325 treeView
->header()->restoreState(
326 getSettings()->value( "headerStateV2" ).toByteArray() );
330 for( int m
= 1, c
= 0; m
!= COLUMN_END
; m
<<= 1, c
++ )
332 treeView
->setColumnHidden( c
, !( m
& COLUMN_DEFAULT
) );
333 if( m
== COLUMN_TITLE
) treeView
->header()->resizeSection( c
, 200 );
334 else if( m
== COLUMN_DURATION
) treeView
->header()->resizeSection( c
, 80 );
338 getSettings()->endGroup();
340 /* Connections for the TreeView */
341 CONNECT( treeView
, activated( const QModelIndex
& ),
342 this, activate( const QModelIndex
& ) );
343 CONNECT( treeView
->header(), customContextMenuRequested( const QPoint
& ),
344 this, popupSelectColumn( QPoint
) );
345 CONNECT( treeView
, customContextMenuRequested( const QPoint
& ),
346 this, popupPlView( const QPoint
& ) );
347 treeView
->installEventFilter( this );
349 /* SignalMapper for columns */
350 selectColumnsSigMapper
= new QSignalMapper( this );
351 CONNECT( selectColumnsSigMapper
, mapped( int ),
352 this, toggleColumnShown( int ) );
354 viewStack
->addWidget( treeView
);
357 void StandardPLPanel::changeModel( bool b_ml
)
365 if( currentView
->model() != mod
)
366 currentView
->setModel( mod
);
370 void StandardPLPanel::showView( int i_view
)
377 if( treeView
== NULL
)
379 currentView
= treeView
;
384 if( iconView
== NULL
)
386 currentView
= iconView
;
391 if( listView
== NULL
)
393 currentView
= listView
;
396 case PICTUREFLOW_VIEW
:
398 if( picFlowView
== NULL
)
400 currentView
= picFlowView
;
406 changeModel( false );
408 viewStack
->setCurrentWidget( currentView
);
413 const int StandardPLPanel::currentViewIndex()
415 if( currentView
== treeView
)
417 else if( currentView
== iconView
)
419 else if( currentView
== listView
)
422 return PICTUREFLOW_VIEW
;
425 void StandardPLPanel::cycleViews()
427 if( currentView
== iconView
)
428 showView( TREE_VIEW
);
429 else if( currentView
== treeView
)
430 showView( LIST_VIEW
);
431 else if( currentView
== listView
)
432 showView( PICTUREFLOW_VIEW
);
433 else if( currentView
== picFlowView
)
434 showView( ICON_VIEW
);
439 void StandardPLPanel::activate( const QModelIndex
&index
)
441 if( currentView
->model() == model
)
443 /* If we are not a leaf node */
444 if( !index
.data( PLModel::IsLeafNodeRole
).toBool() )
446 if( currentView
!= treeView
)
451 playlist_Lock( THEPL
);
452 playlist_item_t
*p_item
= playlist_ItemGetById( THEPL
, model
->itemId( index
) );
453 p_item
->i_flags
|= PLAYLIST_SUBITEM_STOP_FLAG
;
454 lastActivatedId
= p_item
->p_input
->i_id
;
455 playlist_Unlock( THEPL
);
456 model
->activateItem( index
);
461 void StandardPLPanel::browseInto( input_item_t
*p_input
)
463 if( p_input
->i_id
!= lastActivatedId
) return;
465 playlist_Lock( THEPL
);
467 playlist_item_t
*p_item
= playlist_ItemGetByInput( THEPL
, p_input
);
470 playlist_Unlock( THEPL
);
474 QModelIndex index
= model
->index( p_item
->i_id
, 0 );
475 playlist_Unlock( THEPL
);
477 if( currentView
== treeView
)
478 treeView
->setExpanded( index
, true );
482 lastActivatedId
= -1;