ncurses.c 63 KB
Newer Older
1 2 3
/*****************************************************************************
 * ncurses.c : NCurses plugin for vlc
 *****************************************************************************
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>
hartman's avatar
hartman committed
9 10
 *          Yoann Peronneau <yoann@videolan.org>
 *          Derk-Jan Hartman <hartman at videolan dot org>
11
 *
12 13 14 15
 * 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.
16
 *
17 18 19 20 21 22 23
 * 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
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>
#include <errno.h>                                                 /* ENOMEM */
#include <stdio.h>
#include <time.h>

#include <curses.h>

#include <vlc/vlc.h>
#include <vlc/intf.h>
#include <vlc/vout.h>
hartman's avatar
hartman committed
41
#include <vlc/aout.h>
42
#include "charset.h"
43

hartman's avatar
hartman committed
44 45 46 47 48 49 50 51
#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

52 53
#ifdef HAVE_CDDAX
#define CDDA_MRL "cddax://"
54
#else
55 56 57 58 59 60 61 62 63
#define CDDA_MRL "cdda://"
#endif

#ifdef HAVE_VCDX
#define VCD_MRL "vcdx://"
#else
#define VCD_MRL "vcdx://"
#endif

64 65 66
#define SEARCH_CHAIN_SIZE 20
#define OPEN_CHAIN_SIZE 50

67 68 69 70
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int  Open           ( vlc_object_t * );
71
static void Close          ( vlc_object_t * );
72

73
static void Run            ( intf_thread_t * );
74
static void PlayPause      ( intf_thread_t * );
75 76 77
static void Eject          ( intf_thread_t * );

static int  HandleKey      ( intf_thread_t *, int );
78
static void Redraw         ( intf_thread_t *, time_t * );
79 80 81 82 83 84
static void PlaylistRebuild( intf_thread_t * );
static void PlaylistAddNode( intf_thread_t *, playlist_item_t *, int, char *);
static void PlaylistDestroy( intf_thread_t * );
static int  PlaylistChanged( vlc_object_t *, const char *, vlc_value_t,
                             vlc_value_t, void * );
static void FindIndex      ( intf_thread_t * );
85
static void SearchPlaylist ( intf_thread_t *, char * );
86
static int  SubSearchPlaylist( intf_thread_t *, char *, int, int );
87
static void ManageSlider   ( intf_thread_t * );
hartman's avatar
hartman committed
88
static void ReadDir        ( intf_thread_t * );
89 90 91 92

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
hartman's avatar
hartman committed
93 94 95

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

99
vlc_module_begin();
100
    set_shortname( "Ncurses" );
101
    set_description( _("Ncurses interface") );
102
    set_capability( "interface", 10 );
zorglub's avatar
zorglub committed
103
    set_category( CAT_INTERFACE );
zorglub's avatar
zorglub committed
104
    set_subcategory( SUBCAT_INTERFACE_MAIN );
105 106
    set_callbacks( Open, Close );
    add_shortcut( "curses" );
hartman's avatar
hartman committed
107
    add_directory( "browse-dir", NULL, NULL, BROWSE_TEXT, BROWSE_LONGTEXT, VLC_FALSE );
108 109 110 111 112
vlc_module_end();

/*****************************************************************************
 * intf_sys_t: description and status of ncurses interface
 *****************************************************************************/
113 114 115 116 117 118
enum
{
    BOX_NONE,
    BOX_HELP,
    BOX_INFO,
    BOX_LOG,
119 120
    BOX_PLAYLIST,
    BOX_SEARCH,
hartman's avatar
hartman committed
121 122 123 124 125 126 127
    BOX_OPEN,
    BOX_BROWSE
};
struct dir_entry_t
{
    vlc_bool_t  b_file;
    char        *psz_path;
128
};
129 130 131 132 133
struct pl_item_t
{
    playlist_item_t *p_item;
    char            *psz_display;
};
134 135
struct intf_sys_t
{
136 137 138 139 140 141 142 143 144 145 146 147 148 149
    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;

150 151
    int             i_box_plidx;    /* Playlist index */
    int             b_box_plidx_follow;
152 153
    playlist_item_t *p_plnode;      /* Playlist node */
    int             i_box_bidx;     /* browser index */
154

155
    int             b_box_cleared;
156

157
    msg_subscription_t* p_sub;                  /* message bank subscription */
158 159 160 161 162 163

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

