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