ncurses.c 70.4 KB
Newer Older
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>
hartman's avatar
hartman committed
9 10
 *          Yoann Peronneau <yoann@videolan.org>
 *          Derk-Jan Hartman <hartman at videolan dot org>
11
 *          Rafaël Carré <funman@videolanorg>
12
 *
13 14 15 16
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
17
 *
18 19 20 21 22 23 24
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 27 28 29 30
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
31 32
#include <vlc/vlc.h>

33 34 35
#include <errno.h>                                                 /* ENOMEM */
#include <time.h>

36 37
#ifdef HAVE_NCURSESW
#   define _XOPEN_SOURCE_EXTENDED 1
38
#endif
39

40 41
#include <ncurses.h>

zorglub's avatar
zorglub committed
42
#include <vlc_interface.h>
43 44
#include <vlc_vout.h>
#include <vlc_aout.h>
zorglub's avatar
zorglub committed
45
#include <vlc_charset.h>
46 47
#include <vlc_input.h>
#include <vlc_playlist.h>
48
#include <vlc_meta.h>
49

hartman's avatar
hartman committed
50 51 52 53 54 55 56 57
#ifdef HAVE_SYS_STAT_H
#   include <sys/stat.h>
#endif
#if (!defined( WIN32 ) || defined(__MINGW32__))
/* Mingw has its own version of dirent */
#   include <dirent.h>
#endif

58 59
#ifdef HAVE_CDDAX
#define CDDA_MRL "cddax://"
60
#else
61 62 63 64 65 66
#define CDDA_MRL "cdda://"
#endif

#ifdef HAVE_VCDX
#define VCD_MRL "vcdx://"
#else
67
#define VCD_MRL "vcd://"
68 69
#endif

70 71 72
#define SEARCH_CHAIN_SIZE 20
#define OPEN_CHAIN_SIZE 50

73 74 75 76
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
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 82 83
static void Eject          ( intf_thread_t * );

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 * );
Laurent Aimar's avatar
Laurent Aimar committed
88
static void PlaylistAddNode( intf_thread_t *, playlist_item_t *, int, const char *);
89 90 91
static void PlaylistDestroy( intf_thread_t * );
static int  PlaylistChanged( vlc_object_t *, const char *, vlc_value_t,
                             vlc_value_t, void * );
92 93
static inline vlc_bool_t PlaylistIsPlaying( intf_thread_t *,
                                            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 * );
hartman's avatar
hartman committed
99
static void ReadDir        ( intf_thread_t * );
100 101 102 103

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
hartman's avatar
hartman committed
104 105 106

#define BROWSE_TEXT N_("Filebrowser starting point")
#define BROWSE_LONGTEXT N_( \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
107
    "This option allows you to specify the directory the ncurses filebrowser " \
hartman's avatar
hartman committed
108 109
    "will show you initially.")

110
vlc_module_begin();
111
    set_shortname( "Ncurses" );
112
    set_description( _("Ncurses interface") );
113
    set_capability( "interface", 10 );
zorglub's avatar
zorglub committed
114
    set_category( CAT_INTERFACE );
zorglub's avatar
zorglub committed
115
    set_subcategory( SUBCAT_INTERFACE_MAIN );
116 117
    set_callbacks( Open, Close );
    add_shortcut( "curses" );
hartman's avatar
hartman committed
118
    add_directory( "browse-dir", NULL, NULL, BROWSE_TEXT, BROWSE_LONGTEXT, VLC_FALSE );
119 120 121 122 123
vlc_module_end();

/*****************************************************************************
 * intf_sys_t: description and status of ncurses interface
 *****************************************************************************/
124 125 126 127 128 129
enum
{
    BOX_NONE,
    BOX_HELP,
    BOX_INFO,
    BOX_LOG,
130 131
    BOX_PLAYLIST,
    BOX_SEARCH,
hartman's avatar
hartman committed
132
    BOX_OPEN,
133 134
    BOX_BROWSE,
    BOX_META
hartman's avatar
hartman committed
135
};
136 137 138 139 140
enum
{
    VIEW_CATEGORY,
    VIEW_ONELEVEL
};
hartman's avatar
hartman committed
141 142 143 144
struct dir_entry_t
{
    vlc_bool_t  b_file;
    char        *psz_path;
145
};
146 147 148 149 150
struct pl_item_t
{
    playlist_item_t *p_item;
    char            *psz_display;
};
151 152
struct intf_sys_t
{
153 154 155 156 157 158 159 160 161 162 163 164 165 166
    playlist_t     *p_playlist;
    input_thread_t *p_input;

