1 /*****************************************************************************
2 * selector.cpp : Playlist source selector
3 ****************************************************************************
4 * Copyright (C) 2006-2009 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@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 *****************************************************************************/
31 #include "components/playlist/selector.hpp"
32 #include "playlist_item.hpp"
34 #include "../../dialogs_provider.hpp"
35 #include "playlist.hpp"
36 #include "util/customwidgets.hpp"
38 #include <QVBoxLayout>
39 #include <QHeaderView>
41 #include <QInputDialog>
42 #include <QMessageBox>
44 #include <vlc_playlist.h>
45 #include <vlc_services_discovery.h>
47 PLSelItem::PLSelItem ( QTreeWidgetItem
*i
, const QString
& text
)
48 : qitem(i
), lblAction( NULL
)
50 layout
= new QHBoxLayout();
51 layout
->setContentsMargins(0,0,0,0);
52 layout
->addSpacing( 3 );
54 lbl
= new QLabel( text
);
56 layout
->addWidget(lbl
, 1);
60 setMinimumHeight( 22 ); //Action icon height plus 6
63 void PLSelItem::addAction( ItemAction act
, const QString
& tooltip
)
65 if( lblAction
) return; //might change later
72 icon
= QIcon( ":/buttons/playlist/playlist_add" ); break;
74 icon
= QIcon( ":/buttons/playlist/playlist_remove" ); break;
77 lblAction
= new QVLCFramelessButton();
78 lblAction
->setIcon( icon
);
80 if( !tooltip
.isEmpty() ) lblAction
->setToolTip( tooltip
);
82 layout
->addWidget( lblAction
, 0 );
84 layout
->addSpacing( 3 );
86 CONNECT( lblAction
, clicked(), this, triggerAction() );
89 void PLSelItem::showAction() { if( lblAction
) lblAction
->show(); }
91 void PLSelItem::hideAction() { if( lblAction
) lblAction
->hide(); }
93 void PLSelItem::setText( const QString
& text
) { lbl
->setText( text
); }
95 void PLSelItem::enterEvent( QEvent
*ev
){ showAction(); }
97 void PLSelItem::leaveEvent( QEvent
*ev
){ hideAction(); }
99 PLSelector::PLSelector( QWidget
*p
, intf_thread_t
*_p_intf
)
100 : QTreeWidget( p
), p_intf(_p_intf
)
102 setFrameStyle( QFrame::NoFrame
);
103 viewport()->setAutoFillBackground( false );
104 setIconSize( QSize( 24,24 ) );
105 setIndentation( 14 );
107 setRootIsDecorated( true );
108 setAlternatingRowColors( false );
109 podcastsParent
= NULL
;
110 podcastsParentId
= -1;
112 viewport()->setAcceptDrops(true);
113 setDropIndicatorShown(true);
114 invisibleRootItem()->setFlags( invisibleRootItem()->flags() & ~Qt::ItemIsDropEnabled
);
116 CONNECT( THEMIM
, playlistItemAppended( int, int ),
117 this, plItemAdded( int, int ) );
118 CONNECT( THEMIM
, playlistItemRemoved( int ),
119 this, plItemRemoved( int ) );
120 CONNECT( THEMIM
->getIM(), metaChanged( input_item_t
*),
121 this, inputItemUpdate( input_item_t
* ) );
124 CONNECT( this, itemActivated( QTreeWidgetItem
*, int ),
125 this, setSource( QTreeWidgetItem
*) );
126 CONNECT( this, itemClicked( QTreeWidgetItem
*, int ),
127 this, setSource( QTreeWidgetItem
*) );
129 /* I believe this is unnecessary, seeing
130 QStyle::SH_ItemView_ActivateItemOnSingleClick
131 CONNECT( view, itemClicked( QTreeWidgetItem *, int ),
132 this, setSource( QTreeWidgetItem *) ); */
134 /* select the first item */
135 // view->setCurrentIndex( model->index( 0, 0, QModelIndex() ) );
138 PLSelector::~PLSelector()
142 int c
= podcastsParent
->childCount();
143 for( int i
= 0; i
< c
; i
++ )
145 QTreeWidgetItem
*item
= podcastsParent
->child(i
);
146 input_item_t
*p_input
= item
->data( 0, IN_ITEM_ROLE
).value
<input_item_t
*>();
147 vlc_gc_decref( p_input
);
152 void PLSelector::setSource( QTreeWidgetItem
*item
)
158 int i_type
= item
->data( 0, TYPE_ROLE
).toInt( &b_ok
);
159 if( !b_ok
|| i_type
== CATEGORY_TYPE
)
163 if( i_type
== SD_TYPE
)
165 QString qs
= item
->data( 0, NAME_ROLE
).toString();
166 sd_loaded
= playlist_IsServicesDiscoveryLoaded( THEPL
, qtu( qs
) );
168 playlist_ServicesDiscoveryAdd( THEPL
, qtu( qs
) );
171 playlist_Lock( THEPL
);
173 playlist_item_t
*pl_item
= NULL
;
175 if( i_type
== SD_TYPE
)
177 pl_item
= playlist_ChildSearchName( THEPL
->p_root
, qtu( item
->data(0, LONGNAME_ROLE
).toString() ) );
178 if( item
->data( 0, SPECIAL_ROLE
).toInt() == IS_PODCAST
)
180 if( pl_item
&& !sd_loaded
)
182 podcastsParentId
= pl_item
->i_id
;
183 for( int i
=0; i
< pl_item
->i_children
; i
++ )
184 addPodcastItem( pl_item
->pp_children
[i
] );
186 pl_item
= NULL
; //to prevent activating it
190 pl_item
= item
->data( 0, PL_ITEM_ROLE
).value
<playlist_item_t
*>();
192 playlist_Unlock( THEPL
);
195 emit
activated( pl_item
);
198 PLSelItem
* PLSelector::addItem (
199 SelectorItemType type
, const QString
& str
, bool drop
,
200 QTreeWidgetItem
* parentItem
)
202 QTreeWidgetItem
*item
= parentItem
?
203 new QTreeWidgetItem( parentItem
) : new QTreeWidgetItem( this );
204 PLSelItem
*selItem
= new PLSelItem( item
, str
);
205 setItemWidget( item
, 0, selItem
);
206 item
->setData( 0, TYPE_ROLE
, (int)type
);
207 if( !drop
) item
->setFlags( item
->flags() & ~Qt::ItemIsDropEnabled
);
212 PLSelItem
* putSDData( PLSelItem
* item
, const char* name
, const char* longname
)
214 item
->treeItem()->setData( 0, NAME_ROLE
, qfu( name
) );
215 item
->treeItem()->setData( 0, LONGNAME_ROLE
, qfu( longname
) );
219 PLSelItem
* putPLData( PLSelItem
* item
, playlist_item_t
* plItem
)
221 item
->treeItem()->setData( 0, PL_ITEM_ROLE
, QVariant::fromValue( plItem
) );
222 /* item->setData( 0, PL_ITEM_ID_ROLE, plItem->i_id );
223 item->setData( 0, IN_ITEM_ROLE, QVariant::fromValue( (void*) plItem->p_input ) ); );*/
227 PLSelItem
*PLSelector::addPodcastItem( playlist_item_t
*p_item
)
229 vlc_gc_incref( p_item
->p_input
);
230 char *psz_name
= input_item_GetName( p_item
->p_input
);
231 PLSelItem
*item
= addItem(
232 PL_ITEM_TYPE
, qfu( psz_name
), false, podcastsParent
);
233 item
->addAction( RM_ACTION
, qtr( "Remove this podcast subscription" ) );
234 item
->treeItem()->setData( 0, PL_ITEM_ROLE
, QVariant::fromValue( p_item
) );
235 item
->treeItem()->setData( 0, PL_ITEM_ID_ROLE
, QVariant(p_item
->i_id
) );
236 item
->treeItem()->setData( 0, IN_ITEM_ROLE
, QVariant::fromValue( p_item
->p_input
) );
237 CONNECT( item
, action( PLSelItem
* ), this, podcastRemove( PLSelItem
* ) );
242 void PLSelector::createItems()
244 PLSelItem
*pl
= putPLData( addItem( PL_ITEM_TYPE
, qtr( "Playlist" ), true ),
246 pl
->treeItem()->setData( 0, SPECIAL_ROLE
, QVariant( IS_PL
) );
248 PLSelItem
*ml
= putPLData( addItem( PL_ITEM_TYPE
, qtr( "Media Library" ), true ),
249 THEPL
->p_media_library
);
250 ml
->treeItem()->setData( 0, SPECIAL_ROLE
, QVariant( IS_ML
) );
252 QTreeWidgetItem
*mycomp
= addItem( CATEGORY_TYPE
, qtr( "My Computer" ),
254 QTreeWidgetItem
*devices
= addItem( CATEGORY_TYPE
, qtr( "Devices" ),
256 QTreeWidgetItem
*lan
= addItem( CATEGORY_TYPE
, qtr( "Local Network" ),
258 QTreeWidgetItem
*internet
= addItem( CATEGORY_TYPE
, qtr( "Internet" ),
259 false )->treeItem();;
261 char **ppsz_longnames
;
263 char **ppsz_names
= vlc_sd_GetNames( THEPL
, &ppsz_longnames
, &p_categories
);
267 char **ppsz_name
= ppsz_names
, **ppsz_longname
= ppsz_longnames
;
268 int *p_category
= p_categories
;
269 for( ; *ppsz_name
; ppsz_name
++, ppsz_longname
++, p_category
++ )
271 //msg_Dbg( p_intf, "Adding a SD item: %s", *ppsz_longname );
273 if( *p_category
== SD_CAT_INTERNET
)
275 PLSelItem
*selItem
= addItem( SD_TYPE
, *ppsz_longname
, false, internet
);
276 putSDData( selItem
, *ppsz_name
, *ppsz_longname
);
277 if( !strncmp( *ppsz_name
, "podcast", 7 ) )
279 selItem
->treeItem()->setData( 0, SPECIAL_ROLE
, QVariant( IS_PODCAST
) );
280 selItem
->addAction( ADD_ACTION
, qtr( "Subscribe to a podcast" ) );
281 CONNECT( selItem
, action( PLSelItem
* ), this, podcastAdd( PLSelItem
* ) );
282 podcastsParent
= selItem
->treeItem();
285 else if( *p_category
== SD_CAT_DEVICES
)
287 putSDData( addItem( SD_TYPE
, *ppsz_longname
, false, devices
),
288 *ppsz_name
, *ppsz_longname
);
290 else if( *p_category
== SD_CAT_LAN
)
292 putSDData( addItem( SD_TYPE
, *ppsz_longname
, false, lan
),
293 *ppsz_name
, *ppsz_longname
);
295 else if( *p_category
== SD_CAT_MYCOMPUTER
)
297 putSDData( addItem( SD_TYPE
, *ppsz_longname
, false, mycomp
),
298 *ppsz_name
, *ppsz_longname
);
302 putSDData( addItem( SD_TYPE
, qtr( *ppsz_longname
), false ),
303 *ppsz_name
, *ppsz_longname
);
307 free( *ppsz_longname
);
310 free( ppsz_longnames
);
311 free( p_categories
);
313 if( mycomp
->childCount() == 0 ) delete mycomp
;
314 if( devices
->childCount() == 0 ) delete devices
;
315 if( lan
->childCount() == 0 ) delete lan
;
316 if( internet
->childCount() == 0 ) delete internet
;
319 QStringList
PLSelector::mimeTypes() const
322 types
<< "vlc/qt-playlist-item";
326 bool PLSelector::dropMimeData ( QTreeWidgetItem
* parent
, int index
,
327 const QMimeData
* data
, Qt::DropAction action
)
329 if( !parent
) return false;
331 QVariant type
= parent
->data( 0, TYPE_ROLE
);
332 if( type
== QVariant() ) return false;
333 int i_truth
= parent
->data( 0, SPECIAL_ROLE
).toInt();
335 if( i_truth
!= IS_PL
&& i_truth
!= IS_ML
) return false;
336 bool to_pl
= ( i_truth
== IS_PL
);
338 if( data
->hasFormat( "vlc/qt-playlist-item" ) )
340 QByteArray encodedData
= data
->data( "vlc/qt-playlist-item" );
341 QDataStream
stream( &encodedData
, QIODevice::ReadOnly
);
342 playlist_Lock( THEPL
);
343 while( !stream
.atEnd() )
346 stream
.readRawData( (char*)&item
, sizeof(PLItem
*) );
347 input_item_t
*pl_input
=item
->inputItem();
348 playlist_AddExt ( THEPL
,
349 pl_input
->psz_uri
, pl_input
->psz_name
,
350 PLAYLIST_APPEND
| PLAYLIST_SPREPARSE
, PLAYLIST_END
,
351 pl_input
->i_duration
,
352 pl_input
->i_options
, pl_input
->ppsz_options
, pl_input
->optflagc
,
355 playlist_Unlock( THEPL
);
360 void PLSelector::plItemAdded( int item
, int parent
)
362 if( parent
!= podcastsParentId
) return;
364 playlist_Lock( THEPL
);
366 playlist_item_t
*p_item
= playlist_ItemGetById( THEPL
, item
);
368 playlist_Unlock( THEPL
);
372 int c
= podcastsParent
->childCount();
373 for( int i
= 0; i
< c
; i
++ )
375 QTreeWidgetItem
*podItem
= podcastsParent
->child(i
);
376 if( podItem
->data( 0, PL_ITEM_ID_ROLE
).toInt() == item
)
378 //msg_Dbg( p_intf, "Podcast already in: (%d) %s", item, p_item->p_input->psz_uri);
379 playlist_Unlock( THEPL
);
384 //msg_Dbg( p_intf, "Adding podcast: (%d) %s", item, p_item->p_input->psz_uri );
385 addPodcastItem( p_item
);
387 playlist_Unlock( THEPL
);
389 podcastsParent
->setExpanded( true );
392 void PLSelector::plItemRemoved( int id
)
394 int c
= podcastsParent
->childCount();
395 for( int i
= 0; i
< c
; i
++ )
397 QTreeWidgetItem
*item
= podcastsParent
->child(i
);
398 if( item
->data( 0, PL_ITEM_ID_ROLE
).toInt() == id
)
400 input_item_t
*p_input
= item
->data( 0, IN_ITEM_ROLE
).value
<input_item_t
*>();
401 //msg_Dbg( p_intf, "Removing podcast: (%d) %s", id, p_input->psz_uri );
402 vlc_gc_decref( p_input
);
409 void PLSelector::inputItemUpdate( input_item_t
*arg
)
411 int c
= podcastsParent
->childCount();
412 for( int i
= 0; i
< c
; i
++ )
414 QTreeWidgetItem
*item
= podcastsParent
->child(i
);
415 input_item_t
*p_input
= item
->data( 0, IN_ITEM_ROLE
).value
<input_item_t
*>();
418 PLSelItem
*si
= itemWidget( item
);
419 char *psz_name
= input_item_GetName( p_input
);
420 si
->setText( qfu( psz_name
) );
427 void PLSelector::podcastAdd( PLSelItem
* item
)
430 QString url
= QInputDialog::getText( this, qtr( "Subscribe" ),
431 qtr( "Enter URL of the podcast to subscribe to:" ),
432 QLineEdit::Normal
, QString(), &ok
);
433 if( !ok
|| url
.isEmpty() ) return;
435 setSource( podcastsParent
); //to load the SD in case it's not loaded
437 vlc_object_t
*p_obj
= (vlc_object_t
*) vlc_object_find_name(
438 p_intf
->p_libvlc
, "podcast", FIND_CHILD
);
441 QString
request("ADD:");
443 var_SetString( p_obj
, "podcast-request", qtu( request
) );
444 vlc_object_release( p_obj
);
447 void PLSelector::podcastRemove( PLSelItem
* item
)
449 //FIXME will translators know to leave that %1 somewhere inside?
450 QString
question ( qtr( "Do you really want to unsubscribe from %1?" ) );
451 question
= question
.arg( item
->text() );
452 QMessageBox::StandardButton res
=
453 QMessageBox::question( this, qtr( "Unsubscribe" ), question
,
454 QMessageBox::Ok
| QMessageBox::Cancel
,
455 QMessageBox::Cancel
);
456 if( res
== QMessageBox::Cancel
) return;
458 input_item_t
*input
= item
->treeItem()->data( 0, IN_ITEM_ROLE
).value
<input_item_t
*>();
461 vlc_object_t
*p_obj
= (vlc_object_t
*) vlc_object_find_name(
462 p_intf
->p_libvlc
, "podcast", FIND_CHILD
);
465 QString
request("RM:");
466 char *psz_uri
= input_item_GetURI( input
);
467 request
+= qfu( psz_uri
);
468 var_SetString( p_obj
, "podcast-request", qtu( request
) );
469 vlc_object_release( p_obj
);
473 PLSelItem
* PLSelector::itemWidget( QTreeWidgetItem
*item
)
475 return ( static_cast<PLSelItem
*>( QTreeWidget::itemWidget( item
, 0 ) ) );
478 void PLSelector::drawBranches ( QPainter
* painter
, const QRect
& rect
, const QModelIndex
& index
) const
480 if( !model()->hasChildren( index
) ) return;
482 option
.initFrom( this );
483 option
.rect
= rect
.adjusted( rect
.width() - indentation(), 0, 0, 0 );
485 /*option.state = QStyle::State_Children;
486 if( isExpanded( index ) ) option.state |= QStyle::State_Open;*/
487 style()->drawPrimitive( isExpanded( index
) ?
488 QStyle::PE_IndicatorArrowDown
:
489 QStyle::PE_IndicatorArrowRight
, &option
, painter
);