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(),
231 this, moreInformation() );
232 hbox
->addWidget( butMoreInfo
);
235 ExtensionsManager
*EM
= ExtensionsManager::getInstance( p_intf
);
236 QPushButton
*reload
= new QPushButton( QIcon( ":/update" ),
237 qtr( "Reload extensions" ),
239 CONNECT( reload
, clicked(),
240 EM
, reloadExtensions() );
241 hbox
->addWidget( reload
);
244 layout
->addItem( hbox
);
247 ExtensionTab::~ExtensionTab()
251 // Do not close on ESC or ENTER
252 void ExtensionTab::keyPressEvent( QKeyEvent
*keyEvent
)
254 if( keyEvent
->key() == Qt::Key_Return
||
255 keyEvent
->key() == Qt::Key_Enter
)
261 // Show more information
262 void ExtensionTab::moreInformation()
264 if( !extList
->selectionModel() ||
265 extList
->selectionModel()->selectedIndexes().isEmpty() )
271 QModelIndex index
= extList
->selectionModel()->selectedIndexes().first();
272 ExtensionCopy
*ext
= (ExtensionCopy
*) index
.internalPointer();
276 ExtensionInfoDialog
dlg( *ext
, p_intf
, this );
280 /* Safe copy of the extension_t struct */
284 ExtensionCopy( extension_t
*p_ext
)
286 name
= qfu( p_ext
->psz_name
);
287 description
= qfu( p_ext
->psz_description
);
288 shortdesc
= qfu( p_ext
->psz_shortdescription
);
289 if( description
.isEmpty() )
290 description
= shortdesc
;
291 if( shortdesc
.isEmpty() && !description
.isEmpty() )
292 shortdesc
= description
;
293 title
= qfu( p_ext
->psz_title
);
294 author
= qfu( p_ext
->psz_author
);
295 version
= qfu( p_ext
->psz_version
);
296 url
= qfu( p_ext
->psz_url
);
297 icon
= loadPixmapFromData( p_ext
->p_icondata
, p_ext
->i_icondata_size
);
301 QString name
, title
, description
, shortdesc
, author
, version
, url
;
305 /* Extensions list model for the QListView */
307 ExtensionListModel::ExtensionListModel( QListView
*view
, intf_thread_t
*intf
)
308 : QAbstractListModel( view
), p_intf( intf
)
310 // Connect to ExtensionsManager::extensionsUpdated()
311 ExtensionsManager
* EM
= ExtensionsManager::getInstance( p_intf
);
312 CONNECT( EM
, extensionsUpdated(), this, updateList() );
314 // Load extensions now if not already loaded
315 EM
->loadExtensions();
318 ExtensionListModel::~ExtensionListModel()
320 // Clear extensions list
321 while( !extensions
.isEmpty() )
322 delete extensions
.takeLast();
325 void ExtensionListModel::updateList()
329 // Clear extensions list
330 while( !extensions
.isEmpty() )
332 ext
= extensions
.takeLast();
336 // Find new extensions
337 ExtensionsManager
*EM
= ExtensionsManager::getInstance( p_intf
);
338 extensions_manager_t
*p_mgr
= EM
->getManager();
342 vlc_mutex_lock( &p_mgr
->lock
);
344 FOREACH_ARRAY( p_ext
, p_mgr
->extensions
)
346 ext
= new ExtensionCopy( p_ext
);
347 extensions
.append( ext
);
350 vlc_mutex_unlock( &p_mgr
->lock
);
351 vlc_object_release( p_mgr
);
353 emit
dataChanged( index( 0 ), index( rowCount() - 1 ) );
356 int ExtensionListModel::rowCount( const QModelIndex
& ) const
359 ExtensionsManager
*EM
= ExtensionsManager::getInstance( p_intf
);
360 extensions_manager_t
*p_mgr
= EM
->getManager();
364 vlc_mutex_lock( &p_mgr
->lock
);
365 count
= p_mgr
->extensions
.i_size
;
366 vlc_mutex_unlock( &p_mgr
->lock
);
367 vlc_object_release( p_mgr
);
372 QVariant
ExtensionListModel::data( const QModelIndex
& index
, int role
) const
374 if( !index
.isValid() )
384 QModelIndex
ExtensionListModel::index( int row
, int column
,
385 const QModelIndex
& ) const
388 return QModelIndex();
389 if( row
< 0 || row
>= extensions
.count() )
390 return QModelIndex();
392 return createIndex( row
, 0, extensions
.at( row
) );
396 /* Extension List Widget Item */
397 ExtensionItemDelegate::ExtensionItemDelegate( intf_thread_t
*p_intf
,
399 : QStyledItemDelegate( view
), view( view
), p_intf( p_intf
)
403 ExtensionItemDelegate::~ExtensionItemDelegate()
407 void ExtensionItemDelegate::paint( QPainter
*painter
,
408 const QStyleOptionViewItem
&option
,
409 const QModelIndex
&index
) const
411 ExtensionCopy
*ext
= ( ExtensionCopy
* ) index
.internalPointer();
412 assert( ext
!= NULL
);
414 int width
= option
.rect
.width();
416 // Pixmap: buffer where to draw
417 QPixmap
pix(option
.rect
.size());
420 pix
.fill( Qt::transparent
); // FIXME
422 // ItemView primitive style
423 QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem
,
427 // Painter on the pixmap
428 QPainter
*pixpaint
= new QPainter(&pix
);
431 QFont font
= painter
->font();
432 QPen pen
= painter
->pen();
433 if( view
->selectionModel()->selectedIndexes().contains( index
) )
435 pen
.setBrush( option
.palette
.highlightedText() );
439 pen
.setBrush( option
.palette
.text() );
441 pixpaint
->setPen( pen
);
442 QFontMetrics metrics
= option
.fontMetrics
;
445 if( ext
->icon
!= NULL
)
447 pixpaint
->drawPixmap( 7, 7, 2*metrics
.height(), 2*metrics
.height(),
452 pixpaint
->setRenderHint( QPainter::TextAntialiasing
);
453 font
.setBold( true );
454 pixpaint
->setFont( font
);
455 pixpaint
->drawText( QRect( 17 + 2 * metrics
.height(), 7,
456 width
- 40 - 2 * metrics
.height(),
458 Qt::AlignLeft
, ext
->title
);
460 // Short description: normal
461 font
.setBold( false );
462 pixpaint
->setFont( font
);
463 pixpaint
->drawText( QRect( 17 + 2 * metrics
.height(),
464 7 + metrics
.height(), width
- 40,
466 Qt::AlignLeft
, ext
->shortdesc
);
468 // Flush paint operations
471 // Draw it on the screen!
472 painter
->drawPixmap( option
.rect
, pix
);
475 QSize
ExtensionItemDelegate::sizeHint( const QStyleOptionViewItem
&option
,
476 const QModelIndex
&index
) const
478 if (index
.isValid() && index
.column() == 0)
480 QFontMetrics metrics
= option
.fontMetrics
;
481 return QSize( 200, 14 + 2 * metrics
.height() );
487 /* "More information" dialog */
489 ExtensionInfoDialog::ExtensionInfoDialog( const ExtensionCopy
& extension
,
490 intf_thread_t
*p_intf
,
492 : QVLCDialog( parent
, p_intf
),
493 extension( new ExtensionCopy( extension
) )
495 // Let's be a modal dialog
496 setWindowModality( Qt::WindowModal
);
499 setWindowTitle( qtr( "About" ) + " " + extension
.title
);
502 QGridLayout
*layout
= new QGridLayout( this );
505 QLabel
*icon
= new QLabel( this );
506 if( !extension
.icon
)
508 QPixmap
pix( ":/logo/vlc48.png" );
509 icon
->setPixmap( pix
);
513 icon
->setPixmap( *extension
.icon
);
515 icon
->setAlignment( Qt::AlignCenter
);
516 icon
->setFixedSize( 48, 48 );
517 layout
->addWidget( icon
, 1, 0, 2, 1 );
520 QLabel
*label
= new QLabel( extension
.title
, this );
521 QFont font
= label
->font();
522 font
.setBold( true );
523 font
.setPointSizeF( font
.pointSizeF() * 1.3f
);
524 label
->setFont( font
);
525 layout
->addWidget( label
, 0, 0, 1, -1 );
528 label
= new QLabel( "<b>" + qtr( "Version" ) + ":</b>", this );
529 layout
->addWidget( label
, 1, 1, 1, 1, Qt::AlignBottom
);
530 label
= new QLabel( extension
.version
, this );
531 layout
->addWidget( label
, 1, 2, 1, 2, Qt::AlignBottom
);
534 label
= new QLabel( "<b>" + qtr( "Author" ) + ":</b>", this );
535 layout
->addWidget( label
, 2, 1, 1, 1, Qt::AlignTop
);
536 label
= new QLabel( extension
.author
, this );
537 layout
->addWidget( label
, 2, 2, 1, 2, Qt::AlignTop
);
541 label
= new QLabel( this );
542 label
->setText( extension
.description
);
543 label
->setWordWrap( true );
544 label
->setOpenExternalLinks( true );
545 layout
->addWidget( label
, 4, 0, 1, -1 );
548 label
= new QLabel( "<b>" + qtr( "Website" ) + ":</b>", this );
549 layout
->addWidget( label
, 5, 0, 1, 2 );
550 QString txt
= "<a href=\"";
551 txt
+= extension
.url
;
553 txt
+= extension
.url
;
555 label
= new QLabel( txt
, this );
556 label
->setText( txt
);
557 label
->setOpenExternalLinks( true );
558 layout
->addWidget( label
, 5, 2, 1, -1 );
561 label
= new QLabel( "<b>" + qtr( "File" ) + ":</b>", this );
562 layout
->addWidget( label
, 6, 0, 1, 2 );
563 QLineEdit
*line
= new QLineEdit( extension
.name
, this );
564 layout
->addWidget( line
, 6, 2, 1, -1 );
567 QDialogButtonBox
*group
= new QDialogButtonBox( this );
568 QPushButton
*closeButton
= new QPushButton( qtr( "&Close" ) );
569 group
->addButton( closeButton
, QDialogButtonBox::AcceptRole
);
570 BUTTONACT( closeButton
, close() );
572 layout
->addWidget( group
, 7, 0, 1, -1 );
575 layout
->setColumnStretch( 2, 1 );
576 layout
->setRowStretch( 4, 1 );
577 setMinimumSize( 450, 350 );
580 ExtensionInfoDialog::~ExtensionInfoDialog()
585 static QPixmap
*loadPixmapFromData( char *data
, int size
)
587 if( !data
|| size
<= 0 )
589 QPixmap
*pixmap
= new QPixmap();
590 if( !pixmap
->loadFromData( (const uchar
*) data
, (uint
) size
) )