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