/*
 * This file is part of the Colobot: Gold Edition source code
 * Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam
 * http://epsitec.ch; http://colobot.info; http://github.com/colobot
 *
 * 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 3 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, see http://gnu.org/licenses
 */


#include "ui/filedialog.h"

#include "app/app.h"

#include "common/logger.h"
#include "common/restext.h"

#include "common/resources/resourcemanager.h"

#include "level/robotmain.h"

#include "ui/controls/check.h"
#include "ui/controls/control.h"
#include "ui/controls/edit.h"
#include "ui/controls/group.h"
#include "ui/controls/interface.h"
#include "ui/controls/label.h"
#include "ui/controls/list.h"
#include "ui/controls/window.h"

#include <algorithm>
#include <stdio.h>
#include <ctime>


namespace Ui
{


// time limit for double-click on directory
const float DELAY_DBCLICK_DIR = 0.75f;


// Object's constructor.

CFileDialog::CFileDialog()
{
    m_eventQueue = CApplication::GetInstancePointer()->GetEventQueue();
    m_interface = CRobotMain::GetInstancePointer()->GetInterface();
    m_time = 0.0f;
    m_lastTimeClickDir = 0.0f;
}

// Object's destructor.

CFileDialog::~CFileDialog()
{
}

// Beginning of the display of a dialogue.

void CFileDialog::StartDialog()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW0));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW1));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW2));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW3));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW4));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW5));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    // the code battle interface, EVENT_WINDOW6, must be managed seperately

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW7));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW8));
    if ( pw != nullptr )  pw->ClearState(STATE_ENABLE);

    if ( m_dialogtype != CFileDialog::Type::None )
    {
        StartFileDialog();
    }
    else
    {
        m_eventQueue->AddEvent(Event(EVENT_DIALOG_STOP));
    }
}

void CFileDialog::StopDialog()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW0));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW1));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW2));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW3));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW4));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW5));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    // the code battle interface, EVENT_WINDOW6, must be managed seperately

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW7));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW8));
    if ( pw != nullptr )  pw->SetState(STATE_ENABLE);

    if ( m_windowEvent != EVENT_NULL ) // the window was created?
    {
        m_interface->DeleteControl(m_windowEvent); // delete it
    }
}

void CFileDialog::SetUsePublicPrivate(bool usePublic)
{
    m_usePublicPrivate = usePublic;
}

void CFileDialog::SetPublic(bool bPublic)
{
    m_public = bPublic;
}

bool CFileDialog::GetPublic()
{
    return m_public;
}

void CFileDialog::SetPublicFolder(const std::string& dir)
{
    m_pathPublic = dir;
}

void CFileDialog::SetPrivateFolder(const std::string& dir)
{
    m_pathPrivate = dir;
}

void CFileDialog::SetBasePath(const std::string& dir)
{
    m_basePath = dir;
}

std::string CFileDialog::GetBasePath()
{
    return m_basePath;
}

void CFileDialog::SetSubFolderPath(const std::string& dir)
{
    m_subDirPath = dir;
}

std::string CFileDialog::GetSubFolderPath()
{
    return m_subDirPath;
}

void CFileDialog::SetFilename(const std::string& filename)
{
    m_filename = filename;
}

std::string CFileDialog::GetFilename()
{
    return m_filename;
}

// Start display of Open or Save dialogue

