]> git.sesse.net Git - vlc/blob - modules/misc/lua/libs/dialog.c
Extensions: Lua implementation for dialogs
[vlc] / modules / misc / lua / libs / dialog.c
1 /*****************************************************************************
2  * dialog.c: Functions to create interface dialogs from Lua extensions
3  *****************************************************************************
4  * Copyright (C) 2009-2010 VideoLAN and authors
5  * $Id$
6  *
7  * Authors: Jean-Philippe AndrĂ© < jpeg # videolan.org >
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifndef  _GNU_SOURCE
28 #   define  _GNU_SOURCE
29 #endif
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <vlc_common.h>
36 #include <vlc_extensions.h>
37
38 #include <lua.h>        /* Low level lua C API */
39 #include <lauxlib.h>    /* Higher level C API */
40
41 #include "../vlc.h"
42 #include "../libs.h"
43
44 #include "assert.h"
45
46 /*****************************************************************************
47  *
48  *****************************************************************************/
49
50 /* Dialog functions */
51 static int vlclua_dialog_create( lua_State *L );
52 static int vlclua_dialog_delete( lua_State *L );
53 static int vlclua_dialog_show( lua_State *L );
54 static int vlclua_dialog_hide( lua_State *L );
55
56 static int vlclua_dialog_add_button( lua_State *L );
57 static int vlclua_dialog_add_label( lua_State *L );
58 static int vlclua_dialog_add_html( lua_State *L );
59 static int vlclua_dialog_add_text_inner( lua_State *L, int );
60 static inline int vlclua_dialog_add_text_input( lua_State *L )
61 {
62     return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_TEXT_FIELD );
63 }
64 static inline int vlclua_dialog_add_password( lua_State *L )
65 {
66     return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_PASSWORD );
67 }
68 static inline int vlclua_dialog_add_html( lua_State *L )
69 {
70     return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_HTML );
71 }
72 static int vlclua_dialog_add_check_box( lua_State *L );
73 static int vlclua_dialog_add_list( lua_State *L );
74 static int vlclua_dialog_add_dropdown( lua_State *L );
75 static int vlclua_dialog_add_image( lua_State *L );
76 static int vlclua_create_widget_inner( lua_State *L, int i_args,
77                                        extension_widget_t *p_widget);
78
79 static int vlclua_dialog_delete_widget( lua_State *L );
80
81 /* Widget methods */
82 static int vlclua_widget_set_text( lua_State *L );
83 static int vlclua_widget_get_text( lua_State *L );
84 static int vlclua_widget_set_checked( lua_State *L );
85 static int vlclua_widget_get_checked( lua_State *L );
86 static int vlclua_widget_add_value( lua_State *L );
87 static int vlclua_widget_get_value( lua_State *L );
88 static int vlclua_widget_clear( lua_State *L );
89 static int vlclua_widget_get_selection( lua_State *L );
90
91 /* Helpers */
92 static void AddWidget( extension_dialog_t *p_dialog,
93                        extension_widget_t *p_widget );
94 static int DeleteWidget( extension_dialog_t *p_dialog,
95                          extension_widget_t *p_widget );
96
97 static const luaL_Reg vlclua_dialog_reg[] = {
98     { "show", vlclua_dialog_show },
99     { "hide", vlclua_dialog_hide },
100     { "close", vlclua_dialog_delete },
101
102     { "add_button", vlclua_dialog_add_button },
103     { "add_label", vlclua_dialog_add_label },
104     { "add_html", vlclua_dialog_add_html },
105     { "add_text_input", vlclua_dialog_add_text_input },
106     { "add_password", vlclua_dialog_add_password },
107     { "add_check_box", vlclua_dialog_add_check_box },
108     { "add_dropdown", vlclua_dialog_add_dropdown },
109     { "add_list", vlclua_dialog_add_list },
110     { "add_image", vlclua_dialog_add_image },
111
112     { "del_widget", vlclua_dialog_delete_widget },
113     { NULL, NULL }
114 };
115
116 static const luaL_Reg vlclua_widget_reg[] = {
117     { "set_text", vlclua_widget_set_text },
118     { "get_text", vlclua_widget_get_text },
119     { "set_checked", vlclua_widget_set_checked },
120     { "get_checked", vlclua_widget_get_checked },
121     { "add_value", vlclua_widget_add_value },
122     { "get_value", vlclua_widget_get_value },
123     { "clear", vlclua_widget_clear },
124     { "get_selection", vlclua_widget_get_selection },
125     { NULL, NULL }
126 };
127
128 /** Private static variable used for the registry index */
129 static const char key_opaque = 'A';
130
131 /**
132  * Open dialog library for Lua
133  * @param L lua_State
134  * @param opaque Object associated to this lua state
135  * @note opaque will be p_ext for extensions, p_sd for service discoveries
136  **/
137 void luaopen_dialog( lua_State *L, void *opaque )
138 {
139     lua_getglobal( L, "vlc" );
140     lua_pushcfunction( L, vlclua_dialog_create );
141     lua_setfield( L, -2, "dialog" );
142
143     /* Add a private pointer (opaque) in the registry
144      * The &key pointer is used to have a unique entry in the registry
145      */
146     lua_pushlightuserdata( L, (void*) &key_opaque );
147     lua_pushlightuserdata( L, opaque );
148     lua_settable( L, LUA_REGISTRYINDEX );
149 }
150
151 static int vlclua_dialog_create( lua_State *L )
152 {
153     if( !lua_isstring( L, 1 ) )
154         return luaL_error( L, "vlc.dialog() usage: (title)" );
155     const char *psz_title = luaL_checkstring( L, 1 );
156
157     vlc_object_t *p_this = vlclua_get_this( L );
158
159     extension_dialog_t *p_dlg = calloc( 1, sizeof( extension_dialog_t ) );
160     if( !p_dlg )
161         return 0; // luaL_error( L, "Out Of Memory" );
162
163     lua_getglobal( L, "vlc" );
164     lua_getfield( L, -1, "__dialog" );
165     if( lua_topointer( L, lua_gettop( L ) ) != NULL )
166         return luaL_error( L, "Only one dialog allowed per extension!" );
167
168     p_dlg->p_object = p_this;
169     p_dlg->psz_title = strdup( psz_title );
170     p_dlg->b_kill = false;
171     ARRAY_INIT( p_dlg->widgets );
172
173     /* Read the opaque value stored while loading the dialog library */
174     lua_pushlightuserdata( L, (void*) &key_opaque );
175     lua_gettable( L, LUA_REGISTRYINDEX );
176     p_dlg->p_sys = (void*) lua_topointer( L, -1 ); // "const" discarded
177     lua_pop( L, 1 );
178
179     vlc_mutex_init( &p_dlg->lock );
180     vlc_cond_init( &p_dlg->cond );
181
182     /** @todo Use the registry instead of __dialog,
183         so that the user can't tamper with it */
184
185     lua_getglobal( L, "vlc" );
186     lua_pushlightuserdata( L, p_dlg );
187     lua_setfield( L, -2, "__dialog" );
188     lua_pop( L, 1 );
189
190     extension_dialog_t **pp_dlg = lua_newuserdata( L, sizeof( void* ) );
191     *pp_dlg = p_dlg;
192
193     if( luaL_newmetatable( L, "dialog" ) )
194     {
195         lua_newtable( L );
196         luaL_register( L, NULL, vlclua_dialog_reg );
197         lua_setfield( L, -2, "__index" );
198         lua_pushcfunction( L, vlclua_dialog_delete );
199         lua_setfield( L, -2, "__gc" );
200     }
201
202     lua_setmetatable( L, -2 );
203
204     msg_Dbg( p_this, "Creating dialog '%s'", psz_title );
205     int i_ret = dialog_ExtensionUpdate( p_this, p_dlg );
206
207     return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
208 }
209
210 static int vlclua_dialog_delete( lua_State *L )
211 {
212     vlc_object_t *p_mgr = vlclua_get_this( L );
213
214     /* Get dialog descriptor */
215     extension_dialog_t **pp_dlg =
216             (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
217
218     if( !pp_dlg || !*pp_dlg )
219         return luaL_error( L, "Can't get pointer to dialog" );
220
221     extension_dialog_t *p_dlg = *pp_dlg;
222     *pp_dlg = NULL;
223
224     /* Remove private __dialog field */
225     lua_getglobal( L, "vlc" );
226     lua_pushnil( L );
227     lua_setfield( L, -2, "__dialog" );
228
229     assert( !p_dlg->b_kill );
230
231     msg_Dbg( p_mgr, "Deleting dialog '%s'", p_dlg->psz_title );
232     p_dlg->b_kill = true;
233     dialog_ExtensionUpdate( p_mgr, p_dlg );
234
235     /* After dialog_ExtensionUpdate, the UI thread must take the lock asap and
236      * then signal us when it's done deleting the dialog.
237      */
238     vlc_mutex_lock( &p_dlg->lock );
239     while( p_dlg->p_sys_intf != NULL )
240     {
241         mtime_t abstime = mdate() + 1000000; // Waiting 1 second at a time
242         vlc_cond_timedwait( &p_dlg->cond, &p_dlg->lock, abstime );
243     }
244     vlc_mutex_unlock( &p_dlg->lock );
245
246     free( p_dlg->psz_title );
247     p_dlg->psz_title = NULL;
248
249     /* Destroy widgets */
250     extension_widget_t *p_widget;
251     FOREACH_ARRAY( p_widget, p_dlg->widgets )
252     {
253         if( !p_widget )
254             continue;
255         free( p_widget->psz_text );
256
257         /* Free data */
258         struct extension_widget_value_t *p_value, *p_next;
259         for( p_value = p_widget->p_values; p_value != NULL; p_value = p_next )
260         {
261             p_next = p_value->p_next;
262             free( p_value->psz_text );
263             free( p_value );
264         }
265     }
266     FOREACH_END()
267
268     ARRAY_RESET( p_dlg->widgets );
269
270     /* Note: At this point, the UI must not use these resources */
271     vlc_mutex_destroy( &p_dlg->lock );
272     vlc_cond_destroy( &p_dlg->cond );
273
274     return 1;
275 }
276
277 /** Show the dialog */
278 static int vlclua_dialog_show( lua_State *L )
279 {
280     vlc_object_t *p_mgr = vlclua_get_this( L );
281
282     extension_dialog_t **pp_dlg =
283             (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
284     if( !pp_dlg || !*pp_dlg )
285         return luaL_error( L, "Can't get pointer to dialog" );
286     extension_dialog_t *p_dlg = *pp_dlg;
287
288     p_dlg->b_hide = false;
289     dialog_ExtensionUpdate( p_mgr, p_dlg );
290
291     return 1;
292 }
293
294 /** Hide the dialog */
295 static int vlclua_dialog_hide( lua_State *L )
296 {
297     vlc_object_t *p_mgr = vlclua_get_this( L );
298
299     extension_dialog_t **pp_dlg =
300             (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
301     if( !pp_dlg || !*pp_dlg )
302         return luaL_error( L, "Can't get pointer to dialog" );
303     extension_dialog_t *p_dlg = *pp_dlg;
304
305     p_dlg->b_hide = true;
306     dialog_ExtensionUpdate( p_mgr, p_dlg );
307
308     return 1;
309 }
310
311
312 /**
313  * Create a button: add_button
314  * Arguments: text, function (as string)
315  * Qt: QPushButton
316  **/
317 static int vlclua_dialog_add_button( lua_State *L )
318 {
319     /* Verify arguments */
320     if( !lua_isstring( L, 2 ) || !lua_isstring( L, 3 ) )
321         return luaL_error( L, "dialog:add_button usage: (text, func)" );
322
323     extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
324     p_widget->type = EXTENSION_WIDGET_BUTTON;
325     p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
326     p_widget->p_sys = strdup( luaL_checkstring( L, 3 ) );
327
328     return vlclua_create_widget_inner( L, 2, p_widget );
329 }
330
331 /**
332  * Create a text label: add_label
333  * Arguments: text
334  * Qt: QLabel
335  **/
336 static int vlclua_dialog_add_label( lua_State *L )
337 {
338     /* Verify arguments */
339     if( !lua_isstring( L, 2 ) )
340         return luaL_error( L, "dialog:add_label usage: (text)" );
341     extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
342     p_widget->type = EXTENSION_WIDGET_LABEL;
343     p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
344
345     return vlclua_create_widget_inner( L, 1, p_widget );
346 }
347
348 /**
349  * Create a text area: add_html, add_text_input, add_password
350  * Arguments: text (may be nil)
351  * Qt: QLineEdit (Text/Password) or QTextArea (HTML)
352  **/
353 static int vlclua_dialog_add_text_inner( lua_State *L, int i_type )
354 {
355     /* Verify arguments */
356     if( !lua_isstring( L, 2 ) && !lua_isnil( L, 2 ) )
357         return luaL_error( L, "dialog:add_text_input usage: (text = nil)" );
358
359     extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
360     p_widget->type = i_type;
361     if( !lua_isnil( L, 2 ) )
362         p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
363
364     return vlclua_create_widget_inner( L, 1, p_widget );
365 }
366
367 /**
368  * Create a checkable box: add_check_box
369  * Arguments: text, checked (as bool)
370  * Qt: QCheckBox
371  **/
372 static int vlclua_dialog_add_check_box( lua_State *L )
373 {
374     /* Verify arguments */
375     if( !lua_isstring( L, 2 ) )
376         return luaL_error( L, "dialog:add_check_box usage: (text, checked)" );
377
378     extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
379     p_widget->type = EXTENSION_WIDGET_CHECK_BOX;
380     p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
381     p_widget->b_checked = lua_toboolean( L, 3 );
382
383     return vlclua_create_widget_inner( L, 2, p_widget );
384 }
385
386 /**
387  * Create a drop-down list (non editable)
388  * Arguments: (none)
389  * Qt: QComboBox
390  * @todo make it editable?
391  **/
392 static int vlclua_dialog_add_dropdown( lua_State *L )
393 {
394     extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
395     p_widget->type = EXTENSION_WIDGET_DROPDOWN;
396
397     return vlclua_create_widget_inner( L, 0, p_widget );
398 }
399
400 /**
401  * Create a list panel (multiple selection)
402  * Arguments: (none)
403  * Qt: QListWidget
404  **/
405 static int vlclua_dialog_add_list( lua_State *L )
406 {
407     extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
408     p_widget->type = EXTENSION_WIDGET_LIST;
409
410     return vlclua_create_widget_inner( L, 0, p_widget );
411 }
412
413 /**
414  * Create an image label
415  * Arguments: url
416  * Qt: QLabel with setPixmap( QPixmap& )
417  **/
418 static int vlclua_dialog_add_image( lua_State *L )
419 {
420     /* Verify arguments */
421     if( !lua_isstring( L, 2 ) )
422         return luaL_error( L, "dialog:add_image usage: (filename)" );
423
424     extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
425     p_widget->type = EXTENSION_WIDGET_IMAGE;
426     p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
427
428     return vlclua_create_widget_inner( L, 1, p_widget );
429 }
430
431 /**
432  * Internal helper to finalize the creation of a widget
433  * @param L Lua State
434  * @param i_args Number of arguments before "row" (0 or more)
435  * @param p_widget The widget to add
436  **/
437 static int vlclua_create_widget_inner( lua_State *L, int i_args,
438                                        extension_widget_t *p_widget )
439 {
440     int arg = i_args + 2;
441
442     /* Get dialog */
443     extension_dialog_t **pp_dlg =
444             (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
445     if( !pp_dlg || !*pp_dlg )
446         return luaL_error( L, "Can't get pointer to dialog" );
447     extension_dialog_t *p_dlg = *pp_dlg;
448
449     /* Set parent dialog */
450     p_widget->p_dialog = p_dlg;
451
452     /* Set common arguments: col, row, hspan, vspan, width, height */
453     if( lua_isnumber( L, arg ) )
454         p_widget->i_column = luaL_checkinteger( L, arg );
455     else goto end_of_args;
456     if( lua_isnumber( L, ++arg ) )
457         p_widget->i_row = luaL_checkinteger( L, arg );
458     else goto end_of_args;
459     if( lua_isnumber( L, ++arg ) )
460         p_widget->i_horiz_span = luaL_checkinteger( L, arg );
461     else goto end_of_args;
462     if( lua_isnumber( L, ++arg ) )
463         p_widget->i_vert_span = luaL_checkinteger( L, arg );
464     else goto end_of_args;
465     if( lua_isnumber( L, ++arg ) )
466         p_widget->i_width = luaL_checkinteger( L, arg );
467     else goto end_of_args;
468     if( lua_isnumber( L, ++arg ) )
469         p_widget->i_height = luaL_checkinteger( L, arg );
470     else goto end_of_args;
471
472 end_of_args:
473     vlc_mutex_lock( &p_dlg->lock );
474
475     /* Add the widget to the dialog descriptor */
476     AddWidget( p_dlg, p_widget );
477
478     vlc_mutex_unlock( &p_dlg->lock );
479
480     /* Create meta table */
481     extension_widget_t **pp_widget = lua_newuserdata( L, sizeof( void* ) );
482     *pp_widget = p_widget;
483     if( luaL_newmetatable( L, "widget" ) )
484     {
485         lua_newtable( L );
486         luaL_register( L, NULL, vlclua_widget_reg );
487         lua_setfield( L, -2, "__index" );
488     }
489     lua_setmetatable( L, -2 );
490
491     /* Signal interface */
492     vlc_object_t *p_mgr = vlclua_get_this( L );
493     int i_ret = dialog_ExtensionUpdate( p_mgr, p_dlg );
494
495     return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
496 }
497
498 static int vlclua_widget_set_text( lua_State *L )
499 {
500     /* Get dialog */
501     extension_widget_t **pp_widget =
502             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
503     if( !pp_widget || !*pp_widget )
504         return luaL_error( L, "Can't get pointer to widget" );
505     extension_widget_t *p_widget = *pp_widget;
506
507     /* Verify arguments */
508     if( !lua_isstring( L, 2 ) )
509         return luaL_error( L, "widget:set_text usage: (text)" );
510
511     /* Verify widget type */
512     switch( p_widget->type )
513     {
514         case EXTENSION_WIDGET_LABEL:
515         case EXTENSION_WIDGET_BUTTON:
516         case EXTENSION_WIDGET_HTML:
517         case EXTENSION_WIDGET_TEXT_FIELD:
518         case EXTENSION_WIDGET_PASSWORD:
519         case EXTENSION_WIDGET_DROPDOWN:
520         case EXTENSION_WIDGET_CHECK_BOX:
521             break;
522         case EXTENSION_WIDGET_LIST:
523         case EXTENSION_WIDGET_IMAGE:
524         default:
525             vlc_mutex_unlock( &p_widget->p_dialog->lock );
526             return luaL_error( L, "method set_text not valid for this widget" );
527     }
528
529     vlc_mutex_lock( &p_widget->p_dialog->lock );
530
531     /* Update widget */
532     p_widget->b_update = true;
533     free( p_widget->psz_text );
534     p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
535
536     vlc_mutex_unlock( &p_widget->p_dialog->lock );
537
538     /* Update dialog */
539     vlc_object_t *p_mgr = vlclua_get_this( L );
540     dialog_ExtensionUpdate( p_mgr, p_widget->p_dialog );
541
542     return 1;
543 }
544
545 static int vlclua_widget_get_text( lua_State *L )
546 {
547     /* Get dialog */
548     extension_widget_t **pp_widget =
549             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
550     if( !pp_widget || !*pp_widget )
551         return luaL_error( L, "Can't get pointer to widget" );
552     extension_widget_t *p_widget = *pp_widget;
553
554     /* Verify widget type */
555     switch( p_widget->type )
556     {
557         case EXTENSION_WIDGET_LABEL:
558         case EXTENSION_WIDGET_BUTTON:
559         case EXTENSION_WIDGET_HTML:
560         case EXTENSION_WIDGET_TEXT_FIELD:
561         case EXTENSION_WIDGET_PASSWORD:
562         case EXTENSION_WIDGET_DROPDOWN:
563         case EXTENSION_WIDGET_CHECK_BOX:
564             break;
565         case EXTENSION_WIDGET_LIST:
566         case EXTENSION_WIDGET_IMAGE:
567         default:
568             return luaL_error( L, "method get_text not valid for this widget" );
569     }
570
571     extension_dialog_t *p_dlg = p_widget->p_dialog;
572     vlc_mutex_lock( &p_dlg->lock );
573
574     char *psz_text = NULL;
575     if( p_widget->psz_text )
576         psz_text = strdup( p_widget->psz_text );
577     vlc_mutex_unlock( &p_dlg->lock );
578
579     lua_pushstring( L, psz_text );
580
581     free( psz_text );
582     return 1;
583 }
584
585 static int vlclua_widget_get_checked( lua_State *L )
586 {
587     /* Get widget */
588     extension_widget_t **pp_widget =
589             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
590     if( !pp_widget || !*pp_widget )
591         return luaL_error( L, "Can't get pointer to widget" );
592     extension_widget_t *p_widget = *pp_widget;
593
594     if( p_widget->type != EXTENSION_WIDGET_CHECK_BOX )
595         return luaL_error( L, "method get_checked not valid for this widget" );
596
597     vlc_mutex_lock( &p_widget->p_dialog->lock );
598     lua_pushboolean( L, p_widget->b_checked );
599     vlc_mutex_unlock( &p_widget->p_dialog->lock );
600
601     return 1;
602 }
603
604 static int vlclua_widget_add_value( lua_State *L )
605 {
606     /* Get widget */
607     extension_widget_t **pp_widget =
608             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
609     if( !pp_widget || !*pp_widget )
610         return luaL_error( L, "Can't get pointer to widget" );
611     extension_widget_t *p_widget = *pp_widget;
612
613     if( p_widget->type != EXTENSION_WIDGET_DROPDOWN
614         && p_widget->type != EXTENSION_WIDGET_LIST )
615         return luaL_error( L, "method add_value not valid for this widget" );
616
617     if( !lua_isstring( L, 2 ) )
618         return luaL_error( L, "widget:add_value usage: (text, id = 0)" );
619
620     struct extension_widget_value_t *p_value,
621         *p_new_value = calloc( 1, sizeof( struct extension_widget_value_t ) );
622     p_new_value->psz_text = strdup( luaL_checkstring( L, 2 ) );
623     p_new_value->i_id = lua_tointeger( L, 3 );
624
625     vlc_mutex_lock( &p_widget->p_dialog->lock );
626
627     if( !p_widget->p_values )
628     {
629         p_widget->p_values = p_new_value;
630     }
631     else
632     {
633         for( p_value = p_widget->p_values;
634              p_value->p_next != NULL;
635              p_value = p_value->p_next )
636         { /* Do nothing, iterate to find the end */ }
637         p_value->p_next = p_new_value;
638     }
639
640     p_widget->b_update = true;
641     vlc_mutex_unlock( &p_widget->p_dialog->lock );
642
643     vlc_object_t *p_mgr = vlclua_get_this( L );
644     dialog_ExtensionUpdate( p_mgr, p_widget->p_dialog );
645
646     return 1;
647 }
648
649 static int vlclua_widget_get_value( lua_State *L )
650 {
651     /* Get widget */
652     extension_widget_t **pp_widget =
653             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
654     if( !pp_widget || !*pp_widget )
655         return luaL_error( L, "Can't get pointer to widget" );
656     extension_widget_t *p_widget = *pp_widget;
657
658     if( p_widget->type != EXTENSION_WIDGET_DROPDOWN )
659         return luaL_error( L, "method get_value not valid for this widget" );
660
661     vlc_mutex_lock( &p_widget->p_dialog->lock );
662
663     struct extension_widget_value_t *p_value;
664     for( p_value = p_widget->p_values;
665          p_value != NULL;
666          p_value = p_value->p_next )
667     {
668         if( p_value->b_selected )
669         {
670             lua_pushinteger( L, p_value->i_id );
671             lua_pushstring( L, p_value->psz_text );
672             vlc_mutex_unlock( &p_widget->p_dialog->lock );
673             return 2;
674         }
675     }
676
677     vlc_mutex_unlock( &p_widget->p_dialog->lock );
678
679     lua_pushinteger( L, -1 );
680     lua_pushnil( L );
681     return 2;
682 }
683
684 static int vlclua_widget_clear( lua_State *L )
685 {
686     /* Get widget */
687     extension_widget_t **pp_widget =
688             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
689     if( !pp_widget || !*pp_widget )
690         return luaL_error( L, "Can't get pointer to widget" );
691     extension_widget_t *p_widget = *pp_widget;
692
693     if( p_widget->type != EXTENSION_WIDGET_DROPDOWN
694         && p_widget->type != EXTENSION_WIDGET_LIST )
695         return luaL_error( L, "method add_value not valid for this widget" );
696
697     struct extension_widget_value_t *p_value, *p_next;
698
699     vlc_mutex_lock( &p_widget->p_dialog->lock );
700
701     for( p_value = p_widget->p_values;
702          p_value != NULL;
703          p_value = p_next )
704     {
705         p_next = p_value->p_next;
706         free( p_value->psz_text );
707         free( p_value );
708     }
709
710     p_widget->p_values = NULL;
711     p_widget->b_update = true;
712
713     vlc_mutex_unlock( &p_widget->p_dialog->lock );
714
715     vlc_object_t *p_mgr = vlclua_get_this( L );
716     dialog_ExtensionUpdate( p_mgr, p_widget->p_dialog );
717
718     return 1;
719 }
720
721 static int vlclua_widget_get_selection( lua_State *L )
722 {
723     /* Get widget */
724     extension_widget_t **pp_widget =
725             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
726     if( !pp_widget || !*pp_widget )
727         return luaL_error( L, "Can't get pointer to widget" );
728     extension_widget_t *p_widget = *pp_widget;
729
730     if( p_widget->type != EXTENSION_WIDGET_LIST )
731         return luaL_error( L, "method get_selection not valid for this widget" );
732
733     /* Create empty table */
734     lua_newtable( L );
735
736     vlc_mutex_lock( &p_widget->p_dialog->lock );
737
738     struct extension_widget_value_t *p_value;
739     for( p_value = p_widget->p_values;
740          p_value != NULL;
741          p_value = p_value->p_next )
742     {
743         if( p_value->b_selected )
744         {
745             lua_pushinteger( L, p_value->i_id );
746             lua_pushstring( L, p_value->psz_text );
747             lua_settable( L, -3 );
748         }
749     }
750
751     vlc_mutex_unlock( &p_widget->p_dialog->lock );
752
753     return 1;
754 }
755
756
757 static int vlclua_widget_set_checked( lua_State *L )
758 {
759     /* Get dialog */
760     extension_widget_t **pp_widget =
761             (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
762     if( !pp_widget || !*pp_widget )
763         return luaL_error( L, "Can't get pointer to widget" );
764     extension_widget_t *p_widget = *pp_widget;
765
766     if( p_widget->type != EXTENSION_WIDGET_CHECK_BOX )
767         return luaL_error( L, "method set_checked not valid for this widget" );
768
769     /* Verify arguments */
770     if( !lua_isboolean( L, 2 ) )
771         return luaL_error( L, "widget:set_checked usage: (bool)" );
772
773     vlc_mutex_lock( &p_widget->p_dialog->lock );
774
775     bool b_old_check = p_widget->b_checked;
776     p_widget->b_checked = lua_toboolean( L, 2 );
777
778     vlc_mutex_unlock( &p_widget->p_dialog->lock );
779
780     if( b_old_check != p_widget->b_checked )
781     {
782         /* Signal interface of the change */
783         p_widget->b_update = true;
784
785         vlc_object_t *p_mgr = vlclua_get_this( L );
786         dialog_ExtensionUpdate( p_mgr, p_widget->p_dialog );
787     }
788
789     return 1;
790 }
791
792 /**
793  * Delete a widget from a dialog
794  * Remove it from the list once it has been safely destroyed by the interface
795  **/
796 static int vlclua_dialog_delete_widget( lua_State *L )
797 {
798     /* Get dialog */
799     extension_dialog_t **pp_dlg =
800             (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
801     if( !pp_dlg || !*pp_dlg )
802         return luaL_error( L, "Can't get pointer to dialog" );
803     extension_dialog_t *p_dlg = *pp_dlg;
804
805     /* Get widget */
806     if( !lua_isuserdata( L, 2 ) )
807         return luaL_error( L, "Argument to del_widget is not a widget" );
808
809     /* Get dialog */
810     extension_widget_t **pp_widget =
811             (extension_widget_t **) luaL_checkudata( L, 2, "widget" );
812     if( !pp_widget || !*pp_widget )
813         return luaL_error( L, "Can't get pointer to widget" );
814     extension_widget_t *p_widget = *pp_widget;
815
816     /* Delete widget */
817     *pp_widget = NULL;
818
819     vlc_object_t *p_mgr = vlclua_get_this( L );
820
821     p_widget->b_kill = true;
822
823     int i_ret = dialog_ExtensionUpdate( p_mgr, p_dlg );
824
825     if( i_ret != VLC_SUCCESS )
826     {
827         return luaL_error( L, "Could not delete widget" );
828     }
829
830     vlc_mutex_lock( &p_dlg->lock );
831
832     /* Same remarks as for dialog delete */
833     mtime_t abstime = mdate() + 2000000;
834     if( p_widget->p_sys_intf != NULL )
835         vlc_cond_timedwait( &p_dlg->cond, &p_dlg->lock, abstime );
836
837     if( p_widget->p_sys_intf == NULL )
838     {
839         i_ret = DeleteWidget( p_dlg, p_widget );
840
841         if( i_ret != VLC_SUCCESS )
842         {
843             vlc_mutex_unlock( &p_dlg->lock );
844             return luaL_error( L, "Could not remove widget from list" );
845         }
846     }
847     else
848     {
849         msg_Warn( p_mgr, "Could not delete a widget. Leaking its descriptor." );
850         i_ret = VLC_EGENERIC;
851     }
852
853     vlc_mutex_unlock( &p_dlg->lock );
854
855     return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
856 }
857
858
859 /*
860  * Below this line, no Lua specific code.
861  * Extension helpers.
862  */
863
864
865 /**
866  * Add a widget to the widget list of a dialog
867  * @note Must be entered with lock on dialog
868  **/
869 static void AddWidget( extension_dialog_t *p_dialog,
870                        extension_widget_t *p_widget )
871 {
872     ARRAY_APPEND( p_dialog->widgets, p_widget );
873 }
874
875 /**
876  * Remove a widget from the widget list of a dialog
877  * @note The widget MUST have been safely killed before
878  * @note Must be entered with lock on dialog
879  **/
880 static int DeleteWidget( extension_dialog_t *p_dialog,
881                          extension_widget_t *p_widget )
882 {
883     int pos = -1;
884     bool found = false;
885     extension_widget_t *p_iter;
886     FOREACH_ARRAY( p_iter, p_dialog->widgets )
887     {
888         pos++;
889         if( p_iter == p_widget )
890         {
891             found = true;
892             break;
893         }
894     }
895     FOREACH_END()
896
897     if( !found )
898         return VLC_EGENERIC;
899
900     ARRAY_REMOVE( p_dialog->widgets, pos );
901
902     /* Now free the data */
903     free( p_widget->p_sys );
904     struct extension_widget_value_t *p_value = p_widget->p_values;
905     while( p_value )
906     {
907         free( p_value->psz_text );
908         struct extension_widget_value_t *old = p_value;
909         p_value = p_value->p_next;
910         free( old );
911     }
912     free( p_widget->psz_text );
913     free( p_widget );
914
915     return VLC_SUCCESS;
916 }