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