//////////////////////////////////////////////////////////////////////////////
// Name:        src/generic/filedlgg.cpp
// Purpose:     wxGenericFileDialog
// Author:      Robert Roebling
// Created:     12/12/98
// Copyright:   (c) Robert Roebling
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"


#if wxUSE_FILEDLG

// NOTE : it probably also supports MAC, untested
#if !defined(__UNIX__) && !defined(__WIN32__)
#error wxGenericFileDialog currently only supports Unix and MSW
#endif

#ifndef WX_PRECOMP
    #ifdef __WXMSW__
        #include "wx/msw/wrapwin.h"
    #endif
    #include "wx/intl.h"
    #include "wx/settings.h"
    #include "wx/log.h"
    #include "wx/msgdlg.h"
    #include "wx/bmpbuttn.h"
    #include "wx/checkbox.h"
    #include "wx/choice.h"
    #include "wx/stattext.h"
    #include "wx/textctrl.h"
    #include "wx/sizer.h"
    #include "wx/filedlg.h"     // wxFD_OPEN, wxFD_SAVE...
#endif

#include "wx/longlong.h"
#include "wx/config.h"
#include "wx/imaglist.h"
#include "wx/artprov.h"
#include "wx/filefn.h"
#include "wx/filectrl.h"
#include "wx/generic/filedlgg.h"
#include "wx/debug.h"
#include "wx/modalhook.h"

#if wxUSE_TOOLTIPS
    #include "wx/tooltip.h"
#endif
#if wxUSE_CONFIG
    #include "wx/config.h"
#endif

#include <sys/types.h>
#include <sys/stat.h>

#ifdef __UNIX__
    #include <dirent.h>
    #include <pwd.h>
    #ifndef __VMS
    # include <grp.h>
    #endif
#endif

#include <time.h>

#if defined(__UNIX__)
#include <unistd.h>
#endif

#if defined(__WINDOWS__)
#define IsTopMostDir(dir)   (dir.empty())
#else
#define IsTopMostDir(dir)   (dir == wxT("/"))
#endif

//-----------------------------------------------------------------------------
// wxGenericFileDialog
//-----------------------------------------------------------------------------

#define  ID_LIST_MODE     (wxID_FILEDLGG    )
#define  ID_REPORT_MODE   (wxID_FILEDLGG + 1)
#define  ID_UP_DIR        (wxID_FILEDLGG + 2)
#define  ID_HOME_DIR      (wxID_FILEDLGG + 3)
#define  ID_NEW_DIR       (wxID_FILEDLGG + 4)
#define  ID_FILE_CTRL     (wxID_FILEDLGG + 5)

wxIMPLEMENT_DYNAMIC_CLASS(wxGenericFileDialog, wxFileDialogBase);

wxBEGIN_EVENT_TABLE(wxGenericFileDialog,wxDialog)
    EVT_BUTTON(ID_LIST_MODE, wxGenericFileDialog::OnList)
    EVT_BUTTON(ID_REPORT_MODE, wxGenericFileDialog::OnReport)
    EVT_BUTTON(ID_UP_DIR, wxGenericFileDialog::OnUp)
    EVT_BUTTON(ID_HOME_DIR, wxGenericFileDialog::OnHome)
    EVT_BUTTON(ID_NEW_DIR, wxGenericFileDialog::OnNew)
    EVT_BUTTON(wxID_OK, wxGenericFileDialog::OnOk)
    EVT_FILECTRL_FILEACTIVATED(ID_FILE_CTRL, wxGenericFileDialog::OnFileActivated)

    EVT_UPDATE_UI(ID_UP_DIR, wxGenericFileDialog::OnUpdateButtonsUI)
#if defined(__WINDOWS__)
    EVT_UPDATE_UI(ID_NEW_DIR, wxGenericFileDialog::OnUpdateButtonsUI)
#endif // defined(__WINDOWS__)
wxEND_EVENT_TABLE()

long wxGenericFileDialog::ms_lastViewStyle = wxLC_LIST;
bool wxGenericFileDialog::ms_lastShowHidden = false;

void wxGenericFileDialog::Init()
{
    m_bypassGenericImpl = false;

    m_filectrl   = nullptr;
    m_upDirButton  = nullptr;
    m_newDirButton = nullptr;
}

wxGenericFileDialog::wxGenericFileDialog(wxWindow *parent,
                           const wxString& message,
                           const wxString& defaultDir,
                           const wxString& defaultFile,
                           const wxString& wildCard,
                           long  style,
                           const wxPoint& pos,
                           const wxSize& sz,
                           const wxString& name,
                           bool  bypassGenericImpl ) : wxFileDialogBase()
{
    Init();
    Create( parent, message, defaultDir, defaultFile, wildCard, style, pos, sz, name, bypassGenericImpl );
}

