display_name: "The Stormy" href='/cgit.css'/>
summary refs log blame commit diff stats
path: root/tools/mapedit/src/frame.cpp
blob: aad3294be7a92c18dafae782b51616c0a6b54099 (plain) (tree)
1
2
3
4
5
6
7
                  
                            
                        
               
                      
                    
                  





                        

                                        


                    



                     
                  

                     

                 

                 


                     
                    



                      

                       

                         







                        



                         
  

                                          
                                           

                                                     


                                                


                                                        

                                                


                                                

                                                        
                                 

                                                                              


                                                                     
                                                                            


                                                                   

                                                                       

                                                                                        







                                                                           
                                                                              

                   
                                                                                
 

























                                                                                    
                      

                                         
  
                        



                                                     



                                                                          
                              
  




                                                         


                                                           


                                     
                                     
                                     


                      



                                                             
                                                     
                                                         
                                                                
  

                                                                                                                            




                                                           
                                                                 
                               
  
                                                          

                                                                                                               
                                                       
  
                                                                                                                                       
                          
  
                           
                                                                                          
                                                                                                                    
                             
  

                                                                                  
                                                                                          




                                                                                                 


                                                                                                                                       
                                                                                                                   



                                                                                                                                         
                                                                                                                     



                                                                                                                                   
                                                                                                               



                                                                                                                                       
                                                                                                                   








                                                                                



                                                                        
                                                              



                                                                          



































                                                                         


                                              
                         
                                                               









                                                                                           
                                                
   
                                                                         

   

                                                                                  
                                
  

                                                                                                                                                        

                                                                                    









                                                                                     

                                                                                                       





                                                                                              



                                          


                                                         
                                       

                                
  

                                                    
                                                                                           

                                

                                                         
                                              
                                                
                                                                          

                                       
  


                                                                                                

                            
                                                                            

                                                                                     



                                                                                                     
                     
  


                                                   

                     
                          

 
                                              
 
                                           
   
                                                                                                                                                   

                 
                                       
         
                                                                                                        




                                                        


                                                                       

         







                         

                    
               
 





                                           
                                          



                      
                                           


                       
 
                                         
 
             

 
                                          
 
                                                                                                                     




                                                






                                                        

 
                                          
 
                                 
   
                                                                                                  




                                                  


                                                                 
   

 
                                           



               
                                          
 






                                 
 
 
                                                       
 
                                                           


                                                                          
 













































                                                                                                         


                                             























                                                                     




                                              























                                                                    





                                                                       

















                                                                                         



                                                    

                            














                                                   

                                                                   
                  














                                                                                     

 







                                                       











                                                       





















                                          









                                                            

                                                    


                                                                                         
  

                                                                                            
                                                                        


                                                                           
             


                                                                                    




                                                   





                                                                                         





                                                                     

                                                      

                                 

                                                     




                                                     


                                                                                          
  

                                                                                            
                                                                         


                                                                           
             


                                                                                    




                                                    





                                                                                          





                                                                      

                                                      

                                 

                                                     




                                                  


                                                                                       
  

                                                                                            
                                                                      


                                                                           
             


                                                                                    




                                                 





                                                                                       





                                                                   

                                                      

                                 

                                                     




                                                    


                                                                                         
  

                                                                                            
                                                                        


                                                                           
             


                                                                                    




                                                   





                                                                                         





                                                                     

                                                      

                                 

                                                     


      























































                                                                                                                                                                                                                                                                  

                             
                            

 
                                                  


     
                                     
    
                        

                



                                                                                   

               

 
                                             
 
                                                



                                                              
                                                         
 









                                  
 



                                                

































                                                                                           



                           
                                         
                         









                                                                                                  



                                                                                                                                      
  



                                                                                                  
  



                                                                                                       





























                                                                                                                         
 


























                                                          
                                                           
 













































                                                                                                             
#include "frame.h"
#include "mapselect_combo.h"
#include <wx/statline.h>
#include <list>
#include <wx/valgen.h>
#include <exception>
#include <sstream>
#include "widget.h"
#include "tile_widget.h"
#include "panel.h"
#include "map.h"
#include "undo.h"
#include "object.h"

static std::list<wxWindow*> openWindows;

enum {
  MENU_VIEW_ZOOM_IN,
  MENU_VIEW_ZOOM_OUT,
  MENU_FILE_NEW,
  MENU_FILE_OPEN,
  MENU_FILE_SAVE,
  MENU_FILE_CLOSE,
  MENU_MAP_ADD_ROOT,
  MENU_MAP_ADD_CHILD,
  MENU_EDIT_UNDO,
  MENU_EDIT_REDO,
  TOOL_FILE_NEW,
  TOOL_FILE_OPEN,
  TOOL_FILE_SAVE,
  TOOL_MAP_ADD_ROOT,
  TOOL_MAP_ADD_CHILD,
  MAP_EDITOR_WIDGET,
  MAP_EDITOR_NOTEBOOK,
  MAP_EDITOR_TREE,
  MAP_TITLE_TEXTBOX,
  ADD_ENTITY_BUTTON,
  CANCEL_ENTITY_BUTTON,
  SET_STARTPOS_BUTTON,
  CANCEL_STARTPOS_BUTTON,
  LAYOUT_ONE_SPLITTER,
  LAYOUT_THREE_SPLITTER,
  LEFTMAP_TYPE_CHOICE,
  LEFTMAP_MAP_CHOICE,
  RIGHTMAP_TYPE_CHOICE,
  RIGHTMAP_MAP_CHOICE,
  UPMAP_TYPE_CHOICE,
  UPMAP_MAP_CHOICE,
  DOWNMAP_TYPE_CHOICE,
  DOWNMAP_MAP_CHOICE,
  ENTITY_EDITOR,
  ENTITY_PROPERTY_EDITOR,
  PROPERTY_EDITOR
};

wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame)
  EVT_MENU(wxID_EXIT, MapeditFrame::OnQuit)
  EVT_MENU(MENU_VIEW_ZOOM_IN, MapeditFrame::ZoomIn)
  EVT_MENU(MENU_VIEW_ZOOM_OUT, MapeditFrame::ZoomOut)
  EVT_MENU(MENU_FILE_NEW, MapeditFrame::OnNew)
  EVT_MENU(MENU_FILE_OPEN, MapeditFrame::OnOpen)
  EVT_MENU(MENU_FILE_SAVE, MapeditFrame::OnSave)
  EVT_MENU(MENU_FILE_CLOSE, MapeditFrame::OnClose)
  EVT_MENU(MENU_MAP_ADD_ROOT, MapeditFrame::OnAddRoot)
  EVT_MENU(MENU_MAP_ADD_CHILD, MapeditFrame::OnAddChild)
  EVT_MENU(MENU_EDIT_UNDO, MapeditFrame::OnUndo)
  EVT_MENU(MENU_EDIT_REDO, MapeditFrame::OnRedo)
  EVT_TOOL(TOOL_FILE_NEW, MapeditFrame::OnNew)
  EVT_TOOL(TOOL_FILE_OPEN, MapeditFrame::OnOpen)
  EVT_TOOL(TOOL_FILE_SAVE, MapeditFrame::OnSave)
  EVT_TOOL(TOOL_MAP_ADD_ROOT, MapeditFrame::OnAddRoot)
  EVT_TOOL(TOOL_MAP_ADD_CHILD, MapeditFrame::OnAddChild)
  EVT_CLOSE(MapeditFrame::OnExit)
  EVT_NOTEBOOK_PAGE_CHANGED(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChange)
  EVT_NOTEBOOK_PAGE_CHANGING(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChanging)
  EVT_TREE_SEL_CHANGED(MAP_EDITOR_TREE, MapeditFrame::OnDidSelectMap)
  EVT_TREE_BEGIN_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnWillDragMap)
  EVT_TREE_END_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnDidDragMap)
  EVT_TREE_ITEM_RIGHT_CLICK(MAP_EDITOR_TREE, MapeditFrame::OnRightClickTree)
  EVT_TEXT(MAP_TITLE_TEXTBOX, MapeditFrame::OnTitleChange)
  EVT_BUTTON(ADD_ENTITY_BUTTON, MapeditFrame::OnAddEntity)
  EVT_BUTTON(CANCEL_ENTITY_BUTTON, MapeditFrame::OnCancelAddEntity)
  EVT_BUTTON(SET_STARTPOS_BUTTON, MapeditFrame::OnSetStartpos)
  EVT_BUTTON(CANCEL_STARTPOS_BUTTON, MapeditFrame::OnCancelSetStartpos)
  EVT_SPLITTER_SASH_POS_CHANGING(LAYOUT_ONE_SPLITTER, MapeditFrame::OnOneMovingSash)
  EVT_SPLITTER_SASH_POS_CHANGING(LAYOUT_THREE_SPLITTER, MapeditFrame::OnThreeMovingSash)
  EVT_CHOICE(LEFTMAP_TYPE_CHOICE, MapeditFrame::OnSetLeftmapType)
  EVT_CHOICE(RIGHTMAP_TYPE_CHOICE, MapeditFrame::OnSetRightmapType)
  EVT_CHOICE(UPMAP_TYPE_CHOICE, MapeditFrame::OnSetUpmapType)
  EVT_CHOICE(DOWNMAP_TYPE_CHOICE, MapeditFrame::OnSetDownmapType)
  EVT_COMBOBOX_CLOSEUP(LEFTMAP_MAP_CHOICE, MapeditFrame::OnSetLeftmapMap)
  EVT_COMBOBOX_CLOSEUP(RIGHTMAP_MAP_CHOICE, MapeditFrame::OnSetRightmapMap)
  EVT_COMBOBOX_CLOSEUP(UPMAP_MAP_CHOICE, MapeditFrame::OnSetUpmapMap)
  EVT_COMBOBOX_CLOSEUP(DOWNMAP_MAP_CHOICE, MapeditFrame::OnSetDownmapMap)
  EVT_COMMAND(wxID_ANY, EVT_MAP_SELECTED_ENTITY, MapeditFrame::OnSelectEntity)
wxEND_EVENT_TABLE()