    char            *psz_open_chain;
164
    char             psz_partial_keys[7];
165

hartman's avatar
hartman committed
166 167 168
    char            *psz_current_dir;
    int             i_dir_entries;
    struct dir_entry_t  **pp_dir_entries;
169
    vlc_bool_t      b_show_hidden_files;
170 171 172 173 174

    int             i_current_view;             /* playlist view             */
    struct pl_item_t    **pp_plist;
    int             i_plist_entries;
    vlc_bool_t      b_need_update;              /* for playlist view */
175 176
};

177
static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title );
178 179 180
static void DrawLine( WINDOW *win, int y, int x, int w );
static void DrawEmptyLine( WINDOW *win, int y, int x, int w );

181 182 183 184
/*****************************************************************************
 * Open: initialize and create window
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
185
{
186
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
187
    intf_sys_t    *p_sys;
188
    vlc_value_t    val;
189 190

    /* Allocate instance and initialize some members */
191 192 193 194 195 196 197 198 199
    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;
200
    p_sys->b_box_plidx_follow = VLC_TRUE;
201
    p_sys->b_box_cleared = VLC_FALSE;
202
    p_sys->i_box_plidx = 0;
203
    p_sys->p_plnode = NULL;
204
    p_sys->i_box_bidx = 0;
205
    p_sys->p_sub = msg_Subscribe( p_intf, MSG_QUEUE_NORMAL );
206
    memset( p_sys->psz_partial_keys, 0, sizeof( p_sys->psz_partial_keys ) );
207 208

    /* Initialize the curses library */
209 210
    p_sys->w = initscr();
    keypad( p_sys->w, TRUE );
211 212 213 214 215 216 217 218 219 220 221 222
    /* 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();

223 224 225
    /* exported function */
    p_intf->pf_run = Run;

226 227 228 229
    /* Set quiet mode */
    val.i_int = -1;
    var_Set( p_intf->p_vlc, "verbose", val );

230
    /* Set defaul playlist view */
zorglub's avatar
zorglub committed
231
    p_sys->i_current_view = VIEW_CATEGORY;
232 233 234 235
    p_sys->pp_plist = NULL;
    p_sys->i_plist_entries = 0;
    p_sys->b_need_update = VLC_FALSE;

236 237 238 239 240 241 242
    /* 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 );
243

hartman's avatar
hartman committed
244 245 246
    /* Initialize browser options */
    var_Create( p_intf, "browse-dir", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_intf, "browse-dir", &val);
247

hartman's avatar
hartman committed
248 249
    if( val.psz_string && *val.psz_string )
    {
250
        p_sys->psz_current_dir = strdup( val.psz_string );
hartman's avatar
hartman committed
251 252 253 254 255 256
        free( val.psz_string );
    }
    else
    {
        p_sys->psz_current_dir = strdup( p_intf->p_vlc->psz_homedir );
    }
257

hartman's avatar
hartman committed
258
    p_sys->i_dir_entries = 0;
259
    p_sys->pp_dir_entries = NULL;
260
    p_sys->b_show_hidden_files = VLC_FALSE;
hartman's avatar
hartman committed
261
    ReadDir( p_intf );
262

263
    return VLC_SUCCESS;
264 265 266 267 268 269
}

/*****************************************************************************
 * Close: destroy interface window
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
270
{
271
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
272
    intf_sys_t    *p_sys = p_intf->p_sys;
hartman's avatar
hartman committed
273
    int i;
274

275 276 277 278 279 280 281
    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
282 283 284 285 286 287 288 289
    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;
290

hartman's avatar
hartman committed
291
    if( p_sys->psz_current_dir ) free( p_sys->psz_current_dir );
292 293 294 295
    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 );

296 297 298 299 300
    if( p_sys->p_input )
    {
        vlc_object_release( p_sys->p_input );
    }
    if( p_sys->p_playlist )
301
    {
302
        vlc_object_release( p_sys->p_playlist );
303 304 305 306 307
    }

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

308 309
    msg_Unsubscribe( p_intf, p_sys->p_sub );

310
    /* Destroy structure */
311
    free( p_sys );
312 313 314 315 316 317 318
}

/*****************************************************************************
 * Run: ncurses thread
 *****************************************************************************/