    float           f_slider;
    float           f_slider_old;

    WINDOW          *w;

    int             i_box_type;
    int             i_box_y;
    int             i_box_lines;
    int             i_box_lines_total;
    int             i_box_start;

167 168
    int             i_box_plidx;    /* Playlist index */
    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 176 177 178 179

    char            *psz_search_chain;          /* for playlist searching    */
    char            *psz_old_search;            /* for searching next        */
    int             i_before_search;

    char            *psz_open_chain;
180
    char             psz_partial_keys[7];
181

hartman's avatar
hartman committed
182 183 184
    char            *psz_current_dir;
    int             i_dir_entries;
    struct dir_entry_t  **pp_dir_entries;
185
    vlc_bool_t      b_show_hidden_files;
186 187 188 189

    int             i_current_view;             /* playlist view             */
    struct pl_item_t    **pp_plist;
    int             i_plist_entries;
190 191 192
    vlc_bool_t      b_need_update;              /* for playlist view         */

    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 197 198
static void DrawLine( WINDOW *win, int y, int x, int w );
static void DrawEmptyLine( WINDOW *win, int y, int x, int w );

199 200 201 202
/*****************************************************************************
 * Open: initialize and create window
 *****************************************************************************/
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 210 211 212 213 214 215 216 217
    p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
    p_sys->p_playlist = NULL;
    p_sys->p_input = NULL;
    p_sys->f_slider = 0.0;
    p_sys->f_slider_old = 0.0;
    p_sys->i_box_type = BOX_PLAYLIST;
    p_sys->i_box_lines = 0;
    p_sys->i_box_start= 0;
    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 227
    p_sys->w = initscr();
    keypad( p_sys->w, TRUE );
228 229 230 231 232 233 234 235 236 237 238 239
    /* Don't do NL -> CR/NL */
    nonl();
    /* Take input chars one at a time */
    cbreak();
    /* Don't echo */
    noecho();

    curs_set(0);
    timeout(0);

    clear();

240 241 242
    /* exported function */
    p_intf->pf_run = Run;

243 244 245
    /* Remember verbosity level */
    var_Get( p_intf->p_libvlc, "verbose", &val );
    p_sys->i_verbose = val.i_int;
246 247
    /* Set quiet mode */
    val.i_int = -1;
248
    var_Set( p_intf->p_libvlc, "verbose", val );
249

250
    /* Set defaul playlist view */
zorglub's avatar
zorglub committed
251
    p_sys->i_current_view = VIEW_CATEGORY;
252 253 254 255
    p_sys->pp_plist = NULL;
    p_sys->i_plist_entries = 0;
    p_sys->b_need_update = VLC_FALSE;

256 257 258 259 260 261 262
    /* Initialize search chain */
    p_sys->psz_search_chain = (char *)malloc( SEARCH_CHAIN_SIZE + 1 );
    p_sys->psz_old_search = NULL;
    p_sys->i_before_search = 0;

    /* Initialize open chain */
    p_sys->psz_open_chain = (char *)malloc( OPEN_CHAIN_SIZE + 1 );
263

hartman's avatar
hartman committed
264 265 266
    /* Initialize browser options */
    var_Create( p_intf, "browse-dir", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_intf, "browse-dir", &val);
267

hartman's avatar
hartman committed
268 269
    if( val.psz_string && *val.psz_string )
    {
270
        p_sys->psz_current_dir = strdup( val.psz_string );
hartman's avatar
hartman committed
271 272 273 274
        free( val.psz_string );
    }
    else
    {
275
        p_sys->psz_current_dir = strdup( p_intf->p_libvlc->psz_homedir );
hartman's avatar
hartman committed
276
    }
277

hartman's avatar
hartman committed
278
    p_sys->i_dir_entries = 0;
279
    p_sys->pp_dir_entries = NULL;
280
    p_sys->b_show_hidden_files = VLC_FALSE;
hartman's avatar
hartman committed
281
    ReadDir( p_intf );
282

283
    return VLC_SUCCESS;
284 285 286 287 288 289
}

