/*****************************************************************************
 * open.cpp : Open dialog box
 *****************************************************************************
 * Copyright (C) 2000-2005 the VideoLAN team
 * $Id: 4815671f16ab3a76350f6cee42113ce5ead62bbd $
 *
 * Authors: Gildas Bazin <gbazin@videolan.org>
 *
 * 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.
 *
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include "dialogs/open.hpp"
#include "dialogs/streamout.hpp"
#include "dialogs/preferences_widgets.h"
#include "dialogs/subtitles.hpp"

#include "vlc_charset.h"

#undef HAVE_LIBCDIO // Disable broken code

#ifdef HAVE_LIBCDIO
#include <cdio/cdio.h>
#include <cdio/cd_types.h>
#include <cdio/logging.h>
#endif /* HAVE_LIBCDIO */

#ifdef HAVE_VCDINFO
#include <libvcd/version.h>
/* There was a bug in libvcdinfo <= 23 which prevented C++ compilation */
#if LIBVCD_VERSION_NUM > 23
#include <libvcd/info.h>
#endif /* LIBVCD_VERSION_NUM > 23 */
#endif /* HAVE_VCDINFO */

#include <wx/combobox.h>
#include <wx/statline.h>
#include <wx/tokenzr.h>

#ifndef wxRB_SINGLE
#   define wxRB_SINGLE 0
#endif

#define SELECTION_DISC_TYPE_DVD_MENUS 0
#define SELECTION_DISC_TYPE_DVD       1
#define SELECTION_DISC_TYPE_VCD       2
#define SELECTION_DISC_TYPE_CDDA      3

/*****************************************************************************
 * Event Table.
 *****************************************************************************/

/* IDs for the controls and the menu commands */
enum
{
    Notebook_Event = wxID_HIGHEST,
    MRL_Event,

    FileBrowse_Event,
    SubFileBrowse_Event,
    FileName_Event,
    SubFileName_Event,

    DiscType_Event,
#ifdef HAVE_LIBCDIO
    DiscProbe_Event,
#endif
    DiscDevice_Event,
    DiscTitle_Event,
    DiscChapter_Event,
    DiscSub_Event,
    DiscAudio_Event,

    NetType_Event,
    NetRadio1_Event, NetRadio2_Event, NetRadio3_Event, NetRadio4_Event,
    NetPort1_Event, NetPort2_Event, NetPort3_Event,
    NetAddr1_Event, NetAddr2_Event, NetAddr3_Event, NetAddr4_Event,
    NetForceIPv6_Event, NetTimeshift_Event,

    SubsFileEnable_Event,
    SubsFileSettings_Event,

    SoutEnable_Event,
    SoutSettings_Event,

    CachingEnable_Event,
    CachingChange_Event,

    AdvancedOptions_Event
};

BEGIN_EVENT_TABLE(OpenDialog, wxDialog)
    /* Button events */
    EVT_BUTTON(wxID_OK, OpenDialog::OnOk)
    EVT_BUTTON(wxID_CANCEL, OpenDialog::OnCancel)

    EVT_NOTEBOOK_PAGE_CHANGED(Notebook_Event, OpenDialog::OnPageChange)

    EVT_TEXT(MRL_Event, OpenDialog::OnMRLChange)

    /* Events generated by the file panel */
    EVT_TEXT(FileName_Event, OpenDialog::OnFilePanelChange)
    EVT_BUTTON(FileBrowse_Event, OpenDialog::OnFileBrowse)
    EVT_TEXT(SubFileName_Event, OpenDialog::OnSubFileChange)
    EVT_BUTTON(SubFileBrowse_Event, OpenDialog::OnSubFileBrowse)

    /* Events generated by the disc panel */
    EVT_RADIOBOX(DiscType_Event, OpenDialog::OnDiscTypeChange)
#ifdef HAVE_LIBCDIO
    EVT_CHECKBOX(DiscProbe_Event, OpenDialog::OnDiscProbe)
#endif
    EVT_TEXT(DiscDevice_Event, OpenDialog::OnDiscDeviceChange)
    EVT_TEXT(DiscDevice_Event, OpenDialog::OnDiscPanelChange)
    EVT_TEXT(DiscTitle_Event, OpenDialog::OnDiscPanelChange)
    EVT_SPINCTRL(DiscTitle_Event, OpenDialog::OnDiscPanelChangeSpin)
    EVT_TEXT(DiscChapter_Event, OpenDialog::OnDiscPanelChange)
    EVT_SPINCTRL(DiscChapter_Event, OpenDialog::OnDiscPanelChangeSpin)
    EVT_TEXT(DiscSub_Event, OpenDialog::OnDiscPanelChange)
    EVT_TEXT(DiscAudio_Event, OpenDialog::OnDiscPanelChange)
    EVT_SPINCTRL(DiscSub_Event, OpenDialog::OnDiscPanelChangeSpin)

    /* Events generated by the net panel */
    EVT_RADIOBUTTON(NetRadio1_Event, OpenDialog::OnNetTypeChange)
    EVT_RADIOBUTTON(NetRadio2_Event, OpenDialog::OnNetTypeChange)
    EVT_RADIOBUTTON(NetRadio3_Event, OpenDialog::OnNetTypeChange)
    EVT_RADIOBUTTON(NetRadio4_Event, OpenDialog::OnNetTypeChange)
    EVT_TEXT(NetPort1_Event, OpenDialog::OnNetPanelChange)
    EVT_SPINCTRL(NetPort1_Event, OpenDialog::OnNetPanelChangeSpin)
    EVT_TEXT(NetPort2_Event, OpenDialog::OnNetPanelChange)
    EVT_SPINCTRL(NetPort2_Event, OpenDialog::OnNetPanelChangeSpin)
    EVT_TEXT(NetPort3_Event, OpenDialog::OnNetPanelChange)
    EVT_SPINCTRL(NetPort3_Event, OpenDialog::OnNetPanelChangeSpin)
    EVT_TEXT(NetAddr2_Event, OpenDialog::OnNetPanelChange)
    EVT_TEXT(NetAddr3_Event, OpenDialog::OnNetPanelChange)
    EVT_TEXT(NetAddr4_Event, OpenDialog::OnNetPanelChange)
    EVT_CHECKBOX(NetForceIPv6_Event, OpenDialog::OnNetPanelChange)
    EVT_CHECKBOX(NetTimeshift_Event, OpenDialog::OnNetPanelChange)

    /* Events generated by the subtitle file buttons */
    EVT_CHECKBOX(SubsFileEnable_Event, OpenDialog::OnSubsFileEnable)
    EVT_BUTTON(SubsFileSettings_Event, OpenDialog::OnSubsFileSettings)

    /* Events generated by the stream output buttons */
    EVT_CHECKBOX(SoutEnable_Event, OpenDialog::OnSoutEnable)
    EVT_BUTTON(SoutSettings_Event, OpenDialog::OnSoutSettings)

    /* Events generated by the caching button */
    EVT_CHECKBOX(CachingEnable_Event, OpenDialog::OnCachingEnable)
    EVT_TEXT(CachingChange_Event, OpenDialog::OnCachingChange)
    EVT_SPINCTRL(CachingChange_Event, OpenDialog::OnCachingChangeSpin)

    /* Hide the window when the user closes the window */
    EVT_CLOSE(OpenDialog::OnClose)

END_EVENT_TABLE()


/*****************************************************************************
 * AutoBuiltPanel.
 *****************************************************************************/
WX_DEFINE_ARRAY(ConfigControl *, ArrayOfConfigControls);

class AutoBuiltPanel : public wxPanel
{
public:

    AutoBuiltPanel() { }
    AutoBuiltPanel( wxWindow *, OpenDialog *, intf_thread_t *,
                    const module_t * );

    virtual ~AutoBuiltPanel() {}

    void UpdateAdvancedMRL();

    wxString name;
    ArrayOfConfigControls config_array;
    ArrayOfConfigControls advanced_config_array;
    wxComboBox *p_advanced_mrl_combo;

private:
    intf_thread_t *p_intf;
    OpenDialog *p_open_dialog;

    void OnAdvanced( wxCommandEvent& event );
    wxDialog *p_advanced_dialog;

    DECLARE_EVENT_TABLE();
};

BEGIN_EVENT_TABLE(AutoBuiltPanel, wxPanel)
    EVT_BUTTON(AdvancedOptions_Event, AutoBuiltPanel::OnAdvanced)
END_EVENT_TABLE()

static void AutoBuildCallback( void *p_data )
{
    ((OpenDialog *)p_data)->UpdateMRL();
}

static void AutoBuildAdvancedCallback( void *p_data )
{
    ((AutoBuiltPanel *)p_data)->UpdateAdvancedMRL();
}