MapeditFrame::MapeditFrame(World* world) : wxFrame(NULL, wxID_ANY, "Map Editor")
{
  int screenWidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X);
  int screenHeight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y);
  bool setSize = false;
  wxSize toSetSize = GetSize();
  if (screenWidth > 1280)
  {
    setSize = true;
    toSetSize.SetWidth(1280);
  }
  
  if (screenHeight > 800)
  {
    setSize = true;
    toSetSize.SetHeight(800);
  }
  
  if (setSize)
  {
    SetSize(toSetSize);
    
    int numClosers = openWindows.size() - 1;
    SetPosition({GetPosition().x + numClosers*20, GetPosition().y + numClosers*20});
  } else {
    Maximize();
  }
  
  this->world = world;
  this->world->setParent(this);
  currentMap = this->world->getLastMap();
  
  menuFile = new wxMenu;
  menuFile->Append(MENU_FILE_NEW, "New\tCtrl-N");
  menuFile->Append(MENU_FILE_OPEN, "Open\tCtrl-O");
  menuFile->Append(MENU_FILE_SAVE, "Save\tCtrl-S");
  menuFile->Append(MENU_FILE_CLOSE, "Close\tCtrl-W");
  menuFile->AppendSeparator();
  menuFile->Append(MENU_MAP_ADD_ROOT, "New Map\tCtrl-Alt-N");
  menuFile->Append(MENU_MAP_ADD_CHILD, "New Child Map\tCtrl-Alt-Shift-N");
  menuFile->AppendSeparator();
  menuFile->Append(wxID_EXIT);
  
  menuEdit = new wxMenu;
  menuEdit->Append(MENU_EDIT_UNDO, "Undo\tCtrl-Z");
  menuEdit->Append(MENU_EDIT_REDO, "Redo\tCtrl-Shift-Z");
  UpdateUndoLabels();
  
  wxMenu* menuView = new wxMenu;
  menuView->Append(MENU_VIEW_ZOOM_IN, "Zoom In\tCtrl-+");
  menuView->Append(MENU_VIEW_ZOOM_OUT, "Zoom Out\tCtrl--");

  wxMenuBar* menuBar = new wxMenuBar;
  menuBar->Append(menuFile, "&File");
  menuBar->Append(menuEdit, "&Edit");
  menuBar->Append(menuView, "&View");
  
  SetMenuBar(menuBar);
  
  mapTreePopup = new wxMenu;
  mapTreePopup->Append(MENU_MAP_ADD_ROOT, "Add Sibling Map");
  mapTreePopup->Append(MENU_MAP_ADD_CHILD, "Add Child Map");
  
  // Layout 1: Splitter between map tree and layout 2
  // Layout 2: Non-splitter between layout 3 and notebook
  // Layout 3: Splitter between map editor and properties editor
  
  layout1 = new wxSplitterWindow(this, LAYOUT_ONE_SPLITTER);
  mapTree = new wxTreeCtrl(layout1, MAP_EDITOR_TREE, wxDefaultPosition, wxSize(200, -1), wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS);
  wxTreeItemId mapTreeRoot = mapTree->AddRoot("root");
  populateMapTree(mapTreeRoot, this->world->getRootMaps());
  
  wxPanel* layout2 = new wxPanel(layout1, wxID_ANY);
  
  layout3 = new wxSplitterWindow(layout2, LAYOUT_THREE_SPLITTER);
  layout3->SetSashGravity(1.0);
  
  notebook = new wxNotebook(layout2, MAP_EDITOR_NOTEBOOK);
  
  tileEditor = new TileWidget(notebook, wxID_ANY, 6, 6, wxPoint(0,0), wxSize(TILE_WIDTH*6*6,TILE_HEIGHT*10*6));
  notebook->AddPage(tileEditor, "Tile Chooser", false);
  
  mapEditor = new MapeditWidget(layout3, MAP_EDITOR_WIDGET, currentMap, tileEditor, wxPoint(0,0), wxSize(GAME_WIDTH*2, GAME_HEIGHT*2));
  mapEditor->frame = this;
  
  // Set up property editor
  propertyEditor = new wxPanel(layout3, wxID_ANY);//, wxDefaultPosition, wxSize(-1, 100));
  titleBox = new UndoableTextBox(propertyEditor, MAP_TITLE_TEXTBOX, currentMap->getTitle(), "Edit Map Title", this);
  titleBox->SetMaxLength(40);
  
  wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:");
  
  startposLabel = new wxStaticText(propertyEditor, PROPERTY_EDITOR, "Starting Position:");
  
  setStartposButton = new wxButton(propertyEditor, SET_STARTPOS_BUTTON, "Set Starting Position");
  cancelStartposButton = new wxButton(propertyEditor, CANCEL_STARTPOS_BUTTON, "Cancel");
  cancelStartposButton->Disable();
  
  wxStaticText* leftmapLabel = new wxStaticText(propertyEditor, wxID_ANY, "Leftmap Action:");
  wxChoice* leftmapChoice = new wxChoice(propertyEditor, LEFTMAP_TYPE_CHOICE);
  wxComboCtrl* leftmapCombo = new wxComboCtrl(propertyEditor, LEFTMAP_MAP_CHOICE, "", wxDefaultPosition, wxDefaultSize, wxCB_READONLY);
  leftmapCombo->SetPopupControl(new MapSelectComboPopup(mapTree, currentMap->getAdjacent(Map::MoveDir::Left).map));
  
  wxStaticText* rightmapLabel = new wxStaticText(propertyEditor, wxID_ANY, "Rightmap Action:");
  wxChoice* rightmapChoice = new wxChoice(propertyEditor, RIGHTMAP_TYPE_CHOICE);
  wxComboCtrl* rightmapCombo = new wxComboCtrl(propertyEditor, RIGHTMAP_MAP_CHOICE, "", wxDefaultPosition, wxDefaultSize, wxCB_READONLY);
  rightmapCombo->SetPopupControl(new MapSelectComboPopup(mapTree, currentMap->getAdjacent(Map::MoveDir::Right).map));
  
  wxStaticText* upmapLabel = new wxStaticText(propertyEditor, wxID_ANY, "Upmap Action:");
  wxChoice* upmapChoice = new wxChoice(propertyEditor, UPMAP_TYPE_CHOICE);
  wxComboCtrl* upmapCombo = new wxComboCtrl(propertyEditor, UPMAP_MAP_CHOICE, "", wxDefaultPosition, wxDefaultSize, wxCB_READONLY);
  upmapCombo->SetPopupControl(new MapSelectComboPopup(mapTree, currentMap->getAdjacent(Map::MoveDir::Up).map));
  
  wxStaticText* downmapLabel = new wxStaticText(propertyEditor, wxID_ANY, "Downmap Action:");
  wxChoice* downmapChoice = new wxChoice(propertyEditor, DOWNMAP_TYPE_CHOICE);
  wxComboCtrl* downmapCombo = new wxComboCtrl(propertyEditor, DOWNMAP_MAP_CHOICE, "", wxDefaultPosition, wxDefaultSize, wxCB_READONLY);
  downmapCombo->SetPopupControl(new MapSelectComboPopup(mapTree, currentMap->getAdjacent(Map::MoveDir::Down).map));
  
  for (auto type : Map::listMoveTypes())
  {
    leftmapChoice->Append(Map::stringForMoveType(type), new MoveTypeCtr(type));
    rightmapChoice->Append(Map::stringForMoveType(type), new MoveTypeCtr(type));
    upmapChoice->Append(Map::stringForMoveType(type), new MoveTypeCtr(type));
    downmapChoice->Append(Map::stringForMoveType(type), new MoveTypeCtr(type));
  }
  
  wxBoxSizer* propertySizer = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* propertySizer1 = new wxBoxSizer(wxHORIZONTAL);
  propertySizer1->Add(titleLabel, 0, wxALIGN_RIGHT | wxLEFT, 10);
  propertySizer1->Add(titleBox, 1, wxALIGN_LEFT | wxLEFT | wxRIGHT, 10);
  propertySizer->Add(propertySizer1, 0, wxEXPAND | wxTOP, 10);
  wxBoxSizer* propertySizer2 = new wxBoxSizer(wxHORIZONTAL);
  propertySizer2->Add(startposLabel, 0, wxALIGN_RIGHT | wxLEFT, 10);
  propertySizer2->Add(setStartposButton, 0, wxALIGN_LEFT | wxLEFT, 10);
  propertySizer2->Add(cancelStartposButton, 0, wxALIGN_LEFT | wxLEFT, 10);
  propertySizer->Add(propertySizer2, 0, wxEXPAND | wxTOP, 10);
  
  wxBoxSizer* propertySizer3 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* leftmapSizer = new wxBoxSizer(wxHORIZONTAL);
  leftmapSizer->Add(leftmapLabel, 0, wxALIGN_RIGHT, 0);
  wxBoxSizer* leftmapToolsSizer = new wxBoxSizer(wxVERTICAL);
  leftmapToolsSizer->Add(leftmapChoice, 0, wxEXPAND, 0);
  leftmapToolsSizer->Add(leftmapCombo, 0, wxEXPAND | wxTOP, 10);
  leftmapSizer->Add(leftmapToolsSizer, 1, wxEXPAND | wxLEFT, 10);
  propertySizer3->Add(leftmapSizer, 1, wxALIGN_LEFT | wxLEFT, 10);
  wxBoxSizer* rightmapSizer = new wxBoxSizer(wxHORIZONTAL);
  rightmapSizer->Add(rightmapLabel, 0, wxALIGN_RIGHT, 0);
  wxBoxSizer* rightmapToolsSizer = new wxBoxSizer(wxVERTICAL);
  rightmapToolsSizer->Add(rightmapChoice, 0, wxEXPAND, 0);
  rightmapToolsSizer->Add(rightmapCombo, 0, wxEXPAND | wxTOP, 10);
  rightmapSizer->Add(rightmapToolsSizer, 1, wxEXPAND | wxLEFT, 10);
  propertySizer3->Add(rightmapSizer, 1, wxALIGN_LEFT | wxLEFT, 10);
  propertySizer->Add(propertySizer3, 0, wxEXPAND | wxTOP, 10);
  
  wxBoxSizer* propertySizer4 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* upmapSizer = new wxBoxSizer(wxHORIZONTAL);
  upmapSizer->Add(upmapLabel, 0, wxALIGN_RIGHT, 0);
  wxBoxSizer* upmapToolsSizer = new wxBoxSizer(wxVERTICAL);
  upmapToolsSizer->Add(upmapChoice, 0, wxEXPAND, 0);
  upmapToolsSizer->Add(upmapCombo, 0, wxEXPAND | wxTOP, 10);
  upmapSizer->Add(upmapToolsSizer, 1, wxEXPAND | wxLEFT, 10);
  propertySizer4->Add(upmapSizer, 1, wxALIGN_LEFT | wxLEFT, 10);
  wxBoxSizer* downmapSizer = new wxBoxSizer(wxHORIZONTAL);
  downmapSizer->Add(downmapLabel, 0, wxALIGN_RIGHT, 0);
  wxBoxSizer* downmapToolsSizer = new wxBoxSizer(wxVERTICAL);
  downmapToolsSizer->Add(downmapChoice, 0, wxEXPAND, 0);
  downmapToolsSizer->Add(downmapCombo, 0, wxEXPAND | wxTOP, 10);
  downmapSizer->Add(downmapToolsSizer, 1, wxEXPAND | wxLEFT, 10);
  propertySizer4->Add(downmapSizer, 1, wxALIGN_LEFT | wxLEFT, 10);
  propertySizer->Add(propertySizer4, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
  
  propertyEditor->SetSizer(propertySizer);
  propertySizer->SetSizeHints(propertyEditor);
  
  // Set up entity editor
  wxPanel* entityEditor = new wxPanel(notebook, ENTITY_EDITOR);
  notebook->AddPage(entityEditor, "Entity Manager", false);
  
  wxStaticText* entityHeader = new wxStaticText(entityEditor, wxID_ANY, "Add Entity");
  wxFont font = entityHeader->GetFont();
  font.SetWeight(wxFONTWEIGHT_BOLD);
  entityHeader->SetFont(font);
  
  wxStaticText* entityTypeLabel = new wxStaticText(entityEditor, wxID_ANY, "Entity Type:");
  
  entityTypeBox = new wxChoice(entityEditor, wxID_ANY);
  for (auto& entry : MapObject::getAllObjects())
  {
    entityTypeBox->Append(entry.second.getName(), (void*) &entry.second);
  }
  
  addEntityButton = new wxButton(entityEditor, ADD_ENTITY_BUTTON, "Add Entity");
  cancelEntityButton = new wxButton(entityEditor, CANCEL_ENTITY_BUTTON, "Cancel");
  cancelEntityButton->Disable();
  
  wxStaticText* entityInfoLabel = new wxStaticText(entityEditor, wxID_ANY, "Click and drag an entity to move it.\nRight click an entity to delete it.");
  
  wxPanel* entityPropertyEditor = new wxPanel(entityEditor, ENTITY_PROPERTY_EDITOR);
  
  wxBoxSizer* entitySizer = new wxBoxSizer(wxVERTICAL);
  entitySizer->Add(entityHeader, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
  wxBoxSizer* entitySizer1 = new wxBoxSizer(wxHORIZONTAL);
  entitySizer1->Add(entityTypeLabel, 0, wxALIGN_LEFT | wxRIGHT, 5);
  entitySizer1->Add(entityTypeBox, 1, wxALIGN_LEFT, 0);
  entitySizer->Add(entitySizer1, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
  wxBoxSizer* entitySizer2 = new wxBoxSizer(wxHORIZONTAL);
  entitySizer2->Add(addEntityButton, 1, wxEXPAND | wxRIGHT, 2);
  entitySizer2->Add(cancelEntityButton, 1, wxEXPAND | wxLEFT, 2);
  entitySizer->Add(entitySizer2, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
  entitySizer->Add(new wxStaticLine(entityEditor), 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
  entitySizer->Add(entityInfoLabel, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
  entitySizer->Add(entityPropertyEditor, 1, wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxALL, 5);
  wxBoxSizer* entityPropertySizer = new wxBoxSizer(wxVERTICAL);
  entityPropertySizer->Add(new wxStaticLine(entityPropertyEditor), 1, wxEXPAND, 0);
  entityPropertyEditor->SetSizer(entityPropertySizer);
  entityPropertySizer->SetSizeHints(entityPropertyEditor);
  //entitySizer->Add(entityPropertySizer, 1, wxEXPAND, 0);
  entityEditor->SetSizer(entitySizer);
  entitySizer->SetSizeHints(entityEditor);
  
  // Finish setting up the layouts
  wxBoxSizer* sizer1 = new wxBoxSizer(wxHORIZONTAL);
  sizer1->Add(layout1, 1, wxEXPAND, 0);
  sizer1->Add(mapTree, 0, wxALIGN_TOP | wxALIGN_LEFT, 0);
  sizer1->Add(layout2, 2, wxEXPAND, 0);
  layout1->SetSizer(sizer1);
  sizer1->SetSizeHints(layout1);
  
  wxBoxSizer* sizer2 = new wxBoxSizer(wxHORIZONTAL);
  sizer2->Add(layout3, 1, wxEXPAND, 0);
  sizer2->Add(notebook, 0, wxALIGN_TOP | wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxEXPAND, 2);
  layout2->SetSizer(sizer2);
  sizer2->SetSizeHints(layout2);
  
  wxBoxSizer* splitterSizer = new wxBoxSizer(wxVERTICAL);
  splitterSizer->Add(layout3, 0, wxEXPAND, 0);
  splitterSizer->Add(mapEditor, 1, wxEXPAND, 0);
  splitterSizer->Add(propertyEditor, 0, wxALIGN_BOTTOM | wxALIGN_LEFT, 0);
  layout3->SetSizer(splitterSizer);
  splitterSizer->SetSizeHints(layout3);
  
  layout3->SplitHorizontally(mapEditor, propertyEditor, -propertyEditor->GetSize().GetHeight());
  layout1->SplitVertically(mapTree, layout2, mapTree->GetSize().GetWidth());
  
  // Toolbar time!
  toolbar = CreateToolBar();
  toolbar->AddTool(TOOL_FILE_NEW, "New", wxBitmap(wxImage("res/page.png")));
  toolbar->AddTool(TOOL_FILE_OPEN, "Open", wxBitmap(wxImage("res/folder_page.png")));
  toolbar->AddTool(TOOL_FILE_SAVE, "Save", wxBitmap(wxImage("res/disk.png")));
  toolbar->AddSeparator();
  toolbar->AddTool(TOOL_MAP_ADD_ROOT, "Add Map", wxBitmap(wxImage("res/page_add.png")));
  toolbar->AddTool(TOOL_MAP_ADD_CHILD, "Add Child Map", wxBitmap(wxImage("res/page_white_add.png")));
  toolbar->EnableTool(TOOL_FILE_SAVE, this->world->getDirty());
  toolbar->Realize();
  
  dontSelectMap = true;
  mapTree->SelectItem(currentMap->getTreeItemId());
  dontSelectMap = false;
  
  SetStartposLabel();
  mapEditor->SetUpZoom(2);
}

void MapeditFrame::OnExit(wxCloseEvent& event)
{
  if (event.CanVeto() && world->getDirty())
  {
    switch (wxMessageBox("One or more maps have unsaved changes. Save before closing?", "Please confirm", wxICON_QUESTION|wxYES_NO|wxCANCEL, this))
    {
      case wxYES:
        if (world->getFilename() == "")
        {
          wxFileDialog saveFileDialog(this, "Save world", "", "", "XML files (*.xml)|*.xml", wxFD_SAVE);
          if (saveFileDialog.ShowModal() == wxID_CANCEL)
          {
            return;
          }
    
          world->save(saveFileDialog.GetPath().ToStdString(), mapTree);
        } else {
          world->save(world->getFilename(), mapTree);
        }
  
        break;
        
      case wxCANCEL:
        event.Veto(true);
        return;
    }
  }
  
  *closer = nullptr;
  
  event.Skip();
}

MapeditWidget* MapeditFrame::GetMapEditor()
{
  return mapEditor;
}

void MapeditFrame::ZoomIn(wxCommandEvent&)
{
  mapEditor->ZoomIn();
}

void MapeditFrame::ZoomOut(wxCommandEvent&)
{
  mapEditor->ZoomOut();
}

void MapeditFrame::OnNew(wxCommandEvent&)
{
  NewWorld();
}

void MapeditFrame::OnOpen(wxCommandEvent&)
{
  wxFileDialog openFileDialog(this, "Open world", "", "", "XML files (*.xml)|*.xml", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
  if (openFileDialog.ShowModal() == wxID_CANCEL)
  {
    return;
  }
  
  if (OpenWorld(openFileDialog.GetPath().ToStdString()))
  {
    if (world->getEmpty())
    {
      Close(true);
    }
  }
}

void MapeditFrame::OnSave(wxCommandEvent&)
{
  if (world->getFilename() == "")
  {
    wxFileDialog saveFileDialog(this, "Save world", "", "", "XML files (*.xml)|*.xml", wxFD_SAVE);
    if (saveFileDialog.ShowModal() == wxID_CANCEL)
    {
      return;
    }
    
    world->save(saveFileDialog.GetPath().ToStdString(), mapTree);
  } else {
    world->save(world->getFilename(), mapTree);
  }
}

void MapeditFrame::OnClose(wxCommandEvent&)
{
  Close(false);
}

void MapeditFrame::OnQuit(wxCommandEvent&)
{
  for (auto window : openWindows)
  {
    if (window != nullptr)
    {
      window->Close(false);
    }
  }
}

void MapeditFrame::OnTitleChange(wxCommandEvent& event)
{
  currentMap->setTitle(titleBox->GetValue().ToStdString());
  mapTree->SetItemText(currentMap->getTreeItemId(), titleBox->GetValue());
  
  event.Skip();
}

void MapeditFrame::OnTabChange(wxBookCtrlEvent& event)
{
  switch (event.GetSelection())
  {
    case 0:
      mapEditor->SetEditMode(EditTiles);
      break;
      
    case 1:
      mapEditor->SetEditMode(EditEntities);
      break;
  }
  
  event.Skip();
}

void MapeditFrame::OnTabChanging(wxBookCtrlEvent& event)
{
  if (addingEntity)
  {
    event.Veto();
    return;
  }
  
  event.Skip();
}

void MapeditFrame::OnAddEntity(wxCommandEvent&)
{
  addingEntity = true;
  addEntityButton->Disable();
  cancelEntityButton->Enable();
  
  mapEditor->StartAddingEntity((MapObject*) entityTypeBox->GetClientData(entityTypeBox->GetSelection()));
}

void MapeditFrame::OnCancelAddEntity(wxCommandEvent&)
{
  addingEntity = false;
  addEntityButton->Enable();
  cancelEntityButton->Disable();
  
  mapEditor->CancelAddingEntity();
}

void MapeditFrame::OnAddRoot(wxCommandEvent&)
{
  auto map = world->newMap();
  
  commitAction(std::make_shared<Undoable>("New Map", [=] () {
    map->setHidden(false);
    
    wxTreeItemId sel = mapTree->GetSelection();
    wxTreeItemId par = mapTree->GetItemParent(sel);
    wxTreeItemId node = mapTree->AppendItem(par, map->getTitle());
    map->setTreeItemId(node);
    mapTree->SetItemData(node, new MapPtrCtr(map.get()));
    
    dontSelectMap = true;
    mapTree->SelectItem(node);
    dontSelectMap = false;
  }, [=] () {
    map->setHidden(true);
    
    wxTreeItemId sel = mapTree->GetPrevSibling(map->getTreeItemId());
    mapTree->Delete(map->getTreeItemId());
    
    dontSelectMap = true;
    mapTree->SelectItem(sel);
    dontSelectMap = false;
  }));
  
}

void MapeditFrame::OnAddChild(wxCommandEvent&)
{
  auto map = world->newMap();
  
  commitAction(std::make_shared<Undoable>("New Map", [=] () {
    map->setHidden(false);
    
    wxTreeItemId sel = mapTree->GetSelection();
    wxTreeItemId node = mapTree->AppendItem(sel, map->getTitle());
    map->setTreeItemId(node);
    mapTree->SetItemData(node, new MapPtrCtr(map.get()));
    
    mapTree->Expand(sel);
    
    dontSelectMap = true;
    mapTree->SelectItem(node);
    dontSelectMap = false;
  }, [=] () {
    map->setHidden(true);
    
    wxTreeItemId sel = mapTree->GetItemParent(map->getTreeItemId());
    mapTree->Delete(map->getTreeItemId());
    
    dontSelectMap = true;
    mapTree->SelectItem(sel);
    dontSelectMap = false;
  }));
}

void MapeditFrame::OnDidSelectMap(wxTreeEvent& event)
{
  MapPtrCtr* data = (MapPtrCtr*) mapTree->GetItemData(event.GetItem());
  SelectMap(data->map);
  
  if (!dontSelectMap)
  {
    commitAfter(std::make_shared<Undoable>("Selecting " + data->map->getTitle(), [=] () {
      wxTreeItemId toSelect = event.GetItem();
      dontSelectMap = true;
      mapTree->SelectItem(toSelect);
      dontSelectMap = false;
      SelectMap(data->map);
    }, [=] () {
      wxTreeItemId toSelect = event.GetOldItem();
      MapPtrCtr* oldData = (MapPtrCtr*) mapTree->GetItemData(toSelect);
      dontSelectMap = true;
      mapTree->SelectItem(toSelect);
      dontSelectMap = false;
      SelectMap(oldData->map);
    }));
  }
}

void MapeditFrame::OnWillDragMap(wxTreeEvent& event)
{
  event.Allow();
  dragMap = event.GetItem();
}

void MapeditFrame::OnDidDragMap(wxTreeEvent& event)
{
  if (!dragMap.IsOk())
  {
    return;
  }
  
  wxTreeItemId newParent = event.GetItem();
  if (!newParent.IsOk())
  {
    newParent = mapTree->GetRootItem();
  }
  
  wxTreeItemId curParent = mapTree->GetItemParent(event.GetItem());
  wxTreeItemId dragMapCopy = dragMap;
  dragMap.Unset();
  
  Map* theMap = ((MapPtrCtr*) mapTree->GetItemData(dragMap))->map;
  commitAction(std::make_shared<Undoable>("Arranging " + theMap->getTitle(), [=] () {
    wxTreeItemId newChild = MoveTreeNode(dragMapCopy, newParent);
  
    dontSelectMap = true;
    mapTree->SelectItem(newChild);
    dontSelectMap = false;
  }, [=] () {
    wxTreeItemId newChild = MoveTreeNode(dragMapCopy, curParent);
  
    dontSelectMap = true;
    mapTree->SelectItem(newChild);
    dontSelectMap = false;
  }));
}

void MapeditFrame::OnRightClickTree(wxTreeEvent& event)
{
  if (event.GetItem().IsOk())
  {
    PopupMenu(mapTreePopup);
  }
}

void MapeditFrame::OnSetStartpos(wxCommandEvent&)
{
  SetIsSettingStart(true);
  mapEditor->SetIsSettingStart(true);
}

void MapeditFrame::OnCancelSetStartpos(wxCommandEvent&)
{
  SetIsSettingStart(false);
  mapEditor->SetIsSettingStart(false);
}

void MapeditFrame::OnUndo(wxCommandEvent&)
{
  (*currentAction)->endChanges();
  (*currentAction)->undo();
  currentAction++;
  
  UpdateUndoLabels();
}

void MapeditFrame::OnRedo(wxCommandEvent&)
{
  if (currentAction != end(history))
  {
    (*currentAction)->endChanges();
  }
  
  currentAction--;
  (*currentAction)->apply();
  
  UpdateUndoLabels();
}

void MapeditFrame::OnOneMovingSash(wxSplitterEvent& event)
{
  layout1->SetSashPosition(event.GetSashPosition(), true);
}

void MapeditFrame::OnThreeMovingSash(wxSplitterEvent& event)
{
  layout3->SetSashPosition(event.GetSashPosition(), true);
}

void MapeditFrame::OnSetLeftmapType(wxCommandEvent&)
{
  wxChoice* choice = (wxChoice*) wxWindow::FindWindowById(LEFTMAP_TYPE_CHOICE, this);
  wxComboCtrl* combo = (wxComboCtrl*) wxChoice::FindWindowById(LEFTMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Left;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  Map::MoveType newt = ((MoveTypeCtr*) choice->GetClientData(choice->GetSelection()))->type;
  commitAction(std::make_shared<Undoable>("Set Leftmap Action", [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(newt)));
    currentMap->setAdjacent(dir, newt);
    combo->Enable(Map::moveTypeTakesMap(newt));
  }, [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(adjacent.type)));
    currentMap->setAdjacent(dir, adjacent.type);
    combo->Enable(Map::moveTypeTakesMap(adjacent.type));
  }));
}

void MapeditFrame::OnSetLeftmapMap(wxCommandEvent&)
{
  wxComboCtrl* combo = (wxComboCtrl*) wxWindow::FindWindowById(LEFTMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Left;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  MapSelectComboPopup* popup = (MapSelectComboPopup*) combo->GetPopupControl();
  int old = adjacent.map;
  int newt = popup->GetSelectedMapID();
  
  if (old == newt) return;
  
  commitAction(std::make_shared<Undoable>("Set Leftmap Map", [=] () {
    popup->SetSelectedMapID(newt);
    combo->SetValue(world->getMap(newt)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, newt);
  }, [=] () {
    popup->SetSelectedMapID(old);
    combo->SetValue(world->getMap(old)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, old);
  }));
}

void MapeditFrame::OnSetRightmapType(wxCommandEvent&)
{
  wxChoice* choice = (wxChoice*) wxWindow::FindWindowById(RIGHTMAP_TYPE_CHOICE, this);
  wxComboCtrl* combo = (wxComboCtrl*) wxChoice::FindWindowById(RIGHTMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Right;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  Map::MoveType newt = ((MoveTypeCtr*) choice->GetClientData(choice->GetSelection()))->type;
  commitAction(std::make_shared<Undoable>("Set Rightmap Action", [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(newt)));
    currentMap->setAdjacent(dir, newt);
    combo->Enable(Map::moveTypeTakesMap(newt));
  }, [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(adjacent.type)));
    currentMap->setAdjacent(dir, adjacent.type);
    combo->Enable(Map::moveTypeTakesMap(adjacent.type));
  }));
}

void MapeditFrame::OnSetRightmapMap(wxCommandEvent&)
{
  wxComboCtrl* combo = (wxComboCtrl*) wxWindow::FindWindowById(RIGHTMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Right;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  MapSelectComboPopup* popup = (MapSelectComboPopup*) combo->GetPopupControl();
  int old = adjacent.map;
  int newt = popup->GetSelectedMapID();
  
  if (old == newt) return;
  
  commitAction(std::make_shared<Undoable>("Set Rightmap Map", [=] () {
    popup->SetSelectedMapID(newt);
    combo->SetValue(world->getMap(newt)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, newt);
  }, [=] () {
    popup->SetSelectedMapID(old);
    combo->SetValue(world->getMap(old)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, old);
  }));
}

void MapeditFrame::OnSetUpmapType(wxCommandEvent&)
{
  wxChoice* choice = (wxChoice*) wxWindow::FindWindowById(UPMAP_TYPE_CHOICE, this);
  wxComboCtrl* combo = (wxComboCtrl*) wxChoice::FindWindowById(UPMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Up;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  Map::MoveType newt = ((MoveTypeCtr*) choice->GetClientData(choice->GetSelection()))->type;
  commitAction(std::make_shared<Undoable>("Set Upmap Action", [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(newt)));
    currentMap->setAdjacent(dir, newt);
    combo->Enable(Map::moveTypeTakesMap(newt));
  }, [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(adjacent.type)));
    currentMap->setAdjacent(dir, adjacent.type);
    combo->Enable(Map::moveTypeTakesMap(adjacent.type));
  }));
}