/*****************************************************************************
 * Close: destroy interface window
 *****************************************************************************/
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;
hartman's avatar
hartman committed
293
    int i;
294

295 296 297 298 299 300 301
    var_DelCallback( p_sys->p_playlist, "intf-change", PlaylistChanged,
                     p_intf );
    var_DelCallback( p_sys->p_playlist, "item-append", PlaylistChanged,
                     p_intf );

    PlaylistDestroy( p_intf );

hartman's avatar
hartman committed
302 303 304 305 306 307 308 309
    for( i = 0; i < p_sys->i_dir_entries; i++ )
    {
        struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
        if( p_dir_entry->psz_path ) free( p_dir_entry->psz_path );
        REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
        if( p_dir_entry ) free( p_dir_entry );
    }
    p_sys->pp_dir_entries = NULL;
310

hartman's avatar
hartman committed
311
    if( p_sys->psz_current_dir ) free( p_sys->psz_current_dir );
312 313 314 315
    if( p_sys->psz_search_chain ) free( p_sys->psz_search_chain );
    if( p_sys->psz_old_search ) free( p_sys->psz_old_search );
    if( p_sys->psz_open_chain ) free( p_sys->psz_open_chain );

316 317 318 319 320
    if( p_sys->p_input )
    {
        vlc_object_release( p_sys->p_input );
    }
    if( p_sys->p_playlist )
321
    {
322
        vlc_object_release( p_sys->p_playlist );
323 324 325 326 327
    }

    /* Close the ncurses interface */
    endwin();

328 329
    msg_Unsubscribe( p_intf, p_sys->p_sub );

330 331 332 333 334
    /* Restores initial verbose setting */
    vlc_value_t val;
    val.i_int = p_sys->i_verbose;
    var_Set( p_intf->p_libvlc, "verbose", val );

335
    /* Destroy structure */
336
    free( p_sys );
337 338 339 340 341 342 343
}

/*****************************************************************************
 * Run: ncurses thread
 *****************************************************************************/