bool wxGenericFileDialog::Create( wxWindow *parent,
                                  const wxString& message,
                                  const wxString& defaultDir,
                                  const wxString& defaultFile,
                                  const wxString& wildCard,
                                  long  style,
                                  const wxPoint& pos,
                                  const wxSize& sz,
                                  const wxString& name,
                                  bool  bypassGenericImpl )
{
    m_bypassGenericImpl = bypassGenericImpl;

    parent = GetParentForModalDialog(parent, style);

    if (!wxFileDialogBase::Create(parent, message, defaultDir, defaultFile,
                                  wildCard, style, pos, sz, name))
    {
        return false;
    }

    if (m_bypassGenericImpl)
        return true;

    if (!wxDialog::Create( parent, wxID_ANY, message, pos, sz,
                           wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | style, name
                           ))
    {
        return false;
    }

#if wxUSE_CONFIG
    if (wxConfig::Get(false))
    {
        wxConfig::Get()->Read(wxT("/wxWindows/wxFileDialog/ViewStyle"),
                              &ms_lastViewStyle);
        wxConfig::Get()->Read(wxT("/wxWindows/wxFileDialog/ShowHidden"),
                              &ms_lastShowHidden);
    }
#endif

    if ((m_dir.empty()) || (m_dir == wxT(".")))
    {
        m_dir = wxGetCwd();
        if (m_dir.empty())
            m_dir = wxFILE_SEP_PATH;
    }

    const size_t len = m_dir.length();
    if ((len > 1) && (wxEndsWithPathSeparator(m_dir)))
        m_dir.Remove( len-1, 1 );

    m_filterExtension.clear();

    // layout

    const bool is_pda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);

    wxBoxSizer *mainsizer = new wxBoxSizer( wxVERTICAL );

    wxBoxSizer *buttonsizer = new wxBoxSizer( wxHORIZONTAL );
    AddBitmapButton( ID_LIST_MODE, wxART_LIST_VIEW,
                     _("View files as a list view"), buttonsizer );
    AddBitmapButton( ID_REPORT_MODE, wxART_REPORT_VIEW,
                     _("View files as a detailed view"), buttonsizer );
    buttonsizer->Add( 30, 5, 1 );
    m_upDirButton = AddBitmapButton( ID_UP_DIR, wxART_GO_DIR_UP,
                                     _("Go to parent directory"), buttonsizer );

    AddBitmapButton( ID_HOME_DIR, wxART_GO_HOME,
                     _("Go to home directory"), buttonsizer );
    buttonsizer->Add( 20, 20 );

    m_newDirButton = AddBitmapButton( ID_NEW_DIR, wxART_NEW_DIR,
                                      _("Create new directory"), buttonsizer );

    if (is_pda)
        mainsizer->Add( buttonsizer, wxSizerFlags().Expand() );
    else
        mainsizer->Add( buttonsizer, wxSizerFlags().Expand()
                                                   .Border( wxLEFT | wxRIGHT | wxTOP ) );

    long style2 = 0;
    if ( HasFdFlag(wxFD_MULTIPLE) )
        style2 |= wxFC_MULTIPLE;

    m_filectrl = new wxGenericFileCtrl( this, ID_FILE_CTRL,
                                        m_dir, defaultFile,
                                        wildCard,
                                        style2,
                                        wxDefaultPosition, wxSize(540,200)
                                        );

    m_filectrl->ShowHidden( ms_lastShowHidden );

    if (ms_lastViewStyle == wxLC_LIST)
    {
        m_filectrl->ChangeToListMode();
    }
    else if (ms_lastViewStyle == wxLC_REPORT)
    {
        m_filectrl->ChangeToReportMode();
    }

    mainsizer->Add(m_filectrl, wxSizerFlags(1).Expand().HorzBorder());

    wxSizer *bsizer = CreateButtonSizer(wxOK | wxCANCEL);
    if ( bsizer )
    {
        if (is_pda)
            mainsizer->Add(bsizer, wxSizerFlags().Expand().Border());
        else
            mainsizer->Add(bsizer, wxSizerFlags().Expand().DoubleBorder());
    }

    SetSizer( mainsizer );

    if (!is_pda)
    {
        mainsizer->SetSizeHints( this );

        Centre( wxBOTH );
    }

    return true;
}

wxGenericFileDialog::~wxGenericFileDialog()
{
    if (!m_bypassGenericImpl)
    {
#if wxUSE_CONFIG
        if (wxConfig::Get(false))
        {
            wxConfig::Get()->Write(wxT("/wxWindows/wxFileDialog/ViewStyle"),
                                   ms_lastViewStyle);
            wxConfig::Get()->Write(wxT("/wxWindows/wxFileDialog/ShowHidden"),
                                   ms_lastShowHidden);
        }
#endif
    }
}

