1 /*****************************************************************************
2 * selector.cpp : Playlist source selector
3 ****************************************************************************
4 * Copyright (C) 2006-2009 the VideoLAN team
5 * $Id: bb654d41893810a93bea7a8766df9c191693af73 $
6 *
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Jean-Baptiste Kempf
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 "qt.hpp"
30 #include "components/playlist/selector.hpp"
31 #include "playlist_model.hpp" /* plMimeData */
32 #include "input_manager.hpp" /* MainInputManager, for podcast */
33
34 #include <QApplication>
35 #include <QInputDialog>
36 #include <QMessageBox>
37 #include <QMimeData>
38 #include <QDragMoveEvent>
39 #include <QTreeWidgetItem>
40 #include <QHBoxLayout>
41 #include <QPainter>
42 #include <QPalette>
43 #include <QScrollBar>
44 #include <QResource>
45 #include <assert.h>
46
47 #include <vlc_playlist.h>
48 #include <vlc_services_discovery.h>
49
paintEvent(QPaintEvent * event)50 void SelectorActionButton::paintEvent( QPaintEvent *event )
51 {
52 QPainter p( this );
53 QColor color = palette().color( QPalette::HighlightedText );
54 color.setAlpha( 80 );
55 if( underMouse() )
56 p.fillRect( rect(), color );
57 p.setPen( color );
58 int frame = style()->pixelMetric( QStyle::PM_DefaultFrameWidth, 0, this );
59 p.drawLine( rect().topLeft() + QPoint( 0, frame ),
60 rect().bottomLeft() - QPoint( 0, frame ) );
61 QFramelessButton::paintEvent( event );
62 }
63
PLSelItem(QTreeWidgetItem * i,const QString & text)64 PLSelItem::PLSelItem ( QTreeWidgetItem *i, const QString& text )
65 : qitem(i), lblAction( NULL)
66 {
67 layout = new QHBoxLayout( this );
68 layout->setContentsMargins(0,0,0,0);
69 layout->addSpacing( 3 );
70
71 lbl = new QElidingLabel( text );
72 layout->addWidget(lbl, 1);
73
74 int height = qMax( 22, fontMetrics().height() + 8 );
75 setMinimumHeight( height );
76 }
77
addAction(ItemAction act,const QString & tooltip)78 void PLSelItem::addAction( ItemAction act, const QString& tooltip )
79 {
80 if( lblAction ) return; //might change later
81
82 QIcon icon;
83
84 switch( act )
85 {
86 case ADD_ACTION:
87 icon = QIcon( ":/buttons/playlist/playlist_add.svg" ); break;
88 case RM_ACTION:
89 icon = QIcon( ":/buttons/playlist/playlist_remove.svg" ); break;
90 default:
91 return;
92 }
93
94 lblAction = new SelectorActionButton();
95 lblAction->setIcon( icon );
96 int icon_size = fontMetrics().height();
97 lblAction->setIconSize( QSize( icon_size, icon_size ) );
98 lblAction->setMinimumWidth( lblAction->sizeHint().width() + icon_size );
99
100 if( !tooltip.isEmpty() ) lblAction->setToolTip( tooltip );
101
102 layout->addWidget( lblAction, 0 );
103 lblAction->hide();
104
105 CONNECT( lblAction, clicked(), this, triggerAction() );
106 }
107
108
PLSelector(QWidget * p,intf_thread_t * _p_intf)109 PLSelector::PLSelector( QWidget *p, intf_thread_t *_p_intf )
110 : QTreeWidget( p ), p_intf(_p_intf)
111 {
112 /* Properties */
113 setFrameStyle( QFrame::NoFrame );
114 setAttribute( Qt::WA_MacShowFocusRect, false );
115 viewport()->setAutoFillBackground( false );
116 setIconSize( QSize( 24,24 ) );
117 setIndentation( 12 );
118 setHeaderHidden( true );
119 setRootIsDecorated( true );
120 setAlternatingRowColors( false );
121
122 /* drops */
123 viewport()->setAcceptDrops(true);
124 setDropIndicatorShown(true);
125 invisibleRootItem()->setFlags( invisibleRootItem()->flags() & ~Qt::ItemIsDropEnabled );
126
127 setMinimumHeight( 120 );
128
129 /* Podcasts */
130 podcastsParent = NULL;
131 podcastsParentId = -1;
132
133 /* Podcast connects */
134 CONNECT( THEMIM, playlistItemAppended( int, int ),
135 this, plItemAdded( int, int ) );
136 CONNECT( THEMIM, playlistItemRemoved( int ),
137 this, plItemRemoved( int ) );
138 DCONNECT( THEMIM->getIM(), metaChanged( input_item_t *),
139 this, inputItemUpdate( input_item_t * ) );
140
141 createItems();
142
143 setRootIsDecorated( false );
144 setIndentation( 5 );
145 /* Expand at least to show level 2 */
146 for ( int i = 0; i < topLevelItemCount(); i++ )
147 expandItem( topLevelItem( i ) );
148
149 /***
150 * We need to react to both clicks and activation (enter-key) here.
151 * We use curItem to avoid rebuilding twice.
152 * See QStyle::SH_ItemView_ActivateItemOnSingleClick
153 ***/
154 curItem = NULL;
155 CONNECT( this, itemActivated( QTreeWidgetItem *, int ),
156 this, setSource( QTreeWidgetItem *) );
157 CONNECT( this, itemClicked( QTreeWidgetItem *, int ),
158 this, setSource( QTreeWidgetItem *) );
159 }
160
~PLSelector()161 PLSelector::~PLSelector()
162 {
163 if( podcastsParent )
164 {
165 int c = podcastsParent->childCount();
166 for( int i = 0; i < c; i++ )
167 {
168 QTreeWidgetItem *item = podcastsParent->child(i);
169 input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
170 input_item_Release( p_input );
171 }
172 }
173 }
174
putSDData(PLSelItem * item,const char * name,const char * longname)175 PLSelItem * putSDData( PLSelItem* item, const char* name, const char* longname )
176 {
177 item->treeItem()->setData( 0, NAME_ROLE, qfu( name ) );
178 item->treeItem()->setData( 0, LONGNAME_ROLE, qfu( longname ) );
179 return item;
180 }
181
putPLData(PLSelItem * item,playlist_item_t * plItem)182 PLSelItem * putPLData( PLSelItem* item, playlist_item_t* plItem )
183 {
184 item->treeItem()->setData( 0, PL_ITEM_ROLE, QVariant::fromValue( plItem ) );
185 /* item->setData( 0, PL_ITEM_ID_ROLE, plItem->i_id );
186 item->setData( 0, IN_ITEM_ROLE, QVariant::fromValue( (void*) plItem->p_input ) ); );*/
187 return item;
188 }
189
190 /*
191 * Reads and updates the playlist's duration as [xx:xx] after the label in the tree
192 * item - the treeview item to get the duration for
193 * prefix - the string to use before the time (should be the category name)
194 */
updateTotalDuration(PLSelItem * item,const char * prefix)195 void PLSelector::updateTotalDuration( PLSelItem* item, const char* prefix )
196 {
197 /* Getting the playlist */
198 QVariant playlistVariant = item->treeItem()->data( 0, PL_ITEM_ROLE );
199 playlist_item_t* node = playlistVariant.value<playlist_item_t*>();
200
201 /* Get the duration of the playlist item */
202 playlist_Lock( THEPL );
203 mtime_t mt_duration = playlist_GetNodeDuration( node );
204 playlist_Unlock( THEPL );
205
206 /* Formatting time */
207 QString qs_timeLabel( prefix );
208
209 int i_seconds = mt_duration / 1000000;
210 int i_minutes = i_seconds / 60;
211 i_seconds = i_seconds % 60;
212 if( i_minutes >= 60 )
213 {
214 int i_hours = i_minutes / 60;
215 i_minutes = i_minutes % 60;
216 qs_timeLabel += QString(" [%1:%2:%3]").arg( i_hours ).arg( i_minutes, 2, 10, QChar('0') ).arg( i_seconds, 2, 10, QChar('0') );
217 }
218 else
219 qs_timeLabel += QString( " [%1:%2]").arg( i_minutes, 2, 10, QChar('0') ).arg( i_seconds, 2, 10, QChar('0') );
220
221 item->setText( qs_timeLabel );
222 }
223
createItems()224 void PLSelector::createItems()
225 {
226 /* PL */
227 playlistItem = putPLData( addItem( PL_ITEM_TYPE, N_("Playlist"), true ),
228 THEPL->p_playing );
229 playlistItem->treeItem()->setData( 0, SPECIAL_ROLE, QVariant( IS_PL ) );
230 playlistItem->treeItem()->setData( 0, Qt::DecorationRole, QIcon( ":/sidebar/playlist.svg" ) );
231 setCurrentItem( playlistItem->treeItem() );
232
233 /* ML */
234 if( THEPL->p_media_library )
235 {
236 PLSelItem *ml = putPLData( addItem( PL_ITEM_TYPE, N_("Media Library"), true ),
237 THEPL->p_media_library );
238 ml->treeItem()->setData( 0, SPECIAL_ROLE, QVariant( IS_ML ) );
239 ml->treeItem()->setData( 0, Qt::DecorationRole, QIcon( ":/sidebar/library.svg" ) );
240 }
241
242 /* SD nodes */
243 QTreeWidgetItem *mycomp = addItem( CATEGORY_TYPE, N_("My Computer"), false, true )->treeItem();
244 QTreeWidgetItem *devices = addItem( CATEGORY_TYPE, N_("Devices"), false, true )->treeItem();
245 QTreeWidgetItem *lan = addItem( CATEGORY_TYPE, N_("Local Network"), false, true )->treeItem();
246 QTreeWidgetItem *internet = addItem( CATEGORY_TYPE, N_("Internet"), false, true )->treeItem();
247
248 #define NOT_SELECTABLE(w) w->setFlags( w->flags() ^ Qt::ItemIsSelectable );
249 NOT_SELECTABLE( mycomp );
250 NOT_SELECTABLE( devices );
251 NOT_SELECTABLE( lan );
252 NOT_SELECTABLE( internet );
253 #undef NOT_SELECTABLE
254
255 /* SD subnodes */
256 char **ppsz_longnames;
257 int *p_categories;
258 char **ppsz_names = vlc_sd_GetNames( THEPL, &ppsz_longnames, &p_categories );
259 if( !ppsz_names )
260 return;
261
262 char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
263 int *p_category = p_categories;
264 for( ; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++ )
265 {
266 //msg_Dbg( p_intf, "Adding a SD item: %s", *ppsz_longname );
267
268 PLSelItem *selItem;
269 QIcon icon;
270 QString name( *ppsz_name );
271 switch( *p_category )
272 {
273 case SD_CAT_INTERNET:
274 {
275 selItem = addItem( SD_TYPE, *ppsz_longname, false, false, internet );
276 if( name.startsWith( "podcast" ) )
277 {
278 selItem->treeItem()->setData( 0, SPECIAL_ROLE, QVariant( IS_PODCAST ) );
279 selItem->addAction( ADD_ACTION, qtr( "Subscribe to a podcast" ) );
280 CONNECT( selItem, action( PLSelItem* ), this, podcastAdd( PLSelItem* ) );
281 podcastsParent = selItem->treeItem();
282 icon = QIcon( ":/sidebar/podcast.svg" );
283 }
284 else if ( name.startsWith( "lua{" ) )
285 {
286 int i_head = name.indexOf( "sd='" ) + 4;
287 int i_tail = name.indexOf( '\'', i_head );
288 QString iconname = QString( ":/sidebar/sd/%1.svg" ).arg( name.mid( i_head, i_tail - i_head ) );
289 QResource resource( iconname );
290 if ( !resource.isValid() )
291 icon = QIcon( ":/sidebar/network.svg" );
292 else
293 icon = QIcon( iconname );
294 }
295 }
296 break;
297 case SD_CAT_DEVICES:
298 name = name.mid( 0, name.indexOf( '{' ) );
299 selItem = addItem( SD_TYPE, *ppsz_longname, false, false, devices );
300 if ( name == "xcb_apps" )
301 icon = QIcon( ":/sidebar/screen.svg" );
302 else if ( name == "mtp" )
303 icon = QIcon( ":/sidebar/mtp.svg" );
304 else if ( name == "disc" )
305 icon = QIcon( ":/sidebar/disc.svg" );
306 else
307 icon = QIcon( ":/sidebar/capture.svg" );
308 break;
309 case SD_CAT_LAN:
310 selItem = addItem( SD_TYPE, *ppsz_longname, false, false, lan );
311 icon = QIcon( ":/sidebar/lan.svg" );
312 break;
313 case SD_CAT_MYCOMPUTER:
314 name = name.mid( 0, name.indexOf( '{' ) );
315 selItem = addItem( SD_TYPE, *ppsz_longname, false, false, mycomp );
316 if ( name == "video_dir" )
317 icon = QIcon( ":/sidebar/movie.svg" );
318 else if ( name == "audio_dir" )
319 icon = QIcon( ":/sidebar/music.svg" );
320 else if ( name == "picture_dir" )
321 icon = QIcon( ":/sidebar/pictures.svg" );
322 else
323 icon = QIcon( ":/sidebar/movie.svg" );
324 break;
325 default:
326 selItem = addItem( SD_TYPE, *ppsz_longname );
327 }
328
329 selItem->treeItem()->setData( 0, SD_CATEGORY_ROLE, *p_category );
330 putSDData( selItem, *ppsz_name, *ppsz_longname );
331 if ( ! icon.isNull() )
332 selItem->treeItem()->setData( 0, Qt::DecorationRole, icon );
333
334 free( *ppsz_name );
335 free( *ppsz_longname );
336 }
337 free( ppsz_names );
338 free( ppsz_longnames );
339 free( p_categories );
340
341 if( mycomp->childCount() == 0 ) delete mycomp;
342 if( devices->childCount() == 0 ) delete devices;
343 if( lan->childCount() == 0 ) delete lan;
344 if( internet->childCount() == 0 ) delete internet;
345 }
346
setSource(QTreeWidgetItem * item)347 void PLSelector::setSource( QTreeWidgetItem *item )
348 {
349 if( !item || item == curItem )
350 return;
351
352 bool b_ok;
353 int i_type = item->data( 0, TYPE_ROLE ).toInt( &b_ok );
354 if( !b_ok || i_type == CATEGORY_TYPE )
355 return;
356
357 bool sd_loaded;
358 if( i_type == SD_TYPE )
359 {
360 QString qs = item->data( 0, NAME_ROLE ).toString();
361 sd_loaded = playlist_IsServicesDiscoveryLoaded( THEPL, qtu( qs ) );
362 if( !sd_loaded )
363 {
364 if ( playlist_ServicesDiscoveryAdd( THEPL, qtu( qs ) ) != VLC_SUCCESS )
365 return ;
366
367 services_discovery_descriptor_t test;
368
369 if ( playlist_ServicesDiscoveryControl( THEPL, qtu( qs ),
370 SD_CMD_DESCRIPTOR, &test ) == VLC_SUCCESS )
371 {
372 item->setData( 0, CAP_SEARCH_ROLE, (test.i_capabilities & SD_CAP_SEARCH) );
373 }
374 }
375 }
376
377 curItem = item;
378
379 /* */
380 playlist_Lock( THEPL );
381 playlist_item_t *pl_item = NULL;
382
383 /* Special case for podcast */
384 // FIXME: simplify
385 if( i_type == SD_TYPE )
386 {
387 /* Find the right item for the SD */
388 /* FIXME: searching by name - what could possibly go wrong? */
389 pl_item = playlist_ChildSearchName( &(THEPL->root),
390 vlc_gettext(qtu(item->data(0, LONGNAME_ROLE).toString())) );
391
392 /* Podcasts */
393 if( item->data( 0, SPECIAL_ROLE ).toInt() == IS_PODCAST )
394 {
395 if( pl_item && !sd_loaded )
396 {
397 podcastsParentId = pl_item->i_id;
398 for( int i=0; i < pl_item->i_children; i++ )
399 addPodcastItem( pl_item->pp_children[i] );
400 }
401 pl_item = NULL; //to prevent activating it
402 }
403 }
404 else
405 pl_item = item->data( 0, PL_ITEM_ROLE ).value<playlist_item_t*>();
406
407 playlist_Unlock( THEPL );
408
409 /* */
410 if( pl_item )
411 {
412 emit categoryActivated( pl_item, false );
413 int i_cat = item->data( 0, SD_CATEGORY_ROLE ).toInt();
414 emit SDCategorySelected( i_cat == SD_CAT_INTERNET
415 || i_cat == SD_CAT_LAN );
416 }
417 }
418
addItem(SelectorItemType type,const char * str,bool drop,bool bold,QTreeWidgetItem * parentItem)419 PLSelItem * PLSelector::addItem (
420 SelectorItemType type, const char* str, bool drop, bool bold,
421 QTreeWidgetItem* parentItem )
422 {
423 QTreeWidgetItem *item = parentItem ?
424 new QTreeWidgetItem( parentItem ) : new QTreeWidgetItem( this );
425
426 PLSelItem *selItem = new PLSelItem( item, qtr( str ) );
427 if ( bold ) selItem->setStyleSheet( "font-weight: bold;" );
428 setItemWidget( item, 0, selItem );
429 item->setData( 0, TYPE_ROLE, (int)type );
430 if( !drop ) item->setFlags( item->flags() & ~Qt::ItemIsDropEnabled );
431
432 return selItem;
433 }
434
addPodcastItem(playlist_item_t * p_item)435 PLSelItem *PLSelector::addPodcastItem( playlist_item_t *p_item )
436 {
437 input_item_Hold( p_item->p_input );
438
439 char *psz_name = input_item_GetName( p_item->p_input );
440 PLSelItem *item = addItem( PL_ITEM_TYPE, psz_name, false, false, podcastsParent );
441 free( psz_name );
442
443 item->addAction( RM_ACTION, qtr( "Remove this podcast subscription" ) );
444 item->treeItem()->setData( 0, PL_ITEM_ROLE, QVariant::fromValue( p_item ) );
445 item->treeItem()->setData( 0, PL_ITEM_ID_ROLE, QVariant(p_item->i_id) );
446 item->treeItem()->setData( 0, IN_ITEM_ROLE, QVariant::fromValue( p_item->p_input ) );
447 CONNECT( item, action( PLSelItem* ), this, podcastRemove( PLSelItem* ) );
448 return item;
449 }
450
mimeTypes() const451 QStringList PLSelector::mimeTypes() const
452 {
453 QStringList types;
454 types << "vlc/qt-input-items";
455 return types;
456 }
457
dropMimeData(QTreeWidgetItem * parent,int,const QMimeData * data,Qt::DropAction)458 bool PLSelector::dropMimeData ( QTreeWidgetItem * parent, int,
459 const QMimeData * data, Qt::DropAction )
460 {
461 if( !parent ) return false;
462
463 QVariant type = parent->data( 0, TYPE_ROLE );
464 if( type == QVariant() ) return false;
465
466 int i_truth = parent->data( 0, SPECIAL_ROLE ).toInt();
467 if( i_truth != IS_PL && i_truth != IS_ML ) return false;
468
469 bool to_pl = ( i_truth == IS_PL );
470
471 const PlMimeData *plMimeData = qobject_cast<const PlMimeData*>( data );
472 if( !plMimeData ) return false;
473
474 QList<input_item_t*> inputItems = plMimeData->inputItems();
475
476 playlist_Lock( THEPL );
477
478 foreach( input_item_t *p_input, inputItems )
479 {
480 playlist_item_t *p_item = playlist_ItemGetByInput( THEPL, p_input );
481 if( !p_item ) continue;
482
483 playlist_NodeAddCopy( THEPL, p_item,
484 to_pl ? THEPL->p_playing : THEPL->p_media_library,
485 PLAYLIST_END );
486 }
487
488 playlist_Unlock( THEPL );
489
490 return true;
491 }
492
dragMoveEvent(QDragMoveEvent * event)493 void PLSelector::dragMoveEvent ( QDragMoveEvent * event )
494 {
495 event->setDropAction( Qt::CopyAction );
496 QAbstractItemView::dragMoveEvent( event );
497 }
498
plItemAdded(int item,int parent)499 void PLSelector::plItemAdded( int item, int parent )
500 {
501 updateTotalDuration(playlistItem, "Playlist");
502 if( parent != podcastsParentId || podcastsParent == NULL ) return;
503
504 playlist_Lock( THEPL );
505
506 playlist_item_t *p_item = playlist_ItemGetById( THEPL, item );
507 if( !p_item ) {
508 playlist_Unlock( THEPL );
509 return;
510 }
511
512 int c = podcastsParent->childCount();
513 for( int i = 0; i < c; i++ )
514 {
515 QTreeWidgetItem *podItem = podcastsParent->child(i);
516 if( podItem->data( 0, PL_ITEM_ID_ROLE ).toInt() == item )
517 {
518 //msg_Dbg( p_intf, "Podcast already in: (%d) %s", item, p_item->p_input->psz_uri);
519 playlist_Unlock( THEPL );
520 return;
521 }
522 }
523
524 //msg_Dbg( p_intf, "Adding podcast: (%d) %s", item, p_item->p_input->psz_uri );
525 addPodcastItem( p_item );
526
527 playlist_Unlock( THEPL );
528
529 podcastsParent->setExpanded( true );
530 }
531
plItemRemoved(int id)532 void PLSelector::plItemRemoved( int id )
533 {
534 updateTotalDuration(playlistItem, "Playlist");
535 if( !podcastsParent ) return;
536
537 int c = podcastsParent->childCount();
538 for( int i = 0; i < c; i++ )
539 {
540 QTreeWidgetItem *item = podcastsParent->child(i);
541 if( item->data( 0, PL_ITEM_ID_ROLE ).toInt() == id )
542 {
543 input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
544 //msg_Dbg( p_intf, "Removing podcast: (%d) %s", id, p_input->psz_uri );
545 input_item_Release( p_input );
546 delete item;
547 return;
548 }
549 }
550 }
551
inputItemUpdate(input_item_t * arg)552 void PLSelector::inputItemUpdate( input_item_t *arg )
553 {
554 updateTotalDuration(playlistItem, "Playlist");
555
556 if( podcastsParent == NULL )
557 return;
558
559 int c = podcastsParent->childCount();
560 for( int i = 0; i < c; i++ )
561 {
562 QTreeWidgetItem *item = podcastsParent->child(i);
563 input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
564 if( p_input == arg )
565 {
566 PLSelItem *si = itemWidget( item );
567 char *psz_name = input_item_GetName( p_input );
568 si->setText( qfu( psz_name ) );
569 free( psz_name );
570 return;
571 }
572 }
573 }
574
podcastAdd(PLSelItem *)575 void PLSelector::podcastAdd( PLSelItem * )
576 {
577 assert( podcastsParent );
578
579 bool ok;
580 QString url = QInputDialog::getText( this, qtr( "Subscribe" ),
581 qtr( "Enter URL of the podcast to subscribe to:" ),
582 QLineEdit::Normal, QString(), &ok );
583 if( !ok || url.isEmpty() ) return;
584
585 setSource( podcastsParent ); //to load the SD in case it's not loaded
586
587 QString request("ADD:");
588 request += url.trimmed();
589 var_SetString( THEPL, "podcast-request", qtu( request ) );
590 }
591
podcastRemove(PLSelItem * item)592 void PLSelector::podcastRemove( PLSelItem* item )
593 {
594 QString question ( qtr( "Do you really want to unsubscribe from %1?" ) );
595 question = question.arg( item->text() );
596 QMessageBox::StandardButton res =
597 QMessageBox::question( this, qtr( "Unsubscribe" ), question,
598 QMessageBox::Yes | QMessageBox::No,
599 QMessageBox::No );
600 if( res == QMessageBox::No ) return;
601
602 input_item_t *input = item->treeItem()->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
603 if( !input ) return;
604
605 QString request("RM:");
606 char *psz_uri = input_item_GetURI( input );
607 request += qfu( psz_uri );
608 var_SetString( THEPL, "podcast-request", qtu( request ) );
609 free( psz_uri );
610 }
611
itemWidget(QTreeWidgetItem * item)612 PLSelItem * PLSelector::itemWidget( QTreeWidgetItem *item )
613 {
614 return ( static_cast<PLSelItem*>( QTreeWidget::itemWidget( item, 0 ) ) );
615 }
616
drawBranches(QPainter * painter,const QRect & rect,const QModelIndex & index) const617 void PLSelector::drawBranches ( QPainter * painter, const QRect & rect, const QModelIndex & index ) const
618 {
619 if( !model()->hasChildren( index ) ) return;
620 QStyleOption option;
621 option.initFrom( this );
622 option.rect = rect.adjusted( rect.width() - indentation(), 0, 0, 0 );
623 style()->drawPrimitive( isExpanded( index ) ?
624 QStyle::PE_IndicatorArrowDown :
625 QStyle::PE_IndicatorArrowRight, &option, painter );
626 }
627
getCurrentItemInfos(int * type,bool * can_delay_search,QString * string)628 void PLSelector::getCurrentItemInfos( int* type, bool* can_delay_search, QString *string)
629 {
630 *type = currentItem()->data( 0, TYPE_ROLE ).toInt();
631 *string = currentItem()->data( 0, NAME_ROLE ).toString();
632 *can_delay_search = currentItem()->data( 0, CAP_SEARCH_ROLE ).toBool();
633 }
634
getCurrentItemCategory()635 int PLSelector::getCurrentItemCategory()
636 {
637 return currentItem()->data( 0, SPECIAL_ROLE ).toInt();
638 }
639
wheelEvent(QWheelEvent * e)640 void PLSelector::wheelEvent( QWheelEvent *e )
641 {
642 if( verticalScrollBar()->isVisible() && (
643 (verticalScrollBar()->value() != verticalScrollBar()->minimum() && e->delta() >= 0 ) ||
644 (verticalScrollBar()->value() != verticalScrollBar()->maximum() && e->delta() < 0 )
645 ) )
646 QApplication::sendEvent(verticalScrollBar(), e);
647
648 // Accept this event in order to prevent unwanted volume up/down changes
649 e->accept();
650 }
651