]> git.sesse.net Git - vlc/blob - modules/codec/cmml/intf.c
Remove E_()
[vlc] / modules / codec / cmml / intf.c
1 /*****************************************************************************
2  * intf.c: interface for CMML annotations/hyperlinks
3  *****************************************************************************
4  * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
5  *                         Organisation (CSIRO) Australia
6  * Copyright (C) 2004 the VideoLAN team
7  *
8  * $Id$
9  *
10  * Authors: Andre Pang <Andre.Pang@csiro.au>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc/vlc.h>
35
36 #include <stdio.h>
37
38 #ifdef HAVE_UNISTD_H
39 #    include <unistd.h>
40 #endif
41
42 #include <vlc_codec.h>
43 #include <vlc_input.h>
44 #include <vlc_interface.h>
45 #include <vlc_vout.h>
46 #include <vlc_playlist.h>
47 #include <vlc_osd.h>
48
49 #include "vlc_keys.h"
50
51 #include "browser_open.h"
52 #include "history.h"
53 #include "xstrcat.h"
54 #include "xurl.h"
55
56 #undef  CMML_INTF_USE_TIMED_URIS
57
58 #undef  CMML_INTF_DEBUG
59 #undef  CMML_INTF_HISTORY_DEBUG
60
61 /*****************************************************************************
62  * intf_sys_t: description and status of interface
63  *****************************************************************************/
64 struct intf_sys_t
65 {
66     decoder_t *         p_cmml_decoder;
67     input_thread_t *    p_input;
68
69     int                 i_key_action;
70 };
71
72 struct navigation_history_t
73 {
74     int i_history_size;
75     int i_last_item;
76 };
77
78 /*****************************************************************************
79  * Local prototypes.
80  *****************************************************************************/
81
82 int          OpenIntf               ( vlc_object_t * );
83 void         CloseIntf              ( vlc_object_t * );
84
85 static int   InitThread                 ( intf_thread_t * );
86 static int   MouseEvent                 ( vlc_object_t *, char const *,
87                                           vlc_value_t, vlc_value_t, void * );
88 static int   KeyEvent                   ( vlc_object_t *, char const *,
89                                           vlc_value_t, vlc_value_t, void * );
90
91 static void  FollowAnchor               ( intf_thread_t * );
92 static void  GoBack                     ( intf_thread_t * );
93 static void  GoForward                  ( intf_thread_t * );
94
95 static int   FollowAnchorCallback       ( vlc_object_t *, char const *,
96                                           vlc_value_t, vlc_value_t, void * );
97 static int   GoBackCallback             ( vlc_object_t *, char const *,
98                                           vlc_value_t, vlc_value_t, void * );
99 static int   GoForwardCallback          ( vlc_object_t *, char const *,
100                                           vlc_value_t, vlc_value_t, void * );
101
102 static char *GetTimedURLFromPlaylistItem( intf_thread_t *, playlist_item_t * );
103 static char *GetTimedURIFragmentForTime ( int );
104 static int   GetCurrentTimeInSeconds    ( input_thread_t * );
105 static int   DisplayAnchor              ( intf_thread_t *, vout_thread_t *,
106                                           char *, char * );
107 static int   DisplayPendingAnchor       ( intf_thread_t *, vout_thread_t * );
108 static history_t * GetHistory           ( playlist_t * );
109 static void  ReplacePlaylistItem        ( playlist_t *, char * );
110
111 /* Exported functions */
112 static void RunIntf        ( intf_thread_t *p_intf );
113
114 /*****************************************************************************
115  * OpenIntf: initialize CMML interface
116  *****************************************************************************/
117 int OpenIntf ( vlc_object_t *p_this )
118 {
119     intf_thread_t *p_intf = (intf_thread_t *)p_this;
120
121     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
122     if( p_intf->p_sys == NULL )
123     {
124         return( 1 );
125     };
126
127     p_intf->pf_run = RunIntf;
128
129     var_AddCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
130     /* we also need to add the callback for "mouse-clicked", but do that later
131      * when we've found a p_vout */
132
133     var_Create( p_intf->p_libvlc, "browse-go-back", VLC_VAR_VOID );
134     var_AddCallback( p_intf->p_libvlc, "browse-go-back",
135                      GoBackCallback, p_intf );
136     var_Create( p_intf->p_libvlc, "browse-go-forward", VLC_VAR_VOID );
137     var_AddCallback( p_intf->p_libvlc, "browse-go-forward",
138                      GoForwardCallback, p_intf );
139     var_Create( p_intf->p_libvlc, "browse-follow-anchor", VLC_VAR_VOID );
140     var_AddCallback( p_intf->p_libvlc, "browse-follow-anchor",
141                      FollowAnchorCallback, p_intf );
142
143     return( 0 );
144 }
145
146 /*****************************************************************************
147  * CloseIntf: destroy dummy interface
148  *****************************************************************************/
149 void CloseIntf ( vlc_object_t *p_this )
150 {
151     intf_thread_t * p_intf = (intf_thread_t *)p_this;
152     vout_thread_t * p_vout;
153
154 #ifdef CMML_INTF_DEBUG
155     msg_Dbg( p_intf, "freeing CMML interface" );
156 #endif
157
158     /* erase the anchor text description from the video output if it exists */
159     p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
160     if( p_vout )
161     {
162         /* enable CMML as a subtitle track */
163         spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
164         vlc_object_release( p_vout );
165     }
166
167     var_DelCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
168
169     vlc_object_release( p_intf->p_sys->p_cmml_decoder );
170
171     free( p_intf->p_sys );
172 }
173
174
175 /*****************************************************************************
176  * RunIntf: main loop
177  *****************************************************************************/
178 static void RunIntf( intf_thread_t *p_intf )
179 {
180     vout_thread_t * p_vout = NULL;
181
182     if( InitThread( p_intf ) < 0 )
183     {
184         msg_Err( p_intf, "can't initialize CMML interface" );
185         return;
186     }
187 #ifdef CMML_INTF_DEBUG
188     msg_Dbg( p_intf, "CMML intf initialized" );
189 #endif
190
191     /* if video output is dying, disassociate ourselves from it */
192     if( p_vout && p_vout->b_die )
193     {
194         var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
195         vlc_object_release( p_vout );
196         p_vout = NULL;
197     }
198
199     /* Main loop */
200     while( !p_intf->b_die )
201     {
202
203         /* find a video output if we currently don't have one */
204         if( p_vout == NULL )
205         {
206             p_vout = vlc_object_find( p_intf->p_sys->p_input,
207                                       VLC_OBJECT_VOUT, FIND_CHILD );
208             if( p_vout )
209             {
210 #ifdef CMML_INTF_DEBUG
211                 msg_Dbg( p_intf, "found vout thread" );
212 #endif
213                 var_AddCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
214             }
215         }
216
217         vlc_mutex_lock( &p_intf->change_lock );
218
219         /*
220          * keyboard event
221          */
222         switch( p_intf->p_sys->i_key_action )
223         {
224             case ACTIONID_NAV_ACTIVATE:
225                 FollowAnchor( p_intf );
226                 break;
227             case ACTIONID_HISTORY_BACK:
228                 GoBack( p_intf );
229                 break;
230             case ACTIONID_HISTORY_FORWARD:
231                 GoForward( p_intf );
232                 break;
233             default:
234                 break;
235         }
236         p_intf->p_sys->i_key_action = 0;
237         vlc_mutex_unlock( &p_intf->change_lock );
238
239         (void) DisplayPendingAnchor( p_intf, p_vout );
240
241         /* Wait a bit */
242         msleep( INTF_IDLE_SLEEP );
243     }
244
245     /* if we're here, the video output is dying: release the vout object */
246
247     if( p_vout )
248     {
249         var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
250         vlc_object_release( p_vout );
251     }
252
253     vlc_object_release( p_intf->p_sys->p_input );
254 }
255
256 /*****************************************************************************
257  * DisplayPendingAnchor: get a pending anchor description/URL from the CMML
258  * decoder and display it on screen
259  *****************************************************************************/
260 static int DisplayPendingAnchor( intf_thread_t *p_intf, vout_thread_t *p_vout )
261 {
262     decoder_t *p_cmml_decoder;
263     char *psz_description = NULL;
264     char *psz_url = NULL;
265
266     intf_thread_t *p_primary_intf;
267     vlc_value_t val;
268
269     p_cmml_decoder = p_intf->p_sys->p_cmml_decoder;
270     if( var_Get( p_cmml_decoder, "psz-current-anchor-description", &val )
271             != VLC_SUCCESS )
272     {
273         return true;
274     }
275
276     if( !val.p_address )
277         return true;
278
279     psz_description = val.p_address;
280
281     if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val )
282             == VLC_SUCCESS )
283     {
284         psz_url = val.p_address;
285     }
286
287     if( p_vout != NULL )
288     {
289         /* don't display anchor if main interface can display it */
290         p_primary_intf = vlc_object_find( p_intf->p_libvlc, VLC_OBJECT_INTF,
291                 FIND_CHILD );
292
293         if( p_primary_intf )
294         {
295             if( var_Get( p_primary_intf, "intf-displays-cmml-description", &val )
296                     == VLC_SUCCESS )
297             {
298                 if( val.b_bool == true )
299                 {
300                     vlc_object_release( p_primary_intf );
301                     return true;
302                 }
303             }
304
305             vlc_object_release( p_primary_intf );
306         }
307
308         /* display anchor as subtitle on-screen */
309         if( DisplayAnchor( p_intf, p_vout, psz_description, psz_url )
310                 != VLC_SUCCESS )
311         {
312             /* text render unsuccessful: do nothing */
313             return false;
314         }
315
316         /* text render successful: clear description */
317         val.p_address = NULL;
318         if( var_Set( p_cmml_decoder, "psz-current-anchor-description", val )
319                 != VLC_SUCCESS )
320         {
321             msg_Dbg( p_intf,
322                      "reset of psz-current-anchor-description failed" );
323         }
324         free( psz_description );
325         psz_url = NULL;
326     }
327
328     return true;
329 }
330
331
332 /*****************************************************************************
333  * InitThread:
334  *****************************************************************************/
335 static int InitThread( intf_thread_t * p_intf )
336 {
337     /* We might need some locking here */
338     if( !p_intf->b_die )
339     {
340         input_thread_t * p_input;
341         decoder_t *p_cmml_decoder;
342
343         p_cmml_decoder = vlc_object_find( p_intf, VLC_OBJECT_DECODER, FIND_PARENT );
344         p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT );
345
346 #ifdef CMML_INTF_DEBUG
347         msg_Dbg( p_intf, "cmml decoder at %p, input thread at %p",
348                  p_cmml_decoder, p_input );
349 #endif
350
351         /* Maybe the input just died */
352         if( p_input == NULL )
353         {
354             return VLC_EGENERIC;
355         }
356
357         vlc_mutex_lock( &p_intf->change_lock );
358
359         p_intf->p_sys->p_input = p_input;
360         p_intf->p_sys->p_cmml_decoder = p_cmml_decoder;
361
362         p_intf->p_sys->i_key_action = 0;
363
364         vlc_mutex_unlock( &p_intf->change_lock );
365
366         return VLC_SUCCESS;
367     }
368     else
369     {
370         return VLC_EGENERIC;
371     }
372 }
373
374 /*****************************************************************************
375  * MouseEvent: callback for mouse events
376  *****************************************************************************/
377 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
378                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
379 {
380     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
381     VLC_UNUSED(oldval); VLC_UNUSED(newval);
382     VLC_UNUSED(p_data);
383     /* TODO: handle mouse clicks on the anchor text */
384
385     return VLC_SUCCESS;
386 }
387
388 /*****************************************************************************
389  * KeyEvent: callback for keyboard events
390  *****************************************************************************/
391 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
392                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
393 {
394     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
395     VLC_UNUSED(oldval); VLC_UNUSED(newval);
396     intf_thread_t *p_intf = (intf_thread_t *)p_data;
397     vlc_mutex_lock( &p_intf->change_lock );
398
399     p_intf->p_sys->i_key_action = newval.i_int;
400
401     vlc_mutex_unlock( &p_intf->change_lock );
402
403     return VLC_SUCCESS;
404 }
405
406 /*****************************************************************************
407  * FollowAnchor: follow the current anchor being displayed to the user
408  *****************************************************************************/
409 static void FollowAnchor ( intf_thread_t *p_intf )
410 {
411     intf_sys_t *p_sys;
412     decoder_t *p_cmml_decoder;
413     char *psz_url = NULL;
414     vlc_value_t val;
415
416     msg_Dbg( p_intf, "User followed anchor" );
417
418     p_sys = p_intf->p_sys;
419     p_cmml_decoder = p_sys->p_cmml_decoder;
420
421     if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val ) ==
422             VLC_SUCCESS )
423     {
424         if( val.p_address ) psz_url = val.p_address;
425     }
426
427 #ifdef CMML_INTF_DEBUG
428     msg_Dbg( p_intf, "Current URL is \"%s\"", psz_url );
429 #endif
430
431     if( psz_url )
432     {
433         playlist_t *p_playlist;
434         playlist_item_t *p_current_item;
435         char *psz_uri_to_load;
436         mtime_t i_seconds;
437         vlc_value_t time;
438
439         p_playlist = (playlist_t *) vlc_object_find( p_intf,
440                 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
441         if ( !p_playlist )
442         {
443             msg_Warn( p_intf, "can't find playlist" );
444             return;
445         }
446
447         /* Get new URL */
448         p_current_item = p_playlist->status.p_item;
449         char *psz_uri = input_item_GetURI( p_current_item->p_input );
450 #ifdef CMML_INTF_DEBUG
451         msg_Dbg( p_intf, "Current playlist item URL is \"%s\"", psz_uri );
452 #endif
453
454         psz_uri_to_load = XURL_Concat( psz_uri, psz_url );
455         free( psz_uri );
456
457 #ifdef CMML_INTF_DEBUG
458         msg_Dbg( p_intf, "URL to load is \"%s\"", psz_uri_to_load );
459 #endif
460
461         if( var_Get( p_intf->p_sys->p_input, "time", &time ) )
462         {
463             msg_Dbg( p_intf, "couldn't get time from current clip" );
464             time.i_time = 0;
465         }
466         i_seconds = time.i_time / 1000000;
467 #ifdef CMML_INTF_DEBUG
468         msg_Dbg( p_intf, "Current time is \"%lld\"", i_seconds );
469 #endif
470
471         /* TODO: we need a (much) more robust way of detecting whether
472          * the file's a media file ... */
473         if( strstr( psz_uri_to_load, ".anx" ) != NULL )
474         {
475             history_t *p_history = NULL;
476             history_item_t *p_history_item = NULL;
477             char *psz_timed_url;
478
479             p_history = GetHistory( p_playlist );
480
481             /* create history item */
482             psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
483             p_history_item = historyItem_New( psz_timed_url, psz_timed_url );
484             free( psz_timed_url );
485
486             if( !p_history_item )
487             {
488                 msg_Warn( p_intf, "could not initialise history item" );
489             }
490             else
491             {
492 #ifdef CMML_INTF_DEBUG
493                 msg_Dbg( p_intf, "history pre-index %d", p_history->i_index );
494 #endif
495                 history_PruneAndInsert( p_history, p_history_item );
496 #ifdef CMML_INTF_DEBUG
497                 msg_Dbg( p_intf, "new history item at %p, uri is \"%s\"",
498                          p_history_item, p_history_item->psz_uri );
499                 msg_Dbg( p_intf, "history index now %d", p_history->i_index );
500 #endif
501             }
502
503             /* free current-anchor-url */
504             free( psz_url );
505             val.p_address = NULL;
506             if( var_Set( p_cmml_decoder, "psz-current-anchor-url", val ) !=
507                     VLC_SUCCESS )
508             {
509                 msg_Dbg( p_intf, "couldn't reset psz-current-anchor-url" );
510             }
511
512             ReplacePlaylistItem( p_playlist, psz_uri_to_load );
513         }
514         else
515         {
516 #ifdef CMML_INTF_DEBUG
517             msg_Dbg( p_intf, "calling browser_Open with \"%s\"", psz_url );
518 #endif
519             (void) browser_Open( psz_url );
520             playlist_Control( p_playlist, PLAYLIST_PAUSE, true, 0 );
521         }
522
523         free( psz_uri_to_load );
524
525         vlc_object_release( p_playlist );
526     }
527 }
528
529 static
530 char *GetTimedURLFromPlaylistItem( intf_thread_t *p_intf,
531         playlist_item_t *p_current_item )
532 {
533 #ifdef CMML_INTF_USE_TIMED_URIS
534     char *psz_url = NULL;
535     char *psz_return_value = NULL;
536     char *psz_seconds = NULL;
537     int i_seconds;
538
539     char *psz_uri = input_item_GetURI( p_current_item->p_input );
540     psz_url = XURL_GetWithoutFragment( psz_uri );
541     free( psz_uri );
542
543     /* Get current time as a string */
544     if( XURL_IsFileURL( psz_url ) == true )
545         psz_url = xstrcat( psz_url, "#" );
546     else
547         psz_url = xstrcat( psz_url, "?" );
548
549     /* jump back to 2 seconds before where we are now */
550     i_seconds = GetCurrentTimeInSeconds( p_intf->p_sys->p_input ) - 2;
551     psz_seconds = GetTimedURIFragmentForTime( i_seconds < 0 ? 0 : i_seconds );
552     if( psz_seconds )
553     {
554         psz_url = xstrcat( psz_url, psz_seconds );
555         free( psz_seconds );
556         psz_return_value = psz_url;
557     }
558
559     return psz_return_value;
560 #else
561     VLC_UNUSED(p_intf);
562     void *p;
563
564     /* Suppress warning messages about unused functions */
565     p = GetTimedURIFragmentForTime; /* unused */
566     p = GetCurrentTimeInSeconds;    /* unused */
567
568     return input_item_GetURI( p_current_item->p_input );
569 #endif
570 }
571
572
573 /*
574  * Get the current time, rounded down to the nearest second
575  *
576  * http://www.ietf.org/internet-drafts/draft-pfeiffer-temporal-fragments-02.txt
577  */
578 static
579 int GetCurrentTimeInSeconds( input_thread_t *p_input )
580 {
581     vlc_value_t time;
582     mtime_t i_seconds;
583
584     var_Get( p_input, "time", &time );
585     i_seconds = time.i_time / 1000000;
586     return i_seconds;
587 }
588
589 static
590 char *GetTimedURIFragmentForTime( int seconds )
591 {
592     char *psz_time;
593
594     asprintf( &psz_time, "%d", seconds );
595     return psz_time;
596 }
597
598 static
599 int GoBackCallback( vlc_object_t *p_this, char const *psz_var,
600                     vlc_value_t oldval, vlc_value_t newval, void *p_data )
601 {
602     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
603     VLC_UNUSED(oldval); VLC_UNUSED(newval);
604     intf_thread_t *p_intf = (intf_thread_t *) p_data;
605     GoBack( p_intf );
606     return VLC_SUCCESS;
607 }
608
609 static
610 int GoForwardCallback( vlc_object_t *p_this, char const *psz_var,
611                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
612 {
613     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
614     VLC_UNUSED(oldval); VLC_UNUSED(newval);
615     intf_thread_t *p_intf = (intf_thread_t *) p_data;
616     GoForward( p_intf );
617     return VLC_SUCCESS;
618 }
619
620 static
621 int FollowAnchorCallback( vlc_object_t *p_this, char const *psz_var,
622                           vlc_value_t oldval, vlc_value_t newval,
623                           void *p_data )
624 {
625     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
626     VLC_UNUSED(oldval); VLC_UNUSED(newval);
627     intf_thread_t *p_intf = (intf_thread_t *) p_data;
628     FollowAnchor( p_intf );
629     return VLC_SUCCESS;
630 }
631
632 static
633 void GoBack( intf_thread_t *p_intf )
634 {
635     vlc_value_t history;
636     history_t *p_history = NULL;
637     history_item_t *p_history_item = NULL;
638     history_item_t *p_new_history_item = NULL;
639     playlist_t *p_playlist = NULL;
640     char *psz_timed_url = NULL;
641     playlist_item_t *p_current_item;
642
643 #ifdef CMML_INTF_DEBUG
644     msg_Dbg( p_intf, "Going back in navigation history" );
645 #endif
646
647     /* Find the playlist */
648     p_playlist = (playlist_t *) vlc_object_find( p_intf,
649             VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
650     if ( !p_playlist )
651     {
652         msg_Warn( p_intf, "can't find playlist" );
653         return;
654     }
655
656     /* Retrieve navigation history from playlist */
657     if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
658         !history.p_address )
659     {
660         /* History doesn't exist yet: ignore user's request */
661         msg_Warn( p_intf, "can't go back: no history exists yet" );
662         vlc_object_release( p_playlist );
663         return;
664     }
665
666     p_history = history.p_address;
667 #ifdef CMML_INTF_DEBUG
668     msg_Dbg( p_intf, "back: nav history retrieved from %p", p_history );
669     msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
670              p_history->p_xarray );
671 #endif
672
673     /* Check whether we can go back in the history */
674     if( history_CanGoBack( p_history ) == false )
675     {
676         msg_Warn( p_intf, "can't go back: already at beginning of history" );
677         vlc_object_release( p_playlist );
678         return;
679     }
680
681     p_current_item = p_playlist->status.p_item;
682
683     /* Save the currently-playing media in a new history item */
684     psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
685     p_new_history_item = historyItem_New( psz_timed_url, psz_timed_url );
686     free( psz_timed_url );
687
688     if( !p_new_history_item )
689     {
690 #ifdef CMML_INTF_DEBUG
691         msg_Dbg( p_intf, "back: could not initialise new history item" );
692 #endif
693         vlc_object_release( p_playlist );
694         return;
695     }
696
697     /* Go back in the history, saving the currently-playing item */
698     (void) history_GoBackSavingCurrentItem( p_history, p_new_history_item );
699     p_history_item = history_Item( p_history );
700
701 #ifdef CMML_INTF_DEBUG
702     msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
703     msg_Dbg( p_intf, "got previous history item: %p", p_history_item );
704     msg_Dbg( p_intf, "prev history item URL: \"%s\"", p_history_item->psz_uri );
705 #endif
706
707     ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
708     vlc_object_release( p_playlist );
709 }
710
711 static
712 void GoForward( intf_thread_t *p_intf )
713 {
714     vlc_value_t history;
715     history_t *p_history = NULL;
716     history_item_t *p_history_item = NULL;
717     history_item_t *p_new_history_item = NULL;
718     playlist_t *p_playlist = NULL;
719     playlist_item_t *p_current_item;
720
721 #ifdef CMML_INTF_DEBUG
722     msg_Dbg( p_intf, "Going forward in navigation history" );
723 #endif
724
725     /* Find the playlist */
726     p_playlist = (playlist_t *) vlc_object_find( p_intf,
727             VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
728     if ( !p_playlist )
729     {
730         msg_Warn( p_intf, "can't find playlist" );
731         return;
732     }
733
734     /* Retrieve navigation history from playlist */
735     if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
736         !history.p_address )
737     {
738         /* History doesn't exist yet: ignore user's request */
739         msg_Warn( p_intf, "can't go back: no history exists yet" );
740         vlc_object_release( p_playlist );
741         return;
742     }
743
744     p_history = history.p_address;
745 #ifdef CMML_INTF_DEBUG
746     msg_Dbg( p_intf, "forward: nav history retrieved from %p", p_history );
747     msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
748              p_history->p_xarray );
749 #endif
750
751     /* Check whether we can go forward in the history */
752     if( history_CanGoForward( p_history ) == false )
753     {
754         msg_Warn( p_intf, "can't go forward: already at end of history" );
755         vlc_object_release( p_playlist );
756         return;
757     }
758
759     /* Save the currently-playing media in a new history item */
760     p_new_history_item = malloc( sizeof(history_item_t) );
761     if( !p_new_history_item )
762     {
763 #ifdef CMML_INTF_DEBUG
764         msg_Dbg( p_intf, "forward: could not initialise new history item" );
765 #endif
766         vlc_object_release( p_playlist );
767         return;
768     }
769     p_current_item = p_playlist->status.p_item;
770     p_new_history_item->psz_uri = GetTimedURLFromPlaylistItem( p_intf,
771             p_current_item );
772     p_new_history_item->psz_name = p_new_history_item->psz_uri;
773
774     /* Go forward in the history, saving the currently-playing item */
775     (void) history_GoForwardSavingCurrentItem( p_history, p_new_history_item );
776     p_history_item = history_Item( p_history );
777
778 #ifdef CMML_INTF_DEBUG
779     msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
780     msg_Dbg( p_intf, "got next history item: %p", p_history_item );
781     msg_Dbg( p_intf, "next history item URL: \"%s\"", p_history_item->psz_uri );
782 #endif
783
784     ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
785     vlc_object_release( p_playlist );
786 }
787
788 static void ReplacePlaylistItem( playlist_t *p_playlist, char *psz_uri )
789 {
790     playlist_Stop( p_playlist );
791     (void) playlist_Add( p_playlist, psz_uri, psz_uri,
792                          PLAYLIST_INSERT /* FIXME: used to be PLAYLIST_REPLACE */, PLAYLIST_END|PLAYLIST_GO, true /* FIXME: p_playlist->status.i_index */,
793                          false);
794 }
795
796 /****************************************************************************
797  * DisplayAnchor: displays an anchor on the given video output
798  ****************************************************************************/
799 static int DisplayAnchor( intf_thread_t *p_intf,
800         vout_thread_t *p_vout,
801         char *psz_anchor_description,
802         char *psz_anchor_url )
803 {
804     int i_margin_h, i_margin_v;
805     mtime_t i_now;
806
807     i_margin_h = 0;
808     i_margin_v = 10;
809
810     i_now = mdate();
811
812     if( p_vout )
813     {
814         if( psz_anchor_url )
815         {
816             /* Should display subtitle underlined and in blue, but it looks
817              * like VLC doesn't implement any text styles yet.  D'oh! */
818             // p_style = &blue_with_underline;
819
820         }
821
822         /* TODO: p_subpicture doesn't have the proper i_x and i_y
823          * coordinates.  Need to look at the subpicture display system to
824          * work out why. */
825         if ( vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
826                 psz_anchor_description, NULL, OSD_ALIGN_BOTTOM,
827                 i_margin_h, i_margin_v, i_now, 0 ) == VLC_SUCCESS )
828         {
829             /* Displayed successfully */
830         }
831         else
832         {
833             return VLC_EGENERIC;
834         }
835     }
836     else
837     {
838         msg_Dbg( p_intf, "DisplayAnchor couldn't find a video output" );
839         return VLC_EGENERIC;
840     }
841
842     return VLC_SUCCESS;
843 }
844
845 static history_t * GetHistory( playlist_t *p_playlist )
846 {
847     vlc_value_t val;
848     history_t *p_history = NULL;
849
850     if( var_Get( p_playlist, "navigation-history", &val ) != VLC_SUCCESS )
851     {
852         /* history doesn't exist yet: need to create it */
853         history_t *new_history = history_New();
854         val.p_address = new_history;
855         var_Create( p_playlist, "navigation-history",
856                 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
857         if( var_Set( p_playlist, "navigation-history", val ) != VLC_SUCCESS )
858         {
859             msg_Warn( p_playlist, "could not initialise history" );
860         }
861         else
862         {
863             p_history = new_history;
864 #ifdef CMML_INTF_HISTORY_DEBUG
865             msg_Dbg( p_playlist, "nav history created at %p", new_history );
866             msg_Dbg( p_playlist, "nav history index:%d, p_xarray:%p",
867                      p_history->i_index, p_history->p_xarray );
868 #endif
869         }
870     }
871     else
872     {
873         p_history = val.p_address;
874 #ifdef CMML_INTF_HISTORY_DEBUG
875         msg_Dbg( p_playlist, "nav history retrieved from %p", p_history );
876 #endif
877     }
878
879     return p_history;
880 }
881