void MapeditFrame::OnSetUpmapMap(wxCommandEvent&)
{
  wxComboCtrl* combo = (wxComboCtrl*) wxWindow::FindWindowById(UPMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Up;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  MapSelectComboPopup* popup = (MapSelectComboPopup*) combo->GetPopupControl();
  int old = adjacent.map;
  int newt = popup->GetSelectedMapID();
  
  if (old == newt) return;
  
  commitAction(std::make_shared<Undoable>("Set Upmap Map", [=] () {
    popup->SetSelectedMapID(newt);
    combo->SetValue(world->getMap(newt)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, newt);
  }, [=] () {
    popup->SetSelectedMapID(old);
    combo->SetValue(world->getMap(old)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, old);
  }));
}

void MapeditFrame::OnSetDownmapType(wxCommandEvent&)
{
  wxChoice* choice = (wxChoice*) wxWindow::FindWindowById(DOWNMAP_TYPE_CHOICE, this);
  wxComboCtrl* combo = (wxComboCtrl*) wxChoice::FindWindowById(DOWNMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Down;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  Map::MoveType newt = ((MoveTypeCtr*) choice->GetClientData(choice->GetSelection()))->type;
  commitAction(std::make_shared<Undoable>("Set Downmap Action", [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(newt)));
    currentMap->setAdjacent(dir, newt);
    combo->Enable(Map::moveTypeTakesMap(newt));
  }, [=] () {
    choice->SetSelection(choice->FindString(Map::stringForMoveType(adjacent.type)));
    currentMap->setAdjacent(dir, adjacent.type);
    combo->Enable(Map::moveTypeTakesMap(adjacent.type));
  }));
}

