From eaef2ce48a36a786dc60248dcc5e746865570b2d Mon Sep 17 00:00:00 2001 From: oy Date: Tue, 22 Mar 2011 00:31:42 +0100 Subject: [PATCH] added notification for unsaved map data and a confirmation for overwriting an existing map in the editor. Closes #115 --- data/languages/bosnian.txt | 3 + data/languages/czech.txt | 3 + data/languages/dutch.txt | 3 + data/languages/finnish.txt | 3 + data/languages/french.txt | 3 + data/languages/german.txt | 3 + data/languages/italian.txt | 3 + data/languages/polish.txt | 3 + data/languages/portuguese.txt | 3 + data/languages/romanian.txt | 3 + data/languages/russian.txt | 3 + data/languages/serbian.txt | 3 + data/languages/spanish.txt | 3 + data/languages/swedish.txt | 3 + data/languages/ukrainian.txt | 3 + src/engine/editor.h | 1 + src/game/client/components/menus.cpp | 17 +++- src/game/client/gameclient.cpp | 2 + src/game/client/gameclient.h | 2 + src/game/editor/ed_editor.cpp | 127 +++++++++++++++++++++++---- src/game/editor/ed_editor.h | 32 ++++++- src/game/editor/ed_layer_quads.cpp | 7 +- src/game/editor/ed_layer_tiles.cpp | 6 +- src/game/editor/ed_popups.cpp | 80 ++++++++++++++++- 24 files changed, 287 insertions(+), 32 deletions(-) diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt index 45724f941..86585c251 100644 --- a/data/languages/bosnian.txt +++ b/data/languages/bosnian.txt @@ -594,6 +594,9 @@ Stop record The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/data/languages/czech.txt b/data/languages/czech.txt index d6f63c637..3846f22fd 100644 --- a/data/languages/czech.txt +++ b/data/languages/czech.txt @@ -588,6 +588,9 @@ Sudden Death The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Time limit: %d min == diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt index 7a5fb2f88..bec75c5d4 100644 --- a/data/languages/dutch.txt +++ b/data/languages/dutch.txt @@ -600,6 +600,9 @@ Server filter Size: == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt index 59823d544..a5ba9bd72 100644 --- a/data/languages/finnish.txt +++ b/data/languages/finnish.txt @@ -591,6 +591,9 @@ Stop record The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Time limit: %d min == diff --git a/data/languages/french.txt b/data/languages/french.txt index 8e370b1ff..2bb74231e 100644 --- a/data/languages/french.txt +++ b/data/languages/french.txt @@ -597,6 +597,9 @@ Sound error The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/data/languages/german.txt b/data/languages/german.txt index 6f2798f49..540fa3993 100644 --- a/data/languages/german.txt +++ b/data/languages/german.txt @@ -600,6 +600,9 @@ Server filter Size: == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/data/languages/italian.txt b/data/languages/italian.txt index a7e92e77c..6fd6a9f66 100644 --- a/data/languages/italian.txt +++ b/data/languages/italian.txt @@ -597,6 +597,9 @@ Sound error Sudden Death == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/data/languages/polish.txt b/data/languages/polish.txt index fd7bbce5a..1270fe657 100644 --- a/data/languages/polish.txt +++ b/data/languages/polish.txt @@ -591,6 +591,9 @@ Stop record The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Time limit: %d min == diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt index 914e42839..4231ef275 100644 --- a/data/languages/portuguese.txt +++ b/data/languages/portuguese.txt @@ -591,6 +591,9 @@ Stop record The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Time limit: %d min == diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt index 484d40eb7..c059809de 100644 --- a/data/languages/romanian.txt +++ b/data/languages/romanian.txt @@ -600,6 +600,9 @@ Server filter Size: == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/data/languages/russian.txt b/data/languages/russian.txt index 8e7c7a4d3..d976de911 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -591,6 +591,9 @@ Stop record The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Time limit: %d min == diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index bf61f7c2a..21607a5d8 100644 --- a/data/languages/serbian.txt +++ b/data/languages/serbian.txt @@ -591,6 +591,9 @@ Stop record The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Time limit: %d min == diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt index 107eb10a0..0c0339eff 100644 --- a/data/languages/spanish.txt +++ b/data/languages/spanish.txt @@ -600,6 +600,9 @@ Server filter Size: == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index e2cb41400..b3ec1c329 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -585,6 +585,9 @@ Sudden Death The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Time limit: %d min == diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt index 7ec29d08a..af94f4c30 100644 --- a/data/languages/ukrainian.txt +++ b/data/languages/ukrainian.txt @@ -594,6 +594,9 @@ Sound error The audio device couldn't be initialised. == +There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway? +== + Type: == diff --git a/src/engine/editor.h b/src/engine/editor.h index 154dc47f7..1cafea046 100644 --- a/src/engine/editor.h +++ b/src/engine/editor.h @@ -12,6 +12,7 @@ public: virtual ~IEditor() {} virtual void Init() = 0; virtual void UpdateAndRender() = 0; + virtual bool HasUnsavedData() = 0; }; extern IEditor *CreateEditor(); diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 415ff6469..cd8b77606 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -9,11 +9,12 @@ #include "menus.h" #include "skins.h" +#include #include -#include -#include #include +#include #include +#include #include #include @@ -889,6 +890,7 @@ int CMenus::Render() { pTitle = Localize("Quit"); pExtraText = Localize("Are you sure that you want to quit?"); + ExtraAlign = -1; } else if(m_Popup == POPUP_FIRST_LAUNCH) { @@ -923,10 +925,17 @@ int CMenus::Render() CUIRect Yes, No; Box.HSplitBottom(20.f, &Box, &Part); Box.HSplitBottom(24.f, &Box, &Part); + + // additional info + Box.HSplitTop(10.0f, 0, &Box); + Box.VMargin(20.f/UI()->Scale(), &Box); + if(m_pClient->Editor()->HasUnsavedData()) + UI()->DoLabelScaled(&Box, Localize("There's an unsaved map in the editor, you might want to save it before you quit the game.\nQuit anyway?"), + 20.f, -1, Part.w); + + // buttons Part.VMargin(80.0f, &Part); - Part.VSplitMid(&No, &Yes); - Yes.VMargin(20.0f, &Yes); No.VMargin(20.0f, &No); diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 20fad034e..a7e9e66f0 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1,5 +1,6 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include #include #include #include @@ -113,6 +114,7 @@ void CGameClient::OnConsoleInit() m_pDemoPlayer = Kernel()->RequestInterface(); m_pDemoRecorder = Kernel()->RequestInterface(); m_pServerBrowser = Kernel()->RequestInterface(); + m_pEditor = Kernel()->RequestInterface(); // setup pointers m_pBinds = &::gs_Binds; diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 0c8998050..e807592fc 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -42,6 +42,7 @@ class CGameClient : public IGameClient class IDemoPlayer *m_pDemoPlayer; class IDemoRecorder *m_pDemoRecorder; class IServerBrowser *m_pServerBrowser; + class IEditor *m_pEditor; CLayers m_Layers; class CCollision m_Collision; @@ -78,6 +79,7 @@ public: class CRenderTools *RenderTools() { return &m_RenderTools; } class CLayers *Layers() { return &m_Layers; }; class CCollision *Collision() { return &m_Collision; }; + class IEditor *Editor() { return m_pEditor; } int NetobjNumCorrections() { return m_NetObjHandler.NumObjCorrections(); } const char *NetobjCorrectedOn() { return m_NetObjHandler.CorrectedObjOn(); } diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp index 283ded425..c6dfa66c8 100644 --- a/src/game/editor/ed_editor.cpp +++ b/src/game/editor/ed_editor.cpp @@ -119,11 +119,18 @@ void CLayerGroup::Render() pGraphics->ClipDisable(); } +void CLayerGroup::AddLayer(CLayer *l) +{ + m_pMap->m_Modified = true; + m_lLayers.add(l); +} + void CLayerGroup::DeleteLayer(int Index) { if(Index < 0 || Index >= m_lLayers.size()) return; delete m_lLayers[Index]; m_lLayers.remove_index(Index); + m_pMap->m_Modified = true; } void CLayerGroup::GetSize(float *w, float *h) @@ -144,6 +151,7 @@ int CLayerGroup::SwapLayers(int Index0, int Index1) if(Index0 < 0 || Index0 >= m_lLayers.size()) return Index0; if(Index1 < 0 || Index1 >= m_lLayers.size()) return Index0; if(Index0 == Index1) return Index0; + m_pMap->m_Modified = true; swap(m_lLayers[Index0], m_lLayers[Index1]); return Index1; } @@ -568,7 +576,7 @@ CQuad *CEditor::GetSelectedQuad() return 0; } -static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser) +void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUser) { CEditor *pEditor = (CEditor*)pUser; if(pEditor->Load(pFileName, StorageType)) @@ -577,9 +585,10 @@ static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser) pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder; pEditor->SortImages(); pEditor->m_Dialog = DIALOG_NONE; + pEditor->m_Map.m_Modified = false; } } -static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser) +void CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pUser) { CEditor *pEditor = (CEditor*)pUser; if(pEditor->Append(pFileName, StorageType)) @@ -589,7 +598,7 @@ static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUse pEditor->m_Dialog = DIALOG_NONE; } -static void CallbackSaveMap(const char *pFileName, int StorageType, void *pUser) +void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser) { CEditor *pEditor = static_cast(pUser); char aBuf[1024]; @@ -605,6 +614,7 @@ static void CallbackSaveMap(const char *pFileName, int StorageType, void *pUser) { str_copy(pEditor->m_aFileName, pFileName, sizeof(pEditor->m_aFileName)); pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder; + pEditor->m_Map.m_Modified = false; } pEditor->m_Dialog = DIALOG_NONE; @@ -622,13 +632,25 @@ void CEditor::DoToolbar(CUIRect ToolBar) // ctrl+o to open if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) - InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, this); + { + if(HasUnsavedData()) + { + m_PopupEventType = POPEVENT_LOAD; + m_PopupEventActivated = true; + } + else + InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, this); + } // ctrl+s to save if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) { if(m_aFileName[0] && m_ValidSaveFilename) - CallbackSaveMap(m_aFileName, IStorage::TYPE_SAVE, this); + { + str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName)); + m_PopupEventType = POPEVENT_SAVE; + m_PopupEventActivated = true; + } else InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, this); } @@ -2369,10 +2391,23 @@ void CEditor::RenderFileDialog() } else // file { - char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "%s/%s", m_pFileDialogPath, IsDir ? m_FileList[m_FilesSelectedIndex].m_aFilename : m_aFileDialogFileName); - if(m_pfnFileDialogFunc) - m_pfnFileDialogFunc(aBuf, m_FilesSelectedIndex >= 0 ? m_FileList[m_FilesSelectedIndex].m_StorageType : m_FileDialogStorageType, m_pFileDialogUser); + str_format(m_aFileSaveName, sizeof(m_aFileSaveName), "%s/%s", m_pFileDialogPath, m_aFileDialogFileName); + if(!str_comp(m_pFileDialogButtonText, "Save")) + { + IOHANDLE File = Storage()->OpenFile(m_aFileSaveName, IOFLAG_READ, IStorage::TYPE_SAVE); + if(File) + { + io_close(File); + m_PopupEventType = POPEVENT_SAVE; + m_PopupEventActivated = true; + } + else + if(m_pfnFileDialogFunc) + m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_FileList[m_FilesSelectedIndex].m_StorageType : m_FileDialogStorageType, m_pFileDialogUser); + } + else + if(m_pfnFileDialogFunc) + m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_FileList[m_FilesSelectedIndex].m_StorageType : m_FileDialogStorageType, m_pFileDialogUser); } } @@ -2508,13 +2543,19 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) ToolBar.VSplitRight(50.0f, &ToolBar, &Button); static int s_New4dButton = 0; if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope")) + { + m_Map.m_Modified = true; pNewEnv = m_Map.NewEnvelope(4); + } ToolBar.VSplitRight(5.0f, &ToolBar, &Button); ToolBar.VSplitRight(50.0f, &ToolBar, &Button); static int s_New2dButton = 0; if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new pos envelope")) + { + m_Map.m_Modified = true; pNewEnv = m_Map.NewEnvelope(3); + } // Delete button if(m_SelectedEnvelope >= 0) @@ -2524,6 +2565,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) static int s_DelButton = 0; if(DoButton_Editor(&s_DelButton, "Delete", 0, &Button, 0, "Delete this envelope")) { + m_Map.m_Modified = true; m_Map.DeleteEnvelope(m_SelectedEnvelope); if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size()) m_SelectedEnvelope = m_Map.m_lEnvelopes.size()-1; @@ -2571,7 +2613,8 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) ToolBar.VSplitLeft(80.0f, &Button, &ToolBar); static int s_NameBox = 0; - DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f); + if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f)) + m_Map.m_Modified = true; } } @@ -2659,6 +2702,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) pEnvelope->AddPoint(Time, f2fx(aChannels[0]), f2fx(aChannels[1]), f2fx(aChannels[2]), f2fx(aChannels[3])); + m_Map.m_Modified = true; } m_pTooltip = "Press right mouse button to create a new point"; @@ -2825,6 +2869,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i+1].m_Time - 1; } } + m_Map.m_Modified = true; } ColorMod = 100.0f; @@ -2841,7 +2886,10 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) // remove point if(UI()->MouseButtonClicked(1)) + { pEnvelope->m_lPoints.remove_index(i); + m_Map.m_Modified = true; + } ColorMod = 100.0f; Graphics()->SetColor(1,0.75f,0.75f,1); @@ -2882,8 +2930,16 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_NewMapButton, "New", 0, &Slot, 0, "Creates a new map")) { - pEditor->Reset(); - pEditor->m_aFileName[0] = 0; + if(pEditor->HasUnsavedData()) + { + pEditor->m_PopupEventType = POPEVENT_NEW; + pEditor->m_PopupEventActivated = true; + } + else + { + pEditor->Reset(); + pEditor->m_aFileName[0] = 0; + } return 1; } @@ -2891,7 +2947,13 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_OpenButton, "Load", 0, &Slot, 0, "Opens a map for editing")) { - pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, pEditor); + if(pEditor->HasUnsavedData()) + { + pEditor->m_PopupEventType = POPEVENT_LOAD; + pEditor->m_PopupEventActivated = true; + } + else + pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", pEditor->CallbackOpenMap, pEditor); return 1; } @@ -2899,7 +2961,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_AppendButton, "Append", 0, &Slot, 0, "Opens a map and adds everything from that map to the current one")) { - pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Append map", "Append", "maps", "", CallbackAppendMap, pEditor); + pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Append map", "Append", "maps", "", pEditor->CallbackAppendMap, pEditor); return 1; } @@ -2907,10 +2969,14 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_SaveButton, "Save", 0, &Slot, 0, "Saves the current map")) { - if(pEditor->m_aFileName[0] && pEditor->m_ValidSaveFilename) - CallbackSaveMap(pEditor->m_aFileName, IStorage::TYPE_SAVE, pEditor); + if(pEditor->m_aFileName[0] && pEditor->m_ValidSaveFilename) + { + str_copy(pEditor->m_aFileSaveName, pEditor->m_aFileName, sizeof(pEditor->m_aFileSaveName)); + pEditor->m_PopupEventType = POPEVENT_SAVE; + pEditor->m_PopupEventActivated = true; + } else - pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, pEditor); + pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor); return 1; } @@ -2918,7 +2984,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name")) { - pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, pEditor); + pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor); return 1; } @@ -2926,7 +2992,13 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_ExitButton, "Exit", 0, &Slot, 0, "Exits from the editor")) { - g_Config.m_ClEditor = 0; + if(pEditor->HasUnsavedData()) + { + pEditor->m_PopupEventType = POPEVENT_EXIT; + pEditor->m_PopupEventActivated = true; + } + else + g_Config.m_ClEditor = 0; return 1; } @@ -2966,6 +3038,9 @@ void CEditor::Render() CUIRect View = *UI()->Screen(); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); + float Width = View.w; + float Height = View.h; + // reset tip m_pTooltip = 0; @@ -3048,6 +3123,13 @@ void CEditor::Render() RenderFileDialog(); } + if(m_PopupEventActivated) + { + static int s_PopupID = 0; + UiInvokePopupMenu(&s_PopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupEvent); + m_PopupEventActivated = false; + } + UiDoPopupMenu(); @@ -3121,6 +3203,8 @@ void CEditor::Reset(bool CreateDefault) m_MouseDeltaY = 0; m_MouseDeltaWx = 0; m_MouseDeltaWy = 0; + + m_Map.m_Modified = false; } void CEditorMap::DeleteEnvelope(int Index) @@ -3128,6 +3212,8 @@ void CEditorMap::DeleteEnvelope(int Index) if(Index < 0 || Index >= m_lEnvelopes.size()) return; + m_Modified = true; + // fix links between envelopes and quads for(int i = 0; i < m_lGroups.size(); ++i) for(int j = 0; j < m_lGroups[i]->m_lLayers.size(); ++j) @@ -3174,6 +3260,8 @@ void CEditorMap::Clean() m_pGameLayer = 0x0; m_pGameGroup = 0x0; + + m_Modified = false; } void CEditorMap::CreateDefault(int EntitiesTexture) @@ -3230,6 +3318,7 @@ void CEditor::Init() m_Brush.m_pMap = &m_Map; Reset(); + m_Map.m_Modified = false; } void CEditor::DoMapBorder() diff --git a/src/game/editor/ed_editor.h b/src/game/editor/ed_editor.h index 7e96e5c85..9d220bd63 100644 --- a/src/game/editor/ed_editor.h +++ b/src/game/editor/ed_editor.h @@ -209,10 +209,7 @@ public: m_lLayers.delete_all(); } - void AddLayer(CLayer *l) - { - m_lLayers.add(l); - } + void AddLayer(CLayer *l); void ModifyImageIndex(INDEX_MODIFY_FUNC Func) { @@ -260,6 +257,7 @@ class CEditorMap void MakeGameLayer(CLayer *pLayer); public: CEditor *m_pEditor; + bool m_Modified; CEditorMap() { @@ -275,6 +273,7 @@ public: CEnvelope *NewEnvelope(int Channels) { + m_Modified = true; CEnvelope *e = new CEnvelope(Channels); m_lEnvelopes.add(e); return e; @@ -284,6 +283,7 @@ public: CLayerGroup *NewGroup() { + m_Modified = true; CLayerGroup *g = new CLayerGroup; g->m_pMap = this; m_lGroups.add(g); @@ -295,6 +295,7 @@ public: if(Index0 < 0 || Index0 >= m_lGroups.size()) return Index0; if(Index1 < 0 || Index1 >= m_lGroups.size()) return Index0; if(Index0 == Index1) return Index0; + m_Modified = true; swap(m_lGroups[Index0], m_lGroups[Index1]); return Index1; } @@ -302,18 +303,21 @@ public: void DeleteGroup(int Index) { if(Index < 0 || Index >= m_lGroups.size()) return; + m_Modified = true; delete m_lGroups[Index]; m_lGroups.remove_index(Index); } void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc) { + m_Modified = true; for(int i = 0; i < m_lGroups.size(); i++) m_lGroups[i]->ModifyImageIndex(pfnFunc); } void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) { + m_Modified = true; for(int i = 0; i < m_lGroups.size(); i++) m_lGroups[i]->ModifyEnvelopeIndex(pfnFunc); } @@ -467,7 +471,10 @@ public: m_pTooltip = 0; m_aFileName[0] = 0; + m_aFileSaveName[0] = 0; m_ValidSaveFilename = false; + + m_PopupEventActivated = false; m_FileDialogStorageType = 0; m_pFileDialogTitle = 0; @@ -520,6 +527,7 @@ public: virtual void Init(); virtual void UpdateAndRender(); + virtual bool HasUnsavedData() { return m_Map.m_Modified; } void FilelistPopulate(int StorageType); void InvokeFileDialog(int StorageType, int FileType, const char *pTitle, const char *pButtonText, @@ -544,8 +552,20 @@ public: const char *m_pTooltip; char m_aFileName[512]; + char m_aFileSaveName[512]; bool m_ValidSaveFilename; + enum + { + POPEVENT_EXIT=0, + POPEVENT_LOAD, + POPEVENT_NEW, + POPEVENT_SAVE, + }; + + int m_PopupEventType; + int m_PopupEventActivated; + enum { FILETYPE_MAP, @@ -658,11 +678,15 @@ public: static int PopupQuad(CEditor *pEditor, CUIRect View); static int PopupPoint(CEditor *pEditor, CUIRect View); static int PopupNewFolder(CEditor *pEditor, CUIRect View); + static int PopupEvent(CEditor *pEditor, CUIRect View); static int PopupSelectImage(CEditor *pEditor, CUIRect View); static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View); static int PopupImage(CEditor *pEditor, CUIRect View); static int PopupMenuFile(CEditor *pEditor, CUIRect View); + static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser); + static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser); + static void CallbackSaveMap(const char *pFileName, int StorageType, void *pUser); void PopupSelectImageInvoke(int Current, float x, float y); int PopupSelectImageResult(); diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/ed_layer_quads.cpp index c1b36661a..4f2e468c6 100644 --- a/src/game/editor/ed_layer_quads.cpp +++ b/src/game/editor/ed_layer_quads.cpp @@ -50,6 +50,8 @@ void CLayerQuads::Render() CQuad *CLayerQuads::NewQuad() { + m_pEditor->m_Map.m_Modified = true; + CQuad *q = &m_lQuads[m_lQuads.add(CQuad())]; q->m_PosEnv = -1; @@ -158,6 +160,7 @@ void CLayerQuads::BrushPlace(CLayer *pBrush, float wx, float wy) m_lQuads.add(n); } + m_pEditor->m_Map.m_Modified = true; } void CLayerQuads::BrushFlipX() @@ -229,7 +232,9 @@ int CLayerQuads::RenderProperties(CUIRect *pToolBox) static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; - int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); + int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); + if(Prop != -1) + m_pEditor->m_Map.m_Modified = true; if(Prop == PROP_IMAGE) { diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/ed_layer_tiles.cpp index d679512d1..dcbb0afea 100644 --- a/src/game/editor/ed_layer_tiles.cpp +++ b/src/game/editor/ed_layer_tiles.cpp @@ -179,6 +179,7 @@ void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]; } } + m_pEditor->m_Map.m_Modified = true; } void CLayerTiles::BrushDraw(CLayer *pBrush, float wx, float wy) @@ -201,6 +202,7 @@ void CLayerTiles::BrushDraw(CLayer *pBrush, float wx, float wy) m_pTiles[fy*m_Width+fx] = l->m_pTiles[y*l->m_Width+x]; } + m_pEditor->m_Map.m_Modified = true; } void CLayerTiles::BrushFlipX() @@ -416,7 +418,9 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; - int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); + int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); + if(Prop != -1) + m_pEditor->m_Map.m_Modified = true; if(Prop == PROP_WIDTH && NewVal > 1) Resize(NewVal, m_Height); diff --git a/src/game/editor/ed_popups.cpp b/src/game/editor/ed_popups.cpp index dc239cc95..98b0039a5 100644 --- a/src/game/editor/ed_popups.cpp +++ b/src/game/editor/ed_popups.cpp @@ -127,7 +127,10 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) } if(!Found) + { gl->m_pTiles[y*gl->m_Width+x].m_Index = TILE_AIR; + pEditor->m_Map.m_Modified = true; + } } return 1; @@ -198,6 +201,9 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) aProps[PROP_POS_X].m_pName = 0; int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + if(Prop != -1) + pEditor->m_Map.m_Modified = true; + if(Prop == PROP_ORDER) pEditor->m_SelectedGroup = pEditor->m_Map.SwapGroups(pEditor->m_SelectedGroup, NewVal); @@ -261,7 +267,9 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; - int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + if(Prop != -1) + pEditor->m_Map.m_Modified = true; if(Prop == PROP_ORDER) pEditor->m_SelectedLayer = pCurrentGroup->SwapLayers(pEditor->m_SelectedLayer, NewVal); @@ -299,6 +307,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) CLayerQuads *pLayer = (CLayerQuads *)pEditor->GetSelectedLayerType(0, LAYERTYPE_QUADS); if(pLayer) { + pEditor->m_Map.m_Modified = true; pLayer->m_lQuads.remove_index(pEditor->m_SelectedQuad); pEditor->m_SelectedQuad--; } @@ -332,6 +341,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) pQuad->m_aPoints[1].x = Right; pQuad->m_aPoints[1].y = Top; pQuad->m_aPoints[2].x = Left; pQuad->m_aPoints[2].y = Top+Height; pQuad->m_aPoints[3].x = Right; pQuad->m_aPoints[3].y = Top+Height; + pEditor->m_Map.m_Modified = true; return 1; } View.HSplitBottom(6.0f, &View, &Button); @@ -360,6 +370,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) pQuad->m_aPoints[1].x = Right; pQuad->m_aPoints[1].y = Top; pQuad->m_aPoints[2].x = Left; pQuad->m_aPoints[2].y = Bottom; pQuad->m_aPoints[3].x = Right; pQuad->m_aPoints[3].y = Bottom; + pEditor->m_Map.m_Modified = true; return 1; } @@ -384,7 +395,9 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; - int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + if(Prop != -1) + pEditor->m_Map.m_Modified = true; if(Prop == PROP_POS_ENV) pQuad->m_PosEnv = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); if(Prop == PROP_POS_ENV_OFFSET) pQuad->m_PosEnvOffset = NewVal; @@ -440,6 +453,7 @@ int CEditor::PopupPoint(CEditor *pEditor, CUIRect View) pQuad->m_aColors[v].a = NewVal&0xff; } } + pEditor->m_Map.m_Modified = true; } return 0; @@ -447,7 +461,7 @@ int CEditor::PopupPoint(CEditor *pEditor, CUIRect View) int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) { - CUIRect Label, ButtonBar, Origin = View; + CUIRect Label, ButtonBar; // title View.HSplitTop(10.0f, 0, &View); @@ -514,6 +528,66 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) return 0; } +int CEditor::PopupEvent(CEditor *pEditor, CUIRect View) +{ + CUIRect Label, ButtonBar; + + // title + View.HSplitTop(10.0f, 0, &View); + View.HSplitTop(30.0f, &Label, &View); + if(pEditor->m_PopupEventType == POPEVENT_EXIT) + pEditor->UI()->DoLabel(&Label, "Exit the editor", 20.0f, 0); + else if(pEditor->m_PopupEventType == POPEVENT_LOAD) + pEditor->UI()->DoLabel(&Label, "Load map", 20.0f, 0); + else if(pEditor->m_PopupEventType == POPEVENT_NEW) + pEditor->UI()->DoLabel(&Label, "New map", 20.0f, 0); + else if(pEditor->m_PopupEventType == POPEVENT_SAVE) + pEditor->UI()->DoLabel(&Label, "Save map", 20.0f, 0); + + View.HSplitBottom(10.0f, &View, 0); + View.HSplitBottom(20.0f, &View, &ButtonBar); + + // notification text + View.HSplitTop(30.0f, 0, &View); + View.VMargin(40.0f, &View); + View.HSplitTop(20.0f, &Label, &View); + if(pEditor->m_PopupEventType == POPEVENT_EXIT) + pEditor->UI()->DoLabel(&Label, "The map contains unsaved data, you might want to save it before you exit the editor.\nContinue anyway?", 10.0f, -1, Label.w-10.0f); + else if(pEditor->m_PopupEventType == POPEVENT_LOAD) + pEditor->UI()->DoLabel(&Label, "The map contains unsaved data, you might want to save it before you load a new map.\nContinue anyway?", 10.0f, -1, Label.w-10.0f); + else if(pEditor->m_PopupEventType == POPEVENT_NEW) + pEditor->UI()->DoLabel(&Label, "The map contains unsaved data, you might want to save it before you create a new map.\nContinue anyway?", 10.0f, -1, Label.w-10.0f); + else if(pEditor->m_PopupEventType == POPEVENT_SAVE) + pEditor->UI()->DoLabel(&Label, "The file already exists.\nDo you want to overwrite the map?", 10.0f, -1); + + // button bar + ButtonBar.VSplitLeft(30.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(110.0f, &Label, &ButtonBar); + static int s_OkButton = 0; + if(pEditor->DoButton_Editor(&s_OkButton, "Ok", 0, &Label, 0, 0)) + { + if(pEditor->m_PopupEventType == POPEVENT_EXIT) + g_Config.m_ClEditor = 0; + else if(pEditor->m_PopupEventType == POPEVENT_LOAD) + pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", pEditor->CallbackOpenMap, pEditor); + else if(pEditor->m_PopupEventType == POPEVENT_NEW) + { + pEditor->Reset(); + pEditor->m_aFileName[0] = 0; + } + else if(pEditor->m_PopupEventType == POPEVENT_SAVE) + pEditor->CallbackSaveMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor); + return 1; + } + ButtonBar.VSplitRight(30.0f, &ButtonBar, 0); + ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label); + static int s_AbortButton = 0; + if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0)) + return 1; + + return 0; +} + static int g_SelectImageSelected = -100; static int g_SelectImageCurrent = -100;