1 /*****************************************************************************
2 * dialog.c: Functions to create interface dialogs from Lua extensions
3 *****************************************************************************
4 * Copyright (C) 2009-2010 VideoLAN and authors
7 * Authors: Jean-Philippe André < jpeg # videolan.org >
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_extensions.h>
43 /*****************************************************************************
45 *****************************************************************************/
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 );
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 )
64 return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_TEXT_FIELD );
66 static inline int vlclua_dialog_add_password( lua_State *L )
68 return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_PASSWORD );
70 static inline int vlclua_dialog_add_html( lua_State *L )
72 return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_HTML );
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);
82 static int vlclua_dialog_delete_widget( lua_State *L );
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 );
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 );
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 },
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 },
120 { "del_widget", vlclua_dialog_delete_widget },
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 },
138 /** Private static variable used for the registry index */
139 static const char key_opaque = 'A',
143 * Open dialog library for Lua
145 * @param opaque Object associated to this lua state
146 * @note opaque will be p_ext for extensions, p_sd for service discoveries
148 void luaopen_dialog( lua_State *L, void *opaque )
150 lua_getglobal( L, "vlc" );
151 lua_pushcfunction( L, vlclua_dialog_create );
152 lua_setfield( L, -2, "dialog" );
154 /* Add a private pointer (opaque) in the registry
155 * The &key pointer is used to have a unique entry in the registry
157 lua_pushlightuserdata( L, (void*) &key_opaque );
158 lua_pushlightuserdata( L, opaque );
159 lua_settable( L, LUA_REGISTRYINDEX );
161 /* Add private data: dialog update flag */
162 lua_SetDialogUpdate( L, 0 );
165 static int vlclua_dialog_create( lua_State *L )
167 if( !lua_isstring( L, 1 ) )
168 return luaL_error( L, "vlc.dialog() usage: (title)" );
169 const char *psz_title = luaL_checkstring( L, 1 );
171 vlc_object_t *p_this = vlclua_get_this( L );
173 extension_dialog_t *p_dlg = calloc( 1, sizeof( extension_dialog_t ) );
175 return 0; // luaL_error( L, "Out Of Memory" );
177 lua_getglobal( L, "vlc" );
178 lua_getfield( L, -1, "__dialog" );
179 if( lua_topointer( L, lua_gettop( L ) ) != NULL )
182 return luaL_error( L, "Only one dialog allowed per extension!" );
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 );
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
196 vlc_mutex_init( &p_dlg->lock );
197 vlc_cond_init( &p_dlg->cond );
199 /** @todo Use the registry instead of __dialog,
200 so that the user can't tamper with it */
202 lua_getglobal( L, "vlc" );
203 lua_pushlightuserdata( L, p_dlg );
204 lua_setfield( L, -2, "__dialog" );
207 extension_dialog_t **pp_dlg = lua_newuserdata( L, sizeof( extension_dialog_t* ) );
210 if( luaL_newmetatable( L, "dialog" ) )
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" );
219 lua_setmetatable( L, -2 );
221 msg_Dbg( p_this, "Creating dialog '%s'", psz_title );
222 lua_SetDialogUpdate( L, 0 );
227 static int vlclua_dialog_delete( lua_State *L )
229 vlc_object_t *p_mgr = vlclua_get_this( L );
231 /* Get dialog descriptor */
232 extension_dialog_t **pp_dlg =
233 (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
235 if( !pp_dlg || !*pp_dlg )
236 return luaL_error( L, "Can't get pointer to dialog" );
238 extension_dialog_t *p_dlg = *pp_dlg;
241 /* Remove private __dialog field */
242 lua_getglobal( L, "vlc" );
244 lua_setfield( L, -2, "__dialog" );
246 assert( !p_dlg->b_kill );
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 );
254 /* After dialog_ExtensionUpdate, the UI thread must take the lock asap and
255 * then signal us when it's done deleting the dialog.
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 )
261 vlc_cond_wait( &p_dlg->cond, &p_dlg->lock );
263 vlc_mutex_unlock( &p_dlg->lock );
265 free( p_dlg->psz_title );
266 p_dlg->psz_title = NULL;
268 /* Destroy widgets */
269 extension_widget_t *p_widget;
270 FOREACH_ARRAY( p_widget, p_dlg->widgets )
274 free( p_widget->psz_text );
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 )
280 p_next = p_value->p_next;
281 free( p_value->psz_text );
287 ARRAY_RESET( p_dlg->widgets );
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 );
296 /** Show the dialog */
297 static int vlclua_dialog_show( lua_State *L )
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;
305 p_dlg->b_hide = false;
306 lua_SetDialogUpdate( L, 1 );
311 /** Hide the dialog */
312 static int vlclua_dialog_hide( lua_State *L )
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;
320 p_dlg->b_hide = true;
321 lua_SetDialogUpdate( L, 1 );
327 /** Set the dialog's title */
328 static int vlclua_dialog_set_title( lua_State *L )
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;
336 vlc_mutex_lock( &p_dlg->lock );
338 const char *psz_title = luaL_checkstring( L, 2 );
339 free( p_dlg->psz_title );
340 p_dlg->psz_title = strdup( psz_title );
342 vlc_mutex_unlock( &p_dlg->lock );
344 lua_SetDialogUpdate( L, 1 );
349 /** Update the dialog immediately */
350 static int vlclua_dialog_update( lua_State *L )
352 vlc_object_t *p_mgr = vlclua_get_this( L );
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;
360 // Updating dialog immediately
361 dialog_ExtensionUpdate( p_mgr, p_dlg );
364 lua_SetDialogUpdate( L, 0 );
369 static void lua_SetDialogUpdate( lua_State *L, int flag )
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 );
377 static int lua_GetDialogUpdate( lua_State *L )
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 );
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)
391 int lua_DialogFlush( lua_State *L )
393 lua_getglobal( L, "vlc" );
394 lua_getfield( L, -1, "__dialog" );
395 extension_dialog_t *p_dlg = ( extension_dialog_t* )lua_topointer( L, -1 );
400 int i_ret = VLC_SUCCESS;
401 if( lua_GetDialogUpdate( L ) )
403 i_ret = dialog_ExtensionUpdate( vlclua_get_this( L ), p_dlg );
404 lua_SetDialogUpdate( L, 0 );
411 * Create a button: add_button
412 * Arguments: text, function (as string)
415 static int vlclua_dialog_add_button( lua_State *L )
417 /* Verify arguments */
418 if( !lua_isstring( L, 2 ) || !lua_isfunction( L, 3 ) )
419 return luaL_error( L, "dialog:add_button usage: (text, func)" );
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 ) );
425 lua_pushlightuserdata( L, p_widget );
426 lua_pushvalue( L, 3 );
427 lua_settable( L, LUA_REGISTRYINDEX );
428 p_widget->p_sys = NULL;
430 return vlclua_create_widget_inner( L, 2, p_widget );
434 * Create a text label: add_label
438 static int vlclua_dialog_add_label( lua_State *L )
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 ) );
447 return vlclua_create_widget_inner( L, 1, p_widget );
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)
455 static int vlclua_dialog_add_text_inner( lua_State *L, int i_type )
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)" );
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 ) );
466 return vlclua_create_widget_inner( L, 1, p_widget );
470 * Create a checkable box: add_check_box
471 * Arguments: text, checked (as bool)
474 static int vlclua_dialog_add_check_box( lua_State *L )
476 /* Verify arguments */
477 if( !lua_isstring( L, 2 ) )
478 return luaL_error( L, "dialog:add_check_box usage: (text, checked)" );
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 );
485 return vlclua_create_widget_inner( L, 2, p_widget );
489 * Create a drop-down list (non editable)
492 * @todo make it editable?
494 static int vlclua_dialog_add_dropdown( lua_State *L )
496 extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
497 p_widget->type = EXTENSION_WIDGET_DROPDOWN;
499 return vlclua_create_widget_inner( L, 0, p_widget );
503 * Create a list panel (multiple selection)
507 static int vlclua_dialog_add_list( lua_State *L )
509 extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
510 p_widget->type = EXTENSION_WIDGET_LIST;
512 return vlclua_create_widget_inner( L, 0, p_widget );
516 * Create an image label
517 * Arguments: (string) url
518 * Qt: QLabel with setPixmap( QPixmap& )
520 static int vlclua_dialog_add_image( lua_State *L )
522 /* Verify arguments */
523 if( !lua_isstring( L, 2 ) )
524 return luaL_error( L, "dialog:add_image usage: (filename)" );
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 ) );
530 return vlclua_create_widget_inner( L, 1, p_widget );
534 * Create a spinning icon
535 * Arguments: (int) loop count to play: 0 means stopped, -1 means infinite.
536 * Qt: SpinningIcon (custom widget)
538 static int vlclua_dialog_add_spin_icon( lua_State *L )
540 /* Verify arguments */
541 if( !lua_isstring( L, 2 ) )
542 return luaL_error( L, "dialog:add_image usage: (filename)" );
544 extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
545 p_widget->type = EXTENSION_WIDGET_SPIN_ICON;
547 return vlclua_create_widget_inner( L, 0, p_widget );
551 * Internal helper to finalize the creation of a widget
553 * @param i_args Number of arguments before "row" (0 or more)
554 * @param p_widget The widget to add
556 static int vlclua_create_widget_inner( lua_State *L, int i_args,
557 extension_widget_t *p_widget )
559 int arg = i_args + 2;
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;
568 /* Set parent dialog */
569 p_widget->p_dialog = p_dlg;
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;
592 vlc_mutex_lock( &p_dlg->lock );
594 /* Add the widget to the dialog descriptor */
595 AddWidget( p_dlg, p_widget );
597 vlc_mutex_unlock( &p_dlg->lock );
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" ) )
605 luaL_register( L, NULL, vlclua_widget_reg );
606 lua_setfield( L, -2, "__index" );
608 lua_setmetatable( L, -2 );
610 lua_SetDialogUpdate( L, 1 );
615 static int vlclua_widget_set_text( lua_State *L )
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;
624 /* Verify arguments */
625 if( !lua_isstring( L, 2 ) )
626 return luaL_error( L, "widget:set_text usage: (text)" );
628 /* Verify widget type */
629 switch( p_widget->type )
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:
639 case EXTENSION_WIDGET_LIST:
640 case EXTENSION_WIDGET_IMAGE:
642 return luaL_error( L, "method set_text not valid for this widget" );
645 vlc_mutex_lock( &p_widget->p_dialog->lock );
648 p_widget->b_update = true;
649 free( p_widget->psz_text );
650 p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
652 vlc_mutex_unlock( &p_widget->p_dialog->lock );
654 lua_SetDialogUpdate( L, 1 );
659 static int vlclua_widget_get_text( lua_State *L )
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;
668 /* Verify widget type */
669 switch( p_widget->type )
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:
679 case EXTENSION_WIDGET_LIST:
680 case EXTENSION_WIDGET_IMAGE:
682 return luaL_error( L, "method get_text not valid for this widget" );
685 extension_dialog_t *p_dlg = p_widget->p_dialog;
686 vlc_mutex_lock( &p_dlg->lock );
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 );
693 lua_pushstring( L, psz_text );
699 static int vlclua_widget_get_checked( lua_State *L )
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;
708 if( p_widget->type != EXTENSION_WIDGET_CHECK_BOX )
709 return luaL_error( L, "method get_checked not valid for this widget" );
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 );
718 static int vlclua_widget_add_value( lua_State *L )
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;
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" );
731 if( !lua_isstring( L, 2 ) )
732 return luaL_error( L, "widget:add_value usage: (text, id = 0)" );
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 );
739 vlc_mutex_lock( &p_widget->p_dialog->lock );
741 if( !p_widget->p_values )
743 p_widget->p_values = p_new_value;
744 if( p_widget->type == EXTENSION_WIDGET_DROPDOWN )
745 p_new_value->b_selected = true;
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;
756 p_widget->b_update = true;
757 vlc_mutex_unlock( &p_widget->p_dialog->lock );
759 lua_SetDialogUpdate( L, 1 );
764 static int vlclua_widget_get_value( lua_State *L )
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;
773 if( p_widget->type != EXTENSION_WIDGET_DROPDOWN )
774 return luaL_error( L, "method get_value not valid for this widget" );
776 vlc_mutex_lock( &p_widget->p_dialog->lock );
778 struct extension_widget_value_t *p_value;
779 for( p_value = p_widget->p_values;
781 p_value = p_value->p_next )
783 if( p_value->b_selected )
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 );
792 vlc_mutex_unlock( &p_widget->p_dialog->lock );
794 lua_pushinteger( L, -1 );
799 static int vlclua_widget_clear( lua_State *L )
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;
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" );
812 struct extension_widget_value_t *p_value, *p_next;
814 vlc_mutex_lock( &p_widget->p_dialog->lock );
816 for( p_value = p_widget->p_values;
820 p_next = p_value->p_next;
821 free( p_value->psz_text );
825 p_widget->p_values = NULL;
826 p_widget->b_update = true;
828 vlc_mutex_unlock( &p_widget->p_dialog->lock );
830 lua_SetDialogUpdate( L, 1 );
835 static int vlclua_widget_get_selection( lua_State *L )
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;
844 if( p_widget->type != EXTENSION_WIDGET_LIST )
845 return luaL_error( L, "method get_selection not valid for this widget" );
847 /* Create empty table */
850 vlc_mutex_lock( &p_widget->p_dialog->lock );
852 struct extension_widget_value_t *p_value;
853 for( p_value = p_widget->p_values;
855 p_value = p_value->p_next )
857 if( p_value->b_selected )
859 lua_pushinteger( L, p_value->i_id );
860 lua_pushstring( L, p_value->psz_text );
861 lua_settable( L, -3 );
865 vlc_mutex_unlock( &p_widget->p_dialog->lock );
870 static int vlclua_widget_set_checked( lua_State *L )
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;
879 if( p_widget->type != EXTENSION_WIDGET_CHECK_BOX )
880 return luaL_error( L, "method set_checked not valid for this widget" );
882 /* Verify arguments */
883 if( !lua_isboolean( L, 2 ) )
884 return luaL_error( L, "widget:set_checked usage: (bool)" );
886 vlc_mutex_lock( &p_widget->p_dialog->lock );
888 bool b_old_check = p_widget->b_checked;
889 p_widget->b_checked = lua_toboolean( L, 2 );
891 vlc_mutex_unlock( &p_widget->p_dialog->lock );
893 if( b_old_check != p_widget->b_checked )
895 /* Signal interface of the change */
896 p_widget->b_update = true;
897 lua_SetDialogUpdate( L, 1 );
903 static int vlclua_widget_animate( lua_State *L )
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;
912 if( p_widget->type != EXTENSION_WIDGET_SPIN_ICON )
913 return luaL_error( L, "method animate not valid for this widget" );
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;
920 p_widget->i_spin_loops = lua_tointeger( L, 2 );
921 vlc_mutex_unlock( &p_widget->p_dialog->lock );
923 /* Signal interface of the change */
924 p_widget->b_update = true;
925 lua_SetDialogUpdate( L, 1 );
930 static int vlclua_widget_stop( lua_State *L )
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;
939 if( p_widget->type != EXTENSION_WIDGET_SPIN_ICON )
940 return luaL_error( L, "method stop not valid for this widget" );
942 vlc_mutex_lock( &p_widget->p_dialog->lock );
944 bool b_needs_update = p_widget->i_spin_loops != 0;
945 p_widget->i_spin_loops = 0;
947 vlc_mutex_unlock( &p_widget->p_dialog->lock );
951 /* Signal interface of the change */
952 p_widget->b_update = true;
953 lua_SetDialogUpdate( L, 1 );
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
964 static int vlclua_dialog_delete_widget( lua_State *L )
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;
974 if( !lua_isuserdata( L, 2 ) )
975 return luaL_error( L, "Argument to del_widget is not a widget" );
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;
986 if( p_widget->type == EXTENSION_WIDGET_BUTTON )
988 /* Remove button action from registry */
989 lua_pushlightuserdata( L, p_widget );
991 lua_settable( L, LUA_REGISTRYINDEX );
994 vlc_object_t *p_mgr = vlclua_get_this( L );
996 p_widget->b_kill = true;
998 lua_SetDialogUpdate( L, 0 ); // Reset update flag
999 int i_ret = dialog_ExtensionUpdate( p_mgr, p_dlg );
1001 if( i_ret != VLC_SUCCESS )
1003 return luaL_error( L, "Could not delete widget" );
1006 vlc_mutex_lock( &p_dlg->lock );
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 )
1015 vlc_cond_wait( &p_dlg->cond, &p_dlg->lock );
1018 i_ret = DeleteWidget( p_dlg, p_widget );
1020 vlc_mutex_unlock( &p_dlg->lock );
1022 if( i_ret != VLC_SUCCESS )
1024 return luaL_error( L, "Could not remove widget from list" );
1032 * Below this line, no Lua specific code.
1033 * Extension helpers.
1038 * Add a widget to the widget list of a dialog
1039 * @note Must be entered with lock on dialog
1041 static void AddWidget( extension_dialog_t *p_dialog,
1042 extension_widget_t *p_widget )
1044 ARRAY_APPEND( p_dialog->widgets, p_widget );
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
1052 static int DeleteWidget( extension_dialog_t *p_dialog,
1053 extension_widget_t *p_widget )
1057 extension_widget_t *p_iter;
1058 FOREACH_ARRAY( p_iter, p_dialog->widgets )
1061 if( p_iter == p_widget )
1070 return VLC_EGENERIC;
1072 ARRAY_REMOVE( p_dialog->widgets, pos );
1074 /* Now free the data */
1075 free( p_widget->p_sys );
1076 struct extension_widget_value_t *p_value = p_widget->p_values;
1079 free( p_value->psz_text );
1080 struct extension_widget_value_t *old = p_value;
1081 p_value = p_value->p_next;
1084 free( p_widget->psz_text );