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 QPushButton
*viewButton
= new QPushButton( this );
101 viewButton
->setIcon( QIcon( ":/buttons/playlist/playlist_add" ) );
102 layout
->addWidget( viewButton
, 0, 2 );
103 BUTTONACT( viewButton
, toggleView() );
106 getSettings()->beginGroup("Playlist");
108 int i_viewMode
= getSettings()->value( "view-mode", TREE_VIEW
).toInt();
109 if( i_viewMode
== ICON_VIEW
)
112 currentView
= iconView
;
117 currentView
= treeView
;
120 getSettings()->endGroup();
122 last_activated_id
= -1;
123 CONNECT( THEMIM
, inputChanged( input_thread_t
* ),
124 this, handleInputChange( input_thread_t
* ) );
127 StandardPLPanel::~StandardPLPanel()
129 getSettings()->beginGroup("Playlist");
131 getSettings()->setValue( "headerStateV2", treeView
->header()->saveState() );
132 getSettings()->setValue( "view-mode", ( currentView
== iconView
) ? ICON_VIEW
: TREE_VIEW
);
133 getSettings()->endGroup();
136 /* Unused anymore, but might be useful, like in right-click menu */
137 void StandardPLPanel::gotoPlayingItem()
139 currentView
->scrollTo( model
->currentIndex() );
142 void StandardPLPanel::handleExpansion( const QModelIndex
& index
)
144 assert( currentView
);
145 currentView
->scrollTo( index
);
148 /* PopupAdd Menu for the Add Menu */
149 void StandardPLPanel::popupAdd()
152 if( currentRootId
== THEPL
->p_local_category
->i_id
||
153 currentRootId
== THEPL
->p_local_onelevel
->i_id
)
155 popup
.addAction( qtr(I_PL_ADDF
), THEDP
, SLOT( simplePLAppendDialog()) );
156 popup
.addAction( qtr(I_PL_ADDDIR
), THEDP
, SLOT( PLAppendDir()) );
157 popup
.addAction( qtr(I_OP_ADVOP
), THEDP
, SLOT( PLAppendDialog()) );
159 else if( ( THEPL
->p_ml_category
&&
160 currentRootId
== THEPL
->p_ml_category
->i_id
) ||
161 ( THEPL
->p_ml_onelevel
&&
162 currentRootId
== THEPL
->p_ml_onelevel
->i_id
) )
164 popup
.addAction( qtr(I_PL_ADDF
), THEDP
, SLOT( simpleMLAppendDialog()) );
165 popup
.addAction( qtr(I_PL_ADDDIR
), THEDP
, SLOT( MLAppendDir() ) );
166 popup
.addAction( qtr(I_OP_ADVOP
), THEDP
, SLOT( MLAppendDialog() ) );
169 popup
.exec( QCursor::pos() - addButton
->mapFromGlobal( QCursor::pos() )
170 + QPoint( 0, addButton
->height() ) );
173 void StandardPLPanel::popupPlView( const QPoint
&point
)
175 QModelIndex index
= currentView
->indexAt( point
);
176 QPoint globalPoint
= currentView
->viewport()->mapToGlobal( point
);
177 QItemSelectionModel
*selection
= currentView
->selectionModel();
178 QModelIndexList list
= selection
->selectedIndexes();
179 model
->popup( index
, globalPoint
, list
);
182 void StandardPLPanel::popupSelectColumn( QPoint pos
)
187 /* We do not offer the option to hide index 0 column, or
188 * QTreeView will behave weird */
190 for( i
= 1 << 1, j
= 1; i
< COLUMN_END
; i
<<= 1, j
++ )
192 QAction
* option
= menu
.addAction(
193 qfu( psz_column_title( i
) ) );
194 option
->setCheckable( true );
195 option
->setChecked( !treeView
->isColumnHidden( j
) );
196 selectColumnsSigMapper
->setMapping( option
, j
);
197 CONNECT( option
, triggered(), selectColumnsSigMapper
, map() );
199 menu
.exec( QCursor::pos() );
202 void StandardPLPanel::toggleColumnShown( int i
)
204 treeView
->setColumnHidden( i
, !treeView
->isColumnHidden( i
) );
207 /* Search in the playlist */
208 void StandardPLPanel::search( const QString
& searchText
)
210 model
->search( searchText
);
213 /* Set the root of the new Playlist */
214 /* This activated by the selector selection */
215 void StandardPLPanel::setRoot( playlist_item_t
*p_item
)
220 playlist_item_t
*p_pref_item
= playlist_GetPreferredNode( THEPL
, p_item
);
221 if( p_pref_item
) p_item
= p_pref_item
;
223 /* needed for popupAdd() */
224 currentRootId
= p_item
->i_id
;
226 /* cosmetics, ..still need playlist locking.. */
227 /*char *psz_title = input_item_GetName( p_item->p_input );
228 title->setText( qfu(psz_title) );
234 model
->rebuild( p_item
);
236 locationBar
->setIndex( QModelIndex() );
238 /* enable/disable adding */
239 if( p_item
== THEPL
->p_local_category
||
240 p_item
== THEPL
->p_local_onelevel
)
242 addButton
->setEnabled( true );
243 addButton
->setToolTip( qtr(I_PL_ADDPL
) );
245 else if( ( THEPL
->p_ml_category
&& p_item
== THEPL
->p_ml_category
) ||
246 ( THEPL
->p_ml_onelevel
&& p_item
== THEPL
->p_ml_onelevel
) )
248 addButton
->setEnabled( true );
249 addButton
->setToolTip( qtr(I_PL_ADDML
) );
252 addButton
->setEnabled( false );
255 void StandardPLPanel::removeItem( int i_id
)
257 model
->removeItem( i_id
);
260 /* Delete and Suppr key remove the selection
261 FilterKey function and code function */
262 void StandardPLPanel::keyPressEvent( QKeyEvent
*e
)
273 void StandardPLPanel::deleteSelection()
275 QItemSelectionModel
*selection
= currentView
->selectionModel();
276 QModelIndexList list
= selection
->selectedIndexes();
277 model
->doDelete( list
);
280 void StandardPLPanel::createIconView()
282 iconView
= new PlIconView( model
, this );
283 iconView
->setContextMenuPolicy( Qt::CustomContextMenu
);
284 CONNECT( iconView
, customContextMenuRequested( const QPoint
& ),
285 this, popupPlView( const QPoint
& ) );
286 CONNECT( iconView
, activated( const QModelIndex
& ),
287 this, activate( const QModelIndex
& ) );
288 CONNECT( locationBar
, invoked( const QModelIndex
& ),
289 iconView
, setRootIndex( const QModelIndex
& ) );
291 layout
->addWidget( iconView
, 1, 0, 1, -1 );
294 void StandardPLPanel::createTreeView()
296 /* Create and configure the QTreeView */
297 treeView
= new QTreeView
;
299 treeView
->setIconSize( QSize( 20, 20 ) );
300 treeView
->setAlternatingRowColors( true );
301 treeView
->setAnimated( true );
302 treeView
->setUniformRowHeights( true );
303 treeView
->setSortingEnabled( true );
304 treeView
->header()->setSortIndicator( -1 , Qt::AscendingOrder
);
305 treeView
->header()->setSortIndicatorShown( true );
306 treeView
->header()->setClickable( true );
307 treeView
->header()->setContextMenuPolicy( Qt::CustomContextMenu
);
309 treeView
->setSelectionBehavior( QAbstractItemView::SelectRows
);
310 treeView
->setSelectionMode( QAbstractItemView::ExtendedSelection
);
311 treeView
->setDragEnabled( true );
312 treeView
->setAcceptDrops( true );
313 treeView
->setDropIndicatorShown( true );
314 treeView
->setContextMenuPolicy( Qt::CustomContextMenu
);
316 /* setModel after setSortingEnabled(true), or the model will sort immediately! */
317 treeView
->setModel( model
);
319 if( getSettings()->contains( "headerStateV2" ) )
321 treeView
->header()->restoreState(
322 getSettings()->value( "headerStateV2" ).toByteArray() );
326 for( int m
= 1, c
= 0; m
!= COLUMN_END
; m
<<= 1, c
++ )
328 treeView
->setColumnHidden( c
, !( m
& COLUMN_DEFAULT
) );
329 if( m
== COLUMN_TITLE
) treeView
->header()->resizeSection( c
, 200 );
330 else if( m
== COLUMN_DURATION
) treeView
->header()->resizeSection( c
, 80 );
334 /* Connections for the TreeView */
335 CONNECT( treeView
, activated( const QModelIndex
& ),
336 this, activate( const QModelIndex
& ) );
337 CONNECT( treeView
->header(), customContextMenuRequested( const QPoint
& ),
338 this, popupSelectColumn( QPoint
) );
339 CONNECT( treeView
, customContextMenuRequested( const QPoint
& ),
340 this, popupPlView( const QPoint
& ) );
342 /* SignalMapper for columns */
343 selectColumnsSigMapper
= new QSignalMapper( this );
344 CONNECT( selectColumnsSigMapper
, mapped( int ),
345 this, toggleColumnShown( int ) );
347 /* Finish the layout */
348 layout
->addWidget( treeView
, 1, 0, 1, -1 );
351 void StandardPLPanel::toggleView()
353 if( treeView
&& treeView
->isVisible() )
355 if( iconView
== NULL
)
358 locationBar
->setIndex( iconView
->rootIndex() );
361 currentView
= iconView
;
365 if( treeView
== NULL
)
368 locationBar
->setIndex( treeView
->rootIndex() );
371 currentView
= treeView
;
375 void StandardPLPanel::wheelEvent( QWheelEvent
*e
)
377 // Accept this event in order to prevent unwanted volume up/down changes
381 void StandardPLPanel::activate( const QModelIndex
&index
)
383 last_activated_id
= model
->itemId( index
);
384 if( model
->hasChildren( index
) )
386 if( currentView
== iconView
) {
387 iconView
->setRootIndex( index
);
388 //title->setText( index.data().toString() );
389 locationBar
->setIndex( index
);
394 model
->activateItem( index
);
398 void StandardPLPanel::handleInputChange( input_thread_t
*p_input_thread
)
400 if( currentView
!= iconView
) return;
402 input_item_t
*p_input_item
= input_GetItem( p_input_thread
);
403 if( !p_input_item
) return;
405 playlist_Lock( THEPL
);
407 playlist_item_t
*p_item
= playlist_ItemGetByInput( THEPL
, p_input_item
);
409 if( p_item
&& p_item
->p_parent
&&
410 p_item
->p_parent
->i_id
== last_activated_id
)
412 QModelIndex index
= model
->index( p_item
->p_parent
->i_id
, 0 );
413 iconView
->setRootIndex( index
);
414 //title->setText( index.data().toString() );
415 locationBar
->setIndex( index
);
416 last_activated_id
= p_item
->i_id
;
419 playlist_Unlock( THEPL
);
422 LocationBar::LocationBar( PLModel
*m
)
425 mapper
= new QSignalMapper
;
426 CONNECT( mapper
, mapped( int ), this, invoke( int ) );
429 void LocationBar::setIndex( const QModelIndex
&index
)
432 QAction
*prev
= NULL
;
433 QModelIndex i
= index
;
435 QFontMetrics
metrics( font
);
436 font
.setBold( true );
439 PLItem
*item
= model
->getItem( i
);
441 QToolButton
*btn
= new QToolButton
;
442 char *fb_name
= input_item_GetTitleFbName( item
->inputItem() );
443 QString text
= qfu(fb_name
);
445 text
= QString("/ ") + metrics
.elidedText( text
, Qt::ElideRight
, 150 );
446 btn
->setText( text
);
447 btn
->setFont( font
);
448 prev
= insertWidget( prev
, btn
);
450 mapper
->setMapping( btn
, item
->id() );
451 CONNECT( btn
, clicked( ), mapper
, map( ) );
455 if( i
.isValid() ) i
= i
.parent();
460 void LocationBar::invoke( int i_id
)
462 QModelIndex index
= model
->index( i_id
, 0 );
464 emit
invoked ( index
);