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