]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/playlist/standardpanel.cpp
Qt: standardpanel: remove warnings
[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 "menus.hpp"                              /* Popup */
37 #include "input_manager.hpp"                      /* THEMIM */
38
39 #include "sorting.h"                              /* Columns order */
40
41 #include <vlc_services_discovery.h>               /* SD_CMD_SEARCH */
42
43 #include <QHeaderView>
44 #include <QModelIndexList>
45 #include <QMenu>
46 #include <QKeyEvent>
47 #include <QWheelEvent>
48 #include <QStackedLayout>
49 #include <QSignalMapper>
50 #include <QSettings>
51 #include <QScrollBar>
52
53 #include <assert.h>
54
55 StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
56                                   intf_thread_t *_p_intf,
57                                   playlist_item_t *p_root,
58                                   PLSelector *_p_selector,
59                                   PLModel *_p_model,
60                                   MLModel *_p_plmodel)
61                 : QWidget( _parent ),
62                   model( _p_model ),
63                   mlmodel( _p_plmodel),
64                   p_intf( _p_intf ),
65                   p_selector( _p_selector )
66 {
67     viewStack = new QStackedLayout( this );
68     viewStack->setSpacing( 0 ); viewStack->setMargin( 0 );
69     setMinimumWidth( 300 );
70
71     iconView    = NULL;
72     treeView    = NULL;
73     listView    = NULL;
74     picFlowView = NULL;
75
76     currentRootIndexId  = -1;
77     lastActivatedId     = -1;
78
79     /* Saved Settings */
80     int i_savedViewMode = getSettings()->value( "Playlist/view-mode", TREE_VIEW ).toInt();
81     showView( i_savedViewMode );
82
83     DCONNECT( THEMIM, leafBecameParent( int ),
84               this, browseInto( int ) );
85
86     CONNECT( model, currentChanged( const QModelIndex& ),
87              this, handleExpansion( const QModelIndex& ) );
88     CONNECT( model, rootChanged(), this, browseInto() );
89
90     setRoot( p_root, false );
91 }
92
93 StandardPLPanel::~StandardPLPanel()
94 {
95     getSettings()->beginGroup("Playlist");
96     if( treeView )
97         getSettings()->setValue( "headerStateV2", treeView->header()->saveState() );
98     getSettings()->setValue( "view-mode", currentViewIndex() );
99     getSettings()->endGroup();
100 }
101
102 /* Unused anymore, but might be useful, like in right-click menu */
103 void StandardPLPanel::gotoPlayingItem()
104 {
105     currentView->scrollTo( model->currentIndex() );
106 }
107
108 void StandardPLPanel::handleExpansion( const QModelIndex& index )
109 {
110     assert( currentView );
111     if( currentRootIndexId != -1 && currentRootIndexId != model->itemId( index.parent() ) )
112         browseInto( index.parent() );
113     currentView->scrollTo( index );
114 }
115
116 void StandardPLPanel::popupPlView( const QPoint &point )
117 {
118     QModelIndex index = currentView->indexAt( point );
119     QPoint globalPoint = currentView->viewport()->mapToGlobal( point );
120     QItemSelectionModel *selection = currentView->selectionModel();
121     QModelIndexList list = selection->selectedIndexes();
122
123     if( !model->popup( index, globalPoint, list ) )
124         QVLCMenu::PopupMenu( p_intf, true );
125 }
126
127 void StandardPLPanel::popupSelectColumn( QPoint )
128 {
129     QMenu menu;
130     assert( treeView );
131
132     /* We do not offer the option to hide index 0 column, or
133      * QTreeView will behave weird */
134     for( int i = 1 << 1, j = 1; i < COLUMN_END; i <<= 1, j++ )
135     {
136         QAction* option = menu.addAction( qfu( psz_column_title( i ) ) );
137         option->setCheckable( true );
138         option->setChecked( !treeView->isColumnHidden( j ) );
139         selectColumnsSigMapper->setMapping( option, j );
140         CONNECT( option, triggered(), selectColumnsSigMapper, map() );
141     }
142     menu.exec( QCursor::pos() );
143 }
144
145 void StandardPLPanel::toggleColumnShown( int i )
146 {
147     treeView->setColumnHidden( i, !treeView->isColumnHidden( i ) );
148 }
149
150 /* Search in the playlist */
151 void StandardPLPanel::search( const QString& searchText )
152 {
153     int type;
154     QString name;
155     p_selector->getCurrentSelectedItem( &type, &name );
156     if( type != SD_TYPE )
157     {
158         bool flat = ( currentView == iconView ||
159                       currentView == listView ||
160                       currentView == picFlowView );
161         model->search( searchText,
162                        flat ? currentView->rootIndex() : QModelIndex(),
163                        !flat );
164     }
165 }
166
167 void StandardPLPanel::searchDelayed( const QString& searchText )
168 {
169     int type;
170     QString name;
171     p_selector->getCurrentSelectedItem( &type, &name );
172
173     if( type == SD_TYPE )
174     {
175         if( !name.isEmpty() && !searchText.isEmpty() )
176             playlist_ServicesDiscoveryControl( THEPL, qtu( name ), SD_CMD_SEARCH,
177                                               qtu( searchText ) );
178     }
179 }
180
181 /* Set the root of the new Playlist */
182 /* This activated by the selector selection */
183 void StandardPLPanel::setRoot( playlist_item_t *p_item, bool b )
184 {
185 #ifdef MEDIA_LIBRARY
186     if( b )
187     {
188         msg_Dbg( p_intf, "Setting the SQL ML" );
189         currentView->setModel( mlmodel );
190     }
191     else
192 #else
193     Q_UNUSED( b );
194 #endif
195     {
196         msg_Dbg( p_intf, "Normal PL/ML or SD" );
197         if( currentView->model() != model )
198             currentView->setModel( model );
199         model->rebuild( p_item );
200     }
201 }
202
203 void StandardPLPanel::browseInto( const QModelIndex &index )
204 {
205     if( currentView == iconView || currentView == listView || currentView == picFlowView )
206     {
207         currentRootIndexId = model->itemId( index );
208         currentView->setRootIndex( index );
209     }
210
211     emit viewChanged( index );
212 }
213
214 void StandardPLPanel::browseInto()
215 {
216     browseInto( (currentRootIndexId != -1 && currentView != treeView) ?
217                  model->index( currentRootIndexId, 0 ) :
218                  QModelIndex() );
219 }
220
221 void StandardPLPanel::wheelEvent( QWheelEvent *e )
222 {
223     // Accept this event in order to prevent unwanted volume up/down changes
224     e->accept();
225 }
226
227 bool StandardPLPanel::eventFilter ( QObject *, QEvent * event )
228 {
229     if (event->type() == QEvent::KeyPress)
230     {
231         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
232         if( keyEvent->key() == Qt::Key_Delete ||
233             keyEvent->key() == Qt::Key_Backspace )
234         {
235             deleteSelection();
236             return true;
237         }
238     }
239     return false;
240 }
241
242 void StandardPLPanel::deleteSelection()
243 {
244     QItemSelectionModel *selection = currentView->selectionModel();
245     QModelIndexList list = selection->selectedIndexes();
246     model->doDelete( list );
247 }
248
249 void StandardPLPanel::createIconView()
250 {
251     iconView = new PlIconView( model, this );
252     iconView->setContextMenuPolicy( Qt::CustomContextMenu );
253     CONNECT( iconView, customContextMenuRequested( const QPoint & ),
254              this, popupPlView( const QPoint & ) );
255     CONNECT( iconView, activated( const QModelIndex & ),
256              this, activate( const QModelIndex & ) );
257     iconView->installEventFilter( this );
258     viewStack->addWidget( iconView );
259 }
260
261 void StandardPLPanel::createListView()
262 {
263     listView = new PlListView( model, this );
264     listView->setContextMenuPolicy( Qt::CustomContextMenu );
265     CONNECT( listView, customContextMenuRequested( const QPoint & ),
266              this, popupPlView( const QPoint & ) );
267     CONNECT( listView, activated( const QModelIndex & ),
268              this, activate( const QModelIndex & ) );
269     listView->installEventFilter( this );
270     viewStack->addWidget( listView );
271 }
272
273 void StandardPLPanel::createCoverView()
274 {
275     picFlowView = new PicFlowView( model, this );
276     picFlowView->setContextMenuPolicy( Qt::CustomContextMenu );
277     CONNECT( picFlowView, customContextMenuRequested( const QPoint & ),
278              this, popupPlView( const QPoint & ) );
279     CONNECT( picFlowView, activated( const QModelIndex & ),
280              this, activate( const QModelIndex & ) );
281     viewStack->addWidget( picFlowView );
282     picFlowView->installEventFilter( this );
283 }
284
285 void StandardPLPanel::createTreeView()
286 {
287     /* Create and configure the QTreeView */
288     treeView = new PlTreeView;
289
290     treeView->setIconSize( QSize( 20, 20 ) );
291     treeView->setAlternatingRowColors( true );
292     treeView->setAnimated( true );
293     treeView->setUniformRowHeights( true );
294     treeView->setSortingEnabled( true );
295     treeView->setAttribute( Qt::WA_MacShowFocusRect, false );
296     treeView->header()->setSortIndicator( -1 , Qt::AscendingOrder );
297     treeView->header()->setSortIndicatorShown( true );
298     treeView->header()->setClickable( true );
299     treeView->header()->setContextMenuPolicy( Qt::CustomContextMenu );
300
301     treeView->setSelectionBehavior( QAbstractItemView::SelectRows );
302     treeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
303     treeView->setDragEnabled( true );
304     treeView->setAcceptDrops( true );
305     treeView->setDropIndicatorShown( true );
306     treeView->setContextMenuPolicy( Qt::CustomContextMenu );
307
308     /* setModel after setSortingEnabled(true), or the model will sort immediately! */
309
310     getSettings()->beginGroup("Playlist");
311
312     if( getSettings()->contains( "headerStateV2" ) )
313     {
314         treeView->header()->restoreState(
315                 getSettings()->value( "headerStateV2" ).toByteArray() );
316     }
317     else
318     {
319         for( int m = 1, c = 0; m != COLUMN_END; m <<= 1, c++ )
320         {
321             treeView->setColumnHidden( c, !( m & COLUMN_DEFAULT ) );
322             if( m == COLUMN_TITLE ) treeView->header()->resizeSection( c, 200 );
323             else if( m == COLUMN_DURATION ) treeView->header()->resizeSection( c, 80 );
324         }
325     }
326
327     getSettings()->endGroup();
328
329     /* Connections for the TreeView */
330     CONNECT( treeView, activated( const QModelIndex& ),
331              this, activate( const QModelIndex& ) );
332     CONNECT( treeView->header(), customContextMenuRequested( const QPoint & ),
333              this, popupSelectColumn( QPoint ) );
334     CONNECT( treeView, customContextMenuRequested( const QPoint & ),
335              this, popupPlView( const QPoint & ) );
336     treeView->installEventFilter( this );
337
338     /* SignalMapper for columns */
339     selectColumnsSigMapper = new QSignalMapper( this );
340     CONNECT( selectColumnsSigMapper, mapped( int ),
341              this, toggleColumnShown( int ) );
342
343     viewStack->addWidget( treeView );
344 }
345
346 void StandardPLPanel::changeModel( bool b_ml )
347 {
348 #ifdef MEDIA_LIBRARY
349     VLCModel *mod;
350     if( b_ml )
351         mod = mlmodel;
352     else
353         mod = model;
354     if( currentView->model() != mod )
355         currentView->setModel( mod );
356 #else
357     Q_UNUSED( b_ml );
358     if( currentView->model() != model )
359         currentView->setModel( model );
360 #endif
361 }
362
363 void StandardPLPanel::showView( int i_view )
364 {
365
366     switch( i_view )
367     {
368     case ICON_VIEW:
369     {
370         if( iconView == NULL )
371             createIconView();
372         currentView = iconView;
373         break;
374     }
375     case LIST_VIEW:
376     {
377         if( listView == NULL )
378             createListView();
379         currentView = listView;
380         break;
381     }
382     case PICTUREFLOW_VIEW:
383     {
384         if( picFlowView == NULL )
385             createCoverView();
386         currentView = picFlowView;
387         break;
388     }
389     default:
390     case TREE_VIEW:
391     {
392         if( treeView == NULL )
393             createTreeView();
394         currentView = treeView;
395         break;
396     }
397     }
398
399     changeModel( false );
400
401     viewStack->setCurrentWidget( currentView );
402     browseInto();
403     gotoPlayingItem();
404 }
405
406 int StandardPLPanel::currentViewIndex() const
407 {
408     if( currentView == treeView )
409         return TREE_VIEW;
410     else if( currentView == iconView )
411         return ICON_VIEW;
412     else if( currentView == listView )
413         return LIST_VIEW;
414     else
415         return PICTUREFLOW_VIEW;
416 }
417
418 int StandardPLPanel::getScrollBarsSize() const
419 {
420     /* FIXME: should return a set in case of different widths */
421     return currentView->verticalScrollBar()->sizeHint().width();
422 }
423
424 void StandardPLPanel::cycleViews()
425 {
426     if( currentView == iconView )
427         showView( TREE_VIEW );
428     else if( currentView == treeView )
429         showView( LIST_VIEW );
430     else if( currentView == listView )
431         showView( PICTUREFLOW_VIEW  );
432     else if( currentView == picFlowView )
433         showView( ICON_VIEW );
434     else
435         assert( 0 );
436 }
437
438 void StandardPLPanel::activate( const QModelIndex &index )
439 {
440     if( currentView->model() == model )
441     {
442         /* If we are not a leaf node */
443         if( !index.data( PLModel::IsLeafNodeRole ).toBool() )
444         {
445             if( currentView != treeView )
446                 browseInto( index );
447         }
448         else
449         {
450             playlist_Lock( THEPL );
451             playlist_item_t *p_item = playlist_ItemGetById( THEPL, model->itemId( index ) );
452             p_item->i_flags |= PLAYLIST_SUBITEM_STOP_FLAG;
453             lastActivatedId = p_item->p_input->i_id;
454             playlist_Unlock( THEPL );
455             model->activateItem( index );
456         }
457     }
458 }
459
460 void StandardPLPanel::browseInto( int i_id )
461 {
462     if( i_id != lastActivatedId ) return;
463
464     QModelIndex index = model->index( i_id, 0 );
465     playlist_Unlock( THEPL );
466
467     if( currentView == treeView )
468         treeView->setExpanded( index, true );
469     else
470         browseInto( index );
471
472     lastActivatedId = -1;
473 }