/* * 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 #include #include 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(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(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((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()->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()->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()->key == KEY(ESCAPE)) return StopNewFolderMode(true); if (event.GetData()->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()->key == KEY(ESCAPE)) { m_eventQueue->AddEvent(Event(EVENT_DIALOG_STOP)); } if (event.GetData()->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 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 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()->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()->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