void CFileDialog::StartFileDialog()
{
    m_captureClick = false;
    glm::vec2 pos = m_windowPos;
    glm::vec2 dim = m_windowDim;
    int icon = (m_dialogtype == CFileDialog::Type::Open) ? 14 : 13 ;

    CWindow* pw = m_interface->CreateWindows(pos, dim, icon, m_windowEvent);
    if ( pw == nullptr ) return;

    if ( m_windowEvent == EVENT_NULL )      // event type was not set?
    {
        m_windowEvent = pw->GetEventType(); // get the unique event id
    }

    pw->SetState(STATE_SHADOW);
    pw->SetMovable(true);
    pw->SetClosable(true);
    pw->SetMinDim({ 320.0f / 640.0f, (121.0f + 18.0f * 4) / 480.0f });

    if ( m_title.empty() )
    {
        if (m_dialogtype == CFileDialog::Type::Open)
            GetResource(RES_TEXT, RT_IO_OPEN, m_title);

        if (m_dialogtype == CFileDialog::Type::Save)
            GetResource(RES_TEXT, RT_IO_SAVE, m_title);

        if (m_dialogtype == CFileDialog::Type::Folder)
            GetResource(RES_TEXT, RT_IO_SELECT_DIR, m_title);
    }
    pw->SetName(m_title);

    std::string name;
    pos = { 0.0f, 0.0f };
    dim = { 0.0f, 0.0f };

    GetResource(RES_TEXT, RT_IO_LIST, name);
    CLabel* pla = pw->CreateLabel(pos, dim, 0, EVENT_DIALOG_LABEL1, name); // path label
    pla->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);

    CButton* pb = pw->CreateButton(pos, dim, -1, EVENT_DIALOG_NEWDIR);     // New Folder button
    pb->SetState(STATE_SHADOW);

    CList* pli = pw->CreateList(pos, dim, 0, EVENT_DIALOG_LIST);           // file list
    pli->SetState(STATE_SHADOW);

    CEdit* pe = pw->CreateEdit(pos, dim, 0, EVENT_DIALOG_EDIT2);           // new folder edit box
    pe->ClearState(STATE_VISIBLE | STATE_ENABLE);

    if ( m_usePublicPrivate )
    {
        GetResource(RES_TEXT, RT_IO_DIR, name);
        pla = pw->CreateLabel(pos, dim, 0, EVENT_DIALOG_LABEL3, name);     // "Folder:" label
        pla->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);

        CCheck* pc = pw->CreateCheck(pos, dim, 0, EVENT_DIALOG_CHECK1);    // private check box
        GetResource(RES_TEXT, RT_IO_PRIVATE, name);
        pc->SetName(name);
        pc->SetState(STATE_SHADOW);

        pc = pw->CreateCheck(pos, dim, 0, EVENT_DIALOG_CHECK2);            // public check box
        GetResource(RES_TEXT, RT_IO_PUBLIC, name);
        pc->SetName(name);
        pc->SetState(STATE_SHADOW);
    }

    pb = pw->CreateButton(pos, dim, -1, EVENT_DIALOG_CANCEL);              // Cancel button
    pb->SetState(STATE_SHADOW);

    pb = pw->CreateButton(pos, dim, -1, EVENT_DIALOG_OK);                  // open/save button
    pb->SetState(STATE_SHADOW);

    if (m_dialogtype == CFileDialog::Type::Folder)
    {
        m_selectFolderMode = true;
        AdjustDialog();
        UpdatePublic(m_public);
        PopulateList();
        UpdatePathLabel();
        UpdateSelectFolder();
        return;
    }

    GetResource(RES_TEXT, RT_IO_OPEN, name);
    if ( m_dialogtype == CFileDialog::Type::Save ) GetResource(RES_TEXT, RT_IO_SAVE, name);
    pb->SetName(name);

    GetResource(RES_TEXT, RT_IO_NAME, name);
    pla = pw->CreateLabel(pos, dim, 0, EVENT_DIALOG_LABEL2, name);         // "Name:" label
    pla->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);

    pe = pw->CreateEdit(pos, dim, 0, EVENT_DIALOG_EDIT);                   // file name edit box
    pe->SetState(STATE_SHADOW);

    // create "Overwrite existing file?" controls
    CGroup* pg = pw->CreateGroup(pos, dim, 0, EVENT_DIALOG_GROUP1);   // box "Overwrite ?"
    pg->ClearState(STATE_VISIBLE | STATE_ENABLE);
    pg->SetState(STATE_SHADOW);

    pla = pw->CreateLabel(pos, dim, 0, EVENT_LABEL0, name); // label "Overwrite ?"
    pla->SetTextAlign(Gfx::TEXT_ALIGN_CENTER);
    pla->ClearState(STATE_VISIBLE | STATE_ENABLE);
    GetResource(RES_TEXT, RT_IO_REPLACE, name);
    pla->SetName(name);
    pla->SetState(STATE_SHADOW);

    pla = pw->CreateLabel(pos, dim, 0, EVENT_LABEL1, name); // filename label
    pla->SetTextAlign(Gfx::TEXT_ALIGN_CENTER);
    pla->ClearState(STATE_VISIBLE | STATE_ENABLE);

    pb = pw->CreateButton(pos, dim, -1, EVENT_BUTTON_CANCEL); // cancel
    pb->ClearState(STATE_VISIBLE | STATE_ENABLE);
    pb->SetState(STATE_SHADOW);

    pb = pw->CreateButton(pos, dim, -1, EVENT_BUTTON_OK); // ok
    pb->ClearState(STATE_VISIBLE | STATE_ENABLE);
    pb->SetState(STATE_SHADOW);

    AdjustDialog();

    SetFilenameField(pe, m_filename);
    SetFilename("");

    UpdatePublic(m_public);
    PopulateList();
    SearchList(pe->GetText(999));
    UpdateAction();
    UpdatePathLabel();

    pe->SetCursor(999, 0);
    m_interface->SetFocus(pe);
}

void CFileDialog::SetFilenameField(CEdit* edit, const std::string& filename)
{
    std::string name = filename;
    if (name.length() > static_cast<unsigned int>(edit->GetMaxChar()))
    {
        if (name.substr(name.length()-5) == ".cbot")
            name = name.substr(0, name.length()-5);
        else if (name.substr(name.length()-4) == ".txt")
            name = name.substr(0, name.length()-4);
        if (name.length() > static_cast<unsigned int>(edit->GetMaxChar()))
        {
            GetLogger()->Warn("Tried to load too long filename!\n");
            name = name.substr(0, edit->GetMaxChar());  // truncates according to max length
        }
    }
    edit->SetText(name);
}

