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 <vlc_services_discovery.h> /* SD_CMD_SEARCH */
41 #include <QHeaderView>
42 #include <QModelIndexList>
45 #include <QWheelEvent>
46 #include <QStackedLayout>
47 #include <QSignalMapper>
49 #include <QStylePainter>
53 StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
54 intf_thread_t *_p_intf,
55 playlist_item_t *p_root,
56 PLSelector *_p_selector,
63 p_selector( _p_selector )
65 viewStack = new QStackedLayout( this );
66 viewStack->setSpacing( 0 ); viewStack->setMargin( 0 );
67 setMinimumWidth( 300 );
74 currentRootIndexId = -1;
78 int i_savedViewMode = getSettings()->value( "Playlist/view-mode", TREE_VIEW ).toInt();
79 showView( i_savedViewMode );
81 DCONNECT( THEMIM, leafBecameParent( int ),
82 this, browseInto( int ) );
84 CONNECT( model, currentIndexChanged( const QModelIndex& ),
85 this, handleExpansion( const QModelIndex& ) );
86 CONNECT( model, rootIndexChanged(), this, browseInto() );
88 setRootItem( p_root, false );
91 StandardPLPanel::~StandardPLPanel()
93 getSettings()->beginGroup("Playlist");
95 getSettings()->setValue( "headerStateV2", treeView->header()->saveState() );
96 getSettings()->setValue( "view-mode", currentViewIndex() );
97 getSettings()->endGroup();
100 /* Unused anymore, but might be useful, like in right-click menu */
101 void StandardPLPanel::gotoPlayingItem()
103 currentView->scrollTo( model->currentIndex() );
106 void StandardPLPanel::handleExpansion( const QModelIndex& index )
108 assert( currentView );
109 if( currentRootIndexId != -1 && currentRootIndexId != model->itemId( index.parent() ) )
110 browseInto( index.parent() );
111 currentView->scrollTo( index );
114 void StandardPLPanel::popupPlView( const QPoint &point )
116 QModelIndex index = currentView->indexAt( point );
117 QPoint globalPoint = currentView->viewport()->mapToGlobal( point );
118 QItemSelectionModel *selection = currentView->selectionModel();
119 QModelIndexList list = selection->selectedIndexes();
121 if( !model->popup( index, globalPoint, list ) )
122 VLCMenuBar::PopupMenu( p_intf, true );
125 void StandardPLPanel::popupSelectColumn( QPoint )
130 /* We do not offer the option to hide index 0 column, or
131 * QTreeView will behave weird */
132 for( int i = 1 << 1, j = 1; i < COLUMN_END; i <<= 1, j++ )
134 QAction* option = menu.addAction( qfu( psz_column_title( i ) ) );
135 option->setCheckable( true );
136 option->setChecked( !treeView->isColumnHidden( j ) );
137 selectColumnsSigMapper->setMapping( option, j );
138 CONNECT( option, triggered(), selectColumnsSigMapper, map() );
140 menu.exec( QCursor::pos() );
143 void StandardPLPanel::toggleColumnShown( int i )
145 treeView->setColumnHidden( i, !treeView->isColumnHidden( i ) );
148 /* Search in the playlist */
149 void StandardPLPanel::search( const QString& searchText )
154 p_selector->getCurrentItemInfos( &type, &can_search, &name );
156 if( type != SD_TYPE || !can_search )
158 bool flat = ( currentView == iconView ||
159 currentView == listView ||
160 currentView == picFlowView );
161 model->search( searchText,
162 flat ? currentView->rootIndex() : QModelIndex(),
167 void StandardPLPanel::searchDelayed( const QString& searchText )
172 p_selector->getCurrentItemInfos( &type, &can_search, &name );
174 if( type == SD_TYPE && can_search )
176 if( !name.isEmpty() && !searchText.isEmpty() )
177 playlist_ServicesDiscoveryControl( THEPL, qtu( name ), SD_CMD_SEARCH,
182 /* Set the root of the new Playlist */
183 /* This activated by the selector selection */
184 void StandardPLPanel::setRootItem( playlist_item_t *p_item, bool b )
189 msg_Dbg( p_intf, "Setting the SQL ML" );
190 currentView->setModel( mlmodel );
197 if( currentView->model() != model )
198 currentView->setModel( model );
199 model->rebuild( p_item );
203 void StandardPLPanel::browseInto( const QModelIndex &index )
205 if( currentView == iconView || currentView == listView || currentView == picFlowView )
208 currentView->setRootIndex( index );
210 /* When going toward root in LocationBar, scroll to the item
211 that was previously as root */
212 QModelIndex newIndex = model->index(currentRootIndexId,0);
213 while( newIndex.isValid() && (newIndex.parent() != index) )
214 newIndex = newIndex.parent();
215 if( newIndex.isValid() )
216 currentView->scrollTo( newIndex );
218 /* Store new rootindexid*/
219 currentRootIndexId = model->itemId( index );
220 model->ensureArtRequested( index );
223 emit viewChanged( index );
226 void StandardPLPanel::browseInto()
228 browseInto( (currentRootIndexId != -1 && currentView != treeView) ?
229 model->index( currentRootIndexId, 0 ) :
233 void StandardPLPanel::wheelEvent( QWheelEvent *e )
235 // Accept this event in order to prevent unwanted volume up/down changes
239 bool StandardPLPanel::eventFilter ( QObject *obj, QEvent * event )
241 if (event->type() == QEvent::KeyPress)
243 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
244 if( keyEvent->key() == Qt::Key_Delete ||
245 keyEvent->key() == Qt::Key_Backspace )
251 else if ( event->type() == QEvent::Paint )
252 {/* Warn! Don't filter events from anything else than views ! */
253 if ( model->rowCount() == 0 && p_selector->getCurrentItemCategory() == PL_ITEM_TYPE )
255 QWidget *viewport = qobject_cast<QWidget *>( obj );
256 QStylePainter painter( viewport );
257 QPixmap dropzone(":/dropzone");
258 QRect rect = viewport->geometry();
259 QSize size = rect.size() / 2 - dropzone.size() / 2;
260 rect.adjust( 0, size.height(), 0 , 0 );
261 painter.drawItemPixmap( rect, Qt::AlignHCenter, dropzone );
262 /* now select the zone just below the drop zone and let Qt center
263 the text by itself */
264 rect.adjust( 0, dropzone.size().height() + 10, 0, 0 );
265 rect.setRight( viewport->geometry().width() );
267 painter.drawItemText( rect,
271 qtr("Playlist is currently Empty\n"
272 "Drop a file here or select a "
273 "media source from the left"),
280 void StandardPLPanel::deleteSelection()
282 QItemSelectionModel *selection = currentView->selectionModel();
283 QModelIndexList list = selection->selectedIndexes();
284 model->doDelete( list );
287 void StandardPLPanel::createIconView()
289 iconView = new PlIconView( model, this );
290 iconView->setContextMenuPolicy( Qt::CustomContextMenu );
291 CONNECT( iconView, customContextMenuRequested( const QPoint & ),
292 this, popupPlView( const QPoint & ) );
293 CONNECT( iconView, activated( const QModelIndex & ),
294 this, activate( const QModelIndex & ) );
295 iconView->installEventFilter( this );
296 iconView->viewport()->installEventFilter( this );
297 viewStack->addWidget( iconView );
300 void StandardPLPanel::createListView()
302 listView = new PlListView( model, this );
303 listView->setContextMenuPolicy( Qt::CustomContextMenu );
304 CONNECT( listView, customContextMenuRequested( const QPoint & ),
305 this, popupPlView( const QPoint & ) );
306 CONNECT( listView, activated( const QModelIndex & ),
307 this, activate( const QModelIndex & ) );
308 listView->installEventFilter( this );
309 listView->viewport()->installEventFilter( this );
310 viewStack->addWidget( listView );
313 void StandardPLPanel::createCoverView()
315 picFlowView = new PicFlowView( model, this );
316 picFlowView->setContextMenuPolicy( Qt::CustomContextMenu );
317 CONNECT( picFlowView, customContextMenuRequested( const QPoint & ),
318 this, popupPlView( const QPoint & ) );
319 CONNECT( picFlowView, activated( const QModelIndex & ),
320 this, activate( const QModelIndex & ) );
321 viewStack->addWidget( picFlowView );
322 picFlowView->installEventFilter( this );
325 void StandardPLPanel::createTreeView()
327 /* Create and configure the QTreeView */
328 treeView = new PlTreeView;
330 treeView->setIconSize( QSize( 20, 20 ) );
331 treeView->setAlternatingRowColors( true );
332 treeView->setAnimated( true );
333 treeView->setUniformRowHeights( true );
334 treeView->setSortingEnabled( true );
335 treeView->setAttribute( Qt::WA_MacShowFocusRect, false );
336 treeView->header()->setSortIndicator( -1 , Qt::AscendingOrder );
337 treeView->header()->setSortIndicatorShown( true );
338 treeView->header()->setClickable( true );
339 treeView->header()->setContextMenuPolicy( Qt::CustomContextMenu );
341 treeView->setSelectionBehavior( QAbstractItemView::SelectRows );
342 treeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
343 treeView->setDragEnabled( true );
344 treeView->setAcceptDrops( true );
345 treeView->setDropIndicatorShown( true );
346 treeView->setContextMenuPolicy( Qt::CustomContextMenu );
348 /* setModel after setSortingEnabled(true), or the model will sort immediately! */
350 /* Connections for the TreeView */
351 CONNECT( treeView, activated( const QModelIndex& ),
352 this, activate( const QModelIndex& ) );
353 CONNECT( treeView->header(), customContextMenuRequested( const QPoint & ),
354 this, popupSelectColumn( QPoint ) );
355 CONNECT( treeView, customContextMenuRequested( const QPoint & ),
356 this, popupPlView( const QPoint & ) );
357 treeView->installEventFilter( this );
358 treeView->viewport()->installEventFilter( this );
360 /* SignalMapper for columns */
361 selectColumnsSigMapper = new QSignalMapper( this );
362 CONNECT( selectColumnsSigMapper, mapped( int ),
363 this, toggleColumnShown( int ) );
365 viewStack->addWidget( treeView );
368 void StandardPLPanel::changeModel( bool b_ml )
376 if( currentView->model() != mod )
377 currentView->setModel( mod );
380 if( currentView->model() != model )
381 currentView->setModel( model );
385 void StandardPLPanel::showView( int i_view )
387 bool b_treeViewCreated = false;
393 if( iconView == NULL )
395 currentView = iconView;
400 if( listView == NULL )
402 currentView = listView;
405 case PICTUREFLOW_VIEW:
407 if( picFlowView == NULL )
409 currentView = picFlowView;
415 if( treeView == NULL )
418 b_treeViewCreated = true;
420 currentView = treeView;
425 changeModel( false );
427 /* Restoring the header Columns must come after changeModel */
428 if( b_treeViewCreated )
431 if( getSettings()->contains( "Playlist/headerStateV2" ) )
433 treeView->header()->restoreState(getSettings()
434 ->value( "Playlist/headerStateV2" ).toByteArray() );
435 /* if there is allready stuff in playlist, we don't sort it and we reset
437 if( model->rowCount() )
439 treeView->header()->setSortIndicator( -1 , Qt::AscendingOrder );
444 for( int m = 1, c = 0; m != COLUMN_END; m <<= 1, c++ )
446 treeView->setColumnHidden( c, !( m & COLUMN_DEFAULT ) );
447 if( m == COLUMN_TITLE ) treeView->header()->resizeSection( c, 200 );
448 else if( m == COLUMN_DURATION ) treeView->header()->resizeSection( c, 80 );
453 viewStack->setCurrentWidget( currentView );
458 int StandardPLPanel::currentViewIndex() const
460 if( currentView == treeView )
462 else if( currentView == iconView )
464 else if( currentView == listView )
467 return PICTUREFLOW_VIEW;
470 void StandardPLPanel::cycleViews()
472 if( currentView == iconView )
473 showView( TREE_VIEW );
474 else if( currentView == treeView )
475 showView( LIST_VIEW );
476 else if( currentView == listView )
478 showView( PICTUREFLOW_VIEW );
479 else if( currentView == picFlowView )
481 showView( ICON_VIEW );
486 void StandardPLPanel::activate( const QModelIndex &index )
488 if( currentView->model() == model )
490 /* If we are not a leaf node */
491 if( !index.data( PLModel::IsLeafNodeRole ).toBool() )
493 if( currentView != treeView )
498 playlist_Lock( THEPL );
499 playlist_item_t *p_item = playlist_ItemGetById( THEPL, model->itemId( index ) );
500 p_item->i_flags |= PLAYLIST_SUBITEM_STOP_FLAG;
501 lastActivatedId = p_item->p_input->i_id;
502 playlist_Unlock( THEPL );
503 model->activateItem( index );
508 void StandardPLPanel::browseInto( int i_id )
510 if( i_id != lastActivatedId ) return;
512 QModelIndex index = model->index( i_id, 0 );
513 playlist_Unlock( THEPL );
515 if( currentView == treeView )
516 treeView->setExpanded( index, true );
520 lastActivatedId = -1;