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
,
64 p_selector( _p_selector
)
66 viewStack
= new QStackedLayout( this );
67 viewStack
->setSpacing( 0 ); viewStack
->setMargin( 0 );
68 setMinimumWidth( 300 );
75 currentRootIndexId
= -1;
79 int i_savedViewMode
= getSettings()->value( "Playlist/view-mode", TREE_VIEW
).toInt();
80 showView( i_savedViewMode
);
82 DCONNECT( THEMIM
, leafBecameParent( input_item_t
*),
83 this, browseInto( input_item_t
* ) );
85 CONNECT( model
, currentChanged( const QModelIndex
& ),
86 this, handleExpansion( const QModelIndex
& ) );
87 CONNECT( model
, rootChanged(), this, browseInto() );
89 setRoot( p_root
, false );
92 StandardPLPanel::~StandardPLPanel()
94 getSettings()->beginGroup("Playlist");
96 getSettings()->setValue( "headerStateV2", treeView
->header()->saveState() );
97 if( currentView
== treeView
)
98 getSettings()->setValue( "view-mode", TREE_VIEW
);
99 else if( currentView
== listView
)
100 getSettings()->setValue( "view-mode", LIST_VIEW
);
101 else if( currentView
== iconView
)
102 getSettings()->setValue( "view-mode", ICON_VIEW
);
103 else if( currentView
== picFlowView
)
104 getSettings()->setValue( "view-mode", PICTUREFLOW_VIEW
);
105 getSettings()->endGroup();
108 /* Unused anymore, but might be useful, like in right-click menu */
109 void StandardPLPanel::gotoPlayingItem()
111 currentView
->scrollTo( model
->currentIndex() );
114 void StandardPLPanel::handleExpansion( const QModelIndex
& index
)
116 assert( currentView
);
117 if( currentRootIndexId
!= -1 && currentRootIndexId
!= model
->itemId( index
.parent() ) )
118 browseInto( index
.parent() );
119 currentView
->scrollTo( index
);
122 void StandardPLPanel::popupPlView( const QPoint
&point
)
124 QModelIndex index
= currentView
->indexAt( point
);
125 QPoint globalPoint
= currentView
->viewport()->mapToGlobal( point
);
126 QItemSelectionModel
*selection
= currentView
->selectionModel();
127 QModelIndexList list
= selection
->selectedIndexes();
129 if( !model
->popup( index
, globalPoint
, list
) )
130 QVLCMenu::PopupMenu( p_intf
, true );
133 void StandardPLPanel::popupSelectColumn( QPoint pos
)
138 /* We do not offer the option to hide index 0 column, or
139 * QTreeView will behave weird */
140 for( int i
= 1 << 1, j
= 1; i
< COLUMN_END
; i
<<= 1, j
++ )
142 QAction
* option
= menu
.addAction( qfu( psz_column_title( i
) ) );
143 option
->setCheckable( true );
144 option
->setChecked( !treeView
->isColumnHidden( j
) );
145 selectColumnsSigMapper
->setMapping( option
, j
);
146 CONNECT( option
, triggered(), selectColumnsSigMapper
, map() );
148 menu
.exec( QCursor::pos() );
151 void StandardPLPanel::toggleColumnShown( int i
)
153 treeView
->setColumnHidden( i
, !treeView
->isColumnHidden( i
) );
156 /* Search in the playlist */
157 void StandardPLPanel::search( const QString
& searchText
)
161 p_selector
->getCurrentSelectedItem( &type
, &name
);
162 if( type
!= SD_TYPE
)
164 bool flat
= ( currentView
== iconView
||
165 currentView
== listView
||
166 currentView
== picFlowView
);
167 model
->search( searchText
,
168 flat
? currentView
->rootIndex() : QModelIndex(),
173 void StandardPLPanel::searchDelayed( const QString
& searchText
)
177 p_selector
->getCurrentSelectedItem( &type
, &name
);
179 if( type
== SD_TYPE
)
181 if( !name
.isEmpty() && !searchText
.isEmpty() )
182 playlist_ServicesDiscoveryControl( THEPL
, qtu( name
), SD_CMD_SEARCH
,
187 /* Set the root of the new Playlist */
188 /* This activated by the selector selection */
189 void StandardPLPanel::setRoot( playlist_item_t
*p_item
, bool b
)
194 msg_Dbg( p_intf
, "Setting the SQL ML" );
195 currentView
->setModel( mlmodel
);
200 msg_Dbg( p_intf
, "Normal PL/ML or SD" );
201 if( currentView
->model() != model
)
202 currentView
->setModel( model
);
203 model
->rebuild( p_item
);
207 void StandardPLPanel::browseInto( const QModelIndex
&index
)
209 if( currentView
== iconView
|| currentView
== listView
|| currentView
== picFlowView
)
211 currentRootIndexId
= model
->itemId( index
);
212 currentView
->setRootIndex( index
);
215 emit
viewChanged( index
);
218 void StandardPLPanel::browseInto()
220 browseInto( (currentRootIndexId
!= -1 && currentView
!= treeView
) ?
221 model
->index( currentRootIndexId
, 0 ) :
225 void StandardPLPanel::wheelEvent( QWheelEvent
*e
)
227 // Accept this event in order to prevent unwanted volume up/down changes
231 bool StandardPLPanel::eventFilter ( QObject
* watched
, QEvent
* event
)
233 if (event
->type() == QEvent::KeyPress
)
235 QKeyEvent
*keyEvent
= static_cast<QKeyEvent
*>(event
);
236 if( keyEvent
->key() == Qt::Key_Delete
||
237 keyEvent
->key() == Qt::Key_Backspace
)
246 void StandardPLPanel::deleteSelection()
248 QItemSelectionModel
*selection
= currentView
->selectionModel();
249 QModelIndexList list
= selection
->selectedIndexes();
250 model
->doDelete( list
);
253 void StandardPLPanel::createIconView()
255 iconView
= new PlIconView( model
, this );
256 iconView
->setContextMenuPolicy( Qt::CustomContextMenu
);
257 CONNECT( iconView
, customContextMenuRequested( const QPoint
& ),
258 this, popupPlView( const QPoint
& ) );
259 CONNECT( iconView
, activated( const QModelIndex
& ),
260 this, activate( const QModelIndex
& ) );
261 iconView
->installEventFilter( this );
262 viewStack
->addWidget( iconView
);
265 void StandardPLPanel::createListView()
267 listView
= new PlListView( model
, this );
268 listView
->setContextMenuPolicy( Qt::CustomContextMenu
);
269 CONNECT( listView
, customContextMenuRequested( const QPoint
& ),
270 this, popupPlView( const QPoint
& ) );
271 CONNECT( listView
, activated( const QModelIndex
& ),
272 this, activate( const QModelIndex
& ) );
273 listView
->installEventFilter( this );
274 viewStack
->addWidget( listView
);
277 void StandardPLPanel::createCoverView()
279 picFlowView
= new PicFlowView( model
, this );
280 picFlowView
->setContextMenuPolicy( Qt::CustomContextMenu
);
281 CONNECT( picFlowView
, customContextMenuRequested( const QPoint
& ),
282 this, popupPlView( const QPoint
& ) );
283 CONNECT( picFlowView
, activated( const QModelIndex
& ),
284 this, activate( const QModelIndex
& ) );
285 viewStack
->addWidget( picFlowView
);
286 picFlowView
->installEventFilter( this );
289 void StandardPLPanel::createTreeView()
291 /* Create and configure the QTreeView */
292 treeView
= new PlTreeView
;
294 treeView
->setIconSize( QSize( 20, 20 ) );
295 treeView
->setAlternatingRowColors( true );
296 treeView
->setAnimated( true );
297 treeView
->setUniformRowHeights( true );
298 treeView
->setSortingEnabled( true );
299 treeView
->setAttribute( Qt::WA_MacShowFocusRect
, false );
300 treeView
->header()->setSortIndicator( -1 , Qt::AscendingOrder
);
301 treeView
->header()->setSortIndicatorShown( true );
302 treeView
->header()->setClickable( true );
303 treeView
->header()->setContextMenuPolicy( Qt::CustomContextMenu
);
305 treeView
->setSelectionBehavior( QAbstractItemView::SelectRows
);
306 treeView
->setSelectionMode( QAbstractItemView::ExtendedSelection
);
307 treeView
->setDragEnabled( true );
308 treeView
->setAcceptDrops( true );
309 treeView
->setDropIndicatorShown( true );
310 treeView
->setContextMenuPolicy( Qt::CustomContextMenu
);
312 /* setModel after setSortingEnabled(true), or the model will sort immediately! */
314 getSettings()->beginGroup("Playlist");
316 if( getSettings()->contains( "headerStateV2" ) )
318 treeView
->header()->restoreState(
319 getSettings()->value( "headerStateV2" ).toByteArray() );
323 for( int m
= 1, c
= 0; m
!= COLUMN_END
; m
<<= 1, c
++ )
325 treeView
->setColumnHidden( c
, !( m
& COLUMN_DEFAULT
) );
326 if( m
== COLUMN_TITLE
) treeView
->header()->resizeSection( c
, 200 );
327 else if( m
== COLUMN_DURATION
) treeView
->header()->resizeSection( c
, 80 );
331 getSettings()->endGroup();
333 /* Connections for the TreeView */
334 CONNECT( treeView
, activated( const QModelIndex
& ),
335 this, activate( const QModelIndex
& ) );
336 CONNECT( treeView
->header(), customContextMenuRequested( const QPoint
& ),
337 this, popupSelectColumn( QPoint
) );
338 CONNECT( treeView
, customContextMenuRequested( const QPoint
& ),
339 this, popupPlView( const QPoint
& ) );
340 treeView
->installEventFilter( this );
342 /* SignalMapper for columns */
343 selectColumnsSigMapper
= new QSignalMapper( this );
344 CONNECT( selectColumnsSigMapper
, mapped( int ),
345 this, toggleColumnShown( int ) );
347 viewStack
->addWidget( treeView
);
350 void StandardPLPanel::changeModel( bool b_ml
)
358 if( currentView
->model() != mod
)
359 currentView
->setModel( mod
);
363 void StandardPLPanel::showView( int i_view
)
370 if( iconView
== NULL
)
372 currentView
= iconView
;
377 if( listView
== NULL
)
379 currentView
= listView
;
382 case PICTUREFLOW_VIEW
:
384 if( picFlowView
== NULL
)
386 currentView
= picFlowView
;
392 if( treeView
== NULL
)
394 currentView
= treeView
;
399 changeModel( false );
401 viewStack
->setCurrentWidget( currentView
);
406 int StandardPLPanel::currentViewIndex() const
408 if( currentView
== treeView
)
410 else if( currentView
== iconView
)
412 else if( currentView
== listView
)
415 return PICTUREFLOW_VIEW
;
418 void StandardPLPanel::cycleViews()
420 if( currentView
== iconView
)
421 showView( TREE_VIEW
);
422 else if( currentView
== treeView
)
423 showView( LIST_VIEW
);
424 else if( currentView
== listView
)
425 showView( PICTUREFLOW_VIEW
);
426 else if( currentView
== picFlowView
)
427 showView( ICON_VIEW
);
432 void StandardPLPanel::activate( const QModelIndex
&index
)
434 if( currentView
->model() == model
)
436 /* If we are not a leaf node */
437 if( !index
.data( PLModel::IsLeafNodeRole
).toBool() )
439 if( currentView
!= treeView
)
444 playlist_Lock( THEPL
);
445 playlist_item_t
*p_item
= playlist_ItemGetById( THEPL
, model
->itemId( index
) );
446 p_item
->i_flags
|= PLAYLIST_SUBITEM_STOP_FLAG
;
447 lastActivatedId
= p_item
->p_input
->i_id
;
448 playlist_Unlock( THEPL
);
449 model
->activateItem( index
);
454 void StandardPLPanel::browseInto( input_item_t
*p_input
)
456 if( p_input
->i_id
!= lastActivatedId
) return;
458 playlist_Lock( THEPL
);
460 playlist_item_t
*p_item
= playlist_ItemGetByInput( THEPL
, p_input
);
463 playlist_Unlock( THEPL
);
467 QModelIndex index
= model
->index( p_item
->i_id
, 0 );
468 playlist_Unlock( THEPL
);
470 if( currentView
== treeView
)
471 treeView
->setExpanded( index
, true );
475 lastActivatedId
= -1;