void CFileDialog::AdjustDialog()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return;

    glm::vec2 wpos = m_windowPos = pw->GetPos();
    glm::vec2 wdim = m_windowDim = pw->GetDim();

    pw->SetPos(wpos);  // to move the buttons on the titlebar
    pw->SetDim(wdim);

    glm::vec2 ppos(wpos.x+10.0f/640.0f, wpos.y+wdim.y-55.0f/480.0f);
    glm::vec2 ddim(wdim.x-150.0f/640.0f, 20.0f/480.0f);

    CLabel* pla = static_cast< CLabel* >(pw->SearchControl(EVENT_DIALOG_LABEL1)); // path label
    if ( pla != nullptr )
    {
        pla->SetPos(ppos);
        pla->SetDim(ddim);
        UpdatePathLabel();
    }

    ddim.x = wdim.x-20.0f/640.0f;
    int nli = static_cast<int>((wdim.y-120.0f/480.0f)/(17.0f/480.0f));
    ddim.y = nli*17.0f/480.0f+9.0f/480.0f;
    ppos.y = wpos.y+wdim.y-48.0f/480.0f-ddim.y;
    if ( m_newFolderMode ) ddim.y -= 17.5f/480.0f;

    CList* pli = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));     // file list
    if ( pli != nullptr )
    {
        pli->SetPos(ppos);
        pli->SetDim(ddim);
        pli->SetTabs(0, ddim.x-(50.0f+140.0f+16.0f)/640.0f);
        pli->SetTabs(1,  50.0f/640.0f, Gfx::TEXT_ALIGN_RIGHT);
        pli->SetTabs(2, 140.0f/640.0f);
        if (pli->GetSelect() != -1) pli->ShowSelect(false);
    }

    if ( m_newFolderMode )
    {
        ppos.y += ddim.y-3.0f/480.0f;
        ddim.y = 20.0f/480.0f;
        CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT2)); // new folder edit box
        if ( pe != nullptr )
        {
            pe->SetPos(ppos);
            pe->SetDim(ddim);
            int nch = static_cast< int >((ddim.x*640.0f-22.0f)/5.75f);
            std::string name = pe->GetText(nch);
            pe->SetMaxChar(nch);
            pe->SetText(name);
        }
    }

    if (!m_selectFolderMode)
    {
        ppos.y = wpos.y+30.0f/480.0f;
        ddim.x = 50.0f/640.0f;
        ddim.y = 20.0f/480.0f;
        pla = static_cast< CLabel* >(pw->SearchControl(EVENT_DIALOG_LABEL2)); // "Name:" label
        if ( pla != nullptr )
        {
            pla->SetPos(ppos);
            pla->SetDim(ddim);
        }

        ppos.x += 50.0f/640.0f;
        ppos.y = wpos.y+36.0f/480.0f;
        ddim.x = wdim.x-170.0f/640.0f;
        CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT)); // file name edit box
        if ( pe != nullptr )
        {
            pe->SetPos(ppos);
            pe->SetDim(ddim);

            int nch = static_cast< int >((ddim.x*640.0f-22.0f)/5.75f);
            std::string name = pe->GetText(nch);
            pe->SetMaxChar(nch);
            pe->SetText(name);
        }
    }

    if ( m_usePublicPrivate )
    {
        ppos.x = wpos.x+10.0f/640.0f;
        ppos.y = wpos.y+5.0f/480.0f;
        ddim.x = 50.0f/640.0f;
        ddim.y = 16.0f/480.0f;
        pla = static_cast< CLabel* >(pw->SearchControl(EVENT_DIALOG_LABEL3)); // "Folder:" label
        if ( pla != nullptr )
        {
            pla->SetPos(ppos);
            pla->SetDim(ddim);
        }

        ppos.x += 50.0f/640.0f;
        ppos.y = wpos.y+12.0f/480.0f;
        ddim.x = 70.0f/640.0f;
        CCheck* pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK1)); // private check box
        if ( pc != nullptr )
        {
            pc->SetPos(ppos);
            pc->SetDim(ddim);
        }

        ppos.x += 80.0f/640.0f;
        pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK2)); // public check box
        if ( pc != nullptr )
        {
            pc->SetPos(ppos);
            pc->SetDim(ddim);
        }
    }

    ppos.x = wpos.x+wdim.x-100.0f/640.0f;
    ppos.y = wpos.y+34.0f/480.0f;
    ddim.x = 90.0f/640.0f;
    ddim.y = 23.0f/480.0f;
    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_OK)); // open/save button
    if ( pb != nullptr )
    {
        pb->SetPos(ppos);
        pb->SetDim(ddim);
    }

    ppos.y -= 26.0f/480.0f;
    pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_CANCEL)); // Cancel button
    if ( pb != nullptr )
    {
        pb->SetPos(ppos);
        pb->SetDim(ddim);
    }

    ppos.y = wpos.y+wdim.y-49.0f/480.0f;
    pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_NEWDIR)); // New Folder button
    if ( pb != nullptr )
    {
        pb->SetPos(ppos);
        pb->SetDim(ddim);
    }

    if ( m_askOverwriteMode )
    {
        ppos = wpos;
        ppos.x += (wdim.x/2.0f) - (97.5f/640.0f);
        ppos.y += (wdim.y/2.0f) - (50.0f/480.0f);

        ddim.x = 195.0f/640.0f;
        ddim.y = 100.0f/480.0f;
        CGroup* pg = static_cast< CGroup* >(pw->SearchControl(EVENT_DIALOG_GROUP1)); // "Overwrite ?" box
        if ( pg != nullptr )
        {
            pg->SetPos(ppos);
            pg->SetDim(ddim);
        }

        ddim.x = 82.5f/640.0f;
        ddim.y = 23.0f/480.0f;
        ppos.x += 10.0f/640.0f;
        ppos.y += 10.0f/480.0f;
        pb = static_cast< CButton* >(pw->SearchControl(EVENT_BUTTON_CANCEL)); // Cancel button
        if ( pb != nullptr )
        {
            pb->SetPos(ppos);
            pb->SetDim(ddim);
        }

        ppos.x += 92.5f/640.0f;
        pb = static_cast< CButton* >(pw->SearchControl(EVENT_BUTTON_OK)); // Ok button
        if ( pb != nullptr )
        {
            pb->SetPos(ppos);
            pb->SetDim(ddim);
        }

        ddim.x = 185.0f/640.0f;
        ppos.x -= 97.5f/640.0f;
        ppos.y += 28.0f/480.0f;
        pla = static_cast< CLabel* >(pw->SearchControl(EVENT_LABEL1));  // filename label
        if ( pla != nullptr )
        {
            pla->SetPos(ppos);
            pla->SetDim(ddim);
        }

        ppos.y += 28.0f/480.0f;
        pla = static_cast< CLabel* >(pw->SearchControl(EVENT_LABEL0));  // "Overwrite ?" label
        if ( pla != nullptr )
        {
            pla->SetPos(ppos);
            pla->SetDim(ddim);
        }
    }
}