AutoBuiltPanel::AutoBuiltPanel( wxWindow *parent, OpenDialog *dialog,
                                intf_thread_t *_p_intf,
                                const module_t *p_module )
  : wxPanel( parent, -1, wxDefaultPosition, wxDefaultSize ),
    name( wxU(module_GetObjName(p_module)) ),
    p_advanced_mrl_combo( NULL ),
    p_intf( _p_intf ), p_open_dialog( dialog ), p_advanced_dialog( NULL )
{
    wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
    bool b_advanced = false;
    unsigned int i_confsize; 
    module_config_t *p_config;
    int n;

    p_config = module_GetConfig (p_module, &i_confsize);
    for( n = 0; n < i_confsize; n++ )
    {
        module_config_t *p_item = p_config + n;
        if( !(p_item->i_type & CONFIG_HINT) && p_item->b_advanced )
            b_advanced = true;

        if( p_item->i_type & CONFIG_HINT || p_item->b_advanced )
            continue;

        ConfigControl *control =
            CreateConfigControl( VLC_OBJECT(p_intf), p_item, this );

        config_array.Add( control );

        /* Don't add items that were not recognized */
        if( control == NULL ) continue;

        control->SetUpdateCallback( AutoBuildCallback, (void *)dialog );

        sizer->Add( control, 0, wxEXPAND | wxALL, 2 );
    }

    if( b_advanced )
    {
        wxButton *button =
            new wxButton( this, AdvancedOptions_Event,
                          wxU(_("Advanced options...")) );
        sizer->Add( button, 0, wxALL, 5 );

        /* Build the advanced dialog */
        p_advanced_dialog =
            new wxDialog( this, -1, ((wxString)wxU(_("Advanced options"))) +
                          wxT(" (") + wxU( module_GetLongName(p_module) ) +
                          wxT(")"), wxDefaultPosition, wxDefaultSize,
                          wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER );

        wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );

        /* Create MRL combobox */
        wxBoxSizer *mrl_sizer_sizer = new wxBoxSizer( wxHORIZONTAL );
        wxStaticBox *mrl_box =
            new wxStaticBox( p_advanced_dialog, -1,
                             wxU(_("Advanced options")) );
        wxStaticBoxSizer *mrl_sizer =
            new wxStaticBoxSizer( mrl_box, wxHORIZONTAL );
        wxStaticText *mrl_label =
            new wxStaticText( p_advanced_dialog, -1, wxU(_("Options:")) );
        p_advanced_mrl_combo =
            new wxComboBox( p_advanced_dialog, MRL_Event, wxT(""),
                            wxDefaultPosition, wxDefaultSize );
        mrl_sizer->Add( mrl_label, 0, wxALL | wxALIGN_CENTER, 5 );
        mrl_sizer->Add( p_advanced_mrl_combo, 1, wxALL | wxALIGN_CENTER, 5 );
        mrl_sizer_sizer->Add( mrl_sizer, 1, wxEXPAND | wxALL, 5 );
        sizer->Add( mrl_sizer_sizer, 0, wxEXPAND | wxALL, 2 );

        /* Add advanced options to panel */
        for( n = 0; n < i_confsize; n++ )
        {
            module_config_t *p_item = p_config + n;
            if( p_item->i_type & CONFIG_HINT || !p_item->b_advanced )
                continue;

            ConfigControl *control =
                CreateConfigControl( VLC_OBJECT(p_intf), p_item,
                                     p_advanced_dialog );

            advanced_config_array.Add( control );

            /* Don't add items that were not recognized */
            if( control == NULL ) continue;

            control->SetUpdateCallback( AutoBuildAdvancedCallback,
                                        (void *)this );

            sizer->Add( control, 0, wxEXPAND | wxALL, 2 );
        }

        /* Separation */
        wxPanel *dummy_panel = new wxPanel( p_advanced_dialog, -1 );
        sizer->Add( dummy_panel, 1 );
        wxStaticLine *static_line =
            new wxStaticLine( p_advanced_dialog, wxID_OK );
        sizer->Add( static_line, 0, wxEXPAND | wxALL, 5 );

        /* Create buttons */
        wxButton *ok_button =
            new wxButton( p_advanced_dialog, wxID_OK, wxU(_("&OK")) );
        ok_button->SetDefault();
        wxButton *cancel_button =
            new wxButton( p_advanced_dialog, wxID_CANCEL, wxU(_("&Cancel")) );
        wxStdDialogButtonSizer *button_sizer = new wxStdDialogButtonSizer;
        button_sizer->AddButton( ok_button );
        button_sizer->AddButton( cancel_button );
        button_sizer->Realize();
        sizer->Add( button_sizer, 0, wxEXPAND|wxALL, 5 );

        sizer->SetMinSize( 400, -1 );
        p_advanced_dialog->SetSizerAndFit( sizer );
    }

    module_PutConfig (p_config);

    this->SetSizerAndFit( sizer );
}

void AutoBuiltPanel::OnAdvanced( wxCommandEvent& event )
{
    if( p_advanced_dialog->ShowModal() == wxID_OK )
    {
        UpdateAdvancedMRL();
        p_open_dialog->UpdateMRL();
    }
}

void AutoBuiltPanel::UpdateAdvancedMRL()
{
    wxString mrltemp;

    for( int i = 0; i < (int)advanced_config_array.GetCount(); i++ )
    {
        ConfigControl *control = advanced_config_array.Item(i);

        mrltemp += (i ? wxT(" :") : wxT(":"));

        if( control->GetType() == CONFIG_ITEM_BOOL &&
            !control->GetIntValue() ) mrltemp += wxT("no-");

        mrltemp += control->GetName();

        switch( control->GetType() )
        {
        case CONFIG_ITEM_STRING:
        case CONFIG_ITEM_FILE:
        case CONFIG_ITEM_DIRECTORY:
        case CONFIG_ITEM_MODULE:
            mrltemp += wxT("=\"") + control->GetPszValue() + wxT("\"");
            break;
        case CONFIG_ITEM_INTEGER:
            mrltemp +=
                wxString::Format( wxT("=%i"), control->GetIntValue() );
            break;
        case CONFIG_ITEM_FLOAT:
            mrltemp +=
                wxString::Format(wxT("=%f"), control->GetFloatValue());
            break;
        }
    }

    p_advanced_mrl_combo->SetValue( mrltemp );
}

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
OpenDialog::OpenDialog( intf_thread_t *_p_intf, wxWindow *_p_parent,
                        int i_access_method, int i_arg ):
      wxDialog( _p_parent, -1, wxU(_("Open...")), wxDefaultPosition,
             wxDefaultSize, wxDEFAULT_FRAME_STYLE )
{
    OpenDialog( _p_intf, _p_parent, i_access_method, i_arg, OPEN_NORMAL );
}

