]> git.sesse.net Git - vlc/blob - src/interface/interaction.c
Improve dialogs handling
[vlc] / src / interface / interaction.c
1 /*****************************************************************************
2  * interaction.c: User interaction functions
3  *****************************************************************************
4  * Copyright (C) 1998-2004 VideoLAN
5  * $Id: interface.c 10147 2005-03-05 17:18:30Z gbazin $
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /**
25  *   \file
26  *   This file contains functions related to user interaction management
27  */
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32 #include <stdlib.h>                                      /* free(), strtol() */
33 #include <stdio.h>                                                   /* FILE */
34 #include <string.h>                                            /* strerror() */
35
36 #include <vlc/vlc.h>
37 #include <vlc/input.h>
38
39 #include "vlc_interaction.h"
40 #include "vlc_interface.h"
41 #include "vlc_playlist.h"
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 static void                  intf_InteractionInit( playlist_t *p_playlist );
47 static interaction_t *       intf_InteractionGet( vlc_object_t *p_this );
48 static void                  intf_InteractionSearchInterface( interaction_t *
49                                                           p_interaction );
50 static int                   intf_WaitAnswer( interaction_t *p_interact,
51                              interaction_dialog_t *p_dialog );
52 static int                   intf_Send( interaction_t *p_interact,
53                              interaction_dialog_t *p_dialog );
54 static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* , int );
55
56 /**
57  * Send an interaction element to the user
58  *
59  * \param p_this the calling vlc_object_t
60  * \param p_interact the interaction element
61  * \return VLC_SUCCESS or an error code
62  */
63 int  __intf_Interact( vlc_object_t *p_this, interaction_dialog_t *
64                                     p_dialog )
65 {
66
67     interaction_t *p_interaction = intf_InteractionGet( p_this );
68
69     /* Get an id, if we don't already have one */
70     if( p_dialog->i_id == 0 )
71     {
72         p_dialog->i_id = ++p_interaction->i_last_id;
73     }
74
75     if( p_dialog->i_type == INTERACT_ASK )
76     {
77         return intf_WaitAnswer( p_interaction, p_dialog );
78     }
79     else
80     {
81         return intf_Send( p_interaction, p_dialog );
82     }
83 }
84
85 /**
86  * Destroy the interaction system
87  */
88 void intf_InteractionDestroy( interaction_t *p_interaction )
89 {
90     /// \todo Code this, and call it
91 }
92
93 /**
94  * The main interaction processing loop
95  * This function is called from the playlist loop
96  *
97  * \param p_playlist the parent playlist
98  * \return nothing
99  */
100 void intf_InteractionManage( playlist_t *p_playlist )
101 {
102     int i_index;
103     interaction_t *p_interaction;
104
105     p_interaction = p_playlist->p_interaction;
106
107     // Nothing to do
108     if( p_interaction->i_dialogs == 0 ) return;
109
110     vlc_mutex_lock( &p_interaction->object_lock );
111
112     intf_InteractionSearchInterface( p_interaction );
113
114     if( !p_interaction->p_intf )
115     {
116         vlc_mutex_unlock( &p_interaction->object_lock );
117
118         /// \todo Remove all dialogs as we can't display them
119         return;
120     }
121
122     vlc_object_yield( p_interaction->p_intf );
123
124     for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
125     {
126         interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
127
128         switch( p_dialog->i_status )
129         {
130         case ANSWERED_DIALOG:
131             /// \todo Signal we have an answer
132             // - If have answer, signal what is waiting
133             // (vlc_cond ? dangerous in case of pb ?)
134
135             // Ask interface to hide it
136             msg_Dbg( p_interaction, "Hiding dialog %i", p_dialog->i_id );
137             p_interaction->p_intf->pf_interact( p_interaction->p_intf,
138                                                 p_dialog, INTERACT_HIDE );
139             p_dialog->i_status = HIDING_DIALOG;
140             break;
141         case UPDATED_DIALOG:
142             p_interaction->p_intf->pf_interact( p_interaction->p_intf,
143                                                 p_dialog, INTERACT_UPDATE );
144             p_dialog->i_status = SENT_DIALOG;
145             msg_Dbg( p_interaction, "Updating dialog %i, %i widgets",
146                                     p_dialog->i_id, p_dialog->i_widgets );
147             break;
148         case HIDDEN_DIALOG:
149             if( !p_dialog->b_reusable )
150             {
151                 /// \todo Destroy the dialog
152             }
153             break;
154         case NEW_DIALOG:
155             // This is truly a new dialog, send it.
156             p_interaction->p_intf->pf_interact( p_interaction->p_intf,
157                                                 p_dialog, INTERACT_NEW );
158             msg_Dbg( p_interaction, "Creating dialog %i, %i widgets",
159                                         p_dialog->i_id, p_dialog->i_widgets );
160             p_dialog->i_status = SENT_DIALOG;
161             break;
162         }
163     }
164
165     vlc_object_release( p_interaction->p_intf );
166
167     vlc_mutex_unlock( &p_playlist->p_interaction->object_lock );
168 }
169
170
171
172 #define INTERACT_INIT( new )                                            \
173         new = (interaction_dialog_t*)malloc(                            \
174                         sizeof( interaction_dialog_t ) );               \
175         new->i_widgets = 0;                                             \
176         new->pp_widgets = NULL;                                         \
177         new->psz_title = NULL;                                          \
178         new->psz_description = NULL;                                    \
179         new->i_id = 0;                                                  \
180         new->i_status = NEW_DIALOG;
181
182 #define INTERACT_FREE( new )                                            \
183         if( new->psz_title ) free( new->psz_title );                    \
184         if( new->psz_description ) free( new->psz_description );
185
186 /** Helper function to send a fatal message
187  *  \param p_this     Parent vlc_object
188  *  \param i_id       A predefined ID, 0 if not applicable
189  *  \param psz_title  Title for the dialog
190  *  \param psz_format The message to display
191  *  */
192 void __intf_UserFatal( vlc_object_t *p_this, int i_id,
193                        const char *psz_title,
194                        const char *psz_format, ... )
195 {
196     va_list args;
197     interaction_dialog_t *p_new = NULL;
198     user_widget_t *p_widget = NULL;
199
200     if( i_id > 0 )
201     {
202         p_new = intf_InteractionGetById( p_this, i_id );
203     }
204     if( !p_new )
205     {
206         INTERACT_INIT( p_new );
207         if( i_id > 0 ) p_new->i_id = i_id ;
208     }
209     else
210     {
211         p_new->i_status = UPDATED_DIALOG;
212     }
213
214     p_new->i_type = INTERACT_FATAL;
215     p_new->psz_title = strdup( psz_title );
216
217     p_widget = (user_widget_t* )malloc( sizeof( user_widget_t ) );
218
219     p_widget->i_type = WIDGET_TEXT;
220
221     va_start( args, psz_format );
222     vasprintf( &p_widget->psz_text, psz_format, args );
223     va_end( args );
224
225     INSERT_ELEM ( p_new->pp_widgets,
226                   p_new->i_widgets,
227                   p_new->i_widgets,
228                   p_widget );
229
230     intf_Interact( p_this, p_new );
231 }
232
233 #if 0
234 /** Helper function to build a progress bar
235  * \param p_this   Parent vlc object
236  */
237 interaction_dialog_t *__intf_ProgressBuild( vlc_object_t *p_this,
238                                             const char *psz_text )
239 {
240     interaction_dialog_t *p_new = (interaction_dialog_t *)malloc(
241                                         sizeof( interaction_dialog_t ) );
242
243
244     return p_new;
245 }
246 #endif
247
248
249
250 /**********************************************************************
251  * The following functions are local
252  **********************************************************************/
253
254 /* Get the interaction object. Create it if needed */
255 static interaction_t * intf_InteractionGet( vlc_object_t *p_this )
256 {
257     playlist_t *p_playlist;
258     interaction_t *p_interaction;
259
260     p_playlist = (playlist_t*) vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
261                                                 FIND_ANYWHERE );
262
263     if( !p_playlist )
264     {
265         return NULL;
266     }
267
268     if( p_playlist->p_interaction == NULL )
269     {
270         intf_InteractionInit( p_playlist );
271     }
272
273     p_interaction = p_playlist->p_interaction;
274
275     vlc_object_release( p_playlist );
276
277     return p_interaction;
278 }
279
280 /* Create the interaction object in the given playlist object */
281 static void intf_InteractionInit( playlist_t *p_playlist )
282 {
283     interaction_t *p_interaction;
284
285     msg_Dbg( p_playlist, "initializing interaction system" );
286
287     p_interaction = vlc_object_create( VLC_OBJECT( p_playlist ),
288                                        sizeof( interaction_t ) );
289     if( !p_interaction )
290     {
291         msg_Err( p_playlist,"out of memory" );
292         return;
293     }
294
295     p_interaction->i_dialogs = 0;
296     p_interaction->pp_dialogs = NULL;
297     p_interaction->p_intf = NULL;
298     p_interaction->i_last_id = DIALOG_LAST_PREDEFINED + 1;
299
300     vlc_mutex_init( p_interaction , &p_interaction->object_lock );
301
302     p_playlist->p_interaction  = p_interaction;
303 }
304
305 /* Look for an interface suitable for interaction */
306 static void intf_InteractionSearchInterface( interaction_t *p_interaction )
307 {
308     vlc_list_t  *p_list;
309     int          i_index;
310
311     p_interaction->p_intf = NULL;
312
313     p_list = vlc_list_find( p_interaction, VLC_OBJECT_INTF, FIND_ANYWHERE );
314     if( !p_list )
315     {
316         msg_Err( p_interaction, "Unable to create module list" );
317         return;
318     }
319
320     for( i_index = 0; i_index < p_list->i_count; i_index ++ )
321     {
322         intf_thread_t *p_intf = (intf_thread_t *)
323                                         p_list->p_values[i_index].p_object;
324         if( p_intf->pf_interact != NULL )
325         {
326             p_interaction->p_intf = p_intf;
327             break;
328         }
329     }
330     vlc_list_release ( p_list );
331 }
332
333 /* Add a dialog to the queue and wait for answer */
334 static int intf_WaitAnswer( interaction_t *p_interact, interaction_dialog_t *p_dialog )
335 {
336     // TODO: Add to queue, wait for answer
337     return VLC_SUCCESS;
338 }
339
340 /* Add a dialog to the queue and return */
341 static int intf_Send( interaction_t *p_interact, interaction_dialog_t *p_dialog )
342 {
343     vlc_mutex_lock( &p_interact->object_lock );
344
345     /// \todo Check first it does not exist !!!
346     INSERT_ELEM( p_interact->pp_dialogs,
347                  p_interact->i_dialogs,
348                  p_interact->i_dialogs,
349                  p_dialog );
350     vlc_mutex_unlock( &p_interact->object_lock );
351     return VLC_SUCCESS;
352 }
353
354 /* Find an interaction dialog by its id */
355 static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* p_this,
356                                                        int i_id )
357 {
358     interaction_t *p_interaction = intf_InteractionGet( p_this );
359     int i;
360
361     for( i = 0 ; i< p_interaction->i_dialogs; i++ )
362     {
363         if( p_interaction->pp_dialogs[i]->i_id == i_id )
364         {
365             return p_interaction->pp_dialogs[i];
366         }
367     }
368     return NULL;
369 }