// Management of a dialogue event.

bool CFileDialog::EventProcess(const Event &event)
{
    if ( event.type == m_windowEvent )  // window is moved ?
    {
        AdjustDialog();
        return true;
    }

    if ( m_askOverwriteMode ) return EventAskOverwrite(event);

    m_time += event.rTime;
    if ( event.type == EVENT_DIALOG_LIST ) // a list item was clicked ?
    {
        if ( ListItemIsFolder() )
        {
            if ( m_captureClick )
            {
                if (m_time - m_lastTimeClickDir <= DELAY_DBCLICK_DIR) // double click open folder
                {
                    OpenFolder();
                }
                m_captureClick = false;
            }
            else
            {
                m_captureClick = true;
                m_lastTimeClickDir = m_time;
            }
            if (m_selectFolderMode) UpdateSelectFolder();
            return true;
        }

        // a file name was clicked
        m_captureClick = false;
        if (m_selectFolderMode)
        {
            UpdateSelectFolder();
            return true;
        }

        if (!m_newFolderMode) GetListChoice();
        return true;
    }

    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return false;

    if ( event.type == EVENT_DIALOG_CANCEL || event.type == pw->GetEventTypeClose() )
    {
        m_eventQueue->AddEvent(Event(EVENT_DIALOG_STOP));
        return true;
    }

    EventType etype;
    etype = ( m_newFolderMode ) ? EVENT_DIALOG_EDIT2 : EVENT_DIALOG_EDIT;
    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(etype));

    if ( event.type == etype )
    {
        if (pe != nullptr) SearchList(pe->GetText(999), m_newFolderMode);
        return true;
    }

    if ( m_usePublicPrivate )
    {
        if ( event.type == EVENT_DIALOG_CHECK1 )  // private?
        {
            m_subDirPath.clear();
            UpdatePublic(false);
            PopulateList();
            UpdatePathLabel();
            if (pe != nullptr) SearchList(pe->GetText(999), m_newFolderMode);
            if (m_selectFolderMode) UpdateSelectFolder();
        }
        else if ( event.type == EVENT_DIALOG_CHECK2 )  // public?
        {
            m_subDirPath.clear();
            UpdatePublic(true);
            PopulateList();
            UpdatePathLabel();
            if (pe != nullptr) SearchList(pe->GetText(999), m_newFolderMode);
            if (m_selectFolderMode) UpdateSelectFolder();
        }
    }

    if (m_captureClick && m_time - m_lastTimeClickDir > DELAY_DBCLICK_DIR)
    {
        m_captureClick = false;
    }

    if ( m_newFolderMode ) return EventNewFolder(event); // process 'new folder' events

    if ( event.type == EVENT_DIALOG_NEWDIR )
    {
        if ( !StartNewFolderMode() ) StopNewFolderMode(true);
        return true;
    }

    if (m_selectFolderMode) return EventSelectFolder(event);

    if ( event.type == EVENT_DIALOG_OK ||
         (event.type == EVENT_KEY_DOWN && event.GetData<KeyEventData>()->key == KEY(RETURN)) )
    {
        if ( m_dialogtype == CFileDialog::Type::Open )
        {
            if ( !ActionOpen() ) return true;
        }
        if ( m_dialogtype == CFileDialog::Type::Save )
        {
            if ( !ActionSave(m_confirmOverwrite) ) return true;
        }

        m_eventQueue->AddEvent(Event(EVENT_DIALOG_ACTION));
    }

    if ( event.type == EVENT_KEY_DOWN && event.GetData<KeyEventData>()->key == KEY(ESCAPE) )
    {
        m_eventQueue->AddEvent(Event(EVENT_DIALOG_STOP));
    }

    return true;
}