static void Run( intf_thread_t *p_intf )
{
319 320 321
    intf_sys_t    *p_sys = p_intf->p_sys;

    int i_key;
322 323 324 325 326 327 328 329 330 331 332
    time_t t_last_refresh;

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

    while( !p_intf->b_die )
    {
        msleep( INTF_IDLE_SLEEP );

333
        /* Update the input */
334
        if( p_sys->p_playlist == NULL )
335
        {
336 337 338 339 340 341 342 343 344
            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 );
            }
345
        }
346
        if( p_sys->p_playlist )
347
        {
348 349
            vlc_mutex_lock( &p_sys->p_playlist->object_lock );
            if( p_sys->p_input == NULL )
350
            {
351 352 353 354 355 356 357 358
                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 );
                    }
                }
359
            }
360
            else if( p_sys->p_input->b_dead )
361
            {
362 363 364 365
                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;
366
            }
367
            vlc_mutex_unlock( &p_sys->p_playlist->object_lock );
368
        }
369

370
        if( p_sys->b_box_plidx_follow && p_sys->p_playlist->i_index >= 0 )
371
        {
372
            FindIndex( p_intf );
373
        }
374

375
        while( ( i_key = getch()) != -1 )
376 377 378 379
        {
            /*
             * HandleKey returns 1 if the screen needs to be redrawn
             */
380
            if( HandleKey( p_intf, i_key ) )
381 382 383 384
            {
                Redraw( p_intf, &t_last_refresh );
            }
        }
385 386 387 388 389 390 391
        /* 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;
        }
392 393 394 395

        /*
         * redraw the screen every second
         */
396
        if( (time(0) - t_last_refresh) >= 1 )
397
        {
398
            ManageSlider( p_intf );
399 400 401 402 403 404 405
            Redraw( p_intf, &t_last_refresh );
        }
    }
}

/* following functions are local */

406
static char *KeyToUTF8( int i_key, char *psz_part )
407
{
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    char *psz_utf8, *psz;
    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;

    psz_utf8 = FromLocaleDup( psz_part );

    /* Ugly check for incomplete bytes sequences
     * (in case of non-UTF8 multibyte local encoding) */
    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;
    }

    memset( psz_part, 0, 6 );
    return psz_utf8;
441 442 443 444 445 446 447 448 449 450 451
}

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

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

452 453
static int HandleKey( intf_thread_t *p_intf, int i_key )
{
454 455 456 457 458
    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 )
    {
459 460
        int b_ret = VLC_TRUE;

461 462
        switch( i_key )
        {
hartman's avatar
hartman committed
463 464
            vlc_value_t val;
            /* Playlist Settings */
465
            case 'r':
hartman's avatar
hartman committed
466 467 468 469 470 471 472 473
                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 );
474
                return 1;
hartman's avatar
hartman committed
475 476 477 478 479 480 481
            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 */
482
            case 'o':
483
                playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_NORMAL );
484 485
                return 1;
            case 'O':
486
                playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_REVERSE );
487 488
                return 1;

489 490 491 492 493 494 495 496
            /* Playlist view */
            case 'v':
                switch( p_sys->i_current_view )
                {
                    case VIEW_CATEGORY:
                        p_sys->i_current_view = VIEW_ALL;
                        break;
                    default:
zorglub's avatar
zorglub committed
497
                        p_sys->i_current_view = VIEW_CATEGORY;
498 499 500 501 502
                }
                PlaylistRebuild( p_intf );
                FindIndex( p_intf );
                return 1;

503 504
            /* Playlist navigation */
            case KEY_HOME:
505 506
                p_sys->i_box_plidx = 0;
                break;
507
            case KEY_END:
508 509
                p_sys->i_box_plidx = p_sys->p_playlist->i_size - 1;
                break;
510
            case KEY_UP:
511 512
                p_sys->i_box_plidx--;
                break;
513
            case KEY_DOWN:
514 515
                p_sys->i_box_plidx++;
                break;
516
            case KEY_PPAGE:
517 518
                p_sys->i_box_plidx -= p_sys->i_box_lines;
                break;
519
            case KEY_NPAGE:
520 521
                p_sys->i_box_plidx += p_sys->i_box_lines;
                break;
hartman's avatar
hartman committed
522
            case 'D':