wxBitmapButton* wxGenericFileDialog::AddBitmapButton( wxWindowID winId,
                                                      const wxArtID& artId,
                                                      const wxString& tip,
                                                      wxSizer *sizer)
{
    wxBitmapButton *but = new wxBitmapButton(this, winId,
                                         wxArtProvider::GetBitmap(artId, wxART_BUTTON));
    but->SetToolTip(tip);
    sizer->Add(but, wxSizerFlags().Border());
    return but;
}

int wxGenericFileDialog::ShowModal()
{
    WX_HOOK_MODAL_DIALOG();

    if (CreateExtraControl())
    {
        wxSizer *sizer = GetSizer();
        sizer->Insert(2 /* after m_filectrl */, m_extraControl,
                      wxSizerFlags().Expand().HorzBorder());
        sizer->Fit(this);
    }

    m_filectrl->SetDirectory(m_dir);

    const int rc = wxDialog::ShowModal();

    // Destroy the extra controls before ShowModal() returns for consistency
    // with the native implementations.
    if (m_extraControl)
    {
        m_extraControl->Destroy();
        m_extraControl = nullptr;
    }

    return rc;
}

bool wxGenericFileDialog::Show( bool show )
{
    // Called by ShowModal, so don't repeat the update
#ifndef __WIN32__
    if (show)
    {
        m_filectrl->SetDirectory(m_dir);
    }
#endif

    return wxDialog::Show( show );
}

void wxGenericFileDialog::OnOk( wxCommandEvent &WXUNUSED(event) )
{
    wxArrayString selectedFiles;
    m_filectrl->GetPaths(selectedFiles);

    if (selectedFiles.Count() == 0)
        return;

    const wxString& path = selectedFiles[0];

    if (selectedFiles.Count() == 1)
    {
        SetPath(path);
    }

    // check that the file [doesn't] exist if necessary
    if ( HasFdFlag(wxFD_SAVE) && HasFdFlag(wxFD_OVERWRITE_PROMPT) &&
                wxFileExists(path) )
    {
        if ( wxMessageBox
             (
                wxString::Format
                (
                    _("File '%s' already exists, do you really want to overwrite it?"),
                    path
                ),
                _("Confirm"),
                wxYES_NO
             ) != wxYES)
            return;
    }
    else if ( HasFdFlag(wxFD_OPEN) && HasFdFlag(wxFD_FILE_MUST_EXIST) &&
                    !wxFileExists(path) )
    {
        wxMessageBox(_("Please choose an existing file."), _("Error"),
                     wxOK | wxICON_ERROR );
        return;
    }

    TransferDataFromExtraControl();

    EndModal(wxID_OK);
}

void wxGenericFileDialog::OnList( wxCommandEvent &WXUNUSED(event) )
{
    m_filectrl->ChangeToListMode();
    ms_lastViewStyle = wxLC_LIST;
    m_filectrl->GetFileList()->SetFocus();
}

void wxGenericFileDialog::OnReport( wxCommandEvent &WXUNUSED(event) )
{
    m_filectrl->ChangeToReportMode();
    ms_lastViewStyle = wxLC_REPORT;
    m_filectrl->GetFileList()->SetFocus();
}

void wxGenericFileDialog::OnUp( wxCommandEvent &WXUNUSED(event) )
{
    m_filectrl->GoToParentDir();
    m_filectrl->GetFileList()->SetFocus();
}

void wxGenericFileDialog::OnHome( wxCommandEvent &WXUNUSED(event) )
{
    m_filectrl->GoToHomeDir();
    m_filectrl->SetFocus();
}

void wxGenericFileDialog::OnNew( wxCommandEvent &WXUNUSED(event) )
{
    m_filectrl->GetFileList()->MakeDir();
}

void wxGenericFileDialog::OnFileActivated( wxFileCtrlEvent &WXUNUSED(event) )
{
    wxCommandEvent dummy;
    OnOk( dummy );
}

void wxGenericFileDialog::OnUpdateButtonsUI(wxUpdateUIEvent& event)
{
    // surprisingly, we can be called before m_filectrl is set in Create() as
    // wxFileCtrl ctor itself can generate idle events, so we need this test
    if ( m_filectrl )
        event.Enable( !IsTopMostDir(m_filectrl->GetShownDirectory()) );

    UpdateExtraControlUI();
}

#ifdef wxHAS_GENERIC_FILEDIALOG

wxIMPLEMENT_DYNAMIC_CLASS(wxFileDialog, wxGenericFileDialog);

#endif // wxHAS_GENERIC_FILEDIALOG

#endif // wxUSE_FILEDLG