bool CFileDialog::StartNewFolderMode()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent)); // dialog window
    if ( pw == nullptr ) return false;

    if (!m_selectFolderMode)
    {
        CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT));   // filename edit box
        if ( pe == nullptr ) return false;
        pe->SetState(STATE_ENABLE, false);
    }

    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_NEWDIR)); // new folder button
    if ( pb == nullptr ) return false;
    std::string name;
    GetResource(RES_EVENT, EVENT_DIALOG_CANCEL, name);
    pb->SetName(name);


    CList* pli = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));    // file list
    if ( pli == nullptr ) return false;
    glm::vec2 dim = pli->GetDim();
    dim.y -= 17.5f/480.0f;
    pli->SetDim(dim);
    pli->SetSelect(-1);
    pli->ShowSelect(false);

    glm::vec2 pos = pli->GetPos();

    pos.y += dim.y-3.0f/480.0f;
    dim.y = 20.0f/480.0f;

    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT2));  // new folder edit box
    if ( pe == nullptr ) return false;
    pe->SetState(STATE_VISIBLE, true);
    pe->SetState(STATE_ENABLE, true);
    pe->SetPos(pos);
    pe->SetDim(dim);
    pw->SetFocus(pe);

    pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_OK));   // open/save button
    if ( pb == nullptr ) return false;
    pb->SetState(STATE_ENABLE, false);

    m_newFolderMode = true;

    return true;
}

bool CFileDialog::StopNewFolderMode(bool bCancel)
{
    m_newFolderMode = false;

    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent)); // dialog window
    if ( pw == nullptr ) return false;
    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT2)); // new folder edit box
    if ( pe != nullptr )
    {
        pe->SetText("");
        pe->ClearState(STATE_VISIBLE | STATE_ENABLE);
    }

    if (!m_selectFolderMode)
    {
        pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT));      // filename edit box
        if ( pe != nullptr )
        {
            pe->SetState(STATE_ENABLE, true);
            pw->SetFocus(pe);
        }
    }

    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_NEWDIR)); // new folder button
    if ( pb != nullptr )
    {
        std::string name;
        GetResource(RES_EVENT, EVENT_DIALOG_NEWDIR, name);
        pb->SetName(name);
    }

    AdjustDialog();

    if (m_selectFolderMode)
    {
        UpdateSelectFolder();
        return true;
    }

    if ( pe != nullptr )
    {
        pe->SetCursor(999, 0);
        if ( bCancel ) SearchList(pe->GetText(999));
    }

    return true;
}

bool CFileDialog::EventNewFolder(const Event &event)
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return false;

    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_NEWDIR)); // new folder button
    if ( pb == nullptr ) return false;

    if ( event.type == EVENT_DIALOG_NEWDIR )
    {
        std::string text;
        GetResource(RES_EVENT, EVENT_DIALOG_CANCEL, text);
        if (pb->GetName() == text) return StopNewFolderMode(true); // cancel
        CreateNewFolder();
        return StopNewFolderMode();
    }

    if (event.type == EVENT_KEY_DOWN)
    {
        if (event.GetData<KeyEventData>()->key == KEY(ESCAPE))
            return StopNewFolderMode(true);

        if (event.GetData<KeyEventData>()->key == KEY(RETURN))
        {
            std::string text;
            GetResource(RES_EVENT, EVENT_DIALOG_CANCEL, text);
            if (pb->GetName() == text) return true;
            CreateNewFolder();
            return StopNewFolderMode();
        }
    }

    return true;
}

bool CFileDialog::EventSelectFolder(const Event &event)
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return false;

    if ( event.type == EVENT_DIALOG_OK )
    {
        CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
        if ( pl == nullptr ) return false;
        std::string name = pl->GetItemName(pl->GetSelect());
        name = name.substr(0, name.find_first_of("\t"));
        m_subDirPath += m_subDirPath.empty() ? name : "/" + name;
        m_eventQueue->AddEvent(Event(EVENT_DIALOG_ACTION));
    }

    if (event.type == EVENT_KEY_DOWN)
    {
        if (event.GetData<KeyEventData>()->key == KEY(ESCAPE))
        {
            m_eventQueue->AddEvent(Event(EVENT_DIALOG_STOP));
        }

        if (event.GetData<KeyEventData>()->key == KEY(RETURN))
        {
            if ( ListItemIsFolder() )
            {
                CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
                if ( pl == nullptr ) return false;
                std::string name = pl->GetItemName(pl->GetSelect());
                name = name.substr(0, name.find_first_of("\t"));
                if ( name != ".." )
                {
                    m_subDirPath += m_subDirPath.empty() ? name : "/" + name;
                    m_eventQueue->AddEvent(Event(EVENT_DIALOG_ACTION));
                }
            }
        }
    }

    return true;
}

// Updates the file name edit box after a click in the list.

void CFileDialog::GetListChoice()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return;
    CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
    if ( pl == nullptr )  return;
    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT));
    if ( pe == nullptr )  return;

    std::string name = pl->GetItemName(pl->GetSelect());
    name = name.substr(0, name.find_first_of("\t"));
    SetFilenameField(pe, name);
    pe->SetCursor(999, 0);  // select all
    m_interface->SetFocus(pe);

    UpdateAction();
}

// Test if the selected list item is a directory that exists.

bool CFileDialog::ListItemIsFolder()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return false;

    CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
    if ( pl == nullptr )  return false;

    int n = pl->GetTotal();
    int i = pl->GetSelect();
    if (i < 0 || i >= n) return false;

    std::string name = pl->GetItemName(i);
    name = name.substr(0, name.find_first_of("\t"));

    if (name == "..") return !m_subDirPath.empty();

    if (name.find_first_of(".*?:<>\"|/\\") != std::string::npos) return false;

    return DirectoryExists(name);
}