void MapeditFrame::OnSetDownmapMap(wxCommandEvent&)
{
  wxComboCtrl* combo = (wxComboCtrl*) wxWindow::FindWindowById(DOWNMAP_MAP_CHOICE, this);
  Map::MoveDir dir = Map::MoveDir::Down;
  
  Map::Adjacent adjacent = currentMap->getAdjacent(dir);
  MapSelectComboPopup* popup = (MapSelectComboPopup*) combo->GetPopupControl();
  int old = adjacent.map;
  int newt = popup->GetSelectedMapID();
  
  if (old == newt) return;
  
  commitAction(std::make_shared<Undoable>("Set Downmap Map", [=] () {
    popup->SetSelectedMapID(newt);
    combo->SetValue(world->getMap(newt)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, newt);
  }, [=] () {
    popup->SetSelectedMapID(old);
    combo->SetValue(world->getMap(old)->getTitle());
    currentMap->setAdjacent(dir, adjacent.type, old);
  }));
}

void MapeditFrame::OnSelectEntity(wxCommandEvent& event)
{
  MapObjectEntry* entry = (MapObjectEntry*) event.GetClientData();
  wxPanel* entityPropertyEditor = (wxPanel*) wxWindow::FindWindowById(ENTITY_PROPERTY_EDITOR, this);
  
  if (entry == nullptr)
  {
    entityPropertyEditor->GetSizer()->Clear();
    entityPropertyEditor->DestroyChildren();
  } else {
    wxSizer* sizer = entityPropertyEditor->GetSizer();
    for (auto input : entry->getObject().getInputs())
    {
      wxStaticText* inputText = new wxStaticText(entityPropertyEditor, wxID_ANY, input.second.name + ":");
      sizer->Add(inputText, 0, wxEXPAND | wxALIGN_LEFT | wxBOTTOM, 0);
      
      MapObjectEntry::Item& item = entry->getItem(input.first);
      
      wxWindow* inputObject = nullptr;
      switch (input.second.type)
      {
        case MapObject::Input::Type::Choice:
        {
          UndoableChoice* thechoice = new UndoableChoice(entityPropertyEditor, wxID_ANY, this, wxDefaultPosition, wxDefaultSize, 0, NULL, 0, VariableChoiceValidator(*world, item), input.second.name);
          int selected = 0;
          for (auto choice : input.second.choices)
          {
            thechoice->Append(choice.second, (void*) choice.first);
            
            if (item.intvalue == choice.first)
            {
              selected = thechoice->GetCount()-1;
            }
          }
          
          thechoice->SetSelection(selected);
          inputObject = thechoice;
          break;
        }
          
        case MapObject::Input::Type::Slider:
        {
          if (item.intvalue < input.second.minvalue) item.intvalue = input.second.minvalue;
          if (item.intvalue > input.second.maxvalue) item.intvalue = input.second.maxvalue;
          inputObject = new UndoableSlider(entityPropertyEditor, wxID_ANY, this, item.intvalue, input.second.minvalue, input.second.maxvalue, wxDefaultPosition, wxDefaultSize, wxHORIZONTAL | wxSL_LABELS, SliderItemValidator(*world, item), input.second.name);
          break;
        }
      }
      
      sizer->Add(inputObject, 0, wxEXPAND | wxALIGN_LEFT | wxBOTTOM, 10);
    }
    
    entityPropertyEditor->Layout();
  }
}