static void Run( intf_thread_t *p_intf )
{
344 345 346
    intf_sys_t    *p_sys = p_intf->p_sys;

    int i_key;
347 348 349 350 351 352 353
    time_t t_last_refresh;

    /*
     * force drawing the interface for the first time
     */
    t_last_refresh = ( time( 0 ) - 1);

354
    while( !intf_ShouldDie( p_intf ) )
355 356 357
    {
        msleep( INTF_IDLE_SLEEP );

358
        /* Update the input */
359
        if( p_sys->p_playlist == NULL )
360
        {
361 362 363 364 365 366 367 368 369
            p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                 FIND_ANYWHERE );
            if( p_sys->p_playlist )
            {
                var_AddCallback( p_sys->p_playlist, "intf-change",
                                 PlaylistChanged, p_intf );
                var_AddCallback( p_sys->p_playlist, "item-append",
                                 PlaylistChanged, p_intf );
            }
370
        }
371
        if( p_sys->p_playlist )
372
        {
373 374
            vlc_mutex_lock( &p_sys->p_playlist->object_lock );
            if( p_sys->p_input == NULL )
375
            {
376 377 378 379 380 381 382 383
                p_sys->p_input = p_sys->p_playlist->p_input;
                if( p_sys->p_input )
                {
                    if( !p_sys->p_input->b_dead )
                    {
                        vlc_object_yield( p_sys->p_input );
                    }
                }
384
            }
385
            else if( p_sys->p_input->b_dead )
386
            {
387 388 389 390
                vlc_object_release( p_sys->p_input );
                p_sys->p_input = NULL;
                p_sys->f_slider = p_sys->f_slider_old = 0.0;
                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 404
        {
            /*
             * HandleKey returns 1 if the screen needs to be redrawn
             */
405
            if( HandleKey( p_intf, i_key ) )
406 407 408 409
            {
                Redraw( p_intf, &t_last_refresh );
            }
        }
410 411 412 413 414 415 416
        /* Hack */
        if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
        {
            clear();
            Redraw( p_intf, &t_last_refresh );
            p_sys->b_box_cleared = VLC_TRUE;
        }
417 418 419 420

        /*
         * redraw the screen every second
         */
421
        if( (time(0) - t_last_refresh) >= 1 )
422
        {
423
            ManageSlider( p_intf );
424 425 426 427 428 429
            Redraw( p_intf, &t_last_refresh );
        }
    }
}

/* following functions are local */
430
static char *KeyToUTF8( int i_key, char *psz_part )
431
{
432
    char *psz_utf8;
433 434 435 436 437 438 439 440 441 442
    int len = strlen( psz_part );
    if( len == 6 )
    {
        /* overflow error - should not happen */
        memset( psz_part, 0, 6 );
        return NULL;
    }

    psz_part[len] = (char)i_key;

443
#ifdef HAVE_NCURSESW
444 445
    psz_utf8 = strdup( psz_part );
#else
446 447 448 449
    psz_utf8 = FromLocaleDup( psz_part );

    /* Ugly check for incomplete bytes sequences
     * (in case of non-UTF8 multibyte local encoding) */
450
    char *psz;
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
    for( psz = psz_utf8; *psz; psz++ )
        if( ( *psz == '?' ) && ( *psz_utf8 != '?' ) )
        {
            /* incomplete bytes sequence detected
             * (VLC core inserted dummy question marks) */
            free( psz_utf8 );
            return NULL;
        }

    /* Check for incomplete UTF8 bytes sequence */
    if( EnsureUTF8( psz_utf8 ) == NULL )
    {
        free( psz_utf8 );
        return NULL;
    }
466
#endif
467 468 469

    memset( psz_part, 0, 6 );
    return psz_utf8;
470 471 472 473 474 475 476 477 478 479 480
}

static inline int RemoveLastUTF8Entity( char *psz, int len )
{
    while( len && ( (psz[--len] & 0xc0) == 0x80 ) );
                       /* UTF8 continuation byte */

    psz[len] = '\0';
    return len;
}

481 482
static int HandleKey( intf_thread_t *p_intf, int i_key )
{
483 484 485 486 487
    intf_sys_t *p_sys = p_intf->p_sys;
    vlc_value_t val;

    if( p_sys->i_box_type == BOX_PLAYLIST && p_sys->p_playlist )
    {
488
        int b_ret = VLC_TRUE;
489
        vlc_bool_t b_box_plidx_follow = VLC_FALSE;
490

491 492
        switch( i_key )
        {
hartman's avatar
hartman committed
493 494
            vlc_value_t val;
            /* Playlist Settings */
495
            case 'r':
hartman's avatar
hartman committed
496 497 498 499 500 501 502 503
                var_Get( p_sys->p_playlist, "random", &val );
                val.b_bool = !val.b_bool;
                var_Set( p_sys->p_playlist, "random", val );
                return 1;
            case 'l':
                var_Get( p_sys->p_playlist, "loop", &val );
                val.b_bool = !val.b_bool;
                var_Set( p_sys->p_playlist, "loop", val );
504
                return 1;
hartman's avatar
hartman committed
505 506 507 508 509 510 511
            case 'R':
                var_Get( p_sys->p_playlist, "repeat", &val );
                val.b_bool = !val.b_bool;
                var_Set( p_sys->p_playlist, "repeat", val );
                return 1;

            /* Playlist sort */
512
            case 'o':
513 514 515 516
                playlist_RecursiveNodeSort( p_sys->p_playlist,
                                            PlaylistGetRoot( p_intf ),
                                            SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
                p_sys->b_need_update = VLC_TRUE;
517 518
                return 1;
            case 'O':
519 520 521 522
                playlist_RecursiveNodeSort( p_sys->p_playlist,
                                            PlaylistGetRoot( p_intf ),
                                            SORT_TITLE_NODES_FIRST, ORDER_REVERSE );
                p_sys->b_need_update = VLC_TRUE;
523 524
                return 1;

525 526 527 528 529
            /* Playlist view */
            case 'v':
                switch( p_sys->i_current_view )
                {
                    case VIEW_CATEGORY:
530
                        p_sys->i_current_view = VIEW_ONELEVEL;
531 532
                        break;
                    default:
zorglub's avatar
zorglub committed
533
                        p_sys->i_current_view = VIEW_CATEGORY;
534
                }
535
                //p_sys->b_need_update = VLC_TRUE;
536 537 538
                PlaylistRebuild( p_intf );
                return 1;

539 540
            /* Playlist navigation */
            case KEY_HOME:
541 542
                p_sys->i_box_plidx = 0;
                break;
543
            case KEY_END:
544
                p_sys->i_box_plidx = p_sys->p_playlist->items.i_size - 1;
545
                break;
546
            case KEY_UP:
547 548
                p_sys->i_box_plidx--;
                break;
549
            case KEY_DOWN:
550 551
                p_sys->i_box_plidx++;
                break;
552
            case KEY_PPAGE:
553 554
                p_sys->i_box_plidx -= p_sys->i_box_lines;
                break;
555
            case KEY_NPAGE:
556 557
                p_sys->i_box_plidx += p_sys->i_box_lines;
                break;
hartman's avatar
hartman committed
558
            case 'D':
559 560 561
            case KEY_BACKSPACE:
            case KEY_DC:
            {
562 563
                playlist_t *p_playlist = p_sys->p_playlist;
                playlist_item_t *p_item;
564

565 566 567 568
                vlc_mutex_lock( &p_playlist->object_lock );
                p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
                if( p_item->i_children == -1 )
                {
569
                    playlist_DeleteFromInput( p_playlist,
zorglub's avatar
zorglub committed
570
                                              p_item->p_input->i_id, VLC_TRUE );
571 572
                }
                else
573
                {
574 575
                    playlist_NodeDelete( p_playlist, p_item,
                                         VLC_TRUE , VLC_FALSE );
576
                }
577
                vlc_mutex_unlock( &p_playlist->object_lock );
578
                PlaylistRebuild( p_intf );
579
                break;
580 581
            }

582 583
            case KEY_ENTER:
            case 0x0d:
584 585 586 587 588
                if( !p_sys->pp_plist || !p_sys->pp_plist[p_sys->i_box_plidx] )
                {
                    b_ret = VLC_FALSE;
                    break;
                }
589 590
                if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
                        == -1 )
591
                {
592 593
                    playlist_item_t *p_item, *p_parent;
                    p_item = p_parent =
594
                            p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
595

596 597
                    if( !p_parent )
                        p_parent = p_sys->p_playlist->p_root_onelevel;
598 599
                    while( p_parent->p_parent )
                        p_parent = p_parent->p_parent;
600
                    playlist_Control( p_sys->p_playlist, PLAYLIST_VIEWPLAY,
601
                                      VLC_TRUE, p_parent, p_item );
602 603
                }
                else
604
                {   /* FIXME doesn't work if the node is empty */
605
                    playlist_Control( p_sys->p_playlist, PLAYLIST_VIEWPLAY,
606
                        VLC_TRUE,
607 608
                        p_sys->pp_plist[p_sys->i_box_plidx]->p_item,
                        NULL );
609
                }
610
                b_box_plidx_follow = VLC_TRUE;
611
                break;
612
            default:
613
                b_ret = VLC_FALSE;
614 615
                break;
        }
616 617 618

        if( b_ret )
        {
619 620
            int i_max = p_sys->i_plist_entries;
            if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
621
            if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
622 623 624 625
            if( PlaylistIsPlaying( p_intf,
                    p_sys->pp_plist[p_sys->i_box_plidx]->p_item ) )
                b_box_plidx_follow = VLC_TRUE;
            p_sys->b_box_plidx_follow = b_box_plidx_follow;
626 627
            return 1;
        }
628
    }
hartman's avatar
hartman committed
629 630
    if( p_sys->i_box_type == BOX_BROWSE )
    {
631
        vlc_bool_t b_ret = VLC_TRUE;
hartman's avatar
hartman committed
632 633 634 635
        /* Browser navigation */
        switch( i_key )
        {
            case KEY_HOME:
636
                p_sys->i_box_bidx = 0;
hartman's avatar
hartman committed
637 638
                break;
            case KEY_END:
639
                p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
hartman's avatar
hartman committed
640 641
                break;
            case KEY_UP:
642
                p_sys->i_box_bidx--;
hartman's avatar
hartman committed
643 644
                break;
            case KEY_DOWN:
645
                p_sys->i_box_bidx++;
hartman's avatar
hartman committed
646 647
                break;
            case KEY_PPAGE:
648
                p_sys->i_box_bidx -= p_sys->i_box_lines;
hartman's avatar
hartman committed
649 650
                break;
            case KEY_NPAGE:
651
                p_sys->i_box_bidx += p_sys->i_box_lines;
hartman's avatar
hartman committed
652
                break;
653 654 655 656 657
            case '.': /* Toggle show hidden files */
                p_sys->b_show_hidden_files = ( p_sys->b_show_hidden_files ==
                    VLC_TRUE ? VLC_FALSE : VLC_TRUE );
                ReadDir( p_intf );
                break;
hartman's avatar
hartman committed
658 659 660

            case KEY_ENTER:
            case 0x0d:
661 662
            case ' ':
                if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file || i_key == ' ' )
hartman's avatar
hartman committed
663 664
                {
                    int i_size_entry = strlen( p_sys->psz_current_dir ) +
665
                                       strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
hartman's avatar
hartman committed
666 667
                    char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);

668
                    sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
669 670

                    playlist_item_t *p_parent = p_sys->p_playlist->status.p_node;
671 672 673 674
                    if( !p_parent )
                        p_parent = p_sys->p_playlist->p_root_onelevel;

                    while( p_parent->p_parent )
675 676 677 678 679 680
                        p_parent = p_parent->p_parent;

                    playlist_Add( p_sys->p_playlist, psz_uri, NULL,
                                  PLAYLIST_APPEND, PLAYLIST_END,
                                  p_parent == p_sys->p_playlist->p_root_onelevel
                                  , VLC_FALSE );
hartman's avatar
hartman committed
681 682 683 684 685 686
                    p_sys->i_box_type = BOX_PLAYLIST;
                    free( psz_uri );
                }
                else
                {
                    int i_size_entry = strlen( p_sys->psz_current_dir ) +
687
                                       strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
hartman's avatar
hartman committed
688 689
                    char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);

690
                    sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
691

hartman's avatar
hartman committed
692 693 694 695 696 697
                    p_sys->psz_current_dir = strdup( psz_uri );
                    ReadDir( p_intf );
                    free( psz_uri );
                }
                break;
            default:
698
                b_ret = VLC_FALSE;
hartman's avatar
hartman committed
699
                break;
700 701 702
        }
        if( b_ret )
        {
703 704
            if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
            if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
hartman's avatar
hartman committed
705 706 707
            return 1;
        }
    }