// Updates the list after a change in name.

void CFileDialog::SearchList(const std::string &text, bool dirOnly)
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return;
    CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
    if ( pl == nullptr )  return;

    pl->SetSelect(-1);

    // highlight the list item matching what is typed in the edit box
    if (!text.empty())
    {
        for (int i = 0; i < pl->GetTotal(); i++)
        {
            std::string item = pl->GetItemName(i);
            if (dirOnly && item.find("\t** DIR **  \t") == std::string::npos) break;
            item = item.substr(0, item.find_first_of("\t"));
            if (item.substr(0, text.length()) != text) continue;
            pl->SetSelect(i); // select item
            pl->ShowSelect(false);  // scroll list
            break;
        }
    }

    if ( m_newFolderMode ) UpdateNewFolder(); else UpdateAction();
}

// Updates the action button.

void CFileDialog::UpdateAction()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return;
    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT));
    if ( pe == nullptr )  return;
    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_OK));
    if ( pb == nullptr )  return;

    bool bError = true;
    std::string text = pe->GetText(999);
    if ( !text.empty() )
    {
        if (text.find_first_of("*?:<>\"|/\\") == std::string::npos)
            bError = DirectoryExists(text);

        if (!bError && !CheckFilename(text))
            bError = !CheckFilename(text+m_extension);
    }

    pb->SetState(STATE_ENABLE, !bError);
}

void CFileDialog::UpdateSelectFolder()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return;
    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_OK));
    if ( pb == nullptr ) return;

    bool bError = true;
    if (!m_newFolderMode && ListItemIsFolder())
    {
        CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
        if ( pl != nullptr )
        {
            std::string name = pl->GetItemName(pl->GetSelect());
            name = name.substr(0, name.find_first_of("\t"));
            if (name != "..") bError = false;
        }
    }

    pb->SetState(STATE_ENABLE, !bError);
}

// Updates the New Folder button.

void CFileDialog::UpdateNewFolder()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return;
    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT2));
    if ( pe == nullptr )  return;
    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_NEWDIR));
    if ( pb == nullptr )  return;

    bool bError = true;
    std::string text = pe->GetText(999);
    if ( !text.empty() )
    {
        if (text.find_first_of(".*?:<>\"|/\\") == std::string::npos)
            bError = DirectoryExists(text);
    }

    if (bError) GetResource(RES_EVENT, EVENT_DIALOG_CANCEL, text);
    else GetResource(RES_EVENT, EVENT_DIALOG_OK, text);
    pb->SetName(text);
}

// Updates the private/public buttons.

void CFileDialog::UpdatePublic(bool bPublic)
{
    if ( !m_usePublicPrivate ) return;

    m_public = bPublic;

    SetBasePath(bPublic ? m_pathPublic : m_pathPrivate);

    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return;

    CCheck* pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK1));
    if ( pc != nullptr )
    {
        pc->SetState(STATE_CHECK, !bPublic);
    }

    pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK2));
    if ( pc != nullptr )
    {
        pc->SetState(STATE_CHECK, bPublic);
    }
}

// Updates the path label

void CFileDialog::UpdatePathLabel()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return;

    CLabel* pl = static_cast< CLabel* >(pw->SearchControl(EVENT_DIALOG_LABEL1));
    if ( pl != nullptr )
    {
        glm::vec2 dim = pl->GetDim();
        size_t nch = static_cast< size_t >((dim.x*640.0f)/5.75f);
        std::string text = SearchDirectory(false);
        if (text.length() > nch)
        {
            text = "..." + text.substr(text.length()-nch, nch);
        }
        pl->SetName(text, false);
    }
}

// Fills the list with files and folders.

void CFileDialog::PopulateList()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr )  return;
    CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
    if ( pl == nullptr )  return;
    pl->Flush();

    if (!CResourceManager::DirectoryExists(SearchDirectory(false)))
        return;

    int  i = 0;
    char timestr[100];

    // list all folders
    std::vector<std::string> folders = CResourceManager::ListDirectories(SearchDirectory(false));
    if (!m_subDirPath.empty()) folders.insert(folders.begin(), std::string(".."));
    for (auto& dir : folders)
    {
        std::ostringstream temp;
        time_t now = CResourceManager::GetLastModificationTime(SearchDirectory(false) + dir);
        strftime(timestr, 99, "%x %X", localtime(&now));
        temp << dir << '\t' << "** DIR **" << "  \t" << timestr;
        pl->SetItemName(i++, temp.str().c_str());
    }

    // list all files
    std::vector<std::string> files = CResourceManager::ListFiles(SearchDirectory(false), true);
    auto it = std::remove_if(files.begin(), files.end(), [this](const std::string& name)
    {
        return !CheckFilename(name);
    });
    files.erase(it, files.end()); // remove invalid file names

    for (auto& filename : files)
    {
        std::ostringstream temp;
        time_t now = CResourceManager::GetLastModificationTime(SearchDirectory(false) + filename);
        strftime(timestr, 99, "%x %X", localtime(&now));
        temp << filename << '\t' << CResourceManager::GetFileSize(SearchDirectory(false) + filename) << "  \t" << timestr;
        pl->SetItemName(i++, temp.str().c_str());
    }
}