523 524 525 526
            case KEY_BACKSPACE:
            case KEY_DC:
            {
                int i_item = p_sys->p_playlist->i_index;
527

528 529 530
                playlist_LockDelete( p_sys->p_playlist, p_sys->i_box_plidx );
                if( i_item < p_sys->p_playlist->i_size &&
                    i_item != p_sys->p_playlist->i_index )
531 532 533
                {
                    playlist_Goto( p_sys->p_playlist, i_item );
                }
534
                break;
535 536
            }

537 538
            case KEY_ENTER:
            case 0x0d:
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
                if( p_sys->i_current_view == VIEW_ALL )
                {
                    playlist_Goto( p_sys->p_playlist, p_sys->i_box_plidx );
                }
                else
                {
                    if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
                                == -1 )
                    {
                        playlist_Control( p_sys->p_playlist, PLAYLIST_ITEMPLAY,
                            p_sys->pp_plist[p_sys->i_box_plidx]->p_item );
                    }
                    else
                    {
                        playlist_Control( p_sys->p_playlist, PLAYLIST_VIEWPLAY,
                            p_sys->i_current_view,
                            p_sys->pp_plist[p_sys->i_box_plidx]->p_item,
                            NULL );
                    }
                }
                p_sys->b_box_plidx_follow = VLC_TRUE;
560
                break;
561
            default:
562
                b_ret = VLC_FALSE;
563 564
                break;
        }
565 566 567

        if( b_ret )
        {
568 569 570 571
            int i_max = p_sys->i_plist_entries;
            if( p_sys->i_current_view == VIEW_ALL )
                i_max = p_sys->p_playlist->i_size;
            if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
572
            if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
573 574 575 576 577 578 579
            if( p_sys->i_current_view == VIEW_ALL )
            {
                if( p_sys->i_box_plidx == p_sys->p_playlist->i_index )
                    p_sys->b_box_plidx_follow = VLC_TRUE;
                else
                    p_sys->b_box_plidx_follow = VLC_FALSE;
            }
580
            else
581 582 583 584 585 586 587
            {
                if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item ==
                        p_sys->p_playlist->status.p_item )
                    p_sys->b_box_plidx_follow = VLC_TRUE;
                else
                    p_sys->b_box_plidx_follow = VLC_FALSE;
            }
588 589
            return 1;
        }
590
    }
hartman's avatar
hartman committed
591 592
    if( p_sys->i_box_type == BOX_BROWSE )
    {
593
        vlc_bool_t b_ret = VLC_TRUE;
hartman's avatar
hartman committed
594 595 596 597
        /* Browser navigation */
        switch( i_key )
        {
            case KEY_HOME:
598
                p_sys->i_box_bidx = 0;
hartman's avatar
hartman committed
599 600
                break;
            case KEY_END:
601
                p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
hartman's avatar
hartman committed
602 603
                break;
            case KEY_UP:
604
                p_sys->i_box_bidx--;
hartman's avatar
hartman committed
605 606
                break;
            case KEY_DOWN:
607
                p_sys->i_box_bidx++;
hartman's avatar
hartman committed
608 609
                break;
            case KEY_PPAGE:
610
                p_sys->i_box_bidx -= p_sys->i_box_lines;
hartman's avatar
hartman committed
611 612
                break;
            case KEY_NPAGE:
613
                p_sys->i_box_bidx += p_sys->i_box_lines;
hartman's avatar
hartman committed
614
                break;
615 616 617 618 619
            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
620 621 622

            case KEY_ENTER:
            case 0x0d:
623 624
            case ' ':
                if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file || i_key == ' ' )
hartman's avatar
hartman committed
625 626
                {
                    int i_size_entry = strlen( p_sys->psz_current_dir ) +
627
                                       strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
hartman's avatar
hartman committed
628 629
                    char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);

630
                    sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
hartman's avatar
hartman committed
631 632 633 634 635 636 637 638 639
                    playlist_Add( p_sys->p_playlist, psz_uri,
                                  psz_uri,
                                  PLAYLIST_APPEND, PLAYLIST_END );
                    p_sys->i_box_type = BOX_PLAYLIST;
                    free( psz_uri );
                }
                else
                {
                    int i_size_entry = strlen( p_sys->psz_current_dir ) +
640
                                       strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
hartman's avatar
hartman committed
641 642
                    char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);

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

