1 /*****************************************************************************
2 * standardpanel.cpp : The "standard" playlist panel : just a treeview
3 ****************************************************************************
4 * Copyright (C) 2000-2009 VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * JB 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 "dialogs_provider.hpp"
31 #include "components/playlist/playlist_model.hpp"
32 #include "components/playlist/standardpanel.hpp"
33 #include "components/playlist/icon_view.hpp"
34 #include "util/customwidgets.hpp"
36 #include <vlc_intf_strings.h>
38 #include <QPushButton>
39 #include <QHeaderView>
41 #include <QModelIndexList>
44 #include <QSignalMapper>
45 #include <QWheelEvent>
46 #include <QToolButton>
47 #include <QFontMetrics>
53 StandardPLPanel::StandardPLPanel( PlaylistWidget
*_parent
,
54 intf_thread_t
*_p_intf
,
55 playlist_t
*p_playlist
,
56 playlist_item_t
*p_root
):
57 QWidget( _parent
), p_intf( _p_intf
)
59 layout
= new QGridLayout( this );
60 layout
->setSpacing( 0 ); layout
->setMargin( 0 );
61 setMinimumWidth( 300 );
63 model
= new PLModel( p_playlist
, p_intf
, p_root
, this );
64 CONNECT( model
, currentChanged( const QModelIndex
& ),
65 this, handleExpansion( const QModelIndex
& ) );
75 titleFont.setPointSize( titleFont.pointSize() + 6 );
76 titleFont.setFamily( "Verdana" );
77 title->setFont( titleFont );
78 layout->addWidget( title, 0, 0 );*/
80 locationBar
= new LocationBar( model
);
81 layout
->addWidget( locationBar
, 0, 0 );
83 /* A Spacer and the search possibilities */
84 layout
->setColumnStretch( 1, 10 );
86 SearchLineEdit
*search
= new SearchLineEdit( this );
87 search
->setMaximumWidth( 300 );
88 layout
->addWidget( search
, 0, 4 );
89 CONNECT( search
, textChanged( const QString
& ),
90 this, search( const QString
& ) );
91 layout
->setColumnStretch( 4, 2 );
93 /* Add item to the playlist button */
94 addButton
= new QPushButton
;
95 addButton
->setIcon( QIcon( ":/buttons/playlist/playlist_add" ) );
96 addButton
->setMaximumWidth( 30 );
97 BUTTONACT( addButton
, popupAdd() );
98 layout
->addWidget( addButton
, 0, 3 );
100 /* Button to switch views */
101 QPushButton
*viewButton
= new QPushButton( this );
102 viewButton
->setIcon( style()->standardIcon( QStyle::SP_FileDialogContentsView
) );
103 layout
->addWidget( viewButton
, 0, 2 );
105 /* View selection menu */
106 viewSelectionMapper
= new QSignalMapper
;
107 CONNECT( viewSelectionMapper
, mapped( int ), this, showView( int ) );
109 QActionGroup
*actionGroup
= new QActionGroup( this );
111 QAction
*action
= actionGroup
->addAction( "Detailed view" );
112 action
->setCheckable( true );
113 viewSelectionMapper
->setMapping( action
, TREE_VIEW
);
114 CONNECT( action
, triggered(), viewSelectionMapper
, map() );
116 action
= actionGroup
->addAction( "Icon view" );
117 action
->setCheckable( true );
118 viewSelectionMapper
->setMapping( action
, ICON_VIEW
);
119 CONNECT( action
, triggered(), viewSelectionMapper
, map() );
121 QMenu
*viewMenu
= new QMenu( this );
122 viewMenu
->addActions( actionGroup
->actions() );
124 viewButton
->setMenu( viewMenu
);
127 getSettings()->beginGroup("Playlist");
129 int i_viewMode
= getSettings()->value( "view-mode", TREE_VIEW
).toInt();
130 showView( i_viewMode
);
132 getSettings()->endGroup();
134 last_activated_id
= -1;
135 CONNECT( THEMIM
, inputChanged( input_thread_t
* ),
136 this, handleInputChange( input_thread_t
* ) );
139 StandardPLPanel::~StandardPLPanel()
141 getSettings()->beginGroup("Playlist");
143 getSettings()->setValue( "headerStateV2", treeView
->header()->saveState() );
144 getSettings()->setValue( "view-mode", ( currentView
== iconView
) ? ICON_VIEW
: TREE_VIEW
);
145 getSettings()->endGroup();
148 /* Unused anymore, but might be useful, like in right-click menu */
149 void StandardPLPanel::gotoPlayingItem()
151 currentView
->scrollTo( model
->currentIndex() );
154 void StandardPLPanel::handleExpansion( const QModelIndex
& index
)
156 assert( currentView
);
157 currentView
->scrollTo( index
);
160 /* PopupAdd Menu for the Add Menu */
161 void StandardPLPanel::popupAdd()
164 if( currentRootId
== THEPL
->p_local_category
->i_id
||
165 currentRootId
== THEPL
->p_local_onelevel
->i_id
)
167 popup
.addAction( qtr(I_PL_ADDF
), THEDP
, SLOT( simplePLAppendDialog()) );
168 popup
.addAction( qtr(I_PL_ADDDIR
), THEDP
, SLOT( PLAppendDir()) );
169 popup
.addAction( qtr(I_OP_ADVOP
), THEDP
, SLOT( PLAppendDialog()) );
171 else if( ( THEPL
->p_ml_category
&&
172 currentRootId
== THEPL
->p_ml_category
->i_id
) ||
173 ( THEPL
->p_ml_onelevel
&&
174 currentRootId
== THEPL
->p_ml_onelevel
->i_id
) )
176 popup
.addAction( qtr(I_PL_ADDF
), THEDP
, SLOT( simpleMLAppendDialog()) );
177 popup
.addAction( qtr(I_PL_ADDDIR
), THEDP
, SLOT( MLAppendDir() ) );
178 popup
.addAction( qtr(I_OP_ADVOP
), THEDP
, SLOT( MLAppendDialog() ) );
181 popup
.exec( QCursor::pos() - addButton
->mapFromGlobal( QCursor::pos() )
182 + QPoint( 0, addButton
->height() ) );
185 void StandardPLPanel::popupPlView( const QPoint
&point
)
187 QModelIndex index
= currentView
->indexAt( point
);
188 QPoint globalPoint
= currentView
->viewport()->mapToGlobal( point
);
189 QItemSelectionModel
*selection
= currentView
->selectionModel();
190 QModelIndexList list
= selection
->selectedIndexes();
191 model
->popup( index
, globalPoint
, list
);
194 void StandardPLPanel::popupSelectColumn( QPoint pos
)
199 /* We do not offer the option to hide index 0 column, or
200 * QTreeView will behave weird */
202 for( i
= 1 << 1, j
= 1; i
< COLUMN_END
; i
<<= 1, j
++ )
204 QAction
* option
= menu
.addAction(
205 qfu( psz_column_title( i
) ) );
206 option
->setCheckable( true );
207 option
->setChecked( !treeView
->isColumnHidden( j
) );
208 selectColumnsSigMapper
->setMapping( option
, j
);
209 CONNECT( option
, triggered(), selectColumnsSigMapper
, map() );
211 menu
.exec( QCursor::pos() );
214 void StandardPLPanel::toggleColumnShown( int i
)
216 treeView
->setColumnHidden( i
, !treeView
->isColumnHidden( i
) );
219 /* Search in the playlist */
220 void StandardPLPanel::search( const QString
& searchText
)
222 model
->search( searchText
);
225 /* Set the root of the new Playlist */
226 /* This activated by the selector selection */
227 void StandardPLPanel::setRoot( playlist_item_t
*p_item
)
232 playlist_item_t
*p_pref_item
= playlist_GetPreferredNode( THEPL
, p_item
);
233 if( p_pref_item
) p_item
= p_pref_item
;
235 /* needed for popupAdd() */
236 currentRootId
= p_item
->i_id
;
238 /* cosmetics, ..still need playlist locking.. */
239 /*char *psz_title = input_item_GetName( p_item->p_input );
240 title->setText( qfu(psz_title) );
246 model
->rebuild( p_item
);
248 locationBar
->setIndex( QModelIndex() );
250 /* enable/disable adding */
251 if( p_item
== THEPL
->p_local_category
||
252 p_item
== THEPL
->p_local_onelevel
)
254 addButton
->setEnabled( true );
255 addButton
->setToolTip( qtr(I_PL_ADDPL
) );
257 else if( ( THEPL
->p_ml_category
&& p_item
== THEPL
->p_ml_category
) ||
258 ( THEPL
->p_ml_onelevel
&& p_item
== THEPL
->p_ml_onelevel
) )
260 addButton
->setEnabled( true );
261 addButton
->setToolTip( qtr(I_PL_ADDML
) );
264 addButton
->setEnabled( false );
267 void StandardPLPanel::removeItem( int i_id
)
269 model
->removeItem( i_id
);
272 /* Delete and Suppr key remove the selection
273 FilterKey function and code function */
274 void StandardPLPanel::keyPressEvent( QKeyEvent
*e
)
285 void StandardPLPanel::deleteSelection()
287 QItemSelectionModel
*selection
= currentView
->selectionModel();
288 QModelIndexList list
= selection
->selectedIndexes();
289 model
->doDelete( list
);
292 void StandardPLPanel::createIconView()
294 iconView
= new PlIconView( model
, this );
295 iconView
->setContextMenuPolicy( Qt::CustomContextMenu
);
296 CONNECT( iconView
, customContextMenuRequested( const QPoint
& ),
297 this, popupPlView( const QPoint
& ) );
298 CONNECT( iconView
, activated( const QModelIndex
& ),
299 this, activate( const QModelIndex
& ) );
300 CONNECT( locationBar
, invoked( const QModelIndex
& ),
301 iconView
, setRootIndex( const QModelIndex
& ) );
303 layout
->addWidget( iconView
, 1, 0, 1, -1 );
306 void StandardPLPanel::createTreeView()
308 /* Create and configure the QTreeView */
309 treeView
= new QTreeView
;
311 treeView
->setIconSize( QSize( 20, 20 ) );
312 treeView
->setAlternatingRowColors( true );
313 treeView
->setAnimated( true );
314 treeView
->setUniformRowHeights( true );
315 treeView
->setSortingEnabled( true );
316 treeView
->header()->setSortIndicator( -1 , Qt::AscendingOrder
);
317 treeView
->header()->setSortIndicatorShown( true );
318 treeView
->header()->setClickable( true );
319 treeView
->header()->setContextMenuPolicy( Qt::CustomContextMenu
);
321 treeView
->setSelectionBehavior( QAbstractItemView::SelectRows
);
322 treeView
->setSelectionMode( QAbstractItemView::ExtendedSelection
);
323 treeView
->setDragEnabled( true );
324 treeView
->setAcceptDrops( true );
325 treeView
->setDropIndicatorShown( true );
326 treeView
->setContextMenuPolicy( Qt::CustomContextMenu
);
328 /* setModel after setSortingEnabled(true), or the model will sort immediately! */
329 treeView
->setModel( model
);
331 if( getSettings()->contains( "headerStateV2" ) )
333 treeView
->header()->restoreState(
334 getSettings()->value( "headerStateV2" ).toByteArray() );
338 for( int m
= 1, c
= 0; m
!= COLUMN_END
; m
<<= 1, c
++ )
340 treeView
->setColumnHidden( c
, !( m
& COLUMN_DEFAULT
) );
341 if( m
== COLUMN_TITLE
) treeView
->header()->resizeSection( c
, 200 );
342 else if( m
== COLUMN_DURATION
) treeView
->header()->resizeSection( c
, 80 );
346 /* Connections for the TreeView */
347 CONNECT( treeView
, activated( const QModelIndex
& ),
348 this, activate( const QModelIndex
& ) );
349 CONNECT( treeView
->header(), customContextMenuRequested( const QPoint
& ),
350 this, popupSelectColumn( QPoint
) );
351 CONNECT( treeView
, customContextMenuRequested( const QPoint
& ),
352 this, popupPlView( const QPoint
& ) );
354 /* SignalMapper for columns */
355 selectColumnsSigMapper
= new QSignalMapper( this );
356 CONNECT( selectColumnsSigMapper
, mapped( int ),
357 this, toggleColumnShown( int ) );
359 /* Finish the layout */
360 layout
->addWidget( treeView
, 1, 0, 1, -1 );
363 void StandardPLPanel::showView( int i_view
)
369 if( treeView
== NULL
)
371 locationBar
->setIndex( treeView
->rootIndex() );
372 if( iconView
) iconView
->hide();
374 currentView
= treeView
;
379 if( iconView
== NULL
)
382 locationBar
->setIndex( iconView
->rootIndex() );
383 if( treeView
) treeView
->hide();
385 currentView
= iconView
;
392 void StandardPLPanel::wheelEvent( QWheelEvent
*e
)
394 // Accept this event in order to prevent unwanted volume up/down changes
398 void StandardPLPanel::activate( const QModelIndex
&index
)
400 last_activated_id
= model
->itemId( index
);
401 if( model
->hasChildren( index
) )
403 if( currentView
== iconView
) {
404 iconView
->setRootIndex( index
);
405 //title->setText( index.data().toString() );
406 locationBar
->setIndex( index
);
411 model
->activateItem( index
);
415 void StandardPLPanel::handleInputChange( input_thread_t
*p_input_thread
)
417 if( currentView
!= iconView
) return;
419 input_item_t
*p_input_item
= input_GetItem( p_input_thread
);
420 if( !p_input_item
) return;
422 playlist_Lock( THEPL
);
424 playlist_item_t
*p_item
= playlist_ItemGetByInput( THEPL
, p_input_item
);
426 if( p_item
&& p_item
->p_parent
&&
427 p_item
->p_parent
->i_id
== last_activated_id
)
429 QModelIndex index
= model
->index( p_item
->p_parent
->i_id
, 0 );
430 iconView
->setRootIndex( index
);
431 //title->setText( index.data().toString() );
432 locationBar
->setIndex( index
);
433 last_activated_id
= p_item
->i_id
;
436 playlist_Unlock( THEPL
);
439 LocationBar::LocationBar( PLModel
*m
)
442 mapper
= new QSignalMapper
;
443 CONNECT( mapper
, mapped( int ), this, invoke( int ) );
446 void LocationBar::setIndex( const QModelIndex
&index
)
449 QAction
*prev
= NULL
;
450 QModelIndex i
= index
;
452 QFontMetrics
metrics( font
);
453 font
.setBold( true );
456 PLItem
*item
= model
->getItem( i
);
458 QToolButton
*btn
= new QToolButton
;
459 char *fb_name
= input_item_GetTitleFbName( item
->inputItem() );
460 QString text
= qfu(fb_name
);
462 text
= QString("/ ") + metrics
.elidedText( text
, Qt::ElideRight
, 150 );
463 btn
->setText( text
);
464 btn
->setFont( font
);
465 prev
= insertWidget( prev
, btn
);
467 mapper
->setMapping( btn
, item
->id() );
468 CONNECT( btn
, clicked( ), mapper
, map( ) );
472 if( i
.isValid() ) i
= i
.parent();
477 void LocationBar::invoke( int i_id
)
479 QModelIndex index
= model
->index( i_id
, 0 );
481 emit
invoked ( index
);