// Constructs the name of the folder for open/save.
// If the folder does not exist, it can be created.
std::string CFileDialog::SearchDirectory(bool bCreate)
{
    std::string dir = m_basePath;

    if (bCreate && !CResourceManager::DirectoryExists(dir))
    {
        CResourceManager::CreateNewDirectory(dir);
    }

    if (!m_subDirPath.empty())
    {
        dir += "/" + m_subDirPath;
        if (bCreate && !CResourceManager::DirectoryExists(dir))
        {
            CResourceManager::CreateNewDirectory(dir);
        }
    }

    return dir + "/";
}

bool CFileDialog::DirectoryExists(const std::string &name)
{
    if ( name.empty() ) return false;

    if ( name == ".." ) return !m_subDirPath.empty();

    return CResourceManager::DirectoryExists(SearchDirectory(false)+name);
}

// Make folder

void CFileDialog::CreateNewFolder()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return;

    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT2));
    if ( pe == nullptr ) return;
    std::string name = pe->GetText(999);
    if ( name.empty() ) return;

    if ( !m_subDirPath.empty() ) m_subDirPath += "/";

    m_subDirPath += name;   // add to current path

    SearchDirectory(true);  // make the new folder

    size_t pos = m_subDirPath.find_last_of("/");

    // keep the current folder as current path
    if (pos == std::string::npos)
        m_subDirPath.clear();
    else
        m_subDirPath.resize(pos);

    PopulateList();         // redraw the list
    SearchList(name, true); // highlight the new folder in list box
}

// Open folder

void CFileDialog::OpenFolder()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return;

    CList* pl = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST));
    if ( pl == nullptr ) return;

    std::string name = pl->GetItemName(pl->GetSelect());
    name = name.substr(0, name.find_first_of("\t"));

    if ( name.empty() ) return;

    if ( name == ".." ) // parent folder
    {
        size_t pos = m_subDirPath.find_last_of("/");
        if (pos == std::string::npos)
            m_subDirPath.clear();
        else
            m_subDirPath.resize(pos);
    }
    else if ( DirectoryExists(name) )
    {
        if (!m_subDirPath.empty()) m_subDirPath += "/";
        m_subDirPath += name;
    }

    PopulateList();
    UpdatePathLabel();
    EventType type;
    type = m_newFolderMode ? EVENT_DIALOG_EDIT2 : EVENT_DIALOG_EDIT;

    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(type));
    if ( pe != nullptr ) SearchList(pe->GetText(999), m_newFolderMode);
}

bool CFileDialog::CheckFilename(const std::string& name)
{
    if ( name.empty() || name[0] == '.' ) return false;

    size_t namelen = name.length();
    if ( m_extension.empty() && m_extlist.empty() ) return true;      // no required extension?

    for ( std::string ext : m_extlist ) // allowed extensions?
    {
        size_t extlen = ext.length();
        if ( namelen <= extlen ) continue;
        if ( name == ext ) continue;
        if ( ext == name.substr(namelen-extlen, extlen) ) return true;
    }

    if ( !m_extension.empty() ) // default extension?
    {
        size_t extlen = m_extension.length();
        if ( namelen <= extlen ) return false;
        if ( name == m_extension ) return false;
        if ( m_extension == name.substr(namelen-extlen, extlen)) return true;
    }
    return false;
}

bool CFileDialog::ActionOpen()
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return false;

    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT));
    if ( pe == nullptr ) return false;
    std::string filename = pe->GetText(100);
    if ( filename.empty() ) return false;

    if ( !CheckFilename(filename) ) // add default extension ?
    {
        if ( !m_extension.empty() ) filename += m_extension;
        if ( !CheckFilename(filename) ) return false; // file name is ok ?
    }

    SearchDirectory(true);
    SetFilename(filename);
    SetFilenameField(pe, filename);
    pe->SetCursor(999, 0);  // select all
    pw->SetFocus(pe);

    return true;
}

bool CFileDialog::ActionSave(bool checkFileExist)
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent));
    if ( pw == nullptr ) return false;

    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT));
    if ( pe == nullptr ) return false;
    std::string filename = pe->GetText(100);
    if ( filename.empty() ) return false;

    if ( !CheckFilename(filename) ) // add default extension ?
    {
        if ( !m_extension.empty() ) filename += m_extension;
        if ( !CheckFilename(filename) ) return false; // file name is ok ?
    }

    SearchDirectory(true);

    if ( checkFileExist )
    {
        if (CResourceManager::Exists(SearchDirectory(false)+filename))
        {
            if ( !StartAskOverwrite(filename) ) StopAskOverwrite();
            return false;
        }
    }

    SetFilename(filename);
    SetFilenameField(pe, filename);
    pe->SetCursor(999, 0);  // select all
    pw->SetFocus(pe);

    return true;
}