hartman's avatar
hartman committed
645 646 647 648 649 650
                    p_sys->psz_current_dir = strdup( psz_uri );
                    ReadDir( p_intf );
                    free( psz_uri );
                }
                break;
            default:
651
                b_ret = VLC_FALSE;
hartman's avatar
hartman committed
652
                break;
653 654 655
        }
        if( b_ret )
        {
656 657
            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
658 659 660
            return 1;
        }
    }
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
    else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO )
    {
        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;
701
                ManageSlider( p_intf );
702 703 704
                return 1;
            case KEY_END:
                p_sys->f_slider = 99.9;
705
                ManageSlider( p_intf );
706 707
                return 1;
            case KEY_UP:
708
                p_sys->f_slider += 5.0;
709
                if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
710
                ManageSlider( p_intf );
711 712
                return 1;
            case KEY_DOWN:
713
                p_sys->f_slider -= 5.0;
714
                if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
715
                ManageSlider( p_intf );
716 717 718 719 720 721
                return 1;

            default:
                break;
        }
    }
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
    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:
748
                RemoveLastUTF8Entity( p_sys->psz_search_chain, i_chain_len );
749 750
                break;
            default:
751
            {
752
                char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
753

754
                if( psz_utf8 != NULL )
755
                {
756 757 758 759 760 761
                    if( i_chain_len + strlen( psz_utf8 ) < SEARCH_CHAIN_SIZE )
                    {
                        strcpy( p_sys->psz_search_chain + i_chain_len,
                                psz_utf8 );
                    }
                    free( psz_utf8 );
762 763
                }
                break;
764
            }
