about summary refs log tree commit diff
path: root/src/game/editor
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/editor')
-rw-r--r--src/game/editor/ed_editor.cpp127
-rw-r--r--src/game/editor/ed_editor.h32
-rw-r--r--src/game/editor/ed_layer_quads.cpp7
-rw-r--r--src/game/editor/ed_layer_tiles.cpp6
-rw-r--r--src/game/editor/ed_popups.cpp80
5 files changed, 224 insertions, 28 deletions
diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp
index 283ded42..c6dfa66c 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<CEditor*>(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 7e96e5c8..9d220bd6 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,10 +552,22 @@ 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,
 		FILETYPE_IMG,
 
@@ -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 c1b36661..4f2e468c 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 d679512d..dcbb0afe 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 dc239cc9..98b0039a 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;