void MapeditFrame::NewWorld()
{
  LaunchWindow(new World());
}

bool MapeditFrame::OpenWorld(std::string filename)
{
  try
  {
    auto world = new World(filename);
    
    LaunchWindow(world);
    
    return true;
  } catch (std::exception& ex)
  {
    wxMessageBox(ex.what(), "Error loading world", wxOK | wxCENTRE | wxICON_ERROR);
  }
  
  return false;
}

void MapeditFrame::LaunchWindow(World* world)
{
  MapeditFrame* frame = new MapeditFrame(world);
  frame->closer = openWindows.insert(end(openWindows), frame);
  frame->Show(true);
}

void MapeditFrame::SetIsAddingEntity(bool isAddingEntity)
{
  if (isAddingEntity)
  {
    addingEntity = true;
    addEntityButton->Disable();
    cancelEntityButton->Enable();
  } else {
    addingEntity = false;
    addEntityButton->Enable();
    cancelEntityButton->Disable();
  }
}

void MapeditFrame::MapDirtyDidChange(bool dirty)
{
  toolbar->EnableTool(TOOL_FILE_SAVE, dirty);
  menuFile->Enable(MENU_FILE_SAVE, dirty);
  
  if (dirty)
  {
    mapTree->SetItemBold(currentMap->getTreeItemId(), true);
  } else {
    for (auto map : world->getMaps())
    {
      mapTree->SetItemBold(map.second->getTreeItemId(), false);
    }
  }
}

