colobot/colobot-base/ui/filedialog.cpp

1422 lines
43 KiB
C++

/*
* 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