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