void MapeditFrame::populateMapTree(wxTreeItemId node, std::list<std::shared_ptr<Map>> maps)
{
  for (auto map : maps)
  {
    wxTreeItemId childNode = mapTree->AppendItem(node, map->getTitle());
    mapTree->SetItemData(childNode, new MapPtrCtr(map.get()));
    map->setTreeItemId(childNode);
    
    populateMapTree(childNode, map->getChildren());
    
    if (map->getExpanded())
    {
      mapTree->Expand(childNode);
    }
  }
}

void MapeditFrame::SelectMap(Map* map)
{
  currentMap = map;
  mapEditor->SetMap(map);
  
  SetIsAddingEntity(false);
  SetIsSettingStart(false);
  
  titleBox->ChangeValue(map->getTitle());
  world->setLastMap(map);
  
  wxChoice* leftmapChoice = (wxChoice*) wxWindow::FindWindowById(LEFTMAP_TYPE_CHOICE, this);
  wxChoice* rightmapChoice = (wxChoice*) wxWindow::FindWindowById(RIGHTMAP_TYPE_CHOICE, this);
  wxChoice* upmapChoice = (wxChoice*) wxWindow::FindWindowById(UPMAP_TYPE_CHOICE, this);
  wxChoice* downmapChoice = (wxChoice*) wxWindow::FindWindowById(DOWNMAP_TYPE_CHOICE, this);
  wxComboCtrl* leftmapCombo = (wxComboCtrl*) wxWindow::FindWindowById(LEFTMAP_MAP_CHOICE, this);
  wxComboCtrl* rightmapCombo = (wxComboCtrl*) wxWindow::FindWindowById(RIGHTMAP_MAP_CHOICE, this);
  wxComboCtrl* upmapCombo = (wxComboCtrl*) wxWindow::FindWindowById(UPMAP_MAP_CHOICE, this);
  wxComboCtrl* downmapCombo = (wxComboCtrl*) wxWindow::FindWindowById(DOWNMAP_MAP_CHOICE, this);
  
  leftmapChoice->SetSelection(leftmapChoice->FindString(Map::stringForMoveType(currentMap->getAdjacent(Map::MoveDir::Left).type)));
  rightmapChoice->SetSelection(rightmapChoice->FindString(Map::stringForMoveType(currentMap->getAdjacent(Map::MoveDir::Right).type)));
  upmapChoice->SetSelection(upmapChoice->FindString(Map::stringForMoveType(currentMap->getAdjacent(Map::MoveDir::Up).type)));
  downmapChoice->SetSelection(downmapChoice->FindString(Map::stringForMoveType(currentMap->getAdjacent(Map::MoveDir::Down).type)));
  
  leftmapCombo->Enable(Map::moveTypeTakesMap(currentMap->getAdjacent(Map::MoveDir::Left).type));
  rightmapCombo->Enable(Map::moveTypeTakesMap(currentMap->getAdjacent(Map::MoveDir::Right).type));
  upmapCombo->Enable(Map::moveTypeTakesMap(currentMap->getAdjacent(Map::MoveDir::Up).type));
  downmapCombo->Enable(Map::moveTypeTakesMap(currentMap->getAdjacent(Map::MoveDir::Down).type));
  
  leftmapCombo->SetValue(world->getMap(currentMap->getAdjacent(Map::MoveDir::Left).map)->getTitle());
  rightmapCombo->SetValue(world->getMap(currentMap->getAdjacent(Map::MoveDir::Right).map)->getTitle());
  upmapCombo->SetValue(world->getMap(currentMap->getAdjacent(Map::MoveDir::Up).map)->getTitle());
  downmapCombo->SetValue(world->getMap(currentMap->getAdjacent(Map::MoveDir::Down).map)->getTitle());
}