765 766 767 768 769 770 771 772 773 774 775
        }
        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 )
    {
776
        int i_chain_len = strlen( p_sys->psz_open_chain );
777 778 779 780 781 782 783 784 785
        playlist_t *p_playlist = p_sys->p_playlist;

        switch( i_key )
        {
            case 0x0c:      /* ^l */
                clear();
                return 1;
            case KEY_ENTER:
            case 0x0d:
786
                if( p_playlist && i_chain_len > 0 )
787 788 789 790 791 792 793 794 795 796 797 798
                {
                    playlist_Add( p_playlist, p_sys->psz_open_chain,
                                  p_sys->psz_open_chain,
                                  PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END );
                    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:
799
                RemoveLastUTF8Entity( p_sys->psz_open_chain, i_chain_len );
800 801
                break;
            default:
802
            {
803
                char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
804

805
                if( psz_utf8 != NULL )
806
                {
807 808 809 810 811 812
                    if( i_chain_len + strlen( psz_utf8 ) < OPEN_CHAIN_SIZE )
                    {
                        strcpy( p_sys->psz_open_chain + i_chain_len,
                                psz_utf8 );
                    }
                    free( psz_utf8 );
813 814
                }
                break;
815
            }
816 817 818 819
        }
        return 1;
    }

820 821

    /* Common keys */
822 823 824 825
    switch( i_key )
    {
        case 'q':
        case 'Q':
826
        case 0x1b:  /* Esc */
hartman's avatar
hartman committed
827
            p_intf->p_vlc->b_die = VLC_TRUE;
828 829
            return 0;

830 831 832 833 834 835 836 837
        /* 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;
hartman's avatar
hartman committed
838
        case 'L':
839 840 841 842 843 844 845 846 847 848 849
            if( p_sys->i_box_type == BOX_LOG )
                p_sys->i_box_type = BOX_NONE;
            else
                p_sys->i_box_type = BOX_LOG;
            return 1;
        case 'P':
            if( p_sys->i_box_type == BOX_PLAYLIST )
                p_sys->i_box_type = BOX_NONE;
            else
                p_sys->i_box_type = BOX_PLAYLIST;
            return 1;
hartman's avatar
hartman committed
850 851 852 853 854 855
        case 'B':
            if( p_sys->i_box_type == BOX_BROWSE )
                p_sys->i_box_type = BOX_NONE;
            else
                p_sys->i_box_type = BOX_BROWSE;
            return 1;
856 857 858 859 860 861 862
        case 'h':
        case 'H':
            if( p_sys->i_box_type == BOX_HELP )
                p_sys->i_box_type = BOX_NONE;
            else
                p_sys->i_box_type = BOX_HELP;
            p_sys->i_box_lines_total = 0;
863
            return 1;
864 865 866 867 868 869 870 871 872 873 874 875 876
        case '/':
            if( p_sys->i_box_type != BOX_SEARCH )
            {
                if( p_sys->psz_search_chain == NULL )
                {
                    return 1;
                }
                p_sys->psz_search_chain[0] = '\0';
                p_sys->b_box_plidx_follow = VLC_FALSE;
                p_sys->i_before_search = p_sys->i_box_plidx;
                p_sys->i_box_type = BOX_SEARCH;
            }
            return 1;
hartman's avatar
hartman committed
877
        case 'A': /* Open */
878 879 880 881 882 883 884 885 886 887
            if( p_sys->i_box_type != BOX_OPEN )
            {
                if( p_sys->psz_open_chain == NULL )
                {
                    return 1;
                }
                p_sys->psz_open_chain[0] = '\0';
                p_sys->i_box_type = BOX_OPEN;
            }
            return 1;
888

889 890
        /* Navigation */
        case KEY_RIGHT:
891
            p_sys->f_slider += 1.0;
892
            if( p_sys->f_slider > 99.9 ) p_sys->f_slider = 99.9;
893
            ManageSlider( p_intf );
894 895
            return 1;

896
        case KEY_LEFT:
897
            p_sys->f_slider -= 1.0;
898
            if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
899
            ManageSlider( p_intf );
900 901
            return 1;

902 903 904
        /* Common control */
        case 'f':
        {
sigmunau's avatar
sigmunau committed
905
            if( p_intf->p_sys->p_input )
906
            {
907
                vout_thread_t *p_vout;
sigmunau's avatar
sigmunau committed
908 909 910 911
                p_vout = vlc_object_find( p_intf->p_sys->p_input,
                                          VLC_OBJECT_VOUT, FIND_CHILD );
                if( p_vout )
                {
912 913 914
                    var_Get( p_vout, "fullscreen", &val );
                    val.b_bool = !val.b_bool;
                    var_Set( p_vout, "fullscreen", val );
sigmunau's avatar
sigmunau committed
915 916
                    vlc_object_release( p_vout );
                }
917 918 919 920 921 922 923 924 925 926 927 928 929
                else
                {
                    playlist_t *p_playlist;
                    p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                  FIND_ANYWHERE );
                    if( p_playlist )
                    {
                        var_Get( p_playlist, "fullscreen", &val );
                        val.b_bool = !val.b_bool;
                        var_Set( p_playlist, "fullscreen", val );
                        vlc_object_release( p_playlist );
                    }
                }
930 931 932 933 934 935
            }
            return 0;
        }

        case ' ':
            PlayPause( p_intf );
936 937
            return 1;

938 939 940 941 942
        case 's':
            if( p_intf->p_sys->p_playlist )
            {
                playlist_Stop( p_intf->p_sys->p_playlist );
            }
943 944 945 946 947 948 949
            return 1;

        case 'e':
            Eject( p_intf );
            return 1;

        case '[':
950 951 952 953 954
            if( p_sys->p_input )
            {
                val.b_bool = VLC_TRUE;
                var_Set( p_sys->p_input, "prev-title", val );
            }
955
            return 1;
956 957

        case ']':
958 959 960 961 962
            if( p_sys->p_input )
            {
                val.b_bool = VLC_TRUE;
                var_Set( p_sys->p_input, "next-title", val );
            }
963
            return 1;
964 965

        case '<':
966 967 968 969 970
            if( p_sys->p_input )
            {
                val.b_bool = VLC_TRUE;
                var_Set( p_sys->p_input, "prev-chapter", val );
            }
971
            return 1;
972 973

        case '>':
974 975 976 977 978
            if( p_sys->p_input )
            {
                val.b_bool = VLC_TRUE;
                var_Set( p_sys->p_input, "next-chapter", val );
            }
979
            return 1;
980

981 982 983 984 985 986 987
        case 'p':
            if( p_intf->p_sys->p_playlist )
            {
                playlist_Prev( p_intf->p_sys->p_playlist );
            }
            clear();
            return 1;
hartman's avatar
hartman committed
988

989 990 991 992 993 994 995
        case 'n':
            if( p_intf->p_sys->p_playlist )
            {
                playlist_Next( p_intf->p_sys->p_playlist );
            }
            clear();
            return 1;
996

hartman's avatar
hartman committed
997 998 999 1000 1001 1002 1003 1004 1005