708 709
    else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO ||
             p_sys->i_box_type == BOX_META )
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
    {
        switch( i_key )
        {
            case KEY_HOME:
                p_sys->i_box_start = 0;
                return 1;
            case KEY_END:
                p_sys->i_box_start = p_sys->i_box_lines_total - 1;
                return 1;
            case KEY_UP:
                if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
                return 1;
            case KEY_DOWN:
                if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
                {
                    p_sys->i_box_start++;
                }
                return 1;
            case KEY_PPAGE:
                p_sys->i_box_start -= p_sys->i_box_lines;
                if( p_sys->i_box_start < 0 ) p_sys->i_box_start = 0;
                return 1;
            case KEY_NPAGE:
                p_sys->i_box_start += p_sys->i_box_lines;
                if( p_sys->i_box_start >= p_sys->i_box_lines_total )
                {
                    p_sys->i_box_start = p_sys->i_box_lines_total - 1;
                }
                return 1;
            default:
                break;
        }
    }
    else if( p_sys->i_box_type == BOX_NONE )
    {
        switch( i_key )
        {
            case KEY_HOME:
                p_sys->f_slider = 0;
749
                ManageSlider( p_intf );
750 751 752
                return 1;
            case KEY_END:
                p_sys->f_slider = 99.9;
753
                ManageSlider( p_intf );
754 755
                return 1;
            case KEY_UP:
756
                p_sys->f_slider += 5.0;
757
                if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
758
                ManageSlider( p_intf );
759 760
                return 1;
            case KEY_DOWN:
761
                p_sys->f_slider -= 5.0;
762
                if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
763
                ManageSlider( p_intf );
764 765 766 767 768 769
                return 1;

            default:
                break;
        }
    }
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
    else if( p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain )
    {
        int i_chain_len;
        i_chain_len = strlen( p_sys->psz_search_chain );
        switch( i_key )
        {
            case 0x0c:      /* ^l */
                clear();
                return 1;
            case KEY_ENTER:
            case 0x0d:
                if( i_chain_len > 0 )
                {
                    p_sys->psz_old_search = strdup( p_sys->psz_search_chain );
                }
                else if( p_sys->psz_old_search )
                {
                    SearchPlaylist( p_intf, p_sys->psz_old_search );
                }
                p_sys->i_box_type = BOX_PLAYLIST;
                return 1;
            case 0x1b:      /* Esc. */
                p_sys->i_box_plidx = p_sys->i_before_search;
                p_sys->i_box_type = BOX_PLAYLIST;
                return 1;
            case KEY_BACKSPACE:
796
                RemoveLastUTF8Entity( p_sys->psz_search_chain, i_chain_len );
797 798
                break;
            default:
799
            {
800
                char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
801

802
                if( psz_utf8 != NULL )
803
                {
804 805 806 807 808 809
                    if( i_chain_len + strlen( psz_utf8 ) < SEARCH_CHAIN_SIZE )
                    {
                        strcpy( p_sys->psz_search_chain + i_chain_len,
                                psz_utf8 );
                    }
                    free( psz_utf8 );
810 811
                }
                break;
812
            }
813 814 815 816 817 818 819 820 821 822 823
        }
        if( p_sys->psz_old_search )
        {
            free( p_sys->psz_old_search );
            p_sys->psz_old_search = NULL;
        }
        SearchPlaylist( p_intf, p_sys->psz_search_chain );
        return 1;
    }
    else if( p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain )
    {
824
        int i_chain_len = strlen( p_sys->psz_open_chain );
825 826 827 828 829 830 831 832 833
        playlist_t *p_playlist = p_sys->p_playlist;

        switch( i_key )
        {
            case 0x0c:      /* ^l */
                clear();
                return 1;
            case KEY_ENTER:
            case 0x0d:
834
                if( p_playlist && i_chain_len > 0 )
835
                {
836
                    playlist_item_t *p_parent = p_sys->p_playlist->status.p_node;
837 838 839 840
                    if( !p_parent )
                        p_parent = p_sys->p_playlist->p_root_onelevel;

                    while( p_parent->p_parent )
841 842 843 844 845 846
                        p_parent = p_parent->p_parent;

                    playlist_Add( p_playlist, p_sys->psz_open_chain, NULL,
                                  PLAYLIST_APPEND|PLAYLIST_GO, PLAYLIST_END,
                                  p_parent == p_sys->p_playlist->p_root_onelevel
                                  , VLC_FALSE );
847 848 849 850 851 852 853 854
                    p_sys->b_box_plidx_follow = VLC_TRUE;
                }
                p_sys->i_box_type = BOX_PLAYLIST;
                return 1;
            case 0x1b:      /* Esc. */
                p_sys->i_box_type = BOX_PLAYLIST;
                return 1;
            case KEY_BACKSPACE:
855
                RemoveLastUTF8Entity( p_sys->psz_open_chain, i_chain_len );
856 857
                break;
            default:
858
            {
859
                char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
860

861
                if( psz_utf8 != NULL )
862
                {
863 864 865 866 867 868
                    if( i_chain_len + strlen( psz_utf8 ) < OPEN_CHAIN_SIZE )
                    {
                        strcpy( p_sys->psz_open_chain + i_chain_len,
                                psz_utf8 );
                    }
                    free( psz_utf8 );
869 870
                }
                break;
871
            }
872 873 874 875
        }
        return 1;
    }

876 877

    /* Common keys */
878 879 880 881
    switch( i_key )
    {
        case 'q':
        case 'Q':
882
        case 0x1b:  /* Esc */
883
            vlc_object_kill( p_intf->p_libvlc );
884 885
            return 0;

886 887 888 889 890 891 892 893
        /* Box switching */
        case 'i':
            if( p_sys->i_box_type == BOX_INFO )
                p_sys->i_box_type = BOX_NONE;
            else
                p_sys->i_box_type = BOX_INFO;
            p_sys->i_box_lines_total = 0;
            return 1;
894 895 896 897 898 899 900
        case 'm':
            if( p_sys->i_box_type == BOX_META )
                p_sys->i_box_type = BOX_NONE;
            else
                p_sys->i_box_type = BOX_META;
            p_sys->i_box_lines_total = 0;
            return 1;
hartman's avatar
hartman committed
901
        case 'L':