wxTreeItemId MapeditFrame::MoveTreeNode(wxTreeItemId toCopy, wxTreeItemId newParent)
{
  MapPtrCtr* ctl1 = (MapPtrCtr*) mapTree->GetItemData(toCopy);
  MapPtrCtr* ctl2 = new MapPtrCtr(ctl1->map);
  
  wxTreeItemId copied = mapTree->AppendItem(newParent, mapTree->GetItemText(toCopy), -1, -1, ctl2);
  if (mapTree->IsBold(toCopy))
  {
    mapTree->SetItemBold(toCopy, true);
  }
  
  if (mapTree->ItemHasChildren(toCopy))
  {
    wxTreeItemIdValue cookie;
    for (wxTreeItemId it = mapTree->GetFirstChild(toCopy, cookie); it.IsOk(); it = mapTree->GetNextChild(toCopy, cookie))
    {
      MoveTreeNode(it, copied);
    }
  }
  
  if (mapTree->IsExpanded(toCopy))
  {
    mapTree->Expand(copied);
  }
  
  mapTree->Delete(toCopy);
  
  return copied;
}

void MapeditFrame::SetIsSettingStart(bool isSettingStart)
{
  if (isSettingStart)
  {
    setStartposButton->Disable();
    cancelStartposButton->Enable();
  } else {
    SetStartposLabel();
    
    setStartposButton->Enable();
    cancelStartposButton->Disable();
  }
}

void MapeditFrame::SetStartposLabel()
{
  std::ostringstream mappos_out;
  mappos_out << "Starting Position: ";
  mappos_out << world->getStartingMap()->getTitle();
  mappos_out << " (";
  mappos_out << (int) world->getStartingPosition().first;
  mappos_out << ",";
  mappos_out << (int) world->getStartingPosition().second;
  mappos_out << ")";
  
  startposLabel->SetLabel(mappos_out.str());
  propertyEditor->GetSizer()->SetSizeHints(propertyEditor);
}

void MapeditFrame::UpdateUndoLabels()
{
  if (currentAction != end(history))
  {
    menuEdit->SetLabel(MENU_EDIT_UNDO, "Undo " + (*currentAction)->getTitle() + "\tCtrl-Z");
    menuEdit->Enable(MENU_EDIT_UNDO, true);
  } else {
    menuEdit->SetLabel(MENU_EDIT_UNDO, "Undo\tCtrl-Z");
    menuEdit->Enable(MENU_EDIT_UNDO, false);
  }
  
  if (currentAction != begin(history))
  {
    menuEdit->SetLabel(MENU_EDIT_REDO, "Redo " + (*std::prev(currentAction))->getTitle() + "\tCtrl-Shift-Z");
    menuEdit->Enable(MENU_EDIT_REDO, true);
  } else {
    menuEdit->SetLabel(MENU_EDIT_REDO, "Redo\tCtrl-Shift-Z");
    menuEdit->Enable(MENU_EDIT_REDO, false);
  }
}

void MapeditFrame::commitAction(std::shared_ptr<Undoable> action)
{
  action->apply();
  
  commitAfter(action);
}

void MapeditFrame::commitAfter(std::shared_ptr<Undoable> action)
{
  if (currentAction != end(history))
  {
    (*currentAction)->endChanges();
  }

  history.erase(begin(history), currentAction);
  currentAction = history.insert(begin(history), action);
  
  UpdateUndoLabels();
  
  if (history.size() > 100)
  {
    history.pop_back();
  }
}