OpenDialog::OpenDialog( intf_thread_t *_p_intf, wxWindow *_p_parent,
                        int i_access_method, int i_arg, int _i_method ):
      wxDialog( _p_parent, -1, wxU(_("Open...")), wxDefaultPosition,
             wxDefaultSize, wxDEFAULT_FRAME_STYLE )
{
    /* Initializations */
    i_method = _i_method;
    p_intf = _p_intf;
    p_parent = _p_parent;
    SetIcon( *p_intf->p_sys->p_icon );
    file_dialog = NULL;
    i_disc_type_selection = 0;
    i_disc_title = 0;
    i_open_arg = i_arg;

    sout_dialog = NULL;
    subsfile_dialog = NULL;
    b_disc_device_changed = false;

    /* Create a panel to put everything in */
    wxPanel *panel = new wxPanel( this, -1 );
    panel->SetAutoLayout( TRUE );


    /* Advanced options */
    wxStaticBox *adv_box = new wxStaticBox( panel, -1,
                               wxU(_("Advanced options")) );
    wxStaticBoxSizer *adv_sizer = new wxStaticBoxSizer( adv_box,
                                                        wxVERTICAL );


    wxFlexGridSizer *common_opt_sizer = new wxFlexGridSizer( 5, 1, 20 );
    if( i_method == OPEN_NORMAL )
    {
        /* Create Stream Output checkox */
        sout_checkbox = new wxCheckBox( panel, SoutEnable_Event,
                                         wxU(_("Stream/Save")) );
        sout_checkbox->SetToolTip( wxU(_("Use VLC as a stream server")) );
        common_opt_sizer->Add( sout_checkbox, 0,
                               wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );

        sout_button = new wxButton( panel, SoutSettings_Event,
                                    wxU(_("Settings...")) );
        sout_button->Disable();

        char *psz_sout = config_GetPsz( p_intf, "sout" );
        if( psz_sout && *psz_sout )
        {
            sout_checkbox->SetValue(TRUE);
            sout_button->Enable();
            subsfile_mrl.Add( wxString(wxT("sout=")) + wxL2U(psz_sout) );
        }
        free( psz_sout );

        common_opt_sizer->Add( sout_button, 1, wxALIGN_LEFT |
                               wxALIGN_CENTER_VERTICAL );

        common_opt_sizer->Add( new wxPanel( this, -1 ), 1,
                               wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );
    }

    /* Create caching options */
    caching_checkbox = new wxCheckBox( panel, CachingEnable_Event,
                                       wxU(_("Caching")) );
    caching_checkbox->SetToolTip( wxU(_("Change the default caching value "
                                        "(in milliseconds)")) );
    common_opt_sizer->Add( caching_checkbox, 0,
                           wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );
    caching_value = new wxSpinCtrl( panel, CachingChange_Event );
    caching_value->SetRange( 0, 1000000 );
    caching_value->Disable();
    common_opt_sizer->Add( caching_value, 0,
                           wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );


    wxBoxSizer *mrl_sizer = new wxBoxSizer( wxHORIZONTAL );
    wxStaticText *mrl_label = new wxStaticText( panel, -1,
                                                wxU(_("Customize:")) );
    mrl_combo = new wxComboBox( panel, MRL_Event, wxT(""),
                                wxDefaultPosition, wxDefaultSize );
    //Point(20,25), wxSize(120, -1) );
    mrl_combo->SetToolTip( wxU(_("You can use this field directly by typing "
        "the full MRL you want to open.\n""Alternatively, the field will be "
        "filled automatically when you use the controls above.")) );

    mrl_sizer->Add( mrl_label, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5 );
    mrl_sizer->Add( mrl_combo, 1, wxALL | wxEXPAND | wxALIGN_CENTER_VERTICAL,
                    5 );

    adv_sizer->Add( common_opt_sizer, 0, wxTOP | wxLEFT|wxRIGHT | wxEXPAND, 5 );
    adv_sizer->Add( mrl_sizer, 0, wxBOTTOM  | wxLEFT|wxRIGHT  | wxEXPAND, 5 );

    /* Separation */
    wxStaticLine *static_line = new wxStaticLine( panel, wxID_OK );

    /* Create the buttons */
    wxButton *ok_button = new wxButton( panel, wxID_OK, wxU(_("&OK")) );
    ok_button->SetDefault();
    wxButton *cancel_button = new wxButton( panel, wxID_CANCEL,
                                            wxU(_("&Cancel")) );

    /* Create notebook */
    notebook = new wxNotebook( panel, Notebook_Event );

#if (!wxCHECK_VERSION(2,5,2))
    wxNotebookSizer *notebook_sizer = new wxNotebookSizer( notebook );
#endif

    notebook->AddPage( FilePanel( notebook ), wxU(_("File")),
                       i_access_method == FILE_ACCESS );
    notebook->AddPage( DiscPanel( notebook ), wxU(_("Disc")),
                       i_access_method == DISC_ACCESS );
    notebook->AddPage( NetPanel( notebook ), wxU(_("Network")),
                       i_access_method == NET_ACCESS );

    module_t *p_module = module_Find( VLC_OBJECT(p_intf), "v4l" );
    if( p_module )
    {
        AutoBuiltPanel *autopanel =
            new AutoBuiltPanel( notebook, this, p_intf, p_module );
        input_tab_array.Add( autopanel );
        notebook->AddPage( autopanel, wxU( module_GetName(p_module, 0) ),
                           i_access_method == CAPTURE_ACCESS );
        module_Put( p_module );
    }

    p_module = module_Find( VLC_OBJECT(p_intf), "pvr" );
    if( p_module )
    {
        AutoBuiltPanel *autopanel =
            new AutoBuiltPanel( notebook, this, p_intf, p_module );
        input_tab_array.Add( autopanel );
        notebook->AddPage( autopanel, wxU( module_GetName(p_module, 0) ),
                           i_access_method == CAPTURE_ACCESS );
        module_Put( p_module );
    }

    p_module = module_Find( VLC_OBJECT(p_intf), "dvb" );
    if( p_module )
    {
        AutoBuiltPanel *autopanel =
            new AutoBuiltPanel( notebook, this, p_intf, p_module );
        input_tab_array.Add( autopanel );
        notebook->AddPage( autopanel, wxU( module_GetName(p_module, 0) ),
                           i_access_method == CAPTURE_ACCESS );
        module_Put( p_module );
    }

    p_module = module_Find( VLC_OBJECT(p_intf), "dshow" );
    if( p_module )
    {
        AutoBuiltPanel *autopanel =
            new AutoBuiltPanel( notebook, this, p_intf, p_module );
        input_tab_array.Add( autopanel );
        notebook->AddPage( autopanel, wxU( module_GetName(p_module, 0) ),
                           i_access_method == CAPTURE_ACCESS );
        module_Put( p_module );
    }

    /* Update Disc panel */
    wxCommandEvent dummy_event;
    OnDiscTypeChange( dummy_event );

    /* Update Net panel */
    dummy_event.SetId( NetRadio1_Event );
    OnNetTypeChange( dummy_event );

    /* Update MRL */
    wxNotebookEvent event( wxEVT_NULL, 0, i_access_method );
    OnPageChange( event );

    /* Place everything in sizers */
    wxStdDialogButtonSizer *button_sizer = new wxStdDialogButtonSizer;
    button_sizer->AddButton( cancel_button );
    button_sizer->AddButton( ok_button );
    button_sizer->Realize();
    wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
    wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
#if (!wxCHECK_VERSION(2,5,2))
    panel_sizer->Add( notebook_sizer, 1, wxEXPAND | wxALL, 5 );
#else
    panel_sizer->Add( notebook, 1, wxEXPAND | wxALL, 5 );
#endif
    panel_sizer->Add( adv_sizer, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( static_line, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( button_sizer, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Layout();
    panel->SetSizerAndFit( panel_sizer );
    main_sizer->Add( panel, 1, wxGROW, 0 );
    main_sizer->Layout();
    SetSizerAndFit( main_sizer );
}

OpenDialog::~OpenDialog()
{
    /* Clean up */
    delete file_dialog;
    delete sout_dialog;
    delete subsfile_dialog;
}

int OpenDialog::Show( int i_access_method, int i_arg )
{
    notebook->SetSelection( i_access_method );
    int i_ret = wxDialog::Show();
    Raise();
    SetFocus();
    i_open_arg = i_arg;
    return i_ret;
}

int OpenDialog::Show()
{
    int i_ret = wxDialog::Show();
    Raise();
    SetFocus();
    return i_ret;
}

/*****************************************************************************
 * Private methods.
 *****************************************************************************/
wxPanel *OpenDialog::FilePanel( wxWindow* parent )
{
    wxPanel *panel = new wxPanel( parent, -1, wxDefaultPosition,
                                  wxSize(200, 200) );

    wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );

    /* Create browse file line */
    wxBoxSizer *file_sizer = new wxBoxSizer( wxHORIZONTAL );

    file_sizer->Add( new wxStaticText( panel, -1, wxU(_("Open:") ) ), 0,
                     wxALL | wxALIGN_CENTER_VERTICAL, 5 );

    file_combo = new wxComboBox( panel, FileName_Event, wxT("") );
    wxButton *browse_button = new wxButton( panel, FileBrowse_Event,
                                            wxU(_("Browse...")) );
    file_sizer->Add( file_combo, 1, wxALL | wxALIGN_CENTER_VERTICAL, 5 );
    file_sizer->Add( browse_button, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5 );

    /* Create Subtitles File checkox - button  */
    wxFlexGridSizer *subtitles_sizer = new wxFlexGridSizer( 2, 1, 20 );
    subsfile_checkbox = new wxCheckBox( panel, SubsFileEnable_Event,
                                        wxU(_("Use a subtitles file")) );
    subsfile_checkbox->SetToolTip( wxU(_("Use an external subtitles file.")) );
    subtitles_sizer->Add( subsfile_checkbox, 0,  wxALIGN_CENTER_VERTICAL |
                          wxALL, 5 );
    subsfile_button = new wxButton( panel, SubsFileSettings_Event,
                                    wxU(_("Advanced Settings...")) );
    subsfile_button->Disable();
    subtitles_sizer->Add( subsfile_button, 1, wxALIGN_CENTER_VERTICAL |
                           wxALL, 5 );

    /* Create subtitles file line */
    wxBoxSizer *subfile_sizer = new wxBoxSizer( wxHORIZONTAL );

    char *psz_subsfile = config_GetPsz( p_intf, "sub-file" );
    if( psz_subsfile && *psz_subsfile )
    {
        subsfile_checkbox->SetValue(TRUE);
        subsfile_button->Enable();
        subsfile_mrl.Add( wxString(wxT("sub-file=")) + wxL2U(psz_subsfile) );
    }
    if( !psz_subsfile )  psz_subsfile = strdup("");
    subfile_sizer->Add( new wxStaticText( panel, -1, wxU(_("File:") ) ),
                        0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
    subfile_combo = new wxComboBox( panel, SubFileName_Event,
                                    wxL2U( psz_subsfile ) );
    free( psz_subsfile );
    subbrowse_button = new wxButton( panel, SubFileBrowse_Event,
                                     wxU(_("Browse...")) );
    subfile_sizer->Add( subfile_combo, 1, wxALL | wxALIGN_CENTER_VERTICAL, 5 );
    subfile_sizer->Add( subbrowse_button, 0, wxALL |wxALIGN_CENTER_VERTICAL, 5 );

    subfile_combo->Disable();
    subbrowse_button->Disable();

    psz_subsfile = config_GetPsz( p_intf, "sub-file" );
    if( psz_subsfile && *psz_subsfile )
    {
        subfile_combo->Enable();
        subbrowse_button->Enable();
    }

    sizer->Add( file_sizer, 0, wxEXPAND | wxALL, 5 );
    sizer->Add( subtitles_sizer, 0, wxLEFT | wxRIGHT | wxTOP, 5 );
    sizer->Add( subfile_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT| wxBOTTOM, 5 );
    panel->SetSizerAndFit( sizer );
    return panel;
}

wxPanel *OpenDialog::DiscPanel( wxWindow* parent )
{
    wxPanel *panel = new wxPanel( parent, -1, wxDefaultPosition,
                                  wxSize(200, 200) );

    wxBoxSizer *sizer_row = new wxBoxSizer( wxVERTICAL );
    wxFlexGridSizer *sizer = new wxFlexGridSizer( 2, 3, 20 );

    static const wxString disc_type_array[] =
    {
        wxU(_("DVD (menus)")),
        wxU(_("DVD")),
        wxU(_("VCD")),
        wxU(_("Audio CD")),
    };

    disc_type = new wxRadioBox( panel, DiscType_Event, wxU(_("Disc type")),
                                wxDefaultPosition, wxDefaultSize,
                                WXSIZEOF(disc_type_array), disc_type_array,
                                WXSIZEOF(disc_type_array), wxRA_SPECIFY_COLS );

#ifdef HAVE_LIBCDIO
    disc_probe = new wxCheckBox( panel, DiscProbe_Event,
                                 wxU(_("Probe Disc(s)")) );
    disc_probe->SetToolTip( wxU(_("Probe for a DVD, VCD or audio CD. "
"First try the Device name entered for the selected Disc type "
"(DVD, DVD Menu, VCD, audio CD). If that doesn't find media, try any device "
"for the Disc type.  If that doesn't work, then try looking for CD-ROMs or "
"DVD drives. The Disc type, Device name, and some parameter ranges are set "
"based on media we find.")) );
#endif

    sizer_row->Add( disc_type, i_disc_type_selection, wxEXPAND | wxALL, 5 );
#ifdef HAVE_LIBCDIO
    sizer_row->Add( disc_probe, 0, wxEXPAND | wxALL );
#endif

    wxStaticText *label = new wxStaticText( panel, -1, wxU(_("Device name")) );
    disc_device = new wxTextCtrl( panel, DiscDevice_Event, wxT(""),
                                  wxDefaultPosition, wxDefaultSize,
                                  wxTE_PROCESS_ENTER);

#ifdef WIN32
    char psz_default_device[3] = {0};
    char *psz_forced;

    /* find the drive_name for the first cdrom drive,
     * which is probably "D:" and put the drive_name into
     * psz_default_device... */
    for( char drive_letter = 'A'; drive_letter <= 'Z'; ++drive_letter )
    {
        char drive_name[3] = {drive_letter, ':', 0};
        UINT type = GetDriveTypeA( drive_name );
        if( type == DRIVE_CDROM )
        {
            psz_default_device[0] = drive_letter;
            psz_default_device[1] = ':';
            break;
        }
    }

    psz_forced = config_GetPsz( p_intf, "dvd" );

    if( strlen(psz_default_device) > 0 &&
        ( !psz_forced || psz_forced && !*psz_forced ) )
    {
        if(disc_device)
            disc_device->SetValue( wxL2U(psz_default_device) );
    }
#endif

    sizer->Add( label, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    sizer->Add( disc_device, 1, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );

    disc_title_label = new wxStaticText( panel, -1, wxU(_("Title")) );
    disc_title = new wxSpinCtrl( panel, DiscTitle_Event );
    sizer->Add( disc_title_label, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    sizer->Add( disc_title, 1, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );

    disc_chapter_label = new wxStaticText( panel, -1, wxU(_("Chapter")) );
    disc_chapter = new wxSpinCtrl( panel, DiscChapter_Event );
    sizer->Add( disc_chapter_label, 0,
                wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    sizer->Add( disc_chapter, 1, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );

    disc_sub_label = new wxStaticText( panel, -1, wxU(_("Subtitles track")) );
    disc_sub = new wxSpinCtrl( panel, DiscSub_Event );
    sizer->Add( disc_sub_label, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    sizer->Add( disc_sub, 1, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    disc_sub->SetRange( -1, 255 );
    i_disc_sub = config_GetInt( p_intf, "sub-track" );
    disc_sub->SetValue( i_disc_sub );

    disc_audio_label = new wxStaticText( panel, -1, wxU(_("Audio track")) );
    disc_audio = new wxSpinCtrl( panel, DiscAudio_Event );
    sizer->Add( disc_audio_label, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    sizer->Add( disc_audio, 1, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    disc_audio->SetRange( -1, 255 );
    i_disc_audio = config_GetInt( p_intf, "audio-track" );
    disc_audio->SetValue( i_disc_audio );

    sizer_row->Add( sizer, 0, wxEXPAND | wxALL, 5 );

    panel->SetSizerAndFit( sizer_row );
    return panel;
}

wxPanel *OpenDialog::NetPanel( wxWindow* parent )
{
    int i;
    wxPanel *panel = new wxPanel( parent, -1, wxDefaultPosition,
                                  wxSize(200, 200) );

    wxBoxSizer *sizer_row = new wxBoxSizer( wxVERTICAL );
    wxFlexGridSizer *sizer = new wxFlexGridSizer( 2, 4, 20 );

    static const wxString net_type_array[] =
    {
        wxU(_("UDP/RTP")),
        wxU(_("UDP/RTP Multicast")),
        wxU(_("HTTP/HTTPS/FTP/MMS")),
        wxU(_("RTSP"))
    };

    for( i=0; i<4; i++ )
    {
        net_radios[i] = new wxRadioButton( panel, NetRadio1_Event + i,
                                           net_type_array[i],
                                           wxDefaultPosition, wxDefaultSize,
                                           wxRB_SINGLE );

        net_subpanels[i] = new wxPanel( panel, -1,
                                        wxDefaultPosition, wxDefaultSize );
    }

    /* Timeshift */
    net_timeshift  = new wxCheckBox( panel, NetTimeshift_Event,
                                     wxU(_("Allow timeshifting")) );

    /* UDP/RTP row */
    wxFlexGridSizer *subpanel_sizer;
    wxStaticText *label;
    i_net_ports[0] = config_GetInt( p_intf, "server-port" );
    subpanel_sizer = new wxFlexGridSizer( 3, 1, 20 );
    label = new wxStaticText( net_subpanels[0], -1, wxU(_("Port")) );
    net_ports[0] = new wxSpinCtrl( net_subpanels[0], NetPort1_Event,
                                   wxString::Format(wxT("%d"), i_net_ports[0]),
                                   wxDefaultPosition, wxDefaultSize,
                                   wxSP_ARROW_KEYS,
                                   0, 65535, i_net_ports[0] );

    subpanel_sizer->Add( label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );
    subpanel_sizer->Add( net_ports[0], 1,
                         wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    net_ipv6 = new wxCheckBox( net_subpanels[0], NetForceIPv6_Event,
                               wxU(_("Force IPv6")));
    subpanel_sizer->Add( net_ipv6, 0,
                         wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );
    net_subpanels[0]->SetSizerAndFit( subpanel_sizer );
    net_radios[0]->SetValue( TRUE );

    /* UDP/RTP Multicast row */
    subpanel_sizer = new wxFlexGridSizer( 4, 1, 20 );
    label = new wxStaticText( net_subpanels[1], -1, wxU(_("Address")) );
    net_addrs[1] = new wxTextCtrl( net_subpanels[1], NetAddr2_Event, wxT(""),
                                   wxDefaultPosition, wxDefaultSize,
                                   wxTE_PROCESS_ENTER);
    subpanel_sizer->Add( label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );
    subpanel_sizer->Add( net_addrs[1], 1,
                         wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );

    label = new wxStaticText( net_subpanels[1], -1, wxU(_("Port")) );
    i_net_ports[1] = i_net_ports[0];
    net_ports[1] = new wxSpinCtrl( net_subpanels[1], NetPort2_Event,
                                   wxString::Format(wxT("%d"), i_net_ports[1]),
                                   wxDefaultPosition, wxDefaultSize,
                                   wxSP_ARROW_KEYS,
                                   0, 65535, i_net_ports[1] );

    subpanel_sizer->Add( label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );
    subpanel_sizer->Add( net_ports[1], 1,
                         wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
    net_subpanels[1]->SetSizerAndFit( subpanel_sizer );

    /* HTTP and RTSP rows */
    for( i=2; i<4; i++ )
    {
        subpanel_sizer = new wxFlexGridSizer( 2, 1, 20 );
        label = new wxStaticText( net_subpanels[i], -1, wxU(_("URL")) );
        net_addrs[i] = new wxTextCtrl( net_subpanels[i], NetAddr1_Event + i,
                                       (i == 2) ? wxT("") : wxT("rtsp://"),
                                       wxDefaultPosition, wxSize( 200, -1 ),
                                       wxTE_PROCESS_ENTER);
        subpanel_sizer->Add( label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL );
        subpanel_sizer->Add( net_addrs[i], 1,
                             wxEXPAND | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL );
        net_subpanels[i]->SetSizerAndFit( subpanel_sizer );
    }

    /* Stuff everything into the main panel */
    for( i=0; i<4; i++ )
    {
        sizer->Add( net_radios[i], 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL |
                    wxALL, 5 );
        sizer->Add( net_subpanels[i], 1, wxEXPAND | wxALIGN_LEFT |
                    wxALIGN_CENTER_VERTICAL | wxALL, 5  );
    }
    sizer->Add( net_timeshift, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL |
                wxALL, 5 );

    sizer_row->Add( sizer, 0, wxEXPAND | wxALL, 5 );

    panel->SetSizerAndFit( sizer_row );
    return panel;
}

void OpenDialog::UpdateMRL()
{
    UpdateMRL( i_current_access_method );
}

void OpenDialog::UpdateMRL( int i_access_method )
{
    wxString mrltemp, caching_name;

    i_current_access_method = i_access_method;

    switch( i_access_method )
    {
    case FILE_ACCESS:
        mrltemp = file_combo->GetValue();
        caching_name = wxT("file-caching");
        break;

    case DISC_ACCESS:
        i_disc_type_selection = disc_type->GetSelection();

        switch ( i_disc_type_selection )
        {
        case 0: /* DVD with menus */
        case 1: /* DVD without menus */
            disc_device->SetToolTip( wxU(_("DVD device to use" ) )  );
            if( i_disc_type_selection == 0 )
            {
                mrltemp = wxT("dvd://") + disc_device->GetValue();
                caching_name = wxT("dvdnav-caching");
            }
            else
            {
                mrltemp = wxT("dvdsimple://") + disc_device->GetValue();
                caching_name = wxT("dvdread-caching");
            }

            if( i_disc_title > 0 )
            {
                mrltemp += wxString::Format( wxT("@%d"), i_disc_title );

                if( i_disc_chapter > 0 )
                    mrltemp += wxString::Format( wxT(":%d"), i_disc_chapter );
            }

            if( i_disc_sub >= 0 )
                mrltemp += wxString::Format( wxT("  :sub-track=%d"),
                                             i_disc_sub );
            if( i_disc_audio >= 0 )
                mrltemp += wxString::Format( wxT("  :audio-track=%d"),
                                             i_disc_audio );
            break;

        case 2:  /* VCD of some sort */
#ifdef HAVE_VCDX
            disc_device->SetToolTip( wxU(_("Name of CD-ROM device "
            "to read Video CD from. If this field is left empty, we will scan "
            "for a CD-ROM with a VCD in it.")) );
            mrltemp = wxT("vcdx://") + disc_device->GetValue();
            if( i_disc_title > 0 )
                mrltemp += wxString::Format( wxT("@%c%d"),
                                  config_GetInt( p_intf, "vcdx-PBC"  )
                                  ? 'P' : 'E', i_disc_title );
#else
            disc_device->SetToolTip( wxU(_("CD-ROM device to use" ) ) );
            mrltemp = wxT("vcd://") + disc_device->GetValue();
            if( i_disc_title > 0 )
                mrltemp += wxString::Format( wxT("@%d"), i_disc_title );
#endif

            if( i_disc_sub >= 0 )
                mrltemp += wxString::Format( wxT("  :sub-track=%d"),
                                             i_disc_sub );

            if( i_disc_audio >= 0 )
                mrltemp += wxString::Format( wxT("  :audio-track=%d"),
                                             i_disc_audio );
            caching_name = wxT("vcd-caching");
            break;

        case 3: /* CD-DA */
#ifdef HAVE_CDDAX
            disc_device->SetToolTip( wxU(_("Name of CD-ROM device "
            "to read audio CD from. If this field is left empty, we will scan "
            "for a CD-ROM with an audio CD in it." )) );
            mrltemp = wxT("cddax://")
#else
            disc_device->SetToolTip( wxU(_("CD-ROM device to use" ) ) ) ;
            mrltemp = wxT("cdda://")
#endif
              + disc_device->GetValue();
            if( i_disc_title > 0 )
                mrltemp += wxString::Format( wxT("@%d"), i_disc_title );

            caching_name = wxT("cdda-caching");
            break;

        default:
            msg_Err( p_intf, "invalid selection (%d)",
                     disc_type->GetSelection() );
        }

        break;

    case NET_ACCESS:
        switch( i_net_type )
        {
        case 0:
            mrltemp = wxT("udp://@");
            if ( net_ipv6->GetValue() )
            {
                mrltemp += wxT("[::]");
            }
            if( i_net_ports[0] !=
                config_GetInt( p_intf, "server-port" ) )
            {
                mrltemp += wxString::Format( wxT(":%d"), i_net_ports[0] );
            }

            caching_name = wxT("udp-caching");
            break;

        case 1:
            mrltemp = wxT("udp://@");
            if ((net_addrs[1]->GetLineText(0).Find (':') != -1)
                && (net_addrs[1]->GetLineText(0)[0u] != '['))
            {
                /* automatically adds '[' and ']' to IPv6 addresses */
                mrltemp += wxT("[") + net_addrs[1]->GetLineText(0)
                         + wxT("]");
            }
            else
            {
                mrltemp += net_addrs[1]->GetLineText(0);
            }
            if( i_net_ports[1] != config_GetInt( p_intf, "server-port" ) )
            {
                mrltemp += wxString::Format( wxT(":%d"), i_net_ports[1] );
            }

            caching_name = wxT("udp-caching");
            break;

        case 2:
            /* http access */
            if( net_addrs[2]->GetLineText(0).Find(wxT("://")) == -1 )
                mrltemp = wxT("http://");

            mrltemp += net_addrs[2]->GetLineText(0);
            if( ! mrltemp.Left(4).CmpNoCase(wxT("http")) )
                caching_name = wxT("http-caching");
            else if( ! mrltemp.Left(3).CmpNoCase(wxT("mms")) )
                caching_name = wxT("mms-caching");
            else
                caching_name= wxT("ftp-caching");
            break;

        case 3:
            /* RTSP access */
            if( net_addrs[3]->GetLineText(0).Find(wxT("rtsp://")) != 0 )
            {
                mrltemp = wxT("rtsp://");
            }
            mrltemp += net_addrs[3]->GetLineText(0);

            caching_name = wxT("rtsp-caching");
            break;
        }
        if( net_timeshift->IsEnabled() && net_timeshift->IsChecked() )
            mrltemp += wxT(" :access-filter=timeshift");
        break;

    default:
        {
            int i_item = i_access_method - MAX_ACCESS;

            if( i_item < 0 || i_item >= (int)input_tab_array.GetCount() )
                break;

            AutoBuiltPanel *input_panel = input_tab_array.Item( i_item );

            mrltemp = input_panel->name + wxT("://");

            for( int i=0; i < (int)input_panel->config_array.GetCount(); i++ )
            {
                ConfigControl *control = input_panel->config_array.Item(i);

                mrltemp += wxT(" :");

                if( control->GetType() == CONFIG_ITEM_BOOL &&
                    !control->GetIntValue() ) mrltemp += wxT("no-");

                mrltemp += control->GetName();

                switch( control->GetType() )
                {
                case CONFIG_ITEM_STRING:
                case CONFIG_ITEM_FILE:
                case CONFIG_ITEM_DIRECTORY:
                case CONFIG_ITEM_MODULE:
                    mrltemp += wxT("=\"") + control->GetPszValue() + wxT("\"");
                    break;
                case CONFIG_ITEM_INTEGER:
                    mrltemp +=
                        wxString::Format( wxT("=%i"), control->GetIntValue() );
                    break;
                case CONFIG_ITEM_FLOAT:
                    mrltemp +=
                        wxString::Format(wxT("=%f"), control->GetFloatValue());
                    break;
                }
            }

            if( input_panel->p_advanced_mrl_combo &&
                input_panel->p_advanced_mrl_combo->GetValue() )
            {
                mrltemp += wxT(" ") +
                    input_panel->p_advanced_mrl_combo->GetValue();
            }
        }
        break;
    }

    if( caching_name.size() )
    {
        if( caching_value->IsEnabled() )
        {
            mrltemp += wxT("  :") + caching_name +
                wxString::Format( wxT("=%d"), i_caching );
        }
        else
        {
            int i_value = config_GetInt( p_intf, caching_name.mb_str(wxConvUTF8) );
            caching_value->SetValue( i_value );
        }
    }

    mrl_combo->SetValue( mrltemp );
}

/*****************************************************************************
 * Events methods.
 *****************************************************************************/
void OpenDialog::OnOk( wxCommandEvent& WXUNUSED(event) )
{
    mrl = SeparateEntries( mrl_combo->GetValue() );
    mrl_combo->Append( mrl_combo->GetValue() );
    if( mrl_combo->GetCount() > 10 ) mrl_combo->Delete( 0 );
    mrl_combo->SetSelection( mrl_combo->GetCount() - 1 );

    if( i_method == OPEN_STREAM )
    {
        if( IsModal() ) EndModal( wxID_OK );
        Hide();
        return;
    }

    /* Update the playlist */
    playlist_t *p_playlist = pl_Yield( p_intf );
    if( p_playlist == NULL ) return;

    for( int i = 0; i < (int)mrl.GetCount(); i++ )
    {
        bool b_start = !i && i_open_arg;
        input_item_t *p_input;
        char *psz_utf8;

        psz_utf8 = wxFromLocale( mrl[i] );
        p_input = input_item_New( p_intf, psz_utf8, NULL );
        wxLocaleFree( psz_utf8 );

        /* Insert options */
        while( i + 1 < (int)mrl.GetCount() &&
               ((const char *)mrl[i + 1].mb_str(wxConvUTF8))[0] == ':' )
        {
            psz_utf8 = wxFromLocale( mrl[i + 1] );
            input_item_AddOption( p_input, psz_utf8 );
            wxLocaleFree( psz_utf8 );
            i++;
        }

        /* Get the options from the subtitles dialog */
        if( subsfile_checkbox->IsChecked() && subsfile_mrl.GetCount() )
        {
            for( int j = 0; j < (int)subsfile_mrl.GetCount(); j++ )
            {
                psz_utf8 = wxFromLocale( subsfile_mrl[j] );
                input_item_AddOption( p_input, psz_utf8 );
                wxLocaleFree( psz_utf8 );
            }
        }

        /* Get the options from the stream output dialog */
        if( sout_checkbox->IsChecked() && sout_mrl.GetCount() )
        {
            for( int j = 0; j < (int)sout_mrl.GetCount(); j++ )
            {
                psz_utf8 = wxFromLocale( sout_mrl[j] );
                input_item_AddOption( p_input, psz_utf8 );
                wxLocaleFree( psz_utf8 );
            }
        }

        /* FIXME: playlist_AddInput() can fail */
        playlist_AddInput( p_playlist, p_input,
               PLAYLIST_APPEND | ( b_start ? PLAYLIST_GO : PLAYLIST_PREPARSE ),
               PLAYLIST_END, true, pl_Unlocked );
        vlc_gc_decref( p_input );
    }
    pl_Release( p_intf );

    Hide();

    if( IsModal() ) EndModal( wxID_OK );
}

void OpenDialog::OnCancel( wxCommandEvent& WXUNUSED(event) )
{
    wxCloseEvent cevent;
    OnClose(cevent);
}

void OpenDialog::OnClose( wxCloseEvent& WXUNUSED(event) )
{
    Hide();

    if( IsModal() ) EndModal( wxID_CANCEL );
}

void OpenDialog::OnPageChange( wxNotebookEvent& event )
{
    UpdateMRL( event.GetSelection() );
}

void OpenDialog::OnMRLChange( wxCommandEvent& event )
{
    //mrl = SeparateEntries( event.GetString() );
}

/*****************************************************************************
 * File panel event methods.
 *****************************************************************************/
void OpenDialog::OnFilePanelChange( wxCommandEvent& WXUNUSED(event) )
{
    UpdateMRL( FILE_ACCESS );
}

void OpenDialog::OnFileBrowse( wxCommandEvent& WXUNUSED(event) )
{
    if( file_dialog == NULL )
        file_dialog = new wxFileDialog( this, wxU(_("Open File")),
            wxT(""), wxT(""), wxT("*"), wxOPEN | wxMULTIPLE );

    file_dialog->SetWildcard(wxU(_("All Files (*.*)|*"
        "|Sound Files (*.mp3, *.ogg, etc.)|" EXTENSIONS_AUDIO
        "|Video Files (*.avi, *.mpg, etc.)|" EXTENSIONS_VIDEO
        "|Playlist Files (*.m3u, *.pls, etc.)|" EXTENSIONS_PLAYLIST
        "|Subtitle Files (*.srt, *.sub, etc.)|" EXTENSIONS_SUBTITLE)));

    if( file_dialog && file_dialog->ShowModal() == wxID_OK )
    {
        wxArrayString paths;
        wxString path;

        file_dialog->GetPaths( paths );

        for( size_t i = 0; i < paths.GetCount(); i++ )
        {
            if( paths[i].Find( wxT(' ') ) >= 0 )
                path += wxT("\"") + paths[i] + wxT("\" ");
            else
                path += paths[i] + wxT(" ");
        }

        file_combo->SetValue( path );
        file_combo->Append( path );
        if( file_combo->GetCount() > 10 ) file_combo->Delete( 0 );
        UpdateMRL( FILE_ACCESS );
    }
}

void OpenDialog::OnSubFileBrowse( wxCommandEvent& WXUNUSED(event) )
{
    wxFileDialog dialog( this, wxU(_("Open subtitles file")),
                         wxT(""), wxT(""), wxT("*"), wxOPEN );

    if( dialog.ShowModal() == wxID_OK )
    {
        subfile_combo->SetValue( dialog.GetPath() );
    }
    wxCommandEvent event; OnSubFileChange( event );
}

void OpenDialog::OnSubFileChange( wxCommandEvent& WXUNUSED(event) )
{
    if( subsfile_mrl.GetCount() != 0 )
    {
        subsfile_mrl.RemoveAt( 0 );
        subsfile_mrl.Insert( wxString(wxT("sub-file=")) + subfile_combo->GetValue() , 0 );
    }
    else
    {
        subsfile_mrl.Add( wxString(wxT("sub-file=")) + subfile_combo->GetValue() );
    }
}

/*****************************************************************************
 * Disc panel event methods.
 *****************************************************************************/
void OpenDialog::OnDiscPanelChangeSpin( wxSpinEvent& event )
{
    wxCommandEvent cevent;
    cevent.SetId(event.GetId());
    cevent.SetInt(event.GetPosition());
    OnDiscPanelChange(cevent);
}

void OpenDialog::OnDiscPanelChange( wxCommandEvent& event )
{
    if( event.GetId() == DiscTitle_Event ) i_disc_title = event.GetInt();
    if( event.GetId() == DiscChapter_Event ) i_disc_chapter = event.GetInt();
    if( event.GetId() == DiscSub_Event ) i_disc_sub = event.GetInt();
    if( event.GetId() == DiscAudio_Event ) i_disc_audio = event.GetInt();

    UpdateMRL( DISC_ACCESS );
}

void OpenDialog::OnDiscDeviceChange( wxCommandEvent& event )
{
    char *psz_device;

    switch( disc_type->GetSelection() )
    {
        case 3:
            psz_device = config_GetPsz( p_intf, "cd-audio" );
            break;

        case 2:
            psz_device = config_GetPsz( p_intf, "vcd" );
            break;

        default:
            psz_device = config_GetPsz( p_intf, "dvd" );
            break;
    }

    if ( !psz_device ) psz_device = "";

    if( disc_device->GetValue().Cmp( wxL2U( psz_device ) ) )
    {
        b_disc_device_changed = true;
    }

    UpdateMRL( DISC_ACCESS );
}

#ifdef HAVE_LIBCDIO

/* Return true if *psz_drive is a drive with a DVD in it. A more complete
   check would see if the media looks like a *playable* DVD. This should
   go into libcdio.
 */
static bool IsDVD(const char *psz_drive)
{
  CdIo_t *p_cdio = cdio_open (psz_drive, DRIVER_UNKNOWN);
  if (p_cdio)
  {
      discmode_t discmode = cdio_get_discmode(p_cdio);
      cdio_destroy(p_cdio);
      return cdio_is_discmode_dvd(discmode);
  }
  return false;
}


/* Return a device that has a DVD in it. The caller needs to free
   the returned string.
*/
static char * ProbeDVD(const wxChar *device)
{
        /*
  char **ppsz_cd_drives;
  const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(device);
  char *psz_device = (char *) tmp_buf;

  if( IsDVD(psz_device) )
  {
      return strdup(psz_device);
  }

  ppsz_cd_drives = cdio_get_devices(DRIVER_DEVICE);
  if( ppsz_cd_drives )
  {
      char **c;
      for( c = ppsz_cd_drives; *c != NULL; c++ )
      {
      if( IsDVD(*c) )
      {
          char *psz_drive = strdup(*c);
          cdio_free_device_list(ppsz_cd_drives);
          return strdup(psz_drive);
          }
      }
      cdio_free_device_list(ppsz_cd_drives);
  }*/
  return NULL;
}


static char * ProbeDevice(char **ppsz_search_devices, cdio_fs_anal_t mask,
              bool b_any)
{
    char **ppsz_devices;

    if( ppsz_search_devices && !ppsz_search_devices[0] )
        ppsz_search_devices = NULL;

    /* Start out trying the device that has been entered so far. */
    ppsz_devices = cdio_get_devices_with_cap(ppsz_search_devices, mask,
                         b_any);

    if (ppsz_devices && *ppsz_devices)
    {
        char *psz_device = strdup(*ppsz_devices);
        cdio_free_device_list(ppsz_devices);
        return psz_device;
    }

    /* If there was no device specified on the first try, then give up
       now. Otherwise accept any CD-ROM in the class (e.g. VCD or DVD).
    */
    if( !ppsz_search_devices ) return NULL;

    ppsz_devices = cdio_get_devices_with_cap(NULL, mask, true);

    if (ppsz_devices && *ppsz_devices)
    {
        char *psz_device = strdup(*ppsz_devices);
        cdio_free_device_list(ppsz_devices);
        return psz_device;
    }

    return NULL;
}


/* Return a device that has a CD-DA in it. The caller needs to free
   the returned string.
*/
static char * ProbeCDDA(const wxChar *device)
{
        /*
   char *ppsz_device[2];
   const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(device);
   char *psz_device = (char *) tmp_buf;
   ppsz_device[0] = (device && *device) ? psz_device : NULL;
   ppsz_device[1] = NULL;
   return ProbeDevice(ppsz_device, CDIO_FS_AUDIO, false);
   */ return NULL;
}

/* Return a device that has a VCD in it. The caller needs to free
   the returned string.
*/
static char * ProbeVCD(const wxChar *device)
{/*
   char *ppsz_device[2];
   const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(device);
   char *psz_device = (char *) tmp_buf;
   ppsz_device[0] = (device && *device) ? psz_device : NULL;
   ppsz_device[1] = NULL;
   return ProbeDevice(ppsz_device,
                      (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD|CDIO_FS_ANAL_VIDEOCD
                       |CDIO_FS_UNKNOWN), true);
                       */ return NULL;
}


/*
   Probe (find anywhere) a CD-DA, VCD, or a DVD.
   First try the device name that may have been entered for the "disc type"
   selected. If that doesn't work we try any device for the disc type.
   If that doesn't work, try looking for CD-ROMs or DVD drives. the
   disc type, device name and paramter ranges are set to whatever we find.
*/
void OpenDialog::OnDiscProbe( wxCommandEvent& WXUNUSED(event) )
{
    wxCommandEvent dummy_event;
    char *psz_device = NULL;
    bool b_probed_DVD = false;
    bool b_probed_VCD = false;
    const int i_selection = disc_type->GetSelection();

    /* Reduce verbosity of cdio errors. */
    cdio_loglevel_default = CDIO_LOG_ERROR;

 retry:
    switch( disc_type->GetSelection() )
    {

    case SELECTION_DISC_TYPE_DVD_MENUS:
    case SELECTION_DISC_TYPE_DVD:
      /* If not a DVD then try for a VCD. If VCD fails it will
         try for a CD-DA. */
      if (!psz_device) psz_device = ProbeDVD(disc_device->GetValue());
      if (!psz_device)
      {
          b_probed_DVD = true;
          disc_type->SetSelection(SELECTION_DISC_TYPE_VCD);
          OnDiscTypeChange( dummy_event );
          goto retry;
      }
      disc_device->SetValue( wxL2U(psz_device) );
      break;

    case SELECTION_DISC_TYPE_VCD:  /* VCD probe of some sort */
      if(!psz_device) psz_device = ProbeVCD(disc_device->GetValue());
      if( psz_device  )
      {
#ifdef HAVE_VCDX
#if LIBVCD_VERSION_NUM > 23
      vcdinfo_obj_t *p_vcdinfo;

          /* Set LID or entry range accurately if possible. */
      if( vcdinfo_open(&p_vcdinfo, &psz_device, DRIVER_DEVICE,
                NULL) == VCDINFO_OPEN_VCD)
      {
          if (config_GetInt( p_intf, "vcdx-PBC"  ))
          {
          /* Set largest LID. */;
          disc_title->SetRange( 0, vcdinfo_get_num_LIDs(p_vcdinfo) );

          }
          else
          {
          /* Set largest Entry */
          disc_title->SetRange( 0,
                    vcdinfo_get_num_entries(p_vcdinfo)-1 );
          }
          vcdinfo_close(p_vcdinfo);
      }
#endif /* LIBVCD_VERSION_NUM > 23 */
          disc_device->SetValue( wxL2U(psz_device) );
#else
          CdIo_t *p_cdio = cdio_open (psz_device, DRIVER_UNKNOWN);
          disc_device->SetValue( wxL2U(psz_device) );

          /* Set track range accurately if possible. */
          if (p_cdio)
          {
              track_t i_last_track = cdio_get_last_track_num(p_cdio);
              disc_title->SetRange( 0, i_last_track-1 );
          }
          cdio_destroy(p_cdio);
#endif
          break;
      }

      b_probed_VCD = true;

      /* Not a VCD. Try for a DVD unless we've been there before. */
      if( !b_probed_DVD && (psz_device = ProbeDVD(disc_device->GetValue())) )
      {
          disc_type->SetSelection(SELECTION_DISC_TYPE_DVD_MENUS);
          OnDiscTypeChange( dummy_event );
          goto retry;
      }
      b_probed_DVD = true;

      /* Couldn't find a VCD or DVD. See if we can find a CD-DA. */
      psz_device = ProbeCDDA(disc_device->GetValue());
      if( psz_device  )
      {
          disc_type->SetSelection(SELECTION_DISC_TYPE_CDDA);
          OnDiscTypeChange( dummy_event );
          goto retry;
      }

      /* Couldn't find a VCD, DVD or CD-DA. Null out the Device name and
     set to original selection.
       */
      disc_device->SetValue( wxL2U("") );
      disc_type->SetSelection(i_selection);
      OnDiscTypeChange( dummy_event );
      break;

    case SELECTION_DISC_TYPE_CDDA:
      if(!psz_device) psz_device = ProbeCDDA(disc_device->GetValue());
      if( psz_device  )
      {
          CdIo_t *p_cdio = cdio_open (psz_device, DRIVER_UNKNOWN);
          disc_device->SetValue( wxL2U(psz_device) );
          if (p_cdio)
          {
              track_t i_last_track = cdio_get_last_track_num(p_cdio);
              disc_title->SetRange( 0, i_last_track );
#if 0
          MediaInsertCDDA( p_intf,  p_cdio, i_last_track );
#endif
          }
          cdio_destroy(p_cdio);
          break;
      }

      /* Not a CD-DA. Try for a DVD unless we've been there before. */
      if( !b_probed_DVD && (psz_device = ProbeDVD(disc_device->GetValue())) )
      {
          disc_type->SetSelection(SELECTION_DISC_TYPE_DVD_MENUS);
          OnDiscTypeChange( dummy_event );
          goto retry;
      }

      /* Couldn't find a CD-DA or DVD. See if we can find a VCD, unless
         we've tried that before. */
      if (!b_probed_VCD) psz_device = ProbeVCD(disc_device->GetValue());
      if( psz_device  )
      {
          disc_type->SetSelection(SELECTION_DISC_TYPE_VCD);
          OnDiscTypeChange( dummy_event );
          goto retry;
      }
      disc_device->SetValue( wxL2U("") );
      break;

    default:
        msg_Err( p_intf, "invalid Disc type selection (%d)",
                 disc_type->GetSelection() );
        break;
    }

    free( psz_device );
    disc_probe->SetValue(FALSE);

    UpdateMRL( DISC_ACCESS );
}
#endif /* HAVE_LIBCDIO */

void OpenDialog::OnDiscTypeChange( wxCommandEvent& WXUNUSED(event) )
{
    char *psz_device = NULL;

    switch( disc_type->GetSelection() )
    {

    case SELECTION_DISC_TYPE_DVD_MENUS:
    case SELECTION_DISC_TYPE_DVD:
        disc_sub->Enable(); disc_sub_label->Enable();
        disc_audio->Enable(); disc_audio_label->Enable();
        disc_chapter->Enable(); disc_chapter_label->Enable();
        disc_title_label->SetLabel ( wxU(_("Title")) );
        psz_device = config_GetPsz( p_intf, "dvd" );
        if( !b_disc_device_changed )
        {
            if( psz_device ) disc_device->SetValue( wxL2U(psz_device) );
            else disc_device->SetValue( wxT("") );
        }
        disc_title->SetRange( 0, 255 );
        disc_sub->SetRange( -1, 31 );  // up to 32 subtitles -1: no subtitle
        disc_audio->SetRange( 0, 7 );  // up to 8 audio channels
        disc_chapter->SetRange( 0, 255 );
        disc_title->SetToolTip( wxU(_("Title number.")) );
        disc_sub->SetToolTip( wxU(_(
          "DVDs can have up to 32 subtitles numbered 0..31. "
          "Note this is not the same thing as a subtitle name (e.g. 'en'). "
          "If a value -1 is used, no subtitle will be shown." )) );
        disc_audio->SetToolTip( wxU(_("Audio track number. "
          "DVDs can have up to 8 audio tracks numbered 0..7."
        )) );
        break;

    case SELECTION_DISC_TYPE_VCD:
        disc_sub->Enable(); disc_sub_label->Enable();
        disc_audio->Enable(); disc_audio_label->Enable();
        disc_chapter->Disable(); disc_chapter_label->Disable();
        psz_device = config_GetPsz( p_intf, "vcd" );
        if( !b_disc_device_changed )
        {
            if( psz_device ) disc_device->SetValue( wxL2U(psz_device) );
            else disc_device->SetValue( wxT("") );
        }

#ifdef HAVE_VCDX
        if (config_GetInt( p_intf, "vcdx-PBC"  ))
        {
          disc_title_label->SetLabel ( wxT("Playback LID") );
          disc_title->SetToolTip( wxU(_(
          "Playback control (PBC) usually starts with number 1." )) );
        }
        else
        {
          disc_title_label->SetLabel ( wxT("Entry") );
          disc_title->SetToolTip( wxU(_(
          "The first entry (the beginning of the first MPEG track) is 0." )) );
        }

#else
        disc_title_label->SetLabel ( wxU(_("Track")) );
        disc_title->SetToolTip( wxU(_("Track number.")) );
#endif
        disc_title->SetRange( 0, 99 );  // only 100 tracks allowed on VCDs
        disc_sub->SetRange( -1, 3 );    // up to 4 subtitles -1 = no subtitle
        disc_audio->SetRange( 0, 1 );   // up to 2 audio tracks
        disc_sub->SetToolTip( wxU(_(
          "SVCDs can have up to 4 subtitles numbered 0..3. "
          "If a value -1 is used, no subtitle will be shown." )) );
        disc_audio->SetToolTip( wxU(_("Audio track number. "
          "VCDs can have up to 2 audio tracks numbered 0 or 1. "
        )) );
        break;

    case SELECTION_DISC_TYPE_CDDA:
        disc_sub->Disable(); disc_sub_label->Disable();
        disc_chapter->Disable(); disc_chapter_label->Disable();
        disc_audio->Disable(); disc_audio_label->Disable();
        disc_title_label->SetLabel ( wxU(_("Track")) );
#ifdef HAVE_CDDAX
        disc_title->SetToolTip( wxU(_(
        "Audio CDs can have up to 100 tracks, the first track is usually 1. "
        "If 0 is given, then all tracks are played.")) );
#else
        disc_title->SetToolTip( wxU(_(
        "Audio CDs can have up to 100 tracks, the first track is usually 1."
        )) );
#endif
        psz_device = config_GetPsz( p_intf, "cd-audio" );
        if( !b_disc_device_changed )
        {
            if( psz_device ) disc_device->SetValue( wxL2U(psz_device) );
            else disc_device->SetValue( wxT("") );
        }

        /* There are at most 100 tracks in a CD-DA */
        disc_title->SetRange( 0, 100 );
        break;

    default:
        msg_Err( p_intf, "invalid Disc type selection (%d)",
                 disc_type->GetSelection() );
        break;
    }

    disc_title->SetValue( 0 ); i_disc_title = 0;
    disc_chapter->SetValue( 0 ); i_disc_chapter = 0;

    free( psz_device );

    UpdateMRL( DISC_ACCESS );
}

/*****************************************************************************
 * Net panel event methods.
 *****************************************************************************/
void OpenDialog::OnNetPanelChangeSpin( wxSpinEvent& event )
{
    wxCommandEvent cevent;
    cevent.SetId(event.GetId());
    cevent.SetInt(event.GetPosition());
    OnNetPanelChange(cevent);
}

void OpenDialog::OnNetPanelChange( wxCommandEvent& event )
{
    if( event.GetId() >= NetPort1_Event && event.GetId() <= NetPort3_Event )
    {
        i_net_ports[event.GetId() - NetPort1_Event] = event.GetInt();
    }

    UpdateMRL( NET_ACCESS );
}

void OpenDialog::OnNetTypeChange( wxCommandEvent& event )
{
    int i;

    i_net_type = event.GetId() - NetRadio1_Event;

    for(i=0; i<4; i++)
    {
        net_radios[i]->SetValue( event.GetId() == (NetRadio1_Event+i) );
        net_subpanels[i]->Enable( event.GetId() == (NetRadio1_Event+i) );
    }
    /* UDP Unicast or multicast -> timeshift */
    if( i_net_type == 0 || i_net_type == 1 )
        net_timeshift->Enable();
    else
        net_timeshift->Disable();

    UpdateMRL( NET_ACCESS );
}

/*****************************************************************************
 * Subtitles file event methods.
 *****************************************************************************/
void OpenDialog::OnSubsFileEnable( wxCommandEvent& event )
{
    subsfile_button->Enable( event.GetInt() != 0 );
    subbrowse_button->Enable( event.GetInt() != 0 );
    subfile_combo->Enable( event.GetInt() != 0 );
}

void OpenDialog::OnSubsFileSettings( wxCommandEvent& WXUNUSED(event) )
{
    /* Show/hide the open dialog */
    if( subsfile_dialog == NULL )
        subsfile_dialog = new SubsFileDialog( p_intf, this );

    if( subsfile_dialog && subsfile_dialog->ShowModal() == wxID_OK )
    {
        subsfile_mrl.Empty();
        subsfile_mrl.Add( wxString(wxT("sub-file="))  + subfile_combo->GetValue() );
        if( subsfile_dialog->encoding_combo )
        {
            subsfile_mrl.Add( wxString(wxT("subsdec-encoding=")) +
                              subsfile_dialog->encoding_combo->GetValue() );
        }
        if( subsfile_dialog->align_combo )
        {
            subsfile_mrl.Add( wxString::Format(wxT("subsdec-align=%i"),
                        (int)subsfile_dialog->align_combo->GetClientData(
                        subsfile_dialog->align_combo->GetSelection()) ) );
        }
        if( subsfile_dialog->size_combo )
        {
            subsfile_mrl.Add( wxString::Format( wxT("freetype-rel-fontsize=%i"),
                        (int)subsfile_dialog->size_combo->GetClientData(
                        subsfile_dialog->size_combo->GetSelection()) ) );
        }
        subsfile_mrl.Add( wxString( wxT("sub-fps="))+
                                    subsfile_dialog->fps_ctrl->GetValue()  );
        subsfile_mrl.Add( wxString::Format( wxT("sub-delay=%i"),
                          subsfile_dialog->delay_spinctrl->GetValue() ) );
    }
}

/*****************************************************************************
 * Stream output event methods.
 *****************************************************************************/
void OpenDialog::OnSoutEnable( wxCommandEvent& event )
{
    sout_button->Enable( event.GetInt() != 0 );
}

void OpenDialog::OnSoutSettings( wxCommandEvent& WXUNUSED(event) )
{
    /* Show/hide the open dialog */
    if( sout_dialog == NULL )
        sout_dialog = new SoutDialog( p_intf, this );

    if( sout_dialog && sout_dialog->ShowModal() == wxID_OK )
    {
        sout_mrl = sout_dialog->GetOptions();
    }
}

/*****************************************************************************
 * Caching event methods.
 *****************************************************************************/
void OpenDialog::OnCachingEnable( wxCommandEvent& event )
{
    caching_value->Enable( event.GetInt() != 0 );
    i_caching = caching_value->GetValue();
    UpdateMRL();
}

void OpenDialog::OnCachingChangeSpin( wxSpinEvent& event )
{
    wxCommandEvent cevent;
    OnCachingChange(cevent);
}

void OpenDialog::OnCachingChange( wxCommandEvent& event )
{
    i_caching = event.GetInt();
    UpdateMRL();
}

/*****************************************************************************
 * Utility functions.
 *****************************************************************************/
wxArrayString SeparateEntries( wxString entries )
{
    bool b_quotes_mode = false;

    wxArrayString entries_array;
    wxString entry;

    wxStringTokenizer token( entries, wxT(" \t\r\n\""), wxTOKEN_RET_DELIMS );

    while( token.HasMoreTokens() )
    {
        entry += token.GetNextToken();

        if( entry.IsEmpty() ) continue;

        if( !b_quotes_mode && entry.Last() == wxT('\"') )
        {
            /* Enters quotes mode */
            entry.RemoveLast();
            b_quotes_mode = true;
        }
        else if( b_quotes_mode && entry.Last() == wxT('\"') )
        {
            /* Finished the quotes mode */
            entry.RemoveLast();
            b_quotes_mode = false;
        }
        else if( !b_quotes_mode && entry.Last() != wxT('\"') )
        {
            /* we found a non-quoted standalone string */
            if( token.HasMoreTokens() ||
                entry.Last() == wxT(' ') || entry.Last() == wxT('\t') ||
                entry.Last() == wxT('\r') || entry.Last() == wxT('\n') )
                entry.RemoveLast();
            if( !entry.IsEmpty() ) entries_array.Add( entry );
            entry.Empty();
        }
        else
        {;}
    }

    if( !entry.IsEmpty() ) entries_array.Add( entry );

    return entries_array;
}