bool CFileDialog::StartAskOverwrite(const std::string& name)
{
    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent)); // dialog window
    if ( pw == nullptr ) return false;

    // disable controls
    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT)); // filename edit box
    if ( pe == nullptr ) return false;
    pe->SetState(STATE_ENABLE, false);

    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_NEWDIR)); // new folder button
    if ( pb == nullptr ) return false;
    pb->SetState(STATE_ENABLE, false);

    CList* pli = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST)); // file list
    if ( pli == nullptr ) return false;
    pli->SetState(STATE_ENABLE, false);

    if ( m_usePublicPrivate )
    {
        CCheck* pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK1)); // private check box
        if ( pc == nullptr ) return false;
        pc->SetState(STATE_ENABLE, false);

        pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK2)); // public check box
        if ( pc == nullptr ) return false;
        pc->SetState(STATE_ENABLE, false);
    }

    pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_OK));    // open/save button
    if ( pb == nullptr ) return false;
    pb->SetState(STATE_ENABLE, false);

    // show overwrite controls
    CGroup* pg = static_cast< CGroup* >(pw->SearchControl(EVENT_DIALOG_GROUP1)); // "Overwrite ?" box
    if ( pg == nullptr ) return false;
    pg->SetState(STATE_VISIBLE | STATE_ENABLE);

    CLabel* pla = static_cast< CLabel* >(pw->SearchControl(EVENT_LABEL0)); // "Overwrite ?" label
    if ( pla == nullptr ) return false;
    pla->SetState(STATE_VISIBLE | STATE_ENABLE);

    pla = static_cast< CLabel* >(pw->SearchControl(EVENT_LABEL1));       // filename label
    if ( pla == nullptr ) return false;
    pla->SetState(STATE_VISIBLE | STATE_ENABLE);
    pla->SetName(name);

    pb = static_cast< CButton* >(pw->SearchControl(EVENT_BUTTON_CANCEL)); // Cancel button
    if ( pb == nullptr ) return false;
    pb->SetState(STATE_VISIBLE | STATE_ENABLE);

    pb = static_cast< CButton* >(pw->SearchControl(EVENT_BUTTON_OK));    // Ok button
    if ( pb == nullptr ) return false;
    pb->SetState(STATE_VISIBLE | STATE_ENABLE);

    m_askOverwriteMode = true;

    AdjustDialog();

    return true;
}

bool CFileDialog::StopAskOverwrite()
{
    m_askOverwriteMode = false;

    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent)); // dialog window
    if ( pw == nullptr ) return false;

    // hide overwrite controls
    CGroup* pg = static_cast< CGroup* >(pw->SearchControl(EVENT_DIALOG_GROUP1)); // "Overwrite ?" box
    if ( pg != nullptr ) pg->ClearState(STATE_VISIBLE | STATE_ENABLE);

    CLabel* pla = static_cast< CLabel* >(pw->SearchControl(EVENT_LABEL0)); // "Overwrite ?" label
    if ( pla != nullptr ) pla->ClearState(STATE_VISIBLE | STATE_ENABLE);

    pla = static_cast< CLabel* >(pw->SearchControl(EVENT_LABEL1));       // filename label
    if ( pla != nullptr ) pla->ClearState(STATE_VISIBLE | STATE_ENABLE);

    CButton* pb = static_cast< CButton* >(pw->SearchControl(EVENT_BUTTON_CANCEL)); // Cancel button
    if ( pb != nullptr ) pb->ClearState(STATE_VISIBLE | STATE_ENABLE);

    pb = static_cast< CButton* >(pw->SearchControl(EVENT_BUTTON_OK));    // Ok button
    if ( pb != nullptr ) pb->ClearState(STATE_VISIBLE | STATE_ENABLE);

    // enable other controls
    CEdit* pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT)); // filename edit box
    if ( pe != nullptr )
    {
        pe->SetState(STATE_ENABLE, true);
        pe->SetCursor(999, 0);
        pw->SetFocus(pe);
    }

    pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_NEWDIR)); // new folder button
    if ( pb != nullptr ) pb->SetState(STATE_ENABLE, true);

    CList* pli = static_cast< CList* >(pw->SearchControl(EVENT_DIALOG_LIST)); // file list
    if ( pli != nullptr ) pli->SetState(STATE_ENABLE, true);

    if ( m_usePublicPrivate )
    {
        CCheck* pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK1)); // private check box
        if ( pc != nullptr ) pc->SetState(STATE_ENABLE, true);

        pc = static_cast< CCheck* >(pw->SearchControl(EVENT_DIALOG_CHECK2)); // public check box
        if ( pc != nullptr ) pc->SetState(STATE_ENABLE, true);
    }

    pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_OK));    // open/save button
    if ( pb != nullptr ) pb->SetState(STATE_ENABLE, true);

    return true;
}

bool CFileDialog::EventAskOverwrite(const Event &event)
{
    if ( event.type == EVENT_KEY_DOWN && event.GetData<KeyEventData>()->key == KEY(ESCAPE) )
    {
        return StopAskOverwrite();
    }

    if ( event.type == EVENT_BUTTON_CANCEL )
    {
        return StopAskOverwrite();
    }

    if ( event.type == EVENT_BUTTON_OK ||
         (event.type == EVENT_KEY_DOWN && event.GetData<KeyEventData>()->key == KEY(RETURN)) )
    {
        if ( ActionSave() )
        {
            m_eventQueue->AddEvent(Event(EVENT_DIALOG_ACTION));
        }

        return StopAskOverwrite();
    }

    CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(m_windowEvent)); // dialog window
    if ( pw == nullptr ) return false;

    if ( event.type == EVENT_DIALOG_CANCEL || event.type == pw->GetEventTypeClose() )
    {
        m_eventQueue->AddEvent(Event(EVENT_DIALOG_STOP));
        return true;
    }

    return true;
}


} // namespace Ui