Extensions/Qt: information panel for Extensions
[vlc.git] / modules / gui / qt4 / dialogs / plugins.cpp
1 /*****************************************************************************
2  * plugins.hpp : Plug-ins and extensions listing
3  ****************************************************************************
4  * Copyright (C) 2008-2010 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Baptiste Kempf <jb (at) videolan.org>
8  *          Jean-Philippe AndrĂ© <jpeg (at) videolan.org>
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 "plugins.hpp"
30
31 #include "util/customwidgets.hpp"
32 #include "extensions_manager.hpp"
33
34 //#include <vlc_modules.h>
35
36 #include <QTreeWidget>
37 #include <QStringList>
38 #include <QTabWidget>
39 #include <QHeaderView>
40 #include <QDialogButtonBox>
41 #include <QLineEdit>
42 #include <QLabel>
43 #include <QVBoxLayout>
44 #include <QComboBox>
45 #include <QTextBrowser>
46 #include <QHBoxLayout>
47 #include <QSpacerItem>
48
49 PluginDialog::PluginDialog( intf_thread_t *_p_intf ) : QVLCFrame( _p_intf )
50 {
51     setWindowTitle( qtr( "Plugins and extensions" ) );
52     setWindowRole( "vlc-plugins" );
53
54     QVBoxLayout *layout = new QVBoxLayout( this );
55     tabs = new QTabWidget( this );
56     tabs->addTab( extensionTab = new ExtensionTab( p_intf ),
57                   qtr( "Extensions" ) );
58     tabs->addTab( pluginTab = new PluginTab( p_intf ),
59                   qtr( "Plugins" ) );
60     layout->addWidget( tabs );
61
62     QDialogButtonBox *box = new QDialogButtonBox;
63     QPushButton *okButton = new QPushButton( qtr( "&Close" ), this );
64     box->addButton( okButton, QDialogButtonBox::AcceptRole );
65     layout->addWidget( box );
66     BUTTONACT( okButton, close() );
67 }
68
69 PluginDialog::~PluginDialog()
70 {
71 }
72
73 /* Plugins tab */
74
75 PluginTab::PluginTab( intf_thread_t *p_intf )
76         : QVLCFrame( p_intf )
77 {
78     QGridLayout *layout = new QGridLayout( this );
79
80     /* Main Tree for modules */
81     treePlugins = new QTreeWidget;
82     layout->addWidget( treePlugins, 0, 0, 1, -1 );
83
84     /* Users cannot move the columns around but we need to sort */
85     treePlugins->header()->setMovable( false );
86     treePlugins->header()->setSortIndicatorShown( true );
87     //    treePlugins->header()->setResizeMode( QHeaderView::ResizeToContents );
88     treePlugins->setAlternatingRowColors( true );
89     treePlugins->setColumnWidth( 0, 200 );
90
91     QStringList headerNames;
92     headerNames << qtr("Name") << qtr("Capability" ) << qtr( "Score" );
93     treePlugins->setHeaderLabels( headerNames );
94
95     FillTree();
96
97     /* Set capability column to the correct Size*/
98     treePlugins->resizeColumnToContents( 1 );
99     treePlugins->header()->restoreState(
100             getSettings()->value( "Plugins/Header-State" ).toByteArray() );
101
102     treePlugins->setSortingEnabled( true );
103     treePlugins->sortByColumn( 1, Qt::AscendingOrder );
104
105     QLabel *label = new QLabel( qtr("&Search:"), this );
106     edit = new SearchLineEdit( this );
107     label->setBuddy( edit );
108
109     layout->addWidget( label, 1, 0 );
110     layout->addWidget( edit, 1, 1, 1, 1 );
111     CONNECT( edit, textChanged( const QString& ),
112             this, search( const QString& ) );
113
114     setMinimumSize( 500, 300 );
115     readSettings( "Plugins", QSize( 540, 400 ) );
116 }
117
118 inline void PluginTab::FillTree()
119 {
120     module_t **p_list = module_list_get( NULL );
121     module_t *p_module;
122
123     for( unsigned int i = 0; (p_module = p_list[i] ) != NULL; i++ )
124     {
125         QStringList qs_item;
126         qs_item << qfu( module_get_name( p_module, true ) )
127                 << qfu( module_get_capability( p_module ) )
128                 << QString::number( module_get_score( p_module ) );
129 #ifndef DEBUG
130         if( qs_item.at(1).isEmpty() ) continue;
131 #endif
132
133         QTreeWidgetItem *item = new PluginTreeItem( qs_item );
134         treePlugins->addTopLevelItem( item );
135     }
136 }
137
138 void PluginTab::search( const QString& qs )
139 {
140     QList<QTreeWidgetItem *> items = treePlugins->findItems( qs, Qt::MatchContains );
141     items += treePlugins->findItems( qs, Qt::MatchContains, 1 );
142
143     QTreeWidgetItem *item = NULL;
144     for( int i = 0; i < treePlugins->topLevelItemCount(); i++ )
145     {
146         item = treePlugins->topLevelItem( i );
147         item->setHidden( !items.contains( item ) );
148     }
149 }
150
151 PluginTab::~PluginTab()
152 {
153     writeSettings( "Plugins" );
154     getSettings()->setValue( "Plugins/Header-State",
155                              treePlugins->header()->saveState() );
156 }
157
158 bool PluginTreeItem::operator< ( const QTreeWidgetItem & other ) const
159 {
160     int col = treeWidget()->sortColumn();
161     if( col == 2 )
162         return text( col ).toInt() < other.text( col ).toInt();
163     return text( col ) < other.text( col );
164 }
165
166 /* Extensions tab */
167 ExtensionTab::ExtensionTab( intf_thread_t *p_intf )
168         : QVLCFrame( p_intf )
169 {
170     // Layout
171     QGridLayout *layout = new QGridLayout( this );
172
173     // Top: combo
174     extList = new QComboBox( this );
175     layout->addWidget( extList, 0, 0, 1, -1 );
176
177     // Center: Description
178     layout->addWidget( new QLabel( "<b>" + qtr( "Version" ) + "</b>" ),
179                        1, 0, 1, 1 );
180     layout->addWidget( new QLabel( "<b>" + qtr( "Author" ) + "</b>" ),
181                        2, 0, 1, 1 );
182     layout->addWidget( new QLabel( "<b>" + qtr( "Description" ) + "</b>" ),
183                        3, 0, 1, 1 );
184     layout->addWidget( new QLabel( "<b>" + qtr( "Website" ) + "</b>" ),
185                        6, 0, 1, 1 );
186     layout->addWidget( new QLabel( "<b>" + qtr( "File" ) + "</b>" ),
187                        7, 0, 1, 1 );
188
189     version = new QLabel( this );
190     layout->addWidget( version, 1, 1, 1, 1 );
191     author = new QLabel( this );
192     layout->addWidget( author, 2, 1, 1, 1 );
193     description = new QTextBrowser( this );
194     description->setOpenExternalLinks( true );
195     layout->addWidget( description, 4, 0, 1, -1 );
196     url = new QLabel( this );
197     url->setOpenExternalLinks( true );
198     url->setTextFormat( Qt::RichText );
199     layout->addWidget( url, 6, 1, 1, 1 );
200     name = new QLineEdit( this );
201     name->setReadOnly( true );
202     layout->addWidget( name, 7, 1, 1, 1 );
203
204     // Bottom: Configuration tools
205     QHBoxLayout *hbox = new QHBoxLayout;
206     QPushButton *reload = new QPushButton( QIcon( ":/update" ),
207                                            qtr( "Reload extensions" ),
208                                            this );
209     QSpacerItem *spacer = new QSpacerItem( 1, 1, QSizePolicy::Expanding,
210                                            QSizePolicy::Expanding );
211     hbox->addItem( spacer );
212     hbox->addWidget( reload );
213     BUTTONACT( reload, reloadExtensions() );
214     layout->addItem( hbox, 8, 0, 1, -1 );
215
216     // Layout: compact display
217     layout->setHorizontalSpacing( 15 );
218
219     fillList();
220
221     CONNECT( extList, currentIndexChanged( int ),
222              this, selectionChanged( int ) );
223     extList->setCurrentIndex( 0 );
224     selectionChanged( 0 );
225
226     // Connect to ExtensionsManager::extensionsUpdated()
227     ExtensionsManager* EM = ExtensionsManager::getInstance( p_intf );
228     CONNECT( EM, extensionsUpdated(), this, fillList() );
229 }
230
231 ExtensionTab::~ExtensionTab()
232 {
233 }
234
235 void ExtensionTab::fillList()
236 {
237     ExtensionsManager* EM = ExtensionsManager::getInstance( p_intf );
238     if( !EM->isLoaded() )
239         EM->loadExtensions();
240     extensions_manager_t* p_mgr = EM->getManager();
241     if( !p_mgr )
242         return;
243
244     // Disconnect signal: we don't want to call selectionChanged now
245     disconnect( extList, SIGNAL( currentIndexChanged( int ) ),
246                 this, SLOT( selectionChanged( int ) ) );
247
248     extList->clear();
249
250     vlc_mutex_lock( &p_mgr->lock );
251
252     extension_t *p_ext;
253     FOREACH_ARRAY( p_ext, p_mgr->extensions )
254     {
255         extList->addItem( p_ext->psz_title, QString( p_ext->psz_name ) );
256     }
257     FOREACH_END()
258
259     vlc_mutex_unlock( &p_mgr->lock );
260     vlc_object_release( p_mgr );
261
262     // Reconnect signal and update screen
263     connect( extList, SIGNAL( currentIndexChanged( int ) ),
264              this, SLOT( selectionChanged( int ) ) );
265     extList->setCurrentIndex( 0 );
266     selectionChanged( 0 );
267 }
268
269 void ExtensionTab::selectionChanged( int index )
270 {
271     QString extName = extList->itemData( index ).toString();
272     if( extName.isEmpty() )
273         return;
274
275     ExtensionsManager* EM = ExtensionsManager::getInstance( p_intf );
276     extensions_manager_t* p_mgr = EM->getManager();
277     if( !p_mgr )
278         return;
279
280     vlc_mutex_lock( &p_mgr->lock );
281
282     const char *psz_name = qtu( extName );
283
284     extension_t *p_ext;
285     FOREACH_ARRAY( p_ext, p_mgr->extensions )
286     {
287         if( !strcmp( p_ext->psz_name, psz_name ) )
288         {
289             char *psz_url;
290             if( p_ext->psz_url != NULL
291                 && asprintf( &psz_url, "<a href=\"%s\">%s</a>", p_ext->psz_url,
292                              p_ext->psz_url ) != -1 )
293             {
294                 url->setText( psz_url );
295                 free( psz_url );
296             }
297             else
298             {
299                 url->clear();
300             }
301             version->setText( qfu( p_ext->psz_version ) );
302             description->setHtml( qfu( p_ext->psz_description ) );
303             author->setText( qfu( p_ext->psz_author ) );
304             name->setText( qfu( p_ext->psz_name ) );
305             break;
306         }
307     }
308     FOREACH_END()
309
310     vlc_mutex_unlock( &p_mgr->lock );
311     vlc_object_release( p_mgr );
312 }
313
314 void ExtensionTab::reloadExtensions()
315 {
316     ExtensionsManager* EM = ExtensionsManager::getInstance( p_intf );
317     EM->reloadExtensions();
318     fillList();
319 }