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