ebca839f2d5829778af65edfa93674be5affd7fa
[vlc.git] / modules / gui / ncurses.c
1 /*****************************************************************************
2  * ncurses.c : NCurses interface for vlc
3  *****************************************************************************
4  * Copyright (C) 2001-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sam Hocevar <sam@zoy.org>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Yoann Peronneau <yoann@videolan.org>
10  *          Derk-Jan Hartman <hartman at videolan dot org>
11  *          Rafaël Carré <funman@videolanorg>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <vlc/vlc.h>
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <time.h>
35
36 #ifdef HAVE_NCURSESW
37 #   define _XOPEN_SOURCE_EXTENDED 1
38 #endif
39
40 #include <ncurses.h>
41
42 #include <vlc_interface.h>
43 #include <vlc_vout.h>
44 #include <vlc_aout.h>
45 #include <vlc_charset.h>
46 #include <vlc_input.h>
47 #include <vlc_playlist.h>
48 #include <vlc_meta.h>
49
50 #ifdef HAVE_SYS_STAT_H
51 #   include <sys/stat.h>
52 #endif
53 #if (!defined( WIN32 ) || defined(__MINGW32__))
54 /* Mingw has its own version of dirent */
55 #   include <dirent.h>
56 #endif
57
58 #ifdef HAVE_CDDAX
59 #define CDDA_MRL "cddax://"
60 #else
61 #define CDDA_MRL "cdda://"
62 #endif
63
64 #ifdef HAVE_VCDX
65 #define VCD_MRL "vcdx://"
66 #else
67 #define VCD_MRL "vcd://"
68 #endif
69
70 #define SEARCH_CHAIN_SIZE 20
71 #define OPEN_CHAIN_SIZE 50
72
73 /*****************************************************************************
74  * Local prototypes.
75  *****************************************************************************/
76 static int  Open           ( vlc_object_t * );
77 static void Close          ( vlc_object_t * );
78
79 static void Run            ( intf_thread_t * );
80 static void PlayPause      ( intf_thread_t * );
81 static void Eject          ( intf_thread_t * );
82
83 static int  HandleKey      ( intf_thread_t *, int );
84 static void Redraw         ( intf_thread_t *, time_t * );
85
86 static playlist_item_t *PlaylistGetRoot( intf_thread_t * );
87 static void PlaylistRebuild( intf_thread_t * );
88 static void PlaylistAddNode( intf_thread_t *, playlist_item_t *, int, const char *);
89 static void PlaylistDestroy( intf_thread_t * );
90 static int  PlaylistChanged( vlc_object_t *, const char *, vlc_value_t,
91                              vlc_value_t, void * );
92 static inline vlc_bool_t PlaylistIsPlaying( intf_thread_t *,
93                                             playlist_item_t * );
94 static void FindIndex      ( intf_thread_t * );
95 static void SearchPlaylist ( intf_thread_t *, char * );
96 static int  SubSearchPlaylist( intf_thread_t *, char *, int, int );
97
98 static void ManageSlider   ( intf_thread_t * );
99 static void ReadDir        ( intf_thread_t * );
100
101 /*****************************************************************************
102  * Module descriptor
103  *****************************************************************************/
104
105 #define BROWSE_TEXT N_("Filebrowser starting point")
106 #define BROWSE_LONGTEXT N_( \
107     "This option allows you to specify the directory the ncurses filebrowser " \
108     "will show you initially.")
109
110 vlc_module_begin();
111     set_shortname( "Ncurses" );
112     set_description( _("Ncurses interface") );
113     set_capability( "interface", 10 );
114     set_category( CAT_INTERFACE );
115     set_subcategory( SUBCAT_INTERFACE_MAIN );
116     set_callbacks( Open, Close );
117     add_shortcut( "curses" );
118     add_directory( "browse-dir", NULL, NULL, BROWSE_TEXT, BROWSE_LONGTEXT, VLC_FALSE );
119 vlc_module_end();
120
121 /*****************************************************************************
122  * intf_sys_t: description and status of ncurses interface
123  *****************************************************************************/
124 enum
125 {
126     BOX_NONE,
127     BOX_HELP,
128     BOX_INFO,
129     BOX_LOG,
130     BOX_PLAYLIST,
131     BOX_SEARCH,
132     BOX_OPEN,
133     BOX_BROWSE,
134     BOX_META
135 };
136 enum
137 {
138     VIEW_CATEGORY,
139     VIEW_ONELEVEL
140 };
141 struct dir_entry_t
142 {
143     vlc_bool_t  b_file;
144     char        *psz_path;
145 };
146 struct pl_item_t
147 {
148     playlist_item_t *p_item;
149     char            *psz_display;
150 };
151 struct intf_sys_t
152 {
153     playlist_t     *p_playlist;
154     input_thread_t *p_input;
155
156     float           f_slider;
157     float           f_slider_old;
158
159     WINDOW          *w;
160
161     int             i_box_type;
162     int             i_box_y;
163     int             i_box_lines;
164     int             i_box_lines_total;
165     int             i_box_start;
166
167     int             i_box_plidx;    /* Playlist index */
168     int             b_box_plidx_follow;
169     int             i_box_bidx;     /* browser index */
170
171     int             b_box_cleared;
172
173     msg_subscription_t* p_sub;                  /* message bank subscription */
174
175     char            *psz_search_chain;          /* for playlist searching    */
176     char            *psz_old_search;            /* for searching next        */
177     int             i_before_search;
178
179     char            *psz_open_chain;
180     char             psz_partial_keys[7];
181
182     char            *psz_current_dir;
183     int             i_dir_entries;
184     struct dir_entry_t  **pp_dir_entries;
185     vlc_bool_t      b_show_hidden_files;
186
187     int             i_current_view;             /* playlist view             */
188     struct pl_item_t    **pp_plist;
189     int             i_plist_entries;
190     vlc_bool_t      b_need_update;              /* for playlist view         */
191
192     int             i_verbose;                  /* stores verbosity level    */
193 };
194
195 static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title );
196 static void DrawLine( WINDOW *win, int y, int x, int w );
197 static void DrawEmptyLine( WINDOW *win, int y, int x, int w );
198
199 /*****************************************************************************
200  * Open: initialize and create window
201  *****************************************************************************/
202 static int Open( vlc_object_t *p_this )
203 {
204     intf_thread_t *p_intf = (intf_thread_t *)p_this;
205     intf_sys_t    *p_sys;
206     vlc_value_t    val;
207
208     /* Allocate instance and initialize some members */
209     p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
210     p_sys->p_playlist = NULL;
211     p_sys->p_input = NULL;
212     p_sys->f_slider = 0.0;
213     p_sys->f_slider_old = 0.0;
214     p_sys->i_box_type = BOX_PLAYLIST;
215     p_sys->i_box_lines = 0;
216     p_sys->i_box_start= 0;
217     p_sys->i_box_lines_total = 0;
218     p_sys->b_box_plidx_follow = VLC_TRUE;
219     p_sys->b_box_cleared = VLC_FALSE;
220     p_sys->i_box_plidx = 0;
221     p_sys->i_box_bidx = 0;
222     p_sys->p_sub = msg_Subscribe( p_intf, MSG_QUEUE_NORMAL );
223     memset( p_sys->psz_partial_keys, 0, sizeof( p_sys->psz_partial_keys ) );
224
225     /* Initialize the curses library */
226     p_sys->w = initscr();
227     keypad( p_sys->w, TRUE );
228     /* Don't do NL -> CR/NL */
229     nonl();
230     /* Take input chars one at a time */
231     cbreak();
232     /* Don't echo */
233     noecho();
234
235     curs_set(0);
236     timeout(0);
237
238     clear();
239
240     /* exported function */
241     p_intf->pf_run = Run;
242
243     /* Remember verbosity level */
244     var_Get( p_intf->p_libvlc, "verbose", &val );
245     p_sys->i_verbose = val.i_int;
246     /* Set quiet mode */
247     val.i_int = -1;
248     var_Set( p_intf->p_libvlc, "verbose", val );
249
250     /* Set defaul playlist view */
251     p_sys->i_current_view = VIEW_CATEGORY;
252     p_sys->pp_plist = NULL;
253     p_sys->i_plist_entries = 0;
254     p_sys->b_need_update = VLC_FALSE;
255
256     /* Initialize search chain */
257     p_sys->psz_search_chain = (char *)malloc( SEARCH_CHAIN_SIZE + 1 );
258     p_sys->psz_old_search = NULL;
259     p_sys->i_before_search = 0;
260
261     /* Initialize open chain */
262     p_sys->psz_open_chain = (char *)malloc( OPEN_CHAIN_SIZE + 1 );
263
264     /* Initialize browser options */
265     var_Create( p_intf, "browse-dir", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
266     var_Get( p_intf, "browse-dir", &val);
267
268     if( val.psz_string && *val.psz_string )
269     {
270         p_sys->psz_current_dir = strdup( val.psz_string );
271         free( val.psz_string );
272     }
273     else
274     {
275         p_sys->psz_current_dir = strdup( p_intf->p_libvlc->psz_homedir );
276     }
277
278     p_sys->i_dir_entries = 0;
279     p_sys->pp_dir_entries = NULL;
280     p_sys->b_show_hidden_files = VLC_FALSE;
281     ReadDir( p_intf );
282
283     return VLC_SUCCESS;
284 }
285
286 /*****************************************************************************
287  * Close: destroy interface window
288  *****************************************************************************/
289 static void Close( vlc_object_t *p_this )
290 {
291     intf_thread_t *p_intf = (intf_thread_t *)p_this;
292     intf_sys_t    *p_sys = p_intf->p_sys;
293     int i;
294
295     var_DelCallback( p_sys->p_playlist, "intf-change", PlaylistChanged,
296                      p_intf );
297     var_DelCallback( p_sys->p_playlist, "item-append", PlaylistChanged,
298                      p_intf );
299
300     PlaylistDestroy( p_intf );
301
302     for( i = 0; i < p_sys->i_dir_entries; i++ )
303     {
304         struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
305         if( p_dir_entry->psz_path ) free( p_dir_entry->psz_path );
306         REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
307         if( p_dir_entry ) free( p_dir_entry );
308     }
309     p_sys->pp_dir_entries = NULL;
310
311     if( p_sys->psz_current_dir ) free( p_sys->psz_current_dir );
312     if( p_sys->psz_search_chain ) free( p_sys->psz_search_chain );
313     if( p_sys->psz_old_search ) free( p_sys->psz_old_search );
314     if( p_sys->psz_open_chain ) free( p_sys->psz_open_chain );
315
316     if( p_sys->p_input )
317     {
318         vlc_object_release( p_sys->p_input );
319     }
320     if( p_sys->p_playlist )
321     {
322         vlc_object_release( p_sys->p_playlist );
323     }
324
325     /* Close the ncurses interface */
326     endwin();
327
328     msg_Unsubscribe( p_intf, p_sys->p_sub );
329
330     /* Restores initial verbose setting */
331     vlc_value_t val;
332     val.i_int = p_sys->i_verbose;
333     var_Set( p_intf->p_libvlc, "verbose", val );
334
335     /* Destroy structure */
336     free( p_sys );
337 }
338
339 /*****************************************************************************
340  * Run: ncurses thread
341  *****************************************************************************/
342 static void Run( intf_thread_t *p_intf )
343 {
344     intf_sys_t    *p_sys = p_intf->p_sys;
345
346     int i_key;
347     time_t t_last_refresh;
348
349     /*
350      * force drawing the interface for the first time
351      */
352     t_last_refresh = ( time( 0 ) - 1);
353
354     while( !intf_ShouldDie( p_intf ) )
355     {
356         msleep( INTF_IDLE_SLEEP );
357
358         /* Update the input */
359         if( p_sys->p_playlist == NULL )
360         {
361             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
362                                                  FIND_ANYWHERE );
363             if( p_sys->p_playlist )
364             {
365                 var_AddCallback( p_sys->p_playlist, "intf-change",
366                                  PlaylistChanged, p_intf );
367                 var_AddCallback( p_sys->p_playlist, "item-append",
368                                  PlaylistChanged, p_intf );
369             }
370         }
371         if( p_sys->p_playlist )
372         {
373             vlc_mutex_lock( &p_sys->p_playlist->object_lock );
374             if( p_sys->p_input == NULL )
375             {
376                 p_sys->p_input = p_sys->p_playlist->p_input;
377                 if( p_sys->p_input )
378                 {
379                     if( !p_sys->p_input->b_dead )
380                     {
381                         vlc_object_yield( p_sys->p_input );
382                     }
383                 }
384             }
385             else if( p_sys->p_input->b_dead )
386             {
387                 vlc_object_release( p_sys->p_input );
388                 p_sys->p_input = NULL;
389                 p_sys->f_slider = p_sys->f_slider_old = 0.0;
390                 p_sys->b_box_cleared = VLC_FALSE;
391             }
392             vlc_mutex_unlock( &p_sys->p_playlist->object_lock );
393         }
394
395         if( p_sys->b_box_plidx_follow && p_sys->p_playlist->status.p_item )
396         {
397             FindIndex( p_intf );
398         }
399
400         while( ( i_key = getch() ) != -1 )
401         {
402             /*
403              * HandleKey returns 1 if the screen needs to be redrawn
404              */
405             if( HandleKey( p_intf, i_key ) )
406             {
407                 Redraw( p_intf, &t_last_refresh );
408             }
409         }
410         /* Hack */
411         if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
412         {
413             clear();
414             Redraw( p_intf, &t_last_refresh );
415             p_sys->b_box_cleared = VLC_TRUE;
416         }
417
418         /*
419          * redraw the screen every second
420          */
421         if( (time(0) - t_last_refresh) >= 1 )
422         {
423             ManageSlider( p_intf );
424             Redraw( p_intf, &t_last_refresh );
425         }
426     }
427 }
428
429 /* following functions are local */
430 static char *KeyToUTF8( int i_key, char *psz_part )
431 {
432     char *psz_utf8;
433     int len = strlen( psz_part );
434     if( len == 6 )
435     {
436         /* overflow error - should not happen */
437         memset( psz_part, 0, 6 );
438         return NULL;
439     }
440
441     psz_part[len] = (char)i_key;
442
443 #ifdef HAVE_NCURSESW
444     psz_utf8 = strdup( psz_part );
445 #else
446     psz_utf8 = FromLocaleDup( psz_part );
447
448     /* Ugly check for incomplete bytes sequences
449      * (in case of non-UTF8 multibyte local encoding) */
450     char *psz;
451     for( psz = psz_utf8; *psz; psz++ )
452         if( ( *psz == '?' ) && ( *psz_utf8 != '?' ) )
453         {
454             /* incomplete bytes sequence detected
455              * (VLC core inserted dummy question marks) */
456             free( psz_utf8 );
457             return NULL;
458         }
459
460     /* Check for incomplete UTF8 bytes sequence */
461     if( EnsureUTF8( psz_utf8 ) == NULL )
462     {
463         free( psz_utf8 );
464         return NULL;
465     }
466 #endif
467
468     memset( psz_part, 0, 6 );
469     return psz_utf8;
470 }
471
472 static inline int RemoveLastUTF8Entity( char *psz, int len )
473 {
474     while( len && ( (psz[--len] & 0xc0) == 0x80 ) );
475                        /* UTF8 continuation byte */
476
477     psz[len] = '\0';
478     return len;
479 }
480
481 static int HandleKey( intf_thread_t *p_intf, int i_key )
482 {
483     intf_sys_t *p_sys = p_intf->p_sys;
484     vlc_value_t val;
485
486     if( p_sys->i_box_type == BOX_PLAYLIST && p_sys->p_playlist )
487     {
488         int b_ret = VLC_TRUE;
489         vlc_bool_t b_box_plidx_follow = VLC_FALSE;
490
491         switch( i_key )
492         {
493             vlc_value_t val;
494             /* Playlist Settings */
495             case 'r':
496                 var_Get( p_sys->p_playlist, "random", &val );
497                 val.b_bool = !val.b_bool;
498                 var_Set( p_sys->p_playlist, "random", val );
499                 return 1;
500             case 'l':
501                 var_Get( p_sys->p_playlist, "loop", &val );
502                 val.b_bool = !val.b_bool;
503                 var_Set( p_sys->p_playlist, "loop", val );
504                 return 1;
505             case 'R':
506                 var_Get( p_sys->p_playlist, "repeat", &val );
507                 val.b_bool = !val.b_bool;
508                 var_Set( p_sys->p_playlist, "repeat", val );
509                 return 1;
510
511             /* Playlist sort */
512             case 'o':
513                 playlist_RecursiveNodeSort( p_sys->p_playlist,
514                                             PlaylistGetRoot( p_intf ),
515                                             SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
516                 p_sys->b_need_update = VLC_TRUE;
517                 return 1;
518             case 'O':
519                 playlist_RecursiveNodeSort( p_sys->p_playlist,
520                                             PlaylistGetRoot( p_intf ),
521                                             SORT_TITLE_NODES_FIRST, ORDER_REVERSE );
522                 p_sys->b_need_update = VLC_TRUE;
523                 return 1;
524
525             /* Playlist view */
526             case 'v':
527                 switch( p_sys->i_current_view )
528                 {
529                     case VIEW_CATEGORY:
530                         p_sys->i_current_view = VIEW_ONELEVEL;
531                         break;
532                     default:
533                         p_sys->i_current_view = VIEW_CATEGORY;
534                 }
535                 //p_sys->b_need_update = VLC_TRUE;
536                 PlaylistRebuild( p_intf );
537                 return 1;
538
539             /* Playlist navigation */
540             case 'g':
541                 FindIndex( p_intf );
542                 break;
543             case KEY_HOME:
544                 p_sys->i_box_plidx = 0;
545                 break;
546 #ifdef __FreeBSD__
547 /* workaround for FreeBSD + xterm:
548  * see http://www.nabble.com/curses-vs.-xterm-key-mismatch-t3574377.html */
549             case KEY_SELECT:
550 #endif
551             case KEY_END:
552                 p_sys->i_box_plidx = p_sys->p_playlist->items.i_size - 1;
553                 break;
554             case KEY_UP:
555                 p_sys->i_box_plidx--;
556                 break;
557             case KEY_DOWN:
558                 p_sys->i_box_plidx++;
559                 break;
560             case KEY_PPAGE:
561                 p_sys->i_box_plidx -= p_sys->i_box_lines;
562                 break;
563             case KEY_NPAGE:
564                 p_sys->i_box_plidx += p_sys->i_box_lines;
565                 break;
566             case 'D':
567             case KEY_BACKSPACE:
568             case KEY_DC:
569             {
570                 playlist_t *p_playlist = p_sys->p_playlist;
571                 playlist_item_t *p_item;
572
573                 vlc_mutex_lock( &p_playlist->object_lock );
574                 p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
575                 if( p_item->i_children == -1 )
576                 {
577                     playlist_DeleteFromInput( p_playlist,
578                                               p_item->p_input->i_id, VLC_TRUE );
579                 }
580                 else
581                 {
582                     playlist_NodeDelete( p_playlist, p_item,
583                                          VLC_TRUE , VLC_FALSE );
584                 }
585                 vlc_mutex_unlock( &p_playlist->object_lock );
586                 PlaylistRebuild( p_intf );
587                 break;
588             }
589
590             case KEY_ENTER:
591             case 0x0d:
592                 if( !p_sys->pp_plist || !p_sys->pp_plist[p_sys->i_box_plidx] )
593                 {
594                     b_ret = VLC_FALSE;
595                     break;
596                 }
597                 if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
598                         == -1 )
599                 {
600                     playlist_item_t *p_item, *p_parent;
601                     p_item = p_parent =
602                             p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
603
604                     if( !p_parent )
605                         p_parent = p_sys->p_playlist->p_root_onelevel;
606                     while( p_parent->p_parent )
607                         p_parent = p_parent->p_parent;
608                     playlist_Control( p_sys->p_playlist, PLAYLIST_VIEWPLAY,
609                                       VLC_TRUE, p_parent, p_item );
610                 }
611                 else if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
612                         == 0 )
613                 {   /* We only want to set the current node */
614                     vlc_object_lock( p_sys->p_playlist );
615
616                     p_sys->p_playlist->request.p_node = 
617                         p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
618                     p_sys->p_playlist->request.i_skip = 0;
619                     p_sys->p_playlist->request.p_item =
620                         p_sys->p_playlist->status.p_item;
621                     p_sys->p_playlist->request.b_request = VLC_TRUE;
622                     p_sys->p_playlist->request.i_status = PLAYLIST_STOPPED;
623
624                     vlc_object_unlock( p_sys->p_playlist );
625                 }
626                 else
627                 {
628                     playlist_Control( p_sys->p_playlist, PLAYLIST_VIEWPLAY,
629                         VLC_TRUE,
630                         p_sys->pp_plist[p_sys->i_box_plidx]->p_item,
631                         NULL );
632                 }
633                 b_box_plidx_follow = VLC_TRUE;
634                 break;
635             default:
636                 b_ret = VLC_FALSE;
637                 break;
638         }
639
640         if( b_ret )
641         {
642             int i_max = p_sys->i_plist_entries;
643             if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
644             if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
645             if( PlaylistIsPlaying( p_intf,
646                     p_sys->pp_plist[p_sys->i_box_plidx]->p_item ) )
647                 b_box_plidx_follow = VLC_TRUE;
648             p_sys->b_box_plidx_follow = b_box_plidx_follow;
649             return 1;
650         }
651     }
652     if( p_sys->i_box_type == BOX_BROWSE )
653     {
654         vlc_bool_t b_ret = VLC_TRUE;
655         /* Browser navigation */
656         switch( i_key )
657         {
658             case KEY_HOME:
659                 p_sys->i_box_bidx = 0;
660                 break;
661 #ifdef __FreeBSD__
662             case KEY_SELECT:
663 #endif
664             case KEY_END:
665                 p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
666                 break;
667             case KEY_UP:
668                 p_sys->i_box_bidx--;
669                 break;
670             case KEY_DOWN:
671                 p_sys->i_box_bidx++;
672                 break;
673             case KEY_PPAGE:
674                 p_sys->i_box_bidx -= p_sys->i_box_lines;
675                 break;
676             case KEY_NPAGE:
677                 p_sys->i_box_bidx += p_sys->i_box_lines;
678                 break;
679             case '.': /* Toggle show hidden files */
680                 p_sys->b_show_hidden_files = ( p_sys->b_show_hidden_files ==
681                     VLC_TRUE ? VLC_FALSE : VLC_TRUE );
682                 ReadDir( p_intf );
683                 break;
684
685             case KEY_ENTER:
686             case 0x0d:
687             case ' ':
688                 if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file || i_key == ' ' )
689                 {
690                     int i_size_entry = strlen( p_sys->psz_current_dir ) +
691                                        strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
692                     char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
693
694                     sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
695
696                     playlist_item_t *p_parent = p_sys->p_playlist->status.p_node;
697                     if( !p_parent )
698                         p_parent = p_sys->p_playlist->p_local_onelevel;
699
700                     while( p_parent->p_parent && p_parent->p_parent->p_parent )
701                         p_parent = p_parent->p_parent;
702
703                     playlist_Add( p_sys->p_playlist, psz_uri, NULL,
704                                   PLAYLIST_APPEND, PLAYLIST_END,
705                                   p_parent->p_input == 
706                                     p_sys->p_playlist->p_local_onelevel->p_input
707                                   , VLC_FALSE );
708                     p_sys->i_box_type = BOX_PLAYLIST;
709                     free( psz_uri );
710                 }
711                 else
712                 {
713                     int i_size_entry = strlen( p_sys->psz_current_dir ) +
714                                        strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
715                     char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
716
717                     sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
718
719                     p_sys->psz_current_dir = strdup( psz_uri );
720                     ReadDir( p_intf );
721                     free( psz_uri );
722                 }
723                 break;
724             default:
725                 b_ret = VLC_FALSE;
726                 break;
727         }
728         if( b_ret )
729         {
730             if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
731             if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
732             return 1;
733         }
734     }
735     else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO ||
736              p_sys->i_box_type == BOX_META )
737     {
738         switch( i_key )
739         {
740             case KEY_HOME:
741                 p_sys->i_box_start = 0;
742                 return 1;
743 #ifdef __FreeBSD__
744             case KEY_SELECT:
745 #endif
746             case KEY_END:
747                 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
748                 return 1;
749             case KEY_UP:
750                 if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
751                 return 1;
752             case KEY_DOWN:
753                 if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
754                 {
755                     p_sys->i_box_start++;
756                 }
757                 return 1;
758             case KEY_PPAGE:
759                 p_sys->i_box_start -= p_sys->i_box_lines;
760                 if( p_sys->i_box_start < 0 ) p_sys->i_box_start = 0;
761                 return 1;
762             case KEY_NPAGE:
763                 p_sys->i_box_start += p_sys->i_box_lines;
764                 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
765                 {
766                     p_sys->i_box_start = p_sys->i_box_lines_total - 1;
767                 }
768                 return 1;
769             default:
770                 break;
771         }
772     }
773     else if( p_sys->i_box_type == BOX_NONE )
774     {
775         switch( i_key )
776         {
777             case KEY_HOME:
778                 p_sys->f_slider = 0;
779                 ManageSlider( p_intf );
780                 return 1;
781 #ifdef __FreeBSD__
782             case KEY_SELECT:
783 #endif
784             case KEY_END:
785                 p_sys->f_slider = 99.9;
786                 ManageSlider( p_intf );
787                 return 1;
788             case KEY_UP:
789                 p_sys->f_slider += 5.0;
790                 if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
791                 ManageSlider( p_intf );
792                 return 1;
793             case KEY_DOWN:
794                 p_sys->f_slider -= 5.0;
795                 if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
796                 ManageSlider( p_intf );
797                 return 1;
798
799             default:
800                 break;
801         }
802     }
803     else if( p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain )
804     {
805         int i_chain_len;
806         i_chain_len = strlen( p_sys->psz_search_chain );
807         switch( i_key )
808         {
809             case 0x0c:      /* ^l */
810                 clear();
811                 return 1;
812             case KEY_ENTER:
813             case 0x0d:
814                 if( i_chain_len > 0 )
815                 {
816                     p_sys->psz_old_search = strdup( p_sys->psz_search_chain );
817                 }
818                 else if( p_sys->psz_old_search )
819                 {
820                     SearchPlaylist( p_intf, p_sys->psz_old_search );
821                 }
822                 p_sys->i_box_type = BOX_PLAYLIST;
823                 return 1;
824             case 0x1b:      /* Esc. */
825                 p_sys->i_box_plidx = p_sys->i_before_search;
826                 p_sys->i_box_type = BOX_PLAYLIST;
827                 return 1;
828             case KEY_BACKSPACE:
829                 RemoveLastUTF8Entity( p_sys->psz_search_chain, i_chain_len );
830                 break;
831             default:
832             {
833                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
834
835                 if( psz_utf8 != NULL )
836                 {
837                     if( i_chain_len + strlen( psz_utf8 ) < SEARCH_CHAIN_SIZE )
838                     {
839                         strcpy( p_sys->psz_search_chain + i_chain_len,
840                                 psz_utf8 );
841                     }
842                     free( psz_utf8 );
843                 }
844                 break;
845             }
846         }
847         if( p_sys->psz_old_search )
848         {
849             free( p_sys->psz_old_search );
850             p_sys->psz_old_search = NULL;
851         }
852         SearchPlaylist( p_intf, p_sys->psz_search_chain );
853         return 1;
854     }
855     else if( p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain )
856     {
857         int i_chain_len = strlen( p_sys->psz_open_chain );
858         playlist_t *p_playlist = p_sys->p_playlist;
859
860         switch( i_key )
861         {
862             case 0x0c:      /* ^l */
863                 clear();
864                 return 1;
865             case KEY_ENTER:
866             case 0x0d:
867                 if( p_playlist && i_chain_len > 0 )
868                 {
869                     playlist_item_t *p_parent = p_sys->p_playlist->status.p_node;
870
871                     if( !p_parent )
872                         p_parent = p_sys->p_playlist->p_local_onelevel;
873
874                     while( p_parent->p_parent && p_parent->p_parent->p_parent )
875                         p_parent = p_parent->p_parent;
876
877                     playlist_Add( p_playlist, p_sys->psz_open_chain, NULL,
878                                   PLAYLIST_APPEND|PLAYLIST_GO, PLAYLIST_END,
879                                   p_parent->p_input == 
880                                     p_sys->p_playlist->p_local_onelevel->p_input
881                                   , VLC_FALSE );
882                     p_sys->b_box_plidx_follow = VLC_TRUE;
883                 }
884                 p_sys->i_box_type = BOX_PLAYLIST;
885                 return 1;
886             case 0x1b:      /* Esc. */
887                 p_sys->i_box_type = BOX_PLAYLIST;
888                 return 1;
889             case KEY_BACKSPACE:
890                 RemoveLastUTF8Entity( p_sys->psz_open_chain, i_chain_len );
891                 break;
892             default:
893             {
894                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
895
896                 if( psz_utf8 != NULL )
897                 {
898                     if( i_chain_len + strlen( psz_utf8 ) < OPEN_CHAIN_SIZE )
899                     {
900                         strcpy( p_sys->psz_open_chain + i_chain_len,
901                                 psz_utf8 );
902                     }
903                     free( psz_utf8 );
904                 }
905                 break;
906             }
907         }
908         return 1;
909     }
910
911
912     /* Common keys */
913     switch( i_key )
914     {
915         case 'q':
916         case 'Q':
917         case 0x1b:  /* Esc */
918             vlc_object_kill( p_intf->p_libvlc );
919             return 0;
920
921         /* Box switching */
922         case 'i':
923             if( p_sys->i_box_type == BOX_INFO )
924                 p_sys->i_box_type = BOX_NONE;
925             else
926                 p_sys->i_box_type = BOX_INFO;
927             p_sys->i_box_lines_total = 0;
928             return 1;
929         case 'm':
930             if( p_sys->i_box_type == BOX_META )
931                 p_sys->i_box_type = BOX_NONE;
932             else
933                 p_sys->i_box_type = BOX_META;
934             p_sys->i_box_lines_total = 0;
935             return 1;
936         case 'L':
937             if( p_sys->i_box_type == BOX_LOG )
938                 p_sys->i_box_type = BOX_NONE;
939             else
940                 p_sys->i_box_type = BOX_LOG;
941             return 1;
942         case 'P':
943             if( p_sys->i_box_type == BOX_PLAYLIST )
944                 p_sys->i_box_type = BOX_NONE;
945             else
946                 p_sys->i_box_type = BOX_PLAYLIST;
947             return 1;
948         case 'B':
949             if( p_sys->i_box_type == BOX_BROWSE )
950                 p_sys->i_box_type = BOX_NONE;
951             else
952                 p_sys->i_box_type = BOX_BROWSE;
953             return 1;
954         case 'h':
955         case 'H':
956             if( p_sys->i_box_type == BOX_HELP )
957                 p_sys->i_box_type = BOX_NONE;
958             else
959                 p_sys->i_box_type = BOX_HELP;
960             p_sys->i_box_lines_total = 0;
961             return 1;
962         case '/':
963             if( p_sys->i_box_type != BOX_SEARCH )
964             {
965                 if( p_sys->psz_search_chain == NULL )
966                 {
967                     return 1;
968                 }
969                 p_sys->psz_search_chain[0] = '\0';
970                 p_sys->b_box_plidx_follow = VLC_FALSE;
971                 p_sys->i_before_search = p_sys->i_box_plidx;
972                 p_sys->i_box_type = BOX_SEARCH;
973             }
974             return 1;
975         case 'A': /* Open */
976             if( p_sys->i_box_type != BOX_OPEN )
977             {
978                 if( p_sys->psz_open_chain == NULL )
979                 {
980                     return 1;
981                 }
982                 p_sys->psz_open_chain[0] = '\0';
983                 p_sys->i_box_type = BOX_OPEN;
984             }
985             return 1;
986
987         /* Navigation */
988         case KEY_RIGHT:
989             p_sys->f_slider += 1.0;
990             if( p_sys->f_slider > 99.9 ) p_sys->f_slider = 99.9;
991             ManageSlider( p_intf );
992             return 1;
993
994         case KEY_LEFT:
995             p_sys->f_slider -= 1.0;
996             if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
997             ManageSlider( p_intf );
998             return 1;
999
1000         /* Common control */
1001         case 'f':
1002         {
1003             if( p_intf->p_sys->p_input )
1004             {
1005                 vout_thread_t *p_vout;
1006                 p_vout = vlc_object_find( p_intf->p_sys->p_input,
1007                                           VLC_OBJECT_VOUT, FIND_CHILD );
1008                 if( p_vout )
1009                 {
1010                     var_Get( p_vout, "fullscreen", &val );
1011                     val.b_bool = !val.b_bool;
1012                     var_Set( p_vout, "fullscreen", val );
1013                     vlc_object_release( p_vout );
1014                 }
1015                 else
1016                 {
1017                     playlist_t *p_playlist;
1018                     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1019                                                   FIND_ANYWHERE );
1020                     if( p_playlist )
1021                     {
1022                         var_Get( p_playlist, "fullscreen", &val );
1023                         val.b_bool = !val.b_bool;
1024                         var_Set( p_playlist, "fullscreen", val );
1025                         vlc_object_release( p_playlist );
1026                     }
1027                 }
1028             }
1029             return 0;
1030         }
1031
1032         case ' ':
1033             PlayPause( p_intf );
1034             return 1;
1035
1036         case 's':
1037             if( p_intf->p_sys->p_playlist )
1038             {
1039                 playlist_Stop( p_intf->p_sys->p_playlist );
1040             }
1041             return 1;
1042
1043         case 'e':
1044             Eject( p_intf );
1045             return 1;
1046
1047         case '[':
1048             if( p_sys->p_input )
1049             {
1050                 val.b_bool = VLC_TRUE;
1051                 var_Set( p_sys->p_input, "prev-title", val );
1052             }
1053             return 1;
1054
1055         case ']':
1056             if( p_sys->p_input )
1057             {
1058                 val.b_bool = VLC_TRUE;
1059                 var_Set( p_sys->p_input, "next-title", val );
1060             }
1061             return 1;
1062
1063         case '<':
1064             if( p_sys->p_input )
1065             {
1066                 val.b_bool = VLC_TRUE;
1067                 var_Set( p_sys->p_input, "prev-chapter", val );
1068             }
1069             return 1;
1070
1071         case '>':
1072             if( p_sys->p_input )
1073             {
1074                 val.b_bool = VLC_TRUE;
1075                 var_Set( p_sys->p_input, "next-chapter", val );
1076             }
1077             return 1;
1078
1079         case 'p':
1080             if( p_intf->p_sys->p_playlist )
1081             {
1082                 playlist_Prev( p_intf->p_sys->p_playlist );
1083             }
1084             clear();
1085             return 1;
1086
1087         case 'n':
1088             if( p_intf->p_sys->p_playlist )
1089             {
1090                 playlist_Next( p_intf->p_sys->p_playlist );
1091             }
1092             clear();
1093             return 1;
1094
1095         case 'a':
1096             aout_VolumeUp( p_intf, 1, NULL );
1097             clear();
1098             return 1;
1099
1100         case 'z':
1101             aout_VolumeDown( p_intf, 1, NULL );
1102             clear();
1103             return 1;
1104
1105         /*
1106          * ^l should clear and redraw the screen
1107          */
1108         case 0x0c:
1109             clear();
1110             return 1;
1111
1112         default:
1113             return 0;
1114     }
1115 }
1116
1117 static void ManageSlider( intf_thread_t *p_intf )
1118 {
1119     intf_sys_t     *p_sys = p_intf->p_sys;
1120     input_thread_t *p_input = p_sys->p_input;
1121     vlc_value_t     val;
1122
1123     if( p_input == NULL )
1124     {
1125         return;
1126     }
1127     var_Get( p_input, "state", &val );
1128     if( val.i_int != PLAYING_S )
1129     {
1130         return;
1131     }
1132
1133     var_Get( p_input, "position", &val );
1134     if( p_sys->f_slider == p_sys->f_slider_old )
1135     {
1136         p_sys->f_slider =
1137         p_sys->f_slider_old = 100 * val.f_float;
1138     }
1139     else
1140     {
1141         p_sys->f_slider_old = p_sys->f_slider;
1142
1143         val.f_float = p_sys->f_slider / 100.0;
1144         var_Set( p_input, "position", val );
1145     }
1146 }
1147
1148 static void SearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring )
1149 {
1150     int i_max;
1151     int i_first = 0 ;
1152     int i_item = -1;
1153     intf_sys_t *p_sys = p_intf->p_sys;
1154
1155     if( p_sys->i_before_search >= 0 )
1156     {
1157         i_first = p_sys->i_before_search;
1158     }
1159
1160     if( ( ! psz_searchstring ) ||  strlen( psz_searchstring ) <= 0 )
1161     {
1162         p_sys->i_box_plidx = p_sys->i_before_search;
1163         return;
1164     }
1165
1166     i_max = p_sys->i_plist_entries;
1167
1168     i_item = SubSearchPlaylist( p_intf, psz_searchstring, i_first + 1, i_max );
1169     if( i_item < 0 )
1170     {
1171         i_item = SubSearchPlaylist( p_intf, psz_searchstring, 0, i_first );
1172     }
1173
1174     if( i_item < 0 || i_item >= i_max ) return;
1175
1176     p_sys->i_box_plidx = i_item;
1177 }
1178
1179 static int SubSearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring,
1180                               int i_start, int i_stop )
1181 {
1182     intf_sys_t *p_sys = p_intf->p_sys;
1183     int i, i_item = -1;
1184
1185     for( i = i_start + 1; i < i_stop; i++ )
1186     {
1187         if( strcasestr( p_sys->pp_plist[i]->psz_display,
1188                         psz_searchstring ) != NULL )
1189         {
1190             i_item = i;
1191             break;
1192         }
1193     }
1194
1195     return i_item;
1196 }
1197
1198
1199 static void mvnprintw( int y, int x, int w, const char *p_fmt, ... )
1200 {
1201     va_list  vl_args;
1202     char    *p_buf = NULL;
1203     int      i_len;
1204
1205     va_start( vl_args, p_fmt );
1206     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
1207         return;
1208     va_end( vl_args );
1209
1210     if( ( p_buf == NULL ) || ( w <= 0 ) )
1211         return;
1212
1213     i_len = strlen( p_buf );
1214
1215 #ifdef HAVE_NCURSESW
1216     wchar_t psz_wide[i_len + 1];
1217     
1218     EnsureUTF8( p_buf );
1219     size_t i_char_len = mbstowcs( psz_wide, p_buf, i_len );
1220
1221     size_t i_width; /* number of columns */
1222
1223     if( i_char_len == (size_t)-1 )
1224         /* an invalid character was encountered */
1225         abort();
1226     else
1227     {
1228         i_width = wcswidth( psz_wide, i_char_len );
1229         if( i_width == (size_t)-1 )
1230         {
1231             /* a non printable character was encountered */
1232             unsigned int i;
1233             int i_cwidth;
1234             i_width = 0;
1235             for( i = 0 ; i < i_char_len ; i++ )
1236             {
1237                 i_cwidth = wcwidth( psz_wide[i] );
1238                 if( i_cwidth != -1 )
1239                     i_width += i_cwidth;
1240             }
1241         }
1242     }
1243     if( i_width > (size_t)w )
1244     {
1245         int i_total_width = 0;
1246         int i = 0;
1247         while( i_total_width < w )
1248         {
1249             i_total_width += wcwidth( psz_wide[i] );
1250             if( w > 7 && i_total_width >= w/2 )
1251             {
1252                 psz_wide[i  ] = '.';
1253                 psz_wide[i+1] = '.';
1254                 i_total_width -= wcwidth( psz_wide[i] ) - 2;
1255                 if( i > 0 )
1256                 {
1257                     /* we require this check only if at least one character
1258                      * 4 or more columns wide exists (which i doubt) */
1259                     psz_wide[i-1] = '.';
1260                     i_total_width -= wcwidth( psz_wide[i-1] ) - 1;
1261                 }
1262
1263                 /* find the widest string */
1264                 int j, i_2nd_width = 0;
1265                 for( j = i_char_len - 1; i_2nd_width < w - i_total_width; j-- )
1266                     i_2nd_width += wcwidth( psz_wide[j] );
1267
1268                 /* we already have i_total_width columns filled, and we can't
1269                  * have more than w columns */
1270                 if( i_2nd_width > w - i_total_width )
1271                     j++;
1272
1273                 wmemmove( &psz_wide[i+2], &psz_wide[j+1], i_char_len - j - 1 );
1274                 psz_wide[i + 2 + i_char_len - j - 1] = '\0';
1275                 break;
1276             }
1277             i++;
1278         }
1279         if( w <= 7 ) /* we don't add the '...' else we lose too much chars */
1280             psz_wide[i] = '\0';
1281
1282         size_t i_wlen = wcslen( psz_wide ) * 6 + 1; /* worst case */
1283         char psz_ellipsized[i_wlen];
1284         wcstombs( psz_ellipsized, psz_wide, i_wlen );
1285         mvprintw( y, x, "%s", psz_ellipsized );
1286     }
1287     else
1288     {
1289         mvprintw( y, x, "%s", p_buf );
1290         mvhline( y, x + i_width, ' ', w - i_width );
1291     }
1292 #else
1293     if( i_len > w )
1294     {
1295         int i_cut = i_len - w;
1296         int x1 = i_len/2 - i_cut/2;
1297         int x2 = x1 + i_cut;
1298
1299         if( i_len > x2 )
1300         {
1301             memmove( &p_buf[x1], &p_buf[x2], i_len - x2 );
1302         }
1303         p_buf[w] = '\0';
1304         if( w > 7 )
1305         {
1306             p_buf[w/2-1] = '.';
1307             p_buf[w/2  ] = '.';
1308             p_buf[w/2+1] = '.';
1309         }
1310         char *psz_local = ToLocale( p_buf );
1311         mvprintw( y, x, "%s", psz_local );
1312         LocaleFree( p_buf );
1313     }
1314     else
1315     {
1316         char *psz_local = ToLocale( p_buf );
1317         mvprintw( y, x, "%s", psz_local );
1318         LocaleFree( p_buf );
1319         mvhline( y, x + i_len, ' ', w - i_len );
1320     }
1321 #endif
1322 }
1323 static void MainBoxWrite( intf_thread_t *p_intf, int l, int x, const char *p_fmt, ... )
1324 {
1325     intf_sys_t     *p_sys = p_intf->p_sys;
1326
1327     va_list  vl_args;
1328     char    *p_buf = NULL;
1329
1330     if( l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines )
1331     {
1332         return;
1333     }
1334
1335     va_start( vl_args, p_fmt );
1336     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
1337         return;
1338     va_end( vl_args );
1339
1340     if( p_buf == NULL )
1341     {
1342         return;
1343     }
1344
1345     mvnprintw( p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf );
1346 }
1347
1348 static void Redraw( intf_thread_t *p_intf, time_t *t_last_refresh )
1349 {
1350     intf_sys_t     *p_sys = p_intf->p_sys;
1351     input_thread_t *p_input = p_sys->p_input;
1352     int y = 0;
1353     int h;
1354     int y_end;
1355
1356     clear();
1357
1358     /* Title */
1359     attrset( A_REVERSE );
1360     int i_len = strlen( "VLC media player [ h for help ]" );
1361     int mid = ( COLS - i_len ) / 2;
1362     if( mid < 0 )
1363         mid = 0;
1364     int i_size = ( COLS > i_len + 1 ) ? COLS : i_len + 1;
1365     char psz_title[i_size];
1366     memset( psz_title, ' ', mid );
1367     snprintf( &psz_title[mid], i_size, "VLC media player [ h for help ]" );
1368     mvnprintw( y, 0, COLS, "%s", psz_title );
1369     attroff( A_REVERSE );
1370     y += 2;
1371
1372     /* Infos */
1373
1374     char psz_state[25];
1375     /* strlen( "[Repeat] [Random] [Loop]" ) == 24, + '\0' */
1376     psz_state[0] = '\0';
1377     if( var_GetBool( p_sys->p_playlist, "repeat" ) )
1378         strcat( psz_state, "[Repeat] " );
1379     if( var_GetBool( p_sys->p_playlist, "random" ) )
1380         strcat( psz_state, "[Random] " );
1381     if( var_GetBool( p_sys->p_playlist, "loop" ) )
1382         strcat( psz_state, "[Loop]" );
1383
1384     if( p_input && !p_input->b_dead )
1385     {
1386         char buf1[MSTRTIME_MAX_SIZE];
1387         char buf2[MSTRTIME_MAX_SIZE];
1388         vlc_value_t val;
1389         vlc_value_t val_list;
1390
1391         /* Source */
1392         char *psz_uri = input_item_GetURI( input_GetItem( p_input ) );
1393         mvnprintw( y++, 0, COLS, " Source   : %s", psz_uri );
1394         free( psz_uri );
1395
1396         /* State */
1397         var_Get( p_input, "state", &val );
1398         if( val.i_int == PLAYING_S )
1399         {
1400             mvnprintw( y++, 0, COLS, " State    : Playing %s", psz_state );
1401         }
1402         else if( val.i_int == OPENING_S )
1403         {
1404             mvnprintw( y++, 0, COLS, " State    : Opening/Connecting %s", psz_state );
1405         }
1406         else if( val.i_int == BUFFERING_S )
1407         {
1408             mvnprintw( y++, 0, COLS, " State    : Buffering %s", psz_state );
1409         }
1410         else if( val.i_int == PAUSE_S )
1411         {
1412             mvnprintw( y++, 0, COLS, " State    : Paused %s", psz_state );
1413         }
1414
1415         if( val.i_int != INIT_S && val.i_int != END_S )
1416         {
1417             audio_volume_t i_volume;
1418
1419             /* Position */
1420             var_Get( p_input, "time", &val );
1421             msecstotimestr( buf1, val.i_time / 1000 );
1422
1423             var_Get( p_input, "length", &val );
1424             msecstotimestr( buf2, val.i_time / 1000 );
1425
1426             mvnprintw( y++, 0, COLS, " Position : %s/%s (%.2f%%)", buf1, buf2, p_sys->f_slider );
1427
1428             /* Volume */
1429             aout_VolumeGet( p_intf, &i_volume );
1430             mvnprintw( y++, 0, COLS, " Volume   : %i%%", i_volume*200/AOUT_VOLUME_MAX );
1431
1432             /* Title */
1433             if( !var_Get( p_input, "title", &val ) )
1434             {
1435                 var_Change( p_input, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1436                 if( val_list.p_list->i_count > 0 )
1437                 {
1438                     mvnprintw( y++, 0, COLS, " Title    : %d/%d", val.i_int, val_list.p_list->i_count );
1439                 }
1440                 var_Change( p_input, "title", VLC_VAR_FREELIST, &val_list, NULL );
1441             }
1442
1443             /* Chapter */
1444             if( !var_Get( p_input, "chapter", &val ) )
1445             {
1446                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1447                 if( val_list.p_list->i_count > 0 )
1448                 {
1449                     mvnprintw( y++, 0, COLS, " Chapter  : %d/%d", val.i_int, val_list.p_list->i_count );
1450                 }
1451                 var_Change( p_input, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1452             }
1453         }
1454         else
1455         {
1456             y += 2;
1457         }
1458     }
1459     else
1460     {
1461         mvnprintw( y++, 0, COLS, "Source: <no current item> %s", psz_state );
1462         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1463         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1464         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1465     }
1466
1467     DrawBox( p_sys->w, y, 0, 3, COLS, "" );
1468     DrawEmptyLine( p_sys->w, y+1, 1, COLS-2);
1469     DrawLine( p_sys->w, y+1, 1, (int)(p_intf->p_sys->f_slider/100.0 * (COLS -2)) );
1470     y += 3;
1471
1472     p_sys->i_box_y = y + 1;
1473     p_sys->i_box_lines = LINES - y - 2;
1474
1475     h = LINES - y;
1476     y_end = y + h - 1;
1477
1478     if( p_sys->i_box_type == BOX_HELP )
1479     {
1480         /* Help box */
1481         int l = 0;
1482         DrawBox( p_sys->w, y++, 0, h, COLS, " Help " );
1483
1484         MainBoxWrite( p_intf, l++, 1, "[Display]" );
1485         MainBoxWrite( p_intf, l++, 1, "     h,H         Show/Hide help box" );
1486         MainBoxWrite( p_intf, l++, 1, "     i           Show/Hide info box" );
1487         MainBoxWrite( p_intf, l++, 1, "     m           Show/Hide metadata box" );
1488         MainBoxWrite( p_intf, l++, 1, "     L           Show/Hide messages box" );
1489         MainBoxWrite( p_intf, l++, 1, "     P           Show/Hide playlist box" );
1490         MainBoxWrite( p_intf, l++, 1, "     B           Show/Hide filebrowser" );
1491         MainBoxWrite( p_intf, l++, 1, "" );
1492
1493         MainBoxWrite( p_intf, l++, 1, "[Global]" );
1494         MainBoxWrite( p_intf, l++, 1, "     q, Q        Quit" );
1495         MainBoxWrite( p_intf, l++, 1, "     s           Stop" );
1496         MainBoxWrite( p_intf, l++, 1, "     <space>     Pause/Play" );
1497         MainBoxWrite( p_intf, l++, 1, "     f           Toggle Fullscreen" );
1498         MainBoxWrite( p_intf, l++, 1, "     n, p        Next/Previous playlist item" );
1499         MainBoxWrite( p_intf, l++, 1, "     [, ]        Next/Previous title" );
1500         MainBoxWrite( p_intf, l++, 1, "     <, >        Next/Previous chapter" );
1501         MainBoxWrite( p_intf, l++, 1, "     <right>     Seek +1%%" );
1502         MainBoxWrite( p_intf, l++, 1, "     <left>      Seek -1%%" );
1503         MainBoxWrite( p_intf, l++, 1, "     a           Volume Up" );
1504         MainBoxWrite( p_intf, l++, 1, "     z           Volume Down" );
1505         MainBoxWrite( p_intf, l++, 1, "" );
1506
1507         MainBoxWrite( p_intf, l++, 1, "[Playlist]" );
1508         MainBoxWrite( p_intf, l++, 1, "     r           Toggle Random playing" );
1509         MainBoxWrite( p_intf, l++, 1, "     l           Toggle Loop Playlist" );
1510         MainBoxWrite( p_intf, l++, 1, "     R           Toggle Repeat item" );
1511         MainBoxWrite( p_intf, l++, 1, "     o           Order Playlist by title" );
1512         MainBoxWrite( p_intf, l++, 1, "     O           Reverse order Playlist by title" );
1513         MainBoxWrite( p_intf, l++, 1, "     g           Go to the current playing item" );
1514         MainBoxWrite( p_intf, l++, 1, "     /           Look for an item" );
1515         MainBoxWrite( p_intf, l++, 1, "     A           Add an entry" );
1516         MainBoxWrite( p_intf, l++, 1, "     D, <del>    Delete an entry" );
1517         MainBoxWrite( p_intf, l++, 1, "     <backspace> Delete an entry" );
1518         MainBoxWrite( p_intf, l++, 1, "" );
1519
1520         MainBoxWrite( p_intf, l++, 1, "[Filebrowser]" );
1521         MainBoxWrite( p_intf, l++, 1, "     <enter>     Add the selected file to the playlist" );
1522         MainBoxWrite( p_intf, l++, 1, "     <space>     Add the selected directory to the playlist" );
1523         MainBoxWrite( p_intf, l++, 1, "     .           Show/Hide hidden files" );
1524         MainBoxWrite( p_intf, l++, 1, "" );
1525
1526         MainBoxWrite( p_intf, l++, 1, "[Boxes]" );
1527         MainBoxWrite( p_intf, l++, 1, "     <up>,<down>     Navigate through the box line by line" );
1528         MainBoxWrite( p_intf, l++, 1, "     <pgup>,<pgdown> Navigate through the box page by page" );
1529         MainBoxWrite( p_intf, l++, 1, "" );
1530
1531         MainBoxWrite( p_intf, l++, 1, "[Player]" );
1532         MainBoxWrite( p_intf, l++, 1, "     <up>,<down>     Seek +/-5%%" );
1533         MainBoxWrite( p_intf, l++, 1, "" );
1534
1535         MainBoxWrite( p_intf, l++, 1, "[Miscellaneous]" );
1536         MainBoxWrite( p_intf, l++, 1, "     Ctrl-l          Refresh the screen" );
1537
1538         p_sys->i_box_lines_total = l;
1539         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1540         {
1541             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1542         }
1543
1544         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1545         {
1546             y += l - p_sys->i_box_start;
1547         }
1548         else
1549         {
1550             y += p_sys->i_box_lines;
1551         }
1552     }
1553     else if( p_sys->i_box_type == BOX_INFO )
1554     {
1555         /* Info box */
1556         int l = 0;
1557         DrawBox( p_sys->w, y++, 0, h, COLS, " Information " );
1558
1559         if( p_input )
1560         {
1561             int i,j;
1562             vlc_mutex_lock( &input_GetItem(p_input)->lock );
1563             for( i = 0; i < input_GetItem(p_input)->i_categories; i++ )
1564             {
1565                 info_category_t *p_category = input_GetItem(p_input)->pp_categories[i];
1566                 if( y >= y_end ) break;
1567                 MainBoxWrite( p_intf, l++, 1, "  [%s]", p_category->psz_name );
1568                 for( j = 0; j < p_category->i_infos; j++ )
1569                 {
1570                     info_t *p_info = p_category->pp_infos[j];
1571                     if( y >= y_end ) break;
1572                     MainBoxWrite( p_intf, l++, 1, "      %s: %s", p_info->psz_name, p_info->psz_value );
1573                 }
1574             }
1575             vlc_mutex_unlock( &input_GetItem(p_input)->lock );
1576         }
1577         else
1578         {
1579             MainBoxWrite( p_intf, l++, 1, "No item currently playing" );
1580         }
1581         p_sys->i_box_lines_total = l;
1582         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1583         {
1584             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1585         }
1586
1587         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1588         {
1589             y += l - p_sys->i_box_start;
1590         }
1591         else
1592         {
1593             y += p_sys->i_box_lines;
1594         }
1595     }
1596     else if( p_sys->i_box_type == BOX_META )
1597     {
1598         /* Meta data box */
1599         int l = 0;
1600
1601         int i_len = strlen( VLC_META_INFO_CAT );
1602         char psz_title[i_len + 3];
1603         psz_title[0] = ' ';
1604         psz_title[1] = '\0';
1605         strcat( &psz_title[1], VLC_META_INFO_CAT );
1606         psz_title[i_len + 1] = ' ';
1607         psz_title[i_len + 2] = '\0';
1608         DrawBox( p_sys->w, y++, 0, h, COLS, psz_title );
1609
1610         if( p_input )
1611         {
1612             int i;
1613             input_item_t *p_item = input_GetItem( p_input );
1614             vlc_mutex_lock( &p_item->lock );
1615             for( i=0; i<VLC_META_TYPE_COUNT; i++ )
1616             {
1617                 if( y >= y_end ) break;
1618                 char *psz_meta = p_item->p_meta->ppsz_meta[i];
1619                 if( psz_meta && *psz_meta )
1620                 {
1621                     const char *psz_meta_title;
1622                     switch( i )
1623                     {
1624                         case 0:
1625                             psz_meta_title = VLC_META_TITLE; break;
1626                         case 1:
1627                             psz_meta_title = VLC_META_ARTIST; break;
1628                         case 2:
1629                             psz_meta_title = VLC_META_GENRE ; break;
1630                         case 3:
1631                             psz_meta_title = VLC_META_COPYRIGHT; break;
1632                         case 4:
1633                             psz_meta_title = VLC_META_COLLECTION; break;
1634                         case 5:
1635                             psz_meta_title = VLC_META_SEQ_NUM; break;
1636                         case 6:
1637                             psz_meta_title = VLC_META_DESCRIPTION; break;
1638                         case 7:
1639                             psz_meta_title = VLC_META_RATING; break;
1640                         case 8:
1641                             psz_meta_title = VLC_META_DATE; break;
1642                         case 9:
1643                             psz_meta_title = VLC_META_SETTING; break;
1644                         case 10:
1645                             psz_meta_title = VLC_META_URL; break;
1646                         case 11:
1647                             psz_meta_title = VLC_META_LANGUAGE; break;
1648                         case 12:
1649                             psz_meta_title = VLC_META_NOW_PLAYING; break;
1650                         case 13:
1651                             psz_meta_title = VLC_META_PUBLISHER; break;
1652                         case 14:
1653                             psz_meta_title = VLC_META_ENCODED_BY; break;
1654                         case 15:
1655                             psz_meta_title = VLC_META_ART_URL; break;
1656                         case 16:
1657                             psz_meta_title = VLC_META_TRACKID; break;
1658                         default:
1659                             psz_meta_title = ""; break;
1660                     }
1661                     MainBoxWrite( p_intf, l++, 1, "  [%s]", psz_meta_title );
1662                     MainBoxWrite( p_intf, l++, 1, "      %s", psz_meta );
1663                 }
1664             }
1665             vlc_mutex_unlock( &p_item->lock );
1666         }
1667         else
1668         {
1669             MainBoxWrite( p_intf, l++, 1, "No item currently playing" );
1670         }
1671         p_sys->i_box_lines_total = l;
1672         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1673         {
1674             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1675         }
1676
1677         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1678         {
1679             y += l - p_sys->i_box_start;
1680         }
1681         else
1682         {
1683             y += p_sys->i_box_lines;
1684         }
1685     }
1686     else if( p_sys->i_box_type == BOX_LOG )
1687     {
1688         int i_line = 0;
1689         int i_stop;
1690         int i_start;
1691
1692         DrawBox( p_sys->w, y++, 0, h, COLS, " Logs " );
1693
1694         i_start = p_intf->p_sys->p_sub->i_start;
1695
1696         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1697         i_stop = *p_intf->p_sys->p_sub->pi_stop;
1698         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1699
1700         for( ;; )
1701         {
1702             static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
1703             if( i_line >= h - 2 )
1704             {
1705                 break;
1706             }
1707             i_stop--;
1708             i_line++;
1709             if( i_stop < 0 ) i_stop += VLC_MSG_QSIZE;
1710             if( i_stop == i_start )
1711             {
1712                 break;
1713             }
1714             mvnprintw( y + h-2-i_line, 1, COLS - 2, "   [%s] %s",
1715                       ppsz_type[p_sys->p_sub->p_msg[i_stop].i_type],
1716                       p_sys->p_sub->p_msg[i_stop].psz_msg );
1717         }
1718
1719         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1720         p_intf->p_sys->p_sub->i_start = i_stop;
1721         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1722         y = y_end;
1723     }
1724     else if( p_sys->i_box_type == BOX_BROWSE )
1725     {
1726         /* Filebrowser box */
1727         int        i_start, i_stop;
1728         int        i_item;
1729         DrawBox( p_sys->w, y++, 0, h, COLS, " Browse " );
1730
1731         if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_plidx = p_sys->i_dir_entries - 1;
1732         if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
1733
1734         if( p_sys->i_box_bidx < (h - 2)/2 )
1735         {
1736             i_start = 0;
1737             i_stop = h - 2;
1738         }
1739         else if( p_sys->i_dir_entries - p_sys->i_box_bidx > (h - 2)/2 )
1740         {
1741             i_start = p_sys->i_box_bidx - (h - 2)/2;
1742             i_stop = i_start + h - 2;
1743         }
1744         else
1745         {
1746             i_stop = p_sys->i_dir_entries;
1747             i_start = p_sys->i_dir_entries - (h - 2);
1748         }
1749         if( i_start < 0 )
1750         {
1751             i_start = 0;
1752         }
1753         if( i_stop > p_sys->i_dir_entries )
1754         {
1755             i_stop = p_sys->i_dir_entries;
1756         }
1757
1758         for( i_item = i_start; i_item < i_stop; i_item++ )
1759         {
1760             vlc_bool_t b_selected = ( p_sys->i_box_bidx == i_item );
1761
1762             if( y >= y_end ) break;
1763             if( b_selected )
1764             {
1765                 attrset( A_REVERSE );
1766             }
1767             mvnprintw( y++, 1, COLS - 2, " %c %s", p_sys->pp_dir_entries[i_item]->b_file == VLC_TRUE ? ' ' : '+',
1768                             p_sys->pp_dir_entries[i_item]->psz_path );
1769             if( b_selected )
1770             {
1771                 attroff( A_REVERSE );
1772             }
1773         }
1774
1775     }
1776     else if( ( p_sys->i_box_type == BOX_PLAYLIST ||
1777                p_sys->i_box_type == BOX_SEARCH ||
1778                p_sys->i_box_type == BOX_OPEN  ) && p_sys->p_playlist )
1779     {
1780         /* Playlist box */
1781         int        i_start, i_stop, i_max = p_sys->i_plist_entries;
1782         int        i_item;
1783         char       *psz_title;
1784
1785         switch( p_sys->i_current_view )
1786         {
1787             case VIEW_ONELEVEL:
1788                 psz_title = strdup( " Playlist (All, one level) " );
1789                 break;
1790             case VIEW_CATEGORY:
1791                 psz_title = strdup( " Playlist (By category) " );
1792                 break;
1793             default:
1794                 psz_title = strdup( " Playlist (Manually added) " );
1795         }
1796
1797         DrawBox( p_sys->w, y++, 0, h, COLS, psz_title );
1798
1799         if( p_sys->b_need_update || p_sys->pp_plist == NULL )
1800         {
1801             PlaylistRebuild( p_intf );
1802         }
1803         if( p_sys->b_box_plidx_follow )
1804         {
1805             FindIndex( p_intf );
1806         }
1807
1808         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
1809         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
1810         if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
1811
1812         if( p_sys->i_box_plidx < (h - 2)/2 )
1813         {
1814             i_start = 0;
1815             i_stop = h - 2;
1816         }
1817         else if( i_max - p_sys->i_box_plidx > (h - 2)/2 )
1818         {
1819             i_start = p_sys->i_box_plidx - (h - 2)/2;
1820             i_stop = i_start + h - 2;
1821         }
1822         else
1823         {
1824             i_stop = i_max;
1825             i_start = i_max - (h - 2);
1826         }
1827         if( i_start < 0 )
1828         {
1829             i_start = 0;
1830         }
1831         if( i_stop > i_max )
1832         {
1833             i_stop = i_max;
1834         }
1835
1836         for( i_item = i_start; i_item < i_stop; i_item++ )
1837         {
1838             vlc_bool_t b_selected = ( p_sys->i_box_plidx == i_item );
1839             int c = ' ';
1840             if( p_sys->pp_plist[i_item]->p_item->p_input ==
1841                         p_sys->p_playlist->status.p_node->p_input )
1842                 c = '*';
1843             else if( PlaylistIsPlaying( p_intf,
1844                         p_sys->pp_plist[i_item]->p_item ) )
1845                 c = '>';
1846
1847             if( y >= y_end ) break;
1848             if( b_selected )
1849             {
1850                 attrset( A_REVERSE );
1851             }
1852             mvnprintw( y++, 1, COLS - 2, "%c%s", c,
1853                        p_sys->pp_plist[i_item]->psz_display );
1854             if( b_selected )
1855             {
1856                 attroff( A_REVERSE );
1857             }
1858         }
1859
1860     }
1861     else
1862     {
1863         y++;
1864     }
1865     if( p_sys->i_box_type == BOX_SEARCH )
1866     {
1867         DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1868         if( p_sys->psz_search_chain )
1869         {
1870             if( strlen( p_sys->psz_search_chain ) == 0 &&
1871                 p_sys->psz_old_search != NULL )
1872             {
1873                 /* Searching next entry */
1874                 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_old_search );
1875             }
1876             else
1877             {
1878                 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_search_chain );
1879             }
1880         }
1881     }
1882     if( p_sys->i_box_type == BOX_OPEN )
1883     {
1884         if( p_sys->psz_open_chain )
1885         {
1886             DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1887             mvnprintw( 7, 1, COLS-2, "Open: %s", p_sys->psz_open_chain );
1888         }
1889     }
1890
1891     while( y < y_end )
1892     {
1893         DrawEmptyLine( p_sys->w, y++, 1, COLS - 2 );
1894     }
1895
1896     refresh();
1897
1898     *t_last_refresh = time( 0 );
1899 }
1900
1901 static playlist_item_t *PlaylistGetRoot( intf_thread_t *p_intf )
1902 {
1903     intf_sys_t *p_sys = p_intf->p_sys;
1904     playlist_t *p_playlist = p_sys->p_playlist;
1905
1906     if( p_playlist == NULL )
1907     {
1908         return NULL;
1909     }
1910
1911     switch( p_sys->i_current_view )
1912     {
1913         case VIEW_CATEGORY:
1914             return p_playlist->p_root_category;
1915         default:
1916             return p_playlist->p_root_onelevel;
1917     }
1918 }
1919
1920 static void PlaylistRebuild( intf_thread_t *p_intf )
1921 {
1922     intf_sys_t *p_sys = p_intf->p_sys;
1923     playlist_t *p_playlist = p_sys->p_playlist;
1924
1925     if( p_playlist == NULL )
1926     {
1927         return;
1928     }
1929
1930     vlc_mutex_lock( &p_playlist->object_lock );
1931
1932     /* First clear the old one */
1933     PlaylistDestroy( p_intf );
1934
1935     /* Build the new one */
1936     PlaylistAddNode( p_intf, PlaylistGetRoot( p_intf ), 0, "" );
1937
1938     p_sys->b_need_update = VLC_FALSE;
1939
1940     vlc_mutex_unlock( &p_playlist->object_lock );
1941 }
1942
1943 static void PlaylistAddNode( intf_thread_t *p_intf, playlist_item_t *p_node,
1944                              int i, const char *c )
1945 {
1946     intf_sys_t *p_sys = p_intf->p_sys;
1947     playlist_item_t *p_child;
1948     char *psz_tmp;
1949     int k;
1950
1951     psz_tmp = (char *)malloc( strlen( c ) + 4 );
1952     if( psz_tmp == NULL ) return;
1953     for( k = 0; k < p_node->i_children; k++ )
1954     {
1955         struct pl_item_t *p_pl_item;
1956         char *buff;
1957         int i_size;
1958
1959         p_child = p_node->pp_children[k];
1960         i_size = strlen( c ) + strlen( p_child->p_input->psz_name ) + 4;
1961         buff = (char *)malloc( sizeof( char ) * i_size );
1962         p_pl_item = (struct pl_item_t *)malloc( sizeof( struct pl_item_t ) );
1963         if( p_pl_item == NULL || buff == NULL ) return;
1964
1965         if( strlen( c ) )
1966         {
1967             sprintf( buff, "%s%c-%s", c, k == p_node->i_children - 1 ?
1968                      '`' : '|', p_child->p_input->psz_name );
1969         }
1970         else
1971         {
1972             sprintf( buff, " %s", p_child->p_input->psz_name );
1973         }
1974         p_pl_item->psz_display = strdup( buff );
1975         p_pl_item->p_item = p_child;
1976         INSERT_ELEM( p_sys->pp_plist, p_sys->i_plist_entries,
1977                      p_sys->i_plist_entries, p_pl_item );
1978         free( buff );
1979         i++;
1980
1981         if( p_child->i_children > 0 )
1982         {
1983             sprintf( psz_tmp, "%s%c ", c,
1984                      k == p_node->i_children - 1 ? ' ' : '|' );
1985             PlaylistAddNode( p_intf, p_child, i,
1986                              strlen( c ) ? psz_tmp : " " );
1987         }
1988     }
1989     free( psz_tmp );
1990 }
1991
1992 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
1993                             vlc_value_t oval, vlc_value_t nval, void *param )
1994 {
1995     intf_thread_t *p_intf = (intf_thread_t *)param;
1996     p_intf->p_sys->b_need_update = VLC_TRUE;
1997     return VLC_SUCCESS;
1998 }
1999
2000 /* Playlist suxx */
2001 static inline vlc_bool_t PlaylistIsPlaying( intf_thread_t *p_intf,
2002                                             playlist_item_t *p_item )
2003 {
2004     playlist_item_t *p_played_item = p_intf->p_sys->p_playlist->status.p_item;
2005     return( p_item != NULL && p_played_item != NULL &&
2006             p_item->p_input != NULL && p_played_item->p_input != NULL &&
2007             p_item->p_input->i_id == p_played_item->p_input->i_id );
2008 }
2009
2010 static void FindIndex( intf_thread_t *p_intf )
2011 {
2012     intf_sys_t *p_sys = p_intf->p_sys;
2013     int i;
2014
2015     if( p_sys->i_box_plidx < p_sys->i_plist_entries &&
2016         p_sys->i_box_plidx >= 0 &&
2017         PlaylistIsPlaying( p_intf,
2018                            p_sys->pp_plist[p_sys->i_box_plidx]->p_item ) )
2019     {
2020         return;
2021     }
2022
2023     for( i = 0; i < p_sys->i_plist_entries; i++ )
2024     {
2025         if( PlaylistIsPlaying( p_intf, p_sys->pp_plist[i]->p_item ) )
2026         {
2027             p_sys->i_box_plidx = i;
2028             break;
2029         }
2030     }
2031 }
2032
2033 static void PlaylistDestroy( intf_thread_t *p_intf )
2034 {
2035     intf_sys_t *p_sys = p_intf->p_sys;
2036     int i;
2037
2038     for( i = 0; i < p_sys->i_plist_entries; i++ )
2039     {
2040         struct pl_item_t *p_pl_item = p_sys->pp_plist[i];
2041         free( p_pl_item->psz_display );
2042         REMOVE_ELEM( p_sys->pp_plist, p_sys->i_plist_entries, i );
2043         free( p_pl_item );
2044     }
2045     p_sys->pp_plist = NULL;
2046     p_sys->i_plist_entries = 0;
2047 }
2048
2049 static void Eject( intf_thread_t *p_intf )
2050 {
2051     char *psz_device = NULL, *psz_parser, *psz_name;
2052
2053     /*
2054      * Get the active input
2055      * Determine whether we can eject a media, ie it's a DVD, VCD or CD-DA
2056      * If it's neither of these, then return
2057      */
2058
2059     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
2060                                                        FIND_ANYWHERE );
2061
2062     if( p_playlist == NULL )
2063     {
2064         return;
2065     }
2066
2067     vlc_mutex_lock( &p_playlist->object_lock );
2068
2069     if( p_playlist->status.p_item == NULL )
2070     {
2071         vlc_mutex_unlock( &p_playlist->object_lock );
2072         vlc_object_release( p_playlist );
2073         return;
2074     }
2075
2076     psz_name = p_playlist->status.p_item->p_input->psz_name;
2077
2078     if( psz_name )
2079     {
2080         if( !strncmp(psz_name, "dvd://", 4) )
2081         {
2082             switch( psz_name[strlen("dvd://")] )
2083             {
2084             case '\0':
2085             case '@':
2086                 psz_device = config_GetPsz( p_intf, "dvd" );
2087                 break;
2088             default:
2089                 /* Omit the first MRL-selector characters */
2090                 psz_device = strdup( psz_name + strlen("dvd://" ) );
2091                 break;
2092             }
2093         }
2094         else if( !strncmp(psz_name, VCD_MRL, strlen(VCD_MRL)) )
2095         {
2096             switch( psz_name[strlen(VCD_MRL)] )
2097             {
2098             case '\0':
2099             case '@':
2100                 psz_device = config_GetPsz( p_intf, VCD_MRL );
2101                 break;
2102             default:
2103                 /* Omit the beginning MRL-selector characters */
2104                 psz_device = strdup( psz_name + strlen(VCD_MRL) );
2105                 break;
2106             }
2107         }
2108         else if( !strncmp(psz_name, CDDA_MRL, strlen(CDDA_MRL) ) )
2109         {
2110             switch( psz_name[strlen(CDDA_MRL)] )
2111             {
2112             case '\0':
2113             case '@':
2114                 psz_device = config_GetPsz( p_intf, "cd-audio" );
2115                 break;
2116             default:
2117                 /* Omit the beginning MRL-selector characters */
2118                 psz_device = strdup( psz_name + strlen(CDDA_MRL) );
2119                 break;
2120             }
2121         }
2122         else
2123         {
2124             psz_device = strdup( psz_name );
2125         }
2126     }
2127
2128     vlc_mutex_unlock( &p_playlist->object_lock );
2129     vlc_object_release( p_playlist );
2130
2131     if( psz_device == NULL )
2132     {
2133         return;
2134     }
2135
2136     /* Remove what we have after @ */
2137     psz_parser = psz_device;
2138     for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
2139     {
2140         if( *psz_parser == '@' )
2141         {
2142             *psz_parser = '\0';
2143             break;
2144         }
2145     }
2146
2147     /* If there's a stream playing, we aren't allowed to eject ! */
2148     if( p_intf->p_sys->p_input == NULL )
2149     {
2150         msg_Dbg( p_intf, "ejecting %s", psz_device );
2151
2152         intf_Eject( p_intf, psz_device );
2153     }
2154
2155     free(psz_device);
2156     return;
2157 }
2158
2159 static int comp_dir_entries( const void *pp_dir_entry1,
2160                              const void *pp_dir_entry2 )
2161 {
2162     struct dir_entry_t *p_dir_entry1 = *(struct dir_entry_t**)pp_dir_entry1;
2163     struct dir_entry_t *p_dir_entry2 = *(struct dir_entry_t**)pp_dir_entry2;
2164     if ( p_dir_entry1->b_file == p_dir_entry2->b_file ) {
2165         return strcasecmp( p_dir_entry1->psz_path, p_dir_entry2->psz_path );
2166     }
2167     else
2168     {
2169         return ( p_dir_entry1->b_file ? 1 : -1 );
2170     }
2171 }
2172
2173 static void ReadDir( intf_thread_t *p_intf )
2174 {
2175     intf_sys_t *p_sys = p_intf->p_sys;
2176     DIR *p_current_dir;
2177     int i;
2178
2179     if( p_sys->psz_current_dir && *p_sys->psz_current_dir )
2180     {
2181         char *psz_entry;
2182
2183         /* Open the dir */
2184         p_current_dir = utf8_opendir( p_sys->psz_current_dir );
2185
2186         if( p_current_dir == NULL )
2187         {
2188             /* something went bad, get out of here ! */
2189             msg_Warn( p_intf, "cannot open directory `%s' (%m)",
2190                       p_sys->psz_current_dir );
2191             return;
2192         }
2193
2194         /* Clean the old shit */
2195         for( i = 0; i < p_sys->i_dir_entries; i++ )
2196         {
2197             struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
2198             free( p_dir_entry->psz_path );
2199             REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
2200             free( p_dir_entry );
2201         }
2202         p_sys->pp_dir_entries = NULL;
2203         p_sys->i_dir_entries = 0;
2204
2205         /* while we still have entries in the directory */
2206         while( ( psz_entry = utf8_readdir( p_current_dir ) ) != NULL )
2207         {
2208 #if defined( S_ISDIR )
2209             struct stat stat_data;
2210 #endif
2211             struct dir_entry_t *p_dir_entry;
2212             int i_size_entry = strlen( p_sys->psz_current_dir ) +
2213                                strlen( psz_entry ) + 2;
2214             char *psz_uri;
2215
2216             if( p_sys->b_show_hidden_files == VLC_FALSE &&
2217                 ( strlen( psz_entry ) && psz_entry[0] == '.' ) &&
2218                 strcmp( psz_entry, ".." ) )
2219             {
2220                 free( psz_entry );
2221                 continue;
2222             }
2223
2224             psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
2225             sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, psz_entry );
2226
2227             if( !( p_dir_entry = malloc( sizeof( struct dir_entry_t) ) ) )
2228             {
2229                 free( psz_uri );
2230                 free( psz_entry );
2231                 continue;
2232             }
2233
2234 #if defined( S_ISDIR )
2235             if( !utf8_stat( psz_uri, &stat_data )
2236              && S_ISDIR(stat_data.st_mode) )
2237 /*#elif defined( DT_DIR )
2238             if( p_dir_content->d_type & DT_DIR )*/
2239 #else
2240             if( 0 )
2241 #endif
2242             {
2243                 p_dir_entry->psz_path = strdup( psz_entry );
2244                 p_dir_entry->b_file = VLC_FALSE;
2245                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2246                      p_sys->i_dir_entries, p_dir_entry );
2247             }
2248             else
2249             {
2250                 p_dir_entry->psz_path = strdup( psz_entry );
2251                 p_dir_entry->b_file = VLC_TRUE;
2252                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2253                      p_sys->i_dir_entries, p_dir_entry );
2254             }
2255
2256             free( psz_uri );
2257             free( psz_entry );
2258         }
2259
2260         /* Sort */
2261         qsort( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2262                sizeof(struct dir_entry_t*), &comp_dir_entries );
2263
2264         closedir( p_current_dir );
2265         return;
2266     }
2267     else
2268     {
2269         msg_Dbg( p_intf, "no current dir set" );
2270         return;
2271     }
2272 }
2273
2274 static void PlayPause( intf_thread_t *p_intf )
2275 {
2276     input_thread_t *p_input = p_intf->p_sys->p_input;
2277     vlc_value_t val;
2278
2279     if( p_input )
2280     {
2281         var_Get( p_input, "state", &val );
2282         if( val.i_int != PAUSE_S )
2283         {
2284             val.i_int = PAUSE_S;
2285         }
2286         else
2287         {
2288             val.i_int = PLAYING_S;
2289         }
2290         var_Set( p_input, "state", val );
2291     }
2292     else if( p_intf->p_sys->p_playlist )
2293     {
2294         playlist_Play( p_intf->p_sys->p_playlist );
2295     }
2296 }
2297
2298 /****************************************************************************
2299  *
2300  ****************************************************************************/
2301 static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title )
2302 {
2303     int i;
2304     int i_len;
2305
2306     if( w > 3 && h > 2 )
2307     {
2308         if( title == NULL ) title = "";
2309         i_len = strlen( title );
2310
2311         if( i_len > w - 2 ) i_len = w - 2;
2312
2313         mvwaddch( win, y, x,    ACS_ULCORNER );
2314         mvwhline( win, y, x+1,  ACS_HLINE, ( w-i_len-2)/2 );
2315         mvwprintw( win,y, x+1+(w-i_len-2)/2, "%s", title );
2316         mvwhline( win, y, x+(w-i_len)/2+i_len,  ACS_HLINE, w - 1 - ((w-i_len)/2+i_len) );
2317         mvwaddch( win, y, x+w-1,ACS_URCORNER );
2318
2319         for( i = 0; i < h-2; i++ )
2320         {
2321             mvwaddch( win, y+i+1, x,     ACS_VLINE );
2322             mvwaddch( win, y+i+1, x+w-1, ACS_VLINE );
2323         }
2324
2325         mvwaddch( win, y+h-1, x,     ACS_LLCORNER );
2326         mvwhline( win, y+h-1, x+1,   ACS_HLINE, w - 2 );
2327         mvwaddch( win, y+h-1, x+w-1, ACS_LRCORNER );
2328     }
2329 }
2330
2331 static void DrawEmptyLine( WINDOW *win, int y, int x, int w )
2332 {
2333     if( w > 0 )
2334     {
2335         mvhline( y, x, ' ', w );
2336     }
2337 }
2338
2339 static void DrawLine( WINDOW *win, int y, int x, int w )
2340 {
2341     if( w > 0 )
2342     {
2343         attrset( A_REVERSE );
2344         mvhline( y, x, ' ', w );
2345         attroff( A_REVERSE );
2346     }
2347 }