1 /*****************************************************************************
2 * plugins.cpp : Plug-ins and extensions listing
3 ****************************************************************************
4 * Copyright (C) 2008-2010 the VideoLAN team
7 * Authors: Jean-Baptiste Kempf <jb (at) videolan.org>
8 * Jean-Philippe André <jpeg (at) 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 "plugins.hpp"
31 #include "util/searchlineedit.hpp"
32 #include "extensions_manager.hpp"
36 #include <vlc_modules.h>
38 #include <QTreeWidget>
39 #include <QStringList>
41 #include <QHeaderView>
42 #include <QDialogButtonBox>
45 #include <QVBoxLayout>
47 #include <QHBoxLayout>
48 #include <QVBoxLayout>
49 #include <QSpacerItem>
52 #include <QStyleOptionViewItem>
54 #include <QPushButton>
57 static QPixmap
*loadPixmapFromData( char *, int size
);
60 PluginDialog::PluginDialog( intf_thread_t
*_p_intf
) : QVLCFrame( _p_intf
)
62 setWindowTitle( qtr( "Plugins and extensions" ) );
63 setWindowRole( "vlc-plugins" );
65 QVBoxLayout
*layout
= new QVBoxLayout( this );
66 tabs
= new QTabWidget( this );
67 tabs
->addTab( extensionTab
= new ExtensionTab( p_intf
),
68 qtr( "Extensions" ) );
69 tabs
->addTab( pluginTab
= new PluginTab( p_intf
),
71 layout
->addWidget( tabs
);
73 QDialogButtonBox
*box
= new QDialogButtonBox
;
74 QPushButton
*okButton
= new QPushButton( qtr( "&Close" ), this );
75 box
->addButton( okButton
, QDialogButtonBox::AcceptRole
);
76 layout
->addWidget( box
);
77 BUTTONACT( okButton
, close() );
78 readSettings( "PluginsDialog", QSize( 435, 280 ) );
81 PluginDialog::~PluginDialog()
83 writeSettings( "PluginsDialog" );
88 PluginTab::PluginTab( intf_thread_t
*p_intf
)
91 QGridLayout
*layout
= new QGridLayout( this );
93 /* Main Tree for modules */
94 treePlugins
= new QTreeWidget
;
95 layout
->addWidget( treePlugins
, 0, 0, 1, -1 );
97 /* Users cannot move the columns around but we need to sort */
98 treePlugins
->header()->setMovable( false );
99 treePlugins
->header()->setSortIndicatorShown( true );
100 // treePlugins->header()->setResizeMode( QHeaderView::ResizeToContents );
101 treePlugins
->setAlternatingRowColors( true );
102 treePlugins
->setColumnWidth( 0, 200 );
104 QStringList headerNames
;
105 headerNames
<< qtr("Name") << qtr("Capability" ) << qtr( "Score" );
106 treePlugins
->setHeaderLabels( headerNames
);
110 /* Set capability column to the correct Size*/
111 treePlugins
->resizeColumnToContents( 1 );
112 treePlugins
->header()->restoreState(
113 getSettings()->value( "Plugins/Header-State" ).toByteArray() );
115 treePlugins
->setSortingEnabled( true );
116 treePlugins
->sortByColumn( 1, Qt::AscendingOrder
);
118 QLabel
*label
= new QLabel( qtr("&Search:"), this );
119 edit
= new SearchLineEdit( this );
120 label
->setBuddy( edit
);
122 layout
->addWidget( label
, 1, 0 );
123 layout
->addWidget( edit
, 1, 1, 1, 1 );
124 CONNECT( edit
, textChanged( const QString
& ),
125 this, search( const QString
& ) );
127 setMinimumSize( 500, 300 );
128 readSettings( "Plugins", QSize( 540, 400 ) );
131 inline void PluginTab::FillTree()
133 module_t
**p_list
= module_list_get( NULL
);
136 for( unsigned int i
= 0; (p_module
= p_list
[i
] ) != NULL
; i
++ )
139 qs_item
<< qfu( module_get_name( p_module
, true ) )
140 << qfu( module_get_capability( p_module
) )
141 << QString::number( module_get_score( p_module
) );
143 if( qs_item
.at(1).isEmpty() ) continue;
146 QTreeWidgetItem
*item
= new PluginTreeItem( qs_item
);
147 treePlugins
->addTopLevelItem( item
);
149 module_list_free( p_list
);
152 void PluginTab::search( const QString
& qs
)
154 QList
<QTreeWidgetItem
*> items
= treePlugins
->findItems( qs
, Qt::MatchContains
);
155 items
+= treePlugins
->findItems( qs
, Qt::MatchContains
, 1 );
157 QTreeWidgetItem
*item
= NULL
;
158 for( int i
= 0; i
< treePlugins
->topLevelItemCount(); i
++ )
160 item
= treePlugins
->topLevelItem( i
);
161 item
->setHidden( !items
.contains( item
) );
165 PluginTab::~PluginTab()
167 writeSettings( "Plugins" );
168 getSettings()->setValue( "Plugins/Header-State",
169 treePlugins
->header()->saveState() );
172 void PluginTab::keyPressEvent( QKeyEvent
*keyEvent
)
174 if( keyEvent
->key() == Qt::Key_Return
||
175 keyEvent
->key() == Qt::Key_Enter
)
181 bool PluginTreeItem::operator< ( const QTreeWidgetItem
& other
) const
183 int col
= treeWidget()->sortColumn();
185 return text( col
).toInt() < other
.text( col
).toInt();
186 return text( col
) < other
.text( col
);
190 ExtensionTab::ExtensionTab( intf_thread_t
*p_intf
)
191 : QVLCFrame( p_intf
)
194 QVBoxLayout
*layout
= new QVBoxLayout( this );
196 QLabel
*notice
= new QLabel( qtr("Get more extensions from")
197 + QString( " <a href=\"http://addons.videolan.org/\">"
198 "addons.videolan.org</a>" ) );
199 notice
->setOpenExternalLinks( true );
200 layout
->addWidget( notice
);
203 extList
= new QListView( this );
204 CONNECT( extList
, activated( const QModelIndex
& ),
205 this, moreInformation() );
206 layout
->addWidget( extList
);
208 // List item delegate
209 ExtensionItemDelegate
*itemDelegate
= new ExtensionItemDelegate( p_intf
,
211 extList
->setItemDelegate( itemDelegate
);
213 // Extension list look & feeling
214 extList
->setAlternatingRowColors( true );
215 extList
->setSelectionMode( QAbstractItemView::SingleSelection
);
218 ExtensionListModel
*model
= new ExtensionListModel( extList
, p_intf
);
219 extList
->setModel( model
);
222 QHBoxLayout
*hbox
= new QHBoxLayout
;
223 hbox
->addItem( new QSpacerItem( 1, 1, QSizePolicy::Expanding
,
224 QSizePolicy::Fixed
) );
226 // More information button
227 butMoreInfo
= new QPushButton( QIcon( ":/menu/info" ),
228 qtr( "More information..." ),
230 CONNECT( butMoreInfo
, clicked(), this, moreInformation() );
231 hbox
->addWidget( butMoreInfo
);
234 ExtensionsManager
*EM
= ExtensionsManager::getInstance( p_intf
);
235 QPushButton
*reload
= new QPushButton( QIcon( ":/update" ),
236 qtr( "Reload extensions" ),
238 CONNECT( reload
, clicked(), EM
, reloadExtensions() );
239 CONNECT( reload
, clicked(), this, updateButtons() );
240 CONNECT( extList
->selectionModel(),
241 selectionChanged( const QItemSelection
&, const QItemSelection
& ),
244 hbox
->addWidget( reload
);
247 layout
->addItem( hbox
);
251 ExtensionTab::~ExtensionTab()
255 void ExtensionTab::updateButtons()
257 butMoreInfo
->setEnabled( extList
->selectionModel()->hasSelection() );
260 // Do not close on ESC or ENTER
261 void ExtensionTab::keyPressEvent( QKeyEvent
*keyEvent
)
263 if( keyEvent
->key() == Qt::Key_Return
||
264 keyEvent
->key() == Qt::Key_Enter
)
270 // Show more information
271 void ExtensionTab::moreInformation()
273 QModelIndex index
= extList
->selectionModel()->selectedIndexes().first();
274 ExtensionCopy
*ext
= (ExtensionCopy
*) index
.internalPointer();
278 ExtensionInfoDialog
dlg( *ext
, p_intf
, this );
282 /* Safe copy of the extension_t struct */
286 ExtensionCopy( extension_t
*p_ext
)
288 name
= qfu( p_ext
->psz_name
);
289 description
= qfu( p_ext
->psz_description
);
290 shortdesc
= qfu( p_ext
->psz_shortdescription
);
291 if( description
.isEmpty() )
292 description
= shortdesc
;
293 if( shortdesc
.isEmpty() && !description
.isEmpty() )
294 shortdesc
= description
;
295 title
= qfu( p_ext
->psz_title
);
296 author
= qfu( p_ext
->psz_author
);
297 version
= qfu( p_ext
->psz_version
);
298 url
= qfu( p_ext
->psz_url
);
299 icon
= loadPixmapFromData( p_ext
->p_icondata
, p_ext
->i_icondata_size
);
303 QString name
, title
, description
, shortdesc
, author
, version
, url
;
307 /* Extensions list model for the QListView */
309 ExtensionListModel::ExtensionListModel( QListView
*view
, intf_thread_t
*intf
)
310 : QAbstractListModel( view
), p_intf( intf
)
312 // Connect to ExtensionsManager::extensionsUpdated()
313 ExtensionsManager
* EM
= ExtensionsManager::getInstance( p_intf
);
314 CONNECT( EM
, extensionsUpdated(), this, updateList() );
316 // Load extensions now if not already loaded
317 EM
->loadExtensions();
320 ExtensionListModel::~ExtensionListModel()
322 // Clear extensions list
323 while( !extensions
.isEmpty() )
324 delete extensions
.takeLast();
327 void ExtensionListModel::updateList()
331 // Clear extensions list
332 while( !extensions
.isEmpty() )
334 ext
= extensions
.takeLast();
338 // Find new extensions
339 ExtensionsManager
*EM
= ExtensionsManager::getInstance( p_intf
);
340 extensions_manager_t
*p_mgr
= EM
->getManager();
344 vlc_mutex_lock( &p_mgr
->lock
);
346 FOREACH_ARRAY( p_ext
, p_mgr
->extensions
)
348 ext
= new ExtensionCopy( p_ext
);
349 extensions
.append( ext
);
352 vlc_mutex_unlock( &p_mgr
->lock
);
353 vlc_object_release( p_mgr
);
355 emit
dataChanged( index( 0 ), index( rowCount() - 1 ) );
358 int ExtensionListModel::rowCount( const QModelIndex
& ) const
361 ExtensionsManager
*EM
= ExtensionsManager::getInstance( p_intf
);
362 extensions_manager_t
*p_mgr
= EM
->getManager();
366 vlc_mutex_lock( &p_mgr
->lock
);
367 count
= p_mgr
->extensions
.i_size
;
368 vlc_mutex_unlock( &p_mgr
->lock
);
369 vlc_object_release( p_mgr
);
374 QVariant
ExtensionListModel::data( const QModelIndex
& index
, int role
) const
376 if( !index
.isValid() )
386 QModelIndex
ExtensionListModel::index( int row
, int column
,
387 const QModelIndex
& ) const
390 return QModelIndex();
391 if( row
< 0 || row
>= extensions
.count() )
392 return QModelIndex();
394 return createIndex( row
, 0, extensions
.at( row
) );
398 /* Extension List Widget Item */
399 ExtensionItemDelegate::ExtensionItemDelegate( intf_thread_t
*p_intf
,
401 : QStyledItemDelegate( view
), view( view
), p_intf( p_intf
)
405 ExtensionItemDelegate::~ExtensionItemDelegate()
409 void ExtensionItemDelegate::paint( QPainter
*painter
,
410 const QStyleOptionViewItem
&option
,
411 const QModelIndex
&index
) const
413 ExtensionCopy
*ext
= ( ExtensionCopy
* ) index
.internalPointer();
414 assert( ext
!= NULL
);
416 int width
= option
.rect
.width();
418 // Pixmap: buffer where to draw
419 QPixmap
pix(option
.rect
.size());
422 pix
.fill( Qt::transparent
); // FIXME
424 // ItemView primitive style
425 QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem
,
429 // Painter on the pixmap
430 QPainter
*pixpaint
= new QPainter(&pix
);
433 QFont font
= painter
->font();
434 QPen pen
= painter
->pen();
435 if( view
->selectionModel()->selectedIndexes().contains( index
) )
437 pen
.setBrush( option
.palette
.highlightedText() );
441 pen
.setBrush( option
.palette
.text() );
443 pixpaint
->setPen( pen
);
444 QFontMetrics metrics
= option
.fontMetrics
;
447 if( ext
->icon
!= NULL
)
449 pixpaint
->drawPixmap( 7, 7, 2*metrics
.height(), 2*metrics
.height(),
454 pixpaint
->setRenderHint( QPainter::TextAntialiasing
);
455 font
.setBold( true );
456 pixpaint
->setFont( font
);
457 pixpaint
->drawText( QRect( 17 + 2 * metrics
.height(), 7,
458 width
- 40 - 2 * metrics
.height(),
460 Qt::AlignLeft
, ext
->title
);
462 // Short description: normal
463 font
.setBold( false );
464 pixpaint
->setFont( font
);
465 pixpaint
->drawText( QRect( 17 + 2 * metrics
.height(),
466 7 + metrics
.height(), width
- 40,
468 Qt::AlignLeft
, ext
->shortdesc
);
470 // Flush paint operations
473 // Draw it on the screen!
474 painter
->drawPixmap( option
.rect
, pix
);
477 QSize
ExtensionItemDelegate::sizeHint( const QStyleOptionViewItem
&option
,
478 const QModelIndex
&index
) const
480 if (index
.isValid() && index
.column() == 0)
482 QFontMetrics metrics
= option
.fontMetrics
;
483 return QSize( 200, 14 + 2 * metrics
.height() );
489 /* "More information" dialog */
491 ExtensionInfoDialog::ExtensionInfoDialog( const ExtensionCopy
& extension
,
492 intf_thread_t
*p_intf
,
494 : QVLCDialog( parent
, p_intf
),
495 extension( new ExtensionCopy( extension
) )
497 // Let's be a modal dialog
498 setWindowModality( Qt::WindowModal
);
501 setWindowTitle( qtr( "About" ) + " " + extension
.title
);
504 QGridLayout
*layout
= new QGridLayout( this );
507 QLabel
*icon
= new QLabel( this );
508 if( !extension
.icon
)
510 QPixmap
pix( ":/logo/vlc48.png" );
511 icon
->setPixmap( pix
);
515 icon
->setPixmap( *extension
.icon
);
517 icon
->setAlignment( Qt::AlignCenter
);
518 icon
->setFixedSize( 48, 48 );
519 layout
->addWidget( icon
, 1, 0, 2, 1 );
522 QLabel
*label
= new QLabel( extension
.title
, this );
523 QFont font
= label
->font();
524 font
.setBold( true );
525 font
.setPointSizeF( font
.pointSizeF() * 1.3f
);
526 label
->setFont( font
);
527 layout
->addWidget( label
, 0, 0, 1, -1 );
530 label
= new QLabel( "<b>" + qtr( "Version" ) + ":</b>", this );
531 layout
->addWidget( label
, 1, 1, 1, 1, Qt::AlignBottom
);
532 label
= new QLabel( extension
.version
, this );
533 layout
->addWidget( label
, 1, 2, 1, 2, Qt::AlignBottom
);
536 label
= new QLabel( "<b>" + qtr( "Author" ) + ":</b>", this );
537 layout
->addWidget( label
, 2, 1, 1, 1, Qt::AlignTop
);
538 label
= new QLabel( extension
.author
, this );
539 layout
->addWidget( label
, 2, 2, 1, 2, Qt::AlignTop
);
543 label
= new QLabel( this );
544 label
->setText( extension
.description
);
545 label
->setWordWrap( true );
546 label
->setOpenExternalLinks( true );
547 layout
->addWidget( label
, 4, 0, 1, -1 );
550 label
= new QLabel( "<b>" + qtr( "Website" ) + ":</b>", this );
551 layout
->addWidget( label
, 5, 0, 1, 2 );
552 QString txt
= "<a href=\"";
553 txt
+= extension
.url
;
555 txt
+= extension
.url
;
557 label
= new QLabel( txt
, this );
558 label
->setText( txt
);
559 label
->setOpenExternalLinks( true );
560 layout
->addWidget( label
, 5, 2, 1, -1 );
563 label
= new QLabel( "<b>" + qtr( "File" ) + ":</b>", this );
564 layout
->addWidget( label
, 6, 0, 1, 2 );
565 QLineEdit
*line
= new QLineEdit( extension
.name
, this );
566 layout
->addWidget( line
, 6, 2, 1, -1 );
569 QDialogButtonBox
*group
= new QDialogButtonBox( this );
570 QPushButton
*closeButton
= new QPushButton( qtr( "&Close" ) );
571 group
->addButton( closeButton
, QDialogButtonBox::AcceptRole
);
572 BUTTONACT( closeButton
, close() );
574 layout
->addWidget( group
, 7, 0, 1, -1 );
577 layout
->setColumnStretch( 2, 1 );
578 layout
->setRowStretch( 4, 1 );
579 setMinimumSize( 450, 350 );
582 ExtensionInfoDialog::~ExtensionInfoDialog()
587 static QPixmap
*loadPixmapFromData( char *data
, int size
)
589 if( !data
|| size
<= 0 )
591 QPixmap
*pixmap
= new QPixmap();
592 if( !pixmap
->loadFromData( (const uchar
*) data
, (uint
) size
) )