[darkradiant] 01/06: New upstream version 2.1.0

Tobias Frost tobi at moszumanska.debian.org
Sun Dec 11 11:47:25 UTC 2016


This is an automated email from the git hooks/post-receive script.

tobi pushed a commit to branch master
in repository darkradiant.

commit d5251fea5fa16186bd6f94588afa84cf5fcb6b1a
Author: Tobias Frost <tobi at coldtobi.de>
Date:   Wed Dec 7 08:05:02 2016 +0100

    New upstream version 2.1.0
---
 .gitignore                                         |    4 +
 README                                             |   17 +-
 compile                                            |    2 +-
 depcomp                                            |  586 ++-
 include/editable.h                                 |   46 +-
 include/iaasfile.h                                 |  162 +
 include/icameraview.h                              |   12 +
 include/ientity.h                                  |    8 +-
 include/ientityinspector.h                         |    5 +
 include/igame.h                                    |    6 +
 include/igroupdialog.h                             |   21 +-
 include/imap.h                                     |   24 +-
 include/imapinfofile.h                             |  157 +
 include/imapresource.h                             |   33 +-
 include/inode.h                                    |   12 +
 include/iorthocontextmenu.h                        |    1 +
 include/ipatch.h                                   |    4 +-
 include/ipreferencesystem.h                        |   54 +-
 include/irenderable.h                              |   30 +-
 include/iselectable.h                              |   21 +-
 include/iselection.h                               |   10 +-
 include/iselectiongroup.h                          |  120 +
 include/iselectiontest.h                           |    7 +-
 include/iuimanager.h                               |    4 +-
 include/precompiled_interfaces.h                   |    2 +
 include/version.h                                  |    2 +-
 install/bitmaps/empty.png                          |  Bin 143 -> 155 bytes
 install/bitmaps/group_selection.png                |  Bin 0 -> 263 bytes
 install/bitmaps/ungroup_selection.png              |  Bin 0 -> 257 bytes
 install/i18n/darkradiant.pot                       |  835 ++--
 install/i18n/de/LC_MESSAGES/darkradiant.mo         |  Bin 97427 -> 99807 bytes
 install/i18n/de/LC_MESSAGES/darkradiant.po         |  871 ++--
 install/input.xml                                  |    6 +-
 install/menu.xml                                   |    2 +-
 .../commands/check_for_invalid_visportals.py       |   69 +
 install/ui/findandreplacedialog.fbp                |    4 +-
 install/ui/findandreplacedialog.xrc                |  294 +-
 install/ui/particleeditor.fbp                      |   41 +-
 install/ui/particleeditor.xrc                      | 4286 ++++++++++----------
 install/ui/stimeditor.fbp                          |   17 +-
 install/ui/stimeditor.xrc                          |    4 +-
 install/user.xml                                   |  526 +--
 libs/ObservedSelectable.h                          |   13 +-
 libs/SelectableNode.h                              |   52 -
 libs/entitylib.h                                   |   10 +-
 libs/math/Vector2.h                                |    6 +
 libs/os/file.h                                     |  107 +-
 libs/os/path.h                                     |   18 +-
 libs/pivot.h                                       |    2 +-
 libs/registry/Widgets.h                            |   28 +-
 libs/render/RenderableSpacePartition.h             |   10 +-
 libs/render/ShaderStateRenderer.h                  |    5 +-
 libs/scene/BasicRootNode.h                         |    4 +-
 libs/scene/Makefile.am                             |    1 +
 libs/scene/Makefile.in                             |    4 +-
 libs/scene/Node.cpp                                |   35 +-
 libs/scene/Node.h                                  |   17 +-
 libs/scene/SelectableNode.cpp                      |  247 ++
 libs/scene/SelectableNode.h                        |   75 +
 libs/scene/TraversableNodeSet.cpp                  |   14 +-
 libs/scene/TraversableNodeSet.h                    |   11 +-
 libs/scenelib.h                                    |    5 +-
 libs/wxutil/GLWidget.cpp                           |    5 +-
 libs/wxutil/Makefile.am                            |    2 +
 libs/wxutil/Makefile.in                            |    2 +
 libs/wxutil/TreeModel.cpp                          |   40 +-
 libs/wxutil/TreeModel.h                            |   28 +-
 libs/wxutil/TreeView.cpp                           |   13 +
 libs/wxutil/TreeView.h                             |    1 +
 missing                                            |  460 +--
 plugins/dm.conversation/ConversationDialog.cpp     |    9 +-
 plugins/dm.conversation/ConversationDialog.h       |    1 +
 plugins/dm.conversation/ConversationEditor.cpp     |   25 +
 plugins/dm.editing/AIEditingPanel.cpp              |   10 +-
 plugins/dm.editing/AIEditingPanel.h                |    4 +-
 plugins/dm.editing/AIHeadPropertyEditor.cpp        |    5 +
 plugins/dm.editing/AIHeadPropertyEditor.h          |    5 +-
 plugins/dm.editing/AIVocalSetPropertyEditor.cpp    |    5 +
 plugins/dm.editing/AIVocalSetPropertyEditor.h      |    5 +-
 plugins/dm.editing/Makefile.am                     |    6 +-
 plugins/dm.editing/Makefile.in                     |    6 +-
 plugins/dm.gui/plugin.cpp                          |    6 +-
 plugins/dm.objectives/ObjectiveEntityFinder.cpp    |    2 +-
 plugins/dm.stimresponse/EffectEditor.cpp           |    2 +-
 plugins/entity/Doom3Entity.cpp                     |    5 +
 plugins/entity/Doom3Entity.h                       |   25 +-
 plugins/entity/EntityNode.cpp                      |    6 +-
 plugins/entity/EntityNode.h                        |   10 +-
 plugins/entity/VertexInstance.h                    |    6 +-
 plugins/entity/curve/CurveEditInstance.cpp         |   10 +-
 plugins/entity/curve/CurveEditInstance.h           |    1 +
 plugins/entity/doom3group/Doom3Group.cpp           |   11 +-
 plugins/entity/doom3group/Doom3GroupNode.cpp       |   12 +-
 plugins/entity/doom3group/Doom3GroupNode.h         |   17 +-
 plugins/entity/eclassmodel/EclassModelNode.h       |    6 +-
 plugins/entity/generic/GenericEntityNode.cpp       |    7 -
 plugins/entity/generic/GenericEntityNode.h         |   13 +-
 plugins/entity/light/Light.cpp                     |   14 +-
 plugins/entity/light/Light.h                       |    5 -
 plugins/entity/light/LightNode.cpp                 |   19 +-
 plugins/entity/light/LightNode.h                   |   21 +-
 plugins/entity/speaker/SpeakerNode.cpp             |    7 +-
 plugins/entity/speaker/SpeakerNode.h               |   13 +-
 plugins/entity/target/TargetLineNode.cpp           |    6 +-
 plugins/entity/target/TargetLineNode.h             |    2 +-
 plugins/entity/target/TargetableNode.cpp           |    3 +
 plugins/entitylist/EntityList.cpp                  |    2 +-
 plugins/entitylist/GraphTreeModelPopulator.h       |    2 +-
 plugins/eventmanager/MouseToolManager.cpp          |   83 +-
 plugins/eventmanager/MouseToolManager.h            |    2 +-
 plugins/fonts/GlyphSet.cpp                         |    2 +-
 plugins/fonts/Makefile.am                          |    2 +
 plugins/fonts/Makefile.in                          |    2 +
 plugins/grid/Grid.cpp                              |   13 +-
 plugins/mapdoom3/Makefile.am                       |   10 +-
 plugins/mapdoom3/Makefile.in                       |   41 +-
 plugins/mapdoom3/aas/Doom3AasFile.cpp              |  413 ++
 plugins/mapdoom3/aas/Doom3AasFile.h                |   65 +
 plugins/mapdoom3/aas/Doom3AasFileLoader.cpp        |  127 +
 plugins/mapdoom3/aas/Doom3AasFileLoader.h          |   35 +
 plugins/mapdoom3/aas/Doom3AasFileSettings.cpp      |  162 +
 plugins/mapdoom3/aas/Doom3AasFileSettings.h        |   47 +
 plugins/mapdoom3/aas/Util.h                        |   21 +
 plugins/mapdoom3/compiler/DebugRenderer.h          |   10 +-
 plugins/mapdoom3/compiler/Doom3MapCompiler.cpp     |   11 +-
 plugins/mapdoom3/compiler/OptIsland.cpp            |    2 +-
 plugins/mapdoom3/compiler/ProcCompiler.cpp         |    7 +-
 plugins/mapdoom3/compiler/ProcPatch.cpp            |    8 -
 plugins/mapdoom3/mapdoom3.cpp                      |   14 +-
 .../mapdoom3/primitivewriters/PatchDefExporter.h   |    4 +-
 plugins/md5model/MD5ModelNode.h                    |   10 +-
 plugins/md5model/MD5Skeleton.cpp                   |   16 +-
 plugins/model/PicoModelNode.h                      |   10 +-
 plugins/particles/ParticleDef.cpp                  |    3 +
 plugins/particles/ParticleDef.h                    |   31 +-
 plugins/particles/ParticleNode.cpp                 |    4 +-
 plugins/particles/ParticleNode.h                   |    8 +-
 plugins/particles/ParticleQuad.h                   |   10 +-
 plugins/particles/ParticlesManager.cpp             |   19 +-
 plugins/particles/RenderableParticle.h             |   10 +-
 plugins/particles/RenderableParticleBunch.cpp      |    5 +-
 plugins/particles/StageDef.cpp                     |   19 +-
 plugins/script/ScriptingSystem.cpp                 |    2 +-
 plugins/script/interfaces/PatchInterface.cpp       |    7 +-
 plugins/script/interfaces/SceneGraphInterface.h    |   10 +-
 plugins/shaders/Doom3ShaderSystem.cpp              |    6 +-
 plugins/shaders/textures/TextureManipulator.cpp    |    9 +-
 plugins/sound/WavFileLoader.h                      |   16 +-
 plugins/uimanager/GroupDialog.cpp                  |   59 +-
 plugins/uimanager/GroupDialog.h                    |    4 +-
 plugins/uimanager/Makefile.am                      |    2 +
 plugins/uimanager/Makefile.in                      |    2 +
 plugins/uimanager/MenuManager.cpp                  |    4 +-
 plugins/uimanager/StatusBarManager.cpp             |   13 +-
 plugins/uimanager/StatusBarManager.h               |   13 +-
 plugins/uimanager/ToolbarManager.cpp               |    2 +-
 plugins/uimanager/UIManager.cpp                    |    4 +-
 plugins/undo/UndoSystem.cpp                        |   18 +-
 plugins/xmlregistry/Makefile.am                    |    6 +-
 plugins/xmlregistry/Makefile.in                    |    6 +-
 radiant/Makefile.am                                |   16 +-
 radiant/Makefile.in                                |  335 +-
 radiant/RadiantModule.cpp                          |   21 +-
 radiant/RadiantModule.h                            |    7 +-
 radiant/brush/Brush.cpp                            |    5 +-
 radiant/brush/BrushModule.cpp                      |    6 +-
 radiant/brush/BrushNode.cpp                        |   84 +-
 radiant/brush/BrushNode.h                          |   26 +-
 radiant/brush/EdgeInstance.h                       |    2 +-
 radiant/brush/FaceInstance.cpp                     |    8 +-
 radiant/brush/FaceInstance.h                       |    2 +-
 radiant/brush/TextureProjection.cpp                |    8 +-
 radiant/brush/VertexInstance.h                     |    2 +-
 radiant/brush/csg/CSG.cpp                          |    8 +-
 radiant/camera/CamRenderer.cpp                     |   17 +-
 radiant/camera/CamRenderer.h                       |    3 +-
 radiant/camera/CamWnd.cpp                          |   33 +-
 radiant/camera/CamWnd.h                            |   13 +-
 radiant/camera/CameraSettings.cpp                  |   20 +-
 radiant/camera/GlobalCamera.cpp                    |    2 +
 radiant/camera/tools/PanViewTool.h                 |   89 +
 radiant/clipper/Clipper.cpp                        |    6 +-
 radiant/darkradiant.rc                             |   23 +
 radiant/layers/LayerInfoFileModule.cpp             |  282 ++
 radiant/layers/LayerInfoFileModule.h               |   54 +
 radiant/layers/LayerSystem.cpp                     |   21 +-
 radiant/layers/LayerSystem.h                       |    3 +
 radiant/layers/SetLayerSelectedWalker.h            |    5 +-
 radiant/map/AasFileManager.cpp                     |  170 +
 radiant/map/AasFileManager.h                       |   41 +
 radiant/map/AutoSaver.cpp                          |  167 +-
 radiant/map/AutoSaver.h                            |   28 +-
 radiant/map/CounterManager.cpp                     |   23 +-
 radiant/map/CounterManager.h                       |   16 +-
 radiant/map/DeferredDraw.h                         |   63 -
 radiant/map/InfoFile.cpp                           |  308 --
 radiant/map/InfoFile.h                             |   99 -
 radiant/map/Map.cpp                                |  325 +-
 radiant/map/Map.h                                  |   65 +-
 radiant/map/MapResource.cpp                        |  332 +-
 radiant/map/MapResource.h                          |   58 +-
 radiant/map/MapResourceManager.cpp                 |    9 +-
 radiant/map/MapResourceManager.h                   |    9 +-
 radiant/map/PointFile.cpp                          |   10 +
 radiant/map/PointFile.h                            |   21 +-
 radiant/map/RegionManager.cpp                      |  171 +-
 radiant/map/RegionManager.h                        |   85 +-
 radiant/map/RenderableAasFile.cpp                  |  121 +
 radiant/map/RenderableAasFile.h                    |   56 +
 radiant/map/RootNode.h                             |    8 +-
 radiant/map/algorithm/AssignLayerMappingWalker.h   |   48 -
 radiant/map/algorithm/ChildPrimitives.cpp          |    4 +-
 radiant/map/algorithm/Clone.h                      |    5 +
 radiant/map/algorithm/InfoFileExporter.cpp         |  169 -
 radiant/map/algorithm/InfoFileExporter.h           |   66 -
 radiant/map/algorithm/MapExporter.h                |    2 +-
 radiant/map/algorithm/MapImporter.cpp              |   14 +-
 radiant/map/algorithm/MapImporter.h                |   12 +-
 radiant/map/algorithm/Merge.h                      |    4 +-
 radiant/map/algorithm/WorldspawnArgFinder.h        |    2 +-
 radiant/map/infofile/InfoFile.cpp                  |  143 +
 radiant/map/infofile/InfoFile.h                    |   45 +
 radiant/map/infofile/InfoFileExporter.cpp          |   62 +
 radiant/map/infofile/InfoFileExporter.h            |   31 +
 radiant/map/infofile/InfoFileManager.cpp           |   67 +
 radiant/map/infofile/InfoFileManager.h             |   29 +
 radiant/modulesystem/ModuleRegistry.cpp            |    6 +-
 radiant/patch/Patch.cpp                            | 2301 +++--------
 radiant/patch/Patch.h                              |  119 +-
 radiant/patch/PatchBezier.cpp                      |  184 -
 radiant/patch/PatchBezier.h                        |   80 -
 radiant/patch/PatchCreators.cpp                    |    5 +-
 radiant/patch/PatchModule.cpp                      |   39 -
 radiant/patch/PatchNode.cpp                        |   56 +-
 radiant/patch/PatchNode.h                          |   25 +-
 radiant/patch/PatchRenderables.cpp                 |  154 +-
 radiant/patch/PatchRenderables.h                   |   68 +-
 radiant/patch/PatchTesselation.cpp                 |  871 ++++
 radiant/patch/PatchTesselation.h                   |   65 +-
 radiant/referencecache/NullModelNode.h             |   10 +-
 radiant/render/backend/GLProgramFactory.cpp        |    2 +-
 radiant/render/backend/OpenGLShader.cpp            |   30 +
 .../render/frontend/RenderableCollectionWalker.h   |   19 +-
 radiant/selection/BasicSelectable.h                |    2 +-
 radiant/selection/Device.h                         |   46 +-
 radiant/selection/DragManipulator.cpp              |    2 +-
 radiant/selection/OccludeSelector.h                |    2 +-
 radiant/selection/RadiantSelectionSystem.cpp       |  269 +-
 radiant/selection/RadiantSelectionSystem.h         |   34 +-
 radiant/selection/SceneWalkers.h                   |   18 -
 radiant/selection/SelectionTest.cpp                |    4 +-
 radiant/selection/Selectors.h                      |   29 +-
 radiant/selection/TransformationVisitors.cpp       |    7 +-
 radiant/selection/TranslateManipulator.cpp         |    2 +-
 .../algorithm/CommandNotAvailableException.h       |   50 +
 radiant/selection/algorithm/Entity.cpp             |  151 +-
 radiant/selection/algorithm/Entity.h               |   10 +
 radiant/selection/algorithm/General.cpp            |  130 +-
 radiant/selection/algorithm/Group.cpp              |  139 +-
 radiant/selection/algorithm/Group.h                |   30 +-
 radiant/selection/algorithm/GroupCycle.cpp         |    2 +-
 radiant/selection/algorithm/Patch.cpp              |   11 -
 radiant/selection/algorithm/Patch.h                |    2 -
 radiant/selection/algorithm/Primitives.cpp         |    4 +-
 radiant/selection/algorithm/Transformation.cpp     |  101 +-
 radiant/selection/group/SelectionGroup.h           |  120 +
 .../group/SelectionGroupInfoFileModule.cpp         |  342 ++
 .../selection/group/SelectionGroupInfoFileModule.h |   59 +
 radiant/selection/group/SelectionGroupManager.cpp  |  280 ++
 radiant/selection/group/SelectionGroupManager.h    |   62 +
 .../selectionset/SelectionSetInfoFileModule.cpp    |  230 ++
 .../selectionset/SelectionSetInfoFileModule.h      |   61 +
 .../selection/selectionset/SelectionSetManager.cpp |   22 +-
 .../selection/selectionset/SelectionSetManager.h   |    2 +
 .../selection/shaderclipboard/ShaderClipboard.cpp  |   14 +-
 .../selection/shaderclipboard/ShaderClipboard.h    |    5 +-
 radiant/settings/GameManager.cpp                   |   16 +-
 radiant/settings/GameManager.h                     |   26 +-
 radiant/settings/LanguageManager.cpp               |    6 +-
 radiant/settings/PreferenceItemBase.h              |   44 +
 radiant/settings/PreferenceItems.h                 |  170 +
 radiant/settings/PreferencePage.cpp                |  159 +
 radiant/settings/PreferencePage.h                  |   94 +
 radiant/settings/PreferenceSystem.cpp              |   74 +-
 radiant/settings/PreferenceSystem.h                |   46 +-
 radiant/textool/item/PatchItem.cpp                 |    4 +-
 radiant/ui/aas/AasControl.cpp                      |  123 +
 radiant/ui/aas/AasControl.h                        |   63 +
 radiant/ui/aas/AasControlDialog.cpp                |  237 ++
 radiant/ui/aas/AasControlDialog.h                  |   65 +
 radiant/ui/einspector/AnglePropertyEditor.h        |    2 +-
 radiant/ui/einspector/BooleanPropertyEditor.cpp    |    8 +-
 radiant/ui/einspector/BooleanPropertyEditor.h      |    4 +-
 radiant/ui/einspector/ClassnamePropertyEditor.cpp  |   12 +-
 radiant/ui/einspector/ClassnamePropertyEditor.h    |    2 +-
 radiant/ui/einspector/ColourPropertyEditor.cpp     |    7 +-
 radiant/ui/einspector/ColourPropertyEditor.h       |    4 +-
 radiant/ui/einspector/EntityInspector.cpp          |  426 +-
 radiant/ui/einspector/EntityInspector.h            |   19 +-
 radiant/ui/einspector/EntityPropertyEditor.cpp     |    5 +
 radiant/ui/einspector/EntityPropertyEditor.h       |    4 +-
 radiant/ui/einspector/FloatPropertyEditor.cpp      |   28 +-
 radiant/ui/einspector/FloatPropertyEditor.h        |    4 +-
 radiant/ui/einspector/ModelPropertyEditor.h        |    2 +-
 radiant/ui/einspector/PropertyEditor.h             |    7 +-
 radiant/ui/einspector/SkinPropertyEditor.h         |    2 +-
 radiant/ui/einspector/SoundPropertyEditor.h        |    2 +-
 radiant/ui/einspector/TexturePropertyEditor.h      |    2 +-
 radiant/ui/einspector/Vector3PropertyEditor.cpp    |    7 +-
 radiant/ui/einspector/Vector3PropertyEditor.h      |    4 +-
 radiant/ui/layers/LayerControl.cpp                 |    2 +-
 radiant/ui/mainframe/EmbeddedLayout.cpp            |    1 +
 radiant/ui/mainframe/FloatingLayout.cpp            |    1 +
 radiant/ui/mainframe/MainFrame.cpp                 |   62 +-
 radiant/ui/mainframe/MainFrame.h                   |    1 -
 radiant/ui/mainframe/ScreenUpdateBlocker.cpp       |    9 +-
 radiant/ui/mainframe/SplitPaneLayout.cpp           |    1 +
 radiant/ui/mediabrowser/MediaBrowser.cpp           |    4 +-
 radiant/ui/mru/MRU.cpp                             |    6 +-
 radiant/ui/ortho/OrthoContextMenu.cpp              |    7 +-
 radiant/ui/overlay/Overlay.cpp                     |    1 +
 radiant/ui/patch/PatchInspector.cpp                |    4 +-
 radiant/ui/prefabselector/PrefabSelector.cpp       |   39 +-
 radiant/ui/prefabselector/PrefabSelector.h         |   15 +-
 radiant/ui/prefdialog/PrefDialog.cpp               |  227 +-
 radiant/ui/prefdialog/PrefDialog.h                 |   50 +-
 radiant/ui/prefdialog/PrefPage.cpp                 |  364 +-
 radiant/ui/prefdialog/PrefPage.h                   |  125 +-
 radiant/ui/prefdialog/PreferenceItem.cpp           |  164 +
 radiant/ui/prefdialog/PreferenceItem.h             |   47 +
 radiant/ui/texturebrowser/TextureBrowser.cpp       |   80 +-
 .../ui/texturebrowser/TextureBrowserManager.cpp    |   12 +-
 radiant/xyview/GlobalXYWnd.cpp                     |   37 +-
 radiant/xyview/XYRenderer.h                        |   56 +-
 radiant/xyview/XYWnd.cpp                           |   37 +-
 radiant/xyview/XYWnd.h                             |    7 +-
 tools/i18n/darkradiant.pot                         |  835 ++--
 tools/innosetup/create_installer.x64.cmd           |    2 -
 tools/innosetup/create_installer.x86.cmd           |    2 -
 tools/innosetup/darkradiant.iss                    |   11 +-
 tools/innosetup/darkradiant.x64.iss                |   10 +-
 tools/{msvc2013 => msvc2015}/DarkRadiant.sln       |    0
 tools/{msvc2013 => msvc2015}/DarkRadiant.vcxproj   |   61 +-
 .../DarkRadiant.vcxproj.filters                    |  137 +-
 tools/{msvc2013 => msvc2015}/archivezip.vcxproj    |   10 +-
 .../archivezip.vcxproj.filters                     |    0
 tools/{msvc2013 => msvc2015}/commandsystem.vcxproj |   10 +-
 .../commandsystem.vcxproj.filters                  |    0
 tools/{msvc2013 => msvc2015}/ddslib.vcxproj        |   10 +-
 .../{msvc2013 => msvc2015}/dm.conversation.vcxproj |   10 +-
 .../dm.conversation.vcxproj.filters                |    0
 tools/{msvc2013 => msvc2015}/dm.difficulty.vcxproj |   10 +-
 .../dm.difficulty.vcxproj.filters                  |    0
 tools/{msvc2013 => msvc2015}/dm.editing.vcxproj    |   10 +-
 .../dm.editing.vcxproj.filters                     |    0
 tools/{msvc2013 => msvc2015}/dm.gui.vcxproj        |   10 +-
 .../{msvc2013 => msvc2015}/dm.gui.vcxproj.filters  |    0
 tools/{msvc2013 => msvc2015}/dm.objectives.vcxproj |   10 +-
 .../dm.objectives.vcxproj.filters                  |    0
 .../{msvc2013 => msvc2015}/dm.stimresponse.vcxproj |   10 +-
 .../dm.stimresponse.vcxproj.filters                |    0
 tools/{msvc2013 => msvc2015}/eclassmgr.vcxproj     |   10 +-
 .../eclassmgr.vcxproj.filters                      |    0
 tools/{msvc2013 => msvc2015}/eclasstree.vcxproj    |   10 +-
 .../eclasstree.vcxproj.filters                     |    0
 tools/{msvc2013 => msvc2015}/entity.vcxproj        |   18 +-
 .../{msvc2013 => msvc2015}/entity.vcxproj.filters  |    0
 tools/{msvc2013 => msvc2015}/entitylist.vcxproj    |   10 +-
 .../entitylist.vcxproj.filters                     |    0
 tools/{msvc2013 => msvc2015}/eventmanager.vcxproj  |   10 +-
 .../eventmanager.vcxproj.filters                   |    0
 tools/{msvc2013 => msvc2015}/filetypes.vcxproj     |   10 +-
 .../filetypes.vcxproj.filters                      |    0
 tools/{msvc2013 => msvc2015}/filters.vcxproj       |   10 +-
 .../{msvc2013 => msvc2015}/filters.vcxproj.filters |    0
 tools/{msvc2013 => msvc2015}/fonts.vcxproj         |   10 +-
 tools/{msvc2013 => msvc2015}/fonts.vcxproj.filters |    0
 tools/{msvc2013 => msvc2015}/grid.vcxproj          |   10 +-
 tools/{msvc2013 => msvc2015}/grid.vcxproj.filters  |    0
 tools/{msvc2013 => msvc2015}/image.vcxproj         |   10 +-
 tools/{msvc2013 => msvc2015}/image.vcxproj.filters |    0
 tools/{msvc2013 => msvc2015}/include.vcxproj       |   21 +-
 tools/{msvc2013 => msvc2015}/libs.vcxproj          |   11 +-
 tools/{msvc2013 => msvc2015}/libs.vcxproj.filters  |    1 -
 tools/{msvc2013 => msvc2015}/mapdoom3.vcxproj      |   17 +-
 .../mapdoom3.vcxproj.filters                       |   24 +
 tools/{msvc2013 => msvc2015}/mathlib.vcxproj       |   10 +-
 tools/{msvc2013 => msvc2015}/md5model.vcxproj      |   10 +-
 .../md5model.vcxproj.filters                       |    0
 tools/{msvc2013 => msvc2015}/model.vcxproj         |   10 +-
 tools/{msvc2013 => msvc2015}/model.vcxproj.filters |    0
 tools/{msvc2013 => msvc2015}/particles.vcxproj     |   10 +-
 .../particles.vcxproj.filters                      |    0
 tools/{msvc2013 => msvc2015}/picomodellib.vcxproj  |   10 +-
 .../picomodellib.vcxproj.filters                   |    0
 tools/{msvc2013 => msvc2015}/post_build_event.cmd  |   16 +-
 .../post_build_event_x64.cmd                       |   16 +-
 .../{msvc2013 => msvc2015}/properties/Boost.props  |    0
 .../properties/DarkRadiant Base Debug Win32.props  |    0
 .../properties/DarkRadiant Base Debug x64.props    |    3 +
 .../DarkRadiant Base Release Win32.props           |    0
 .../properties/DarkRadiant Base Release x64.props  |    6 +-
 .../properties/DarkRadiant Base.props              |    0
 tools/{msvc2013 => msvc2015}/properties/GLEW.props |    0
 .../properties/OpenAL + Vorbis.props               |    0
 .../{msvc2013 => msvc2015}/properties/Python.props |    0
 tools/{msvc2013 => msvc2015}/properties/ftgl.props |    0
 .../{msvc2013 => msvc2015}/properties/libpng.props |    0
 .../properties/libxml2.props                       |    0
 .../properties/win_iconv.props                     |    0
 .../properties/wxWidgets.props                     |    0
 tools/{msvc2013 => msvc2015}/properties/zlib.props |    0
 tools/{msvc2013 => msvc2015}/scenegraph.vcxproj    |   10 +-
 .../scenegraph.vcxproj.filters                     |    0
 tools/{msvc2013 => msvc2015}/scenelib.vcxproj      |   12 +-
 .../scenelib.vcxproj.filters                       |    6 +
 tools/{msvc2013 => msvc2015}/script.vcxproj        |   10 +-
 .../{msvc2013 => msvc2015}/script.vcxproj.filters  |    0
 tools/{msvc2013 => msvc2015}/shaders.vcxproj       |   10 +-
 .../{msvc2013 => msvc2015}/shaders.vcxproj.filters |    0
 tools/{msvc2013 => msvc2015}/skins.vcxproj         |   10 +-
 tools/{msvc2013 => msvc2015}/skins.vcxproj.filters |    0
 tools/{msvc2013 => msvc2015}/sound.vcxproj         |   10 +-
 tools/{msvc2013 => msvc2015}/sound.vcxproj.filters |    0
 tools/{msvc2013 => msvc2015}/uimanager.vcxproj     |   10 +-
 .../uimanager.vcxproj.filters                      |    0
 tools/{msvc2013 => msvc2015}/undo.vcxproj          |   10 +-
 tools/{msvc2013 => msvc2015}/undo.vcxproj.filters  |    0
 tools/{msvc2013 => msvc2015}/vfspk3.vcxproj        |   10 +-
 .../{msvc2013 => msvc2015}/vfspk3.vcxproj.filters  |    0
 tools/{msvc2013 => msvc2015}/wavefront.vcxproj     |    0
 .../wavefront.vcxproj.filters                      |    0
 tools/{msvc2013 => msvc2015}/wxutillib.vcxproj     |   10 +-
 .../wxutillib.vcxproj.filters                      |    0
 tools/{msvc2013 => msvc2015}/xmlregistry.vcxproj   |   10 +-
 .../xmlregistry.vcxproj.filters                    |    0
 tools/{msvc2013 => msvc2015}/xmlutillib.vcxproj    |   10 +-
 tools/scripts/build_boost_libs.cmd                 |   35 +-
 tools/scripts/build_boost_libs.x64.cmd             |   35 +-
 tools/scripts/compile_release_package.ps1          |   57 +-
 tools/scripts/copy_install_files.cmd               |   10 -
 tools/scripts/copy_install_files.x64.cmd           |   10 -
 442 files changed, 15623 insertions(+), 10618 deletions(-)

diff --git a/.gitignore b/.gitignore
index 60268e0..1810829 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,7 @@ testsuite.ilk
 /tools/innosetup/*.7z
 *.vspx
 *.bak
+*.opendb
+tools/msvc2015/DarkRadiant.VC.db
+*.iobj
+*.ipdb
diff --git a/README b/README
index 9706fa7..0258444 100644
--- a/README
+++ b/README
@@ -10,11 +10,11 @@ Compiling on Windows
 Prerequisites
 -------------
 
-DarkRadiant is built on Windows using Microsoft Visual C++ 2013. 
-The free Express version can be obtained here:
+DarkRadiant is built on Windows using Microsoft Visual C++ 2015. 
+The free Community Edition can be obtained here:
 
-VC++ 2013: http://www.visualstudio.com/de-de/downloads
-           (Choose Visual Studio Express 2013 for Windows Desktop)
+VC++ 2015: https://www.visualstudio.com/en-us/downloads
+           (Choose Visual Studio Community)
 
 Since DarkRadiant uses a couple of open-source libraries that are not available on
 Windows by default, you will also need to download and install the
@@ -22,11 +22,11 @@ dependencies. 7-Zip packages of the dependencies are available at the following
 URL(s). (Get 7-zip here: http://www.7-zip.org/)
 
 32-bit only builds:
-https://github.com/codereader/DarkRadiant/releases/download/2.0.0/w32deps.7z
+https://github.com/codereader/DarkRadiant/releases/download/2.1.0/w32deps.7z
 
 64-bit builds:
-https://github.com/codereader/DarkRadiant/releases/download/2.0.0/w32deps.7z
-https://github.com/codereader/DarkRadiant/releases/download/2.0.0/w64deps.7z
+https://github.com/codereader/DarkRadiant/releases/download/2.1.0/w32deps.7z
+https://github.com/codereader/DarkRadiant/releases/download/2.1.0/w64deps.7z
 
 Note that 64-bit builds need the 32-bit dependencies in addition to their own
 64-bit dependencies.
@@ -39,7 +39,7 @@ Build
 
 The main Visual C++ solution file is:
 
-Visual Studio 2013: tools/msvc2013/DarkRadiant.sln
+Visual Studio 2015: tools/msvc2015/DarkRadiant.sln
 
 Open this file with Visual Studio and start a build by right-clicking on the
 top-level "Solution 'DarkRadiant'" item and choosing Build Solution.
@@ -82,6 +82,7 @@ Build
 
 To build DarkRadiant the standard Autotools build process is used:
 
+$ ./autogen.sh
 $ ./configure
 $ make
 $ sudo make install
diff --git a/compile b/compile
old mode 100644
new mode 100755
index 531136b..a85b723
--- a/compile
+++ b/compile
@@ -3,7 +3,7 @@
 
 scriptversion=2012-10-14.11; # UTC
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 # Written by Tom Tromey <tromey at cygnus.com>.
 #
 # This program is free software; you can redistribute it and/or modify
diff --git a/depcomp b/depcomp
index e5f9736..fc98710 100755
--- a/depcomp
+++ b/depcomp
@@ -1,10 +1,9 @@
 #! /bin/sh
 # depcomp - compile a program generating dependencies as side-effects
 
-scriptversion=2007-03-29.01
+scriptversion=2013-05-30.07; # UTC
 
-# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007 Free Software
-# Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -17,9 +16,7 @@ scriptversion=2007-03-29.01
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -30,9 +27,9 @@ scriptversion=2007-03-29.01
 
 case $1 in
   '')
-     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
-     exit 1;
-     ;;
+    echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+    exit 1;
+    ;;
   -h | --h*)
     cat <<\EOF
 Usage: depcomp [--help] [--version] PROGRAM [ARGS]
@@ -42,11 +39,11 @@ as side-effects.
 
 Environment variables:
   depmode     Dependency tracking mode.
-  source      Source file read by `PROGRAMS ARGS'.
-  object      Object file output by `PROGRAMS ARGS'.
+  source      Source file read by 'PROGRAMS ARGS'.
+  object      Object file output by 'PROGRAMS ARGS'.
   DEPDIR      directory where to store dependencies.
   depfile     Dependency file to output.
-  tmpdepfile  Temporary file to use when outputing dependencies.
+  tmpdepfile  Temporary file to use when outputting dependencies.
   libtool     Whether libtool is used (yes/no).
 
 Report bugs to <bug-automake at gnu.org>.
@@ -59,6 +56,66 @@ EOF
     ;;
 esac
 
+# Get the directory component of the given path, and save it in the
+# global variables '$dir'.  Note that this directory component will
+# be either empty or ending with a '/' character.  This is deliberate.
+set_dir_from ()
+{
+  case $1 in
+    */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
+      *) dir=;;
+  esac
+}
+
+# Get the suffix-stripped basename of the given path, and save it the
+# global variable '$base'.
+set_base_from ()
+{
+  base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
+}
+
+# If no dependency file was actually created by the compiler invocation,
+# we still have to create a dummy depfile, to avoid errors with the
+# Makefile "include basename.Plo" scheme.
+make_dummy_depfile ()
+{
+  echo "#dummy" > "$depfile"
+}
+
+# Factor out some common post-processing of the generated depfile.
+# Requires the auxiliary global variable '$tmpdepfile' to be set.
+aix_post_process_depfile ()
+{
+  # If the compiler actually managed to produce a dependency file,
+  # post-process it.
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form 'foo.o: dependency.h'.
+    # Do two passes, one to just change these to
+    #   $object: dependency.h
+    # and one to simply output
+    #   dependency.h:
+    # which is needed to avoid the deleted-header problem.
+    { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
+      sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
+    } > "$depfile"
+    rm -f "$tmpdepfile"
+  else
+    make_dummy_depfile
+  fi
+}
+
+# A tabulation character.
+tab='	'
+# A newline character.
+nl='
+'
+# Character ranges might be problematic outside the C locale.
+# These definitions help.
+upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
+lower=abcdefghijklmnopqrstuvwxyz
+digits=0123456789
+alpha=${upper}${lower}
+
 if test -z "$depmode" || test -z "$source" || test -z "$object"; then
   echo "depcomp: Variables source, object and depmode must be set" 1>&2
   exit 1
@@ -71,6 +128,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
 
 rm -f "$tmpdepfile"
 
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
 # Some modes work just like other modes, but use different flags.  We
 # parameterize here, but still list the modes in the big case below,
 # to make depend.m4 easier to write.  Note that we *cannot* use a case
@@ -82,9 +142,32 @@ if test "$depmode" = hp; then
 fi
 
 if test "$depmode" = dashXmstdout; then
-   # This is just like dashmstdout with a different argument.
-   dashmflag=-xM
-   depmode=dashmstdout
+  # This is just like dashmstdout with a different argument.
+  dashmflag=-xM
+  depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+  # This is just like msvisualcpp but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+  # This is just like msvc7 but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvc7
+fi
+
+if test "$depmode" = xlc; then
+  # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
+  gccflag=-qmakedep=gcc,-MF
+  depmode=gcc
 fi
 
 case "$depmode" in
@@ -107,8 +190,7 @@ gcc3)
   done
   "$@"
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
@@ -116,13 +198,17 @@ gcc3)
   ;;
 
 gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
 ## There are various ways to get dependency output from gcc.  Here's
 ## why we pick this rather obscure method:
 ## - Don't want to use -MD because we'd like the dependencies to end
 ##   up in a subdir.  Having to rename by hand is ugly.
 ##   (We might end up doing this anyway to support other compilers.)
 ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
-##   -MM, not -M (despite what the docs say).
+##   -MM, not -M (despite what the docs say).  Also, it might not be
+##   supported by the other compilers which use the 'gcc' depmode.
 ## - Using -M directly means running the compiler twice (even worse
 ##   than renaming).
   if test -z "$gccflag"; then
@@ -130,31 +216,31 @@ gcc)
   fi
   "$@" -Wp,"$gccflag$tmpdepfile"
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
   rm -f "$depfile"
   echo "$object : \\" > "$depfile"
-  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
-## The second -e expression handles DOS-style file names with drive letters.
+  # The second -e expression handles DOS-style file names with drive
+  # letters.
   sed -e 's/^[^:]*: / /' \
       -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
-## This next piece of magic avoids the `deleted header file' problem.
+## This next piece of magic avoids the "deleted header file" problem.
 ## The problem is that when a header file which appears in a .P file
 ## is deleted, the dependency causes make to die (because there is
 ## typically no way to rebuild the header).  We avoid this by adding
 ## dummy dependencies for each header file.  Too bad gcc doesn't do
 ## this for us directly.
-  tr ' ' '
-' < "$tmpdepfile" |
-## Some versions of gcc put a space before the `:'.  On the theory
+## Some versions of gcc put a space before the ':'.  On the theory
 ## that the space means something, we add a space to the output as
-## well.
+## well.  hp depmode also adds that space, but also prefixes the VPATH
+## to the object.  Take care to not repeat it in the output.
 ## Some versions of the HPUX 10.20 sed can't process this invocation
 ## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
@@ -172,8 +258,7 @@ sgi)
     "$@" -MDupdate "$tmpdepfile"
   fi
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
@@ -181,43 +266,41 @@ sgi)
 
   if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
     echo "$object : \\" > "$depfile"
-
     # Clip off the initial element (the dependent).  Don't try to be
     # clever and replace this with sed code, as IRIX sed won't handle
     # lines with more than a fixed number of characters (4096 in
     # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
-    # the IRIX cc adds comments like `#:fec' to the end of the
+    # the IRIX cc adds comments like '#:fec' to the end of the
     # dependency line.
-    tr ' ' '
-' < "$tmpdepfile" \
-    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
-    tr '
-' ' ' >> $depfile
-    echo >> $depfile
-
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
+      | tr "$nl" ' ' >> "$depfile"
+    echo >> "$depfile"
     # The second pass generates a dummy entry for each header file.
-    tr ' ' '
-' < "$tmpdepfile" \
-   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
-   >> $depfile
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+      >> "$depfile"
   else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
+    make_dummy_depfile
   fi
   rm -f "$tmpdepfile"
   ;;
 
+xlc)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
 aix)
   # The C for AIX Compiler uses -M and outputs the dependencies
   # in a .u file.  In older versions, this file always lives in the
-  # current directory.  Also, the AIX compiler puts `$object:' at the
+  # current directory.  Also, the AIX compiler puts '$object:' at the
   # start of each line; $object doesn't have directory information.
   # Version 6 uses the directory in both cases.
-  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-  test "x$dir" = "x$object" && dir=
-  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  set_dir_from "$object"
+  set_base_from "$object"
   if test "$libtool" = yes; then
     tmpdepfile1=$dir$base.u
     tmpdepfile2=$base.u
@@ -230,9 +313,7 @@ aix)
     "$@" -M
   fi
   stat=$?
-
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
     exit $stat
   fi
@@ -241,44 +322,100 @@ aix)
   do
     test -f "$tmpdepfile" && break
   done
-  if test -f "$tmpdepfile"; then
-    # Each line is of the form `foo.o: dependent.h'.
-    # Do two passes, one to just change these to
-    # `$object: dependent.h' and one to simply `dependent.h:'.
-    sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-    # That's a tab and a space in the [].
-    sed -e 's,^.*\.[a-z]*:[	 ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
-  else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
+  aix_post_process_depfile
+  ;;
+
+tcc)
+  # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
+  # FIXME: That version still under development at the moment of writing.
+  #        Make that this statement remains true also for stable, released
+  #        versions.
+  # It will wrap lines (doesn't matter whether long or short) with a
+  # trailing '\', as in:
+  #
+  #   foo.o : \
+  #    foo.c \
+  #    foo.h \
+  #
+  # It will put a trailing '\' even on the last line, and will use leading
+  # spaces rather than leading tabs (at least since its commit 0394caf7
+  # "Emit spaces for -MD").
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
   fi
+  rm -f "$depfile"
+  # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
+  # We have to change lines of the first kind to '$object: \'.
+  sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
+  # And for each line of the second kind, we have to emit a 'dep.h:'
+  # dummy dependency, to avoid the deleted-header problem.
+  sed -n -e 's|^  *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
-icc)
-  # Intel's C compiler understands `-MD -MF file'.  However on
-  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
-  # ICC 7.0 will fill foo.d with something like
-  #    foo.o: sub/foo.c
-  #    foo.o: sub/foo.h
-  # which is wrong.  We want:
-  #    sub/foo.o: sub/foo.c
-  #    sub/foo.o: sub/foo.h
-  #    sub/foo.c:
-  #    sub/foo.h:
-  # ICC 7.1 will output
+## The order of this option in the case statement is important, since the
+## shell code in configure will try each of these formats in the order
+## listed in this file.  A plain '-MD' option would be understood by many
+## compilers, so we must ensure this comes after the gcc and icc options.
+pgcc)
+  # Portland's C compiler understands '-MD'.
+  # Will always output deps to 'file.d' where file is the root name of the
+  # source file under compilation, even if file resides in a subdirectory.
+  # The object file name does not affect the name of the '.d' file.
+  # pgcc 10.2 will output
   #    foo.o: sub/foo.c sub/foo.h
-  # and will wrap long lines using \ :
+  # and will wrap long lines using '\' :
   #    foo.o: sub/foo.c ... \
   #     sub/foo.h ... \
   #     ...
+  set_dir_from "$object"
+  # Use the source, not the object, to determine the base name, since
+  # that's sadly what pgcc will do too.
+  set_base_from "$source"
+  tmpdepfile=$base.d
+
+  # For projects that build the same source file twice into different object
+  # files, the pgcc approach of using the *source* file root name can cause
+  # problems in parallel builds.  Use a locking strategy to avoid stomping on
+  # the same $tmpdepfile.
+  lockdir=$base.d-lock
+  trap "
+    echo '$0: caught signal, cleaning up...' >&2
+    rmdir '$lockdir'
+    exit 1
+  " 1 2 13 15
+  numtries=100
+  i=$numtries
+  while test $i -gt 0; do
+    # mkdir is a portable test-and-set.
+    if mkdir "$lockdir" 2>/dev/null; then
+      # This process acquired the lock.
+      "$@" -MD
+      stat=$?
+      # Release the lock.
+      rmdir "$lockdir"
+      break
+    else
+      # If the lock is being held by a different process, wait
+      # until the winning process is done or we timeout.
+      while test -d "$lockdir" && test $i -gt 0; do
+        sleep 1
+        i=`expr $i - 1`
+      done
+    fi
+    i=`expr $i - 1`
+  done
+  trap - 1 2 13 15
+  if test $i -le 0; then
+    echo "$0: failed to acquire lock after $numtries attempts" >&2
+    echo "$0: check lockdir '$lockdir'" >&2
+    exit 1
+  fi
 
-  "$@" -MD -MF "$tmpdepfile"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
@@ -290,8 +427,8 @@ icc)
   sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
   # Some versions of the HPUX 10.20 sed can't process this invocation
   # correctly.  Breaking it into two sed invocations is a workaround.
-  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
-    sed -e 's/$/ :/' >> "$depfile"
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
@@ -302,9 +439,8 @@ hp2)
   # 'foo.d', which lands next to the object file, wherever that
   # happens to be.
   # Much of this is similar to the tru64 case; see comments there.
-  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-  test "x$dir" = "x$object" && dir=
-  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  set_dir_from  "$object"
+  set_base_from "$object"
   if test "$libtool" = yes; then
     tmpdepfile1=$dir$base.d
     tmpdepfile2=$dir.libs/$base.d
@@ -315,8 +451,7 @@ hp2)
     "$@" +Maked
   fi
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
      rm -f "$tmpdepfile1" "$tmpdepfile2"
      exit $stat
   fi
@@ -326,72 +461,107 @@ hp2)
     test -f "$tmpdepfile" && break
   done
   if test -f "$tmpdepfile"; then
-    sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
-    # Add `dependent.h:' lines.
-    sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile"
+    sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
+    # Add 'dependent.h:' lines.
+    sed -ne '2,${
+               s/^ *//
+               s/ \\*$//
+               s/$/:/
+               p
+             }' "$tmpdepfile" >> "$depfile"
   else
-    echo "#dummy" > "$depfile"
+    make_dummy_depfile
   fi
   rm -f "$tmpdepfile" "$tmpdepfile2"
   ;;
 
 tru64)
-   # The Tru64 compiler uses -MD to generate dependencies as a side
-   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
-   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
-   # dependencies in `foo.d' instead, so we check for that too.
-   # Subdirectories are respected.
-   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-   test "x$dir" = "x$object" && dir=
-   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
-
-   if test "$libtool" = yes; then
-      # With Tru64 cc, shared objects can also be used to make a
-      # static library.  This mechanism is used in libtool 1.4 series to
-      # handle both shared and static libraries in a single compilation.
-      # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
-      #
-      # With libtool 1.5 this exception was removed, and libtool now
-      # generates 2 separate objects for the 2 libraries.  These two
-      # compilations output dependencies in $dir.libs/$base.o.d and
-      # in $dir$base.o.d.  We have to check for both files, because
-      # one of the two compilations can be disabled.  We should prefer
-      # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
-      # automatically cleaned when .libs/ is deleted, while ignoring
-      # the former would cause a distcleancheck panic.
-      tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4
-      tmpdepfile2=$dir$base.o.d          # libtool 1.5
-      tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5
-      tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504
-      "$@" -Wc,-MD
-   else
-      tmpdepfile1=$dir$base.o.d
-      tmpdepfile2=$dir$base.d
-      tmpdepfile3=$dir$base.d
-      tmpdepfile4=$dir$base.d
-      "$@" -MD
-   fi
-
-   stat=$?
-   if test $stat -eq 0; then :
-   else
-      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-      exit $stat
-   fi
-
-   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-   do
-     test -f "$tmpdepfile" && break
-   done
-   if test -f "$tmpdepfile"; then
-      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-      # That's a tab and a space in the [].
-      sed -e 's,^.*\.[a-z]*:[	 ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
-   else
-      echo "#dummy" > "$depfile"
-   fi
-   rm -f "$tmpdepfile"
-   ;;
+  # The Tru64 compiler uses -MD to generate dependencies as a side
+  # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+  # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+  # dependencies in 'foo.d' instead, so we check for that too.
+  # Subdirectories are respected.
+  set_dir_from  "$object"
+  set_base_from "$object"
+
+  if test "$libtool" = yes; then
+    # Libtool generates 2 separate objects for the 2 libraries.  These
+    # two compilations output dependencies in $dir.libs/$base.o.d and
+    # in $dir$base.o.d.  We have to check for both files, because
+    # one of the two compilations can be disabled.  We should prefer
+    # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+    # automatically cleaned when .libs/ is deleted, while ignoring
+    # the former would cause a distcleancheck panic.
+    tmpdepfile1=$dir$base.o.d          # libtool 1.5
+    tmpdepfile2=$dir.libs/$base.o.d    # Likewise.
+    tmpdepfile3=$dir.libs/$base.d      # Compaq CCC V6.2-504
+    "$@" -Wc,-MD
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    tmpdepfile3=$dir$base.d
+    "$@" -MD
+  fi
+
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  # Same post-processing that is required for AIX mode.
+  aix_post_process_depfile
+  ;;
+
+msvc7)
+  if test "$libtool" = yes; then
+    showIncludes=-Wc,-showIncludes
+  else
+    showIncludes=-showIncludes
+  fi
+  "$@" $showIncludes > "$tmpdepfile"
+  stat=$?
+  grep -v '^Note: including file: ' "$tmpdepfile"
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The first sed program below extracts the file names and escapes
+  # backslashes for cygpath.  The second sed program outputs the file
+  # name when reading, but also accumulates all include files in the
+  # hold buffer in order to output them again at the end.  This only
+  # works with sed implementations that can handle large buffers.
+  sed < "$tmpdepfile" -n '
+/^Note: including file:  *\(.*\)/ {
+  s//\1/
+  s/\\/\\\\/g
+  p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/'"$tab"'\1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+  s/.*/'"$tab"'/
+  G
+  p
+}' >> "$depfile"
+  echo >> "$depfile" # make sure the fragment doesn't end with a backslash
+  rm -f "$tmpdepfile"
+  ;;
+
+msvc7msys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
 
 #nosideeffect)
   # This comment above is used by automake to tell side-effect
@@ -404,13 +574,13 @@ dashmstdout)
 
   # Remove the call to Libtool.
   if test "$libtool" = yes; then
-    while test $1 != '--mode=compile'; do
+    while test "X$1" != 'X--mode=compile'; do
       shift
     done
     shift
   fi
 
-  # Remove `-o $object'.
+  # Remove '-o $object'.
   IFS=" "
   for arg
   do
@@ -430,18 +600,18 @@ dashmstdout)
   done
 
   test -z "$dashmflag" && dashmflag=-M
-  # Require at least two characters before searching for `:'
+  # Require at least two characters before searching for ':'
   # in the target name.  This is to cope with DOS-style filenames:
-  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+  # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
   "$@" $dashmflag |
-    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
+    sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
   rm -f "$depfile"
   cat < "$tmpdepfile" > "$depfile"
-  tr ' ' '
-' < "$tmpdepfile" | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this sed invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
@@ -455,41 +625,51 @@ makedepend)
   "$@" || exit $?
   # Remove any Libtool call
   if test "$libtool" = yes; then
-    while test $1 != '--mode=compile'; do
+    while test "X$1" != 'X--mode=compile'; do
       shift
     done
     shift
   fi
   # X makedepend
   shift
-  cleared=no
-  for arg in "$@"; do
+  cleared=no eat=no
+  for arg
+  do
     case $cleared in
     no)
       set ""; shift
       cleared=yes ;;
     esac
+    if test $eat = yes; then
+      eat=no
+      continue
+    fi
     case "$arg" in
     -D*|-I*)
       set fnord "$@" "$arg"; shift ;;
     # Strip any option that makedepend may not understand.  Remove
     # the object too, otherwise makedepend will parse it as a source file.
+    -arch)
+      eat=yes ;;
     -*|$object)
       ;;
     *)
       set fnord "$@" "$arg"; shift ;;
     esac
   done
-  obj_suffix="`echo $object | sed 's/^.*\././'`"
+  obj_suffix=`echo "$object" | sed 's/^.*\././'`
   touch "$tmpdepfile"
   ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
   rm -f "$depfile"
-  cat < "$tmpdepfile" > "$depfile"
-  sed '1,2d' "$tmpdepfile" | tr ' ' '
-' | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  # makedepend may prepend the VPATH from the source file name to the object.
+  # No need to regex-escape $object, excess matching of '.' is harmless.
+  sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process the last invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed '1,2d' "$tmpdepfile" \
+    | tr ' ' "$nl" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile" "$tmpdepfile".bak
   ;;
 
@@ -500,13 +680,13 @@ cpp)
 
   # Remove the call to Libtool.
   if test "$libtool" = yes; then
-    while test $1 != '--mode=compile'; do
+    while test "X$1" != 'X--mode=compile'; do
       shift
     done
     shift
   fi
 
-  # Remove `-o $object'.
+  # Remove '-o $object'.
   IFS=" "
   for arg
   do
@@ -525,10 +705,10 @@ cpp)
     esac
   done
 
-  "$@" -E |
-    sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-       -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
-    sed '$ s: \\$::' > "$tmpdepfile"
+  "$@" -E \
+    | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+             -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+    | sed '$ s: \\$::' > "$tmpdepfile"
   rm -f "$depfile"
   echo "$object : \\" > "$depfile"
   cat < "$tmpdepfile" >> "$depfile"
@@ -538,35 +718,56 @@ cpp)
 
 msvisualcpp)
   # Important note: in order to support this mode, a compiler *must*
-  # always write the preprocessed file to stdout, regardless of -o,
-  # because we must use -o when running libtool.
+  # always write the preprocessed file to stdout.
   "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
   IFS=" "
   for arg
   do
     case "$arg" in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
     "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
-	set fnord "$@"
-	shift
-	shift
-	;;
+        set fnord "$@"
+        shift
+        shift
+        ;;
     *)
-	set fnord "$@" "$arg"
-	shift
-	shift
-	;;
+        set fnord "$@" "$arg"
+        shift
+        shift
+        ;;
     esac
   done
-  "$@" -E |
-  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
+  "$@" -E 2>/dev/null |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
   rm -f "$depfile"
   echo "$object : \\" > "$depfile"
-  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::	\1 \\:p' >> "$depfile"
-  echo "	" >> "$depfile"
-  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+  echo "$tab" >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
+msvcmsys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
 none)
   exec "$@"
   ;;
@@ -585,5 +786,6 @@ exit 0
 # eval: (add-hook 'write-file-hooks 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
 # End:
diff --git a/include/editable.h b/include/editable.h
index df49470..d150ed9 100644
--- a/include/editable.h
+++ b/include/editable.h
@@ -1,49 +1,9 @@
-/*
-Copyright (C) 2001-2006, William Joseph.
-All Rights Reserved.
+#pragma once
 
-This file is part of GtkRadiant.
-
-GtkRadiant is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-GtkRadiant is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GtkRadiant; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#if !defined(INCLUDED_EDITABLE_H)
-#define INCLUDED_EDITABLE_H
-
-template<typename Element> class BasicVector3;
-typedef BasicVector3<double> Vector3;
-template<typename Element> class BasicVector4;
-typedef BasicVector4<double> Vector4;
-class Matrix4;
-class Quaternion;
+class AABB;
 
 #include "inode.h"
 
-class Editable
-{
-public:
-    virtual ~Editable() {}
-	virtual const Matrix4& getLocalPivot() const = 0;
-};
-typedef std::shared_ptr<Editable> EditablePtr;
-
-inline EditablePtr Node_getEditable(const scene::INodePtr& node)
-{
-	return std::dynamic_pointer_cast<Editable>(node);
-}
-
 class Snappable
 {
 public:
@@ -80,5 +40,3 @@ inline ComponentSnappablePtr Node_getComponentSnappable(const scene::INodePtr& n
 {
 	return std::dynamic_pointer_cast<ComponentSnappable>(node);
 }
-
-#endif
diff --git a/include/iaasfile.h b/include/iaasfile.h
new file mode 100644
index 0000000..46c61cb
--- /dev/null
+++ b/include/iaasfile.h
@@ -0,0 +1,162 @@
+#pragma once
+
+#include <memory>
+#include "imodule.h"
+#include <list>
+
+#include "math/Plane3.h"
+#include "math/Vector3.h"
+#include "math/AABB.h"
+
+namespace map
+{
+
+// An AAS type is defined by an entityDef block
+// Each AAS type has its own file extension
+struct AasType
+{
+    std::string entityDefName;
+    std::string fileExtension;
+};
+typedef std::list<AasType> AasTypeList;
+
+/**
+ * Representation of a Area Awareness System file.
+ * Provides read-only access to Area and Portal information.
+ * Use the GlobalAasFileManager() to acquire an instance of
+ * this class.
+ */
+class IAasFile
+{
+public:
+    virtual std::size_t     getNumPlanes() const = 0;
+    virtual const Plane3&   getPlane(std::size_t planeNum) const = 0;
+
+    virtual std::size_t     getNumVertices() const = 0;
+    virtual const Vector3&	getVertex(std::size_t vertexNum) const = 0;
+
+    // An edge references two vertices by index
+    struct Edge
+    {
+        int vertexNumber[2];
+    };
+
+    virtual std::size_t     getNumEdges() const = 0;
+    virtual const Edge&     getEdge(std::size_t index) const = 0;
+
+    virtual std::size_t     getNumEdgeIndexes() const = 0;
+    virtual int 			getEdgeByIndex(int edgeIdx) const = 0;
+
+    struct Face 
+    {
+	    int 				planeNum;   // number of the plane this face is on
+	    unsigned short		flags;      // face flags
+	    int					numEdges;   // number of edges in the boundary of the face
+	    int					firstEdge;  // first edge in the edge index
+	    short				areas[2];   // area at the front and back of this face
+    };
+
+    virtual std::size_t     getNumFaces() const = 0;
+    virtual const Face&		getFace(int faceIndex) const = 0;
+    virtual std::size_t     getNumFaceIndexes() const = 0;
+    virtual int 			getFaceByIndex(int faceIdx) const = 0;
+
+    struct Area
+    {
+        int             numFaces;			 // number of faces used for the boundary of the area
+        int             firstFace;			 // first face in the face index used for the boundary of the area
+        AABB            bounds;				 // bounds of the area
+        Vector3         center;				 // center of the area an AI can move towards
+        unsigned short  flags;				 // several area flags
+        unsigned short  contents;			 // contents of the area
+        short           cluster;			 // cluster the area belongs to, if negative it's a portal
+        short           clusterAreaNum;		 // number of the area in the cluster
+        int             travelFlags;		 // travel flags for traveling through this area
+    };
+
+    virtual std::size_t     getNumAreas() const = 0;
+    virtual const Area&     getArea(int areaNum) const = 0;
+};
+typedef std::shared_ptr<IAasFile> IAasFilePtr;
+
+/**
+ * A loader class capable of constructing an IAasFile instance from a token stream.
+ */
+class IAasFileLoader :
+    public RegisterableModule
+{
+public:
+    /**
+	 * Get the display name of this AAS file loader, e.g. "Doom 3", "Quake 4", etc.
+	 */
+	virtual const std::string& getAasFormatName() const = 0;
+
+	/**
+	 * Each MapFormat can have a certain game type it is designed for,
+	 * a value which conincides with the type attribute in the game tag
+	 * found in the .game file, e.g. "doom3" or "quake4".
+	 */
+	virtual const std::string& getGameType() const = 0;
+
+	/**
+	 * greebo: Returns true if this loader is able to parse
+	 * the contents of this file. Usually this includes a version
+	 * check of the file header.
+	 */
+	virtual bool canLoad(std::istream& stream) const = 0;
+
+    /**
+     * Load the AAS file contents from the given stream. 
+     */
+    virtual IAasFilePtr loadFromStream(std::istream& stream) = 0;
+};
+typedef std::shared_ptr<IAasFileLoader> IAasFileLoaderPtr;
+
+// Info structure representing a single AAS file on disk
+struct AasFileInfo
+{
+    std::string absolutePath;
+
+    AasType type;
+};
+
+class IAasFileManager : 
+    public RegisterableModule
+{
+public:
+    virtual ~IAasFileManager() {}
+
+    // Register a loader which is considered by all future AAS file load attempts
+    virtual void registerLoader(const IAasFileLoaderPtr& loader) = 0;
+
+    // Unregister a previously registered loader instance
+    virtual void unregisterLoader(const IAasFileLoaderPtr& loader) = 0;
+
+    // Get a loader capable of loading the given stream
+    virtual IAasFileLoaderPtr getLoaderForStream(std::istream& stream) = 0;
+
+    // Get the list of valid AAS types
+    virtual AasTypeList getAasTypes() = 0;
+
+    // Returns a specific AAS type. Will throw a std::runtime_error if the 
+    // type is not valid.
+    virtual AasType getAasTypeByName(const std::string& typeName) = 0;
+
+    // Returns a list of AAS files for the given map (absolute) map path
+    virtual std::list<AasFileInfo> getAasFilesForMap(const std::string& mapPath) = 0;
+};
+
+} // namespace
+
+const char* const MODULE_AASFILEMANAGER("ZAasFileManager");
+
+// Application-wide Accessor to the global AAS file manager
+inline map::IAasFileManager& GlobalAasFileManager()
+{
+	// Cache the reference locally
+	static map::IAasFileManager& _manager(
+		*std::static_pointer_cast<map::IAasFileManager>(
+		module::GlobalModuleRegistry().getModule(MODULE_AASFILEMANAGER))
+	);
+	return _manager;
+}
diff --git a/include/icameraview.h b/include/icameraview.h
index dfd1e92..7cff703 100644
--- a/include/icameraview.h
+++ b/include/icameraview.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "iinteractiveview.h"
+#include "math/Vector3.h"
 
 // Abstract class used when handling mouse events
 // see also: class IOrthoView in iorthoview.h
@@ -10,6 +11,17 @@ class ICameraView :
 public:
     virtual ~ICameraView() {}
 
+	// Move the camera's origin
+	virtual Vector3 getCameraOrigin() const = 0;
+	virtual void setCameraOrigin(const Vector3& newOrigin) = 0;
+
+	// Returns the vector pointing to the "right"
+	virtual Vector3 getRightVector() const = 0;
+	// Returns the vector pointing "up"
+	virtual Vector3 getUpVector() const = 0;
+	// Returns the vector pointing "forward"
+	virtual Vector3 getForwardVector() const = 0;
+
     // Freemove mode
     virtual void enableFreeMove() = 0;
     virtual void disableFreeMove() = 0;
diff --git a/include/ientity.h b/include/ientity.h
index af2e326..6ebbd0c 100644
--- a/include/ientity.h
+++ b/include/ientity.h
@@ -184,7 +184,13 @@ public:
      */
     virtual bool isModel() const = 0;
 
-  virtual bool isContainer() const = 0;
+	/**
+	 * Returns true if this entity is the worldspawn, which can be game-specific,
+	 * but is usually true if this entity's classname equals "worldspawn"
+	 */
+	virtual bool isWorldspawn() const = 0;
+
+	virtual bool isContainer() const = 0;
 
     /**
      * \brief
diff --git a/include/ientityinspector.h b/include/ientityinspector.h
index dfd71f6..d1456bd 100644
--- a/include/ientityinspector.h
+++ b/include/ientityinspector.h
@@ -43,6 +43,11 @@ public:
 	virtual wxPanel* getWidget() = 0;
 
 	/**
+	 * Instructs the editor to update its widgets from the edited entity's key values.
+	 */
+	virtual void updateFromEntity() = 0;
+
+	/**
 	 * Clone method for virtual construction. This method must create a new
 	 * PropertyEditor of the same type as the derive class which is implementing
 	 * the method.
diff --git a/include/igame.h b/include/igame.h
index 62385ac..55129df 100644
--- a/include/igame.h
+++ b/include/igame.h
@@ -55,6 +55,12 @@ class IGameManager :
 	public RegisterableModule
 {
 public:
+	// Returns the user's local engine path, on POSIX systems this might
+	// point to the folder in the home directory, e.g. ~/.doom3/ if it exists.
+	// If no engine directory is found in the home directory, the regular
+	// engine path is returned, e.g. /usr/local/doom3 or c:\games\doom3
+	virtual std::string getUserEnginePath() = 0;
+
 	// Returns the setting for fs_game
 	virtual const std::string& getFSGame() const = 0;
 
diff --git a/include/igroupdialog.h b/include/igroupdialog.h
index f34312f..205c317 100644
--- a/include/igroupdialog.h
+++ b/include/igroupdialog.h
@@ -38,9 +38,24 @@ public:
 		// to be displayed when this tab is active
 		std::string windowLabel;
 
-		// Optionally specify the name of an already added page to let this page
-		// be inserted at a specific point in the tab bar
-		std::string insertBefore;
+		// Define the order of the "native" group dialog pages
+		// Use this enum values to indicate which tab position 
+		// you need your page to have sorted at
+		struct Position
+		{
+			enum PredefinedValues
+			{
+				EntityInspector = 100,
+				MediaBrowser = 200,
+				Console = 300,
+				TextureBrowser = 400,
+				End = 5000
+			};
+		};
+
+		// Defines the position page in the group dialog (defaults to "End")
+		// See the predefined Position enum for already existing positions
+		int position = Position::End;
 	};
 	typedef std::shared_ptr<Page> PagePtr;
 
diff --git a/include/imap.h b/include/imap.h
index de05d11..62da416 100644
--- a/include/imap.h
+++ b/include/imap.h
@@ -2,6 +2,7 @@
 
 #include "imodule.h"
 #include "inode.h"
+#include <sigc++/signal.h>
 
 // Registry setting for suppressing the map load progress dialog
 const char* const RKEY_MAP_SUPPRESS_LOAD_STATUS_DIALOG = "user/ui/map/suppressMapLoadDialog";
@@ -59,12 +60,33 @@ class IMap :
 	public RegisterableModule
 {
 public:
+	enum MapEvent
+	{
+		MapLoading,     // emitted just before a map is starting to be loaded
+		MapLoaded,      // emitted when the current map is done loading
+		MapUnloading,   // emitted just before a map is unloaded from memory
+		MapUnloaded,    // emitted after a map has been unloaded
+	};
+
+	typedef sigc::signal<void, MapEvent> MapEventSignal;
+
+	/// Returns the signal that is emitted on various events
+	virtual MapEventSignal signal_mapEvent() const = 0;
+
 	/**
 	 * Returns the worldspawn node of this map. The worldspawn
 	 * node is NOT created if it doesn't exist yet, so this
 	 * might return an empty pointer.
 	 */
-	virtual scene::INodePtr getWorldspawn() = 0;
+	virtual const scene::INodePtr& getWorldspawn() = 0;
+
+	/**
+	 * This retrieves the worldspawn node of this map.
+	 * If no worldspawn can be found, this creates one.
+	 * Use this instead of getWorldSpawn() if your code needs
+	 * a worldspawn to work with.
+	 */
+	virtual const scene::INodePtr& findOrInsertWorldspawn() = 0;
 
 	/**
 	 * Returns the root node of this map or NULL if this is an empty map.
diff --git a/include/imapinfofile.h b/include/imapinfofile.h
new file mode 100644
index 0000000..1b961db
--- /dev/null
+++ b/include/imapinfofile.h
@@ -0,0 +1,157 @@
+#pragma once
+
+#include <string>
+#include <map>
+#include "imodule.h"
+#include "imap.h"
+#include <functional>
+
+namespace parser { class DefTokeniser; }
+namespace scene { class INode; typedef std::shared_ptr<INode> INodePtr; }
+
+namespace map
+{
+
+typedef std::pair<std::size_t, std::size_t> NodeIndexPair;
+typedef std::map<NodeIndexPair, scene::INodePtr> NodeIndexMap;
+
+/**
+ * An info file module is allowed to write text-based information
+ * to the auxiliary .darkradiant file that is written alongside to the 
+ * game-compatible .map file. Things like layer or selection set/group
+ * information can be stored persistently between mapping sessions this way.
+ *
+ * The module should write its information in named blocks, like 
+ *
+ * MyModuleInfo
+ * {
+ *      // arbitrary parseable info here
+ * }
+ *
+ * Later, when the info file is parsed after map load, the module will be asked 
+ * to parse the blocks it's responsible for, and apply its information to the map.
+ */
+class IMapInfoFileModule
+{
+public:
+	virtual ~IMapInfoFileModule() {}
+
+	// Info File Saving / Exporting
+
+	// The name of this info file module, mainly for internal book-keeping
+	virtual std::string getName() = 0;
+
+	/**
+	 * Called before any node is written to the .map file. Use tihs
+	 * to prepare the internal structures for exporting.
+	 */
+	virtual void onInfoFileSaveStart() = 0;
+
+	/**
+	 * Called during map export traversal when a single
+	 * primitive is about to be written to the .map file.
+	 * Assemble information about the primitive and save it
+	 * internally, until the writeBlocks() method is called.
+	 */
+	virtual void onSavePrimitive(const scene::INodePtr& node, 
+		std::size_t entityNum, std::size_t primitiveNum) = 0;
+
+	/**
+	* Called during map export traversal when a single
+	* entity is about to be written to the .map file.
+	* Assemble information about the entity and save it
+	* internally, until the writeBlocks() method is called.
+	*/
+	virtual void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) = 0;
+
+	/**
+	 * Final export function, write the assembled data to the
+	 * info file stream. This method should include the block file name
+	 * and the opening and closing braces in its write operation.
+	 */
+	virtual void writeBlocks(std::ostream& stream) = 0;
+
+	/**
+	 * Called before the info file stream is closed, time for cleanup.
+	 */
+	virtual void onInfoFileSaveFinished() = 0;
+
+	// Info File Loading / Parsing
+
+	/**
+	 * Called before the info file is loaded, so take tihs opportunity to 
+	 * clear internal structures that are going to be filled during the parse process.
+	 */
+	virtual void onInfoFileLoadStart() = 0;
+
+	/**
+	 * The info file parser will ask this module when a named block is encountered.
+	 * A module which returns true on a block will sign up for parsing the block
+	 * and a subsequent call to parseBlock() is imminent.
+	 */
+	virtual bool canParseBlock(const std::string& blockName) = 0;
+
+	/**
+	 * Parse a block as found in the info file. The block name as passed to this method
+	 * needs to be registered in the IMapInfoFileManager class before.
+	 *
+	 * Regarding the state of the tokeniser: the block name will already have been parsed 
+	 * by the time this method is called, so expect the opening brace { as first token.
+	 */
+	virtual void parseBlock(const std::string& blockName, parser::DefTokeniser& tok) = 0;
+
+	/**
+	 * Invoked by the map parsing code when the info file has been fully loaded,
+	 * so modules should now apply the loaded information to the map.
+	 * The info file is always loaded after the actual .map file, so this method
+	 * can assume that the scene graph is already in place.
+	 * For convenience, a NodeMap is passed to this method, mapping
+	 * the entity/primitive number combination to scene::INodes.
+	 */
+	virtual void applyInfoToScene(const scene::IMapRootNodePtr& root, const NodeIndexMap& nodeMap) = 0;
+
+	/**
+	 * Post-parsing cleanup routine, called after applyInfoToScene().
+	 */
+	virtual void onInfoFileLoadFinished() = 0;
+};
+typedef std::shared_ptr<IMapInfoFileModule> IMapInfoFileModulePtr;
+
+class IMapInfoFileManager :
+	public RegisterableModule
+{
+public:
+	virtual ~IMapInfoFileManager() 
+	{}
+
+	/**
+	 * Add an info file module to the global list. The module will be considered
+	 * during info file export/import.
+	 */
+	virtual void registerInfoFileModule(const IMapInfoFileModulePtr& module) = 0;
+
+	/**
+	 * Unregister a previouly registered info file module.
+	 */
+	virtual void unregisterInfoFileModule(const IMapInfoFileModulePtr& module) = 0;
+
+	/**
+	 * Call the functor for each registered module.
+	 */
+	virtual void foreachModule(const std::function<void(IMapInfoFileModule&)>& functor) = 0;
+};
+
+}
+
+const char* const MODULE_MAPINFOFILEMANAGER("MapInfoFileManager");
+
+// Application-wide Accessor to the global map info file manager
+inline map::IMapInfoFileManager& GlobalMapInfoFileManager()
+{
+	// Cache the reference locally
+	static map::IMapInfoFileManager& _manager(
+		*std::static_pointer_cast<map::IMapInfoFileManager>(
+			module::GlobalModuleRegistry().getModule(MODULE_MAPINFOFILEMANAGER))
+	);
+	return _manager;
+}
diff --git a/include/imapresource.h b/include/imapresource.h
index 663edb5..3e75f91 100644
--- a/include/imapresource.h
+++ b/include/imapresource.h
@@ -13,45 +13,40 @@ namespace map
 class IMapResource
 {
 public:
-	class Observer {
-	public:
-	    virtual ~Observer() {}
-		virtual void onResourceRealise() = 0;
-		virtual void onResourceUnrealise() = 0;
-	};
-
 	virtual ~IMapResource() {}
 
 	// Renames this map resource to the new path
 	virtual void rename(const std::string& fullPath) = 0;
 
 	virtual bool load() = 0;
-	virtual bool save(const map::MapFormatPtr& mapFormat = map::MapFormatPtr()) = 0;
 
-	// Reloads the map file from disk
-	virtual void reload() = 0;
+	/**
+	* Save this resource
+	*
+	* It's possible to pass a mapformat to be used for saving. If the map
+	* format argument is omitted, the format corresponding to the current
+	* game type is used.
+	*
+	* @returns
+	* true if the resource was saved, false otherwise.
+	*/
+	virtual bool save(const map::MapFormatPtr& mapFormat = map::MapFormatPtr()) = 0;
 
     virtual scene::IMapRootNodePtr getNode() = 0;
     virtual void setNode(const scene::IMapRootNodePtr& node) = 0;
-
-	virtual void addObserver(Observer& observer) = 0;
-	virtual void removeObserver(Observer& observer) = 0;
-
-	virtual void realise() = 0;
-	virtual void unrealise() = 0;
 };
 typedef std::shared_ptr<IMapResource> IMapResourcePtr;
 
-const std::string MODULE_MAPRESOURCEMANAGER("MapResourceManager");
+const char* const MODULE_MAPRESOURCEMANAGER("MapResourceManager");
 
 class IMapResourceManager :
 	public RegisterableModule
 {
 public:
 	/**
-	 * Capture a named model resource, and return a pointer to it.
+	 * Load the named map resource from VFS or from a physical path.
 	 */
-	virtual IMapResourcePtr capture(const std::string& path) = 0;
+	virtual IMapResourcePtr loadFromPath(const std::string& path) = 0;
 };
 
 inline IMapResourceManager& GlobalMapResourceManager() {
diff --git a/include/inode.h b/include/inode.h
index da8a5a6..b87dd98 100644
--- a/include/inode.h
+++ b/include/inode.h
@@ -145,8 +145,20 @@ public:
 	 */
 	virtual bool excluded() const = 0;
 
+	// Set the "forced visible" flag, which overrides the ordinary filtered/excluded state
+	// This is used to force the rendering of nodes that are selected but would otherwise
+	// be hidden due to the filtered/layered state.
+	virtual void setForcedVisibility(bool forceVisible, bool includeChildren) = 0;
+
 	// Child node handling
+
+	// Adds a new child node (is appended at the end of the list of existing children)
 	virtual void addChildNode(const INodePtr& node) = 0;
+
+	// Adds a new child node (is inserted at the front of any existing children)
+	// Useful in special scenarios like when adding a world spawn node.
+	virtual void addChildNodeToFront(const INodePtr& node) = 0;
+
 	virtual void removeChildNode(const INodePtr& node) = 0;
 	virtual bool hasChildNodes() const = 0;
 
diff --git a/include/iorthocontextmenu.h b/include/iorthocontextmenu.h
index fedb5a7..fd0a3dc 100644
--- a/include/iorthocontextmenu.h
+++ b/include/iorthocontextmenu.h
@@ -16,6 +16,7 @@ public:
 	{
 		SECTION_CREATE,	// Create Entity, Create Speaker, etc.
 		SECTION_ACTION, // Make Visportals
+		SECTION_SELECTION_GROUPS, // Selection Groups
 		SECTION_LAYER,	// Layer operations
 		SECTION_USER = 100,
 	};
diff --git a/include/ipatch.h b/include/ipatch.h
index 70658df..9b5eb52 100644
--- a/include/ipatch.h
+++ b/include/ipatch.h
@@ -154,11 +154,11 @@ public:
 	/**
 	 * greebo: Sets/gets whether this patch is a patchDef3 (fixed tesselation)
 	 */
-	virtual bool subdivionsFixed() const = 0;
+	virtual bool subdivisionsFixed() const = 0;
 
 	/** greebo: Returns the x,y subdivision values (for tesselation)
 	 */
-	virtual Subdivisions getSubdivisions() const = 0;
+	virtual const Subdivisions& getSubdivisions() const = 0;
 
 	/** greebo: Sets the subdivision of this patch
 	 *
diff --git a/include/ipreferencesystem.h b/include/ipreferencesystem.h
index facb035..07610ae 100644
--- a/include/ipreferencesystem.h
+++ b/include/ipreferencesystem.h
@@ -1,38 +1,34 @@
 #pragma once
 
 #include <list>
-#include <vector>
 #include "imodule.h"
 
 // A list containing possible values for a combo box widgets
 typedef std::list<std::string> ComboBoxValueList;
-typedef std::vector<std::string> IconList;
-typedef std::vector<std::string> IconDescriptionList;
 
 /* greebo: This is the interface the preference page has to provide for adding
  * elements to the dialog page. */
-class PreferencesPage
+class IPreferencePage
 {
 public:
     // destructor
-	virtual ~PreferencesPage() {}
+	virtual ~IPreferencePage() {}
 
-	/** greebo: Allows to set a custom title of this page. The default title
-	 * 			upon construction is "guessed" by adding a " Settings" to
-	 * 			the page name, "Settings/Patch" gets assigned
-	 * 			a "Patch Settings" as default title.
-	 * 			Use this method to change this to fit your needs.
+	/** 
+	 * greebo: Allows to set a custom title of this page. The default title
+	 * upon construction is "guessed" by adding a " Settings" to
+	 * the page name, "Settings/Patch" gets assigned
+	 * a "Patch Settings" as default title.
+	 * Use this method to change this to fit your needs.
 	 */
 	virtual void setTitle(const std::string& title) = 0;
 
 	// greebo: Use this to add a checkbox to the preference dialog that is connected to a registry value
-	virtual void appendCheckBox(const std::string& name, const std::string& flag, const std::string& registryKey) = 0;
+	virtual void appendCheckBox(const std::string& label, const std::string& registryKey) = 0;
 
-	/* greebo: This adds a horizontal slider to the internally referenced VBox and connects
-	 * it to the given registryKey. */
-	virtual void appendSlider(const std::string& name, const std::string& registryKey, bool drawValue,
-							  double value, double lower, double upper,
-							  double step_increment, double page_increment, double page_size) = 0;
+	// greebo: This adds a horizontal slider and connects it to the given registryKey.
+	virtual void appendSlider(const std::string& name, const std::string& registryKey, 
+		double lower, double upper, double step_increment, double page_increment) = 0;
 
    /**
     * \brief
@@ -76,30 +72,30 @@ public:
 	// Appends a static label (to add some text to the preference page)
 	virtual void appendLabel(const std::string& caption) = 0;
 };
-typedef std::shared_ptr<PreferencesPage> PreferencesPagePtr;
 
-const std::string MODULE_PREFERENCESYSTEM("PreferenceSystem");
+const char* const MODULE_PREFERENCESYSTEM("PreferenceSystem");
 
 class IPreferenceSystem :
 	public RegisterableModule
 {
 public:
-	/** greebo: Retrieves the page for the given path, for example:
+	/** 
+	 * greebo: Retrieves the page for the given path. If the page 
+	 * doesn't exist yet, it will be created at the given path, for example:
 	 *
-	 * 			"Settings/Patch Settings"
-	 * 			(spaces are ok, slashes are treated as delimiters, don't use them)
+	 * "Settings/Patch Settings"
+	 * (spaces are ok, slashes are treated as delimiters, don't use them in the page name)
 	 *
-	 * 			Use the PreferencesPage interface to add widgets
-	 * 			and connect them to the registry.
-	 *
-	 * @path: The path to lookup
-	 *
-	 * @returns: the PreferencesPage pointer.
+	 * Use the page interface to add widgets and connect them to registry keys.
+
+	 * @path: The path to lookup/create
+	 * @returns: the IPreferencePage reference.
 	 */
-	virtual PreferencesPagePtr getPage(const std::string& path) = 0;
+	virtual IPreferencePage& getPage(const std::string& path) = 0;
 };
 
-inline IPreferenceSystem& GlobalPreferenceSystem() {
+inline IPreferenceSystem& GlobalPreferenceSystem()
+{
 	// Cache the reference locally
 	static IPreferenceSystem& _prefSystem(
 		*std::static_pointer_cast<IPreferenceSystem>(
diff --git a/include/irenderable.h b/include/irenderable.h
index 082b539..bc53266 100644
--- a/include/irenderable.h
+++ b/include/irenderable.h
@@ -106,11 +106,18 @@ public:
      */
 	virtual bool supportsFullMaterials() const = 0;
 
-    /// Highlight faces of subsequently-submitted objects, if supported
-    virtual void highlightFaces(bool enable) = 0;
+	struct Highlight
+	{
+		enum Flags
+		{
+			NoHighlight	= 0,
+			Faces		= 1 << 0, /// Highlight faces of subsequently-submitted objects, if supported
+			Primitives	= 1 << 1, /// Highlight primitives of subsequently-submitted objects, if supported
+			GroupMember	= 1 << 2, /// Highlight as member of group, if supported
+		};
+	};
 
-    /// Highlight primitives of subsequently-submitted objects, if supported
-    virtual void highlightPrimitives(bool enable) = 0;
+	virtual void setHighlightFlag(Highlight::Flags flags, bool enabled) = 0;
 
   	/**
   	 * Set the list of lights to be used for lighting-mode rendering. This
@@ -161,10 +168,19 @@ public:
 	virtual void viewChanged() const
 	{ }
 
+	struct Highlight
+	{
+		enum Flags
+		{
+			NoHighlight = 0,
+			Selected	= 1 << 0,
+			GroupMember	= 1 << 1,
+		};
+	};
+
 	/**
-	 * Method to determine whether this node should be rendered as highlighted.
-	 * This is usually true for selected nodes.
+	 * Returns information about whether the renderer should highlight this node and how.
 	 */
-	virtual bool isHighlighted() const = 0;
+	virtual std::size_t getHighlightFlags() = 0;
 };
 typedef std::shared_ptr<Renderable> RenderablePtr;
diff --git a/include/iselectable.h b/include/iselectable.h
index aada844..a70aef1 100644
--- a/include/iselectable.h
+++ b/include/iselectable.h
@@ -1,25 +1,24 @@
 #pragma once
 
+#include <memory>
+
 /**
  * greebo: A Selectable is everything that can be highlighted
- *         by the user in the scene (e.g. by interaction with the mouse).
+ * by the user in the scene (e.g. by interaction with the mouse).
  */
-class Selectable
+class ISelectable
 {
 public:
     // destructor
-	virtual ~Selectable() {}
+	virtual ~ISelectable() {}
 
 	// Set the selection status of this object
 	virtual void setSelected(bool select) = 0;
 
 	// Check the selection status of this object (TRUE == selected)
 	virtual bool isSelected() const = 0;
-
-	// Toggle the selection status
-	virtual void invertSelected() = 0;
 };
-typedef std::shared_ptr<Selectable> SelectablePtr;
+typedef std::shared_ptr<ISelectable> ISelectablePtr;
 
 namespace scene
 {
@@ -27,14 +26,14 @@ namespace scene
 	typedef std::shared_ptr<INode> INodePtr;
 }
 
-inline SelectablePtr Node_getSelectable(const scene::INodePtr& node)
+inline ISelectablePtr Node_getSelectable(const scene::INodePtr& node)
 {
-    return std::dynamic_pointer_cast<Selectable>(node);
+    return std::dynamic_pointer_cast<ISelectable>(node);
 }
 
 inline void Node_setSelected(const scene::INodePtr& node, bool selected)
 {
-    SelectablePtr selectable = Node_getSelectable(node);
+	ISelectablePtr selectable = Node_getSelectable(node);
 
     if (selectable)
 	{
@@ -44,7 +43,7 @@ inline void Node_setSelected(const scene::INodePtr& node, bool selected)
 
 inline bool Node_isSelected(const scene::INodePtr& node)
 {
-    SelectablePtr selectable = Node_getSelectable(node);
+	ISelectablePtr selectable = Node_getSelectable(node);
 
     if (selectable)
 	{
diff --git a/include/iselection.h b/include/iselection.h
index 50bdb21..cc41c57 100644
--- a/include/iselection.h
+++ b/include/iselection.h
@@ -8,7 +8,7 @@
 class RenderableCollector;
 namespace render { class View; }
 
-class Selectable;
+class ISelectable;
 
 
 namespace scene
@@ -26,8 +26,8 @@ typedef BasicVector4<double> Vector4;
 class Matrix4;
 class Quaternion;
 
-typedef sigc::signal<void, const Selectable&> SelectionChangedSignal;
-typedef sigc::slot<void, const Selectable&> SelectionChangedSlot;
+typedef sigc::signal<void, const ISelectable&> SelectionChangedSignal;
+typedef sigc::slot<void, const ISelectable&> SelectionChangedSlot;
 
 class SelectionInfo;
 class Face;
@@ -103,8 +103,8 @@ public:
 
   virtual std::size_t countSelected() const = 0;
   virtual std::size_t countSelectedComponents() const = 0;
-  virtual void onSelectedChanged(const scene::INodePtr& node, const Selectable& selectable) = 0;
-  virtual void onComponentSelection(const scene::INodePtr& node, const Selectable& selectable) = 0;
+  virtual void onSelectedChanged(const scene::INodePtr& node, const ISelectable& selectable) = 0;
+  virtual void onComponentSelection(const scene::INodePtr& node, const ISelectable& selectable) = 0;
 
 	virtual scene::INodePtr ultimateSelected() = 0;
 	virtual scene::INodePtr penultimateSelected() = 0;
diff --git a/include/iselectiongroup.h b/include/iselectiongroup.h
new file mode 100644
index 0000000..d70d36d
--- /dev/null
+++ b/include/iselectiongroup.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include "imodule.h"
+#include "iselectable.h"
+#include <sigc++/signal.h>
+
+// GroupSelectables are regular selectables which can be part of 
+// one or more SelectionGroups.
+class IGroupSelectable :
+	public ISelectable
+{
+public:
+	typedef std::vector<std::size_t> GroupIds;
+
+	virtual ~IGroupSelectable() {}
+
+	// Adds this item to the group specified by its ID
+	virtual void addToGroup(std::size_t groupId) = 0;
+
+	// Removes this item from the group specified by its ID
+	virtual void removeFromGroup(std::size_t groupId) = 0;
+
+	// Returns true if this node is member of any group
+	virtual bool isGroupMember() = 0;
+
+	// Returns the group this node has been added to last
+	// This represents the currently "active" group ID
+	// Will throw an exception if this node is not a member of any group
+	virtual std::size_t getMostRecentGroupId() = 0;
+
+	// Returns all group assignments of this node
+	// The most recently added group is at the back of the list
+	virtual const GroupIds& getGroupIds() = 0;
+
+	// Special overload to control whether this selectable should propagate
+	// the status change to the group it belongs to.
+	virtual void setSelected(bool select, bool changeGroupStatus) = 0;
+};
+
+namespace selection
+{
+
+// Represents a SelectionGroup which can contain 0 or more IGroupSelectable nodes.
+class ISelectionGroup
+{
+public:
+	virtual ~ISelectionGroup() {}
+
+	// Returns the ID of this group
+	virtual std::size_t getId() const = 0;
+
+	// Gets the name of this group
+	virtual const std::string& getName() const = 0;
+
+	// Sets the name of this group
+	virtual void setName(const std::string& name) = 0;
+
+	// Adds the given node to this group. The node should be a IGroupSelectable
+	// which will be checked internally. If the node is not matching, nothing happens.
+	virtual void addNode(const scene::INodePtr& node) = 0;
+
+	// Remvoes the given node from this group. The node should be a IGroupSelectable
+	// which will be checked internally. If the node is not matching, nothing happens.
+	// The group will not be removed if this was the last member node
+	virtual void removeNode(const scene::INodePtr& node) = 0;
+	
+	// Returns the number of nodes in this group
+	virtual std::size_t size() const = 0;
+
+	// Sets the selection status of all the nodes in this group
+	virtual void setSelected(bool selected) = 0;
+
+	// Calls the given functor for each node in this group.
+	// The functor should not change the membership of this group, this will likely lead
+	// to internal iterator corruption.
+	virtual void foreachNode(const std::function<void(const scene::INodePtr&)>& functor) = 0;
+};
+typedef std::shared_ptr<ISelectionGroup> ISelectionGroupPtr;
+
+class ISelectionGroupManager :
+	public RegisterableModule
+{
+public:
+	virtual ~ISelectionGroupManager() {}
+	
+	// Creates a new selection group. The group is stored within the SelectionGroupManager
+	// so the returned shared_ptr can safely be let go by the client code.
+	// In the pathological case of being run out of IDs this will throw a std::runtime_error
+	virtual ISelectionGroupPtr createSelectionGroup() = 0;
+
+	// Tries to get a selection group by ID. Returns an empty ptr if the ID doesn't exist
+	virtual ISelectionGroupPtr getSelectionGroup(std::size_t id) = 0;
+
+	// Unline getSelectionGroup() this will create the group if it doesn't exist
+	virtual ISelectionGroupPtr findOrCreateSelectionGroup(std::size_t id) = 0;
+
+	// Sets the selection status of all members of the given group
+	virtual void setGroupSelected(std::size_t id, bool selected) = 0;
+
+	// Deletes all selection groups
+	virtual void deleteAllSelectionGroups() = 0;
+
+	// Deletes the group with the given ID. All nodes will be removed from this group as well.
+	virtual void deleteSelectionGroup(std::size_t id) = 0;
+};
+
+} // namespace
+
+const char* const MODULE_SELECTIONGROUP = "SelectionGroupManager";
+
+inline selection::ISelectionGroupManager& GlobalSelectionGroupManager()
+{
+	// Cache the reference locally
+	static selection::ISelectionGroupManager& _manager(
+		*std::static_pointer_cast<selection::ISelectionGroupManager>(
+			module::GlobalModuleRegistry().getModule(MODULE_SELECTIONGROUP)
+		)
+	);
+	return _manager;
+}
diff --git a/include/iselectiontest.h b/include/iselectiontest.h
index 9bdc0bb..48f40f3 100644
--- a/include/iselectiontest.h
+++ b/include/iselectiontest.h
@@ -234,19 +234,19 @@ class Selector
 {
 public:
   virtual ~Selector() {}
-  virtual void pushSelectable(Selectable& selectable) = 0;
+  virtual void pushSelectable(ISelectable& selectable) = 0;
   virtual void popSelectable() = 0;
   virtual void addIntersection(const SelectionIntersection& intersection) = 0;
 };
 
-inline void Selector_add(Selector& selector, Selectable& selectable)
+inline void Selector_add(Selector& selector, ISelectable& selectable)
 {
   selector.pushSelectable(selectable);
   selector.addIntersection(SelectionIntersection(0, 0));
   selector.popSelectable();
 }
 
-inline void Selector_add(Selector& selector, Selectable& selectable, const SelectionIntersection& intersection)
+inline void Selector_add(Selector& selector, ISelectable& selectable, const SelectionIntersection& intersection)
 {
   selector.pushSelectable(selectable);
   selector.addIntersection(intersection);
@@ -271,6 +271,7 @@ public:
     virtual ~ComponentSelectionTestable() {}
 	virtual bool isSelectedComponents() const = 0;
 	virtual void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) = 0;
+	virtual void invertSelectedComponents(SelectionSystem::EComponentMode mode) = 0;
 	virtual void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) = 0;
 };
 typedef std::shared_ptr<ComponentSelectionTestable> ComponentSelectionTestablePtr;
diff --git a/include/iuimanager.h b/include/iuimanager.h
index 0fdb3bb..196a793 100644
--- a/include/iuimanager.h
+++ b/include/iuimanager.h
@@ -159,8 +159,10 @@ public:
 	 *        if no icon is desired.
 	 * @pos: the position to insert. Use POS_FRONT or POS_BACK to put the element
 	 *       at the front or back of the status bar container.
+	 * @description: a description shown when the mouse pointer hovers of this item.
  	 */
-	virtual void addTextElement(const std::string& name, const std::string& icon, int pos) = 0;
+	virtual void addTextElement(const std::string& name, const std::string& icon, int pos,
+								const std::string& description) = 0;
 
 	/**
 	 * Updates the content of the named text element. The name must refer to
diff --git a/include/precompiled_interfaces.h b/include/precompiled_interfaces.h
index fad4093..6cbe544 100644
--- a/include/precompiled_interfaces.h
+++ b/include/precompiled_interfaces.h
@@ -45,6 +45,7 @@
 #include "imainframelayout.h"
 #include "imap.h"
 #include "imapformat.h"
+#include "imapinfofile.h"
 //#include "imapcompiler.h"
 #include "imapresource.h"
 #include "imd5anim.h"
@@ -78,6 +79,7 @@
 #include "iscript.h"
 #include "iselectable.h"
 #include "iselection.h"
+#include "iselectiongroup.h"
 #include "iselectionset.h"
 #include "iselectiontest.h"
 #include "ishaderexpression.h"
diff --git a/include/version.h b/include/version.h
index fe36bf6..639eefa 100644
--- a/include/version.h
+++ b/include/version.h
@@ -2,7 +2,7 @@
 #include <config.h>
 #define RADIANT_VERSION PACKAGE_VERSION
 #else
-#define RADIANT_VERSION "2.0.4"
+#define RADIANT_VERSION "2.1.0"
 #endif
 
 #define RADIANT_APPNAME "DarkRadiant"
diff --git a/install/bitmaps/empty.png b/install/bitmaps/empty.png
index c5fbfa2..c400b22 100644
Binary files a/install/bitmaps/empty.png and b/install/bitmaps/empty.png differ
diff --git a/install/bitmaps/group_selection.png b/install/bitmaps/group_selection.png
new file mode 100644
index 0000000..b6b98d5
Binary files /dev/null and b/install/bitmaps/group_selection.png differ
diff --git a/install/bitmaps/ungroup_selection.png b/install/bitmaps/ungroup_selection.png
new file mode 100644
index 0000000..7a51274
Binary files /dev/null and b/install/bitmaps/ungroup_selection.png differ
diff --git a/install/i18n/darkradiant.pot b/install/i18n/darkradiant.pot
index b55290c..9f99a86 100644
--- a/install/i18n/darkradiant.pot
+++ b/install/i18n/darkradiant.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: greebo at angua.at\n"
-"POT-Creation-Date: 2016-01-05 11:54+0100\n"
+"POT-Creation-Date: 2016-11-18 18:32+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -79,11 +79,11 @@ msgstr ""
 msgid "Solid selection boxes"
 msgstr ""
 
-#: ..\..\radiant\camera\CameraSettings.cpp:75
+#: ..\..\radiant\camera\CameraSettings.cpp:74
 msgid "Show camera toolbar"
 msgstr ""
 
-#: ..\..\radiant\camera\FloatingCamWnd.cpp:18 xml_file_content.cpp:60
+#: ..\..\radiant\camera\FloatingCamWnd.cpp:18 xml_file_content.cpp:61
 msgid "Camera"
 msgstr ""
 
@@ -95,27 +95,31 @@ msgstr ""
 msgid "Jump to Object"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:97
+#: ..\..\radiant\camera\tools\PanViewTool.h:23
+msgid "Pan Camera View"
+msgstr ""
+
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:116
 msgid "Pick Shader"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:120
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:139
 msgid "Paste Shader Projected"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:138
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:158
 msgid "Paste Shader Natural"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:156
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:177
 msgid "Paste Texture Coordinates"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:174
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:196
 msgid "Paste Shader to all Brush Faces"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:192
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:215
 msgid "Paste Shader Name"
 msgstr ""
 
@@ -131,40 +135,40 @@ msgstr ""
 msgid "Caulk shader name"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:34
+#: ..\..\radiant\layers\LayerSystem.cpp:36
 msgid "Default"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:37
+#: ..\..\radiant\layers\LayerSystem.cpp:39
 msgid "Create Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:39
+#: ..\..\radiant\layers\LayerSystem.cpp:41
 msgid "Add to Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:40
+#: ..\..\radiant\layers\LayerSystem.cpp:42
 msgid "Move to Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:41
+#: ..\..\radiant\layers\LayerSystem.cpp:43
 msgid "Remove from Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:536
+#: ..\..\radiant\layers\LayerSystem.cpp:550
 #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1537
 msgid "Enter Name"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:537
+#: ..\..\radiant\layers\LayerSystem.cpp:551
 msgid "Enter Layer Name"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:549
+#: ..\..\radiant\layers\LayerSystem.cpp:563
 msgid "Cannot create layer with empty name."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:563
+#: ..\..\radiant\layers\LayerSystem.cpp:577
 msgid "This name already exists."
 msgstr ""
 
@@ -195,33 +199,39 @@ msgstr ""
 msgid "Loading entity %d\n"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:299
+#: ..\..\radiant\map\AutoSaver.cpp:276
 msgid "Settings/Autosave"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:302
+#: ..\..\radiant\map\AutoSaver.cpp:279
 msgid "Enable Autosave"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:303
+#: ..\..\radiant\map\AutoSaver.cpp:280
 msgid "Autosave Interval (in minutes)"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:305
+#: ..\..\radiant\map\AutoSaver.cpp:282
 msgid "Save Snapshots"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:306
+#: ..\..\radiant\map\AutoSaver.cpp:283
 msgid "Snapshot folder (relative to map folder)"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:307
+#: ..\..\radiant\map\AutoSaver.cpp:284
 msgid "Max Snapshot Folder size (MB)"
 msgstr ""
 
-#: ..\..\radiant\map\CounterManager.cpp:67
+#: ..\..\radiant\map\CounterManager.cpp:63
+msgid ""
+"Number of brushes/patches/entities in this map\n"
+"(Number of selected items shown in parentheses)"
+msgstr ""
+
+#: ..\..\radiant\map\CounterManager.cpp:81
 #, c-format
-msgid "Brushes: %lu Patches: %lu Entities: %lu"
+msgid "Brushes: %lu (%lu) Patches: %lu (%lu) Entities: %lu (%lu)"
 msgstr ""
 
 #: ..\..\radiant\map\FindMapElements.cpp:95
@@ -236,92 +246,92 @@ msgstr ""
 msgid "Brush Number:"
 msgstr ""
 
-#: ..\..\radiant\map\InfoFile.cpp:73
+#: ..\..\radiant\map\infofile\InfoFile.cpp:70
 msgid "Map Info File Version invalid"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:59
+#: ..\..\radiant\map\Map.cpp:62
 msgid "unnamed.map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:182 ..\..\radiant\map\Map.cpp:518
-#: ..\..\radiant\map\Map.cpp:610 ..\..\radiant\map\Map.cpp:638
-#: ..\..\radiant\RadiantModule.cpp:134
+#: ..\..\radiant\map\Map.cpp:106 ..\..\radiant\map\Map.cpp:431
+#: ..\..\radiant\map\Map.cpp:522 ..\..\radiant\map\Map.cpp:550
+#: ..\..\radiant\RadiantModule.cpp:135
 #: ..\..\radiant\referencecache\ModelCache.cpp:245
 #: ..\..\radiant\referencecache\ModelCache.cpp:261
-#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:319
+#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:321
 msgid "Processing..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:182
+#: ..\..\radiant\map\Map.cpp:106
 msgid "Loading textures..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:518
+#: ..\..\radiant\map\Map.cpp:431
 msgid "Saving Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:569
+#: ..\..\radiant\map\Map.cpp:481
 msgid "Importing..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:664
+#: ..\..\radiant\map\Map.cpp:576
 #, c-format
 msgid ""
 "Save changes to map \"%s\"\n"
 "before closing?"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:672
+#: ..\..\radiant\map\Map.cpp:584
 #, c-format
 msgid "%d minutes"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:676
+#: ..\..\radiant\map\Map.cpp:588
 #, c-format
 msgid "%d seconds"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:680
+#: ..\..\radiant\map\Map.cpp:592
 #, c-format
 msgid ""
 "If you don't save, changes from the last %s\n"
 "will be lost."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:738
+#: ..\..\radiant\map\Map.cpp:650
 msgid "Save Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:778
+#: ..\..\radiant\map\Map.cpp:690
 msgid "Save Copy As..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:854
+#: ..\..\radiant\map\Map.cpp:781
 msgid "New Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:865 ..\..\radiant\ui\mru\MRU.cpp:93
+#: ..\..\radiant\map\Map.cpp:790 ..\..\radiant\ui\mru\MRU.cpp:93
 msgid "Open Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:869
+#: ..\..\radiant\map\Map.cpp:794
 msgid "Open map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:883
+#: ..\..\radiant\map\Map.cpp:808
 msgid "Import map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:912
+#: ..\..\radiant\map\Map.cpp:837
 msgid "Export selection"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:927
+#: ..\..\radiant\map\Map.cpp:852
 msgid "Save selected as Prefab"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:1008
+#: ..\..\radiant\map\Map.cpp:933
 #, c-format
 msgid ""
 "Failure reading map from clipboard:\n"
@@ -332,39 +342,32 @@ msgstr ""
 msgid "Map"
 msgstr ""
 
-#: ..\..\radiant\map\MapFileManager.cpp:36 xml_file_content.cpp:89
+#: ..\..\radiant\map\MapFileManager.cpp:36 xml_file_content.cpp:90
 msgid "Region"
 msgstr ""
 
 #: ..\..\radiant\map\MapFileManager.cpp:37
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:327
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:343
 msgid "Prefab"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:273 ..\..\radiant\map\MapResource.cpp:641
+#: ..\..\radiant\map\MapResource.cpp:257 ..\..\radiant\map\MapResource.cpp:529
 #, c-format
 msgid "File is write-protected: %s"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:434
-#, c-format
-msgid ""
-"Failure opening map file:\n"
-"%s"
-msgstr ""
-
-#: ..\..\radiant\map\MapResource.cpp:476
+#: ..\..\radiant\map\MapResource.cpp:363
 #, c-format
 msgid ""
 "Could not determine map format of file:\n"
 "%s"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:602
+#: ..\..\radiant\map\MapResource.cpp:410
 msgid "Map loading cancelled"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:614
+#: ..\..\radiant\map\MapResource.cpp:422
 #, c-format
 msgid ""
 "Failure reading map file:\n"
@@ -373,40 +376,47 @@ msgid ""
 "%s"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:706
+#: ..\..\radiant\map\MapResource.cpp:485 ..\..\radiant\map\MapResource.cpp:504
+#, c-format
+msgid ""
+"Failure opening file:\n"
+"%s"
+msgstr ""
+
+#: ..\..\radiant\map\MapResource.cpp:594
 msgid "Map writing cancelled"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:721
+#: ..\..\radiant\map\MapResource.cpp:609
 msgid "Could not open output streams for writing"
 msgstr ""
 
-#: ..\..\radiant\map\PointFile.cpp:116
+#: ..\..\radiant\map\PointFile.cpp:126
 #, c-format
 msgid "Could not open pointfile: %s"
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:203
+#: ..\..\radiant\map\RegionManager.cpp:173
 msgid "Warning: Camera not within region, can't set info_player_start."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:293
+#: ..\..\radiant\map\RegionManager.cpp:261
 msgid "Could not set Region: XY Top View not found."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:320
+#: ..\..\radiant\map\RegionManager.cpp:288
 msgid "Could not set Region: please select a single Brush."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:345
+#: ..\..\radiant\map\RegionManager.cpp:313
 msgid "This command is not available in component mode."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:351
+#: ..\..\radiant\map\RegionManager.cpp:319
 msgid "Could not set Region: nothing selected."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:368
+#: ..\..\radiant\map\RegionManager.cpp:336
 msgid "Export region"
 msgstr ""
 
@@ -423,7 +433,7 @@ msgstr ""
 msgid "Initialising Modules"
 msgstr ""
 
-#: ..\..\radiant\modulesystem\ModuleRegistry.cpp:165
+#: ..\..\radiant\modulesystem\ModuleRegistry.cpp:162
 msgid "Modules initialised"
 msgstr ""
 
@@ -455,7 +465,7 @@ msgstr ""
 msgid "Cannot create cylinder-cap, patch must have a width of 9."
 msgstr ""
 
-#: ..\..\radiant\patch\Patch.cpp:1667
+#: ..\..\radiant\patch\Patch.cpp:1639
 msgid "Sorry. Patch is not suitable for this kind of operation."
 msgstr ""
 
@@ -471,11 +481,11 @@ msgstr ""
 #: ..\..\radiant\settings\GameManager.cpp:97
 #: ..\..\radiant\settings\GameManager.cpp:323
 #: ..\..\radiant\settings\GameManager.cpp:344
-#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:198
+#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:169
 msgid "Game"
 msgstr ""
 
-#: ..\..\radiant\RadiantModule.cpp:238
+#: ..\..\radiant\RadiantModule.cpp:239
 msgid "Settings"
 msgstr ""
 
@@ -508,41 +518,82 @@ msgstr ""
 msgid "Can't convert curves - no entities with curves selected."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:62
+#: ..\..\radiant\selection\algorithm\Entity.cpp:82
+#, c-format
+msgid "The name %s already exists in this map!"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:106
+msgid "Cannot set classname to an empty string."
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:112
+msgid "Cannot change classname to worldspawn."
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:131
 msgid "Cannot change classname of worldspawn entity."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:104
+#: ..\..\radiant\selection\algorithm\Entity.cpp:169
 msgid "Critical: Cannot find selected entities."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:109
-#: ..\..\radiant\selection\algorithm\Entity.cpp:125
+#: ..\..\radiant\selection\algorithm\Entity.cpp:174
+#: ..\..\radiant\selection\algorithm\Entity.cpp:190
 msgid "Exactly two entities must be selected for this operation."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:171
+#: ..\..\radiant\selection\algorithm\Entity.cpp:236
 #, c-format
 msgid "Unable to create entity %s, no brushes selected."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Group.cpp:236
+#: ..\..\radiant\selection\algorithm\Group.cpp:234
 msgid ""
 "Cannot reparent primitives to entity. Please select at least one brush/patch "
 "and exactly one entity.(The entity has to be selected last.)"
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Group.cpp:386
+#: ..\..\radiant\selection\algorithm\Group.cpp:384
 msgid ""
 "Cannot merge entities, the selection must consist of func_* entities only.\n"
 "(The first selected entity will be preserved.)"
 msgstr ""
 
+#: ..\..\radiant\selection\algorithm\Group.cpp:394
+msgid "Groups can be formed in Primitive selection mode only"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:399
+msgid "Nothing selected, cannot group anything"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:404
+msgid "Select more than one element to form a group"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:430
+msgid "The selected elements already form a group"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:455
+msgid "Groups can be dissolved in Primitive selection mode only"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:460
+msgid "Nothing selected, cannot un-group anything"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:480
+msgid "The selected elements aren't part of any group"
+msgstr ""
+
 #: ..\..\radiant\selection\algorithm\Patch.cpp:60
 msgid "Cannot create caps, no patches selected."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Patch.cpp:208
+#: ..\..\radiant\selection\algorithm\Patch.cpp:197
 msgid "Cannot thicken patch. Nothing selected."
 msgstr ""
 
@@ -573,42 +624,52 @@ msgstr ""
 msgid "At least one brush must be selected for this operation."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:285
+#: ..\..\radiant\selection\algorithm\Shader.cpp:307
 msgid ""
 "Can't paste shader to entire brush.\n"
 "Target is not a brush."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:320
+#: ..\..\radiant\selection\algorithm\Shader.cpp:346
 msgid ""
 "Can't paste Texture Coordinates.\n"
 "Target patch dimensions must match."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:327
+#: ..\..\radiant\selection\algorithm\Shader.cpp:355
 msgid "Can't paste Texture Coordinates from patches to faces."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:332
+#: ..\..\radiant\selection\algorithm\Shader.cpp:361
 msgid "Can't paste Texture Coordinates from faces."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:388
+#: ..\..\radiant\selection\algorithm\Shader.cpp:417
 msgid "Can't copy Shader. Couldn't retrieve patch."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:398
+#: ..\..\radiant\selection\algorithm\Shader.cpp:427
 msgid "Can't copy Shader. Couldn't retrieve face."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:404
+#: ..\..\radiant\selection\algorithm\Shader.cpp:433
 msgid "Can't copy Shader. Please select a single face or patch."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Transformation.cpp:55
+#: ..\..\radiant\selection\algorithm\Transformation.cpp:56
 msgid "Cannot scale by zero value."
 msgstr ""
 
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:81
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:97
+msgid "Ungroup Selection"
+msgstr ""
+
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:84
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:91
+msgid "Group Selection"
+msgstr ""
+
 #: ..\..\radiant\selection\ManipulateMouseTool.cpp:28
 msgid "Manipulate"
 msgstr ""
@@ -617,31 +678,31 @@ msgstr ""
 msgid "Select"
 msgstr ""
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:168 xml_file_content.cpp:4
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:175 xml_file_content.cpp:4
 msgid "Select Faces"
 msgstr ""
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:187
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:194
 msgid "Cycle Selection"
 msgstr ""
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:233
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:247
 msgid "Cycle Face Selection"
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:84
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:104
 msgid "Selection Set: "
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:92
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:112
 msgid "Clear Selection Sets"
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:194
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:214
 msgid "Delete all selection sets?"
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:195
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:215
 msgid ""
 "This will delete all set definitions. The actual map objects will not be "
 "affected by this step.\n"
@@ -669,20 +730,24 @@ msgid ""
 "scene."
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:87
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:24
+msgid "The name of the shader in the clipboard"
+msgstr ""
+
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:91
 #, c-format
 msgid "ShaderClipboard: %s"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:90
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:94
 msgid "Face"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:93
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:97
 msgid "Patch"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:96
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:100
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:359
 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:73
 #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:60
@@ -690,7 +755,7 @@ msgstr ""
 msgid "Shader"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:100
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:104
 msgid "ShaderClipboard is empty."
 msgstr ""
 
@@ -1487,10 +1552,35 @@ msgstr ""
 msgid "Zulu"
 msgstr ""
 
+#: ..\..\radiant\settings\PreferencePage.cpp:19
+#, c-format
+msgid "%s Settings"
+msgstr ""
+
 #: ..\..\radiant\textool\TexTool.cpp:38
 msgid "Texture Tool"
 msgstr ""
 
+#: ..\..\radiant\ui\aas\AasControl.cpp:36
+msgid "Reload AAS File"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:30
+msgid "AAS Viewer"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:80
+msgid "Search for AAS Files"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:84
+msgid "Show Area Numbers"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:87
+msgid "Hide distant Areas"
+msgstr ""
+
 #: ..\..\radiant\ui\about\AboutDialog.cpp:24
 msgid "About DarkRadiant"
 msgstr ""
@@ -1521,7 +1611,7 @@ msgid "Renderer: %s"
 msgstr ""
 
 #: ..\..\radiant\ui\animationpreview\MD5AnimationViewer.cpp:16
-#: xml_file_content.cpp:139
+#: xml_file_content.cpp:140
 msgid "MD5 Animation Viewer"
 msgstr ""
 
@@ -1556,7 +1646,7 @@ msgid "Shortcut List"
 msgstr ""
 
 #: ..\..\radiant\ui\commandlist\CommandList.cpp:53
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:94
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:116
 #: xml_file_content.cpp:2
 msgid "Command"
 msgstr ""
@@ -1630,7 +1720,7 @@ msgstr ""
 
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:195
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:225
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:437
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:481
 #: ..\..\plugins\eclasstree\EClassTree.cpp:143
 #: ..\..\libs\wxutil\KeyValueTable.cpp:44
 msgid "Value"
@@ -1678,7 +1768,7 @@ msgstr ""
 #: ..\..\radiant\ui\common\SoundChooser.cpp:189
 #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:290
 #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:340
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:380
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:396
 msgid "Loading..."
 msgstr ""
 
@@ -1711,12 +1801,12 @@ msgid "Custom properties defined for this entity class, if any"
 msgstr ""
 
 #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:76
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:432
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:476
 #: ..\..\plugins\eclasstree\EClassTree.cpp:140
 msgid "Property"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:25
+#: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:26
 msgid "Choose entity class..."
 msgstr ""
 
@@ -1728,41 +1818,42 @@ msgstr ""
 msgid "Show help"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:292
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:312
 msgid "Add property..."
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:296
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:317
 msgid "Delete property"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:304
-msgid "Copy Spawnarg"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:325
+msgid "Copy Spawnarg(s)"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:309
-msgid "Cut Spawnarg"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:330
+msgid "Cut Spawnarg(s)"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:314
-msgid "Paste Spawnarg"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:335
+msgid "Paste Spawnarg(s)"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:442
-msgid "?"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:347
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:350
+#: ..\..\plugins\uimanager\GroupDialog.cpp:28
+msgid "Entity"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:654
-#, c-format
-msgid "The name %s already exists in this map!"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:486
+msgid "?"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1064
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1200
 #, c-format
 msgid "Entity %d"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1076
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1212
 #, c-format
 msgid "Entity %d, Primitive %d"
 msgstr ""
@@ -1771,7 +1862,7 @@ msgstr ""
 msgid "Choose target entity..."
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:78
+#: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:71
 #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:62
 msgid "Apply..."
 msgstr ""
@@ -2033,59 +2124,53 @@ msgstr ""
 msgid "Window Layout"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:93
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:84
 msgid "Settings/Multi Monitor"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:112
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:103
 msgid "Start DarkRadiant on monitor"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:131
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:122
 msgid "Settings/Compatibility"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:133
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:124
 msgid "Disable Windows Desktop Composition"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:267
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:256
 msgid "Exit DarkRadiant"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:380
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:383
-#: ..\..\plugins\uimanager\GroupDialog.cpp:28
-msgid "Entity"
-msgstr ""
-
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:391
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:394
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:369
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:372
 msgid "Media"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:402
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:405
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:381
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:384
 msgid "Console"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:144
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:145
 msgid "Camera Position"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:147
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:148
 msgid "Top Left"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:149
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:150
 msgid "Top Right"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:151
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:152
 msgid "Bottom Left"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:153
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:154
 msgid "Bottom Right"
 msgstr ""
 
@@ -2381,7 +2466,7 @@ msgstr ""
 msgid "Merge Entities"
 msgstr ""
 
-#: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:74 xml_file_content.cpp:155
+#: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:74 xml_file_content.cpp:156
 msgid "Make Visportal"
 msgstr ""
 
@@ -2455,7 +2540,7 @@ msgid "Patch Inspector"
 msgstr ""
 
 #: ..\..\radiant\ui\patch\PatchInspector.cpp:29
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:55
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:56
 #: ..\..\radiant\ui\transform\TransformDialog.cpp:38
 msgid "Step:"
 msgstr ""
@@ -2464,135 +2549,139 @@ msgstr ""
 msgid "Patch Thicken"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:41
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:42
 msgid "Choose Prefab"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:97
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:100
 msgid "Rescan Prefab Folders"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:128
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:105
+msgid "Create Group out of Prefab parts"
+msgstr ""
+
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:140
 msgid "Browse mod resources"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:131
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:143
 msgid "Select recently used path:"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:133
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:145
 msgid "Browse custom path:"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:532
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:553
 msgid "<no description>"
 msgstr ""
 
-#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:37
+#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:23
 msgid "DarkRadiant Preferences"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:38
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:39
 msgid "Surface Inspector"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:39
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:40
 msgid "Texture Properties"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:40
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:41
 msgid "Texture Operations"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:48
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:49
 msgid "Horiz. Shift:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:49
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:50
 msgid "Vert. Shift:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:50
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:51
 msgid "Horiz. Scale:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:51
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52
 msgid "Vert. Scale:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53
 msgid "Rotation:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:54
 #: xml_file_content.cpp:15
 msgid "Shader:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:57
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:58
 msgid "Fit Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:58
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:59
 msgid "Fit"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:60
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61
 msgid "Align Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62
 msgid "Top"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:63
 msgid "Bottom"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:63
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64
 #: xml_file_content.cpp:16
 msgid "Right"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:65
 #: xml_file_content.cpp:15
 msgid "Left"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:66
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67
 msgid "Flip Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68
 msgid "Flip Horizontal"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:69
 msgid "Flip Vertical"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:70
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71
 msgid "Modify Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72
 msgid "Natural"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:73
 msgid "Normalise"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:74
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75
 msgid "Default Scale:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:76
 #: xml_file_content.cpp:9
 msgid "Texture Lock"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:634
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:649
 msgid "Both fit values must be > 0."
 msgstr ""
 
@@ -2601,7 +2690,7 @@ msgid "Seek in Media Browser"
 msgstr ""
 
 #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:269
-#: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:811
+#: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:809
 msgid "No shader"
 msgstr ""
 
@@ -2701,7 +2790,7 @@ msgstr ""
 msgid "Show Entity Names"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:205 xml_file_content.cpp:79
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:205 xml_file_content.cpp:80
 msgid "Show Blocks"
 msgstr ""
 
@@ -2709,15 +2798,15 @@ msgstr ""
 msgid "Show Coordinates"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:207 xml_file_content.cpp:82
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:207 xml_file_content.cpp:83
 msgid "Show Axes"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:208 xml_file_content.cpp:81
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:208 xml_file_content.cpp:82
 msgid "Show Window Outline"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:209 xml_file_content.cpp:83
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:209 xml_file_content.cpp:84
 msgid "Show Workzone"
 msgstr ""
 
@@ -2729,6 +2818,10 @@ msgstr ""
 msgid "Higher Selection Priority for Entities"
 msgstr ""
 
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:665
+msgid "Shows the mouse position in the orthoview"
+msgstr ""
+
 #: ..\..\radiant\xyview\tools\BrushCreatorTool.cpp:25
 msgid "Drag-create Brush"
 msgstr ""
@@ -2741,7 +2834,7 @@ msgstr ""
 msgid "Drag Camera"
 msgstr ""
 
-#: ..\..\radiant\xyview\tools\ClipperTool.cpp:21 xml_file_content.cpp:149
+#: ..\..\radiant\xyview\tools\ClipperTool.cpp:21 xml_file_content.cpp:150
 #: xml_file_content.cpp:12 xml_file_content.cpp:17
 msgid "Clipper"
 msgstr ""
@@ -2766,7 +2859,7 @@ msgstr ""
 msgid "YZ Side"
 msgstr ""
 
-#: ..\..\radiant\xyview\XYWnd.cpp:637
+#: ..\..\radiant\xyview\XYWnd.cpp:498
 #, c-format
 msgid "x: %6.1lf y: %6.1lf z: %6.1lf"
 msgstr ""
@@ -2785,7 +2878,7 @@ msgstr ""
 msgid "Conversation Editor"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationDialog.cpp:278
+#: ..\..\plugins\dm.conversation\ConversationDialog.cpp:280
 #, c-format
 msgid "Unable to create conversation Entity: class '%s' not found."
 msgstr ""
@@ -2794,35 +2887,35 @@ msgstr ""
 msgid "Edit Conversation"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:67
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:89
 msgid "Actor (click to edit)"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:92
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:114
 #: xml_file_content.cpp:1
 msgid "Actor"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:96
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:118
 msgid "Wait"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:201
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:223
 #, c-format
 msgid "Actor %d"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:203
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:225
 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206
 msgid "yes"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:203
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:225
 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206
 msgid "no"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:369
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:394
 msgid "New Actor"
 msgstr ""
 
@@ -3941,7 +4034,7 @@ msgstr ""
 #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:412
 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:403
 #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:95
-#: xml_file_content.cpp:179 xml_file_content.cpp:3 xml_file_content.cpp:7
+#: xml_file_content.cpp:180 xml_file_content.cpp:3 xml_file_content.cpp:7
 #: xml_file_content.cpp:9 xml_file_content.cpp:15 xml_file_content.cpp:2
 #: xml_file_content.cpp:5 xml_file_content.cpp:6 xml_file_content.cpp:18
 msgid "Delete"
@@ -4069,7 +4162,8 @@ msgstr ""
 msgid "Custom Stims"
 msgstr ""
 
-#: ..\..\plugins\eclassmgr\EClassManager.cpp:330
+#: ..\..\plugins\eclassmgr\EClassManager.cpp:327
+#: ..\..\plugins\eclassmgr\EClassManager.cpp:328
 msgid "Reloading Defs"
 msgstr ""
 
@@ -4113,47 +4207,51 @@ msgstr ""
 msgid "All Files"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:151
+#: ..\..\plugins\grid\Grid.cpp:55
+msgid "Current Grid Size"
+msgstr ""
+
+#: ..\..\plugins\grid\Grid.cpp:152
 msgid "Settings/Grid"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:153
+#: ..\..\plugins\grid\Grid.cpp:154
 msgid "Default Grid Size"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:157
+#: ..\..\plugins\grid\Grid.cpp:158
 msgid "Lines"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:158
+#: ..\..\plugins\grid\Grid.cpp:159
 msgid "Dotted Lines"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:159
+#: ..\..\plugins\grid\Grid.cpp:160
 msgid "More Dotted Lines"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:160
+#: ..\..\plugins\grid\Grid.cpp:161
 msgid "Crosses"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:161
+#: ..\..\plugins\grid\Grid.cpp:162
 msgid "Dots"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:162
+#: ..\..\plugins\grid\Grid.cpp:163
 msgid "Big Dots"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:163
+#: ..\..\plugins\grid\Grid.cpp:164
 msgid "Squares"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:165
+#: ..\..\plugins\grid\Grid.cpp:166
 msgid "Major Grid Style"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:166
+#: ..\..\plugins\grid\Grid.cpp:167
 msgid "Minor Grid Style"
 msgstr ""
 
@@ -4182,24 +4280,25 @@ msgid "Incorrect map version: required %f, found %f"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:144
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:94
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:102
 #, c-format
 msgid "Primitive #%d: parse error"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:154
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:104
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:112
 #, c-format
 msgid "Primitive #%d: parse exception %s"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:245
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:195
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:203
 #, c-format
 msgid "Parsed invalid value '%s' for key '%s'"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:125
+#: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:234
 #, c-format
 msgid "BrushDefParser: invalid token '%s'"
 msgstr ""
@@ -4269,23 +4368,23 @@ msgstr ""
 msgid "Cannot save particle, it has not been registered yet."
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:316
-#: ..\..\plugins\particles\ParticlesManager.cpp:344
+#: ..\..\plugins\particles\ParticlesManager.cpp:325
+#: ..\..\plugins\particles\ParticlesManager.cpp:353
 #, c-format
 msgid "Cannot open file for writing: %s"
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:357
+#: ..\..\plugins\particles\ParticlesManager.cpp:366
 #, c-format
 msgid "Cannot open file for reading: %s"
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:404
+#: ..\..\plugins\particles\ParticlesManager.cpp:413
 #, c-format
 msgid "Could not remove the file %s"
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:418
+#: ..\..\plugins\particles\ParticlesManager.cpp:427
 #, c-format
 msgid "Could not rename the temporary file %s"
 msgstr ""
@@ -4299,7 +4398,7 @@ msgstr ""
 msgid "Reload Scripts"
 msgstr ""
 
-#: ..\..\plugins\script\ScriptMenu.cpp:51
+#: ..\..\plugins\script\ScriptMenu.cpp:60
 msgid "No scripts available"
 msgstr ""
 
@@ -4311,7 +4410,7 @@ msgstr ""
 msgid "Run Script"
 msgstr ""
 
-#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:319
+#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:321
 msgid "Loading Shaders"
 msgstr ""
 
@@ -4350,20 +4449,16 @@ msgstr ""
 msgid "_Filters"
 msgstr ""
 
-#: ..\..\plugins\undo\UndoSystem.cpp:384
-msgid "Settings/Undo System"
+#: ..\..\plugins\uimanager\UIManager.cpp:115
+msgid "Describes available Mouse Commands"
 msgstr ""
 
-#: ..\..\plugins\undo\UndoSystem.cpp:385
-msgid "Undo Queue Size"
-msgstr ""
-
-#: ..\..\plugins\wavefront\WaveFrontModule.cpp:31
-msgid "Save as Obj"
+#: ..\..\plugins\undo\UndoSystem.cpp:398
+msgid "Settings/Undo System"
 msgstr ""
 
-#: ..\..\plugins\wavefront\WaveFrontModule.cpp:89
-msgid "Export Selection as OBJ..."
+#: ..\..\plugins\undo\UndoSystem.cpp:399
+msgid "Undo Queue Size"
 msgstr ""
 
 #: ..\..\libs\wxutil\dialog\MessageBox.cpp:20 xml_file_content.cpp:8
@@ -4502,7 +4597,7 @@ msgstr ""
 msgid "Reload S&kins"
 msgstr ""
 
-#: xml_file_content.cpp:16 xml_file_content.cpp:26
+#: xml_file_content.cpp:16 xml_file_content.cpp:28
 msgid "Reload Materials"
 msgstr ""
 
@@ -4662,496 +4757,496 @@ msgstr ""
 msgid "&Texture Tool"
 msgstr ""
 
-#: xml_file_content.cpp:61
-msgid "&Center"
+#: xml_file_content.cpp:60
+msgid "AAS Area Viewer"
 msgstr ""
 
 #: xml_file_content.cpp:62
-msgid "&Up Floor"
+msgid "&Center"
 msgstr ""
 
 #: xml_file_content.cpp:63
-msgid "&Down Floor"
+msgid "&Up Floor"
 msgstr ""
 
 #: xml_file_content.cpp:64
-msgid "Far Clip Plane In"
+msgid "&Down Floor"
 msgstr ""
 
 #: xml_file_content.cpp:65
-msgid "Far Clip Plane Out"
+msgid "Far Clip Plane In"
 msgstr ""
 
 #: xml_file_content.cpp:66
-msgid "Next leak spot"
+msgid "Far Clip Plane Out"
 msgstr ""
 
 #: xml_file_content.cpp:67
-msgid "Previous leak spot"
+msgid "Next leak spot"
 msgstr ""
 
 #: xml_file_content.cpp:68
-msgid "Orthographic"
+msgid "Previous leak spot"
 msgstr ""
 
 #: xml_file_content.cpp:69
-msgid "Next (XY, XZ, YZ)"
+msgid "Orthographic"
 msgstr ""
 
 #: xml_file_content.cpp:70
-msgid "XY (Top)"
+msgid "Next (XY, XZ, YZ)"
 msgstr ""
 
 #: xml_file_content.cpp:71
-msgid "YZ"
+msgid "XY (Top)"
 msgstr ""
 
 #: xml_file_content.cpp:72
-msgid "XZ"
+msgid "YZ"
 msgstr ""
 
 #: xml_file_content.cpp:73
-msgid "&XY 100%"
+msgid "XZ"
 msgstr ""
 
 #: xml_file_content.cpp:74
-msgid "&XY Zoom In"
+msgid "&XY 100%"
 msgstr ""
 
 #: xml_file_content.cpp:75
-msgid "&XY Zoom Out"
+msgid "&XY Zoom In"
 msgstr ""
 
 #: xml_file_content.cpp:76
-msgid "Show"
+msgid "&XY Zoom Out"
 msgstr ""
 
 #: xml_file_content.cpp:77
-msgid "Show &Angles"
+msgid "Show"
 msgstr ""
 
 #: xml_file_content.cpp:78
+msgid "Show &Angles"
+msgstr ""
+
+#: xml_file_content.cpp:79
 msgid "Show &Names"
 msgstr ""
 
-#: xml_file_content.cpp:80
+#: xml_file_content.cpp:81
 msgid "Show C&oordinates"
 msgstr ""
 
-#: xml_file_content.cpp:84
+#: xml_file_content.cpp:85
 msgid "Show size info"
 msgstr ""
 
-#: xml_file_content.cpp:85
+#: xml_file_content.cpp:86
 msgid "Hide/Show"
 msgstr ""
 
-#: xml_file_content.cpp:86
+#: xml_file_content.cpp:87
 msgid "Hide Selected"
 msgstr ""
 
-#: xml_file_content.cpp:87
+#: xml_file_content.cpp:88
 msgid "Hide Deselected"
 msgstr ""
 
-#: xml_file_content.cpp:88
+#: xml_file_content.cpp:89
 msgid "Show hidden"
 msgstr ""
 
-#: xml_file_content.cpp:90
+#: xml_file_content.cpp:91
 msgid "&Switch off"
 msgstr ""
 
-#: xml_file_content.cpp:91
+#: xml_file_content.cpp:92
 msgid "Set from &XY view"
 msgstr ""
 
-#: xml_file_content.cpp:92
+#: xml_file_content.cpp:93
 msgid "Set from &Brush"
 msgstr ""
 
-#: xml_file_content.cpp:93
+#: xml_file_content.cpp:94
 msgid "Set from Se&lection"
 msgstr ""
 
-#: xml_file_content.cpp:94
+#: xml_file_content.cpp:95
 msgid "Colours..."
 msgstr ""
 
-#: xml_file_content.cpp:95
+#: xml_file_content.cpp:96
 msgid "Background Image..."
 msgstr ""
 
-#: xml_file_content.cpp:96
+#: xml_file_content.cpp:97
 msgid "Mo&dify"
 msgstr ""
 
-#: xml_file_content.cpp:97 xml_file_content.cpp:16
+#: xml_file_content.cpp:98 xml_file_content.cpp:16
 msgid "Components"
 msgstr ""
 
-#: xml_file_content.cpp:98
+#: xml_file_content.cpp:99
 msgid "&Edges"
 msgstr ""
 
-#: xml_file_content.cpp:99
+#: xml_file_content.cpp:100
 msgid "&Vertices"
 msgstr ""
 
-#: xml_file_content.cpp:100
+#: xml_file_content.cpp:101
 msgid "&Faces"
 msgstr ""
 
-#: xml_file_content.cpp:101
+#: xml_file_content.cpp:102
 msgid "En&tities"
 msgstr ""
 
-#: xml_file_content.cpp:102
+#: xml_file_content.cpp:103
 msgid "Nudge"
 msgstr ""
 
-#: xml_file_content.cpp:103
+#: xml_file_content.cpp:104
 msgid "Nudge Left"
 msgstr ""
 
-#: xml_file_content.cpp:104
+#: xml_file_content.cpp:105
 msgid "Nudge Right"
 msgstr ""
 
-#: xml_file_content.cpp:105
+#: xml_file_content.cpp:106
 msgid "Nudge Up"
 msgstr ""
 
-#: xml_file_content.cpp:106
+#: xml_file_content.cpp:107
 msgid "Nudge Down"
 msgstr ""
 
-#: xml_file_content.cpp:107 xml_file_content.cpp:15
+#: xml_file_content.cpp:108 xml_file_content.cpp:15
 msgid "Rotate"
 msgstr ""
 
-#: xml_file_content.cpp:108
+#: xml_file_content.cpp:109
 msgid "Rotate X"
 msgstr ""
 
-#: xml_file_content.cpp:109
+#: xml_file_content.cpp:110
 msgid "Rotate Y"
 msgstr ""
 
-#: xml_file_content.cpp:110
+#: xml_file_content.cpp:111
 msgid "Rotate Z"
 msgstr ""
 
-#: xml_file_content.cpp:111
+#: xml_file_content.cpp:112
 msgid "Mirror"
 msgstr ""
 
-#: xml_file_content.cpp:112
+#: xml_file_content.cpp:113
 msgid "Mirror &X"
 msgstr ""
 
-#: xml_file_content.cpp:113
+#: xml_file_content.cpp:114
 msgid "Mirror &Y"
 msgstr ""
 
-#: xml_file_content.cpp:114
+#: xml_file_content.cpp:115
 msgid "Mirror &Z"
 msgstr ""
 
-#: xml_file_content.cpp:115 xml_file_content.cpp:10
+#: xml_file_content.cpp:116 xml_file_content.cpp:10
 msgid "Rotate Objects independently"
 msgstr ""
 
-#: xml_file_content.cpp:116
+#: xml_file_content.cpp:117
 msgid "Rotate and scale..."
 msgstr ""
 
-#: xml_file_content.cpp:117
+#: xml_file_content.cpp:118
 msgid "&Grid"
 msgstr ""
 
-#: xml_file_content.cpp:118
+#: xml_file_content.cpp:119
 msgid "Snap selected to grid"
 msgstr ""
 
-#: xml_file_content.cpp:119
+#: xml_file_content.cpp:120
 msgid "Grid0.125"
 msgstr ""
 
-#: xml_file_content.cpp:120
+#: xml_file_content.cpp:121
 msgid "Grid0.25"
 msgstr ""
 
-#: xml_file_content.cpp:121
+#: xml_file_content.cpp:122
 msgid "Grid0.5"
 msgstr ""
 
-#: xml_file_content.cpp:122
+#: xml_file_content.cpp:123
 msgid "Grid1"
 msgstr ""
 
-#: xml_file_content.cpp:123
+#: xml_file_content.cpp:124
 msgid "Grid2"
 msgstr ""
 
-#: xml_file_content.cpp:124
+#: xml_file_content.cpp:125
 msgid "Grid4"
 msgstr ""
 
-#: xml_file_content.cpp:125
+#: xml_file_content.cpp:126
 msgid "Grid8"
 msgstr ""
 
-#: xml_file_content.cpp:126
+#: xml_file_content.cpp:127
 msgid "Grid16"
 msgstr ""
 
-#: xml_file_content.cpp:127
+#: xml_file_content.cpp:128
 msgid "Grid32"
 msgstr ""
 
-#: xml_file_content.cpp:128
+#: xml_file_content.cpp:129
 msgid "Grid64"
 msgstr ""
 
-#: xml_file_content.cpp:129
+#: xml_file_content.cpp:130
 msgid "Grid128"
 msgstr ""
 
-#: xml_file_content.cpp:130
+#: xml_file_content.cpp:131
 msgid "Grid256"
 msgstr ""
 
-#: xml_file_content.cpp:131
+#: xml_file_content.cpp:132
 msgid "M&ap"
 msgstr ""
 
-#: xml_file_content.cpp:132
+#: xml_file_content.cpp:133
 msgid "Find brush..."
 msgstr ""
 
-#: xml_file_content.cpp:133
+#: xml_file_content.cpp:134
 msgid "Find and replace textures..."
 msgstr ""
 
-#: xml_file_content.cpp:134
+#: xml_file_content.cpp:135
 msgid "Map info..."
 msgstr ""
 
-#: xml_file_content.cpp:135
+#: xml_file_content.cpp:136
 msgid "E&ntity"
 msgstr ""
 
-#: xml_file_content.cpp:136
+#: xml_file_content.cpp:137
 msgid "&Revert group to worldspawn"
 msgstr ""
 
-#: xml_file_content.cpp:137
+#: xml_file_content.cpp:138
 msgid "&Connect selected entities"
 msgstr ""
 
-#: xml_file_content.cpp:138
+#: xml_file_content.cpp:139
 msgid "&Bind selected entities"
 msgstr ""
 
-#: xml_file_content.cpp:140
+#: xml_file_content.cpp:141
 msgid "B&rush"
 msgstr ""
 
-#: xml_file_content.cpp:141
+#: xml_file_content.cpp:142
 msgid "Prism..."
 msgstr ""
 
-#: xml_file_content.cpp:142
+#: xml_file_content.cpp:143
 msgid "Cone..."
 msgstr ""
 
-#: xml_file_content.cpp:143
+#: xml_file_content.cpp:144
 msgid "Sphere..."
 msgstr ""
 
-#: xml_file_content.cpp:144
+#: xml_file_content.cpp:145
 msgid "CSG"
 msgstr ""
 
-#: xml_file_content.cpp:145
+#: xml_file_content.cpp:146
 msgid "Make &Hollow"
 msgstr ""
 
-#: xml_file_content.cpp:146
+#: xml_file_content.cpp:147
 msgid "Make &Room"
 msgstr ""
 
-#: xml_file_content.cpp:147
+#: xml_file_content.cpp:148
 msgid "CSG &Subtract"
 msgstr ""
 
-#: xml_file_content.cpp:148
+#: xml_file_content.cpp:149
 msgid "CSG &Merge"
 msgstr ""
 
-#: xml_file_content.cpp:150
+#: xml_file_content.cpp:151
 msgid "Clip Selection"
 msgstr ""
 
-#: xml_file_content.cpp:151
+#: xml_file_content.cpp:152
 msgid "Split Selection"
 msgstr ""
 
-#: xml_file_content.cpp:152
+#: xml_file_content.cpp:153
 msgid "Flip Clip Orientation"
 msgstr ""
 
-#: xml_file_content.cpp:153
+#: xml_file_content.cpp:154
 msgid "Texture lock"
 msgstr ""
 
-#: xml_file_content.cpp:154
+#: xml_file_content.cpp:155
 msgid "Create Decal Patches"
 msgstr ""
 
-#: xml_file_content.cpp:156
+#: xml_file_content.cpp:157
 msgid "Make Detail"
 msgstr ""
 
-#: xml_file_content.cpp:157
+#: xml_file_content.cpp:158
 msgid "Make Structural"
 msgstr ""
 
-#: xml_file_content.cpp:158
+#: xml_file_content.cpp:159
 msgid "&Patch"
 msgstr ""
 
-#: xml_file_content.cpp:159
+#: xml_file_content.cpp:160
 msgid "Create Simple Patch Mesh"
 msgstr ""
 
-#: xml_file_content.cpp:160
+#: xml_file_content.cpp:161
 msgid "Create End cap"
 msgstr ""
 
-#: xml_file_content.cpp:161
+#: xml_file_content.cpp:162
 msgid "Create Bevel"
 msgstr ""
 
-#: xml_file_content.cpp:162
+#: xml_file_content.cpp:163
 msgid "Create Cone"
 msgstr ""
 
-#: xml_file_content.cpp:163
+#: xml_file_content.cpp:164
 msgid "Create Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:164
+#: xml_file_content.cpp:165
 msgid "Create Sphere"
 msgstr ""
 
-#: xml_file_content.cpp:165
+#: xml_file_content.cpp:166
 msgid "More cylinders"
 msgstr ""
 
-#: xml_file_content.cpp:166
+#: xml_file_content.cpp:167
 msgid "Create Dense Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:167
+#: xml_file_content.cpp:168
 msgid "Create Very Dense Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:168
+#: xml_file_content.cpp:169
 msgid "Create Square Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:169
+#: xml_file_content.cpp:170
 msgid "Insert"
 msgstr ""
 
-#: xml_file_content.cpp:170
+#: xml_file_content.cpp:171
 msgid "Insert 2 Columns at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:171
+#: xml_file_content.cpp:172
 msgid "Insert 2 Columns at the end"
 msgstr ""
 
-#: xml_file_content.cpp:172
+#: xml_file_content.cpp:173
 msgid "Insert 2 Rows at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:173
+#: xml_file_content.cpp:174
 msgid "Insert 2 Rows at the end"
 msgstr ""
 
-#: xml_file_content.cpp:174
+#: xml_file_content.cpp:175
 msgid "Append"
 msgstr ""
 
-#: xml_file_content.cpp:175
+#: xml_file_content.cpp:176
 msgid "Append 2 columns at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:176
+#: xml_file_content.cpp:177
 msgid "Append 2 columns at the end"
 msgstr ""
 
-#: xml_file_content.cpp:177
+#: xml_file_content.cpp:178
 msgid "Append 2 rows at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:178
+#: xml_file_content.cpp:179
 msgid "Append 2 rows at the end"
 msgstr ""
 
-#: xml_file_content.cpp:180
-msgid "Delete 2 columns from the beginning"
-msgstr ""
-
 #: xml_file_content.cpp:181
-msgid "Delete 2 columns from the end"
+msgid "Delete 2 columns from the beginning"
 msgstr ""
 
 #: xml_file_content.cpp:182
-msgid "Delete 2 rows from the beginning"
+msgid "Delete 2 columns from the end"
 msgstr ""
 
 #: xml_file_content.cpp:183
-msgid "Delete 2 rows from the end"
+msgid "Delete 2 rows from the beginning"
 msgstr ""
 
 #: xml_file_content.cpp:184
-msgid "Matrix"
+msgid "Delete 2 rows from the end"
 msgstr ""
 
 #: xml_file_content.cpp:185
-msgid "Invert"
+msgid "Matrix"
 msgstr ""
 
 #: xml_file_content.cpp:186
-msgid "Re-disperse"
+msgid "Invert"
 msgstr ""
 
 #: xml_file_content.cpp:187
-msgid "Rows"
+msgid "Re-disperse"
 msgstr ""
 
 #: xml_file_content.cpp:188
-msgid "Columns"
+msgid "Rows"
 msgstr ""
 
 #: xml_file_content.cpp:189
-msgid "Transpose"
+msgid "Columns"
 msgstr ""
 
 #: xml_file_content.cpp:190
-msgid "Thicken Selected Patches"
+msgid "Transpose"
 msgstr ""
 
 #: xml_file_content.cpp:191
-msgid "Cap Selection"
+msgid "Thicken Selected Patches"
 msgstr ""
 
 #: xml_file_content.cpp:192
-msgid "Cycle Cap Texture"
+msgid "Cap Selection"
 msgstr ""
 
 #: xml_file_content.cpp:193
@@ -5391,58 +5486,66 @@ msgid "Make Room"
 msgstr ""
 
 #: xml_file_content.cpp:20
-msgid "Put caps on the current patch"
+msgid "Group selected items"
 msgstr ""
 
 #: xml_file_content.cpp:21
-msgid "Creates a NURBS curve"
+msgid "Ungroup selected items"
 msgstr ""
 
 #: xml_file_content.cpp:22
-msgid "Convert the selected curve (NURBS <-> CatmullRom)"
+msgid "Put caps on the current patch"
 msgstr ""
 
 #: xml_file_content.cpp:23
-msgid "Appends a control point to the selected curves"
+msgid "Creates a NURBS curve"
 msgstr ""
 
 #: xml_file_content.cpp:24
-msgid "Inserts a curve control point before the selected ones"
+msgid "Convert the selected curve (NURBS <-> CatmullRom)"
 msgstr ""
 
 #: xml_file_content.cpp:25
-msgid "Removes the selected curve control points"
+msgid "Appends a control point to the selected curves"
+msgstr ""
+
+#: xml_file_content.cpp:26
+msgid "Inserts a curve control point before the selected ones"
 msgstr ""
 
 #: xml_file_content.cpp:27
+msgid "Removes the selected curve control points"
+msgstr ""
+
+#: xml_file_content.cpp:29
 msgid "Find & Replace"
 msgstr ""
 
-#: xml_file_content.cpp:28
+#: xml_file_content.cpp:30
 msgid "Decrease Grid Size"
 msgstr ""
 
-#: xml_file_content.cpp:29
+#: xml_file_content.cpp:31
 msgid "Increase Grid Size"
 msgstr ""
 
-#: xml_file_content.cpp:30
+#: xml_file_content.cpp:32
 msgid "Snap to Grid"
 msgstr ""
 
-#: xml_file_content.cpp:31
+#: xml_file_content.cpp:33
 msgid "Merge Selection"
 msgstr ""
 
-#: xml_file_content.cpp:32
+#: xml_file_content.cpp:34
 msgid "Flip Selection Horiz (S-Axis)"
 msgstr ""
 
-#: xml_file_content.cpp:33
+#: xml_file_content.cpp:35
 msgid "Flip Selection Vertical (T-Axis)"
 msgstr ""
 
-#: xml_file_content.cpp:34
+#: xml_file_content.cpp:36
 msgid "Select Related Items"
 msgstr ""
 
diff --git a/install/i18n/de/LC_MESSAGES/darkradiant.mo b/install/i18n/de/LC_MESSAGES/darkradiant.mo
index cc2aa2f..01725d6 100644
Binary files a/install/i18n/de/LC_MESSAGES/darkradiant.mo and b/install/i18n/de/LC_MESSAGES/darkradiant.mo differ
diff --git a/install/i18n/de/LC_MESSAGES/darkradiant.po b/install/i18n/de/LC_MESSAGES/darkradiant.po
index 4669bd6..a219b76 100644
--- a/install/i18n/de/LC_MESSAGES/darkradiant.po
+++ b/install/i18n/de/LC_MESSAGES/darkradiant.po
@@ -2,9 +2,9 @@ msgid ""
 msgstr ""
 "Project-Id-Version: DarkRadiant\n"
 "Report-Msgid-Bugs-To: greebo at angua.at\n"
-"POT-Creation-Date: 2016-01-05 11:54+0100\n"
-"PO-Revision-Date: 2016-01-05 11:59+0100\n"
-"Last-Translator: greebo <greebo at angua.at>\n"
+"POT-Creation-Date: 2016-11-18 18:32+0100\n"
+"PO-Revision-Date: 2016-11-18 18:36+0100\n"
+"Last-Translator: greebo <greebo at thedarkmod.com>\n"
 "Language-Team: The Dark Mod\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -84,12 +84,12 @@ msgstr "Invertiere vertikale Mausachse (freier Umsichtsmodus)"
 msgid "Solid selection boxes"
 msgstr "Feste Auswahlrahmen"
 
-#: ..\..\radiant\camera\CameraSettings.cpp:75
+#: ..\..\radiant\camera\CameraSettings.cpp:74
 msgid "Show camera toolbar"
 msgstr "Zeige Toolbar im Kamera-Fenster"
 
 #: ..\..\radiant\camera\FloatingCamWnd.cpp:18
-#: xml_file_content.cpp:60
+#: xml_file_content.cpp:61
 msgid "Camera"
 msgstr "Kamera"
 
@@ -101,27 +101,31 @@ msgstr "Umsichtsmodus"
 msgid "Jump to Object"
 msgstr "Zu Objekt springen"
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:97
+#: ..\..\radiant\camera\tools\PanViewTool.h:23
+msgid "Pan Camera View"
+msgstr "Kamera-Ansicht verschieben"
+
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:116
 msgid "Pick Shader"
 msgstr "Shader auswählen"
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:120
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:139
 msgid "Paste Shader Projected"
 msgstr "Shader einfügen (Projektion)"
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:138
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:158
 msgid "Paste Shader Natural"
 msgstr "Shader einfügen (Natürliche Skalierung)"
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:156
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:177
 msgid "Paste Texture Coordinates"
 msgstr "Texturkoordinaten einfügen"
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:174
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:196
 msgid "Paste Shader to all Brush Faces"
 msgstr "Shader einfügen (gesamter Brush)"
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:192
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:215
 msgid "Paste Shader Name"
 msgstr "Shadernamen einfügen"
 
@@ -137,40 +141,40 @@ msgstr "Clipper verwendet Caulk-Textur für neu erzeugte Faces."
 msgid "Caulk shader name"
 msgstr "Name des Caulk Shaders"
 
-#: ..\..\radiant\layers\LayerSystem.cpp:34
+#: ..\..\radiant\layers\LayerSystem.cpp:36
 msgid "Default"
 msgstr "Standard"
 
-#: ..\..\radiant\layers\LayerSystem.cpp:37
+#: ..\..\radiant\layers\LayerSystem.cpp:39
 msgid "Create Layer..."
 msgstr "Neuer Layer..."
 
-#: ..\..\radiant\layers\LayerSystem.cpp:39
+#: ..\..\radiant\layers\LayerSystem.cpp:41
 msgid "Add to Layer..."
 msgstr "Zu Layer hinzufügen..."
 
-#: ..\..\radiant\layers\LayerSystem.cpp:40
+#: ..\..\radiant\layers\LayerSystem.cpp:42
 msgid "Move to Layer..."
 msgstr "Nach Layer verschieben..."
 
-#: ..\..\radiant\layers\LayerSystem.cpp:41
+#: ..\..\radiant\layers\LayerSystem.cpp:43
 msgid "Remove from Layer..."
 msgstr "Von Layer entfernen..."
 
-#: ..\..\radiant\layers\LayerSystem.cpp:536
+#: ..\..\radiant\layers\LayerSystem.cpp:550
 #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1537
 msgid "Enter Name"
 msgstr "Namen eingeben"
 
-#: ..\..\radiant\layers\LayerSystem.cpp:537
+#: ..\..\radiant\layers\LayerSystem.cpp:551
 msgid "Enter Layer Name"
 msgstr "Ebenennamen eingeben"
 
-#: ..\..\radiant\layers\LayerSystem.cpp:549
+#: ..\..\radiant\layers\LayerSystem.cpp:563
 msgid "Cannot create layer with empty name."
 msgstr "Es ist nicht möglich eine Ebene ohne Namen zu erstellen."
 
-#: ..\..\radiant\layers\LayerSystem.cpp:563
+#: ..\..\radiant\layers\LayerSystem.cpp:577
 msgid "This name already exists."
 msgstr "Dieser Name existiert bereits"
 
@@ -201,36 +205,44 @@ msgstr "Lade Map"
 msgid "Loading entity %d\n"
 msgstr "Lade Entity %d\n"
 
-#: ..\..\radiant\map\AutoSaver.cpp:299
+#: ..\..\radiant\map\AutoSaver.cpp:276
 msgid "Settings/Autosave"
 msgstr "Einstellungen/Automatisches Speichern"
 
-#: ..\..\radiant\map\AutoSaver.cpp:302
+#: ..\..\radiant\map\AutoSaver.cpp:279
 msgid "Enable Autosave"
 msgstr "Aktiviere automatisches Speichern"
 
-#: ..\..\radiant\map\AutoSaver.cpp:303
+#: ..\..\radiant\map\AutoSaver.cpp:280
 msgid "Autosave Interval (in minutes)"
 msgstr "Speicherintervall"
 
 #  Snapshots oder Schnappschüsse?
-#: ..\..\radiant\map\AutoSaver.cpp:305
+#: ..\..\radiant\map\AutoSaver.cpp:282
 msgid "Save Snapshots"
 msgstr "Speichere Schnappschüsse"
 
 #  Snapshot oder Schnappschuss?
-#: ..\..\radiant\map\AutoSaver.cpp:306
+#: ..\..\radiant\map\AutoSaver.cpp:283
 msgid "Snapshot folder (relative to map folder)"
 msgstr "Schnappschussverzeichniss (relativ zu map Verzeichniss)"
 
-#: ..\..\radiant\map\AutoSaver.cpp:307
+#: ..\..\radiant\map\AutoSaver.cpp:284
 msgid "Max Snapshot Folder size (MB)"
 msgstr "Maximale Grösse des Schnappschussverzeichnisses (MB)"
 
-#: ..\..\radiant\map\CounterManager.cpp:67
+#: ..\..\radiant\map\CounterManager.cpp:63
+msgid ""
+"Number of brushes/patches/entities in this map\n"
+"(Number of selected items shown in parentheses)"
+msgstr ""
+"Anzahl der Brushes/Patches/Entities in der Map\n"
+"(Anzahl der selektierten Elemente in Klammern)"
+
+#: ..\..\radiant\map\CounterManager.cpp:81
 #, c-format
-msgid "Brushes: %lu Patches: %lu Entities: %lu"
-msgstr "Brushes: %lu Patches: %lu Entities: %lu"
+msgid "Brushes: %lu (%lu) Patches: %lu (%lu) Entities: %lu (%lu)"
+msgstr "Brushes: %lu (%lu) Patches: %lu (%lu) Entities: %lu (%lu)"
 
 #: ..\..\radiant\map\FindMapElements.cpp:95
 msgid "Find Brush"
@@ -244,38 +256,38 @@ msgstr "Entitynummer:"
 msgid "Brush Number:"
 msgstr "Brushnummer:"
 
-#: ..\..\radiant\map\InfoFile.cpp:73
+#: ..\..\radiant\map\infofile\InfoFile.cpp:70
 msgid "Map Info File Version invalid"
 msgstr "Ungültige Map Info File Verson"
 
-#: ..\..\radiant\map\Map.cpp:59
+#: ..\..\radiant\map\Map.cpp:62
 msgid "unnamed.map"
 msgstr "unbenannt.map"
 
-#: ..\..\radiant\map\Map.cpp:182
-#: ..\..\radiant\map\Map.cpp:518
-#: ..\..\radiant\map\Map.cpp:610
-#: ..\..\radiant\map\Map.cpp:638
-#: ..\..\radiant\RadiantModule.cpp:134
+#: ..\..\radiant\map\Map.cpp:106
+#: ..\..\radiant\map\Map.cpp:431
+#: ..\..\radiant\map\Map.cpp:522
+#: ..\..\radiant\map\Map.cpp:550
+#: ..\..\radiant\RadiantModule.cpp:135
 #: ..\..\radiant\referencecache\ModelCache.cpp:245
 #: ..\..\radiant\referencecache\ModelCache.cpp:261
-#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:319
+#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:321
 msgid "Processing..."
 msgstr "Vorgang läuft..."
 
-#: ..\..\radiant\map\Map.cpp:182
+#: ..\..\radiant\map\Map.cpp:106
 msgid "Loading textures..."
 msgstr "Lade Texturen..."
 
-#: ..\..\radiant\map\Map.cpp:518
+#: ..\..\radiant\map\Map.cpp:431
 msgid "Saving Map"
 msgstr "Speichere Map"
 
-#: ..\..\radiant\map\Map.cpp:569
+#: ..\..\radiant\map\Map.cpp:481
 msgid "Importing..."
 msgstr "Importiere..."
 
-#: ..\..\radiant\map\Map.cpp:664
+#: ..\..\radiant\map\Map.cpp:576
 #, c-format
 msgid ""
 "Save changes to map \"%s\"\n"
@@ -284,17 +296,17 @@ msgstr ""
 "Sollen die Änderungen an der Map \"%s\"\n"
 "vor dem Schließen gespeichert werden?"
 
-#: ..\..\radiant\map\Map.cpp:672
+#: ..\..\radiant\map\Map.cpp:584
 #, c-format
 msgid "%d minutes"
 msgstr "%d Minuten"
 
-#: ..\..\radiant\map\Map.cpp:676
+#: ..\..\radiant\map\Map.cpp:588
 #, c-format
 msgid "%d seconds"
 msgstr "%d Sekunden"
 
-#: ..\..\radiant\map\Map.cpp:680
+#: ..\..\radiant\map\Map.cpp:592
 #, c-format
 msgid ""
 "If you don't save, changes from the last %s\n"
@@ -303,40 +315,40 @@ msgstr ""
 "Wenn Du nicht sicherst, gehen die Änderungen\n"
 "der letzten %s verloren."
 
-#: ..\..\radiant\map\Map.cpp:738
+#: ..\..\radiant\map\Map.cpp:650
 msgid "Save Map"
 msgstr "Map speichern"
 
-#: ..\..\radiant\map\Map.cpp:778
+#: ..\..\radiant\map\Map.cpp:690
 msgid "Save Copy As..."
 msgstr "Speichere Kopie als..."
 
-#: ..\..\radiant\map\Map.cpp:854
+#: ..\..\radiant\map\Map.cpp:781
 msgid "New Map"
 msgstr "Neue Map"
 
-#: ..\..\radiant\map\Map.cpp:865
+#: ..\..\radiant\map\Map.cpp:790
 #: ..\..\radiant\ui\mru\MRU.cpp:93
 msgid "Open Map"
 msgstr "Map öffnen"
 
-#: ..\..\radiant\map\Map.cpp:869
+#: ..\..\radiant\map\Map.cpp:794
 msgid "Open map"
 msgstr "Map öffnen"
 
-#: ..\..\radiant\map\Map.cpp:883
+#: ..\..\radiant\map\Map.cpp:808
 msgid "Import map"
 msgstr "Map importieren"
 
-#: ..\..\radiant\map\Map.cpp:912
+#: ..\..\radiant\map\Map.cpp:837
 msgid "Export selection"
 msgstr "Auswahl exportieren"
 
-#: ..\..\radiant\map\Map.cpp:927
+#: ..\..\radiant\map\Map.cpp:852
 msgid "Save selected as Prefab"
 msgstr "Auswahl als Prefab speichern"
 
-#: ..\..\radiant\map\Map.cpp:1008
+#: ..\..\radiant\map\Map.cpp:933
 #, c-format
 msgid ""
 "Failure reading map from clipboard:\n"
@@ -350,42 +362,33 @@ msgid "Map"
 msgstr "Map"
 
 #: ..\..\radiant\map\MapFileManager.cpp:36
-#: xml_file_content.cpp:89
+#: xml_file_content.cpp:90
 msgid "Region"
 msgstr "Region"
 
 #: ..\..\radiant\map\MapFileManager.cpp:37
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:327
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:343
 msgid "Prefab"
 msgstr "Prefab"
 
-#: ..\..\radiant\map\MapResource.cpp:273
-#: ..\..\radiant\map\MapResource.cpp:641
+#: ..\..\radiant\map\MapResource.cpp:257
+#: ..\..\radiant\map\MapResource.cpp:529
 #, c-format
 msgid "File is write-protected: %s"
 msgstr "Datei ist schreibgeschützt: %s"
 
-#: ..\..\radiant\map\MapResource.cpp:434
-#, c-format
-msgid ""
-"Failure opening map file:\n"
-"%s"
-msgstr ""
-"Fehler beim Öffnen der Mapdatei:\n"
-"%s"
-
-#: ..\..\radiant\map\MapResource.cpp:476
+#: ..\..\radiant\map\MapResource.cpp:363
 #, c-format
 msgid ""
 "Could not determine map format of file:\n"
 "%s"
 msgstr "Konnte das Format der Mapdatei nicht bestimmen: %s"
 
-#: ..\..\radiant\map\MapResource.cpp:602
+#: ..\..\radiant\map\MapResource.cpp:410
 msgid "Map loading cancelled"
 msgstr "Ladevorgang abgebrochen"
 
-#: ..\..\radiant\map\MapResource.cpp:614
+#: ..\..\radiant\map\MapResource.cpp:422
 #, c-format
 msgid ""
 "Failure reading map file:\n"
@@ -398,40 +401,50 @@ msgstr ""
 "\n"
 "%s"
 
-#: ..\..\radiant\map\MapResource.cpp:706
+#: ..\..\radiant\map\MapResource.cpp:485
+#: ..\..\radiant\map\MapResource.cpp:504
+#, c-format
+msgid ""
+"Failure opening file:\n"
+"%s"
+msgstr ""
+"Fehler beim Öffnen der Datei:\n"
+"%s"
+
+#: ..\..\radiant\map\MapResource.cpp:594
 msgid "Map writing cancelled"
 msgstr "Schreibvorgang abgebrochen"
 
-#: ..\..\radiant\map\MapResource.cpp:721
+#: ..\..\radiant\map\MapResource.cpp:609
 msgid "Could not open output streams for writing"
 msgstr "Fehler beim Öffnen der Output-Streams"
 
-#: ..\..\radiant\map\PointFile.cpp:116
+#: ..\..\radiant\map\PointFile.cpp:126
 #, c-format
 msgid "Could not open pointfile: %s"
 msgstr "Konnte das Pointfile nicht öffnen: %s"
 
-#: ..\..\radiant\map\RegionManager.cpp:203
+#: ..\..\radiant\map\RegionManager.cpp:173
 msgid "Warning: Camera not within region, can't set info_player_start."
 msgstr "Warnung: die Kamera befindet sich nicht innerhalb der Region, konnte daher den Spielerstartpunkt nicht setzen."
 
-#: ..\..\radiant\map\RegionManager.cpp:293
+#: ..\..\radiant\map\RegionManager.cpp:261
 msgid "Could not set Region: XY Top View not found."
 msgstr "Konnte Region nicht festlegen, weil die XY-Ansicht nicht gefunden wurde."
 
-#: ..\..\radiant\map\RegionManager.cpp:320
+#: ..\..\radiant\map\RegionManager.cpp:288
 msgid "Could not set Region: please select a single Brush."
 msgstr "Kann Region nicht festlegen: bitte einen einzelnen Brush auswählen."
 
-#: ..\..\radiant\map\RegionManager.cpp:345
+#: ..\..\radiant\map\RegionManager.cpp:313
 msgid "This command is not available in component mode."
 msgstr "Dieser Befehl steht im Komponentenmodus nicht zur Verfügung."
 
-#: ..\..\radiant\map\RegionManager.cpp:351
+#: ..\..\radiant\map\RegionManager.cpp:319
 msgid "Could not set Region: nothing selected."
 msgstr "Kann keine Region festlegen: keine Elemente ausgewählt."
 
-#: ..\..\radiant\map\RegionManager.cpp:368
+#: ..\..\radiant\map\RegionManager.cpp:336
 msgid "Export region"
 msgstr "Exportiere Region"
 
@@ -448,7 +461,7 @@ msgstr "Initialisiere Modul: %s"
 msgid "Initialising Modules"
 msgstr "Initialisiere Module"
 
-#: ..\..\radiant\modulesystem\ModuleRegistry.cpp:165
+#: ..\..\radiant\modulesystem\ModuleRegistry.cpp:162
 msgid "Modules initialised"
 msgstr "Module initialisiert"
 
@@ -482,7 +495,7 @@ msgstr "Kann keine Bevel-Caps erzeugen, Patch muss eine Breite von 3 haben"
 msgid "Cannot create cylinder-cap, patch must have a width of 9."
 msgstr "Kann keine Zylinder-Caps erzeugen, Patch muss eine Breite von 9 haben"
 
-#: ..\..\radiant\patch\Patch.cpp:1667
+#: ..\..\radiant\patch\Patch.cpp:1639
 msgid "Sorry. Patch is not suitable for this kind of operation."
 msgstr "Patch ist nicht geeignet für diese Operation."
 
@@ -498,11 +511,11 @@ msgstr "Patch-Tesselierungsgrenzwert"
 #: ..\..\radiant\settings\GameManager.cpp:97
 #: ..\..\radiant\settings\GameManager.cpp:323
 #: ..\..\radiant\settings\GameManager.cpp:344
-#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:198
+#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:169
 msgid "Game"
 msgstr "Spiel"
 
-#: ..\..\radiant\RadiantModule.cpp:238
+#: ..\..\radiant\RadiantModule.cpp:239
 msgid "Settings"
 msgstr "Einstellungen"
 
@@ -535,29 +548,42 @@ msgstr "Kann Kontrollpunkte nicht einfügen, keine Kurven ausgewählt."
 msgid "Can't convert curves - no entities with curves selected."
 msgstr "Kann Kurve nicht konvertieren, keine Kurven ausgewählt."
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:62
+#: ..\..\radiant\selection\algorithm\Entity.cpp:82
+#, c-format
+msgid "The name %s already exists in this map!"
+msgstr "Der Name %s existiert bereits in dieser Map!"
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:106
+msgid "Cannot set classname to an empty string."
+msgstr "Der Klassenname darf nicht leer sein"
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:112
+msgid "Cannot change classname to worldspawn."
+msgstr "Der Klassenname darf nicht auf worldspawn gesetzt werden."
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:131
 msgid "Cannot change classname of worldspawn entity."
 msgstr "Der Klassenname der Worldspawn-Entity darf nicht geändert werden."
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:104
+#: ..\..\radiant\selection\algorithm\Entity.cpp:169
 msgid "Critical: Cannot find selected entities."
 msgstr "Kritischer Fehler: kann ausgewählte Entities nicht finden."
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:109
-#: ..\..\radiant\selection\algorithm\Entity.cpp:125
+#: ..\..\radiant\selection\algorithm\Entity.cpp:174
+#: ..\..\radiant\selection\algorithm\Entity.cpp:190
 msgid "Exactly two entities must be selected for this operation."
 msgstr "Für diese Operation müssen genau zwei Entities ausgewählt sein."
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:171
+#: ..\..\radiant\selection\algorithm\Entity.cpp:236
 #, c-format
 msgid "Unable to create entity %s, no brushes selected."
 msgstr "Entity %s kann nicht erzeugt werden. Keine Brushes ausgewählt."
 
-#: ..\..\radiant\selection\algorithm\Group.cpp:236
+#: ..\..\radiant\selection\algorithm\Group.cpp:234
 msgid "Cannot reparent primitives to entity. Please select at least one brush/patch and exactly one entity.(The entity has to be selected last.)"
 msgstr "Kann die ausgewählten Primitive nicht neu zuordnen. Bitte zumindest einen Brush/Patch sowie eine Entity auswählen. (Die Entity muss als letztes ausgewählt worden sein.)"
 
-#: ..\..\radiant\selection\algorithm\Group.cpp:386
+#: ..\..\radiant\selection\algorithm\Group.cpp:384
 msgid ""
 "Cannot merge entities, the selection must consist of func_* entities only.\n"
 "(The first selected entity will be preserved.)"
@@ -565,11 +591,39 @@ msgstr ""
 "Kann Entities nicht zusammenführen, die Auswahl muss ausschließlich aus func_*-Entities bestehen.\n"
 "(Die zuerst ausgewählte Entity bleibt dabei erhalten.)"
 
+#: ..\..\radiant\selection\algorithm\Group.cpp:394
+msgid "Groups can be formed in Primitive selection mode only"
+msgstr "Gruppen können nur im Primitiv-Selektionsmodus erstellt werden"
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:399
+msgid "Nothing selected, cannot group anything"
+msgstr "Nichts ausgewählt, kann keine Gruppe erstellen"
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:404
+msgid "Select more than one element to form a group"
+msgstr "Um eine Gruppe zu erstellen muss mehr als ein Element ausgewählt werden"
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:430
+msgid "The selected elements already form a group"
+msgstr "Die selektierten Elemente bilden bereits eine Gruppe"
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:455
+msgid "Groups can be dissolved in Primitive selection mode only"
+msgstr "Gruppen können nur im Primitiv-Selektionsmodus aufgelöst werden"
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:460
+msgid "Nothing selected, cannot un-group anything"
+msgstr "Nichts ausgewählt, kann keine Gruppierung auflösen"
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:480
+msgid "The selected elements aren't part of any group"
+msgstr "Keines der selektierten Elemente ist Teil einer Gruppe"
+
 #: ..\..\radiant\selection\algorithm\Patch.cpp:60
 msgid "Cannot create caps, no patches selected."
 msgstr "Kann keine Caps erzeugen, da keine Patches ausgewählt sind."
 
-#: ..\..\radiant\selection\algorithm\Patch.cpp:208
+#: ..\..\radiant\selection\algorithm\Patch.cpp:197
 msgid "Cannot thicken patch. Nothing selected."
 msgstr "Nichts ausgewählt: kann Patch nicht extrudieren."
 
@@ -600,42 +654,52 @@ msgstr "Keine Brushes ausgewählt."
 msgid "At least one brush must be selected for this operation."
 msgstr "Für diese Operation muss mindestens ein Brush ausgewählt sein."
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:285
+#: ..\..\radiant\selection\algorithm\Shader.cpp:307
 msgid ""
 "Can't paste shader to entire brush.\n"
 "Target is not a brush."
 msgstr "Kann Shader nicht auf gesamten Brush anwenden, das Ziel ist kein Brush."
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:320
+#: ..\..\radiant\selection\algorithm\Shader.cpp:346
 msgid ""
 "Can't paste Texture Coordinates.\n"
 "Target patch dimensions must match."
 msgstr "Kann Texturkoordinaten nicht kopieren, da die Patch-Dimensionen nicht passend sind."
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:327
+#: ..\..\radiant\selection\algorithm\Shader.cpp:355
 msgid "Can't paste Texture Coordinates from patches to faces."
 msgstr "Es ist nicht möglich, Texturkoordinaten von Patches auf Faces zu kopieren."
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:332
+#: ..\..\radiant\selection\algorithm\Shader.cpp:361
 msgid "Can't paste Texture Coordinates from faces."
 msgstr "Es ist nicht möglich, Texturkoordinaten von Faces zu kopieren."
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:388
+#: ..\..\radiant\selection\algorithm\Shader.cpp:417
 msgid "Can't copy Shader. Couldn't retrieve patch."
 msgstr "Konnte Patch nicht casten, Shader wurde nicht kopiert."
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:398
+#: ..\..\radiant\selection\algorithm\Shader.cpp:427
 msgid "Can't copy Shader. Couldn't retrieve face."
 msgstr "Konnte Face nicht casten, Shader wurde nicht kopiert."
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:404
+#: ..\..\radiant\selection\algorithm\Shader.cpp:433
 msgid "Can't copy Shader. Please select a single face or patch."
 msgstr "Shader nicht kopiert: bitte ein einzelnes Face oder einen Patch auswählen."
 
-#: ..\..\radiant\selection\algorithm\Transformation.cpp:55
+#: ..\..\radiant\selection\algorithm\Transformation.cpp:56
 msgid "Cannot scale by zero value."
 msgstr "Kann nicht mit 0 skalieren."
 
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:81
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:97
+msgid "Ungroup Selection"
+msgstr "Gruppierung auflösen"
+
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:84
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:91
+msgid "Group Selection"
+msgstr "Gruppe erstellen"
+
 #: ..\..\radiant\selection\ManipulateMouseTool.cpp:28
 msgid "Manipulate"
 msgstr "Manipulieren"
@@ -644,32 +708,32 @@ msgstr "Manipulieren"
 msgid "Select"
 msgstr "Auswählen"
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:168
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:175
 #: xml_file_content.cpp:4
 msgid "Select Faces"
 msgstr "Faces auswählen"
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:187
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:194
 msgid "Cycle Selection"
 msgstr "Auswahl durchrotieren"
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:233
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:247
 msgid "Cycle Face Selection"
 msgstr "Face-Auswahl durchrotieren"
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:84
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:104
 msgid "Selection Set: "
 msgstr "Auswahlsatz: "
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:92
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:112
 msgid "Clear Selection Sets"
 msgstr "Auswahlsätze löschen"
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:194
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:214
 msgid "Delete all selection sets?"
 msgstr "Alle Auswahlsätze löschen?"
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:195
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:215
 msgid ""
 "This will delete all set definitions. The actual map objects will not be affected by this step.\n"
 "\n"
@@ -701,20 +765,24 @@ msgstr "Kann Auswahlsatz nicht erstellen"
 msgid "Cannot create a selection set, there is nothing selected in the current scene."
 msgstr "Kann Auswahlsatz nicht anlegen, keine Auswahl vorhanden."
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:87
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:24
+msgid "The name of the shader in the clipboard"
+msgstr "Der Name des Shaders in der Zwischenablage"
+
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:91
 #, c-format
 msgid "ShaderClipboard: %s"
 msgstr "Shader-Zwischenablage: %s"
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:90
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:94
 msgid "Face"
 msgstr "Face"
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:93
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:97
 msgid "Patch"
 msgstr "Patch"
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:96
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:100
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:359
 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:73
 #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:60
@@ -723,7 +791,7 @@ msgstr "Patch"
 msgid "Shader"
 msgstr "Shader"
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:100
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:104
 msgid "ShaderClipboard is empty."
 msgstr "Shader-Zwischenablage ist leer."
 
@@ -1524,10 +1592,35 @@ msgstr "Chinesisch"
 msgid "Zulu"
 msgstr "Zulu"
 
+#: ..\..\radiant\settings\PreferencePage.cpp:19
+#, c-format
+msgid "%s Settings"
+msgstr "%s Einstellungen"
+
 #: ..\..\radiant\textool\TexTool.cpp:38
 msgid "Texture Tool"
 msgstr "Textur-Werkzeug"
 
+#: ..\..\radiant\ui\aas\AasControl.cpp:36
+msgid "Reload AAS File"
+msgstr "AAS File neu laden"
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:30
+msgid "AAS Viewer"
+msgstr "AAS Visualisierung"
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:80
+msgid "Search for AAS Files"
+msgstr "Suche AAS Files"
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:84
+msgid "Show Area Numbers"
+msgstr "AAS-Nummern anzeigen"
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:87
+msgid "Hide distant Areas"
+msgstr "Weit entfernte ausblenden"
+
 #: ..\..\radiant\ui\about\AboutDialog.cpp:24
 msgid "About DarkRadiant"
 msgstr "Über DarkRadiant"
@@ -1558,7 +1651,7 @@ msgid "Renderer: %s"
 msgstr "Renderer: %s"
 
 #: ..\..\radiant\ui\animationpreview\MD5AnimationViewer.cpp:16
-#: xml_file_content.cpp:139
+#: xml_file_content.cpp:140
 msgid "MD5 Animation Viewer"
 msgstr "MD5-Animationsvorschau"
 
@@ -1593,7 +1686,7 @@ msgid "Shortcut List"
 msgstr "Liste der Tastenkombinationen"
 
 #: ..\..\radiant\ui\commandlist\CommandList.cpp:53
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:94
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:116
 #: xml_file_content.cpp:2
 msgid "Command"
 msgstr "Befehl"
@@ -1682,7 +1775,7 @@ msgstr "Shaderdefinition ansehen"
 
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:195
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:225
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:437
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:481
 #: ..\..\plugins\eclasstree\EClassTree.cpp:143
 #: ..\..\libs\wxutil\KeyValueTable.cpp:44
 msgid "Value"
@@ -1731,7 +1824,7 @@ msgstr "Soundshader"
 #: ..\..\radiant\ui\common\SoundChooser.cpp:189
 #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:290
 #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:340
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:380
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:396
 msgid "Loading..."
 msgstr "Lade..."
 
@@ -1764,12 +1857,12 @@ msgid "Custom properties defined for this entity class, if any"
 msgstr "Benutzerdefinierte Eigenschaften dieser Entityklasse, falls definiert"
 
 #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:76
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:432
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:476
 #: ..\..\plugins\eclasstree\EClassTree.cpp:140
 msgid "Property"
 msgstr "Eigenschaft"
 
-#: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:25
+#: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:26
 msgid "Choose entity class..."
 msgstr "Entityklasse auswählen"
 
@@ -1781,41 +1874,42 @@ msgstr "Zeige vererbte Eigenschaften"
 msgid "Show help"
 msgstr "Zeige Hilfe"
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:292
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:312
 msgid "Add property..."
 msgstr "Eigenschaft hinzufügen..."
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:296
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:317
 msgid "Delete property"
 msgstr "Eigenschaft entfernen"
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:304
-msgid "Copy Spawnarg"
-msgstr "Wertepaar kopieren"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:325
+msgid "Copy Spawnarg(s)"
+msgstr "Wertepaar(e) kopieren"
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:309
-msgid "Cut Spawnarg"
-msgstr "Wertepaar ausschneiden"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:330
+msgid "Cut Spawnarg(s)"
+msgstr "Wertepaar(e) ausschneiden"
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:314
-msgid "Paste Spawnarg"
-msgstr "Wertepaar einfügen"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:335
+msgid "Paste Spawnarg(s)"
+msgstr "Wertepaar(e) einfügen"
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:442
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:347
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:350
+#: ..\..\plugins\uimanager\GroupDialog.cpp:28
+msgid "Entity"
+msgstr "Entity"
+
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:486
 msgid "?"
 msgstr "?"
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:654
-#, c-format
-msgid "The name %s already exists in this map!"
-msgstr "Der Name %s existiert bereits in dieser Map!"
-
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1064
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1200
 #, c-format
 msgid "Entity %d"
 msgstr "Entity %d"
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1076
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1212
 #, c-format
 msgid "Entity %d, Primitive %d"
 msgstr "Entity %d, Primitiv %d"
@@ -1824,7 +1918,7 @@ msgstr "Entity %d, Primitiv %d"
 msgid "Choose target entity..."
 msgstr "Ziel-Entity auswählen..."
 
-#: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:78
+#: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:71
 #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:62
 msgid "Apply..."
 msgstr "Anwenden..."
@@ -2094,59 +2188,53 @@ msgstr "Texturen"
 msgid "Window Layout"
 msgstr "Fensterlayout"
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:93
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:84
 msgid "Settings/Multi Monitor"
 msgstr "Einstellungen/Multimonitor"
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:112
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:103
 msgid "Start DarkRadiant on monitor"
 msgstr "Starte DarkRadiant auf Monitor"
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:131
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:122
 msgid "Settings/Compatibility"
 msgstr "Einstellungen/Kompatibilität"
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:133
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:124
 msgid "Disable Windows Desktop Composition"
 msgstr "Desktopgestaltung deaktivieren"
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:267
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:256
 msgid "Exit DarkRadiant"
 msgstr "DarkRadiant beenden"
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:380
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:383
-#: ..\..\plugins\uimanager\GroupDialog.cpp:28
-msgid "Entity"
-msgstr "Entity"
-
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:391
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:394
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:369
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:372
 msgid "Media"
 msgstr "Media"
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:402
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:405
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:381
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:384
 msgid "Console"
 msgstr "Konsole"
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:144
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:145
 msgid "Camera Position"
 msgstr "Kamera-Position"
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:147
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:148
 msgid "Top Left"
 msgstr "Oben links"
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:149
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:150
 msgid "Top Right"
 msgstr "Oben rechts"
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:151
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:152
 msgid "Bottom Left"
 msgstr "Unten links"
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:153
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:154
 msgid "Bottom Right"
 msgstr "Unten rechts"
 
@@ -2450,7 +2538,7 @@ msgid "Merge Entities"
 msgstr "Entities zusammenführen"
 
 #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:74
-#: xml_file_content.cpp:155
+#: xml_file_content.cpp:156
 msgid "Make Visportal"
 msgstr "Erzeuge Visportal"
 
@@ -2524,7 +2612,7 @@ msgid "Patch Inspector"
 msgstr "Patch Inspector"
 
 #: ..\..\radiant\ui\patch\PatchInspector.cpp:29
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:55
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:56
 #: ..\..\radiant\ui\transform\TransformDialog.cpp:38
 msgid "Step:"
 msgstr "Schritt:"
@@ -2533,135 +2621,139 @@ msgstr "Schritt:"
 msgid "Patch Thicken"
 msgstr "Patch-Extrusion"
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:41
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:42
 msgid "Choose Prefab"
 msgstr "Prefab wählen"
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:97
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:100
 msgid "Rescan Prefab Folders"
 msgstr "Prefab-Ordner neu durchsuchen"
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:128
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:105
+msgid "Create Group out of Prefab parts"
+msgstr "Eine Gruppe aus diesem Prefab erstellen"
+
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:140
 msgid "Browse mod resources"
 msgstr "Mod-Ressourcen durchsuchen"
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:131
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:143
 msgid "Select recently used path:"
 msgstr "Zuletzt geöffneten Pfad auswählen:"
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:133
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:145
 msgid "Browse custom path:"
 msgstr "Pfad durchsuchen:"
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:532
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:553
 msgid "<no description>"
 msgstr "<keine Beschreibung>"
 
-#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:37
+#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:23
 msgid "DarkRadiant Preferences"
 msgstr "DarkRadiant Einstellungen"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:38
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:39
 msgid "Surface Inspector"
 msgstr "Surface Inspector"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:39
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:40
 msgid "Texture Properties"
 msgstr "Textureigenschaften"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:40
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:41
 msgid "Texture Operations"
 msgstr "Texturoperationen"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:48
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:49
 msgid "Horiz. Shift:"
 msgstr "Horiz. Verschiebung:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:49
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:50
 msgid "Vert. Shift:"
 msgstr "Vert. Verschiebung:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:50
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:51
 msgid "Horiz. Scale:"
 msgstr "Horiz. Skalierung:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:51
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52
 msgid "Vert. Scale:"
 msgstr "Vert. Skalierung:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53
 msgid "Rotation:"
 msgstr "Rotation:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:54
 #: xml_file_content.cpp:15
 msgid "Shader:"
 msgstr "Shader:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:57
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:58
 msgid "Fit Texture:"
 msgstr "Texture einpassen:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:58
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:59
 msgid "Fit"
 msgstr "Einpassen"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:60
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61
 msgid "Align Texture:"
 msgstr "Textur ausrichten:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62
 msgid "Top"
 msgstr "Oben"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:63
 msgid "Bottom"
 msgstr "Unten"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:63
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64
 #: xml_file_content.cpp:16
 msgid "Right"
 msgstr "Rechts"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:65
 #: xml_file_content.cpp:15
 msgid "Left"
 msgstr "Links"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:66
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67
 msgid "Flip Texture:"
 msgstr "Textur kippen:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68
 msgid "Flip Horizontal"
 msgstr "Horizonal kippen"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:69
 msgid "Flip Vertical"
 msgstr "Vertikal kippen"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:70
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71
 msgid "Modify Texture:"
 msgstr "Texturierung bearbeiten:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72
 msgid "Natural"
 msgstr "Natürliche Skala"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:73
 msgid "Normalise"
 msgstr "Normalisieren"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:74
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75
 msgid "Default Scale:"
 msgstr "Standardskalierung:"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:76
 #: xml_file_content.cpp:9
 msgid "Texture Lock"
 msgstr "Textur-Lock"
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:634
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:649
 msgid "Both fit values must be > 0."
 msgstr "Beide Fit-Werte müssen größer als 0 sein."
 
@@ -2670,7 +2762,7 @@ msgid "Seek in Media Browser"
 msgstr "In Media Browser zeigen"
 
 #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:269
-#: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:811
+#: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:809
 msgid "No shader"
 msgstr "Kein Shader"
 
@@ -2771,7 +2863,7 @@ msgid "Show Entity Names"
 msgstr "Zeige Namen der Entities"
 
 #: ..\..\radiant\xyview\GlobalXYWnd.cpp:205
-#: xml_file_content.cpp:79
+#: xml_file_content.cpp:80
 msgid "Show Blocks"
 msgstr "Zeige Blöcke"
 
@@ -2780,17 +2872,17 @@ msgid "Show Coordinates"
 msgstr "Zeige Koordinaten"
 
 #: ..\..\radiant\xyview\GlobalXYWnd.cpp:207
-#: xml_file_content.cpp:82
+#: xml_file_content.cpp:83
 msgid "Show Axes"
 msgstr "Zeige Achsen"
 
 #: ..\..\radiant\xyview\GlobalXYWnd.cpp:208
-#: xml_file_content.cpp:81
+#: xml_file_content.cpp:82
 msgid "Show Window Outline"
 msgstr "Zeige Fensterumrandung"
 
 #: ..\..\radiant\xyview\GlobalXYWnd.cpp:209
-#: xml_file_content.cpp:83
+#: xml_file_content.cpp:84
 msgid "Show Workzone"
 msgstr "Zeige Arbeitsbereich"
 
@@ -2802,6 +2894,10 @@ msgstr "Translationen sind immer achsengebunden"
 msgid "Higher Selection Priority for Entities"
 msgstr "Höhere Auswahlpriorität für Entities"
 
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:665
+msgid "Shows the mouse position in the orthoview"
+msgstr "Zeigt die aktuelle Position des Mauszeigers in der Ortho-Ansicht"
+
 #: ..\..\radiant\xyview\tools\BrushCreatorTool.cpp:25
 msgid "Drag-create Brush"
 msgstr "Erzeuge neue Brush"
@@ -2815,7 +2911,7 @@ msgid "Drag Camera"
 msgstr "Kamera verschieben"
 
 #: ..\..\radiant\xyview\tools\ClipperTool.cpp:21
-#: xml_file_content.cpp:149
+#: xml_file_content.cpp:150
 #: xml_file_content.cpp:12
 #: xml_file_content.cpp:17
 msgid "Clipper"
@@ -2841,7 +2937,7 @@ msgstr "XZ Vorne"
 msgid "YZ Side"
 msgstr "YZ Seite"
 
-#: ..\..\radiant\xyview\XYWnd.cpp:637
+#: ..\..\radiant\xyview\XYWnd.cpp:498
 #, c-format
 msgid "x: %6.1lf y: %6.1lf z: %6.1lf"
 msgstr "x: %6.1lf y: %6.1lf z: %6.1lf"
@@ -2860,7 +2956,7 @@ msgstr "Befehl bearbeiten"
 msgid "Conversation Editor"
 msgstr "Konversations-Editor"
 
-#: ..\..\plugins\dm.conversation\ConversationDialog.cpp:278
+#: ..\..\plugins\dm.conversation\ConversationDialog.cpp:280
 #, c-format
 msgid "Unable to create conversation Entity: class '%s' not found."
 msgstr "Kann Konversationsentity nicht anlegen, Klasse '%s' nicht gefunden."
@@ -2869,35 +2965,35 @@ msgstr "Kann Konversationsentity nicht anlegen, Klasse '%s' nicht gefunden."
 msgid "Edit Conversation"
 msgstr "Konversation bearbeiten"
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:67
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:89
 msgid "Actor (click to edit)"
 msgstr "Akteur (zum Bearbeiten klicken)"
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:92
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:114
 #: xml_file_content.cpp:1
 msgid "Actor"
 msgstr "Akteur"
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:96
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:118
 msgid "Wait"
 msgstr "Warten"
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:201
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:223
 #, c-format
 msgid "Actor %d"
 msgstr "Akteur %d"
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:203
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:225
 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206
 msgid "yes"
 msgstr "Ja"
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:203
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:225
 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206
 msgid "no"
 msgstr "Nein"
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:369
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:394
 msgid "New Actor"
 msgstr "Neuer Akteur"
 
@@ -4025,7 +4121,7 @@ msgstr "Entfernen"
 #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:412
 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:403
 #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:95
-#: xml_file_content.cpp:179
+#: xml_file_content.cpp:180
 #: xml_file_content.cpp:3
 #: xml_file_content.cpp:7
 #: xml_file_content.cpp:9
@@ -4171,7 +4267,8 @@ msgstr "Responses"
 msgid "Custom Stims"
 msgstr "Benutzerdefinierte Stims"
 
-#: ..\..\plugins\eclassmgr\EClassManager.cpp:330
+#: ..\..\plugins\eclassmgr\EClassManager.cpp:327
+#: ..\..\plugins\eclassmgr\EClassManager.cpp:328
 msgid "Reloading Defs"
 msgstr "Neuladen der Definitionen"
 
@@ -4217,47 +4314,51 @@ msgstr "Unbekannt"
 msgid "All Files"
 msgstr "Alle Dateien"
 
-#: ..\..\plugins\grid\Grid.cpp:151
+#: ..\..\plugins\grid\Grid.cpp:55
+msgid "Current Grid Size"
+msgstr "Aktuelle Rastergröße"
+
+#: ..\..\plugins\grid\Grid.cpp:152
 msgid "Settings/Grid"
 msgstr "Einstellungen/Raster"
 
-#: ..\..\plugins\grid\Grid.cpp:153
+#: ..\..\plugins\grid\Grid.cpp:154
 msgid "Default Grid Size"
 msgstr "Standard-Rastergröße"
 
-#: ..\..\plugins\grid\Grid.cpp:157
+#: ..\..\plugins\grid\Grid.cpp:158
 msgid "Lines"
 msgstr "Linien"
 
-#: ..\..\plugins\grid\Grid.cpp:158
+#: ..\..\plugins\grid\Grid.cpp:159
 msgid "Dotted Lines"
 msgstr "Gepunktete Linien"
 
-#: ..\..\plugins\grid\Grid.cpp:159
+#: ..\..\plugins\grid\Grid.cpp:160
 msgid "More Dotted Lines"
 msgstr "Stärker gepunktete Linien"
 
-#: ..\..\plugins\grid\Grid.cpp:160
+#: ..\..\plugins\grid\Grid.cpp:161
 msgid "Crosses"
 msgstr "Kreuze"
 
-#: ..\..\plugins\grid\Grid.cpp:161
+#: ..\..\plugins\grid\Grid.cpp:162
 msgid "Dots"
 msgstr "Punkte"
 
-#: ..\..\plugins\grid\Grid.cpp:162
+#: ..\..\plugins\grid\Grid.cpp:163
 msgid "Big Dots"
 msgstr "Große Punkte"
 
-#: ..\..\plugins\grid\Grid.cpp:163
+#: ..\..\plugins\grid\Grid.cpp:164
 msgid "Squares"
 msgstr "Quadrate"
 
-#: ..\..\plugins\grid\Grid.cpp:165
+#: ..\..\plugins\grid\Grid.cpp:166
 msgid "Major Grid Style"
 msgstr "Hauptgitter-Stil"
 
-#: ..\..\plugins\grid\Grid.cpp:166
+#: ..\..\plugins\grid\Grid.cpp:167
 msgid "Minor Grid Style"
 msgstr "Nebengitter-Stil"
 
@@ -4288,24 +4389,25 @@ msgid "Incorrect map version: required %f, found %f"
 msgstr "Ungültige Map-Version: erwartet: %f, gefunden: %f"
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:144
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:94
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:102
 #, c-format
 msgid "Primitive #%d: parse error"
 msgstr "Primitiv #%d: Parserfehler"
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:154
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:104
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:112
 #, c-format
 msgid "Primitive #%d: parse exception %s"
 msgstr "Primitiv #%d: Parserfehler %s"
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:245
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:195
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:203
 #, c-format
 msgid "Parsed invalid value '%s' for key '%s'"
 msgstr "Ungültigen Wert '%s' für Schlüssel '%s' eingelesen"
 
 #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:125
+#: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:234
 #, c-format
 msgid "BrushDefParser: invalid token '%s'"
 msgstr "BrushDefParser: Ungültiges Token '%s'"
@@ -4377,23 +4479,23 @@ msgstr "Partikeleditor..."
 msgid "Cannot save particle, it has not been registered yet."
 msgstr "Kann Partikeldefinition nicht speichern, sie wurde nicht registriert."
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:316
-#: ..\..\plugins\particles\ParticlesManager.cpp:344
+#: ..\..\plugins\particles\ParticlesManager.cpp:325
+#: ..\..\plugins\particles\ParticlesManager.cpp:353
 #, c-format
 msgid "Cannot open file for writing: %s"
 msgstr "Kann die Datei nicht im Schreibmodus öffnen: %s"
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:357
+#: ..\..\plugins\particles\ParticlesManager.cpp:366
 #, c-format
 msgid "Cannot open file for reading: %s"
 msgstr "Kann die Datei nicht im Lesemodus öffnen: %s"
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:404
+#: ..\..\plugins\particles\ParticlesManager.cpp:413
 #, c-format
 msgid "Could not remove the file %s"
 msgstr "Kann die Datei nicht entfernen: %s"
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:418
+#: ..\..\plugins\particles\ParticlesManager.cpp:427
 #, c-format
 msgid "Could not rename the temporary file %s"
 msgstr "Konnte die temporäre Datei nicht entfernen: %s"
@@ -4407,7 +4509,7 @@ msgstr "Skript"
 msgid "Reload Scripts"
 msgstr "Skripts neu laden"
 
-#: ..\..\plugins\script\ScriptMenu.cpp:51
+#: ..\..\plugins\script\ScriptMenu.cpp:60
 msgid "No scripts available"
 msgstr "Keine Skripts verfügbar"
 
@@ -4419,7 +4521,7 @@ msgstr "Python Skripteingabe"
 msgid "Run Script"
 msgstr "Starte Skript"
 
-#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:319
+#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:321
 msgid "Loading Shaders"
 msgstr "Lade Shader"
 
@@ -4458,22 +4560,18 @@ msgstr "Es existiert bereits ein Schema mit diesem Namen."
 msgid "_Filters"
 msgstr "_Filter"
 
-#: ..\..\plugins\undo\UndoSystem.cpp:384
+#: ..\..\plugins\uimanager\UIManager.cpp:115
+msgid "Describes available Mouse Commands"
+msgstr "Listet verfügbare Mauskommandos auf"
+
+#: ..\..\plugins\undo\UndoSystem.cpp:398
 msgid "Settings/Undo System"
 msgstr "Einstellungen/Undo"
 
-#: ..\..\plugins\undo\UndoSystem.cpp:385
+#: ..\..\plugins\undo\UndoSystem.cpp:399
 msgid "Undo Queue Size"
 msgstr "Größe des Undo-Stapels"
 
-#: ..\..\plugins\wavefront\WaveFrontModule.cpp:31
-msgid "Save as Obj"
-msgstr "Als OBJ speichern"
-
-#: ..\..\plugins\wavefront\WaveFrontModule.cpp:89
-msgid "Export Selection as OBJ..."
-msgstr "Auswahl als OBJ exportieren..."
-
 #: ..\..\libs\wxutil\dialog\MessageBox.cpp:20
 #: xml_file_content.cpp:8
 #: xml_file_content.cpp:10
@@ -4617,7 +4715,7 @@ msgid "Reload S&kins"
 msgstr "S&kins neu laden"
 
 #: xml_file_content.cpp:16
-#: xml_file_content.cpp:26
+#: xml_file_content.cpp:28
 msgid "Reload Materials"
 msgstr "Materials neu laden"
 
@@ -4777,501 +4875,501 @@ msgstr "Patch Inspector"
 msgid "&Texture Tool"
 msgstr "Textur-Werkzeug"
 
-#: xml_file_content.cpp:61
+#: xml_file_content.cpp:60
+msgid "AAS Area Viewer"
+msgstr "AAS Visualisierung"
+
+#: xml_file_content.cpp:62
 msgid "&Center"
 msgstr "Zentrieren"
 
-#: xml_file_content.cpp:62
+#: xml_file_content.cpp:63
 msgid "&Up Floor"
 msgstr "Ein Stockwerk höher"
 
-#: xml_file_content.cpp:63
+#: xml_file_content.cpp:64
 msgid "&Down Floor"
 msgstr "Ein Stockwerk tiefer"
 
-#: xml_file_content.cpp:64
+#: xml_file_content.cpp:65
 msgid "Far Clip Plane In"
 msgstr "Entfernte Clip-Ebene heran"
 
-#: xml_file_content.cpp:65
+#: xml_file_content.cpp:66
 msgid "Far Clip Plane Out"
 msgstr "Entfernte Clip-Ebene hinaus"
 
-#: xml_file_content.cpp:66
+#: xml_file_content.cpp:67
 msgid "Next leak spot"
 msgstr "Nächster Leak-Spot"
 
-#: xml_file_content.cpp:67
+#: xml_file_content.cpp:68
 msgid "Previous leak spot"
 msgstr "Vorheriger Leak-Spot"
 
-#: xml_file_content.cpp:68
+#: xml_file_content.cpp:69
 msgid "Orthographic"
 msgstr "2D-Ansicht"
 
-#: xml_file_content.cpp:69
+#: xml_file_content.cpp:70
 msgid "Next (XY, XZ, YZ)"
 msgstr "Nächste Ansicht (XY, XZ, YZ)"
 
-#: xml_file_content.cpp:70
+#: xml_file_content.cpp:71
 msgid "XY (Top)"
 msgstr "XY (Oben)"
 
-#: xml_file_content.cpp:71
+#: xml_file_content.cpp:72
 msgid "YZ"
 msgstr "YZ"
 
-#: xml_file_content.cpp:72
+#: xml_file_content.cpp:73
 msgid "XZ"
 msgstr "XZ"
 
-#: xml_file_content.cpp:73
+#: xml_file_content.cpp:74
 msgid "&XY 100%"
 msgstr "XY 100%"
 
-#: xml_file_content.cpp:74
+#: xml_file_content.cpp:75
 msgid "&XY Zoom In"
 msgstr "XY Einzoomen"
 
-#: xml_file_content.cpp:75
+#: xml_file_content.cpp:76
 msgid "&XY Zoom Out"
 msgstr "XY Auszoomen"
 
-#: xml_file_content.cpp:76
+#: xml_file_content.cpp:77
 msgid "Show"
 msgstr "Zeigen"
 
-#: xml_file_content.cpp:77
+#: xml_file_content.cpp:78
 msgid "Show &Angles"
 msgstr "Zeige Winkel"
 
-#: xml_file_content.cpp:78
+#: xml_file_content.cpp:79
 msgid "Show &Names"
 msgstr "Zeige Namen"
 
-#: xml_file_content.cpp:80
+#: xml_file_content.cpp:81
 msgid "Show C&oordinates"
 msgstr "Zeige Koordinaten"
 
-#: xml_file_content.cpp:84
+#: xml_file_content.cpp:85
 msgid "Show size info"
 msgstr "Zeige Größeninformation"
 
-#: xml_file_content.cpp:85
+#: xml_file_content.cpp:86
 msgid "Hide/Show"
 msgstr "Verbergen/Zeigen"
 
-#: xml_file_content.cpp:86
+#: xml_file_content.cpp:87
 msgid "Hide Selected"
 msgstr "Auswahl verbergen"
 
-#: xml_file_content.cpp:87
+#: xml_file_content.cpp:88
 msgid "Hide Deselected"
 msgstr "Nicht ausgewähltes verbergen"
 
-#: xml_file_content.cpp:88
+#: xml_file_content.cpp:89
 msgid "Show hidden"
 msgstr "Zeige Verborgenes"
 
-#: xml_file_content.cpp:90
+#: xml_file_content.cpp:91
 msgid "&Switch off"
 msgstr "Ausschalten"
 
-#: xml_file_content.cpp:91
+#: xml_file_content.cpp:92
 msgid "Set from &XY view"
 msgstr "Über XY-Ansicht festlegen"
 
-#: xml_file_content.cpp:92
+#: xml_file_content.cpp:93
 msgid "Set from &Brush"
 msgstr "Über Brush festlegen"
 
-#: xml_file_content.cpp:93
+#: xml_file_content.cpp:94
 msgid "Set from Se&lection"
 msgstr "Über Auswahl festlegen"
 
-#: xml_file_content.cpp:94
+#: xml_file_content.cpp:95
 msgid "Colours..."
 msgstr "Farben..."
 
-#: xml_file_content.cpp:95
+#: xml_file_content.cpp:96
 msgid "Background Image..."
 msgstr "Hintergrundbild..."
 
-#: xml_file_content.cpp:96
+#: xml_file_content.cpp:97
 msgid "Mo&dify"
 msgstr "Komponentenmodus"
 
-#: xml_file_content.cpp:97
+#: xml_file_content.cpp:98
 #: xml_file_content.cpp:16
 msgid "Components"
 msgstr "Komponenten"
 
-#: xml_file_content.cpp:98
+#: xml_file_content.cpp:99
 msgid "&Edges"
 msgstr "Edges"
 
-#: xml_file_content.cpp:99
+#: xml_file_content.cpp:100
 msgid "&Vertices"
 msgstr "Vertices"
 
-#: xml_file_content.cpp:100
+#: xml_file_content.cpp:101
 msgid "&Faces"
 msgstr "Faces"
 
-#: xml_file_content.cpp:101
+#: xml_file_content.cpp:102
 msgid "En&tities"
 msgstr "Entities"
 
-#: xml_file_content.cpp:102
+#: xml_file_content.cpp:103
 msgid "Nudge"
 msgstr "Verschieben"
 
-#: xml_file_content.cpp:103
+#: xml_file_content.cpp:104
 msgid "Nudge Left"
 msgstr "Verschiebe nach links"
 
-#: xml_file_content.cpp:104
+#: xml_file_content.cpp:105
 msgid "Nudge Right"
 msgstr "Verschiebe nach rechts"
 
-#: xml_file_content.cpp:105
+#: xml_file_content.cpp:106
 msgid "Nudge Up"
 msgstr "Verschiebe nach oben"
 
-#: xml_file_content.cpp:106
+#: xml_file_content.cpp:107
 msgid "Nudge Down"
 msgstr "Verschiebe nach unten"
 
-#: xml_file_content.cpp:107
+#: xml_file_content.cpp:108
 #: xml_file_content.cpp:15
 msgid "Rotate"
 msgstr "Drehen"
 
-#: xml_file_content.cpp:108
+#: xml_file_content.cpp:109
 msgid "Rotate X"
 msgstr "Drehe um X-Achse"
 
-#: xml_file_content.cpp:109
+#: xml_file_content.cpp:110
 msgid "Rotate Y"
 msgstr "Drehe um Y-Achse"
 
-#: xml_file_content.cpp:110
+#: xml_file_content.cpp:111
 msgid "Rotate Z"
 msgstr "Drehe um Z-Achse"
 
-#: xml_file_content.cpp:111
+#: xml_file_content.cpp:112
 msgid "Mirror"
 msgstr "Spiegeln"
 
-#: xml_file_content.cpp:112
+#: xml_file_content.cpp:113
 msgid "Mirror &X"
 msgstr "Spiegeln um X-Achse"
 
-#: xml_file_content.cpp:113
+#: xml_file_content.cpp:114
 msgid "Mirror &Y"
 msgstr "Spiegeln um Y-Achse"
 
-#: xml_file_content.cpp:114
+#: xml_file_content.cpp:115
 msgid "Mirror &Z"
 msgstr "Spiegeln um Z-Achse"
 
-#: xml_file_content.cpp:115
+#: xml_file_content.cpp:116
 #: xml_file_content.cpp:10
 msgid "Rotate Objects independently"
 msgstr "Objekte unabhängig voneinander drehen"
 
-#: xml_file_content.cpp:116
+#: xml_file_content.cpp:117
 msgid "Rotate and scale..."
 msgstr "Drehen und skalieren..."
 
-#: xml_file_content.cpp:117
+#: xml_file_content.cpp:118
 msgid "&Grid"
 msgstr "Raster"
 
-#: xml_file_content.cpp:118
+#: xml_file_content.cpp:119
 msgid "Snap selected to grid"
 msgstr "Auswahl am Raster ausrichten"
 
-#: xml_file_content.cpp:119
+#: xml_file_content.cpp:120
 msgid "Grid0.125"
 msgstr "Grid0.125"
 
-#: xml_file_content.cpp:120
+#: xml_file_content.cpp:121
 msgid "Grid0.25"
 msgstr "Grid0.25"
 
-#: xml_file_content.cpp:121
+#: xml_file_content.cpp:122
 msgid "Grid0.5"
 msgstr "Grid0.5"
 
-#: xml_file_content.cpp:122
+#: xml_file_content.cpp:123
 msgid "Grid1"
 msgstr "Grid1"
 
-#: xml_file_content.cpp:123
+#: xml_file_content.cpp:124
 msgid "Grid2"
 msgstr "Grid2"
 
-#: xml_file_content.cpp:124
+#: xml_file_content.cpp:125
 msgid "Grid4"
 msgstr "Grid4"
 
-#: xml_file_content.cpp:125
+#: xml_file_content.cpp:126
 msgid "Grid8"
 msgstr "Grid8"
 
-#: xml_file_content.cpp:126
+#: xml_file_content.cpp:127
 msgid "Grid16"
 msgstr "Grid16"
 
-#: xml_file_content.cpp:127
+#: xml_file_content.cpp:128
 msgid "Grid32"
 msgstr "Grid32"
 
-#: xml_file_content.cpp:128
+#: xml_file_content.cpp:129
 msgid "Grid64"
 msgstr "Grid64"
 
-#: xml_file_content.cpp:129
+#: xml_file_content.cpp:130
 msgid "Grid128"
 msgstr "Grid128"
 
-#: xml_file_content.cpp:130
+#: xml_file_content.cpp:131
 msgid "Grid256"
 msgstr "Grid256"
 
-#: xml_file_content.cpp:131
+#: xml_file_content.cpp:132
 msgid "M&ap"
 msgstr "Map"
 
-#: xml_file_content.cpp:132
+#: xml_file_content.cpp:133
 msgid "Find brush..."
 msgstr "Brush suchen..."
 
-#: xml_file_content.cpp:133
+#: xml_file_content.cpp:134
 msgid "Find and replace textures..."
 msgstr "Texturen suchen & ersetzen..."
 
-#: xml_file_content.cpp:134
+#: xml_file_content.cpp:135
 msgid "Map info..."
 msgstr "Map-Information"
 
-#: xml_file_content.cpp:135
+#: xml_file_content.cpp:136
 msgid "E&ntity"
 msgstr "Entity"
 
-#: xml_file_content.cpp:136
+#: xml_file_content.cpp:137
 msgid "&Revert group to worldspawn"
 msgstr "Gruppe zu Worldspawn umwandeln"
 
-#: xml_file_content.cpp:137
+#: xml_file_content.cpp:138
 msgid "&Connect selected entities"
 msgstr "Ausgewählte Entities verbinden"
 
-#: xml_file_content.cpp:138
+#: xml_file_content.cpp:139
 msgid "&Bind selected entities"
 msgstr "Ausgewählte Entities verknüpfen"
 
-#: xml_file_content.cpp:140
+#: xml_file_content.cpp:141
 msgid "B&rush"
 msgstr "Brush"
 
-#: xml_file_content.cpp:141
+#: xml_file_content.cpp:142
 msgid "Prism..."
 msgstr "Prisma..."
 
-#: xml_file_content.cpp:142
+#: xml_file_content.cpp:143
 msgid "Cone..."
 msgstr "Kegel..."
 
-#: xml_file_content.cpp:143
+#: xml_file_content.cpp:144
 msgid "Sphere..."
 msgstr "Kugel..."
 
-#: xml_file_content.cpp:144
+#: xml_file_content.cpp:145
 msgid "CSG"
 msgstr "CSG"
 
-#: xml_file_content.cpp:145
+#: xml_file_content.cpp:146
 msgid "Make &Hollow"
 msgstr "Aushöhlen"
 
-#: xml_file_content.cpp:146
+#: xml_file_content.cpp:147
 msgid "Make &Room"
 msgstr "Raum erstellen"
 
-#: xml_file_content.cpp:147
+#: xml_file_content.cpp:148
 msgid "CSG &Subtract"
 msgstr "CSG Subtraktion"
 
-#: xml_file_content.cpp:148
+#: xml_file_content.cpp:149
 msgid "CSG &Merge"
 msgstr "CSG Zusammenfügen"
 
-#: xml_file_content.cpp:150
+#: xml_file_content.cpp:151
 msgid "Clip Selection"
 msgstr "Auswahl clippen"
 
-#: xml_file_content.cpp:151
+#: xml_file_content.cpp:152
 msgid "Split Selection"
 msgstr "Auswahl splitten"
 
-#: xml_file_content.cpp:152
+#: xml_file_content.cpp:153
 msgid "Flip Clip Orientation"
 msgstr "Clip-Orientierung umkehren"
 
-#: xml_file_content.cpp:153
+#: xml_file_content.cpp:154
 msgid "Texture lock"
 msgstr "Textur-Lock"
 
-#: xml_file_content.cpp:154
+#: xml_file_content.cpp:155
 msgid "Create Decal Patches"
 msgstr "Decal-Patches erstellen"
 
-#: xml_file_content.cpp:156
+#: xml_file_content.cpp:157
 msgid "Make Detail"
 msgstr "Als Detail markieren"
 
-#: xml_file_content.cpp:157
+#: xml_file_content.cpp:158
 msgid "Make Structural"
 msgstr "Als Struktur markieren"
 
-#: xml_file_content.cpp:158
+#: xml_file_content.cpp:159
 msgid "&Patch"
 msgstr "Patch"
 
-#: xml_file_content.cpp:159
+#: xml_file_content.cpp:160
 msgid "Create Simple Patch Mesh"
 msgstr "Neuer einfacher Patch"
 
-#: xml_file_content.cpp:160
+#: xml_file_content.cpp:161
 msgid "Create End cap"
 msgstr "Endcap erstellen"
 
-#: xml_file_content.cpp:161
+#: xml_file_content.cpp:162
 msgid "Create Bevel"
 msgstr "Bogen erstellen"
 
-#: xml_file_content.cpp:162
+#: xml_file_content.cpp:163
 msgid "Create Cone"
 msgstr "Kegel erstellen"
 
-#: xml_file_content.cpp:163
+#: xml_file_content.cpp:164
 msgid "Create Cylinder"
 msgstr "Zylinder erstellen"
 
-#: xml_file_content.cpp:164
+#: xml_file_content.cpp:165
 msgid "Create Sphere"
 msgstr "Kugel erzeugen"
 
-#: xml_file_content.cpp:165
+#: xml_file_content.cpp:166
 msgid "More cylinders"
 msgstr "Mehr Zylinderarten"
 
-#: xml_file_content.cpp:166
+#: xml_file_content.cpp:167
 msgid "Create Dense Cylinder"
 msgstr "Erstelle \"dichten\" Zylinder"
 
-#: xml_file_content.cpp:167
+#: xml_file_content.cpp:168
 msgid "Create Very Dense Cylinder"
 msgstr "Erstelle \"sehr dichten\" Zylinder"
 
-#: xml_file_content.cpp:168
+#: xml_file_content.cpp:169
 msgid "Create Square Cylinder"
 msgstr "Erstelle quadratischen Zylinder"
 
-#: xml_file_content.cpp:169
+#: xml_file_content.cpp:170
 msgid "Insert"
 msgstr "Einfügen"
 
-#: xml_file_content.cpp:170
+#: xml_file_content.cpp:171
 msgid "Insert 2 Columns at the beginning"
 msgstr "Zwei Spalten am Anfang einfügen"
 
-#: xml_file_content.cpp:171
+#: xml_file_content.cpp:172
 msgid "Insert 2 Columns at the end"
 msgstr "Zwei Spalten am Ende einfügen"
 
-#: xml_file_content.cpp:172
+#: xml_file_content.cpp:173
 msgid "Insert 2 Rows at the beginning"
 msgstr "Zwei Zeilen am Anfang einfügen"
 
-#: xml_file_content.cpp:173
+#: xml_file_content.cpp:174
 msgid "Insert 2 Rows at the end"
 msgstr "Zwei Zeilen am Ende einfügen"
 
-#: xml_file_content.cpp:174
+#: xml_file_content.cpp:175
 msgid "Append"
 msgstr "Anfügen"
 
-#: xml_file_content.cpp:175
+#: xml_file_content.cpp:176
 msgid "Append 2 columns at the beginning"
 msgstr "Zwei Spalten am Anfang anfügen"
 
-#: xml_file_content.cpp:176
+#: xml_file_content.cpp:177
 msgid "Append 2 columns at the end"
 msgstr "Zwei Spalten am Ende anfügen"
 
-#: xml_file_content.cpp:177
+#: xml_file_content.cpp:178
 msgid "Append 2 rows at the beginning"
 msgstr "Zwei Zeilen am Anfang anfügen"
 
-#: xml_file_content.cpp:178
+#: xml_file_content.cpp:179
 msgid "Append 2 rows at the end"
 msgstr "Zwei Zeilen am Ende anfügen"
 
-#: xml_file_content.cpp:180
+#: xml_file_content.cpp:181
 msgid "Delete 2 columns from the beginning"
 msgstr "Zwei Spalten am Anfang löschen"
 
-#: xml_file_content.cpp:181
+#: xml_file_content.cpp:182
 msgid "Delete 2 columns from the end"
 msgstr "Zwei Spalten am Ende löschen"
 
-#: xml_file_content.cpp:182
+#: xml_file_content.cpp:183
 msgid "Delete 2 rows from the beginning"
 msgstr "Zwei Zeilen am Anfang löschen"
 
-#: xml_file_content.cpp:183
+#: xml_file_content.cpp:184
 msgid "Delete 2 rows from the end"
 msgstr "Zwei Spalten am Ende löschen"
 
-#: xml_file_content.cpp:184
+#: xml_file_content.cpp:185
 msgid "Matrix"
 msgstr "Matrix"
 
-#: xml_file_content.cpp:185
+#: xml_file_content.cpp:186
 msgid "Invert"
 msgstr "Invertieren"
 
-#: xml_file_content.cpp:186
+#: xml_file_content.cpp:187
 msgid "Re-disperse"
 msgstr "Neu verteilen"
 
-#: xml_file_content.cpp:187
+#: xml_file_content.cpp:188
 msgid "Rows"
 msgstr "Zeilen"
 
-#: xml_file_content.cpp:188
+#: xml_file_content.cpp:189
 msgid "Columns"
 msgstr "Spalten"
 
-#: xml_file_content.cpp:189
+#: xml_file_content.cpp:190
 msgid "Transpose"
 msgstr "Transponieren"
 
-#: xml_file_content.cpp:190
+#: xml_file_content.cpp:191
 msgid "Thicken Selected Patches"
 msgstr "Ausgewählte Patches extrudieren"
 
-#: xml_file_content.cpp:191
+#: xml_file_content.cpp:192
 msgid "Cap Selection"
 msgstr "Cap für Auswahl erstellen"
 
-#: xml_file_content.cpp:192
-msgid "Cycle Cap Texture"
-msgstr "Cap-Textur durchrotieren"
-
 #: xml_file_content.cpp:193
 msgid "Stitch Patch Textures"
 msgstr "Patch-Texturen zusammenfügen"
@@ -5509,58 +5607,66 @@ msgid "Make Room"
 msgstr "Raum erstellen"
 
 #: xml_file_content.cpp:20
+msgid "Group selected items"
+msgstr "Ausgewählte Elemente gruppieren"
+
+#: xml_file_content.cpp:21
+msgid "Ungroup selected items"
+msgstr "Gruppierte Elemente auflösen"
+
+#: xml_file_content.cpp:22
 msgid "Put caps on the current patch"
 msgstr "Cap-Patches für Auswahl erstellen"
 
-#: xml_file_content.cpp:21
+#: xml_file_content.cpp:23
 msgid "Creates a NURBS curve"
 msgstr "NURBS-Kurve erstellen"
 
-#: xml_file_content.cpp:22
+#: xml_file_content.cpp:24
 msgid "Convert the selected curve (NURBS <-> CatmullRom)"
 msgstr "Konvertiert die ausgewählte Kurve NURBS <-> CatmullRom"
 
-#: xml_file_content.cpp:23
+#: xml_file_content.cpp:25
 msgid "Appends a control point to the selected curves"
 msgstr "Einen Kurvenkontrollpunkt zur ausgewählten Kurve hinzufügen"
 
-#: xml_file_content.cpp:24
+#: xml_file_content.cpp:26
 msgid "Inserts a curve control point before the selected ones"
 msgstr "Fügt einen Kurvenkontrollpunkt vor dem ausgewählten Punkt ein"
 
-#: xml_file_content.cpp:25
+#: xml_file_content.cpp:27
 msgid "Removes the selected curve control points"
 msgstr "Ausgewählte Kurvenkontrollpunkte entfernen"
 
-#: xml_file_content.cpp:27
+#: xml_file_content.cpp:29
 msgid "Find & Replace"
 msgstr "Suchen & Ersetzen"
 
-#: xml_file_content.cpp:28
+#: xml_file_content.cpp:30
 msgid "Decrease Grid Size"
 msgstr "Rastergröße verkleinern"
 
-#: xml_file_content.cpp:29
+#: xml_file_content.cpp:31
 msgid "Increase Grid Size"
 msgstr "Rastergröße vergrößern"
 
-#: xml_file_content.cpp:30
+#: xml_file_content.cpp:32
 msgid "Snap to Grid"
 msgstr "Am Raster ausrichten"
 
-#: xml_file_content.cpp:31
+#: xml_file_content.cpp:33
 msgid "Merge Selection"
 msgstr "Auswahl zusammenführen"
 
-#: xml_file_content.cpp:32
+#: xml_file_content.cpp:34
 msgid "Flip Selection Horiz (S-Axis)"
 msgstr "Auswahl Horizontal kippen (S-Achse)"
 
-#: xml_file_content.cpp:33
+#: xml_file_content.cpp:35
 msgid "Flip Selection Vertical (T-Axis)"
 msgstr "Auswahl vertikal kippen (T-Achse)"
 
-#: xml_file_content.cpp:34
+#: xml_file_content.cpp:36
 msgid "Select Related Items"
 msgstr "Verbundene Objekte auswählen"
 
@@ -6626,6 +6732,15 @@ msgstr "Min:"
 msgid "Max:"
 msgstr "Max:"
 
+#~ msgid "Cycle Cap Texture"
+#~ msgstr "Cap-Textur durchrotieren"
+
+#~ msgid "Save as Obj"
+#~ msgstr "Als OBJ speichern"
+
+#~ msgid "Export Selection as OBJ..."
+#~ msgstr "Auswahl als OBJ exportieren..."
+
 #~ msgid "Loading models"
 #~ msgstr "Lade Modelle"
 
diff --git a/install/input.xml b/install/input.xml
index 14a9adb..f517d29 100644
--- a/install/input.xml
+++ b/install/input.xml
@@ -17,6 +17,7 @@
     <mouseToolMapping name="CameraView" id="1">
       <tool name="FreeMoveTool" button="RMB" modifiers="" />
       <tool name="JumpToObjectTool" button="RMB" modifiers="ALT" />
+      <tool name="PanViewTool" button="RMB" modifiers="CONTROL" />
       <tool name="PickShaderTool" button="MMB" modifiers="" />
       <tool name="PasteShaderProjectedTool" button="MMB" modifiers="CONTROL" />
       <tool name="PasteShaderNaturalTool" button="MMB" modifiers="SHIFT" />
@@ -127,7 +128,6 @@
 		<shortcut command="RedisperseCols" key="E" modifiers="SHIFT+CONTROL" />
 		<shortcut command="MatrixTranspose" key="M" modifiers="SHIFT+CONTROL" />
 		<shortcut command="CapCurrentCurve" key="C" modifiers="SHIFT" />
-		<shortcut command="CycleCapTexturePatch" key="N" modifiers="SHIFT+CONTROL" />
 		<shortcut command="NextLeakSpot" key="K" modifiers="SHIFT+CONTROL" />
 		<shortcut command="PrevLeakSpot" key="L" modifiers="SHIFT+CONTROL" />
 		<shortcut command="SurfaceInspector" key="S" />
@@ -169,7 +169,6 @@
 		<shortcut command="NormaliseTexture" key="" modifiers=""/>
 		<shortcut command="TexToolGridUp" key="plus" modifiers="SHIFT" />
 		<shortcut command="TexToolGridDown" key="minus" modifiers="SHIFT" />
-		<shortcut command="TexToolSnapToGrid" key="G" modifiers="CONTROL+SHIFT" />
 		<shortcut command="TexToolMergeItems" key="M" modifiers="ALT" />
 		<shortcut command="GroupCycleBackward" key="ISO_Left_Tab" modifiers="SHIFT"/>
 		<shortcut command="GroupCycleForward" key="Tab" modifiers=""/>
@@ -196,5 +195,8 @@
 		<shortcut command="LoadPosition9" key="9" modifiers="ALT"/>
 		<shortcut command="OverlayDialog" key="O" modifiers="ALT"/>
 		<shortcut command="ToggleRotationPivot" key="R" modifiers="CONTROL" />
+    <shortcut command="ToggleAasControlDialog" key="A" modifiers="CONTROL+SHIFT" />
+    <shortcut command="GroupSelected" key="G" modifiers="CONTROL+SHIFT" />
+    <shortcut command="UngroupSelected" key="U" modifiers="CONTROL+SHIFT" />
 	</shortcuts>
 </input>
diff --git a/install/menu.xml b/install/menu.xml
index 042277b..22c5c2f 100644
--- a/install/menu.xml
+++ b/install/menu.xml
@@ -76,6 +76,7 @@
 		<menuItem name="patchinspector" caption="&Patch Inspector" command="PatchInspector" />
 		<menuItem name="texturetool" caption="&Texture Tool" command="TextureTool" />
 		<menuItem name="entityList" caption="Entity List" command="EntityList" />
+    <menuItem name="aasDialog" caption="AAS Area Viewer" command="ToggleAasControlDialog" />
 		<menuSeparator />
 
 		<subMenu name="camera" caption="Camera">
@@ -293,7 +294,6 @@
 		<menuSeparator />
 		<menuItem name="thickenPatches" caption="Thicken Selected Patches" command="ThickenPatch" />
 		<menuItem name="capselection" caption="Cap Selection" command="CapCurrentCurve" icon="curve_cap.png" />
-		<menuItem name="cycleCapTexture" caption="Cycle Cap Texture" command="CycleCapTexturePatch" />
 		<menuSeparator />
 		<menuItem name="stitchTextures" caption="Stitch Patch Textures" command="StitchPatchTexture" />
     <menuItem name="bulgePatch" caption="Bulge patch" command="BulgePatch" />
diff --git a/install/scripts/commands/check_for_invalid_visportals.py b/install/scripts/commands/check_for_invalid_visportals.py
new file mode 100644
index 0000000..7641eb4
--- /dev/null
+++ b/install/scripts/commands/check_for_invalid_visportals.py
@@ -0,0 +1,69 @@
+"""Find any visportal brushes in the map with more than exactly one
+   face assigned to the textures/editor/visportal shader.   
+
+Tracker item: #4397 (http://bugs.thedarkmod.com/view.php?id=4397)
+Author: greebo
+Version: 1.0
+"""
+
+__commandName__ = 'check_invalid_visportals' # should not contain spaces
+__commandDisplayName__ = 'Test for invalid Visportals '
+
+def execute():
+
+    # Returns 1 if the brush is a valid visportal, 0 otherwise
+    def testVisportalBrush(brush, visportalShader):
+        numVisportalFaces = 0
+        
+        for index in range(brush.getNumFaces()):
+            if brush.getFace(index).getShader() == visportalShader:
+                numVisportalFaces += 1
+        
+        if numVisportalFaces != 1:
+            return 0 # brush is not OK
+            
+        return 1 # brush is ok
+
+    class VisportalResults(object):
+        numVisportals = 0
+        invalidPortals = []
+    
+    results = VisportalResults()
+    visportalShader = 'textures/editor/visportal'
+    
+    class VisportalChecker(SceneNodeVisitor):
+        def pre(self, node):
+            if node.isBrush():
+                brush = node.getBrush()
+                if brush.hasShader(visportalShader):
+                    results.numVisportals += 1
+                    
+                    if not testVisportalBrush(brush, visportalShader):
+                        results.invalidPortals.append(brush)
+                return 0 # don't traverse brushes
+            return 1
+            
+    # Instantiate a new walker object and check brushes
+    walker = VisportalChecker()
+    GlobalSceneGraph.root().traverse(walker)
+    
+    # Notify the user about the results
+    msg = '%d visportals checked, %d have errors' % (results.numVisportals, len(results.invalidPortals)) + '\n\n'
+    
+    if results.numVisportals == 0:
+        msg = 'There are no visportals in this map.'
+    
+    if len(results.invalidPortals) > 0:
+        # Unselect everything in the scene before highlighting the problematic portals
+        GlobalSelectionSystem.setSelectedAll(0)
+        
+        # Highlight the brushes one by one
+        for brush in results.invalidPortals:
+            brush.setSelected(1)
+        
+        msg += 'The problematic visportals have been highlighted.'
+    
+    GlobalDialogManager.createMessageBox('Visportal Test Results', msg, Dialog.CONFIRM).run()
+
+if __executeCommand__:
+    execute()
diff --git a/install/ui/findandreplacedialog.fbp b/install/ui/findandreplacedialog.fbp
index 0ce16e1..811de25 100644
--- a/install/ui/findandreplacedialog.fbp
+++ b/install/ui/findandreplacedialog.fbp
@@ -920,7 +920,7 @@
                         <property name="permission">none</property>
                         <object class="sizeritem" expanded="1">
                             <property name="border">6</property>
-                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxRIGHT</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
                             <property name="proportion">1</property>
                             <object class="wxStaticText" expanded="1">
                                 <property name="BottomDockable">1</property>
@@ -1003,7 +1003,7 @@
                         </object>
                         <object class="sizeritem" expanded="1">
                             <property name="border">0</property>
-                            <property name="flag">wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP</property>
+                            <property name="flag">wxBOTTOM|wxLEFT|wxRIGHT|wxTOP</property>
                             <property name="proportion">0</property>
                             <object class="wxBoxSizer" expanded="1">
                                 <property name="minimum_size"></property>
diff --git a/install/ui/findandreplacedialog.xrc b/install/ui/findandreplacedialog.xrc
index 3e547a7..4ee8ad3 100644
--- a/install/ui/findandreplacedialog.xrc
+++ b/install/ui/findandreplacedialog.xrc
@@ -1,150 +1,150 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
 <resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
-	<object class="wxPanel" name="FindReplaceDialogMainPanel">
-		<style>wxTAB_TRAVERSAL</style>
-		<size>500,200</size>
-		<object class="wxBoxSizer">
-			<orient>wxVERTICAL</orient>
-			<object class="sizeritem">
-				<option>0</option>
-				<flag>wxALL|wxEXPAND</flag>
-				<border>12</border>
-				<object class="wxFlexGridSizer">
-					<rows>2</rows>
-					<cols>4</cols>
-					<vgap>3</vgap>
-					<hgap>0</hgap>
-					<growablecols>1</growablecols>
-					<growablerows></growablerows>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_CENTER_VERTICAL</flag>
-						<border>0</border>
-						<object class="wxStaticText" name="m_staticText1">
-							<label>Find:</label>
-							<wrap>-1</wrap>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND</flag>
-						<border>6</border>
-						<object class="wxTextCtrl" name="FindReplaceDialogFindEntry">
-							<value></value>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_CENTER_VERTICAL</flag>
-						<border>0</border>
-						<object class="wxBitmapButton" name="FindReplaceDialogFindSelectButton">
-							<style>wxBU_AUTODRAW</style>
-							<bitmap stock_id="darkradiant:folder16.png" stock_client="">undefined.png</bitmap>
-							<default>0</default>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALL</flag>
-						<border>5</border>
-						<object class="wxBitmapButton" name="FindReplaceDialogFindPickButton">
-							<style>wxBU_AUTODRAW</style>
-							<bitmap stock_id="darkradiant:texture_pick.png" stock_client="">undefined.png</bitmap>
-							<default>0</default>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_CENTER_VERTICAL</flag>
-						<border>0</border>
-						<object class="wxStaticText" name="m_staticText3">
-							<label>Replace:</label>
-							<wrap>-1</wrap>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND</flag>
-						<border>6</border>
-						<object class="wxTextCtrl" name="FindReplaceDialogReplaceEntry">
-							<value></value>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_CENTER_VERTICAL</flag>
-						<border>0</border>
-						<object class="wxBitmapButton" name="FindReplaceDialogReplaceSelectButton">
-							<style>wxBU_AUTODRAW</style>
-							<bitmap stock_id="darkradiant:folder16.png" stock_client="">undefined.png</bitmap>
-							<default>0</default>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALL</flag>
-						<border>5</border>
-						<object class="wxBitmapButton" name="FindReplaceDialogReplacePickButton">
-							<style>wxBU_AUTODRAW</style>
-							<bitmap stock_id="darkradiant:texture_pick.png" stock_client="">undefined.png</bitmap>
-							<default>0</default>
-						</object>
-					</object>
-				</object>
-			</object>
-			<object class="sizeritem">
-				<option>0</option>
-				<flag>wxLEFT</flag>
-				<border>12</border>
-				<object class="wxCheckBox" name="FindReplaceDialogSearchCurSelection">
-					<label>Search current selection only</label>
-					<checked>0</checked>
-				</object>
-			</object>
-			<object class="sizeritem">
-				<option>0</option>
-				<flag>wxALL|wxEXPAND</flag>
-				<border>12</border>
-				<object class="wxBoxSizer">
-					<orient>wxHORIZONTAL</orient>
-					<object class="sizeritem">
-						<option>1</option>
-						<flag>wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxRIGHT</flag>
-						<border>6</border>
-						<object class="wxStaticText" name="FindReplaceDialogStatusLabel">
-							<size>200,-1</size>
-							<label>x shader(s) replaced.</label>
-							<wrap>-1</wrap>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP</flag>
-						<border>0</border>
-						<object class="wxBoxSizer">
-							<orient>wxHORIZONTAL</orient>
-							<object class="sizeritem">
-								<option>0</option>
-								<flag>wxLEFT</flag>
-								<border>6</border>
-								<object class="wxButton" name="FindReplaceDialogFindButton">
-									<label>Find and Replace</label>
-									<default>0</default>
-								</object>
-							</object>
-							<object class="sizeritem">
-								<option>0</option>
-								<flag>wxLEFT</flag>
-								<border>6</border>
-								<object class="wxButton" name="FindReplaceDialogCloseButton">
-									<label>Close</label>
-									<default>0</default>
-								</object>
-							</object>
-						</object>
-					</object>
-				</object>
-			</object>
-		</object>
-	</object>
+  <object class="wxPanel" name="FindReplaceDialogMainPanel">
+    <style>wxTAB_TRAVERSAL</style>
+    <size>500,200</size>
+    <object class="wxBoxSizer">
+      <orient>wxVERTICAL</orient>
+      <object class="sizeritem">
+        <option>0</option>
+        <flag>wxALL|wxEXPAND</flag>
+        <border>12</border>
+        <object class="wxFlexGridSizer">
+          <rows>2</rows>
+          <cols>4</cols>
+          <vgap>3</vgap>
+          <hgap>0</hgap>
+          <growablecols>1</growablecols>
+          <growablerows></growablerows>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALIGN_CENTER_VERTICAL</flag>
+            <border>0</border>
+            <object class="wxStaticText" name="m_staticText1">
+              <label>Find:</label>
+              <wrap>-1</wrap>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND</flag>
+            <border>6</border>
+            <object class="wxTextCtrl" name="FindReplaceDialogFindEntry">
+              <value></value>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALIGN_CENTER_VERTICAL</flag>
+            <border>0</border>
+            <object class="wxBitmapButton" name="FindReplaceDialogFindSelectButton">
+              <style>wxBU_AUTODRAW</style>
+              <bitmap stock_id="darkradiant:folder16.png" stock_client="">undefined.png</bitmap>
+              <default>0</default>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALL</flag>
+            <border>5</border>
+            <object class="wxBitmapButton" name="FindReplaceDialogFindPickButton">
+              <style>wxBU_AUTODRAW</style>
+              <bitmap stock_id="darkradiant:texture_pick.png" stock_client="">undefined.png</bitmap>
+              <default>0</default>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALIGN_CENTER_VERTICAL</flag>
+            <border>0</border>
+            <object class="wxStaticText" name="m_staticText3">
+              <label>Replace:</label>
+              <wrap>-1</wrap>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND</flag>
+            <border>6</border>
+            <object class="wxTextCtrl" name="FindReplaceDialogReplaceEntry">
+              <value></value>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALIGN_CENTER_VERTICAL</flag>
+            <border>0</border>
+            <object class="wxBitmapButton" name="FindReplaceDialogReplaceSelectButton">
+              <style>wxBU_AUTODRAW</style>
+              <bitmap stock_id="darkradiant:folder16.png" stock_client="">undefined.png</bitmap>
+              <default>0</default>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxALL</flag>
+            <border>5</border>
+            <object class="wxBitmapButton" name="FindReplaceDialogReplacePickButton">
+              <style>wxBU_AUTODRAW</style>
+              <bitmap stock_id="darkradiant:texture_pick.png" stock_client="">undefined.png</bitmap>
+              <default>0</default>
+            </object>
+          </object>
+        </object>
+      </object>
+      <object class="sizeritem">
+        <option>0</option>
+        <flag>wxLEFT</flag>
+        <border>12</border>
+        <object class="wxCheckBox" name="FindReplaceDialogSearchCurSelection">
+          <label>Search current selection only</label>
+          <checked>0</checked>
+        </object>
+      </object>
+      <object class="sizeritem">
+        <option>0</option>
+        <flag>wxALL|wxEXPAND</flag>
+        <border>12</border>
+        <object class="wxBoxSizer">
+          <orient>wxHORIZONTAL</orient>
+          <object class="sizeritem">
+            <option>1</option>
+            <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+            <border>6</border>
+            <object class="wxStaticText" name="FindReplaceDialogStatusLabel">
+              <size>200,-1</size>
+              <label>x shader(s) replaced.</label>
+              <wrap>-1</wrap>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxBOTTOM|wxLEFT|wxRIGHT|wxTOP</flag>
+            <border>0</border>
+            <object class="wxBoxSizer">
+              <orient>wxHORIZONTAL</orient>
+              <object class="sizeritem">
+                <option>0</option>
+                <flag>wxLEFT</flag>
+                <border>6</border>
+                <object class="wxButton" name="FindReplaceDialogFindButton">
+                  <label>Find and Replace</label>
+                  <default>0</default>
+                </object>
+              </object>
+              <object class="sizeritem">
+                <option>0</option>
+                <flag>wxLEFT</flag>
+                <border>6</border>
+                <object class="wxButton" name="FindReplaceDialogCloseButton">
+                  <label>Close</label>
+                  <default>0</default>
+                </object>
+              </object>
+            </object>
+          </object>
+        </object>
+      </object>
+    </object>
+  </object>
 </resource>
diff --git a/install/ui/particleeditor.fbp b/install/ui/particleeditor.fbp
index 8aa1ed0..21f34e2 100644
--- a/install/ui/particleeditor.fbp
+++ b/install/ui/particleeditor.fbp
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
 <wxFormBuilder_Project>
-    <FileVersion major="1" minor="11" />
+    <FileVersion major="1" minor="13" />
     <object class="Project" expanded="1">
         <property name="class_decoration"></property>
         <property name="code_generation">C++</property>
@@ -1704,6 +1704,7 @@
                                                                         <event name="OnSize"></event>
                                                                         <event name="OnSpinCtrl"></event>
                                                                         <event name="OnSpinCtrlText"></event>
+                                                                        <event name="OnTextEnter"></event>
                                                                         <event name="OnUpdateUI"></event>
                                                                     </object>
                                                                 </object>
@@ -2775,6 +2776,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -3064,6 +3066,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -3353,6 +3356,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -3725,6 +3729,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -3895,6 +3900,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -4263,6 +4269,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -4552,6 +4559,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -4841,6 +4849,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -5130,6 +5139,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -5419,6 +5429,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -5708,6 +5719,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -6184,6 +6196,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -6462,6 +6475,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -6834,6 +6848,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -7112,6 +7127,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -7484,6 +7500,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -7762,6 +7779,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -8134,6 +8152,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -8412,6 +8431,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -8701,6 +8721,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -9078,6 +9099,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -9721,6 +9743,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -10010,6 +10033,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -10299,6 +10323,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -10588,6 +10613,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -11596,6 +11622,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -11885,6 +11912,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -12708,6 +12736,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -12997,6 +13026,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -13286,6 +13316,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -14037,6 +14068,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -14326,6 +14358,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -14615,6 +14648,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -14904,6 +14938,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -15193,6 +15228,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -15482,6 +15518,7 @@
                                                                                 <event name="OnSize"></event>
                                                                                 <event name="OnSpinCtrl"></event>
                                                                                 <event name="OnSpinCtrlText"></event>
+                                                                                <event name="OnTextEnter"></event>
                                                                                 <event name="OnUpdateUI"></event>
                                                                             </object>
                                                                         </object>
@@ -15784,7 +15821,7 @@
                         </object>
                         <object class="sizeritem" expanded="1">
                             <property name="border">6</property>
-                            <property name="flag">wxALIGN_RIGHT|wxLEFT</property>
+                            <property name="flag">wxLEFT</property>
                             <property name="proportion">0</property>
                             <object class="wxButton" expanded="1">
                                 <property name="BottomDockable">1</property>
diff --git a/install/ui/particleeditor.xrc b/install/ui/particleeditor.xrc
index 0d21131..b9b8b21 100644
--- a/install/ui/particleeditor.xrc
+++ b/install/ui/particleeditor.xrc
@@ -1,2146 +1,2146 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
 <resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
-	<object class="wxPanel" name="ParticleEditorMainPanel">
-		<style>wxTAB_TRAVERSAL</style>
-		<size>800,665</size>
-		<object class="wxBoxSizer">
-			<orient>wxVERTICAL</orient>
-			<object class="sizeritem">
-				<option>1</option>
-				<flag>wxALL|wxEXPAND</flag>
-				<border>12</border>
-				<object class="wxSplitterWindow" name="ParticleEditorSplitter">
-					<style>wxSP_3D</style>
-					<sashpos>0</sashpos>
-					<gravity>0</gravity>
-					<minsize>0</minsize>
-					<orientation>vertical</orientation>
-					<object class="wxPanel" name="m_panel1">
-						<style>wxTAB_TRAVERSAL</style>
-						<size>250,-1</size>
-						<object class="wxBoxSizer">
-							<orient>wxVERTICAL</orient>
-							<object class="sizeritem">
-								<option>0</option>
-								<flag>wxBOTTOM</flag>
-								<border>6</border>
-								<object class="wxStaticText" name="ParticleEditorDefinitionLabel">
-									<label>Particle Definitions</label>
-									<wrap>-1</wrap>
-								</object>
-							</object>
-							<object class="sizeritem">
-								<option>3</option>
-								<flag>wxEXPAND|wxLEFT</flag>
-								<border>12</border>
-								<object class="wxBoxSizer">
-									<orient>wxHORIZONTAL</orient>
-									<object class="sizeritem">
-										<option>1</option>
-										<flag>wxEXPAND | wxALL</flag>
-										<border>5</border>
-										<object class="wxPanel" name="ParticleEditorDefinitionView">
-											<style>wxTAB_TRAVERSAL</style>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-											</object>
-										</object>
-									</object>
-									<object class="sizeritem">
-										<option>0</option>
-										<flag>wxEXPAND|wxLEFT</flag>
-										<border>6</border>
-										<object class="wxBoxSizer">
-											<orient>wxVERTICAL</orient>
-											<object class="sizeritem">
-												<option>0</option>
-												<flag>wxALL</flag>
-												<border>0</border>
-												<object class="wxButton" name="ParticleEditorNewDefButton">
-													<label>New</label>
-													<default>0</default>
-												</object>
-											</object>
-											<object class="sizeritem">
-												<option>0</option>
-												<flag>wxTOP</flag>
-												<border>6</border>
-												<object class="wxButton" name="ParticleEditorSaveDefButton">
-													<label>Save</label>
-													<default>0</default>
-												</object>
-											</object>
-											<object class="sizeritem">
-												<option>0</option>
-												<flag>wxTOP</flag>
-												<border>6</border>
-												<object class="wxButton" name="ParticleEditorCopyDefButton">
-													<label>Copy</label>
-													<default>0</default>
-												</object>
-											</object>
-										</object>
-									</object>
-								</object>
-							</object>
-							<object class="sizeritem">
-								<option>0</option>
-								<flag>wxBOTTOM|wxTOP</flag>
-								<border>6</border>
-								<object class="wxStaticText" name="ParticleEditorStageLabel">
-									<label>Particle Stages</label>
-									<wrap>-1</wrap>
-								</object>
-							</object>
-							<object class="sizeritem">
-								<option>2</option>
-								<flag>wxEXPAND | wxALL</flag>
-								<border>0</border>
-								<object class="wxPanel" name="ParticleEditorStagePanel">
-									<style>wxTAB_TRAVERSAL</style>
-									<object class="wxBoxSizer">
-										<orient>wxHORIZONTAL</orient>
-										<object class="sizeritem">
-											<option>1</option>
-											<flag>wxEXPAND|wxLEFT</flag>
-											<border>12</border>
-											<object class="wxPanel" name="ParticleEditorStageView">
-												<style>wxTAB_TRAVERSAL</style>
-												<object class="wxBoxSizer">
-													<orient>wxVERTICAL</orient>
-												</object>
-											</object>
-										</object>
-										<object class="sizeritem">
-											<option>0</option>
-											<flag>wxEXPAND|wxLEFT</flag>
-											<border>6</border>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-												<object class="sizeritem">
-													<option>1</option>
-													<flag>wxBOTTOM|wxEXPAND</flag>
-													<border>6</border>
-													<object class="wxBoxSizer">
-														<orient>wxHORIZONTAL</orient>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxRIGHT</flag>
-															<border>6</border>
-															<object class="wxBoxSizer">
-																<orient>wxVERTICAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALL|wxEXPAND</flag>
-																	<border>0</border>
-																	<object class="wxButton" name="ParticleEditorAddStageButton">
-																		<label>Add</label>
-																		<default>0</default>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxTOP|wxEXPAND</flag>
-																	<border>6</border>
-																	<object class="wxButton" name="ParticleEditorRemoveStageButton">
-																		<label>Remove</label>
-																		<default>0</default>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxTOP|wxEXPAND</flag>
-																	<border>6</border>
-																	<object class="wxButton" name="ParticleEditorToggleStageButton">
-																		<label>Toggle Visibility</label>
-																		<default>0</default>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag></flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxVERTICAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALL|wxEXPAND</flag>
-																	<border>0</border>
-																	<object class="wxButton" name="ParticleEditorMoveUpStageButton">
-																		<label>Up</label>
-																		<default>0</default>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxTOP|wxEXPAND</flag>
-																	<border>6</border>
-																	<object class="wxButton" name="ParticleEditorMoveDownStageButton">
-																		<label>Down</label>
-																		<default>0</default>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxTOP|wxEXPAND</flag>
-																	<border>6</border>
-																	<object class="wxButton" name="ParticleEditorDuplicateStageButton">
-																		<label>Duplicate</label>
-																		<default>0</default>
-																	</object>
-																</object>
-															</object>
-														</object>
-													</object>
-												</object>
-												<object class="sizeritem">
-													<option>0</option>
-													<flag>wxALIGN_RIGHT</flag>
-													<border>0</border>
-													<object class="wxBoxSizer">
-														<orient>wxHORIZONTAL</orient>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-															<border>6</border>
-															<object class="wxStaticText" name="m_staticText4">
-																<label>Depth Hack:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALL</flag>
-															<border>0</border>
-															<object class="wxSpinCtrl" name="ParticleEditorDepthHack">
-																<style>wxSP_ARROW_KEYS</style>
-																<size>80,-1</size>
-																<value>0</value>
-																<min>0</min>
-																<max>10</max>
-															</object>
-														</object>
-													</object>
-												</object>
-											</object>
-										</object>
-									</object>
-								</object>
-							</object>
-							<object class="sizeritem">
-								<option>0</option>
-								<flag>wxBOTTOM</flag>
-								<border>12</border>
-								<object class="wxStaticText" name="ParticleEditorStageSettingsLabel">
-									<label>Stage Settings</label>
-									<wrap>-1</wrap>
-								</object>
-							</object>
-							<object class="sizeritem">
-								<option>0</option>
-								<flag>wxEXPAND|wxLEFT</flag>
-								<border>12</border>
-								<object class="wxNotebook" name="ParticleEditorSettingsNotebook">
-									<object class="notebookpage">
-										<label>Shader</label>
-										<selected>0</selected>
-										<object class="wxPanel" name="m_panel5">
-											<style>wxTAB_TRAVERSAL</style>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-												<object class="sizeritem">
-													<option>1</option>
-													<flag>wxALL|wxEXPAND</flag>
-													<border>6</border>
-													<object class="wxFlexGridSizer">
-														<rows>7</rows>
-														<cols>2</cols>
-														<vgap>6</vgap>
-														<hgap>12</hgap>
-														<growablecols>1</growablecols>
-														<growablerows></growablerows>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText8">
-																<label>Shader:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxTextCtrl" name="ParticleEditorStageShader">
-																<value></value>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText9">
-																<label>Colour:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxTextCtrl" name="ParticleEditorStageColour">
-																<value></value>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText10">
-																<label>Fade Colour:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxTextCtrl" name="ParticleEditorStageFadeColour">
-																		<value></value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxCheckBox" name="ParticleEditorStageUseEntityColour">
-																		<label>Use Entity Colour</label>
-																		<checked>0</checked>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText11">
-																<label>Fade In Fraction:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageFadeInFrac">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageFadeInFracSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText12">
-																<label>Fade Out Fraction:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageFadeOutFrac">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageFadeOutFracSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText13">
-																<label>Fade Index Fraction:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageFadeIdxFrac">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageFadeIdxFracSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText14">
-																<label>Animation:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText15">
-																		<label>Frames:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageAnimFrames">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>60,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>9999</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxLEFT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText16">
-																		<label>Rate:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALL</flag>
-																	<border>0</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageAnimRate">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>60,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxLEFT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText17">
-																		<label>FPS</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-															</object>
-														</object>
-													</object>
-												</object>
-											</object>
-										</object>
-									</object>
-									<object class="notebookpage">
-										<label>Count / Time</label>
-										<selected>1</selected>
-										<object class="wxPanel" name="m_panel6">
-											<style>wxTAB_TRAVERSAL</style>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-												<object class="sizeritem">
-													<option>1</option>
-													<flag>wxALL|wxEXPAND</flag>
-													<border>6</border>
-													<object class="wxFlexGridSizer">
-														<rows>7</rows>
-														<cols>2</cols>
-														<vgap>6</vgap>
-														<hgap>12</hgap>
-														<growablecols>1</growablecols>
-														<growablerows></growablerows>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText111">
-																<label>Count:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageCount">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>999</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageCountSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>999</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText121">
-																<label>Duration / sec:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageDuration">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageDurationSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText131">
-																<label>Bunching:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageBunching">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageBunchingSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1311">
-																<label>Cycles:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageCycles">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>999</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageCyclesSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>1</value>
-																		<min>1</min>
-																		<max>999</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText13111">
-																<label>Time Offset / sec:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageTimeOffset">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageTimeOffsetSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText131111">
-																<label>Dead Time / sec:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageDeadTime">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageDeadTimeSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-													</object>
-												</object>
-											</object>
-										</object>
-									</object>
-									<object class="notebookpage">
-										<label>Size / Speed</label>
-										<selected>0</selected>
-										<object class="wxPanel" name="m_panel7">
-											<style>wxTAB_TRAVERSAL</style>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-												<object class="sizeritem">
-													<option>1</option>
-													<flag>wxALL|wxEXPAND</flag>
-													<border>6</border>
-													<object class="wxFlexGridSizer">
-														<rows>7</rows>
-														<cols>2</cols>
-														<vgap>6</vgap>
-														<hgap>12</hgap>
-														<growablecols>1</growablecols>
-														<growablerows></growablerows>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1111">
-																<label>Speed:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText66">
-																		<label>From:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageSpeedFrom">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageSpeedFromSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText67">
-																		<label>To:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageSpeedTo">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageSpeedToSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1211">
-																<label>Size:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText661">
-																		<label>From:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageSizeFrom">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageSizeFromSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText671">
-																		<label>To:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageSizeTo">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageSizeToSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1312">
-																<label>Rotation Speed:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText6611">
-																		<label>From:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageRotationSpeedFrom">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageRotationSpeedFromSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText6711">
-																		<label>To:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageRotationSpeedTo">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageRotationSpeedToSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText13112">
-																<label>Aspect Ratio:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText6612">
-																		<label>From:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageAspectFrom">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageAspectFromSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
-																	<border>6</border>
-																	<object class="wxStaticText" name="m_staticText6712">
-																		<label>To:</label>
-																		<wrap>-1</wrap>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageAspectTo">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>50,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>0</border>
-																	<object class="wxSlider" name="ParticleEditorStageAspectToSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText131112">
-																<label>Gravity:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageGravity">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageGravitySlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxLEFT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxCheckBox" name="ParticleEditorStageUseWorldGravity">
-																		<label>Use World Gravity</label>
-																		<checked>0</checked>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1311111">
-																<label>Bounds Expansion:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageBoundsExpansion">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-													</object>
-												</object>
-											</object>
-										</object>
-									</object>
-									<object class="notebookpage">
-										<label>Distribution</label>
-										<selected>0</selected>
-										<object class="wxPanel" name="m_panel61">
-											<style>wxTAB_TRAVERSAL</style>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-												<object class="sizeritem">
-													<option>1</option>
-													<flag>wxALL|wxEXPAND</flag>
-													<border>6</border>
-													<object class="wxFlexGridSizer">
-														<rows>7</rows>
-														<cols>2</cols>
-														<vgap>6</vgap>
-														<hgap>12</hgap>
-														<growablecols>1</growablecols>
-														<growablerows></growablerows>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1112">
-																<label>Shape:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageShapeRect">
-																		<style>wxRB_GROUP</style>
-																		<label>Rectangular</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageShapeCyl">
-																		<label>Cylindric</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageSpherical">
-																		<label>Spherical</label>
-																		<value>0</value>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1212">
-																<label>X Size:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageXSize">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageXSizeSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1313">
-																<label>Y Size:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageYSize">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageYSizeSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText13113">
-																<label>Z Size:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageZSize">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageZSizeSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageRingSizeLabel">
-																<label>Ring Size:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageRingSize">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageRingSizeSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText1311112">
-																<label>Offset:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxTextCtrl" name="ParticleEditorStageOffset">
-																<value></value>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText97">
-																<label>Randomness:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM</flag>
-															<border>3</border>
-															<object class="wxCheckBox" name="ParticleEditorStageRandomDist">
-																<label>Distribute Particles randomly within Volume</label>
-																<checked>0</checked>
-															</object>
-														</object>
-													</object>
-												</object>
-											</object>
-										</object>
-									</object>
-									<object class="notebookpage">
-										<label>Direction / Orientation</label>
-										<selected>0</selected>
-										<object class="wxPanel" name="m_panel611">
-											<style>wxTAB_TRAVERSAL</style>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-												<object class="sizeritem">
-													<option>1</option>
-													<flag>wxALL|wxEXPAND</flag>
-													<border>6</border>
-													<object class="wxFlexGridSizer">
-														<rows>7</rows>
-														<cols>2</cols>
-														<vgap>6</vgap>
-														<hgap>12</hgap>
-														<growablecols>1</growablecols>
-														<growablerows></growablerows>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText11121">
-																<label>Direction:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageCone">
-																		<style>wxRB_GROUP</style>
-																		<label>Cone</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxALL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageOutward">
-																		<label>Outward</label>
-																		<value>0</value>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageConeAngleLabel">
-																<label>Cone Angle:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageConeAngle">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageConeAngleSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageUpwardBiasLabel">
-																<label>Upward Bias:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageUpwardBias">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageUpwardBiasSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText131131">
-																<label>Orientation:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALL|wxBOTTOM|wxRIGHT|wxTOP|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageOrientView">
-																		<style>wxRB_GROUP</style>
-																		<label>View</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageOrientAimed">
-																		<label>Aimed</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageOrientX">
-																		<label>X</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageOrientY">
-																		<label>Y</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStageOrientZ">
-																		<label>Z</label>
-																		<value>0</value>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageTrailsLabel">
-																<label>Trails:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageTrails">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>500</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageTrailsSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageTimeLabel">
-																<label>Time:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageAimedTime">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageAimedTimeSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText971">
-																<label>Initial Angle:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageInitialAngle">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageInitialAngleSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-													</object>
-												</object>
-											</object>
-										</object>
-									</object>
-									<object class="notebookpage">
-										<label>Path</label>
-										<selected>0</selected>
-										<object class="wxPanel" name="m_panel6111">
-											<style>wxTAB_TRAVERSAL</style>
-											<object class="wxBoxSizer">
-												<orient>wxVERTICAL</orient>
-												<object class="sizeritem">
-													<option>1</option>
-													<flag>wxALL|wxEXPAND</flag>
-													<border>6</border>
-													<object class="wxFlexGridSizer">
-														<rows>7</rows>
-														<cols>2</cols>
-														<vgap>6</vgap>
-														<hgap>12</hgap>
-														<growablecols>1</growablecols>
-														<growablerows></growablerows>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="m_staticText111211">
-																<label>Path Type:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStagePathStandard">
-																		<style>wxRB_GROUP</style>
-																		<label>Standard</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALIGN_CENTER_VERTICAL|wxALL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStagePathFlies">
-																		<label>Flies</label>
-																		<value>0</value>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxALL</flag>
-																	<border>3</border>
-																	<object class="wxRadioButton" name="ParticleEditorStagePathHelix">
-																		<label>Helix</label>
-																		<value>0</value>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageRadialSpeedLabel">
-																<label>Radial Speed:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageRadialSpeed">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageRadialSpeedSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageAxialSpeedLabel">
-																<label>Axial Speed:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageAxialSpeed">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageAxialSpeedSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageSphereRadiusLabel">
-																<label>Sphere Radius:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageSphereRadius">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageSphereRadiusSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageCylSizeXLabel">
-																<label>Cylinder Size X:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>5</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageCylSizeX">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageCylSizeXSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageCylSizeYLabel">
-																<label>Cylinder Size Y:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageCylSizeY">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageCylSizeYSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>0</option>
-															<flag>wxALIGN_CENTER_VERTICAL</flag>
-															<border>5</border>
-															<object class="wxStaticText" name="ParticleEditorStageCylSizeZLabel">
-																<label>Cylinder Size Z:</label>
-																<wrap>-1</wrap>
-															</object>
-														</object>
-														<object class="sizeritem">
-															<option>1</option>
-															<flag>wxEXPAND</flag>
-															<border>0</border>
-															<object class="wxBoxSizer">
-																<orient>wxHORIZONTAL</orient>
-																<object class="sizeritem">
-																	<option>0</option>
-																	<flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
-																	<border>6</border>
-																	<object class="wxSpinCtrl" name="ParticleEditorStageCylSizeZ">
-																		<style>wxSP_ARROW_KEYS</style>
-																		<size>70,-1</size>
-																		<value>0</value>
-																		<min>0</min>
-																		<max>10</max>
-																	</object>
-																</object>
-																<object class="sizeritem">
-																	<option>1</option>
-																	<flag>wxALIGN_CENTER_VERTICAL</flag>
-																	<border>5</border>
-																	<object class="wxSlider" name="ParticleEditorStageCylSizeZSlider">
-																		<style>wxSL_HORIZONTAL</style>
-																		<value>50</value>
-																		<min>0</min>
-																		<max>100</max>
-																	</object>
-																</object>
-															</object>
-														</object>
-													</object>
-												</object>
-											</object>
-										</object>
-									</object>
-								</object>
-							</object>
-						</object>
-					</object>
-					<object class="wxPanel" name="ParticleEditorPreviewPanel">
-						<style>wxTAB_TRAVERSAL</style>
-						<object class="wxBoxSizer">
-							<orient>wxVERTICAL</orient>
-						</object>
-					</object>
-				</object>
-			</object>
-			<object class="sizeritem">
-				<option>0</option>
-				<flag>wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT</flag>
-				<border>12</border>
-				<object class="wxBoxSizer">
-					<orient>wxHORIZONTAL</orient>
-					<object class="sizeritem">
-						<option>1</option>
-						<flag>wxALIGN_CENTER_VERTICAL</flag>
-						<border>5</border>
-						<object class="wxStaticText" name="ParticleEditorSaveNote">
-							<label>Note: changes will be written to the file .....</label>
-							<wrap>-1</wrap>
-						</object>
-					</object>
-					<object class="sizeritem">
-						<option>0</option>
-						<flag>wxALIGN_RIGHT|wxLEFT</flag>
-						<border>6</border>
-						<object class="wxButton" name="ParticleEditorCloseButton">
-							<label>Close</label>
-							<default>0</default>
-						</object>
-					</object>
-				</object>
-			</object>
-		</object>
-	</object>
+  <object class="wxPanel" name="ParticleEditorMainPanel">
+    <style>wxTAB_TRAVERSAL</style>
+    <size>800,665</size>
+    <object class="wxBoxSizer">
+      <orient>wxVERTICAL</orient>
+      <object class="sizeritem">
+        <option>1</option>
+        <flag>wxALL|wxEXPAND</flag>
+        <border>12</border>
+        <object class="wxSplitterWindow" name="ParticleEditorSplitter">
+          <style>wxSP_3D</style>
+          <sashpos>0</sashpos>
+          <gravity>0</gravity>
+          <minsize>0</minsize>
+          <orientation>vertical</orientation>
+          <object class="wxPanel" name="m_panel1">
+            <style>wxTAB_TRAVERSAL</style>
+            <size>250,-1</size>
+            <object class="wxBoxSizer">
+              <orient>wxVERTICAL</orient>
+              <object class="sizeritem">
+                <option>0</option>
+                <flag>wxBOTTOM</flag>
+                <border>6</border>
+                <object class="wxStaticText" name="ParticleEditorDefinitionLabel">
+                  <label>Particle Definitions</label>
+                  <wrap>-1</wrap>
+                </object>
+              </object>
+              <object class="sizeritem">
+                <option>3</option>
+                <flag>wxEXPAND|wxLEFT</flag>
+                <border>12</border>
+                <object class="wxBoxSizer">
+                  <orient>wxHORIZONTAL</orient>
+                  <object class="sizeritem">
+                    <option>1</option>
+                    <flag>wxEXPAND | wxALL</flag>
+                    <border>5</border>
+                    <object class="wxPanel" name="ParticleEditorDefinitionView">
+                      <style>wxTAB_TRAVERSAL</style>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                      </object>
+                    </object>
+                  </object>
+                  <object class="sizeritem">
+                    <option>0</option>
+                    <flag>wxEXPAND|wxLEFT</flag>
+                    <border>6</border>
+                    <object class="wxBoxSizer">
+                      <orient>wxVERTICAL</orient>
+                      <object class="sizeritem">
+                        <option>0</option>
+                        <flag>wxALL</flag>
+                        <border>0</border>
+                        <object class="wxButton" name="ParticleEditorNewDefButton">
+                          <label>New</label>
+                          <default>0</default>
+                        </object>
+                      </object>
+                      <object class="sizeritem">
+                        <option>0</option>
+                        <flag>wxTOP</flag>
+                        <border>6</border>
+                        <object class="wxButton" name="ParticleEditorSaveDefButton">
+                          <label>Save</label>
+                          <default>0</default>
+                        </object>
+                      </object>
+                      <object class="sizeritem">
+                        <option>0</option>
+                        <flag>wxTOP</flag>
+                        <border>6</border>
+                        <object class="wxButton" name="ParticleEditorCopyDefButton">
+                          <label>Copy</label>
+                          <default>0</default>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                </object>
+              </object>
+              <object class="sizeritem">
+                <option>0</option>
+                <flag>wxBOTTOM|wxTOP</flag>
+                <border>6</border>
+                <object class="wxStaticText" name="ParticleEditorStageLabel">
+                  <label>Particle Stages</label>
+                  <wrap>-1</wrap>
+                </object>
+              </object>
+              <object class="sizeritem">
+                <option>2</option>
+                <flag>wxEXPAND | wxALL</flag>
+                <border>0</border>
+                <object class="wxPanel" name="ParticleEditorStagePanel">
+                  <style>wxTAB_TRAVERSAL</style>
+                  <object class="wxBoxSizer">
+                    <orient>wxHORIZONTAL</orient>
+                    <object class="sizeritem">
+                      <option>1</option>
+                      <flag>wxEXPAND|wxLEFT</flag>
+                      <border>12</border>
+                      <object class="wxPanel" name="ParticleEditorStageView">
+                        <style>wxTAB_TRAVERSAL</style>
+                        <object class="wxBoxSizer">
+                          <orient>wxVERTICAL</orient>
+                        </object>
+                      </object>
+                    </object>
+                    <object class="sizeritem">
+                      <option>0</option>
+                      <flag>wxEXPAND|wxLEFT</flag>
+                      <border>6</border>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                        <object class="sizeritem">
+                          <option>1</option>
+                          <flag>wxBOTTOM|wxEXPAND</flag>
+                          <border>6</border>
+                          <object class="wxBoxSizer">
+                            <orient>wxHORIZONTAL</orient>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxRIGHT</flag>
+                              <border>6</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxVERTICAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALL|wxEXPAND</flag>
+                                  <border>0</border>
+                                  <object class="wxButton" name="ParticleEditorAddStageButton">
+                                    <label>Add</label>
+                                    <default>0</default>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxTOP|wxEXPAND</flag>
+                                  <border>6</border>
+                                  <object class="wxButton" name="ParticleEditorRemoveStageButton">
+                                    <label>Remove</label>
+                                    <default>0</default>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxTOP|wxEXPAND</flag>
+                                  <border>6</border>
+                                  <object class="wxButton" name="ParticleEditorToggleStageButton">
+                                    <label>Toggle Visibility</label>
+                                    <default>0</default>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag></flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxVERTICAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALL|wxEXPAND</flag>
+                                  <border>0</border>
+                                  <object class="wxButton" name="ParticleEditorMoveUpStageButton">
+                                    <label>Up</label>
+                                    <default>0</default>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxTOP|wxEXPAND</flag>
+                                  <border>6</border>
+                                  <object class="wxButton" name="ParticleEditorMoveDownStageButton">
+                                    <label>Down</label>
+                                    <default>0</default>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxTOP|wxEXPAND</flag>
+                                  <border>6</border>
+                                  <object class="wxButton" name="ParticleEditorDuplicateStageButton">
+                                    <label>Duplicate</label>
+                                    <default>0</default>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                        <object class="sizeritem">
+                          <option>0</option>
+                          <flag>wxALIGN_RIGHT</flag>
+                          <border>0</border>
+                          <object class="wxBoxSizer">
+                            <orient>wxHORIZONTAL</orient>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                              <border>6</border>
+                              <object class="wxStaticText" name="m_staticText4">
+                                <label>Depth Hack:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALL</flag>
+                              <border>0</border>
+                              <object class="wxSpinCtrl" name="ParticleEditorDepthHack">
+                                <style>wxSP_ARROW_KEYS</style>
+                                <size>80,-1</size>
+                                <value>0</value>
+                                <min>0</min>
+                                <max>10</max>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                </object>
+              </object>
+              <object class="sizeritem">
+                <option>0</option>
+                <flag>wxBOTTOM</flag>
+                <border>12</border>
+                <object class="wxStaticText" name="ParticleEditorStageSettingsLabel">
+                  <label>Stage Settings</label>
+                  <wrap>-1</wrap>
+                </object>
+              </object>
+              <object class="sizeritem">
+                <option>0</option>
+                <flag>wxEXPAND|wxLEFT</flag>
+                <border>12</border>
+                <object class="wxNotebook" name="ParticleEditorSettingsNotebook">
+                  <object class="notebookpage">
+                    <label>Shader</label>
+                    <selected>0</selected>
+                    <object class="wxPanel" name="m_panel5">
+                      <style>wxTAB_TRAVERSAL</style>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                        <object class="sizeritem">
+                          <option>1</option>
+                          <flag>wxALL|wxEXPAND</flag>
+                          <border>6</border>
+                          <object class="wxFlexGridSizer">
+                            <rows>7</rows>
+                            <cols>2</cols>
+                            <vgap>6</vgap>
+                            <hgap>12</hgap>
+                            <growablecols>1</growablecols>
+                            <growablerows></growablerows>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText8">
+                                <label>Shader:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxTextCtrl" name="ParticleEditorStageShader">
+                                <value></value>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText9">
+                                <label>Colour:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxTextCtrl" name="ParticleEditorStageColour">
+                                <value></value>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText10">
+                                <label>Fade Colour:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxTextCtrl" name="ParticleEditorStageFadeColour">
+                                    <value></value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxCheckBox" name="ParticleEditorStageUseEntityColour">
+                                    <label>Use Entity Colour</label>
+                                    <checked>0</checked>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText11">
+                                <label>Fade In Fraction:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageFadeInFrac">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageFadeInFracSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText12">
+                                <label>Fade Out Fraction:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageFadeOutFrac">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageFadeOutFracSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText13">
+                                <label>Fade Index Fraction:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageFadeIdxFrac">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageFadeIdxFracSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText14">
+                                <label>Animation:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText15">
+                                    <label>Frames:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageAnimFrames">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>60,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>9999</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxLEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText16">
+                                    <label>Rate:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALL</flag>
+                                  <border>0</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageAnimRate">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>60,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxLEFT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText17">
+                                    <label>FPS</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                  <object class="notebookpage">
+                    <label>Count / Time</label>
+                    <selected>1</selected>
+                    <object class="wxPanel" name="m_panel6">
+                      <style>wxTAB_TRAVERSAL</style>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                        <object class="sizeritem">
+                          <option>1</option>
+                          <flag>wxALL|wxEXPAND</flag>
+                          <border>6</border>
+                          <object class="wxFlexGridSizer">
+                            <rows>7</rows>
+                            <cols>2</cols>
+                            <vgap>6</vgap>
+                            <hgap>12</hgap>
+                            <growablecols>1</growablecols>
+                            <growablerows></growablerows>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText111">
+                                <label>Count:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageCount">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>999</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageCountSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>999</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText121">
+                                <label>Duration / sec:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageDuration">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageDurationSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText131">
+                                <label>Bunching:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageBunching">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageBunchingSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1311">
+                                <label>Cycles:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageCycles">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>999</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageCyclesSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>1</value>
+                                    <min>1</min>
+                                    <max>999</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText13111">
+                                <label>Time Offset / sec:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageTimeOffset">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageTimeOffsetSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText131111">
+                                <label>Dead Time / sec:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageDeadTime">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageDeadTimeSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                  <object class="notebookpage">
+                    <label>Size / Speed</label>
+                    <selected>0</selected>
+                    <object class="wxPanel" name="m_panel7">
+                      <style>wxTAB_TRAVERSAL</style>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                        <object class="sizeritem">
+                          <option>1</option>
+                          <flag>wxALL|wxEXPAND</flag>
+                          <border>6</border>
+                          <object class="wxFlexGridSizer">
+                            <rows>7</rows>
+                            <cols>2</cols>
+                            <vgap>6</vgap>
+                            <hgap>12</hgap>
+                            <growablecols>1</growablecols>
+                            <growablerows></growablerows>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1111">
+                                <label>Speed:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText66">
+                                    <label>From:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageSpeedFrom">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageSpeedFromSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText67">
+                                    <label>To:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageSpeedTo">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageSpeedToSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1211">
+                                <label>Size:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText661">
+                                    <label>From:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageSizeFrom">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageSizeFromSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText671">
+                                    <label>To:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageSizeTo">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageSizeToSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1312">
+                                <label>Rotation Speed:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText6611">
+                                    <label>From:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageRotationSpeedFrom">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageRotationSpeedFromSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText6711">
+                                    <label>To:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageRotationSpeedTo">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageRotationSpeedToSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText13112">
+                                <label>Aspect Ratio:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText6612">
+                                    <label>From:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageAspectFrom">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageAspectFromSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
+                                  <border>6</border>
+                                  <object class="wxStaticText" name="m_staticText6712">
+                                    <label>To:</label>
+                                    <wrap>-1</wrap>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageAspectTo">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>50,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>0</border>
+                                  <object class="wxSlider" name="ParticleEditorStageAspectToSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText131112">
+                                <label>Gravity:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageGravity">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageGravitySlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxLEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxCheckBox" name="ParticleEditorStageUseWorldGravity">
+                                    <label>Use World Gravity</label>
+                                    <checked>0</checked>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1311111">
+                                <label>Bounds Expansion:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageBoundsExpansion">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                  <object class="notebookpage">
+                    <label>Distribution</label>
+                    <selected>0</selected>
+                    <object class="wxPanel" name="m_panel61">
+                      <style>wxTAB_TRAVERSAL</style>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                        <object class="sizeritem">
+                          <option>1</option>
+                          <flag>wxALL|wxEXPAND</flag>
+                          <border>6</border>
+                          <object class="wxFlexGridSizer">
+                            <rows>7</rows>
+                            <cols>2</cols>
+                            <vgap>6</vgap>
+                            <hgap>12</hgap>
+                            <growablecols>1</growablecols>
+                            <growablerows></growablerows>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1112">
+                                <label>Shape:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageShapeRect">
+                                    <style>wxRB_GROUP</style>
+                                    <label>Rectangular</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageShapeCyl">
+                                    <label>Cylindric</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageSpherical">
+                                    <label>Spherical</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1212">
+                                <label>X Size:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageXSize">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageXSizeSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1313">
+                                <label>Y Size:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageYSize">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageYSizeSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText13113">
+                                <label>Z Size:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageZSize">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageZSizeSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageRingSizeLabel">
+                                <label>Ring Size:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageRingSize">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageRingSizeSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText1311112">
+                                <label>Offset:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxTextCtrl" name="ParticleEditorStageOffset">
+                                <value></value>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText97">
+                                <label>Randomness:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM</flag>
+                              <border>3</border>
+                              <object class="wxCheckBox" name="ParticleEditorStageRandomDist">
+                                <label>Distribute Particles randomly within Volume</label>
+                                <checked>0</checked>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                  <object class="notebookpage">
+                    <label>Direction / Orientation</label>
+                    <selected>0</selected>
+                    <object class="wxPanel" name="m_panel611">
+                      <style>wxTAB_TRAVERSAL</style>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                        <object class="sizeritem">
+                          <option>1</option>
+                          <flag>wxALL|wxEXPAND</flag>
+                          <border>6</border>
+                          <object class="wxFlexGridSizer">
+                            <rows>7</rows>
+                            <cols>2</cols>
+                            <vgap>6</vgap>
+                            <hgap>12</hgap>
+                            <growablecols>1</growablecols>
+                            <growablerows></growablerows>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText11121">
+                                <label>Direction:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageCone">
+                                    <style>wxRB_GROUP</style>
+                                    <label>Cone</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxALL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageOutward">
+                                    <label>Outward</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageConeAngleLabel">
+                                <label>Cone Angle:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageConeAngle">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageConeAngleSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageUpwardBiasLabel">
+                                <label>Upward Bias:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageUpwardBias">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageUpwardBiasSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText131131">
+                                <label>Orientation:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALL|wxBOTTOM|wxRIGHT|wxTOP|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageOrientView">
+                                    <style>wxRB_GROUP</style>
+                                    <label>View</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageOrientAimed">
+                                    <label>Aimed</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageOrientX">
+                                    <label>X</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageOrientY">
+                                    <label>Y</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStageOrientZ">
+                                    <label>Z</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageTrailsLabel">
+                                <label>Trails:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageTrails">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>500</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageTrailsSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageTimeLabel">
+                                <label>Time:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageAimedTime">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageAimedTimeSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText971">
+                                <label>Initial Angle:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageInitialAngle">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageInitialAngleSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                  <object class="notebookpage">
+                    <label>Path</label>
+                    <selected>0</selected>
+                    <object class="wxPanel" name="m_panel6111">
+                      <style>wxTAB_TRAVERSAL</style>
+                      <object class="wxBoxSizer">
+                        <orient>wxVERTICAL</orient>
+                        <object class="sizeritem">
+                          <option>1</option>
+                          <flag>wxALL|wxEXPAND</flag>
+                          <border>6</border>
+                          <object class="wxFlexGridSizer">
+                            <rows>7</rows>
+                            <cols>2</cols>
+                            <vgap>6</vgap>
+                            <hgap>12</hgap>
+                            <growablecols>1</growablecols>
+                            <growablerows></growablerows>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="m_staticText111211">
+                                <label>Path Type:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStagePathStandard">
+                                    <style>wxRB_GROUP</style>
+                                    <label>Standard</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL|wxALL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStagePathFlies">
+                                    <label>Flies</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxALL</flag>
+                                  <border>3</border>
+                                  <object class="wxRadioButton" name="ParticleEditorStagePathHelix">
+                                    <label>Helix</label>
+                                    <value>0</value>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageRadialSpeedLabel">
+                                <label>Radial Speed:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageRadialSpeed">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageRadialSpeedSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageAxialSpeedLabel">
+                                <label>Axial Speed:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageAxialSpeed">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageAxialSpeedSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageSphereRadiusLabel">
+                                <label>Sphere Radius:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageSphereRadius">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageSphereRadiusSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageCylSizeXLabel">
+                                <label>Cylinder Size X:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>5</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageCylSizeX">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageCylSizeXSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageCylSizeYLabel">
+                                <label>Cylinder Size Y:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageCylSizeY">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageCylSizeYSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>0</option>
+                              <flag>wxALIGN_CENTER_VERTICAL</flag>
+                              <border>5</border>
+                              <object class="wxStaticText" name="ParticleEditorStageCylSizeZLabel">
+                                <label>Cylinder Size Z:</label>
+                                <wrap>-1</wrap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <option>1</option>
+                              <flag>wxEXPAND</flag>
+                              <border>0</border>
+                              <object class="wxBoxSizer">
+                                <orient>wxHORIZONTAL</orient>
+                                <object class="sizeritem">
+                                  <option>0</option>
+                                  <flag>wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>6</border>
+                                  <object class="wxSpinCtrl" name="ParticleEditorStageCylSizeZ">
+                                    <style>wxSP_ARROW_KEYS</style>
+                                    <size>70,-1</size>
+                                    <value>0</value>
+                                    <min>0</min>
+                                    <max>10</max>
+                                  </object>
+                                </object>
+                                <object class="sizeritem">
+                                  <option>1</option>
+                                  <flag>wxALIGN_CENTER_VERTICAL</flag>
+                                  <border>5</border>
+                                  <object class="wxSlider" name="ParticleEditorStageCylSizeZSlider">
+                                    <style>wxSL_HORIZONTAL</style>
+                                    <value>50</value>
+                                    <min>0</min>
+                                    <max>100</max>
+                                  </object>
+                                </object>
+                              </object>
+                            </object>
+                          </object>
+                        </object>
+                      </object>
+                    </object>
+                  </object>
+                </object>
+              </object>
+            </object>
+          </object>
+          <object class="wxPanel" name="ParticleEditorPreviewPanel">
+            <style>wxTAB_TRAVERSAL</style>
+            <object class="wxBoxSizer">
+              <orient>wxVERTICAL</orient>
+            </object>
+          </object>
+        </object>
+      </object>
+      <object class="sizeritem">
+        <option>0</option>
+        <flag>wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT</flag>
+        <border>12</border>
+        <object class="wxBoxSizer">
+          <orient>wxHORIZONTAL</orient>
+          <object class="sizeritem">
+            <option>1</option>
+            <flag>wxALIGN_CENTER_VERTICAL</flag>
+            <border>5</border>
+            <object class="wxStaticText" name="ParticleEditorSaveNote">
+              <label>Note: changes will be written to the file .....</label>
+              <wrap>-1</wrap>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <option>0</option>
+            <flag>wxLEFT</flag>
+            <border>6</border>
+            <object class="wxButton" name="ParticleEditorCloseButton">
+              <label>Close</label>
+              <default>0</default>
+            </object>
+          </object>
+        </object>
+      </object>
+    </object>
+  </object>
 </resource>
diff --git a/install/ui/stimeditor.fbp b/install/ui/stimeditor.fbp
index 68801f7..679a0db 100644
--- a/install/ui/stimeditor.fbp
+++ b/install/ui/stimeditor.fbp
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
 <wxFormBuilder_Project>
-    <FileVersion major="1" minor="11" />
+    <FileVersion major="1" minor="13" />
     <object class="Project" expanded="1">
         <property name="class_decoration"></property>
         <property name="code_generation">C++</property>
@@ -627,6 +627,7 @@
                                             <event name="OnSize"></event>
                                             <event name="OnSpinCtrl"></event>
                                             <event name="OnSpinCtrlText"></event>
+                                            <event name="OnTextEnter"></event>
                                             <event name="OnUpdateUI"></event>
                                         </object>
                                     </object>
@@ -797,6 +798,7 @@
                                             <event name="OnSize"></event>
                                             <event name="OnSpinCtrl"></event>
                                             <event name="OnSpinCtrlText"></event>
+                                            <event name="OnTextEnter"></event>
                                             <event name="OnUpdateUI"></event>
                                         </object>
                                     </object>
@@ -885,7 +887,7 @@
                                     </object>
                                     <object class="sizeritem" expanded="0">
                                         <property name="border">6</property>
-                                        <property name="flag">wxALIGN_RIGHT|wxRIGHT|wxALIGN_CENTER_VERTICAL</property>
+                                        <property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
                                         <property name="proportion">0</property>
                                         <object class="wxSpinCtrl" expanded="0">
                                             <property name="BottomDockable">1</property>
@@ -967,6 +969,7 @@
                                             <event name="OnSize"></event>
                                             <event name="OnSpinCtrl"></event>
                                             <event name="OnSpinCtrlText"></event>
+                                            <event name="OnTextEnter"></event>
                                             <event name="OnUpdateUI"></event>
                                         </object>
                                     </object>
@@ -1137,6 +1140,7 @@
                                             <event name="OnSize"></event>
                                             <event name="OnSpinCtrl"></event>
                                             <event name="OnSpinCtrlText"></event>
+                                            <event name="OnTextEnter"></event>
                                             <event name="OnUpdateUI"></event>
                                         </object>
                                     </object>
@@ -1569,6 +1573,7 @@
                                             <event name="OnSize"></event>
                                             <event name="OnSpinCtrl"></event>
                                             <event name="OnSpinCtrlText"></event>
+                                            <event name="OnTextEnter"></event>
                                             <event name="OnUpdateUI"></event>
                                         </object>
                                     </object>
@@ -1945,6 +1950,7 @@
                                         <event name="OnSize"></event>
                                         <event name="OnSpinCtrl"></event>
                                         <event name="OnSpinCtrlText"></event>
+                                        <event name="OnTextEnter"></event>
                                         <event name="OnUpdateUI"></event>
                                     </object>
                                 </object>
@@ -2132,7 +2138,7 @@
                                 <property name="permission">none</property>
                                 <object class="sizeritem" expanded="1">
                                     <property name="border">5</property>
-                                    <property name="flag">wxEXPAND|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxEXPAND</property>
                                     <property name="proportion">1</property>
                                     <object class="wxSpinCtrl" expanded="1">
                                         <property name="BottomDockable">1</property>
@@ -2214,6 +2220,7 @@
                                         <event name="OnSize"></event>
                                         <event name="OnSpinCtrl"></event>
                                         <event name="OnSpinCtrlText"></event>
+                                        <event name="OnTextEnter"></event>
                                         <event name="OnUpdateUI"></event>
                                     </object>
                                 </object>
@@ -2483,6 +2490,7 @@
                                         <event name="OnSize"></event>
                                         <event name="OnSpinCtrl"></event>
                                         <event name="OnSpinCtrlText"></event>
+                                        <event name="OnTextEnter"></event>
                                         <event name="OnUpdateUI"></event>
                                     </object>
                                 </object>
@@ -2748,6 +2756,7 @@
                                 <event name="OnSize"></event>
                                 <event name="OnSpinCtrl"></event>
                                 <event name="OnSpinCtrlText"></event>
+                                <event name="OnTextEnter"></event>
                                 <event name="OnUpdateUI"></event>
                             </object>
                         </object>
@@ -3006,6 +3015,7 @@
                                             <event name="OnSize"></event>
                                             <event name="OnSpinCtrl"></event>
                                             <event name="OnSpinCtrlText"></event>
+                                            <event name="OnTextEnter"></event>
                                             <event name="OnUpdateUI"></event>
                                         </object>
                                     </object>
@@ -3272,6 +3282,7 @@
                                 <event name="OnSize"></event>
                                 <event name="OnSpinCtrl"></event>
                                 <event name="OnSpinCtrlText"></event>
+                                <event name="OnTextEnter"></event>
                                 <event name="OnUpdateUI"></event>
                             </object>
                         </object>
diff --git a/install/ui/stimeditor.xrc b/install/ui/stimeditor.xrc
index 75a742f..f37bb25 100644
--- a/install/ui/stimeditor.xrc
+++ b/install/ui/stimeditor.xrc
@@ -112,7 +112,7 @@
 								</object>
 								<object class="sizeritem">
 									<option>0</option>
-									<flag>wxALIGN_RIGHT|wxRIGHT|wxALIGN_CENTER_VERTICAL</flag>
+									<flag>wxALIGN_CENTER_VERTICAL|wxRIGHT</flag>
 									<border>6</border>
 									<object class="wxSpinCtrl" name="StimEditorAcivationTimerSecond">
 										<style>wxSP_ARROW_KEYS</style>
@@ -281,7 +281,7 @@
 							<orient>wxHORIZONTAL</orient>
 							<object class="sizeritem">
 								<option>1</option>
-								<flag>wxEXPAND|wxALIGN_CENTER_VERTICAL</flag>
+								<flag>wxEXPAND</flag>
 								<border>5</border>
 								<object class="wxSpinCtrl" name="StimEditorDurationValue">
 									<style>wxSP_ARROW_KEYS</style>
diff --git a/install/user.xml b/install/user.xml
index 04f7423..6c4e6ea 100644
--- a/install/user.xml
+++ b/install/user.xml
@@ -8,273 +8,283 @@
 		<fs_game value="darkmod" />
 	</game>
   <ui>
-  	<commandsystem>
-		<binds>
-			<bind name="SelectNudgeLeft" value="NudgeSelected left" readonly="1" />
-			<bind name="SelectNudgeRight" value="NudgeSelected right" readonly="1" />
-			<bind name="SelectNudgeUp" value="NudgeSelected up" readonly="1" />
-			<bind name="SelectNudgeDown" value="NudgeSelected down" readonly="1" />
-		</binds>
-	</commandsystem>
-	<map>
-		<numMRU value="5" />
-		<loadLastMap value="0" />
-		<autoSaveEnabled value="1" />
-		<autoSaveInterval value="5" />
-		<autoSaveSnapshots value="0" />
-		<snapshotFolder value="snapshots/" />
-		<maxSnapshotFolderSize value="100" />
-		<loadStatusInterleave value="50" />
-		<saveStatusInterleave value="50" />
-	</map>
-	<undo>
-		<queueSize value="256" />
-	</undo>
-	<stimResponseEditor>
-		<window xPosition="80" yPosition="100" width="740" height="480" />
-		<showStimTypeIDs value="0" />
-	</stimResponseEditor>
-	<lightInspector>
-		<window xPosition="100" yPosition="150" width="780" height="400" />
-		<instantApply value="1" />
-	</lightInspector>
-	<ModelSelector>
-		<!-- Size of the model preview, as a fraction of the screen height. The
-		     preview is always square.
-		-->
-    	<previewSizeFactor value="0.7"/>
+    <commandsystem>
+      <binds>
+        <bind name="SelectNudgeLeft" value="NudgeSelected left" readonly="1" />
+        <bind name="SelectNudgeRight" value="NudgeSelected right" readonly="1" />
+        <bind name="SelectNudgeUp" value="NudgeSelected up" readonly="1" />
+        <bind name="SelectNudgeDown" value="NudgeSelected down" readonly="1" />
+      </binds>
+    </commandsystem>
+    <map>
+      <numMRU value="5" />
+      <loadLastMap value="0" />
+      <autoSaveEnabled value="1" />
+      <autoSaveInterval value="5" />
+      <autoSaveSnapshots value="0" />
+      <snapshotFolder value="snapshots/" />
+      <maxSnapshotFolderSize value="100" />
+      <loadStatusInterleave value="50" />
+      <saveStatusInterleave value="50" />
+    </map>
+    <undo>
+      <queueSize value="256" />
+    </undo>
+    <stimResponseEditor>
+      <window xPosition="80" yPosition="100" width="900" height="560" />
+      <showStimTypeIDs value="0" />
+    </stimResponseEditor>
+    <lightInspector>
+      <window xPosition="100" yPosition="150" width="780" height="400" />
+      <instantApply value="1" />
+    </lightInspector>
+    <ModelSelector>
+      <!-- Size of the model preview, as a fraction of the screen height. The
+		       preview is always square.
+		  -->
+      <previewSizeFactor value="0.7"/>
     </ModelSelector>
-	<showAllLightRadii value="0"/>
-	<alwaysShowLightVertices value="1"/>
+    <prefabSelector>
+      <insertAsGroup value="1"/>
+    </prefabSelector>
+    <aasViewer>
+      <showNumbers value="1" />
+      <hideDistantAreas value="0" />
+      <hideDistance value="800" />
+    </aasViewer>
+    <showAllLightRadii value="0"/>
+    <alwaysShowLightVertices value="1"/>
     <rotateObjectsIndependently value="0" />
-	<rotationPivotIsOrigin value="0" />
-	<selectionEpsilon value="8.0" />
-	<dragResizeEntitiesSymmetrically value="1" />
-	<transientComponentSelection value="1" />
-	<offsetClonedObjects value="1" />
-	<transformDialog>
-    <window xPosition="150" yPosition="200" width="260" height="314" />
-		<rotXStep value="45" />
-		<rotYStep value="45" />
-		<rotZStep value="45" />
-		<scaleXStep value="1.1" />
-		<scaleYStep value="1.1" />
-		<scaleZStep value="1.1" />
-	</transformDialog>
-	<layers>
-		<controlDialog>
-			<window xPosition="57" yPosition="449" width="164" height="299" />
-		</controlDialog>
-	</layers>
-	<textures>
-		<shaderChooser>
-			<window xPosition="200" yPosition="100" width="550" height="500" />
-		</shaderChooser>
-		<browser>
-			<hideUnused value="0" />
-			<textureScale value="2" />
-			<uniformSize value="128" />
-			<showScrollBar value="1" />
-			<mouseWheelIncrement value="64" />
-			<showFilter value="0" />
-			<contextMenuMouseEpsilon value="5" />
-			<maxShadernameLength value="18" />
-			<clampToUniformSize value="1" />
-		</browser>
-		<defaultTextureScale value="0.5" />
-		<quality value="3" />
-		<mode value="5" />
-		<gamma value="1.0" />
-		<surfaceInspector>
-			<hShiftStep value="1" />
-			<vShiftStep value="1" />
-			<hScaleStep value="0.05" />
-			<vScaleStep value="0.05" />
-			<rotStep value="45" />
-			<window xPosition="200" yPosition="100" width="370" height="440" />
-		</surfaceInspector>
-		<texTool>
-			<gridActive value="1" />
-			<faceVertexScalePivotIsCentroid value="0" />
-		</texTool>
-	</textures>
-	<grid>
-		<defaultGridPower value="3" />
-	</grid>
-	<mediaBrowser>
-		<preLoadMediaTree value="1" />
-	</mediaBrowser>
-	<mainFrame>
-		<windowLayout value="1" />
-		<window xPosition="0" yPosition="0" width="800" height="580" state="4" />
-		<regular>
-			<pane name="vertical" position="650" />
-			<pane name="horizontal" position="500" />
-			<pane name="texcam" position="350" />
-		</regular>
-		<splitPane>
-			<pane name="horizontal" position="300" />
-			<pane name="vertical1" position="250" />
-			<pane name="vertical2" position="250" />
-		</splitPane>
-		<floating>
-			<groupDialogVisible value="1" />
-		</floating>
-	</mainFrame>
-	<groupDialog>
-		<window xPosition="50" yPosition="530" width="350" height="200" />
-	</groupDialog>
-	<entityList>
-		<window xPosition="50" yPosition="530" width="250" height="400" />
-	</entityList>
-	<multiMonitor>
-		<startMonitorNum value="0" />
-	</multiMonitor>
-	<xyview>
-		<views name="default">
-			<view type="XY" xPosition="500" yPosition="100" width="400" height="430"/>
-		</views>
-		<showGrid value="1" />
-		<chaseMouse value="1" />
-    <chaseMouseCap value="32" />
-		<camXYUpdate value="0" />
-		<solidSelectionBoxes value="0" />
-		<showCrossHairs value="0" />
-		<showEntityAngles value="1" />
-		<showEntityNames value="0" />
-		<showBlocks value="0" />
-		<showSizeInfo value="1" />
-		<defaultBlockSize value="1024" />
-		<showCoordinates value="1" />
-		<showOutline value="0" />
-		<showAxes value="1" />
-		<showWorkzone value="0" />
-		<overlay>
-			<visible value="0" />
-			<transparency value="0.3" />
-			<image value="" />
-			<proportional value="1" />
-			<translationX value="0.0" />
-			<translationY value="0.0" />
-			<scale value="1.0" />
-			<scaleWithOrthoView value="1" />
-			<panWithOrthoView value="0" />
-		</overlay>
-		<translateConstrained value="1" />
-		<higherEntitySelectionPriority value="1" />
-	</xyview>
-	<clipper>
-		<useCaulk value="1" />
-		<caulkTexture value="textures/common/caulk" />
-	</clipper>
-	<brush>
-		<textureLock value="1" />
-		<emitCSGSubtractWarning value="1" />
-	</brush>
-	<patch>
-		<subdivideThreshold value="2" />
-		<patchInspector>
-			<xCoordStep value="1.0" />
-			<yCoordStep value="1.0" />
-			<zCoordStep value="1.0" />
-			<sCoordStep value="0.1" />
-			<tCoordStep value="0.1" />
-			<window xPosition="130" yPosition="100" width="280" height="480" />
-		</patchInspector>
-	</patch>
+    <rotationPivotIsOrigin value="0" />
+    <selectionEpsilon value="8.0" />
+    <dragResizeEntitiesSymmetrically value="1" />
+    <transientComponentSelection value="1" />
+    <offsetClonedObjects value="1" />
+    <transformDialog>
+      <window xPosition="150" yPosition="200" width="260" height="314" />
+      <rotXStep value="45" />
+      <rotYStep value="45" />
+      <rotZStep value="45" />
+      <scaleXStep value="1.1" />
+      <scaleYStep value="1.1" />
+      <scaleZStep value="1.1" />
+    </transformDialog>
+    <layers>
+      <controlDialog>
+        <window xPosition="57" yPosition="449" width="164" height="299" />
+      </controlDialog>
+    </layers>
+    <textures>
+      <shaderChooser>
+        <window xPosition="200" yPosition="100" width="550" height="500" />
+      </shaderChooser>
+      <browser>
+        <hideUnused value="0" />
+        <textureScale value="2" />
+        <uniformSize value="128" />
+        <showScrollBar value="1" />
+        <mouseWheelIncrement value="64" />
+        <showFilter value="0" />
+        <contextMenuMouseEpsilon value="5" />
+        <maxShadernameLength value="18" />
+        <clampToUniformSize value="1" />
+      </browser>
+      <defaultTextureScale value="0.5" />
+      <quality value="3" />
+      <mode value="5" />
+      <gamma value="1.0" />
+      <surfaceInspector>
+        <hShiftStep value="1" />
+        <vShiftStep value="1" />
+        <hScaleStep value="0.05" />
+        <vScaleStep value="0.05" />
+        <rotStep value="45" />
+        <window xPosition="200" yPosition="100" width="370" height="440" />
+      </surfaceInspector>
+      <texTool>
+        <gridActive value="1" />
+        <faceVertexScalePivotIsCentroid value="0" />
+      </texTool>
+    </textures>
+    <grid>
+      <defaultGridPower value="3" />
+    </grid>
+    <mediaBrowser>
+      <preLoadMediaTree value="1" />
+    </mediaBrowser>
+    <mainFrame>
+      <windowLayout value="1" />
+      <window xPosition="0" yPosition="0" width="800" height="580" state="4" />
+      <regular>
+        <pane name="vertical" position="650" />
+        <pane name="horizontal" position="500" />
+        <pane name="texcam" position="350" />
+      </regular>
+      <splitPane>
+        <pane name="horizontal" position="300" />
+        <pane name="vertical1" position="250" />
+        <pane name="vertical2" position="250" />
+      </splitPane>
+      <floating>
+        <groupDialogVisible value="1" />
+      </floating>
+    </mainFrame>
+    <groupDialog>
+      <window xPosition="50" yPosition="530" width="350" height="200" />
+    </groupDialog>
+    <entityList>
+      <window xPosition="50" yPosition="530" width="250" height="400" />
+    </entityList>
+    <multiMonitor>
+      <startMonitorNum value="0" />
+    </multiMonitor>
+    <xyview>
+      <views name="default">
+        <view type="XY" xPosition="500" yPosition="100" width="400" height="430"/>
+      </views>
+      <showGrid value="1" />
+      <chaseMouse value="1" />
+      <chaseMouseCap value="32" />
+      <camXYUpdate value="0" />
+      <solidSelectionBoxes value="0" />
+      <showCrossHairs value="0" />
+      <showEntityAngles value="1" />
+      <showEntityNames value="0" />
+      <showBlocks value="0" />
+      <showSizeInfo value="1" />
+      <defaultBlockSize value="1024" />
+      <showCoordinates value="1" />
+      <showOutline value="0" />
+      <showAxes value="1" />
+      <showWorkzone value="0" />
+      <overlay>
+        <visible value="0" />
+        <transparency value="0.3" />
+        <image value="" />
+        <proportional value="1" />
+        <translationX value="0.0" />
+        <translationY value="0.0" />
+        <scale value="1.0" />
+        <scaleWithOrthoView value="1" />
+        <panWithOrthoView value="0" />
+      </overlay>
+      <translateConstrained value="1" />
+      <higherEntitySelectionPriority value="1" />
+    </xyview>
+    <clipper>
+      <useCaulk value="1" />
+      <caulkTexture value="textures/common/caulk" />
+    </clipper>
+    <brush>
+      <textureLock value="1" />
+      <emitCSGSubtractWarning value="1" />
+    </brush>
+    <patch>
+      <patchInspector>
+        <xCoordStep value="1.0" />
+        <yCoordStep value="1.0" />
+        <zCoordStep value="1.0" />
+        <sCoordStep value="0.1" />
+        <tCoordStep value="0.1" />
+        <window xPosition="130" yPosition="100" width="280" height="480" />
+      </patchInspector>
+    </patch>
     <particleEditor>
       <window xPosition="0" yPosition="0" width="1000" height="830" />
       <splitPos position="500" />
     </particleEditor>
     <camera>
-		<toggleFreeMove value="1" />
-		<enableCubicClipping value="1" />
-		<discreteMovement value="1" />
-		<invertMouseVerticalAxis value="0" />
-		<movementSpeed value="100" />
-		<rotationSpeed value="3" />
-		<strafespeed value="0.65" />
-		<!-- Use the forward strafe factor to invert the direction of the mouse movement -->
-		<forwardStrafeFactor value="1" />
-		<cubicScale value="13" />
-		<drawMode value="2" />
-		<window xPosition="37" yPosition="100" width="450" height="430" />
-	</camera>
+      <toggleFreeMove value="1" />
+      <enableCubicClipping value="1" />
+      <discreteMovement value="1" />
+      <invertMouseVerticalAxis value="0" />
+      <movementSpeed value="100" />
+      <rotationSpeed value="3" />
+      <strafespeed value="0.65" />
+      <!-- Use the forward strafe factor to invert the direction of the mouse movement -->
+      <forwardStrafeFactor value="1" />
+      <cubicScale value="13" />
+      <drawMode value="2" />
+      <window xPosition="37" yPosition="100" width="450" height="430" />
+    </camera>
     <toolbar name="view" align="horizontal">
-			<toolbutton name="open" action="OpenMap" tooltip="Open a map file" icon="file_open.png"/>
-			<toolbutton name="save" action="SaveMap" tooltip="Save the current map" icon="file_save.png"/>
-		<separator/>
-			<toolbutton name="changeviews" action="NextView" tooltip="Change views" icon="view_change.png"/>
-			<toggletoolbutton name="cubicclip" action="ToggleCubicClip" tooltip="Cubic clip the camera view" icon="view_cubicclipping.png"/>
-		<separator/>
-			<toolbutton name="selectcompletetall" action="SelectCompleteTall" tooltip="Select complete tall" icon="selection_selectcompletetall.png" />
-			<toolbutton name="selecttouching" action="SelectTouching" tooltip="Select touching" icon="selection_selecttouching.png"/>
-			<toolbutton name="selectinside" action="SelectInside" tooltip="Select inside" icon="selection_selectinside.png"/>
-		<separator/>
-			<toggletoolbutton name="dragvertices" action="DragVertices" tooltip="Select Vertices" icon="modify_vertices.png"/>
-			<toggletoolbutton name="dragedges" action="DragEdges" tooltip="Select Edges" icon="modify_edges.png"/>
-			<toggletoolbutton name="dragfaces" action="DragFaces" tooltip="Select Faces" icon="modify_faces.png"/>
-			<toggletoolbutton name="dragentities" action="DragEntities" tooltip="Select Entities" icon="select_entities.png"/>
-			<toggletoolbutton name="selectionModeGroupPart" action="SelectionModeGroupPart" tooltip="Select Group Parts" icon="select_group_parts.png"/>
-		<separator/>
-			<toggletoolbutton name="showlightradii" action="ToggleShowAllLightRadii" tooltip="Show/hide all light volumes" icon="view_show_lightradii.png"/>
-			<toggletoolbutton name="showspeakerradii" action="ToggleShowAllSpeakerRadii" tooltip="Show/hide all speaker volumes" icon="view_show_speakerradii.png"/>
-		<separator/>
-			<toggletoolbutton name="texturelock" action="TogTexLock" tooltip="Texture Lock" icon="texture_lock.png"/>
-			<toggletoolbutton name="freeobjectrotation" action="ToggleFreeObjectRotation" tooltip="Rotate Objects independently" icon="free_model_rotation.png" />
-			<toggletoolbutton name="togglerotationpivot" action="ToggleRotationPivot" tooltip="Rotate func_* Entities around origin" icon="rotation_pivot_origin.png" />
-			<toggletoolbutton name="dragresizesymm" action="ToggleDragResizeEntitiesSymmetrically" tooltip="Drag-resize entities symmetrically (leave origin unchanged)" icon="dragresize_symm.png" />
-			<toggletoolbutton name="offsetclones" action="ToggleOffsetClones" tooltip="Offset cloned objects by one grid unit to the right and downwards." icon="offset_clone.png" />
-		<separator/>
-			<toolbutton name="createDecals" action="CreateDecalsForFaces" tooltip="Create Decals for selected Faces" icon="create_decals.png"/>
-	</toolbar>
-	<toolbar name="edit" align="vertical">
-			<toolbutton name="xflip" action="MirrorSelectionX" tooltip="x-axis Mirror" icon="brush_flipx.png"/>
-			<toolbutton name="xrotate" action="RotateSelectionX" tooltip="x-axis Rotate" icon="brush_rotatex.png"/>
-			<toolbutton name="yflip" action="MirrorSelectionY" tooltip="y-axis Mirror" icon="brush_flipy.png"/>
-			<toolbutton name="yrotate" action="RotateSelectionY" tooltip="y-axis Rotate" icon="brush_rotatey.png"/>
-			<toolbutton name="zflip" action="MirrorSelectionZ" tooltip="z-axis Mirror" icon="brush_flipz.png"/>
-			<toolbutton name="zrotate" action="RotateSelectionZ" tooltip="z-axis Rotate" icon="brush_rotatez.png"/>
-		<separator/>
-			<toolbutton name="snaptogrid" action="SnapToGrid" tooltip="Snap selection to grid" icon="grid_snap.png"/>
+      <toolbutton name="open" action="OpenMap" tooltip="Open a map file" icon="file_open.png"/>
+      <toolbutton name="save" action="SaveMap" tooltip="Save the current map" icon="file_save.png"/>
+      <separator/>
+      <toolbutton name="changeviews" action="NextView" tooltip="Change views" icon="view_change.png"/>
+      <toggletoolbutton name="cubicclip" action="ToggleCubicClip" tooltip="Cubic clip the camera view" icon="view_cubicclipping.png"/>
+      <separator/>
+      <toolbutton name="selectcompletetall" action="SelectCompleteTall" tooltip="Select complete tall" icon="selection_selectcompletetall.png" />
+      <toolbutton name="selecttouching" action="SelectTouching" tooltip="Select touching" icon="selection_selecttouching.png"/>
+      <toolbutton name="selectinside" action="SelectInside" tooltip="Select inside" icon="selection_selectinside.png"/>
+      <separator/>
+      <toggletoolbutton name="dragvertices" action="DragVertices" tooltip="Select Vertices" icon="modify_vertices.png"/>
+      <toggletoolbutton name="dragedges" action="DragEdges" tooltip="Select Edges" icon="modify_edges.png"/>
+      <toggletoolbutton name="dragfaces" action="DragFaces" tooltip="Select Faces" icon="modify_faces.png"/>
+      <toggletoolbutton name="dragentities" action="DragEntities" tooltip="Select Entities" icon="select_entities.png"/>
+      <toggletoolbutton name="selectionModeGroupPart" action="SelectionModeGroupPart" tooltip="Select Group Parts" icon="select_group_parts.png"/>
+      <separator/>
+      <toggletoolbutton name="showlightradii" action="ToggleShowAllLightRadii" tooltip="Show/hide all light volumes" icon="view_show_lightradii.png"/>
+      <toggletoolbutton name="showspeakerradii" action="ToggleShowAllSpeakerRadii" tooltip="Show/hide all speaker volumes" icon="view_show_speakerradii.png"/>
+      <separator/>
+      <toggletoolbutton name="texturelock" action="TogTexLock" tooltip="Texture Lock" icon="texture_lock.png"/>
+      <toggletoolbutton name="freeobjectrotation" action="ToggleFreeObjectRotation" tooltip="Rotate Objects independently" icon="free_model_rotation.png" />
+      <toggletoolbutton name="togglerotationpivot" action="ToggleRotationPivot" tooltip="Rotate func_* Entities around origin" icon="rotation_pivot_origin.png" />
+      <toggletoolbutton name="dragresizesymm" action="ToggleDragResizeEntitiesSymmetrically" tooltip="Drag-resize entities symmetrically (leave origin unchanged)" icon="dragresize_symm.png" />
+      <toggletoolbutton name="offsetclones" action="ToggleOffsetClones" tooltip="Offset cloned objects by one grid unit to the right and downwards." icon="offset_clone.png" />
+      <separator/>
+      <toolbutton name="createDecals" action="CreateDecalsForFaces" tooltip="Create Decals for selected Faces" icon="create_decals.png"/>
+    </toolbar>
+    <toolbar name="edit" align="vertical">
+      <toolbutton name="xflip" action="MirrorSelectionX" tooltip="x-axis Mirror" icon="brush_flipx.png"/>
+      <toolbutton name="xrotate" action="RotateSelectionX" tooltip="x-axis Rotate" icon="brush_rotatex.png"/>
+      <toolbutton name="yflip" action="MirrorSelectionY" tooltip="y-axis Mirror" icon="brush_flipy.png"/>
+      <toolbutton name="yrotate" action="RotateSelectionY" tooltip="y-axis Rotate" icon="brush_rotatey.png"/>
+      <toolbutton name="zflip" action="MirrorSelectionZ" tooltip="z-axis Mirror" icon="brush_flipz.png"/>
+      <toolbutton name="zrotate" action="RotateSelectionZ" tooltip="z-axis Rotate" icon="brush_rotatez.png"/>
+      <separator/>
+      <toolbutton name="snaptogrid" action="SnapToGrid" tooltip="Snap selection to grid" icon="grid_snap.png"/>
       <toolbutton name="floorselected" action="FloorSelection" tooltip="Floor selection" icon="floorSelected.png"/>
-		<separator/>
-			<toggletoolbutton name="translate" action="MouseTranslate" tooltip="Translate" icon="select_mousetranslate.png"/>
-			<toggletoolbutton name="rotate" action="MouseRotate" tooltip="Rotate" icon="select_mouserotate.png"/>
-			<toggletoolbutton name="resize" action="MouseDrag" tooltip="Resize" icon="select_mouseresize.png"/>
-			<toggletoolbutton name="clipper" action="ToggleClipper" tooltip="Clipper" icon="view_clipper.png"/>
-		<separator/>
-			<toolbutton name="csgsubstract" action="CSGSubtract" tooltip="CSG Subtract" icon="selection_csgsubtract.png"/>
-			<toolbutton name="csgmerge" action="CSGMerge" tooltip="CSG Merge" icon="selection_csgmerge.png"/>
-			<toolbutton name="csghollow" action="CSGHollow" tooltip="Hollow" icon="selection_makehollow.png"/>
-			<toolbutton name="csgroom" action="CSGRoom" tooltip="Make Room" icon="selection_makeroom.png"/>
-		<separator/>
-			<toolbutton name="capcurrentcurve" action="CapCurrentCurve" tooltip="Put caps on the current patch" icon="curve_cap.png"/>
-		<separator/>
-			<toolbutton name="curvecreate" action="CreateCurveNURBS" tooltip="Creates a NURBS curve" icon="curve_create.png"/>
-			<toolbutton name="curveconvert" action="CurveConvertType" tooltip="Convert the selected curve (NURBS <-> CatmullRom)" icon="curve_convert.png"/>
-			<toolbutton name="curveappendpoint" action="CurveAppendControlPoint" tooltip="Appends a control point to the selected curves" icon="curve_append_point.png"/>
-			<toolbutton name="curveinsertpoint" action="CurveInsertControlPoint" tooltip="Inserts a curve control point before the selected ones" icon="curve_insert_points.png"/>
-			<toolbutton name="curveremovepoint" action="CurveRemoveControlPoint" tooltip="Removes the selected curve control points" icon="curve_remove_points.png"/>
-	</toolbar>
-	<toolbar name="texture" align="horizontal">
-		<toolbutton name="flush" action="RefreshShaders" tooltip="Reload Materials" icon="texwindow_flushandreload.png"/>
-		<toggletoolbutton name="showinuse" action="ShowInUse" tooltip="Hide Unused" icon="texwindow_hideunused.png"/>
-		<separator/>
-		<toolbutton name="findandreplace" action="FindReplaceTextures" tooltip="Find & Replace" icon="texwindow_findandreplace.png"/>
+      <separator/>
+      <toggletoolbutton name="translate" action="MouseTranslate" tooltip="Translate" icon="select_mousetranslate.png"/>
+      <toggletoolbutton name="rotate" action="MouseRotate" tooltip="Rotate" icon="select_mouserotate.png"/>
+      <toggletoolbutton name="resize" action="MouseDrag" tooltip="Resize" icon="select_mouseresize.png"/>
+      <toggletoolbutton name="clipper" action="ToggleClipper" tooltip="Clipper" icon="view_clipper.png"/>
+      <separator/>
+      <toolbutton name="csgsubstract" action="CSGSubtract" tooltip="CSG Subtract" icon="selection_csgsubtract.png"/>
+      <toolbutton name="csgmerge" action="CSGMerge" tooltip="CSG Merge" icon="selection_csgmerge.png"/>
+      <toolbutton name="csghollow" action="CSGHollow" tooltip="Hollow" icon="selection_makehollow.png"/>
+      <toolbutton name="csgroom" action="CSGRoom" tooltip="Make Room" icon="selection_makeroom.png"/>
+      <separator/>
+      <toolbutton name="groupSelected" action="GroupSelected" tooltip="Group selected items" icon="group_selection.png"/>
+      <toolbutton name="unGroupSelected" action="UngroupSelected" tooltip="Ungroup selected items" icon="ungroup_selection.png"/>
+      <separator/>
+      <toolbutton name="capcurrentcurve" action="CapCurrentCurve" tooltip="Put caps on the current patch" icon="curve_cap.png"/>
+      <separator/>
+      <toolbutton name="curvecreate" action="CreateCurveNURBS" tooltip="Creates a NURBS curve" icon="curve_create.png"/>
+      <toolbutton name="curveconvert" action="CurveConvertType" tooltip="Convert the selected curve (NURBS <-> CatmullRom)" icon="curve_convert.png"/>
+      <toolbutton name="curveappendpoint" action="CurveAppendControlPoint" tooltip="Appends a control point to the selected curves" icon="curve_append_point.png"/>
+      <toolbutton name="curveinsertpoint" action="CurveInsertControlPoint" tooltip="Inserts a curve control point before the selected ones" icon="curve_insert_points.png"/>
+      <toolbutton name="curveremovepoint" action="CurveRemoveControlPoint" tooltip="Removes the selected curve control points" icon="curve_remove_points.png"/>
+    </toolbar>
+    <toolbar name="texture" align="horizontal">
+      <toolbutton name="flush" action="RefreshShaders" tooltip="Reload Materials" icon="texwindow_flushandreload.png"/>
+      <toggletoolbutton name="showinuse" action="ShowInUse" tooltip="Hide Unused" icon="texwindow_hideunused.png"/>
+      <separator/>
+      <toolbutton name="findandreplace" action="FindReplaceTextures" tooltip="Find & Replace" icon="texwindow_findandreplace.png"/>
     </toolbar>
-	<toolbar name="textool" align="horizontal">
-		<toolbutton name="griddown" action="TexToolGridDown" tooltip="Decrease Grid Size" icon="grid_down.png"/>
-		<toolbutton name="gridup" action="TexToolGridUp" tooltip="Increase Grid Size" icon="grid_up.png"/>
-		<toolbutton name="gridsnap" action="TexToolSnapToGrid" tooltip="Snap to Grid" icon="grid_snap.png"/>
-		<toggletoolbutton name="usegrid" action="TexToolToggleGrid" tooltip="Toggle Grid" icon="grid_toggle.png"/>
-		<separator/>
-		<toolbutton name="mergeitems" action="TexToolMergeItems" tooltip="Merge Selection" icon="textool_merge.png"/>
-		<toolbutton name="texflips" action="TexToolFlipS" tooltip="Flip Selection Horiz (S-Axis)" icon="tex_flips.png"/>
-		<toolbutton name="texflipt" action="TexToolFlipT" tooltip="Flip Selection Vertical (T-Axis)" icon="tex_flipt.png"/>
-		<toolbutton name="selectrelated" action="TexToolSelectRelated" tooltip="Select Related Items" icon="textool_select_related.png"/>
-		<separator/>
-		<toggletoolbutton name="togglefacevertexscalepivot" action="TexToolToggleFaceVertexScalePivot" tooltip="Center Pivot when scaling faces" icon="textool_facescale_pivot.png"/>
+    <toolbar name="textool" align="horizontal">
+      <toolbutton name="griddown" action="TexToolGridDown" tooltip="Decrease Grid Size" icon="grid_down.png"/>
+      <toolbutton name="gridup" action="TexToolGridUp" tooltip="Increase Grid Size" icon="grid_up.png"/>
+      <toolbutton name="gridsnap" action="TexToolSnapToGrid" tooltip="Snap to Grid" icon="grid_snap.png"/>
+      <toggletoolbutton name="usegrid" action="TexToolToggleGrid" tooltip="Toggle Grid" icon="grid_toggle.png"/>
+      <separator/>
+      <toolbutton name="mergeitems" action="TexToolMergeItems" tooltip="Merge Selection" icon="textool_merge.png"/>
+      <toolbutton name="texflips" action="TexToolFlipS" tooltip="Flip Selection Horiz (S-Axis)" icon="tex_flips.png"/>
+      <toolbutton name="texflipt" action="TexToolFlipT" tooltip="Flip Selection Vertical (T-Axis)" icon="tex_flipt.png"/>
+      <toolbutton name="selectrelated" action="TexToolSelectRelated" tooltip="Select Related Items" icon="textool_select_related.png"/>
+      <separator/>
+      <toggletoolbutton name="togglefacevertexscalepivot" action="TexToolToggleFaceVertexScalePivot" tooltip="Center Pivot when scaling faces" icon="textool_facescale_pivot.png"/>
     </toolbar>
- </ui>
+  </ui>
 </user>
diff --git a/libs/ObservedSelectable.h b/libs/ObservedSelectable.h
index 8310338..4b69a3a 100644
--- a/libs/ObservedSelectable.h
+++ b/libs/ObservedSelectable.h
@@ -12,7 +12,7 @@ namespace selection
  * callback function when the selection state is changed.
  */
 class ObservedSelectable : 
-	public Selectable
+	public ISelectable
 {
     // Callback to invoke on selection changed
     SelectionChangedSlot _onchanged;
@@ -36,7 +36,7 @@ public:
      * Copy constructor.
      */
     ObservedSelectable(const ObservedSelectable& other) : 
-		Selectable(other), 
+		ISelectable(other), 
 		_onchanged(other._onchanged), 
 		_selected(false)
     {
@@ -58,7 +58,7 @@ public:
      * \brief
      * Set the selection state.
      */
-    virtual void setSelected(bool select)
+    virtual void setSelected(bool select) override
     {
         // Change state and invoke callback only if the new state is different
         // from the current state
@@ -73,15 +73,10 @@ public:
         }
     }
 
-	virtual bool isSelected() const
+	virtual bool isSelected() const override
 	{
 		return _selected;
 	}
-
-	virtual void invertSelected()
-	{
-		setSelected(!isSelected());
-	}
 };
 
 } // namespace
diff --git a/libs/SelectableNode.h b/libs/SelectableNode.h
deleted file mode 100644
index 3046a70..0000000
--- a/libs/SelectableNode.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include "scene/Node.h"
-#include "ObservedSelectable.h"
-#include <functional>
-
-namespace scene
-{
-
-/**
- * \brief
- * Subclass of scene::Node which implements the Selectable interface.
- *
- * The GlobalSelectionSystem will be notified of selection changes.
- */
-class SelectableNode :
-	public scene::Node,
-	public selection::ObservedSelectable
-{
-public:
-	SelectableNode() :
-		ObservedSelectable(std::bind(&SelectableNode::selectedChanged, this, std::placeholders::_1))
-	{}
-
-	// The copy-constructor doesn't copy the signal, re-connect to this instance instead
-	SelectableNode(const SelectableNode& other) :
-		scene::Node(other),
-        ObservedSelectable(std::bind(&SelectableNode::selectedChanged, this, std::placeholders::_1))
-	{}
-
-    virtual ~SelectableNode() {}
-
-    // override scene::Inode::onRemoveFromScene to de-select self
-    virtual void onRemoveFromScene(IMapRootNode& root) override
-	{
-		setSelected(false);
-
-		Node::onRemoveFromScene(root);
-	}
-
-private:
-	/**
-     * \brief
-     * Callback invoked by the ObservedSelectable when the selection changes.
-     */
-	void selectedChanged(const Selectable& selectable)
-    {
-		GlobalSelectionSystem().onSelectedChanged(Node::getSelf(), selectable);
-	}
-};
-
-} // namespace
diff --git a/libs/entitylib.h b/libs/entitylib.h
index 4658aad..cee9ef5 100644
--- a/libs/entitylib.h
+++ b/libs/entitylib.h
@@ -307,6 +307,10 @@ public:
   {
     aabb_draw_solid(m_aabb, info.getFlags());
   }
+  const AABB& getAABB() const
+  {
+	  return m_aabb;
+  }
 };
 
 class RenderableWireframeAABB : public OpenGLRenderable
@@ -398,9 +402,11 @@ inline Entity* Scene_FindEntityByClass(const std::string& className)
 
 /* Check if a node is the worldspawn.
  */
-inline bool node_is_worldspawn(const scene::INodePtr& node) {
+inline bool Node_isWorldspawn(const scene::INodePtr& node) 
+{
 	Entity* entity = Node_getEntity(node);
-	return entity != 0 && entity->getKeyValue("classname") == "worldspawn";
+
+	return entity != nullptr && entity->isWorldspawn();
 }
 
 /**
diff --git a/libs/math/Vector2.h b/libs/math/Vector2.h
index df377c2..824ad97 100644
--- a/libs/math/Vector2.h
+++ b/libs/math/Vector2.h
@@ -305,6 +305,12 @@ public:
 	Element crossProduct(const BasicVector2<OtherT>& other) const {
 		return Element(_v[0] * other.y() - _v[1] * other.x());
 	}
+
+	// Returns the mid-point of this vector and the other one
+	BasicVector2<Element> mid(const BasicVector2<Element>& other) const
+	{
+		return (*this + other) * 0.5f;
+	}
 };
 
 // ==========================================================================================
diff --git a/libs/os/file.h b/libs/os/file.h
index 2fa759e..300a87d 100644
--- a/libs/os/file.h
+++ b/libs/os/file.h
@@ -1,26 +1,4 @@
-/*
-Copyright (C) 2001-2006, William Joseph.
-All Rights Reserved.
-
-This file is part of GtkRadiant.
-
-GtkRadiant is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-GtkRadiant is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GtkRadiant; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#if !defined(INCLUDED_OS_FILE_H)
-#define INCLUDED_OS_FILE_H
+#pragma once
 
 /// \file
 /// \brief OS file-system querying and manipulation.
@@ -47,32 +25,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #include <cstddef>
 #include <ctime>
 
+#include "itextstream.h"
+#include <boost/filesystem/operations.hpp>
 #include "debugging/debugging.h"
 
-/// \brief Attempts to move the file identified by \p from to \p to and returns true if the operation was successful.
-///
-/// The operation will fail unless:
-/// - The path \p from identifies an existing file which is accessible for writing.
-/// - The directory component of \p from identifies an existing directory which is accessible for writing.
-/// - The path \p to does not identify an existing file or directory.
-/// - The directory component of \p to identifies an existing directory which is accessible for writing.
-inline bool file_move(const char* from, const char* to)
-{
-  ASSERT_MESSAGE(from != 0 && to != 0, "file_move: invalid path");
-  return rename(from, to) == 0;
-}
-
-/// \brief Attempts to remove the file identified by \p path and returns true if the operation was successful.
-///
-/// The operation will fail unless:
-/// - The \p path identifies an existing file.
-/// - The parent-directory component of \p path identifies an existing directory which is accessible for writing.
-inline bool file_remove(const char* path)
-{
-  ASSERT_MESSAGE(path != 0, "file_remove: invalid path");
-  return remove(path) == 0;
-}
-
 namespace FileAccess
 {
   enum Mode
@@ -112,51 +68,18 @@ inline bool fileOrDirExists(const std::string& path)
   return file_accessible(path.c_str(), FileAccess::Exists);
 }
 
-}
-
-/// \brief Returns true if the file or directory identified by \p path exists and is a directory.
-inline bool file_is_directory(const char* path)
+// Returns the file size in bytes, or static_cast<uintmax_t>(-1)
+inline std::size_t getFileSize(const std::string& path)
 {
-  ASSERT_MESSAGE(path != 0, "file_is_directory: invalid path");
-  struct stat st;
-  if(stat(path, &st) == -1)
-  {
-    return false;
-  }
-  return S_ISDIR (st.st_mode) != 0;
+	try
+	{
+		return static_cast<std::size_t>(boost::filesystem::file_size(path));
+	}
+	catch (boost::filesystem::filesystem_error& err)
+	{
+		rError() << "Error checking filesize: " << err.what() << std::endl;
+		return static_cast<std::size_t>(-1);
+	}
 }
 
-typedef std::size_t FileSize;
-
-/// \brief Returns the size in bytes of the file identified by \p path, or 0 if the file was not found.
-inline FileSize file_size(const char* path)
-{
-  ASSERT_MESSAGE(path != 0, "file_size: invalid path");
-  struct stat st;
-  if(stat(path, &st) == -1)
-  {
-    return 0;
-  }
-  return st.st_size;
-}
-
-/// Seconds elapsed since Jan 1, 1970
-typedef std::time_t FileTime;
-/// No file can have been modified earlier than this time.
-const FileTime c_invalidFileTime = -1;
-
-/// \brief Returns the time that the file identified by \p path was last modified, or c_invalidFileTime if the file was not found.
-inline FileTime file_modified(const char* path)
-{
-  ASSERT_MESSAGE(path != 0, "file_modified: invalid path");
-  struct stat st;
-  if(stat(path, &st) == -1)
-  {
-    return c_invalidFileTime;
-  }
-  return st.st_mtime;
-}
-
-
-
-#endif
+} // namespace
diff --git a/libs/os/path.h b/libs/os/path.h
index fc213e6..8ebb03b 100644
--- a/libs/os/path.h
+++ b/libs/os/path.h
@@ -18,9 +18,7 @@ You should have received a copy of the GNU General Public License
 along with GtkRadiant; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
-
-#if !defined (INCLUDED_OS_PATH_H)
-#define INCLUDED_OS_PATH_H
+#pragma once
 
 /// \file
 /// \brief OS file-system path comparison, decomposition and manipulation.
@@ -66,17 +64,6 @@ inline int path_compare(const char* path, const char* other)
 #endif
 }
 
-/// \brief Returns true if \p path and \p other refer to the same file or directory.
-/// O(n)
-inline bool path_equal(const char* path, const char* other)
-{
-#if defined(OS_CASE_INSENSITIVE)
-  return string_equal_nocase(path, other);
-#else
-  return string_equal(path, other);
-#endif
-}
-
 /// \brief Returns true if the first \p n bytes of \p path and \p other form paths that refer to the same file or directory.
 /// If the paths are UTF-8 encoded, [\p path, \p path + \p n) must be a complete path.
 /// O(n)
@@ -278,6 +265,3 @@ namespace os
         return trimmed.substr(lastSlash + 1);
     }
 }
-
-
-#endif
diff --git a/libs/pivot.h b/libs/pivot.h
index d0b36fd..2e2d52f 100644
--- a/libs/pivot.h
+++ b/libs/pivot.h
@@ -300,7 +300,7 @@ public:
 		// greebo: Commented this out to avoid the point from being moved along with the view.
 		//Pivot2World_worldSpace(m_localToWorld, localToWorld, volume.GetModelview(), volume.GetProjection(), volume.GetViewport());
 
-		collector.highlightPrimitives(false);
+		collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
 		collector.SetState(_shader, RenderableCollector::eWireframeOnly);
 		collector.SetState(_shader, RenderableCollector::eFullMaterials);
 		collector.addRenderable(*this, localToWorld);
diff --git a/libs/registry/Widgets.h b/libs/registry/Widgets.h
index b104cc3..b5660e3 100644
--- a/libs/registry/Widgets.h
+++ b/libs/registry/Widgets.h
@@ -4,6 +4,7 @@
 #include <wx/textctrl.h>
 #include <wx/checkbox.h>
 #include <wx/slider.h>
+#include <wx/tglbtn.h>
 #include <wx/choice.h>
 
 #include "registry.h"
@@ -66,13 +67,28 @@ inline void bindWidget(wxCheckBox* checkbox, const std::string& key)
 	});
 }
 
+inline void bindWidget(wxToggleButton* toggleButton, const std::string& key)
+{
+	// Set initial value then connect to changed signal
+	if (GlobalRegistry().keyExists(key))
+	{
+		toggleButton->SetValue(registry::getValue<bool>(key));
+	}
+
+	toggleButton->Bind(wxEVT_TOGGLEBUTTON, [=](wxCommandEvent& ev)
+	{
+		registry::setValue(key, toggleButton->GetValue() ? "1" : "0");
+		ev.Skip();
+	});
+}
+
 // ------------- Variants supporting registry::Buffer ---------------------
 
 inline void bindWidgetToBufferedKey(wxCheckBox* checkbox, const std::string& key, 
 							 Buffer& buffer, sigc::signal<void>& resetSignal)
 {
 	// Set initial value then connect to changed signal
-	checkbox->SetValue(GlobalRegistry().get(key) == "1");
+	checkbox->SetValue(registry::getValue<std::string>(key) == "1");
 
 	checkbox->Bind(wxEVT_CHECKBOX, [=, &buffer] (wxCommandEvent& ev)
 	{ 
@@ -80,7 +96,7 @@ inline void bindWidgetToBufferedKey(wxCheckBox* checkbox, const std::string& key
 		ev.Skip();
 	});
 
-	resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { checkbox->SetValue(buffer.get(key) == "1"); } });
+	resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { checkbox->SetValue(registry::getValue<std::string>(key) == "1"); } });
 }
 
 inline void bindWidgetToBufferedKey(wxSlider* slider, const std::string& key, 
@@ -100,7 +116,13 @@ inline void bindWidgetToBufferedKey(wxSlider* slider, const std::string& key,
 		ev.Skip();
 	});
 
-	resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { slider->SetValue(registry::getValue<float>(key) * factor); } });
+	resetSignal.connect([=, &buffer]
+	{ 
+		if (buffer.keyExists(key)) 
+		{ 
+			slider->SetValue(registry::getValue<float>(key) * factor); 
+		} 
+	});
 }
 
 inline void bindWidgetToBufferedKey(wxChoice* choice, const std::string& key, 
diff --git a/libs/render/RenderableSpacePartition.h b/libs/render/RenderableSpacePartition.h
index ea0c0b1..282c2b7 100644
--- a/libs/render/RenderableSpacePartition.h
+++ b/libs/render/RenderableSpacePartition.h
@@ -37,7 +37,7 @@ public:
 		_spacePartition = spacePartition;
 	}
 
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override
 	{
 		if (_shader != NULL)
 		{
@@ -46,7 +46,7 @@ public:
 		}
 	}
 
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override
 	{
 		if (_shader != NULL)
 		{
@@ -55,7 +55,7 @@ public:
 		}
 	}
 
-	void setRenderSystem(const RenderSystemPtr& renderSystem)
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override
 	{
 		if (renderSystem)
 		{
@@ -67,9 +67,9 @@ public:
 		}
 	}
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // never highlighted
+		return Highlight::NoHighlight; // never highlighted
 	}
 
 	void renderNode(const scene::ISPNodePtr& node) const
diff --git a/libs/render/ShaderStateRenderer.h b/libs/render/ShaderStateRenderer.h
index ecf44f0..ed98c43 100644
--- a/libs/render/ShaderStateRenderer.h
+++ b/libs/render/ShaderStateRenderer.h
@@ -80,9 +80,8 @@ public:
 	}
 
     // No support for selection highlighting
-	void highlightFaces(bool enable = true) { }
-	void highlightPrimitives(bool enable = true) { }
-
+	void setHighlightFlag(Highlight::Flags flags, bool enabled) {}
+	
     void setLights(const LightList& lights)
     {
         _stateStack.back().lights = &lights;
diff --git a/libs/scene/BasicRootNode.h b/libs/scene/BasicRootNode.h
index 92bee69..042dc2e 100644
--- a/libs/scene/BasicRootNode.h
+++ b/libs/scene/BasicRootNode.h
@@ -64,9 +64,9 @@ public:
     void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override
     {}
 
-    bool isHighlighted() const override
+	std::size_t getHighlightFlags() override
     {
-        return false; // never highlighted
+        return Highlight::NoHighlight; // never highlighted
     }
 };
 
diff --git a/libs/scene/Makefile.am b/libs/scene/Makefile.am
index f65741d..f1a75f1 100644
--- a/libs/scene/Makefile.am
+++ b/libs/scene/Makefile.am
@@ -5,4 +5,5 @@ libscenegraph_la_LDFLAGS = -release @PACKAGE_VERSION@ $(LIBSIGC_LIBS)
 libscenegraph_la_LIBADD = $(top_builddir)/libs/math/libmath.la
 libscenegraph_la_SOURCES = InstanceWalkers.cpp \
 			 			  TraversableNodeSet.cpp \
+						  SelectableNode.cpp \
 						  Node.cpp
diff --git a/libs/scene/Makefile.in b/libs/scene/Makefile.in
index d7d8cd7..1ce347e 100644
--- a/libs/scene/Makefile.in
+++ b/libs/scene/Makefile.in
@@ -128,7 +128,7 @@ am__installdirs = "$(DESTDIR)$(pkglibdir)"
 LTLIBRARIES = $(pkglib_LTLIBRARIES)
 libscenegraph_la_DEPENDENCIES = $(top_builddir)/libs/math/libmath.la
 am_libscenegraph_la_OBJECTS = InstanceWalkers.lo TraversableNodeSet.lo \
-	Node.lo
+	SelectableNode.lo Node.lo
 libscenegraph_la_OBJECTS = $(am_libscenegraph_la_OBJECTS)
 AM_V_lt = $(am__v_lt_ at AM_V@)
 am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
@@ -404,6 +404,7 @@ libscenegraph_la_LDFLAGS = -release @PACKAGE_VERSION@ $(LIBSIGC_LIBS)
 libscenegraph_la_LIBADD = $(top_builddir)/libs/math/libmath.la
 libscenegraph_la_SOURCES = InstanceWalkers.cpp \
 			 			  TraversableNodeSet.cpp \
+						  SelectableNode.cpp \
 						  Node.cpp
 
 all: all-am
@@ -487,6 +488,7 @@ distclean-compile:
 
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/InstanceWalkers.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/Node.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/SelectableNode.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/TraversableNodeSet.Plo at am__quote@
 
 .cpp.o:
diff --git a/libs/scene/Node.cpp b/libs/scene/Node.cpp
index fa7edea..3ce3d26 100644
--- a/libs/scene/Node.cpp
+++ b/libs/scene/Node.cpp
@@ -49,6 +49,7 @@ Node::Node() :
 	_transformMutex(false),
 	_local2world(Matrix4::getIdentity()),
 	_instantiated(false),
+	_forceVisible(false),
     _renderEntity(nullptr)
 {
 	// Each node is part of layer 0 by default
@@ -68,6 +69,7 @@ Node::Node(const Node& other) :
 	_childBoundsMutex(false),
 	_local2world(other._local2world),
 	_instantiated(false),
+	_forceVisible(false),
 	_layers(other._layers),
     _renderEntity(other._renderEntity)
 {}
@@ -118,7 +120,8 @@ bool Node::checkStateFlag(unsigned int state) const
 bool Node::visible() const
 {
 	// Only instantiated nodes can be considered visible
-	return _state == eVisible && _instantiated;
+	// The force visible flag is allowed to override the regular status
+	return (_state == eVisible && _instantiated) || _forceVisible;
 }
 
 bool Node::excluded() const
@@ -170,7 +173,16 @@ void Node::addChildNode(const INodePtr& node)
 	// Add the node to the TraversableNodeSet, this triggers an
 	// Node::onChildAdded() event, where the parent of the new
 	// child is set, among other things
-	_children.insert(node);
+	_children.append(node);
+}
+
+void Node::addChildNodeToFront(const INodePtr& node)
+{
+	// Add the node to the TraversableNodeSet at the front
+	// This behaves the same as addChildNode(), triggering a
+	// Node::onChildAdded() event, where the parent of the new
+	// child is set, among other things
+	_children.prepend(node);
 }
 
 void Node::removeChildNode(const INodePtr& node)
@@ -475,6 +487,25 @@ void Node::setRenderSystem(const RenderSystemPtr& renderSystem)
 	_children.setRenderSystem(renderSystem);
 }
 
+void Node::setForcedVisibility(bool forceVisible, bool includeChildren)
+{
+	_forceVisible = forceVisible;
+
+	if (includeChildren)
+	{
+		_children.foreachNode([&](const INodePtr& node)
+		{
+			node->setForcedVisibility(forceVisible, includeChildren);
+			return true;
+		});
+	}
+}
+
+bool Node::isForcedVisible() const
+{
+	return _forceVisible;
+}
+
 unsigned long Node::_maxNodeId = 0;
 
 } // namespace scene
diff --git a/libs/scene/Node.h b/libs/scene/Node.h
index 15f805e..feb9ef7 100644
--- a/libs/scene/Node.h
+++ b/libs/scene/Node.h
@@ -57,6 +57,10 @@ private:
 	// Is true when the node is part of the scenegraph
 	bool _instantiated;
 
+	// A special flag capable of overriding the ordinary state flags
+	// We use this to force the rendering of hidden but selected nodes
+	bool _forceVisible;
+
 	// The list of layers this object is associated to
 	LayerList _layers;
 
@@ -94,6 +98,9 @@ public:
 	bool visible() const override;
 	bool excluded() const override;
 
+	// Set the "forced visible" flag, only to be used internally by subclasses
+	void setForcedVisibility(bool forceVisible, bool includeChildren) override;
+
 	// Layered implementation
 	virtual void addToLayer(int layerId);
     virtual void removeFromLayer(int layerId);
@@ -101,9 +108,10 @@ public:
     virtual LayerList getLayers() const;
 	virtual void assignToLayers(const LayerList& newLayers);
 
-	virtual void addChildNode(const INodePtr& node);
-	virtual void removeChildNode(const INodePtr& node);
-	virtual bool hasChildNodes() const;
+	virtual void addChildNode(const INodePtr& node) override;
+	virtual void addChildNodeToFront(const INodePtr& node) override;
+	virtual void removeChildNode(const INodePtr& node) override;
+	virtual bool hasChildNodes() const override;
 
 	virtual void traverse(NodeVisitor& visitor);
 	virtual void traverseChildren(NodeVisitor& visitor) const;
@@ -187,6 +195,9 @@ public:
 	virtual void setRenderSystem(const RenderSystemPtr& renderSystem);
 
 protected:
+	// Method for subclasses to check whether this node is forcedly visible
+	bool isForcedVisible() const;
+
 	// Fills in the ancestors and self (in this order) into the given targetPath.
 	void getPathRecursively(scene::Path& targetPath);
 
diff --git a/libs/scene/SelectableNode.cpp b/libs/scene/SelectableNode.cpp
new file mode 100644
index 0000000..48677a7
--- /dev/null
+++ b/libs/scene/SelectableNode.cpp
@@ -0,0 +1,247 @@
+#include "SelectableNode.h"
+
+#include "iselection.h"
+#include "BasicUndoMemento.h"
+#include "imap.h"
+#include <stdexcept>
+#include <algorithm>
+#include <iterator>
+
+namespace scene
+{
+
+SelectableNode::SelectableNode() :
+	_selected(false),
+	_undoStateSaver(nullptr)
+{}
+
+SelectableNode::SelectableNode(const SelectableNode& other) :
+	scene::Node(other),
+	_selected(false),
+	_undoStateSaver(nullptr)
+{}
+
+SelectableNode::~SelectableNode()
+{
+	setSelected(false);
+}
+
+void SelectableNode::onInsertIntoScene(IMapRootNode& root)
+{
+	connectUndoSystem(root.getUndoChangeTracker());
+
+	Node::onInsertIntoScene(root);
+
+	// If the group ID set is not empty, this node was likely removed while
+	// it was still member of one or more groups.
+	// Try to add ourselves to any groups we were assigned to, if the group
+	// is not there anymore, it will be created.
+	for (std::size_t id : _groups)
+	{
+		selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().findOrCreateSelectionGroup(id);
+
+		if (group)
+		{
+			group->addNode(getSelf());
+		}
+	}
+}
+
+void SelectableNode::onRemoveFromScene(IMapRootNode& root)
+{
+	setSelected(false);
+
+	disconnectUndoSystem(root.getUndoChangeTracker());
+
+	// When a node is removed from the scene with a non-empty group assignment
+	// we do notify the SelectionGroup to remove ourselves, but we keep the ID list
+	// That way we can re-add ourselves when being inserted into the scene again
+
+	if (!_groups.empty())
+	{
+		// Copy the group IDs, as calling removeNode() will alter the group ID list
+		GroupIds copy(_groups);
+
+		// Remove ourselves from all groups
+		while (!_groups.empty())
+		{
+			std::size_t id = _groups.front();
+
+			selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().getSelectionGroup(id);
+
+			if (group)
+			{
+				group->removeNode(getSelf());
+			}
+			else
+			{
+				_groups.erase(_groups.begin());
+			}
+		}
+
+		// Now copy the values back in for later use
+		_groups.swap(copy);
+	}
+
+	Node::onRemoveFromScene(root);
+}
+
+void SelectableNode::setSelected(bool select)
+{
+	// Set selection status and notify group members if applicable
+	setSelected(select, false);
+}
+
+void SelectableNode::addToGroup(std::size_t groupId)
+{
+	if (std::find(_groups.begin(), _groups.end(), groupId) == _groups.end())
+	{
+		undoSave();
+
+		_groups.push_back(groupId);
+	}
+}
+
+void SelectableNode::removeFromGroup(std::size_t groupId)
+{
+	std::vector<std::size_t>::iterator i = std::find(_groups.begin(), _groups.end(), groupId);
+
+	if (i != _groups.end())
+	{
+		undoSave();
+
+		_groups.erase(i);
+	}
+}
+
+bool SelectableNode::isGroupMember()
+{
+	return !_groups.empty();
+}
+
+std::size_t SelectableNode::getMostRecentGroupId()
+{
+	if (_groups.empty()) throw std::runtime_error("This node is not a member of any group.");
+
+	return _groups.back();
+}
+
+const SelectableNode::GroupIds& SelectableNode::getGroupIds()
+{
+	return _groups;
+}
+
+void SelectableNode::setSelected(bool select, bool changeGroupStatus)
+{
+	// Change state and invoke callback only if the new state is different
+	// from the current state
+	if (select ^ _selected)
+	{
+		_selected = select;
+
+		onSelectionStatusChange(changeGroupStatus);
+	}
+}
+
+bool SelectableNode::isSelected() const
+{
+	return _selected;
+}
+
+IUndoMementoPtr SelectableNode::exportState() const
+{
+	return IUndoMementoPtr(new undo::BasicUndoMemento<GroupIds>(_groups));
+}
+
+void SelectableNode::importState(const IUndoMementoPtr& state)
+{
+	undoSave();
+
+	// Process the incoming set difference
+	GroupIds newGroups = std::static_pointer_cast< undo::BasicUndoMemento<GroupIds> >(state)->data();
+
+	// The set_difference algorithm requires the sets to be sorted
+	std::sort(_groups.begin(), _groups.end());
+	std::sort(newGroups.begin(), newGroups.end());
+
+	GroupIds removedGroups;
+	std::set_difference(_groups.begin(), _groups.end(), newGroups.begin(), newGroups.end(),
+		std::inserter(removedGroups, removedGroups.begin()));
+
+	GroupIds addedGroups;
+	std::set_difference(newGroups.begin(), newGroups.end(), _groups.begin(), _groups.end(),
+		std::inserter(addedGroups, addedGroups.begin()));
+
+	// Remove ourselves from each removed group ID
+	for (GroupIds::value_type id : removedGroups)
+	{
+		selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().getSelectionGroup(id);
+
+		if (group)
+		{
+			group->removeNode(getSelf());
+		}
+	}
+
+	// Add ourselves to each missing group ID
+	for (GroupIds::value_type id : addedGroups)
+	{
+		selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().findOrCreateSelectionGroup(id);
+
+		assert(group);
+		group->addNode(getSelf());
+	}
+
+	// The _groups array should now contain the same elements as the imported ones
+#if _DEBUG
+	std::sort(_groups.begin(), _groups.end());
+	assert(std::equal(_groups.begin(), _groups.end(), newGroups.begin()));
+#endif
+
+	// After undoing group assignments, highlight this node for better user feedback
+	setSelected(true, false);
+}
+
+void SelectableNode::onSelectionStatusChange(bool changeGroupStatus)
+{
+	bool selected = isSelected();
+
+	// Update the flag to render selected nodes regardless of their hidden status 
+	setForcedVisibility(selected, true);
+
+	GlobalSelectionSystem().onSelectedChanged(Node::getSelf(), *this);
+
+	// Check if this node is member of a group
+	if (changeGroupStatus && !_groups.empty())
+	{
+		std::size_t mostRecentGroupId = _groups.back();
+
+		// Propagate the selection status of this node to all members of the topmost group
+		GlobalSelectionGroupManager().setGroupSelected(mostRecentGroupId, selected);
+	}
+}
+
+void SelectableNode::connectUndoSystem(IMapFileChangeTracker& changeTracker)
+{
+	_undoStateSaver = GlobalUndoSystem().getStateSaver(*this, changeTracker);
+
+	Node::connectUndoSystem(changeTracker);
+}
+
+void SelectableNode::disconnectUndoSystem(IMapFileChangeTracker& changeTracker)
+{
+	_undoStateSaver = nullptr;
+	GlobalUndoSystem().releaseStateSaver(*this);
+
+	Node::disconnectUndoSystem(changeTracker);
+}
+
+void SelectableNode::undoSave()
+{
+	if (_undoStateSaver != nullptr)
+	{
+		_undoStateSaver->save(*this);
+	}
+}
+
+} // namespace
diff --git a/libs/scene/SelectableNode.h b/libs/scene/SelectableNode.h
new file mode 100644
index 0000000..798b17f
--- /dev/null
+++ b/libs/scene/SelectableNode.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "scene/Node.h"
+#include "iselectiongroup.h"
+#include "iundo.h"
+
+namespace scene
+{
+
+/**
+ * \brief
+ * Subclass of scene::Node which implements the Selectable interface.
+ *
+ * The GlobalSelectionSystem will be notified of selection changes.
+ */
+class SelectableNode :
+	public scene::Node,
+	public IGroupSelectable,
+	public IUndoable
+{
+private:
+	// Current selection state
+	bool _selected;
+
+	// The groups this node is a member of. The last value in the list 
+	// represents the group this node has been added to most recently
+	GroupIds _groups;
+
+	IUndoStateSaver* _undoStateSaver;
+
+public:
+	SelectableNode();
+
+	SelectableNode(const SelectableNode& other);
+
+	virtual ~SelectableNode();
+
+	virtual void onInsertIntoScene(IMapRootNode& root) override;
+
+    // override scene::Inode::onRemoveFromScene to de-select self
+	virtual void onRemoveFromScene(IMapRootNode& root) override;
+
+	/**
+	* \brief
+	* Set the selection state.
+	*/
+	virtual void setSelected(bool select) override;
+
+	virtual void addToGroup(std::size_t groupId) override;
+	virtual void removeFromGroup(std::size_t groupId) override;
+
+	virtual bool isGroupMember() override;
+	virtual std::size_t getMostRecentGroupId() override;
+	virtual const GroupIds& getGroupIds() override;
+	virtual void setSelected(bool select, bool changeGroupStatus) override;
+	virtual bool isSelected() const override;
+
+	IUndoMementoPtr exportState() const override;
+	void importState(const IUndoMementoPtr& state) override;
+
+protected:
+	/**
+     * \brief
+     * Invoked when the selection status changes.
+     */
+	virtual void onSelectionStatusChange(bool changeGroupStatus);
+
+	virtual void connectUndoSystem(IMapFileChangeTracker& changeTracker) override;
+	virtual void disconnectUndoSystem(IMapFileChangeTracker& changeTracker) override;
+
+private:
+	void undoSave();
+};
+
+} // namespace
diff --git a/libs/scene/TraversableNodeSet.cpp b/libs/scene/TraversableNodeSet.cpp
index 3a451f8..7f29785 100644
--- a/libs/scene/TraversableNodeSet.cpp
+++ b/libs/scene/TraversableNodeSet.cpp
@@ -104,7 +104,7 @@ TraversableNodeSet::~TraversableNodeSet()
 	notifyEraseAll();
 }
 
-void TraversableNodeSet::insert(const INodePtr& node)
+void TraversableNodeSet::append(const INodePtr& node)
 {
 	// Submit the UndoMemento to the UndoSystem
 	undoSave();
@@ -116,6 +116,18 @@ void TraversableNodeSet::insert(const INodePtr& node)
 	_owner.onChildAdded(node);
 }
 
+void TraversableNodeSet::prepend(const INodePtr& node)
+{
+	// Submit the UndoMemento to the UndoSystem
+	undoSave();
+
+	// Insert the child node at the front of the list
+	_children.push_front(node);
+
+	// Notify the owner (note: this usually triggers instantiation of the node)
+	_owner.onChildAdded(node);
+}
+
 void TraversableNodeSet::erase(const INodePtr& node)
 {
 	undoSave();
diff --git a/libs/scene/TraversableNodeSet.h b/libs/scene/TraversableNodeSet.h
index 0873a59..40b2f2a 100644
--- a/libs/scene/TraversableNodeSet.h
+++ b/libs/scene/TraversableNodeSet.h
@@ -48,9 +48,16 @@ public:
 	~TraversableNodeSet();
 
 	/**
-	 * greebo: This inserts a child node, saves the Undo state and notifies the owning node.
+	 * greebo: This inserts a child node at the end of the list, 
+	 * saves the Undo state and notifies the owning node.
 	 */
-	void insert(const INodePtr& node);
+	void append(const INodePtr& node);
+
+	/**
+	* greebo: This inserts a child node at the front of the list,
+	* saves the Undo state and notifies the owning node.
+	*/
+	void prepend(const INodePtr& node);
 
 	/**
 	 * greebo: This removes the node from the local set, saves the UndoMemento and notifies the owning node.
diff --git a/libs/scenelib.h b/libs/scenelib.h
index 5f854f3..f10fe18 100644
--- a/libs/scenelib.h
+++ b/libs/scenelib.h
@@ -162,8 +162,9 @@ public:
             node->disable(Node::eLayered);
         }
 
-        if (!node->visible()) {
-            // Node is hidden after update (and no children are visible), de-select
+        if (node->checkStateFlag(Node::eLayered))
+		{
+            // Node is hidden by layers after update (and no children are visible), de-select
             Node_setSelected(node, false);
         }
 
diff --git a/libs/wxutil/GLWidget.cpp b/libs/wxutil/GLWidget.cpp
index baad6c7..dcb5155 100644
--- a/libs/wxutil/GLWidget.cpp
+++ b/libs/wxutil/GLWidget.cpp
@@ -11,11 +11,12 @@ namespace wxutil
 const int ATTRIBS [] = {
 	WX_GL_RGBA,
 	WX_GL_DOUBLEBUFFER,
-	WX_GL_DEPTH_SIZE, 16
+	WX_GL_DEPTH_SIZE, 16,
+	0
 };
 
 GLWidget::GLWidget(wxWindow *parent, const std::function<void()>& renderCallback, const std::string& name) :
-	wxGLCanvas(parent, -1, ATTRIBS, wxDefaultPosition, wxDefaultSize,
+	wxGLCanvas(parent, wxID_ANY, ATTRIBS, wxDefaultPosition, wxDefaultSize,
                wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS,
                wxString(name.c_str(), *wxConvCurrent)),
 	_registered(false),
diff --git a/libs/wxutil/Makefile.am b/libs/wxutil/Makefile.am
index 0de7507..83719e1 100644
--- a/libs/wxutil/Makefile.am
+++ b/libs/wxutil/Makefile.am
@@ -14,6 +14,8 @@ libwxutil_la_LDFLAGS = -release @PACKAGE_VERSION@ \
                         $(LIBSIGC_LIBS) \
                         $(WX_LIBS) \
                         $(GL_LIBS) \
+                        $(BOOST_FILESYSTEM_LIBS) \
+                        $(BOOST_SYSTEM_LIBS) \
                         $(GLU_LIBS) \
                         $(FTGL_LIBS)
 libwxutil_la_LIBADD = $(top_builddir)/libs/xmlutil/libxmlutil.la \
diff --git a/libs/wxutil/Makefile.in b/libs/wxutil/Makefile.in
index f6ec834..57155db 100644
--- a/libs/wxutil/Makefile.in
+++ b/libs/wxutil/Makefile.in
@@ -424,6 +424,8 @@ libwxutil_la_LDFLAGS = -release @PACKAGE_VERSION@ \
                         $(LIBSIGC_LIBS) \
                         $(WX_LIBS) \
                         $(GL_LIBS) \
+                        $(BOOST_FILESYSTEM_LIBS) \
+                        $(BOOST_SYSTEM_LIBS) \
                         $(GLU_LIBS) \
                         $(FTGL_LIBS)
 
diff --git a/libs/wxutil/TreeModel.cpp b/libs/wxutil/TreeModel.cpp
index f3616ca..56aeca7 100644
--- a/libs/wxutil/TreeModel.cpp
+++ b/libs/wxutil/TreeModel.cpp
@@ -13,14 +13,9 @@ wxString TreeModel::Column::getWxType() const
 	if (types.empty())
 	{
 		types[String] = "string";
-#ifdef __WXGTK__
-		// For wxGTK we store numbers as strings
+		// we store numbers as strings
 		types[Integer] = "string";
 		types[Double] = "string";
-#else
-		types[Integer] = "long";
-		types[Double] = "double";
-#endif
 		types[Boolean] = "bool";
 		types[Icon] = "icon";
 		types[IconText] = "wxDataViewIconText";
@@ -50,6 +45,9 @@ public:
 	typedef std::vector<wxDataViewItemAttr> Attributes;
 	Attributes attributes;
 
+	typedef std::vector<bool> EnabledFlags; // Each value can be flagged as enabled/disabled
+	EnabledFlags enabledFlags;
+
 	// Public constructor, does not accept NULL pointers
 	Node(Node* parent_) :
 		parent(parent_),
@@ -926,4 +924,34 @@ wxDataViewItem TreeModel::FindPrevString(const wxString& needle,
 	return functor.getMatch();
 }
 
+bool TreeModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const
+{
+	Node* owningNode = item.IsOk() ? static_cast<Node*>(item.GetID()) : _rootNode.get();
+
+	if (col < owningNode->enabledFlags.size())
+	{
+		return owningNode->enabledFlags[col];
+	}
+	
+	// Column values without flags render as enabled by default
+	return true;
+}
+
+void TreeModel::SetEnabled(const wxDataViewItem& item, unsigned int col, bool enabled)
+{
+	if (!item.IsOk())
+	{
+		return;
+	}
+
+	Node* owningNode = static_cast<Node*>(item.GetID());
+
+	if (owningNode->enabledFlags.size() < col + 1)
+	{
+		owningNode->enabledFlags.resize(col + 1, true); // fill with true by default
+	}
+
+	owningNode->enabledFlags[col] = enabled;
+}
+
 } // namespace
diff --git a/libs/wxutil/TreeModel.h b/libs/wxutil/TreeModel.h
index d119a6e..be53dcc 100644
--- a/libs/wxutil/TreeModel.h
+++ b/libs/wxutil/TreeModel.h
@@ -161,8 +161,7 @@ public:
 		// get/set operators
 		ItemValueProxy& operator=(const wxVariant& data)
 		{
-#ifdef __WXGTK__
-			// wxGTK doesn't like rendering number columns directly (even though it works)
+			// wxGTK and wxWidgets 3.1.0+ doesn't like rendering number values as text
 			// so let's store numbers as strings automatically
 			if ((_column.type == Column::Integer || _column.type == Column::Double) &&
 				 data.GetType() != "string")
@@ -174,9 +173,10 @@ public:
 			{
 				_model.SetValue(data, _item, _column.getColumnIndex());
 			}
-#else
-			_model.SetValue(data, _item, _column.getColumnIndex());
-#endif
+
+			// Newly assigned values are enabled by default
+			_model.SetEnabled(_item, _column.getColumnIndex(), true);
+
 			return *this;
 		}
 
@@ -237,6 +237,18 @@ public:
 
 			return variant.IsNull() ? "" : variant.GetString().ToStdString();
 		}
+
+		bool isEnabled()
+		{
+			return _model.IsEnabled(_item, _column.getColumnIndex());
+		}
+
+		// Enable/disable the column value, by default values are enabled after assignment
+		// This is adhered by some cell renderers which draw the value greyed out or inactive
+		void setEnabled(bool enabled)
+		{
+			_model.SetEnabled(_item, _column.getColumnIndex(), enabled);
+		}
 	};
 
 	/**
@@ -376,6 +388,9 @@ public:
 	// Returns a Row reference to the topmost element
 	virtual Row GetRootItem();
 
+	// Returns true if the given column value should render as "enabled" or not
+	virtual bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override;
+
 	// Removes all items - internally the root node will be kept, but cleared too
 	// This also fires the "Cleared" event to any listeners
 	virtual void Clear();
@@ -415,6 +430,9 @@ public:
 	virtual wxDataViewItem FindPrevString(const wxString& needle,
 		const std::vector<Column>& columns, const wxDataViewItem& previousMatch = wxDataViewItem());
 
+	// Marks a specific column value as enabled or disabled.
+	virtual void SetEnabled(const wxDataViewItem& item, unsigned int col, bool enabled);
+
 	// Base class implementation / overrides
 
 	virtual bool HasDefaultCompare() const;
diff --git a/libs/wxutil/TreeView.cpp b/libs/wxutil/TreeView.cpp
index 42a8c92..d41344c 100644
--- a/libs/wxutil/TreeView.cpp
+++ b/libs/wxutil/TreeView.cpp
@@ -21,6 +21,7 @@ TreeView::TreeView(wxWindow* parent, TreeModel::Ptr model, long style) :
 
 	Connect(wxEVT_CHAR, wxKeyEventHandler(TreeView::_onChar), NULL, this);
 	Connect(EV_TREEVIEW_SEARCH_EVENT, SearchEventHandler(TreeView::_onSearch), NULL, this);
+	Connect(wxEVT_DATAVIEW_ITEM_ACTIVATED, wxDataViewEventHandler(TreeView::_onItemActivated), NULL, this);
 }
 
 TreeView* TreeView::Create(wxWindow* parent, long style)
@@ -121,6 +122,18 @@ void TreeView::_onItemExpanded(wxDataViewEvent& ev)
 	ev.Skip();
 }
 
+void TreeView::_onItemActivated(wxDataViewEvent& ev)
+{
+	if (!IsExpanded(ev.GetItem()))
+	{
+		Expand(ev.GetItem());
+	}
+	else
+	{
+		Collapse(ev.GetItem());
+	}
+}
+
 wxDEFINE_EVENT(EV_TREEVIEW_SEARCH_EVENT, TreeView::SearchEvent);
 
 TreeView::SearchEvent::SearchEvent(int id) :
diff --git a/libs/wxutil/TreeView.h b/libs/wxutil/TreeView.h
index 58fd194..4b22dc5 100644
--- a/libs/wxutil/TreeView.h
+++ b/libs/wxutil/TreeView.h
@@ -94,6 +94,7 @@ private:
 	void _onItemExpanded(wxDataViewEvent& ev);
 	void _onChar(wxKeyEvent& ev);
 	void _onSearch(SearchEvent& ev);
+	void _onItemActivated(wxDataViewEvent& ev);
 };
 
 // wx event macros
diff --git a/missing b/missing
index 1c8ff70..f62bbae 100755
--- a/missing
+++ b/missing
@@ -1,11 +1,10 @@
 #! /bin/sh
-# Common stub for a few missing GNU programs while installing.
+# Common wrapper for a few potentially missing GNU programs.
 
-scriptversion=2006-05-10.23
+scriptversion=2013-10-28.13; # UTC
 
-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006
-#   Free Software Foundation, Inc.
-# Originally by Fran,cois Pinard <pinard at iro.umontreal.ca>, 1996.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard at iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -18,9 +17,7 @@ scriptversion=2006-05-10.23
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -28,66 +25,40 @@ scriptversion=2006-05-10.23
 # the same distribution terms that you use for the rest of that program.
 
 if test $# -eq 0; then
-  echo 1>&2 "Try \`$0 --help' for more information"
+  echo 1>&2 "Try '$0 --help' for more information"
   exit 1
 fi
 
-run=:
-sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
-sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
-
-# In the cases where this matters, `missing' is being run in the
-# srcdir already.
-if test -f configure.ac; then
-  configure_ac=configure.ac
-else
-  configure_ac=configure.in
-fi
+case $1 in
 
-msg="missing on your system"
+  --is-lightweight)
+    # Used by our autoconf macros to check whether the available missing
+    # script is modern enough.
+    exit 0
+    ;;
 
-case $1 in
---run)
-  # Try to run requested program, and just exit if it succeeds.
-  run=
-  shift
-  "$@" && exit 0
-  # Exit code 63 means version mismatch.  This often happens
-  # when the user try to use an ancient version of a tool on
-  # a file that requires a minimum version.  In this case we
-  # we should proceed has if the program had been absent, or
-  # if --run hadn't been passed.
-  if test $? = 63; then
-    run=:
-    msg="probably too old"
-  fi
-  ;;
+  --run)
+    # Back-compat with the calling convention used by older automake.
+    shift
+    ;;
 
   -h|--h|--he|--hel|--help)
     echo "\
 $0 [OPTION]... PROGRAM [ARGUMENT]...
 
-Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
-error status if there is no known handling for PROGRAM.
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
 
 Options:
   -h, --help      display this help and exit
   -v, --version   output version information and exit
-  --run           try to run the given command, and emulate it if it fails
 
 Supported PROGRAM values:
-  aclocal      touch file \`aclocal.m4'
-  autoconf     touch file \`configure'
-  autoheader   touch file \`config.h.in'
-  autom4te     touch the output file, or create a stub one
-  automake     touch all \`Makefile.in' files
-  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
-  flex         create \`lex.yy.c', if possible, from existing .c
-  help2man     touch the output file
-  lex          create \`lex.yy.c', if possible, from existing .c
-  makeinfo     touch the output file
-  tar          try tar, gnutar, gtar, then tar without non-portable flags
-  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
+  aclocal   autoconf  autoheader   autom4te  automake  makeinfo
+  bison     yacc      flex         lex       help2man
+
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
 
 Send bug reports to <bug-automake at gnu.org>."
     exit $?
@@ -99,269 +70,146 @@ Send bug reports to <bug-automake at gnu.org>."
     ;;
 
   -*)
-    echo 1>&2 "$0: Unknown \`$1' option"
-    echo 1>&2 "Try \`$0 --help' for more information"
+    echo 1>&2 "$0: unknown '$1' option"
+    echo 1>&2 "Try '$0 --help' for more information"
     exit 1
     ;;
 
 esac
 
-# Now exit if we have it, but it failed.  Also exit now if we
-# don't have it and --version was passed (most likely to detect
-# the program).
-case $1 in
-  lex|yacc)
-    # Not GNU programs, they don't have --version.
-    ;;
-
-  tar)
-    if test -n "$run"; then
-       echo 1>&2 "ERROR: \`tar' requires --run"
-       exit 1
-    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
-       exit 1
-    fi
-    ;;
-
-  *)
-    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
-       # We have it, but it failed.
-       exit 1
-    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
-       # Could not run --version or --help.  This is probably someone
-       # running `$TOOL --version' or `$TOOL --help' to check whether
-       # $TOOL exists and not knowing $TOOL uses missing.
-       exit 1
-    fi
-    ;;
-esac
-
-# If it does not exist, or fails to run (possibly an outdated version),
-# try to emulate it.
-case $1 in
-  aclocal*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
-         to install the \`Automake' and \`Perl' packages.  Grab them from
-         any GNU archive site."
-    touch aclocal.m4
-    ;;
-
-  autoconf)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`${configure_ac}'.  You might want to install the
-         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
-         archive site."
-    touch configure
-    ;;
-
-  autoheader)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
-         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
-         from any GNU archive site."
-    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
-    test -z "$files" && files="config.h"
-    touch_files=
-    for f in $files; do
-      case $f in
-      *:*) touch_files="$touch_files "`echo "$f" |
-				       sed -e 's/^[^:]*://' -e 's/:.*//'`;;
-      *) touch_files="$touch_files $f.in";;
-      esac
-    done
-    touch $touch_files
-    ;;
-
-  automake*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
-         You might want to install the \`Automake' and \`Perl' packages.
-         Grab them from any GNU archive site."
-    find . -type f -name Makefile.am -print |
-	   sed 's/\.am$/.in/' |
-	   while read f; do touch "$f"; done
-    ;;
-
-  autom4te)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, but is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.
-         You can get \`$1' as part of \`Autoconf' from any GNU
-         archive site."
-
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -f "$file"; then
-	touch $file
-    else
-	test -z "$file" || exec >$file
-	echo "#! /bin/sh"
-	echo "# Created by GNU Automake missing as a replacement of"
-	echo "#  $ $@"
-	echo "exit 0"
-	chmod +x $file
-	exit 1
-    fi
-    ;;
-
-  bison|yacc)
-    echo 1>&2 "\
-WARNING: \`$1' $msg.  You should only need it if
-         you modified a \`.y' file.  You may need the \`Bison' package
-         in order for those modifications to take effect.  You can get
-         \`Bison' from any GNU archive site."
-    rm -f y.tab.c y.tab.h
-    if test $# -ne 1; then
-        eval LASTARG="\${$#}"
-	case $LASTARG in
-	*.y)
-	    SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" y.tab.c
-	    fi
-	    SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" y.tab.h
-	    fi
-	  ;;
-	esac
-    fi
-    if test ! -f y.tab.h; then
-	echo >y.tab.h
-    fi
-    if test ! -f y.tab.c; then
-	echo 'main() { return 0; }' >y.tab.c
-    fi
-    ;;
-
-  lex|flex)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.l' file.  You may need the \`Flex' package
-         in order for those modifications to take effect.  You can get
-         \`Flex' from any GNU archive site."
-    rm -f lex.yy.c
-    if test $# -ne 1; then
-        eval LASTARG="\${$#}"
-	case $LASTARG in
-	*.l)
-	    SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" lex.yy.c
-	    fi
-	  ;;
-	esac
-    fi
-    if test ! -f lex.yy.c; then
-	echo 'main() { return 0; }' >lex.yy.c
-    fi
-    ;;
-
-  help2man)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-	 you modified a dependency of a manual page.  You may need the
-	 \`Help2man' package in order for those modifications to take
-	 effect.  You can get \`Help2man' from any GNU archive site."
-
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -f "$file"; then
-	touch $file
-    else
-	test -z "$file" || exec >$file
-	echo ".ab help2man is required to generate this page"
-	exit 1
-    fi
-    ;;
-
-  makeinfo)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.texi' or \`.texinfo' file, or any other file
-         indirectly affecting the aspect of the manual.  The spurious
-         call might also be the consequence of using a buggy \`make' (AIX,
-         DU, IRIX).  You might want to install the \`Texinfo' package or
-         the \`GNU make' package.  Grab either from any GNU archive site."
-    # The file to touch is that specified with -o ...
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -z "$file"; then
-      # ... or it is the one specified with @setfilename ...
-      infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
-      file=`sed -n '
-	/^@setfilename/{
-	  s/.* \([^ ]*\) *$/\1/
-	  p
-	  q
-	}' $infile`
-      # ... or it is derived from the source name (dir/f.texi becomes f.info)
-      test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
-    fi
-    # If the file does not exist, the user really needs makeinfo;
-    # let's fail without touching anything.
-    test -f $file || exit 1
-    touch $file
-    ;;
-
-  tar)
-    shift
-
-    # We have already tried tar in the generic part.
-    # Look for gnutar/gtar before invocation to avoid ugly error
-    # messages.
-    if (gnutar --version > /dev/null 2>&1); then
-       gnutar "$@" && exit 0
-    fi
-    if (gtar --version > /dev/null 2>&1); then
-       gtar "$@" && exit 0
-    fi
-    firstarg="$1"
-    if shift; then
-	case $firstarg in
-	*o*)
-	    firstarg=`echo "$firstarg" | sed s/o//`
-	    tar "$firstarg" "$@" && exit 0
-	    ;;
-	esac
-	case $firstarg in
-	*h*)
-	    firstarg=`echo "$firstarg" | sed s/h//`
-	    tar "$firstarg" "$@" && exit 0
-	    ;;
-	esac
-    fi
-
-    echo 1>&2 "\
-WARNING: I can't seem to be able to run \`tar' with the given arguments.
-         You may want to install GNU tar or Free paxutils, or check the
-         command line arguments."
-    exit 1
-    ;;
-
-  *)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, and is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.  Check the \`README' file,
-         it often tells you about the needed prerequisites for installing
-         this package.  You may also peek at any GNU archive site, in case
-         some other package would contain this missing \`$1' program."
-    exit 1
-    ;;
-esac
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch.  This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+  msg="probably too old"
+elif test $st -eq 127; then
+  # Program was missing.
+  msg="missing on your system"
+else
+  # Program was found and executed, but failed.  Give up.
+  exit $st
+fi
 
-exit 0
+perl_URL=http://www.perl.org/
+flex_URL=http://flex.sourceforge.net/
+gnu_software_URL=http://www.gnu.org/software
+
+program_details ()
+{
+  case $1 in
+    aclocal|automake)
+      echo "The '$1' program is part of the GNU Automake package:"
+      echo "<$gnu_software_URL/automake>"
+      echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/autoconf>"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+    autoconf|autom4te|autoheader)
+      echo "The '$1' program is part of the GNU Autoconf package:"
+      echo "<$gnu_software_URL/autoconf/>"
+      echo "It also requires GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+  esac
+}
+
+give_advice ()
+{
+  # Normalize program name to check for.
+  normalized_program=`echo "$1" | sed '
+    s/^gnu-//; t
+    s/^gnu//; t
+    s/^g//; t'`
+
+  printf '%s\n' "'$1' is $msg."
+
+  configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+  case $normalized_program in
+    autoconf*)
+      echo "You should only need it if you modified 'configure.ac',"
+      echo "or m4 files included by it."
+      program_details 'autoconf'
+      ;;
+    autoheader*)
+      echo "You should only need it if you modified 'acconfig.h' or"
+      echo "$configure_deps."
+      program_details 'autoheader'
+      ;;
+    automake*)
+      echo "You should only need it if you modified 'Makefile.am' or"
+      echo "$configure_deps."
+      program_details 'automake'
+      ;;
+    aclocal*)
+      echo "You should only need it if you modified 'acinclude.m4' or"
+      echo "$configure_deps."
+      program_details 'aclocal'
+      ;;
+   autom4te*)
+      echo "You might have modified some maintainer files that require"
+      echo "the 'autom4te' program to be rebuilt."
+      program_details 'autom4te'
+      ;;
+    bison*|yacc*)
+      echo "You should only need it if you modified a '.y' file."
+      echo "You may want to install the GNU Bison package:"
+      echo "<$gnu_software_URL/bison/>"
+      ;;
+    lex*|flex*)
+      echo "You should only need it if you modified a '.l' file."
+      echo "You may want to install the Fast Lexical Analyzer package:"
+      echo "<$flex_URL>"
+      ;;
+    help2man*)
+      echo "You should only need it if you modified a dependency" \
+           "of a man page."
+      echo "You may want to install the GNU Help2man package:"
+      echo "<$gnu_software_URL/help2man/>"
+    ;;
+    makeinfo*)
+      echo "You should only need it if you modified a '.texi' file, or"
+      echo "any other file indirectly affecting the aspect of the manual."
+      echo "You might want to install the Texinfo package:"
+      echo "<$gnu_software_URL/texinfo/>"
+      echo "The spurious makeinfo call might also be the consequence of"
+      echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+      echo "want to install GNU make:"
+      echo "<$gnu_software_URL/make/>"
+      ;;
+    *)
+      echo "You might have modified some files without having the proper"
+      echo "tools for further handling them.  Check the 'README' file, it"
+      echo "often tells you about the needed prerequisites for installing"
+      echo "this package.  You may also peek at any GNU archive site, in"
+      echo "case some other package contains this missing '$1' program."
+      ;;
+  esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+                       -e '2,$s/^/         /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
 
 # Local variables:
 # eval: (add-hook 'write-file-hooks 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
 # End:
diff --git a/plugins/dm.conversation/ConversationDialog.cpp b/plugins/dm.conversation/ConversationDialog.cpp
index 64ec440..a1f3000 100644
--- a/plugins/dm.conversation/ConversationDialog.cpp
+++ b/plugins/dm.conversation/ConversationDialog.cpp
@@ -46,7 +46,7 @@ ConversationDialog::ConversationDialog() :
 
 void ConversationDialog::populateWindow()
 {
-	wxPanel* mainPanel = loadNamedPanel(this, "ConvDialogMainPanel");
+	loadNamedPanel(this, "ConvDialogMainPanel");
 
 	wxPanel* entityPanel = findNamedObject<wxPanel>(this, "ConvDialogEntityPanel");
 
@@ -148,6 +148,8 @@ void ConversationDialog::refreshConversationList()
 
 	// If there is at least one conversation, make the Clear button available
 	_clearConvButton->Enable(!_curEntity->second->isEmpty());
+
+	handleConversationSelectionChange();
 }
 
 void ConversationDialog::populateWidgets()
@@ -306,6 +308,11 @@ void ConversationDialog::onDeleteEntity(wxCommandEvent& ev)
 // Callback for current conversation selection changed
 void ConversationDialog::onConversationSelectionChanged(wxDataViewEvent& ev)
 {
+	handleConversationSelectionChange();
+}
+
+void ConversationDialog::handleConversationSelectionChange()
+{
 	// Get the selection
 	_currentConversation = _convView->GetSelection();
 
diff --git a/plugins/dm.conversation/ConversationDialog.h b/plugins/dm.conversation/ConversationDialog.h
index 04d0dad..bd986fb 100644
--- a/plugins/dm.conversation/ConversationDialog.h
+++ b/plugins/dm.conversation/ConversationDialog.h
@@ -73,6 +73,7 @@ private:
 	// Re-loads the conversation from the selected entity
 	void refreshConversationList();
 	void updateConversationPanelSensitivity();
+	void handleConversationSelectionChange();
 
 	// WIDGET POPULATION
 	void populateWindow();
diff --git a/plugins/dm.conversation/ConversationEditor.cpp b/plugins/dm.conversation/ConversationEditor.cpp
index 65c8641..7407363 100644
--- a/plugins/dm.conversation/ConversationEditor.cpp
+++ b/plugins/dm.conversation/ConversationEditor.cpp
@@ -52,9 +52,31 @@ void ConversationEditor::populateWindow()
 	makeLabelBold(this, "ConvEditorActorLabel");
 	makeLabelBold(this, "ConvEditorCommandLabel");
 
+	findNamedObject<wxTextCtrl>(this, "ConvEditorNameEntry")->Bind(wxEVT_TEXT, [&](wxCommandEvent& ev)
+	{
+		if (!_updateInProgress)
+		{
+			_conversation.name = ev.GetString();
+		}
+	});
+
 	findNamedObject<wxCheckBox>(this, "ConvEditorRepeatCheckbox")->Connect(
 		wxEVT_CHECKBOX, wxCommandEventHandler(ConversationEditor::onMaxPlayCountEnabled), NULL, this);
 	
+	findNamedObject<wxCheckBox>(this, "ConvEditorActorsWithinTalkDistance")->Bind(wxEVT_CHECKBOX,
+		[&](wxCommandEvent& ev) { _conversation.actorsMustBeWithinTalkdistance = ev.IsChecked(); });
+	findNamedObject<wxCheckBox>(this, "ConvEditorActorsMustFace")->Bind(wxEVT_CHECKBOX,
+		[&](wxCommandEvent& ev) { _conversation.actorsAlwaysFaceEachOther = ev.IsChecked(); });
+
+	findNamedObject<wxSpinCtrl>(this, "ConvEditorRepeatTimes")->Bind(wxEVT_SPINCTRL, [&](wxSpinEvent& ev)
+	{ 
+		if (!_updateInProgress)
+		{
+			_conversation.maxPlayCount = ev.GetValue();
+			updateWidgets();
+		}
+	});
+
 	// Actor Panel
 	wxPanel* actorPanel = findNamedObject<wxPanel>(this, "ConvEditorActorPanel");
 
@@ -350,6 +372,9 @@ void ConversationEditor::onMaxPlayCountEnabled(wxCommandEvent& ev)
 		findNamedObject<wxSpinCtrl>(this, "ConvEditorRepeatTimes")->Enable(false);
 		findNamedObject<wxStaticText>(this, "ConvEditorRepeatAdditionalText")->Enable(false);
 	}
+
+	// Update the value in the conversation as well
+	_conversation.maxPlayCount = findNamedObject<wxSpinCtrl>(this, "ConvEditorRepeatTimes")->GetValue();
 }
 
 void ConversationEditor::onAddActor(wxCommandEvent& ev)
diff --git a/plugins/dm.editing/AIEditingPanel.cpp b/plugins/dm.editing/AIEditingPanel.cpp
index 1663055..e7b5c93 100644
--- a/plugins/dm.editing/AIEditingPanel.cpp
+++ b/plugins/dm.editing/AIEditingPanel.cpp
@@ -94,7 +94,7 @@ void AIEditingPanel::constructWidgets()
 	_spinButtons["min_interleave_think_dist"] = new SpawnargLinkedSpinButton(_mainPanel, _("Min. Interleave Distance"), "min_interleave_think_dist", 0, 60000, 50, 0);
 	_spinButtons["max_interleave_think_dist"] = new SpawnargLinkedSpinButton(_mainPanel, _("Max. Interleave Distance"), "max_interleave_think_dist", 0, 60000, 50, 0);
 
-	_spinButtons["health"] = new SpawnargLinkedSpinButton(_mainPanel, _("Health"), "health", 0, 1000, 5, 0);
+	_spinButtons["health"] = new SpawnargLinkedSpinButton(_mainPanel, _("Health"), "health", 0, 9999999, 5, 0);
 	_spinButtons["health_critical"] = new SpawnargLinkedSpinButton(_mainPanel, _("Critical Health"), "health_critical", 0, 1000, 5, 0);
 	_spinButtons["melee_range"] = new SpawnargLinkedSpinButton(_mainPanel, _("Melee Range"), "melee_range", 0, 200, 1, 0);
 
@@ -207,7 +207,7 @@ wxSizer* AIEditingPanel::createSpinButtonHbox(SpawnargLinkedSpinButton* spinButt
 
 	wxStaticText* label = new wxStaticText(spinButton->GetParent(), wxID_ANY, spinButton->getLabel() + ":");
 	hbox->Add(label, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
-	hbox->Add(spinButton, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
+	hbox->Add(spinButton, 0, wxALIGN_CENTER_VERTICAL);
 
 	return hbox;
 }
@@ -267,9 +267,9 @@ void AIEditingPanel::onRadiantStartup()
 	page->page = Instance()._mainPanel;
 	page->tabIcon = "icon_ai.png";
 	page->tabLabel = _("AI");
-	page->insertBefore = "mediabrowser";
+	page->position = IGroupDialog::Page::Position::MediaBrowser - 10;
 
-	// Add the Media Browser page
+	// Add the page
 	GlobalGroupDialog().addPage(page);
 
 	// Delete the temporary parent
@@ -383,7 +383,7 @@ void AIEditingPanel::rescanSelection()
 	updateWidgetsFromSelection();
 }
 
-void AIEditingPanel::onSelectionChanged(const Selectable& selectable)
+void AIEditingPanel::onSelectionChanged(const ISelectable& selectable)
 {
 	// Immediately disconnect from the current entity in any case
 	if (_entity != nullptr)
diff --git a/plugins/dm.editing/AIEditingPanel.h b/plugins/dm.editing/AIEditingPanel.h
index 3e2ba6c..29067e6 100644
--- a/plugins/dm.editing/AIEditingPanel.h
+++ b/plugins/dm.editing/AIEditingPanel.h
@@ -8,7 +8,7 @@
 #include <sigc++/connection.h>
 #include <wx/event.h>
 
-class Selectable;
+class ISelectable;
 class Entity;
 class wxStaticText;
 class wxScrolledWindow;
@@ -78,7 +78,7 @@ private:
 									  const std::string& key);
 
 	void onRadiantShutdown();
-	void onSelectionChanged(const Selectable& selectable);
+	void onSelectionChanged(const ISelectable& selectable);
 
 	void rescanSelection();
 
diff --git a/plugins/dm.editing/AIHeadPropertyEditor.cpp b/plugins/dm.editing/AIHeadPropertyEditor.cpp
index a8fb5bc..4495f24 100644
--- a/plugins/dm.editing/AIHeadPropertyEditor.cpp
+++ b/plugins/dm.editing/AIHeadPropertyEditor.cpp
@@ -48,6 +48,11 @@ wxPanel* AIHeadPropertyEditor::getWidget()
 	return _widget;
 }
 
+void AIHeadPropertyEditor::updateFromEntity()
+{
+	// nothing to do
+}
+
 IPropertyEditorPtr AIHeadPropertyEditor::createNew(wxWindow* parent, Entity* entity,
 	const std::string& key, const std::string& options)
 {
diff --git a/plugins/dm.editing/AIHeadPropertyEditor.h b/plugins/dm.editing/AIHeadPropertyEditor.h
index 89a5815..1ab7f9f 100644
--- a/plugins/dm.editing/AIHeadPropertyEditor.h
+++ b/plugins/dm.editing/AIHeadPropertyEditor.h
@@ -28,14 +28,15 @@ public:
 
 	~AIHeadPropertyEditor();
 
-	wxPanel* getWidget();
+	wxPanel* getWidget() override;
+	void updateFromEntity() override;
 
 	AIHeadPropertyEditor(wxWindow* parent, Entity* entity,
 		const std::string& key, const std::string& options);
 
 	IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
 								const std::string& key,
-								const std::string& options);
+								const std::string& options) override;
 
 	std::string runDialog(Entity* entity, const std::string& key);
 
diff --git a/plugins/dm.editing/AIVocalSetPropertyEditor.cpp b/plugins/dm.editing/AIVocalSetPropertyEditor.cpp
index b83dd2a..463100a 100644
--- a/plugins/dm.editing/AIVocalSetPropertyEditor.cpp
+++ b/plugins/dm.editing/AIVocalSetPropertyEditor.cpp
@@ -48,6 +48,11 @@ wxPanel* AIVocalSetPropertyEditor::getWidget()
 	return _widget;
 }
 
+void AIVocalSetPropertyEditor::updateFromEntity()
+{
+	// Nothing to do
+}
+
 IPropertyEditorPtr AIVocalSetPropertyEditor::createNew(wxWindow* parent, Entity* entity,
 	const std::string& key, const std::string& options)
 {
diff --git a/plugins/dm.editing/AIVocalSetPropertyEditor.h b/plugins/dm.editing/AIVocalSetPropertyEditor.h
index 0657e5e..f91a56c 100644
--- a/plugins/dm.editing/AIVocalSetPropertyEditor.h
+++ b/plugins/dm.editing/AIVocalSetPropertyEditor.h
@@ -31,11 +31,12 @@ public:
 	AIVocalSetPropertyEditor(wxWindow* parent, Entity* entity,
 		const std::string& key, const std::string& options);
 
-	wxPanel* getWidget();
+	wxPanel* getWidget() override;
+	void updateFromEntity() override;
 
 	IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
 								 const std::string& key,
-								 const std::string& options);
+								 const std::string& options) override;
 
 	std::string runDialog(Entity* entity, const std::string& key);
 
diff --git a/plugins/dm.editing/Makefile.am b/plugins/dm.editing/Makefile.am
index ba57af2..6cec7bb 100644
--- a/plugins/dm.editing/Makefile.am
+++ b/plugins/dm.editing/Makefile.am
@@ -7,7 +7,11 @@ plugins_LTLIBRARIES = dm_editing.la
 dm_editing_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \
                     $(top_builddir)/libs/xmlutil/libxmlutil.la
 dm_editing_la_LDFLAGS = -module -avoid-version \
-                        $(WX_LIBS) $(XML_LIBS) $(BOOST_REGEX_LIBS)
+                        $(WX_LIBS) \
+                        $(XML_LIBS) \
+                        $(BOOST_FILESYSTEM_LIBS) \
+                        $(BOOST_SYSTEM_LIBS) \
+                        $(BOOST_REGEX_LIBS)
 dm_editing_la_SOURCES = plugin.cpp \
                   AIHeadPropertyEditor.cpp \
 				  AIEditingPanel.cpp \
diff --git a/plugins/dm.editing/Makefile.in b/plugins/dm.editing/Makefile.in
index b57d3c2..d89b992 100644
--- a/plugins/dm.editing/Makefile.in
+++ b/plugins/dm.editing/Makefile.in
@@ -410,7 +410,11 @@ dm_editing_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \
                     $(top_builddir)/libs/xmlutil/libxmlutil.la
 
 dm_editing_la_LDFLAGS = -module -avoid-version \
-                        $(WX_LIBS) $(XML_LIBS) $(BOOST_REGEX_LIBS)
+                        $(WX_LIBS) \
+                        $(XML_LIBS) \
+                        $(BOOST_FILESYSTEM_LIBS) \
+                        $(BOOST_SYSTEM_LIBS) \
+                        $(BOOST_REGEX_LIBS)
 
 dm_editing_la_SOURCES = plugin.cpp \
                   AIHeadPropertyEditor.cpp \
diff --git a/plugins/dm.gui/plugin.cpp b/plugins/dm.gui/plugin.cpp
index bb4c1d1..e40e7d3 100644
--- a/plugins/dm.gui/plugin.cpp
+++ b/plugins/dm.gui/plugin.cpp
@@ -107,7 +107,7 @@ public:
 	void constructPreferences()
 	{
 		// Add a page to the given group
-		PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Readable Editor"));
+		IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Readable Editor"));
 
 		ComboBoxValueList options;
 
@@ -115,9 +115,9 @@ public:
 		options.push_back(_("Mod Base/xdata"));
 		options.push_back(_("Custom Folder"));
 
-		page->appendCombo(_("XData Storage Folder"), ui::RKEY_READABLES_STORAGE_FOLDER, options);
+		page.appendCombo(_("XData Storage Folder"), ui::RKEY_READABLES_STORAGE_FOLDER, options);
 
-		page->appendPathEntry(_("Custom Folder"), ui::RKEY_READABLES_CUSTOM_FOLDER, true);
+		page.appendPathEntry(_("Custom Folder"), ui::RKEY_READABLES_CUSTOM_FOLDER, true);
 	}
 
 	void shutdownModule()
diff --git a/plugins/dm.objectives/ObjectiveEntityFinder.cpp b/plugins/dm.objectives/ObjectiveEntityFinder.cpp
index 5d124df..db20580 100644
--- a/plugins/dm.objectives/ObjectiveEntityFinder.cpp
+++ b/plugins/dm.objectives/ObjectiveEntityFinder.cpp
@@ -14,7 +14,7 @@ bool ObjectiveEntityFinder::pre(const scene::INodePtr& node)
 
     // We have an entity at this point
 
-    if (ePtr->getKeyValue("classname") == "worldspawn")
+    if (ePtr->isWorldspawn())
     {
         _worldSpawn = ePtr;
         return false; // Don't traverse worldspawn children
diff --git a/plugins/dm.stimresponse/EffectEditor.cpp b/plugins/dm.stimresponse/EffectEditor.cpp
index 340dbf0..15b3b25 100644
--- a/plugins/dm.stimresponse/EffectEditor.cpp
+++ b/plugins/dm.stimresponse/EffectEditor.cpp
@@ -167,7 +167,7 @@ void EffectEditor::createArgumentWidgets(ResponseEffect& effect)
 	for (ResponseEffect::ArgumentList::iterator i = list.begin();
 		 i != list.end(); ++i)
 	{
-		int index = i->first;
+		//int index = i->first;
 		ResponseEffect::Argument& arg = i->second;
 		ArgumentItemPtr item;
 
diff --git a/plugins/entity/Doom3Entity.cpp b/plugins/entity/Doom3Entity.cpp
index 5120f25..304365a 100644
--- a/plugins/entity/Doom3Entity.cpp
+++ b/plugins/entity/Doom3Entity.cpp
@@ -211,6 +211,11 @@ EntityKeyValuePtr Doom3Entity::getEntityKeyValue(const std::string& key)
 	return (found != _keyValues.end()) ? found->second : EntityKeyValuePtr();
 }
 
+bool Doom3Entity::isWorldspawn() const
+{
+	return getKeyValue("classname") == "worldspawn";
+}
+
 bool Doom3Entity::isContainer() const
 {
 	return _isContainer;
diff --git a/plugins/entity/Doom3Entity.h b/plugins/entity/Doom3Entity.h
index 84b29a0..79046c2 100644
--- a/plugins/entity/Doom3Entity.h
+++ b/plugins/entity/Doom3Entity.h
@@ -54,44 +54,45 @@ public:
 	void importState(const KeyValues& keyValues);
 
     /* Entity implementation */
-	void attachObserver(Observer* observer);
-	void detachObserver(Observer* observer);
+	void attachObserver(Observer* observer) override;
+	void detachObserver(Observer* observer) override;
 
 	void connectUndoSystem(IMapFileChangeTracker& changeTracker);
     void disconnectUndoSystem(IMapFileChangeTracker& changeTracker);
 
 	/** Return the EntityClass associated with this entity.
 	 */
-	IEntityClassPtr getEntityClass() const;
+	IEntityClassPtr getEntityClass() const override;
 
-	void forEachKeyValue(const KeyValueVisitFunctor& func) const;
-    void forEachEntityKeyValue(const EntityKeyValueVisitFunctor& visitor);
+	void forEachKeyValue(const KeyValueVisitFunctor& func) const override;
+    void forEachEntityKeyValue(const EntityKeyValueVisitFunctor& visitor) override;
 
 	/** Set a keyvalue on the entity.
 	 */
-	void setKeyValue(const std::string& key, const std::string& value);
+	void setKeyValue(const std::string& key, const std::string& value) override;
 
 	/** Retrieve a keyvalue from the entity.
 	 */
-	std::string getKeyValue(const std::string& key) const;
+	std::string getKeyValue(const std::string& key) const override;
 
 	// Returns true if the given key is inherited
-	bool isInherited(const std::string& key) const;
+	bool isInherited(const std::string& key) const override;
 
 	// Get all KeyValues matching the given prefix.
-	KeyValuePairs getKeyValuePairs(const std::string& prefix) const;
+	KeyValuePairs getKeyValuePairs(const std::string& prefix) const override;
 
-	bool isContainer() const;
+	bool isWorldspawn() const override;
+	bool isContainer() const override;
 	void setIsContainer(bool isContainer);
 
-	bool isModel() const;
+	bool isModel() const override;
 
 	// Returns the actual pointer to a KeyValue (or NULL if not found),
 	// not just the string like getKeyValue() does.
 	// Only returns non-NULL for non-inherited keyvalues.
 	EntityKeyValuePtr getEntityKeyValue(const std::string& key);
 
-	bool isOfType(const std::string& className);
+	bool isOfType(const std::string& className) override;
 
 private:
 
diff --git a/plugins/entity/EntityNode.cpp b/plugins/entity/EntityNode.cpp
index 4456717..eb27d5a 100644
--- a/plugins/entity/EntityNode.cpp
+++ b/plugins/entity/EntityNode.cpp
@@ -252,9 +252,11 @@ void EntityNode::setRenderSystem(const RenderSystemPtr& renderSystem)
 	_colourKey.setRenderSystem(renderSystem);
 }
 
-bool EntityNode::isHighlighted() const
+std::size_t EntityNode::getHighlightFlags()
 {
-	return isSelected();
+	if (!isSelected()) return Highlight::NoHighlight;
+
+	return isGroupMember() ? (Highlight::Selected | Highlight::GroupMember) : Highlight::Selected;
 }
 
 const Vector3& EntityNode::getColour() const
diff --git a/plugins/entity/EntityNode.h b/plugins/entity/EntityNode.h
index 193f5ad..716e34b 100644
--- a/plugins/entity/EntityNode.h
+++ b/plugins/entity/EntityNode.h
@@ -4,7 +4,7 @@
 #include "inamespace.h"
 #include "Bounded.h"
 
-#include "SelectableNode.h"
+#include "scene/SelectableNode.h"
 #include "transformlib.h"
 
 #include "NamespaceManager.h"
@@ -118,10 +118,10 @@ public:
 	Type getNodeType() const;
 
 	// Renderable implementation, can be overridden by subclasses
-	virtual void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	virtual void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	virtual void setRenderSystem(const RenderSystemPtr& renderSystem);
-	virtual bool isHighlighted() const;
+	virtual void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	virtual void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	virtual void setRenderSystem(const RenderSystemPtr& renderSystem) override;
+	virtual std::size_t getHighlightFlags() override;
 
 	// Adds/removes the keyobserver to/from the KeyObserverMap
 	void addKeyObserver(const std::string& key, KeyObserver& observer);
diff --git a/plugins/entity/VertexInstance.h b/plugins/entity/VertexInstance.h
index 2ca5c9d..8ef9255 100644
--- a/plugins/entity/VertexInstance.h
+++ b/plugins/entity/VertexInstance.h
@@ -10,7 +10,7 @@
 
 class VertexInstance :
 	public OpenGLRenderable,
-	public Selectable
+	public ISelectable
 {
 protected:
 	Vector3& _vertex;
@@ -87,8 +87,8 @@ public:
                 const VolumeTest& volume,
                 const Matrix4& localToWorld) const
     {
-		collector.highlightPrimitives(false);
-		collector.highlightFaces(false);
+		collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
+		collector.setHighlightFlag(RenderableCollector::Highlight::Faces, false);
 		collector.SetState(_shader, RenderableCollector::eFullMaterials);
 		collector.SetState(_shader, RenderableCollector::eWireframeOnly);
 
diff --git a/plugins/entity/curve/CurveEditInstance.cpp b/plugins/entity/curve/CurveEditInstance.cpp
index 631455c..e389169 100644
--- a/plugins/entity/curve/CurveEditInstance.cpp
+++ b/plugins/entity/curve/CurveEditInstance.cpp
@@ -56,6 +56,14 @@ bool CurveEditInstance::isSelected() const {
     return false;
 }
 
+void CurveEditInstance::invertSelected()
+{
+	for (selection::ObservedSelectable& i : _selectables)
+	{
+		i.setSelected(!i.isSelected());
+	}
+}
+
 void CurveEditInstance::setSelected(bool selected) {
 	for(Selectables::iterator i = _selectables.begin(); i != _selectables.end(); ++i) {
 		i->setSelected(selected);
@@ -173,7 +181,7 @@ void CurveEditInstance::renderComponentsSelected(RenderableCollector& collector,
     updateSelected();
     if(!m_selectedRender.empty())
     {
-      collector.highlightPrimitives(false);
+      collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
       collector.SetState(_shaders.selectedShader, RenderableCollector::eWireframeOnly);
       collector.SetState(_shaders.selectedShader, RenderableCollector::eFullMaterials);
       collector.addRenderable(m_selectedRender, localToWorld);
diff --git a/plugins/entity/curve/CurveEditInstance.h b/plugins/entity/curve/CurveEditInstance.h
index 50c57d9..9b70764 100644
--- a/plugins/entity/curve/CurveEditInstance.h
+++ b/plugins/entity/curve/CurveEditInstance.h
@@ -68,6 +68,7 @@ public:
 
 	bool isSelected() const;
 	void setSelected(bool selected);
+	void invertSelected();
 	unsigned int numSelected() const;
 
 	void write(const std::string& key, Entity& entity);
diff --git a/plugins/entity/doom3group/Doom3Group.cpp b/plugins/entity/doom3group/Doom3Group.cpp
index a60aa86..63dde03 100644
--- a/plugins/entity/doom3group/Doom3Group.cpp
+++ b/plugins/entity/doom3group/Doom3Group.cpp
@@ -320,17 +320,20 @@ void Doom3Group::setIsModel(bool newValue) {
 /** Determine if this Doom3Group is a model (func_static) or a
  * brush-containing entity. If the "model" key is equal to the
  * "name" key, then this is a brush-based entity, otherwise it is
- * a model entity. The exception to this is for the "worldspawn"
+ * a model entity. The exception to this is the "worldspawn"
  * entity class, which is always a brush-based entity.
  */
-void Doom3Group::updateIsModel() {
-	if (m_modelKey != m_name && _entity.getKeyValue("classname") != "worldspawn") {
+void Doom3Group::updateIsModel()
+{
+	if (m_modelKey != m_name && !_entity.isWorldspawn())
+	{
 		setIsModel(true);
 
 		// Set the renderable name back to 0,0,0
 		_owner._renderableName.setOrigin(Vector3(0,0,0));
 	}
-	else {
+	else 
+	{
 		setIsModel(false);
 
 		// Update the renderable name
diff --git a/plugins/entity/doom3group/Doom3GroupNode.cpp b/plugins/entity/doom3group/Doom3GroupNode.cpp
index dfbaf62..9660015 100644
--- a/plugins/entity/doom3group/Doom3GroupNode.cpp
+++ b/plugins/entity/doom3group/Doom3GroupNode.cpp
@@ -152,7 +152,7 @@ void Doom3GroupNode::removeOriginFromChildren()
 	}
 }
 
-void Doom3GroupNode::selectionChangedComponent(const Selectable& selectable) {
+void Doom3GroupNode::selectionChangedComponent(const ISelectable& selectable) {
 	GlobalSelectionSystem().onComponentSelection(Node::getSelf(), selectable);
 }
 
@@ -168,6 +168,16 @@ void Doom3GroupNode::setSelectedComponents(bool selected, SelectionSystem::EComp
 	}
 }
 
+void Doom3GroupNode::invertSelectedComponents(SelectionSystem::EComponentMode mode)
+{
+	if (mode == SelectionSystem::eVertex)
+	{
+		_nurbsEditInstance.invertSelected();
+		_catmullRomEditInstance.invertSelected();
+		_originInstance.invertSelected();
+	}
+}
+
 void Doom3GroupNode::testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode)
 {
 	if (mode == SelectionSystem::eVertex)
diff --git a/plugins/entity/doom3group/Doom3GroupNode.h b/plugins/entity/doom3group/Doom3GroupNode.h
index da698de..f0b9b67 100644
--- a/plugins/entity/doom3group/Doom3GroupNode.h
+++ b/plugins/entity/doom3group/Doom3GroupNode.h
@@ -66,9 +66,10 @@ public:
 	void testSelect(Selector& selector, SelectionTest& test);
 
 	// ComponentSelectionTestable implementation
-	bool isSelectedComponents() const;
-	void setSelectedComponents(bool selected, SelectionSystem::EComponentMode mode);
-	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode);
+	bool isSelectedComponents() const override;
+	void setSelectedComponents(bool selected, SelectionSystem::EComponentMode mode) override;
+	void invertSelectedComponents(SelectionSystem::EComponentMode mode) override;
+	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) override;
 
 	// override scene::Inode::onRemoveFromScene to deselect the child components
 	virtual void onInsertIntoScene(scene::IMapRootNode& root) override;
@@ -83,7 +84,7 @@ public:
 	// Snappable implementation
 	virtual void snapto(float snap);
 
-	void selectionChangedComponent(const Selectable& selectable);
+	void selectionChangedComponent(const ISelectable& selectable);
 
 	scene::INodePtr clone() const;
 
@@ -94,11 +95,11 @@ public:
 	void removeOriginFromChildren();
 
 	// Renderable implementation
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
-	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const override;
 
 	void transformComponents(const Matrix4& matrix);
 
diff --git a/plugins/entity/eclassmodel/EclassModelNode.h b/plugins/entity/eclassmodel/EclassModelNode.h
index d4626ba..fd0ddcd 100644
--- a/plugins/entity/eclassmodel/EclassModelNode.h
+++ b/plugins/entity/eclassmodel/EclassModelNode.h
@@ -63,9 +63,9 @@ public:
 	scene::INodePtr clone() const;
 
 	// Renderable implementation
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
     
     // Returns the original "origin" value
     const Vector3& getUntransformedOrigin() override;
diff --git a/plugins/entity/generic/GenericEntityNode.cpp b/plugins/entity/generic/GenericEntityNode.cpp
index 8603b42..d0317db 100644
--- a/plugins/entity/generic/GenericEntityNode.cpp
+++ b/plugins/entity/generic/GenericEntityNode.cpp
@@ -7,7 +7,6 @@ namespace entity {
 GenericEntityNode::GenericEntityNode(const IEntityClassPtr& eclass) :
 	EntityNode(eclass),
 	m_contained(*this),
-	_localPivot(Matrix4::getIdentity()),
     _solidAABBRenderMode(SolidBoxes)
 {}
 
@@ -15,7 +14,6 @@ GenericEntityNode::GenericEntityNode(const GenericEntityNode& other) :
 	EntityNode(other),
 	Snappable(other),
 	m_contained(other.m_contained, *this),
-	_localPivot(other._localPivot),
     _solidAABBRenderMode(other._solidAABBRenderMode)
 {}
 
@@ -156,9 +154,4 @@ void GenericEntityNode::onChildRemoved(const scene::INodePtr& child)
     });
 }
 
-const Matrix4& GenericEntityNode::getLocalPivot() const
-{
-	return _localPivot;
-}
-
 } // namespace entity
diff --git a/plugins/entity/generic/GenericEntityNode.h b/plugins/entity/generic/GenericEntityNode.h
index c48c391..6bdbc9f 100644
--- a/plugins/entity/generic/GenericEntityNode.h
+++ b/plugins/entity/generic/GenericEntityNode.h
@@ -19,16 +19,12 @@ typedef std::shared_ptr<GenericEntityNode> GenericEntityNodePtr;
 
 class GenericEntityNode :
 	public EntityNode,
-	public Snappable,
-	public Editable
+	public Snappable
 {
 	friend class GenericEntity;
 
 	GenericEntity m_contained;
 
-	// The local pivot of this generic node is always at the local origin 0,0,0
-	Matrix4 _localPivot;
-
     // Whether to draw a solid/shaded box in full material render mode or just the wireframe
     enum SolidAAABBRenderMode
     {
@@ -59,17 +55,14 @@ public:
 	scene::INodePtr clone() const;
 
 	// Renderable implementation
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
 
     SolidAAABBRenderMode getSolidAABBRenderMode() const;
 
 	// Override EntityNode::getDirection()
 	const Vector3& getDirection() const;
 
-	// Editable - to prevent the selection system from including particle bounds in the pivot calculation
-	const Matrix4& getLocalPivot() const;
-
     // Returns the original "origin" value
     const Vector3& getUntransformedOrigin() override;
 
diff --git a/plugins/entity/light/Light.cpp b/plugins/entity/light/Light.cpp
index cbca7ee..9668427 100644
--- a/plugins/entity/light/Light.cpp
+++ b/plugins/entity/light/Light.cpp
@@ -536,8 +536,8 @@ void Light::renderProjectionPoints(RenderableCollector& collector,
                                    const Matrix4& localToWorld) const 
 {
     // Add the renderable light target
-    collector.highlightPrimitives(false);
-    collector.highlightFaces(false);
+    collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
+    collector.setHighlightFlag(RenderableCollector::Highlight::Faces, false);
 
     collector.SetState(_rRight.getShader(), RenderableCollector::eFullMaterials);
     collector.SetState(_rRight.getShader(), RenderableCollector::eWireframeOnly);
@@ -569,8 +569,8 @@ void Light::renderLightCentre(RenderableCollector& collector,
                               const VolumeTest& volume,
                               const Matrix4& localToWorld) const 
 {
-    collector.highlightPrimitives(false);
-    collector.highlightFaces(false);
+    collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
+    collector.setHighlightFlag(RenderableCollector::Highlight::Faces, false);
     collector.SetState(_rCentre.getShader(), RenderableCollector::eFullMaterials);
     collector.SetState(_rCentre.getShader(), RenderableCollector::eWireframeOnly);
 
@@ -670,12 +670,6 @@ void Light::rotate(const Quaternion& rotation)
     m_rotation.rotate(rotation);
 }
 
-const Matrix4& Light::getLocalPivot() const {
-    m_localPivot = m_rotation.getMatrix4();
-    m_localPivot.t().getVector3() = _lightBox.origin;
-    return m_localPivot;
-}
-
 // greebo: This returns the AABB of the WHOLE light (this includes the volume and all its selectable vertices)
 // Used to test the light for selection on mouse click.
 const AABB& Light::localAABB() const
diff --git a/plugins/entity/light/Light.h b/plugins/entity/light/Light.h
index 59b1383..244e845 100644
--- a/plugins/entity/light/Light.h
+++ b/plugins/entity/light/Light.h
@@ -46,7 +46,6 @@ class LightNode;
 class Light :
 	public OpenGLRenderable,
 	public Bounded,
-	public Editable,
 	public Snappable
 {
 	friend class LightNode;
@@ -244,10 +243,6 @@ public:
 	void revertTransform();
 	void freezeTransform();
 
-	// note: move this
-	mutable Matrix4 m_localPivot;
-	const Matrix4& getLocalPivot() const;
-
     // Is this light projected or omni?
     bool isProjected() const;
 
diff --git a/plugins/entity/light/LightNode.cpp b/plugins/entity/light/LightNode.cpp
index c7a964e..571ffb0 100644
--- a/plugins/entity/light/LightNode.cpp
+++ b/plugins/entity/light/LightNode.cpp
@@ -57,10 +57,6 @@ void LightNode::construct()
 	_light.construct();
 }
 
-const Matrix4& LightNode::getLocalPivot() const {
-	return _light.getLocalPivot();
-}
-
 // Snappable implementation
 void LightNode::snapto(float snap) {
 	_light.snapto(snap);
@@ -142,6 +138,19 @@ void LightNode::setSelectedComponents(bool select, SelectionSystem::EComponentMo
 	}
 }
 
+void LightNode::invertSelectedComponents(SelectionSystem::EComponentMode mode)
+{
+	if (mode == SelectionSystem::eVertex)
+	{
+		_lightCenterInstance.invertSelected();
+		_lightTargetInstance.invertSelected();
+		_lightRightInstance.invertSelected();
+		_lightUpInstance.invertSelected();
+		_lightStartInstance.invertSelected();
+		_lightEndInstance.invertSelected();
+	}
+}
+
 void LightNode::testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode)
 {
 	if (mode == SelectionSystem::eVertex)
@@ -251,7 +260,7 @@ scene::INodePtr LightNode::clone() const
 	return node;
 }
 
-void LightNode::selectedChangedComponent(const Selectable& selectable) {
+void LightNode::selectedChangedComponent(const ISelectable& selectable) {
 	// add the selectable to the list of selected components (see RadiantSelectionSystem::onComponentSelection)
 	GlobalSelectionSystem().onComponentSelection(Node::getSelf(), selectable);
 }
diff --git a/plugins/entity/light/LightNode.h b/plugins/entity/light/LightNode.h
index befaac9..278c819 100644
--- a/plugins/entity/light/LightNode.h
+++ b/plugins/entity/light/LightNode.h
@@ -17,7 +17,6 @@ class LightNode :
 	public EntityNode,
 	public ILightNode,
 	public Snappable,
-	public Editable,
 	public ComponentSelectionTestable,
 	public ComponentEditable,
 	public ComponentSnappable,
@@ -61,9 +60,6 @@ public:
 	virtual void onInsertIntoScene(scene::IMapRootNode& root) override;
 	virtual void onRemoveFromScene(scene::IMapRootNode& root) override;
 
-	// Editable implementation
-	virtual const Matrix4& getLocalPivot() const;
-
 	// Snappable implementation
 	virtual void snapto(float snap);
 
@@ -95,10 +91,11 @@ public:
 	void testSelect(Selector& selector, SelectionTest& test);
 
 	// greebo: Returns true if drag planes or the light center is selected (both are components)
-	bool isSelectedComponents() const;
+	bool isSelectedComponents() const override;
 	// greebo: Selects/deselects all components, depending on the chosen componentmode
-	void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode);
-	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode);
+	void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) override;
+	void invertSelectedComponents(SelectionSystem::EComponentMode mode) override;
+	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) override;
 
 	/**
 	 * greebo: This returns the AABB of all the selectable vertices. This method
@@ -109,15 +106,15 @@ public:
 
 	scene::INodePtr clone() const;
 
-	void selectedChangedComponent(const Selectable& selectable);
+	void selectedChangedComponent(const ISelectable& selectable);
 
 	// Renderable implementation
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
   	// Renders the components of this light instance
-	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const override;
 
 	// RendererLight implementation
     const Vector3& worldOrigin() const;
diff --git a/plugins/entity/speaker/SpeakerNode.cpp b/plugins/entity/speaker/SpeakerNode.cpp
index c7d3f84..f06e432 100644
--- a/plugins/entity/speaker/SpeakerNode.cpp
+++ b/plugins/entity/speaker/SpeakerNode.cpp
@@ -199,7 +199,7 @@ void SpeakerNode::testSelect(Selector& selector, SelectionTest& test)
 	}
 }
 
-void SpeakerNode::selectedChangedComponent(const Selectable& selectable)
+void SpeakerNode::selectedChangedComponent(const ISelectable& selectable)
 {
 	// add the selectable to the list of selected components (see RadiantSelectionSystem::onComponentSelection)
 	GlobalSelectionSystem().onComponentSelection(Node::getSelf(), selectable);
@@ -218,6 +218,11 @@ void SpeakerNode::setSelectedComponents(bool select, SelectionSystem::EComponent
 	}
 }
 
+void SpeakerNode::invertSelectedComponents(SelectionSystem::EComponentMode mode)
+{
+	// nothing, planes are selected via selectPlanes()
+}
+
 void SpeakerNode::testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode)
 {
 	// nothing, planes are selected via selectPlanes()
diff --git a/plugins/entity/speaker/SpeakerNode.h b/plugins/entity/speaker/SpeakerNode.h
index 0f448a2..f67ba16 100644
--- a/plugins/entity/speaker/SpeakerNode.h
+++ b/plugins/entity/speaker/SpeakerNode.h
@@ -96,9 +96,10 @@ public:
     void selectReversedPlanes(Selector& selector, const SelectedPlanes& selectedPlanes);
 
     // ComponentSelectionTestable implementation
-    bool isSelectedComponents() const;
-    void setSelectedComponents(bool selected, SelectionSystem::EComponentMode mode);
-    void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode);
+    bool isSelectedComponents() const override;
+    void setSelectedComponents(bool selected, SelectionSystem::EComponentMode mode) override;
+	void invertSelectedComponents(SelectionSystem::EComponentMode mode) override;
+    void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) override;
 
     // SelectionTestable implementation
     void testSelect(Selector& selector, SelectionTest& test);
@@ -106,10 +107,10 @@ public:
     scene::INodePtr clone() const;
 
     // Renderable implementation
-    void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-    void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
+    void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+    void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
 
-    void selectedChangedComponent(const Selectable& selectable);
+    void selectedChangedComponent(const ISelectable& selectable);
 
 protected:
     // Gets called by the Transformable implementation whenever
diff --git a/plugins/entity/target/TargetLineNode.cpp b/plugins/entity/target/TargetLineNode.cpp
index 6f90676..478fadb 100644
--- a/plugins/entity/target/TargetLineNode.cpp
+++ b/plugins/entity/target/TargetLineNode.cpp
@@ -43,11 +43,11 @@ void TargetLineNode::renderWireframe(RenderableCollector& collector, const Volum
 	_targetLines.render(collector, volume, getOwnerPosition());
 }
 
-bool TargetLineNode::isHighlighted() const
+std::size_t TargetLineNode::getHighlightFlags()
 {
-    // We don't need to return true, since the render system will use 
+    // We don't need to return highlighting, since the render system will use 
     // the result of the parent entity node
-    return false;
+    return Highlight::NoHighlight;
 }
 
 const Vector3& TargetLineNode::getOwnerPosition() const
diff --git a/plugins/entity/target/TargetLineNode.h b/plugins/entity/target/TargetLineNode.h
index 1bde596..52ad7be 100644
--- a/plugins/entity/target/TargetLineNode.h
+++ b/plugins/entity/target/TargetLineNode.h
@@ -38,7 +38,7 @@ public:
 
     void renderSolid(RenderableCollector& collector, const VolumeTest& volumeTest) const override;
     void renderWireframe(RenderableCollector& collector, const VolumeTest& volumeTest) const override;
-    bool isHighlighted() const override;
+	std::size_t getHighlightFlags() override;
 
 private:
     const Vector3& getOwnerPosition() const;
diff --git a/plugins/entity/target/TargetableNode.cpp b/plugins/entity/target/TargetableNode.cpp
index c426f60..5f66413 100644
--- a/plugins/entity/target/TargetableNode.cpp
+++ b/plugins/entity/target/TargetableNode.cpp
@@ -124,6 +124,9 @@ void TargetableNode::onTargetKeyCollectionChanged()
         {
             _targetLineNode.reset(new TargetLineNode(_node));
             scene::addNodeToContainer(_targetLineNode, _node.shared_from_this());
+
+			// Fix #4373: Move the target lines to the same layers as the owning node
+			_targetLineNode->assignToLayers(_node.getLayers());
         }
     }
     else // No more targets
diff --git a/plugins/entitylist/EntityList.cpp b/plugins/entitylist/EntityList.cpp
index 9773e68..5d757be 100644
--- a/plugins/entitylist/EntityList.cpp
+++ b/plugins/entitylist/EntityList.cpp
@@ -299,7 +299,7 @@ void EntityList::onSelection(wxDataViewEvent& ev)
 		wxutil::TreeModel::Row row(*i, *_treeModel.getModel());
 		scene::INode* node = static_cast<scene::INode*>(row[_treeModel.getColumns().node].getPointer());
 
-		Selectable* selectable = dynamic_cast<Selectable*>(node);
+		ISelectable* selectable = dynamic_cast<ISelectable*>(node);
 
 		if (selectable != NULL)
 		{
diff --git a/plugins/entitylist/GraphTreeModelPopulator.h b/plugins/entitylist/GraphTreeModelPopulator.h
index b45cc2a..a07df67 100644
--- a/plugins/entitylist/GraphTreeModelPopulator.h
+++ b/plugins/entitylist/GraphTreeModelPopulator.h
@@ -41,7 +41,7 @@ public:
 
 		Entity* ent = Node_getEntity(node);
 
-		if (ent != NULL && ent->getKeyValue("classname") == "worldspawn") {
+		if (ent != NULL && ent->isWorldspawn()) {
 			// Don't accumulate the worldspawn brushes
 			return false;
 		}
diff --git a/plugins/eventmanager/MouseToolManager.cpp b/plugins/eventmanager/MouseToolManager.cpp
index e2acc3c..212ec96 100644
--- a/plugins/eventmanager/MouseToolManager.cpp
+++ b/plugins/eventmanager/MouseToolManager.cpp
@@ -42,58 +42,53 @@ void MouseToolManager::initialiseModule(const ApplicationContext& ctx)
         sigc::mem_fun(this, &MouseToolManager::onRadiantStartup));
 }
 
-void MouseToolManager::loadGroupMapping(MouseToolGroup& group, const xml::Node& mappingNode)
+void MouseToolManager::loadGroupMapping(MouseToolGroup::Type type, const xml::NodeList& userMappings, const xml::NodeList& defaultMappings)
 {
-    group.clearToolMappings();
-
-    for (const xml::Node& node : mappingNode.getNamedChildren("tool"))
-    {
-        // Load the condition
-        unsigned int state = wxutil::MouseButton::LoadFromNode(node) | wxutil::Modifier::LoadFromNode(node);
-        std::string name = node.getAttributeValue("name");
-        MouseToolPtr tool = group.getMouseToolByName(name);
-
-        if (!tool)
-        {
-            rWarning() << "Unregistered MouseTool name in XML for group " << 
-                static_cast<int>(group.getType()) << ": " << name << std::endl;
-            continue;
-        }
-
-        group.addToolMapping(state, tool);
-    }
+	MouseToolGroup& group = getGroup(type);
+
+	group.clearToolMappings();
+
+	group.foreachMouseTool([&] (const MouseToolPtr& tool)
+	{
+		// First, look in the userMappings if we have a user-defined setting
+		for (const xml::Node& node : userMappings)
+		{
+			if (node.getAttributeValue("name") == tool->getName())
+			{
+				// Load the condition
+				unsigned int state = wxutil::MouseButton::LoadFromNode(node) | wxutil::Modifier::LoadFromNode(node);
+				group.addToolMapping(state, tool);
+
+				return; // done here
+			}
+		}
+
+		// nothing found in the user mapping, fall back to default
+		for (const xml::Node& node : defaultMappings)
+		{
+			if (node.getAttributeValue("name") == tool->getName())
+			{
+				// Load the condition
+				unsigned int state = wxutil::MouseButton::LoadFromNode(node) | wxutil::Modifier::LoadFromNode(node);
+				group.addToolMapping(state, tool);
+
+				return; // done here
+			}
+		}
+
+		// No mapping for this tool
+	});
 }
 
 void MouseToolManager::loadToolMappings()
 {
     // All modules have registered their stuff, now load the mapping
     // Try the user-defined mapping first
-    xml::NodeList mappings = GlobalRegistry().findXPath("user/ui/input/mouseToolMappings[@name='user']//mouseToolMapping");
-
-    if (mappings.empty())
-    {
-        // Fall back to the default mapping
-        mappings = GlobalRegistry().findXPath("user/ui/input/mouseToolMappings[@name='default']//mouseToolMapping");
-    }
+    xml::NodeList userMappings = GlobalRegistry().findXPath("user/ui/input/mouseToolMappings[@name='user']//mouseToolMapping//tool");
+	xml::NodeList defaultMappings = GlobalRegistry().findXPath("user/ui/input/mouseToolMappings[@name='default']//mouseToolMapping//tool");
 
-    for (const xml::Node& node : mappings)
-    {
-        std::string mappingName = node.getAttributeValue("name");
-
-        int mappingId = string::convert<int>(node.getAttributeValue("id"), -1);
-
-        if (mappingId == -1)
-        {
-            rMessage() << "Skipping invalid view id in mouse tool mapping " << mappingName << std::endl;
-            continue;
-        }
-
-        rMessage() << "Loading mouse tool mapping for " << mappingName << std::endl;
-
-        MouseToolGroup::Type type = static_cast<MouseToolGroup::Type>(mappingId);
-
-        loadGroupMapping(getGroup(type), node);
-    }
+	loadGroupMapping(MouseToolGroup::Type::CameraView, userMappings, defaultMappings);
+	loadGroupMapping(MouseToolGroup::Type::OrthoView, userMappings, defaultMappings);
 }
 
 void MouseToolManager::resetBindingsToDefault()
diff --git a/plugins/eventmanager/MouseToolManager.h b/plugins/eventmanager/MouseToolManager.h
index d545c99..550c396 100644
--- a/plugins/eventmanager/MouseToolManager.h
+++ b/plugins/eventmanager/MouseToolManager.h
@@ -47,7 +47,7 @@ public:
 
 private:
     void loadToolMappings();
-    void loadGroupMapping(MouseToolGroup& group, const xml::Node& mappingNode);
+	void loadGroupMapping(MouseToolGroup::Type type, const xml::NodeList& userMappings, const xml::NodeList& defaultMappings);
 
     void saveToolMappings();
 };
diff --git a/plugins/fonts/GlyphSet.cpp b/plugins/fonts/GlyphSet.cpp
index 52a5724..1f54179 100644
--- a/plugins/fonts/GlyphSet.cpp
+++ b/plugins/fonts/GlyphSet.cpp
@@ -68,7 +68,7 @@ GlyphSetPtr GlyphSet::createFromDatFile(const std::string& vfsPath,
 	q3font::Q3FontInfoPtr buf(new q3font::Q3FontInfo);
 
 	InputStream& stream = file->getInputStream();
-	StreamBase::size_type bytesRead = stream.read(
+	stream.read(
 		reinterpret_cast<StreamBase::byte_type*>(buf.get()),
 		sizeof(q3font::Q3FontInfo)
 	);
diff --git a/plugins/fonts/Makefile.am b/plugins/fonts/Makefile.am
index 2699ad8..2ea17c4 100644
--- a/plugins/fonts/Makefile.am
+++ b/plugins/fonts/Makefile.am
@@ -10,6 +10,8 @@ fonts_la_LDFLAGS = -module -avoid-version \
                    $(XML_LIBS) \
                    $(GL_LIBS) \
                    $(GLU_LIBS) \
+                   $(BOOST_FILESYSTEM_LIBS) \
+                   $(BOOST_SYSTEM_LIBS) \
                    $(BOOST_REGEX_LIBS) \
                    $(LIBSIGC_LIBS)
 fonts_la_SOURCES = plugin.cpp \
diff --git a/plugins/fonts/Makefile.in b/plugins/fonts/Makefile.in
index 914819a..b0ede33 100644
--- a/plugins/fonts/Makefile.in
+++ b/plugins/fonts/Makefile.in
@@ -408,6 +408,8 @@ fonts_la_LDFLAGS = -module -avoid-version \
                    $(XML_LIBS) \
                    $(GL_LIBS) \
                    $(GLU_LIBS) \
+                   $(BOOST_FILESYSTEM_LIBS) \
+                   $(BOOST_SYSTEM_LIBS) \
                    $(BOOST_REGEX_LIBS) \
                    $(LIBSIGC_LIBS)
 
diff --git a/plugins/grid/Grid.cpp b/plugins/grid/Grid.cpp
index 2e8bf62..cb78edd 100644
--- a/plugins/grid/Grid.cpp
+++ b/plugins/grid/Grid.cpp
@@ -52,7 +52,7 @@ public:
 		rMessage() << "GridManager::initialiseModule called.\n";
 
 		// Add the grid status bar element
-		GlobalUIManager().getStatusBarManager().addTextElement("GridStatus", "grid_up.png", IStatusBarManager::POS_GRID);
+		GlobalUIManager().getStatusBarManager().addTextElement("GridStatus", "grid_up.png", IStatusBarManager::POS_GRID, _("Current Grid Size"));
 		GlobalUIManager().getStatusBarManager().setText("GridStatus", "-");
 
 		populateGridItems();
@@ -147,10 +147,11 @@ public:
 		return returnValue;
 	}
 
-	void constructPreferences() {
-		PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Grid"));
+	void constructPreferences()
+	{
+		IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Grid"));
 
-		page->appendCombo(_("Default Grid Size"), RKEY_DEFAULT_GRID_SIZE, getGridList());
+		page.appendCombo(_("Default Grid Size"), RKEY_DEFAULT_GRID_SIZE, getGridList());
 
 		ComboBoxValueList looks;
 
@@ -162,8 +163,8 @@ public:
 		looks.push_back(_("Big Dots"));
 		looks.push_back(_("Squares"));
 
-		page->appendCombo(_("Major Grid Style"), RKEY_GRID_LOOK_MAJOR, looks);
-		page->appendCombo(_("Minor Grid Style"), RKEY_GRID_LOOK_MINOR, looks);
+		page.appendCombo(_("Major Grid Style"), RKEY_GRID_LOOK_MAJOR, looks);
+		page.appendCombo(_("Minor Grid Style"), RKEY_GRID_LOOK_MINOR, looks);
 	}
 
 
diff --git a/plugins/mapdoom3/Makefile.am b/plugins/mapdoom3/Makefile.am
index 2692331..5284dcb 100644
--- a/plugins/mapdoom3/Makefile.am
+++ b/plugins/mapdoom3/Makefile.am
@@ -9,7 +9,12 @@ mapdoom3_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \
                      $(top_builddir)/libs/scene/libscenegraph.la \
                      $(top_builddir)/libs/math/libmath.la
 mapdoom3_la_LDFLAGS = -module -avoid-version \
-                      $(WX_LIBS) $(XML_LIBS) $(GLEW_LIBS) $(GL_LIBS)
+                      $(WX_LIBS) \
+                      $(XML_LIBS) \
+                      $(BOOST_FILESYSTEM_LIBS) \
+                      $(BOOST_SYSTEM_LIBS) \
+                      $(GLEW_LIBS) \
+                      $(GL_LIBS)
 mapdoom3_la_SOURCES = Doom3MapFormat.cpp \
                       Doom3PrefabFormat.cpp \
                       Quake3MapFormat.cpp \
@@ -27,6 +32,9 @@ mapdoom3_la_SOURCES = Doom3MapFormat.cpp \
                       compiler/ProcWinding.cpp \
                       compiler/ProcLight.cpp \
                       compiler/Surface.cpp \
+					  aas/Doom3AasFile.cpp \
+					  aas/Doom3AasFileLoader.cpp \
+					  aas/Doom3AasFileSettings.cpp \
                       primitiveparsers/BrushDef.cpp \
                       primitiveparsers/BrushDef3.cpp \
                       primitiveparsers/Patch.cpp \
diff --git a/plugins/mapdoom3/Makefile.in b/plugins/mapdoom3/Makefile.in
index ca93772..7fda851 100644
--- a/plugins/mapdoom3/Makefile.in
+++ b/plugins/mapdoom3/Makefile.in
@@ -138,9 +138,11 @@ am_mapdoom3_la_OBJECTS = Doom3MapFormat.lo Doom3PrefabFormat.lo \
 	compiler/OptIsland.lo compiler/ProcCompiler.lo \
 	compiler/ProcFile.lo compiler/ProcPatch.lo \
 	compiler/ProcWinding.lo compiler/ProcLight.lo \
-	compiler/Surface.lo primitiveparsers/BrushDef.lo \
-	primitiveparsers/BrushDef3.lo primitiveparsers/Patch.lo \
-	primitiveparsers/PatchDef2.lo primitiveparsers/PatchDef3.lo
+	compiler/Surface.lo aas/Doom3AasFile.lo \
+	aas/Doom3AasFileLoader.lo aas/Doom3AasFileSettings.lo \
+	primitiveparsers/BrushDef.lo primitiveparsers/BrushDef3.lo \
+	primitiveparsers/Patch.lo primitiveparsers/PatchDef2.lo \
+	primitiveparsers/PatchDef3.lo
 mapdoom3_la_OBJECTS = $(am_mapdoom3_la_OBJECTS)
 AM_V_lt = $(am__v_lt_ at AM_V@)
 am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
@@ -420,7 +422,12 @@ mapdoom3_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \
                      $(top_builddir)/libs/math/libmath.la
 
 mapdoom3_la_LDFLAGS = -module -avoid-version \
-                      $(WX_LIBS) $(XML_LIBS) $(GLEW_LIBS) $(GL_LIBS)
+                      $(WX_LIBS) \
+                      $(XML_LIBS) \
+                      $(BOOST_FILESYSTEM_LIBS) \
+                      $(BOOST_SYSTEM_LIBS) \
+                      $(GLEW_LIBS) \
+                      $(GL_LIBS)
 
 mapdoom3_la_SOURCES = Doom3MapFormat.cpp \
                       Doom3PrefabFormat.cpp \
@@ -439,6 +446,9 @@ mapdoom3_la_SOURCES = Doom3MapFormat.cpp \
                       compiler/ProcWinding.cpp \
                       compiler/ProcLight.cpp \
                       compiler/Surface.cpp \
+					  aas/Doom3AasFile.cpp \
+					  aas/Doom3AasFileLoader.cpp \
+					  aas/Doom3AasFileSettings.cpp \
                       primitiveparsers/BrushDef.cpp \
                       primitiveparsers/BrushDef3.cpp \
                       primitiveparsers/Patch.cpp \
@@ -536,6 +546,17 @@ compiler/ProcLight.lo: compiler/$(am__dirstamp) \
 	compiler/$(DEPDIR)/$(am__dirstamp)
 compiler/Surface.lo: compiler/$(am__dirstamp) \
 	compiler/$(DEPDIR)/$(am__dirstamp)
+aas/$(am__dirstamp):
+	@$(MKDIR_P) aas
+	@: > aas/$(am__dirstamp)
+aas/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) aas/$(DEPDIR)
+	@: > aas/$(DEPDIR)/$(am__dirstamp)
+aas/Doom3AasFile.lo: aas/$(am__dirstamp) aas/$(DEPDIR)/$(am__dirstamp)
+aas/Doom3AasFileLoader.lo: aas/$(am__dirstamp) \
+	aas/$(DEPDIR)/$(am__dirstamp)
+aas/Doom3AasFileSettings.lo: aas/$(am__dirstamp) \
+	aas/$(DEPDIR)/$(am__dirstamp)
 primitiveparsers/$(am__dirstamp):
 	@$(MKDIR_P) primitiveparsers
 	@: > primitiveparsers/$(am__dirstamp)
@@ -558,6 +579,8 @@ mapdoom3.la: $(mapdoom3_la_OBJECTS) $(mapdoom3_la_DEPENDENCIES) $(EXTRA_mapdoom3
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
+	-rm -f aas/*.$(OBJEXT)
+	-rm -f aas/*.lo
 	-rm -f compiler/*.$(OBJEXT)
 	-rm -f compiler/*.lo
 	-rm -f primitiveparsers/*.$(OBJEXT)
@@ -575,6 +598,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/Quake4MapFormat.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/Quake4MapReader.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/mapdoom3.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at aas/$(DEPDIR)/Doom3AasFile.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at aas/$(DEPDIR)/Doom3AasFileLoader.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at aas/$(DEPDIR)/Doom3AasFileSettings.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at compiler/$(DEPDIR)/Doom3MapCompiler.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at compiler/$(DEPDIR)/OptIsland.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at compiler/$(DEPDIR)/ProcCompiler.Plo at am__quote@
@@ -618,6 +644,7 @@ mostlyclean-libtool:
 
 clean-libtool:
 	-rm -rf .libs _libs
+	-rm -rf aas/.libs aas/_libs
 	-rm -rf compiler/.libs compiler/_libs
 	-rm -rf primitiveparsers/.libs primitiveparsers/_libs
 
@@ -736,6 +763,8 @@ clean-generic:
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f aas/$(DEPDIR)/$(am__dirstamp)
+	-rm -f aas/$(am__dirstamp)
 	-rm -f compiler/$(DEPDIR)/$(am__dirstamp)
 	-rm -f compiler/$(am__dirstamp)
 	-rm -f primitiveparsers/$(DEPDIR)/$(am__dirstamp)
@@ -750,7 +779,7 @@ clean-am: clean-generic clean-libtool clean-modulesLTLIBRARIES \
 	mostlyclean-am
 
 distclean: distclean-am
-	-rm -rf ./$(DEPDIR) compiler/$(DEPDIR) primitiveparsers/$(DEPDIR)
+	-rm -rf ./$(DEPDIR) aas/$(DEPDIR) compiler/$(DEPDIR) primitiveparsers/$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-tags
@@ -796,7 +825,7 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-	-rm -rf ./$(DEPDIR) compiler/$(DEPDIR) primitiveparsers/$(DEPDIR)
+	-rm -rf ./$(DEPDIR) aas/$(DEPDIR) compiler/$(DEPDIR) primitiveparsers/$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
diff --git a/plugins/mapdoom3/aas/Doom3AasFile.cpp b/plugins/mapdoom3/aas/Doom3AasFile.cpp
new file mode 100644
index 0000000..5e7d4b0
--- /dev/null
+++ b/plugins/mapdoom3/aas/Doom3AasFile.cpp
@@ -0,0 +1,413 @@
+#include "Doom3AasFile.h"
+
+#include "itextstream.h"
+#include "string/convert.h"
+#include "Util.h"
+
+namespace map
+{
+
+// area flags
+#define AREA_FLOOR					(1 << 0)		// AI can stand on the floor in this area
+#define AREA_GAP					(1 << 1)		// area has a gap
+#define AREA_LEDGE					(1 << 2)		// if entered the AI bbox partly floats above a ledge
+#define AREA_LADDER					(1 << 3)		// area contains one or more ladder faces
+#define AREA_LIQUID					(1 << 4)		// area contains a liquid
+#define AREA_CROUCH					(1 << 5)		// AI cannot walk but can only crouch in this area
+#define AREA_REACHABLE_WALK			(1 << 6)		// area is reachable by walking or swimming
+#define AREA_REACHABLE_FLY			(1 << 7)		// area is reachable by flying
+#define AREA_DOOR					(1 << 8)		// area contains one ore more doors
+
+// face flags
+#define FACE_SOLID					(1 << 0)		// solid at the other side
+#define FACE_LADDER					(1 << 1)		// ladder surface
+#define FACE_FLOOR					(1 << 2)		// standing on floor when on this face
+#define FACE_LIQUID					(1 << 3)		// face seperating two areas with liquid
+#define FACE_LIQUIDSURFACE			(1 << 4)		// face seperating liquid and air
+
+std::size_t Doom3AasFile::getNumPlanes() const
+{
+    return _planes.size();
+}
+
+const Plane3& Doom3AasFile::getPlane(std::size_t planeNum) const
+{
+    return _planes[planeNum];
+}
+
+std::size_t Doom3AasFile::getNumVertices() const
+{
+    return _vertices.size();
+}
+
+const Vector3& Doom3AasFile::getVertex(std::size_t vertexNum) const
+{
+    return _vertices[vertexNum];
+}
+
+std::size_t Doom3AasFile::getNumEdges() const
+{
+    return _edges.size();
+}
+
+const IAasFile::Edge& Doom3AasFile::getEdge(std::size_t index) const
+{
+    return _edges[index];
+}
+
+std::size_t Doom3AasFile::getNumEdgeIndexes() const
+{
+    return _edgeIndex.size();
+}
+
+int Doom3AasFile::getEdgeByIndex(int edgeIdx) const
+{
+    return _edgeIndex[edgeIdx];
+}
+
+std::size_t Doom3AasFile::getNumFaces() const
+{
+    return _faces.size();
+}
+
+const IAasFile::Face& Doom3AasFile::getFace(int faceIndex) const
+{
+    return _faces[faceIndex];
+}
+
+std::size_t Doom3AasFile::getNumFaceIndexes() const
+{
+    return _faceIndex.size();
+}
+
+int Doom3AasFile::getFaceByIndex(int faceIdx) const
+{
+    return _faceIndex[faceIdx];
+}
+
+std::size_t Doom3AasFile::getNumAreas() const
+{
+    return _areas.size();
+}
+
+const IAasFile::Area& Doom3AasFile::getArea(int areaNum) const
+{
+    return _areas[areaNum];
+}
+
+void Doom3AasFile::parseFromTokens(parser::DefTokeniser& tok)
+{
+    while (tok.hasMoreTokens())
+    {
+        std::string token = tok.nextToken();
+
+        if (token == "settings")
+        {
+            _settings.parseFromTokens(tok);
+        }
+        else if (token == "planes")
+        {
+            std::size_t planesCount = string::convert<std::size_t>(tok.nextToken());
+
+            _planes.reserve(planesCount);
+
+            tok.assertNextToken("{");
+
+            // num ( a b c dist )
+            for (std::size_t i = 0; i < planesCount; ++i)
+            {
+                string::convert<int>(tok.nextToken()); // plane index
+
+                tok.assertNextToken("(");
+
+                Plane3 plane;
+                plane.normal().x() = string::convert<Vector3::ElementType>(tok.nextToken());
+                plane.normal().y() = string::convert<Vector3::ElementType>(tok.nextToken());
+                plane.normal().z() = string::convert<Vector3::ElementType>(tok.nextToken());
+                plane.dist() = string::convert<Vector3::ElementType>(tok.nextToken());
+
+                _planes.push_back(plane);
+
+                tok.assertNextToken(")");
+            }
+
+            tok.assertNextToken("}");
+        }
+        else if (token == "vertices")
+        {
+            std::size_t vertCount = string::convert<std::size_t>(tok.nextToken());
+
+            _vertices.reserve(vertCount);
+
+            tok.assertNextToken("{");
+
+            // num ( x y z )
+            for (std::size_t i = 0; i < vertCount; ++i)
+            {
+                string::convert<int>(tok.nextToken()); // index
+                _vertices.push_back(parseVector3(tok)); // components
+            }
+
+            tok.assertNextToken("}");
+        }
+        else if (token == "edges")
+        {
+            std::size_t edgeCount = string::convert<std::size_t>(tok.nextToken());
+
+            _edges.reserve(edgeCount);
+
+            tok.assertNextToken("{");
+
+            // num ( vertIdx1 vertIdx2 )
+            for (std::size_t i = 0; i < edgeCount; ++i)
+            {
+                string::convert<int>(tok.nextToken()); // index
+
+                tok.assertNextToken("(");
+
+                Edge edge;
+                edge.vertexNumber[0] = string::convert<int>(tok.nextToken());
+                edge.vertexNumber[1] = string::convert<int>(tok.nextToken());
+
+                tok.assertNextToken(")");
+
+                _edges.push_back(edge); // components
+            }
+
+            tok.assertNextToken("}");
+        }
+        else if (token == "edgeIndex")
+        {
+            parseIndex(tok, _edgeIndex);
+        }
+        else if (token == "faces")
+        {
+            std::size_t faceCount = string::convert<std::size_t>(tok.nextToken());
+
+            _faces.reserve(faceCount);
+
+            tok.assertNextToken("{");
+
+            // num ( planeNum flags areas[0] areas[1] firstEdge numEdges )
+            for (std::size_t i = 0; i < faceCount; ++i)
+            {
+                string::convert<int>(tok.nextToken()); // number
+
+                tok.assertNextToken("(");
+
+                Face face;
+
+                face.planeNum = string::convert<int>(tok.nextToken());
+                face.flags = string::convert<int>(tok.nextToken());
+                face.areas[0] = string::convert<int>(tok.nextToken());
+                face.areas[1] = string::convert<int>(tok.nextToken());
+                face.firstEdge = string::convert<int>(tok.nextToken());
+                face.numEdges = string::convert<int>(tok.nextToken());
+
+                _faces.push_back(face);
+
+                tok.assertNextToken(")");
+            }
+
+            tok.assertNextToken("}");
+        }
+        else if (token == "faceIndex")
+        {
+            parseIndex(tok, _faceIndex);
+        }
+        else if (token == "areas")
+        {
+            std::size_t areaCount = string::convert<std::size_t>(tok.nextToken());
+
+            _areas.reserve(areaCount);
+
+            tok.assertNextToken("{");
+
+            // num ( flags contents firstFace numFaces cluster clusterAreaNum ) reachabilityCount { reachabilities }
+            for (std::size_t i = 0; i < areaCount; ++i)
+            {
+                string::convert<int>(tok.nextToken()); // number
+
+                tok.assertNextToken("(");
+
+                Area area;
+
+                area.flags = string::convert<int>(tok.nextToken());
+                area.contents = string::convert<int>(tok.nextToken());
+                area.firstFace = string::convert<int>(tok.nextToken());
+                area.numFaces = string::convert<int>(tok.nextToken());
+                area.cluster = string::convert<int>(tok.nextToken());
+                area.clusterAreaNum = string::convert<int>(tok.nextToken());
+
+                _areas.push_back(area);
+
+                tok.assertNextToken(")");
+
+                // Skip over reachabilities for the moment being
+                /*std::size_t reachCount = */string::convert<std::size_t>(tok.nextToken());
+                tok.assertNextToken("{");
+
+                while (tok.nextToken() != "}")
+                {
+                    // do nothing
+                }
+            }
+
+            // Skip the step LinkReversedReachability();
+
+            tok.assertNextToken("}");
+        }
+        else if (token == "nodes" || token == "portals" || token == "portalIndex" || token == "clusters")
+        {
+            tok.nextToken(); // integer
+            tok.assertNextToken("{");
+
+            while (tok.nextToken() != "}")
+            {
+                // do nothing
+            }
+        }
+        else
+        {
+            throw parser::ParseException("Unknown token: " + token);
+        }
+    }
+
+    finishAreas();
+}
+
+void Doom3AasFile::finishAreas()
+{
+    for (Area& area : _areas)
+    {
+        area.center = calcReachableGoalForArea(area);
+		area.bounds = calcAreaBounds(area);
+    }
+}
+
+#define INTSIGNBITSET(i)		(((const unsigned int)(i)) >> 31)
+
+AABB Doom3AasFile::calcFaceBounds(int faceNum) const
+{
+	AABB bounds;
+
+	const Face& face = _faces[faceNum];
+
+	for (int i = 0; i < face.numEdges; i++)
+    {
+		int edgeNum = _edgeIndex[face.firstEdge + i];
+		const Edge& edge = _edges[abs(edgeNum)];
+
+		bounds.includePoint(_vertices[edge.vertexNumber[INTSIGNBITSET(edgeNum)]]);
+	}
+	return bounds;
+}
+
+AABB Doom3AasFile::calcAreaBounds(const IAasFile::Area& area) const
+{
+	AABB bounds;
+
+	for (int i = 0; i < area.numFaces; i++)
+    {
+		int faceNum = _faceIndex[area.firstFace + i];
+		bounds.includeAABB(calcFaceBounds(abs(faceNum)));
+	}
+
+	return bounds;
+}
+
+Vector3 Doom3AasFile::calcFaceCenter(int faceNum) const
+{
+	Vector3 center(0,0,0);
+
+	const Face& face = _faces[faceNum];
+
+	if (face.numEdges > 0)
+    {
+		for (int i = 0; i < face.numEdges; i++)
+        {
+			int edgeNum = _edgeIndex[face.firstEdge + i];
+			const Edge& edge = _edges[abs(edgeNum)];
+
+			center += _vertices[edge.vertexNumber[INTSIGNBITSET(edgeNum)]];
+		}
+		center /= face.numEdges;
+	}
+
+	return center;
+}
+
+Vector3 Doom3AasFile::calcAreaCenter(const IAasFile::Area& area) const
+{
+	Vector3 center(0,0,0);
+
+	if (area.numFaces > 0)
+    {
+		for (int i = 0; i < area.numFaces; i++)
+        {
+			int faceNum = _faceIndex[area.firstFace + i];
+			center += calcFaceCenter(abs(faceNum));
+		}
+
+		center /= area.numFaces;
+	}
+
+	return center;
+}
+
+Vector3 Doom3AasFile::calcReachableGoalForArea(const IAasFile::Area& area) const
+{
+	if (!(area.flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) || (area.flags & AREA_LIQUID))
+    {
+		return calcAreaCenter(area);
+	}
+
+    Vector3 center(0,0,0);
+
+	int numFaces = 0;
+
+	for (int i = 0; i < area.numFaces; i++)
+    {
+		int faceNum = _faceIndex[area.firstFace + i];
+
+		if (!(_faces[abs(faceNum)].flags & FACE_FLOOR))
+        {
+			continue;
+		}
+
+		center += calcFaceCenter(abs(faceNum));
+		numFaces++;
+	}
+
+	if (numFaces > 0)
+    {
+		center /= numFaces;
+	}
+
+    // No downward trace here
+
+    return center;
+}
+
+void Doom3AasFile::parseIndex(parser::DefTokeniser& tok, Index& index)
+{
+    std::size_t idxCount = string::convert<std::size_t>(tok.nextToken());
+
+    index.reserve(idxCount);
+
+    tok.assertNextToken("{");
+
+    // num ( idx )
+    for (std::size_t i = 0; i < idxCount; ++i)
+    {
+        string::convert<int>(tok.nextToken()); // number
+
+        tok.assertNextToken("(");
+        index.push_back(string::convert<int>(tok.nextToken()));
+        tok.assertNextToken(")");
+    }
+
+    tok.assertNextToken("}");
+}
+
+}
diff --git a/plugins/mapdoom3/aas/Doom3AasFile.h b/plugins/mapdoom3/aas/Doom3AasFile.h
new file mode 100644
index 0000000..2228678
--- /dev/null
+++ b/plugins/mapdoom3/aas/Doom3AasFile.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "iaasfile.h"
+#include "parser/DefTokeniser.h"
+#include "Doom3AasFileSettings.h"
+#include <vector>
+#include "math/Plane3.h"
+#include "math/AABB.h"
+
+namespace map
+{
+    
+class Doom3AasFile :
+    public IAasFile
+{
+private:
+    Doom3AasFileSettings _settings;
+
+    std::vector<Plane3> _planes;
+    std::vector<Vector3> _vertices;
+    std::vector<Edge> _edges;
+
+    typedef std::vector<int> Index;
+    Index _edgeIndex;
+
+    std::vector<Face> _faces;
+    Index _faceIndex;
+
+    std::vector<Area> _areas;
+
+public:
+    virtual std::size_t     getNumPlanes() const override;
+    virtual const Plane3&   getPlane(std::size_t planeNum) const;
+
+    virtual std::size_t     getNumVertices() const override;
+    virtual const Vector3&	getVertex(std::size_t vertexNum) const override;
+
+    virtual std::size_t     getNumEdges() const override;
+    virtual const Edge&     getEdge(std::size_t index) const override;
+
+    virtual std::size_t     getNumEdgeIndexes() const override;
+    virtual int 			getEdgeByIndex(int edgeIdx) const override;
+
+    virtual std::size_t     getNumFaces() const override;
+    virtual const Face&		getFace(int faceIndex) const override;
+    virtual std::size_t     getNumFaceIndexes() const override;
+    virtual int 			getFaceByIndex(int faceIdx) const override;
+
+    virtual std::size_t     getNumAreas() const override;
+    virtual const Area&     getArea(int areaNum) const override;
+
+    void parseFromTokens(parser::DefTokeniser& tok);
+
+private:
+    void parseIndex(parser::DefTokeniser& tok, Index& index);
+    void finishAreas();
+    Vector3 calcReachableGoalForArea(const IAasFile::Area& area) const;
+    Vector3 calcFaceCenter(int faceNum) const;
+    Vector3 calcAreaCenter(const IAasFile::Area& area) const;
+    AABB calcAreaBounds(const IAasFile::Area& area) const;
+    AABB calcFaceBounds(int faceNum) const;
+};
+typedef std::shared_ptr<Doom3AasFile> Doom3AasFilePtr;
+
+}
diff --git a/plugins/mapdoom3/aas/Doom3AasFileLoader.cpp b/plugins/mapdoom3/aas/Doom3AasFileLoader.cpp
new file mode 100644
index 0000000..15b0302
--- /dev/null
+++ b/plugins/mapdoom3/aas/Doom3AasFileLoader.cpp
@@ -0,0 +1,127 @@
+#include "Doom3AasFileLoader.h"
+
+#include "itextstream.h"
+
+#include "parser/DefTokeniser.h"
+#include "string/convert.h"
+#include "Doom3AasFile.h"
+
+namespace map
+{
+
+namespace
+{
+    const float DEWM3_AAS_VERSION = 1.07f;
+}
+
+const std::string& Doom3AasFileLoader::getAasFormatName() const
+{
+    static std::string _name = "Doom 3 AAS";
+	return _name;
+}
+
+const std::string& Doom3AasFileLoader::getGameType() const
+{
+    static std::string _gameType = "doom3";
+	return _gameType;
+}
+
+bool Doom3AasFileLoader::canLoad(std::istream& stream) const
+{
+    // Instantiate a tokeniser to read the first few tokens
+	parser::BasicDefTokeniser<std::istream> tok(stream);
+
+	try
+	{
+        parseVersion(tok);
+	}
+	catch (parser::ParseException&)
+	{
+        return false;
+    }
+	catch (boost::bad_lexical_cast&)
+	{
+        return false;
+    }
+
+	return true;
+}
+
+IAasFilePtr Doom3AasFileLoader::loadFromStream(std::istream& stream)
+{
+    Doom3AasFilePtr aasFile = std::make_shared<Doom3AasFile>();
+
+    // We assume that the stream is rewound to the beginning
+
+    // Instantiate a tokeniser to read the version tag
+	parser::BasicDefTokeniser<std::istream> tok(stream);
+
+    try
+	{
+        // File header
+        parseVersion(tok);
+
+        // Checksum (will throw if the stirng conversion fails)
+        string::convert<long>(tok.nextToken());
+
+        aasFile->parseFromTokens(tok);
+	}
+	catch (parser::ParseException& ex)
+	{
+        rError() << "Failure parsing AAS file: " << ex.what() << std::endl;
+        return IAasFilePtr();
+    }
+	catch (boost::bad_lexical_cast& ex)
+	{
+        rError() << "Conversion error while parsing AAS file: " << ex.what() << std::endl;
+        return IAasFilePtr();
+    }
+
+    return aasFile;
+}
+
+void Doom3AasFileLoader::parseVersion(parser::DefTokeniser& tok) const
+{
+    // Require a "Version" token
+    tok.assertNextToken("DewmAAS");
+
+	// Require specific version, return true on success 
+    if (string::convert<float>(tok.nextToken()) != DEWM3_AAS_VERSION)
+    {
+        throw parser::ParseException("AAS File version mismatch");
+    }
+}
+
+const std::string& Doom3AasFileLoader::getName() const
+{
+	static std::string _name("Doom3AasFileLoader");
+	return _name;
+}
+
+const StringSet& Doom3AasFileLoader::getDependencies() const
+{
+	static StringSet _dependencies;
+
+	if (_dependencies.empty())
+	{
+        _dependencies.insert(MODULE_AASFILEMANAGER);
+	}
+
+	return _dependencies;
+}
+
+void Doom3AasFileLoader::initialiseModule(const ApplicationContext& ctx)
+{
+	rMessage() << getName() << ": initialiseModule called." << std::endl;
+
+	// Register ourselves as aas format
+    GlobalAasFileManager().registerLoader(shared_from_this());
+}
+
+void Doom3AasFileLoader::shutdownModule()
+{
+	// Unregister now that we're shutting down
+	GlobalAasFileManager().unregisterLoader(shared_from_this());
+}
+
+}
diff --git a/plugins/mapdoom3/aas/Doom3AasFileLoader.h b/plugins/mapdoom3/aas/Doom3AasFileLoader.h
new file mode 100644
index 0000000..20ec209
--- /dev/null
+++ b/plugins/mapdoom3/aas/Doom3AasFileLoader.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "iaasfile.h"
+
+namespace parser { class DefTokeniser; }
+
+namespace map
+{
+
+/**
+ * A loader class designed to parse Doom 3 AAS Files.
+ */
+class Doom3AasFileLoader :
+    public IAasFileLoader,
+    public std::enable_shared_from_this<Doom3AasFileLoader>
+{
+public:
+    virtual const std::string& getAasFormatName() const override;
+	virtual const std::string& getGameType() const override;
+
+	virtual bool canLoad(std::istream& stream) const override;
+    virtual IAasFilePtr loadFromStream(std::istream& stream) override;
+
+    // RegisterableModule implementation
+	virtual const std::string& getName() const override;
+	virtual const StringSet& getDependencies() const override;
+	virtual void initialiseModule(const ApplicationContext& ctx) override;
+	virtual void shutdownModule() override;
+
+private:
+    // Parses the file header, throws exception on failure
+    void parseVersion(parser::DefTokeniser& tok) const;
+};
+
+}
\ No newline at end of file
diff --git a/plugins/mapdoom3/aas/Doom3AasFileSettings.cpp b/plugins/mapdoom3/aas/Doom3AasFileSettings.cpp
new file mode 100644
index 0000000..915e35c
--- /dev/null
+++ b/plugins/mapdoom3/aas/Doom3AasFileSettings.cpp
@@ -0,0 +1,162 @@
+#include "Doom3AasFileSettings.h"
+
+#include "string/convert.h"
+#include <boost/algorithm/string/trim.hpp>
+#include "Util.h"
+
+namespace map
+{
+
+Doom3AasFileSettings::Doom3AasFileSettings() :
+    numBoundingBoxes(1),
+    usePatches(false),
+    writeBrushMap(false),
+    playerFlood(false),
+    noOptimize(false),
+    allowSwimReachabilities(false),
+    allowFlyReachabilities(false),
+	fileExtension("aas48"),
+	gravity(0, 0, -1066),
+	gravityDir(gravity.getNormalised()),
+    gravityValue(gravity.getLength()),
+	invGravityDir(-gravityDir),
+	maxStepHeight(14.0f),
+	maxBarrierHeight(32.0f),
+	maxWaterJumpHeight(20.0f),
+	maxFallHeight(64.0f),
+	minFloorCos(0.7f),
+	tt_barrierJump(100),
+	tt_startCrouching(100),
+	tt_waterJump(100),
+	tt_startWalkOffLedge(100)
+{
+    boundingBoxes[0] = AABB::createFromMinMax(Vector3(-16, -16, 0), Vector3(16, 16, 72));
+}
+
+void Doom3AasFileSettings::parseFromTokens(parser::DefTokeniser& tok)
+{
+    tok.assertNextToken("{");
+
+    while (tok.hasMoreTokens())
+    {
+        std::string token = tok.nextToken();
+
+        if (token == "}")
+        {
+            break;
+        }
+        else if (token == "bboxes")
+        {
+            // Parse bboxes
+            tok.assertNextToken("{");
+
+            std::size_t index = 0;
+
+            while (tok.hasMoreTokens() && index < MAX_AAS_BOUNDING_BOXES)
+            {
+                if (tok.peek() == "}")
+                {
+                    tok.nextToken();
+                    break;
+                }
+
+                // Parse bbox
+                boundingBoxes[index].origin = parseVector3(tok);
+                tok.assertNextToken("-");
+                boundingBoxes[index].extents = parseVector3(tok);
+
+                ++index;
+            }
+        }
+        else if (token == "usePatches")
+        {
+            tok.assertNextToken("=");
+            usePatches = string::convert<bool>(tok.nextToken());
+        }
+        else if (token == "writeBrushMap")
+        {
+            tok.assertNextToken("=");
+            writeBrushMap = string::convert<bool>(tok.nextToken());
+        }
+        else if (token == "playerFlood")
+        {
+            tok.assertNextToken("=");
+            playerFlood = string::convert<bool>(tok.nextToken());
+        }
+        else if (token == "allowSwimReachabilities")
+        {
+            tok.assertNextToken("=");
+            allowSwimReachabilities = string::convert<bool>(tok.nextToken());
+        }
+        else if (token == "allowFlyReachabilities")
+        {
+            tok.assertNextToken("=");
+            allowFlyReachabilities = string::convert<bool>(tok.nextToken());
+        }
+        else if (token == "fileExtension")
+        {
+            tok.assertNextToken("=");
+            fileExtension = boost::algorithm::trim_copy_if(tok.nextToken(), boost::algorithm::is_any_of("\""));
+        }
+        else if (token == "gravity")
+        {
+            tok.assertNextToken("=");
+            gravity = parseVector3(tok);
+
+            gravityDir = gravity.getNormalised();
+			gravityValue = gravity.getLength();
+			invGravityDir = -gravityDir;
+        }
+        else if (token == "maxStepHeight")
+        {
+            tok.assertNextToken("=");
+            maxStepHeight = string::convert<float>(tok.nextToken());
+        }
+        else if (token == "maxBarrierHeight")
+        {
+            tok.assertNextToken("=");
+            maxBarrierHeight = string::convert<float>(tok.nextToken());
+        }
+        else if (token == "maxWaterJumpHeight")
+        {
+            tok.assertNextToken("=");
+            maxWaterJumpHeight = string::convert<float>(tok.nextToken());
+        }
+        else if (token == "maxFallHeight")
+        {
+            tok.assertNextToken("=");
+            maxFallHeight = string::convert<float>(tok.nextToken());
+        }
+        else if (token == "minFloorCos")
+        {
+            tok.assertNextToken("=");
+            minFloorCos = string::convert<float>(tok.nextToken());
+        }
+        else if (token == "tt_barrierJump")
+        {
+            tok.assertNextToken("=");
+            tt_barrierJump = string::convert<int>(tok.nextToken());
+        }
+        else if (token == "tt_startCrouching")
+        {
+            tok.assertNextToken("=");
+            tt_startCrouching = string::convert<int>(tok.nextToken());
+        }
+        else if (token == "tt_waterJump")
+        {
+            tok.assertNextToken("=");
+            tt_waterJump = string::convert<int>(tok.nextToken());
+        }
+        else if (token == "tt_startWalkOffLedge")
+        {
+            tok.assertNextToken("=");
+            tt_startWalkOffLedge = string::convert<int>(tok.nextToken());
+        }
+        else
+        {
+            throw parser::ParseException("Unknown settings token: " + token);
+        }
+    }
+}
+
+}
diff --git a/plugins/mapdoom3/aas/Doom3AasFileSettings.h b/plugins/mapdoom3/aas/Doom3AasFileSettings.h
new file mode 100644
index 0000000..d94ce98
--- /dev/null
+++ b/plugins/mapdoom3/aas/Doom3AasFileSettings.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "math/AABB.h"
+#include "math/Vector3.h"
+#include "parser/DefTokeniser.h"
+
+namespace map
+{
+
+#define MAX_AAS_BOUNDING_BOXES 4
+
+class Doom3AasFileSettings
+{
+public:
+    Doom3AasFileSettings();
+
+    int         numBoundingBoxes;
+	AABB        boundingBoxes[MAX_AAS_BOUNDING_BOXES];
+	bool        usePatches;
+	bool        writeBrushMap;
+	bool        playerFlood;
+	bool        noOptimize;
+	bool        allowSwimReachabilities;
+	bool        allowFlyReachabilities;
+	std::string fileExtension;
+								// physics settings
+	Vector3     gravity;
+	Vector3     gravityDir;
+	Vector3     invGravityDir;
+	float       gravityValue;
+	float       maxStepHeight;
+	float       maxBarrierHeight;
+	float       maxWaterJumpHeight;
+	float       maxFallHeight;
+	float       minFloorCos;
+			
+    // fixed travel times
+	int		    tt_barrierJump;
+	int		    tt_startCrouching;
+	int		    tt_waterJump;
+	int		    tt_startWalkOffLedge;
+
+    // Parse from token stream. The opening "settings" token should already have been consumed
+    void parseFromTokens(parser::DefTokeniser& tok);
+};
+
+}
diff --git a/plugins/mapdoom3/aas/Util.h b/plugins/mapdoom3/aas/Util.h
new file mode 100644
index 0000000..1ca69d1
--- /dev/null
+++ b/plugins/mapdoom3/aas/Util.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "math/Vector3.h"
+#include "parser/DefTokeniser.h"
+#include "string/convert.h"
+
+namespace map
+{
+    inline Vector3 parseVector3(parser::DefTokeniser& tok)
+    {
+        Vector3 vec;
+
+        tok.assertNextToken("(");
+        vec[0] = string::convert<Vector3::ElementType>(tok.nextToken());
+        vec[1] = string::convert<Vector3::ElementType>(tok.nextToken());
+        vec[2] = string::convert<Vector3::ElementType>(tok.nextToken());
+        tok.assertNextToken(")");
+
+        return vec;
+    }
+}
diff --git a/plugins/mapdoom3/compiler/DebugRenderer.h b/plugins/mapdoom3/compiler/DebugRenderer.h
index e63fb43..478ae9c 100644
--- a/plugins/mapdoom3/compiler/DebugRenderer.h
+++ b/plugins/mapdoom3/compiler/DebugRenderer.h
@@ -96,7 +96,7 @@ public:
 	{}
 
 	// Renderable implementation
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override
 	{
 		if (!_procFile) return;
 
@@ -118,17 +118,17 @@ public:
 		collector.addRenderable(*this, Matrix4::getIdentity());
 	}
 
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override
 	{
 		renderSolid(collector, volume);
 	}
 
-	void setRenderSystem(const RenderSystemPtr& renderSystem)
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override
 	{}
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // never highlighted
+		return Highlight::NoHighlight; // never highlighted
 	}
 
 	void setActiveNode(int nodeId)
diff --git a/plugins/mapdoom3/compiler/Doom3MapCompiler.cpp b/plugins/mapdoom3/compiler/Doom3MapCompiler.cpp
index e417443..861b3ee 100644
--- a/plugins/mapdoom3/compiler/Doom3MapCompiler.cpp
+++ b/plugins/mapdoom3/compiler/Doom3MapCompiler.cpp
@@ -8,6 +8,7 @@
 
 #include <functional>
 #include <boost/format.hpp>
+#include <boost/filesystem.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 
 #include "os/path.h"
@@ -36,10 +37,10 @@ namespace map
 			}
 
 			// Renderable implementation (empty)
-			void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
+			void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override
 			{}
 
-			void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
+			void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override
 			{}
 
 			const AABB& localAABB() const
@@ -47,9 +48,9 @@ namespace map
 				return _emptyAABB;
 			}
 
-			bool isHighlighted() const
+			std::size_t getHighlightFlags() override
 			{
-				return false; // never highlighted
+				return Highlight::NoHighlight; // never highlighted
 			}
 		};
 
@@ -109,7 +110,7 @@ void Doom3MapCompiler::runDmap(const scene::INodePtr& root)
 
 void Doom3MapCompiler::runDmap(const std::string& mapFile)
 {
-	if (!os::fileOrDirExists(mapFile) || file_is_directory(mapFile.c_str()))
+	if (!os::fileOrDirExists(mapFile) || boost::filesystem::is_directory(mapFile))
 	{
 		rError() << "Can't dmap, file doesn't exist: " << mapFile << std::endl;
 		return;
diff --git a/plugins/mapdoom3/compiler/OptIsland.cpp b/plugins/mapdoom3/compiler/OptIsland.cpp
index 680488a..3a7b11d 100644
--- a/plugins/mapdoom3/compiler/OptIsland.cpp
+++ b/plugins/mapdoom3/compiler/OptIsland.cpp
@@ -677,7 +677,7 @@ void OptIsland::removeIfColinear(OptVertex* ov)
 
 	// see if they are colinear
 	Vector3 dir1 = v3->v.vertex - v1->v.vertex;
-	float len = dir1.normalise();
+	dir1.normalise();
 
 	Vector3 dir2 = v2->v.vertex - v1->v.vertex;
 
diff --git a/plugins/mapdoom3/compiler/ProcCompiler.cpp b/plugins/mapdoom3/compiler/ProcCompiler.cpp
index ed0270c..2b1dc72 100644
--- a/plugins/mapdoom3/compiler/ProcCompiler.cpp
+++ b/plugins/mapdoom3/compiler/ProcCompiler.cpp
@@ -159,7 +159,7 @@ public:
 
             ProcPatch surface(*patch);
 
-            if (patch->subdivionsFixed())
+            if (patch->subdivisionsFixed())
             {
                 surface.subdivideExplicit(patch->getSubdivisions(), true);
             }
@@ -326,8 +326,6 @@ private:
 
         _buildBrush.contentShader = firstSide.material;
         
-        bool mixed = false;
-
         // a brush is only opaque if all sides are opaque
         _buildBrush.opaque = true;
 
@@ -344,7 +342,6 @@ private:
 
             if (flags != contents)
             {
-                mixed = true;
                 contents |= flags;
             }
 
@@ -4503,7 +4500,7 @@ Surface ProcCompiler::createShadowVolume(const Matrix4& transform, const Surface
     bool overflowed = false;
     _indexFrustumNumber = 0;
     int capPlaneBits = 0;
-    bool callOptimizer = (optimize == SG_OFFLINE);
+    //bool callOptimizer = (optimize == SG_OFFLINE);
 
     // the facing information will be the same for all six projections
     // from a point light, as well as for any directed lights
diff --git a/plugins/mapdoom3/compiler/ProcPatch.cpp b/plugins/mapdoom3/compiler/ProcPatch.cpp
index 26cd33e..bb017e0 100644
--- a/plugins/mapdoom3/compiler/ProcPatch.cpp
+++ b/plugins/mapdoom3/compiler/ProcPatch.cpp
@@ -656,7 +656,6 @@ void ProcPatch::generateNormals()
 	{
 		for (int j = 0; j < _height; ++j)
 		{
-			std::size_t count = 0;
 			const Vector3& base = _vertices[j * _width + i].vertex;
 
 			for (std::size_t k = 0; k < 8; ++k)
@@ -730,13 +729,6 @@ void ProcPatch::generateNormals()
 				}
 
 				sum += norm;
-				count++;
-			}
-
-			if (count == 0)
-			{
-				//idLib::common->Printf("bad normal\n");
-				count = 1;
 			}
 
 			_vertices[j * _width + i].normal = sum;
diff --git a/plugins/mapdoom3/mapdoom3.cpp b/plugins/mapdoom3/mapdoom3.cpp
index 6c4e421..20537c1 100644
--- a/plugins/mapdoom3/mapdoom3.cpp
+++ b/plugins/mapdoom3/mapdoom3.cpp
@@ -3,6 +3,7 @@
 #include "Quake4MapFormat.h"
 #include "Quake3MapFormat.h"
 #include "compiler/Doom3MapCompiler.h"
+#include "aas/Doom3AasFileLoader.h"
 
 #include "imapformat.h"
 #include "itextstream.h"
@@ -10,12 +11,13 @@
 
 extern "C" void DARKRADIANT_DLLEXPORT RegisterModule(IModuleRegistry& registry)
 {
-	registry.registerModule(map::Doom3MapFormatPtr(new map::Doom3MapFormat));
-	registry.registerModule(map::Quake4MapFormatPtr(new map::Quake4MapFormat));
-	registry.registerModule(map::Doom3PrefabFormatPtr(new map::Doom3PrefabFormat));
-	registry.registerModule(map::Doom3MapCompilerPtr(new map::Doom3MapCompiler));
-	registry.registerModule(map::Quake3MapFormatPtr(new map::Quake3MapFormat));
-	
+	registry.registerModule(std::make_shared<map::Doom3MapFormat>());
+	registry.registerModule(std::make_shared<map::Quake4MapFormat>());
+	registry.registerModule(std::make_shared<map::Doom3PrefabFormat>());
+	registry.registerModule(std::make_shared<map::Doom3MapCompiler>());
+	registry.registerModule(std::make_shared<map::Quake3MapFormat>());
+    registry.registerModule(std::make_shared<map::Doom3AasFileLoader>());
+
 	// Initialise the streams using the given application context
 	module::initialiseStreams(registry.getApplicationContext());
 
diff --git a/plugins/mapdoom3/primitivewriters/PatchDefExporter.h b/plugins/mapdoom3/primitivewriters/PatchDefExporter.h
index 0b34975..ba26477 100644
--- a/plugins/mapdoom3/primitivewriters/PatchDefExporter.h
+++ b/plugins/mapdoom3/primitivewriters/PatchDefExporter.h
@@ -38,7 +38,7 @@ public:
 	// Writes a patchDef2/3 definition from the given patch to the given stream
 	static void exportPatch(std::ostream& stream, const IPatch& patch)
 	{
-		if (patch.subdivionsFixed())
+		if (patch.subdivisionsFixed())
 		{
 			exportPatchDef3(stream, patch);
 		}
@@ -87,7 +87,7 @@ private:
 		stream << patch.getWidth() << " ";
 		stream << patch.getHeight() << " ";
 
-		assert(patch.subdivionsFixed());
+		assert(patch.subdivisionsFixed());
 
 		Subdivisions divisions = patch.getSubdivisions();
 		stream << divisions.x() << " ";
diff --git a/plugins/md5model/MD5ModelNode.h b/plugins/md5model/MD5ModelNode.h
index 879e7c1..c278e3d 100644
--- a/plugins/md5model/MD5ModelNode.h
+++ b/plugins/md5model/MD5ModelNode.h
@@ -59,13 +59,13 @@ public:
 	void clearLights();
 
 	// Renderable implementation
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // models are never highlighted themselves
+		return Highlight::NoHighlight; // models are never highlighted themselves
 	}
 
 	// Returns the name of the currently active skin
diff --git a/plugins/md5model/MD5Skeleton.cpp b/plugins/md5model/MD5Skeleton.cpp
index d8fad10..16a7921 100644
--- a/plugins/md5model/MD5Skeleton.cpp
+++ b/plugins/md5model/MD5Skeleton.cpp
@@ -14,10 +14,10 @@ namespace
 		Quaternion qm;
 
 		// Calculate angle between them.
-		float cosHalfTheta = qa.w() * qb.w() + qa.x() * qb.x() + qa.y() * qb.y() + qa.z() * qb.z();
+		double cosHalfTheta = qa.w() * qb.w() + qa.x() * qb.x() + qa.y() * qb.y() + qa.z() * qb.z();
 
 		// if qa=qb or qa=-qb then theta = 0 and we can return qa
-		if (abs(cosHalfTheta) > 1.0f)
+		if (abs(cosHalfTheta) > 1.0)
 		{
  			return qb;
 		}
@@ -27,7 +27,7 @@ namespace
 		// in a single frame - use this to rectify that.
 		Quaternion temp;
 
-		if (cosHalfTheta < 0.0f)
+		if (cosHalfTheta < 0.0)
 		{
 			temp = qb*(-1);
 			cosHalfTheta = -cosHalfTheta;
@@ -38,12 +38,12 @@ namespace
 		}
 
 		// Calculate temporary values.
-		float halfTheta = acos(cosHalfTheta);
-		float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta);
+		double halfTheta = acos(cosHalfTheta);
+		double sinHalfTheta = sqrt(1.0 - cosHalfTheta*cosHalfTheta);
 
 		// if theta = 180 degrees then result is not fully defined
 		// we could rotate around any axis normal to qa or qb
-		if (fabs(sinHalfTheta) < 0.006f)
+		if (fabs(sinHalfTheta) < 0.006)
 		{ 
 			qm.w() = (qa.w() * (1-fraction) + temp.w() * fraction);
 			qm.x() = (qa.x() * (1-fraction) + temp.x() * fraction);
@@ -52,8 +52,8 @@ namespace
 			return qm;
 		}
 
-		float ratioA = sin((1 - fraction) * halfTheta) / sinHalfTheta;
-		float ratioB = sin(fraction * halfTheta) / sinHalfTheta; 
+		double ratioA = sin((1 - fraction) * halfTheta) / sinHalfTheta;
+		double ratioB = sin(fraction * halfTheta) / sinHalfTheta;
 
 		//calculate Quaternion.
 		qm.w() = (qa.w() * ratioA + temp.w() * ratioB);
diff --git a/plugins/model/PicoModelNode.h b/plugins/model/PicoModelNode.h
index faa9566..b8e41d6 100644
--- a/plugins/model/PicoModelNode.h
+++ b/plugins/model/PicoModelNode.h
@@ -78,13 +78,13 @@ public:
 	void clearLights();
 
 	// Renderable implementation
-  	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+  	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // models are never highlighted themselves
+		return Highlight::NoHighlight; // models are never highlighted themselves
 	}
 
 	// Traceable implementation
diff --git a/plugins/particles/ParticleDef.cpp b/plugins/particles/ParticleDef.cpp
index 68152ec..a9df49e 100644
--- a/plugins/particles/ParticleDef.cpp
+++ b/plugins/particles/ParticleDef.cpp
@@ -59,6 +59,9 @@ void ParticleDef::copyFrom(const IParticleDef& other)
         stage->signal_changed().connect(_changedSignal.make_slot());
         _stages.push_back(stage);
     }
+
+	// We've changed all the stages, so emit the changed signal now (#4411)
+	_changedSignal.emit();
 }
 
 void ParticleDef::parseFromTokens(parser::DefTokeniser& tok)
diff --git a/plugins/particles/ParticleDef.h b/plugins/particles/ParticleDef.h
index 135beee..6d81dc5 100644
--- a/plugins/particles/ParticleDef.h
+++ b/plugins/particles/ParticleDef.h
@@ -47,12 +47,12 @@ public:
 	/**
 	 * Return the ParticleDef name.
 	 */
-	const std::string& getName() const
+	const std::string& getName() const override
 	{
 		return _name;
 	}
 
-	const std::string& getFilename() const
+	const std::string& getFilename() const override
 	{
 		return _filename;
 	}
@@ -71,42 +71,45 @@ public:
 	}
 
     // IParticleDef implementation
-    sigc::signal<void> signal_changed() const { return _changedSignal; }
+    sigc::signal<void> signal_changed() const override 
+	{ 
+		return _changedSignal;
+	}
 
-	float getDepthHack() const
+	float getDepthHack() const override
 	{
 		return _depthHack;
 	}
 
-	void setDepthHack(float value)
+	void setDepthHack(float value) override
 	{
 		_depthHack = value;
 	}
 
-	std::size_t getNumStages() const
+	std::size_t getNumStages() const override
 	{
 		return _stages.size();
 	}
 
-	const IStageDef& getStage(std::size_t stageNum) const
+	const IStageDef& getStage(std::size_t stageNum) const override
 	{
 		return *_stages[stageNum];
 	}
 
-	IStageDef& getStage(std::size_t stageNum)
+	IStageDef& getStage(std::size_t stageNum) override
 	{
 		return *_stages[stageNum];
 	}
 
-	std::size_t addParticleStage() ;
+	std::size_t addParticleStage() override;
 
-	void removeParticleStage(std::size_t index);
+	void removeParticleStage(std::size_t index) override;
 
-	void swapParticleStages(std::size_t index, std::size_t index2);
+	void swapParticleStages(std::size_t index, std::size_t index2) override;
 
 	void appendStage(const StageDefPtr& stage);
 
-	bool operator==(const IParticleDef& other) const 
+	bool operator==(const IParticleDef& other) const override
 	{
 		// Compare depth hack flag
 		if (getDepthHack() != other.getDepthHack()) return false;
@@ -124,12 +127,12 @@ public:
 		return true;
 	}
 
-	bool operator!=(const IParticleDef& other) const
+	bool operator!=(const IParticleDef& other) const override
 	{
 		return !operator==(other);
 	}
 
-	void copyFrom(const IParticleDef& other);
+	void copyFrom(const IParticleDef& other) override;
 
 	void parseFromTokens(parser::DefTokeniser& tok);
 
diff --git a/plugins/particles/ParticleNode.cpp b/plugins/particles/ParticleNode.cpp
index 3975069..46f055d 100644
--- a/plugins/particles/ParticleNode.cpp
+++ b/plugins/particles/ParticleNode.cpp
@@ -31,9 +31,9 @@ const AABB& ParticleNode::localAABB() const
 	return _renderableParticle->getBounds();
 }
 
-bool ParticleNode::isHighlighted(void) const
+std::size_t ParticleNode::getHighlightFlags()
 {
-	return false;
+	return Highlight::NoHighlight;
 }
 
 const Matrix4& ParticleNode::localToParent() const
diff --git a/plugins/particles/ParticleNode.h b/plugins/particles/ParticleNode.h
index e8ebd7d..a3680e9 100644
--- a/plugins/particles/ParticleNode.h
+++ b/plugins/particles/ParticleNode.h
@@ -33,12 +33,12 @@ public:
 
 	IRenderableParticlePtr getParticle() const;
 	const AABB& localAABB() const;
-	bool isHighlighted(void) const;
+	std::size_t getHighlightFlags() override;
 
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
 
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
 	// ITransformNode
 	const Matrix4& localToParent() const;
diff --git a/plugins/particles/ParticleQuad.h b/plugins/particles/ParticleQuad.h
index cc5742d..eb7f157 100644
--- a/plugins/particles/ParticleQuad.h
+++ b/plugins/particles/ParticleQuad.h
@@ -55,7 +55,7 @@ struct ParticleQuad
 	 *
 	 * [Optional]: s0 and sWidth are used for particle animation frames.
 	 *
-	 * @aspect: scales the horizontal coords by this factor.
+	 * @aspect: scales the vertical size of the quad by this factor.
 	 * @s0: defines the horizontal frame start coordinate in texture space (s).
 	 * @sWidth: defines the width of this frame in texture space.
 	 */
@@ -71,10 +71,10 @@ struct ParticleQuad
 			0, 0, 1, 0,
 			0, 0, 0, 1);
 
-		verts[0] = Vertex(rotation.transformPoint(Vector3(-size*aspect, +size, 0)), Vector2(s0,t0), colour, normal);
-		verts[1] = Vertex(rotation.transformPoint(Vector3(+size*aspect, +size, 0)), Vector2(s0 + sWidth,t0), colour, normal);
-		verts[2] = Vertex(rotation.transformPoint(Vector3(+size*aspect, -size, 0)), Vector2(s0 + sWidth,t0 + tWidth), colour, normal);
-		verts[3] = Vertex(rotation.transformPoint(Vector3(-size*aspect, -size, 0)), Vector2(s0,t0 + tWidth), colour, normal);
+		verts[0] = Vertex(rotation.transformPoint(Vector3(-size, +size*aspect, 0)), Vector2(s0, t0), colour, normal);
+		verts[1] = Vertex(rotation.transformPoint(Vector3(+size, +size*aspect, 0)), Vector2(s0 + sWidth, t0), colour, normal);
+		verts[2] = Vertex(rotation.transformPoint(Vector3(+size, -size*aspect, 0)), Vector2(s0 + sWidth, t0 + tWidth), colour, normal);
+		verts[3] = Vertex(rotation.transformPoint(Vector3(-size, -size*aspect, 0)), Vector2(s0,t0 + tWidth), colour, normal);
 	}
 
 	void translate(const Vector3& offset)
diff --git a/plugins/particles/ParticlesManager.cpp b/plugins/particles/ParticlesManager.cpp
index e9f26b5..fca77a9 100644
--- a/plugins/particles/ParticlesManager.cpp
+++ b/plugins/particles/ParticlesManager.cpp
@@ -290,20 +290,29 @@ void ParticlesManager::saveParticleDef(const std::string& particleName)
 
 	std::string relativePath = PARTICLES_DIR + particle->getFilename();
 
-	fs::path particlesModPath = GlobalGameManager().getModPath();
-	particlesModPath /= PARTICLES_DIR;
+	fs::path targetPath = GlobalGameManager().getModPath();
+
+	if (targetPath.empty())
+	{
+		targetPath = GlobalGameManager().getUserEnginePath();
+
+		rMessage() << "No mod base path found, falling back to user engine path to save particle file: " << 
+			targetPath.string() << std::endl;
+	}
+
+	targetPath /= PARTICLES_DIR;
 
 	// Ensure the particles folder exists
-	fs::create_directories(particlesModPath);
+	fs::create_directories(targetPath);
 
-	fs::path targetFile = particlesModPath / particle->getFilename();
+	fs::path targetFile = targetPath / particle->getFilename();
 
 	// If the file doesn't exist yet, let's check if we need to inherit stuff first from the VFS
 	if (!fs::exists(targetFile))
 	{
 		ArchiveTextFilePtr inheritFile = GlobalFileSystem().openTextFile(relativePath);
 
-		if (inheritFile != NULL)
+		if (inheritFile)
 		{
 			// There is a file with that name already in the VFS, copy it to the target file
 			TextInputStream& inheritStream = inheritFile->getInputStream();
diff --git a/plugins/particles/RenderableParticle.h b/plugins/particles/RenderableParticle.h
index b3d868c..6c73280 100644
--- a/plugins/particles/RenderableParticle.h
+++ b/plugins/particles/RenderableParticle.h
@@ -64,19 +64,19 @@ public:
 	void update(const Matrix4& viewRotation);
 
 	// Front-end render methods
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
 	void renderSolid(RenderableCollector& collector, const VolumeTest& volume, 
 					 const Matrix4& localToWorld, const IRenderEntity* entity) const;
 
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
 	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume, 
 						 const Matrix4& localToWorld, const IRenderEntity* entity) const;
 
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // never highlighted
+		return Highlight::NoHighlight; // never highlighted
 	}
 
 	const IParticleDefPtr& getParticleDef() const;
diff --git a/plugins/particles/RenderableParticleBunch.cpp b/plugins/particles/RenderableParticleBunch.cpp
index d5bc34f..ac97da1 100644
--- a/plugins/particles/RenderableParticleBunch.cpp
+++ b/plugins/particles/RenderableParticleBunch.cpp
@@ -197,7 +197,7 @@ Matrix4 RenderableParticleBunch::getAimedMatrix(const Vector3& particleVelocity)
     Vector3 z = object2Vel.z().getVector3();
 
     // The particle needs to be rotated by this angle around the velocity axis
-    float aimedAngle = z.angle(-viewProj);
+    double aimedAngle = z.angle(-viewProj);
 
     // Use the cross to check whether to rotate in negative or positive direction
     if (z.crossProduct(-viewProj).dot(vel) > 0)
@@ -609,8 +609,7 @@ void RenderableParticleBunch::pushAimedParticles(ParticleRenderInfo& particle, s
 
         float height = static_cast<float>(velocity.getLength());
 
-        aimedParticle.aspect = 2 * aimedParticle.size / height;
-        aimedParticle.size = height * 0.5f;
+        aimedParticle.aspect = height / (2 * aimedParticle.size);
 
         // Calculate the vertical texture coordinates
         aimedParticle.tWidth = 1.0f / static_cast<float>(numQuads);
diff --git a/plugins/particles/StageDef.cpp b/plugins/particles/StageDef.cpp
index d6952a4..8a4d4fd 100644
--- a/plugins/particles/StageDef.cpp
+++ b/plugins/particles/StageDef.cpp
@@ -159,28 +159,28 @@ void StageDef::copyFrom(const IStageDef& other)
 	setOffset(other.getOffset());
 	setOrientationType(other.getOrientationType());
 
-	for (int i = 0; i < 3; ++i)
+	for (int i = 0; i < 4; ++i)
 	{
 		setOrientationParm(i, other.getOrientationParm(i));
 	}
 
 	setDistributionType(other.getDistributionType());
 
-	for (int i = 0; i < 3; ++i)
+	for (int i = 0; i < 4; ++i)
 	{
 		setDistributionParm(i, other.getDistributionParm(i));
 	}
 
 	setDirectionType(other.getDirectionType());
 
-	for (int i = 0; i < 3; ++i)
+	for (int i = 0; i < 4; ++i)
 	{
 		setDirectionParm(i, other.getDirectionParm(i));
 	}
 
 	setCustomPathType(other.getCustomPathType());
 
-	for (int i = 0; i < 7; ++i)
+	for (int i = 0; i < 8; ++i)
 	{
 		setCustomPathParm(i, other.getCustomPathParm(i));
 	}
@@ -604,7 +604,8 @@ std::ostream& operator<<(std::ostream& stream, const StageDef& stage)
 	case StageDef::DISTRIBUTION_SPHERE:
 		stream << "sphere " << stage.getDistributionParm(0) << " "
 							<< stage.getDistributionParm(1) << " "
-							<< stage.getDistributionParm(2) << std::endl;
+							<< stage.getDistributionParm(2) << " "
+							<< stage.getDistributionParm(3) << std::endl;
 		break;
 	};
 
@@ -743,28 +744,28 @@ bool StageDef::operator==(const IStageDef& other) const
     if (getOffset() != other.getOffset()) return false;
     if (getOrientationType() != other.getOrientationType()) return false;
 
-    for (int i = 0; i < 3; ++i)
+    for (int i = 0; i < 4; ++i)
     {
         if (getOrientationParm(i) != other.getOrientationParm(i)) return false;
     }
 
     if (getDistributionType() != other.getDistributionType()) return false;
 
-    for (int i = 0; i < 3; ++i)
+    for (int i = 0; i < 4; ++i)
     {
         if (getDistributionParm(i) != other.getDistributionParm(i)) return false;
     }
 
     if (getDirectionType() != other.getDirectionType()) return false;
 
-    for (int i = 0; i < 3; ++i)
+    for (int i = 0; i < 4; ++i)
     {
         if (getDirectionParm(i) != other.getDirectionParm(i)) return false;
     }
 
     if (getCustomPathType() != other.getCustomPathType()) return false;
 
-    for (int i = 0; i < 7; ++i)
+    for (int i = 0; i < 8; ++i)
     {
         if (getCustomPathParm(i) != other.getCustomPathParm(i)) return false;
     }
diff --git a/plugins/script/ScriptingSystem.cpp b/plugins/script/ScriptingSystem.cpp
index fb1b3ec..96e8f97 100644
--- a/plugins/script/ScriptingSystem.cpp
+++ b/plugins/script/ScriptingSystem.cpp
@@ -195,7 +195,7 @@ void ScriptingSystem::initialise()
 	page->page = new ScriptWindow(GlobalMainFrame().getWxTopLevelWindow());
 	page->tabIcon = "icon_script.png";
 	page->tabLabel = _("Script");
-	page->insertBefore = "console";
+	page->position = IGroupDialog::Page::Position::Console - 10; // insert before console
 
 	GlobalGroupDialog().addPage(page);
 }
diff --git a/plugins/script/interfaces/PatchInterface.cpp b/plugins/script/interfaces/PatchInterface.cpp
index 3eead2e..7fc27b1 100644
--- a/plugins/script/interfaces/PatchInterface.cpp
+++ b/plugins/script/interfaces/PatchInterface.cpp
@@ -168,12 +168,12 @@ public:
 		return patchNode->getPatch().hasVisibleMaterial();
 	}
 
-	bool subdivionsFixed() const
+	bool subdivisionsFixed() const
 	{
 		IPatchNodePtr patchNode = std::dynamic_pointer_cast<IPatchNode>(_node.lock());
 		if (patchNode == NULL) return false;
 
-		return patchNode->getPatch().subdivionsFixed();
+		return patchNode->getPatch().subdivisionsFixed();
 	}
 
 	Subdivisions getSubdivisions() const
@@ -274,7 +274,8 @@ void PatchInterface::registerInterface(boost::python::object& nspace) {
 			boost::python::return_value_policy<boost::python::copy_const_reference>())
 		.def("setShader", &ScriptPatchNode::setShader)
 		.def("hasVisibleMaterial", &ScriptPatchNode::hasVisibleMaterial)
-		.def("subdivionsFixed", &ScriptPatchNode::subdivionsFixed)
+		.def("subdivionsFixed", &ScriptPatchNode::subdivisionsFixed) // typo used to be there in previous releases, leave it in there for compatibility reasons
+		.def("subdivisionsFixed", &ScriptPatchNode::subdivisionsFixed)
 		.def("getSubdivisions", &ScriptPatchNode::getSubdivisions)
 		.def("setFixedSubdivisions", &ScriptPatchNode::setFixedSubdivisions)
 		.def("controlPointsChanged", &ScriptPatchNode::controlPointsChanged)
diff --git a/plugins/script/interfaces/SceneGraphInterface.h b/plugins/script/interfaces/SceneGraphInterface.h
index eecb6e0..47de454 100644
--- a/plugins/script/interfaces/SceneGraphInterface.h
+++ b/plugins/script/interfaces/SceneGraphInterface.h
@@ -83,7 +83,7 @@ public:
 		scene::INodePtr node = _node.lock();
 		if (node == NULL) return false;
 
-		SelectablePtr selectable = Node_getSelectable(node);
+		ISelectablePtr selectable = Node_getSelectable(node);
 
 		return (selectable != NULL) ? selectable->isSelected() : false;
 	}
@@ -92,21 +92,21 @@ public:
 		scene::INodePtr node = _node.lock();
 		if (node == NULL) return;
 
-		SelectablePtr selectable = Node_getSelectable(node);
+		ISelectablePtr selectable = Node_getSelectable(node);
 
 		if (selectable != NULL) {
 			selectable->setSelected(selected);
 		}
 	}
 
-	void invertSelected(bool selected) {
+	void invertSelected() {
 		scene::INodePtr node = _node.lock();
 		if (node == NULL) return;
 
-		SelectablePtr selectable = Node_getSelectable(node);
+		ISelectablePtr selectable = Node_getSelectable(node);
 
 		if (selectable != NULL) {
-			selectable->invertSelected();
+			selectable->setSelected(!selectable->isSelected());
 		}
 	}
 };
diff --git a/plugins/shaders/Doom3ShaderSystem.cpp b/plugins/shaders/Doom3ShaderSystem.cpp
index 989ccbb..862a8fb 100644
--- a/plugins/shaders/Doom3ShaderSystem.cpp
+++ b/plugins/shaders/Doom3ShaderSystem.cpp
@@ -164,8 +164,10 @@ void Doom3ShaderSystem::refresh() {
 }
 
 // Is the shader system realised
-bool Doom3ShaderSystem::isRealised() {
-	return _realised;
+bool Doom3ShaderSystem::isRealised()
+{
+	// Don't report true until we have at least some definitions loaded
+	return _realised && _library->getNumDefinitions() > 0;
 }
 
 // Return a shader by name
diff --git a/plugins/shaders/textures/TextureManipulator.cpp b/plugins/shaders/textures/TextureManipulator.cpp
index c9bc78a..2b98ce2 100644
--- a/plugins/shaders/textures/TextureManipulator.cpp
+++ b/plugins/shaders/textures/TextureManipulator.cpp
@@ -544,8 +544,9 @@ void TextureManipulator::mipReduce(byte *in, byte *out,
 
 /* greebo: This gets called by the preference system and is responsible for adding the
  * according pages and elements to the preference dialog.*/
-void TextureManipulator::constructPreferences() {
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage("Settings/Textures");
+void TextureManipulator::constructPreferences()
+{
+	IPreferencePage& page = GlobalPreferenceSystem().getPage("Settings/Textures");
 
 	// Create the string list containing the quality captions
 	std::list<std::string> percentages;
@@ -555,10 +556,10 @@ void TextureManipulator::constructPreferences() {
 	percentages.push_back("50%");
 	percentages.push_back("100%");
 
-	page->appendCombo("Texture Quality", RKEY_TEXTURES_QUALITY, percentages);
+	page.appendCombo("Texture Quality", RKEY_TEXTURES_QUALITY, percentages);
 
 	// Texture Gamma Settings
-	page->appendSpinner("Texture Gamma", RKEY_TEXTURES_GAMMA, 0.0f, 1.0f, 10);
+	page.appendSpinner("Texture Gamma", RKEY_TEXTURES_GAMMA, 0.0f, 1.0f, 10);
 }
 
 } // namespace shaders
diff --git a/plugins/sound/WavFileLoader.h b/plugins/sound/WavFileLoader.h
index a44b8ce..06a666e 100644
--- a/plugins/sound/WavFileLoader.h
+++ b/plugins/sound/WavFileLoader.h
@@ -87,36 +87,36 @@ public:
 		unsigned short bps = 0;
 		stream.read(reinterpret_cast<byte*>(&bps), 2);
 
-		int bufferSize = 0;
+		//int bufferSize = 0;
 
 		if (channels == 1) {
 			if (bps == 8) {
 				format = AL_FORMAT_MONO8;
 				// Set BufferSize to 250ms (Frequency divided by 4 (quarter of a second))
-				bufferSize = freq / 4;
+				//bufferSize = freq / 4;
 			}
 			else {
 				format = AL_FORMAT_MONO16;
 				// Set BufferSize to 250ms (Frequency * 2 (16bit) divided by 4 (quarter of a second))
-				bufferSize = freq >> 1;
+				//bufferSize = freq >> 1;
 				// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
-				bufferSize -= (bufferSize % 2);
+				//bufferSize -= (bufferSize % 2);
 			}
 		}
 		else {
 			if (bps == 8) {
 				format = AL_FORMAT_STEREO16;
 				// Set BufferSize to 250ms (Frequency * 2 (8bit stereo) divided by 4 (quarter of a second))
-				bufferSize = freq >> 1;
+				//bufferSize = freq >> 1;
 				// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
-				bufferSize -= (bufferSize % 2);
+				//bufferSize -= (bufferSize % 2);
 			}
 			else {
 				format = AL_FORMAT_STEREO16;
 				// Set BufferSize to 250ms (Frequency * 4 (16bit stereo) divided by 4 (quarter of a second))
-				bufferSize = freq;
+				//bufferSize = freq;
 				// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
-				bufferSize -= (bufferSize % 4);
+				//bufferSize -= (bufferSize % 4);
 			}
 		}
 
diff --git a/plugins/uimanager/GroupDialog.cpp b/plugins/uimanager/GroupDialog.cpp
index e74871c..d618f79 100644
--- a/plugins/uimanager/GroupDialog.cpp
+++ b/plugins/uimanager/GroupDialog.cpp
@@ -107,12 +107,12 @@ std::string GroupDialog::getPageName()
 	wxWindow* curPage = getPage();
 
 	// Now cycle through the list of pages and find the matching one
-	for (std::size_t i = 0; i < _pages.size(); i++)
+	for (Pages::value_type& i : _pages)
 	{
-		if (_pages[i].page == curPage)
+		if (i.second.page == curPage)
 		{
 			// Found page. Set it to active if it is not already active.
-			return _pages[i].name;
+			return i.second.name;
 		}
 	}
 
@@ -127,14 +127,14 @@ void GroupDialog::setPage(const std::string& name)
 	setPage(_pages[0].page);
 
 	// Now search for the correct page.
-	for (std::size_t i = 0; i < _pages.size(); i++)
+	for (Pages::value_type& i : _pages)
 	{
-		if (_pages[i].name == name)
+		if (i.second.name == name)
 		{
 			// Found page. Set it to active if it is not already active.
-			if (_pages[i].page != NULL && getPage() != _pages[i].page)
+			if (i.second.page != nullptr && getPage() != i.second.page)
 			{
-				setPage(_pages[i].page);
+				setPage(i.second.page);
 			}
 
 			// Show the window if the notebook is hosted here
@@ -151,7 +151,14 @@ void GroupDialog::setPage(const std::string& name)
 
 void GroupDialog::setPage(wxWindow* page)
 {
-	_notebook->SetSelection(_notebook->FindPage(page));
+	if (page == nullptr) return;
+
+	int pageIndex = _notebook->FindPage(page);
+
+	if (pageIndex != wxNOT_FOUND)
+	{
+		_notebook->SetSelection(pageIndex);
+	}
 }
 
 void GroupDialog::togglePage(const std::string& name)
@@ -246,30 +253,36 @@ wxWindow* GroupDialog::addPage(const PagePtr& page)
 	int imageId = page->tabIcon.empty() ? -1 : 
 		_imageList->Add(wxArtProvider::GetBitmap(LocalBitmapArtProvider::ArtIdPrefix() + page->tabIcon));
 	
-	// Create the notebook page
-	size_t position = _notebook->GetPageCount();
-	Pages::iterator insertIter = _pages.end();
+	// Handle position conflicts first
+	Pages::const_iterator conflictingPage = _pages.find(page->position);
 
-	if (!page->insertBefore.empty())
+	// Move back one position until we find a free slot
+	while (conflictingPage != _pages.end())
 	{
-		// Find the page with that name
-		for (Pages::iterator i = _pages.begin(); i != _pages.end(); ++i)
-        {
-			// Skip the wrong ones
-			if (i->name != page->insertBefore) continue;
+		page->position = conflictingPage->second.position + 1;
 
+		conflictingPage = _pages.find(page->position);
+	}
+
+	// Create the notebook page
+	size_t insertPosition = _notebook->GetPageCount();
+
+	// Find a page with a higher position value and sort the incoming one to the left
+	for (Pages::value_type existing : _pages)
+	{
+		if (page->position < existing.second.position)
+		{
 			// Found, extract the tab position and break the loop
-			position = _notebook->FindPage(i->page);
-			insertIter = i;
+			insertPosition = _notebook->FindPage(existing.second.page);
 			break;
 		}
 	}
 
 	page->page->Reparent(_notebook.get());
-	_notebook->InsertPage(position, page->page, page->tabLabel, false, imageId);
+	_notebook->InsertPage(insertPosition, page->page, page->tabLabel, false, imageId);
 
 	// Add this page by copy to the local list
-	_pages.insert(insertIter, Page(*page));
+	_pages.insert(std::make_pair(page->position, Page(*page)));
 
 	return page->page;
 }
@@ -280,10 +293,10 @@ void GroupDialog::removePage(const std::string& name)
 	for (Pages::iterator i = _pages.begin(); i != _pages.end(); ++i)
 	{
 		// Skip the wrong ones
-		if (i->name != name) continue;
+		if (i->second.name != name) continue;
 
 		// Remove the page from the notebook
-		_notebook->DeletePage(_notebook->FindPage(i->page));
+		_notebook->DeletePage(_notebook->FindPage(i->second.page));
 
 		// Remove the page and break the loop, iterators are invalid
 		_pages.erase(i);
diff --git a/plugins/uimanager/GroupDialog.h b/plugins/uimanager/GroupDialog.h
index f0079f0..44b480e 100644
--- a/plugins/uimanager/GroupDialog.h
+++ b/plugins/uimanager/GroupDialog.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <memory>
+#include <map>
 #include "iradiant.h"
 #include "igroupdialog.h"
 #include "wxutil/window/TransientWindow.h"
@@ -35,7 +36,8 @@ class GroupDialog :
 	public IGroupDialog
 {
 private:
-	typedef std::vector<Page> Pages;
+	// Pages, sorted by position
+	typedef std::map<int, Page> Pages;
 
 	// The actual list instance
 	Pages _pages;
diff --git a/plugins/uimanager/Makefile.am b/plugins/uimanager/Makefile.am
index b625ca8..73ecaf5 100644
--- a/plugins/uimanager/Makefile.am
+++ b/plugins/uimanager/Makefile.am
@@ -7,6 +7,8 @@ modules_LTLIBRARIES = uimanager.la
 uimanager_la_LDFLAGS = -module -avoid-version \
                        $(WX_LIBS) \
                        $(XML_LIBS) \
+                       $(BOOST_FILESYSTEM_LIBS) \
+                       $(BOOST_SYSTEM_LIBS) \
                        $(GLEW_LIBS) \
                        $(GL_LIBS)
 uimanager_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \
diff --git a/plugins/uimanager/Makefile.in b/plugins/uimanager/Makefile.in
index d48eecf..54d1196 100644
--- a/plugins/uimanager/Makefile.in
+++ b/plugins/uimanager/Makefile.in
@@ -412,6 +412,8 @@ modules_LTLIBRARIES = uimanager.la
 uimanager_la_LDFLAGS = -module -avoid-version \
                        $(WX_LIBS) \
                        $(XML_LIBS) \
+                       $(BOOST_FILESYSTEM_LIBS) \
+                       $(BOOST_SYSTEM_LIBS) \
                        $(GLEW_LIBS) \
                        $(GL_LIBS)
 
diff --git a/plugins/uimanager/MenuManager.cpp b/plugins/uimanager/MenuManager.cpp
index 7bb34f0..524cd57 100644
--- a/plugins/uimanager/MenuManager.cpp
+++ b/plugins/uimanager/MenuManager.cpp
@@ -378,7 +378,7 @@ void MenuManager::remove(const std::string& path)
 	else if (parent->getType() == menuBar)
 	{
 		wxMenuBar* parentBar = static_cast<wxMenuBar*>(parent->getWidget());
-		wxMenu* menu = static_cast<wxMenu*>(item->getWidget());
+		static_cast<wxMenu*>(item->getWidget());
 
 		int oldPosition = parent->getMenuPosition(item);
 
@@ -388,7 +388,7 @@ void MenuManager::remove(const std::string& path)
 			item->removeAllChildren();
 			item->setWidget(NULL);
 
-			menu = parentBar->Remove(oldPosition);
+			wxMenu* menu = parentBar->Remove(oldPosition);
 			delete menu;
 		}
 		else
diff --git a/plugins/uimanager/StatusBarManager.cpp b/plugins/uimanager/StatusBarManager.cpp
index d0932a1..6d9cdaa 100644
--- a/plugins/uimanager/StatusBarManager.cpp
+++ b/plugins/uimanager/StatusBarManager.cpp
@@ -57,7 +57,8 @@ wxWindow* StatusBarManager::getElement(const std::string& name)
 	return (found != _elements.end()) ? found->second->toplevel : NULL;
 }
 
-void StatusBarManager::addTextElement(const std::string& name, const std::string& icon, int pos)
+void StatusBarManager::addTextElement(const std::string& name, const std::string& icon, 
+	int pos, const std::string& description)
 {
 	// Get a free position
 	int freePos = getFreePosition(pos);
@@ -66,6 +67,11 @@ void StatusBarManager::addTextElement(const std::string& name, const std::string
 	textPanel->SetSizer(new wxBoxSizer(wxHORIZONTAL));
 	textPanel->SetName("Statusbarconainer " + name);
 
+	if (!description.empty())
+	{
+		textPanel->SetToolTip(description);
+	}
+
 	if (!icon.empty())
 	{
 		wxStaticBitmap* img = new wxStaticBitmap(textPanel, wxID_ANY, 
@@ -76,6 +82,11 @@ void StatusBarManager::addTextElement(const std::string& name, const std::string
 	wxStaticText* label = new wxStaticText(textPanel, wxID_ANY, "");
 	textPanel->GetSizer()->Add(label, 1, wxEXPAND | wxALL, 1);
 
+	if (!description.empty())
+	{
+		label->SetToolTip(description);
+	}
+
 	StatusBarElementPtr element(new StatusBarElement(textPanel, label));
 
 	// Store this element
diff --git a/plugins/uimanager/StatusBarManager.h b/plugins/uimanager/StatusBarManager.h
index fa907d0..a3cc571 100644
--- a/plugins/uimanager/StatusBarManager.h
+++ b/plugins/uimanager/StatusBarManager.h
@@ -59,7 +59,7 @@ public:
 	/**
 	 * Get the status bar widget, for packing into the main window.
 	 */
-	wxWindow* getStatusBar();
+	wxWindow* getStatusBar() override;
 
 	/**
 	 * greebo: This adds a named element to the status bar. Pass the widget
@@ -70,14 +70,14 @@ public:
 	 * @pos: the position to insert. Use POS_FRONT or POS_BACK to put the element
 	 *       at the front or back of the status bar container.
 	 */
-	void addElement(const std::string& name, wxWindow* widget, int pos);
+	void addElement(const std::string& name, wxWindow* widget, int pos) override;
 
 	/**
 	 * Returns a named status bar widget, previously added by addElement().
 	 *
 	 * @returns: NULL if the named widget does not exist.
 	 */
-	wxWindow* getElement(const std::string& name);
+	wxWindow* getElement(const std::string& name) override;
 
 	/**
 	 * greebo: A specialised method, adding a named text element.
@@ -89,19 +89,20 @@ public:
 	 * @pos: the position to insert. Use POS_FRONT or POS_BACK to put the element
 	 *       at the front or back of the status bar container.
  	 */
-	void addTextElement(const std::string& name, const std::string& icon, int pos);
+	void addTextElement(const std::string& name, const std::string& icon, int pos, 
+						const std::string& description) override;
 
 	/**
 	 * Updates the content of the named text element. The name must refer to
 	 * an element previously added by addTextElement().
 	 */
-    void setText(const std::string& name, const std::string& text, bool immediateUpdate);
+    void setText(const std::string& name, const std::string& text, bool immediateUpdate) override;
 
 	void onRadiantShutdown();
 
 protected:
 	// Gets called when the app is idle - this fills in the status text
-	void onIdle();
+	void onIdle() override;
 
 private:
 	// Returns an integer position which is not used yet.
diff --git a/plugins/uimanager/ToolbarManager.cpp b/plugins/uimanager/ToolbarManager.cpp
index b572ca3..2a00a9b 100644
--- a/plugins/uimanager/ToolbarManager.cpp
+++ b/plugins/uimanager/ToolbarManager.cpp
@@ -76,7 +76,7 @@ wxToolBarToolBase* ToolbarManager::createToolItem(wxToolBar* toolbar, xml::Node&
 		if (nodeName == "toolbutton")
 		{
 			// Create a new ToolButton and assign the right callback
-			toolItem = toolbar->AddTool(_nextToolItemId++, 
+			toolItem = toolbar->AddTool(_nextToolItemId++, name,
 				wxArtProvider::GetBitmap(LocalBitmapArtProvider::ArtIdPrefix() + icon), 
 				tooltip);
 		}
diff --git a/plugins/uimanager/UIManager.cpp b/plugins/uimanager/UIManager.cpp
index dcea218..26b5900 100644
--- a/plugins/uimanager/UIManager.cpp
+++ b/plugins/uimanager/UIManager.cpp
@@ -1,5 +1,6 @@
 #include "UIManager.h"
 
+#include "i18n.h"
 #include "itextstream.h"
 #include "iregistry.h"
 #include "iradiant.h"
@@ -110,7 +111,8 @@ void UIManager::initialiseModule(const ApplicationContext& ctx)
 	_statusBarManager.addTextElement(
 		STATUSBAR_COMMAND,
 		"",  // no icon
-		IStatusBarManager::POS_COMMAND
+		IStatusBarManager::POS_COMMAND,
+		_("Describes available Mouse Commands")
 	);
 
 	wxFileSystem::AddHandler(new wxLocalFSHandler);
diff --git a/plugins/undo/UndoSystem.cpp b/plugins/undo/UndoSystem.cpp
index 49e5939..bae5374 100644
--- a/plugins/undo/UndoSystem.cpp
+++ b/plugins/undo/UndoSystem.cpp
@@ -8,6 +8,7 @@
 #include "ieventmanager.h"
 #include "ipreferencesystem.h"
 #include "iscenegraph.h"
+#include "imap.h"
 
 #include <iostream>
 #include <map>
@@ -254,6 +255,7 @@ public:
 			_dependencies.insert(MODULE_COMMANDSYSTEM);
 			_dependencies.insert(MODULE_SCENEGRAPH);
 			_dependencies.insert(MODULE_EVENTMANAGER);
+			_dependencies.insert(MODULE_MAP);
 		}
 
 		return _dependencies;
@@ -280,6 +282,10 @@ public:
 
 		// add the preference settings
 		constructPreferences();
+
+		GlobalMapModule().signal_mapEvent().connect(
+			sigc::mem_fun(*this, &RadiantUndoSystem::onMapEvent)
+		);
 	}
 
 	// This is connected to the CommandSystem
@@ -295,6 +301,14 @@ public:
 	}
 
 private:
+	void onMapEvent(IMap::MapEvent ev)
+	{
+		if (ev == IMap::MapUnloaded)
+		{
+			clear();
+		}
+	}
+
 	// Sets the size of the undoStack
 	void setLevels(std::size_t levels) 
 	{
@@ -381,8 +395,8 @@ private:
 	// Gets called by the PreferenceSystem as request to create the according settings page
 	void constructPreferences() 
 	{
-		PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Undo System"));
-		page->appendSpinner(_("Undo Queue Size"), RKEY_UNDO_QUEUE_SIZE, 0, 1024, 1);
+		IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Undo System"));
+		page.appendSpinner(_("Undo Queue Size"), RKEY_UNDO_QUEUE_SIZE, 0, 1024, 1);
 	}
 
 }; // class RadiantUndoSystem
diff --git a/plugins/xmlregistry/Makefile.am b/plugins/xmlregistry/Makefile.am
index 8ba5726..7da2f2c 100644
--- a/plugins/xmlregistry/Makefile.am
+++ b/plugins/xmlregistry/Makefile.am
@@ -7,6 +7,10 @@ modules_LTLIBRARIES = xmlregistry.la
 
 xmlregistry_la_LIBADD = $(top_builddir)/libs/xmlutil/libxmlutil.la
 xmlregistry_la_LDFLAGS = -module -avoid-version \
-                         $(XML_LIBS) $(WX_LIBS) $(LIBSIGC_LIBS)
+                         $(XML_LIBS) \
+                         $(WX_LIBS) \
+                         $(BOOST_FILESYSTEM_LIBS) \
+                         $(BOOST_SYSTEM_LIBS) \
+                         $(LIBSIGC_LIBS)
 xmlregistry_la_SOURCES = RegistryTree.cpp XMLRegistry.cpp XMLRegistryModule.cpp
 
diff --git a/plugins/xmlregistry/Makefile.in b/plugins/xmlregistry/Makefile.in
index 093ca9f..4fe36bf 100644
--- a/plugins/xmlregistry/Makefile.in
+++ b/plugins/xmlregistry/Makefile.in
@@ -407,7 +407,11 @@ modulesdir = $(pkglibdir)/modules
 modules_LTLIBRARIES = xmlregistry.la
 xmlregistry_la_LIBADD = $(top_builddir)/libs/xmlutil/libxmlutil.la
 xmlregistry_la_LDFLAGS = -module -avoid-version \
-                         $(XML_LIBS) $(WX_LIBS) $(LIBSIGC_LIBS)
+                         $(XML_LIBS) \
+                         $(WX_LIBS) \
+                         $(BOOST_FILESYSTEM_LIBS) \
+                         $(BOOST_SYSTEM_LIBS) \
+                         $(LIBSIGC_LIBS)
 
 xmlregistry_la_SOURCES = RegistryTree.cpp XMLRegistry.cpp XMLRegistryModule.cpp
 all: all-am
diff --git a/radiant/Makefile.am b/radiant/Makefile.am
index 64b3bb6..612cb77 100644
--- a/radiant/Makefile.am
+++ b/radiant/Makefile.am
@@ -120,6 +120,7 @@ darkradiant_SOURCES = main.cpp \
                       ui/menu/FiltersMenu.cpp \
                       ui/prefdialog/PrefPage.cpp \
                       ui/prefdialog/PrefDialog.cpp \
+					  ui/prefdialog/PreferenceItem.cpp \
                       ui/transform/TransformDialog.cpp \
                       ui/modelselector/MaterialsList.cpp \
                       ui/modelselector/ModelSelector.cpp \
@@ -146,6 +147,8 @@ darkradiant_SOURCES = main.cpp \
                       ui/mousetool/ToolMappingDialog.cpp \
 					  ui/animationpreview/AnimationPreview.cpp \
 					  ui/animationpreview/MD5AnimationViewer.cpp \
+					  ui/aas/AasControl.cpp \
+					  ui/aas/AasControlDialog.cpp \
                       xyview/tools/BrushCreatorTool.cpp \
                       xyview/tools/ClipperTool.cpp \
                       xyview/XYWnd.cpp \
@@ -158,6 +161,7 @@ darkradiant_SOURCES = main.cpp \
                       textool/item/PatchItem.cpp \
                       textool/item/PatchVertexItem.cpp \
                       textool/TexTool.cpp \
+					  layers/LayerInfoFileModule.cpp \
                       layers/LayerCommandTarget.cpp \
                       layers/LayerSystem.cpp \
                       modulesystem/DynamicLibrary.cpp \
@@ -172,6 +176,7 @@ darkradiant_SOURCES = main.cpp \
 					  selection/shaderclipboard/ClosestTexturableFinder.cpp \
                       selection/selectionset/SelectionSetManager.cpp \
                       selection/selectionset/SelectionSetToolmenu.cpp \
+					  selection/selectionset/SelectionSetInfoFileModule.cpp \
                       selection/selectionset/SelectionSet.cpp \
                       selection/ManipulateMouseTool.cpp \
                       selection/SelectionMouseTools.cpp \
@@ -195,12 +200,15 @@ darkradiant_SOURCES = main.cpp \
                       selection/RotateManipulator.cpp \
                       selection/Manipulatables.cpp \
                       selection/ScaleManipulator.cpp \
+					  selection/group/SelectionGroupManager.cpp \
+					  selection/group/SelectionGroupInfoFileModule.cpp \
                       selection/TranslateManipulator.cpp \
                       selection/Intersection.cpp \
                       settings/GameFileLoader.cpp \
                       settings/Game.cpp \
                       settings/GameManager.cpp \
                       settings/PreferenceSystem.cpp \
+					  settings/PreferencePage.cpp \
                       settings/Win32Registry.cpp \
 					  patch/algorithm/General.cpp \
 					  patch/algorithm/Prefab.cpp \
@@ -208,20 +216,20 @@ darkradiant_SOURCES = main.cpp \
 					  patch/PatchCreators.cpp \
                       patch/PatchNode.cpp \
                       patch/PatchModule.cpp \
-                      patch/PatchBezier.cpp \
                       patch/PatchRenderables.cpp \
                       patch/PatchTesselation.cpp \
                       map/RootNode.cpp \
                       map/MapPosition.cpp \
                       map/FindMapElements.cpp \
-					  map/InfoFile.cpp \
+					  map/infofile/InfoFileManager.cpp \
+					  map/infofile/InfoFile.cpp \
+					  map/infofile/InfoFileExporter.cpp \
                       map/MapFileManager.cpp \
 					  map/algorithm/ChildPrimitives.cpp \
                       map/algorithm/Skins.cpp \
                       map/algorithm/Traverse.cpp \
 					  map/algorithm/MapExporter.cpp \
                       map/algorithm/MapImporter.cpp \
-					  map/algorithm/InfoFileExporter.cpp \
                       map/CounterManager.cpp \
                       map/RegionManager.cpp \
                       map/PointFile.cpp \
@@ -232,6 +240,8 @@ darkradiant_SOURCES = main.cpp \
                       map/StartupMapLoader.cpp \
                       map/MapResourceManager.cpp \
                       map/MapFormatManager.cpp \
+					  map/AasFileManager.cpp \
+					  map/RenderableAasFile.cpp \
                       clipper/ClipPoint.cpp \
                       clipper/Clipper.cpp \
                       log/Console.cpp \
diff --git a/radiant/Makefile.in b/radiant/Makefile.in
index 1dfc880..fd28949 100644
--- a/radiant/Makefile.in
+++ b/radiant/Makefile.in
@@ -198,6 +198,7 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \
 	ui/menu/darkradiant-FiltersMenu.$(OBJEXT) \
 	ui/prefdialog/darkradiant-PrefPage.$(OBJEXT) \
 	ui/prefdialog/darkradiant-PrefDialog.$(OBJEXT) \
+	ui/prefdialog/darkradiant-PreferenceItem.$(OBJEXT) \
 	ui/transform/darkradiant-TransformDialog.$(OBJEXT) \
 	ui/modelselector/darkradiant-MaterialsList.$(OBJEXT) \
 	ui/modelselector/darkradiant-ModelSelector.$(OBJEXT) \
@@ -224,6 +225,8 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \
 	ui/mousetool/darkradiant-ToolMappingDialog.$(OBJEXT) \
 	ui/animationpreview/darkradiant-AnimationPreview.$(OBJEXT) \
 	ui/animationpreview/darkradiant-MD5AnimationViewer.$(OBJEXT) \
+	ui/aas/darkradiant-AasControl.$(OBJEXT) \
+	ui/aas/darkradiant-AasControlDialog.$(OBJEXT) \
 	xyview/tools/darkradiant-BrushCreatorTool.$(OBJEXT) \
 	xyview/tools/darkradiant-ClipperTool.$(OBJEXT) \
 	xyview/darkradiant-XYWnd.$(OBJEXT) \
@@ -236,6 +239,7 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \
 	textool/item/darkradiant-PatchItem.$(OBJEXT) \
 	textool/item/darkradiant-PatchVertexItem.$(OBJEXT) \
 	textool/darkradiant-TexTool.$(OBJEXT) \
+	layers/darkradiant-LayerInfoFileModule.$(OBJEXT) \
 	layers/darkradiant-LayerCommandTarget.$(OBJEXT) \
 	layers/darkradiant-LayerSystem.$(OBJEXT) \
 	modulesystem/darkradiant-DynamicLibrary.$(OBJEXT) \
@@ -250,6 +254,7 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \
 	selection/shaderclipboard/darkradiant-ClosestTexturableFinder.$(OBJEXT) \
 	selection/selectionset/darkradiant-SelectionSetManager.$(OBJEXT) \
 	selection/selectionset/darkradiant-SelectionSetToolmenu.$(OBJEXT) \
+	selection/selectionset/darkradiant-SelectionSetInfoFileModule.$(OBJEXT) \
 	selection/selectionset/darkradiant-SelectionSet.$(OBJEXT) \
 	selection/darkradiant-ManipulateMouseTool.$(OBJEXT) \
 	selection/darkradiant-SelectionMouseTools.$(OBJEXT) \
@@ -273,12 +278,15 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \
 	selection/darkradiant-RotateManipulator.$(OBJEXT) \
 	selection/darkradiant-Manipulatables.$(OBJEXT) \
 	selection/darkradiant-ScaleManipulator.$(OBJEXT) \
+	selection/group/darkradiant-SelectionGroupManager.$(OBJEXT) \
+	selection/group/darkradiant-SelectionGroupInfoFileModule.$(OBJEXT) \
 	selection/darkradiant-TranslateManipulator.$(OBJEXT) \
 	selection/darkradiant-Intersection.$(OBJEXT) \
 	settings/darkradiant-GameFileLoader.$(OBJEXT) \
 	settings/darkradiant-Game.$(OBJEXT) \
 	settings/darkradiant-GameManager.$(OBJEXT) \
 	settings/darkradiant-PreferenceSystem.$(OBJEXT) \
+	settings/darkradiant-PreferencePage.$(OBJEXT) \
 	settings/darkradiant-Win32Registry.$(OBJEXT) \
 	patch/algorithm/darkradiant-General.$(OBJEXT) \
 	patch/algorithm/darkradiant-Prefab.$(OBJEXT) \
@@ -286,20 +294,20 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \
 	patch/darkradiant-PatchCreators.$(OBJEXT) \
 	patch/darkradiant-PatchNode.$(OBJEXT) \
 	patch/darkradiant-PatchModule.$(OBJEXT) \
-	patch/darkradiant-PatchBezier.$(OBJEXT) \
 	patch/darkradiant-PatchRenderables.$(OBJEXT) \
 	patch/darkradiant-PatchTesselation.$(OBJEXT) \
 	map/darkradiant-RootNode.$(OBJEXT) \
 	map/darkradiant-MapPosition.$(OBJEXT) \
 	map/darkradiant-FindMapElements.$(OBJEXT) \
-	map/darkradiant-InfoFile.$(OBJEXT) \
+	map/infofile/darkradiant-InfoFileManager.$(OBJEXT) \
+	map/infofile/darkradiant-InfoFile.$(OBJEXT) \
+	map/infofile/darkradiant-InfoFileExporter.$(OBJEXT) \
 	map/darkradiant-MapFileManager.$(OBJEXT) \
 	map/algorithm/darkradiant-ChildPrimitives.$(OBJEXT) \
 	map/algorithm/darkradiant-Skins.$(OBJEXT) \
 	map/algorithm/darkradiant-Traverse.$(OBJEXT) \
 	map/algorithm/darkradiant-MapExporter.$(OBJEXT) \
 	map/algorithm/darkradiant-MapImporter.$(OBJEXT) \
-	map/algorithm/darkradiant-InfoFileExporter.$(OBJEXT) \
 	map/darkradiant-CounterManager.$(OBJEXT) \
 	map/darkradiant-RegionManager.$(OBJEXT) \
 	map/darkradiant-PointFile.$(OBJEXT) \
@@ -310,6 +318,8 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \
 	map/darkradiant-StartupMapLoader.$(OBJEXT) \
 	map/darkradiant-MapResourceManager.$(OBJEXT) \
 	map/darkradiant-MapFormatManager.$(OBJEXT) \
+	map/darkradiant-AasFileManager.$(OBJEXT) \
+	map/darkradiant-RenderableAasFile.$(OBJEXT) \
 	clipper/darkradiant-ClipPoint.$(OBJEXT) \
 	clipper/darkradiant-Clipper.$(OBJEXT) \
 	log/darkradiant-Console.$(OBJEXT) \
@@ -928,6 +938,7 @@ darkradiant_SOURCES = main.cpp \
                       ui/menu/FiltersMenu.cpp \
                       ui/prefdialog/PrefPage.cpp \
                       ui/prefdialog/PrefDialog.cpp \
+					  ui/prefdialog/PreferenceItem.cpp \
                       ui/transform/TransformDialog.cpp \
                       ui/modelselector/MaterialsList.cpp \
                       ui/modelselector/ModelSelector.cpp \
@@ -954,6 +965,8 @@ darkradiant_SOURCES = main.cpp \
                       ui/mousetool/ToolMappingDialog.cpp \
 					  ui/animationpreview/AnimationPreview.cpp \
 					  ui/animationpreview/MD5AnimationViewer.cpp \
+					  ui/aas/AasControl.cpp \
+					  ui/aas/AasControlDialog.cpp \
                       xyview/tools/BrushCreatorTool.cpp \
                       xyview/tools/ClipperTool.cpp \
                       xyview/XYWnd.cpp \
@@ -966,6 +979,7 @@ darkradiant_SOURCES = main.cpp \
                       textool/item/PatchItem.cpp \
                       textool/item/PatchVertexItem.cpp \
                       textool/TexTool.cpp \
+					  layers/LayerInfoFileModule.cpp \
                       layers/LayerCommandTarget.cpp \
                       layers/LayerSystem.cpp \
                       modulesystem/DynamicLibrary.cpp \
@@ -980,6 +994,7 @@ darkradiant_SOURCES = main.cpp \
 					  selection/shaderclipboard/ClosestTexturableFinder.cpp \
                       selection/selectionset/SelectionSetManager.cpp \
                       selection/selectionset/SelectionSetToolmenu.cpp \
+					  selection/selectionset/SelectionSetInfoFileModule.cpp \
                       selection/selectionset/SelectionSet.cpp \
                       selection/ManipulateMouseTool.cpp \
                       selection/SelectionMouseTools.cpp \
@@ -1003,12 +1018,15 @@ darkradiant_SOURCES = main.cpp \
                       selection/RotateManipulator.cpp \
                       selection/Manipulatables.cpp \
                       selection/ScaleManipulator.cpp \
+					  selection/group/SelectionGroupManager.cpp \
+					  selection/group/SelectionGroupInfoFileModule.cpp \
                       selection/TranslateManipulator.cpp \
                       selection/Intersection.cpp \
                       settings/GameFileLoader.cpp \
                       settings/Game.cpp \
                       settings/GameManager.cpp \
                       settings/PreferenceSystem.cpp \
+					  settings/PreferencePage.cpp \
                       settings/Win32Registry.cpp \
 					  patch/algorithm/General.cpp \
 					  patch/algorithm/Prefab.cpp \
@@ -1016,20 +1034,20 @@ darkradiant_SOURCES = main.cpp \
 					  patch/PatchCreators.cpp \
                       patch/PatchNode.cpp \
                       patch/PatchModule.cpp \
-                      patch/PatchBezier.cpp \
                       patch/PatchRenderables.cpp \
                       patch/PatchTesselation.cpp \
                       map/RootNode.cpp \
                       map/MapPosition.cpp \
                       map/FindMapElements.cpp \
-					  map/InfoFile.cpp \
+					  map/infofile/InfoFileManager.cpp \
+					  map/infofile/InfoFile.cpp \
+					  map/infofile/InfoFileExporter.cpp \
                       map/MapFileManager.cpp \
 					  map/algorithm/ChildPrimitives.cpp \
                       map/algorithm/Skins.cpp \
                       map/algorithm/Traverse.cpp \
 					  map/algorithm/MapExporter.cpp \
                       map/algorithm/MapImporter.cpp \
-					  map/algorithm/InfoFileExporter.cpp \
                       map/CounterManager.cpp \
                       map/RegionManager.cpp \
                       map/PointFile.cpp \
@@ -1040,6 +1058,8 @@ darkradiant_SOURCES = main.cpp \
                       map/StartupMapLoader.cpp \
                       map/MapResourceManager.cpp \
                       map/MapFormatManager.cpp \
+					  map/AasFileManager.cpp \
+					  map/RenderableAasFile.cpp \
                       clipper/ClipPoint.cpp \
                       clipper/Clipper.cpp \
                       log/Console.cpp \
@@ -1540,6 +1560,9 @@ ui/prefdialog/darkradiant-PrefPage.$(OBJEXT):  \
 ui/prefdialog/darkradiant-PrefDialog.$(OBJEXT):  \
 	ui/prefdialog/$(am__dirstamp) \
 	ui/prefdialog/$(DEPDIR)/$(am__dirstamp)
+ui/prefdialog/darkradiant-PreferenceItem.$(OBJEXT):  \
+	ui/prefdialog/$(am__dirstamp) \
+	ui/prefdialog/$(DEPDIR)/$(am__dirstamp)
 ui/transform/$(am__dirstamp):
 	@$(MKDIR_P) ui/transform
 	@: > ui/transform/$(am__dirstamp)
@@ -1673,6 +1696,16 @@ ui/animationpreview/darkradiant-AnimationPreview.$(OBJEXT):  \
 ui/animationpreview/darkradiant-MD5AnimationViewer.$(OBJEXT):  \
 	ui/animationpreview/$(am__dirstamp) \
 	ui/animationpreview/$(DEPDIR)/$(am__dirstamp)
+ui/aas/$(am__dirstamp):
+	@$(MKDIR_P) ui/aas
+	@: > ui/aas/$(am__dirstamp)
+ui/aas/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) ui/aas/$(DEPDIR)
+	@: > ui/aas/$(DEPDIR)/$(am__dirstamp)
+ui/aas/darkradiant-AasControl.$(OBJEXT): ui/aas/$(am__dirstamp) \
+	ui/aas/$(DEPDIR)/$(am__dirstamp)
+ui/aas/darkradiant-AasControlDialog.$(OBJEXT): ui/aas/$(am__dirstamp) \
+	ui/aas/$(DEPDIR)/$(am__dirstamp)
 xyview/tools/$(am__dirstamp):
 	@$(MKDIR_P) xyview/tools
 	@: > xyview/tools/$(am__dirstamp)
@@ -1734,6 +1767,8 @@ layers/$(am__dirstamp):
 layers/$(DEPDIR)/$(am__dirstamp):
 	@$(MKDIR_P) layers/$(DEPDIR)
 	@: > layers/$(DEPDIR)/$(am__dirstamp)
+layers/darkradiant-LayerInfoFileModule.$(OBJEXT):  \
+	layers/$(am__dirstamp) layers/$(DEPDIR)/$(am__dirstamp)
 layers/darkradiant-LayerCommandTarget.$(OBJEXT):  \
 	layers/$(am__dirstamp) layers/$(DEPDIR)/$(am__dirstamp)
 layers/darkradiant-LayerSystem.$(OBJEXT): layers/$(am__dirstamp) \
@@ -1803,6 +1838,9 @@ selection/selectionset/darkradiant-SelectionSetManager.$(OBJEXT):  \
 selection/selectionset/darkradiant-SelectionSetToolmenu.$(OBJEXT):  \
 	selection/selectionset/$(am__dirstamp) \
 	selection/selectionset/$(DEPDIR)/$(am__dirstamp)
+selection/selectionset/darkradiant-SelectionSetInfoFileModule.$(OBJEXT):  \
+	selection/selectionset/$(am__dirstamp) \
+	selection/selectionset/$(DEPDIR)/$(am__dirstamp)
 selection/selectionset/darkradiant-SelectionSet.$(OBJEXT):  \
 	selection/selectionset/$(am__dirstamp) \
 	selection/selectionset/$(DEPDIR)/$(am__dirstamp)
@@ -1866,6 +1904,18 @@ selection/darkradiant-Manipulatables.$(OBJEXT):  \
 	selection/$(am__dirstamp) selection/$(DEPDIR)/$(am__dirstamp)
 selection/darkradiant-ScaleManipulator.$(OBJEXT):  \
 	selection/$(am__dirstamp) selection/$(DEPDIR)/$(am__dirstamp)
+selection/group/$(am__dirstamp):
+	@$(MKDIR_P) selection/group
+	@: > selection/group/$(am__dirstamp)
+selection/group/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) selection/group/$(DEPDIR)
+	@: > selection/group/$(DEPDIR)/$(am__dirstamp)
+selection/group/darkradiant-SelectionGroupManager.$(OBJEXT):  \
+	selection/group/$(am__dirstamp) \
+	selection/group/$(DEPDIR)/$(am__dirstamp)
+selection/group/darkradiant-SelectionGroupInfoFileModule.$(OBJEXT):  \
+	selection/group/$(am__dirstamp) \
+	selection/group/$(DEPDIR)/$(am__dirstamp)
 selection/darkradiant-TranslateManipulator.$(OBJEXT):  \
 	selection/$(am__dirstamp) selection/$(DEPDIR)/$(am__dirstamp)
 selection/darkradiant-Intersection.$(OBJEXT):  \
@@ -1884,6 +1934,8 @@ settings/darkradiant-GameManager.$(OBJEXT): settings/$(am__dirstamp) \
 	settings/$(DEPDIR)/$(am__dirstamp)
 settings/darkradiant-PreferenceSystem.$(OBJEXT):  \
 	settings/$(am__dirstamp) settings/$(DEPDIR)/$(am__dirstamp)
+settings/darkradiant-PreferencePage.$(OBJEXT):  \
+	settings/$(am__dirstamp) settings/$(DEPDIR)/$(am__dirstamp)
 settings/darkradiant-Win32Registry.$(OBJEXT):  \
 	settings/$(am__dirstamp) settings/$(DEPDIR)/$(am__dirstamp)
 patch/algorithm/$(am__dirstamp):
@@ -1912,8 +1964,6 @@ patch/darkradiant-PatchNode.$(OBJEXT): patch/$(am__dirstamp) \
 	patch/$(DEPDIR)/$(am__dirstamp)
 patch/darkradiant-PatchModule.$(OBJEXT): patch/$(am__dirstamp) \
 	patch/$(DEPDIR)/$(am__dirstamp)
-patch/darkradiant-PatchBezier.$(OBJEXT): patch/$(am__dirstamp) \
-	patch/$(DEPDIR)/$(am__dirstamp)
 patch/darkradiant-PatchRenderables.$(OBJEXT): patch/$(am__dirstamp) \
 	patch/$(DEPDIR)/$(am__dirstamp)
 patch/darkradiant-PatchTesselation.$(OBJEXT): patch/$(am__dirstamp) \
@@ -1930,8 +1980,21 @@ map/darkradiant-MapPosition.$(OBJEXT): map/$(am__dirstamp) \
 	map/$(DEPDIR)/$(am__dirstamp)
 map/darkradiant-FindMapElements.$(OBJEXT): map/$(am__dirstamp) \
 	map/$(DEPDIR)/$(am__dirstamp)
-map/darkradiant-InfoFile.$(OBJEXT): map/$(am__dirstamp) \
-	map/$(DEPDIR)/$(am__dirstamp)
+map/infofile/$(am__dirstamp):
+	@$(MKDIR_P) map/infofile
+	@: > map/infofile/$(am__dirstamp)
+map/infofile/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) map/infofile/$(DEPDIR)
+	@: > map/infofile/$(DEPDIR)/$(am__dirstamp)
+map/infofile/darkradiant-InfoFileManager.$(OBJEXT):  \
+	map/infofile/$(am__dirstamp) \
+	map/infofile/$(DEPDIR)/$(am__dirstamp)
+map/infofile/darkradiant-InfoFile.$(OBJEXT):  \
+	map/infofile/$(am__dirstamp) \
+	map/infofile/$(DEPDIR)/$(am__dirstamp)
+map/infofile/darkradiant-InfoFileExporter.$(OBJEXT):  \
+	map/infofile/$(am__dirstamp) \
+	map/infofile/$(DEPDIR)/$(am__dirstamp)
 map/darkradiant-MapFileManager.$(OBJEXT): map/$(am__dirstamp) \
 	map/$(DEPDIR)/$(am__dirstamp)
 map/algorithm/$(am__dirstamp):
@@ -1955,9 +2018,6 @@ map/algorithm/darkradiant-MapExporter.$(OBJEXT):  \
 map/algorithm/darkradiant-MapImporter.$(OBJEXT):  \
 	map/algorithm/$(am__dirstamp) \
 	map/algorithm/$(DEPDIR)/$(am__dirstamp)
-map/algorithm/darkradiant-InfoFileExporter.$(OBJEXT):  \
-	map/algorithm/$(am__dirstamp) \
-	map/algorithm/$(DEPDIR)/$(am__dirstamp)
 map/darkradiant-CounterManager.$(OBJEXT): map/$(am__dirstamp) \
 	map/$(DEPDIR)/$(am__dirstamp)
 map/darkradiant-RegionManager.$(OBJEXT): map/$(am__dirstamp) \
@@ -1978,6 +2038,10 @@ map/darkradiant-MapResourceManager.$(OBJEXT): map/$(am__dirstamp) \
 	map/$(DEPDIR)/$(am__dirstamp)
 map/darkradiant-MapFormatManager.$(OBJEXT): map/$(am__dirstamp) \
 	map/$(DEPDIR)/$(am__dirstamp)
+map/darkradiant-AasFileManager.$(OBJEXT): map/$(am__dirstamp) \
+	map/$(DEPDIR)/$(am__dirstamp)
+map/darkradiant-RenderableAasFile.$(OBJEXT): map/$(am__dirstamp) \
+	map/$(DEPDIR)/$(am__dirstamp)
 clipper/$(am__dirstamp):
 	@$(MKDIR_P) clipper
 	@: > clipper/$(am__dirstamp)
@@ -2053,6 +2117,7 @@ mostlyclean-compile:
 	-rm -f log/*.$(OBJEXT)
 	-rm -f map/*.$(OBJEXT)
 	-rm -f map/algorithm/*.$(OBJEXT)
+	-rm -f map/infofile/*.$(OBJEXT)
 	-rm -f modulesystem/*.$(OBJEXT)
 	-rm -f namespace/*.$(OBJEXT)
 	-rm -f patch/*.$(OBJEXT)
@@ -2065,12 +2130,14 @@ mostlyclean-compile:
 	-rm -f selection/*.$(OBJEXT)
 	-rm -f selection/algorithm/*.$(OBJEXT)
 	-rm -f selection/clipboard/*.$(OBJEXT)
+	-rm -f selection/group/*.$(OBJEXT)
 	-rm -f selection/selectionset/*.$(OBJEXT)
 	-rm -f selection/shaderclipboard/*.$(OBJEXT)
 	-rm -f settings/*.$(OBJEXT)
 	-rm -f test/*.$(OBJEXT)
 	-rm -f textool/*.$(OBJEXT)
 	-rm -f textool/item/*.$(OBJEXT)
+	-rm -f ui/aas/*.$(OBJEXT)
 	-rm -f ui/about/*.$(OBJEXT)
 	-rm -f ui/animationpreview/*.$(OBJEXT)
 	-rm -f ui/brush/*.$(OBJEXT)
@@ -2132,6 +2199,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at clipper/$(DEPDIR)/darkradiant-ClipPoint.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at clipper/$(DEPDIR)/darkradiant-Clipper.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at layers/$(DEPDIR)/darkradiant-LayerCommandTarget.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at layers/$(DEPDIR)/darkradiant-LayerInfoFileModule.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at layers/$(DEPDIR)/darkradiant-LayerSystem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at log/$(DEPDIR)/darkradiant-COutRedirector.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at log/$(DEPDIR)/darkradiant-Console.Po at am__quote@
@@ -2140,10 +2208,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at log/$(DEPDIR)/darkradiant-LogStreamBuf.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at log/$(DEPDIR)/darkradiant-LogWriter.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at log/$(DEPDIR)/darkradiant-StringLogDevice.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-AasFileManager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-AutoSaver.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-CounterManager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-FindMapElements.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-InfoFile.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-Map.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-MapFileManager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-MapFormatManager.Po at am__quote@
@@ -2153,14 +2221,17 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-MapResourceManager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-PointFile.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-RegionManager.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-RenderableAasFile.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-RootNode.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/$(DEPDIR)/darkradiant-StartupMapLoader.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/algorithm/$(DEPDIR)/darkradiant-ChildPrimitives.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at map/algorithm/$(DEPDIR)/darkradiant-InfoFileExporter.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/algorithm/$(DEPDIR)/darkradiant-MapExporter.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/algorithm/$(DEPDIR)/darkradiant-MapImporter.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/algorithm/$(DEPDIR)/darkradiant-Skins.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at map/algorithm/$(DEPDIR)/darkradiant-Traverse.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at map/infofile/$(DEPDIR)/darkradiant-InfoFile.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at map/infofile/$(DEPDIR)/darkradiant-InfoFileExporter.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at map/infofile/$(DEPDIR)/darkradiant-InfoFileManager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at modulesystem/$(DEPDIR)/darkradiant-ApplicationContextImpl.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at modulesystem/$(DEPDIR)/darkradiant-DynamicLibrary.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at modulesystem/$(DEPDIR)/darkradiant-DynamicLibraryLoader.Po at am__quote@
@@ -2170,7 +2241,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at namespace/$(DEPDIR)/darkradiant-Namespace.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at namespace/$(DEPDIR)/darkradiant-NamespaceFactory.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at patch/$(DEPDIR)/darkradiant-Patch.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at patch/$(DEPDIR)/darkradiant-PatchBezier.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at patch/$(DEPDIR)/darkradiant-PatchCreators.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at patch/$(DEPDIR)/darkradiant-PatchModule.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at patch/$(DEPDIR)/darkradiant-PatchNode.Po at am__quote@
@@ -2221,7 +2291,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at selection/algorithm/$(DEPDIR)/darkradiant-Shader.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at selection/algorithm/$(DEPDIR)/darkradiant-Transformation.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at selection/clipboard/$(DEPDIR)/darkradiant-Clipboard.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at selection/group/$(DEPDIR)/darkradiant-SelectionGroupInfoFileModule.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at selection/group/$(DEPDIR)/darkradiant-SelectionGroupManager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at selection/selectionset/$(DEPDIR)/darkradiant-SelectionSet.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetInfoFileModule.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetManager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetToolmenu.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at selection/shaderclipboard/$(DEPDIR)/darkradiant-ClosestTexturableFinder.Po at am__quote@
@@ -2230,6 +2303,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at settings/$(DEPDIR)/darkradiant-Game.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at settings/$(DEPDIR)/darkradiant-GameFileLoader.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at settings/$(DEPDIR)/darkradiant-GameManager.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at settings/$(DEPDIR)/darkradiant-PreferencePage.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at settings/$(DEPDIR)/darkradiant-PreferenceSystem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at settings/$(DEPDIR)/darkradiant-Win32Registry.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at test/$(DEPDIR)/facePlaneTest.Po at am__quote@
@@ -2240,6 +2314,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at textool/item/$(DEPDIR)/darkradiant-FaceVertexItem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at textool/item/$(DEPDIR)/darkradiant-PatchItem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at textool/item/$(DEPDIR)/darkradiant-PatchVertexItem.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ui/aas/$(DEPDIR)/darkradiant-AasControl.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/about/$(DEPDIR)/darkradiant-AboutDialog.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/animationpreview/$(DEPDIR)/darkradiant-AnimationPreview.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/animationpreview/$(DEPDIR)/darkradiant-MD5AnimationViewer.Po at am__quote@
@@ -2316,6 +2392,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at ui/prefabselector/$(DEPDIR)/darkradiant-PrefabSelector.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/prefdialog/$(DEPDIR)/darkradiant-PrefDialog.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/prefdialog/$(DEPDIR)/darkradiant-PrefPage.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ui/prefdialog/$(DEPDIR)/darkradiant-PreferenceItem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/splash/$(DEPDIR)/darkradiant-Splash.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/surfaceinspector/$(DEPDIR)/darkradiant-SurfaceInspector.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ui/texturebrowser/$(DEPDIR)/darkradiant-TextureBrowser.Po at am__quote@
@@ -3681,6 +3758,20 @@ ui/prefdialog/darkradiant-PrefDialog.obj: ui/prefdialog/PrefDialog.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/prefdialog/darkradiant-PrefDialog.obj `if test -f 'ui/prefdialog/PrefDialog.cpp'; then $(CYGPATH_W) 'ui/prefdialog/PrefDialog.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/prefdialog/PrefDialog.cpp'; fi`
 
+ui/prefdialog/darkradiant-PreferenceItem.o: ui/prefdialog/PreferenceItem.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/prefdialog/darkradiant-PreferenceItem.o -MD -MP -MF ui/prefdialog/$(DEPDIR)/darkradiant-PreferenceItem.Tpo -c -o ui/prefdialog/darkradiant-PreferenceItem.o `test -f 'ui/prefdialog/PreferenceItem.cpp' || echo '$(srcdir)/'`ui/prefdialog/PreferenceItem.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) ui/prefdialog/$(DEPDIR)/darkradiant-PreferenceItem.Tpo ui/prefdialog/$(DEPDIR)/darkradiant-PreferenceItem.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='ui/prefdialog/PreferenceItem.cpp' object='ui/prefdialog/darkradiant-PreferenceItem.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/prefdialog/darkradiant-PreferenceItem.o `test -f 'ui/prefdialog/PreferenceItem.cpp' || echo '$(srcdir)/'`ui/prefdialog/PreferenceItem.cpp
+
+ui/prefdialog/darkradiant-PreferenceItem.obj: ui/prefdialog/PreferenceItem.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/prefdialog/darkradiant-PreferenceItem.obj -MD -MP -MF ui/prefdialog/$(DEPDIR)/darkradiant-PreferenceItem.Tpo -c -o ui/prefdialog/darkradiant-PreferenceItem.obj `if test -f 'ui/prefdialog/PreferenceItem.cpp'; then $(CYGPATH_W) 'ui/prefdialog/PreferenceItem.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/prefdialog/PreferenceItem.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) ui/prefdialog/$(DEPDIR)/darkradiant-PreferenceItem.Tpo ui/prefdialog/$(DEPDIR)/darkradiant-PreferenceItem.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='ui/prefdialog/PreferenceItem.cpp' object='ui/prefdialog/darkradiant-PreferenceItem.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/prefdialog/darkradiant-PreferenceItem.obj `if test -f 'ui/prefdialog/PreferenceItem.cpp'; then $(CYGPATH_W) 'ui/prefdialog/PreferenceItem.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/prefdialog/PreferenceItem.cpp'; fi`
+
 ui/transform/darkradiant-TransformDialog.o: ui/transform/TransformDialog.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/transform/darkradiant-TransformDialog.o -MD -MP -MF ui/transform/$(DEPDIR)/darkradiant-TransformDialog.Tpo -c -o ui/transform/darkradiant-TransformDialog.o `test -f 'ui/transform/TransformDialog.cpp' || echo '$(srcdir)/'`ui/transform/TransformDialog.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) ui/transform/$(DEPDIR)/darkradiant-TransformDialog.Tpo ui/transform/$(DEPDIR)/darkradiant-TransformDialog.Po
@@ -4045,6 +4136,34 @@ ui/animationpreview/darkradiant-MD5AnimationViewer.obj: ui/animationpreview/MD5A
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/animationpreview/darkradiant-MD5AnimationViewer.obj `if test -f 'ui/animationpreview/MD5AnimationViewer.cpp'; then $(CYGPATH_W) 'ui/animationpreview/MD5AnimationViewer.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/animationpreview/MD5AnimationViewer.cpp'; fi`
 
+ui/aas/darkradiant-AasControl.o: ui/aas/AasControl.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/aas/darkradiant-AasControl.o -MD -MP -MF ui/aas/$(DEPDIR)/darkradiant-AasControl.Tpo -c -o ui/aas/darkradiant-AasControl.o `test -f 'ui/aas/AasControl.cpp' || echo '$(srcdir)/'`ui/aas/AasControl.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) ui/aas/$(DEPDIR)/darkradiant-AasControl.Tpo ui/aas/$(DEPDIR)/darkradiant-AasControl.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='ui/aas/AasControl.cpp' object='ui/aas/darkradiant-AasControl.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/aas/darkradiant-AasControl.o `test -f 'ui/aas/AasControl.cpp' || echo '$(srcdir)/'`ui/aas/AasControl.cpp
+
+ui/aas/darkradiant-AasControl.obj: ui/aas/AasControl.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/aas/darkradiant-AasControl.obj -MD -MP -MF ui/aas/$(DEPDIR)/darkradiant-AasControl.Tpo -c -o ui/aas/darkradiant-AasControl.obj `if test -f 'ui/aas/AasControl.cpp'; then $(CYGPATH_W) 'ui/aas/AasControl.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/aas/AasControl.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) ui/aas/$(DEPDIR)/darkradiant-AasControl.Tpo ui/aas/$(DEPDIR)/darkradiant-AasControl.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='ui/aas/AasControl.cpp' object='ui/aas/darkradiant-AasControl.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/aas/darkradiant-AasControl.obj `if test -f 'ui/aas/AasControl.cpp'; then $(CYGPATH_W) 'ui/aas/AasControl.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/aas/AasControl.cpp'; fi`
+
+ui/aas/darkradiant-AasControlDialog.o: ui/aas/AasControlDialog.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/aas/darkradiant-AasControlDialog.o -MD -MP -MF ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Tpo -c -o ui/aas/darkradiant-AasControlDialog.o `test -f 'ui/aas/AasControlDialog.cpp' || echo '$(srcdir)/'`ui/aas/AasControlDialog.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Tpo ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='ui/aas/AasControlDialog.cpp' object='ui/aas/darkradiant-AasControlDialog.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/aas/darkradiant-AasControlDialog.o `test -f 'ui/aas/AasControlDialog.cpp' || echo '$(srcdir)/'`ui/aas/AasControlDialog.cpp
+
+ui/aas/darkradiant-AasControlDialog.obj: ui/aas/AasControlDialog.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/aas/darkradiant-AasControlDialog.obj -MD -MP -MF ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Tpo -c -o ui/aas/darkradiant-AasControlDialog.obj `if test -f 'ui/aas/AasControlDialog.cpp'; then $(CYGPATH_W) 'ui/aas/AasControlDialog.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/aas/AasControlDialog.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Tpo ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='ui/aas/AasControlDialog.cpp' object='ui/aas/darkradiant-AasControlDialog.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/aas/darkradiant-AasControlDialog.obj `if test -f 'ui/aas/AasControlDialog.cpp'; then $(CYGPATH_W) 'ui/aas/AasControlDialog.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/aas/AasControlDialog.cpp'; fi`
+
 xyview/tools/darkradiant-BrushCreatorTool.o: xyview/tools/BrushCreatorTool.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT xyview/tools/darkradiant-BrushCreatorTool.o -MD -MP -MF xyview/tools/$(DEPDIR)/darkradiant-BrushCreatorTool.Tpo -c -o xyview/tools/darkradiant-BrushCreatorTool.o `test -f 'xyview/tools/BrushCreatorTool.cpp' || echo '$(srcdir)/'`xyview/tools/BrushCreatorTool.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) xyview/tools/$(DEPDIR)/darkradiant-BrushCreatorTool.Tpo xyview/tools/$(DEPDIR)/darkradiant-BrushCreatorTool.Po
@@ -4213,6 +4332,20 @@ textool/darkradiant-TexTool.obj: textool/TexTool.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o textool/darkradiant-TexTool.obj `if test -f 'textool/TexTool.cpp'; then $(CYGPATH_W) 'textool/TexTool.cpp'; else $(CYGPATH_W) '$(srcdir)/textool/TexTool.cpp'; fi`
 
+layers/darkradiant-LayerInfoFileModule.o: layers/LayerInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT layers/darkradiant-LayerInfoFileModule.o -MD -MP -MF layers/$(DEPDIR)/darkradiant-LayerInfoFileModule.Tpo -c -o layers/darkradiant-LayerInfoFileModule.o `test -f 'layers/LayerInfoFileModule.cpp' || echo '$(srcdir)/'`layers/LayerInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) layers/$(DEPDIR)/darkradiant-LayerInfoFileModule.Tpo layers/$(DEPDIR)/darkradiant-LayerInfoFileModule.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='layers/LayerInfoFileModule.cpp' object='layers/darkradiant-LayerInfoFileModule.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o layers/darkradiant-LayerInfoFileModule.o `test -f 'layers/LayerInfoFileModule.cpp' || echo '$(srcdir)/'`layers/LayerInfoFileModule.cpp
+
+layers/darkradiant-LayerInfoFileModule.obj: layers/LayerInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT layers/darkradiant-LayerInfoFileModule.obj -MD -MP -MF layers/$(DEPDIR)/darkradiant-LayerInfoFileModule.Tpo -c -o layers/darkradiant-LayerInfoFileModule.obj `if test -f 'layers/LayerInfoFileModule.cpp'; then $(CYGPATH_W) 'layers/LayerInfoFileModule.cpp'; else $(CYGPATH_W) '$(srcdir)/layers/LayerInfoFileModule.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) layers/$(DEPDIR)/darkradiant-LayerInfoFileModule.Tpo layers/$(DEPDIR)/darkradiant-LayerInfoFileModule.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='layers/LayerInfoFileModule.cpp' object='layers/darkradiant-LayerInfoFileModule.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o layers/darkradiant-LayerInfoFileModule.obj `if test -f 'layers/LayerInfoFileModule.cpp'; then $(CYGPATH_W) 'layers/LayerInfoFileModule.cpp'; else $(CYGPATH_W) '$(srcdir)/layers/LayerInfoFileModule.cpp'; fi`
+
 layers/darkradiant-LayerCommandTarget.o: layers/LayerCommandTarget.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT layers/darkradiant-LayerCommandTarget.o -MD -MP -MF layers/$(DEPDIR)/darkradiant-LayerCommandTarget.Tpo -c -o layers/darkradiant-LayerCommandTarget.o `test -f 'layers/LayerCommandTarget.cpp' || echo '$(srcdir)/'`layers/LayerCommandTarget.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) layers/$(DEPDIR)/darkradiant-LayerCommandTarget.Tpo layers/$(DEPDIR)/darkradiant-LayerCommandTarget.Po
@@ -4409,6 +4542,20 @@ selection/selectionset/darkradiant-SelectionSetToolmenu.obj: selection/selection
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/selectionset/darkradiant-SelectionSetToolmenu.obj `if test -f 'selection/selectionset/SelectionSetToolmenu.cpp'; then $(CYGPATH_W) 'selection/selectionset/SelectionSetToolmenu.cpp'; else $(CYGPATH_W) '$(srcdir)/selection/selectionset/SelectionSetToolmenu.cpp'; fi`
 
+selection/selectionset/darkradiant-SelectionSetInfoFileModule.o: selection/selectionset/SelectionSetInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/selectionset/darkradiant-SelectionSetInfoFileModule.o -MD -MP -MF selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetInfoFileModule.Tpo -c -o selection/selectionset/darkradiant-SelectionSetInfoFileModule.o `test -f 'selection/selectionset/SelectionSetInfoFileModule.cpp' || echo '$(srcdir)/'`selection/selectionset/SelectionSetIn [...]
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetInfoFileModule.Tpo selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetInfoFileModule.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='selection/selectionset/SelectionSetInfoFileModule.cpp' object='selection/selectionset/darkradiant-SelectionSetInfoFileModule.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/selectionset/darkradiant-SelectionSetInfoFileModule.o `test -f 'selection/selectionset/SelectionSetInfoFileModule.cpp' || echo '$(srcdir)/'`selection/selectionset/SelectionSetInfoFileModule.cpp
+
+selection/selectionset/darkradiant-SelectionSetInfoFileModule.obj: selection/selectionset/SelectionSetInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/selectionset/darkradiant-SelectionSetInfoFileModule.obj -MD -MP -MF selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetInfoFileModule.Tpo -c -o selection/selectionset/darkradiant-SelectionSetInfoFileModule.obj `if test -f 'selection/selectionset/SelectionSetInfoFileModule.cpp'; then $(CYGPATH_W) 'selection/selectionset/Selectio [...]
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetInfoFileModule.Tpo selection/selectionset/$(DEPDIR)/darkradiant-SelectionSetInfoFileModule.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='selection/selectionset/SelectionSetInfoFileModule.cpp' object='selection/selectionset/darkradiant-SelectionSetInfoFileModule.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/selectionset/darkradiant-SelectionSetInfoFileModule.obj `if test -f 'selection/selectionset/SelectionSetInfoFileModule.cpp'; then $(CYGPATH_W) 'selection/selectionset/SelectionSetInfoFileModule.cpp'; else $(CYGPATH_W) '$(srcdir)/selection/selectionset/SelectionSetInfoFileModule.cpp'; fi`
+
 selection/selectionset/darkradiant-SelectionSet.o: selection/selectionset/SelectionSet.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/selectionset/darkradiant-SelectionSet.o -MD -MP -MF selection/selectionset/$(DEPDIR)/darkradiant-SelectionSet.Tpo -c -o selection/selectionset/darkradiant-SelectionSet.o `test -f 'selection/selectionset/SelectionSet.cpp' || echo '$(srcdir)/'`selection/selectionset/SelectionSet.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/selectionset/$(DEPDIR)/darkradiant-SelectionSet.Tpo selection/selectionset/$(DEPDIR)/darkradiant-SelectionSet.Po
@@ -4731,6 +4878,34 @@ selection/darkradiant-ScaleManipulator.obj: selection/ScaleManipulator.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/darkradiant-ScaleManipulator.obj `if test -f 'selection/ScaleManipulator.cpp'; then $(CYGPATH_W) 'selection/ScaleManipulator.cpp'; else $(CYGPATH_W) '$(srcdir)/selection/ScaleManipulator.cpp'; fi`
 
+selection/group/darkradiant-SelectionGroupManager.o: selection/group/SelectionGroupManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/group/darkradiant-SelectionGroupManager.o -MD -MP -MF selection/group/$(DEPDIR)/darkradiant-SelectionGroupManager.Tpo -c -o selection/group/darkradiant-SelectionGroupManager.o `test -f 'selection/group/SelectionGroupManager.cpp' || echo '$(srcdir)/'`selection/group/SelectionGroupManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/group/$(DEPDIR)/darkradiant-SelectionGroupManager.Tpo selection/group/$(DEPDIR)/darkradiant-SelectionGroupManager.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='selection/group/SelectionGroupManager.cpp' object='selection/group/darkradiant-SelectionGroupManager.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/group/darkradiant-SelectionGroupManager.o `test -f 'selection/group/SelectionGroupManager.cpp' || echo '$(srcdir)/'`selection/group/SelectionGroupManager.cpp
+
+selection/group/darkradiant-SelectionGroupManager.obj: selection/group/SelectionGroupManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/group/darkradiant-SelectionGroupManager.obj -MD -MP -MF selection/group/$(DEPDIR)/darkradiant-SelectionGroupManager.Tpo -c -o selection/group/darkradiant-SelectionGroupManager.obj `if test -f 'selection/group/SelectionGroupManager.cpp'; then $(CYGPATH_W) 'selection/group/SelectionGroupManager.cpp'; else $(CYGPATH_W) '$(srcdir)/select [...]
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/group/$(DEPDIR)/darkradiant-SelectionGroupManager.Tpo selection/group/$(DEPDIR)/darkradiant-SelectionGroupManager.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='selection/group/SelectionGroupManager.cpp' object='selection/group/darkradiant-SelectionGroupManager.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/group/darkradiant-SelectionGroupManager.obj `if test -f 'selection/group/SelectionGroupManager.cpp'; then $(CYGPATH_W) 'selection/group/SelectionGroupManager.cpp'; else $(CYGPATH_W) '$(srcdir)/selection/group/SelectionGroupManager.cpp'; fi`
+
+selection/group/darkradiant-SelectionGroupInfoFileModule.o: selection/group/SelectionGroupInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/group/darkradiant-SelectionGroupInfoFileModule.o -MD -MP -MF selection/group/$(DEPDIR)/darkradiant-SelectionGroupInfoFileModule.Tpo -c -o selection/group/darkradiant-SelectionGroupInfoFileModule.o `test -f 'selection/group/SelectionGroupInfoFileModule.cpp' || echo '$(srcdir)/'`selection/group/SelectionGroupInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/group/$(DEPDIR)/darkradiant-SelectionGroupInfoFileModule.Tpo selection/group/$(DEPDIR)/darkradiant-SelectionGroupInfoFileModule.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='selection/group/SelectionGroupInfoFileModule.cpp' object='selection/group/darkradiant-SelectionGroupInfoFileModule.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/group/darkradiant-SelectionGroupInfoFileModule.o `test -f 'selection/group/SelectionGroupInfoFileModule.cpp' || echo '$(srcdir)/'`selection/group/SelectionGroupInfoFileModule.cpp
+
+selection/group/darkradiant-SelectionGroupInfoFileModule.obj: selection/group/SelectionGroupInfoFileModule.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/group/darkradiant-SelectionGroupInfoFileModule.obj -MD -MP -MF selection/group/$(DEPDIR)/darkradiant-SelectionGroupInfoFileModule.Tpo -c -o selection/group/darkradiant-SelectionGroupInfoFileModule.obj `if test -f 'selection/group/SelectionGroupInfoFileModule.cpp'; then $(CYGPATH_W) 'selection/group/SelectionGroupInfoFileModule.cpp';  [...]
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/group/$(DEPDIR)/darkradiant-SelectionGroupInfoFileModule.Tpo selection/group/$(DEPDIR)/darkradiant-SelectionGroupInfoFileModule.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='selection/group/SelectionGroupInfoFileModule.cpp' object='selection/group/darkradiant-SelectionGroupInfoFileModule.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o selection/group/darkradiant-SelectionGroupInfoFileModule.obj `if test -f 'selection/group/SelectionGroupInfoFileModule.cpp'; then $(CYGPATH_W) 'selection/group/SelectionGroupInfoFileModule.cpp'; else $(CYGPATH_W) '$(srcdir)/selection/group/SelectionGroupInfoFileModule.cpp'; fi`
+
 selection/darkradiant-TranslateManipulator.o: selection/TranslateManipulator.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT selection/darkradiant-TranslateManipulator.o -MD -MP -MF selection/$(DEPDIR)/darkradiant-TranslateManipulator.Tpo -c -o selection/darkradiant-TranslateManipulator.o `test -f 'selection/TranslateManipulator.cpp' || echo '$(srcdir)/'`selection/TranslateManipulator.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) selection/$(DEPDIR)/darkradiant-TranslateManipulator.Tpo selection/$(DEPDIR)/darkradiant-TranslateManipulator.Po
@@ -4815,6 +4990,20 @@ settings/darkradiant-PreferenceSystem.obj: settings/PreferenceSystem.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o settings/darkradiant-PreferenceSystem.obj `if test -f 'settings/PreferenceSystem.cpp'; then $(CYGPATH_W) 'settings/PreferenceSystem.cpp'; else $(CYGPATH_W) '$(srcdir)/settings/PreferenceSystem.cpp'; fi`
 
+settings/darkradiant-PreferencePage.o: settings/PreferencePage.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT settings/darkradiant-PreferencePage.o -MD -MP -MF settings/$(DEPDIR)/darkradiant-PreferencePage.Tpo -c -o settings/darkradiant-PreferencePage.o `test -f 'settings/PreferencePage.cpp' || echo '$(srcdir)/'`settings/PreferencePage.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) settings/$(DEPDIR)/darkradiant-PreferencePage.Tpo settings/$(DEPDIR)/darkradiant-PreferencePage.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='settings/PreferencePage.cpp' object='settings/darkradiant-PreferencePage.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o settings/darkradiant-PreferencePage.o `test -f 'settings/PreferencePage.cpp' || echo '$(srcdir)/'`settings/PreferencePage.cpp
+
+settings/darkradiant-PreferencePage.obj: settings/PreferencePage.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT settings/darkradiant-PreferencePage.obj -MD -MP -MF settings/$(DEPDIR)/darkradiant-PreferencePage.Tpo -c -o settings/darkradiant-PreferencePage.obj `if test -f 'settings/PreferencePage.cpp'; then $(CYGPATH_W) 'settings/PreferencePage.cpp'; else $(CYGPATH_W) '$(srcdir)/settings/PreferencePage.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) settings/$(DEPDIR)/darkradiant-PreferencePage.Tpo settings/$(DEPDIR)/darkradiant-PreferencePage.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='settings/PreferencePage.cpp' object='settings/darkradiant-PreferencePage.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o settings/darkradiant-PreferencePage.obj `if test -f 'settings/PreferencePage.cpp'; then $(CYGPATH_W) 'settings/PreferencePage.cpp'; else $(CYGPATH_W) '$(srcdir)/settings/PreferencePage.cpp'; fi`
+
 settings/darkradiant-Win32Registry.o: settings/Win32Registry.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT settings/darkradiant-Win32Registry.o -MD -MP -MF settings/$(DEPDIR)/darkradiant-Win32Registry.Tpo -c -o settings/darkradiant-Win32Registry.o `test -f 'settings/Win32Registry.cpp' || echo '$(srcdir)/'`settings/Win32Registry.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) settings/$(DEPDIR)/darkradiant-Win32Registry.Tpo settings/$(DEPDIR)/darkradiant-Win32Registry.Po
@@ -4913,20 +5102,6 @@ patch/darkradiant-PatchModule.obj: patch/PatchModule.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o patch/darkradiant-PatchModule.obj `if test -f 'patch/PatchModule.cpp'; then $(CYGPATH_W) 'patch/PatchModule.cpp'; else $(CYGPATH_W) '$(srcdir)/patch/PatchModule.cpp'; fi`
 
-patch/darkradiant-PatchBezier.o: patch/PatchBezier.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT patch/darkradiant-PatchBezier.o -MD -MP -MF patch/$(DEPDIR)/darkradiant-PatchBezier.Tpo -c -o patch/darkradiant-PatchBezier.o `test -f 'patch/PatchBezier.cpp' || echo '$(srcdir)/'`patch/PatchBezier.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) patch/$(DEPDIR)/darkradiant-PatchBezier.Tpo patch/$(DEPDIR)/darkradiant-PatchBezier.Po
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='patch/PatchBezier.cpp' object='patch/darkradiant-PatchBezier.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o patch/darkradiant-PatchBezier.o `test -f 'patch/PatchBezier.cpp' || echo '$(srcdir)/'`patch/PatchBezier.cpp
-
-patch/darkradiant-PatchBezier.obj: patch/PatchBezier.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT patch/darkradiant-PatchBezier.obj -MD -MP -MF patch/$(DEPDIR)/darkradiant-PatchBezier.Tpo -c -o patch/darkradiant-PatchBezier.obj `if test -f 'patch/PatchBezier.cpp'; then $(CYGPATH_W) 'patch/PatchBezier.cpp'; else $(CYGPATH_W) '$(srcdir)/patch/PatchBezier.cpp'; fi`
- at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) patch/$(DEPDIR)/darkradiant-PatchBezier.Tpo patch/$(DEPDIR)/darkradiant-PatchBezier.Po
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='patch/PatchBezier.cpp' object='patch/darkradiant-PatchBezier.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o patch/darkradiant-PatchBezier.obj `if test -f 'patch/PatchBezier.cpp'; then $(CYGPATH_W) 'patch/PatchBezier.cpp'; else $(CYGPATH_W) '$(srcdir)/patch/PatchBezier.cpp'; fi`
-
 patch/darkradiant-PatchRenderables.o: patch/PatchRenderables.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT patch/darkradiant-PatchRenderables.o -MD -MP -MF patch/$(DEPDIR)/darkradiant-PatchRenderables.Tpo -c -o patch/darkradiant-PatchRenderables.o `test -f 'patch/PatchRenderables.cpp' || echo '$(srcdir)/'`patch/PatchRenderables.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) patch/$(DEPDIR)/darkradiant-PatchRenderables.Tpo patch/$(DEPDIR)/darkradiant-PatchRenderables.Po
@@ -4997,19 +5172,47 @@ map/darkradiant-FindMapElements.obj: map/FindMapElements.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-FindMapElements.obj `if test -f 'map/FindMapElements.cpp'; then $(CYGPATH_W) 'map/FindMapElements.cpp'; else $(CYGPATH_W) '$(srcdir)/map/FindMapElements.cpp'; fi`
 
-map/darkradiant-InfoFile.o: map/InfoFile.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-InfoFile.o -MD -MP -MF map/$(DEPDIR)/darkradiant-InfoFile.Tpo -c -o map/darkradiant-InfoFile.o `test -f 'map/InfoFile.cpp' || echo '$(srcdir)/'`map/InfoFile.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/$(DEPDIR)/darkradiant-InfoFile.Tpo map/$(DEPDIR)/darkradiant-InfoFile.Po
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/InfoFile.cpp' object='map/darkradiant-InfoFile.o' libtool=no @AMDEPBACKSLASH@
+map/infofile/darkradiant-InfoFileManager.o: map/infofile/InfoFileManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/infofile/darkradiant-InfoFileManager.o -MD -MP -MF map/infofile/$(DEPDIR)/darkradiant-InfoFileManager.Tpo -c -o map/infofile/darkradiant-InfoFileManager.o `test -f 'map/infofile/InfoFileManager.cpp' || echo '$(srcdir)/'`map/infofile/InfoFileManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/infofile/$(DEPDIR)/darkradiant-InfoFileManager.Tpo map/infofile/$(DEPDIR)/darkradiant-InfoFileManager.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/infofile/InfoFileManager.cpp' object='map/infofile/darkradiant-InfoFileManager.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/infofile/darkradiant-InfoFileManager.o `test -f 'map/infofile/InfoFileManager.cpp' || echo '$(srcdir)/'`map/infofile/InfoFileManager.cpp
+
+map/infofile/darkradiant-InfoFileManager.obj: map/infofile/InfoFileManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/infofile/darkradiant-InfoFileManager.obj -MD -MP -MF map/infofile/$(DEPDIR)/darkradiant-InfoFileManager.Tpo -c -o map/infofile/darkradiant-InfoFileManager.obj `if test -f 'map/infofile/InfoFileManager.cpp'; then $(CYGPATH_W) 'map/infofile/InfoFileManager.cpp'; else $(CYGPATH_W) '$(srcdir)/map/infofile/InfoFileManager.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/infofile/$(DEPDIR)/darkradiant-InfoFileManager.Tpo map/infofile/$(DEPDIR)/darkradiant-InfoFileManager.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/infofile/InfoFileManager.cpp' object='map/infofile/darkradiant-InfoFileManager.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/infofile/darkradiant-InfoFileManager.obj `if test -f 'map/infofile/InfoFileManager.cpp'; then $(CYGPATH_W) 'map/infofile/InfoFileManager.cpp'; else $(CYGPATH_W) '$(srcdir)/map/infofile/InfoFileManager.cpp'; fi`
+
+map/infofile/darkradiant-InfoFile.o: map/infofile/InfoFile.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/infofile/darkradiant-InfoFile.o -MD -MP -MF map/infofile/$(DEPDIR)/darkradiant-InfoFile.Tpo -c -o map/infofile/darkradiant-InfoFile.o `test -f 'map/infofile/InfoFile.cpp' || echo '$(srcdir)/'`map/infofile/InfoFile.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/infofile/$(DEPDIR)/darkradiant-InfoFile.Tpo map/infofile/$(DEPDIR)/darkradiant-InfoFile.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/infofile/InfoFile.cpp' object='map/infofile/darkradiant-InfoFile.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/infofile/darkradiant-InfoFile.o `test -f 'map/infofile/InfoFile.cpp' || echo '$(srcdir)/'`map/infofile/InfoFile.cpp
+
+map/infofile/darkradiant-InfoFile.obj: map/infofile/InfoFile.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/infofile/darkradiant-InfoFile.obj -MD -MP -MF map/infofile/$(DEPDIR)/darkradiant-InfoFile.Tpo -c -o map/infofile/darkradiant-InfoFile.obj `if test -f 'map/infofile/InfoFile.cpp'; then $(CYGPATH_W) 'map/infofile/InfoFile.cpp'; else $(CYGPATH_W) '$(srcdir)/map/infofile/InfoFile.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/infofile/$(DEPDIR)/darkradiant-InfoFile.Tpo map/infofile/$(DEPDIR)/darkradiant-InfoFile.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/infofile/InfoFile.cpp' object='map/infofile/darkradiant-InfoFile.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/infofile/darkradiant-InfoFile.obj `if test -f 'map/infofile/InfoFile.cpp'; then $(CYGPATH_W) 'map/infofile/InfoFile.cpp'; else $(CYGPATH_W) '$(srcdir)/map/infofile/InfoFile.cpp'; fi`
+
+map/infofile/darkradiant-InfoFileExporter.o: map/infofile/InfoFileExporter.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/infofile/darkradiant-InfoFileExporter.o -MD -MP -MF map/infofile/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo -c -o map/infofile/darkradiant-InfoFileExporter.o `test -f 'map/infofile/InfoFileExporter.cpp' || echo '$(srcdir)/'`map/infofile/InfoFileExporter.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/infofile/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo map/infofile/$(DEPDIR)/darkradiant-InfoFileExporter.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/infofile/InfoFileExporter.cpp' object='map/infofile/darkradiant-InfoFileExporter.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-InfoFile.o `test -f 'map/InfoFile.cpp' || echo '$(srcdir)/'`map/InfoFile.cpp
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/infofile/darkradiant-InfoFileExporter.o `test -f 'map/infofile/InfoFileExporter.cpp' || echo '$(srcdir)/'`map/infofile/InfoFileExporter.cpp
 
-map/darkradiant-InfoFile.obj: map/InfoFile.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-InfoFile.obj -MD -MP -MF map/$(DEPDIR)/darkradiant-InfoFile.Tpo -c -o map/darkradiant-InfoFile.obj `if test -f 'map/InfoFile.cpp'; then $(CYGPATH_W) 'map/InfoFile.cpp'; else $(CYGPATH_W) '$(srcdir)/map/InfoFile.cpp'; fi`
- at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/$(DEPDIR)/darkradiant-InfoFile.Tpo map/$(DEPDIR)/darkradiant-InfoFile.Po
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/InfoFile.cpp' object='map/darkradiant-InfoFile.obj' libtool=no @AMDEPBACKSLASH@
+map/infofile/darkradiant-InfoFileExporter.obj: map/infofile/InfoFileExporter.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/infofile/darkradiant-InfoFileExporter.obj -MD -MP -MF map/infofile/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo -c -o map/infofile/darkradiant-InfoFileExporter.obj `if test -f 'map/infofile/InfoFileExporter.cpp'; then $(CYGPATH_W) 'map/infofile/InfoFileExporter.cpp'; else $(CYGPATH_W) '$(srcdir)/map/infofile/InfoFileExporter.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/infofile/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo map/infofile/$(DEPDIR)/darkradiant-InfoFileExporter.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/infofile/InfoFileExporter.cpp' object='map/infofile/darkradiant-InfoFileExporter.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-InfoFile.obj `if test -f 'map/InfoFile.cpp'; then $(CYGPATH_W) 'map/InfoFile.cpp'; else $(CYGPATH_W) '$(srcdir)/map/InfoFile.cpp'; fi`
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/infofile/darkradiant-InfoFileExporter.obj `if test -f 'map/infofile/InfoFileExporter.cpp'; then $(CYGPATH_W) 'map/infofile/InfoFileExporter.cpp'; else $(CYGPATH_W) '$(srcdir)/map/infofile/InfoFileExporter.cpp'; fi`
 
 map/darkradiant-MapFileManager.o: map/MapFileManager.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-MapFileManager.o -MD -MP -MF map/$(DEPDIR)/darkradiant-MapFileManager.Tpo -c -o map/darkradiant-MapFileManager.o `test -f 'map/MapFileManager.cpp' || echo '$(srcdir)/'`map/MapFileManager.cpp
@@ -5095,20 +5298,6 @@ map/algorithm/darkradiant-MapImporter.obj: map/algorithm/MapImporter.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/algorithm/darkradiant-MapImporter.obj `if test -f 'map/algorithm/MapImporter.cpp'; then $(CYGPATH_W) 'map/algorithm/MapImporter.cpp'; else $(CYGPATH_W) '$(srcdir)/map/algorithm/MapImporter.cpp'; fi`
 
-map/algorithm/darkradiant-InfoFileExporter.o: map/algorithm/InfoFileExporter.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/algorithm/darkradiant-InfoFileExporter.o -MD -MP -MF map/algorithm/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo -c -o map/algorithm/darkradiant-InfoFileExporter.o `test -f 'map/algorithm/InfoFileExporter.cpp' || echo '$(srcdir)/'`map/algorithm/InfoFileExporter.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/algorithm/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo map/algorithm/$(DEPDIR)/darkradiant-InfoFileExporter.Po
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/algorithm/InfoFileExporter.cpp' object='map/algorithm/darkradiant-InfoFileExporter.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/algorithm/darkradiant-InfoFileExporter.o `test -f 'map/algorithm/InfoFileExporter.cpp' || echo '$(srcdir)/'`map/algorithm/InfoFileExporter.cpp
-
-map/algorithm/darkradiant-InfoFileExporter.obj: map/algorithm/InfoFileExporter.cpp
- at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/algorithm/darkradiant-InfoFileExporter.obj -MD -MP -MF map/algorithm/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo -c -o map/algorithm/darkradiant-InfoFileExporter.obj `if test -f 'map/algorithm/InfoFileExporter.cpp'; then $(CYGPATH_W) 'map/algorithm/InfoFileExporter.cpp'; else $(CYGPATH_W) '$(srcdir)/map/algorithm/InfoFileExporter.cpp'; fi`
- at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/algorithm/$(DEPDIR)/darkradiant-InfoFileExporter.Tpo map/algorithm/$(DEPDIR)/darkradiant-InfoFileExporter.Po
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/algorithm/InfoFileExporter.cpp' object='map/algorithm/darkradiant-InfoFileExporter.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/algorithm/darkradiant-InfoFileExporter.obj `if test -f 'map/algorithm/InfoFileExporter.cpp'; then $(CYGPATH_W) 'map/algorithm/InfoFileExporter.cpp'; else $(CYGPATH_W) '$(srcdir)/map/algorithm/InfoFileExporter.cpp'; fi`
-
 map/darkradiant-CounterManager.o: map/CounterManager.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-CounterManager.o -MD -MP -MF map/$(DEPDIR)/darkradiant-CounterManager.Tpo -c -o map/darkradiant-CounterManager.o `test -f 'map/CounterManager.cpp' || echo '$(srcdir)/'`map/CounterManager.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/$(DEPDIR)/darkradiant-CounterManager.Tpo map/$(DEPDIR)/darkradiant-CounterManager.Po
@@ -5249,6 +5438,34 @@ map/darkradiant-MapFormatManager.obj: map/MapFormatManager.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-MapFormatManager.obj `if test -f 'map/MapFormatManager.cpp'; then $(CYGPATH_W) 'map/MapFormatManager.cpp'; else $(CYGPATH_W) '$(srcdir)/map/MapFormatManager.cpp'; fi`
 
+map/darkradiant-AasFileManager.o: map/AasFileManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-AasFileManager.o -MD -MP -MF map/$(DEPDIR)/darkradiant-AasFileManager.Tpo -c -o map/darkradiant-AasFileManager.o `test -f 'map/AasFileManager.cpp' || echo '$(srcdir)/'`map/AasFileManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/$(DEPDIR)/darkradiant-AasFileManager.Tpo map/$(DEPDIR)/darkradiant-AasFileManager.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/AasFileManager.cpp' object='map/darkradiant-AasFileManager.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-AasFileManager.o `test -f 'map/AasFileManager.cpp' || echo '$(srcdir)/'`map/AasFileManager.cpp
+
+map/darkradiant-AasFileManager.obj: map/AasFileManager.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-AasFileManager.obj -MD -MP -MF map/$(DEPDIR)/darkradiant-AasFileManager.Tpo -c -o map/darkradiant-AasFileManager.obj `if test -f 'map/AasFileManager.cpp'; then $(CYGPATH_W) 'map/AasFileManager.cpp'; else $(CYGPATH_W) '$(srcdir)/map/AasFileManager.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/$(DEPDIR)/darkradiant-AasFileManager.Tpo map/$(DEPDIR)/darkradiant-AasFileManager.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/AasFileManager.cpp' object='map/darkradiant-AasFileManager.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-AasFileManager.obj `if test -f 'map/AasFileManager.cpp'; then $(CYGPATH_W) 'map/AasFileManager.cpp'; else $(CYGPATH_W) '$(srcdir)/map/AasFileManager.cpp'; fi`
+
+map/darkradiant-RenderableAasFile.o: map/RenderableAasFile.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-RenderableAasFile.o -MD -MP -MF map/$(DEPDIR)/darkradiant-RenderableAasFile.Tpo -c -o map/darkradiant-RenderableAasFile.o `test -f 'map/RenderableAasFile.cpp' || echo '$(srcdir)/'`map/RenderableAasFile.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/$(DEPDIR)/darkradiant-RenderableAasFile.Tpo map/$(DEPDIR)/darkradiant-RenderableAasFile.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/RenderableAasFile.cpp' object='map/darkradiant-RenderableAasFile.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-RenderableAasFile.o `test -f 'map/RenderableAasFile.cpp' || echo '$(srcdir)/'`map/RenderableAasFile.cpp
+
+map/darkradiant-RenderableAasFile.obj: map/RenderableAasFile.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT map/darkradiant-RenderableAasFile.obj -MD -MP -MF map/$(DEPDIR)/darkradiant-RenderableAasFile.Tpo -c -o map/darkradiant-RenderableAasFile.obj `if test -f 'map/RenderableAasFile.cpp'; then $(CYGPATH_W) 'map/RenderableAasFile.cpp'; else $(CYGPATH_W) '$(srcdir)/map/RenderableAasFile.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) map/$(DEPDIR)/darkradiant-RenderableAasFile.Tpo map/$(DEPDIR)/darkradiant-RenderableAasFile.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='map/RenderableAasFile.cpp' object='map/darkradiant-RenderableAasFile.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o map/darkradiant-RenderableAasFile.obj `if test -f 'map/RenderableAasFile.cpp'; then $(CYGPATH_W) 'map/RenderableAasFile.cpp'; else $(CYGPATH_W) '$(srcdir)/map/RenderableAasFile.cpp'; fi`
+
 clipper/darkradiant-ClipPoint.o: clipper/ClipPoint.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT clipper/darkradiant-ClipPoint.o -MD -MP -MF clipper/$(DEPDIR)/darkradiant-ClipPoint.Tpo -c -o clipper/darkradiant-ClipPoint.o `test -f 'clipper/ClipPoint.cpp' || echo '$(srcdir)/'`clipper/ClipPoint.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) clipper/$(DEPDIR)/darkradiant-ClipPoint.Tpo clipper/$(DEPDIR)/darkradiant-ClipPoint.Po
@@ -5724,6 +5941,8 @@ distclean-generic:
 	-rm -f map/$(am__dirstamp)
 	-rm -f map/algorithm/$(DEPDIR)/$(am__dirstamp)
 	-rm -f map/algorithm/$(am__dirstamp)
+	-rm -f map/infofile/$(DEPDIR)/$(am__dirstamp)
+	-rm -f map/infofile/$(am__dirstamp)
 	-rm -f modulesystem/$(DEPDIR)/$(am__dirstamp)
 	-rm -f modulesystem/$(am__dirstamp)
 	-rm -f namespace/$(DEPDIR)/$(am__dirstamp)
@@ -5748,6 +5967,8 @@ distclean-generic:
 	-rm -f selection/algorithm/$(am__dirstamp)
 	-rm -f selection/clipboard/$(DEPDIR)/$(am__dirstamp)
 	-rm -f selection/clipboard/$(am__dirstamp)
+	-rm -f selection/group/$(DEPDIR)/$(am__dirstamp)
+	-rm -f selection/group/$(am__dirstamp)
 	-rm -f selection/selectionset/$(DEPDIR)/$(am__dirstamp)
 	-rm -f selection/selectionset/$(am__dirstamp)
 	-rm -f selection/shaderclipboard/$(DEPDIR)/$(am__dirstamp)
@@ -5760,6 +5981,8 @@ distclean-generic:
 	-rm -f textool/$(am__dirstamp)
 	-rm -f textool/item/$(DEPDIR)/$(am__dirstamp)
 	-rm -f textool/item/$(am__dirstamp)
+	-rm -f ui/aas/$(DEPDIR)/$(am__dirstamp)
+	-rm -f ui/aas/$(am__dirstamp)
 	-rm -f ui/about/$(DEPDIR)/$(am__dirstamp)
 	-rm -f ui/about/$(am__dirstamp)
 	-rm -f ui/animationpreview/$(DEPDIR)/$(am__dirstamp)
@@ -5830,7 +6053,7 @@ clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
 	clean-libtool mostlyclean-am
 
 distclean: distclean-am
-	-rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEPDIR) selection/selectio [...]
+	-rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) map/infofile/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEP [...]
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-tags
@@ -5876,7 +6099,7 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-	-rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEPDIR) selection/selectio [...]
+	-rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) map/infofile/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEP [...]
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
diff --git a/radiant/RadiantModule.cpp b/radiant/RadiantModule.cpp
index 0a37d83..1706115 100644
--- a/radiant/RadiantModule.cpp
+++ b/radiant/RadiantModule.cpp
@@ -19,7 +19,6 @@
 
 #include "scene/Node.h"
 
-#include "map/AutoSaver.h"
 #include "map/PointFile.h"
 #include "ui/texturebrowser/TextureBrowser.h"
 #include "ui/mediabrowser/MediaBrowser.h"
@@ -43,6 +42,7 @@
 #include "ui/surfaceinspector/SurfaceInspector.h"
 #include "ui/transform/TransformDialog.h"
 #include "ui/mapinfo/MapInfoDialog.h"
+#include "ui/layers/LayerControlDialog.h"
 #include "ui/animationpreview/MD5AnimationViewer.h"
 #include "ui/commandlist/CommandList.h"
 #include "ui/findshader/FindShader.h"
@@ -52,6 +52,7 @@
 #include "map/FindMapElements.h"
 #include "ui/modelselector/ModelSelector.h"
 #include "EventRateLimiter.h"
+#include "selection/shaderclipboard/ShaderClipboard.h"
 
 #include <wx/app.h>
 
@@ -207,7 +208,6 @@ void RadiantModule::initialiseModule(const ApplicationContext& ctx)
 
 	ui::TexTool::registerCommands();
 	ui::MediaBrowser::registerCommandsAndPreferences();
-    map::AutoSaver().init();
     
 	selection::algorithm::registerCommands();
 	brush::algorithm::registerCommands();
@@ -234,9 +234,10 @@ void RadiantModule::shutdownModule()
 void RadiantModule::postModuleInitialisation()
 {
 	// Create the empty Settings node and set the title to empty.
-	ui::PrefDialog::Instance().createOrFindPage(_("Game"));
-	ui::PrefPagePtr settingsPage = ui::PrefDialog::Instance().createOrFindPage(_("Settings"));
-	settingsPage->setTitle("");
+	GlobalPreferenceSystem().getPage(_("Game"));
+
+	IPreferencePage& settingsPage = GlobalPreferenceSystem().getPage(_("Settings"));
+	settingsPage.setTitle("");
 
 	// Construct the MRU commands and menu structure, load the recently used files
 	GlobalMRU().initialise();
@@ -249,6 +250,11 @@ void RadiantModule::postModuleInitialisation()
     // Initialise the mainframe
     GlobalMainFrame().construct();
 
+	// Initialise the shaderclipboard
+	GlobalShaderClipboard().clear();
+
+	ui::LayerControlDialog::init();
+
 	// Broadcast the startup event
     broadcastStartupEvent();
 
@@ -258,6 +264,9 @@ void RadiantModule::postModuleInitialisation()
     // Pre-load models
     ui::ModelSelector::Populate();
 
+	// Show the top level window as late as possible
+	GlobalMainFrame().getWxTopLevelWindow()->Show();
+
     time_t localtime;
     time(&localtime);
     rMessage() << "Startup complete at " << ctime(&localtime) << std::endl;
@@ -266,7 +275,7 @@ void RadiantModule::postModuleInitialisation()
 void RadiantModule::registerUICommands()
 {
 	GlobalCommandSystem().addCommand("ProjectSettings", ui::PrefDialog::ShowProjectSettings);
-	GlobalCommandSystem().addCommand("Preferences", ui::PrefDialog::ShowDialog);
+	GlobalCommandSystem().addCommand("Preferences", ui::PrefDialog::ShowPrefDialog);
 
 	GlobalCommandSystem().addCommand("ToggleConsole", ui::Console::toggle);
 	GlobalCommandSystem().addCommand("ToggleLightInspector", ui::LightInspector::toggleInspector);
diff --git a/radiant/RadiantModule.h b/radiant/RadiantModule.h
index d9846be..8335852 100644
--- a/radiant/RadiantModule.h
+++ b/radiant/RadiantModule.h
@@ -17,7 +17,7 @@ class RadiantModule :
     // Our signals
     sigc::signal<void> _radiantStarted;
     sigc::signal<void> _radiantShutdown;
-
+    
     // Thread manager instance
     mutable std::unique_ptr<RadiantThreadManager> _threadManager;
 
@@ -30,8 +30,9 @@ public:
 	void broadcastStartupEvent();
 
     // IRadiant implementation
-    sigc::signal<void> signal_radiantStarted() const;
-    sigc::signal<void> signal_radiantShutdown() const;
+    sigc::signal<void> signal_radiantStarted() const override;
+    sigc::signal<void> signal_radiantShutdown() const override;
+
     ThreadManager& getThreadManager();
 	void performLongRunningOperation(
 		const std::function<void(ILongRunningOperation&)>& operationFunc,
diff --git a/radiant/brush/Brush.cpp b/radiant/brush/Brush.cpp
index 666a6cb..664d80e 100644
--- a/radiant/brush/Brush.cpp
+++ b/radiant/brush/Brush.cpp
@@ -1094,8 +1094,9 @@ bool Brush::buildWindings() {
                 // update brush bounds
                 const Winding& winding = f.getWinding();
 
-                for (Winding::const_iterator i = winding.begin(); i != winding.end(); ++i) {
-                    m_aabb_local.includePoint(i->vertex);
+                for (const WindingVertex& wv : winding)
+				{
+                    m_aabb_local.includePoint(wv.vertex);
                 }
 
                 // update texture coordinates
diff --git a/radiant/brush/BrushModule.cpp b/radiant/brush/BrushModule.cpp
index 53c22b2..c13381d 100644
--- a/radiant/brush/BrushModule.cpp
+++ b/radiant/brush/BrushModule.cpp
@@ -24,14 +24,14 @@
 void BrushModuleImpl::constructPreferences()
 {
 	// Add a page to the given group
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Primitives"));
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Primitives"));
 
 	// Add the default texture scale preference and connect it to the according registryKey
 	// Note: this should be moved somewhere else, I think
-	page->appendEntry(_("Default texture scale"), "user/ui/textures/defaultTextureScale");
+	page.appendEntry(_("Default texture scale"), "user/ui/textures/defaultTextureScale");
 
 	// The checkbox to enable/disable the texture lock option
-	page->appendCheckBox("", _("Enable Texture Lock (for Brushes)"), "user/ui/brush/textureLock");
+	page.appendCheckBox(_("Enable Texture Lock (for Brushes)"), "user/ui/brush/textureLock");
 }
 
 void BrushModuleImpl::construct()
diff --git a/radiant/brush/BrushNode.cpp b/radiant/brush/BrushNode.cpp
index 24a9d03..d74f037 100644
--- a/radiant/brush/BrushNode.cpp
+++ b/radiant/brush/BrushNode.cpp
@@ -82,44 +82,6 @@ void BrushNode::snapComponents(float snap) {
 	}
 }
 
-void BrushNode::invertSelected()
-{
-	// Override default behaviour of SelectableNode, we have components
-
-	// Check if we are in component mode or not
-	if (GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
-	{
-		// Non-component mode, invert the selection of the whole brush
-		SelectableNode::invertSelected();
-	} 
-	else 
-	{
-		// Component mode, invert the component selection
-		switch (GlobalSelectionSystem().ComponentMode()) {
-			case SelectionSystem::eVertex:
-				for (VertexInstances::iterator i = m_vertexInstances.begin(); i != m_vertexInstances.end(); ++i)
-				{
-					i->invertSelected();
-				}
-				break;
-			case SelectionSystem::eEdge:
-				for (EdgeInstances::iterator i = m_edgeInstances.begin(); i != m_edgeInstances.end(); ++i)
-				{
-					i->invertSelected();
-				}
-				break;
-			case SelectionSystem::eFace:
-				for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i)
-				{
-					i->invertSelected();
-				}
-				break;
-			case SelectionSystem::eDefault:
-				break;
-		} // switch
-	}
-}
-
 void BrushNode::testSelect(Selector& selector, SelectionTest& test) {
 	test.BeginMesh(localToWorld());
 
@@ -152,6 +114,34 @@ void BrushNode::setSelectedComponents(bool select, SelectionSystem::EComponentMo
 	}
 }
 
+void BrushNode::invertSelectedComponents(SelectionSystem::EComponentMode mode)
+{
+	// Component mode, invert the component selection
+	switch (mode)
+	{
+	case SelectionSystem::eVertex:
+		for (brush::VertexInstance& vertex : m_vertexInstances)
+		{
+			vertex.invertSelected();
+		}
+		break;
+	case SelectionSystem::eEdge:
+		for (EdgeInstance& edge : m_edgeInstances)
+		{
+			edge.invertSelected();
+		}
+		break;
+	case SelectionSystem::eFace:
+		for (FaceInstance& face : m_faceInstances)
+		{
+			face.invertSelected();
+		}
+		break;
+	case SelectionSystem::eDefault:
+		break;
+	} // switch
+}
+
 void BrushNode::testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) {
 	test.BeginMesh(localToWorld());
 
@@ -217,7 +207,7 @@ void BrushNode::selectReversedPlanes(Selector& selector, const SelectedPlanes& s
 	}
 }
 
-void BrushNode::selectedChangedComponent(const Selectable& selectable)
+void BrushNode::selectedChangedComponent(const ISelectable& selectable)
 {
 	_renderableComponentsNeedUpdate = true;
 
@@ -393,9 +383,11 @@ void BrushNode::viewChanged() const {
 	m_viewChanged = true;
 }
 
-bool BrushNode::isHighlighted() const
+std::size_t BrushNode::getHighlightFlags()
 {
-	return isSelected();
+	if (!isSelected()) return Highlight::NoHighlight;
+
+	return isGroupMember() ? (Highlight::Selected | Highlight::GroupMember) : Highlight::Selected;
 }
 
 void BrushNode::evaluateViewDependent(const VolumeTest& volume, const Matrix4& localToWorld) const
@@ -412,6 +404,7 @@ void BrushNode::evaluateViewDependent(const VolumeTest& volume, const Matrix4& l
 
 	std::size_t numVisibleFaces(0);
 	bool* j = faces_visible;
+	bool forceVisible = isForcedVisible();
 
 	// Iterator to an index of a visible face
 	std::size_t* visibleFaceIter = visibleFaceIndices;
@@ -423,7 +416,7 @@ void BrushNode::evaluateViewDependent(const VolumeTest& volume, const Matrix4& l
 	{
 		// Check if face is filtered before adding to visibility matrix
 		// greebo: Removed localToWorld transformation here, brushes don't have a non-identity l2w
-		if (i->faceIsVisible() && i->intersectVolume(volume))
+		if (forceVisible || (i->faceIsVisible() && i->intersectVolume(volume)))
 		{
 			*j = true;
 
@@ -449,13 +442,16 @@ void BrushNode::renderSolid(RenderableCollector& collector,
 
 	assert(_renderEntity); // brushes rendered without parent entity - no way!
 
+	// Check for the override status of this brush
+	bool forceVisible = isForcedVisible();
+
     // Submit the lights and renderable geometry for each face
 	for (FaceInstances::const_iterator i = m_faceInstances.begin();
          i != m_faceInstances.end();
          ++i)
     {
 		// Skip invisible faces before traversing further
-		if (!i->faceIsVisible()) continue;
+		if (!forceVisible && !i->faceIsVisible()) continue;
 
         collector.setLights(i->m_lights);
 
@@ -502,7 +498,7 @@ void BrushNode::renderSelectedPoints(RenderableCollector& collector,
 	update_selected();
 	if (!_selectedPoints.empty())
     {
-		collector.highlightPrimitives(false);
+		collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
 		collector.SetState(BrushNode::m_state_selpoint, RenderableCollector::eWireframeOnly);
 		collector.SetState(BrushNode::m_state_selpoint, RenderableCollector::eFullMaterials);
 		collector.addRenderable(_selectedPoints, localToWorld);
diff --git a/radiant/brush/BrushNode.h b/radiant/brush/BrushNode.h
index 0cabf92..c681a88 100644
--- a/radiant/brush/BrushNode.h
+++ b/radiant/brush/BrushNode.h
@@ -6,7 +6,7 @@
 #include "iscenegraph.h"
 
 #include "Brush.h"
-#include "SelectableNode.h"
+#include "scene/SelectableNode.h"
 #include "FaceInstance.h"
 #include "EdgeInstance.h"
 #include "VertexInstance.h"
@@ -88,16 +88,14 @@ public:
 	// Bounded implementation
 	virtual const AABB& localAABB() const;
 
-	// Override ObservedSelectable implementation
-	virtual void invertSelected();
-
 	// SelectionTestable implementation
 	virtual void testSelect(Selector& selector, SelectionTest& test);
 
 	// ComponentSelectionTestable
-	bool isSelectedComponents() const;
-	void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode);
-	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode);
+	bool isSelectedComponents() const override;
+	void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) override;
+	void invertSelectedComponents(SelectionSystem::EComponentMode mode) override;
+	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) override;
 
 	// override scene::Inode::onRemoveFromScene to deselect the child components
     virtual void onInsertIntoScene(scene::IMapRootNode& root) override;
@@ -106,7 +104,7 @@ public:
 	// ComponentEditable implementation
 	const AABB& getSelectedComponentsBounds() const;
 
-	void selectedChangedComponent(const Selectable& selectable);
+	void selectedChangedComponent(const ISelectable& selectable);
 
 	// PlaneSelectable implementation
 	void selectPlanes(Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback);
@@ -143,13 +141,13 @@ public:
 	void clearLights();
 
 	// Renderable implementation
-	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
-	void viewChanged() const;
-	bool isHighlighted() const;
+	void viewChanged() const override;
+	std::size_t getHighlightFlags() override;
 
 	void evaluateTransform();
 
diff --git a/radiant/brush/EdgeInstance.h b/radiant/brush/EdgeInstance.h
index 422f383..4f3cf39 100644
--- a/radiant/brush/EdgeInstance.h
+++ b/radiant/brush/EdgeInstance.h
@@ -3,7 +3,7 @@
 
 #include "FaceInstance.h"
 
-class EdgeInstance : public Selectable {
+class EdgeInstance : public ISelectable {
 	FaceInstances& m_faceInstances;
 	SelectableEdge* m_edge;
 
diff --git a/radiant/brush/FaceInstance.cpp b/radiant/brush/FaceInstance.cpp
index aef1966..e15a581 100644
--- a/radiant/brush/FaceInstance.cpp
+++ b/radiant/brush/FaceInstance.cpp
@@ -56,7 +56,7 @@ const Face& FaceInstance::getFace() const {
 	return *m_face;
 }
 
-void FaceInstance::selectedChanged(const Selectable& selectable)
+void FaceInstance::selectedChanged(const ISelectable& selectable)
 {
 	if (selectable.isSelected())
 	{
@@ -133,7 +133,7 @@ void FaceInstance::setSelected(SelectionSystem::EComponentMode mode, bool select
 void FaceInstance::invertSelected() {
 	switch (GlobalSelectionSystem().ComponentMode()) {
 		case SelectionSystem::eFace:
-			m_selectable.invertSelected();
+			m_selectable.setSelected(!m_selectable.isSelected());
 			break;
 		case SelectionSystem::eVertex:
 			break;
@@ -173,7 +173,7 @@ void FaceInstance::submitRenderables(RenderableCollector& collector,
 
 		if (selectedComponents())
 		{
-			collector.highlightFaces(true);
+			collector.setHighlightFlag(RenderableCollector::Highlight::Faces, true);
 		}
 
 		m_face->submitRenderables(collector, Matrix4::getIdentity(), entity);
@@ -193,7 +193,7 @@ void FaceInstance::submitRenderables(RenderableCollector& collector,
 
 		if (selectedComponents())
 		{
-			collector.highlightFaces(true);
+			collector.setHighlightFlag(RenderableCollector::Highlight::Faces, true);
 		}
 
 		m_face->submitRenderables(collector, localToWorld, entity);
diff --git a/radiant/brush/FaceInstance.h b/radiant/brush/FaceInstance.h
index 3c2a8ec..d104fe9 100644
--- a/radiant/brush/FaceInstance.h
+++ b/radiant/brush/FaceInstance.h
@@ -44,7 +44,7 @@ public:
 
 	const Face& getFace() const;
 
-	void selectedChanged(const Selectable& selectable);
+	void selectedChanged(const ISelectable& selectable);
 
 	bool selectedVertices() const;
 	bool selectedEdges() const;
diff --git a/radiant/brush/TextureProjection.cpp b/radiant/brush/TextureProjection.cpp
index 29f9f32..bbce582 100644
--- a/radiant/brush/TextureProjection.cpp
+++ b/radiant/brush/TextureProjection.cpp
@@ -200,12 +200,12 @@ void TextureProjection::fitTexture(std::size_t width, std::size_t height, const
     AABB perfect(Vector3(s_repeat * 0.5, t_repeat * 0.5, 0), Vector3(s_repeat * 0.5, t_repeat * 0.5, 1));
 
     // the difference between the current texture transform and the perfectly fitted transform
-    Matrix4 matrix = Matrix4::getTranslation(bounds.origin - perfect.origin);
-    matrix.scaleBy(bounds.extents / perfect.extents, perfect.origin);
-    matrix.invert();
+    Matrix4 diffMatrix = Matrix4::getTranslation(bounds.origin - perfect.origin);
+	diffMatrix.scaleBy(bounds.extents / perfect.extents, perfect.origin);
+	diffMatrix.invert();
 
     // apply the difference to the current texture transform
-    st2tex.premultiplyBy(matrix);
+    st2tex.premultiplyBy(diffMatrix);
 
     setTransform((float)width, (float)height, st2tex);
     normalise((float)width, (float)height);
diff --git a/radiant/brush/VertexInstance.h b/radiant/brush/VertexInstance.h
index be0405e..4390025 100644
--- a/radiant/brush/VertexInstance.h
+++ b/radiant/brush/VertexInstance.h
@@ -6,7 +6,7 @@
 namespace brush {
 
 class VertexInstance :
-	public Selectable
+	public ISelectable
 {
 	FaceInstances& m_faceInstances;
 	SelectableVertex* m_vertex;
diff --git a/radiant/brush/csg/CSG.cpp b/radiant/brush/csg/CSG.cpp
index 368471e..e0ea44d 100644
--- a/radiant/brush/csg/CSG.cpp
+++ b/radiant/brush/csg/CSG.cpp
@@ -323,8 +323,8 @@ void subtractBrushesFromUnselected(const cmd::ArgumentList& args)
 // greebo: TODO: Make this a member method of the Brush class
 bool Brush_merge(Brush& brush, const BrushPtrVector& in, bool onlyshape) {
 	// gather potential outer faces
-	typedef std::vector<const Face*> Faces;
-	Faces faces;
+	typedef std::vector<const Face*> FaceList;
+	FaceList faces;
 
 	for (BrushPtrVector::const_iterator i(in.begin()); i != in.end(); ++i) {
 		(*i)->getBrush().evaluateBRep();
@@ -356,7 +356,7 @@ bool Brush_merge(Brush& brush, const BrushPtrVector& in, bool onlyshape) {
 			}
 
 			// check faces already stored
-			for (Faces::const_iterator m = faces.begin(); !skip && m != faces.end(); ++m) {
+			for (FaceList::const_iterator m = faces.begin(); !skip && m != faces.end(); ++m) {
 				const Face& face2 = *(*m);
 
 				// face equals another face
@@ -388,7 +388,7 @@ bool Brush_merge(Brush& brush, const BrushPtrVector& in, bool onlyshape) {
 		}
 	}
 
-	for (Faces::const_iterator i = faces.begin(); i != faces.end(); ++i) {
+	for (FaceList::const_iterator i = faces.begin(); i != faces.end(); ++i) {
 		if (!brush.addFace(*(*i))) {
 			// result would have too many sides
 			return false;
diff --git a/radiant/camera/CamRenderer.cpp b/radiant/camera/CamRenderer.cpp
index 2819e6e..9195ab8 100644
--- a/radiant/camera/CamRenderer.cpp
+++ b/radiant/camera/CamRenderer.cpp
@@ -44,14 +44,17 @@ void CamRenderer::PopState()
     _stateStack.pop_back();
 }
 
-void CamRenderer::highlightFaces(bool enable)
+void CamRenderer::setHighlightFlag(Highlight::Flags flags, bool enabled)
 {
-    _stateStack.back().highlightFaces = enable;
-}
-
-void CamRenderer::highlightPrimitives(bool enable)
-{
-    _stateStack.back().highlightPrimitives = enable;
+	if (flags & Highlight::Faces)
+	{
+		_stateStack.back().highlightFaces = enabled;
+	}
+
+	if (flags & Highlight::Primitives)
+	{
+		_stateStack.back().highlightPrimitives = enabled;
+	}
 }
 
 void CamRenderer::setLights(const LightList& lights)
diff --git a/radiant/camera/CamRenderer.h b/radiant/camera/CamRenderer.h
index bb50e87..dbb393a 100644
--- a/radiant/camera/CamRenderer.h
+++ b/radiant/camera/CamRenderer.h
@@ -51,8 +51,7 @@ public:
     bool supportsFullMaterials() const;
     void PushState();
     void PopState();
-    void highlightFaces(bool enable = true);
-    void highlightPrimitives(bool enable = true);
+	void setHighlightFlag(Highlight::Flags flags, bool enabled) override;
     void setLights(const LightList& lights);
     void addRenderable(const OpenGLRenderable& renderable, const Matrix4& world);
 	void addRenderable(const OpenGLRenderable& renderable,
diff --git a/radiant/camera/CamWnd.cpp b/radiant/camera/CamWnd.cpp
index cf9c9cd..96787a5 100644
--- a/radiant/camera/CamWnd.cpp
+++ b/radiant/camera/CamWnd.cpp
@@ -149,17 +149,12 @@ CamWnd::CamWnd(wxWindow* parent) :
     _freeMoveEnabled(false),
 	_wxGLWidget(new wxutil::GLWidget(_mainWxWidget, std::bind(&CamWnd::onRender, this), "CamWnd")),
     _timer(this),
-    _timerLock(false),
-    _deferredDraw(std::bind(&CamWnd::performDeferredDraw, this))
+    _timerLock(false)
 {
 	Connect(wxEVT_TIMER, wxTimerEventHandler(CamWnd::onFrame), NULL, this);
 
     constructGUIComponents();
 
-    GlobalMap().signal_mapValidityChanged().connect(
-        sigc::mem_fun(_deferredDraw, &DeferredDraw::onMapValidChanged)
-    );
-
     // Deactivate all commands, just to make sure
     disableDiscreteMoveEvents();
     disableFreeMoveEvents();
@@ -335,7 +330,7 @@ SelectionTestPtr CamWnd::createSelectionTestForPoint(const Vector2& point)
     float selectEpsilon = registry::getValue<float>(RKEY_SELECT_EPSILON);
 
     // Get the mouse position
-    DeviceVector deviceEpsilon(selectEpsilon / getCamera().width, selectEpsilon / getCamera().height);
+    Vector2 deviceEpsilon(selectEpsilon / getCamera().width, selectEpsilon / getCamera().height);
 
     // Copy the current view and constrain it to a small rectangle
     render::View scissored(_view);
@@ -775,18 +770,13 @@ void CamWnd::onRender()
 	draw();
 }
 
-void CamWnd::performDeferredDraw()
-{
-	_wxGLWidget->Refresh(false);
-}
-
 void CamWnd::draw()
 {
     if (_drawing) return;
 
     util::ScopedBoolLock lock(_drawing);
 
-    if (GlobalMap().isValid() && GlobalMainFrame().screenUpdatesEnabled())
+    if (GlobalMainFrame().screenUpdatesEnabled())
 	{
         GlobalOpenGL().assertNoErrors();
 
@@ -929,7 +919,7 @@ void CamWnd::queueDraw()
         return;
     }
 
-    _deferredDraw.draw();
+	_wxGLWidget->Refresh(false);
 }
 
 Vector3 CamWnd::getCameraOrigin() const
@@ -942,6 +932,21 @@ void CamWnd::setCameraOrigin(const Vector3& origin)
     _camera.setOrigin(origin);
 }
 
+Vector3 CamWnd::getRightVector() const
+{
+	return _camera.vright;
+}
+
+Vector3 CamWnd::getUpVector() const
+{
+	return _camera.vup;
+}
+
+Vector3 CamWnd::getForwardVector() const
+{
+	return _camera.vpn;
+}
+
 Vector3 CamWnd::getCameraAngles() const
 {
     return _camera.getAngles();
diff --git a/radiant/camera/CamWnd.h b/radiant/camera/CamWnd.h
index fb040b2..79f42d3 100644
--- a/radiant/camera/CamWnd.h
+++ b/radiant/camera/CamWnd.h
@@ -15,7 +15,6 @@
 #include <wx/glcanvas.h>
 #include <wx/timer.h>
 #include "render/View.h"
-#include "map/DeferredDraw.h"
 
 #include "RadiantCameraView.h"
 #include "Camera.h"
@@ -77,8 +76,6 @@ private:
 	wxTimer _timer;
     bool _timerLock; // to avoid double-timer-firings
 
-	DeferredDraw _deferredDraw;
-
 	sigc::connection _glExtensionsInitialisedNotifier;
 
     wxutil::KeyEventFilterPtr _escapeListener;
@@ -111,8 +108,12 @@ public:
 
 	Camera& getCamera();
 
-	Vector3 getCameraOrigin() const;
-	void setCameraOrigin(const Vector3& origin);
+	Vector3 getCameraOrigin() const override;
+	void setCameraOrigin(const Vector3& origin) override;
+
+	Vector3 getRightVector() const override;
+	Vector3 getUpVector() const override;
+	Vector3 getForwardVector() const override;
 
 	Vector3 getCameraAngles() const;
 	void setCameraAngles(const Vector3& angles);
@@ -174,8 +175,6 @@ private:
 	void onRender();
 	void drawTime();
 
-	void performDeferredDraw();
-
     CameraMouseToolEvent createMouseEvent(const Vector2& point, const Vector2& delta = Vector2(0, 0));
 
 	void onGLResize(wxSizeEvent& ev);
diff --git a/radiant/camera/CameraSettings.cpp b/radiant/camera/CameraSettings.cpp
index 80ef3ae..dbd3a1a 100644
--- a/radiant/camera/CameraSettings.cpp
+++ b/radiant/camera/CameraSettings.cpp
@@ -53,27 +53,25 @@ void CameraSettings::observeKey(const std::string& key)
 
 void CameraSettings::constructPreferencePage() 
 {
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Camera"));
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Camera"));
 
 	// Add the sliders for the movement and angle speed and connect them to the observer
-    page->appendSlider(_("Movement Speed (game units)"), RKEY_MOVEMENT_SPEED, TRUE, 100, 1, MAX_CAMERA_SPEED, 1, 1, 1);
-    page->appendSlider(_("Rotation Speed"), RKEY_ROTATION_SPEED, TRUE, 3, 1, 180, 1, 10, 10);
+    page.appendSlider(_("Movement Speed (game units)"), RKEY_MOVEMENT_SPEED, 1, MAX_CAMERA_SPEED, 1, 1);
+    page.appendSlider(_("Rotation Speed"), RKEY_ROTATION_SPEED, 1, 180, 1, 10);
 
 	// Add the checkboxes and connect them with the registry key and the according observer
-	page->appendCheckBox("", _("Freelook mode can be toggled"), RKEY_TOGGLE_FREE_MOVE);
-	page->appendCheckBox("", _("Discrete movement (non-freelook mode)"), RKEY_DISCRETE_MOVEMENT);
-	page->appendCheckBox("", _("Enable far-clip plane (hides distant objects)"), RKEY_ENABLE_FARCLIP);
+	page.appendCheckBox(_("Freelook mode can be toggled"), RKEY_TOGGLE_FREE_MOVE);
+	page.appendCheckBox(_("Discrete movement (non-freelook mode)"), RKEY_DISCRETE_MOVEMENT);
+	page.appendCheckBox(_("Enable far-clip plane (hides distant objects)"), RKEY_ENABLE_FARCLIP);
 
 	// Add the "inverse mouse vertical axis in free-look mode" preference
-	page->appendCheckBox("", _("Invert mouse vertical axis (freelook mode)"), RKEY_INVERT_MOUSE_VERTICAL_AXIS);
+	page.appendCheckBox(_("Invert mouse vertical axis (freelook mode)"), RKEY_INVERT_MOUSE_VERTICAL_AXIS);
 
 	// States whether the selection boxes are stippled or not
-	page->appendCheckBox("", _("Solid selection boxes"), RKEY_SOLID_SELECTION_BOXES);
+	page.appendCheckBox(_("Solid selection boxes"), RKEY_SOLID_SELECTION_BOXES);
 
     // Whether to show the toolbar (to please the screenspace addicts)
-    page->appendCheckBox(
-        "", _("Show camera toolbar"), RKEY_SHOW_CAMERA_TOOLBAR
-    );
+    page.appendCheckBox(_("Show camera toolbar"), RKEY_SHOW_CAMERA_TOOLBAR);
 }
 
 bool CameraSettings::showCameraToolbar() const
diff --git a/radiant/camera/GlobalCamera.cpp b/radiant/camera/GlobalCamera.cpp
index 199e8d5..621f0ea 100644
--- a/radiant/camera/GlobalCamera.cpp
+++ b/radiant/camera/GlobalCamera.cpp
@@ -16,6 +16,7 @@
 #include "tools/ShaderClipboardTools.h"
 #include "tools/JumpToObjectTool.h"
 #include "tools/FreeMoveTool.h"
+#include "tools/PanViewTool.h"
 
 #include "FloatingCamWnd.h"
 #include <functional>
@@ -600,6 +601,7 @@ void GlobalCameraManager::initialiseModule(const ApplicationContext& ctx)
     IMouseToolGroup& toolGroup = GlobalMouseToolManager().getGroup(IMouseToolGroup::Type::CameraView);
 
     toolGroup.registerMouseTool(std::make_shared<FreeMoveTool>());
+	toolGroup.registerMouseTool(std::make_shared<PanViewTool>());
     toolGroup.registerMouseTool(std::make_shared<PickShaderTool>());
     toolGroup.registerMouseTool(std::make_shared<PasteShaderProjectedTool>());
     toolGroup.registerMouseTool(std::make_shared<PasteShaderNaturalTool>());
diff --git a/radiant/camera/tools/PanViewTool.h b/radiant/camera/tools/PanViewTool.h
new file mode 100644
index 0000000..2987176
--- /dev/null
+++ b/radiant/camera/tools/PanViewTool.h
@@ -0,0 +1,89 @@
+#pragma once
+
+#include "imousetool.h"
+#include "i18n.h"
+#include "../GlobalCamera.h"
+#include "../CameraSettings.h"
+
+namespace ui
+{
+
+class PanViewTool :
+	public MouseTool
+{
+public:
+	const std::string& getName() override
+	{
+		static std::string name("PanViewTool");
+		return name;
+	}
+
+	const std::string& getDisplayName() override
+	{
+		static std::string displayName(_("Pan Camera View"));
+		return displayName;
+	}
+
+	unsigned int getPointerMode() override
+	{
+		return PointerMode::Capture | PointerMode::Freeze |
+			PointerMode::Hidden | PointerMode::MotionDeltas;
+	}
+
+	Result onMouseDown(Event& ev) override
+	{
+		try
+		{
+			CameraMouseToolEvent& camEvent = dynamic_cast<CameraMouseToolEvent&>(ev);
+
+			// Don't operate when the camera is already in free look mode
+			if (camEvent.getView().freeMoveEnabled())
+			{
+				return Result::Ignored;
+			}
+
+			return Result::Activated; 
+		}
+		catch (std::bad_cast&)
+		{
+		}
+
+		return Result::Ignored; // not handled
+	}
+
+	Result onMouseMove(Event& ev) override
+	{
+		try
+		{
+			// We use capture mode, so xy event will contain the delta only
+			CameraMouseToolEvent& camEvent = dynamic_cast<CameraMouseToolEvent&>(ev);
+			ICameraView& view = camEvent.getView();
+
+			const float strafespeed = GlobalCamera().getCameraStrafeSpeed();
+
+			double dx = camEvent.getDeviceDelta().x();
+			double dy = camEvent.getDeviceDelta().y();
+
+			Vector3 delta(0, 0, 0);
+
+			delta += view.getRightVector() * strafespeed * dx;
+			delta += view.getUpVector() * strafespeed * dy * (-1); // invert y direction
+
+			view.setCameraOrigin(view.getCameraOrigin() + delta);
+
+			return Result::Continued;
+		}
+		catch (std::bad_cast&)
+		{
+		}
+
+		return Result::Ignored;
+	}
+
+	Result onMouseUp(Event& ev) override
+	{
+		return Result::Finished;
+	}
+};
+
+}
diff --git a/radiant/clipper/Clipper.cpp b/radiant/clipper/Clipper.cpp
index f7b4c02..4ce2578 100644
--- a/radiant/clipper/Clipper.cpp
+++ b/radiant/clipper/Clipper.cpp
@@ -38,10 +38,10 @@ void Clipper::keyChanged()
 }
 
 void Clipper::constructPreferences() {
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Clipper"));
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Clipper"));
 
-	page->appendCheckBox("", _("Clipper tool uses caulk texture"), RKEY_CLIPPER_USE_CAULK);
-	page->appendEntry(_("Caulk shader name"), RKEY_CLIPPER_CAULK_SHADER);
+	page.appendCheckBox(_("Clipper tool uses caulk texture"), RKEY_CLIPPER_USE_CAULK);
+	page.appendEntry(_("Caulk shader name"), RKEY_CLIPPER_CAULK_SHADER);
 }
 
 EViewType Clipper::getViewType() const {
diff --git a/radiant/darkradiant.rc b/radiant/darkradiant.rc
index f773827..c262e14 100644
--- a/radiant/darkradiant.rc
+++ b/radiant/darkradiant.rc
@@ -1,4 +1,5 @@
 #include "../w32deps/wxWidgets/include/wx/msw/wx.rc"
+#include "../include/version.h"
 
 //Microsoft Developer Studio generated resource script.
 //
@@ -72,3 +73,25 @@ END
 /////////////////////////////////////////////////////////////////////////////
 #endif    // not APSTUDIO_INVOKED
 
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+VS_VERSION_INFO VERSIONINFO
+	FILEVERSION        2.0.0
+BEGIN
+	BLOCK "StringFileInfo"
+		BEGIN
+			BLOCK "040904b0"
+			BEGIN
+				VALUE "ProductName", RADIANT_APPNAME
+				VALUE "ProductVersion", RADIANT_VERSION RADIANT_BLANK RADIANT_PLATFORM "\0"
+				VALUE "LegalCopyright", "www.thedarkmod.com" "\0"
+				VALUE "FileDescription", "Level Editor for The Dark Mod" "\0"
+			END
+		END
+	BLOCK "VarFileInfo"
+	BEGIN
+		VALUE "Translation", 0x409, 1200
+	END
+END
diff --git a/radiant/layers/LayerInfoFileModule.cpp b/radiant/layers/LayerInfoFileModule.cpp
new file mode 100644
index 0000000..2aac129
--- /dev/null
+++ b/radiant/layers/LayerInfoFileModule.cpp
@@ -0,0 +1,282 @@
+#include "LayerInfoFileModule.h"
+
+#include "ilayer.h"
+#include "ientity.h"
+#include "itextstream.h"
+#include "scenelib.h"
+#include "scene/LayerValidityCheckWalker.h"
+#include "string/convert.h"
+#include "debugging/ScenegraphUtils.h"
+#include "parser/DefTokeniser.h"
+
+namespace scene
+{
+
+namespace
+{
+	const char* const NODE_TO_LAYER_MAPPING = "NodeToLayerMapping";
+	const char* const LAYER = "Layer";
+	const char* const LAYERS = "Layers";
+	const char* const NODE = "Node";
+}
+
+LayerInfoFileModule::LayerInfoFileModule() :
+	_layerInfoCount(0)
+{
+	_standardLayerList.insert(0);
+}
+
+std::string LayerInfoFileModule::getName()
+{
+	return "Layer Mapping";
+}
+
+void LayerInfoFileModule::onInfoFileSaveStart()
+{
+	_layerInfoCount = 0;
+	_output.str(std::string());
+	_output.clear();
+}
+
+void LayerInfoFileModule::onSavePrimitive(const INodePtr& node, std::size_t entityNum, std::size_t primitiveNum)
+{
+	saveNode(node);
+}
+
+void LayerInfoFileModule::onSaveEntity(const INodePtr& node, std::size_t entityNum)
+{
+	saveNode(node);
+}
+
+void LayerInfoFileModule::saveNode(const INodePtr& node)
+{
+	// Don't export the layer settings for models and particles, as they are not there
+	// at map load/parse time - these shouldn't even be passed in here
+	assert(Node_isEntity(node) || Node_isPrimitive(node));
+
+	// Open a Node block
+	_output << "\t\t" << NODE << " { ";
+
+	scene::LayerList layers = node->getLayers();
+
+	// Write a space-separated list of node IDs
+	for (const scene::LayerList::value_type& i : layers)
+	{
+		_output << i << " ";
+	}
+
+	// Close the Node block
+	_output << "}";
+
+	// Write additional node info, for easier debugging of layer issues
+	_output << " // " << getNodeInfo(node);
+
+	_output << std::endl;
+
+	_layerInfoCount++;
+}
+
+void LayerInfoFileModule::writeBlocks(std::ostream& stream)
+{
+	// Write the layer names block
+	writeLayerNames(stream);
+
+	// Write the NodeToLayerMapping block
+	stream << "\t" << NODE_TO_LAYER_MAPPING << std::endl;
+	stream << "\t{" << std::endl;
+
+	// Write the output buffer to the stream
+	stream << _output.rdbuf();
+
+	// Closing braces of NodeToLayerMapping block
+	stream << "\t}" << std::endl;
+
+	rMessage() << _layerInfoCount << " node-to-layer mappings written." << std::endl;
+}
+
+void LayerInfoFileModule::onInfoFileSaveFinished()
+{
+	_layerInfoCount = 0;
+	_output.str(std::string());
+	_output.clear();
+}
+
+void LayerInfoFileModule::onInfoFileLoadStart()
+{
+	_layerNames.clear();
+	_layerMappings.clear();
+}
+
+bool LayerInfoFileModule::canParseBlock(const std::string& blockName)
+{
+	return blockName == LAYERS || blockName == NODE_TO_LAYER_MAPPING;
+}
+
+void LayerInfoFileModule::parseBlock(const std::string& blockName, parser::DefTokeniser& tok)
+{
+	assert(canParseBlock(blockName));
+
+	if (blockName == LAYERS)
+	{
+		parseLayerNames(tok);
+	}
+	else if (blockName == NODE_TO_LAYER_MAPPING)
+	{
+		parseNodeToLayerMapping(tok);
+	}
+}
+
+void LayerInfoFileModule::parseLayerNames(parser::DefTokeniser& tok)
+{
+	// The opening brace
+	tok.assertNextToken("{");
+
+	while (tok.hasMoreTokens()) 
+	{
+		std::string token = tok.nextToken();
+
+		if (token == LAYER)
+		{
+			// Get the ID
+			std::string layerIDStr = tok.nextToken();
+			int layerID = string::convert<int>(layerIDStr);
+
+			tok.assertNextToken("{");
+
+			// Assemble the name
+			std::string name;
+
+			token = tok.nextToken();
+
+			while (token != "}")
+			{
+				name += token;
+				token = tok.nextToken();
+			}
+
+			rMessage() << "[InfoFile]: Parsed layer #" << layerID << " with name " << name << std::endl;
+
+			_layerNames.insert(LayerNameMap::value_type(layerID, name));
+
+			continue;
+		}
+
+		if (token == "}")
+		{
+			break;
+		}
+	}
+}
+
+void LayerInfoFileModule::parseNodeToLayerMapping(parser::DefTokeniser& tok)
+{
+	// The opening brace
+	tok.assertNextToken("{");
+
+	while (tok.hasMoreTokens())
+	{
+		std::string token = tok.nextToken();
+
+		if (token == NODE)
+		{
+			tok.assertNextToken("{");
+
+			// Create a new LayerList
+			_layerMappings.push_back(scene::LayerList());
+
+			while (tok.hasMoreTokens())
+			{
+				std::string nodeToken = tok.nextToken();
+
+				if (nodeToken == "}")
+				{
+					break;
+				}
+
+				// Add the ID to the list
+				_layerMappings.back().insert(string::convert<int>(nodeToken));
+			}
+		}
+
+		if (token == "}")
+		{
+			break;
+		}
+	}
+}
+
+void LayerInfoFileModule::applyInfoToScene(const IMapRootNodePtr& root, const map::NodeIndexMap& nodeMap)
+{
+	// Create the layers according to the data found in the map information file
+	for (const LayerNameMap::value_type& i : _layerNames)
+	{
+		// Create the named layer with the saved ID
+		GlobalLayerSystem().createLayer(i.second, i.first);
+	}
+
+	// Set the layer mapping iterator to the beginning
+	LayerLists::const_iterator mapping = _layerMappings.begin();
+
+	// Assign the layers
+	root->foreachNode([&](const INodePtr& node)
+	{
+		// To prevent all the support node types from getting layers assigned
+		// filter them out, only Entities and Primitives get mapped in the info file
+		if (Node_isEntity(node) || Node_isPrimitive(node))
+		{
+			// Check if the node index is out of bounds
+			if (mapping == _layerMappings.end())
+			{
+				node->assignToLayers(_standardLayerList);
+				return true;
+			}
+
+			// Retrieve the next set of layer mappings and assign them
+			node->assignToLayers(*(mapping++));
+			return true;
+		}
+
+		// All other node types inherit the layers from their parent node
+		// Model / particle / target line
+		scene::INodePtr parent = node->getParent();
+
+		if (parent)
+		{
+			node->assignToLayers(parent->getLayers());
+		}
+
+		return true;
+	});
+
+	rMessage() << "Sanity-checking the layer assignments...";
+
+	// Sanity-check the layer mapping, it's possible that some .darkradiant
+	// files are mapping nodes to non-existent layer IDs
+	LayerValidityCheckWalker checker;
+	root->traverseChildren(checker);
+
+	rMessage() << "done, had to fix " << checker.getNumFixed() << " assignments." << std::endl;
+}
+
+void LayerInfoFileModule::onInfoFileLoadFinished()
+{
+	_layerNames.clear();
+	_layerMappings.clear();
+}
+
+void LayerInfoFileModule::writeLayerNames(std::ostream& stream)
+{
+	// Open a "Layers" block
+	stream << "\t" << LAYERS << std::endl;
+	stream << "\t{" << std::endl;
+
+	// Visit all layers and write them to the stream
+	GlobalLayerSystem().foreachLayer([&](int layerId, const std::string& layerName)
+	{
+		stream << "\t\t" << LAYER << " " << layerId << " { " << layerName << " }" << std::endl;
+	});
+
+	stream << "\t}" << std::endl;
+}
+
+}
diff --git a/radiant/layers/LayerInfoFileModule.h b/radiant/layers/LayerInfoFileModule.h
new file mode 100644
index 0000000..c0f73c0
--- /dev/null
+++ b/radiant/layers/LayerInfoFileModule.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "imapinfofile.h"
+#include <sstream>
+
+namespace scene
+{
+
+class LayerInfoFileModule :
+	public map::IMapInfoFileModule
+{
+private:
+	// Number of node-to-layer mappings written
+	std::size_t _layerInfoCount;
+
+	// Buffer to hold our output
+	std::stringstream _output;
+
+	// The list of layernames
+	typedef std::map<int, std::string> LayerNameMap;
+	LayerNameMap _layerNames;
+
+	typedef std::vector<scene::LayerList> LayerLists;
+	LayerLists _layerMappings;
+
+	// The standard list (node is part of layer 0)
+	scene::LayerList _standardLayerList;
+
+public:
+	LayerInfoFileModule();
+
+	std::string getName() override;
+
+	void onInfoFileSaveStart() override;
+	void onSavePrimitive(const INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) override;
+	void onSaveEntity(const INodePtr& node, std::size_t entityNum) override;
+	void writeBlocks(std::ostream& stream) override;
+	void onInfoFileSaveFinished() override;
+
+	void onInfoFileLoadStart() override;
+	bool canParseBlock(const std::string& blockName) override;
+	void parseBlock(const std::string& blockName, parser::DefTokeniser& tok) override;
+	void applyInfoToScene(const IMapRootNodePtr& root, const map::NodeIndexMap& nodeMap) override;
+	void onInfoFileLoadFinished() override;
+
+private:
+	void saveNode(const INodePtr& node);
+	void writeLayerNames(std::ostream& stream);
+
+	void parseLayerNames(parser::DefTokeniser& tok);
+	void parseNodeToLayerMapping(parser::DefTokeniser& tok);
+};
+
+}
diff --git a/radiant/layers/LayerSystem.cpp b/radiant/layers/LayerSystem.cpp
index e04bd1b..97dc6cf 100644
--- a/radiant/layers/LayerSystem.cpp
+++ b/radiant/layers/LayerSystem.cpp
@@ -6,6 +6,7 @@
 #include "iuimanager.h"
 #include "itextstream.h"
 #include "imainframe.h"
+#include "imapinfofile.h"
 #include "icommandsystem.h"
 #include "scene/Node.h"
 #include "scenelib.h"
@@ -15,6 +16,7 @@
 #include "MoveToLayerWalker.h"
 #include "RemoveFromLayerWalker.h"
 #include "SetLayerSelectedWalker.h"
+#include "LayerInfoFileModule.h"
 
 #include "wxutil/dialog/Dialog.h"
 #include "wxutil/dialog/MessageBox.h"
@@ -466,6 +468,7 @@ const StringSet& LayerSystem::getDependencies() const
 		_dependencies.insert(MODULE_COMMANDSYSTEM);
 		_dependencies.insert(MODULE_UIMANAGER);
 		_dependencies.insert(MODULE_ORTHOCONTEXTMENU);
+		_dependencies.insert(MODULE_MAPINFOFILEMANAGER);
 	}
 
 	return _dependencies;
@@ -473,7 +476,7 @@ const StringSet& LayerSystem::getDependencies() const
 
 void LayerSystem::initialiseModule(const ApplicationContext& ctx)
 {
-	rMessage() << "LayerSystem::initialiseModule called.\n";
+	rMessage() << getName() << "::initialiseModule called." << std::endl;
 
 	// Create the "master" layer with ID DEFAULT_LAYER
 	createLayer(_(DEFAULT_LAYER_NAME));
@@ -494,6 +497,9 @@ void LayerSystem::initialiseModule(const ApplicationContext& ctx)
 	GlobalCommandSystem().addCommand("ToggleLayerControlDialog", ui::LayerControlDialog::toggle);
 	GlobalEventManager().addCommand("ToggleLayerControlDialog", "ToggleLayerControlDialog");
 
+	GlobalMapModule().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &LayerSystem::onMapEvent)
+	);
 
 	// Create a new menu item connected to the CreateNewLayer command
 	wxutil::CommandMenuItemPtr menuItem(new wxutil::CommandMenuItem(
@@ -516,6 +522,10 @@ void LayerSystem::initialiseModule(const ApplicationContext& ctx)
 	GlobalOrthoContextMenu().addItem(addMenu, ui::IOrthoContextMenu::SECTION_LAYER);
 	GlobalOrthoContextMenu().addItem(moveMenu, ui::IOrthoContextMenu::SECTION_LAYER);
 	GlobalOrthoContextMenu().addItem(removeMenu, ui::IOrthoContextMenu::SECTION_LAYER);
+
+	GlobalMapInfoFileManager().registerInfoFileModule(
+		std::make_shared<LayerInfoFileModule>()
+	);
 }
 
 void LayerSystem::createLayerCmd(const cmd::ArgumentList& args)
@@ -570,6 +580,15 @@ void LayerSystem::createLayerCmd(const cmd::ArgumentList& args)
 	}
 }
 
+void LayerSystem::onMapEvent(IMap::MapEvent ev)
+{
+	if (ev == IMap::MapUnloaded || ev == IMap::MapLoading)
+	{ 
+		// Purge layer info before map is loading or after it has been unloaded
+		reset();
+	}
+}
+
 // Define the static LayerSystem module
 module::StaticModule<LayerSystem> layerSystemModule;
 
diff --git a/radiant/layers/LayerSystem.h b/radiant/layers/LayerSystem.h
index 667fb3e..e50fc8a 100644
--- a/radiant/layers/LayerSystem.h
+++ b/radiant/layers/LayerSystem.h
@@ -3,6 +3,7 @@
 #include <vector>
 #include <map>
 #include "ilayer.h"
+#include "imap.h"
 #include "LayerCommandTarget.h"
 
 namespace scene {
@@ -129,6 +130,8 @@ public:
 	void createLayerCmd(const cmd::ArgumentList& args);
 
 private:
+	void onMapEvent(IMap::MapEvent ev);
+
 	// Internal event, updates the scenegraph
 	void onLayerVisibilityChanged();
 
diff --git a/radiant/layers/SetLayerSelectedWalker.h b/radiant/layers/SetLayerSelectedWalker.h
index b45520e..f63324e 100644
--- a/radiant/layers/SetLayerSelectedWalker.h
+++ b/radiant/layers/SetLayerSelectedWalker.h
@@ -2,6 +2,7 @@
 
 #include "ientity.h"
 #include "ilayer.h"
+#include "entitylib.h"
 
 namespace scene
 {
@@ -26,9 +27,7 @@ public:
 			return false; // skip hidden nodes
 		}
 
-		Entity* entity = Node_getEntity(node);
-
-		if (entity != NULL && entity->getKeyValue("classname") == "worldspawn")
+		if (Node_isWorldspawn(node))
 		{
 			// Skip the worldspawn
 			return true;
diff --git a/radiant/map/AasFileManager.cpp b/radiant/map/AasFileManager.cpp
new file mode 100644
index 0000000..4d3ea6f
--- /dev/null
+++ b/radiant/map/AasFileManager.cpp
@@ -0,0 +1,170 @@
+#include "AasFileManager.h"
+
+#include "itextstream.h"
+
+#include "iarchive.h"
+#include "ieclass.h"
+#include "ifilesystem.h"
+#include "eclass.h"
+
+#include "modulesystem/StaticModule.h"
+#include "ui/aas/AasControlDialog.h"
+
+namespace map
+{
+
+namespace
+{
+    const char* const AAS_TYPES_ENTITYDEF = "aas_types";
+}
+
+AasFileManager::AasFileManager() :
+    _typesLoaded(false)
+{}
+
+void AasFileManager::registerLoader(const IAasFileLoaderPtr& loader)
+{
+    _loaders.insert(loader);
+}
+
+void AasFileManager::unregisterLoader(const IAasFileLoaderPtr& loader)
+{
+    _loaders.erase(loader);
+}
+
+IAasFileLoaderPtr AasFileManager::getLoaderForStream(std::istream& stream)
+{
+    IAasFileLoaderPtr loader;
+
+    for (const IAasFileLoaderPtr& candidate : _loaders)
+    {
+        // Rewind the stream before passing it to the format for testing
+		stream.seekg(0, std::ios_base::beg);
+
+		if (candidate->canLoad(stream))
+		{
+            loader = candidate;
+            break;
+		}
+	}
+
+	// Rewind the stream when we're done
+	stream.seekg(0, std::ios_base::beg);
+    
+    return loader;
+}
+
+void AasFileManager::ensureAasTypesLoaded()
+{
+    if (_typesLoaded) return;
+
+    _typesLoaded = true;
+    _typeList.clear();
+
+    IEntityClassPtr aasTypesClass = GlobalEntityClassManager().findClass(AAS_TYPES_ENTITYDEF);
+
+    if (aasTypesClass)
+    {
+        eclass::AttributeList list = eclass::getSpawnargsWithPrefix(*aasTypesClass, "type");
+
+        for (const EntityClassAttribute& attr : list)
+        {
+            AasType type;
+            type.entityDefName = attr.getValue();
+
+            IEntityClassPtr aasType = GlobalEntityClassManager().findClass(type.entityDefName);
+
+            if (!aasType)
+            {
+                rWarning() << "Could not find entityDef for AAS type " << type.entityDefName <<
+                    " mentioned in " << AAS_TYPES_ENTITYDEF << " entityDef." << std::endl;
+                continue;
+            }
+
+            type.fileExtension = aasType->getAttribute("fileExtension").getValue();
+            _typeList.push_back(type);
+        }
+    }
+}
+
+AasTypeList AasFileManager::getAasTypes()
+{
+    ensureAasTypesLoaded();
+
+    return _typeList;
+}
+
+AasType AasFileManager::getAasTypeByName(const std::string& typeName)
+{
+    ensureAasTypesLoaded();
+
+    for (AasType& type : _typeList)
+    {
+        if (type.entityDefName == typeName)
+        {
+            return type;
+        }
+    }
+
+    throw std::runtime_error("Could not find AAS type " + typeName);
+}
+
+std::list<AasFileInfo> AasFileManager::getAasFilesForMap(const std::string& mapPath)
+{
+    std::list<AasFileInfo> list;
+
+    AasTypeList types = getAasTypes();
+
+    for (const AasType& type : types)
+    {
+        std::string path = mapPath;
+
+        // Cut off the extension
+        path = path.substr(0, path.rfind('.'));
+        path += "." + type.fileExtension;
+
+        ArchiveTextFilePtr file = GlobalFileSystem().openTextFileInAbsolutePath(path);
+
+        if (file)
+        {
+            // Add this file to the list
+            list.push_back(AasFileInfo());
+            list.back().absolutePath = path;
+            list.back().type = type;
+        }
+    }
+
+    return list;
+}
+
+const std::string& AasFileManager::getName() const
+{
+	static std::string _name(MODULE_AASFILEMANAGER);
+	return _name;
+}
+
+const StringSet& AasFileManager::getDependencies() const
+{
+	static StringSet _dependencies;
+
+	if (_dependencies.empty())
+	{
+		_dependencies.insert(MODULE_VIRTUALFILESYSTEM);
+        _dependencies.insert(MODULE_ECLASSMANAGER);
+	}
+
+	return _dependencies;
+}
+
+void AasFileManager::initialiseModule(const ApplicationContext& ctx)
+{
+	rMessage() << getName() << "::initialiseModule called." << std::endl;
+
+    // Initialise the UI
+    ui::AasControlDialog::Init();
+}
+
+// Define the static AasFileManager module
+module::StaticModule<AasFileManager> aasFileManagerModule;
+
+}
diff --git a/radiant/map/AasFileManager.h b/radiant/map/AasFileManager.h
new file mode 100644
index 0000000..14e3692
--- /dev/null
+++ b/radiant/map/AasFileManager.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "iaasfile.h"
+#include <set>
+
+namespace map
+{
+
+class AasFileManager :
+    public IAasFileManager
+{
+private:
+    // A mapping between extensions and format modules
+	// Multiple modules can register themselves for a single extension
+	typedef std::set<IAasFileLoaderPtr> Loaders;
+	Loaders _loaders;
+
+    AasTypeList _typeList;
+    bool _typesLoaded;
+
+public:
+    AasFileManager();
+
+    // IAasFileManager implementation
+    void registerLoader(const IAasFileLoaderPtr& loader) override;
+    void unregisterLoader(const IAasFileLoaderPtr& loader) override;
+    IAasFileLoaderPtr getLoaderForStream(std::istream& stream) override;
+    AasTypeList getAasTypes() override;
+    AasType getAasTypeByName(const std::string& typeName) override;
+    std::list<AasFileInfo> getAasFilesForMap(const std::string& mapPath) override;
+
+    // RegisterableModule implementation
+	const std::string& getName() const override;
+	const StringSet& getDependencies() const override;
+	void initialiseModule(const ApplicationContext& ctx) override;
+
+private:
+    void ensureAasTypesLoaded();
+};
+
+} // namespace
diff --git a/radiant/map/AutoSaver.cpp b/radiant/map/AutoSaver.cpp
index 8421850..4d609f7 100644
--- a/radiant/map/AutoSaver.cpp
+++ b/radiant/map/AutoSaver.cpp
@@ -5,8 +5,8 @@
 #include "mapfile.h"
 #include "itextstream.h"
 #include "iscenegraph.h"
-#include "imainframe.h"
 #include "iradiant.h"
+#include "imainframe.h"
 #include "ipreferencesystem.h"
 
 #include "registry/registry.h"
@@ -21,6 +21,7 @@
 #include "string/string.h"
 #include "map/Map.h"
 #include "modulesystem/ApplicationContextImpl.h"
+#include "modulesystem/StaticModule.h"
 
 #include <wx/frame.h>
 
@@ -28,11 +29,12 @@
 #include <boost/filesystem/convenience.hpp>
 #include <boost/filesystem/exception.hpp>
 
-namespace map {
-
-namespace {
+namespace map 
+{
 
-	/* Registry key names */
+namespace 
+{
+	// Registry key names
 	const char* RKEY_AUTOSAVE_ENABLED = "user/ui/map/autoSaveEnabled";
 	const char* RKEY_AUTOSAVE_INTERVAL = "user/ui/map/autoSaveInterval";
 	const char* RKEY_AUTOSAVE_SNAPSHOTS_ENABLED = "user/ui/map/autoSaveSnapshots";
@@ -44,35 +46,16 @@ namespace {
 	typedef boost::filesystem::path Path;
 }
 
-
 AutoMapSaver::AutoMapSaver() :
-	_enabled(registry::getValue<bool>(RKEY_AUTOSAVE_ENABLED)),
-	_snapshotsEnabled(registry::getValue<bool>(RKEY_AUTOSAVE_SNAPSHOTS_ENABLED)),
-	_interval(registry::getValue<int>(RKEY_AUTOSAVE_INTERVAL) * 60),
-	_timer(this),
+	_enabled(false),
+	_snapshotsEnabled(false),
+	_interval(5*60),
 	_changes(0)
-{
-	Connect(wxEVT_TIMER, wxTimerEventHandler(AutoMapSaver::onIntervalReached), NULL, this);
-
-	GlobalRegistry().signalForKey(RKEY_AUTOSAVE_INTERVAL).connect(
-        sigc::mem_fun(this, &AutoMapSaver::registryKeyChanged)
-    );
-	GlobalRegistry().signalForKey(RKEY_AUTOSAVE_SNAPSHOTS_ENABLED).connect(
-        sigc::mem_fun(this, &AutoMapSaver::registryKeyChanged)
-    );
-	GlobalRegistry().signalForKey(RKEY_AUTOSAVE_ENABLED).connect(
-        sigc::mem_fun(this, &AutoMapSaver::registryKeyChanged)
-    );
-
-	// Register this instance with GlobalRadiant() at once
-	GlobalRadiant().signal_radiantShutdown().connect(
-		sigc::mem_fun(*this, &AutoMapSaver::onRadiantShutdown)
-    );
-}
+{}
 
 AutoMapSaver::~AutoMapSaver() 
 {
-	stopTimer();
+	assert(!_timer);
 }
 
 void AutoMapSaver::registryKeyChanged() 
@@ -91,17 +74,6 @@ void AutoMapSaver::registryKeyChanged()
 	}
 }
 
-void AutoMapSaver::init() 
-{
-	constructPreferences();
-}
-
-void AutoMapSaver::onRadiantShutdown()
-{
-	_enabled = false;
-	stopTimer();
-}
-
 void AutoMapSaver::clearChanges()
 {
 	_changes = 0;
@@ -109,12 +81,18 @@ void AutoMapSaver::clearChanges()
 
 void AutoMapSaver::startTimer()
 {
-	_timer.Start(_interval * 1000);
+	assert(_timer);
+	if (!_timer) return;
+
+	_timer->Start(_interval * 1000);
 }
 
 void AutoMapSaver::stopTimer()
 {
-	_timer.Stop();
+	assert(_timer);
+	if (!_timer) return;
+
+	_timer->Stop();
 }
 
 void AutoMapSaver::saveSnapshot() 
@@ -168,7 +146,7 @@ void AutoMapSaver::saveSnapshot()
 			if (os::fileOrDirExists(filename)) 
 			{
 				// Add to the folder size
-				folderSize += file_size(filename.c_str());
+				folderSize += os::getFileSize(filename);
 			}
 			else 
 			{
@@ -199,8 +177,7 @@ void AutoMapSaver::saveSnapshot()
 
 void AutoMapSaver::checkSave()
 {
-	// Check if we have a proper map
-	if (!GlobalMap().isValid() || !GlobalMainFrame().screenUpdatesEnabled())
+	if (!GlobalMainFrame().screenUpdatesEnabled())
 	{
 		return;
 	}
@@ -296,15 +273,15 @@ void AutoMapSaver::checkSave()
 void AutoMapSaver::constructPreferences()
 {
 	// Add a page to the given group
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Autosave"));
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Autosave"));
 
 	// Add the checkboxes and connect them with the registry key and the according observer
-	page->appendCheckBox("", _("Enable Autosave"), RKEY_AUTOSAVE_ENABLED);
-	page->appendSlider(_("Autosave Interval (in minutes)"), RKEY_AUTOSAVE_INTERVAL, TRUE, 5, 1, 61, 1, 1, 1);
+	page.appendCheckBox(_("Enable Autosave"), RKEY_AUTOSAVE_ENABLED);
+	page.appendSlider(_("Autosave Interval (in minutes)"), RKEY_AUTOSAVE_INTERVAL, 1, 61, 1, 1);
 
-	page->appendCheckBox("", _("Save Snapshots"), RKEY_AUTOSAVE_SNAPSHOTS_ENABLED);
-	page->appendEntry(_("Snapshot folder (relative to map folder)"), RKEY_AUTOSAVE_SNAPSHOTS_FOLDER);
-	page->appendEntry(_("Max Snapshot Folder size (MB)"), RKEY_AUTOSAVE_MAX_SNAPSHOT_FOLDER_SIZE);
+	page.appendCheckBox(_("Save Snapshots"), RKEY_AUTOSAVE_SNAPSHOTS_ENABLED);
+	page.appendEntry(_("Snapshot folder (relative to map folder)"), RKEY_AUTOSAVE_SNAPSHOTS_FOLDER);
+	page.appendEntry(_("Max Snapshot Folder size (MB)"), RKEY_AUTOSAVE_MAX_SNAPSHOT_FOLDER_SIZE);
 }
 
 void AutoMapSaver::onIntervalReached(wxTimerEvent& ev)
@@ -312,10 +289,94 @@ void AutoMapSaver::onIntervalReached(wxTimerEvent& ev)
 	checkSave();
 }
 
+void AutoMapSaver::onMapEvent(IMap::MapEvent ev)
+{
+	// We reset our change count regardless of whether a map
+	// is loaded or unloaded
+	switch (ev)
+	{
+	case IMap::MapLoading:
+	case IMap::MapLoaded:
+	case IMap::MapUnloading:
+	case IMap::MapUnloaded:
+		clearChanges();
+		break;
+	};
+}
+
+const std::string& AutoMapSaver::getName() const
+{
+	static std::string _name("AutomaticMapSaver");
+	return _name;
+}
+
+const StringSet& AutoMapSaver::getDependencies() const
+{
+	static StringSet _dependencies;
+
+	if (_dependencies.empty())
+	{
+		_dependencies.insert(MODULE_MAP);
+		_dependencies.insert(MODULE_PREFERENCESYSTEM);
+		_dependencies.insert(MODULE_XMLREGISTRY);
+		_dependencies.insert(MODULE_MAINFRAME);
+		_dependencies.insert(MODULE_RADIANT);
+	}
+
+	return _dependencies;
+}
+
+void AutoMapSaver::initialiseModule(const ApplicationContext& ctx)
+{
+	rMessage() << getName() << "::initialiseModule called." << std::endl;
+
+	_timer.reset(new wxTimer(this));
+
+	constructPreferences();
+
+	Connect(wxEVT_TIMER, wxTimerEventHandler(AutoMapSaver::onIntervalReached), NULL, this);
+
+	_signalConnections.push_back(GlobalRegistry().signalForKey(RKEY_AUTOSAVE_INTERVAL).connect(
+		sigc::mem_fun(this, &AutoMapSaver::registryKeyChanged)
+	));
+	_signalConnections.push_back(GlobalRegistry().signalForKey(RKEY_AUTOSAVE_SNAPSHOTS_ENABLED).connect(
+		sigc::mem_fun(this, &AutoMapSaver::registryKeyChanged)
+	));
+	_signalConnections.push_back(GlobalRegistry().signalForKey(RKEY_AUTOSAVE_ENABLED).connect(
+		sigc::mem_fun(this, &AutoMapSaver::registryKeyChanged)
+	));
+
+	// Get notified when the map is loaded afresh
+	_signalConnections.push_back(GlobalMap().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &AutoMapSaver::onMapEvent)
+	));
+
+	// Refresh all values from the registry right now (this might also start the timer)
+	registryKeyChanged();
+}
+
+void AutoMapSaver::shutdownModule()
+{
+	// Unsubscribe from all connections
+	for (sigc::connection& connection : _signalConnections)
+	{
+		connection.disconnect();
+	}
+
+	_signalConnections.clear();
+
+	_enabled = false;
+	stopTimer();
+
+	// Destroy the timer
+	_timer.reset();
+}
+
+module::StaticModule<AutoMapSaver> staticAutoSaverModule;
+
 AutoMapSaver& AutoSaver()
 {
-	static AutoMapSaver _autoSaver;
-	return _autoSaver;
+	return *staticAutoSaverModule.getModule();
 }
 
 } // namespace map
diff --git a/radiant/map/AutoSaver.h b/radiant/map/AutoSaver.h
index fac5da5..714bddb 100644
--- a/radiant/map/AutoSaver.h
+++ b/radiant/map/AutoSaver.h
@@ -1,8 +1,13 @@
 #pragma once
 
 #include "iregistry.h"
+#include "imodule.h"
+#include "imap.h"
 
+#include <vector>
+#include <sigc++/connection.h>
 #include <wx/timer.h>
+#include <wx/sharedptr.h>
 
 /* greebo: The AutoMapSaver class lets itself being called in distinct intervals
  * and saves the map files either to snapshots or to a single yyyy.autosave.map file.
@@ -12,8 +17,8 @@ namespace map
 {
 
 class AutoMapSaver : 
-	public sigc::trackable,
-	public wxEvtHandler
+	public wxEvtHandler,
+	public RegisterableModule
 {
 	// TRUE, if autosaving is enabled
 	bool _enabled;
@@ -25,16 +30,21 @@ class AutoMapSaver :
 	unsigned long _interval;
 
 	// The timer object that triggers the callback
-	wxTimer _timer;
+	wxSharedPtr<wxTimer> _timer;
 
 	std::size_t _changes;
 
+	std::vector<sigc::connection> _signalConnections;
+
 public:
 	// Constructor
 	AutoMapSaver();
 
-	// Initialises the preferences and the registrykeyobserver
-	void init();
+	// RegisterableModule implementation
+	const std::string& getName() const override;
+	const StringSet& getDependencies() const override;
+	void initialiseModule(const ApplicationContext& ctx) override;
+	void shutdownModule() override;
 
 	~AutoMapSaver();
 
@@ -45,13 +55,13 @@ public:
 	// Clears the _changes member variable that indicates how many changes have been made
 	void clearChanges();
 
-	void registryKeyChanged();
-
+private:
 	// Adds the elements to the according preference page
 	void constructPreferences();
 
-private:
-	void onRadiantShutdown();
+	void registryKeyChanged();
+
+	void onMapEvent(IMap::MapEvent ev);
 
 	// This performs is called to check if the map is valid/changed/should be saved
 	// and calls the save routines accordingly.
diff --git a/radiant/map/CounterManager.cpp b/radiant/map/CounterManager.cpp
index ee800f4..f373ee9 100644
--- a/radiant/map/CounterManager.cpp
+++ b/radiant/map/CounterManager.cpp
@@ -2,6 +2,7 @@
 
 #include "i18n.h"
 #include "iuimanager.h"
+#include "selectionlib.h"
 #include "string/string.h"
 #include "modulesystem/StaticModule.h"
 
@@ -46,6 +47,7 @@ const StringSet& CounterManager::getDependencies() const
 	if (_dependencies.empty())
 	{
 		_dependencies.insert(MODULE_UIMANAGER);
+		_dependencies.insert(MODULE_SELECTIONSYSTEM);
 	}
 
 	return _dependencies;
@@ -57,17 +59,32 @@ void CounterManager::initialiseModule(const ApplicationContext& ctx)
 	GlobalUIManager().getStatusBarManager().addTextElement(
 		"MapCounters",
 		"",  // no icon
-		IStatusBarManager::POS_BRUSHCOUNT
+		IStatusBarManager::POS_BRUSHCOUNT,
+		_("Number of brushes/patches/entities in this map\n(Number of selected items shown in parentheses)")
 	);
+
+	_selectionChangedConn = GlobalSelectionSystem().signal_selectionChanged().connect(
+		[this] (const ISelectable&) { requestIdleCallback(); }
+	);
+}
+
+void CounterManager::shutdownModule()
+{
+	_selectionChangedConn.disconnect();
 }
 
 void CounterManager::onIdle()
 {
+	const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo();
+
 	std::string text =
-		(boost::format(_("Brushes: %lu Patches: %lu Entities: %lu")) %
+		(boost::format(_("Brushes: %lu (%lu) Patches: %lu (%lu) Entities: %lu (%lu)")) %
 		_counters[counterBrushes]->get() %
+		info.brushCount %
 		_counters[counterPatches]->get() %
-		_counters[counterEntities]->get()).str();
+		info.patchCount %
+		_counters[counterEntities]->get() %
+		info.entityCount).str();
 
 	GlobalUIManager().getStatusBarManager().setText("MapCounters", text);
 }
diff --git a/radiant/map/CounterManager.h b/radiant/map/CounterManager.h
index 95d0677..9d3a4da 100644
--- a/radiant/map/CounterManager.h
+++ b/radiant/map/CounterManager.h
@@ -4,6 +4,7 @@
 #include "iuimanager.h"
 #include "wxutil/event/SingleIdleCallback.h"
 
+#include <sigc++/connection.h>
 #include <map>
 #include <memory>
 
@@ -52,22 +53,25 @@ class CounterManager :
 	typedef std::map<CounterType, CounterPtr> CounterMap;
 	CounterMap _counters;
 
+	sigc::connection _selectionChangedConn;
+
 public:
 	CounterManager();
 
 	virtual ~CounterManager() {}
 
-	ICounter& getCounter(CounterType counter);
+	ICounter& getCounter(CounterType counter) override;
 
 	// ICounter::Observer implementation
-	void countChanged();
+	void countChanged() override;
 
-	const std::string& getName() const;
-	const StringSet& getDependencies() const;
-	void initialiseModule(const ApplicationContext& ctx);
+	const std::string& getName() const override;
+	const StringSet& getDependencies() const override;
+	void initialiseModule(const ApplicationContext& ctx) override;
+	void shutdownModule() override;
 
 protected:
-	void onIdle();
+	void onIdle() override;
 };
 
 } // namespace map
diff --git a/radiant/map/DeferredDraw.h b/radiant/map/DeferredDraw.h
deleted file mode 100644
index 7154abf..0000000
--- a/radiant/map/DeferredDraw.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#pragma once
-
-#include <functional>
-#include "map/Map.h"
-#include <sigc++/trackable.h>
-
-class DeferredDraw :
-    public sigc::trackable
-{
-public:
-	typedef std::function<void()> DrawCallback;
-
-private:
-	DrawCallback _draw;
-	bool _defer;
-	bool _deferred;
-public:
-	DeferredDraw(const DrawCallback& draw) :
-		_draw(draw),
-		_defer(false),
-		_deferred(false)
-	{}
-
-	void defer() {
-		_defer = true;
-	}
-
-	void draw()
-    {
-		if (_defer)
-        {
-			_deferred = true;
-		}
-		else
-        {
-			_draw();
-		}
-	}
-
-	void flush()
-    {
-        if (_defer && _deferred && _draw)
-        {
-			_draw();
-		}
-
-		_deferred = false;
-		_defer = false;
-	}
-
-	// Callback target
-	void onMapValidChanged()
-	{
-		if (GlobalMap().isValid())
-		{
-			flush();
-		}
-		else
-		{
-			defer();
-		}
-	}
-};
diff --git a/radiant/map/InfoFile.cpp b/radiant/map/InfoFile.cpp
deleted file mode 100644
index 0fd76c4..0000000
--- a/radiant/map/InfoFile.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-#include "InfoFile.h"
-
-#include <limits>
-#include "itextstream.h"
-#include "string/convert.h"
-
-#include "i18n.h"
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/replace.hpp>
-#include <boost/lexical_cast.hpp>
-
-namespace map
-{
-
-const char* const InfoFile::HEADER_SEQUENCE = "DarkRadiant Map Information File Version";
-const char* const InfoFile::NODE_TO_LAYER_MAPPING = "NodeToLayerMapping";
-const char* const InfoFile::LAYER = "Layer";
-const char* const InfoFile::LAYERS = "Layers";
-const char* const InfoFile::NODE = "Node";
-const char* const InfoFile::SELECTION_SETS = "SelectionSets";
-const char* const InfoFile::SELECTION_SET = "SelectionSet";
-
-std::size_t InfoFile::EMPTY_PRIMITVE_NUM = std::numeric_limits<std::size_t>::max();
-
-// Pass the input stream to the constructor
-InfoFile::InfoFile(std::istream& infoStream) :
-	_tok(infoStream),
-	_isValid(true)
-{
-	_standardLayerList.insert(0);
-}
-
-const InfoFile::LayerNameMap& InfoFile::getLayerNames() const {
-	return _layerNames;
-}
-
-std::size_t InfoFile::getLayerMappingCount() const {
-	return _layerMappings.size();
-}
-
-const scene::LayerList& InfoFile::getNextLayerMapping() {
-	// Check if we have a valid infofile
-	if (!_isValid) {
-		return _standardLayerList;
-	}
-
-	// Check if the node index is out of bounds
-	if (_layerMappingIterator == _layerMappings.end()) {
-		return _standardLayerList;
-	}
-
-	// Return the current list and increase the iterator afterwards
-	return *(_layerMappingIterator++);
-}
-
-void InfoFile::parse()
-{
-	// parse the header
-	try {
-		std::vector<std::string> parts;
-		boost::algorithm::split(parts, HEADER_SEQUENCE, boost::algorithm::is_any_of(" "));
-
-		// Parse the string "DarkRadiant Map Information File Version"
-		for (std::size_t i = 0; i < parts.size(); i++) {
-			_tok.assertNextToken(parts[i]);
-		}
-
-		float version = boost::lexical_cast<float>(_tok.nextToken());
-
-		if (version != MAP_INFO_VERSION) {
-			_isValid = false;
-			throw parser::ParseException(_("Map Info File Version invalid"));
-		}
-	}
-	catch (parser::ParseException& e) {
-        rError()
-            << "[InfoFile] Unable to parse info file header: "
-			<< e.what() << std::endl;
-		_isValid = false;
-        return;
-    }
-    catch (boost::bad_lexical_cast& e) {
-        rError()
-            << "[InfoFile] Unable to parse info file version: "
-			<< e.what() << std::endl;
-		_isValid = false;
-        return;
-    }
-
-	// The opening brace of the master block
-	_tok.assertNextToken("{");
-
-	parseInfoFileBody();
-
-	// Set the layer mapping iterator to the beginning
-	_layerMappingIterator = _layerMappings.begin();
-}
-
-void InfoFile::parseInfoFileBody()
-{
-	while (_tok.hasMoreTokens())
-	{
-		std::string token = _tok.nextToken();
-
-		if (token == LAYERS)
-		{
-			parseLayerNames();
-			continue;
-		}
-
-		if (token == NODE_TO_LAYER_MAPPING)
-		{
-			parseNodeToLayerMapping();
-			continue;
-		}
-
-		if (token == SELECTION_SETS)
-		{
-			parseSelectionSetInfo();
-			continue;
-		}
-
-		if (token == "}")
-		{
-			break;
-		}
-
-		// Unknown token, try to ignore that block
-		rWarning() << "Unknown keyword " << token << " encountered, will try to ignore this block." << std::endl;
-
-		// We can only ignore a block if there is a block beginning curly brace
-		_tok.assertNextToken("{");
-
-		// Ignore the block
-		int depth = 1;
-
-		while (_tok.hasMoreTokens() && depth > 0)
-		{
-			std::string token = _tok.nextToken();
-
-			if (token == "{") 
-			{
-				depth++;
-			}
-			else if (token == "}") 
-			{
-				depth--;
-			}
-		}
-	}
-}
-
-void InfoFile::parseLayerNames()
-{
-	// The opening brace
-	_tok.assertNextToken("{");
-
-	while (_tok.hasMoreTokens()) {
-		std::string token = _tok.nextToken();
-
-		if (token == LAYER) {
-			// Get the ID
-			std::string layerIDStr = _tok.nextToken();
-			int layerID = string::convert<int>(layerIDStr);
-
-			_tok.assertNextToken("{");
-
-			// Assemble the name
-			std::string name;
-
-			token = _tok.nextToken();
-			while (token != "}") {
-				name += token;
-				token = _tok.nextToken();
-			}
-
-			rMessage() << "[InfoFile]: Parsed layer #"
-				<< layerID << " with name " << name << std::endl;
-
-			_layerNames.insert(LayerNameMap::value_type(layerID, name));
-
-			continue;
-		}
-
-		if (token == "}") {
-			break;
-		}
-	}
-}
-
-void InfoFile::parseNodeToLayerMapping()
-{
-	// The opening brace
-	_tok.assertNextToken("{");
-
-	while (_tok.hasMoreTokens()) {
-		std::string token = _tok.nextToken();
-
-		if (token == NODE) {
-			_tok.assertNextToken("{");
-
-			// Create a new LayerList
-			_layerMappings.push_back(scene::LayerList());
-
-			while (_tok.hasMoreTokens()) {
-				std::string nodeToken = _tok.nextToken();
-
-				if (nodeToken == "}") {
-					break;
-				}
-
-				// Add the ID to the list
-				_layerMappings.back().insert(string::convert<int>(nodeToken));
-			}
-		}
-
-		if (token == "}") {
-			break;
-		}
-	}
-}
-
-void InfoFile::parseSelectionSetInfo()
-{
-	_selectionSetInfo.clear();
-
-	// SelectionSet 2 { "Stairs" }  { (0 4076) (0 4077) (0 4078) (0 4079) (0 4309) (2) } 
-
-	// The opening brace
-	_tok.assertNextToken("{");
-
-	while (_tok.hasMoreTokens())
-	{
-		std::string token = _tok.nextToken();
-
-		if (token == SELECTION_SET)
-		{
-			// Create a new SelectionSet info structure
-			_selectionSetInfo.push_back(SelectionSetImportInfo());
-
-			std::size_t selectionSetIndex = string::convert<std::size_t>(_tok.nextToken());
-
-			rMessage() << "Parsing Selection Set #" << selectionSetIndex << std::endl;
-
-			_tok.assertNextToken("{");
-
-			// Parse the name, replacing the " placeholder with a proper quote
-			_selectionSetInfo.back().name = boost::algorithm::replace_all_copy(_tok.nextToken(), """, "\"");
-
-			_tok.assertNextToken("}");
-
-			_tok.assertNextToken("{");
-
-			while (_tok.hasMoreTokens())
-			{
-				std::string nextToken = _tok.nextToken();
-
-				if (nextToken == "}") break;
-
-				// If it's not a closing curly brace, it must be an opening parenthesis
-				if (nextToken != "(")
-				{
-					throw parser::ParseException("InfoFile: Assertion failed: Required \"("
-						"\", found \"" + nextToken + "\"");
-				}
-
-				// Expect one or two numbers now
-				std::size_t entityNum = string::convert<std::size_t>(_tok.nextToken());
-
-				nextToken = _tok.nextToken();
-
-				if (nextToken == ")")
-				{
-					// Just the entity number, no primitive number
-					_selectionSetInfo.back().nodeIndices.insert(
-						SelectionSetImportInfo::IndexPair(entityNum, EMPTY_PRIMITVE_NUM));
-				}
-				else
-				{
-					// Primitive number is provided as well
-					std::size_t primitiveNum = string::convert<std::size_t>(nextToken);
-
-					// No more than 2 numbers are supported, so assume a closing parenthesis now
-					_tok.assertNextToken(")");
-
-					_selectionSetInfo.back().nodeIndices.insert(
-						SelectionSetImportInfo::IndexPair(entityNum, primitiveNum));
-				}
-			}
-		}
-
-		if (token == "}") break;
-	}
-}
-
-std::size_t InfoFile::getSelectionSetCount() const
-{
-	return _selectionSetInfo.size();
-}
-
-// Traversal function for the parsed selection sets
-void InfoFile::foreachSelectionSetInfo(const std::function<void(const SelectionSetImportInfo&)>& functor)
-{
-	std::for_each(_selectionSetInfo.begin(), _selectionSetInfo.end(), functor);
-}
-
-} // namespace map
diff --git a/radiant/map/InfoFile.h b/radiant/map/InfoFile.h
deleted file mode 100644
index d018d39..0000000
--- a/radiant/map/InfoFile.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#pragma once
-
-#include <map>
-#include "ilayer.h"
-#include "parser/DefTokeniser.h"
-
-namespace map
-{
-
-class InfoFile
-{
-public:
-	// Tokens / Constants
-	// The version of the map info file
-	static const int MAP_INFO_VERSION = 2;
-
-	// InfoFile tokens --------------------------------------------------
-	static const char* const HEADER_SEQUENCE;
-	static const char* const NODE_TO_LAYER_MAPPING;
-	static const char* const LAYER;
-	static const char* const LAYERS;
-	static const char* const NODE;
-	static const char* const SELECTION_SETS;
-	static const char* const SELECTION_SET;
-
-	// The internal placeholder number for "no primitive number"
-	static std::size_t EMPTY_PRIMITVE_NUM;
-
-	typedef std::map<int, std::string> LayerNameMap;
-
-	struct SelectionSetImportInfo
-	{
-		// The name of this set
-		std::string name;
-
-		typedef std::pair<std::size_t, std::size_t> IndexPair;
-
-		// The node indices, which will be resolved to nodes after import
-		std::set<IndexPair> nodeIndices;
-	};
-
-private:
-	// The actual DefTokeniser to split the infoStream into pieces
-	parser::BasicDefTokeniser<std::istream> _tok;
-
-	// The list of layernames
-	LayerNameMap _layerNames;
-
-	typedef std::vector<scene::LayerList> LayerLists;
-	LayerLists _layerMappings;
-
-	// The standard list (node is part of layer 0)
-	scene::LayerList _standardLayerList;
-
-	// TRUE if the map info file was found to be valid
-	bool _isValid;
-
-	// The internal "iterator" into the NodeToLayerMapping vector
-	LayerLists::const_iterator _layerMappingIterator;
-
-	// Parsed selection set information
-	std::vector<SelectionSetImportInfo> _selectionSetInfo;
-
-public:
-	// Pass the input stream to the constructor
-	InfoFile(std::istream& infoStream);
-
-	// Parse the entire file
-	void parse();
-
-	// Get the parsed Layer list
-	const LayerNameMap& getLayerNames() const;
-
-	// Returns the next layer mapping. The internal iterator is increased by this call.
-	// This allows the client code to treat this class like a LayerList input stream.
-	const scene::LayerList& getNextLayerMapping();
-
-	// Returns the number of parsed layer mappings
-	std::size_t getLayerMappingCount() const;
-
-	// Returns the number of selection sets
-	std::size_t getSelectionSetCount() const;
-
-	// Traversal function for the parsed selection sets
-	void foreachSelectionSetInfo(const std::function<void(const SelectionSetImportInfo&)>& functor);
-
-private:
-	void parseInfoFileBody();
-
-	// Parses the Layers section
-	void parseLayerNames();
-
-	// SelectionSet information parser
-	void parseSelectionSetInfo();
-
-	void parseNodeToLayerMapping();
-};
-
-} // namespace map
diff --git a/radiant/map/Map.cpp b/radiant/map/Map.cpp
index e63dda4..04bed4f 100644
--- a/radiant/map/Map.cpp
+++ b/radiant/map/Map.cpp
@@ -6,15 +6,16 @@
 #include "iscenegraph.h"
 #include "idialogmanager.h"
 #include "ieventmanager.h"
-#include "iundo.h"
 #include "ifilesystem.h"
 #include "ifiletypes.h"
+#include "iselectiongroup.h"
 #include "ifilter.h"
 #include "icounter.h"
 #include "iradiant.h"
 #include "imainframe.h"
 #include "imapresource.h"
-#include "iselectionset.h"
+#include "iaasfile.h"
+#include "igame.h"
 
 #include "registry/registry.h"
 #include "stream/textfilestream.h"
@@ -28,12 +29,10 @@
 #include "brush/BrushModule.h"
 #include "xyview/GlobalXYWnd.h"
 #include "camera/GlobalCamera.h"
-#include "map/AutoSaver.h"
 #include "scene/BasicRootNode.h"
 #include "map/MapFileManager.h"
 #include "map/MapPositionManager.h"
 #include "map/PointFile.h"
-#include "map/RegionManager.h"
 #include "map/RootNode.h"
 #include "map/MapResource.h"
 #include "map/algorithm/Clone.h"
@@ -46,135 +45,60 @@
 #include "ui/layers/LayerControlDialog.h"
 #include "ui/prefabselector/PrefabSelector.h"
 #include "selection/algorithm/Primitives.h"
+#include "selection/algorithm/Group.h"
 #include "selection/shaderclipboard/ShaderClipboard.h"
 #include "modulesystem/ModuleRegistry.h"
 #include "modulesystem/StaticModule.h"
+#include "RenderableAasFile.h"
 
 #include <boost/format.hpp>
 #include "algorithm/ChildPrimitives.h"
 
-namespace map {
+namespace map 
+{
 
-    namespace {
+    namespace 
+	{
         const char* const MAP_UNNAMED_STRING = N_("unnamed.map");
 
         const char* const GKEY_LAST_CAM_POSITION = "/mapFormat/lastCameraPositionKey";
         const char* const GKEY_LAST_CAM_ANGLE = "/mapFormat/lastCameraAngleKey";
         const char* const GKEY_PLAYER_START_ECLASS = "/mapFormat/playerStartPoint";
         const char* const GKEY_PLAYER_HEIGHT = "/defaults/playerHeight";
-
-        // Traverse all entities and store the first worldspawn into the map
-        class MapWorldspawnFinder :
-            public scene::NodeVisitor
-        {
-        public:
-            virtual bool pre(const scene::INodePtr& node) {
-                if (node_is_worldspawn(node)) {
-                    if (GlobalMap().getWorldspawn() == NULL) {
-                        GlobalMap().setWorldspawn(node);
-                    }
-                }
-                return false;
-            }
-        };
-
-        class CollectAllWalker :
-            public scene::NodeVisitor
-        {
-            scene::INodePtr _root;
-            std::vector<scene::INodePtr>& _nodes;
-        public:
-            CollectAllWalker(scene::INodePtr root, std::vector<scene::INodePtr>& nodes) :
-                _root(root),
-                _nodes(nodes)
-            {}
-
-            ~CollectAllWalker() {
-                for (std::vector<scene::INodePtr>::iterator i = _nodes.begin();
-                     i != _nodes.end(); ++i)
-                {
-                    _root->removeChildNode(*i);
-                }
-            }
-
-            virtual bool pre(const scene::INodePtr& node) {
-                // Add this to the list
-                _nodes.push_back(node);
-                // Don't traverse deeper than first level
-                return false;
-            }
-        };
-
-        void Node_insertChildFirst(scene::INodePtr parent, scene::INodePtr child) {
-            // Create a container to collect all the existing entities in the scenegraph
-            std::vector<scene::INodePtr> nodes;
-
-            // Collect all the child nodes of <parent> and move them into the container
-            {
-                CollectAllWalker visitor(parent, nodes);
-                parent->traverseChildren(visitor);
-
-                // the CollectAllWalker removes the nodes from the parent on destruction
-            }
-
-            // Now that the <parent> is empty, insert the worldspawn as first child
-            parent->addChildNode(child);
-
-            // Insert all the nodes again
-            for (std::vector<scene::INodePtr>::iterator i = nodes.begin();
-                 i != nodes.end();
-                 ++i)
-            {
-                parent->addChildNode(*i);
-            }
-        }
-
-        scene::INodePtr createWorldspawn()
-        {
-          scene::INodePtr worldspawn(GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("worldspawn", true)));
-          Node_insertChildFirst(GlobalSceneGraph().root(), worldspawn);
-          return worldspawn;
-        }
     }
 
 Map::Map() :
     _lastCopyMapName(""),
-    m_valid(false),
     _saveInProgress(false)
 {
 	_mapSaveTimer.Pause();
 }
 
-void Map::realiseResource() {
-    if (m_resource != NULL) {
-        m_resource->realise();
-    }
-}
+void Map::loadMapResourceFromPath(const std::string& path)
+{
+	// Map loading started
+	signal_mapEvent().emit(MapLoading);
 
-void Map::unrealiseResource() {
-    if (m_resource != NULL) {
-        m_resource->unrealise();
-    }
-}
+	_resource = GlobalMapResourceManager().loadFromPath(_mapName);
 
-void Map::onResourceRealise() {
-    if (m_resource == NULL) {
+    if (!_resource)
+    {
         return;
     }
 
-    if (isUnnamed() || !m_resource->load())
+    if (isUnnamed() || !_resource->load())
     {
         // Map is unnamed or load failed, reset map resource node to empty
-        m_resource->setNode(std::make_shared<RootNode>(""));
+        _resource->setNode(std::make_shared<RootNode>(""));
 
-        m_resource->getNode()->getUndoChangeTracker().save();
+        _resource->getNode()->getUndoChangeTracker().save();
         
         // Rename the map to "unnamed" in any case to avoid overwriting the failed map
         setMapName(_(MAP_UNNAMED_STRING));
     }
 
     // Take the new node and insert it as map root
-    GlobalSceneGraph().setRoot(m_resource->getNode());
+    GlobalSceneGraph().setRoot(_resource->getNode());
 
     // Associate the Scenegaph with the global RenderSystem
     // This usually takes a while since all editor textures are loaded - display a dialog to inform the user
@@ -185,38 +109,8 @@ void Map::onResourceRealise() {
             module::GlobalModuleRegistry().getModule(MODULE_RENDERSYSTEM)));
     }
 
-    AutoSaver().clearChanges();
-
-    setValid(true);
-}
-
-void Map::onResourceUnrealise() {
-    if(m_resource != 0)
-    {
-        setValid(false);
-      setWorldspawn(scene::INodePtr());
-
-      GlobalUndoSystem().clear();
-      GlobalSelectionSetManager().deleteAllSelectionSets();
-
-      GlobalSceneGraph().setRoot(scene::IMapRootNodePtr());
-    }
-}
-
-sigc::signal<void> Map::signal_mapValidityChanged() const
-{
-    return _sigMapValidityChanged;
-}
-
-void Map::setValid(bool valid)
-{
-    m_valid = valid;
-    _sigMapValidityChanged();
-}
-
-bool Map::isValid() const
-{
-    return m_valid;
+    // Map loading finished, emit the signal
+    signal_mapEvent().emit(MapLoaded);
 }
 
 void Map::updateTitle()
@@ -239,8 +133,9 @@ void Map::setMapName(const std::string& newName) {
     _mapName = newName;
 
     // Update the map resource's root node, if there is one
-    if (m_resource != NULL) {
-        m_resource->rename(newName);
+    if (_resource)
+	{
+        _resource->rename(newName);
     }
 
     // Update the title of the main window
@@ -255,18 +150,27 @@ bool Map::isUnnamed() const {
     return _mapName == _(MAP_UNNAMED_STRING);
 }
 
-void Map::setWorldspawn(scene::INodePtr node) {
-    m_world_node = node;
+void Map::setWorldspawn(const scene::INodePtr& node)
+{
+    _worldSpawnNode = node;
 }
 
-scene::INodePtr Map::getWorldspawn() {
-    return m_world_node;
+Map::MapEventSignal Map::signal_mapEvent() const
+{
+	return _mapEvent;
 }
 
-scene::IMapRootNodePtr Map::getRoot() {
-    if (m_resource != NULL) {
+const scene::INodePtr& Map::getWorldspawn()
+{
+    return _worldSpawnNode;
+}
+
+scene::IMapRootNodePtr Map::getRoot()
+{
+    if (_resource)
+	{
         // Try to cast the node onto a root node and return
-        return std::dynamic_pointer_cast<scene::IMapRootNode>(m_resource->getNode());
+        return std::dynamic_pointer_cast<scene::IMapRootNode>(_resource->getNode());
     }
 
     return scene::IMapRootNodePtr();
@@ -291,24 +195,20 @@ MapFormatPtr Map::getFormat()
 }
 
 // free all map elements, reinitialize the structures that depend on them
-void Map::freeMap() {
-    map::PointFile::Instance().clear();
+void Map::freeMap() 
+{
+	// Fire the map unloading event, 
+	// This will de-select stuff, clear the pointfile, etc.
+	signal_mapEvent().emit(MapUnloading);
 
-    GlobalSelectionSystem().setSelectedAll(false);
-    GlobalSelectionSystem().setSelectedAllComponents(false);
+	setWorldspawn(scene::INodePtr());
 
-    GlobalShaderClipboard().clear();
-    GlobalRegion().clear();
+	GlobalSceneGraph().setRoot(scene::IMapRootNodePtr());
 
-    if (m_resource)
-    {
-        m_resource->removeObserver(*this);
-    }
+	signal_mapEvent().emit(MapUnloaded);
 
     // Reset the resource pointer
-    m_resource = IMapResourcePtr();
-
-    GlobalLayerSystem().reset();
+    _resource.reset();
 }
 
 bool Map::isModified() const {
@@ -336,9 +236,9 @@ void Map::removeCameraPosition() {
     const std::string keyLastCamPos = game::current::getValue<std::string>(GKEY_LAST_CAM_POSITION);
     const std::string keyLastCamAngle = game::current::getValue<std::string>(GKEY_LAST_CAM_ANGLE);
 
-    if (m_world_node != NULL) {
+    if (_worldSpawnNode != NULL) {
         // Retrieve the entity from the worldspawn node
-        Entity* worldspawn = Node_getEntity(m_world_node);
+        Entity* worldspawn = Node_getEntity(_worldSpawnNode);
         assert(worldspawn != NULL); // This must succeed
 
         worldspawn->setKeyValue(keyLastCamPos, "");
@@ -353,9 +253,9 @@ void Map::saveCameraPosition()
     const std::string keyLastCamPos = game::current::getValue<std::string>(GKEY_LAST_CAM_POSITION);
     const std::string keyLastCamAngle = game::current::getValue<std::string>(GKEY_LAST_CAM_ANGLE);
 
-    if (m_world_node != NULL) {
+    if (_worldSpawnNode != NULL) {
         // Retrieve the entity from the worldspawn node
-        Entity* worldspawn = Node_getEntity(m_world_node);
+        Entity* worldspawn = Node_getEntity(_worldSpawnNode);
         assert(worldspawn != NULL); // This must succeed
 
         ui::CamWndPtr camWnd = GlobalCamera().getActiveCamWnd();
@@ -379,9 +279,9 @@ void Map::gotoStartPosition()
     Vector3 angles(0,0,0);
     Vector3 origin(0,0,0);
 
-    if (m_world_node != NULL) {
+    if (_worldSpawnNode != NULL) {
         // Retrieve the entity from the worldspawn node
-        Entity* worldspawn = Node_getEntity(m_world_node);
+        Entity* worldspawn = Node_getEntity(_worldSpawnNode);
         assert(worldspawn != NULL); // This must succeed
 
         // Try to find a saved "last camera position"
@@ -392,7 +292,7 @@ void Map::gotoStartPosition()
             // Construct the vector out of the std::string
             origin = string::convert<Vector3>(savedOrigin);
 
-            Vector3 angles = string::convert<Vector3>(
+            angles = string::convert<Vector3>(
                 worldspawn->getKeyValue(keyLastCamAngle)
             );
 
@@ -434,26 +334,49 @@ void Map::gotoStartPosition()
     focusViews(origin, angles);
 }
 
-scene::INodePtr Map::findWorldspawn() {
-    // Clear the current worldspawn node
-    setWorldspawn(scene::INodePtr());
+scene::INodePtr Map::findWorldspawn()
+{
+	scene::INodePtr worldspawn;
 
     // Traverse the scenegraph and search for the worldspawn
-    MapWorldspawnFinder visitor;
-    GlobalSceneGraph().root()->traverseChildren(visitor);
+	GlobalSceneGraph().root()->foreachNode([&](const scene::INodePtr& node)
+	{
+		if (Node_isWorldspawn(node)) 
+		{
+			worldspawn = node;
+			return false; // done traversing
+		}
+
+		return true;
+	});
+
+	// Set the worldspawn, might be null if nothing was found
+	setWorldspawn(worldspawn);
 
-    return getWorldspawn();
+    return worldspawn;
 }
 
-void Map::updateWorldspawn() {
-    if (findWorldspawn() == NULL) {
-        setWorldspawn(createWorldspawn());
-    }
+scene::INodePtr Map::createWorldspawn()
+{
+	scene::INodePtr worldspawn(GlobalEntityCreator().createEntity(
+		GlobalEntityClassManager().findOrInsert("worldspawn", true)));
+
+	// We want the world spawn entity to go for the pole position
+	GlobalSceneGraph().root()->addChildNode(worldspawn);
+
+	return worldspawn;
 }
 
-scene::INodePtr Map::findOrInsertWorldspawn() {
-    updateWorldspawn();
-    return getWorldspawn();
+const scene::INodePtr& Map::findOrInsertWorldspawn()
+{
+	// If we don't know any worldspawn yet, and can't find one either,
+	// let's create one afresh
+	if (!_worldSpawnNode && findWorldspawn() == nullptr)
+	{
+		setWorldspawn(createWorldspawn());
+	}
+
+    return _worldSpawnNode;
 }
 
 void Map::load(const std::string& filename) {
@@ -461,20 +384,13 @@ void Map::load(const std::string& filename) {
 
     setMapName(filename);
 
-    // Reset all layers before loading the file
-    GlobalLayerSystem().reset();
-    GlobalSelectionSystem().setSelectedAll(false);
-
     {
         wxutil::ScopeTimer timer("map load");
 
-        m_resource = GlobalMapResourceManager().capture(_mapName);
-        // greebo: Add the observer, this usually triggers a onResourceRealise() call.
-        m_resource->addObserver(*this);
+		loadMapResourceFromPath(_mapName);
 
         // Traverse the scenegraph and find the worldspawn
-        MapWorldspawnFinder finder;
-        GlobalSceneGraph().root()->traverseChildren(finder);
+		findWorldspawn();
     }
 
     rMessage() << "--- LoadMapFile ---\n";
@@ -492,9 +408,6 @@ void Map::load(const std::string& filename) {
     // Remove them, so that the user doesn't get bothered with them
     GlobalMapPosition().removePositions();
 
-    // Disable the region to make sure
-    GlobalRegion().disable();
-
     // Clear the shaderclipboard, the references are most probably invalid now
     GlobalShaderClipboard().clear();
 
@@ -528,7 +441,7 @@ bool Map::save(const MapFormatPtr& mapFormat)
     wxutil::ScopeTimer timer("map save");
 
     // Save the actual map resource
-    bool success = m_resource->save(mapFormat);
+    bool success = _resource->save(mapFormat);
 
     // Remove the saved camera position
     removeCameraPosition();
@@ -554,8 +467,7 @@ bool Map::save(const MapFormatPtr& mapFormat)
 void Map::createNew() {
     setMapName(_(MAP_UNNAMED_STRING));
 
-    m_resource = GlobalMapResourceManager().capture(_mapName);
-    m_resource->addObserver(*this);
+	loadMapResourceFromPath(_mapName);
 
     SceneChangeNotify();
 
@@ -571,7 +483,7 @@ bool Map::import(const std::string& filename)
     bool success = false;
 
     {
-        IMapResourcePtr resource = GlobalMapResourceManager().capture(filename);
+        IMapResourcePtr resource = GlobalMapResourceManager().loadFromPath(filename);
 
         if (resource->load())
         {
@@ -795,9 +707,9 @@ void Map::loadPrefabAt(const Vector3& targetCoords)
     /*MapFileSelection fileInfo =
         MapFileManager::getMapFileSelection(true, _("Load Prefab"), "prefab");*/
 
-	std::string path = ui::PrefabSelector::ChoosePrefab();
+	ui::PrefabSelector::Result result = ui::PrefabSelector::ChoosePrefab();
 
-	if (!path.empty())
+	if (!result.prefabPath.empty())
 	{
         UndoableCommand undo("loadPrefabAt");
 
@@ -805,7 +717,7 @@ void Map::loadPrefabAt(const Vector3& targetCoords)
         GlobalSelectionSystem().setSelectedAll(false);
 
         // Now import the prefab (imported items get selected)
-		import(path);
+		import(result.prefabPath);
 
         // Switch texture lock on
         bool prevTexLockState = GlobalBrush().textureLockEnabled();
@@ -816,6 +728,20 @@ void Map::loadPrefabAt(const Vector3& targetCoords)
 
         // Revert to previous state
         GlobalBrush().setTextureLock(prevTexLockState);
+
+		// Check whether we should group the prefab parts
+		if (result.insertAsGroup && GlobalSelectionSystem().countSelected() > 1)
+		{
+			try
+			{
+				selection::algorithm::groupSelected();
+			}
+			catch (selection::algorithm::CommandNotAvailableException& ex)
+			{
+				// Ignore grouping errors on prefab insert, just log the message
+				rError() << "Error grouping the prefab: " << ex.what() << std::endl;
+			}
+		}
     }
 }
 
@@ -850,11 +776,10 @@ void Map::registerCommands()
 }
 
 // Static command targets
-void Map::newMap(const cmd::ArgumentList& args) {
-    if (GlobalMap().askForSave(_("New Map"))) {
-        // Turn regioning off when starting a new map
-        GlobalRegion().disable();
-
+void Map::newMap(const cmd::ArgumentList& args)
+{
+    if (GlobalMap().askForSave(_("New Map")))
+	{
         GlobalMap().freeMap();
         GlobalMap().createNew();
     }
@@ -938,7 +863,7 @@ void Map::rename(const std::string& filename) {
         SceneChangeNotify();
     }
     else {
-        m_resource->save();
+        _resource->save();
         setModified(false);
     }
 }
@@ -1032,11 +957,14 @@ const std::string& Map::getName() const {
     return _name;
 }
 
-const StringSet& Map::getDependencies() const {
+const StringSet& Map::getDependencies() const 
+{
     static StringSet _dependencies;
 
-    if (_dependencies.empty()) {
+    if (_dependencies.empty())
+	{
         _dependencies.insert(MODULE_RADIANT);
+		_dependencies.insert(MODULE_GAMEMANAGER);
     }
 
     return _dependencies;
@@ -1058,9 +986,6 @@ void Map::initialiseModule(const ApplicationContext& ctx)
     // Add the Map-related commands to the EventManager
     registerCommands();
 
-    // Add the region-related commands to the EventManager
-    RegionManager::initialiseCommands();
-
     // Add the map position commands to the EventManager
     GlobalMapPosition().initialise();
 
diff --git a/radiant/map/Map.h b/radiant/map/Map.h
index 72b9e3f..a688c70 100644
--- a/radiant/map/Map.h
+++ b/radiant/map/Map.h
@@ -19,8 +19,7 @@ class TextInputStream;
 namespace map {
 
 class Map :
-	public IMap,
-	public IMapResource::Observer
+	public IMap
 {
 	// The map name
 	std::string _mapName;
@@ -29,14 +28,11 @@ class Map :
 	std::string _lastCopyMapName;
 
 	// Pointer to the resource for this map
-	IMapResourcePtr m_resource;
+	IMapResourcePtr _resource;
 
-	bool m_valid;
 	bool m_modified;
 
-    sigc::signal<void> _sigMapValidityChanged;
-
-	scene::INodePtr m_world_node; // "classname" "worldspawn" !
+	scene::INodePtr _worldSpawnNode; // "classname" "worldspawn" !
 
 	bool _saveInProgress;
 
@@ -47,26 +43,23 @@ class Map :
     // messages
     wxStopWatch _mapSaveTimer;
 
-private:
-
-    // If no worldspawn can be found in the scenegraph, this creates one
-	void updateWorldspawn();
+	MapEventSignal _mapEvent;
 
+private:
     std::string getSaveConfirmationText() const;
 
 public:
 	Map();
 
-	virtual scene::INodePtr getWorldspawn() override;
+	virtual MapEventSignal signal_mapEvent() const override;
+	virtual const scene::INodePtr& getWorldspawn() override;
+	virtual const scene::INodePtr& findOrInsertWorldspawn() override;
 	virtual scene::IMapRootNodePtr getRoot() override;
 
 	// RegisterableModule implementation
-	virtual const std::string& getName() const;
-	virtual const StringSet& getDependencies() const;
-	virtual void initialiseModule(const ApplicationContext& ctx);
-
-	void realiseResource();
-	void unrealiseResource();
+	virtual const std::string& getName() const override;
+	virtual const StringSet& getDependencies() const override;
+	virtual void initialiseModule(const ApplicationContext& ctx) override;
 
 	/** greebo: Returns true if the map has not been named yet.
 	 */
@@ -150,14 +143,6 @@ public:
 	// free all map elements, reinitialize the structures that depend on them
 	void freeMap();
 
-	// Resource::Observer implementation
-	void onResourceRealise();
-	void onResourceUnrealise();
-
-	// Accessor methods for the "valid" flag
-	void setValid(bool valid);
-	bool isValid() const;
-
 	/** greebo: Returns true if the map has unsaved changes.
 	 */
 	bool isModified() const;
@@ -165,24 +150,11 @@ public:
 	// Sets the modified status of this map
 	void setModified(bool modifiedFlag);
 
-    // Signal emitted when the map validity changes
-    sigc::signal<void> signal_mapValidityChanged() const;
-
 	// Updates the window title of the mainframe
 	void updateTitle();
 
 	// Accessor methods for the worldspawn node
-	void setWorldspawn(scene::INodePtr node);
-
-	/** greebo: This retrieves the worldspawn node of this map.
-	 *			If no worldspawn can be found, this creates one.
-	 */
-	scene::INodePtr findOrInsertWorldspawn();
-
-	/** greebo: Tries to locate the worldspawn in the global scenegraph
-	 *			Returns NULL (empty shared_ptr) if nothing is found.
-	 */
-	scene::INodePtr findWorldspawn();
+	void setWorldspawn(const scene::INodePtr& node);
 
 	/** greebo: Returns the map format for this map
 	 */
@@ -241,6 +213,19 @@ public:
 	static void loadPrefab(const cmd::ArgumentList& args);
 	static void saveSelectedAsPrefab(const cmd::ArgumentList& args);
 
+private:
+	/**
+	 * greebo: Tries to locate the worldspawn in the global scenegraph and 
+	 * stores it into the local member variable.
+	 * Returns the node that was found (can be an empty ptr).
+	 */
+	scene::INodePtr findWorldspawn();
+
+	// Creates a fresh worldspawn node and inserts it into the root scene node
+	scene::INodePtr createWorldspawn();
+
+	void loadMapResourceFromPath(const std::string& path);
+
 }; // class Map
 
 } // namespace map
diff --git a/radiant/map/MapResource.cpp b/radiant/map/MapResource.cpp
index b778dec..688ebf7 100644
--- a/radiant/map/MapResource.cpp
+++ b/radiant/map/MapResource.cpp
@@ -11,6 +11,8 @@
 #include "ifilesystem.h"
 #include "imainframe.h"
 #include "iregistry.h"
+#include "imapinfofile.h"
+
 #include "map/Map.h"
 #include "map/RootNode.h"
 #include "mapfile.h"
@@ -27,15 +29,13 @@
 #include <boost/format.hpp>
 #include <boost/filesystem.hpp>
 
-#include "InfoFile.h"
+#include "infofile/InfoFile.h"
 #include "string/string.h"
 
 #include "algorithm/MapImporter.h"
 #include "algorithm/MapExporter.h"
-#include "algorithm/InfoFileExporter.h"
-#include "algorithm/AssignLayerMappingWalker.h"
+#include "infofile/InfoFileExporter.h"
 #include "algorithm/ChildPrimitives.h"
-#include "scene/LayerValidityCheckWalker.h"
 
 namespace fs = boost::filesystem;
 
@@ -85,9 +85,7 @@ std::string MapResource::_infoFileExt;
 // Constructor
 MapResource::MapResource(const std::string& name) :
 	_originalName(name),
-	_type(os::getExtension(name)),
-	_modified(0),
-	_realised(false)
+	_type(os::getExtension(name))
 {
 	// Initialise the paths, this is all needed for realisation
     _path = rootPath(_originalName);
@@ -104,12 +102,6 @@ MapResource::MapResource(const std::string& name) :
 	}
 }
 
-MapResource::~MapResource() {
-    if (realised()) {
-		unrealise();
-	}
-}
-
 void MapResource::rename(const std::string& fullPath)
 {
 	// Save the paths locally and split them into parts
@@ -124,8 +116,6 @@ void MapResource::rename(const std::string& fullPath)
 
 bool MapResource::load()
 {
-	ASSERT_MESSAGE(realised(), "resource not realised");
-
 	if (!_mapRoot)
     {
 		// Map not loaded yet, acquire map root node from loader
@@ -138,12 +128,6 @@ bool MapResource::load()
 	return _mapRoot != nullptr;
 }
 
-/**
- * Save this resource (only for map resources).
- *
- * @returns
- * true if the resource was saved, false otherwise.
- */
 bool MapResource::save(const MapFormatPtr& mapFormat)
 {
 	// For saving, take the default map format for this game type
@@ -290,59 +274,8 @@ void MapResource::setNode(const scene::IMapRootNodePtr& node)
 	connectMap();
 }
 
-void MapResource::addObserver(Observer& observer) {
-	if (realised()) {
-		observer.onResourceRealise();
-	}
-	_observers.insert(&observer);
-}
-
-void MapResource::removeObserver(Observer& observer) {
-	if (realised()) {
-		observer.onResourceUnrealise();
-	}
-	_observers.erase(&observer);
-}
-
-bool MapResource::realised() {
-	return _realised;
-}
-
-// Realise this MapResource
-void MapResource::realise() {
-	if (_realised) {
-		return; // nothing to do
-	}
-
-	_realised = true;
-
-	// Realise the observers
-	for (ResourceObserverList::iterator i = _observers.begin();
-		 i != _observers.end(); i++)
-	{
-		(*i)->onResourceRealise();
-	}
-}
-
-void MapResource::unrealise() {
-	if (!_realised) {
-		return; // nothing to do
-	}
-
-	_realised = false;
-
-	// Realise the observers
-	for (ResourceObserverList::iterator i = _observers.begin();
-		 i != _observers.end(); i++)
-	{
-		(*i)->onResourceUnrealise();
-	}
-
-	//rMessage() << "MapResource::unrealise: " << _path.c_str() << _name.c_str() << "\n";
-	_mapRoot.reset();
-}
-
-void MapResource::onMapChanged() {
+void MapResource::onMapChanged() 
+{
 	GlobalMap().setModified(true);
 }
 
@@ -355,33 +288,14 @@ void MapResource::connectMap()
     }
 }
 
-std::time_t MapResource::modified() const {
-	std::string fullpath = _path + _name;
-	return file_modified(fullpath.c_str());
-}
-
 void MapResource::mapSave()
 {
-	_modified = modified();
-    
     if (_mapRoot)
     {
         _mapRoot->getUndoChangeTracker().save();
     }
 }
 
-bool MapResource::isModified() const {
-	// had or has an absolute path // AND disk timestamp changed
-	return (!_path.empty() && _modified != modified())
-			|| !path_equal(rootPath(_originalName).c_str(), _path.c_str()); // OR absolute vfs-root changed
-}
-
-void MapResource::reload()
-{
-    unrealise();
-	realise();
-}
-
 MapFormatPtr MapResource::determineMapFormat(std::istream& stream)
 {
 	// Get all registered map formats
@@ -389,15 +303,15 @@ MapFormatPtr MapResource::determineMapFormat(std::istream& stream)
 
 	MapFormatPtr format;
 
-	for (std::set<MapFormatPtr>::const_iterator f = availableFormats.begin(); f != availableFormats.end(); ++f)
+	for (const MapFormatPtr& candidate : availableFormats)
 	{
 		// Rewind the stream before passing it to the format for testing
 		// Map format valid, rewind the stream
 		stream.seekg(0, std::ios_base::beg);
 
-		if ((*f)->canLoad(stream))
+		if (candidate->canLoad(stream))
 		{
-			format = *f;
+			format = candidate;
 			break;
 		}
 	}
@@ -410,72 +324,43 @@ MapFormatPtr MapResource::determineMapFormat(std::istream& stream)
 
 RootNodePtr MapResource::loadMapNode()
 {
+	RootNodePtr rootNode;
+
 	// greebo: Check if we have valid settings
 	// The _path might be empty if we're loading from a folder outside the mod
 	if (_name.empty() && _type.empty())
 	{
-        return RootNodePtr();
+        return rootNode;
 	}
 
-	// Build the map path
-	std::string fullpath = _path + _name;
-
-	if (path_is_absolute(fullpath.c_str()))
+	try
 	{
-		rMessage() << "Open file " << fullpath << " for determining the map format...";
-
-		TextFileInputStream file(fullpath);
+		// Build the map path
+		std::string fullpath = _path + _name;
 
-		if (file.failed())
+		// Open a stream (from physical file or VFS)
+		openFileStream(fullpath, [&](std::istream& mapStream)
 		{
-			rError() << "failure" << std::endl;
-
-			wxutil::Messagebox::ShowError(
-				(boost::format(_("Failure opening map file:\n%s")) % fullpath).str());
-
-            return RootNodePtr();
-		}
-
-		std::istream mapStream(&file);
-		return loadMapNodeFromStream(mapStream, fullpath);
+			rootNode = loadMapNodeFromStream(mapStream, fullpath);
+		});
 	}
-	else 
+	catch (std::runtime_error& ex)
 	{
-		// Not an absolute path, might as well be a VFS path, so try to load it from the PAKs
-		rMessage() << "Open file " << fullpath << " from VFS for determining the map format...";
-
-		ArchiveTextFilePtr vfsFile = GlobalFileSystem().openTextFile(fullpath);
-
-		if (!vfsFile)
-		{
-			rError() << "Could not find file in VFS either: " << fullpath << std::endl;
-            return RootNodePtr();
-		}
-
-		std::istream mapStream(&(vfsFile->getInputStream()));
-
-		// Deflated text files don't support stream positioning (seeking)
-		// so load everything into one large string and create a new buffer
-		std::stringstream stringStream;
-		stringStream << mapStream.rdbuf();
-		
-		return loadMapNodeFromStream(stringStream, fullpath);
+		wxutil::Messagebox::ShowError(ex.what());
 	}
+
+	return rootNode;
 }
 
 RootNodePtr MapResource::loadMapNodeFromStream(std::istream& stream, const std::string& fullpath)
 {
-	rMessage() << "success" << std::endl;
-
 	// Get the mapformat
 	MapFormatPtr format = determineMapFormat(stream);
 
 	if (format == NULL)
 	{
-		wxutil::Messagebox::ShowError(
+		throw std::runtime_error(
 			(boost::format(_("Could not determine map format of file:\n%s")) % fullpath).str());
-
-        return RootNodePtr();
 	}
 
 	// Map format valid, rewind the stream
@@ -515,84 +400,7 @@ bool MapResource::loadFile(std::istream& mapStream, const MapFormat& format, con
 		}
 
 		// Check for an additional info file
-		std::string infoFilename(filename.substr(0, filename.rfind('.')));
-		infoFilename += game::current::getValue<std::string>(GKEY_INFO_FILE_EXTENSION);
-
-		std::ifstream infoFileStream(infoFilename.c_str());
-
-		if (infoFileStream.is_open())
-		{
-			rMessage() << " found information file... ";
-		}
-
-		rMessage() << "success" << std::endl;
-
-		// Read the infofile
-		InfoFile infoFile(infoFileStream);
-
-		try
-		{
-			// Start parsing, this will throw if any errors occur
-			infoFile.parse();
-
-			// Create the layers according to the data found in the map information file
-			const InfoFile::LayerNameMap& layers = infoFile.getLayerNames();
-
-			for (InfoFile::LayerNameMap::const_iterator i = layers.begin();
-				 i != layers.end(); ++i)
-			{
-				// Create the named layer with the saved ID
-				GlobalLayerSystem().createLayer(i->second, i->first);
-			}
-
-			// Now that the graph is in place, assign the layers
-			AssignLayerMappingWalker walker(infoFile);
-			root->traverseChildren(walker);
-
-			rMessage() << "Sanity-checking the layer assignments...";
-
-			// Sanity-check the layer mapping, it's possible that some .darkradiant
-			// files are mapping nodes to non-existent layer IDs
-			scene::LayerValidityCheckWalker checker;
-			root->traverseChildren(checker);
-
-			rMessage() << "done, had to fix " << checker.getNumFixed() << " assignments." << std::endl;
-
-			// Remove all selection sets, there shouldn't be many left at this point
-			GlobalSelectionSetManager().deleteAllSelectionSets();
-
-			// Re-construct the selection sets
-			infoFile.foreachSelectionSetInfo([&] (const InfoFile::SelectionSetImportInfo& info)
-			{
-				selection::ISelectionSetPtr set = GlobalSelectionSetManager().createSelectionSet(info.name);
-
-				std::size_t failedNodes = 0;
-
-				std::for_each(info.nodeIndices.begin(), info.nodeIndices.end(), 
-					[&] (const InfoFile::SelectionSetImportInfo::IndexPair& indexPair)
-				{
-					scene::INodePtr node = importFilter.getNodeByIndexPair(indexPair);
-
-					if (node)
-					{
-						set->addNode(node);
-					}
-					else
-					{
-						failedNodes++;
-					}
-				});
-
-				if (failedNodes > 0)
-				{
-					rWarning() << "Couldn't resolve " << failedNodes << " nodes in selection set " << set->getName() << std::endl;
-				}
-			});
-		}
-		catch (parser::ParseException& e)
-		{
-			rError() << "[MapResource] Unable to parse info file: " << e.what() << std::endl;
-		}
+		loadInfoFile(root, filename, importFilter.getNodeMap());
 
 		return true;
 	}
@@ -621,12 +429,92 @@ bool MapResource::loadFile(std::istream& mapStream, const MapFormat& format, con
 	}
 }
 
-std::string MapResource::getTemporaryFileExtension()
+void MapResource::loadInfoFile(const RootNodePtr& root, const std::string& filename, const NodeIndexMap& nodeMap)
 {
-	time_t localtime;
-	time(&localtime);
+	try
+	{
+		std::string infoFilename(filename.substr(0, filename.rfind('.')));
+		infoFilename += game::current::getValue<std::string>(GKEY_INFO_FILE_EXTENSION);
 
-	return string::to_string(localtime);
+		openFileStream(infoFilename, [&](std::istream& infoFileStream)
+		{
+			loadInfoFileFromStream(infoFileStream, root, nodeMap);
+		});
+	}
+	catch (std::runtime_error& ex)
+	{
+		rWarning() << ex.what() << std::endl;
+	}
+}
+
+void MapResource::loadInfoFileFromStream(std::istream& infoFileStream, const RootNodePtr& root, const NodeIndexMap& nodeMap)
+{
+	if (!infoFileStream.good())
+	{
+		rError() << "[MapResource] No valid info file stream" << std::endl;
+		return;
+	}
+
+	rMessage() << "Parsing info file..." << std::endl;
+
+	try
+	{
+		// Read the infofile
+		InfoFile infoFile(infoFileStream, root, nodeMap);
+
+		// Start parsing, this will throw if any errors occur
+		infoFile.parse();
+	}
+	catch (parser::ParseException& e)
+	{
+		rError() << "[MapResource] Unable to parse info file: " << e.what() << std::endl;
+	}
+}
+
+void MapResource::openFileStream(const std::string& path, const std::function<void(std::istream&)>& streamProcessor)
+{
+	if (path_is_absolute(path.c_str()))
+	{
+		rMessage() << "Open file " << path << " from filesystem...";
+
+		TextFileInputStream file(path);
+
+		if (file.failed())
+		{
+			rError() << "failure" << std::endl;
+			throw std::runtime_error((boost::format(_("Failure opening file:\n%s")) % path).str());
+		}
+
+		std::istream stream(&file);
+
+		rMessage() << "success." << std::endl;
+
+		streamProcessor(stream);
+	}
+	else
+	{
+		// Not an absolute path, might as well be a VFS path, so try to load it from the PAKs
+		rMessage() << "Trying to open file " << path << " from VFS...";
+
+		ArchiveTextFilePtr vfsFile = GlobalFileSystem().openTextFile(path);
+
+		if (!vfsFile)
+		{
+			rError() << "failure" << std::endl;
+			throw std::runtime_error((boost::format(_("Failure opening file:\n%s")) % path).str());
+		}
+
+		rMessage() << "success." << std::endl;
+
+		std::istream vfsStream(&(vfsFile->getInputStream()));
+
+		// Deflated text files don't support stream positioning (seeking)
+		// so load everything into one large string and create a new buffer
+		std::stringstream stringStream;
+		stringStream << vfsStream.rdbuf();
+
+		streamProcessor(stringStream);
+	}
 }
 
 bool MapResource::checkIsWriteable(const boost::filesystem::path& path)
diff --git a/radiant/map/MapResource.h b/radiant/map/MapResource.h
index b22ba2c..d86a978 100644
--- a/radiant/map/MapResource.h
+++ b/radiant/map/MapResource.h
@@ -2,6 +2,7 @@
 
 #include "imapresource.h"
 #include "imapformat.h"
+#include "imapinfofile.h"
 #include "imodel.h"
 #include "imap.h"
 #include <set>
@@ -15,6 +16,7 @@ class MapResource :
 	public IMapResource,
 	public boost::noncopyable
 {
+private:
     RootNodePtr _mapRoot;
 
 	// Name given during construction
@@ -28,62 +30,26 @@ class MapResource :
 	// Type of resource "map"
 	std::string _type;
 
-	typedef std::set<IMapResource::Observer*> ResourceObserverList;
-	ResourceObserverList _observers;
-
-	std::time_t _modified;
-	bool _realised;
-
 public:
 	// Constructor
 	MapResource(const std::string& name);
 
-	virtual ~MapResource();
-
-	void rename(const std::string& fullPath);
+	void rename(const std::string& fullPath) override;
 
-	bool load();
-
-	/**
-	 * Save this resource (only for map resources).
-	 *
-	 * It's possible to pass a mapformat to be used for saving. If the map
-	 * format argument is omitted, the format corresponding to the current
-	 * game type is used.
-	 *
-	 * @returns
-	 * true if the resource was saved, false otherwise.
-	 */
-	bool save(const MapFormatPtr& mapFormat = MapFormatPtr());
-
-	// Reloads from disk
-	void reload();
+	bool load() override;
+	bool save(const MapFormatPtr& mapFormat = MapFormatPtr()) override;
 
 	scene::IMapRootNodePtr getNode() override;
     void setNode(const scene::IMapRootNodePtr& node) override;
 
-	virtual void addObserver(Observer& observer);
-	virtual void removeObserver(Observer& observer);
-
-	bool realised();
-
-	// Realise this MapResource
-	void realise();
-	void unrealise();
-
-  std::time_t modified() const;
-  void mapSave();
-
-  bool isModified() const;
-  void refresh();
-
-	void onMapChanged();
-
 	// Save the map contents to the given filename using the given MapFormat export module
 	static bool saveFile(const MapFormat& format, const scene::INodePtr& root,
 						 const GraphTraversalFunc& traverse, const std::string& filename);
 
 private:
+	void mapSave();
+	void onMapChanged();
+
 	// Create a backup copy of the map (used before saving)
 	bool saveBackup();
 
@@ -99,8 +65,12 @@ private:
 	bool loadFile(std::istream& mapStream, const MapFormat& format, 
                   const RootNodePtr& root, const std::string& filename);
 
-	// Returns a (hopefully) unique file extension for saving
-	static std::string getTemporaryFileExtension();
+	void loadInfoFile(const RootNodePtr& root, const std::string& filename, const NodeIndexMap& nodeMap);
+	void loadInfoFileFromStream(std::istream& infoFileStream, const RootNodePtr& root, const NodeIndexMap& nodeMap);
+
+	// Opens a stream for the given path, which might be VFS path or an absolute one. The streamProcessor
+	// function is then called with the opened stream. Throws std::runtime_error on stream open failure.
+	void openFileStream(const std::string& path, const std::function<void(std::istream&)>& streamProcessor);
 
 	static bool checkIsWriteable(const boost::filesystem::path& path);
 };
diff --git a/radiant/map/MapResourceManager.cpp b/radiant/map/MapResourceManager.cpp
index 6acd65a..77c2f40 100644
--- a/radiant/map/MapResourceManager.cpp
+++ b/radiant/map/MapResourceManager.cpp
@@ -10,15 +10,10 @@
 namespace map
 {
 
-IMapResourcePtr MapResourceManager::capture(const std::string& path)
+IMapResourcePtr MapResourceManager::loadFromPath(const std::string& path)
 {
 	// Create a new MapResource and return it.
-	MapResourcePtr newResource(new map::MapResource(path));
-
-	// Realise the new resource
-	newResource->realise();
-
-	return newResource;
+	return std::make_shared<MapResource>(path);
 }
 
 // RegisterableModule implementation
diff --git a/radiant/map/MapResourceManager.h b/radiant/map/MapResourceManager.h
index bda8fd5..d35caa1 100644
--- a/radiant/map/MapResourceManager.h
+++ b/radiant/map/MapResourceManager.h
@@ -10,13 +10,12 @@ class MapResourceManager :
 	public IMapResourceManager
 {
 public:
-	// Capture a named resource.
-	IMapResourcePtr capture(const std::string& path);
+	IMapResourcePtr loadFromPath(const std::string& path) override;
 
 	// RegisterableModule implementation
-	virtual const std::string& getName() const;
-	virtual const StringSet& getDependencies() const;
-	virtual void initialiseModule(const ApplicationContext& ctx);
+	virtual const std::string& getName() const override;
+	virtual const StringSet& getDependencies() const override;
+	virtual void initialiseModule(const ApplicationContext& ctx) override;
 };
 
 }
diff --git a/radiant/map/PointFile.cpp b/radiant/map/PointFile.cpp
index d52b4fb..3d44714 100644
--- a/radiant/map/PointFile.cpp
+++ b/radiant/map/PointFile.cpp
@@ -31,6 +31,8 @@ PointFile::PointFile() :
 {
 	_renderstate = GlobalRenderSystem().capture("$POINTFILE");
 	GlobalRenderSystem().attachRenderable(*this);
+
+	GlobalMap().signal_mapEvent().connect(sigc::mem_fun(*this, &PointFile::onMapEvent));
 }
 
 void PointFile::destroy() {
@@ -38,6 +40,14 @@ void PointFile::destroy() {
 	_renderstate = ShaderPtr();
 }
 
+void PointFile::onMapEvent(IMap::MapEvent ev)
+{
+	if (ev == IMap::MapUnloading)
+	{
+		clear();
+	}
+}
+
 // Static accessor method
 PointFile& PointFile::Instance() {
 	static PointFile _instance;
diff --git a/radiant/map/PointFile.h b/radiant/map/PointFile.h
index 6ff076c..77b008a 100644
--- a/radiant/map/PointFile.h
+++ b/radiant/map/PointFile.h
@@ -1,13 +1,14 @@
-#ifndef POINTFILE_H_
-#define POINTFILE_H_
+#pragma once
 
 #include <vector>
 #include "irender.h"
+#include "imap.h"
 #include "icommandsystem.h"
 #include "irenderable.h"
 #include "math/Vector3.h"
 
-namespace map {
+namespace map 
+{
 
 class PointFile :
 	public Renderable,
@@ -58,19 +59,19 @@ public:
 	/*
 	 * Solid renderable submission function (front-end)
 	 */
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
 
 	/*
 	 * Wireframe renderable submission function (front-end).
 	 */
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
 
-	void setRenderSystem(const RenderSystemPtr& renderSystem)
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override
 	{}
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // never highlighted
+		return Highlight::NoHighlight;
 	}
 
 	/** greebo: This sets the camera position to the next/prev leak spot.
@@ -99,8 +100,8 @@ private:
 
 	// Generates the OpenGL displaylist from the point vector
 	void generateDisplayList();
+
+	void onMapEvent(IMap::MapEvent ev);
 };
 
 } // namespace map
-
-#endif /*POINTFILE_H_*/
diff --git a/radiant/map/RegionManager.cpp b/radiant/map/RegionManager.cpp
index c7b9cfd..e719b1e 100644
--- a/radiant/map/RegionManager.cpp
+++ b/radiant/map/RegionManager.cpp
@@ -27,62 +27,31 @@
 #include "selection/algorithm/Primitives.h"
 #include "selection/algorithm/General.h"
 #include "map/MapResource.h"
+#include "map/Map.h"
+#include "modulesystem/StaticModule.h"
 
 #include <memory>
 
-namespace map {
+namespace map
+{
 
-    namespace {
+    namespace
+	{
         typedef std::shared_ptr<RegionManager> RegionManagerPtr;
         const std::string GKEY_PLAYER_START_ECLASS = "/mapFormat/playerStartPoint";
-
-        class AABBCollectorVisible :
-            public scene::NodeVisitor
-        {
-            AABB& _targetAABB;
-        public:
-            AABBCollectorVisible(AABB& targetAABB) :
-                _targetAABB(targetAABB)
-            {}
-
-            bool pre(const scene::INodePtr& node)
-            {
-                if (node->visible())
-                {
-                    _targetAABB.includeAABB(node->worldAABB());
-                }
-
-                return true;
-            }
-        };
-
-        AABB getVisibleBounds()
-        {
-            AABB returnValue;
-            AABBCollectorVisible collector(returnValue);
-
-            GlobalSceneGraph().root()->traverse(collector);
-
-            return returnValue;
-        }
     }
 
 RegionManager::RegionManager() :
     _active(false)
-{
-    _worldMin = game::current::getValue<float>("/defaults/minWorldCoord");
-    _worldMax = game::current::getValue<float>("/defaults/maxWorldCoord");
-
-    for (int i = 0; i < 6; i++) {
-        _brushes[i] = scene::INodePtr();
-    }
-}
+{}
 
-bool RegionManager::isEnabled() const {
+bool RegionManager::isEnabled() const 
+{
     return _active;
 }
 
-void RegionManager::disable() {
+void RegionManager::disable()
+{
     _active = false;
 
     _bounds = AABB::createFromMinMax(Vector3(1,1,1)*_worldMin, Vector3(1,1,1)*_worldMax);
@@ -162,6 +131,7 @@ void RegionManager::addRegionBrushes()
     for (int i = 0; i < 6; i++) {
         // Create a new brush
         _brushes[i] = GlobalBrushCreator().createBrush();
+
         // Insert it into worldspawn
         scene::addNodeToContainer(_brushes[i], GlobalMap().findOrInsertWorldspawn());
     }
@@ -250,7 +220,7 @@ void RegionManager::removeRegionBrushes() {
     for (int i = 0; i < 6; i++) {
         // Remove the brushes from the scene
         if (_brushes[i] != NULL) {
-            GlobalMap().getWorldspawn()->removeChildNode(_brushes[i]);
+            GlobalMap().findOrInsertWorldspawn()->removeChildNode(_brushes[i]);
             _brushes[i] = scene::INodePtr();
         }
     }
@@ -264,7 +234,7 @@ void RegionManager::removeRegionBrushes() {
 // Static members (used as command targets for EventManager)
 
 void RegionManager::disableRegion(const cmd::ArgumentList& args) {
-    GlobalRegion().disable();
+    disable();
     SceneChangeNotify();
 }
 
@@ -284,12 +254,12 @@ void RegionManager::setRegionXY(const cmd::ArgumentList& args) {
         );
 
         // Set the bounds from the calculated XY rectangle
-        GlobalRegion().setRegionFromXY(topLeft, lowerRight);
+        setRegionFromXY(topLeft, lowerRight);
     }
     else {
         wxutil::Messagebox::ShowError(
             _("Could not set Region: XY Top View not found."));
-        GlobalRegion().disable();
+        disable();
     }
     SceneChangeNotify();
 }
@@ -303,7 +273,7 @@ void RegionManager::setRegionFromBrush(const cmd::ArgumentList& args) {
         const scene::INodePtr& node = GlobalSelectionSystem().ultimateSelected();
 
         // Set the bounds of the region to the selection's extents
-        GlobalRegion().setRegion(node->worldAABB());
+        setRegion(node->worldAABB());
 
         // Delete the currently selected brush (undoable command)
         {
@@ -316,7 +286,7 @@ void RegionManager::setRegionFromBrush(const cmd::ArgumentList& args) {
     else {
         wxutil::Messagebox::ShowError(
             _("Could not set Region: please select a single Brush."));
-        GlobalRegion().disable();
+        disable();
     }
 }
 
@@ -331,7 +301,7 @@ void RegionManager::setRegionFromSelection(const cmd::ArgumentList& args) {
             AABB regionBounds = GlobalSelectionSystem().getWorkZone().bounds;
 
             // Set the region
-            GlobalRegion().setRegion(regionBounds);
+            setRegion(regionBounds);
 
             // De-select all the selected items
             GlobalSelectionSystem().setSelectedAll(false);
@@ -341,13 +311,13 @@ void RegionManager::setRegionFromSelection(const cmd::ArgumentList& args) {
         }
         else {
             wxutil::Messagebox::ShowError(_("This command is not available in component mode."));
-            GlobalRegion().disable();
+            disable();
         }
     }
     else {
         wxutil::Messagebox::ShowError(
             _("Could not set Region: nothing selected."));
-        GlobalRegion().disable();
+        disable();
     }
 }
 
@@ -370,16 +340,16 @@ void RegionManager::saveRegion(const cmd::ArgumentList& args)
         // Filename is ok, start preparation
 
         // Save the old region
-        AABB oldRegionAABB = GlobalRegion().getRegion();
+        AABB oldRegionAABB = getRegion();
 
         // Now check for the effective bounds so that all visible items are included
         AABB visibleBounds = getVisibleBounds();
 
         // Set the region bounds, but don't traverse the graph!
-        GlobalRegion().setRegion(visibleBounds, false);
+        setRegion(visibleBounds, false);
 
         // Add the region brushes
-        GlobalRegion().addRegionBrushes();
+        addRegionBrushes();
 
 		if (!fileInfo.mapFormat)
 		{
@@ -394,10 +364,10 @@ void RegionManager::saveRegion(const cmd::ArgumentList& args)
                              fileInfo.fullPath);
 
         // Remove the region brushes
-        GlobalRegion().removeRegionBrushes();
+        removeRegionBrushes();
 
         // Set the region AABB back to the state before saving
-        GlobalRegion().setRegion(oldRegionAABB, false);
+        setRegion(oldRegionAABB, false);
 
         // Add the filename to the recently used map list
         GlobalMRU().insert(fileInfo.fullPath);
@@ -406,11 +376,11 @@ void RegionManager::saveRegion(const cmd::ArgumentList& args)
 
 void RegionManager::initialiseCommands()
 {
-    GlobalCommandSystem().addCommand("SaveRegion", saveRegion);
-    GlobalCommandSystem().addCommand("RegionOff", disableRegion);
-    GlobalCommandSystem().addCommand("RegionSetXY", setRegionXY);
-    GlobalCommandSystem().addCommand("RegionSetBrush", setRegionFromBrush);
-    GlobalCommandSystem().addCommand("RegionSetSelection", setRegionFromSelection);
+    GlobalCommandSystem().addCommand("SaveRegion", std::bind(&RegionManager::saveRegion, this, std::placeholders::_1));
+    GlobalCommandSystem().addCommand("RegionOff", std::bind(&RegionManager::disableRegion, this, std::placeholders::_1));
+    GlobalCommandSystem().addCommand("RegionSetXY", std::bind(&RegionManager::setRegionXY, this, std::placeholders::_1));
+    GlobalCommandSystem().addCommand("RegionSetBrush", std::bind(&RegionManager::setRegionFromBrush, this, std::placeholders::_1));
+    GlobalCommandSystem().addCommand("RegionSetSelection", std::bind(&RegionManager::setRegionFromSelection, this, std::placeholders::_1));
 
     GlobalEventManager().addCommand("SaveRegion", "SaveRegion");
     GlobalEventManager().addCommand("RegionOff", "RegionOff");
@@ -419,14 +389,79 @@ void RegionManager::initialiseCommands()
     GlobalEventManager().addCommand("RegionSetSelection", "RegionSetSelection");
 }
 
-} // namespace map
+const std::string& RegionManager::getName() const
+{
+	static std::string _name("RegionManager");
+	return _name;
+}
 
-map::RegionManager& GlobalRegion() {
-    static map::RegionManagerPtr _regionManager;
+const StringSet& RegionManager::getDependencies() const
+{
+	static StringSet _dependencies;
 
-    if (_regionManager == NULL) {
-        _regionManager = map::RegionManagerPtr(new map::RegionManager);
-    }
+	if (_dependencies.empty())
+	{
+		_dependencies.insert(MODULE_MAP);
+	}
+
+	return _dependencies;
+}
+
+void RegionManager::initialiseModule(const ApplicationContext& ctx)
+{
+	rMessage() << getName() << "::initialiseModule called." << std::endl;
+
+	initialiseCommands();
+
+	_worldMin = game::current::getValue<float>("/defaults/minWorldCoord");
+	_worldMax = game::current::getValue<float>("/defaults/maxWorldCoord");
+
+	for (int i = 0; i < 6; i++)
+	{
+		_brushes[i].reset();
+	}
+
+	GlobalMap().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &RegionManager::onMapEvent));
+}
+
+void RegionManager::onMapEvent(IMap::MapEvent ev)
+{
+	if (ev == IMap::MapUnloading)
+	{
+		// Turn regioning off when unloading the map
+		disable();
+		clear();
+	}
+	else if (ev == IMap::MapLoaded)
+	{
+		// Disable when a new map has been loaded 
+		disable();
+	}
+}
+
+AABB RegionManager::getVisibleBounds()
+{
+	AABB returnValue;
+
+	GlobalSceneGraph().root()->foreachNode([&](const scene::INodePtr& node)
+	{
+		if (node->visible())
+		{
+			returnValue.includeAABB(node->worldAABB());
+		}
 
-    return *_regionManager;
+		return true;
+	});
+
+	return returnValue;
+}
+
+module::StaticModule<RegionManager> staticRegionManagerModule;
+
+} // namespace map
+
+map::RegionManager& GlobalRegion()
+{
+	return *map::staticRegionManagerModule.getModule();
 }
diff --git a/radiant/map/RegionManager.h b/radiant/map/RegionManager.h
index e4c5f68..0743865 100644
--- a/radiant/map/RegionManager.h
+++ b/radiant/map/RegionManager.h
@@ -1,28 +1,32 @@
 #pragma once
 
 #include <list>
+#include "imodule.h"
 #include "icommandsystem.h"
 #include "math/AABB.h"
 #include "math/Vector2.h"
 #include "iscenegraph.h"
 #include "ientity.h"
+#include "imap.h"
 
-/** greebo: The RegionManager provides methods to enable/disable
- * 			the regioning when map editing as well as functions
- * 			to set the region bounds from brushes/xyview/current selection.
+/** 
+ * greebo: The RegionManager provides methods to enable/disable
+ * the regioning for map editing as well as functions
+ * to set the region bounds from brushes/xyview/current selection.
  *
  * Regioned nodes are hidden during map editing (they get their excluded bit set).
  * It's still possible to apply additional filtering to a region via show/hide,
  * these systems are independent of each other.
  *
  * @SaveRegion: This saves the current region (all non-excluded nodes) to a
- * 				specified file and places six wall/floor/ceiling brushes to
- * 				the file together with an info_player_start entity.
- * 				The info_player_start is placed at the current camera position.
+ * specified file and places six wall/floor/ceiling brushes to
+ * the file together with an info_player_start entity.
+ * The info_player_start is placed at the current camera position.
  */
 namespace map {
 
-class RegionManager
+class RegionManager :
+	public RegisterableModule
 {
 	// TRUE, if regioning is active
 	bool _active;
@@ -86,60 +90,67 @@ public:
 	 */
 	void setRegionFromXY(Vector2 topLeft, Vector2 lowerRight);
 
+	// RegisterableModule
+	virtual const std::string& getName() const override;
+	virtual const StringSet& getDependencies() const override;
+	virtual void initialiseModule(const ApplicationContext& ctx) override;
+
+private:
 	/** greebo: Adds the bounding brushes that enclose the current region.
-	 */
+	*/
 	void addRegionBrushes();
 
 	/** greebo: Removes the bounding brushes added by addRegionBrushes().
-	 */
+	*/
 	void removeRegionBrushes();
 
 	/** greebo: The traversal function that is used to save the map to a file.
-	 * 			This ensures that only regioned items are saved.
-	 *
-	 * Note: the map saver passes its own walker to this function and leaves it up to it
-	 * 		 whether the walker.pre() and walker.post() methods are invoked. This allows
-	 * 		 filtering of the non-regioned nodes.
-	 */
+	* 			This ensures that only regioned items are saved.
+	*
+	* Note: the map saver passes its own walker to this function and leaves it up to it
+	* 		 whether the walker.pre() and walker.post() methods are invoked. This allows
+	* 		 filtering of the non-regioned nodes.
+	*/
 	static void traverseRegion(const scene::INodePtr& root, scene::NodeVisitor& walker);
 
-	// Static command targets for use in EventManager
-
 	/** greebo: Saves the current selection as Region to the queried file.
-	 */
-	static void saveRegion(const cmd::ArgumentList& args);
+	*/
+	void saveRegion(const cmd::ArgumentList& args);
 
 	/** greebo: Disables regioning and resets the bounds.
-	 */
-	static void disableRegion(const cmd::ArgumentList& args);
+	*/
+	void disableRegion(const cmd::ArgumentList& args);
 
 	/** greebo: Sets the region according to the XY bounds of the current orthoview
-	 */
-	static void setRegionXY(const cmd::ArgumentList& args);
+	*/
+	void setRegionXY(const cmd::ArgumentList& args);
 
 	/** greebo: Sets the region to the bounds of the currently drawn brush,
-	 * 			similar to the partial tall selection method.
-	 * 			A single brush has to be selected (an errormsg is displayed otherwise).
-	 *
-	 * Note: The brush is deleted after "use".
-	 */
-	static void setRegionFromBrush(const cmd::ArgumentList& args);
+	* 			similar to the partial tall selection method.
+	* 			A single brush has to be selected (an errormsg is displayed otherwise).
+	*
+	* Note: The brush is deleted after "use".
+	*/
+	void setRegionFromBrush(const cmd::ArgumentList& args);
 
 	/** greebo: Retrieves the AABB from the current selection and
-	 * 			takes it as new region bounds. The selection is NOT deleted.
-	 * 			Not available in component selection mode.
-	 * 			The selected items are de-selected after "use".
-	 */
-	static void setRegionFromSelection(const cmd::ArgumentList& args);
+	* 			takes it as new region bounds. The selection is NOT deleted.
+	* 			Not available in component selection mode.
+	* 			The selected items are de-selected after "use".
+	*/
+	void setRegionFromSelection(const cmd::ArgumentList& args);
 
 	/** greebo: Adds the region commands to the EventManager.
-	 */
-	static void initialiseCommands();
+	*/
+	void initialiseCommands();
+
+	void onMapEvent(IMap::MapEvent ev);
 
-private:
 	// Helper to create the actual brushes bounding the region
 	static void constructRegionBrushes(scene::INodePtr brushes[6], 
 		const Vector3& region_mins, const Vector3& region_maxs);
+
+	AABB getVisibleBounds();
 };
 
 } // namespace map
diff --git a/radiant/map/RenderableAasFile.cpp b/radiant/map/RenderableAasFile.cpp
new file mode 100644
index 0000000..ff61ce7
--- /dev/null
+++ b/radiant/map/RenderableAasFile.cpp
@@ -0,0 +1,121 @@
+#include "RenderableAasFile.h"
+
+#include "iregistry.h"
+#include "imainframe.h"
+
+#include "registry/registry.h"
+
+namespace map
+{
+
+RenderableAasFile::RenderableAasFile() :
+	_renderNumbers(registry::getValue<bool>(RKEY_SHOW_AAS_AREA_NUMBERS)),
+	_hideDistantAreas(registry::getValue<bool>(RKEY_HIDE_DISTANT_AAS_AREAS)),
+	_hideDistanceSquared(registry::getValue<float>(RKEY_AAS_AREA_HIDE_DISTANCE))
+{
+	_hideDistanceSquared *= _hideDistanceSquared;
+
+	GlobalRegistry().signalForKey(RKEY_SHOW_AAS_AREA_NUMBERS).connect([this]()
+	{
+		_renderNumbers = registry::getValue<bool>(RKEY_SHOW_AAS_AREA_NUMBERS);
+		GlobalMainFrame().updateAllWindows();
+	});
+
+	GlobalRegistry().signalForKey(RKEY_HIDE_DISTANT_AAS_AREAS).connect([this]()
+	{
+		_hideDistantAreas = registry::getValue<bool>(RKEY_HIDE_DISTANT_AAS_AREAS);
+		_hideDistanceSquared = registry::getValue<float>(RKEY_AAS_AREA_HIDE_DISTANCE);
+		_hideDistanceSquared *= _hideDistanceSquared;
+		GlobalMainFrame().updateAllWindows();
+	});
+}
+
+void RenderableAasFile::setRenderSystem(const RenderSystemPtr& renderSystem)
+{
+	_renderSystem = renderSystem;
+}
+
+void RenderableAasFile::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
+{
+	if (!_aasFile) return;
+
+	collector.SetState(_normalShader, RenderableCollector::eFullMaterials);
+
+	// Get the camera position for distance clipping
+	Matrix4 invModelView = volume.GetModelview().getFullInverse();
+	Vector3 viewPos = invModelView.t().getProjected();
+
+	for (const RenderableSolidAABB& aabb : _renderableAabbs)
+	{
+		if (_hideDistantAreas && (aabb.getAABB().getOrigin() - viewPos).getLengthSquared() > _hideDistanceSquared)
+		{
+			continue;
+		}
+
+		collector.addRenderable(aabb, Matrix4::getIdentity());
+	}
+
+	if (_renderNumbers)
+	{
+		collector.addRenderable(*this, Matrix4::getIdentity());
+	}
+}
+
+void RenderableAasFile::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
+{
+	// Do nothing in wireframe mode
+	//renderSolid(collector, volume);
+}
+
+std::size_t RenderableAasFile::getHighlightFlags()
+{
+	return Highlight::NoHighlight;
+}
+
+void RenderableAasFile::setAasFile(const IAasFilePtr& aasFile)
+{
+	_aasFile = aasFile;
+
+	prepare();
+}
+
+void RenderableAasFile::render(const RenderInfo& info) const
+{
+	// draw label
+	// Render the area numbers
+	for (std::size_t areaNum = 0; areaNum < _aasFile->getNumAreas(); ++areaNum)
+	{
+		const IAasFile::Area& area = _aasFile->getArea(areaNum);
+
+		if (_hideDistantAreas && (area.center - info.getViewerLocation()).getLengthSquared() > _hideDistanceSquared)
+		{
+			continue;
+		}
+
+		glRasterPos3dv(area.center);
+		GlobalOpenGL().drawString(string::to_string(areaNum));
+	}
+}
+
+void RenderableAasFile::prepare()
+{
+	if (!_aasFile) return;
+
+	_normalShader = GlobalRenderSystem().capture("$AAS_AREA");
+
+	constructRenderables();
+}
+
+void RenderableAasFile::constructRenderables()
+{
+	_renderableAabbs.clear();
+
+	for (std::size_t areaNum = 0; areaNum < _aasFile->getNumAreas(); ++areaNum)
+	{
+		const IAasFile::Area& area = _aasFile->getArea(areaNum);
+
+		_renderableAabbs.push_back(RenderableSolidAABB(area.bounds));
+	}
+}
+
+} // namespace
diff --git a/radiant/map/RenderableAasFile.h b/radiant/map/RenderableAasFile.h
new file mode 100644
index 0000000..5f86c2c
--- /dev/null
+++ b/radiant/map/RenderableAasFile.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <list>
+#include <sigc++/trackable.h>
+
+#include "irenderable.h"
+#include "irender.h"
+#include "iaasfile.h"
+
+#include "entitylib.h"
+
+namespace map
+{
+
+const char* const RKEY_SHOW_AAS_AREA_NUMBERS = "user/ui/aasViewer/showNumbers";
+const char* const RKEY_HIDE_DISTANT_AAS_AREAS = "user/ui/aasViewer/hideDistantAreas";
+const char* const RKEY_AAS_AREA_HIDE_DISTANCE = "user/ui/aasViewer/hideDistance";
+
+// Renderable drawing all the area bounds of the attached AAS file,
+// optionally showing the area numbers too
+class RenderableAasFile :
+    public Renderable,
+	public OpenGLRenderable,
+	public sigc::trackable
+{
+private:
+    RenderSystemPtr _renderSystem;
+
+    IAasFilePtr _aasFile;
+
+	ShaderPtr _normalShader;
+
+    std::list<RenderableSolidAABB> _renderableAabbs;
+
+	bool _renderNumbers;
+	bool _hideDistantAreas;
+	float _hideDistanceSquared;
+
+public:
+	RenderableAasFile();
+
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	std::size_t getHighlightFlags() override;
+
+	void setAasFile(const IAasFilePtr& aasFile);
+
+	void render(const RenderInfo& info) const override;
+
+private:
+	void prepare();
+	void constructRenderables();
+};
+
+} // namespace
diff --git a/radiant/map/RootNode.h b/radiant/map/RootNode.h
index 8d2fd01..0d5828f 100644
--- a/radiant/map/RootNode.h
+++ b/radiant/map/RootNode.h
@@ -47,15 +47,15 @@ public:
     ITargetManager& getTargetManager() override;
 
 	// Renderable implementation (empty)
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override
 	{}
 
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override
 	{}
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // never highlighted
+		return Highlight::NoHighlight; // never highlighted
 	}
 
 	std::string name() const;
diff --git a/radiant/map/algorithm/AssignLayerMappingWalker.h b/radiant/map/algorithm/AssignLayerMappingWalker.h
deleted file mode 100644
index 77a7f23..0000000
--- a/radiant/map/algorithm/AssignLayerMappingWalker.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include "imodel.h"
-#include "iparticlenode.h"
-
-#include "../InfoFile.h"
-
-namespace map
-{
-
-class AssignLayerMappingWalker :
-    public scene::NodeVisitor
-{
-private:
-    InfoFile& _infoFile;
-
-public:
-    AssignLayerMappingWalker(InfoFile& infoFile) :
-        _infoFile(infoFile)
-    {}
-
-    virtual ~AssignLayerMappingWalker() {}
-
-    bool pre(const scene::INodePtr& node)
-    {
-        // To prevent all the support node types from getting layers assigned
-        // filter them out, only Entities and Primitives get mapped in the info file
-        if (Node_isEntity(node) || Node_isPrimitive(node))
-        {
-            // Retrieve the next set of layer mappings and assign them
-            node->assignToLayers(_infoFile.getNextLayerMapping());
-            return true;
-        }
-
-        // All other node types inherit the layers from their parent node
-        // Model / particle / target line
-        scene::INodePtr parent = node->getParent();
-
-        if (parent)
-        {
-            node->assignToLayers(parent->getLayers());
-        }
-
-        return true;
-    }
-};
-
-} // namespace
diff --git a/radiant/map/algorithm/ChildPrimitives.cpp b/radiant/map/algorithm/ChildPrimitives.cpp
index 6ec9e98..b6e4fcb 100644
--- a/radiant/map/algorithm/ChildPrimitives.cpp
+++ b/radiant/map/algorithm/ChildPrimitives.cpp
@@ -26,7 +26,7 @@ public:
 			scene::GroupNodePtr groupNode = Node_getGroupNode(node);
 
 			// Don't handle the worldspawn children, they're safe&sound
-			if (groupNode != NULL && entity->getKeyValue("classname") != "worldspawn")
+			if (groupNode != NULL && !entity->isWorldspawn())
 			{
 				groupNode->addOriginToChildren();
 				// Don't traverse the children
@@ -53,7 +53,7 @@ public:
 			scene::GroupNodePtr groupNode = Node_getGroupNode(node);
 
 			// Don't handle the worldspawn children, they're safe&sound
-			if (groupNode != NULL && entity->getKeyValue("classname") != "worldspawn")
+			if (groupNode != NULL && !entity->isWorldspawn())
 			{
 				groupNode->removeOriginFromChildren();
 				// Don't traverse the children
diff --git a/radiant/map/algorithm/Clone.h b/radiant/map/algorithm/Clone.h
index db2d3cb..dbb5bd6 100644
--- a/radiant/map/algorithm/Clone.h
+++ b/radiant/map/algorithm/Clone.h
@@ -71,6 +71,11 @@ inline scene::INodePtr Node_Clone(const scene::INodePtr& node)
 	CloneAll visitor(clone);
 	node->traverseChildren(visitor);
 
+	// Cloned child nodes are assigned the layers of the source nodes
+	// update the layer visibility flags to make the layers assignemnt take effect
+	scene::UpdateNodeVisibilityWalker visibilityUpdater;
+	clone->traverse(visibilityUpdater);
+
 	return clone;
 }
 
diff --git a/radiant/map/algorithm/InfoFileExporter.cpp b/radiant/map/algorithm/InfoFileExporter.cpp
deleted file mode 100644
index 826dec5..0000000
--- a/radiant/map/algorithm/InfoFileExporter.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "InfoFileExporter.h"
-
-#include "imodel.h"
-#include "iselectionset.h"
-#include "iparticlenode.h"
-#include "itextstream.h"
-#include "../InfoFile.h"
-#include "debugging/ScenegraphUtils.h"
-
-#include <boost/algorithm/string/replace.hpp>
-
-namespace map
-{
-
-InfoFileExporter::InfoFileExporter(std::ostream& stream) :
-    _stream(stream),
-    _layerInfoCount(0)
-{
-    // Write the information file header
-    _stream << InfoFile::HEADER_SEQUENCE << " " << InfoFile::MAP_INFO_VERSION << std::endl;
-    _stream << "{" << std::endl;
-
-    // Export the names of the layers
-    writeLayerNames();
-	assembleSelectionSetInfo();
-
-    // Write the NodeToLayerMapping header
-    _stream << "\t" << InfoFile::NODE_TO_LAYER_MAPPING << std::endl;
-    _stream << "\t{" << std::endl;
-}
-
-InfoFileExporter::~InfoFileExporter()
-{
-    // Closing braces of NodeToLayerMapping block
-    _stream << "\t}" << std::endl;
-	
-	rMessage() << _layerInfoCount << " node-to-layer mappings written." << std::endl;
-
-	writeSelectionSetInfo();
-	
-	// Write the closing braces of the information file
-    _stream << "}" << std::endl;
-}
-
-void InfoFileExporter::handleNode(const scene::INodePtr& node)
-{
-	// Don't export the layer settings for models and particles, as they are not there
-    // at map load/parse time - these shouldn't even be passed in here
-    assert(node && !Node_isModel(node) && !particles::isParticleNode(node));
-
-    // Open a Node block
-    _stream << "\t\t" << InfoFile::NODE << " { ";
-
-    scene::LayerList layers = node->getLayers();
-
-    // Write a space-separated list of node IDs
-    for (scene::LayerList::const_iterator i = layers.begin(); i != layers.end(); ++i)
-    {
-        _stream << *i << " ";
-    }
-    
-    // Close the Node block
-    _stream << "}";
-
-    // Write additional node info, for easier debugging of layer issues
-    _stream << " // " << getNodeInfo(node);
-
-    _stream << std::endl;
-
-    _layerInfoCount++;
-}
-
-void InfoFileExporter::visitEntity(const scene::INodePtr& node, std::size_t entityNum)
-{
-	handleNode(node);
-
-	// Determine the item index for the selection set index mapping
-	std::for_each(_selectionSetInfo.begin(), _selectionSetInfo.end(), [&] (SelectionSetExportInfo& info)
-	{
-		if (info.nodes.find(node) != info.nodes.end())
-		{
-			info.nodeIndices.insert(InfoFileExporter::SelectionSetExportInfo::IndexPair(entityNum, InfoFile::EMPTY_PRIMITVE_NUM));
-		}
-	});
-}
-
-void InfoFileExporter::visitPrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum)
-{
-	handleNode(node);
-
-	// Determine the item index for the selection set index mapping
-	std::for_each(_selectionSetInfo.begin(), _selectionSetInfo.end(), [&] (SelectionSetExportInfo& info)
-	{
-		if (info.nodes.find(node) != info.nodes.end())
-		{
-			info.nodeIndices.insert(InfoFileExporter::SelectionSetExportInfo::IndexPair(entityNum, primitiveNum));
-		}
-	});
-}
-
-void InfoFileExporter::writeLayerNames()
-{
-    // Open a "Layers" block
-    _stream << "\t" << InfoFile::LAYERS << std::endl;
-    _stream << "\t{" << std::endl;
-
-    // Visit all layers and write them to the stream
-    GlobalLayerSystem().foreachLayer([&](int layerId, const std::string& layerName)
-    {
-        _stream << "\t\t" << InfoFile::LAYER << " " << layerId << " { " << layerName << " }" << std::endl;
-    });
-
-    _stream << "\t}" << std::endl;
-}
-
-void InfoFileExporter::writeSelectionSetInfo()
-{
-	// Selection Set output
-	_stream << "\t" << InfoFile::SELECTION_SETS << std::endl;
-		
-	_stream << "\t{" << std::endl;
-	
-	std::size_t selectionSetCount = 0;
-
-	std::for_each(_selectionSetInfo.begin(), _selectionSetInfo.end(), [&] (SelectionSetExportInfo& info)
-	{
-		std::string indices = "";
-
-		std::for_each(info.nodeIndices.begin(), info.nodeIndices.end(), 
-			[&] (const InfoFileExporter::SelectionSetExportInfo::IndexPair& pair)
-		{
-			if (pair.second == InfoFile::EMPTY_PRIMITVE_NUM)
-			{
-				// only entity number
-				indices += "( " + string::to_string(pair.first) + " ) ";
-			}
-			else
-			{
-				// entity & primitive number
-				indices += "( " + string::to_string(pair.first) + " " + string::to_string(pair.second) +  " ) ";
-			}
-		});
-
-		// Make sure to escape the quotes of the set name, use the XML quote entity
-		_stream << "\t\t" << InfoFile::SELECTION_SET << " " << selectionSetCount++ 
-			<< " { \"" << boost::algorithm::replace_all_copy(info.set->getName(), "\"", """) << "\" } " 
-			<< " { " << indices << " } "
-			<< std::endl;
-	});
-
-	_stream << "\t}" << std::endl;
-
-	rMessage() << _selectionSetInfo.size() << " selection sets exported." << std::endl;
-}
-
-void InfoFileExporter::assembleSelectionSetInfo()
-{
-	// Visit all selection sets and assemble the info into the structures
-	GlobalSelectionSetManager().foreachSelectionSet([&] (const selection::ISelectionSetPtr& set)
-	{
-		// Get all nodes of this selection set and store them for later use
-		_selectionSetInfo.push_back(InfoFileExporter::SelectionSetExportInfo());
-
-		_selectionSetInfo.back().set = set;
-		_selectionSetInfo.back().nodes = set->getNodes();
-	});
-}
-
-} // namespace
diff --git a/radiant/map/algorithm/InfoFileExporter.h b/radiant/map/algorithm/InfoFileExporter.h
deleted file mode 100644
index 5c9de33..0000000
--- a/radiant/map/algorithm/InfoFileExporter.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-
-#include <ostream>
-#include <sstream>
-#include "inode.h"
-#include "iselectionset.h"
-#include <map>
-
-namespace map
-{
-
-class InfoFileExporter
-{
-private:
-	// The stream we're writing to
-	std::ostream& _stream;
-
-	// Number of node-to-layer mappings written
-	std::size_t _layerInfoCount;
-
-	struct SelectionSetExportInfo
-	{
-		// The set we're working with
-		selection::ISelectionSetPtr set;
-
-		// The nodes in this set
-		std::set<scene::INodePtr> nodes;
-
-		// The entity and primitive number pair
-		typedef std::pair<std::size_t, std::size_t> IndexPair;
-
-		// The node indices, which will be resolved during traversal
-		std::set<IndexPair> nodeIndices;
-	};
-
-	// SelectionSet-related
-	typedef std::vector<SelectionSetExportInfo> SelectionSetInfo;
-	SelectionSetInfo _selectionSetInfo;
-
-public:
-	// The constructor prepares the output stream
-	InfoFileExporter(std::ostream& stream);
-
-	// Cleans up the scene on destruction
-	~InfoFileExporter();
-
-	// Is called by the owning MapExporter
-	// Requirements: node must not be NULL and not a model/particle node.
-	void visitEntity(const scene::INodePtr& node, std::size_t entityNum);
-	void visitPrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum);
-
-private:
-	// General handling of map nodes
-	void handleNode(const scene::INodePtr& node);
-
-	// Writes the names of the layers existing in this map
-	void writeLayerNames();
-
-	void writeSelectionSetInfo();
-
-	// Get SelectionSet node mapping
-	void assembleSelectionSetInfo();
-};
-typedef std::shared_ptr<InfoFileExporter> InfoFileExporterPtr;
-
-} // namespace
diff --git a/radiant/map/algorithm/MapExporter.h b/radiant/map/algorithm/MapExporter.h
index 1d347d5..1c49a92 100644
--- a/radiant/map/algorithm/MapExporter.h
+++ b/radiant/map/algorithm/MapExporter.h
@@ -5,7 +5,7 @@
 #include "igame.h"
 
 #include "wxutil/ModalProgressDialog.h"
-#include "InfoFileExporter.h"
+#include "../infofile/InfoFileExporter.h"
 #include "EventRateLimiter.h"
 
 namespace map
diff --git a/radiant/map/algorithm/MapImporter.cpp b/radiant/map/algorithm/MapImporter.cpp
index 808506b..30809c5 100644
--- a/radiant/map/algorithm/MapImporter.cpp
+++ b/radiant/map/algorithm/MapImporter.cpp
@@ -9,7 +9,6 @@
 #include "registry/registry.h"
 #include "string/string.h"
 #include "wxutil/dialog/MessageBox.h"
-#include "../InfoFile.h"
 
 namespace map
 {
@@ -17,6 +16,7 @@ namespace map
 namespace
 {
 	const char* const RKEY_MAP_LOAD_STATUS_INTERLEAVE = "user/ui/map/loadStatusInterleave";
+	std::size_t EMPTY_PRIMITVE_NUM = std::numeric_limits<std::size_t>::max();
 }
 
 MapImporter::MapImporter(const scene::INodePtr& root, std::istream& inputStream) :
@@ -48,8 +48,8 @@ MapImporter::MapImporter(const scene::INodePtr& root, std::istream& inputStream)
 bool MapImporter::addEntity(const scene::INodePtr& entityNode)
 {
 	// Keep track of this entity
-	_nodes.insert(NodeMap::value_type(
-		NodeIndexPair(_entityCount, InfoFile::EMPTY_PRIMITVE_NUM), entityNode));
+	_nodes.insert(NodeIndexMap::value_type(
+		NodeIndexPair(_entityCount, EMPTY_PRIMITVE_NUM), entityNode));
 
 	_entityCount++;
 
@@ -74,7 +74,7 @@ bool MapImporter::addEntity(const scene::INodePtr& entityNode)
 
 bool MapImporter::addPrimitiveToEntity(const scene::INodePtr& primitive, const scene::INodePtr& entity)
 {
-	_nodes.insert(NodeMap::value_type(
+	_nodes.insert(NodeIndexMap::value_type(
 		NodeIndexPair(_entityCount, _primitiveCount), primitive));
 
 	_primitiveCount++;
@@ -98,11 +98,9 @@ bool MapImporter::addPrimitiveToEntity(const scene::INodePtr& primitive, const s
 	}
 }
 
-scene::INodePtr MapImporter::getNodeByIndexPair(const NodeIndexPair& pair)
+const NodeIndexMap& MapImporter::getNodeMap() const
 {
-	NodeMap::const_iterator i = _nodes.find(pair);
-
-	return i != _nodes.end() ? i->second : scene::INodePtr();
+	return _nodes;
 }
 
 double MapImporter::getProgressFraction()
diff --git a/radiant/map/algorithm/MapImporter.h b/radiant/map/algorithm/MapImporter.h
index 49362e9..a447382 100644
--- a/radiant/map/algorithm/MapImporter.h
+++ b/radiant/map/algorithm/MapImporter.h
@@ -2,6 +2,7 @@
 
 #include "inode.h"
 #include "imapformat.h"
+#include "imapinfofile.h"
 #include <map>
 
 #include "wxutil/ModalProgressDialog.h"
@@ -22,9 +23,6 @@ namespace map
 class MapImporter :
 	public IMapImportFilter
 {
-public:
-	typedef std::pair<std::size_t, std::size_t> NodeIndexPair;
-
 private:
 	scene::INodePtr _root;
 
@@ -45,9 +43,8 @@ private:
 	std::istream& _inputStream;
 	std::size_t _fileSize;
 
-	// Keep track of all the entities and primitives for retrieval
-	typedef std::map<NodeIndexPair, scene::INodePtr> NodeMap;
-	NodeMap _nodes;
+	// Keep track of all the entities and primitives for later retrieval
+	NodeIndexMap _nodes;
 
 public:
 	MapImporter(const scene::INodePtr& root, std::istream& inputStream);
@@ -55,8 +52,7 @@ public:
 	bool addEntity(const scene::INodePtr& entityNode);
 	bool addPrimitiveToEntity(const scene::INodePtr& primitive, const scene::INodePtr& entity);
 
-	// Get the entity or primitive by their number as they appear in the map file
-	scene::INodePtr getNodeByIndexPair(const NodeIndexPair& pair);
+	const NodeIndexMap& getNodeMap() const;
 
 private:
 	double getProgressFraction();
diff --git a/radiant/map/algorithm/Merge.h b/radiant/map/algorithm/Merge.h
index b099f8a..776e94c 100644
--- a/radiant/map/algorithm/Merge.h
+++ b/radiant/map/algorithm/Merge.h
@@ -62,10 +62,10 @@ public:
 		scene::INodePtr node = originalNode;
 
 		// greebo: Check if the visited node is the worldspawn of the other map
-		if (node_is_worldspawn(node))
+		if (Node_isWorldspawn(node))
 		{
 			// Find the worldspawn of the target map
-			scene::INodePtr world_node = GlobalMap().findWorldspawn();
+			const scene::INodePtr& world_node = GlobalMap().getWorldspawn();
 
 			if (world_node == NULL)
 			{
diff --git a/radiant/map/algorithm/WorldspawnArgFinder.h b/radiant/map/algorithm/WorldspawnArgFinder.h
index be2995d..2e62a5b 100644
--- a/radiant/map/algorithm/WorldspawnArgFinder.h
+++ b/radiant/map/algorithm/WorldspawnArgFinder.h
@@ -30,7 +30,7 @@ public:
 
 		if (ent != NULL) {
 
-			if (ent->getKeyValue("classname") == "worldspawn") {
+			if (ent->isWorldspawn()) {
 				// Load the description spawnarg
 				_value = ent->getKeyValue(_key);
 			}
diff --git a/radiant/map/infofile/InfoFile.cpp b/radiant/map/infofile/InfoFile.cpp
new file mode 100644
index 0000000..6b9d300
--- /dev/null
+++ b/radiant/map/infofile/InfoFile.cpp
@@ -0,0 +1,143 @@
+#include "InfoFile.h"
+
+#include "itextstream.h"
+#include "imapinfofile.h"
+#include "string/convert.h"
+
+#include "i18n.h"
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace map
+{
+
+const char* const InfoFile::HEADER_SEQUENCE = "DarkRadiant Map Information File Version";
+
+// Pass the input stream to the constructor
+InfoFile::InfoFile(std::istream& infoStream, const scene::IMapRootNodePtr& root, const NodeIndexMap& nodeMap) :
+	_tok(infoStream),
+	_isValid(true),
+	_root(root),
+	_nodeMap(nodeMap)
+{}
+
+void InfoFile::parse()
+{
+	// Initialise the modules
+	GlobalMapInfoFileManager().foreachModule([](IMapInfoFileModule& module)
+	{
+		module.onInfoFileLoadStart();
+	});
+
+	// Parse the Header
+	parseInfoFileHeader();
+
+	// Parse the blocks
+	parseInfoFileBody();
+
+	// Apply the parsed info to the scene
+	GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module)
+	{
+		module.applyInfoToScene(_root, _nodeMap);
+	});
+
+	// De-initialise the modules
+	GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module)
+	{
+		module.onInfoFileLoadFinished();
+	});
+}
+
+void InfoFile::parseInfoFileHeader()
+{
+	try
+	{
+		std::vector<std::string> parts;
+		boost::algorithm::split(parts, HEADER_SEQUENCE, boost::algorithm::is_any_of(" "));
+
+		// Parse the string "DarkRadiant Map Information File Version"
+		for (std::size_t i = 0; i < parts.size(); i++)
+		{
+			_tok.assertNextToken(parts[i]);
+		}
+
+		float version = boost::lexical_cast<float>(_tok.nextToken());
+
+		if (version != MAP_INFO_VERSION)
+		{
+			_isValid = false;
+			throw parser::ParseException(_("Map Info File Version invalid"));
+		}
+	}
+	catch (parser::ParseException& e)
+	{
+		rError() << "[InfoFile] Unable to parse info file header: " << e.what() << std::endl;
+		_isValid = false;
+		return;
+	}
+	catch (boost::bad_lexical_cast& e)
+	{
+		rError() << "[InfoFile] Unable to parse info file version: " << e.what() << std::endl;
+		_isValid = false;
+		return;
+	}
+}
+
+void InfoFile::parseInfoFileBody()
+{
+	// The opening brace of the master block
+	_tok.assertNextToken("{");
+
+	while (_tok.hasMoreTokens())
+	{
+		std::string token = _tok.nextToken();
+
+		bool blockParsed = false;
+
+		// Send each block to the modules that are able to load it
+		GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module)
+		{
+			if (!blockParsed && module.canParseBlock(token))
+			{
+				module.parseBlock(token, _tok);
+				blockParsed = true;
+			}
+		});
+
+		if (blockParsed)
+		{
+			continue; // block was processed by a module
+		}
+
+		if (token == "}")
+		{
+			break;
+		}
+
+		// Unknown token, try to ignore that block
+		rWarning() << "Unknown keyword " << token << " encountered, will try to ignore this block." << std::endl;
+
+		// We can only ignore a block if there is a block beginning curly brace
+		_tok.assertNextToken("{");
+
+		// Ignore the block
+		int depth = 1;
+
+		while (_tok.hasMoreTokens() && depth > 0)
+		{
+			std::string token2 = _tok.nextToken();
+
+			if (token2 == "{") 
+			{
+				depth++;
+			}
+			else if (token2 == "}") 
+			{
+				depth--;
+			}
+		}
+	}
+}
+
+} // namespace map
diff --git a/radiant/map/infofile/InfoFile.h b/radiant/map/infofile/InfoFile.h
new file mode 100644
index 0000000..7b59277
--- /dev/null
+++ b/radiant/map/infofile/InfoFile.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "imap.h"
+#include "imapinfofile.h"
+#include "parser/DefTokeniser.h"
+
+namespace map
+{
+
+class InfoFile
+{
+public:
+	// Tokens / Constants
+	// The version of the map info file
+	static const int MAP_INFO_VERSION = 2;
+
+	// InfoFile tokens --------------------------------------------------
+	static const char* const HEADER_SEQUENCE;
+
+private:
+	// The actual DefTokeniser to split the infoStream into pieces
+	parser::BasicDefTokeniser<std::istream> _tok;
+
+	// TRUE if the map info file was found to be valid
+	bool _isValid;
+
+	// Import root node
+	const scene::IMapRootNodePtr& _root;
+
+	// Index to Node mapping
+	const NodeIndexMap& _nodeMap;
+
+public:
+	// Pass the input stream to the constructor, plus some info about the map we're dealing with
+	InfoFile(std::istream& infoStream, const scene::IMapRootNodePtr& root, const NodeIndexMap& nodeMap);
+
+	// Parse the entire file
+	void parse();
+
+private:
+	void parseInfoFileHeader();
+	void parseInfoFileBody();
+};
+
+} // namespace map
diff --git a/radiant/map/infofile/InfoFileExporter.cpp b/radiant/map/infofile/InfoFileExporter.cpp
new file mode 100644
index 0000000..007a3c2
--- /dev/null
+++ b/radiant/map/infofile/InfoFileExporter.cpp
@@ -0,0 +1,62 @@
+#include "InfoFileExporter.h"
+
+#include "imapinfofile.h"
+#include "itextstream.h"
+#include "InfoFile.h"
+
+namespace map
+{
+
+InfoFileExporter::InfoFileExporter(std::ostream& stream) :
+    _stream(stream)
+{
+	GlobalMapInfoFileManager().foreachModule([](IMapInfoFileModule& module)
+	{
+		module.onInfoFileSaveStart();
+	});
+
+    // Write the information file header
+    _stream << InfoFile::HEADER_SEQUENCE << " " << InfoFile::MAP_INFO_VERSION << std::endl;
+    _stream << "{" << std::endl;
+}
+
+InfoFileExporter::~InfoFileExporter()
+{
+	// Tell the info file modules to write their data now
+	GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module)
+	{
+		rMessage() << "Writing info file blocks for " << module.getName() << std::endl;
+
+		module.writeBlocks(_stream);
+	});
+
+	// Write the closing braces of the information file
+    _stream << "}" << std::endl;
+
+	_stream.flush();
+
+	GlobalMapInfoFileManager().foreachModule([](IMapInfoFileModule& module)
+	{
+		module.onInfoFileSaveFinished();
+	});
+}
+
+void InfoFileExporter::visitEntity(const scene::INodePtr& node, std::size_t entityNum)
+{
+	GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module)
+	{
+		module.onSaveEntity(node, entityNum);
+	});
+}
+
+void InfoFileExporter::visitPrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum)
+{
+	GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module)
+	{
+		module.onSavePrimitive(node, entityNum, primitiveNum);
+	});
+}
+
+
+
+} // namespace
diff --git a/radiant/map/infofile/InfoFileExporter.h b/radiant/map/infofile/InfoFileExporter.h
new file mode 100644
index 0000000..0d382dc
--- /dev/null
+++ b/radiant/map/infofile/InfoFileExporter.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <ostream>
+#include <sstream>
+#include "inode.h"
+#include <map>
+
+namespace map
+{
+
+class InfoFileExporter
+{
+private:
+	// The stream we're writing to
+	std::ostream& _stream;
+
+public:
+	// The constructor prepares the output stream
+	InfoFileExporter(std::ostream& stream);
+
+	// Cleans up the scene on destruction
+	~InfoFileExporter();
+
+	// Is called by the owning MapExporter
+	// Requirements: node must not be NULL and not a model/particle node.
+	void visitEntity(const scene::INodePtr& node, std::size_t entityNum);
+	void visitPrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum);
+};
+typedef std::shared_ptr<InfoFileExporter> InfoFileExporterPtr;
+
+} // namespace
diff --git a/radiant/map/infofile/InfoFileManager.cpp b/radiant/map/infofile/InfoFileManager.cpp
new file mode 100644
index 0000000..877aa95
--- /dev/null
+++ b/radiant/map/infofile/InfoFileManager.cpp
@@ -0,0 +1,67 @@
+#include "InfoFileManager.h"
+
+#include "itextstream.h"
+#include "modulesystem/StaticModule.h"
+
+namespace map
+{
+
+void InfoFileManager::registerInfoFileModule(const IMapInfoFileModulePtr& module)
+{
+	if (_modules.find(module) != _modules.end())
+	{
+		rWarning() << "Duplicate info file module registered: " << module->getName() << std::endl;
+		return;
+	}
+
+	_modules.insert(module);
+}
+
+void InfoFileManager::unregisterInfoFileModule(const IMapInfoFileModulePtr& module)
+{
+	if (_modules.find(module) == _modules.end())
+	{
+		rWarning() << "Trying to unregister non-existent info file module: " << module->getName() << std::endl;
+		return;
+
+	}
+
+	_modules.erase(module);
+}
+
+void InfoFileManager::foreachModule(const std::function<void(IMapInfoFileModule&)>& functor)
+{
+	for (const IMapInfoFileModulePtr& module : _modules)
+	{
+		functor(*module);
+	}
+}
+
+const std::string& InfoFileManager::getName() const
+{
+	static std::string _name(MODULE_MAPINFOFILEMANAGER);
+	return _name;
+}
+
+const StringSet& InfoFileManager::getDependencies() const
+{
+	static StringSet _dependencies;
+	return _dependencies;
+}
+
+void InfoFileManager::initialiseModule(const ApplicationContext& ctx)
+{
+	rMessage() << getName() << "::initialiseModule called." << std::endl;
+}
+
+void InfoFileManager::shutdownModule()
+{
+	rMessage() << getName() << "::shudownModule called." << std::endl;
+
+	_modules.clear();
+}
+
+// Define the static InfoFileManager module
+module::StaticModule<InfoFileManager> infoFileManagerModule;
+
+}
diff --git a/radiant/map/infofile/InfoFileManager.h b/radiant/map/infofile/InfoFileManager.h
new file mode 100644
index 0000000..8d8af86
--- /dev/null
+++ b/radiant/map/infofile/InfoFileManager.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <set>
+#include "imapinfofile.h"
+
+namespace map
+{
+
+class InfoFileManager :
+	public IMapInfoFileManager
+{
+private:
+	std::set<IMapInfoFileModulePtr> _modules;
+
+public:
+	void registerInfoFileModule(const IMapInfoFileModulePtr& module) override;
+	void unregisterInfoFileModule(const IMapInfoFileModulePtr& module) override;
+
+	void foreachModule(const std::function<void(IMapInfoFileModule&)>& functor) override;
+
+	// Module interface
+	const std::string& getName() const override;
+	const StringSet& getDependencies() const override;
+	void initialiseModule(const ApplicationContext& ctx) override;
+	void shutdownModule() override;
+};
+
+}
+
diff --git a/radiant/modulesystem/ModuleRegistry.cpp b/radiant/modulesystem/ModuleRegistry.cpp
index 97628f8..7efec10 100644
--- a/radiant/modulesystem/ModuleRegistry.cpp
+++ b/radiant/modulesystem/ModuleRegistry.cpp
@@ -158,11 +158,11 @@ void ModuleRegistry::initialiseModules()
 	// Make sure this isn't called again
 	_modulesInitialised = true;
 
-    // Fire the signal now
-    _sigAllModulesInitialised.emit();
-
     _progress = 1.0f;
     ui::Splash::Instance().setProgressAndText(_("Modules initialised"), _progress);
+
+	// Fire the signal now, this will destroy the Splash dialog as well
+	_sigAllModulesInitialised.emit();
 }
 
 void ModuleRegistry::shutdownModules()
diff --git a/radiant/patch/Patch.cpp b/radiant/patch/Patch.cpp
index e6093ed..32eac56 100644
--- a/radiant/patch/Patch.cpp
+++ b/radiant/patch/Patch.cpp
@@ -45,32 +45,29 @@ inline bool double_valid(double f) {
 
 // ====== Patch Implementation =========================================================================
 
-// Initialise the cycle cap index member variable
-int Patch::m_CycleCapIndex = 0;
-
 	namespace {
 		const std::size_t MAX_PATCH_SUBDIVISIONS = 32;
 	}
 
 // Constructor
-Patch::Patch(PatchNode& node, const Callback& evaluateTransform) :
+Patch::Patch(PatchNode& node) :
 	_node(node),
 	_shader(texdef_name_default()),
 	_undoStateSaver(NULL),
 	_solidRenderable(_mesh),
 	_wireframeRenderable(_mesh),
 	_fixedWireframeRenderable(_mesh),
-	_renderableCtrlPoints(GL_POINTS, m_ctrl_vertices),
-	_renderableLattice(GL_LINES, m_lattice_indices, m_ctrl_vertices),
-	m_transformChanged(false),
-	_tesselationChanged(true),
-	m_evaluateTransform(evaluateTransform)
+	_renderableNTBVectors(_mesh),
+	_renderableCtrlPoints(GL_POINTS, _ctrl_vertices),
+	_renderableLattice(GL_LINES, _latticeIndices, _ctrl_vertices),
+	_transformChanged(false),
+	_tesselationChanged(true)
 {
 	construct();
 }
 
 // Copy constructor	(create this patch from another patch)
-Patch::Patch(const Patch& other, PatchNode& node, const Callback& evaluateTransform) :
+Patch::Patch(const Patch& other, PatchNode& node) :
 	IPatch(other),
 	Bounded(other),
 	Snappable(other),
@@ -81,33 +78,30 @@ Patch::Patch(const Patch& other, PatchNode& node, const Callback& evaluateTransf
 	_solidRenderable(_mesh),
 	_wireframeRenderable(_mesh),
 	_fixedWireframeRenderable(_mesh),
-	_renderableCtrlPoints(GL_POINTS, m_ctrl_vertices),
-	_renderableLattice(GL_LINES, m_lattice_indices, m_ctrl_vertices),
-	m_transformChanged(false),
-	_tesselationChanged(true),
-	m_evaluateTransform(evaluateTransform)
+	_renderableNTBVectors(_mesh),
+	_renderableCtrlPoints(GL_POINTS, _ctrl_vertices),
+	_renderableLattice(GL_LINES, _latticeIndices, _ctrl_vertices),
+	_transformChanged(false),
+	_tesselationChanged(true)
 {
 	// Initalise the default values
 	construct();
 
 	// Copy over the definitions from the <other> patch
-	m_patchDef3 = other.m_patchDef3;
-	m_subdivisions_x = other.m_subdivisions_x;
-	m_subdivisions_y = other.m_subdivisions_y;
-	setDims(other.m_width, other.m_height);
-	copy_ctrl(m_ctrl.begin(), other.m_ctrl.begin(), other.m_ctrl.begin()+(m_width*m_height));
+	_patchDef3 = other._patchDef3;
+	_subDivisions = other._subDivisions;
+	setDims(other._width, other._height);
+	copy_ctrl(_ctrl.begin(), other._ctrl.begin(), other._ctrl.begin()+(_width*_height));
 	_shader.setMaterialName(other._shader.getMaterialName());
 	controlPointsChanged();
 }
 
-// greebo: Initialises the patch member variables
-void Patch::construct() {
-	m_bOverlay = false;
-	m_width = m_height = 0;
+void Patch::construct()
+{
+	_width = _height = 0;
 
-	m_patchDef3 = false;
-	m_subdivisions_x = 0;
-	m_subdivisions_y = 0;
+	_patchDef3 = false;
+	_subDivisions = Subdivisions(0, 0);
 
 	// Check, if the shader name is correct
 	check_shader();
@@ -115,29 +109,29 @@ void Patch::construct() {
 
 // Get the current control point array
 PatchControlArray& Patch::getControlPoints() {
-	return m_ctrl;
+	return _ctrl;
 }
 
 // Same as above, just for const arguments
 const PatchControlArray& Patch::getControlPoints() const {
-	return m_ctrl;
+	return _ctrl;
 }
 
 // Get the (temporary) transformed control point array, not the saved ones
 PatchControlArray& Patch::getControlPointsTransformed() {
-	return m_ctrlTransformed;
+	return _ctrlTransformed;
 }
 
 const PatchControlArray& Patch::getControlPointsTransformed() const {
-	return m_ctrlTransformed;
+	return _ctrlTransformed;
 }
 
 std::size_t Patch::getWidth() const {
-	return m_width;
+	return _width;
 }
 
 std::size_t Patch::getHeight() const {
-	return m_height;
+	return _height;
 }
 
 void Patch::setDims(std::size_t w, std::size_t h)
@@ -151,19 +145,20 @@ void Patch::setDims(std::size_t w, std::size_t h)
     w = MIN_PATCH_WIDTH;
 
   if((h%2)==0)
-    m_height -= 1;
+    _height -= 1;
   ASSERT_MESSAGE(h <= MAX_PATCH_HEIGHT, "patch too tall");
   if(h > MAX_PATCH_HEIGHT)
     h = MAX_PATCH_HEIGHT;
   else if(h < MIN_PATCH_HEIGHT)
     h = MIN_PATCH_HEIGHT;
 
-  m_width = w; m_height = h;
+  _width = w; 
+  _height = h;
 
-  if(m_width * m_height != m_ctrl.size())
+  if(_width * _height != _ctrl.size())
   {
-    m_ctrl.resize(m_width * m_height);
-    onAllocate(m_ctrl.size());
+    _ctrl.resize(_width * _height);
+    onAllocate(_ctrl.size());
   }
 }
 
@@ -196,8 +191,9 @@ void Patch::onAllocate(std::size_t size)
 }
 
 // Return the interally stored AABB
-const AABB& Patch::localAABB() const {
-	return m_aabb_local;
+const AABB& Patch::localAABB() const 
+{
+	return _localAABB;
 }
 
 // Render functions: solid mode
@@ -209,6 +205,10 @@ void Patch::render_solid(RenderableCollector& collector, const VolumeTest& volum
 
     collector.SetState(_shader.getGLShader(), RenderableCollector::eFullMaterials);
 	collector.addRenderable(_solidRenderable, localToWorld, entity);
+
+#if DEBUG_PATCH_NTB_VECTORS
+    _renderableVectors.render(collector, volume, localToWorld);
+#endif
 }
 
 // Render functions for WireFrame rendering
@@ -219,7 +219,7 @@ void Patch::render_wireframe(RenderableCollector& collector, const VolumeTest& v
 
 	collector.SetState(_shader.getGLShader(), RenderableCollector::eFullMaterials);
 
-	if (m_patchDef3) {
+	if (_patchDef3) {
 		collector.addRenderable(_fixedWireframeRenderable, localToWorld);
 	}
 	else {
@@ -254,6 +254,10 @@ void Patch::setRenderSystem(const RenderSystemPtr& renderSystem)
 	_renderSystem = renderSystem;
     _shader.setRenderSystem(renderSystem);
 
+#if DEBUG_PATCH_NTB_VECTORS
+    _renderableNTBVectors.setRenderSystem(renderSystem);
+#endif
+
     if (renderSystem)
     {
         _pointShader = renderSystem->capture("$POINT");
@@ -279,9 +283,9 @@ void Patch::testSelect(Selector& selector, SelectionTest& test)
 	SelectionIntersection best;
 	IndexPointer::index_type* pIndex = &_mesh.indices.front();
 
-	for (std::size_t s=0; s<_mesh.m_numStrips; s++) {
-		test.TestQuadStrip(vertexpointer_arbitrarymeshvertex(&_mesh.vertices.front()), IndexPointer(pIndex, _mesh.m_lenStrips), best);
-		pIndex += _mesh.m_lenStrips;
+	for (std::size_t s=0; s<_mesh.numStrips; s++) {
+		test.TestQuadStrip(vertexpointer_arbitrarymeshvertex(&_mesh.vertices.front()), IndexPointer(pIndex, _mesh.lenStrips), best);
+		pIndex += _mesh.lenStrips;
 	}
 
 	if (best.valid()) {
@@ -293,8 +297,8 @@ void Patch::testSelect(Selector& selector, SelectionTest& test)
 void Patch::transform(const Matrix4& matrix)
 {
 	// Cycle through all the patch control vertices and transform the points
-	for (PatchControlIter i = m_ctrlTransformed.begin();
-		 i != m_ctrlTransformed.end();
+	for (PatchControlIter i = _ctrlTransformed.begin();
+		 i != _ctrlTransformed.end();
 		 ++i)
 	{
 		i->vertex = matrix.transformPoint(i->vertex);
@@ -303,7 +307,7 @@ void Patch::transform(const Matrix4& matrix)
 	// Check the handedness of the matrix and invert it if needed
 	if(matrix.getHandedness() == Matrix4::LEFTHANDED)
 	{
-		PatchControlArray_invert(m_ctrlTransformed, m_width, m_height);
+		PatchControlArray_invert(_ctrlTransformed, _width, _height);
 	}
 
 	// Mark this patch as changed
@@ -313,7 +317,7 @@ void Patch::transform(const Matrix4& matrix)
 // Called if the patch has changed, so that the dirty flags are set
 void Patch::transformChanged()
 {
-	m_transformChanged = true;
+	_transformChanged = true;
 	_node.lightsChanged();
 	_tesselationChanged = true;
 }
@@ -322,27 +326,27 @@ void Patch::transformChanged()
 void Patch::evaluateTransform()
 {
 	// Only do something, if the patch really has changed
-	if (m_transformChanged)
+	if (_transformChanged)
 	{
-		m_transformChanged = false;
+		_transformChanged = false;
 		revertTransform();
-		m_evaluateTransform();
+		_node.evaluateTransform();
 	}
 }
 
-// Revert the changes, fall back to the saved state in <m_ctrl>
+// Revert the changes, fall back to the saved state in <_ctrl>
 void Patch::revertTransform()
 {
-	m_ctrlTransformed = m_ctrl;
+	_ctrlTransformed = _ctrl;
 }
 
-// Apply the transformed control array, save it into <m_ctrl> and overwrite the old values
+// Apply the transformed control array, save it into <_ctrl> and overwrite the old values
 void Patch::freezeTransform()
 {
 	undoSave();
 
-	// Save the transformed working set array over m_ctrl
-	m_ctrl = m_ctrlTransformed;
+	// Save the transformed working set array over _ctrl
+	_ctrl = _ctrlTransformed;
 
     // Don't call controlPointsChanged() here since that one will re-apply the 
     // current transformation matrix, possible the second time.
@@ -373,7 +377,7 @@ void Patch::snapto(float snap)
 {
 	undoSave();
 
-	for(PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i)
+	for(PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i)
 	{
 		i->vertex.snap(snap);
 	}
@@ -428,12 +432,12 @@ int Patch::getShaderFlags() const
 
 // Return a defined patch control vertex at <row>,<col>
 PatchControl& Patch::ctrlAt(std::size_t row, std::size_t col) {
-	return m_ctrl[row*m_width+col];
+	return _ctrl[row*_width+col];
 }
 
 // The same as above just for const
 const PatchControl& Patch::ctrlAt(std::size_t row, std::size_t col) const {
-	return m_ctrl[row*m_width+col];
+	return _ctrl[row*_width+col];
 }
 
 // called just before an action to save the undo state
@@ -449,7 +453,7 @@ void Patch::undoSave()
 // Save the current patch state into a new UndoMemento instance (allocated on heap) and return it to the undo observer
 IUndoMementoPtr Patch::exportState() const
 {
-	return IUndoMementoPtr(new SavedState(m_width, m_height, m_ctrl, m_patchDef3, m_subdivisions_x, m_subdivisions_y, _shader.getMaterialName()));
+	return IUndoMementoPtr(new SavedState(_width, _height, _ctrl, _patchDef3, _subDivisions.x(), _subDivisions.y(), _shader.getMaterialName()));
 }
 
 // Revert the state of this patch to the one that has been saved in the UndoMemento
@@ -463,13 +467,12 @@ void Patch::importState(const IUndoMementoPtr& state)
 
     // copy construct
 	{
-		m_width = other.m_width;
-		m_height = other.m_height;
-		m_ctrl = other.m_ctrl;
-		onAllocate(m_ctrl.size());
-		m_patchDef3 = other.m_patchDef3;
-		m_subdivisions_x = other.m_subdivisions_x;
-		m_subdivisions_y = other.m_subdivisions_y;
+		_width = other.m_width;
+		_height = other.m_height;
+		_ctrl = other.m_ctrl;
+		onAllocate(_ctrl.size());
+		_patchDef3 = other.m_patchDef3;
+		_subDivisions = Subdivisions(other.m_subdivisions_x, other.m_subdivisions_y);
         _shader.setMaterialName(other._materialName);
 	}
 
@@ -496,9 +499,6 @@ Patch::~Patch()
 		(*i++)->onPatchDestruction();
 	}
 
-	BezierCurveTreeArray_deleteAll(_mesh.curveTreeU);
-	BezierCurveTreeArray_deleteAll(_mesh.curveTreeV);
-
 	// Release the shaders
     _pointShader.reset();
     _latticeShader.reset();
@@ -506,12 +506,12 @@ Patch::~Patch()
 
 bool Patch::isValid() const
 {
-  if(!m_width || !m_height)
+  if(!_width || !_height)
   {
     return false;
   }
 
-  for(PatchControlConstIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i)
+  for(PatchControlConstIter i = _ctrl.begin(); i != _ctrl.end(); ++i)
   {
     if(!double_valid((*i).vertex.x())
       || !double_valid((*i).vertex.y())
@@ -537,10 +537,10 @@ bool Patch::isDegenerate() const {
 
 	// Compare each control's 3D coordinates with the previous one and break out
 	// on the first non-equal one
-	for (PatchControlConstIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
+	for (PatchControlConstIter i = _ctrl.begin(); i != _ctrl.end(); ++i) {
 
 		// Skip the first comparison
-		if (i != m_ctrl.begin() && !i->vertex.isEqual(prev, 0.0001)) {
+		if (i != _ctrl.begin() && !i->vertex.isEqual(prev, 0.0001)) {
 			return false;
 		}
 
@@ -559,49 +559,50 @@ void Patch::updateTesselation()
 
 	_tesselationChanged = false;
 
-    m_ctrl_vertices.clear();
-    m_lattice_indices.clear();
+    _ctrl_vertices.clear();
+    _latticeIndices.clear();
     
-    if(!isValid())
+    if (!isValid())
     {
         _mesh.clear();
-        m_aabb_local = AABB();
+		_localAABB = AABB();
         return;
     }
-    
-    BuildTesselationCurves(ROW);
-    BuildTesselationCurves(COL);
-    BuildVertexArray();
+
+	// Run the tesselation code
+	_mesh.generate(_width, _height, _ctrlTransformed, subdivisionsFixed(), getSubdivisions());
+
     updateAABB();
-    
+
+    // Generate the indices for the coloured control points and the lines in between
     IndexBuffer ctrl_indices;
     
-    m_lattice_indices.reserve(((m_width * (m_height - 1)) + (m_height * (m_width - 1))) << 1);
-    ctrl_indices.reserve(m_ctrlTransformed.size());
+    _latticeIndices.reserve(((_width * (_height - 1)) + (_height * (_width - 1))) << 1);
+    ctrl_indices.reserve(_ctrlTransformed.size());
 
-    UniqueVertexBuffer<VertexCb> inserter(m_ctrl_vertices);
-    for(PatchControlIter i = m_ctrlTransformed.begin(); i != m_ctrlTransformed.end(); ++i)
+    UniqueVertexBuffer<VertexCb> inserter(_ctrl_vertices);
+    for (PatchControlIter i = _ctrlTransformed.begin(); i != _ctrlTransformed.end(); ++i)
     {
-        ctrl_indices.push_back(inserter.insert(pointvertex_quantised(VertexCb(i->vertex, colour_for_index(i - m_ctrlTransformed.begin(), m_width)))));
+        ctrl_indices.push_back(inserter.insert(pointvertex_quantised(VertexCb(i->vertex, colour_for_index(i - _ctrlTransformed.begin(), _width)))));
     }
 
     for(IndexBuffer::iterator i = ctrl_indices.begin(); i != ctrl_indices.end(); ++i)
     {
-        if(std::size_t(i - ctrl_indices.begin()) % m_width)
+        if(std::size_t(i - ctrl_indices.begin()) % _width)
         {
-            m_lattice_indices.push_back(*(i - 1));
-            m_lattice_indices.push_back(*i);
+            _latticeIndices.push_back(*(i - 1));
+            _latticeIndices.push_back(*i);
         }
-        if(std::size_t(i - ctrl_indices.begin()) >= m_width)
+        if(std::size_t(i - ctrl_indices.begin()) >= _width)
         {
-            m_lattice_indices.push_back(*(i - m_width));
-            m_lattice_indices.push_back(*i);
+            _latticeIndices.push_back(*(i - _width));
+            _latticeIndices.push_back(*i);
         }
     }
 
     _solidRenderable.queueUpdate();
 
-    if (m_patchDef3)
+    if (_patchDef3)
     {
         _fixedWireframeRenderable.queueUpdate();
     }
@@ -615,7 +616,7 @@ void Patch::InvertMatrix()
 {
   undoSave();
 
-  PatchControlArray_invert(m_ctrl, m_width, m_height);
+  PatchControlArray_invert(_ctrl, _width, _height);
 
   controlPointsChanged();
 }
@@ -625,20 +626,20 @@ void Patch::TransposeMatrix()
 	undoSave();
 
 	// greebo: create a new temporary control array to hold the "old" matrix
-	PatchControlArray tmp = m_ctrl;
+	PatchControlArray tmp = _ctrl;
 
 	std::size_t i = 0;
 
-	for (std::size_t w = 0; w < m_width; ++w)
+	for (std::size_t w = 0; w < _width; ++w)
 	{
-		for (std::size_t h = 0; h < m_height; ++h)
+		for (std::size_t h = 0; h < _height; ++h)
 		{
 			// Copy elements such that the columns end up as rows
-			m_ctrl[i++] = tmp[h*m_width + w];
+			_ctrl[i++] = tmp[h*_width + w];
 		}
 	}
 
-	std::swap(m_width, m_height);
+	std::swap(_width, _height);
 
 	controlPointsChanged();
 }
@@ -653,15 +654,15 @@ void Patch::Redisperse(EMatrixMajor mt)
   switch(mt)
   {
   case COL:
-    width = (m_width-1)>>1;
-    height = m_height;
+    width = (_width-1)>>1;
+    height = _height;
     col_stride = 1;
-    row_stride = m_width;
+    row_stride = _width;
     break;
   case ROW:
-    width = (m_height-1)>>1;
-    height = m_width;
-    col_stride = m_width;
+    width = (_height-1)>>1;
+    height = _width;
+    col_stride = _width;
     row_stride = 1;
     break;
   default:
@@ -671,7 +672,7 @@ void Patch::Redisperse(EMatrixMajor mt)
 
   for(h=0;h<height;h++)
   {
-    p1 = m_ctrl.begin()+(h*row_stride);
+    p1 = _ctrl.begin()+(h*row_stride);
     for(w=0;w<width;w++)
     {
       p2 = p1+col_stride;
@@ -692,22 +693,22 @@ void Patch::InsertRemove(bool bInsert, bool bColumn, bool bFirst) {
 			// Decide whether we should insert rows or columns
 			if (bColumn) {
 				// The insert point is 1 for "beginning" and width-2 for "end"
-				insertColumns(bFirst ? 1 : m_width-2);
+				insertColumns(bFirst ? 1 : _width-2);
 			}
 			else {
 				// The insert point is 1 for "beginning" and height-2 for "end"
-				insertRows(bFirst ? 1 : m_height-2);
+				insertRows(bFirst ? 1 : _height-2);
 			}
 		}
 		else {
 			// Col/Row Removal
 			if (bColumn) {
 				// Column removal, pass TRUE
-				removePoints(true, bFirst ? 2 : m_width - 3);
+				removePoints(true, bFirst ? 2 : _width - 3);
 			}
 			else {
 				// Row removal, pass FALSE
-				removePoints(false, bFirst ? 2 : m_height - 3);
+				removePoints(false, bFirst ? 2 : _height - 3);
 			}
 		}
 	}
@@ -721,8 +722,8 @@ void Patch::InsertRemove(bool bInsert, bool bColumn, bool bFirst) {
 void Patch::appendPoints(bool columns, bool beginning) {
 	bool rows = !columns; // Shortcut for readability
 
-	if ((columns && m_width + 2 > MAX_PATCH_WIDTH) ||
-		(rows && m_height + 2 > MAX_PATCH_HEIGHT))
+	if ((columns && _width + 2 > MAX_PATCH_WIDTH) ||
+		(rows && _height + 2 > MAX_PATCH_HEIGHT))
 	{
 		rError() << "Patch::appendPoints() error: " <<
 							   "Cannot make patch any larger.\n";
@@ -733,9 +734,9 @@ void Patch::appendPoints(bool columns, bool beginning) {
 	undoSave();
 
 	// Create a backup of the old control vertices
-	PatchControlArray oldCtrl = m_ctrl;
-	std::size_t oldHeight = m_height;
-	std::size_t oldWidth = m_width;
+	PatchControlArray oldCtrl = _ctrl;
+	std::size_t oldHeight = _height;
+	std::size_t oldWidth = _width;
 
 	// Resize this patch
 	setDims(columns ? oldWidth+2 : oldWidth, rows ? oldHeight+2 : oldHeight);
@@ -747,11 +748,11 @@ void Patch::appendPoints(bool columns, bool beginning) {
 	// We're copying the old patch matrix into a sub-matrix of the new patch
 	// Fill in the control vertex values into the target area using this loop
 	for (std::size_t newRow = targetRowStart, oldRow = 0;
-		 newRow < m_height && oldRow < oldHeight;
+		 newRow < _height && oldRow < oldHeight;
 		 newRow++, oldRow++)
 	{
 		for (std::size_t newCol = targetColStart, oldCol = 0;
-			 oldCol < oldWidth && newCol < m_width;
+			 oldCol < oldWidth && newCol < _width;
 			 oldCol++, newCol++)
 		{
 			// Copy the control vertex from the old patch to the new patch
@@ -764,15 +765,15 @@ void Patch::appendPoints(bool columns, bool beginning) {
 		// Extrapolate the vertex attributes of the columns
 
 		// These are the indices of the new columns
-		std::size_t newCol1 = beginning ? 0 : m_width - 1; // The outermost column
-		std::size_t newCol2 = beginning ? 1 : m_width - 2; // The nearest column
+		std::size_t newCol1 = beginning ? 0 : _width - 1; // The outermost column
+		std::size_t newCol2 = beginning ? 1 : _width - 2; // The nearest column
 
 		// This indicates the direction we are taking the base values from
 		// If we start at the beginning, we have to take the values on
 		// the "right", hence the +1 index
 		int neighbour = beginning ? +1 : -1;
 
-		for (std::size_t row = 0; row < m_height; row++) {
+		for (std::size_t row = 0; row < _height; row++) {
 			// The distance of the two neighbouring columns,
 			// this is taken as extrapolation value
 			Vector3 vertexDiff = ctrlAt(row, newCol2 + neighbour).vertex -
@@ -793,15 +794,15 @@ void Patch::appendPoints(bool columns, bool beginning) {
 		// Extrapolate the vertex attributes of the rows
 
 		// These are the indices of the new rows
-		std::size_t newRow1 = beginning ? 0 : m_height - 1; // The outermost row
-		std::size_t newRow2 = beginning ? 1 : m_height - 2; // The nearest row
+		std::size_t newRow1 = beginning ? 0 : _height - 1; // The outermost row
+		std::size_t newRow2 = beginning ? 1 : _height - 2; // The nearest row
 
 		// This indicates the direction we are taking the base values from
 		// If we start at the beginning, we have to take the values on
 		// the "right", hence the +1 index
 		int neighbour = beginning ? +1 : -1;
 
-		for (std::size_t col = 0; col < m_width; col++) {
+		for (std::size_t col = 0; col < _width; col++) {
 			// The distance of the two neighbouring rows,
 			// this is taken as extrapolation value
 			Vector3 vertexDiff = ctrlAt(newRow2 + neighbour, col).vertex -
@@ -829,12 +830,12 @@ Patch* Patch::MakeCap(Patch* patch, EPatchCap eType, EMatrixMajor mt, bool bFirs
   switch(mt)
   {
   case ROW:
-    width = m_width;
-    height = m_height;
+    width = _width;
+    height = _height;
     break;
   case COL:
-    width = m_height;
-    height = m_width;
+    width = _height;
+    height = _width;
     break;
   default:
     ERROR_MESSAGE("neither row-major nor column-major");
@@ -870,7 +871,7 @@ void Patch::FlipTexture(int nAxis)
 {
   undoSave();
 
-  for(PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i)
+  for(PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i)
   {
     i->texcoord[nAxis] = -i->texcoord[nAxis];
   }
@@ -883,7 +884,7 @@ void Patch::FlipTexture(int nAxis)
  */
 void Patch::translateTexCoords(Vector2 translation) {
 	// Cycle through all control points and shift them in texture space
-	for (PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
+	for (PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i) {
     	i->texcoord += translation;
   	}
 }
@@ -904,7 +905,7 @@ void Patch::ScaleTexture(float s, float t)
 {
   undoSave();
 
-  for(PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i)
+  for(PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i)
   {
     (*i).texcoord[0] *= s;
     (*i).texcoord[1] *= t;
@@ -920,7 +921,7 @@ void Patch::RotateTexture(float angle)
   const double s = sin(degrees_to_radians(angle));
   const double c = cos(degrees_to_radians(angle));
 
-  for(PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i)
+  for(PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i)
   {
     const double x = i->texcoord[0];
     const double y = i->texcoord[1];
@@ -942,23 +943,23 @@ void Patch::SetTextureRepeat(float s, float t)
 	 * If we have a 4x4 patch and want to tile it 3x3, the distance
 	 * from one control point to the next one has to cover 3/4 of a full texture,
 	 * hence texture_x_repeat/patch_width and texture_y_repeat/patch_height.*/
-	float sIncr = s / static_cast<float>(m_width - 1);
-	float tIncr = t / static_cast<float>(m_height - 1);
+	float sIncr = s / static_cast<float>(_width - 1);
+	float tIncr = t / static_cast<float>(_height - 1);
 
 	// Set the pointer to the first control point
-	PatchControlIter pDest = m_ctrl.begin();
+	PatchControlIter pDest = _ctrl.begin();
 
 	float tc = 0;
 
 	// Cycle through the patch matrix (row per row)
 	// Increment the <tc> counter by <tIncr> increment
-	for (std::size_t h=0; h < m_height; h++, tc += tIncr)
+	for (std::size_t h=0; h < _height; h++, tc += tIncr)
 	{
 		float sc = 0;
 
 		// Cycle through the row points: reset sc to zero
 		// and increment it by sIncr at each step.
-		for (std::size_t w = 0; w < m_width; w++, sc += sIncr)
+		for (std::size_t w = 0; w < _width; w++, sc += sIncr)
 		{
 			// Set the texture coordinates
 			pDest->texcoord[0] = sc;
@@ -972,37 +973,6 @@ void Patch::SetTextureRepeat(float s, float t)
 	controlPointsChanged();
 }
 
-inline int texture_axis(const Vector3& normal)
-{
-  // axis dominance order: Z, X, Y
-  return (normal.x() >= normal.y()) ? (normal.x() > normal.z()) ? 0 : 2 : (normal.y() > normal.z()) ? 1 : 2;
-}
-
-void Patch::CapTexture() {
-	const PatchControl& p1 = m_ctrl[m_width];
-	const PatchControl& p2 = m_ctrl[m_width*(m_height-1)];
-	const PatchControl& p3 = m_ctrl[(m_width*m_height)-1];
-
-  Vector3 normal(g_vector3_identity);
-
-  {
-    Vector3 tmp( (p2.vertex - m_ctrl[0].vertex).crossProduct(p3.vertex - m_ctrl[0].vertex) );
-    if(tmp != g_vector3_identity)
-    {
-      normal += tmp;
-    }
-  }
-  {
-    Vector3 tmp( (p1.vertex - p3.vertex).crossProduct(m_ctrl[0].vertex - p3.vertex) );
-    if(tmp != g_vector3_identity)
-    {
-      normal += tmp;
-    }
-  }
-
-  ProjectTexture(texture_axis(normal));
-}
-
 /* uses longest parallel chord to calculate texture coords for each row/col
  * greebo: The default texture scale is used when applying "Natural" texturing.
  * Note: all the comments in this method are by myself, so be careful... ;)
@@ -1042,24 +1012,24 @@ void Patch::NaturalTexture() {
 		double tex = 0;
 
 		// Cycle through the patch width,
-		for (std::size_t w=0; w<m_width; w++)
+		for (std::size_t w=0; w<_width; w++)
 		{
 			// Apply the currently active <tex> value to the control point texture coordinates.
-			for (std::size_t h = 0; h < m_height; h++)
+			for (std::size_t h = 0; h < _height; h++)
 			{
 				// Set the x-coord (or better s-coord?) of the texture to tex.
 				// For the first width cycle this is tex=0, so the texture is not shifted at the first vertex
 				ctrlAt(h, w).texcoord[0] = static_cast<float>(tex);
 			}
 
-			// If we reached the last row (m_width - 1) we are finished (all coordinates are applied)
-			if (w + 1 == m_width)
+			// If we reached the last row (_width - 1) we are finished (all coordinates are applied)
+			if (w + 1 == _width)
 				break;
 
 			// Determine the longest distance to the next column.
 			{
 				// Again, cycle through the current column
-				for (std::size_t h = 0; h < m_height; h++)
+				for (std::size_t h = 0; h < _height; h++)
 				{
 					// v is the vector pointing from one control point to the next neighbour
 					Vector3 v = ctrlAt(h, w).vertex - ctrlAt(h, w+1).vertex;
@@ -1095,18 +1065,18 @@ void Patch::NaturalTexture() {
 		double tex = 0;
 
 		// Each row is visited once
-		for (std::size_t h = 0; h < m_height; h++)
+		for (std::size_t h = 0; h < _height; h++)
 		{
 			// During each row, we cycle through every height point
-	       	for (std::size_t w = 0; w < m_width; w++)
+	       	for (std::size_t w = 0; w < _width; w++)
 			{
 				ctrlAt(h, w).texcoord[1] = static_cast<float>(tex);
 			}
 
-			if (h + 1 == m_height)
+			if (h + 1 == _height)
 				break;
 
-			for (std::size_t w = 0; w < m_width; w++)
+			for (std::size_t w = 0; w < _width; w++)
 			{
 				Vector3 v = ctrlAt(h, w).vertex - ctrlAt(h+1, w).vertex;
 
@@ -1130,15 +1100,15 @@ void Patch::updateAABB()
 {
 	AABB aabb;
 
-	for(PatchControlIter i = m_ctrlTransformed.begin(); i != m_ctrlTransformed.end(); ++i)
+	for(PatchControlIter i = _ctrlTransformed.begin(); i != _ctrlTransformed.end(); ++i)
 	{
 		aabb.includePoint(i->vertex);
 	}
 
 	// greebo: Only trigger the callbacks if the bounds actually changed
-	if (m_aabb_local != aabb)
+	if (_localAABB != aabb)
 	{
-		m_aabb_local = aabb;
+		_localAABB = aabb;
 
 		_node.boundsChanged();
 		_node.lightsChanged();
@@ -1147,28 +1117,28 @@ void Patch::updateAABB()
 
 // Inserts two columns before and after the column having the index <colIndex>
 void Patch::insertColumns(std::size_t colIndex) {
-	if (colIndex == 0 || colIndex == m_width) {
+	if (colIndex == 0 || colIndex == _width) {
 		throw GenericPatchException("Patch::insertColumns: can't insert at this index.");
 	}
 
-	if (m_width + 2 > MAX_PATCH_WIDTH) {
+	if (_width + 2 > MAX_PATCH_WIDTH) {
 		throw GenericPatchException("Patch::insertColumns: patch has too many columns.");
 	}
 
 	// Create a backup of the old control vertices
-	PatchControlArray oldCtrl = m_ctrl;
-	std::size_t oldHeight = m_height;
-	std::size_t oldWidth = m_width;
+	PatchControlArray oldCtrl = _ctrl;
+	std::size_t oldHeight = _height;
+	std::size_t oldWidth = _width;
 
 	// Resize this patch
 	setDims(oldWidth + 2, oldHeight);
 
 	// Now fill in the control vertex values and interpolate
 	// before and after the insert point.
-	for (std::size_t row = 0; row < m_height; row++) {
+	for (std::size_t row = 0; row < _height; row++) {
 
 		for (std::size_t newCol = 0, oldCol = 0;
-			 newCol < m_width && oldCol < oldWidth;
+			 newCol < _width && oldCol < oldWidth;
 			 newCol++, oldCol++)
 		{
 			// Is this the insert point?
@@ -1210,28 +1180,28 @@ void Patch::insertColumns(std::size_t colIndex) {
 
 // Inserts two rows before and after the column having the index <colIndex>
 void Patch::insertRows(std::size_t rowIndex) {
-	if (rowIndex == 0 || rowIndex == m_height) {
+	if (rowIndex == 0 || rowIndex == _height) {
 		throw GenericPatchException("Patch::insertRows: can't insert at this index.");
 	}
 
-	if (m_height + 2 > MAX_PATCH_HEIGHT) {
+	if (_height + 2 > MAX_PATCH_HEIGHT) {
 		throw GenericPatchException("Patch::insertRows: patch has too many rows.");
 	}
 
 	// Create a backup of the old control vertices
-	PatchControlArray oldCtrl = m_ctrl;
-	std::size_t oldHeight = m_height;
-	std::size_t oldWidth = m_width;
+	PatchControlArray oldCtrl = _ctrl;
+	std::size_t oldHeight = _height;
+	std::size_t oldWidth = _width;
 
 	// Resize this patch
 	setDims(oldWidth, oldHeight + 2);
 
 	// Now fill in the control vertex values and interpolate
 	// before and after the insert point.
-	for (std::size_t col = 0; col < m_width; col++) {
+	for (std::size_t col = 0; col < _width; col++) {
 
 		for (std::size_t newRow = 0, oldRow = 0;
-			 newRow < m_height && oldRow < oldHeight;
+			 newRow < _height && oldRow < oldHeight;
 			 newRow++, oldRow++)
 		{
 			// Is this the insert point?
@@ -1275,25 +1245,25 @@ void Patch::insertRows(std::size_t rowIndex) {
 void Patch::removePoints(bool columns, std::size_t index) {
 	bool rows = !columns; // readability shortcut ;)
 
-	if ((columns && m_width<5) || (!columns && m_height < 5))
+	if ((columns && _width<5) || (!columns && _height < 5))
     {
 		throw GenericPatchException("Patch::removePoints: can't remove any more rows/columns.");
 	}
 
 	// Check column index bounds
-	if (columns && (index < 2 || index > m_width - 3)) {
+	if (columns && (index < 2 || index > _width - 3)) {
 		throw GenericPatchException("Patch::removePoints: can't remove columns at this index.");
 	}
 
 	// Check row index bounds
-	if (rows && (index < 2 || index > m_height - 3)) {
+	if (rows && (index < 2 || index > _height - 3)) {
 		throw GenericPatchException("Patch::removePoints: can't remove rows at this index.");
 	}
 
 	// Create a backup of the old control vertices
-	PatchControlArray oldCtrl = m_ctrl;
-	std::size_t oldHeight = m_height;
-	std::size_t oldWidth = m_width;
+	PatchControlArray oldCtrl = _ctrl;
+	std::size_t oldHeight = _height;
+	std::size_t oldWidth = _width;
 
 	// Resize this patch
 	setDims(columns ? oldWidth - 2 : oldWidth, rows ? oldHeight - 2 : oldHeight);
@@ -1301,7 +1271,7 @@ void Patch::removePoints(bool columns, std::size_t index) {
 	// Now fill in the control vertex values and skip
 	// the rows/cols before and after the remove point.
 	for (std::size_t newRow = 0, oldRow = 0;
-		 newRow < m_height && oldRow < oldHeight;
+		 newRow < _height && oldRow < oldHeight;
 		 newRow++, oldRow++)
 	{
 		// Skip the row before and after the removal point
@@ -1311,7 +1281,7 @@ void Patch::removePoints(bool columns, std::size_t index) {
 		}
 
 		for (std::size_t oldCol = 0, newCol = 0;
-			 oldCol < oldWidth && newCol < m_width;
+			 oldCol < oldWidth && newCol < _width;
 			 oldCol++, newCol++)
 		{
 			// Skip the column before and after the removal point
@@ -1334,30 +1304,30 @@ void Patch::ConstructSeam(EPatchCap eType, Vector3* p, std::size_t width)
   case eCapIBevel:
     {
       setDims(3, 3);
-      m_ctrl[0].vertex = p[0];
-      m_ctrl[1].vertex = p[1];
-      m_ctrl[2].vertex = p[1];
-      m_ctrl[3].vertex = p[1];
-      m_ctrl[4].vertex = p[1];
-      m_ctrl[5].vertex = p[1];
-      m_ctrl[6].vertex = p[2];
-      m_ctrl[7].vertex = p[1];
-      m_ctrl[8].vertex = p[1];
+      _ctrl[0].vertex = p[0];
+      _ctrl[1].vertex = p[1];
+      _ctrl[2].vertex = p[1];
+      _ctrl[3].vertex = p[1];
+      _ctrl[4].vertex = p[1];
+      _ctrl[5].vertex = p[1];
+      _ctrl[6].vertex = p[2];
+      _ctrl[7].vertex = p[1];
+      _ctrl[8].vertex = p[1];
     }
     break;
   case eCapBevel:
     {
       setDims(3, 3);
       Vector3 p3(p[2] + (p[0] - p[1]));
-      m_ctrl[0].vertex = p3;
-      m_ctrl[1].vertex = p3;
-      m_ctrl[2].vertex = p[2];
-      m_ctrl[3].vertex = p3;
-      m_ctrl[4].vertex = p3;
-      m_ctrl[5].vertex = p[1];
-      m_ctrl[6].vertex = p3;
-      m_ctrl[7].vertex = p3;
-      m_ctrl[8].vertex = p[0];
+      _ctrl[0].vertex = p3;
+      _ctrl[1].vertex = p3;
+      _ctrl[2].vertex = p[2];
+      _ctrl[3].vertex = p3;
+      _ctrl[4].vertex = p3;
+      _ctrl[5].vertex = p[1];
+      _ctrl[6].vertex = p3;
+      _ctrl[7].vertex = p3;
+      _ctrl[8].vertex = p[0];
     }
     break;
   case eCapEndCap:
@@ -1365,35 +1335,35 @@ void Patch::ConstructSeam(EPatchCap eType, Vector3* p, std::size_t width)
       Vector3 p5(p[0].mid(p[4]));
 
       setDims(3, 3);
-      m_ctrl[0].vertex = p[0];
-      m_ctrl[1].vertex = p5;
-      m_ctrl[2].vertex = p[4];
-      m_ctrl[3].vertex = p[1];
-      m_ctrl[4].vertex = p[2];
-      m_ctrl[5].vertex = p[3];
-      m_ctrl[6].vertex = p[2];
-      m_ctrl[7].vertex = p[2];
-      m_ctrl[8].vertex = p[2];
+      _ctrl[0].vertex = p[0];
+      _ctrl[1].vertex = p5;
+      _ctrl[2].vertex = p[4];
+      _ctrl[3].vertex = p[1];
+      _ctrl[4].vertex = p[2];
+      _ctrl[5].vertex = p[3];
+      _ctrl[6].vertex = p[2];
+      _ctrl[7].vertex = p[2];
+      _ctrl[8].vertex = p[2];
     }
     break;
   case eCapIEndCap:
     {
       setDims(5, 3);
-      m_ctrl[0].vertex = p[4];
-      m_ctrl[1].vertex = p[3];
-      m_ctrl[2].vertex = p[2];
-      m_ctrl[3].vertex = p[1];
-      m_ctrl[4].vertex = p[0];
-      m_ctrl[5].vertex = p[3];
-      m_ctrl[6].vertex = p[3];
-      m_ctrl[7].vertex = p[2];
-      m_ctrl[8].vertex = p[1];
-      m_ctrl[9].vertex = p[1];
-      m_ctrl[10].vertex = p[3];
-      m_ctrl[11].vertex = p[3];
-      m_ctrl[12].vertex = p[2];
-      m_ctrl[13].vertex = p[1];
-      m_ctrl[14].vertex = p[1];
+      _ctrl[0].vertex = p[4];
+      _ctrl[1].vertex = p[3];
+      _ctrl[2].vertex = p[2];
+      _ctrl[3].vertex = p[1];
+      _ctrl[4].vertex = p[0];
+      _ctrl[5].vertex = p[3];
+      _ctrl[6].vertex = p[3];
+      _ctrl[7].vertex = p[2];
+      _ctrl[8].vertex = p[1];
+      _ctrl[9].vertex = p[1];
+      _ctrl[10].vertex = p[3];
+      _ctrl[11].vertex = p[3];
+      _ctrl[12].vertex = p[2];
+      _ctrl[13].vertex = p[1];
+      _ctrl[14].vertex = p[1];
     }
     break;
   case eCapCylinder:
@@ -1416,18 +1386,20 @@ void Patch::ConstructSeam(EPatchCap eType, Vector3* p, std::size_t width)
       }
 
       {
-        PatchControlIter pCtrl = m_ctrl.begin();
-        for(std::size_t i = 0; i != m_height; ++i, pCtrl += m_width)
+        PatchControlIter pCtrl = _ctrl.begin();
+        for(std::size_t i = 0; i != _height; ++i, pCtrl += _width)
         {
           pCtrl->vertex = p[i];
         }
       }
       {
-        PatchControlIter pCtrl = m_ctrl.begin() + 2;
-        std::size_t h = m_height - 1;
-        for(std::size_t i = 0; i != m_height; ++i, pCtrl += m_width)
+        PatchControlIter pCtrl = _ctrl.begin() + 2;
+        std::size_t h = _height - 1;
+        for(std::size_t i = 0; i != _height; ++i, pCtrl += _width)
         {
           pCtrl->vertex = p[h + (h - i)];
+
+          if (i == _height - 1) break; // prevent iterator from being incremented post bounds
         }
       }
 
@@ -1438,7 +1410,6 @@ void Patch::ConstructSeam(EPatchCap eType, Vector3* p, std::size_t width)
     ERROR_MESSAGE("invalid patch-cap type");
     return;
   }
-  CapTexture();
   controlPointsChanged();
 }
 
@@ -1452,14 +1423,14 @@ PatchControlIter Patch::getClosestPatchControlToPoint(const Vector3& point) {
 	double closestDist = -1.0;
 
 	PatchControlIter corners[4] = {
-		m_ctrl.begin(),
-		m_ctrl.begin() + (m_width-1),
-		m_ctrl.begin() + (m_width*(m_height-1)),
-		m_ctrl.begin() + (m_width*m_height - 1)
+		_ctrl.begin(),
+		_ctrl.begin() + (_width-1),
+		_ctrl.begin() + (_width*(_height-1)),
+		_ctrl.begin() + (_width*_height - 1)
 	};
 
 	// Cycle through all the control points with an iterator
-	//for (PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
+	//for (PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i) {
 	for (unsigned int i = 0; i < 4; i++) {
 
 		// Calculate the distance of the current vertex
@@ -1565,13 +1536,13 @@ Vector2 Patch::getPatchControlArrayIndices(const PatchControlIter& control)
 	std::size_t count = 0;
 
 	// Go through the patch column per column and find the control vertex
-	for (PatchControlIter p = m_ctrl.begin(); p != m_ctrl.end(); ++p, ++count)
+	for (PatchControlIter p = _ctrl.begin(); p != _ctrl.end(); ++p, ++count)
 	{
 		// Compare the iterators to check if we have found the control
 		if (p == control)
 		{
-			int row = static_cast<int>(floor(static_cast<float>(count) / m_width));
-			int col = static_cast<int>(count % m_width);
+			int row = static_cast<int>(floor(static_cast<float>(count) / _width));
+			int col = static_cast<int>(count % _width);
 
 			return Vector2(col, row);
 		}
@@ -1621,8 +1592,8 @@ void Patch::pasteTextureNatural(const Face* face)
     if (face == nullptr) return;
 
 	// Convert the size_t stuff into int, because we need it for signed comparisons
-	int patchHeight = static_cast<int>(m_height);
-	int patchWidth = static_cast<int>(m_width);
+	int patchHeight = static_cast<int>(_height);
+	int patchWidth = static_cast<int>(_width);
 
 	// Get the plane and its normalised normal vector of the face
 	Plane3 plane = face->getPlane().getPlane().getNormalised();
@@ -1648,15 +1619,15 @@ void Patch::pasteTextureNatural(const Face* face)
 	int hIncr = (hStart == patchHeight-1) ? -1 : 1;
 	int hEnd = (hIncr<0) ? -1 : patchHeight;
 
-	PatchControl* startControl = &m_ctrl[(patchWidth*hStart) + wStart];
+	PatchControl* startControl = &_ctrl[(patchWidth*hStart) + wStart];
 
 	// Calculate the base directions that are used to "flatten" the patch
 	// These have to be orthogonal to the facePlane normal, so that the texture coordinates
 	// can be retrieved by projection onto the facePlane.
 
 	// Get the control points of the next column and the next row
-	PatchControl& nextColumn = m_ctrl[(patchWidth*(hStart + hIncr)) + wStart];
-	PatchControl& nextRow = m_ctrl[(patchWidth*hStart) + (wStart + wIncr)];
+	PatchControl& nextColumn = _ctrl[(patchWidth*(hStart + hIncr)) + wStart];
+	PatchControl& nextRow = _ctrl[(patchWidth*hStart) + (wStart + wIncr)];
 
 	// Calculate the world direction of these control points and extract a base
 	Vector3 widthVector = (nextRow.vertex - startControl->vertex);
@@ -1686,7 +1657,7 @@ void Patch::pasteTextureNatural(const Face* face)
 	for (int w = wStart; w != wEnd; w += wIncr) {
 
 		// The first control in this row, calculate its virtual coords
-		PatchControl* curColumn = &m_ctrl[(patchWidth*hStart) + w];
+		PatchControl* curColumn = &_ctrl[(patchWidth*hStart) + w];
 
 		// The distance between the last column and this column
 		double xyzColDist = (curColumn->vertex - prevColumn->vertex).getLength();
@@ -1702,7 +1673,7 @@ void Patch::pasteTextureNatural(const Face* face)
 		for (int h = hStart; h != hEnd; h += hIncr) {
 
 			// The current control
-			PatchControl* control = &m_ctrl[(patchWidth*h) + w];
+			PatchControl* control = &_ctrl[(patchWidth*h) + w];
 
 			// The distance between the last and the current vertex
 			double xyzRowDist = (control->vertex - prevRow->vertex).getLength();
@@ -1732,8 +1703,8 @@ void Patch::pasteTextureNatural(Patch& sourcePatch) {
 	undoSave();
 
 	// Convert the size_t stuff into int, because we need it for signed comparisons
-	int patchHeight = static_cast<int>(m_height);
-	int patchWidth = static_cast<int>(m_width);
+	int patchHeight = static_cast<int>(_height);
+	int patchWidth = static_cast<int>(_width);
 
 	// Calculate the nearest corner vertex of this patch (to the sourcepatch vertices)
 	PatchControlIter nearestControl = getClosestPatchControlToPatch(sourcePatch);
@@ -1773,7 +1744,7 @@ void Patch::pasteTextureProjected(const Face* face) {
 		Matrix4 worldToTexture = face->getProjection().getWorldToTexture(faceNormal, Matrix4::getIdentity());
 
 		// Cycle through all the control points with an iterator
-		for (PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
+		for (PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i) {
 			// Project the vertex onto the face plane and transform it into texture space
 			i->texcoord = getProjectedTextureCoords(i->vertex, plane, worldToTexture);
 		}
@@ -1791,13 +1762,13 @@ void Patch::pasteTextureCoordinates(const Patch* otherPatch) {
 
 	if (otherPatch != NULL) {
 
-		if (otherPatch->getWidth() == m_width && otherPatch->getHeight() == m_height) {
+		if (otherPatch->getWidth() == _width && otherPatch->getHeight() == _height) {
 
 			PatchControlConstIter other;
 			PatchControlIter self;
 
 			// Clone the texture coordinates one by one
-			for (other = otherPatch->begin(), self = m_ctrl.begin();
+			for (other = otherPatch->begin(), self = _ctrl.begin();
 				 other != otherPatch->end();
 				 ++other, ++self)
 			{
@@ -1813,63 +1784,6 @@ void Patch::pasteTextureCoordinates(const Patch* otherPatch) {
 	}
 }
 
-/* greebo: This gets called when clicking on the "CAP" button in the surface dialogs.
- *
- * The texture is projected onto either the <x,y>, <y,z>, or <x,z> planes and the result
- * is not stretched in any direction, as the pure components of the vector are translated
- * into texture coordinates. Points on the x-axis that are near to each other get
- * texture coordinates that are "near" to each other.
- *
- * The argument nAxis can be 0,1,2 and determines, which components are taken to
- * calculate the texture coordinates.
- *
- * Note: the default texture scale is used to calculate the texture coordinates.
- */
-void Patch::ProjectTexture(int nAxis) {
-	// Save the undo memento
-	undoSave();
-
-	// Hold the component index of the vector (e.g. 0,1 = x,y or 1,2 = y,z)
-	int s, t;
-
-	switch (nAxis) {
-		case 2:		// Projection onto <x,y> plane
-			s = 0;
-			t = 1;
-		break;
-		case 0:		// Projection onto <y,z> plane
-			s = 1;
-			t = 2;
-		break;
-		case 1:		// Projection onto <x,z> plane
-			s = 0;
-			t = 2;
-		break;
-		default:
-			ERROR_MESSAGE("invalid axis");
-		return;
-	}
-
-	// Retrieve the default scale from the registry
-	float defaultScale = registry::getValue<float>("user/ui/defaultTextureScale");
-
-	/* Calculate the conversion factor between world and texture coordinates
-	 * by using the image width/height.*/
-	float fWidth = 1 / (_shader.getWidth() * defaultScale);
-	float fHeight = 1 / (_shader.getHeight() * -defaultScale);
-
-	// Cycle through all the control points with an iterator
-	for (PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
-		// Take the according value (e.g. s = x, t = y, depending on the nAxis argument)
-		// and apply the appropriate texture coordinate
-		i->texcoord[0] = i->vertex[s] * fWidth;
-		i->texcoord[1] = i->vertex[t] * fHeight;
-	}
-
-	// Notify the patch about the change
-	controlPointsChanged();
-}
-
 void Patch::alignTexture(EAlignType align)
 {
 	if (isDegenerate()) return;
@@ -1881,22 +1795,22 @@ void Patch::alignTexture(EAlignType align)
 	std::vector<Vector2> texCoords;
 
 	// Calculate all edges in texture space
-	for (std::size_t h = 0; h < m_height-1; ++h)
+	for (std::size_t h = 0; h < _height-1; ++h)
 	{
-		for (std::size_t w = 0; w < m_width-1; ++w)
+		for (std::size_t w = 0; w < _width-1; ++w)
 		{
 			texEdges.push_back(ctrlAt(0, w).texcoord - ctrlAt(0, w+1).texcoord);
 			texCoords.push_back(ctrlAt(0,w).texcoord);
 
-			texEdges.push_back(ctrlAt(m_height-1, w+1).texcoord - ctrlAt(m_height-1, w).texcoord);
-			texCoords.push_back(ctrlAt(m_height-1, w+1).texcoord);
+			texEdges.push_back(ctrlAt(_height-1, w+1).texcoord - ctrlAt(_height-1, w).texcoord);
+			texCoords.push_back(ctrlAt(_height-1, w+1).texcoord);
 		}
 
 		texEdges.push_back(ctrlAt(h, 0).texcoord - ctrlAt(h+1, 0).texcoord);
 		texCoords.push_back(ctrlAt(h, 0).texcoord);
 
-		texEdges.push_back(ctrlAt(h+1, m_width-1).texcoord - ctrlAt(h, m_width-1).texcoord);
-		texCoords.push_back(ctrlAt(h+1, m_width-1).texcoord);
+		texEdges.push_back(ctrlAt(h+1, _width-1).texcoord - ctrlAt(h, _width-1).texcoord);
+		texCoords.push_back(ctrlAt(h+1, _width-1).texcoord);
 	}
 
 	// Find the edge which is nearest to the s,t base vector, to classify them as "top" or "left"
@@ -1970,8 +1884,8 @@ PatchMesh Patch::getTesselatedPatchMesh() const
 
 	PatchMesh mesh;
 
-	mesh.width = _mesh.m_nArrayWidth;
-	mesh.height = _mesh.m_nArrayHeight;
+	mesh.width = _mesh.width;
+	mesh.height = _mesh.height;
 
 	for (std::vector<ArbitraryMeshVertex>::const_iterator i = _mesh.vertices.begin();
 		i != _mesh.vertices.end(); ++i)
@@ -2003,26 +1917,26 @@ void Patch::constructPlane(const AABB& aabb, int axis, std::size_t width, std::s
     return;
   }
 
-  if(m_width < MIN_PATCH_WIDTH || m_width > MAX_PATCH_WIDTH) m_width = 3;
-  if(m_height < MIN_PATCH_HEIGHT || m_height > MAX_PATCH_HEIGHT) m_height = 3;
+  if(_width < MIN_PATCH_WIDTH || _width > MAX_PATCH_WIDTH) _width = 3;
+  if(_height < MIN_PATCH_HEIGHT || _height > MAX_PATCH_HEIGHT) _height = 3;
 
   Vector3 vStart;
   vStart[x] = aabb.origin[x] - aabb.extents[x];
   vStart[y] = aabb.origin[y] - aabb.extents[y];
   vStart[z] = aabb.origin[z];
 
-  float xAdj = fabsf((vStart[x] - (aabb.origin[x] + aabb.extents[x])) / (float)(m_width - 1));
-  float yAdj = fabsf((vStart[y] - (aabb.origin[y] + aabb.extents[y])) / (float)(m_height - 1));
+  float xAdj = fabsf((vStart[x] - (aabb.origin[x] + aabb.extents[x])) / (float)(_width - 1));
+  float yAdj = fabsf((vStart[y] - (aabb.origin[y] + aabb.extents[y])) / (float)(_height - 1));
 
   Vector3 vTmp;
   vTmp[z] = vStart[z];
-  PatchControlIter pCtrl = m_ctrl.begin();
+  PatchControlIter pCtrl = _ctrl.begin();
 
   vTmp[y]=vStart[y];
-  for (std::size_t h=0; h<m_height; h++)
+  for (std::size_t h=0; h<_height; h++)
   {
     vTmp[x]=vStart[x];
-    for (std::size_t w=0; w<m_width; w++, ++pCtrl)
+    for (std::size_t w=0; w<_width; w++, ++pCtrl)
     {
       pCtrl->vertex = vTmp;
       vTmp[x]+=xAdj;
@@ -2073,7 +1987,7 @@ void Patch::constructBevel(const AABB& aabb, EViewType viewType)
 
 	setDims(3, 3);
 
-	PatchControlIter ctrl = m_ctrl.begin();
+	PatchControlIter ctrl = _ctrl.begin();
 
 	for (std::size_t h = 0; h < 3; ++h)
 	{
@@ -2120,7 +2034,7 @@ void Patch::constructEndcap(const AABB& aabb, EViewType viewType)
 
 	setDims(5, 3);
 
-	PatchControlIter pCtrl = m_ctrl.begin();
+	PatchControlIter pCtrl = _ctrl.begin();
 
 	for (std::size_t h = 0; h < 3; ++h)
 	{
@@ -2171,20 +2085,20 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 		{
 		case eSqCylinder:
 			setDims(9, 3);
-			pStart = m_ctrl.begin();
+			pStart = _ctrl.begin();
 			break;
 		case eDenseCylinder:
 		case eVeryDenseCylinder:
 		case eCylinder:
 			setDims(9, 3);
-			pStart = m_ctrl.begin() + 1;
+			pStart = _ctrl.begin() + 1;
 			break;
 		case eCone: setDims(9, 3);
-			pStart = m_ctrl.begin() + 1;
+			pStart = _ctrl.begin() + 1;
 			break;
 		case eSphere:
 			setDims(9, 5);
-			pStart = m_ctrl.begin() + (9+1);
+			pStart = _ctrl.begin() + (9+1);
 			break;
 		default:
 			ERROR_MESSAGE("this should be unreachable");
@@ -2243,7 +2157,7 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 		{
 		case eSqCylinder:
 			{
-				PatchControlIter pCtrl = m_ctrl.begin();
+				PatchControlIter pCtrl = _ctrl.begin();
 
 				for (std::size_t h = 0; h < 3; ++h)
 				{
@@ -2261,7 +2175,7 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 			{
 				// Regular cylinders get the first column snapped to the last one
 				// to form a closed loop
-				PatchControlIter pCtrl = m_ctrl.begin();
+				PatchControlIter pCtrl = _ctrl.begin();
 
 				for (std::size_t h = 0; h < 3; ++h)
 				{
@@ -2275,7 +2189,7 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 		case eCone:
 			// Close the control vertex loop of cones
 			{
-				PatchControlIter pCtrl = m_ctrl.begin();
+				PatchControlIter pCtrl = _ctrl.begin();
 
 				for (std::size_t h = 0; h < 2; ++h)
 				{
@@ -2286,7 +2200,7 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 			}
 			// And "merge" the vertices of the last row into one single point
 			{
-				PatchControlIter pCtrl = m_ctrl.begin() + 9*2;
+				PatchControlIter pCtrl = _ctrl.begin() + 9*2;
 
 				for (std::size_t w = 0; w < 9; ++w, ++pCtrl)
 				{
@@ -2299,7 +2213,7 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 		case eSphere:
 			// Close the vertex loop for spheres too (middle row)
 			{
-				PatchControlIter pCtrl = m_ctrl.begin() + 9;
+				PatchControlIter pCtrl = _ctrl.begin() + 9;
 
 				for (std::size_t h = 0; h < 3; ++h)
 				{
@@ -2311,7 +2225,7 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 			}
 			// Merge the first and last row vertices into one single point
 			{
-				PatchControlIter pCtrl = m_ctrl.begin();
+				PatchControlIter pCtrl = _ctrl.begin();
 
 				for (std::size_t w = 0; w < 9; ++w, ++pCtrl)
 				{
@@ -2321,7 +2235,7 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 				}
 			}
 			{
-				PatchControlIter pCtrl = m_ctrl.begin() + (9*4);
+				PatchControlIter pCtrl = _ctrl.begin() + (9*4);
 
 				for (std::size_t w = 0; w < 9; ++w, ++pCtrl)
 				{
@@ -2356,1507 +2270,198 @@ void Patch::ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType view
 	NaturalTexture();
 }
 
-#define DEGEN_0a  0x01
-#define DEGEN_1a  0x02
-#define DEGEN_2a  0x04
-#define DEGEN_0b  0x08
-#define DEGEN_1b  0x10
-#define DEGEN_2b  0x20
-#define SPLIT     0x40
-#define AVERAGE   0x80
-
-
-unsigned int subarray_get_degen(PatchControlIter subarray, std::size_t strideU, std::size_t strideV)
-{
-  unsigned int nDegen = 0;
-  PatchControlIter p1;
-  PatchControlIter p2;
-
-  p1 = subarray;
-  p2 = p1 + strideU;
-  if(p1->vertex == p2->vertex)
-    nDegen |= DEGEN_0a;
-  p1 = p2;
-  p2 = p1 + strideU;
-  if(p1->vertex == p2->vertex)
-    nDegen |= DEGEN_0b;
-
-  p1 = subarray + strideV;
-  p2 = p1 + strideU;
-  if(p1->vertex == p2->vertex)
-    nDegen |= DEGEN_1a;
-  p1 = p2;
-  p2 = p1 + strideU;
-  if(p1->vertex == p2->vertex)
-    nDegen |= DEGEN_1b;
-
-  p1 = subarray + (strideV << 1);
-  p2 = p1 + strideU;
-  if(p1->vertex == p2->vertex)
-    nDegen |= DEGEN_2a;
-  p1 = p2;
-  p2 = p1 + strideU;
-  if(p1->vertex == p2->vertex)
-    nDegen |= DEGEN_2b;
-
-  return nDegen;
-}
-
-
-inline void deCasteljau3(const Vector3& P0, const Vector3& P1, const Vector3& P2, Vector3& P01, Vector3& P12, Vector3& P012)
-{
-  P01 = P0.mid(P1);
-  P12 = P1.mid(P2);
-  P012 = P01.mid(P12);
-}
-
-inline void BezierInterpolate3( const Vector3& start, Vector3& left, Vector3& mid, Vector3& right, const Vector3& end )
-{
-  left = start.mid(mid);
-  right = mid.mid(end);
-  mid = left.mid(right);
-}
-
-inline void BezierInterpolate2( const Vector2& start, Vector2& left, Vector2& mid, Vector2& right, const Vector2& end )
-{
-  left[0]= float_mid(start[0], mid[0]);
-  left[1] = float_mid(start[1], mid[1]);
-  right[0] = float_mid(mid[0], end[0]);
-  right[1] = float_mid(mid[1], end[1]);
-  mid[0] = float_mid(left[0], right[0]);
-  mid[1] = float_mid(left[1], right[1]);
-}
-
-#include "math/curve.h"
-
-inline PatchControl QuadraticBezier_evaluate(const PatchControl* firstPoint, double t)
+Vector3 getAverageNormal(const Vector3& normal1, const Vector3& normal2, double thickness)
 {
-  PatchControl result = { Vector3(0, 0, 0), Vector2(0, 0) };
-  double denominator = 0;
+	// Beware of normals with 0 length
+	if (normal1.getLengthSquared() == 0) return normal2;
+	if (normal2.getLengthSquared() == 0) return normal1;
 
-  {
-    double weight = BernsteinPolynomial<Zero, Two>::apply(t);
-    result.vertex += firstPoint[0].vertex * weight;
-    result.texcoord += firstPoint[0].texcoord * weight;
-    denominator += weight;
-  }
-  {
-    double weight = BernsteinPolynomial<One, Two>::apply(t);
-    result.vertex += firstPoint[1].vertex * weight;
-    result.texcoord += firstPoint[1].texcoord * weight;
-    denominator += weight;
-  }
-  {
-    double weight = BernsteinPolynomial<Two, Two>::apply(t);
-    result.vertex  += firstPoint[2].vertex * weight;
-    result.texcoord += firstPoint[2].texcoord * weight;
-    denominator += weight;
-  }
+	// Both normals have length > 0
+	Vector3 n1 = normal1.getNormalised();
+	Vector3 n2 = normal2.getNormalised();
 
-  result.vertex /= denominator;
-  result.texcoord /= denominator;
-  return result;
-}
+	// Get the angle bisector
+	Vector3 normal = (n1 + n2).getNormalised();
 
-inline Vector3 vector3_linear_interpolated(const Vector3& a, const Vector3& b, double t)
-{
-  return a*(1.0 - t) + b*t;
-}
+	// Now calculate the length correction out of the angle
+	// of the two normals
+	float factor = cos(n1.angle(n2) * 0.5);
 
-inline Vector2 vector2_linear_interpolated(const Vector2& a, const Vector2& b, double t)
-{
-  return a*(1.0 - t) + b*t;
-}
+	// Stretch the normal to fit the required thickness
+	normal *= thickness;
 
-void normalise_safe(Vector3& normal)
-{
-	if (normal != g_vector3_identity)
+	// Check for div by zero (if the normals are antiparallel)
+	// and stretch the resulting normal, if necessary
+	if (factor != 0)
 	{
-		normal.normalise();
+		normal /= factor;
 	}
-}
 
-inline void QuadraticBezier_evaluate(const PatchControl& a, const PatchControl& b, const PatchControl& c, double t, PatchControl& point, PatchControl& left, PatchControl& right)
-{
-  left.vertex = vector3_linear_interpolated(a.vertex, b.vertex, t);
-  left.texcoord = vector2_linear_interpolated(a.texcoord, b.texcoord, t);
-  right.vertex = vector3_linear_interpolated(b.vertex, c.vertex, t);
-  right.texcoord = vector2_linear_interpolated(b.texcoord, c.texcoord, t);
-  point.vertex = vector3_linear_interpolated(left.vertex, right.vertex, t);
-  point.texcoord = vector2_linear_interpolated(left.texcoord, right.texcoord, t);
+	return normal;
 }
 
-void Patch::TesselateSubMatrixFixed(ArbitraryMeshVertex* vertices,
-									std::size_t strideX, std::size_t strideY,
-									unsigned int nFlagsX, unsigned int nFlagsY,
-									PatchControlIter subMatrix[3][3])
+void Patch::createThickenedOpposite(const Patch& sourcePatch,
+									const float thickness,
+									const int axis)
 {
-  double incrementU = 1.0 / m_subdivisions_x;
-  double incrementV = 1.0 / m_subdivisions_y;
-  const std::size_t width = m_subdivisions_x + 1;
-  const std::size_t height = m_subdivisions_y + 1;
-
-  for(std::size_t i = 0; i != width; ++i)
-  {
-    double tU = (i + 1 == width) ? 1 : i * incrementU;
-    PatchControl pointX[3];
-    PatchControl leftX[3];
-    PatchControl rightX[3];
-    QuadraticBezier_evaluate(*subMatrix[0][0], *subMatrix[0][1], *subMatrix[0][2], tU, pointX[0], leftX[0], rightX[0]);
-    QuadraticBezier_evaluate(*subMatrix[1][0], *subMatrix[1][1], *subMatrix[1][2], tU, pointX[1], leftX[1], rightX[1]);
-    QuadraticBezier_evaluate(*subMatrix[2][0], *subMatrix[2][1], *subMatrix[2][2], tU, pointX[2], leftX[2], rightX[2]);
-
-    ArbitraryMeshVertex* p = vertices + i * strideX;
-    for(std::size_t j = 0; j != height; ++j)
-    {
-      if((j == 0 || j + 1 == height) && (i == 0 || i + 1 == width))
-      {
-      }
-      else
-      {
-        double tV = (j + 1 == height) ? 1 : j * incrementV;
-
-        PatchControl pointY[3];
-        PatchControl leftY[3];
-        PatchControl rightY[3];
-        QuadraticBezier_evaluate(*subMatrix[0][0], *subMatrix[1][0], *subMatrix[2][0], tV, pointY[0], leftY[0], rightY[0]);
-        QuadraticBezier_evaluate(*subMatrix[0][1], *subMatrix[1][1], *subMatrix[2][1], tV, pointY[1], leftY[1], rightY[1]);
-        QuadraticBezier_evaluate(*subMatrix[0][2], *subMatrix[1][2], *subMatrix[2][2], tV, pointY[2], leftY[2], rightY[2]);
-
-        PatchControl point;
-        PatchControl left;
-        PatchControl right;
-        QuadraticBezier_evaluate(pointX[0], pointX[1], pointX[2], tV, point, left, right);
-        PatchControl up;
-        PatchControl down;
-        QuadraticBezier_evaluate(pointY[0], pointY[1], pointY[2], tU, point, up, down);
-
-        p->vertex = Vertex3f(point.vertex);
-        p->texcoord = point.texcoord;
-
-        ArbitraryMeshVertex a, b, c;
-
-        a.vertex = Vertex3f(left.vertex);
-        a.texcoord = left.texcoord;
-        b.vertex = Vertex3f(right.vertex);
-        b.texcoord = right.texcoord;
-
-        if(i != 0)
-        {
-          c.vertex = Vertex3f(up.vertex);
-          c.texcoord = up.texcoord;
-        }
-        else
-        {
-          c.vertex = Vertex3f(down.vertex);
-          c.texcoord = down.texcoord;
-        }
+	// Clone the dimensions from the other patch
+	setDims(sourcePatch.getWidth(), sourcePatch.getHeight());
 
-        Vector3 normal = (right.vertex - left.vertex).crossProduct(up.vertex - down.vertex).getNormalised();
+	// Also inherit the tesselation from the source patch
+	setFixedSubdivisions(sourcePatch.subdivisionsFixed(), sourcePatch.getSubdivisions());
 
-        Vector3 tangent, bitangent;
-        ArbitraryMeshTriangle_calcTangents(a, b, c, tangent, bitangent);
-        tangent = tangent.getNormalised();
-        bitangent = bitangent.getNormalised();
+	// Copy the shader from the source patch
+	setShader(sourcePatch.getShader());
 
-        if(((nFlagsX & AVERAGE) != 0 && i == 0) || ((nFlagsY & AVERAGE) != 0  && j == 0))
-        {
-          p->normal = Normal3f(p->normal + normal.getNormalised());
-          p->tangent = Normal3f(p->tangent + tangent.getNormalised());
-          p->bitangent = Normal3f((p->bitangent + bitangent).getNormalised());
-        }
-        else
-        {
-          p->normal = Normal3f(normal);
-          p->tangent = Normal3f(tangent);
-          p->bitangent = Normal3f(bitangent);
-        }
-      }
+	// if extrudeAxis == 0,0,0 the patch is extruded along its vertex normals
+	Vector3 extrudeAxis(0,0,0);
 
-      p += strideY;
-    }
-  }
-}
+	switch (axis) {
+		case 0: // X-Axis
+			extrudeAxis = Vector3(1,0,0);
+			break;
+		case 1: // Y-Axis
+			extrudeAxis = Vector3(0,1,0);
+			break;
+		case 2: // Z-Axis
+			extrudeAxis = Vector3(0,0,1);
+			break;
+		default:
+			// Default value already set during initialisation
+			break;
+	}
 
-void Patch::TesselateSubMatrix( const BezierCurveTree *BX, const BezierCurveTree *BY,
-                                        std::size_t offStartX, std::size_t offStartY,
-                                        std::size_t offEndX, std::size_t offEndY,
-                                        std::size_t nFlagsX, std::size_t nFlagsY,
-                                        Vector3& left, Vector3& mid, Vector3& right,
-                                        Vector2& texLeft, Vector2& texMid, Vector2& texRight,
-                                        bool bTranspose )
-{
-  int newFlagsX, newFlagsY;
+	for (std::size_t col = 0; col < _width; col++)
+	{
+		for (std::size_t row = 0; row < _height; row++)
+		{
+			// The current control vertex on the other patch
+			const PatchControl& curCtrl = sourcePatch.ctrlAt(row, col);
 
-  Vector3 tmp;
-  Vector3 vertex_0_0, vertex_0_1, vertex_1_0, vertex_1_1, vertex_2_0, vertex_2_1;
-  Vector2 texTmp;
-  Vector2 texcoord_0_0, texcoord_0_1, texcoord_1_0, texcoord_1_1, texcoord_2_0, texcoord_2_1;
+			Vector3 normal;
 
-  {
-   // texcoords
+			// Are we extruding along vertex normals (i.e. extrudeAxis == 0,0,0)?
+			if (extrudeAxis == Vector3(0,0,0))
+			{
+				// The col tangents (empty if 0,0,0)
+				Vector3 colTangent[2] = { Vector3(0,0,0), Vector3(0,0,0) };
 
-    BezierInterpolate2( _mesh.vertices[offStartX + offStartY].texcoord,
-                     texcoord_0_0,
-                     _mesh.vertices[BX->index + offStartY].texcoord,
-                     texcoord_0_1,
-                     _mesh.vertices[offEndX + offStartY].texcoord);
+				// Are we at the beginning/end of the column?
+				if (col == 0 || col == _width - 1)
+				{
+					// Get the next row index
+					std::size_t nextCol = (col == _width - 1) ? (col - 1) : (col + 1);
 
+					const PatchControl& colNeighbour = sourcePatch.ctrlAt(row, nextCol);
 
-    BezierInterpolate2( _mesh.vertices[offStartX + offEndY].texcoord,
-                     texcoord_2_0,
-                     _mesh.vertices[BX->index + offEndY].texcoord,
-                     texcoord_2_1,
-                     _mesh.vertices[offEndX + offEndY].texcoord);
+					// One available tangent
+					colTangent[0] = colNeighbour.vertex - curCtrl.vertex;
+					// Reverse it if we're at the end of the column
+					colTangent[0] *= (col == _width - 1) ? -1 : +1;
+				}
+				// We are in between, two tangents can be calculated
+				else
+				{
+					// Take two neighbouring vertices that should form a line segment
+					const PatchControl& neighbour1 = sourcePatch.ctrlAt(row, col+1);
+					const PatchControl& neighbour2 = sourcePatch.ctrlAt(row, col-1);
 
-    texTmp = texMid;
+					// Calculate both available tangents
+					colTangent[0] = neighbour1.vertex - curCtrl.vertex;
+					colTangent[1] = neighbour2.vertex - curCtrl.vertex;
 
-    BezierInterpolate2(texLeft,
-                      texcoord_1_0,
-                      texTmp,
-                      texcoord_1_1,
-                      texRight);
+					// Reverse the second one
+					colTangent[1] *= -1;
 
-	if(!BY->isLeaf())
-    {
-      _mesh.vertices[BX->index + BY->index].texcoord = texTmp;
-    }
+					// Cull redundant tangents
+					if (colTangent[1].isParallel(colTangent[0]))
+					{
+						colTangent[1] = Vector3(0,0,0);
+					}
+				}
 
+				// Calculate the tangent vectors to the next row
+				Vector3 rowTangent[2] = { Vector3(0,0,0), Vector3(0,0,0) };
 
-	if(!BX->left->isLeaf())
-    {
-      _mesh.vertices[BX->left->index + offStartY].texcoord = texcoord_0_0;
-      _mesh.vertices[BX->left->index + offEndY].texcoord = texcoord_2_0;
+				// Are we at the beginning or the end?
+				if (row == 0 || row == _height - 1)
+				{
+					// Yes, only calculate one row tangent
+					// Get the next row index
+					std::size_t nextRow = (row == _height - 1) ? (row - 1) : (row + 1);
 
-	  if(!BY->isLeaf())
-      {
-        _mesh.vertices[BX->left->index + BY->index].texcoord = texcoord_1_0;
-      }
-    }
-	if(!BX->right->isLeaf())
-    {
-      _mesh.vertices[BX->right->index + offStartY].texcoord = texcoord_0_1;
-      _mesh.vertices[BX->right->index + offEndY].texcoord = texcoord_2_1;
+					const PatchControl& rowNeighbour = sourcePatch.ctrlAt(nextRow, col);
 
-	  if(!BY->isLeaf())
-      {
-        _mesh.vertices[BX->right->index + BY->index].texcoord = texcoord_1_1;
-      }
-    }
+					// First tangent
+					rowTangent[0] = rowNeighbour.vertex - curCtrl.vertex;
+					// Reverse it accordingly
+					rowTangent[0] *= (row == _height - 1) ? -1 : +1;
+				}
+				else
+				{
+					// Two tangents to calculate
+					const PatchControl& rowNeighbour1 = sourcePatch.ctrlAt(row + 1, col);
+					const PatchControl& rowNeighbour2 = sourcePatch.ctrlAt(row - 1, col);
 
+					// First tangent
+					rowTangent[0] = rowNeighbour1.vertex - curCtrl.vertex;
+					rowTangent[1] = rowNeighbour2.vertex - curCtrl.vertex;
 
-    // verts
+					// Reverse the second one
+					rowTangent[1] *= -1;
 
-    BezierInterpolate3( _mesh.vertices[offStartX + offStartY].vertex,
-                     vertex_0_0,
-                     _mesh.vertices[BX->index + offStartY].vertex,
-                     vertex_0_1,
-                     _mesh.vertices[offEndX + offStartY].vertex);
+					// Cull redundant tangents
+					if (rowTangent[1].isParallel(rowTangent[0]))
+					{
+						rowTangent[1] = Vector3(0,0,0);
+					}
+				}
 
+				// If two column tangents are available, take the length-corrected average
+				if (colTangent[1].getLengthSquared() > 0)
+				{
+					// Two column normals to calculate
+					Vector3 normal1 = rowTangent[0].crossProduct(colTangent[0]).getNormalised();
+					Vector3 normal2 = rowTangent[0].crossProduct(colTangent[1]).getNormalised();
 
-    BezierInterpolate3( _mesh.vertices[offStartX + offEndY].vertex,
-                     vertex_2_0,
-                     _mesh.vertices[BX->index + offEndY].vertex,
-                     vertex_2_1,
-                     _mesh.vertices[offEndX + offEndY].vertex);
+					normal = getAverageNormal(normal1, normal2, thickness);
 
+					// Scale the normal down, as it is multiplied with thickness later on
+					normal /= thickness;
+				}
+				else
+				{
+					// One column tangent available, maybe we have a second rowtangent?
+					if (rowTangent[1].getLengthSquared() > 0)
+					{
+						// Two row normals to calculate
+						Vector3 normal1 = rowTangent[0].crossProduct(colTangent[0]).getNormalised();
+						Vector3 normal2 = rowTangent[1].crossProduct(colTangent[0]).getNormalised();
 
-    tmp = mid;
+						normal = getAverageNormal(normal1, normal2, thickness);
 
-    BezierInterpolate3( left,
-                     vertex_1_0,
-                     tmp,
-                     vertex_1_1,
-                     right );
+						// Scale the normal down, as it is multiplied with thickness later on
+						normal /= thickness;
+					}
+					else
+					{
+						normal = rowTangent[0].crossProduct(colTangent[0]);
+						
+						if (normal.getLengthSquared() > 0)
+						{
+							normal.normalise();
+						}
+					}
+				}
+			}
+			else
+			{
+				// Take the predefined extrude direction instead
+				normal = extrudeAxis;
+			}
 
-	if(!BY->isLeaf())
-    {
-      _mesh.vertices[BX->index + BY->index].vertex = tmp;
-    }
-
-
-	if(!BX->left->isLeaf())
-    {
-      _mesh.vertices[BX->left->index + offStartY].vertex = vertex_0_0;
-      _mesh.vertices[BX->left->index + offEndY].vertex = vertex_2_0;
-
-	  if(!BY->isLeaf())
-      {
-        _mesh.vertices[BX->left->index + BY->index].vertex = vertex_1_0;
-      }
-    }
-	if(!BX->right->isLeaf())
-    {
-      _mesh.vertices[BX->right->index + offStartY].vertex = vertex_0_1;
-      _mesh.vertices[BX->right->index + offEndY].vertex = vertex_2_1;
-
-	  if(!BY->isLeaf())
-      {
-        _mesh.vertices[BX->right->index + BY->index].vertex = vertex_1_1;
-      }
-    }
-
-    // normals
-
-    if(nFlagsX & SPLIT)
-    {
-      ArbitraryMeshVertex a, b, c;
-      Vector3 tangentU;
-
-      if(!(nFlagsX & DEGEN_0a) || !(nFlagsX & DEGEN_0b))
-      {
-        tangentU = vertex_0_1 - vertex_0_0;
-        a.vertex = Vertex3f(vertex_0_0);
-        a.texcoord = texcoord_0_0;
-        c.vertex = Vertex3f(vertex_0_1);
-        c.texcoord = texcoord_0_1;
-      }
-      else if(!(nFlagsX & DEGEN_1a) || !(nFlagsX & DEGEN_1b))
-      {
-        tangentU = vertex_1_1 - vertex_1_0;
-        a.vertex = Vertex3f(vertex_1_0);
-        a.texcoord = texcoord_1_0;
-        c.vertex = Vertex3f(vertex_1_1);
-        c.texcoord = texcoord_1_1;
-      }
-      else
-      {
-        tangentU = vertex_2_1 - vertex_2_0;
-        a.vertex = Vertex3f(vertex_2_0);
-        a.texcoord = texcoord_2_0;
-        c.vertex = Vertex3f(vertex_2_1);
-        c.texcoord = texcoord_2_1;
-      }
-
-      Vector3 tangentV;
-
-      if((nFlagsY & DEGEN_0a) && (nFlagsY & DEGEN_1a) && (nFlagsY & DEGEN_2a))
-      {
-        tangentV = _mesh.vertices[BX->index + offEndY].vertex - tmp;
-        b.vertex = Vertex3f(tmp);//_mesh.vertices[BX->index + offEndY].vertex;
-        b.texcoord = texTmp;//_mesh.vertices[BX->index + offEndY].texcoord;
-      }
-      else
-      {
-        tangentV = tmp - _mesh.vertices[BX->index + offStartY].vertex;
-        b.vertex = Vertex3f(tmp);//_mesh.vertices[BX->index + offStartY].vertex;
-        b.texcoord = texTmp; //_mesh.vertices[BX->index + offStartY].texcoord;
-      }
-
-
-      Vector3 normal, s, t;
-      ArbitraryMeshVertex& v = _mesh.vertices[offStartY + BX->index];
-      Vector3& p = v.normal;
-      Vector3& ps = v.tangent;
-      Vector3& pt = v.bitangent;
-
-      if(bTranspose)
-      {
-        normal = tangentV.crossProduct(tangentU);
-      }
-      else
-      {
-        normal = tangentU.crossProduct(tangentV);
-      }
-      normalise_safe(normal);
-
-      ArbitraryMeshTriangle_calcTangents(a, b, c, s, t);
-      normalise_safe(s);
-      normalise_safe(t);
-
-      if(nFlagsX & AVERAGE)
-      {
-        p = (p + normal).getNormalised();
-        ps = (ps + s).getNormalised();
-        pt = (pt + t).getNormalised();
-      }
-      else
-      {
-        p = normal;
-        ps = s;
-        pt = t;
-      }
-    }
-
-    {
-      ArbitraryMeshVertex a, b, c;
-      Vector3 tangentU;
-
-      if(!(nFlagsX & DEGEN_2a) || !(nFlagsX & DEGEN_2b))
-      {
-        tangentU = vertex_2_1 - vertex_2_0;
-        a.vertex = Vertex3f(vertex_2_0);
-        a.texcoord = texcoord_2_0;
-        c.vertex = Vertex3f(vertex_2_1);
-        c.texcoord = texcoord_2_1;
-      }
-      else if(!(nFlagsX & DEGEN_1a) || !(nFlagsX & DEGEN_1b))
-      {
-        tangentU = vertex_1_1 - vertex_1_0;
-        a.vertex = Vertex3f(vertex_1_0);
-        a.texcoord = texcoord_1_0;
-        c.vertex = Vertex3f(vertex_1_1);
-        c.texcoord = texcoord_1_1;
-      }
-      else
-      {
-        tangentU = vertex_0_1 - vertex_0_0;
-        a.vertex = Vertex3f(vertex_0_0);
-        a.texcoord = texcoord_0_0;
-        c.vertex = Vertex3f(vertex_0_1);
-        c.texcoord = texcoord_0_1;
-      }
-
-      Vector3 tangentV;
-
-      if((nFlagsY & DEGEN_0b) && (nFlagsY & DEGEN_1b) && (nFlagsY & DEGEN_2b))
-      {
-        tangentV = tmp - _mesh.vertices[BX->index + offStartY].vertex;
-        b.vertex = Vertex3f(tmp);//_mesh.vertices[BX->index + offStartY].vertex;
-        b.texcoord = texTmp;//_mesh.vertices[BX->index + offStartY].texcoord;
-      }
-      else
-      {
-        tangentV = _mesh.vertices[BX->index + offEndY].vertex - tmp;
-        b.vertex = Vertex3f(tmp);//_mesh.vertices[BX->index + offEndY].vertex;
-        b.texcoord = texTmp;//_mesh.vertices[BX->index + offEndY].texcoord;
-      }
-
-      ArbitraryMeshVertex& v = _mesh.vertices[offEndY+BX->index];
-      Vector3& p = v.normal;
-      Vector3& ps = v.tangent;
-      Vector3& pt = v.bitangent;
-
-      if(bTranspose)
-      {
-        p = tangentV.crossProduct(tangentU);
-      }
-      else
-      {
-        p = tangentU.crossProduct(tangentV);
-      }
-      normalise_safe(p);
-
-      ArbitraryMeshTriangle_calcTangents(a, b, c, ps, pt);
-      normalise_safe(ps);
-      normalise_safe(pt);
-    }
-  }
-
-
-  newFlagsX = newFlagsY = 0;
-
-  if((nFlagsX & DEGEN_0a) && (nFlagsX & DEGEN_0b))
-  {
-    newFlagsX |= DEGEN_0a;
-    newFlagsX |= DEGEN_0b;
-  }
-  if((nFlagsX & DEGEN_1a) && (nFlagsX & DEGEN_1b))
-  {
-    newFlagsX |= DEGEN_1a;
-    newFlagsX |= DEGEN_1b;
-  }
-  if((nFlagsX & DEGEN_2a) && (nFlagsX & DEGEN_2b))
-  {
-    newFlagsX |= DEGEN_2a;
-    newFlagsX |= DEGEN_2b;
-  }
-  if((nFlagsY & DEGEN_0a) && (nFlagsY & DEGEN_1a) && (nFlagsY & DEGEN_2a))
-  {
-    newFlagsY |= DEGEN_0a;
-    newFlagsY |= DEGEN_1a;
-    newFlagsY |= DEGEN_2a;
-  }
-  if((nFlagsY & DEGEN_0b) && (nFlagsY & DEGEN_1b) && (nFlagsY & DEGEN_2b))
-  {
-    newFlagsY |= DEGEN_0b;
-    newFlagsY |= DEGEN_1b;
-    newFlagsY |= DEGEN_2b;
-  }
-
-
-  //if((nFlagsX & DEGEN_0a) && (nFlagsX & DEGEN_1a) && (nFlagsX & DEGEN_2a)) { newFlagsX |= DEGEN_0a; newFlagsX |= DEGEN_1a; newFlagsX |= DEGEN_2a; }
-  //if((nFlagsX & DEGEN_0b) && (nFlagsX & DEGEN_1b) && (nFlagsX & DEGEN_2b)) { newFlagsX |= DEGEN_0b; newFlagsX |= DEGEN_1b; newFlagsX |= DEGEN_2b; }
-
-  newFlagsX |= (nFlagsX & SPLIT);
-  newFlagsX |= (nFlagsX & AVERAGE);
-
-  if(!BY->isLeaf())
-  {
-    {
-      int nTemp = newFlagsY;
-
-      if((nFlagsY & DEGEN_0a) && (nFlagsY & DEGEN_0b))
-      {
-        newFlagsY |= DEGEN_0a;
-        newFlagsY |= DEGEN_0b;
-      }
-      newFlagsY |= (nFlagsY & SPLIT);
-      newFlagsY |= (nFlagsY & AVERAGE);
-
-      Vector3& p = _mesh.vertices[BX->index+BY->index].vertex;
-      Vector3 vTemp(p);
-
-      Vector2& p2 = _mesh.vertices[BX->index+BY->index].texcoord;
-      Vector2 stTemp(p2);
-
-      TesselateSubMatrix( BY, BX->left,
-                          offStartY, offStartX,
-                          offEndY, BX->index,
-                          newFlagsY, newFlagsX,
-                          vertex_0_0, vertex_1_0, vertex_2_0,
-                          texcoord_0_0, texcoord_1_0, texcoord_2_0,
-                          !bTranspose );
-
-      newFlagsY = nTemp;
-      p = vTemp;
-      p2 = stTemp;
-    }
-
-    if((nFlagsY & DEGEN_2a) && (nFlagsY & DEGEN_2b)) { newFlagsY |= DEGEN_2a; newFlagsY |= DEGEN_2b; }
-
-    TesselateSubMatrix( BY, BX->right,
-                        offStartY, BX->index,
-                        offEndY, offEndX,
-                        newFlagsY, newFlagsX,
-                        vertex_0_1, vertex_1_1, vertex_2_1,
-                        texcoord_0_1, texcoord_1_1, texcoord_2_1,
-                        !bTranspose );
-  }
-  else
-  {
-	  if(!BX->left->isLeaf())
-    {
-      TesselateSubMatrix( BX->left,  BY,
-                          offStartX, offStartY,
-                          BX->index, offEndY,
-                          newFlagsX, newFlagsY,
-                          left, vertex_1_0, tmp,
-                          texLeft, texcoord_1_0, texTmp,
-                          bTranspose );
-    }
-
-	  if(!BX->right->isLeaf())
-    {
-      TesselateSubMatrix( BX->right, BY,
-                          BX->index, offStartY,
-                          offEndX, offEndY,
-                          newFlagsX, newFlagsY,
-                          tmp, vertex_1_1, right,
-                          texTmp, texcoord_1_1, texRight,
-                          bTranspose );
-    }
-  }
-
-}
-
-void Patch::BuildTesselationCurves(EMatrixMajor major)
-{
-  std::size_t nArrayStride, length, cross, strideU, strideV;
-  switch(major)
-  {
-  case ROW:
-    nArrayStride = 1;
-    length = (m_width - 1) >> 1;
-    cross = m_height;
-    strideU = 1;
-    strideV = m_width;
-
-    if(!m_patchDef3)
-    {
-      BezierCurveTreeArray_deleteAll(_mesh.curveTreeU);
-    }
-
-    break;
-  case COL:
-    nArrayStride = _mesh.m_nArrayWidth;
-    length = (m_height - 1) >> 1;
-    cross = m_width;
-    strideU = m_width;
-    strideV = 1;
-
-    if(!m_patchDef3)
-    {
-      BezierCurveTreeArray_deleteAll(_mesh.curveTreeV);
-    }
-
-    break;
-  default:
-    ERROR_MESSAGE("neither row-major nor column-major");
-    return;
-  }
-
-	std::vector<std::size_t> arrayLength;
-	std::vector<BezierCurveTree*> pCurveTree(length);
-
-	std::size_t nArrayLength = 1;
-
-	if (m_patchDef3)
-	{
-		// This is a patch using fixed tesselation (patchDef3)
-		std::size_t subdivisions = (major == ROW) ? m_subdivisions_x : m_subdivisions_y;
-
-		// Assign the fixed number of tesselations to each column
-		arrayLength.resize(length, subdivisions);
-		nArrayLength += subdivisions * length;
-	}
-	else
-	{
-		// Resize the array and calculate the values
-		arrayLength.resize(length);
-
-		// create a list of the horizontal control curves in each column of sub-patches
-		// adaptively tesselate each horizontal control curve in the list
-		// create a binary tree representing the combined tesselation of the list
-		for (std::size_t i = 0; i != length; ++i)
-		{
-			PatchControlIter p1 = m_ctrlTransformed.begin() + (i * 2 * strideU);
-
-			BezierCurveList curveList;
-
-			for (std::size_t j = 0; j < cross; j += 2)
-			{
-				// directly taken from one row of control points
-				{
-					BezierCurve* pCurve = new BezierCurve(
-						(p1+strideU)->vertex,		// crd
-						p1->vertex,					// left
-						(p1+(strideU<<1))->vertex	// right
-					);
-
-					curveList.push_front(pCurve);
-				}
-
-				// Skip the rest if this is the last turn
-				if (j+2 >= cross) break;
-
-				PatchControlIter p2 = p1 + strideV;
-				PatchControlIter p3 = p2 + strideV;
-
-				// interpolated from three columns of control points
-				{
-					BezierCurve* pCurve = new BezierCurve(
-						(p1+strideU)->vertex.mid((p3+strideU)->vertex),			// crd
-						p1->vertex.mid(p3->vertex),								// left
-						(p1+(strideU<<1))->vertex.mid((p3+(strideU<<1))->vertex)	// right
-					);
-
-					pCurve->crd = pCurve->crd.mid((p2+strideU)->vertex);
-					pCurve->left = pCurve->left.mid(p2->vertex);
-					pCurve->right = pCurve->right.mid((p2+(strideU<<1))->vertex);
-
-					curveList.push_front(pCurve);
-				}
-
-				p1 = p3;
-			}
-
-			// Sort the curve list into a BezierCurveTree
-			pCurveTree[i] = new BezierCurveTree;
-
-			BezierCurveTree_FromCurveList(pCurveTree[i], curveList);
-
-			// The curve list is not needed anymore, free it
-			std::for_each(curveList.begin(), curveList.end(), [] (BezierCurve* curve)
-			{
-				delete curve;
-			});
-
-			curveList.clear();
-
-			// set up array indices for binary tree
-			// accumulate subarray width
-			std::size_t l = pCurveTree[i]->setup(nArrayLength, nArrayStride) - (nArrayLength - 1);
-			arrayLength[i] = l;
-
-			// accumulate total array width
-			nArrayLength += l;
-		}
-	}
-
-  switch(major)
-  {
-  case ROW:
-    _mesh.m_nArrayWidth = nArrayLength;
-    std::swap(_mesh.arrayWidth, arrayLength);
-
-    if(!m_patchDef3)
-    {
-      std::swap(_mesh.curveTreeU, pCurveTree);
-    }
-    break;
-  case COL:
-    _mesh.m_nArrayHeight = nArrayLength;
-    std::swap(_mesh.arrayHeight, arrayLength);
-
-    if(!m_patchDef3)
-    {
-      std::swap(_mesh.curveTreeV, pCurveTree);
-    }
-    break;
-  }
-}
-
-inline void vertex_assign_ctrl(ArbitraryMeshVertex& vertex, const PatchControl& ctrl)
-{
-  vertex.vertex = Vertex3f(ctrl.vertex);
-  vertex.texcoord = ctrl.texcoord;
-}
-
-inline void vertex_clear_normal(ArbitraryMeshVertex& vertex)
-{
-  vertex.normal = Normal3f(0, 0, 0);
-  vertex.tangent = Normal3f(0, 0, 0);
-  vertex.bitangent = Normal3f(0, 0, 0);
-}
-
-inline void tangents_remove_degenerate(Vector3 tangents[6], Vector2 textureTangents[6], unsigned int flags)
-{
-  if(flags & DEGEN_0a)
-  {
-    const std::size_t i =
-      (flags & DEGEN_0b)
-      ? (flags & DEGEN_1a)
-        ? (flags & DEGEN_1b)
-          ? (flags & DEGEN_2a)
-            ? 5
-            : 4
-          : 3
-        : 2
-      : 1;
-    tangents[0] = tangents[i];
-    textureTangents[0] = textureTangents[i];
-  }
-  if(flags & DEGEN_0b)
-  {
-    const std::size_t i =
-      (flags & DEGEN_0a)
-      ? (flags & DEGEN_1b)
-        ? (flags & DEGEN_1a)
-          ? (flags & DEGEN_2b)
-            ? 4
-            : 5
-          : 2
-        : 3
-      : 0;
-    tangents[1] = tangents[i];
-    textureTangents[1] = textureTangents[i];
-  }
-  if(flags & DEGEN_2a)
-  {
-    const std::size_t i =
-      (flags & DEGEN_2b)
-      ? (flags & DEGEN_1a)
-        ? (flags & DEGEN_1b)
-          ? (flags & DEGEN_0a)
-            ? 1
-            : 0
-          : 3
-        : 2
-      : 5;
-    tangents[4] = tangents[i];
-    textureTangents[4] = textureTangents[i];
-  }
-  if(flags & DEGEN_2b)
-  {
-    const std::size_t i =
-      (flags & DEGEN_2a)
-      ? (flags & DEGEN_1b)
-        ? (flags & DEGEN_1a)
-          ? (flags & DEGEN_0b)
-            ? 0
-            : 1
-          : 2
-        : 3
-      : 4;
-    tangents[5] = tangents[i];
-    textureTangents[5] = textureTangents[i];
-  }
-}
-
-void bestTangents00(unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1)
-{
-  if(fabs(dot + length) < 0.001) // opposing direction = degenerate
-  {
-    if(!(degenerateFlags & DEGEN_1a)) // if this tangent is degenerate we cannot use it
-    {
-      index0 = 2;
-      index1 = 0;
-    }
-    else if(!(degenerateFlags & DEGEN_0b))
-    {
-      index0 = 0;
-      index1 = 1;
-    }
-    else
-    {
-      index0 = 1;
-      index1 = 0;
-    }
-  }
-  else if(fabs(dot - length) < 0.001) // same direction = degenerate
-  {
-    if(degenerateFlags & DEGEN_0b)
-    {
-      index0 = 0;
-      index1 = 1;
-    }
-    else
-    {
-      index0 = 1;
-      index1 = 0;
-    }
-  }
-}
-
-void bestTangents01(unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1)
-{
-  if(fabs(dot - length) < 0.001) // same direction = degenerate
-  {
-    if(!(degenerateFlags & DEGEN_1a)) // if this tangent is degenerate we cannot use it
-    {
-      index0 = 2;
-      index1 = 1;
-    }
-    else if(!(degenerateFlags & DEGEN_2b))
-    {
-      index0 = 4;
-      index1 = 0;
-    }
-    else
-    {
-      index0 = 5;
-      index1 = 1;
-    }
-  }
-  else if(fabs(dot + length) < 0.001) // opposing direction = degenerate
-  {
-    if(degenerateFlags & DEGEN_2b)
-    {
-      index0 = 4;
-      index1 = 0;
-    }
-    else
-    {
-      index0 = 5;
-      index1 = 1;
-    }
-  }
-}
-
-void bestTangents10(unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1)
-{
-  if(fabs(dot - length) < 0.001) // same direction = degenerate
-  {
-    if(!(degenerateFlags & DEGEN_1b)) // if this tangent is degenerate we cannot use it
-    {
-      index0 = 3;
-      index1 = 4;
-    }
-    else if(!(degenerateFlags & DEGEN_0a))
-    {
-      index0 = 1;
-      index1 = 5;
-    }
-    else
-    {
-      index0 = 0;
-      index1 = 4;
-    }
-  }
-  else if(fabs(dot + length) < 0.001) // opposing direction = degenerate
-  {
-    if(degenerateFlags & DEGEN_0a)
-    {
-      index0 = 1;
-      index1 = 5;
-    }
-    else
-    {
-      index0 = 0;
-      index1 = 4;
-    }
-  }
-}
-
-void bestTangents11(unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1)
-{
-  if(fabs(dot + length) < 0.001) // opposing direction = degenerate
-  {
-    if(!(degenerateFlags & DEGEN_1b)) // if this tangent is degenerate we cannot use it
-    {
-      index0 = 3;
-      index1 = 5;
-    }
-    else if(!(degenerateFlags & DEGEN_2a))
-    {
-      index0 = 5;
-      index1 = 4;
-    }
-    else
-    {
-      index0 = 4;
-      index1 = 5;
-    }
-  }
-  else if(fabs(dot - length) < 0.001) // same direction = degenerate
-  {
-    if(degenerateFlags & DEGEN_2a)
-    {
-      index0 = 5;
-      index1 = 4;
-    }
-    else
-    {
-      index0 = 4;
-      index1 = 5;
-    }
-  }
-}
-
-void Patch::accumulateVertexTangentSpace(std::size_t index, Vector3 tangentX[6], Vector3 tangentY[6], Vector2 tangentS[6], Vector2 tangentT[6], std::size_t index0, std::size_t index1)
-{
-  {
-    Vector3 normal(tangentX[index0].crossProduct(tangentY[index1]));
-    if(normal != g_vector3_identity)
-    {
-      _mesh.vertices[index].normal += normal.getNormalised();
-    }
-  }
-
-  {
-    ArbitraryMeshVertex a, b, c;
-    a.vertex = Vertex3f(0, 0, 0);
-    a.texcoord = TexCoord2f(0, 0);
-    b.vertex = Vertex3f(tangentX[index0]);
-    b.texcoord = tangentS[index0];
-    c.vertex = Vertex3f(tangentY[index1]);
-    c.texcoord = tangentT[index1];
-
-    Vector3 s, t;
-    ArbitraryMeshTriangle_calcTangents(a, b, c, s, t);
-    if(s != g_vector3_identity)
-    {
-		_mesh.vertices[index].tangent += s.getNormalised();
-    }
-    if(t != g_vector3_identity)
-    {
-		_mesh.vertices[index].bitangent += t.getNormalised();
-    }
-  }
-}
-
-const std::size_t PATCH_MAX_VERTEX_ARRAY = 1048576;
-
-void Patch::BuildVertexArray()
-{
-  const std::size_t strideU = 1;
-  const std::size_t strideV = m_width;
-
-  const std::size_t numElems = _mesh.m_nArrayWidth*_mesh.m_nArrayHeight; // total number of elements in vertex array
-
-  const bool bWidthStrips = (_mesh.m_nArrayWidth >= _mesh.m_nArrayHeight); // decide if horizontal strips are longer than vertical
-
-
-  // allocate vertex, normal, texcoord and primitive-index arrays
-  _mesh.vertices.resize(numElems);
-  _mesh.indices.resize(_mesh.m_nArrayWidth *2 * (_mesh.m_nArrayHeight - 1));
-
-  // set up strip indices
-  if(bWidthStrips)
-  {
-    _mesh.m_numStrips = _mesh.m_nArrayHeight-1;
-    _mesh.m_lenStrips = _mesh.m_nArrayWidth*2;
-
-    for(std::size_t i=0; i<_mesh.m_nArrayWidth; i++)
-    {
-      for(std::size_t j=0; j<_mesh.m_numStrips; j++)
-      {
-        _mesh.indices[(j*_mesh.m_lenStrips)+i*2] = RenderIndex(j*_mesh.m_nArrayWidth+i);
-        _mesh.indices[(j*_mesh.m_lenStrips)+i*2+1] = RenderIndex((j+1)*_mesh.m_nArrayWidth+i);
-        // reverse because radiant uses CULL_FRONT
-        //_mesh.indices[(j*_mesh.m_lenStrips)+i*2+1] = RenderIndex(j*_mesh.m_nArrayWidth+i);
-        //_mesh.indices[(j*_mesh.m_lenStrips)+i*2] = RenderIndex((j+1)*_mesh.m_nArrayWidth+i);
-      }
-    }
-  }
-  else
-  {
-    _mesh.m_numStrips = _mesh.m_nArrayWidth-1;
-    _mesh.m_lenStrips = _mesh.m_nArrayHeight*2;
-
-    for(std::size_t i=0; i<_mesh.m_nArrayHeight; i++)
-    {
-      for(std::size_t j=0; j<_mesh.m_numStrips; j++)
-      {
-        _mesh.indices[(j*_mesh.m_lenStrips)+i*2] = RenderIndex(((_mesh.m_nArrayHeight-1)-i)*_mesh.m_nArrayWidth+j);
-        _mesh.indices[(j*_mesh.m_lenStrips)+i*2+1] = RenderIndex(((_mesh.m_nArrayHeight-1)-i)*_mesh.m_nArrayWidth+j+1);
-        // reverse because radiant uses CULL_FRONT
-        //_mesh.indices[(j*_mesh.m_lenStrips)+i*2+1] = RenderIndex(((_mesh.m_nArrayHeight-1)-i)*_mesh.m_nArrayWidth+j);
-        //_mesh.indices[(j*_mesh.m_lenStrips)+i*2] = RenderIndex(((_mesh.m_nArrayHeight-1)-i)*_mesh.m_nArrayWidth+j+1);
-
-      }
-    }
-  }
-
-  {
-    PatchControlIter pCtrl = m_ctrlTransformed.begin();
-    for(std::size_t j = 0, offStartY = 0; j+1 < m_height; j += 2, pCtrl += (strideU + strideV))
-    {
-      // set up array offsets for this sub-patch
-		const bool leafY = (m_patchDef3) ? false : _mesh.curveTreeV[j>>1]->isLeaf();
-      const std::size_t offMidY = (m_patchDef3) ? 0 : _mesh.curveTreeV[j>>1]->index;
-      const std::size_t widthY = _mesh.arrayHeight[j>>1] * _mesh.m_nArrayWidth;
-      const std::size_t offEndY = offStartY + widthY;
-
-      for(std::size_t i = 0, offStartX = 0; i+1 < m_width; i += 2, pCtrl += (strideU << 1))
-      {
-		  const bool leafX = (m_patchDef3) ? false : _mesh.curveTreeU[i>>1]->isLeaf();
-        const std::size_t offMidX = (m_patchDef3) ? 0 : _mesh.curveTreeU[i>>1]->index;
-        const std::size_t widthX = _mesh.arrayWidth[i>>1];
-        const std::size_t offEndX = offStartX + widthX;
-
-        PatchControlIter subMatrix[3][3];
-        subMatrix[0][0] = pCtrl;
-        subMatrix[0][1] = subMatrix[0][0]+strideU;
-        subMatrix[0][2] = subMatrix[0][1]+strideU;
-        subMatrix[1][0] = subMatrix[0][0]+strideV;
-        subMatrix[1][1] = subMatrix[1][0]+strideU;
-        subMatrix[1][2] = subMatrix[1][1]+strideU;
-        subMatrix[2][0] = subMatrix[1][0]+strideV;
-        subMatrix[2][1] = subMatrix[2][0]+strideU;
-        subMatrix[2][2] = subMatrix[2][1]+strideU;
-
-        // assign on-patch control points to vertex array
-        if(i == 0 && j == 0)
-        {
-          vertex_clear_normal(_mesh.vertices[offStartX + offStartY]);
-        }
-        vertex_assign_ctrl(_mesh.vertices[offStartX + offStartY], *subMatrix[0][0]);
-        if(j == 0)
-        {
-          vertex_clear_normal(_mesh.vertices[offEndX + offStartY]);
-        }
-        vertex_assign_ctrl(_mesh.vertices[offEndX + offStartY], *subMatrix[0][2]);
-        if(i == 0)
-        {
-          vertex_clear_normal(_mesh.vertices[offStartX + offEndY]);
-        }
-        vertex_assign_ctrl(_mesh.vertices[offStartX + offEndY], *subMatrix[2][0]);
-
-        vertex_clear_normal(_mesh.vertices[offEndX + offEndY]);
-        vertex_assign_ctrl(_mesh.vertices[offEndX + offEndY], *subMatrix[2][2]);
-
-        if(!m_patchDef3)
-        {
-          // assign remaining control points to vertex array
-          if(!leafX)
-          {
-            vertex_assign_ctrl(_mesh.vertices[offMidX + offStartY], *subMatrix[0][1]);
-            vertex_assign_ctrl(_mesh.vertices[offMidX + offEndY], *subMatrix[2][1]);
-          }
-          if(!leafY)
-          {
-            vertex_assign_ctrl(_mesh.vertices[offStartX + offMidY], *subMatrix[1][0]);
-            vertex_assign_ctrl(_mesh.vertices[offEndX + offMidY], *subMatrix[1][2]);
-
-            if(!leafX)
-            {
-              vertex_assign_ctrl(_mesh.vertices[offMidX + offMidY], *subMatrix[1][1]);
-            }
-          }
-        }
-
-        // test all 12 edges for degeneracy
-        unsigned int nFlagsX = subarray_get_degen(pCtrl, strideU, strideV);
-        unsigned int nFlagsY = subarray_get_degen(pCtrl, strideV, strideU);
-        Vector3 tangentX[6], tangentY[6];
-        Vector2 tangentS[6], tangentT[6];
-
-        // set up tangents for each of the 12 edges if they were not degenerate
-        if(!(nFlagsX & DEGEN_0a))
-        {
-          tangentX[0] = subMatrix[0][1]->vertex - subMatrix[0][0]->vertex;
-          tangentS[0] = subMatrix[0][1]->texcoord - subMatrix[0][0]->texcoord;
-        }
-        if(!(nFlagsX & DEGEN_0b))
-        {
-          tangentX[1] = subMatrix[0][2]->vertex - subMatrix[0][1]->vertex;
-          tangentS[1] = subMatrix[0][2]->texcoord - subMatrix[0][1]->texcoord;
-        }
-        if(!(nFlagsX & DEGEN_1a))
-        {
-          tangentX[2] = subMatrix[1][1]->vertex - subMatrix[1][0]->vertex;
-          tangentS[2] = subMatrix[1][1]->texcoord - subMatrix[1][0]->texcoord;
-        }
-        if(!(nFlagsX & DEGEN_1b))
-        {
-          tangentX[3] = subMatrix[1][2]->vertex - subMatrix[1][1]->vertex;
-          tangentS[3] = subMatrix[1][2]->texcoord - subMatrix[1][1]->texcoord;
-        }
-        if(!(nFlagsX & DEGEN_2a))
-        {
-          tangentX[4] = subMatrix[2][1]->vertex - subMatrix[2][0]->vertex;
-          tangentS[4] = subMatrix[2][1]->texcoord - subMatrix[2][0]->texcoord;
-        }
-        if(!(nFlagsX & DEGEN_2b))
-        {
-          tangentX[5] = subMatrix[2][2]->vertex - subMatrix[2][1]->vertex;
-          tangentS[5] = subMatrix[2][2]->texcoord - subMatrix[2][1]->texcoord;
-        }
-
-        if(!(nFlagsY & DEGEN_0a))
-        {
-          tangentY[0] = subMatrix[1][0]->vertex - subMatrix[0][0]->vertex;
-          tangentT[0] = subMatrix[1][0]->texcoord - subMatrix[0][0]->texcoord;
-        }
-        if(!(nFlagsY & DEGEN_0b))
-        {
-          tangentY[1] = subMatrix[2][0]->vertex - subMatrix[1][0]->vertex;
-          tangentT[1] = subMatrix[2][0]->texcoord - subMatrix[1][0]->texcoord;
-        }
-        if(!(nFlagsY & DEGEN_1a))
-        {
-          tangentY[2] = subMatrix[1][1]->vertex - subMatrix[0][1]->vertex;
-          tangentT[2] = subMatrix[1][1]->texcoord - subMatrix[0][1]->texcoord;
-        }
-        if(!(nFlagsY & DEGEN_1b))
-        {
-          tangentY[3] = subMatrix[2][1]->vertex - subMatrix[1][1]->vertex;
-          tangentT[3] = subMatrix[2][1]->texcoord - subMatrix[1][1]->texcoord;
-        }
-        if(!(nFlagsY & DEGEN_2a))
-        {
-          tangentY[4] = subMatrix[1][2]->vertex - subMatrix[0][2]->vertex;
-          tangentT[4] = subMatrix[1][2]->texcoord - subMatrix[0][2]->texcoord;
-        }
-        if(!(nFlagsY & DEGEN_2b))
-        {
-          tangentY[5] = subMatrix[2][2]->vertex - subMatrix[1][2]->vertex;
-          tangentT[5] = subMatrix[2][2]->texcoord - subMatrix[1][2]->texcoord;
-        }
-
-        // set up remaining edge tangents by borrowing the tangent from the closest parallel non-degenerate edge
-        tangents_remove_degenerate(tangentX, tangentS, nFlagsX);
-        tangents_remove_degenerate(tangentY, tangentT, nFlagsY);
-
-        {
-          // x=0, y=0
-          std::size_t index = offStartX + offStartY;
-          std::size_t index0 = 0;
-          std::size_t index1 = 0;
-
-          double dot = tangentX[index0].dot(tangentY[index1]);
-          double length = tangentX[index0].getLength() * tangentY[index1].getLength();
-
-          bestTangents00(nFlagsX, dot, length, index0, index1);
-
-          accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
-        }
-
-        {
-          // x=1, y=0
-          std::size_t index = offEndX + offStartY;
-          std::size_t index0 = 1;
-          std::size_t index1 = 4;
-
-          double dot = tangentX[index0].dot(tangentY[index1]);
-          double length = tangentX[index0].getLength() * tangentY[index1].getLength();
-
-          bestTangents10(nFlagsX, dot, length, index0, index1);
-
-          accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
-        }
-
-        {
-          // x=0, y=1
-          std::size_t index = offStartX + offEndY;
-          std::size_t index0 = 4;
-          std::size_t index1 = 1;
-
-          double dot = tangentX[index0].dot(tangentY[index1]);
-          double length = tangentX[index1].getLength() * tangentY[index1].getLength();
-
-          bestTangents01(nFlagsX, dot, length, index0, index1);
-
-          accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
-        }
-
-        {
-          // x=1, y=1
-          std::size_t index = offEndX + offEndY;
-          std::size_t index0 = 5;
-          std::size_t index1 = 5;
-
-          double dot = tangentX[index0].dot(tangentY[index1]);
-          double length = tangentX[index0].getLength() * tangentY[index1].getLength();
-
-          bestTangents11(nFlagsX, dot, length, index0, index1);
-
-          accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
-        }
-
-        //normalise normals that won't be accumulated again
-        if(i!=0 || j!=0)
-        {
-			normalise_safe(_mesh.vertices[offStartX + offStartY].normal);
-			normalise_safe(_mesh.vertices[offStartX + offStartY].tangent);
-			normalise_safe(_mesh.vertices[offStartX + offStartY].bitangent);
-        }
-        if(i+3 == m_width)
-        {
-			normalise_safe(_mesh.vertices[offEndX + offStartY].normal);
-			normalise_safe(_mesh.vertices[offEndX + offStartY].tangent);
-			normalise_safe(_mesh.vertices[offEndX + offStartY].bitangent);
-        }
-        if(j+3 == m_height)
-        {
-			normalise_safe(_mesh.vertices[offStartX + offEndY].normal);
-			normalise_safe(_mesh.vertices[offStartX + offEndY].tangent);
-			normalise_safe(_mesh.vertices[offStartX + offEndY].bitangent);
-        }
-        if(i+3 == m_width && j+3 == m_height)
-        {
-			normalise_safe(_mesh.vertices[offEndX + offEndY].normal);
-			normalise_safe(_mesh.vertices[offEndX + offEndY].tangent);
-			normalise_safe(_mesh.vertices[offEndX + offEndY].bitangent);
-        }
-
-        // set flags to average normals between shared edges
-        if(j != 0)
-        {
-          nFlagsX |= AVERAGE;
-        }
-        if(i != 0)
-        {
-          nFlagsY |= AVERAGE;
-        }
-        // set flags to save evaluating shared edges twice
-        nFlagsX |= SPLIT;
-        nFlagsY |= SPLIT;
-
-        // if the patch is curved.. tesselate recursively
-        // use the relevant control curves for this sub-patch
-        if(m_patchDef3)
-        {
-          TesselateSubMatrixFixed(&_mesh.vertices[offStartX + offStartY], 1, _mesh.m_nArrayWidth, nFlagsX, nFlagsY, subMatrix);
-        }
-        else
-        {
-          if(!leafX)
-          {
-            TesselateSubMatrix( _mesh.curveTreeU[i>>1], _mesh.curveTreeV[j>>1],
-                                offStartX, offStartY, offEndX, offEndY, // array offsets
-                                nFlagsX, nFlagsY,
-                                subMatrix[1][0]->vertex, subMatrix[1][1]->vertex, subMatrix[1][2]->vertex,
-                                subMatrix[1][0]->texcoord, subMatrix[1][1]->texcoord, subMatrix[1][2]->texcoord,
-                                false );
-          }
-          else if(!leafY)
-          {
-            TesselateSubMatrix( _mesh.curveTreeV[j>>1], _mesh.curveTreeU[i>>1],
-                                offStartY, offStartX, offEndY, offEndX, // array offsets
-                                nFlagsY, nFlagsX,
-                                subMatrix[0][1]->vertex, subMatrix[1][1]->vertex, subMatrix[2][1]->vertex,
-                                subMatrix[0][1]->texcoord, subMatrix[1][1]->texcoord, subMatrix[2][1]->texcoord,
-                                true );
-          }
-        }
-
-        offStartX = offEndX;
-      }
-      offStartY = offEndY;
-    }
-  }
-}
-
-Vector3 getAverageNormal(const Vector3& normal1, const Vector3& normal2, double thickness)
-{
-	// Beware of normals with 0 length
-	if (normal1.getLengthSquared() == 0) return normal2;
-	if (normal2.getLengthSquared() == 0) return normal1;
-
-	// Both normals have length > 0
-	Vector3 n1 = normal1.getNormalised();
-	Vector3 n2 = normal2.getNormalised();
-
-	// Get the angle bisector
-	Vector3 normal = (n1 + n2).getNormalised();
-
-	// Now calculate the length correction out of the angle
-	// of the two normals
-	float factor = cos(n1.angle(n2) * 0.5);
-
-	// Stretch the normal to fit the required thickness
-	normal *= thickness;
-
-	// Check for div by zero (if the normals are antiparallel)
-	// and stretch the resulting normal, if necessary
-	if (factor != 0)
-	{
-		normal /= factor;
-	}
-
-	return normal;
-}
-
-void Patch::createThickenedOpposite(const Patch& sourcePatch,
-									const float thickness,
-									const int axis)
-{
-	// Clone the dimensions from the other patch
-	setDims(sourcePatch.getWidth(), sourcePatch.getHeight());
-
-	// Also inherit the tesselation from the source patch
-	setFixedSubdivisions(sourcePatch.subdivionsFixed(), sourcePatch.getSubdivisions());
-
-	// Copy the shader from the source patch
-	setShader(sourcePatch.getShader());
-
-	// if extrudeAxis == 0,0,0 the patch is extruded along its vertex normals
-	Vector3 extrudeAxis(0,0,0);
-
-	switch (axis) {
-		case 0: // X-Axis
-			extrudeAxis = Vector3(1,0,0);
-			break;
-		case 1: // Y-Axis
-			extrudeAxis = Vector3(0,1,0);
-			break;
-		case 2: // Z-Axis
-			extrudeAxis = Vector3(0,0,1);
-			break;
-		default:
-			// Default value already set during initialisation
-			break;
-	}
-
-	for (std::size_t col = 0; col < m_width; col++)
-	{
-		for (std::size_t row = 0; row < m_height; row++)
-		{
-			// The current control vertex on the other patch
-			const PatchControl& curCtrl = sourcePatch.ctrlAt(row, col);
-
-			Vector3 normal;
-
-			// Are we extruding along vertex normals (i.e. extrudeAxis == 0,0,0)?
-			if (extrudeAxis == Vector3(0,0,0))
-			{
-				// The col tangents (empty if 0,0,0)
-				Vector3 colTangent[2] = { Vector3(0,0,0), Vector3(0,0,0) };
-
-				// Are we at the beginning/end of the column?
-				if (col == 0 || col == m_width - 1)
-				{
-					// Get the next row index
-					std::size_t nextCol = (col == m_width - 1) ? (col - 1) : (col + 1);
-
-					const PatchControl& colNeighbour = sourcePatch.ctrlAt(row, nextCol);
-
-					// One available tangent
-					colTangent[0] = colNeighbour.vertex - curCtrl.vertex;
-					// Reverse it if we're at the end of the column
-					colTangent[0] *= (col == m_width - 1) ? -1 : +1;
-				}
-				// We are in between, two tangents can be calculated
-				else
-				{
-					// Take two neighbouring vertices that should form a line segment
-					const PatchControl& neighbour1 = sourcePatch.ctrlAt(row, col+1);
-					const PatchControl& neighbour2 = sourcePatch.ctrlAt(row, col-1);
-
-					// Calculate both available tangents
-					colTangent[0] = neighbour1.vertex - curCtrl.vertex;
-					colTangent[1] = neighbour2.vertex - curCtrl.vertex;
-
-					// Reverse the second one
-					colTangent[1] *= -1;
-
-					// Cull redundant tangents
-					if (colTangent[1].isParallel(colTangent[0]))
-					{
-						colTangent[1] = Vector3(0,0,0);
-					}
-				}
-
-				// Calculate the tangent vectors to the next row
-				Vector3 rowTangent[2] = { Vector3(0,0,0), Vector3(0,0,0) };
-
-				// Are we at the beginning or the end?
-				if (row == 0 || row == m_height - 1)
-				{
-					// Yes, only calculate one row tangent
-					// Get the next row index
-					std::size_t nextRow = (row == m_height - 1) ? (row - 1) : (row + 1);
-
-					const PatchControl& rowNeighbour = sourcePatch.ctrlAt(nextRow, col);
-
-					// First tangent
-					rowTangent[0] = rowNeighbour.vertex - curCtrl.vertex;
-					// Reverse it accordingly
-					rowTangent[0] *= (row == m_height - 1) ? -1 : +1;
-				}
-				else
-				{
-					// Two tangents to calculate
-					const PatchControl& rowNeighbour1 = sourcePatch.ctrlAt(row + 1, col);
-					const PatchControl& rowNeighbour2 = sourcePatch.ctrlAt(row - 1, col);
-
-					// First tangent
-					rowTangent[0] = rowNeighbour1.vertex - curCtrl.vertex;
-					rowTangent[1] = rowNeighbour2.vertex - curCtrl.vertex;
-
-					// Reverse the second one
-					rowTangent[1] *= -1;
-
-					// Cull redundant tangents
-					if (rowTangent[1].isParallel(rowTangent[0]))
-					{
-						rowTangent[1] = Vector3(0,0,0);
-					}
-				}
-
-				// If two column tangents are available, take the length-corrected average
-				if (colTangent[1].getLengthSquared() > 0)
-				{
-					// Two column normals to calculate
-					Vector3 normal1 = rowTangent[0].crossProduct(colTangent[0]).getNormalised();
-					Vector3 normal2 = rowTangent[0].crossProduct(colTangent[1]).getNormalised();
-
-					normal = getAverageNormal(normal1, normal2, thickness);
-
-					// Scale the normal down, as it is multiplied with thickness later on
-					normal /= thickness;
-				}
-				else
-				{
-					// One column tangent available, maybe we have a second rowtangent?
-					if (rowTangent[1].getLengthSquared() > 0)
-					{
-						// Two row normals to calculate
-						Vector3 normal1 = rowTangent[0].crossProduct(colTangent[0]).getNormalised();
-						Vector3 normal2 = rowTangent[1].crossProduct(colTangent[0]).getNormalised();
-
-						normal = getAverageNormal(normal1, normal2, thickness);
-
-						// Scale the normal down, as it is multiplied with thickness later on
-						normal /= thickness;
-					}
-					else
-					{
-						normal = rowTangent[0].crossProduct(colTangent[0]).getNormalised();
-					}
-				}
-			}
-			else
-			{
-				// Take the predefined extrude direction instead
-				normal = extrudeAxis;
-			}
-
-			// Store the new coordinates into this patch at the current coords
-			ctrlAt(row, col).vertex = curCtrl.vertex + normal*thickness;
+			// Store the new coordinates into this patch at the current coords
+			ctrlAt(row, col).vertex = curCtrl.vertex + normal*thickness;
 
 			// Clone the texture cooordinates of the source patch
 			ctrlAt(row, col).texcoord = curCtrl.texcoord;
@@ -3888,7 +2493,7 @@ void Patch::createThickenedWall(const Patch& sourcePatch,
 	int sourceWidth = static_cast<int>(sourcePatch.getWidth());
 	int sourceHeight = static_cast<int>(sourcePatch.getHeight());
 
-	bool sourceTesselationFixed = sourcePatch.subdivionsFixed();
+	bool sourceTesselationFixed = sourcePatch.subdivisionsFixed();
 	Subdivisions sourceTesselationX(sourcePatch.getSubdivisions().x(), 1);
 	Subdivisions sourceTesselationY(sourcePatch.getSubdivisions().y(), 1);
 
@@ -3959,8 +2564,8 @@ void Patch::stitchTextureFrom(Patch& sourcePatch) {
 	undoSave();
 
 	// Convert the size_t stuff into int, because we need it for signed comparisons
-	int patchHeight = static_cast<int>(m_height);
-	int patchWidth = static_cast<int>(m_width);
+	int patchHeight = static_cast<int>(_height);
+	int patchWidth = static_cast<int>(_width);
 
 	// Calculate the nearest corner vertex of this patch (to the sourcepatch vertices)
 	PatchControlIter nearestControl = getClosestPatchControlToPatch(sourcePatch);
@@ -3980,7 +2585,7 @@ void Patch::stitchTextureFrom(Patch& sourcePatch) {
 
 	// Now shift all the texture vertices in the right direction, so that this patch
 	// is getting as close as possible to the origin in texture space.
-	for (PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
+	for (PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i) {
 		i->texcoord += shift;
 	}
 
@@ -4020,10 +2625,10 @@ void Patch::normaliseTexture() {
 	// Find the nearest control vertex
 
 	// Initialise the compare value
-	PatchControlIter nearestControl = m_ctrl.begin();
+	PatchControlIter nearestControl = _ctrl.begin();
 
 	// Cycle through all the control points with an iterator
-	for (PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
+	for (PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i) {
 		// Take the according value (e.g. s = x, t = y, depending on the nAxis argument)
 		// and apply the appropriate texture coordinate
 		if (i->texcoord.getLength() < nearestControl->texcoord.getLength()) {
@@ -4049,7 +2654,7 @@ void Patch::normaliseTexture() {
 
 		// Now shift all the texture vertices in the right direction, so that this patch
 		// is getting as close as possible to the origin in texture space.
-		for (PatchControlIter i = m_ctrl.begin(); i != m_ctrl.end(); ++i) {
+		for (PatchControlIter i = _ctrl.begin(); i != _ctrl.end(); ++i) {
 			i->texcoord += shift;
 		}
 
@@ -4058,29 +2663,34 @@ void Patch::normaliseTexture() {
 	}
 }
 
-Subdivisions Patch::getSubdivisions() const {
-	return Subdivisions(m_subdivisions_x, m_subdivisions_y);
+const Subdivisions& Patch::getSubdivisions() const 
+{
+	return _subDivisions;
 }
 
-void Patch::setFixedSubdivisions(bool isFixed, const Subdivisions& divisions) {
+void Patch::setFixedSubdivisions(bool isFixed, const Subdivisions& divisions)
+{
 	undoSave();
 
-	m_patchDef3 = isFixed;
-	m_subdivisions_x = divisions.x();
-	m_subdivisions_y = divisions.y();
+	_patchDef3 = isFixed;
+	_subDivisions = divisions;
 
-	if (m_subdivisions_x == 0) {
-		m_subdivisions_x = 4;
+	if (_subDivisions.x() == 0)
+	{
+		_subDivisions.x() = 4;
 	}
-	else if (m_subdivisions_x > MAX_PATCH_SUBDIVISIONS) {
-		m_subdivisions_x = MAX_PATCH_SUBDIVISIONS;
+	else if (_subDivisions.x() > MAX_PATCH_SUBDIVISIONS)
+	{
+		_subDivisions.x() = MAX_PATCH_SUBDIVISIONS;
 	}
 
-	if (m_subdivisions_y == 0) {
-		m_subdivisions_y = 4;
+	if (_subDivisions.y() == 0)
+	{
+		_subDivisions.y() = 4;
 	}
-	else if (m_subdivisions_y > MAX_PATCH_SUBDIVISIONS) {
-		m_subdivisions_y = MAX_PATCH_SUBDIVISIONS;
+	else if (_subDivisions.y() > MAX_PATCH_SUBDIVISIONS)
+	{
+		_subDivisions.y() = MAX_PATCH_SUBDIVISIONS;
 	}
 
 	SceneChangeNotify();
@@ -4088,8 +2698,9 @@ void Patch::setFixedSubdivisions(bool isFixed, const Subdivisions& divisions) {
 	controlPointsChanged();
 }
 
-bool Patch::subdivionsFixed() const {
-	return m_patchDef3;
+bool Patch::subdivisionsFixed() const
+{
+	return _patchDef3;
 }
 
 bool Patch::getIntersection(const Ray& ray, Vector3& intersection)
@@ -4097,11 +2708,11 @@ bool Patch::getIntersection(const Ray& ray, Vector3& intersection)
 	std::vector<RenderIndex>::const_iterator stripStartIndex = _mesh.indices.begin();
 
 	// Go over each quad strip and intersect the ray with its triangles
-	for (std::size_t strip = 0; strip < _mesh.m_numStrips; ++strip)
+	for (std::size_t strip = 0; strip < _mesh.numStrips; ++strip)
 	{
 		// Iterate over the indices. The +2 increment will lead up to the next quad
 		for (std::vector<RenderIndex>::const_iterator indexIter = stripStartIndex;
-			indexIter + 2 < stripStartIndex + _mesh.m_lenStrips; indexIter += 2)
+			indexIter + 2 < stripStartIndex + _mesh.lenStrips; indexIter += 2)
 		{
 			Vector3 triangleIntersection;
 
@@ -4131,7 +2742,7 @@ bool Patch::getIntersection(const Ray& ray, Vector3& intersection)
 			}
 		}
 
-		stripStartIndex += _mesh.m_lenStrips;
+		stripStartIndex += _mesh.lenStrips;
 	}
 
 	return false;
diff --git a/radiant/patch/Patch.h b/radiant/patch/Patch.h
index c79bd8a..24cb36b 100644
--- a/radiant/patch/Patch.h
+++ b/radiant/patch/Patch.h
@@ -18,6 +18,9 @@
 #include "brush/FacePlane.h"
 #include "brush/Face.h"
 
+// Enable to render the vertex normal/tangent/bitangent vectors in the cam view
+#define DEBUG_PATCH_NTB_VECTORS 0
+
 class PatchNode;
 class Ray;
 
@@ -38,18 +41,18 @@ class Patch :
 	typedef std::set<IPatch::Observer*> Observers;
 	Observers _observers;
 
-	AABB m_aabb_local; // local bbox
+	AABB _localAABB; // local bbox
 
 	// Patch dimensions
-	std::size_t m_width;
-	std::size_t m_height;
+	std::size_t _width;
+	std::size_t _height;
 
 	IUndoStateSaver* _undoStateSaver;
 
-	// dynamically allocated array of control points, size is m_width*m_height
-	PatchControlArray m_ctrl;				// the true control array
-	PatchControlArray m_ctrlTransformed;	// a temporary control array used during transformations, so that the
-											// changes can be reverted and overwritten by <m_ctrl>
+	// dynamically allocated array of control points, size is _width*_height
+	PatchControlArray _ctrl;			// the true control array
+	PatchControlArray _ctrlTransformed;	// a temporary control array used during transformations, so that the
+										// changes can be reverted and overwritten by <_ctrl>
 
 	// The tesselation for this patch
 	PatchTesselation _mesh;
@@ -58,60 +61,54 @@ class Patch :
 	RenderablePatchSolid _solidRenderable;
 	RenderablePatchWireframe _wireframeRenderable;
 	RenderablePatchFixedWireframe _fixedWireframeRenderable;
+    RenderablePatchVectorsNTB _renderableNTBVectors;
 
 	// The shader states for the control points and the lattice
 	ShaderPtr _pointShader;
 	ShaderPtr _latticeShader;
 
 	// greebo: The vertex list of the control points, can be passed to the RenderableVertexBuffer
-    std::vector<VertexCb> m_ctrl_vertices;
+    std::vector<VertexCb> _ctrl_vertices;
 	// The renderable of the control points
 	RenderableVertexBuffer _renderableCtrlPoints;
 
 	// The lattice indices and their renderable
-	IndexBuffer m_lattice_indices;
+	IndexBuffer _latticeIndices;
 	RenderableIndexBuffer _renderableLattice;
 
-	bool m_bOverlay;
-
-	bool m_transformChanged;
+	bool _transformChanged;
 
 	// TRUE if the patch tesselation needs an update
 	bool _tesselationChanged;
 
-	// Callback functions when the patch gets changed
-	Callback m_evaluateTransform;
-
 	// The rendersystem we're attached to, to acquire materials
 	RenderSystemWeakPtr _renderSystem;
 
     // Shader container, taking care of use count
     SurfaceShader _shader;
 
+	// If true, this patch is using fixed subdivisions
+	bool _patchDef3;
+	
+	// Fixed subdivision layout of this patch
+	Subdivisions _subDivisions;
+
 	// greebo: Initialises the patch member variables
 	void construct();
 
 public:
-	bool m_patchDef3;
-	// The number of subdivisions of this patch
-	std::size_t m_subdivisions_x;
-	std::size_t m_subdivisions_y;
-
-public:
-	static int m_CycleCapIndex;// = 0;
-
 	// Constructor
-	Patch(PatchNode& node, const Callback& evaluateTransform);
+	Patch(PatchNode& node);
 
 	// Copy constructors (create this patch from another patch)
-	Patch(const Patch& other, PatchNode& node, const Callback& evaluateTransform);
+	Patch(const Patch& other, PatchNode& node);
 
 	~Patch();
 
 	PatchNode& getPatchNode();
 
-	void attachObserver(Observer* observer);
-	void detachObserver(Observer* observer);
+	void attachObserver(Observer* observer) override;
+	void detachObserver(Observer* observer) override;
 
 	void connectUndoSystem(IMapFileChangeTracker& changeTracker);
     void disconnectUndoSystem(IMapFileChangeTracker& changeTracker);
@@ -157,51 +154,51 @@ public:
 	void freezeTransform();
 
 	// callback for changed control points
-	void controlPointsChanged();
+	void controlPointsChanged() override;
 
 	// Check if the patch has invalid control points or width/height are zero
-	bool isValid() const;
+	bool isValid() const override;
 
 	// Check whether all control vertices are in the same 3D spot (with minimal tolerance)
-	bool isDegenerate() const;
+	bool isDegenerate() const override;
 
 	// Snaps the control points to the grid
 	void snapto(float snap);
 
 	// Gets the shader name or sets the shader to <name>
-	const std::string& getShader() const;
-	void setShader(const std::string& name);
+	const std::string& getShader() const override;
+	void setShader(const std::string& name) override;
 
     const SurfaceShader& getSurfaceShader() const;
     SurfaceShader& getSurfaceShader();
 
 	// greebo: returns true if the patch's shader is visible, false otherwise
-	bool hasVisibleMaterial() const;
+	bool hasVisibleMaterial() const override;
 
 	// As the name states: get the shader flags of the m_state shader
 	int getShaderFlags() const;
 
 	// Const and non-const iterators
 	PatchControlIter begin() {
-		return m_ctrl.begin();
+		return _ctrl.begin();
 	}
 
 	PatchControlConstIter begin() const {
-		return m_ctrl.begin();
+		return _ctrl.begin();
 	}
 
 	PatchControlIter end() {
-		return m_ctrl.end();
+		return _ctrl.end();
 	}
 
 	PatchControlConstIter end() const {
-		return m_ctrl.end();
+		return _ctrl.end();
 	}
 
 	PatchTesselation& getTesselation();
 
 	// Returns a copy of the tesselated geometry
-	PatchMesh getTesselatedPatchMesh() const;
+	PatchMesh getTesselatedPatchMesh() const override;
 
 	// Get the current control point array
 	PatchControlArray& getControlPoints();
@@ -211,35 +208,35 @@ public:
 	const PatchControlArray& getControlPointsTransformed() const;
 
 	// Set the dimensions of this patch to width <w>, height <h>
-	void setDims(std::size_t w, std::size_t h);
+	void setDims(std::size_t w, std::size_t h) override;
 
 	// Get the patch dimensions
-	std::size_t getWidth() const;
-	std::size_t getHeight() const;
+	std::size_t getWidth() const override;
+	std::size_t getHeight() const override;
 
 	// Return a defined patch control vertex at <row>,<col>
-	PatchControl& ctrlAt(std::size_t row, std::size_t col);
+	PatchControl& ctrlAt(std::size_t row, std::size_t col) override;
 	// The same as above just for const
-	const PatchControl& ctrlAt(std::size_t row, std::size_t col) const;
+	const PatchControl& ctrlAt(std::size_t row, std::size_t col) const override;
 
  	/** greebo: Inserts two columns before and after the column with index <colIndex>.
  	 * 			Throws an GenericPatchException if an error occurs.
  	 */
- 	void insertColumns(std::size_t colIndex);
+ 	void insertColumns(std::size_t colIndex) override;
 
  	/** greebo: Inserts two rows before and after the row with index <rowIndex>.
  	 * 			Throws an GenericPatchException if an error occurs.
  	 */
- 	void insertRows(std::size_t rowIndex);
+ 	void insertRows(std::size_t rowIndex) override;
 
  	/** greebo: Removes columns or rows right before and after the col/row
  	 * 			with the given index, reducing the according dimension by 2.
  	 */
- 	void removePoints(bool columns, std::size_t index);
+ 	void removePoints(bool columns, std::size_t index) override;
 
  	/** greebo: Appends two rows or columns at the beginning or the end.
  	 */
- 	void appendPoints(bool columns, bool beginning);
+ 	void appendPoints(bool columns, bool beginning) override;
 
 	void ConstructPrefab(const AABB& aabb, EPatchPrefab eType, EViewType viewType, std::size_t width = 3, std::size_t height = 3);
 	void constructPlane(const AABB& aabb, int axis, std::size_t width, std::size_t height);
@@ -278,7 +275,6 @@ public:
 	void SetTextureRepeat(float s, float t); // call with s=1 t=1 for FIT
 	void CapTexture();
 	void NaturalTexture();
-	void ProjectTexture(int nAxis);
 
 	// Aligns the patch texture along the given side/border - if possible
 	void alignTexture(EAlignType align);
@@ -340,20 +336,20 @@ public:
 	// Revert the state of this patch to the one that has been saved in the UndoMemento
 	void importState(const IUndoMementoPtr& state);
 
-	/** greebo: Sets/gets whether this patch is a patchDef3 (fixed tesselation)
+	/** greebo: Gets whether this patch is a patchDef3 (fixed tesselation)
 	 */
-	bool subdivionsFixed() const;
+	bool subdivisionsFixed() const override;
 
 	/** greebo: Returns the x,y subdivision values (for tesselation)
 	 */
-	Subdivisions getSubdivisions() const;
+	const Subdivisions& getSubdivisions() const override;
 
 	/** greebo: Sets the subdivision of this patch
 	 *
 	 * @isFixed: TRUE, if this patch should be a patchDef3 (fixed tesselation)
 	 * @divisions: a two-component vector containing the desired subdivisions
 	 */
-	void setFixedSubdivisions(bool isFixed, const Subdivisions& divisions);
+	void setFixedSubdivisions(bool isFixed, const Subdivisions& divisions) override;
 
 	// Calculate the intersection of the given ray with the full patch mesh, 
 	// returns true on intersection and fills in the out variable
@@ -369,23 +365,4 @@ private:
 	void check_shader();
 
 	void updateAABB();
-
-	void TesselateSubMatrixFixed(ArbitraryMeshVertex* vertices,
-								 std::size_t strideX, std::size_t strideY,
-								 unsigned int nFlagsX, unsigned int nFlagsY,
-								 PatchControlIter subMatrix[3][3]);
-
-	// uses binary trees representing bezier curves to recursively tesselate a bezier sub-patch
-	void TesselateSubMatrix( const BezierCurveTree *BX, const BezierCurveTree *BY,
-                           std::size_t offStartX, std::size_t offStartY,
-                           std::size_t offEndX, std::size_t offEndY,
-                           std::size_t nFlagsX, std::size_t nFlagsY,
-                           Vector3& left, Vector3& mid, Vector3& right,
-                           Vector2& texLeft, Vector2& texMid, Vector2& texRight,
-                           bool bTranspose );
-
-	// tesselates the entire surface
-	void BuildTesselationCurves(EMatrixMajor major);
-	void accumulateVertexTangentSpace(std::size_t index, Vector3 tangentX[6], Vector3 tangentY[6], Vector2 tangentS[6], Vector2 tangentT[6], std::size_t index0, std::size_t index1);
-	void BuildVertexArray();
 };
diff --git a/radiant/patch/PatchBezier.cpp b/radiant/patch/PatchBezier.cpp
deleted file mode 100644
index 651aefe..0000000
--- a/radiant/patch/PatchBezier.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-#include "PatchBezier.h"
-
-#include "math/pi.h"
-#include "registry/registry.h"
-
-	namespace {
-		const std::string RKEY_PATCH_SUBDIVIDE_THRESHOLD = "user/ui/patch/subdivideThreshold";
-	}
-
-/* greebo: These are a lot of helper functions related to bezier curves
- */
-
-void BezierInterpolate(BezierCurve *pCurve) {
-	pCurve->left = pCurve->left.mid(pCurve->crd);
-	pCurve->right = pCurve->crd.mid(pCurve->right);
-	pCurve->crd = pCurve->left.mid(pCurve->right);
-}
-
-bool BezierCurve::isCurved() const
-{
-	// Calculate the deltas
-	Vector3 vTemp(right - left);
-	Vector3 v1(crd - left);
-
-	if (v1 == g_vector3_identity || vTemp == v1) // return 0 if 1->2 == 0 or 1->2 == 1->3
-	{
-		return false;
-	}
-
-	Vector3 v2(right - crd);
-
-	v1.normalise();
-	v2.normalise();
-
-	if (v1 == v2)
-	{
-		// All points are on the same line
-		return false;
-	}
-
-	Vector3 v3(vTemp);
-	const double width = v3.getLength();
-
-	v3 *= 1.0 / width;
-
-	if (v1 == v3 && v2 == v3)
-	{
-		return false;
-	}
-
-	// The points are not on the same line, determine the angle
-	const double angle = acos(v1.dot(v2)) / c_pi;
-
-	const double index = width * angle;
-
-	static float subdivideThreshold = registry::getValue<float>(RKEY_PATCH_SUBDIVIDE_THRESHOLD);
-
-	if (index > subdivideThreshold)
-	{
-		return true;
-	}
-
-	return false;
-}
-
-/**
- * greebo: The vertex interpolation works like this:
- *
- * The original segment is LEFT >> CRD >> RIGHT, which will be sudivided into two segments: "left" and "right".
- *
- * The "left" segment will be LEFT >> ip_left >> ip_crd
- * The "right" segment will be ip_crd >> ip_right >> RIGHT
- *
- * In the end, the two segments will still be using the LEFT and RIGHT vertices (which is important as these are the
- * "fixed" control points of the patch, but the CRD one will be disregarded.
-
-   LEFT O
-        |
-        |
-        |
-        |
-        |
-        |
-ip_left X
-        |\
-        | \
-        |  \
-        |   X  ip_crd
-        |    \
-        |     \
-    CRD O------X------O RIGHT
-             ip_right
-*/
-void BezierCurve::interpolate(BezierCurve* leftCurve, BezierCurve* rightCurve) const
-{
-	// The left and right vertices are the anchors
-	leftCurve->left = left;
-	rightCurve->right = right;
-
-	// The mid-point of the current curve
-	leftCurve->crd = left.mid(crd);		// ip_left
-	rightCurve->crd = crd.mid(right);		// ip_right
-	leftCurve->right = rightCurve->left = leftCurve->crd.mid(rightCurve->crd); // ip_crd
-}
-
-std::size_t BezierCurveTree::setup(std::size_t idx, std::size_t stride)
-{
-	if (left != NULL && right != NULL)
-	{
-		idx = left->setup(idx, stride);
-
-		// Store new index
-		index = idx*stride;
-
-		idx++;
-		idx = right->setup(idx, stride);
-	}
-	else
-	{
-		// Either left or right is NULL, assign leaf index
-		index = BEZIERCURVETREE_MAX_INDEX;
-	}
-
-	// idx will be returned unchanged if no children have been setup
-	return idx;
-}
-
-const std::size_t PATCH_MAX_SUBDIVISION_DEPTH = 16;
-
-void BezierCurveTree_FromCurveList(BezierCurveTree *pTree, BezierCurveList& curveList, std::size_t depth)
-{
-	BezierCurveList leftList;
-	BezierCurveList rightList;
-
-	bool listSplit = false;
-
-	// Traverse the list and interpolate all curves which satisfy the "isCurved" condition
-	for (BezierCurveList::iterator l = curveList.begin(); l != curveList.end(); ++l)
-	{
-		BezierCurve* curve = *l;
-
-		if (listSplit || curve->isCurved())
-		{
-			// Set the flag to TRUE to indicate that we already subdivided one part of this list
-			// All other parts will be subdivided too
-			listSplit = true;
-
-			// Split this curve in two, by allocating two new curves
-			BezierCurve* leftCurve = new BezierCurve;
-			BezierCurve* rightCurve = new BezierCurve;
-
-			// Let the current curve submit interpolation data to the newly allocated curves
-			curve->interpolate(leftCurve, rightCurve);
-
-			// Add the new curves to the allocated list
-			leftList.push_front(leftCurve);
-			rightList.push_front(rightCurve);
-		}
-	}
-
-	// If we have a subdivision in this list, enter to the next level of recursion
-	if (!leftList.empty() && !rightList.empty() && depth != PATCH_MAX_SUBDIVISION_DEPTH)
-	{
-		// Allocate two new tree nodes for the left and right part
-		pTree->left = new BezierCurveTree;
-		pTree->right = new BezierCurveTree;
-
-		BezierCurveTree_FromCurveList(pTree->left, leftList, depth + 1);
-		BezierCurveTree_FromCurveList(pTree->right, rightList, depth + 1);
-
-		// Free the left and right lists from this level recursion
-		std::for_each(leftList.begin(), leftList.end(), [] (BezierCurve* curve)
-		{
-			delete curve;
-		});
-
-		std::for_each(rightList.begin(), rightList.end(), [] (BezierCurve* curve)
-		{
-			delete curve;
-		});
-	}
-
-	// If no subdivisions have been calculated, just leave this tree node, children are NULL by default
-}
diff --git a/radiant/patch/PatchBezier.h b/radiant/patch/PatchBezier.h
deleted file mode 100644
index 6978191..0000000
--- a/radiant/patch/PatchBezier.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#pragma once
-
-#include "math/Vector3.h"
-#include <limits>
-#include <vector>
-#include <forward_list>
-
-struct BezierCurve
-{
-	Vector3 crd;
-	Vector3 left;
-	Vector3 right;
-
-	BezierCurve()
-	{}
-
-	BezierCurve(const Vector3& crd_, const Vector3& left_, const Vector3& right_) :
-		crd(crd_),
-		left(left_),
-		right(right_)
-	{}
-
-	// Returns true if the given set of coordinates satisfies the "curved" condition
-	// This is determined by comparing the directions of the various deltas
-	bool isCurved() const;
-
-	// Interpolates the curve values and writes them to <leftCurve> and <rightCurve>
-	// which will form two new (connected) segments interpolating the current one.
-	void interpolate(BezierCurve* leftCurve, BezierCurve* rightCurve) const;
-};
-
-const std::size_t BEZIERCURVETREE_MAX_INDEX = std::numeric_limits<std::size_t>::max();
-
-/**
- * greebo: A BezierCurveTree represents a node in the binary subdivision tree.
- * If left and right are both NULL, this node is a leaf and no further subdivisions
- * are available for this part of the patch. The index variable holds the depth of this node.
- * A leaf carries BEZIERCURVETREE_MAX_INDEX as index.
- */
-class BezierCurveTree
-{
-public:
-	std::size_t index;
-	BezierCurveTree* left;
-	BezierCurveTree* right;
-
-	BezierCurveTree() :
-		left(NULL),
-		right(NULL)
-	{}
-
-	~BezierCurveTree()
-	{
-		delete left;	// it's safe to pass NULL to delete
-		delete right;
-	}
-
-	// Returns TRUE when no more subdivisions are available beyond this depth
-	bool isLeaf() const
-	{
-		return left == NULL && right == NULL;
-	}
-
-	// Sets up the indices of this part of the curve tree
-	// Recursively calls setup() on the children if available
-	// The index is returned unchanged if no children are set up.
-	std::size_t setup(std::size_t idx, std::size_t stride);
-};
-
-typedef std::forward_list<BezierCurve*> BezierCurveList;
-
-void BezierCurveTree_FromCurveList(BezierCurveTree *pTree, BezierCurveList& curveList, std::size_t depth = 0);
-
-void BezierInterpolate(BezierCurve *pCurve);
-
-inline void BezierCurveTreeArray_deleteAll(std::vector<BezierCurveTree*>& curveTrees) {
-  for(std::vector<BezierCurveTree*>::iterator i = curveTrees.begin(); i != curveTrees.end(); ++i) {
-     delete *i;
-  }
-}
diff --git a/radiant/patch/PatchCreators.cpp b/radiant/patch/PatchCreators.cpp
index 3407c24..08fd8f5 100644
--- a/radiant/patch/PatchCreators.cpp
+++ b/radiant/patch/PatchCreators.cpp
@@ -59,8 +59,8 @@ void Doom3PatchCreator::initialiseModule(const ApplicationContext& ctx)
 	registerPatchCommands();
 
 	// Construct and Register the patch-related preferences
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Patch"));
-	page->appendEntry(_("Patch Subdivide Threshold"), RKEY_PATCH_SUBDIVIDE_THRESHOLD);
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Patch"));
+	page.appendEntry(_("Patch Subdivide Threshold"), RKEY_PATCH_SUBDIVIDE_THRESHOLD);
 }
 
 void Doom3PatchCreator::registerPatchCommands()
@@ -92,7 +92,6 @@ void Doom3PatchCreator::registerPatchCommands()
 	GlobalCommandSystem().addCommand("RedisperseCols", selection::algorithm::redispersePatchCols);
 	GlobalCommandSystem().addCommand("MatrixTranspose", selection::algorithm::transposePatch);
 	GlobalCommandSystem().addCommand("CapCurrentCurve", selection::algorithm::capPatch);
-	GlobalCommandSystem().addCommand("CycleCapTexturePatch", selection::algorithm::cyclePatchProjection);
 	GlobalCommandSystem().addCommand("ThickenPatch", selection::algorithm::thickenPatches);
 	GlobalCommandSystem().addCommand("StitchPatchTexture", patch::algorithm::stitchTextures);
 	GlobalCommandSystem().addCommand("BulgePatch", patch::algorithm::bulge);
diff --git a/radiant/patch/PatchModule.cpp b/radiant/patch/PatchModule.cpp
index 3f0f83d..ac96167 100644
--- a/radiant/patch/PatchModule.cpp
+++ b/radiant/patch/PatchModule.cpp
@@ -1,45 +1,6 @@
-/*
-Copyright (C) 2001-2006, William Joseph.
-All Rights Reserved.
-
-This file is part of GtkRadiant.
-
-GtkRadiant is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-GtkRadiant is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GtkRadiant; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "iradiant.h"
-#include "ipatch.h"
-#include "ifilter.h"
-#include "ipreferencesystem.h"
-
-#include "PatchNode.h"
-#include "PatchSceneWalk.h"
 #include "PatchCreators.h"
-
 #include "modulesystem/StaticModule.h"
-#include "modulesystem/ModuleRegistry.h"
 
 // Define the static patchcreator modules
 module::StaticModule<Doom3PatchCreator> doom3PatchDef3Creator;
 module::StaticModule<Doom3PatchDef2Creator> doom3PatchDef2Creator;
-
-// These are the "host" functions containing the static instances of the patch creators.
-PatchCreator& GetDoom3PatchCreator() {
-	return GlobalPatchCreator(DEF3);
-}
-
-PatchCreator& GetDoom3PatchDef2Creator() {
-	return GlobalPatchCreator(DEF2);
-}
diff --git a/radiant/patch/PatchNode.cpp b/radiant/patch/PatchNode.cpp
index abf6737..ea21ebe 100644
--- a/radiant/patch/PatchNode.cpp
+++ b/radiant/patch/PatchNode.cpp
@@ -12,11 +12,10 @@ PatchNode::PatchNode(bool patchDef3) :
 	m_dragPlanes(std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)),
 	m_render_selected(GL_POINTS),
 	m_lightList(&GlobalRenderSystem().attachLitObject(*this)),
-	m_patch(*this,
-			Callback(std::bind(&PatchNode::evaluateTransform, this))), // create the m_patch member with the node parameters
+	m_patch(*this),
     _untransformedOriginChanged(true)
 {
-	m_patch.m_patchDef3 = patchDef3;
+	m_patch.setFixedSubdivisions(patchDef3, Subdivisions(m_patch.getSubdivisions()));
 
 	SelectableNode::setTransformChangedCallback(Callback(std::bind(&PatchNode::lightsChanged, this)));
 }
@@ -37,9 +36,7 @@ PatchNode::PatchNode(const PatchNode& other) :
 	m_dragPlanes(std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)),
 	m_render_selected(GL_POINTS),
 	m_lightList(&GlobalRenderSystem().attachLitObject(*this)),
-	m_patch(other.m_patch,
-			*this,
-			Callback(std::bind(&PatchNode::evaluateTransform, this))), // create the patch out of the <other> one
+	m_patch(other.m_patch, *this), // create the patch out of the <other> one
     _untransformedOriginChanged(true)
 {
 	SelectableNode::setTransformChangedCallback(Callback(std::bind(&PatchNode::lightsChanged, this)));
@@ -175,6 +172,18 @@ void PatchNode::setSelectedComponents(bool select, SelectionSystem::EComponentMo
 	}
 }
 
+void PatchNode::invertSelectedComponents(SelectionSystem::EComponentMode mode)
+{
+	if (mode == SelectionSystem::eVertex)
+	{
+		// Cycle through the transformed patch vertices and set the colour of all selected control vertices to BLUE (hardcoded)
+		for (PatchControlInstance& i : m_ctrl_instances)
+		{
+			i.setSelected(!i.isSelected());
+		}
+	}
+}
+
 void PatchNode::testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) {
 	test.BeginMesh(localToWorld());
 
@@ -216,28 +225,7 @@ bool PatchNode::hasVisibleMaterial() const
 	return m_patch.getSurfaceShader().getGLShader()->getMaterial()->isVisible();
 }
 
-void PatchNode::invertSelected()
-{
-	// Override default behaviour of SelectableNode, we have components
-
-	if (GlobalSelectionSystem().Mode() == SelectionSystem::eComponent)
-	{
-		// Cycle through the transformed patch vertices and set the colour of all selected control vertices to BLUE (hardcoded)
-		PatchControlIter ctrl = m_patch.getControlPointsTransformed().begin();
-
-		for (PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i, ++ctrl)
-		{
-			i->invertSelected();
-		}
-	}
-	else // primitive mode
-	{
-		// Invert the selection of the patch itself
-		SelectableNode::invertSelected();
-	}
-}
-
-void PatchNode::selectedChangedComponent(const Selectable& selectable) {
+void PatchNode::selectedChangedComponent(const ISelectable& selectable) {
 	// Notify the selection system that this PatchNode was selected. The RadiantSelectionSystem adds
 	// this to its internal list of selected nodes.
 	GlobalSelectionSystem().onComponentSelection(SelectableNode::getSelf(), selectable);
@@ -291,7 +279,7 @@ bool PatchNode::intersectsLight(const RendererLight& light) const {
 void PatchNode::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
 {
 	// Don't render invisible shaders
-	if (!m_patch.hasVisibleMaterial()) return;
+	if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return;
 
 	const_cast<Patch&>(m_patch).evaluateTransform();
 	collector.setLights(*m_lightList);
@@ -308,7 +296,7 @@ void PatchNode::renderSolid(RenderableCollector& collector, const VolumeTest& vo
 void PatchNode::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
 {
 	// Don't render invisible shaders
-	if (!m_patch.getSurfaceShader().getGLShader()->getMaterial()->isVisible()) return;
+	if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return;
 
 	const_cast<Patch&>(m_patch).evaluateTransform();
 
@@ -380,16 +368,18 @@ void PatchNode::renderComponentsSelected(RenderableCollector& collector, const V
 	// If there are any selected components, add them to the collector
 	if (!m_render_selected.empty())
     {
-		collector.highlightPrimitives(false);
+		collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
 		collector.SetState(PatchNode::m_state_selpoint, RenderableCollector::eWireframeOnly);
 		collector.SetState(PatchNode::m_state_selpoint, RenderableCollector::eFullMaterials);
 		collector.addRenderable(m_render_selected, localToWorld());
 	}
 }
 
-bool PatchNode::isHighlighted() const
+std::size_t PatchNode::getHighlightFlags()
 {
-	return isSelected();
+	if (!isSelected()) return Highlight::NoHighlight;
+
+	return isGroupMember() ? (Highlight::Selected | Highlight::GroupMember) : Highlight::Selected;
 }
 
 void PatchNode::evaluateTransform()
diff --git a/radiant/patch/PatchNode.h b/radiant/patch/PatchNode.h
index a2aaa51..44bc88f 100644
--- a/radiant/patch/PatchNode.h
+++ b/radiant/patch/PatchNode.h
@@ -5,7 +5,7 @@
 #include "itraceable.h"
 #include "imap.h"
 #include "Patch.h"
-#include "SelectableNode.h"
+#include "scene/SelectableNode.h"
 #include "PatchControlInstance.h"
 #include "dragplanes.h"
 
@@ -82,11 +82,13 @@ public:
   	void selectReversedPlanes(Selector& selector, const SelectedPlanes& selectedPlanes);
 
 	// Returns true if any of the patch components is selected
-	bool isSelectedComponents() const;
+	bool isSelectedComponents() const override;
 	// Set the components (control points or dragplanes) selection to <select>
-	void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode);
+	void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) override;
+	// Invert the component selection
+	void invertSelectedComponents(SelectionSystem::EComponentMode mode) override;
 	// Tests the patch components on selection using the passed SelectionTest
-	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode);
+	void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) override;
 
 	// override scene::Inode::onRemoveFromScene to deselect the child components
     virtual void onInsertIntoScene(scene::IMapRootNode& root) override;
@@ -116,12 +118,9 @@ public:
 	// Clones this node, allocates a new Node on the heap and passes itself to the constructor of the new node
 	scene::INodePtr clone() const;
 
-	// Override ObservedSelectable behaviour
-	virtual void invertSelected();
-
 	// greebo: This gets called by the ObservedSelectable as soon as its selection state changes
 	// (see ObservedSelectable and PatchControlInstance)
-	void selectedChangedComponent(const Selectable& selectable);
+	void selectedChangedComponent(const ISelectable& selectable);
 
 	// LitObject implementation
 	bool intersectsLight(const RendererLight& light) const;
@@ -130,15 +129,15 @@ public:
 
 	// Render functions, these make sure that all things get rendered properly. The calls are also passed on
 	// to the contained patch <m_patch>
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
 	// Renders the components of this patch instance, makes use of the Patch::render_component() method
-	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const override;
 
 	void evaluateTransform();
-	bool isHighlighted() const;
+	std::size_t getHighlightFlags() override;
 
     // Returns the center of the untransformed world AABB
     const Vector3& getUntransformedOrigin() override;
diff --git a/radiant/patch/PatchRenderables.cpp b/radiant/patch/PatchRenderables.cpp
index affd00c..63aa68b 100644
--- a/radiant/patch/PatchRenderables.cpp
+++ b/radiant/patch/PatchRenderables.cpp
@@ -15,76 +15,6 @@ void RenderablePatchWireframe::render(const RenderInfo& info) const
     {
         _needsUpdate = false;
 
-        const std::vector<ArbitraryMeshVertex>& patchVerts = _tess.vertices;
-
-        // Vertex buffer to receive and render vertices
-        VertexBuffer_T currentVBuf;
-
-        std::size_t firstIndex = 0;
-        for (std::size_t i = 0; i <= _tess.curveTreeV.size(); ++i)
-        {
-            currentVBuf.addBatch(patchVerts.begin() + firstIndex,
-                _tess.m_nArrayWidth);
-
-            if (i == _tess.curveTreeV.size()) break;
-
-            if (!_tess.curveTreeV[i]->isLeaf())
-            {
-                currentVBuf.addBatch(
-                    patchVerts.begin() + GLint(_tess.curveTreeV[i]->index),
-                    _tess.m_nArrayWidth
-                    );
-            }
-
-            firstIndex += (_tess.arrayHeight[i] * _tess.m_nArrayWidth);
-        }
-
-        const ArbitraryMeshVertex* p = &patchVerts.front();
-        std::size_t uStride = _tess.m_nArrayWidth;
-        for (std::size_t i = 0; i <= _tess.curveTreeU.size(); ++i)
-        {
-            currentVBuf.addBatch(p, _tess.m_nArrayHeight, uStride);
-
-            if (i == _tess.curveTreeU.size()) break;
-
-            if (!_tess.curveTreeU[i]->isLeaf())
-            {
-                currentVBuf.addBatch(
-                    patchVerts.begin() + _tess.curveTreeU[i]->index,
-                    _tess.m_nArrayHeight, uStride
-                    );
-            }
-
-            p += _tess.arrayWidth[i];
-        }
-
-        // Render all vertex batches
-        _vertexBuf.replaceData(currentVBuf);
-    }
-
-    _vertexBuf.renderAllBatches(GL_LINE_STRIP);
-}
-
-void RenderablePatchWireframe::queueUpdate()
-{
-    _needsUpdate = true;
-}
-
-void RenderablePatchFixedWireframe::render(const RenderInfo& info) const
-{
-    if (_tess.vertices.empty() || _tess.indices.empty()) return;
-
-    // No colour changing
-    glDisableClientState(GL_COLOR_ARRAY);
-    if (info.checkFlag(RENDER_VERTEX_COLOUR))
-    {
-        glColor3f(1, 1, 1);
-    }
-
-    if (_needsUpdate)
-    {
-        _needsUpdate = false;
-
         // Create a VBO and add the vertex data
         VertexBuffer_T currentVBuf;
         currentVBuf.addVertices(_tess.vertices.begin(), _tess.vertices.end());
@@ -92,10 +22,10 @@ void RenderablePatchFixedWireframe::render(const RenderInfo& info) const
         // Submit index batches
         const RenderIndex* strip_indices = &_tess.indices.front();
         for (std::size_t i = 0;
-            i < _tess.m_numStrips;
-            i++, strip_indices += _tess.m_lenStrips)
+            i < _tess.numStrips;
+            i++, strip_indices += _tess.lenStrips)
         {
-            currentVBuf.addIndexBatch(strip_indices, _tess.m_lenStrips);
+            currentVBuf.addIndexBatch(strip_indices, _tess.lenStrips);
         }
 
         // Render all index batches
@@ -105,7 +35,7 @@ void RenderablePatchFixedWireframe::render(const RenderInfo& info) const
     _vertexBuf.renderAllBatches(GL_QUAD_STRIP);
 }
 
-void RenderablePatchFixedWireframe::queueUpdate()
+void RenderablePatchWireframe::queueUpdate()
 {
     _needsUpdate = true;
 }
@@ -142,10 +72,10 @@ void RenderablePatchSolid::render(const RenderInfo& info) const
         // Submit indices
         const RenderIndex* strip_indices = &_tess.indices.front();
         for (std::size_t i = 0;
-            i < _tess.m_numStrips;
-            i++, strip_indices += _tess.m_lenStrips)
+            i < _tess.numStrips;
+            i++, strip_indices += _tess.lenStrips)
         {
-            currentVBuf.addIndexBatch(strip_indices, _tess.m_lenStrips);
+            currentVBuf.addIndexBatch(strip_indices, _tess.lenStrips);
         }
 
         // Render all batches
@@ -159,3 +89,73 @@ void RenderablePatchSolid::queueUpdate()
 {
     _needsUpdate = true;
 }
+
+const ShaderPtr& RenderablePatchVectorsNTB::getShader() const
+{
+	return _shader;
+}
+
+RenderablePatchVectorsNTB::RenderablePatchVectorsNTB(const PatchTesselation& tess) :
+	_tess(tess)
+{}
+
+void RenderablePatchVectorsNTB::setRenderSystem(const RenderSystemPtr& renderSystem)
+{
+	if (renderSystem)
+	{
+		_shader = renderSystem->capture("$PIVOT");
+	}
+	else
+	{
+		_shader.reset();
+	}
+}
+
+#define	VectorMA( v, s, b, o )		((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))
+
+void RenderablePatchVectorsNTB::render(const RenderInfo& info) const
+{
+	if (_tess.vertices.empty()) return;
+
+	glBegin(GL_LINES);
+
+	for (int j = 0; j < _tess.vertices.size(); j++)
+	{
+		const ArbitraryMeshVertex& v = _tess.vertices[j];
+
+		Vector3 end;
+
+		glColor3f(0, 0, 1);
+		glVertex3dv(static_cast<double*>(Vector3(v.vertex)));
+		VectorMA(v.vertex, 5, v.normal, end);
+		glVertex3dv(static_cast<double*>(end));
+
+		glColor3f(1, 0, 0);
+		glVertex3dv(static_cast<double*>(Vector3(v.vertex)));
+		VectorMA(v.vertex, 5, v.tangent, end);
+		glVertex3dv(static_cast<double*>(end));
+
+		glColor3f(0, 1, 0);
+		glVertex3dv(static_cast<double*>(Vector3(v.vertex)));
+		VectorMA(v.vertex, 5, v.bitangent, end);
+		glVertex3dv(static_cast<double*>(end));
+
+		glColor3f(1, 1, 1);
+		glVertex3dv(static_cast<double*>(Vector3(v.vertex)));
+		glVertex3dv(static_cast<double*>(Vector3(v.vertex)));
+	}
+
+	glEnd();
+}
+
+void RenderablePatchVectorsNTB::render(RenderableCollector& collector, const VolumeTest& volume, const Matrix4& localToWorld) const
+{
+	collector.PushState();
+
+	collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
+	collector.SetState(_shader, RenderableCollector::eWireframeOnly);
+	collector.SetState(_shader, RenderableCollector::eFullMaterials);
+	collector.addRenderable(*this, localToWorld);
+
+	collector.PopState();
+}
diff --git a/radiant/patch/PatchRenderables.h b/radiant/patch/PatchRenderables.h
index ac4bda6..16cb8b8 100644
--- a/radiant/patch/PatchRenderables.h
+++ b/radiant/patch/PatchRenderables.h
@@ -3,7 +3,6 @@
  * These are the renderables that are used in the PatchNode/Patch class to draw
  * the patch onto the screen.
  */
-
 #pragma once
 
 #include "igl.h"
@@ -13,23 +12,26 @@
 #include "render/IndexedVertexBuffer.h"
 
 /// Helper class to render a PatchTesselation in wireframe mode
-class RenderablePatchWireframe : public OpenGLRenderable
+class RenderablePatchWireframe :
+	public OpenGLRenderable
 {
-    // Geometry source
-    const PatchTesselation& _tess;
+protected:
+	// Geometry source
+	const PatchTesselation& _tess;
 
-    // VertexBuffer for rendering
-    typedef render::VertexBuffer<Vertex3f> VertexBuffer_T;
-    mutable VertexBuffer_T _vertexBuf;
+	// VertexBuffer for rendering
+	typedef render::IndexedVertexBuffer<Vertex3f> VertexBuffer_T;
+	mutable VertexBuffer_T _vertexBuf;
 
-    mutable bool _needsUpdate;
+	mutable bool _needsUpdate;
 
 public:
+	RenderablePatchWireframe(const PatchTesselation& tess) :
+		_tess(tess),
+		_needsUpdate(true)
+	{ }
 
-    RenderablePatchWireframe(const PatchTesselation& tess) : 
-        _tess(tess),
-        _needsUpdate(true)
-    { }
+	virtual ~RenderablePatchWireframe() {}
 
     void render(const RenderInfo& info) const;
 
@@ -37,27 +39,13 @@ public:
 };
 
 /// Helper class to render a fixed geometry PatchTesselation in wireframe mode
-class RenderablePatchFixedWireframe : public OpenGLRenderable
+class RenderablePatchFixedWireframe : 
+	public RenderablePatchWireframe
 {
-    // Geometry source
-    PatchTesselation& _tess;
-
-    // VertexBuffer for rendering
-    typedef render::IndexedVertexBuffer<Vertex3f> VertexBuffer_T;
-    mutable VertexBuffer_T _vertexBuf;
-
-    mutable bool _needsUpdate;
-
 public:
-
     RenderablePatchFixedWireframe(PatchTesselation& tess) : 
-        _tess(tess),
-        _needsUpdate(true)
+		RenderablePatchWireframe(tess)
     {}
-
-    void render(const RenderInfo& info) const;
-
-    void queueUpdate();
 };
 
 /// Helper class to render a PatchTesselation in solid mode
@@ -80,3 +68,25 @@ public:
 
     void queueUpdate();
 };
+
+// Renders a vertex' normal/tangent/bitangent vector (for debugging purposes)
+class RenderablePatchVectorsNTB :
+	public OpenGLRenderable
+{
+private:
+    std::vector<VertexCb> _vertices;
+	const PatchTesselation& _tess;
+
+	ShaderPtr _shader;
+
+public:
+	const ShaderPtr& getShader() const;
+
+	RenderablePatchVectorsNTB(const PatchTesselation& tess);
+
+	void setRenderSystem(const RenderSystemPtr& renderSystem);
+
+	void render(const RenderInfo& info) const;
+
+	void render(RenderableCollector& collector, const VolumeTest& volume, const Matrix4& localToWorld) const;
+};
diff --git a/radiant/patch/PatchTesselation.cpp b/radiant/patch/PatchTesselation.cpp
index 9e50725..8c06998 100644
--- a/radiant/patch/PatchTesselation.cpp
+++ b/radiant/patch/PatchTesselation.cpp
@@ -1,6 +1,877 @@
 #include "PatchTesselation.h"
 
+#include "Patch.h"
+
 void PatchTesselation::clear()
 {
     *this = PatchTesselation();
 }
+
+#define	COPLANAR_EPSILON	0.1f
+
+void PatchTesselation::generateNormals()
+{
+	//
+	// if all points are coplanar, set all normals to that plane
+	//
+	Vector3	extent[3];
+
+	extent[0] = vertices[width - 1].vertex - vertices[0].vertex;
+	extent[1] = vertices[(height - 1) * width + width - 1].vertex - vertices[0].vertex;
+	extent[2] = vertices[(height - 1) * width].vertex - vertices[0].vertex;
+
+	Vector3 norm = extent[0].crossProduct(extent[1]);
+
+	if (norm.getLengthSquared() == 0.0f)
+	{
+		norm = extent[0].crossProduct(extent[2]);
+
+		if (norm.getLengthSquared() == 0.0f)
+		{
+			norm = extent[1].crossProduct(extent[2]);
+		}
+	}
+
+	// wrapped patched may not get a valid normal here
+	if (norm.normalise() != 0.0f)
+	{
+		float offset = vertices[0].vertex.dot(norm);
+
+		std::size_t i = 0;
+
+		for (i = 1; i < width * height; i++)
+		{
+			float d = vertices[i].vertex.dot(norm);
+
+			if (fabs(d - offset) > COPLANAR_EPSILON)
+			{
+				break;
+			}
+		}
+
+		if (i == width * height)
+		{
+			// all are coplanar
+			for (i = 0; i < width * height; i++)
+			{
+				vertices[i].normal = norm;
+			}
+
+			return;
+		}
+	}
+
+	// check for wrapped edge cases, which should smooth across themselves
+	bool wrapWidth = false;
+
+	{
+		std::size_t i = 0;
+
+		for (i = 0; i < height; i++)
+		{
+			Vector3 delta = vertices[i * width].vertex - vertices[i * width + width - 1].vertex;
+
+			if (delta.getLengthSquared() > 1.0f)
+			{
+				break;
+			}
+		}
+
+		if (i == height)
+		{
+			wrapWidth = true;
+		}
+	}
+
+	bool wrapHeight = false;
+
+	{
+		std::size_t i = 0;
+
+		for (i = 0; i < width; i++)
+		{
+			Vector3 delta = vertices[i].vertex - vertices[(height - 1) * width + i].vertex;
+
+			if (delta.getLengthSquared() > 1.0f)
+			{
+				break;
+			}
+		}
+
+		if (i == width)
+		{
+			wrapHeight = true;
+		}
+	}
+
+	Vector3 around[8];
+	bool good[8];
+	static int neighbors[8][2] = { { 0,1 },{ 1,1 },{ 1,0 },{ 1,-1 },{ 0,-1 },{ -1,-1 },{ -1,0 },{ -1,1 } };
+
+	for (std::size_t i = 0; i < width; i++)
+	{
+		for (std::size_t j = 0; j < height; j++)
+		{
+			Vector3 base = vertices[j * width + i].vertex;
+
+			for (std::size_t k = 0; k < 8; k++)
+			{
+				around[k] = Vector3(0, 0, 0);
+				good[k] = false;
+
+				for (std::size_t dist = 1; dist <= 3; dist++)
+				{
+					int x = i + neighbors[k][0] * dist;
+					int y = j + neighbors[k][1] * dist;
+
+					if (wrapWidth)
+					{
+						if (x < 0)
+						{
+							x = width - 1 + x;
+						}
+						else if (x >= width)
+						{
+							x = 1 + x - width;
+						}
+					}
+
+					if (wrapHeight)
+					{
+						if (y < 0)
+						{
+							y = height - 1 + y;
+						}
+						else if (y >= height)
+						{
+							y = 1 + y - height;
+						}
+					}
+
+					if (x < 0 || x >= width || y < 0 || y >= height)
+					{
+						break;					// edge of patch
+					}
+
+					Vector3 temp = vertices[y * width + x].vertex - base;
+
+					if (temp.normalise() == 0.0f)
+					{
+						continue;				// degenerate edge, get more dist
+					}
+					else
+					{
+						good[k] = true;
+						around[k] = temp;
+						break;					// good edge
+					}
+				}
+			}
+
+			Vector3 sum(0, 0, 0);
+
+			for (std::size_t k = 0; k < 8; k++)
+			{
+				if (!good[k] || !good[(k + 1) & 7])
+				{
+					continue;	// didn't get two points
+				}
+
+				Vector3 tempNormal = around[(k + 1) & 7].crossProduct(around[k]);
+				if (tempNormal.normalise() == 0.0f)
+				{
+					continue;
+				}
+
+				sum += tempNormal;
+			}
+
+			vertices[j * width + i].normal = sum;
+			vertices[j * width + i].normal.normalise();
+		}
+	}
+}
+
+void PatchTesselation::sampleSinglePatchPoint(const ArbitraryMeshVertex ctrl[3][3], float u, float v, ArbitraryMeshVertex& out) const
+{
+	float vCtrl[3][8];
+
+	// find the control points for the v coordinate
+	for (std::size_t vPoint = 0; vPoint < 3; vPoint++)
+	{
+		for (std::size_t axis = 0; axis < 8; axis++)
+		{
+			float a, b, c;
+
+			if (axis < 3)
+			{
+				a = ctrl[0][vPoint].vertex[axis];
+				b = ctrl[1][vPoint].vertex[axis];
+				c = ctrl[2][vPoint].vertex[axis];
+			}
+			else if (axis < 6)
+			{
+				a = ctrl[0][vPoint].normal[axis - 3];
+				b = ctrl[1][vPoint].normal[axis - 3];
+				c = ctrl[2][vPoint].normal[axis - 3];
+			}
+			else
+			{
+				a = ctrl[0][vPoint].texcoord[axis - 6];
+				b = ctrl[1][vPoint].texcoord[axis - 6];
+				c = ctrl[2][vPoint].texcoord[axis - 6];
+			}
+
+			float qA = a - 2.0f * b + c;
+			float qB = 2.0f * b - 2.0f * a;
+			float qC = a;
+
+			vCtrl[vPoint][axis] = qA * u * u + qB * u + qC;
+		}
+	}
+
+	// interpolate the v value
+	for (std::size_t axis = 0; axis < 8; axis++)
+	{
+		float a = vCtrl[0][axis];
+		float b = vCtrl[1][axis];
+		float c = vCtrl[2][axis];
+		float qA = a - 2.0f * b + c;
+		float qB = 2.0f * b - 2.0f * a;
+		float qC = a;
+
+		if (axis < 3)
+		{
+			out.vertex[axis] = qA * v * v + qB * v + qC;
+		}
+		else if (axis < 6)
+		{
+			out.normal[axis - 3] = qA * v * v + qB * v + qC;
+		}
+		else
+		{
+			out.texcoord[axis - 6] = qA * v * v + qB * v + qC;
+		}
+	}
+}
+
+void PatchTesselation::sampleSinglePatch(const ArbitraryMeshVertex ctrl[3][3], 
+	std::size_t baseCol, std::size_t baseRow,
+	std::size_t w, std::size_t horzSub, std::size_t vertSub,
+	std::vector<ArbitraryMeshVertex>& outVerts) const
+{
+	horzSub++;
+	vertSub++;
+
+	for (std::size_t i = 0; i < horzSub; i++)
+	{
+		for (std::size_t j = 0; j < vertSub; j++)
+		{
+			float u = static_cast<float>(i) / (horzSub - 1);
+			float v = static_cast<float>(j) / (vertSub - 1);
+
+			sampleSinglePatchPoint(ctrl, u, v, outVerts[((baseRow + j) * w) + i + baseCol]);
+		}
+	}
+}
+
+void PatchTesselation::subdivideMeshFixed(std::size_t subdivX, std::size_t subdivY)
+{
+	std::size_t outWidth = ((width - 1) / 2 * subdivX) + 1;
+	std::size_t outHeight = ((height - 1) / 2 * subdivY) + 1;
+
+	std::vector<ArbitraryMeshVertex> dv(outWidth * outHeight);
+
+	std::size_t baseCol = 0;
+	ArbitraryMeshVertex sample[3][3];
+
+	for (std::size_t i = 0; i + 2 < width; i += 2)
+	{
+		std::size_t baseRow = 0;
+
+		for (std::size_t j = 0; j + 2 < height; j += 2)
+		{
+			for (std::size_t k = 0; k < 3; k++)
+			{
+				for (std::size_t l = 0; l < 3; l++)
+				{
+					sample[k][l] = vertices[((j + l) * width) + i + k];
+				}
+			}
+
+			sampleSinglePatch(sample, baseCol, baseRow, outWidth, subdivX, subdivY, dv);
+
+			baseRow += subdivY;
+		}
+
+		baseCol += subdivX;
+	}
+
+	vertices.swap(dv);
+	
+	width = _maxWidth = outWidth;
+	height = _maxHeight = outHeight;
+}
+
+void PatchTesselation::collapseMesh()
+{
+	if (width != _maxWidth)
+	{
+		for (std::size_t j = 0; j < height; j++)
+		{
+			for (std::size_t i = 0; i < width; i++)
+			{
+				vertices[j*width + i] = vertices[j*_maxWidth + i];
+			}
+		}
+	}
+
+	vertices.resize(width * height);
+}
+
+void PatchTesselation::expandMesh()
+{
+	vertices.resize(_maxWidth * _maxHeight);
+
+	if (width != _maxWidth)
+	{
+		for (int j = height - 1; j >= 0; j--)
+		{
+			for (int i = width - 1; i >= 0; i--)
+			{
+				vertices[j*_maxWidth + i] = vertices[j*width + i];
+			}
+		}
+	}
+}
+
+void PatchTesselation::resizeExpandedMesh(int newHeight, int newWidth)
+{
+	if (newHeight <= _maxHeight && newWidth <= _maxWidth)
+	{
+		return;
+	}
+
+	if (newHeight * newWidth > _maxHeight * _maxWidth)
+	{
+		vertices.resize(newHeight * newWidth);
+	}
+
+	// space out verts for new height and width
+	for (int j = _maxHeight - 1; j >= 0; j--)
+	{
+		for (int i = _maxWidth - 1; i >= 0; i--)
+		{
+			vertices[j*newWidth + i] = vertices[j*_maxWidth + i];
+		}
+	}
+
+	_maxHeight = newHeight;
+	_maxWidth = newWidth;
+}
+
+void PatchTesselation::lerpVert(const ArbitraryMeshVertex& a, const ArbitraryMeshVertex& b, ArbitraryMeshVertex&out)
+{
+	out.vertex = a.vertex.mid(b.vertex);
+	out.normal = a.normal.mid(b.normal);
+	out.texcoord = a.texcoord.mid(b.texcoord);
+}
+
+void PatchTesselation::putOnCurve()
+{
+	ArbitraryMeshVertex prev, next;
+
+	// put all the approximating points on the curve
+	for (std::size_t i = 0; i < width; i++)
+	{
+		for (std::size_t j = 1; j < height; j += 2)
+		{
+			lerpVert(vertices[j*_maxWidth + i], vertices[(j + 1)*_maxWidth + i], prev);
+			lerpVert(vertices[j*_maxWidth + i], vertices[(j - 1)*_maxWidth + i], next);
+			lerpVert(prev, next, vertices[j*_maxWidth + i]);
+		}
+	}
+
+	for (std::size_t j = 0; j < height; j++)
+	{
+		for (std::size_t i = 1; i < width; i += 2)
+		{
+			lerpVert(vertices[j*_maxWidth + i], vertices[j*_maxWidth + i + 1], prev);
+			lerpVert(vertices[j*_maxWidth + i], vertices[j*_maxWidth + i - 1], next);
+			lerpVert(prev, next, vertices[j*_maxWidth + i]);
+		}
+	}
+}
+
+Vector3 PatchTesselation::projectPointOntoVector(const Vector3& point, const Vector3& vStart, const Vector3& vEnd)
+{
+	Vector3 pVec = point - vStart;
+	Vector3 vec = vEnd - vStart;
+
+	vec.normalise();
+
+	// project onto the directional vector for this segment
+	return vStart + vec * pVec.dot(vec);
+}
+
+void PatchTesselation::removeLinearColumnsRows()
+{
+	for (std::size_t j = 1; j < width - 1; j++)
+	{
+		float maxLength = 0;
+
+		for (std::size_t i = 0; i < height; i++)
+		{
+			Vector3 proj = projectPointOntoVector(vertices[i*_maxWidth + j].vertex,
+				vertices[i*_maxWidth + j - 1].vertex,
+				vertices[i*_maxWidth + j + 1].vertex);
+
+			Vector3 dir = vertices[i*_maxWidth + j].vertex - proj;
+
+			float len = dir.getLengthSquared();
+
+			if (len > maxLength)
+			{
+				maxLength = len;
+			}
+		}
+
+		if (maxLength < 0.2f*0.2f)
+		{
+			width--;
+
+			for (std::size_t i = 0; i < height; i++)
+			{
+				for (std::size_t k = j; k < width; k++)
+				{
+					vertices[i*_maxWidth + k] = vertices[i*_maxWidth + k + 1];
+				}
+			}
+
+			j--;
+		}
+	}
+
+	for (std::size_t j = 1; j < height - 1; j++)
+	{
+		float maxLength = 0;
+
+		for (std::size_t i = 0; i < width; i++)
+		{
+			Vector3 proj = projectPointOntoVector(vertices[j*_maxWidth + i].vertex,
+				vertices[(j - 1)*_maxWidth + i].vertex,
+				vertices[(j + 1)*_maxWidth + i].vertex);
+
+			Vector3 dir = vertices[j*_maxWidth + i].vertex - proj;
+
+			float len = dir.getLengthSquared();
+
+			if (len > maxLength)
+			{
+				maxLength = len;
+			}
+		}
+
+		if (maxLength < 0.2f*0.2f)
+		{
+			height--;
+
+			for (std::size_t i = 0; i < width; i++)
+			{
+				for (std::size_t k = j; k < height; k++)
+				{
+					vertices[k*_maxWidth + i] = vertices[(k + 1)*_maxWidth + i];
+				}
+			}
+
+			j--;
+		}
+	}
+}
+
+void PatchTesselation::subdivideMesh()
+{
+	static const float DEFAULT_CURVE_MAX_ERROR = 4.0f;
+	static const float DEFAULT_CURVE_MAX_LENGTH = -1.0f;
+
+	Vector3 prevxyz, nextxyz, midxyz;
+	ArbitraryMeshVertex prev, next, mid;
+
+	static float maxHorizontalErrorSqr = DEFAULT_CURVE_MAX_ERROR * DEFAULT_CURVE_MAX_ERROR;
+	static float maxVerticalErrorSqr = DEFAULT_CURVE_MAX_ERROR * DEFAULT_CURVE_MAX_ERROR;
+	static float maxLengthSqr = DEFAULT_CURVE_MAX_LENGTH * DEFAULT_CURVE_MAX_LENGTH;
+
+	expandMesh();
+
+	// horizontal subdivisions
+	for (std::size_t j = 0; j + 2 < width; j += 2)
+	{
+		std::size_t i;
+
+		// check subdivided midpoints against control points
+		for (i = 0; i < height; i++)
+		{
+			for (std::size_t l = 0; l < 3; l++)
+			{
+				prevxyz[l] = vertices[i*_maxWidth + j + 1].vertex[l] - vertices[i*_maxWidth + j].vertex[l];
+				nextxyz[l] = vertices[i*_maxWidth + j + 2].vertex[l] - vertices[i*_maxWidth + j + 1].vertex[l];
+				midxyz[l] = (vertices[i*_maxWidth + j].vertex[l] + vertices[i*_maxWidth + j + 1].vertex[l] * 2.0f + vertices[i*_maxWidth + j + 2].vertex[l]) * 0.25f;
+			}
+
+			if (DEFAULT_CURVE_MAX_LENGTH > 0.0f)
+			{
+				// if the span length is too long, force a subdivision
+				if (prevxyz.getLengthSquared() > maxLengthSqr || nextxyz.getLengthSquared() > maxLengthSqr)
+				{
+					break;
+				}
+			}
+
+			// see if this midpoint is off far enough to subdivide
+			Vector3 delta = vertices[i*_maxWidth + j + 1].vertex - midxyz;
+
+			if (delta.getLengthSquared() > maxHorizontalErrorSqr)
+			{
+				break;
+			}
+		}
+
+		if (i == height)
+		{
+			continue;	// didn't need subdivision
+		}
+
+		if (width + 2 >= _maxWidth)
+		{
+			resizeExpandedMesh(_maxHeight, _maxWidth + 4);
+		}
+
+		// insert two columns and replace the peak
+		width += 2;
+
+		for (i = 0; i < height; i++)
+		{
+			lerpVert(vertices[i*_maxWidth + j], vertices[i*_maxWidth + j + 1], prev);
+			lerpVert(vertices[i*_maxWidth + j + 1], vertices[i*_maxWidth + j + 2], next);
+			lerpVert(prev, next, mid);
+
+			for (int k = width - 1; k > j + 3; k--)
+			{
+				vertices[i*_maxWidth + k] = vertices[i*_maxWidth + k - 2];
+			}
+			vertices[i*_maxWidth + j + 1] = prev;
+			vertices[i*_maxWidth + j + 2] = mid;
+			vertices[i*_maxWidth + j + 3] = next;
+		}
+
+		// back up and recheck this set again, it may need more subdivision
+		j -= 2;
+	}
+
+	// vertical subdivisions
+	for (std::size_t j = 0; j + 2 < height; j += 2)
+	{
+		int i;
+
+		// check subdivided midpoints against control points
+		for (i = 0; i < width; i++)
+		{
+			for (std::size_t l = 0; l < 3; l++)
+			{
+				prevxyz[l] = vertices[(j + 1)*_maxWidth + i].vertex[l] - vertices[j*_maxWidth + i].vertex[l];
+				nextxyz[l] = vertices[(j + 2)*_maxWidth + i].vertex[l] - vertices[(j + 1)*_maxWidth + i].vertex[l];
+				midxyz[l] = (vertices[j*_maxWidth + i].vertex[l] + vertices[(j + 1)*_maxWidth + i].vertex[l] * 2.0f + vertices[(j + 2)*_maxWidth + i].vertex[l]) * 0.25f;
+			}
+
+			if (DEFAULT_CURVE_MAX_LENGTH > 0.0f)
+			{
+				// if the span length is too long, force a subdivision
+				if (prevxyz.getLengthSquared() > maxLengthSqr || nextxyz.getLengthSquared() > maxLengthSqr)
+				{
+					break;
+				}
+			}
+
+			// see if this midpoint is off far enough to subdivide
+			Vector3 delta = vertices[(j + 1)*_maxWidth + i].vertex - midxyz;
+
+			if (delta.getLengthSquared() > maxVerticalErrorSqr)
+			{
+				break;
+			}
+		}
+
+		if (i == width)
+		{
+			continue;	// didn't need subdivision
+		}
+
+		if (height + 2 >= _maxHeight)
+		{
+			resizeExpandedMesh(_maxHeight + 4, _maxWidth);
+		}
+
+		// insert two columns and replace the peak
+		height += 2;
+
+		for (i = 0; i < width; i++)
+		{
+			lerpVert(vertices[j*_maxWidth + i], vertices[(j + 1)*_maxWidth + i], prev);
+			lerpVert(vertices[(j + 1)*_maxWidth + i], vertices[(j + 2)*_maxWidth + i], next);
+			lerpVert(prev, next, mid);
+
+			for (int k = height - 1; k > j + 3; k--)
+			{
+				vertices[k*_maxWidth + i] = vertices[(k - 2)*_maxWidth + i];
+			}
+
+			vertices[(j + 1)*_maxWidth + i] = prev;
+			vertices[(j + 2)*_maxWidth + i] = mid;
+			vertices[(j + 3)*_maxWidth + i] = next;
+		}
+
+		// back up and recheck this set again, it may need more subdivision
+		j -= 2;
+	}
+
+	putOnCurve();
+
+	removeLinearColumnsRows();
+
+	collapseMesh();
+}
+
+struct FaceTangents
+{
+	Vector3 tangents[2];
+};
+
+namespace
+{
+
+void calculateFaceTangent(FaceTangents& ft, const ArbitraryMeshVertex& a, const ArbitraryMeshVertex& b, const ArbitraryMeshVertex& c)
+{
+	float		d0[5], d1[5];
+
+	d0[0] = b.vertex[0] - a.vertex[0];
+	d0[1] = b.vertex[1] - a.vertex[1];
+	d0[2] = b.vertex[2] - a.vertex[2];
+	d0[3] = b.texcoord[0] - a.texcoord[0];
+	d0[4] = b.texcoord[1] - a.texcoord[1];
+
+	d1[0] = c.vertex[0] - a.vertex[0];
+	d1[1] = c.vertex[1] - a.vertex[1];
+	d1[2] = c.vertex[2] - a.vertex[2];
+	d1[3] = c.texcoord[0] - a.texcoord[0];
+	d1[4] = c.texcoord[1] - a.texcoord[1];
+
+	float area = d0[3] * d1[4] - d0[4] * d1[3];
+
+	if (fabs(area) < 1e-20f)
+	{
+		ft.tangents[0].set(0, 0, 0);
+		ft.tangents[1].set(0, 0, 0);
+		return;
+	}
+
+	float inva = area < 0.0f ? -1 : 1;		// was = 1.0f / area;
+
+	Vector3 temp;
+
+	temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva;
+	temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva;
+	temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva;
+	temp.normalise();
+	ft.tangents[0] = temp.getNormalised();
+
+	temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva;
+	temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva;
+	temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva;
+	temp.normalise();
+	ft.tangents[1] = temp;
+}
+
+} // namespace
+
+void PatchTesselation::deriveFaceTangents(std::vector<FaceTangents>& faceTangents)
+{
+	assert(lenStrips >= 3);
+
+	// calculate tangent vectors for each face in isolation
+
+	// DR is using indices that are sent to openGL as GL_QUAD_STRIPs 
+	// It takes N+2 indices to describe N triangles when using QUAD_STRIPs
+	std::size_t numFacesPerStrip = lenStrips - 2;
+	std::size_t numFaces = numFacesPerStrip * numStrips;
+
+	faceTangents.resize(numFaces); // one tangent per face
+
+	// Go through each strip and derive tangents for each triangle like idTech4 does
+	const RenderIndex* strip_indices = &indices.front();
+
+	for (std::size_t strip = 0; strip < numStrips; strip++, strip_indices += lenStrips)
+	{
+		for (std::size_t i = 0; i < lenStrips - 2; i += 2)
+		{
+			// First tri of the quad (indices 0,1,2)
+			calculateFaceTangent(faceTangents[strip*numFacesPerStrip + i],
+				vertices[strip_indices[i + 0]],
+				vertices[strip_indices[i + 1]],
+				vertices[strip_indices[i + 2]]);
+
+			// Second tri of the quad (indices 1,2,3)
+			calculateFaceTangent(faceTangents[strip*numFacesPerStrip + i + 1],
+				vertices[strip_indices[i + 1]],
+				vertices[strip_indices[i + 2]],
+				vertices[strip_indices[i + 3]]);
+		}
+	}
+}
+
+void PatchTesselation::deriveTangents()
+{
+	if (lenStrips < 2) return;
+
+	std::vector<FaceTangents> faceTangents;
+	deriveFaceTangents(faceTangents);
+
+	// Note: we don't clear the tangent vectors here since the calling code
+	// just allocated the mesh which initialises all vectors to 0,0,0
+
+	std::size_t numFacesPerStrip = lenStrips - 2;
+
+	// The sum of all tangent vectors is assigned to each vertex of every face
+	// Since vertices can be shared across triangles this might very well add
+	// tangents of neighbouring triangles too
+	const RenderIndex* strip_indices = &indices.front();
+
+	for (std::size_t strip = 0; strip < numStrips; strip++, strip_indices += lenStrips)
+	{
+		for (std::size_t i = 0; i < lenStrips - 2; i += 2)
+		{
+			// First tri of the quad
+			const FaceTangents& ft1 = faceTangents[strip*numFacesPerStrip + i];
+
+			for (std::size_t j = 0; j < 3; j++)
+			{
+				ArbitraryMeshVertex& vert = vertices[strip_indices[i + j]];
+
+				vert.tangent += ft1.tangents[0];
+				vert.bitangent += ft1.tangents[1];
+			}
+
+			// Second tri of the quad
+			const FaceTangents& ft2 = faceTangents[strip*numFacesPerStrip + i + 1];
+
+			for (std::size_t j = 0; j < 3; j++)
+			{
+				ArbitraryMeshVertex& vert = vertices[strip_indices[i + j + 1]];
+
+				vert.tangent += ft2.tangents[0];
+				vert.bitangent += ft2.tangents[1];
+			}
+		}
+	}
+
+	// project the summed vectors onto the normal plane
+	// and normalize.  The tangent vectors will not necessarily
+	// be orthogonal to each other, but they will be orthogonal
+	// to the surface normal.
+	for (ArbitraryMeshVertex& vert : vertices)
+	{
+		float d = vert.tangent.dot(vert.normal);
+		vert.tangent = vert.tangent - vert.normal * d;
+		vert.tangent.normalise();
+
+		d = vert.bitangent.dot(vert.normal);
+		vert.bitangent = vert.bitangent - vert.normal * d;
+		vert.bitangent.normalise();
+	}
+}
+
+void PatchTesselation::generateIndices()
+{
+	const std::size_t numElems = width*height; // total number of elements in vertex array
+
+	const bool bWidthStrips = (width >= height); // decide if horizontal strips are longer than vertical
+
+	// allocate vertex, normal, texcoord and primitive-index arrays
+	vertices.resize(numElems);
+	indices.resize(width * 2 * (height - 1));
+
+	// set up strip indices
+	if (bWidthStrips)
+	{
+		numStrips = height - 1;
+		lenStrips = width * 2;
+
+		for (std::size_t i = 0; i<width; i++)
+		{
+			for (std::size_t j = 0; j<numStrips; j++)
+			{
+				indices[(j*lenStrips) + i * 2] = RenderIndex(j*width + i);
+				indices[(j*lenStrips) + i * 2 + 1] = RenderIndex((j + 1)*width + i);
+			}
+		}
+	}
+	else
+	{
+		numStrips = width - 1;
+		lenStrips = height * 2;
+
+		for (std::size_t i = 0; i<height; i++)
+		{
+			for (std::size_t j = 0; j<numStrips; j++)
+			{
+				indices[(j*lenStrips) + i * 2] = RenderIndex(((height - 1) - i)*width + j);
+				indices[(j*lenStrips) + i * 2 + 1] = RenderIndex(((height - 1) - i)*width + j + 1);
+			}
+		}
+	}
+}
+
+void PatchTesselation::generate(std::size_t patchWidth, std::size_t patchHeight, 
+	const PatchControlArray& controlPoints, bool subdivionsFixed, const Subdivisions& subdivs)
+{
+	width = patchWidth;
+	height = patchHeight;
+
+	_maxWidth = width;
+	_maxHeight = height;
+
+	// We start off with the control vertex grid, copy it into our tesselation structure
+	vertices.resize(controlPoints.size());
+
+	for (std::size_t w = 0; w < width; w++)
+	{
+		for (std::size_t h = 0; h < height; h++)
+		{
+			vertices[h*width + w].vertex = controlPoints[h*width + w].vertex;
+			vertices[h*width + w].texcoord = controlPoints[h*width + w].texcoord;
+		}
+	}
+
+	// generate normals for the control mesh
+	generateNormals();
+
+	if (subdivionsFixed)
+	{
+		subdivideMeshFixed(subdivs.x(), subdivs.y());
+	}
+	else
+	{
+		subdivideMesh();
+	}
+
+	// normalize all the lerped normals
+	for (ArbitraryMeshVertex& vertex : vertices)
+	{
+		vertex.normal.normalise();
+	}
+
+	// Build the strip indices for rendering the quads
+	generateIndices();
+
+	// With indices in place we can derive the tangent/bitangent vectors
+	deriveTangents();
+}
diff --git a/radiant/patch/PatchTesselation.h b/radiant/patch/PatchTesselation.h
index 5b6925e..3385639 100644
--- a/radiant/patch/PatchTesselation.h
+++ b/radiant/patch/PatchTesselation.h
@@ -1,37 +1,72 @@
 #pragma once
 
 #include "render.h"
-#include "PatchBezier.h"
+#include "PatchControl.h"
+
+struct FaceTangents;
 
 /// Representation of a patch as mesh geometry
-struct PatchTesselation
+class PatchTesselation
 {
 public:
-
+	// The vertex data, each vertex equipped with texcoord and ntb vectors
 	std::vector<ArbitraryMeshVertex> vertices;
+
+	// The indices, arranged in the way it's expected by GL_QUAD_STRIPS
+	// The number of indices is (lenStrips*numStrips), which is the same as (width*height)
 	std::vector<RenderIndex> indices;
 
-	std::size_t m_numStrips;
-	std::size_t m_lenStrips;
+	// Strip index layout
+	std::size_t numStrips;
+	std::size_t lenStrips;
 
-	std::vector<std::size_t> arrayWidth;
-	std::size_t m_nArrayWidth;
-	std::vector<std::size_t> arrayHeight;
-	std::size_t m_nArrayHeight;
+	// Geometry of the tesselated mesh
+	std::size_t width;
+	std::size_t height;
 
-	std::vector<BezierCurveTree*> curveTreeU;
-	std::vector<BezierCurveTree*> curveTreeV;
+private:
+	// Used during the tesselation phase
+	std::size_t _maxWidth;
+	std::size_t _maxHeight;
 
 public:
 
     /// Construct an uninitialised patch tesselation
 	PatchTesselation() :
-		m_numStrips(0),
-		m_lenStrips(0),
-		m_nArrayWidth(0),
-		m_nArrayHeight(0)
+		numStrips(0),
+		lenStrips(0),
+		width(0),
+		height(0),
+		_maxWidth(0),
+		_maxHeight(0)
 	{}
 
     /// Clear all patch data
     void clear();
+
+	// Generates the tesselated mesh based on the input parameters
+	void generate(std::size_t width, std::size_t height, const PatchControlArray& controlPoints, 
+		bool subdivionsFixed, const Subdivisions& subdivs);
+
+private:
+	// Private methods used for tesselation, modeled after the patch subdivision code found in idTech4
+	void generateIndices();
+	void generateNormals();
+	void subdivideMesh();
+	void subdivideMeshFixed(std::size_t subdivX, std::size_t subdivY);
+	void collapseMesh();
+	void expandMesh();
+	void resizeExpandedMesh(int newHeight, int newWidth);
+	void putOnCurve();
+	void removeLinearColumnsRows();
+
+	static void lerpVert(const ArbitraryMeshVertex& a, const ArbitraryMeshVertex& b, ArbitraryMeshVertex&out);
+	static Vector3 projectPointOntoVector(const Vector3& point, const Vector3& vStart, const Vector3& vEnd);
+
+	void sampleSinglePatch(const ArbitraryMeshVertex ctrl[3][3], std::size_t baseCol, std::size_t baseRow, 
+		std::size_t width, std::size_t horzSub, std::size_t vertSub, 
+		std::vector<ArbitraryMeshVertex>& outVerts) const;
+	void sampleSinglePatchPoint(const ArbitraryMeshVertex ctrl[3][3], float u, float v, ArbitraryMeshVertex& out) const;
+	void deriveTangents();
+	void deriveFaceTangents(std::vector<FaceTangents>& faceTangents);
 };
diff --git a/radiant/referencecache/NullModelNode.h b/radiant/referencecache/NullModelNode.h
index 2c6c51f..b720bfd 100644
--- a/radiant/referencecache/NullModelNode.h
+++ b/radiant/referencecache/NullModelNode.h
@@ -35,13 +35,13 @@ public:
 
 	void testSelect(Selector& selector, SelectionTest& test);
 
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
-	void setRenderSystem(const RenderSystemPtr& renderSystem);
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override;
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // never highlighted
+		return Highlight::NoHighlight; // never highlighted
 	}
 
 	// Bounded implementation
diff --git a/radiant/render/backend/GLProgramFactory.cpp b/radiant/render/backend/GLProgramFactory.cpp
index e01ac3a..2b88b86 100644
--- a/radiant/render/backend/GLProgramFactory.cpp
+++ b/radiant/render/backend/GLProgramFactory.cpp
@@ -100,7 +100,7 @@ GLProgramFactory::getFileAsBuffer(const std::string& filename,
     std::string absFileName = getBuiltInGLProgramPath(filename);
 
     // Open the file
-	std::size_t size = file_size(absFileName.c_str());
+	std::size_t size = os::getFileSize(absFileName);
 	std::ifstream file(absFileName.c_str());
 
     // Throw an exception if the file could not be found
diff --git a/radiant/render/backend/OpenGLShader.cpp b/radiant/render/backend/OpenGLShader.cpp
index e7d5738..1cfb04b 100644
--- a/radiant/render/backend/OpenGLShader.cpp
+++ b/radiant/render/backend/OpenGLShader.cpp
@@ -783,6 +783,18 @@ void OpenGLShader::construct(const std::string& name)
               state.m_linewidth = 2;
               state.m_linestipple_factor = 3;
             }
+			else if (name == "$XY_OVERLAY_GROUP")
+			{
+				Vector3 colorSelBrushes(0, 0.4, 0.8);
+				state.setColour(colorSelBrushes[0],
+					colorSelBrushes[1],
+					colorSelBrushes[2],
+					1);
+				state.setRenderFlag(RENDER_LINESTIPPLE);
+				state.setSortPosition(OpenGLState::SORT_OVERLAY_FIRST);
+				state.m_linewidth = 2;
+				state.m_linestipple_factor = 3;
+			}
             else if (name == "$DEBUG_CLIPPED")
             {
               state.setRenderFlag(RENDER_DEPTHWRITE);
@@ -848,6 +860,24 @@ void OpenGLShader::construct(const std::string& name)
                                  | RENDER_POLYGONSTIPPLE);
               state.setSortPosition(OpenGLState::SORT_OVERLAY_FIRST);
             }
+            else if (name == "$AAS_AREA")
+            {
+				state.setColour(1, 1, 1, 1);
+				state.setRenderFlags(RENDER_DEPTHWRITE
+					| RENDER_DEPTHTEST
+					| RENDER_OVERRIDE);
+				state.setSortPosition(OpenGLState::SORT_OVERLAY_LAST);
+				state.setDepthFunc(GL_LEQUAL);
+
+				OpenGLState& hiddenLine = appendDefaultPass();
+				hiddenLine.setColour(1, 1, 1, 1);
+				hiddenLine.setRenderFlags(RENDER_DEPTHWRITE
+					| RENDER_DEPTHTEST
+					| RENDER_OVERRIDE
+					| RENDER_LINESTIPPLE);
+				hiddenLine.setSortPosition(OpenGLState::SORT_OVERLAY_LAST);
+				hiddenLine.setDepthFunc(GL_GREATER);
+            }
             else
             {
                 assert(false);
diff --git a/radiant/render/frontend/RenderableCollectionWalker.h b/radiant/render/frontend/RenderableCollectionWalker.h
index 6db6daa..adca455 100644
--- a/radiant/render/frontend/RenderableCollectionWalker.h
+++ b/radiant/render/frontend/RenderableCollectionWalker.h
@@ -77,18 +77,31 @@ public:
 
         node->viewChanged();
 
-        if (node->isHighlighted() || (parent != NULL && parent->isHighlighted()))
+		std::size_t highlightFlags = node->getHighlightFlags();
+
+		if (parent)
+		{
+			highlightFlags |= parent->getHighlightFlags();
+		}
+
+        if (highlightFlags & Renderable::Highlight::Selected)
         {
             if (GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
             {
-                _collector.highlightFaces(true);
+				_collector.setHighlightFlag(RenderableCollector::Highlight::Faces, true);
             }
             else
             {
                 node->renderComponents(_collector, _volume);
             }
 
-            _collector.highlightPrimitives(true);
+			_collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, true);
+
+			// Pass on the info about whether we have a group member selected
+			if (highlightFlags & Renderable::Highlight::GroupMember)
+			{
+				_collector.setHighlightFlag(RenderableCollector::Highlight::GroupMember, true);
+			}
         }
 
         render(*node);
diff --git a/radiant/selection/BasicSelectable.h b/radiant/selection/BasicSelectable.h
index abbec98..32b8314 100644
--- a/radiant/selection/BasicSelectable.h
+++ b/radiant/selection/BasicSelectable.h
@@ -11,7 +11,7 @@ namespace selection
  * the selected state by means of a boolean.
  */
 class BasicSelectable : 
-	public Selectable
+	public ISelectable
 {
 private:
 	bool _selected;
diff --git a/radiant/selection/Device.h b/radiant/selection/Device.h
index ebd578b..fccaaf1 100644
--- a/radiant/selection/Device.h
+++ b/radiant/selection/Device.h
@@ -1,39 +1,27 @@
-#ifndef DEVICE_H_
-#define DEVICE_H_
+#pragma once
 
 #include "math/Vector2.h"
-#include <functional>
+#include <algorithm>
 
-// A vector representing the mouse pointer coordinates
-typedef Vector2 DeviceVector;
-typedef Vector2 WindowVector;
-typedef struct _GdkEventButton GdkEventButton;
-
-inline float screen_normalised(float pos, std::size_t size) {
-  return ((2.0f * pos) / size) - 1.0f;
+// Maps the range [0..size] to [-1..+1]
+inline float screen_normalised(Vector2::ElementType pos, std::size_t size)
+{
+	return ((2.0f * pos) / size) - 1.0f;
 }
 
-inline DeviceVector window_to_normalised_device(Vector2 window, std::size_t width, std::size_t height) {
-  return DeviceVector(screen_normalised(window.x(), width), screen_normalised(height - 1 - window.y(), height));
+inline Vector2 window_to_normalised_device(const Vector2& window, std::size_t width, std::size_t height)
+{
+	return Vector2(screen_normalised(window.x(), width), screen_normalised(height - 1 - window.y(), height));
 }
 
-/* greebo: This returns a number between -1 and +1
- * returns -1, if pos < -1
- * returns pos, if -1 < pos < +1
- * returns +1, if pos > +1
- *
- * So this performs some kind of cut-off of the value <pos> at the [-1,+1] boundaries
- */
-inline float device_constrained(float pos) {
-  return std::min(1.0f, std::max(-1.0f, pos));
+// Constrains the value pos to the range [-1..+1]
+inline Vector2::ElementType device_constrained(Vector2::ElementType pos)
+{
+	return std::min(1.0, std::max(-1.0, pos));
 }
 
-// See device_constrained(float), this cuts off the DeviceVector's components at -1 or +1
-inline DeviceVector device_constrained(DeviceVector device) {
-  return DeviceVector(device_constrained(device.x()), device_constrained(device.y()));
+// See device_constrained(), this cuts off the Vector's components at -1 or +1
+inline Vector2 device_constrained(const Vector2& device)
+{
+	return Vector2(device_constrained(device.x()), device_constrained(device.y()));
 }
-
-// greebo: The mouseOperations callbacks, should they really be a global?
-typedef std::function<void (const DeviceVector&)> MouseEventCallback;
-
-#endif /*DEVICE_H_*/
diff --git a/radiant/selection/DragManipulator.cpp b/radiant/selection/DragManipulator.cpp
index 6171171..235dc7c 100644
--- a/radiant/selection/DragManipulator.cpp
+++ b/radiant/selection/DragManipulator.cpp
@@ -111,7 +111,7 @@ void DragManipulator::testSelect(const render::View& view, const Matrix4& pivot2
 		ComponentSelector selectionTester(bestSelector, test, GlobalSelectionSystem().ComponentMode());
 		GlobalSelectionSystem().foreachSelected(selectionTester);
 
-		for (std::list<Selectable*>::iterator i = bestSelector.best().begin();
+		for (std::list<ISelectable*>::iterator i = bestSelector.best().begin();
 			 i != bestSelector.best().end(); ++i)
 		{
 			// greebo: Disabled this, it caused the currently selected patch vertices being deselected.
diff --git a/radiant/selection/OccludeSelector.h b/radiant/selection/OccludeSelector.h
index b52e2ad..0ceb2cc 100644
--- a/radiant/selection/OccludeSelector.h
+++ b/radiant/selection/OccludeSelector.h
@@ -20,7 +20,7 @@ public:
 		_occluded = false;
 	}
 
-	void pushSelectable(Selectable& selectable) {}
+	void pushSelectable(ISelectable& selectable) {}
 	void popSelectable() {}
 
 	void addIntersection(const SelectionIntersection& intersection)
diff --git a/radiant/selection/RadiantSelectionSystem.cpp b/radiant/selection/RadiantSelectionSystem.cpp
index 4a4a518..35b755e 100644
--- a/radiant/selection/RadiantSelectionSystem.cpp
+++ b/radiant/selection/RadiantSelectionSystem.cpp
@@ -2,8 +2,10 @@
 
 #include "iundo.h"
 #include "igrid.h"
+#include "iselectiongroup.h"
 #include "iradiant.h"
 #include "ieventmanager.h"
+#include "ilightnode.h"
 #include "imousetoolmanager.h"
 #include "editable.h"
 #include "Selectors.h"
@@ -23,39 +25,9 @@
 // Initialise the shader pointer
 ShaderPtr RadiantSelectionSystem::_state;
 
-    namespace {
-        const std::string RKEY_ROTATION_PIVOT = "user/ui/rotationPivotIsOrigin";
-    }
-
-// ------------ Helper Functions --------------------------------------------
-
-inline void matrix4_assign_rotation(Matrix4& matrix, const Matrix4& other) {
-    matrix[0] = other[0];
-    matrix[1] = other[1];
-    matrix[2] = other[2];
-    matrix[4] = other[4];
-    matrix[5] = other[5];
-    matrix[6] = other[6];
-    matrix[8] = other[8];
-    matrix[9] = other[9];
-    matrix[10] = other[10];
-}
-
-void matrix4_assign_rotation_for_pivot(Matrix4& matrix, const scene::INodePtr& node)
+namespace 
 {
-#if 0
-    EditablePtr editable = Node_getEditable(node);
-    // If the instance is editable, take the localpivot point into account, otherwise just apply the rotation
-    if (editable != 0) {
-        matrix4_assign_rotation(matrix, node->localToWorld().getMultipliedBy(editable->getLocalPivot()));
-    }
-    else
-    {
-        matrix4_assign_rotation(matrix, node->localToWorld());
-    }
-#else
-    return;
-#endif
+    const std::string RKEY_ROTATION_PIVOT = "user/ui/rotationPivotIsOrigin";
 }
 
 // --------- RadiantSelectionSystem Implementation ------------------------------------------
@@ -221,7 +193,7 @@ void RadiantSelectionSystem::pivotChanged() const
     SceneChangeNotify();
 }
 
-void RadiantSelectionSystem::pivotChangedSelection(const Selectable& selectable) {
+void RadiantSelectionSystem::pivotChangedSelection(const ISelectable& selectable) {
     pivotChanged();
 }
 
@@ -278,8 +250,8 @@ std::size_t RadiantSelectionSystem::countSelectedComponents() const {
 }
 
 // This is called if the selection changes, so that the local list of selected nodes can be updated
-void RadiantSelectionSystem::onSelectedChanged(const scene::INodePtr& node, const Selectable& selectable) {
-
+void RadiantSelectionSystem::onSelectedChanged(const scene::INodePtr& node, const ISelectable& selectable)
+{
     // Cache the selection state
     bool isSelected = selectable.isSelected();
     int delta = isSelected ? +1 : -1;
@@ -324,8 +296,8 @@ void RadiantSelectionSystem::onSelectedChanged(const scene::INodePtr& node, cons
 
 // greebo: This should be called "onComponentSelectionChanged", as it is a similar function of the above one
 // Updates the internal list of component nodes if the component selection gets changed
-void RadiantSelectionSystem::onComponentSelection(const scene::INodePtr& node, const Selectable& selectable) {
-
+void RadiantSelectionSystem::onComponentSelection(const scene::INodePtr& node, const ISelectable& selectable)
+{
     int delta = selectable.isSelected() ? +1 : -1;
 
     _countComponent += delta;
@@ -600,59 +572,101 @@ void RadiantSelectionSystem::SelectPoint(const render::View& view,
         }
 
         // Was the selection test successful (have we found anything to select)?
-        if (candidates.size() > 0) {
-            // Yes, now determine how we should interpret the click
-            switch (modifier) {
-                // If we are in toggle mode (Shift-Left-Click by default), just toggle the
-                // selection of the "topmost" item
-                case SelectionSystem::eToggle: {
-                    Selectable* best = *candidates.begin();
-                    // toggle selection of the object with least depth (=first in the list)
-                    best->setSelected(!best->isSelected());
-                }
-                break;
-                // greebo: eReplace mode gets active as soon as the user holds the replace modifiers down
-                // and clicks (by default: Alt-Shift). eReplace is only active during the first click
-                // afterwards we are in cycle mode.
-                // if cycle mode not enabled, enable it
-                case SelectionSystem::eReplace: {
-                    // select closest (=first in the list)
-                    (*candidates.begin())->setSelected(true);
-                }
-                break;
-                // select the next object in the list from the one already selected
-                // greebo: eCycle is set if the user keeps holding the replace modifiers (Alt-Shift)
-                // and does NOT move the mouse between the clicks, otherwise we fall back into eReplace mode
-                // Note: The mode is set in SelectObserver::testSelect()
-                case SelectionSystem::eCycle: {
-                    // Cycle through the selection pool and activate the item right after the currently selected
-                    SelectablesList::iterator i = candidates.begin();
-
-                    while (i != candidates.end()) {
-                        if ((*i)->isSelected()) {
-                            // unselect the currently selected one
-                            (*i)->setSelected(false);
-                            // check if there is a "next" item in the list, if not: select the first item
-                            ++i;
-                            if (i != candidates.end()) {
-                                (*i)->setSelected(true);
-                            }
-                            else {
-                                (*candidates.begin())->setSelected(true);
-                            }
-                            break;
-                        }
-                        ++i;
-                    } // while
-                } // case
-                break;
-                default:
-                break;
-            } // switch
-        }
+		performPointSelection(candidates, modifier);
     }
 }
 
+namespace algorithm
+{
+
+// If the selectable is a GroupSelectable, the respective callback is used
+inline void setSelectionStatus(ISelectable* selectable, bool selected)
+{
+	IGroupSelectable* groupSelectable = dynamic_cast<IGroupSelectable*>(selectable);
+
+	if (groupSelectable)
+	{
+		groupSelectable->setSelected(selected, true); // propagate selection state to group peers
+	}
+	else
+	{
+		selectable->setSelected(selected);
+	}
+}
+
+}
+
+void RadiantSelectionSystem::performPointSelection(const SelectablesList& candidates, EModifier modifier)
+{
+	if (candidates.empty()) return;
+
+	// Yes, now determine how we should interpret the click
+	switch (modifier) 
+	{
+		// If we are in toggle mode (Shift-Left-Click by default), just toggle the
+		// selection of the "topmost" item
+		case eToggle: 
+		{
+			ISelectable* best = candidates.front();
+			// toggle selection of the object with least depth (=first in the list)
+			algorithm::setSelectionStatus(best, !best->isSelected());
+		}
+		break;
+
+		// greebo: eReplace mode gets active as soon as the user holds the replace modifiers down
+		// and clicks (by default: Alt-Shift). eReplace is only active during the first click
+		// afterwards we are in cycle mode.
+		// if cycle mode not enabled, enable it
+		case eReplace:
+		{
+			// select closest (=first in the list)
+			algorithm::setSelectionStatus(candidates.front(), true);
+		}
+		break;
+
+		// select the next object in the list from the one already selected
+		// greebo: eCycle is set if the user keeps holding the replace modifiers (Alt-Shift)
+		// and does NOT move the mouse between the clicks, otherwise we fall back into eReplace mode
+		// Note: The mode is set in SelectObserver::testSelect()
+		case eCycle:
+		{
+			// Cycle through the selection pool and activate the item right after the currently selected
+			SelectablesList::const_iterator i = candidates.begin();
+
+			while (i != candidates.end())
+			{
+				if ((*i)->isSelected())
+				{
+					// unselect the currently selected one
+					algorithm::setSelectionStatus(*i, false);
+					//(*i)->setSelected(false);
+
+					// check if there is a "next" item in the list, if not: select the first item
+					++i;
+
+					if (i != candidates.end()) 
+					{
+						algorithm::setSelectionStatus(*i, true);
+						//(*i)->setSelected(true);
+					}
+					else
+					{
+						algorithm::setSelectionStatus(candidates.front(), true);
+						//candidates.front()->setSelected(true);
+					}
+					break;
+				}
+
+				++i;
+			}
+		}
+		break;
+
+		default: 
+			break;
+	};
+}
+
 /* greebo: This gets called by the SelectObserver if the user drags a box and holds down
  * any of the selection modifiers. Possible selection candidates are determined and selected/deselected
  */
@@ -697,10 +711,21 @@ void RadiantSelectionSystem::SelectArea(const render::View& view,
             testSelectScene(candidates, volume, scissored, Mode(), ComponentMode());
         }
 
-        // Cycle through the selection pool and toggle the candidates, but only if we are in toggle mode
-        for (SelectablesList::iterator i = candidates.begin(); i != candidates.end(); i++) {
-            (*i)->setSelected(!(modifier == SelectionSystem::eToggle && (*i)->isSelected()));
-        }
+		// Since toggling a selectable might trigger a group-selection
+		// we need to keep track of the desired state of each selectable 
+		typedef std::map<ISelectable*, bool> SelectablesMap;
+		SelectablesMap selectableStates;
+
+		for (ISelectable* selectable : candidates)
+		{
+			bool desiredState = !(modifier == SelectionSystem::eToggle && selectable->isSelected());
+			selectableStates.insert(SelectablesMap::value_type(selectable, desiredState));
+		}
+
+		for (const SelectablesMap::value_type& state : selectableStates)
+		{
+			algorithm::setSelectionStatus(state.first, state.second);
+		}
     }
 }
 
@@ -741,8 +766,6 @@ void RadiantSelectionSystem::rotate(const Quaternion& rotation)
     if (Mode() == eComponent)
     {
         Scene_Rotate_Component_Selected(GlobalSceneGraph(), _rotation, _pivot2world.t().getVector3());
-
-        matrix4_assign_rotation_for_pivot(_pivot2world, _componentSelection.ultimate());
     }
     else
     {
@@ -1006,8 +1029,6 @@ void RadiantSelectionSystem::keyChanged()
 /* greebo: This calculates and constructs the pivot point of the selection.
  * It cycles through all selected objects and creates its AABB. The origin point of the AABB
  * is basically the pivot point. Pivot2World is therefore a translation from (0,0,0) to the calculated origin.
- *
- * The pivot point is also snapped to the grid.
  */
 void RadiantSelectionSystem::ConstructPivot()
 {
@@ -1020,7 +1041,13 @@ void RadiantSelectionSystem::ConstructPivot()
 
     if (!nothingSelected())
     {
-        if (_selectionInfo.entityCount == 1 && _selectionInfo.totalCount == 1 &&
+		if (_selectionInfo.entityCount == 1 && _selectionInfo.totalCount == 1 &&
+			Node_getLightNode(ultimateSelected()))
+		{
+			// When a single light is selected, use the origin for rotation
+			objectPivot = Node_getLightNode(ultimateSelected())->getSelectAABB().origin;
+		}
+        else if (_selectionInfo.entityCount == 1 && _selectionInfo.totalCount == 1 &&
             registry::getValue<bool>(RKEY_ROTATION_PIVOT))
         {
             // Test, if a single entity is selected
@@ -1057,30 +1084,6 @@ void RadiantSelectionSystem::ConstructPivot()
 
         // The pivot2world matrix is just a translation from the world origin (0,0,0) to the object pivot
         _pivot2world = Matrix4::getTranslation(objectPivot);
-
-        // Only rotation and scaling need further calculations
-        switch (_manipulatorMode) {
-            case eTranslate:
-                break;
-            case eRotate:
-                if (Mode() == eComponent) {
-                    matrix4_assign_rotation_for_pivot(_pivot2world, _componentSelection.ultimate());
-                }
-                else {
-                    matrix4_assign_rotation_for_pivot(_pivot2world, _selection.ultimate());
-                }
-                break;
-            case eScale:
-                if (Mode() == eComponent) {
-                    matrix4_assign_rotation_for_pivot(_pivot2world, _componentSelection.ultimate());
-                }
-                else {
-                    matrix4_assign_rotation_for_pivot(_pivot2world, _selection.ultimate());
-                }
-                break;
-            default:
-                break;
-        } // switch
     }
 }
 /* greebo: Renders the currently active manipulator by setting the render state and
@@ -1088,8 +1091,8 @@ void RadiantSelectionSystem::ConstructPivot()
  */
 void RadiantSelectionSystem::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const {
     if (!nothingSelected()) {
-        collector.highlightFaces(false);
-        collector.highlightPrimitives(false);
+        collector.setHighlightFlag(RenderableCollector::Highlight::Faces, false);
+        collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false);
 
         collector.SetState(_state, RenderableCollector::eWireframeOnly);
         collector.SetState(_state, RenderableCollector::eFullMaterials);
@@ -1113,16 +1116,20 @@ const std::string& RadiantSelectionSystem::getName() const {
     return _name;
 }
 
-const StringSet& RadiantSelectionSystem::getDependencies() const {
+const StringSet& RadiantSelectionSystem::getDependencies() const
+{
     static StringSet _dependencies;
 
-    if (_dependencies.empty()) {
+    if (_dependencies.empty())
+	{
         _dependencies.insert(MODULE_RENDERSYSTEM);
         _dependencies.insert(MODULE_EVENTMANAGER);
         _dependencies.insert(MODULE_XMLREGISTRY);
+		_dependencies.insert(MODULE_RADIANT);
         _dependencies.insert(MODULE_GRID);
         _dependencies.insert(MODULE_SCENEGRAPH);
         _dependencies.insert(MODULE_MOUSETOOLMANAGER);
+		_dependencies.insert(MODULE_MAP);
     }
 
     return _dependencies;
@@ -1196,6 +1203,9 @@ void RadiantSelectionSystem::initialiseModule(const ApplicationContext& ctx)
     camGroup.registerMouseTool(std::make_shared<ui::DragSelectionMouseToolFaceOnly>());
     camGroup.registerMouseTool(std::make_shared<ui::CycleSelectionMouseTool>());
     camGroup.registerMouseTool(std::make_shared<ui::CycleSelectionMouseToolFaceOnly>());
+
+	GlobalMapModule().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &RadiantSelectionSystem::onMapEvent));
 }
 
 void RadiantSelectionSystem::shutdownModule() 
@@ -1210,7 +1220,7 @@ void RadiantSelectionSystem::shutdownModule()
     destroyStatic();
 }
 
-void RadiantSelectionSystem::checkComponentModeSelectionMode(const Selectable& selectable)
+void RadiantSelectionSystem::checkComponentModeSelectionMode(const ISelectable& selectable)
 {
 	// This seems to be a fail-safe method, to detect situations where component mode is still
 	// active without any primitive selected - in which case the method exits component mode.
@@ -1481,5 +1491,14 @@ void RadiantSelectionSystem::deselectCmd(const cmd::ArgumentList& args)
 	}
 }
 
+void RadiantSelectionSystem::onMapEvent(IMap::MapEvent ev)
+{
+	if (ev == IMap::MapUnloading || ev == IMap::MapLoading)
+	{
+		setSelectedAll(false);
+		setSelectedAllComponents(false);
+	}
+}
+
 // Define the static SelectionSystem module
 module::StaticModule<RadiantSelectionSystem> radiantSelectionSystemModule;
diff --git a/radiant/selection/RadiantSelectionSystem.h b/radiant/selection/RadiantSelectionSystem.h
index d396432..900e75a 100644
--- a/radiant/selection/RadiantSelectionSystem.h
+++ b/radiant/selection/RadiantSelectionSystem.h
@@ -3,7 +3,9 @@
 #include "iregistry.h"
 #include "irenderable.h"
 #include "iselection.h"
+#include "iradiant.h"
 #include "icommandsystem.h"
+#include "imap.h"
 
 #include "selectionlib.h"
 #include "math/Matrix4.h"
@@ -85,7 +87,7 @@ public:
 private:
 	SelectionInfo _selectionInfo;
 
-    sigc::signal<void, const Selectable&> _sigSelectionChanged;
+    sigc::signal<void, const ISelectable&> _sigSelectionChanged;
 
 	EManipulatorMode _defaultManipulatorMode;
 	EManipulatorMode _manipulatorMode;
@@ -138,7 +140,7 @@ public:
 
 	void pivotChanged() const;
 
-  	void pivotChangedSelection(const Selectable& selectable);
+  	void pivotChangedSelection(const ISelectable& selectable);
 
 	void addObserver(Observer* observer);
 	void removeObserver(Observer* observer);
@@ -155,8 +157,8 @@ public:
 	std::size_t countSelected() const;
 	std::size_t countSelectedComponents() const;
 
-	void onSelectedChanged(const scene::INodePtr& node, const Selectable& selectable);
-	void onComponentSelection(const scene::INodePtr& node, const Selectable& selectable);
+	void onSelectedChanged(const scene::INodePtr& node, const ISelectable& selectable);
+	void onComponentSelection(const scene::INodePtr& node, const ISelectable& selectable);
 
     SelectionChangedSignal signal_selectionChanged() const
     {
@@ -210,15 +212,15 @@ public:
 
 	const selection::WorkZone& getWorkZone();
 
-	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const;
-	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const;
+	void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override;
+	void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override;
 
-	void setRenderSystem(const RenderSystemPtr& renderSystem)
+	void setRenderSystem(const RenderSystemPtr& renderSystem) override
 	{}
 
-	bool isHighlighted() const
+	std::size_t getHighlightFlags() override
 	{
-		return false; // never highlighted
+		return Highlight::NoHighlight; // never highlighted
 	}
 
 	const Matrix4& GetPivot2World() const;
@@ -227,10 +229,10 @@ public:
 	static void destroyStatic();
 
 	// RegisterableModule implementation
-	virtual const std::string& getName() const;
-	virtual const StringSet& getDependencies() const;
-	virtual void initialiseModule(const ApplicationContext& ctx);
-	virtual void shutdownModule();
+	virtual const std::string& getName() const override;
+	virtual const StringSet& getDependencies() const override;
+	virtual void initialiseModule(const ApplicationContext& ctx) override;
+	virtual void shutdownModule() override;
 
 protected:
 	// Called when the app is idle to recalculate the workzone (if necessary)
@@ -265,7 +267,11 @@ private:
 	void onManipulatorModeChanged();
 	void onComponentModeChanged();
 
-	void checkComponentModeSelectionMode(const Selectable& selectable); // connects to the selection change signal
+	void checkComponentModeSelectionMode(const ISelectable& selectable); // connects to the selection change signal
+
+	void performPointSelection(const SelectablesList& candidates, EModifier modifier);
 
 	void deselectCmd(const cmd::ArgumentList& args);
+
+	void onMapEvent(IMap::MapEvent ev);
 };
diff --git a/radiant/selection/SceneWalkers.h b/radiant/selection/SceneWalkers.h
index 3dae274..dc08c64 100644
--- a/radiant/selection/SceneWalkers.h
+++ b/radiant/selection/SceneWalkers.h
@@ -10,24 +10,6 @@
 #include "editable.h"
 #include "brush/BrushNode.h"
 
-// -------------- Helper functions -------------------------------------
-
-inline AABB Node_getPivotBounds(const scene::INodePtr& node) {
-	Entity* entity = Node_getEntity(node);
-	if (entity != NULL && (entity->getEntityClass()->isFixedSize() || !scene::hasChildPrimitives(node)))
-	{
-		EditablePtr editable = Node_getEditable(node);
-		if (editable != NULL) {
-			return AABB(node->localToWorld().getMultipliedBy(editable->getLocalPivot()).t().getVector3(), Vector3(0, 0, 0));
-		}
-		else {
-			return node->worldAABB();
-		}
-	}
-
-	return node->worldAABB();
-}
-
 // ----------- The Walker Classes ------------------------------------------------
 
 // Selects the visited component instances in the graph, according to the current component mode
diff --git a/radiant/selection/SelectionTest.cpp b/radiant/selection/SelectionTest.cpp
index 177adac..a28d47f 100644
--- a/radiant/selection/SelectionTest.cpp
+++ b/radiant/selection/SelectionTest.cpp
@@ -213,13 +213,13 @@ scene::INodePtr SelectionTestWalker::getParentGroupEntity(const scene::INodePtr&
 
 bool SelectionTestWalker::entityIsWorldspawn(const scene::INodePtr& node)
 {
-	return node_is_worldspawn(node);
+	return Node_isWorldspawn(node);
 }
 
 void SelectionTestWalker::performSelectionTest(const scene::INodePtr& selectableNode, 
 	const scene::INodePtr& nodeToBeTested)
 {
-	SelectablePtr selectable = Node_getSelectable(selectableNode);
+	ISelectablePtr selectable = Node_getSelectable(selectableNode);
 
 	if (selectable == NULL) return; // skip non-selectables
 
diff --git a/radiant/selection/Selectors.h b/radiant/selection/Selectors.h
index 1c195be..63f6f95 100644
--- a/radiant/selection/Selectors.h
+++ b/radiant/selection/Selectors.h
@@ -1,17 +1,16 @@
-#ifndef SELECTOR_H_
-#define SELECTOR_H_
+#pragma once
 
 #include <map>
 #include <set>
 #include "iselectiontest.h"
 #include "iselectable.h"
 
-typedef std::multimap<SelectionIntersection, Selectable*> SelectableSortedSet;
+typedef std::multimap<SelectionIntersection, ISelectable*> SelectableSortedSet;
 
 // A simple set that gets filled after the SelectableSortedSet is populated.
 // greebo: I used this to merge two SelectionPools (entities and primitives)
 // 		   with a preferred sorting (see RadiantSelectionSystem::Scene_TestSelect())
-typedef std::list<Selectable*> SelectablesList;
+typedef std::list<ISelectable*> SelectablesList;
 
 /* greebo: The SelectionPool contains all the instances that come into question for a selection operation.
  * It can be seen as some kind of stack that can be traversed through
@@ -25,12 +24,12 @@ class SelectionPool :
 {
 	SelectableSortedSet 	_pool;
 	SelectionIntersection	_intersection;
-	Selectable* 			_selectable;
+	ISelectable* 			_selectable;
 
 	// A set of all current Selectable* candidates, to prevent double-insertions
 	// The iterator value points to an element in the SelectableSortedSet
 	// to allow for fast removals.
-	typedef std::map<Selectable*, SelectableSortedSet::iterator> SelectablesMap;
+	typedef std::map<ISelectable*, SelectableSortedSet::iterator> SelectablesMap;
 	SelectablesMap _currentSelectables;
 
 public:
@@ -39,7 +38,7 @@ public:
 	 * 			tested against selection to notify the SelectionPool
 	 * 			which Selectable we're talking about.
 	 */
-	void pushSelectable(Selectable& selectable)
+	void pushSelectable(ISelectable& selectable)
 	{
 		_intersection = SelectionIntersection();
 		_selectable = &selectable;
@@ -69,7 +68,7 @@ public:
 	/** greebo: This makes sure that only valid Intersections get added, otherwise
 	 * 			we would add Selectables that haven't passed the test.
 	 */
-	void addSelectable(const SelectionIntersection& intersection, Selectable* selectable)
+	void addSelectable(const SelectionIntersection& intersection, ISelectable* selectable)
 	{
 		if (!intersection.valid()) return; // skip invalid intersections
 
@@ -126,13 +125,13 @@ public:
 class BooleanSelector : public Selector {
   bool _selected;
   SelectionIntersection _intersection;
-  Selectable* _selectable;
+  ISelectable* _selectable;
 public:
   BooleanSelector() : _selected(false), _selectable(NULL)
   {
   }
 
-  void pushSelectable(Selectable& selectable)
+  void pushSelectable(ISelectable& selectable)
   {
     _intersection = SelectionIntersection();
     _selectable = &selectable;
@@ -162,16 +161,16 @@ public:
 
 class BestSelector : public Selector {
   SelectionIntersection _intersection;
-  Selectable* _selectable;
+  ISelectable* _selectable;
   SelectionIntersection _bestIntersection;
-  std::list<Selectable*> _bestSelectable;
+  std::list<ISelectable*> _bestSelectable;
 
 public:
   BestSelector() : _bestIntersection(SelectionIntersection()), _bestSelectable(0)
   {
   }
 
-  void pushSelectable(Selectable& selectable)
+  void pushSelectable(ISelectable& selectable)
   {
     _intersection = SelectionIntersection();
     _selectable = &selectable;
@@ -198,10 +197,8 @@ public:
     assign_if_closer(_intersection, intersection);
   }
 
-  std::list<Selectable*>& best()
+  std::list<ISelectable*>& best()
   {
     return _bestSelectable;
   }
 };
-
-#endif /*SELECTOR_H_*/
diff --git a/radiant/selection/TransformationVisitors.cpp b/radiant/selection/TransformationVisitors.cpp
index 9fe088e..b713b1d 100644
--- a/radiant/selection/TransformationVisitors.cpp
+++ b/radiant/selection/TransformationVisitors.cpp
@@ -105,16 +105,13 @@ void ScaleSelected::visit(const scene::INodePtr& node) const {
         transform->setType(TRANSFORM_PRIMITIVE);
         transform->setScale(m_scale);
         {
-          EditablePtr editable = Node_getEditable(node);
-          const Matrix4& localPivot = editable != 0 ? editable->getLocalPivot() : Matrix4::getIdentity();
-
           Vector3 parent_translation;
           translation_for_pivoted_scale(
             parent_translation,
             m_scale,
             m_world_pivot,
-			node->localToWorld().getMultipliedBy(localPivot),
-			transformNode->localToParent().getMultipliedBy(localPivot)
+			node->localToWorld(),
+			transformNode->localToParent()
           );
 
           transform->setTranslation(parent_translation);
diff --git a/radiant/selection/TranslateManipulator.cpp b/radiant/selection/TranslateManipulator.cpp
index cf3cb5a..706b4e6 100644
--- a/radiant/selection/TranslateManipulator.cpp
+++ b/radiant/selection/TranslateManipulator.cpp
@@ -149,7 +149,7 @@ void TranslateManipulator::testSelect(const render::View& view, const Matrix4& p
     if(!selector.failed()) {
       (*selector.begin()).second->setSelected(true);
     } else {
-    	Selectable* selectable = NULL;
+    	ISelectable* selectable = NULL;
 
     	if (registry::getValue<bool>(RKEY_TRANSLATE_CONSTRAINED)) {
 	    	// None of the shown arrows (or quad) has been selected, select an axis based on the precedence
diff --git a/radiant/selection/algorithm/CommandNotAvailableException.h b/radiant/selection/algorithm/CommandNotAvailableException.h
new file mode 100644
index 0000000..a142dfa
--- /dev/null
+++ b/radiant/selection/algorithm/CommandNotAvailableException.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <stdexcept>
+
+namespace selection
+{
+
+namespace algorithm
+{
+
+/**
+ * Exception thrown by some algorithm routines if the desired
+ * command cannot be executed due to the current state of the
+ * scenegraph, selection or other reasons.
+ */
+class CommandNotAvailableException :
+	public std::exception
+{
+private:
+	std::string _what;
+public:
+	CommandNotAvailableException(const std::string& msg) :
+		std::exception(),
+		_what(msg)
+	{}
+
+	virtual const char* what() const throw() override
+	{
+		return _what.c_str();
+	}
+
+	// Converts a function that is throwing a CommandNotAvailableException
+	// to one that returns false instead (true if no exception is thrown)
+	static bool ToBool(const std::function<void()>& func)
+	{
+		try
+		{
+			func();
+			return true;
+		}
+		catch (CommandNotAvailableException&)
+		{
+			return false;
+		}
+	}
+};
+
+}
+
+}
diff --git a/radiant/selection/algorithm/Entity.cpp b/radiant/selection/algorithm/Entity.cpp
index edc9a76..5462142 100644
--- a/radiant/selection/algorithm/Entity.cpp
+++ b/radiant/selection/algorithm/Entity.cpp
@@ -16,66 +16,131 @@
 #include "map/Map.h"
 #include "eclass.h"
 
-namespace selection {
-	namespace algorithm {
+namespace selection 
+{
+
+namespace algorithm 
+{
 
 const char* const GKEY_BIND_KEY("/defaults/bindKey");
 
-/**
- * greebo: This walker traverses a subgraph and changes the classname
- *         of all selected entities to the one passed to the constructor.
- */
-class EntitySetClassnameSelected :
-	public SelectionSystem::Visitor
+void setEntityKeyvalue(const scene::INodePtr& node, const std::string& key, const std::string& value)
 {
-	std::string _classname;
-
-	// Entites are getting accumulated in this list and processed at destruction time
-	mutable std::set<scene::INodePtr> _entities;
-public:
-	EntitySetClassnameSelected(const std::string& classname) :
-		_classname(classname)
-	{}
-
-	~EntitySetClassnameSelected() {
-		for (std::set<scene::INodePtr>::iterator i = _entities.begin();
-			 i != _entities.end(); ++i)
+	Entity* entity = Node_getEntity(node);
+
+	if (entity)
+	{
+		// Check if we have a func_static-style entity
+		std::string name = entity->getKeyValue("name");
+		std::string model = entity->getKeyValue("model");
+		bool isFuncType = (!name.empty() && name == model);
+
+		// Set the actual value
+		entity->setKeyValue(key, value);
+
+		// Check for name key changes of func_statics
+		if (isFuncType && key == "name")
 		{
-			// "Rename" the entity, this deletes the old node and creates a new one
-			scene::INodePtr newNode = changeEntityClassname(*i, _classname);
+			// Adapt the model key along with the name
+			entity->setKeyValue("model", value);
+		}
+	}
+	else if (Node_isPrimitive(node))
+	{
+		// We have a primitve node selected, check its parent
+		scene::INodePtr parent(node->getParent());
 
-			// Select the new entity node
-			Node_setSelected(newNode, true);
+		if (!parent) return;
+
+		Entity* parentEnt = Node_getEntity(parent);
+
+		if (parentEnt)
+		{
+			// We have child primitive of an entity selected, the change
+			// should go right into that parent entity
+			parentEnt->setKeyValue(key, value);
 		}
 	}
+}
 
-	virtual void visit(const scene::INodePtr& node) const {
-		// Check if we have an entity
-		Entity* entity = Node_getEntity(node);
+void setEntityKeyvalue(const std::string& key, const std::string& value)
+{
+	if (key.empty()) return;
 
-		if (entity != NULL && Node_isSelected(node)) {
-			if (entity->getKeyValue("classname") != "worldspawn") {
-				_entities.insert(node);
-			}
-			else {
-				wxutil::Messagebox::ShowError(
-					_("Cannot change classname of worldspawn entity."));
+	if (key == "name")
+	{
+		// Check the global namespace if this change is ok
+		scene::IMapRootNodePtr mapRoot = GlobalMapModule().getRoot();
+
+		if (mapRoot)
+		{
+			INamespacePtr nspace = mapRoot->getNamespace();
+
+			if (nspace && nspace->nameExists(value))
+			{
+				// name exists, cancel the change
+				throw std::runtime_error((boost::format(_("The name %s already exists in this map!")) % value).str());
 			}
 		}
 	}
-};
 
-void setEntityClassname(const std::string& classname) {
+	// Detect classname changes
+	if (key == "classname")
+	{
+		// Classname changes are handled in a special way
+		setEntityClassname(value);
+		return;
+	}
+		
+	// Regular key change, set value on all selected entities
+	GlobalSelectionSystem().foreachSelected([&](const scene::INodePtr& node)
+	{
+		setEntityKeyvalue(node, key, value);
+	});
+}
 
-	if (classname.empty()) {
-		rError() << "Cannot set classname to an empty string!" << std::endl;
+void setEntityClassname(const std::string& classname) 
+{
+	if (classname.empty())
+	{
+		wxutil::Messagebox::ShowError(_("Cannot set classname to an empty string."));
+		return;
 	}
 
-	// greebo: instantiate a walker and traverse the current selection
-	EntitySetClassnameSelected classnameSetter(classname);
-	GlobalSelectionSystem().foreachSelected(classnameSetter);
+	if (classname == "worldspawn")
+	{
+		throw std::runtime_error(_("Cannot change classname to worldspawn."));
+	}
+
+	std::set<scene::INodePtr> entitiesToProcess;
+
+	// Collect all entities that should have their classname set
+	GlobalSelectionSystem().foreachSelected([&](const scene::INodePtr& node)
+	{
+		// Check if we have an entity
+		Entity* entity = Node_getEntity(node);
 
-	// The destructor of the classNameSetter will rename the entities
+		if (entity != NULL && Node_isSelected(node))
+		{
+			if (!entity->isWorldspawn())
+			{
+				entitiesToProcess.insert(node);
+			}
+			else
+			{
+				throw std::runtime_error(_("Cannot change classname of worldspawn entity."));
+			}
+		}
+	});
+
+	for (const scene::INodePtr& node : entitiesToProcess)
+	{
+		// "Rename" the entity, this deletes the old node and creates a new one
+		scene::INodePtr newNode = changeEntityClassname(node, classname);
+
+		// Select the new entity node
+		Node_setSelected(newNode, true);
+	}
 }
 
 void bindEntities(const cmd::ArgumentList& args) {
@@ -281,5 +346,5 @@ scene::INodePtr createEntityFromSelection(const std::string& name, const Vector3
     return node;
 }
 
-	} // namespace algorithm
+} // namespace algorithm
 } // namespace selection
diff --git a/radiant/selection/algorithm/Entity.h b/radiant/selection/algorithm/Entity.h
index d6d2a41..74a4546 100644
--- a/radiant/selection/algorithm/Entity.h
+++ b/radiant/selection/algorithm/Entity.h
@@ -12,6 +12,16 @@ namespace algorithm
 {
 
 /**
+ * Applies the key/value combination to the currently selected entities.
+ * It's safe to set a "classname" key through this method.
+ * This doesn't open an UndoableCommand session, take care of this in the 
+ * client code.
+ *
+ * Throws a std::runtime_error in case the keyvalue cannot be applied.
+ */
+void setEntityKeyvalue(const std::string& key, const std::string& value);
+
+/**
  * greebo: Changes the classname of the currently selected entities.
  */
 void setEntityClassname(const std::string& classname);
diff --git a/radiant/selection/algorithm/General.cpp b/radiant/selection/algorithm/General.cpp
index e4d2223..6482a44 100644
--- a/radiant/selection/algorithm/General.cpp
+++ b/radiant/selection/algorithm/General.cpp
@@ -16,6 +16,7 @@
 #include "ui/texturebrowser/TextureBrowser.h"
 #include "string/convert.h"
 #include "selectionlib.h"
+#include "entitylib.h"
 
 #include "SelectionPolicies.h"
 #include "selection/SceneWalkers.h"
@@ -313,11 +314,20 @@ void showAllHidden(const cmd::ArgumentList& args) {
 	SceneChangeNotify();
 }
 
+/**
+ * Invert Selection walker
+ * Worldspawn brushes and patches will be considered, unless 
+ * entity selection mode is active.
+ * An entity with or without any child nodes will be considered. 
+ * The worldspawn node itself will be ignored. 
+ * func_static children will be ignored.
+ */
 class InvertSelectionWalker :
 	public scene::NodeVisitor
 {
+private:
 	SelectionSystem::EMode _mode;
-	SelectablePtr _selectable;
+
 public:
 	InvertSelectionWalker(SelectionSystem::EMode mode) :
 		_mode(mode)
@@ -325,44 +335,103 @@ public:
 
 	bool pre(const scene::INodePtr& node)
 	{
-		// Ignore hidden nodes
+		// Ignore hidden nodes (including their whole subgraph)
 		if (!node->visible()) return false;
 
 		Entity* entity = Node_getEntity(node);
 
 		// Check if we have a selectable
-		SelectablePtr selectable = Node_getSelectable(node);
+		ISelectablePtr selectable = Node_getSelectable(node);
 
-		if (selectable != NULL)
+		if (selectable)
 		{
 			switch (_mode)
 			{
 				case SelectionSystem::eEntity:
-					if (entity != NULL && entity->getKeyValue("classname") != "worldspawn")
+					// Only consider non-worldspawn entities
+					if (entity != nullptr && !entity->isWorldspawn())
 					{
-						_selectable = selectable;
+						selectable->setSelected(!selectable->isSelected());
 					}
 					break;
 				case SelectionSystem::ePrimitive:
-					_selectable = selectable;
+					// Never select the worldspawn entity
+					if (entity == nullptr || !entity->isWorldspawn())
+					{
+						selectable->setSelected(!selectable->isSelected());
+					}
 					break;
-				case SelectionSystem::eComponent:
-					// Check if we have a componentselectiontestable instance
-					ComponentSelectionTestablePtr compSelTestable =
-						Node_getComponentSelectionTestable(node);
-
-					// Only add it to the list if the instance has components and is already selected
-					if (compSelTestable != NULL && selectable->isSelected())
+				case SelectionSystem::eGroupPart:
+					// All child primitives can be selected (worldspawn entity is filtered out)
+					if (entity == nullptr && Node_isEntity(node->getParent()))
 					{
-						_selectable = selectable;
+						selectable->setSelected(!selectable->isSelected());
 					}
 					break;
+				case SelectionSystem::eComponent:
+					break; // not handled here
+			}
+		}
+
+		// Determine whether we want to traverse child objects of entities
+		if (entity != nullptr)
+		{
+			if (_mode == SelectionSystem::eGroupPart)
+			{
+				// In group part mode, we want to traverse all entities but worldspawn
+				return !entity->isWorldspawn();
+			}
+			else if (_mode == SelectionSystem::eEntity)
+			{
+				// In Entity selection mode, we don't need to traverse entity subgraphs
+				// as we only want the parent entity
+				return false;
+			}
+
+			// In primitive mode, we want to traverse worldspawn only
+			return entity->isWorldspawn();
+		}
+
+		// We can traverse all non-entities by default
+		return true;
+	}
+};
+
+class InvertComponentSelectionWalker :
+	public scene::NodeVisitor
+{
+	SelectionSystem::EComponentMode _mode;
+	ComponentSelectionTestablePtr _selectable;
+public:
+	InvertComponentSelectionWalker(SelectionSystem::EComponentMode mode) :
+		_mode(mode)
+	{}
+
+	bool pre(const scene::INodePtr& node)
+	{
+		// Ignore hidden nodes
+		if (!node->visible()) return false;
+
+		Entity* entity = Node_getEntity(node);
+
+		// Check if we have a selectable
+		ISelectablePtr selectable = Node_getSelectable(node);
+
+		if (selectable != NULL)
+		{
+			// Check if we have a componentselectiontestable instance
+			ComponentSelectionTestablePtr compSelTestable =
+				Node_getComponentSelectionTestable(node);
+
+			// Only add it to the list if the instance has components and is already selected
+			if (compSelTestable && selectable->isSelected())
+			{
+				_selectable = compSelTestable;
 			}
 		}
 
 		// Do we have a groupnode? If yes, don't traverse the children
-		if (entity != NULL && scene::hasChildPrimitives(node) &&
-			entity->getKeyValue("classname") != "worldspawn")
+		if (entity != NULL && scene::hasChildPrimitives(node) && !entity->isWorldspawn())
 		{
 			// Don't traverse the children of this groupnode
 			return false;
@@ -373,17 +442,26 @@ public:
 
 	void post(const scene::INodePtr& node)
 	{
-		if (_selectable != NULL)
+		if (_selectable)
 		{
-			_selectable->invertSelected();
-			_selectable = SelectablePtr();
+			_selectable->invertSelectedComponents(_mode);
+			_selectable.reset();
 		}
 	}
 };
 
-void invertSelection(const cmd::ArgumentList& args) {
-	InvertSelectionWalker walker(GlobalSelectionSystem().Mode());
-	GlobalSceneGraph().root()->traverse(walker);
+void invertSelection(const cmd::ArgumentList& args)
+{
+	if (GlobalSelectionSystem().Mode() == SelectionSystem::eComponent)
+	{
+		InvertComponentSelectionWalker walker(GlobalSelectionSystem().ComponentMode());
+		GlobalSceneGraph().root()->traverse(walker);
+	}
+	else
+	{
+		InvertSelectionWalker walker(GlobalSelectionSystem().Mode());
+		GlobalSceneGraph().root()->traverse(walker);
+	}
 }
 
 void deleteSelection()
@@ -454,12 +532,12 @@ public:
 			return false;
 		}
 
-		SelectablePtr selectable = Node_getSelectable(node);
+		ISelectablePtr selectable = Node_getSelectable(node);
 
 		// ignore worldspawn
 		Entity* entity = Node_getEntity(node);
 		if (entity != NULL) {
-			if (entity->getKeyValue("classname") == "worldspawn") {
+			if (entity->isWorldspawn()) {
 				return true;
 			}
 		}
@@ -575,7 +653,7 @@ AABB getCurrentSelectionBounds()
 
 	GlobalSelectionSystem().foreachSelected([&] (const scene::INodePtr& node)
 	{
-		bounds.includeAABB(Node_getPivotBounds(node));
+		bounds.includeAABB(node->worldAABB());
 	});
 
 	return bounds;
diff --git a/radiant/selection/algorithm/Group.cpp b/radiant/selection/algorithm/Group.cpp
index d6e7f07..6f22cf6 100644
--- a/radiant/selection/algorithm/Group.cpp
+++ b/radiant/selection/algorithm/Group.cpp
@@ -5,9 +5,11 @@
 #include "igroupnode.h"
 #include "imainframe.h"
 #include "itextstream.h"
+#include "iselectiongroup.h"
 #include "selectionlib.h"
 #include "entitylib.h"
 #include "map/Map.h"
+#include "scene/SelectableNode.h"
 #include "wxutil/dialog/MessageBox.h"
 #include "selection/algorithm/Entity.h"
 
@@ -107,7 +109,7 @@ void ParentPrimitivesToEntityWalker::reparent()
 		if (!scene::hasChildPrimitives(oldParent))
 		{
 			// Is empty, but make sure we're not removing the worldspawn
-			if (node_is_worldspawn(oldParent)) continue;
+			if (Node_isWorldspawn(oldParent)) continue;
 
 			// Is empty now, remove it
 			scene::removeNodeFromParent(oldParent);
@@ -360,12 +362,12 @@ void mergeSelectedEntities(const cmd::ArgumentList& args)
 		scene::INodePtr newParent = walker.getFirstSelectedGroupNode();
 
 		// Gather all group nodes in the selection
-		GroupNodeCollector walker;
-		GlobalSelectionSystem().foreachSelected(walker);
+		GroupNodeCollector collector;
+		GlobalSelectionSystem().foreachSelected(collector);
 
 		// Traverse all group nodes using a ParentPrimitivesToEntityWalker
-		for (GroupNodeCollector::GroupNodeList::const_iterator i = walker.getList().begin();
-			 i != walker.getList().end(); ++i)
+		for (GroupNodeCollector::GroupNodeList::const_iterator i = collector.getList().begin();
+			 i != collector.getList().end(); ++i)
 		{
 			if (*i == newParent) continue;
 
@@ -375,7 +377,7 @@ void mergeSelectedEntities(const cmd::ArgumentList& args)
 			reparentor.reparent();
 		}
 
-		rMessage() << walker.getList().size() << " group nodes merged." << std::endl;
+		rMessage() << collector.getList().size() << " group nodes merged." << std::endl;
 	}
 	else
 	{
@@ -385,6 +387,131 @@ void mergeSelectedEntities(const cmd::ArgumentList& args)
 	}
 }
 
+void checkGroupSelectedAvailable()
+{
+	if (GlobalSelectionSystem().Mode() != SelectionSystem::ePrimitive)
+	{
+		throw CommandNotAvailableException(_("Groups can be formed in Primitive selection mode only"));
+	}
+
+	if (GlobalSelectionSystem().getSelectionInfo().totalCount == 0)
+	{
+		throw CommandNotAvailableException(_("Nothing selected, cannot group anything"));
+	}
+
+	if (GlobalSelectionSystem().getSelectionInfo().totalCount == 1)
+	{
+		throw CommandNotAvailableException(_("Select more than one element to form a group"));
+		return;
+	}
+
+	// Check if the current selection already is member of the same group
+	std::set<std::size_t> groupIds;
+	bool hasUngroupedNode = false;
+
+	GlobalSelectionSystem().foreachSelected([&](const scene::INodePtr& node)
+	{
+		std::shared_ptr<IGroupSelectable> selectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+		if (!selectable) return;
+
+		if (!selectable->getGroupIds().empty())
+		{
+			groupIds.insert(selectable->getMostRecentGroupId());
+		}
+		else
+		{
+			hasUngroupedNode = true;
+		}
+	});
+
+	if (!hasUngroupedNode && groupIds.size() == 1)
+	{
+		throw CommandNotAvailableException(_("The selected elements already form a group"));
+	}
+}
+
+void groupSelected()
+{
+	// This will throw exceptions
+	checkGroupSelectedAvailable();
+
+	UndoableCommand cmd("GroupSelected");
+
+	ISelectionGroupPtr group = GlobalSelectionGroupManager().createSelectionGroup();
+
+	GlobalSelectionSystem().foreachSelected([&](const scene::INodePtr& node)
+	{
+		group->addNode(node);
+	});
+
+	GlobalMainFrame().updateAllWindows();
+}
+
+void checkUngroupSelectedAvailable()
+{
+	if (GlobalSelectionSystem().Mode() != SelectionSystem::ePrimitive)
+	{
+		throw CommandNotAvailableException(_("Groups can be dissolved in Primitive selection mode only"));
+	}
+
+	if (GlobalSelectionSystem().getSelectionInfo().totalCount == 0)
+	{
+		throw CommandNotAvailableException(_("Nothing selected, cannot un-group anything"));
+	}
+
+	// Check if the current selection already is member of the same group
+	bool hasOnlyUngroupedNodes = true;
+
+	GlobalSelectionSystem().foreachSelected([&](const scene::INodePtr& node)
+	{
+		std::shared_ptr<IGroupSelectable> selectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+		if (!selectable) return;
+
+		if (!selectable->getGroupIds().empty())
+		{
+			hasOnlyUngroupedNodes = false;
+		}
+	});
+
+	if (hasOnlyUngroupedNodes)
+	{
+		throw CommandNotAvailableException(_("The selected elements aren't part of any group"));
+	}
+}
+
+void ungroupSelected()
+{
+	// Will throw exceptions if not available
+	checkUngroupSelectedAvailable();
+
+	UndoableCommand cmd("UngroupSelected");
+
+	// Collect all the latest group Ids from all selected nodes
+	std::set<std::size_t> ids;
+
+	GlobalSelectionSystem().foreachSelected([&](const scene::INodePtr& node)
+	{
+		std::shared_ptr<scene::SelectableNode> selectable = std::dynamic_pointer_cast<scene::SelectableNode>(node);
+
+		if (!selectable) return;
+
+		if (selectable->isGroupMember())
+		{
+			ids.insert(selectable->getMostRecentGroupId());
+		}
+	});
+
+	// Now remove the found group by ID (maybe convert them to a selection set before removal?)
+	std::for_each(ids.begin(), ids.end(), [](std::size_t id)
+	{
+		GlobalSelectionGroupManager().deleteSelectionGroup(id);
+	});
+
+	GlobalMainFrame().updateAllWindows();
+}
+
 } // namespace algorithm
 
 } // namespace selection
diff --git a/radiant/selection/algorithm/Group.h b/radiant/selection/algorithm/Group.h
index cd82f50..c86246b 100644
--- a/radiant/selection/algorithm/Group.h
+++ b/radiant/selection/algorithm/Group.h
@@ -1,10 +1,10 @@
-#ifndef SELECTIONGROUP_H_
-#define SELECTIONGROUP_H_
+#pragma once
 
 #include "icommandsystem.h"
 #include "iselection.h"
 #include "inode.h"
 #include <list>
+#include "CommandNotAvailableException.h"
 
 namespace selection {
 	namespace algorithm {
@@ -137,7 +137,29 @@ namespace selection {
 	 */
 	void mergeSelectedEntities(const cmd::ArgumentList& args);
 
+	/**
+	 * Groups the currently selected elements.
+	 * Will throw a CommandNotAvailableException if it cannot execute.
+	 */
+	void groupSelected();
+
+	/**
+	 * Returns if the groupSelected command is able to execute
+	 * at this point, otherwise throws a CommandNotAvailableException
+	 */
+	void checkGroupSelectedAvailable();
+
+	/**
+	 * Resolve the currently selected group.
+	 * Will throw a CommandNotAvailableException if it cannot execute.
+	 */
+	void ungroupSelected();
+
+	/**
+	* Returns if the ungroupSelected command is able to execute
+	* at this point, otherwise throws a CommandNotAvailableException.
+	*/
+	void checkUngroupSelectedAvailable();
+
 	} // namespace algorithm
 } // namespace selection
-
-#endif /*SELECTIONGROUP_H_*/
diff --git a/radiant/selection/algorithm/GroupCycle.cpp b/radiant/selection/algorithm/GroupCycle.cpp
index 877f8d0..7fe3881 100644
--- a/radiant/selection/algorithm/GroupCycle.cpp
+++ b/radiant/selection/algorithm/GroupCycle.cpp
@@ -21,7 +21,7 @@ namespace algorithm {
 		{}
 
 		bool pre(const scene::INodePtr& node) {
-			SelectablePtr selectable = Node_getSelectable(node);
+			ISelectablePtr selectable = Node_getSelectable(node);
 
 			// If a visible selectable was found and the path depth is appropriate, add it
 			if (selectable != NULL && node->visible()) {
diff --git a/radiant/selection/algorithm/Patch.cpp b/radiant/selection/algorithm/Patch.cpp
index d515d58..28a1519 100644
--- a/radiant/selection/algorithm/Patch.cpp
+++ b/radiant/selection/algorithm/Patch.cpp
@@ -83,17 +83,6 @@ void capPatch(const cmd::ArgumentList& args)
 	createPatchCaps(GlobalTextureBrowser().getSelectedShader());
 }
 
-void cyclePatchProjection(const cmd::ArgumentList& args)
-{
-	UndoableCommand undo("patchCycleUVProjectionAxis");
-
-	GlobalSelectionSystem().foreachPatch([&] (Patch& patch) { patch.ProjectTexture(Patch::m_CycleCapIndex); });
-
-	Patch::m_CycleCapIndex = (Patch::m_CycleCapIndex == 0) ? 1 : (Patch::m_CycleCapIndex == 1) ? 2 : 0;
-
-	SceneChangeNotify();
-}
-
 void insertPatchColumnsAtEnd(const cmd::ArgumentList& args)
 {
 	UndoableCommand undo("patchInsertColumnsAtEnd");
diff --git a/radiant/selection/algorithm/Patch.h b/radiant/selection/algorithm/Patch.h
index 4c4dae5..ac556ff 100644
--- a/radiant/selection/algorithm/Patch.h
+++ b/radiant/selection/algorithm/Patch.h
@@ -18,8 +18,6 @@ void transposePatch(const cmd::ArgumentList& args);
 
 void capPatch(const cmd::ArgumentList& args);
 
-void cyclePatchProjection(const cmd::ArgumentList& args);
-
 void insertPatchColumnsAtEnd(const cmd::ArgumentList& args);
 void insertPatchColumnsAtBeginning(const cmd::ArgumentList& args);
 void insertPatchRowsAtEnd(const cmd::ArgumentList& args);
diff --git a/radiant/selection/algorithm/Primitives.cpp b/radiant/selection/algorithm/Primitives.cpp
index 6592781..eafa124 100644
--- a/radiant/selection/algorithm/Primitives.cpp
+++ b/radiant/selection/algorithm/Primitives.cpp
@@ -326,8 +326,8 @@ public:
 			patch->FlipTexture(1);
 
 			// Insert the patch into worldspawn
-			scene::INodePtr worldSpawnNode = GlobalMap().getWorldspawn();
-			assert(worldSpawnNode != NULL); // This must be non-NULL, otherwise we won't have faces
+			scene::INodePtr worldSpawnNode = GlobalMap().findOrInsertWorldspawn();
+			assert(worldSpawnNode); // This must be non-NULL, otherwise we won't have faces
 
 			worldSpawnNode->addChildNode(patchNode);
 
diff --git a/radiant/selection/algorithm/Transformation.cpp b/radiant/selection/algorithm/Transformation.cpp
index 127ee48..ffabb31 100644
--- a/radiant/selection/algorithm/Transformation.cpp
+++ b/radiant/selection/algorithm/Transformation.cpp
@@ -11,6 +11,7 @@
 #include "iselection.h"
 #include "imainframe.h"
 #include "itextstream.h"
+#include "iselectiongroup.h"
 
 #include "scenelib.h"
 #include "registry/registry.h"
@@ -79,6 +80,10 @@ private:
 	// A container, which temporarily holds the cloned nodes
     std::shared_ptr<scene::BasicRootNode> _cloneRoot;
 
+	// Map group IDs in this selection to new groups
+	typedef std::map<std::size_t, ISelectionGroupPtr> GroupMap;
+	GroupMap _groupMap;
+
 public:
 	SelectionCloner() :
 		_cloneRoot(new scene::BasicRootNode)
@@ -122,21 +127,60 @@ public:
 
 			// Insert this node in the root
 			_cloneRoot->addChildNode(clone);
+
+			// Collect and add the group IDs of the source node
+			std::shared_ptr<IGroupSelectable> groupSelectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+			if (groupSelectable)
+			{
+				const IGroupSelectable::GroupIds& groupIds = groupSelectable->getGroupIds();
+
+				// Get the Groups the source node was assigned to, and add the
+				// cloned node to the mapped group, one by one, keeping the order intact
+				for (std::size_t id : groupIds)
+				{
+					// Try to insert the ID, ignore if already exists
+					// Get a new mapping for the given group ID
+					const ISelectionGroupPtr& mappedGroup = getMappedGroup(id);
+
+					// Assign the new group ID to this clone
+					mappedGroup->addNode(clone);
+				}
+			}
+		}
+	}
+
+	// Gets the replacement ID for the given group ID
+	const ISelectionGroupPtr& getMappedGroup(std::size_t id)
+	{
+		std::pair<GroupMap::iterator, bool> found = _groupMap.insert(GroupMap::value_type(id, ISelectionGroupPtr()));
+
+		if (!found.second)
+		{
+			// We already covered this ID, return the mapped group
+			return found.first->second;
 		}
+
+		// Insertion was successful, so we didn't cover this ID yet
+		found.first->second = GlobalSelectionGroupManager().createSelectionGroup();
+
+		return found.first->second;
 	}
 
 	// Adds the cloned nodes to their designated parents. Pass TRUE to select the nodes.
-	void moveClonedNodes(bool select) {
-		for (Map::iterator i = _cloned.begin(); i != _cloned.end(); ++i)
+	void moveClonedNodes(bool select)
+	{
+		for (Map::value_type pair : _cloned)
 		{
 			// Remove the child from the basic container first
-			_cloneRoot->removeChildNode(i->first);
+			_cloneRoot->removeChildNode(pair.first);
 
 			// Add the node to its parent
-			i->second->addChildNode(i->first);
+			pair.second->addChildNode(pair.first);
 
-			if (select) {
-				Node_setSelected(i->first, true);
+			if (select) 
+			{
+				Node_setSelected(pair.first, true);
 			}
 		}
 	}
@@ -151,9 +195,6 @@ void cloneSelected(const cmd::ArgumentList& args)
 
 	UndoableCommand undo("cloneSelected");
 
-	// Create the list that will take the cloned instances
-	SelectionCloner::Map cloned;
-
 	SelectionCloner cloner;
 	GlobalSceneGraph().root()->traverse(cloner);
 
@@ -321,6 +362,12 @@ void moveSelectedCmd(const cmd::ArgumentList& args)
 		return;
 	}
 
+	if (GlobalSelectionSystem().countSelected() == 0)
+	{
+		rMessage() << "Nothing selected." << std::endl;
+		return;
+	}
+
 	UndoableCommand undo("moveSelectionVertically");
 
 	std::string arg = boost::algorithm::to_lower_copy(args[0].getString());
@@ -417,18 +464,36 @@ void rotateSelectionAboutAxis(axis_t axis, float deg)
 
 void rotateSelectionX(const cmd::ArgumentList& args)
 {
+	if (GlobalSelectionSystem().countSelected() == 0)
+	{
+		rMessage() << "Nothing selected." << std::endl;
+		return;
+	}
+
 	UndoableCommand undo("rotateSelected -axis x -angle -90");
 	rotateSelectionAboutAxis(eAxisX, -90);
 }
 
 void rotateSelectionY(const cmd::ArgumentList& args)
 {
+	if (GlobalSelectionSystem().countSelected() == 0)
+	{
+		rMessage() << "Nothing selected." << std::endl;
+		return;
+	}
+
 	UndoableCommand undo("rotateSelected -axis y -angle 90");
 	rotateSelectionAboutAxis(eAxisY, 90);
 }
 
 void rotateSelectionZ(const cmd::ArgumentList& args)
 {
+	if (GlobalSelectionSystem().countSelected() == 0)
+	{
+		rMessage() << "Nothing selected." << std::endl;
+		return;
+	}
+
 	UndoableCommand undo("rotateSelected -axis z -angle -90");
 	rotateSelectionAboutAxis(eAxisZ, -90);
 }
@@ -443,18 +508,36 @@ void mirrorSelection(int axis)
 
 void mirrorSelectionX(const cmd::ArgumentList& args)
 {
+	if (GlobalSelectionSystem().countSelected() == 0)
+	{
+		rMessage() << "Nothing selected." << std::endl;
+		return;
+	}
+
 	UndoableCommand undo("mirrorSelected -axis x");
 	mirrorSelection(0);
 }
 
 void mirrorSelectionY(const cmd::ArgumentList& args)
 {
+	if (GlobalSelectionSystem().countSelected() == 0)
+	{
+		rMessage() << "Nothing selected." << std::endl;
+		return;
+	}
+
 	UndoableCommand undo("mirrorSelected -axis y");
 	mirrorSelection(1);
 }
 
 void mirrorSelectionZ(const cmd::ArgumentList& args)
 {
+	if (GlobalSelectionSystem().countSelected() == 0)
+	{
+		rMessage() << "Nothing selected." << std::endl;
+		return;
+	}
+
 	UndoableCommand undo("mirrorSelected -axis z");
 	mirrorSelection(2);
 }
diff --git a/radiant/selection/group/SelectionGroup.h b/radiant/selection/group/SelectionGroup.h
new file mode 100644
index 0000000..b8f18a8
--- /dev/null
+++ b/radiant/selection/group/SelectionGroup.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include "iselectiongroup.h"
+#include "util/ScopedBoolLock.h"
+
+namespace selection
+{
+
+// Represents a named group of selectable items
+class SelectionGroup :
+	public ISelectionGroup
+{
+private:
+	std::size_t _id;
+
+	std::string _name;
+
+	// The contained nodes of this group
+	std::set<scene::INodeWeakPtr, std::owner_less<scene::INodeWeakPtr> > _nodes;
+
+	// To avoid entering feedback loops during group updates
+	bool _selectionLock;
+
+public:
+	SelectionGroup(std::size_t id) :
+		_id(id),
+		_selectionLock(false)
+	{}
+
+	std::size_t getId() const override
+	{
+		return _id;
+	}
+
+	const std::string& getName() const override
+	{
+		return _name;
+	}
+
+	void setName(const std::string& name) override
+	{
+		_name = name;
+	}
+
+	void addNode(const scene::INodePtr& node) override
+	{
+		std::shared_ptr<IGroupSelectable> selectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+		if (!selectable) return;
+
+		selectable->addToGroup(_id);
+
+		_nodes.insert(scene::INodeWeakPtr(node));
+	}
+
+	void removeNode(const scene::INodePtr& node) override
+	{
+		std::shared_ptr<IGroupSelectable> selectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+		assert(selectable);
+
+		selectable->removeFromGroup(_id);
+
+		_nodes.erase(scene::INodeWeakPtr(node));
+	}
+
+	void removeAllNodes()
+	{
+		foreachNode([this](const scene::INodePtr& node)
+		{
+			std::shared_ptr<IGroupSelectable> selectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+			assert(selectable);
+
+			selectable->removeFromGroup(_id);
+		});
+	}
+
+	std::size_t size() const
+	{
+		return _nodes.size();
+	}
+
+	void setSelected(bool selected) override
+	{
+		// In debug build's, I'd like to see the feedback loops, 
+		// so fire the debugger if we're re-entering the setSelected loop
+		assert(!_selectionLock);
+
+		if (_selectionLock) return; // already updating this group
+
+		util::ScopedBoolLock lock(_selectionLock);
+
+		foreachNode([&](const scene::INodePtr& node)
+		{
+			std::shared_ptr<IGroupSelectable> selectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+			assert(selectable);
+
+			// Set the node status, but don't do a group update (we're already here)
+			selectable->setSelected(selected, false);
+		});
+	}
+
+	void foreachNode(const std::function<void(const scene::INodePtr&)>& functor) override
+	{
+		for (const scene::INodeWeakPtr& node : _nodes)
+		{
+			scene::INodePtr locked = node.lock();
+
+			if (locked)
+			{
+				functor(locked);
+			}
+		}
+	}
+};
+typedef std::shared_ptr<SelectionGroup> SelectionGroupPtr;
+
+} // namespace
diff --git a/radiant/selection/group/SelectionGroupInfoFileModule.cpp b/radiant/selection/group/SelectionGroupInfoFileModule.cpp
new file mode 100644
index 0000000..6f96e5e
--- /dev/null
+++ b/radiant/selection/group/SelectionGroupInfoFileModule.cpp
@@ -0,0 +1,342 @@
+#include "SelectionGroupInfoFileModule.h"
+
+#include <limits>
+#include "iselectiongroup.h"
+#include "ientity.h"
+#include "string/convert.h"
+#include <boost/algorithm/string/replace.hpp>
+#include "parser/DefTokeniser.h"
+
+#include "scenelib.h"
+#include "debugging/ScenegraphUtils.h"
+#include "SelectionGroupManager.h"
+
+namespace selection
+{
+
+namespace
+{
+	const char* const SELECTION_GROUPS = "SelectionGroups";
+	const char* const SELECTION_GROUP = "SelectionGroup";
+	const char* const NODE_MAPPING = "SelectionGroupNodeMapping";
+	const char* const NODE = "Node";
+	std::size_t EMPTY_PRIMITVE_NUM = std::numeric_limits<std::size_t>::max();
+}
+
+std::string SelectionGroupInfoFileModule::getName()
+{
+	return "Selection Groups";
+}
+
+void SelectionGroupInfoFileModule::onInfoFileSaveStart()
+{
+	_output.str(std::string());
+	_output.clear();
+	_nodeInfoCount = 0;
+}
+
+void SelectionGroupInfoFileModule::onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum)
+{
+	saveNode(node, entityNum, primitiveNum);
+}
+
+void SelectionGroupInfoFileModule::onSaveEntity(const scene::INodePtr& node, std::size_t entityNum)
+{
+	saveNode(node, entityNum, EMPTY_PRIMITVE_NUM);
+}
+
+void SelectionGroupInfoFileModule::saveNode(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum)
+{
+	// Don't export the group settings for models and particles, as they are not there
+	// at map load/parse time - these shouldn't even be passed in here
+	assert(Node_isEntity(node) || Node_isPrimitive(node));
+
+	std::shared_ptr<IGroupSelectable> selectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
+
+	if (!selectable) return;
+
+	const IGroupSelectable::GroupIds& ids = selectable->getGroupIds();
+
+	// Ignore nodes that are not part of any group
+	if (ids.empty()) return;
+
+	// Node { ( EntityNum [PrimitiveNum] ) ( GroupId1 GroupId2 ... )
+	// e.g.
+	// Node { ( 3 78 ) ( 1 2 8 ) }
+
+	// Open a Node block
+	_output << "\t\t" << NODE << " { ";
+
+	// Write the node coordinates
+	_output << "( " << entityNum;
+
+	if (primitiveNum != EMPTY_PRIMITVE_NUM)
+	{
+		_output << " " << primitiveNum;
+	}
+	
+	_output << " )";
+
+	_output << " ( ";
+
+	// Write a space-separated list of group IDs
+	for (const IGroupSelectable::GroupIds::value_type& i : ids)
+	{
+		_output << i << " ";
+	}
+
+	_output << ") ";
+
+	// Close the Node block
+	_output << "}";
+
+	// Write additional node info, for easier debugging in case of issues
+	_output << " // " << getNodeInfo(node);
+
+	_output << std::endl;
+
+	_nodeInfoCount++;
+}
+
+void SelectionGroupInfoFileModule::writeBlocks(std::ostream& stream)
+{
+	// Selection Group output
+	stream << "\t" << SELECTION_GROUPS << std::endl;
+
+	stream << "\t{" << std::endl;
+
+	// SelectionGroup 0 { Name of this group }
+
+	std::size_t selectionGroupCount = 0;
+
+	getSelectionGroupManagerInternal().foreachSelectionGroup([&] (ISelectionGroup& group)
+	{
+		// Ignore empty groups
+		if (group.size() == 0) return;
+
+		// Make sure to escape the quotes of the set name, use the XML quote entity
+		stream << "\t\t" << SELECTION_GROUP << " " << group.getId()
+			<< " { \"" << boost::algorithm::replace_all_copy(group.getName(), "\"", """) << "\" }"
+			<< std::endl;
+
+		selectionGroupCount++;
+	});
+
+	stream << "\t}" << std::endl;
+
+	rMessage() << selectionGroupCount << " selection groups exported." << std::endl;
+
+	// Write the NodeToLayerMapping block
+	stream << "\t" << NODE_MAPPING << std::endl;
+	stream << "\t{" << std::endl;
+
+	// Write the output buffer to the stream
+	stream << _output.str();
+
+	// Closing braces of NodeToLayerMapping block
+	stream << "\t}" << std::endl;
+
+	rMessage() << _nodeInfoCount << " selection group member mappings written." << std::endl;
+}
+
+void SelectionGroupInfoFileModule::onInfoFileSaveFinished()
+{
+	_output.str(std::string());
+	_output.clear();
+	_nodeInfoCount = 0;
+}
+
+void SelectionGroupInfoFileModule::onInfoFileLoadStart()
+{
+	_groupInfo.clear();
+	_nodeMapping.clear();
+}
+
+bool SelectionGroupInfoFileModule::canParseBlock(const std::string& blockName)
+{
+	return blockName == SELECTION_GROUPS || blockName == NODE_MAPPING;
+}
+
+void SelectionGroupInfoFileModule::parseBlock(const std::string& blockName, parser::DefTokeniser& tok)
+{
+	assert(canParseBlock(blockName));
+
+	if (blockName == SELECTION_GROUPS)
+	{
+		parseSelectionGroups(tok);
+	}
+	else if (blockName == NODE_MAPPING)
+	{
+		parseNodeMappings(tok);
+	}
+}
+
+void SelectionGroupInfoFileModule::parseSelectionGroups(parser::DefTokeniser& tok)
+{
+	// The opening brace
+	tok.assertNextToken("{");
+
+	while (tok.hasMoreTokens())
+	{
+		std::string token = tok.nextToken();
+
+		if (token == SELECTION_GROUP)
+		{
+			// SelectionGroup 0 { Name of this group }
+
+			// Get the ID
+			std::string idStr = tok.nextToken();
+			std::size_t id = string::convert<std::size_t>(idStr);
+
+			tok.assertNextToken("{");
+
+			std::string name = tok.nextToken();
+
+			tok.assertNextToken("}");
+
+			_groupInfo.push_back(SelectionGroupImportInfo());
+			_groupInfo.back().id = id;
+			_groupInfo.back().name = name;
+
+			rMessage() << "[InfoFile]: Parsed group #" << id << " with name \"" << name << "\"" << std::endl;
+
+			continue;
+		}
+
+		if (token == "}")
+		{
+			break;
+		}
+	}
+}
+
+void SelectionGroupInfoFileModule::parseNodeMappings(parser::DefTokeniser& tok)
+{
+	// The opening brace
+	tok.assertNextToken("{");
+
+	while (tok.hasMoreTokens())
+	{
+		std::string token = tok.nextToken();
+
+		if (token == NODE)
+		{
+			// Node { ( 3 78 ) ( 1 2 8 ) }
+
+			// Get the node coordinates
+			tok.assertNextToken("{");
+
+			tok.assertNextToken("(");
+
+			// Entity number is always there
+			std::size_t entityNum = string::convert<std::size_t>(tok.nextToken());
+			std::size_t primitiveNum = EMPTY_PRIMITVE_NUM;
+
+			token = tok.nextToken();
+
+			// If we hit the closing parenthesis, we don't have a primitive number
+			if (token != ")")
+			{
+				// We have a primitive number
+				primitiveNum = string::convert<std::size_t>(token);
+				tok.assertNextToken(")");
+			}
+
+			// Initialise the node mapping with an empty list
+			NodeMapping::iterator mapped = _nodeMapping.insert(NodeMapping::value_type(
+				map::NodeIndexPair(entityNum, primitiveNum),
+				IGroupSelectable::GroupIds())).first;
+
+			tok.assertNextToken("(");
+
+			// Parse the group IDs until we hit the closing parenthesis
+			for (token = tok.nextToken(); token != ")"; token = tok.nextToken())
+			{
+				std::size_t groupId = string::convert<std::size_t>(token);
+
+				mapped->second.push_back(groupId);
+			}
+
+			// Node closed
+			tok.assertNextToken("}");
+
+			continue;
+		}
+
+		if (token == "}")
+		{
+			break;
+		}
+	}
+}
+
+void SelectionGroupInfoFileModule::applyInfoToScene(const scene::IMapRootNodePtr& root, const map::NodeIndexMap& nodeMap)
+{
+	// Remove all selection sets, there shouldn't be any left at this point
+	GlobalSelectionGroupManager().deleteAllSelectionGroups();
+
+	typedef std::map<std::size_t, ISelectionGroupPtr> GroupMap;
+	GroupMap groups;
+
+	// Re-construct the groups first
+	for (const SelectionGroupImportInfo& info : _groupInfo)
+	{
+		try
+		{
+			// Create the group by ID
+			ISelectionGroupPtr group = getSelectionGroupManagerInternal().createSelectionGroupInternal(info.id);
+			group->setName(info.name);
+
+			// Store for later retrieval
+			groups[info.id] = group;
+		}
+		catch (std::runtime_error& ex)
+		{
+			rError() << ex.what() << std::endl;
+		}
+	}
+
+	// Assign the nodes, as found in the mapping, keeping the group ID order intact
+	std::size_t failedNodes = 0;
+
+	for (const NodeMapping::value_type& mapping : _nodeMapping)
+	{
+		map::NodeIndexMap::const_iterator foundNode = nodeMap.find(mapping.first);
+
+		if (foundNode != nodeMap.end())
+		{
+			// Assign this node to its groups, following the order
+			for (const IGroupSelectable::GroupIds::value_type& id : mapping.second)
+			{
+				// Get the group and assign the node
+				GroupMap::iterator found = groups.find(id);
+
+				if (found == groups.end())
+				{
+					rWarning() << "Invalid group ID " << id << " encountered for node (" << 
+						mapping.first.first << "," << mapping.first.second << ")" << std::endl;
+					continue;
+				}
+
+				found->second->addNode(foundNode->second);
+			}
+		}
+		else
+		{
+			failedNodes++;
+		}
+	}
+		
+	if (failedNodes > 0)
+	{
+		rWarning() << "Couldn't resolve " << failedNodes << " nodes in group mapping " << std::endl;
+	}
+}
+
+void SelectionGroupInfoFileModule::onInfoFileLoadFinished()
+{
+	_groupInfo.clear();
+	_nodeMapping.clear();
+}
+
+}
diff --git a/radiant/selection/group/SelectionGroupInfoFileModule.h b/radiant/selection/group/SelectionGroupInfoFileModule.h
new file mode 100644
index 0000000..c916dfc
--- /dev/null
+++ b/radiant/selection/group/SelectionGroupInfoFileModule.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <sstream>
+#include "iselectiongroup.h"
+#include "imapinfofile.h"
+
+namespace selection
+{
+
+/**
+ * Info file module importing/exporting the selection group info
+ * to the .darkradiant file of persistence between mapping sessions.
+ */
+class SelectionGroupInfoFileModule :
+	public map::IMapInfoFileModule
+{
+private:
+	struct SelectionGroupImportInfo
+	{
+		// Group ID
+		std::size_t id;
+
+		// The name of this group
+		std::string name;
+	};
+
+	// Parsed selection group information
+	std::vector<SelectionGroupImportInfo> _groupInfo;
+	
+	// Parsed node mapping
+	typedef std::map<map::NodeIndexPair, IGroupSelectable::GroupIds> NodeMapping;
+	NodeMapping _nodeMapping;
+
+	std::stringstream _output;
+
+	std::size_t _nodeInfoCount;
+
+public:
+	std::string getName() override;
+
+	void onInfoFileSaveStart() override;
+	void onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) override;
+	void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) override;
+	void writeBlocks(std::ostream& stream) override;
+	void onInfoFileSaveFinished() override;
+
+	void onInfoFileLoadStart() override;
+	bool canParseBlock(const std::string& blockName) override;
+	void parseBlock(const std::string& blockName, parser::DefTokeniser& tok) override;
+	void applyInfoToScene(const scene::IMapRootNodePtr& root, const map::NodeIndexMap& nodeMap) override;
+	void onInfoFileLoadFinished() override;
+
+private:
+	void saveNode(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum);
+	void parseSelectionGroups(parser::DefTokeniser& tok);
+	void parseNodeMappings(parser::DefTokeniser& tok);
+};
+
+}
diff --git a/radiant/selection/group/SelectionGroupManager.cpp b/radiant/selection/group/SelectionGroupManager.cpp
new file mode 100644
index 0000000..28e2065
--- /dev/null
+++ b/radiant/selection/group/SelectionGroupManager.cpp
@@ -0,0 +1,280 @@
+#include "SelectionGroupManager.h"
+
+#include "i18n.h"
+#include "imap.h"
+#include "iradiant.h"
+#include "itextstream.h"
+#include "imapinfofile.h"
+#include "icommandsystem.h"
+#include "iselection.h"
+#include "ieventmanager.h"
+#include "imainframe.h"
+#include "iundo.h"
+#include "iorthocontextmenu.h"
+#include "modulesystem/StaticModule.h"
+#include "selection/algorithm/Group.h"
+
+#include "wxutil/dialog/MessageBox.h"
+#include "selectionlib.h"
+#include "SelectionGroup.h"
+#include "scene/SelectableNode.h"
+#include "SelectionGroupInfoFileModule.h"
+#include "wxutil/menu/MenuItem.h"
+#include "wxutil/menu/IconTextMenuItem.h"
+
+namespace selection
+{
+
+SelectionGroupManager::SelectionGroupManager() :
+	_nextGroupId(1)
+{}
+
+const std::string& SelectionGroupManager::getName() const
+{
+	static std::string _name(MODULE_SELECTIONGROUP);
+	return _name;
+}
+
+const StringSet& SelectionGroupManager::getDependencies() const
+{
+	static StringSet _dependencies;
+
+	if (_dependencies.empty())
+	{
+		_dependencies.insert(MODULE_SELECTIONSYSTEM);
+		_dependencies.insert(MODULE_EVENTMANAGER);
+		_dependencies.insert(MODULE_COMMANDSYSTEM);
+		_dependencies.insert(MODULE_RADIANT);
+		_dependencies.insert(MODULE_MAP);
+		_dependencies.insert(MODULE_MAPINFOFILEMANAGER);
+	}
+
+	return _dependencies;
+}
+
+void SelectionGroupManager::initialiseModule(const ApplicationContext& ctx)
+{
+	rMessage() << getName() << "::initialiseModule called." << std::endl;
+
+	GlobalCommandSystem().addCommand("GroupSelected",
+		std::bind(&SelectionGroupManager::groupSelectedCmd, this, std::placeholders::_1));
+	GlobalCommandSystem().addCommand("UngroupSelected",
+		std::bind(&SelectionGroupManager::ungroupSelectedCmd, this, std::placeholders::_1));
+	GlobalCommandSystem().addCommand("DeleteAllSelectionGroups",
+		std::bind(&SelectionGroupManager::deleteAllSelectionGroupsCmd, this, std::placeholders::_1));
+
+	GlobalEventManager().addCommand("GroupSelected", "GroupSelected");
+	GlobalEventManager().addCommand("UngroupSelected", "UngroupSelected");
+	GlobalEventManager().addCommand("DeleteAllSelectionGroups", "DeleteAllSelectionGroups");
+
+	GlobalMapModule().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &SelectionGroupManager::onMapEvent)
+	);
+
+	GlobalMapInfoFileManager().registerInfoFileModule(
+		std::make_shared<SelectionGroupInfoFileModule>()
+	);
+
+	GlobalRadiant().signal_radiantStarted().connect([this] ()
+	{
+		GlobalUIManager().getMenuManager().insert(
+			"main/edit/parent", "ungroupSelected", ui::eMenuItemType::menuItem, _("Ungroup Selection"), "ungroup_selection.png", "UngroupSelected");
+
+		GlobalUIManager().getMenuManager().insert(
+			"main/edit/ungroupSelected", "groupSelected", ui::eMenuItemType::menuItem, _("Group Selection"), "group_selection.png", "GroupSelected");
+
+		GlobalUIManager().getMenuManager().insert(
+			"main/edit/parent", "groupSelectedSeparator", ui::eMenuItemType::menuSeparator, "", "", "");
+	});
+
+	GlobalOrthoContextMenu().addItem(std::make_shared<wxutil::MenuItem>(
+		new wxutil::IconTextMenuItem(_("Group Selection"), "group_selection.png"),
+		[]() { algorithm::groupSelected(); },
+		[]() { return algorithm::CommandNotAvailableException::ToBool(algorithm::checkGroupSelectedAvailable); }),
+		ui::IOrthoContextMenu::SECTION_SELECTION_GROUPS);
+
+	GlobalOrthoContextMenu().addItem(std::make_shared<wxutil::MenuItem>(
+		new wxutil::IconTextMenuItem(_("Ungroup Selection"), "ungroup_selection.png"),
+		[]() { algorithm::ungroupSelected(); },
+		[]() { return algorithm::CommandNotAvailableException::ToBool(algorithm::checkUngroupSelectedAvailable); }), 
+		ui::IOrthoContextMenu::SECTION_SELECTION_GROUPS);
+}
+
+void SelectionGroupManager::onMapEvent(IMap::MapEvent ev)
+{
+	if (ev == IMap::MapUnloaded)
+	{
+		deleteAllSelectionGroups();
+		resetNextGroupId();
+	}
+}
+
+ISelectionGroupPtr SelectionGroupManager::createSelectionGroup()
+{
+	// Reserve a new group ID
+	std::size_t id = generateGroupId();
+
+	SelectionGroupPtr group = std::make_shared<SelectionGroup>(id);
+	_groups[id] = group;
+
+	return group;
+}
+
+ISelectionGroupPtr SelectionGroupManager::getSelectionGroup(std::size_t id)
+{
+	SelectionGroupMap::iterator found = _groups.find(id);
+
+	return found != _groups.end() ? found->second : ISelectionGroupPtr();
+}
+
+ISelectionGroupPtr SelectionGroupManager::findOrCreateSelectionGroup(std::size_t id)
+{
+	SelectionGroupMap::iterator found = _groups.find(id);
+
+	return found != _groups.end() ? found->second : createSelectionGroupInternal(id);
+}
+
+void SelectionGroupManager::setGroupSelected(std::size_t id, bool selected)
+{
+	SelectionGroupMap::iterator found = _groups.find(id);
+
+	if (found == _groups.end())
+	{
+		rError() << "Cannot find the group with ID " << id << std::endl;
+		return;
+	}
+
+	found->second->setSelected(selected);
+}
+
+void SelectionGroupManager::deleteSelectionGroup(std::size_t id)
+{
+	UndoableCommand cmd("DeleteSelectionGroup");
+
+	doDeleteSelectionGroup(id);
+}
+
+void SelectionGroupManager::doDeleteSelectionGroup(std::size_t id)
+{
+	SelectionGroupMap::iterator found = _groups.find(id);
+
+	if (found == _groups.end())
+	{
+		rError() << "Cannot delete the group with ID " << id << " as it doesn't exist." << std::endl;
+		return;
+	}
+
+	found->second->removeAllNodes();
+
+	_groups.erase(found);
+}
+
+void SelectionGroupManager::deleteAllSelectionGroups()
+{
+	UndoableCommand cmd("DeleteAllSelectionGroups");
+
+	for (SelectionGroupMap::iterator g = _groups.begin(); g != _groups.end(); )
+	{
+		doDeleteSelectionGroup((g++)->first);
+	}
+
+	assert(_groups.empty());
+}
+
+void SelectionGroupManager::foreachSelectionGroup(const std::function<void(ISelectionGroup&)>& func)
+{
+	for (SelectionGroupMap::value_type& pair : _groups)
+	{
+		func(*pair.second);
+	}
+}
+
+ISelectionGroupPtr SelectionGroupManager::createSelectionGroupInternal(std::size_t id)
+{
+	if (_groups.find(id) != _groups.end())
+	{
+		rWarning() << "Cannot create group with ID " << id << ", as it's already taken." << std::endl;
+		throw std::runtime_error("Group ID already taken");
+	}
+
+	SelectionGroupPtr group = std::make_shared<SelectionGroup>(id);
+	_groups[id] = group;
+
+	// Adjust the next group ID 
+	resetNextGroupId();
+
+	return group;
+}
+
+void SelectionGroupManager::deleteAllSelectionGroupsCmd(const cmd::ArgumentList& args)
+{
+	deleteAllSelectionGroups();
+}
+
+void SelectionGroupManager::groupSelectedCmd(const cmd::ArgumentList& args)
+{
+	try
+	{
+		algorithm::groupSelected();
+	}
+	catch (selection::algorithm::CommandNotAvailableException& ex)
+	{
+		rError() << ex.what() << std::endl;
+		wxutil::Messagebox::ShowError(ex.what());
+	}
+}
+
+void SelectionGroupManager::ungroupSelectedCmd(const cmd::ArgumentList& args)
+{
+	try
+	{
+		algorithm::ungroupSelected();
+	}
+	catch (selection::algorithm::CommandNotAvailableException& ex)
+	{
+		rError() << ex.what() << std::endl;
+		wxutil::Messagebox::ShowError(ex.what());
+	}
+}
+
+void SelectionGroupManager::resetNextGroupId()
+{
+	if (_groups.empty())
+	{
+		_nextGroupId = 0;
+	}
+	else
+	{
+		_nextGroupId = _groups.rbegin()->first + 1;
+	}
+}
+
+std::size_t SelectionGroupManager::generateGroupId()
+{
+#if 0
+	for (std::size_t i = 0; i < std::numeric_limits<std::size_t>::max(); ++i)
+	{
+		if (_groups.find(i) == _groups.end())
+		{
+			// Found a free ID
+			return i;
+		}
+	}
+#endif
+
+	if (_nextGroupId + 1 == std::numeric_limits<std::size_t>::max())
+	{
+		throw std::runtime_error("Out of group IDs.");
+	}
+
+	return _nextGroupId++;
+}
+
+module::StaticModule<SelectionGroupManager> staticSelectionGroupManagerModule;
+
+SelectionGroupManager& getSelectionGroupManagerInternal()
+{
+	return *staticSelectionGroupManagerModule.getModule();
+}
+
+}
diff --git a/radiant/selection/group/SelectionGroupManager.h b/radiant/selection/group/SelectionGroupManager.h
new file mode 100644
index 0000000..471c2f0
--- /dev/null
+++ b/radiant/selection/group/SelectionGroupManager.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "iselectiongroup.h"
+#include "imap.h"
+#include "icommandsystem.h"
+#include <map>
+
+namespace selection
+{
+
+class SelectionGroup;
+typedef std::shared_ptr<SelectionGroup> SelectionGroupPtr;
+
+class SelectionGroupManager :
+	public ISelectionGroupManager
+{
+private:
+	typedef std::map<std::size_t, SelectionGroupPtr> SelectionGroupMap;
+	SelectionGroupMap _groups;
+
+	// Group IDs are never re-used during the same mapping session
+	// to support undo/redo operations.
+	std::size_t _nextGroupId;
+
+public:
+	SelectionGroupManager();
+
+	const std::string& getName() const override;
+	const StringSet& getDependencies() const override;
+	void initialiseModule(const ApplicationContext& ctx) override;
+
+	ISelectionGroupPtr createSelectionGroup() override;
+	void setGroupSelected(std::size_t id, bool selected) override;
+	void deleteAllSelectionGroups() override;
+	void deleteSelectionGroup(std::size_t id) override;
+	ISelectionGroupPtr getSelectionGroup(std::size_t id) override;
+	ISelectionGroupPtr findOrCreateSelectionGroup(std::size_t id) override;
+
+	// Internal methods only accessible through getSelectionGroupManagerInternal()
+
+	void foreachSelectionGroup(const std::function<void(ISelectionGroup&)>& func);
+	
+	// Internal method allowing to create groups by ID
+	ISelectionGroupPtr createSelectionGroupInternal(std::size_t id);
+
+private:
+	void deleteAllSelectionGroupsCmd(const cmd::ArgumentList& args);
+	void groupSelectedCmd(const cmd::ArgumentList& args);
+	void ungroupSelectedCmd(const cmd::ArgumentList& args);
+
+	void onMapEvent(IMap::MapEvent ev);
+
+	std::size_t generateGroupId();
+	void resetNextGroupId();
+
+	void doDeleteSelectionGroup(std::size_t id);
+};
+
+// Internal accessor method to get hold of the implementing subclass
+SelectionGroupManager& getSelectionGroupManagerInternal();
+
+}
diff --git a/radiant/selection/selectionset/SelectionSetInfoFileModule.cpp b/radiant/selection/selectionset/SelectionSetInfoFileModule.cpp
new file mode 100644
index 0000000..b78a59b
--- /dev/null
+++ b/radiant/selection/selectionset/SelectionSetInfoFileModule.cpp
@@ -0,0 +1,230 @@
+#include "SelectionSetInfoFileModule.h"
+
+#include <limits>
+#include "itextstream.h"
+#include "string/convert.h"
+#include <boost/algorithm/string/replace.hpp>
+#include "parser/DefTokeniser.h"
+
+namespace selection
+{
+
+namespace
+{
+	const char* const SELECTION_SETS = "SelectionSets";
+	const char* const SELECTION_SET = "SelectionSet";
+	std::size_t EMPTY_PRIMITVE_NUM = std::numeric_limits<std::size_t>::max();
+}
+
+std::string SelectionSetInfoFileModule::getName()
+{
+	return "Selection Set Mapping";
+}
+
+void SelectionSetInfoFileModule::onInfoFileSaveStart()
+{
+	_exportInfo.clear();
+
+	// Visit all selection sets and assemble the info into the structures
+	GlobalSelectionSetManager().foreachSelectionSet([&](const ISelectionSetPtr& set)
+	{
+		// Get all nodes of this selection set and store them for later use
+		_exportInfo.push_back(SelectionSetExportInfo());
+
+		_exportInfo.back().set = set;
+		_exportInfo.back().nodes = set->getNodes();
+	});
+}
+
+void SelectionSetInfoFileModule::onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum)
+{
+	// Determine the item index for the selection set index mapping
+	std::for_each(_exportInfo.begin(), _exportInfo.end(), [&](SelectionSetExportInfo& info)
+	{
+		if (info.nodes.find(node) != info.nodes.end())
+		{
+			info.nodeIndices.insert(map::NodeIndexPair(entityNum, primitiveNum));
+		}
+	});
+}
+
+void SelectionSetInfoFileModule::onSaveEntity(const scene::INodePtr& node, std::size_t entityNum)
+{
+	// Determine the item index for the selection set index mapping
+	std::for_each(_exportInfo.begin(), _exportInfo.end(), [&](SelectionSetExportInfo& info)
+	{
+		if (info.nodes.find(node) != info.nodes.end())
+		{
+			info.nodeIndices.insert(map::NodeIndexPair(entityNum, EMPTY_PRIMITVE_NUM));
+		}
+	});
+}
+
+void SelectionSetInfoFileModule::writeBlocks(std::ostream& stream)
+{
+	// Selection Set output
+	stream << "\t" << SELECTION_SETS << std::endl;
+
+	stream << "\t{" << std::endl;
+
+	std::size_t selectionSetCount = 0;
+
+	std::for_each(_exportInfo.begin(), _exportInfo.end(), [&](SelectionSetExportInfo& info)
+	{
+		std::string indices = "";
+
+		std::for_each(info.nodeIndices.begin(), info.nodeIndices.end(),
+			[&](const map::NodeIndexPair& pair)
+		{
+			if (pair.second == EMPTY_PRIMITVE_NUM)
+			{
+				// only entity number
+				indices += "( " + string::to_string(pair.first) + " ) ";
+			}
+			else
+			{
+				// entity & primitive number
+				indices += "( " + string::to_string(pair.first) + " " + string::to_string(pair.second) + " ) ";
+			}
+		});
+
+		// Make sure to escape the quotes of the set name, use the XML quote entity
+		stream << "\t\t" << SELECTION_SET << " " << selectionSetCount++
+			<< " { \"" << boost::algorithm::replace_all_copy(info.set->getName(), "\"", """) << "\" } "
+			<< " { " << indices << " } "
+			<< std::endl;
+	});
+
+	stream << "\t}" << std::endl;
+
+	rMessage() << _exportInfo.size() << " selection sets exported." << std::endl;
+}
+
+void SelectionSetInfoFileModule::onInfoFileSaveFinished()
+{
+	_exportInfo.clear();
+}
+
+void SelectionSetInfoFileModule::onInfoFileLoadStart()
+{
+	_importInfo.clear();
+}
+
+bool SelectionSetInfoFileModule::canParseBlock(const std::string& blockName)
+{
+	return blockName == SELECTION_SETS;
+}
+
+void SelectionSetInfoFileModule::parseBlock(const std::string& blockName, parser::DefTokeniser& tok)
+{
+	if (blockName != SELECTION_SETS) return;
+
+	// SelectionSet 2 { "Stairs" }  { (0 4076) (0 4077) (0 4078) (0 4079) (0 4309) (2) } 
+
+	// The opening brace
+	tok.assertNextToken("{");
+
+	while (tok.hasMoreTokens())
+	{
+		std::string token = tok.nextToken();
+
+		if (token == SELECTION_SET)
+		{
+			// Create a new SelectionSet info structure
+			_importInfo.push_back(SelectionSetImportInfo());
+
+			std::size_t selectionSetIndex = string::convert<std::size_t>(tok.nextToken());
+
+			rMessage() << "Parsing Selection Set #" << selectionSetIndex << std::endl;
+
+			tok.assertNextToken("{");
+
+			// Parse the name, replacing the " placeholder with a proper quote
+			_importInfo.back().name = boost::algorithm::replace_all_copy(tok.nextToken(), """, "\"");
+
+			tok.assertNextToken("}");
+
+			tok.assertNextToken("{");
+
+			while (tok.hasMoreTokens())
+			{
+				std::string nextToken = tok.nextToken();
+
+				if (nextToken == "}") break;
+
+				// If it's not a closing curly brace, it must be an opening parenthesis
+				if (nextToken != "(")
+				{
+					throw parser::ParseException("InfoFile: Assertion failed: Required \"("
+						"\", found \"" + nextToken + "\"");
+				}
+
+				// Expect one or two numbers now
+				std::size_t entityNum = string::convert<std::size_t>(tok.nextToken());
+
+				nextToken = tok.nextToken();
+
+				if (nextToken == ")")
+				{
+					// Just the entity number, no primitive number
+					_importInfo.back().nodeIndices.insert(
+						map::NodeIndexPair(entityNum, EMPTY_PRIMITVE_NUM));
+				}
+				else
+				{
+					// Primitive number is provided as well
+					std::size_t primitiveNum = string::convert<std::size_t>(nextToken);
+
+					// No more than 2 numbers are supported, so assume a closing parenthesis now
+					tok.assertNextToken(")");
+
+					_importInfo.back().nodeIndices.insert(
+						map::NodeIndexPair(entityNum, primitiveNum));
+				}
+			}
+		}
+
+		if (token == "}") break;
+	}
+}
+
+void SelectionSetInfoFileModule::applyInfoToScene(const scene::IMapRootNodePtr& root, const map::NodeIndexMap& nodeMap)
+{
+	// Remove all selection sets, there shouldn't be any left at this point
+	GlobalSelectionSetManager().deleteAllSelectionSets();
+
+	// Re-construct the selection sets
+	std::for_each(_importInfo.begin(), _importInfo.end(), [&](const SelectionSetImportInfo& info)
+	{
+		ISelectionSetPtr set = GlobalSelectionSetManager().createSelectionSet(info.name);
+
+		std::size_t failedNodes = 0;
+
+		std::for_each(info.nodeIndices.begin(), info.nodeIndices.end(),
+			[&](const map::NodeIndexPair& indexPair)
+		{
+			map::NodeIndexMap::const_iterator i = nodeMap.find(indexPair);
+
+			if (i != nodeMap.end())
+			{
+				set->addNode(i->second);
+			}
+			else
+			{
+				failedNodes++;
+			}
+		});
+
+		if (failedNodes > 0)
+		{
+			rWarning() << "Couldn't resolve " << failedNodes << " nodes in selection set " << set->getName() << std::endl;
+		}
+	});
+}
+
+void SelectionSetInfoFileModule::onInfoFileLoadFinished()
+{
+	_importInfo.clear();
+}
+
+}
diff --git a/radiant/selection/selectionset/SelectionSetInfoFileModule.h b/radiant/selection/selectionset/SelectionSetInfoFileModule.h
new file mode 100644
index 0000000..e21cde4
--- /dev/null
+++ b/radiant/selection/selectionset/SelectionSetInfoFileModule.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include "imapinfofile.h"
+#include "iselectionset.h"
+
+namespace selection
+{
+
+/**
+ * Info file module importing/exporting the selection set mapping 
+ * to the .darkradiant file of persistence between mapping sessions.
+ */
+class SelectionSetInfoFileModule :
+	public map::IMapInfoFileModule
+{
+private:
+	struct SelectionSetImportInfo
+	{
+		// The name of this set
+		std::string name;
+
+		// The node indices, which will be resolved to nodes after import
+		std::set<map::NodeIndexPair> nodeIndices;
+	};
+
+	// Parsed selection set information
+	std::vector<SelectionSetImportInfo> _importInfo;
+
+	struct SelectionSetExportInfo
+	{
+		// The set we're working with
+		selection::ISelectionSetPtr set;
+
+		// The nodes in this set
+		std::set<scene::INodePtr> nodes;
+
+		// The node indices, which will be resolved during traversal
+		std::set<map::NodeIndexPair> nodeIndices;
+	};
+
+	// SelectionSet-related
+	typedef std::vector<SelectionSetExportInfo> SelectionSetInfo;
+	SelectionSetInfo _exportInfo;
+
+public:
+	std::string getName() override;
+
+	void onInfoFileSaveStart() override;
+	void onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) override;
+	void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) override;
+	void writeBlocks(std::ostream& stream) override;
+	void onInfoFileSaveFinished() override;
+
+	void onInfoFileLoadStart() override;
+	bool canParseBlock(const std::string& blockName) override;
+	void parseBlock(const std::string& blockName, parser::DefTokeniser& tok) override;
+	void applyInfoToScene(const scene::IMapRootNodePtr& root, const map::NodeIndexMap& nodeMap) override;
+	void onInfoFileLoadFinished() override;
+};
+
+}
diff --git a/radiant/selection/selectionset/SelectionSetManager.cpp b/radiant/selection/selectionset/SelectionSetManager.cpp
index 361b699..45491b7 100644
--- a/radiant/selection/selectionset/SelectionSetManager.cpp
+++ b/radiant/selection/selectionset/SelectionSetManager.cpp
@@ -1,15 +1,16 @@
 #include "SelectionSetManager.h"
 
 #include "itextstream.h"
-#include "iradiant.h"
 #include "i18n.h"
 #include "iselection.h"
 #include "idialogmanager.h"
+#include "imapinfofile.h"
 #include "iuimanager.h"
 #include "ieventmanager.h"
 #include "imainframe.h"
 #include "modulesystem/StaticModule.h"
 #include "SelectionSetToolmenu.h"
+#include "SelectionSetInfoFileModule.h"
 
 #include <wx/toolbar.h>
 #include <wx/frame.h>
@@ -48,6 +49,8 @@ const StringSet& SelectionSetManager::getDependencies() const
 		_dependencies.insert(MODULE_EVENTMANAGER);
 		_dependencies.insert(MODULE_COMMANDSYSTEM);
 		_dependencies.insert(MODULE_RADIANT);
+		_dependencies.insert(MODULE_MAP);
+		_dependencies.insert(MODULE_MAPINFOFILEMANAGER);
 	}
 
 	return _dependencies;
@@ -66,13 +69,30 @@ void SelectionSetManager::initialiseModule(const ApplicationContext& ctx)
 		std::bind(&SelectionSetManager::deleteAllSelectionSetsCmd, this, std::placeholders::_1));
 
 	GlobalEventManager().addCommand("DeleteAllSelectionSets", "DeleteAllSelectionSets");
+
+	GlobalMapModule().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &SelectionSetManager::onMapEvent)
+	);
+
+	GlobalMapInfoFileManager().registerInfoFileModule(
+		std::make_shared<SelectionSetInfoFileModule>()
+	);
 }
 
 void SelectionSetManager::shutdownModule()
 {
+	_sigSelectionSetsChanged.clear();
 	_selectionSets.clear();
 }
 
+void SelectionSetManager::onMapEvent(IMap::MapEvent ev)
+{
+	if (ev == IMap::MapUnloaded)
+	{
+		deleteAllSelectionSets();
+	}
+}
+
 void SelectionSetManager::onRadiantStartup()
 {
 	// Get the horizontal toolbar and add a custom widget
diff --git a/radiant/selection/selectionset/SelectionSetManager.h b/radiant/selection/selectionset/SelectionSetManager.h
index 25690aa..616a54a 100644
--- a/radiant/selection/selectionset/SelectionSetManager.h
+++ b/radiant/selection/selectionset/SelectionSetManager.h
@@ -2,6 +2,7 @@
 
 #include "iselectionset.h"
 #include "iradiant.h"
+#include "imap.h"
 #include "icommandsystem.h"
 
 #include <map>
@@ -53,6 +54,7 @@ public:
 	void deleteAllSelectionSetsCmd(const cmd::ArgumentList& args);
 
 private:
+	void onMapEvent(IMap::MapEvent ev);
 	void onDeleteAllSetsClicked(wxCommandEvent& ev);
 };
 
diff --git a/radiant/selection/shaderclipboard/ShaderClipboard.cpp b/radiant/selection/shaderclipboard/ShaderClipboard.cpp
index 276eb25..9897e08 100644
--- a/radiant/selection/shaderclipboard/ShaderClipboard.cpp
+++ b/radiant/selection/shaderclipboard/ShaderClipboard.cpp
@@ -20,10 +20,14 @@ ShaderClipboard::ShaderClipboard() :
 	GlobalUIManager().getStatusBarManager().addTextElement(
 		"ShaderClipBoard",
 		"icon_texture.png",
-		IStatusBarManager::POS_SHADERCLIPBOARD
+		IStatusBarManager::POS_SHADERCLIPBOARD,
+		_("The name of the shader in the clipboard")
 	);
 
 	GlobalUndoSystem().addObserver(this);
+
+	GlobalMapModule().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &ShaderClipboard::onMapEvent));
 }
 
 void ShaderClipboard::clear() {
@@ -161,6 +165,14 @@ sigc::signal<void> ShaderClipboard::signal_sourceChanged() const
     return _signalSourceChanged;
 }
 
+void ShaderClipboard::onMapEvent(IMap::MapEvent ev)
+{
+	if (ev == IMap::MapUnloading)
+	{
+		clear();
+	}
+}
+
 } // namespace selection
 
 // global accessor function
diff --git a/radiant/selection/shaderclipboard/ShaderClipboard.h b/radiant/selection/shaderclipboard/ShaderClipboard.h
index 4167d1d..56e0543 100644
--- a/radiant/selection/shaderclipboard/ShaderClipboard.h
+++ b/radiant/selection/shaderclipboard/ShaderClipboard.h
@@ -1,10 +1,12 @@
 #pragma once
 
 #include "iundo.h"
+#include "imap.h"
 #include "Texturable.h"
 #include <sigc++/signal.h>
 
-namespace selection {
+namespace selection 
+{
 
 class ShaderClipboard :
 	public UndoSystem::Observer
@@ -56,6 +58,7 @@ public:
 	void postRedo();
 
 private:
+	void onMapEvent(IMap::MapEvent ev);
 
 	/** greebo: Updates the shader information in the status bar.
 	 */
diff --git a/radiant/settings/GameManager.cpp b/radiant/settings/GameManager.cpp
index 027db16..896c58e 100644
--- a/radiant/settings/GameManager.cpp
+++ b/radiant/settings/GameManager.cpp
@@ -94,17 +94,17 @@ IGamePtr Manager::currentGame()
 
 void Manager::constructPreferences()
 {
-	PreferencesPagePtr page = GetPreferenceSystem().getPage(_("Game"));
+	IPreferencePage& page = GetPreferenceSystem().getPage(_("Game"));
 
 	ComboBoxValueList gameList;
 	for (GameMap::iterator i = _games.begin(); i != _games.end(); ++i)
 	{
 		gameList.push_back(i->second->getKeyValue("name"));
 	}
-	page->appendCombo(_("Select a Game:"), RKEY_GAME_TYPE, gameList, true);
-	page->appendPathEntry(_("Engine Path"), RKEY_ENGINE_PATH, true);
-	page->appendEntry(_("Mod (fs_game)"), RKEY_FS_GAME);
-	page->appendEntry(_("Mod Base (fs_game_base, optional)"), RKEY_FS_GAME_BASE);
+	page.appendCombo(_("Select a Game:"), RKEY_GAME_TYPE, gameList, true);
+	page.appendPathEntry(_("Engine Path"), RKEY_ENGINE_PATH, true);
+	page.appendEntry(_("Mod (fs_game)"), RKEY_FS_GAME);
+	page.appendEntry(_("Mod Base (fs_game_base, optional)"), RKEY_FS_GAME_BASE);
 }
 
 void Manager::initialise(const std::string& appPath)
@@ -320,7 +320,7 @@ void Manager::initEnginePath()
 	while (!settingsValid())
 	{
 		// Engine path doesn't exist, ask the user
-		ui::PrefDialog::ShowModal(_("Game"));
+		ui::PrefDialog::ShowDialog(_("Game"));
 
 		// After the dialog, the settings are located in the registry.
 		// Construct the paths with the settings found there
@@ -341,8 +341,8 @@ void Manager::initEnginePath()
 	updateEnginePath(true);
 
 	// Add the note to the preference page
-	PreferencesPagePtr page = GetPreferenceSystem().getPage(_("Game"));
-	page->appendLabel(_("<b>Note</b>: You will have to restart DarkRadiant\nfor the changes to take effect."));
+	IPreferencePage& page = GetPreferenceSystem().getPage(_("Game"));
+	page.appendLabel(_("<b>Note</b>: You will have to restart DarkRadiant\nfor the changes to take effect."));
 }
 
 void Manager::observeKey(const std::string& key)
diff --git a/radiant/settings/GameManager.h b/radiant/settings/GameManager.h
index 0de93e7..cdfc83a 100644
--- a/radiant/settings/GameManager.h
+++ b/radiant/settings/GameManager.h
@@ -63,10 +63,6 @@ private:
 	 */
 	bool settingsValid() const;
 
-	/** greebo: Get the user engine path (is OS-specific)
-	 */
-	std::string getUserEnginePath();
-
 	/** 
 	 * DerSaidin: Adds a path to the VFS search list, skipping any duplicates.
 	 * Note that the order of search paths must be preserved.
@@ -96,25 +92,29 @@ public:
 	 */
 	const std::string& getEnginePath() const;
 
+	/** greebo: Get the user engine path (is OS-specific)
+	*/
+	std::string getUserEnginePath() override;
+
 	/**
 	 * greebo: Gets the mod path (e.g. ~/.doom3/gathers/).
 	 * Returns the mod base path if the mod path itself is empty.
 	 */
-	const std::string& getModPath() const;
+	const std::string& getModPath() const override;
 
 	/**
 	 * greebo: Returns the mod base path (e.g. ~/.doom3/darkmod/),
 	 * can be an empty string if fs_game_base is not set.
 	 */
-	const std::string& getModBasePath() const;
+	const std::string& getModBasePath() const override;
 
 	/** greebo: Accessor method for the fs_game parameter
 	 */
-	const std::string& getFSGame() const;
+	const std::string& getFSGame() const override;
 
 	/** greebo: Accessor method for the fs_game_base parameter
 	 */
-	const std::string& getFSGameBase() const;
+	const std::string& getFSGameBase() const override;
 
 	/** greebo: Initialises the engine path from the settings in the registry.
 	 * 			If nothing is found, the game file is queried.
@@ -123,7 +123,7 @@ public:
 
 	/** greebo: Returns the current Game (shared_ptr).
 	 */
-	virtual IGamePtr currentGame();
+	virtual IGamePtr currentGame() override;
 
 	/** greebo: Loads the game files and the saved settings.
 	 * 			If no saved game setting is found, the user
@@ -136,12 +136,12 @@ public:
 	void loadGameFiles(const std::string& appPath);
 
 	// Returns the sorted game path list
-	virtual const PathList& getVFSSearchPaths() const;
+	virtual const PathList& getVFSSearchPaths() const override;
 
 	// RegisterableModule implementation
-	virtual const std::string& getName() const;
-	virtual const StringSet& getDependencies() const;
-	virtual void initialiseModule(const ApplicationContext& ctx);
+	virtual const std::string& getName() const override;
+	virtual const StringSet& getDependencies() const override;
+	virtual void initialiseModule(const ApplicationContext& ctx) override;
 
 };
 
diff --git a/radiant/settings/LanguageManager.cpp b/radiant/settings/LanguageManager.cpp
index f5282b5..1e24485 100644
--- a/radiant/settings/LanguageManager.cpp
+++ b/radiant/settings/LanguageManager.cpp
@@ -101,10 +101,10 @@ void LanguageManager::initialiseModule(const ApplicationContext& ctx)
 	GlobalRegistry().setAttribute(RKEY_LANGUAGE, "volatile", "1"); // don't save this to user.xml
 
 	// Add Preferences
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Language"));
-	page->appendCombo(_("Language"), RKEY_LANGUAGE, langs);
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Language"));
+	page.appendCombo(_("Language"), RKEY_LANGUAGE, langs);
 
-	page->appendLabel(_("<b>Note:</b> You'll need to restart DarkRadiant\nafter changing the language setting."));
+	page.appendLabel(_("<b>Note:</b> You'll need to restart DarkRadiant\nafter changing the language setting."));
 }
 
 void LanguageManager::shutdownModule()
diff --git a/radiant/settings/PreferenceItemBase.h b/radiant/settings/PreferenceItemBase.h
new file mode 100644
index 0000000..51f9807
--- /dev/null
+++ b/radiant/settings/PreferenceItemBase.h
@@ -0,0 +1,44 @@
+#pragma once
+
+namespace settings
+{
+
+// Base class for all pereference items. All of them can carry 
+// a title and a registry key
+class PreferenceItemBase
+{
+protected:
+	std::string _registryKey;
+
+	std::string _label;
+
+public:
+	PreferenceItemBase(const std::string& label) :
+		_label(label)
+	{}
+
+	PreferenceItemBase(const std::string& label, const std::string& registryKey) :
+		_registryKey(registryKey),
+		_label(label)
+	{}
+
+	virtual ~PreferenceItemBase() {}
+
+	virtual const std::string& getLabel() const
+	{
+		return _label;
+	}
+
+	virtual const std::string& getRegistryKey() const
+	{
+		return _registryKey;
+	}
+
+	virtual void setRegistryKey(const std::string& key)
+	{
+		_registryKey = key;
+	}
+};
+typedef std::shared_ptr<PreferenceItemBase> PreferenceItemBasePtr;
+
+} // namespace
diff --git a/radiant/settings/PreferenceItems.h b/radiant/settings/PreferenceItems.h
new file mode 100644
index 0000000..f630819
--- /dev/null
+++ b/radiant/settings/PreferenceItems.h
@@ -0,0 +1,170 @@
+#pragma once
+
+#include "ipreferencesystem.h"
+#include "PreferenceItemBase.h"
+
+/**
+ * greebo: This file contains a couple of classes describing
+ * the Preference Items visible in the Preference Dialog. 
+ *
+ * Client code registers their configurable options with the
+ * GlobalPreferenceSystem during module initialisation, these
+ * classes here store this information for later retrieval
+ * (when the Preference Dialog is constructed, more specifically).
+ * 
+ * All these are derived from a single base, as most of them
+ * want to save their stuff in the Registry.
+ */
+namespace settings
+{
+
+class PreferenceLabel :
+	public PreferenceItemBase
+{
+public:
+	PreferenceLabel(const std::string& label) :
+		PreferenceItemBase(label)
+	{}
+};
+
+class PreferenceEntry :
+	public PreferenceItemBase
+{
+public:
+	PreferenceEntry(const std::string& label, const std::string& registryKey) :
+		PreferenceItemBase(label, registryKey)
+	{}
+};
+
+class PreferenceCheckbox :
+	public PreferenceItemBase
+{
+public:
+	PreferenceCheckbox(const std::string& label, const std::string& registryKey) :
+		PreferenceItemBase(label, registryKey)
+	{}
+};
+
+class PreferenceCombobox :
+	public PreferenceItemBase
+{
+private:
+	ComboBoxValueList _values;
+	bool _storeValueNotIndex;
+
+public:
+	PreferenceCombobox(const std::string& label, const std::string& registryKey, 
+					   const ComboBoxValueList& values, bool storeValueNotIndex) :
+		PreferenceItemBase(label, registryKey),
+		_values(values),
+		_storeValueNotIndex(storeValueNotIndex)
+	{}
+
+	const ComboBoxValueList& getValues() const
+	{
+		return _values;
+	}
+
+	bool storeValueNotIndex() const
+	{
+		return _storeValueNotIndex;
+	}
+};
+
+class PreferencePathEntry :
+	public PreferenceItemBase
+{
+private:
+	bool _browseDirectories;
+
+public:
+	PreferencePathEntry(const std::string& label, const std::string& registryKey, bool browseDirectories) :
+		PreferenceItemBase(label, registryKey),
+		_browseDirectories(browseDirectories)
+	{}
+
+	bool browseDirectories() const
+	{
+		return _browseDirectories;
+	}
+};
+
+class PreferenceSpinner :
+	public PreferenceItemBase
+{
+private:
+	double _lower;
+	double _upper;
+	int _fraction;
+
+public:
+	PreferenceSpinner(const std::string& label, const std::string& registryKey, double lower, double upper, int fraction) :
+		PreferenceItemBase(label, registryKey),
+		_lower(lower),
+		_upper(upper),
+		_fraction(fraction)
+	{}
+
+	double getLower()
+	{
+		return _lower;
+	}
+
+	double getUpper()
+	{
+		return _upper;
+	}
+
+	int getFraction()
+	{
+		return _fraction;
+	}
+};
+
+class PreferenceSlider :
+	public PreferenceItemBase
+{
+private:
+	double _lower;
+	double _upper;
+	double _stepIncrement;
+	double _pageIncrement;
+	int _factor;
+
+public:
+	PreferenceSlider(const std::string& label, const std::string& registryKey, double lower, double upper, double stepIncrement, double pageIncrement) :
+		PreferenceItemBase(label, registryKey),
+		_lower(lower),
+		_upper(upper),
+		_stepIncrement(stepIncrement),
+		_pageIncrement(pageIncrement),
+		_factor(1)
+	{}
+
+	double getLower()
+	{
+		return _lower;
+	}
+
+	double getUpper()
+	{
+		return _upper;
+	}
+
+	double getStepIncrement()
+	{
+		return _stepIncrement;
+	}
+
+	double getPageIncrement()
+	{
+		return _pageIncrement;
+	}
+
+	int getFactor()
+	{
+		return _factor;
+	}
+};
+
+} // namespace
diff --git a/radiant/settings/PreferencePage.cpp b/radiant/settings/PreferencePage.cpp
new file mode 100644
index 0000000..ac0ca4d
--- /dev/null
+++ b/radiant/settings/PreferencePage.cpp
@@ -0,0 +1,159 @@
+#include "PreferencePage.h"
+
+#include <stdexcept>
+#include "i18n.h"
+#include "itextstream.h"
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/format.hpp>
+
+#include "PreferenceItems.h"
+
+namespace settings
+{
+
+PreferencePage::PreferencePage(const std::string& name, const PreferencePagePtr& parentPage) :
+	_name(name)
+{
+	_title = (boost::format(_("%s Settings")) % _name).str();
+
+	// Construct the _path member value
+	if (parentPage && !parentPage->getPath().empty())
+	{
+		_path = parentPage->getPath() + "/" + _name;
+	}
+	else
+	{
+		_path = _name;
+	}
+}
+
+const std::string& PreferencePage::getTitle() const
+{
+	return _title;
+}
+
+void PreferencePage::setTitle(const std::string& title)
+{
+	_title = title;
+}
+
+const std::string& PreferencePage::getPath() const
+{
+	return _path;
+}
+
+const std::string& PreferencePage::getName() const
+{
+	return _name;
+}
+
+PreferencePage& PreferencePage::createOrFindPage(const std::string& path)
+{
+	// Split the path into parts
+	std::list<std::string> parts;
+	boost::algorithm::split(parts, path, boost::algorithm::is_any_of("/"));
+
+	if (parts.empty())
+	{
+		rConsole() << "Cannot resolve empty preference path: " << path << std::endl;
+		throw std::logic_error("Cannot resolve empty preference path.");
+	}
+
+	PreferencePagePtr child;
+
+	// Try to lookup the page in the child list
+	for (const PreferencePagePtr& candidate : _children)
+	{
+		if (candidate->getName() == parts.front())
+		{
+			child = candidate;
+			break;
+		}
+	}
+
+	if (!child)
+	{
+		// No child found, create a new page and add it to the list
+		child = std::make_shared<PreferencePage>(parts.front(), shared_from_this());
+		_children.push_back(child);
+	}
+
+	// We now have a child with this name, do we have a leaf?
+	if (parts.size() > 1) 
+	{
+		// We have still more parts, split off the first part
+		parts.pop_front();
+		std::string subPath = boost::algorithm::join(parts, "/");
+
+		// Pass the call to the child
+		return child->createOrFindPage(subPath);
+	}
+	else 
+	{
+		// We have found a leaf, return the child page
+		return *child;
+	}
+}
+
+void PreferencePage::foreachChildPage(const std::function<void(PreferencePage&)>& functor)
+{
+	for (const PreferencePagePtr& child : _children)
+	{
+		// Visit this instance
+		functor(*child);
+
+		// Pass the visitor recursively
+		child->foreachChildPage(functor);
+	}
+}
+
+void PreferencePage::foreachItem(const std::function<void(const PreferenceItemBasePtr&)>& functor) const
+{
+	for (const PreferenceItemBasePtr& item : _items)
+	{
+		functor(item);
+	}
+}
+
+void PreferencePage::appendCheckBox(const std::string& label, const std::string& registryKey)
+{
+	_items.push_back(std::make_shared<PreferenceCheckbox>(label, registryKey));
+}
+
+void PreferencePage::appendSlider(const std::string& name, const std::string& registryKey,
+	double lower, double upper, double stepIncrement, double pageIncrement)
+{
+	_items.push_back(std::make_shared<PreferenceSlider>(name, registryKey, 
+		lower, upper, stepIncrement, pageIncrement));
+}
+
+void PreferencePage::appendCombo(const std::string& name, const std::string& registryKey,
+	const ComboBoxValueList& valueList, bool storeValueNotIndex)
+{
+	_items.push_back(std::make_shared<PreferenceCombobox>(name, registryKey, valueList, storeValueNotIndex));
+}
+
+void PreferencePage::appendEntry(const std::string& name, const std::string& registryKey)
+{
+	_items.push_back(std::make_shared<PreferenceEntry>(name, registryKey));
+}
+
+void PreferencePage::appendLabel(const std::string& caption)
+{
+	_items.push_back(std::make_shared<PreferenceLabel>(caption));
+}
+
+void PreferencePage::appendPathEntry(const std::string& name, const std::string& registryKey, bool browseDirectories)
+{
+	_items.push_back(std::make_shared<PreferencePathEntry>(name, registryKey, browseDirectories));
+}
+
+void PreferencePage::appendSpinner(const std::string& name, const std::string& registryKey,
+	double lower, double upper, int fraction)
+{
+	_items.push_back(std::make_shared<PreferenceSpinner>(name, registryKey, lower, upper, fraction));
+}
+
+} // namespace
diff --git a/radiant/settings/PreferencePage.h b/radiant/settings/PreferencePage.h
new file mode 100644
index 0000000..81041eb
--- /dev/null
+++ b/radiant/settings/PreferencePage.h
@@ -0,0 +1,94 @@
+#pragma once
+
+#include "ipreferencesystem.h"
+#include <vector>
+#include <memory>
+
+#include "PreferenceItemBase.h"
+
+namespace settings
+{
+
+class PreferencePage;
+typedef std::shared_ptr<PreferencePage> PreferencePagePtr;
+
+class PreferencePage :
+	public IPreferencePage,
+	public std::enable_shared_from_this<PreferencePage>
+{
+private:
+	// The name (caption) of this page
+	std::string _name;
+
+	// The title which is by default "{_name} Settings" but can be overridden
+	std::string _title;
+
+	// The full path of this object
+	std::string _path;
+
+	// The list of child pages
+	std::vector<PreferencePagePtr> _children;
+
+	// The registered items
+	std::vector<PreferenceItemBasePtr> _items;
+
+public:
+	/** 
+	 * Construct this passing the name and the optional parent page.
+	*/
+	PreferencePage(const std::string& name,
+				   const PreferencePagePtr& parentPage = PreferencePagePtr());
+
+	// Returns the title as displayed on top of the page's area
+	const std::string& getTitle() const;
+
+	/** 
+	 * greebo: Sets the title caption that is displayed on the right.
+	 * Overrides the default title that is generated
+	 * on construction (the one with the " Settings" postfix).
+	 */
+	void setTitle(const std::string& title) override;
+
+	// greebo: Returns the full path to this page
+	const std::string& getPath() const;
+
+	// greebo: Returns the name (caption) of this Page (e.g. "Settings")
+	const std::string& getName() const;
+
+	/** 
+	 * greebo: Performs a recursive lookup of the given path
+	 * and creates any items that do not exist.
+ 	 * @returns: the shared_ptr to the PrefPage.
+	 * @throws: a std::logic_error if the given path is empty
+	 */
+	PreferencePage& createOrFindPage(const std::string& path);
+
+	// Recursively visit all children of this page
+	void foreachChildPage(const std::function<void(PreferencePage&)>& functor);
+
+	// Hit the functor with each item on this page
+	void foreachItem(const std::function<void(const PreferenceItemBasePtr&)>& functor) const;
+	
+	// IPreferencePage implementation
+	void appendCheckBox(const std::string& label, const std::string& registryKey) override;
+
+	void appendSlider(const std::string& name, const std::string& registryKey,
+		double lower, double upper, double stepIncrement, double pageIncrement) override;
+
+	void appendCombo(const std::string& name,
+		const std::string& registryKey,
+		const ComboBoxValueList& valueList,
+		bool storeValueNotIndex = false) override;
+
+	void appendEntry(const std::string& name, const std::string& registryKey) override;
+
+	void appendSpinner(const std::string& name, const std::string& registryKey,
+		double lower, double upper, int fraction) override;
+
+	void appendPathEntry(const std::string& name, const std::string& registryKey,
+		bool browseDirectories) override;
+
+	void appendLabel(const std::string& caption) override;
+};
+
+}
\ No newline at end of file
diff --git a/radiant/settings/PreferenceSystem.cpp b/radiant/settings/PreferenceSystem.cpp
index 60f4649..832d5cf 100644
--- a/radiant/settings/PreferenceSystem.cpp
+++ b/radiant/settings/PreferenceSystem.cpp
@@ -2,55 +2,59 @@
 
 #include "ipreferencesystem.h"
 #include "itextstream.h"
-#include "imodule.h"
-#include "iregistry.h"
-
-#include <time.h>
-#include <iostream>
-
-#include "os/file.h"
-#include "string/string.h"
 
 #include "modulesystem/StaticModule.h"
-#include "modulesystem/ApplicationContextImpl.h"
 #include "ui/prefdialog/PrefDialog.h"
 
-#include <boost/algorithm/string/replace.hpp>
+namespace settings
+{
 
-class PreferenceSystem :
-	public IPreferenceSystem
+IPreferencePage& PreferenceSystem::getPage(const std::string& path)
 {
-public:
-	// Looks up a page for the given path and returns it to the client
-	PreferencesPagePtr getPage(const std::string& path) {
-		return ui::PrefDialog::Instance().createOrFindPage(path);
-	}
+	ensureRootPage();
 
-	// RegisterableModule implementation
-	virtual const std::string& getName() const {
-		static std::string _name(MODULE_PREFERENCESYSTEM);
-		return _name;
-	}
+	return _rootPage->createOrFindPage(path);
+}
 
-	virtual const StringSet& getDependencies() const {
-		static StringSet _dependencies;
+void PreferenceSystem::foreachPage(const std::function<void(PreferencePage&)>& functor)
+{
+	ensureRootPage();
 
-		if (_dependencies.empty()) {
-			_dependencies.insert(MODULE_XMLREGISTRY);
-			_dependencies.insert(MODULE_RADIANT);
-		}
+	_rootPage->foreachChildPage(functor);
+}
 
-		return _dependencies;
+void PreferenceSystem::ensureRootPage()
+{
+	if (!_rootPage)
+	{
+		_rootPage = std::make_shared<PreferencePage>("");
 	}
+}
 
-	virtual void initialiseModule(const ApplicationContext& ctx) {
-		rMessage() << "PreferenceSystem::initialiseModule called\n";
-	}
-};
+// RegisterableModule implementation
+const std::string& PreferenceSystem::getName() const
+{
+	static std::string _name(MODULE_PREFERENCESYSTEM);
+	return _name;
+}
+
+const StringSet& PreferenceSystem::getDependencies() const
+{
+	static StringSet _dependencies;
+	return _dependencies;
+}
+
+void PreferenceSystem::initialiseModule(const ApplicationContext& ctx)
+{
+	rMessage() << "PreferenceSystem::initialiseModule called" << std::endl;
+}
 
 // Define the static PreferenceSystem module
 module::StaticModule<PreferenceSystem> preferenceSystemModule;
 
-IPreferenceSystem& GetPreferenceSystem() {
-	return *preferenceSystemModule.getModule();
+}
+
+settings::PreferenceSystem& GetPreferenceSystem()
+{
+	return *settings::preferenceSystemModule.getModule();
 }
diff --git a/radiant/settings/PreferenceSystem.h b/radiant/settings/PreferenceSystem.h
index a3db104..9d21563 100644
--- a/radiant/settings/PreferenceSystem.h
+++ b/radiant/settings/PreferenceSystem.h
@@ -1,14 +1,38 @@
-#ifndef PREFERENCESYSTEM_H_
-#define PREFERENCESYSTEM_H_
+#pragma once
 
-#include <string>
-class IPreferenceSystem;
+#include "ipreferencesystem.h"
+#include "PreferencePage.h"
 
-/** greebo: Direct accessor method for the preferencesystem for situations
- * 			where the actual PreferenceSystemModule is not loaded yet.
- *
- * 			Everything else should use the GlobalPreferenceSystem() access method.
- */
-IPreferenceSystem& GetPreferenceSystem();
+namespace settings
+{
+
+class PreferenceSystem :
+	public IPreferenceSystem
+{
+private:
+	PreferencePagePtr _rootPage;
+
+public:
+	// Looks up a page for the given path and returns it to the client
+	IPreferencePage& getPage(const std::string& path) override;
+
+	// Internal method to walk over the registered pages
+	void foreachPage(const std::function<void(PreferencePage&)>& functor);
 
-#endif /*PREFERENCESYSTEM_H_*/
+	// RegisterableModule implementation
+	virtual const std::string& getName() const override;
+	virtual const StringSet& getDependencies() const override;
+	virtual void initialiseModule(const ApplicationContext& ctx) override;
+
+private:
+	void ensureRootPage();
+};
+
+} // namespace
+
+/** 
+ * greebo: Direct accessor method for the preferencesystem for situations
+ * where the actual PreferenceSystemModule is not loaded yet.
+ * Everything else should use the GlobalPreferenceSystem() access method.
+ */
+settings::PreferenceSystem& GetPreferenceSystem();
diff --git a/radiant/textool/item/PatchItem.cpp b/radiant/textool/item/PatchItem.cpp
index 95b826a..289e7c9 100644
--- a/radiant/textool/item/PatchItem.cpp
+++ b/radiant/textool/item/PatchItem.cpp
@@ -32,11 +32,11 @@ void PatchItem::render() {
 
 	const RenderIndex* strip_indices = &tess.indices.front();
 
-	for (std::size_t i = 0; i < tess.m_numStrips; i++, strip_indices += tess.m_lenStrips)
+	for (std::size_t i = 0; i < tess.numStrips; i++, strip_indices += tess.lenStrips)
 	{
 		glBegin(GL_QUAD_STRIP);
 
-		for (std::size_t offset = 0; offset < tess.m_lenStrips; offset++)
+		for (std::size_t offset = 0; offset < tess.lenStrips; offset++)
 		{
 			// Retrieve the mesh vertex from the line strip
 			ArbitraryMeshVertex& meshVertex = tess.vertices[*(strip_indices + offset)];
diff --git a/radiant/ui/aas/AasControl.cpp b/radiant/ui/aas/AasControl.cpp
new file mode 100644
index 0000000..109b92e
--- /dev/null
+++ b/radiant/ui/aas/AasControl.cpp
@@ -0,0 +1,123 @@
+#include "AasControl.h"
+
+#include "i18n.h"
+#include "iarchive.h"
+#include "imainframe.h"
+#include "iuimanager.h"
+#include "ifilesystem.h"
+
+#include <wx/event.h>
+#include <wx/button.h>
+#include <wx/bmpbuttn.h>
+#include <wx/tglbtn.h>
+#include <wx/sizer.h>
+#include <wx/artprov.h>
+#include <memory>
+
+#include "map/RenderableAasFile.h"
+
+namespace ui
+{
+
+AasControl::AasControl(wxWindow* parent, const map::AasFileInfo& info) :
+    _toggle(nullptr),
+    _refreshButton(nullptr),
+    _buttonHBox(nullptr),
+    _updateActive(nullptr),
+    _info(info)
+{
+    // Create the main toggle
+	_toggle = new wxToggleButton(parent, wxID_ANY, info.type.fileExtension);
+	_toggle->Connect(wxEVT_TOGGLEBUTTON, wxCommandEventHandler(AasControl::onToggle), NULL, this);
+
+    _refreshButton = new wxBitmapButton(parent, wxID_ANY, 
+		wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "refresh.png"));
+	_refreshButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(AasControl::onRefresh), NULL, this);
+	_refreshButton->SetToolTip(_("Reload AAS File"));
+
+	_buttonHBox = new wxBoxSizer(wxHORIZONTAL);
+	_buttonHBox->Add(_refreshButton, 0, wxEXPAND);
+
+    // Refresh the Control
+	update();
+}
+
+AasControl::~AasControl()
+{
+    // Detach before destruction
+    if (_toggle->GetValue())
+    {
+        GlobalRenderSystem().detachRenderable(_renderable);
+    }
+}
+
+wxSizer* AasControl::getButtons()
+{
+    return _buttonHBox;
+}
+
+wxToggleButton* AasControl::getToggle()
+{
+    return _toggle;
+}
+
+void AasControl::update()
+{
+
+}
+
+void AasControl::ensureAasFileLoaded()
+{
+    if (_aasFile) return;
+
+    ArchiveTextFilePtr file = GlobalFileSystem().openTextFileInAbsolutePath(_info.absolutePath);
+
+    if (file)
+    {
+        std::istream stream(&file->getInputStream());
+        map::IAasFileLoaderPtr loader = GlobalAasFileManager().getLoaderForStream(stream);
+
+        if (loader && loader->canLoad(stream))
+        {
+            stream.seekg(0, std::ios_base::beg);
+
+            _aasFile = loader->loadFromStream(stream);
+
+            // Construct a renderable to attach to the rendersystem
+            _renderable.setAasFile(_aasFile);
+        }
+    }
+}
+
+void AasControl::onToggle(wxCommandEvent& ev)
+{
+    if (_toggle->GetValue())
+    {
+        ensureAasFileLoaded();
+        GlobalRenderSystem().attachRenderable(_renderable);
+    }
+    else
+    {
+        // Disable rendering
+        GlobalRenderSystem().detachRenderable(_renderable);
+    }
+
+    GlobalMainFrame().updateAllWindows();
+}
+
+void AasControl::onRefresh(wxCommandEvent& ev)
+{
+    // Detach renderable
+    _aasFile.reset();
+
+    if (_toggle->GetValue())
+    {
+		GlobalRenderSystem().detachRenderable(_renderable);
+
+        ensureAasFileLoaded();
+
+        GlobalRenderSystem().attachRenderable(_renderable);
+    }
+}
+
+} // namespace ui
diff --git a/radiant/ui/aas/AasControl.h b/radiant/ui/aas/AasControl.h
new file mode 100644
index 0000000..61e4145
--- /dev/null
+++ b/radiant/ui/aas/AasControl.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <wx/event.h>
+#include <memory>
+#include "iaasfile.h"
+#include "map/RenderableAasFile.h"
+
+class wxWindow;
+class wxButton;
+class wxToggleButton;
+class wxSizer;
+
+namespace ui
+{
+
+/**
+ * greebo: An AasControl contains a set of widgets needed
+ * to control an associated AAS file.
+ *
+ * Multiple of these Controls can be packed as children into the
+ * owning AasControlDialog.
+ */
+class AasControl :
+	public wxEvtHandler
+{
+private:
+	wxToggleButton* _toggle;
+	wxButton* _refreshButton;
+	wxSizer* _buttonHBox;
+
+	// Locks down the callbacks during widget update
+	bool _updateActive;
+
+    // The AAS file this control is referring to
+    map::AasFileInfo _info;
+
+    // The AAS file reference (can be empty)
+    map::IAasFilePtr _aasFile;
+
+    // The renderable that is attached to the rendersystem when active
+    map::RenderableAasFile _renderable;
+
+public:
+	AasControl(wxWindow* parent, const map::AasFileInfo& info);
+
+    virtual ~AasControl();
+
+	// Returns the widgets for packing this object into a container/table
+	wxSizer* getButtons();
+	wxToggleButton* getToggle();
+
+	// Updates the state of all widgets
+	void update();
+
+private:
+    void ensureAasFileLoaded();
+
+	void onToggle(wxCommandEvent& ev);
+	void onRefresh(wxCommandEvent& ev);
+};
+typedef std::shared_ptr<AasControl> AasControlPtr;
+
+} // namespace ui
diff --git a/radiant/ui/aas/AasControlDialog.cpp b/radiant/ui/aas/AasControlDialog.cpp
new file mode 100644
index 0000000..25860dc
--- /dev/null
+++ b/radiant/ui/aas/AasControlDialog.cpp
@@ -0,0 +1,237 @@
+#include "AasControlDialog.h"
+
+#include "i18n.h"
+#include "iaasfile.h"
+#include "iradiant.h"
+#include "imainframe.h"
+#include "itextstream.h"
+#include "ieventmanager.h"
+
+#include <wx/button.h>
+#include <wx/tglbtn.h>
+#include <wx/sizer.h>
+#include <wx/panel.h>
+#include <wx/scrolwin.h>
+
+#include "registry/Widgets.h"
+#include "map/Map.h"
+#include "map/RenderableAasFile.h"
+
+namespace ui
+{
+
+namespace
+{
+	const std::string RKEY_ROOT = "user/ui/aas/controlDialog/";
+	const std::string RKEY_WINDOW_STATE = RKEY_ROOT + "window";
+}
+
+AasControlDialog::AasControlDialog() :
+	TransientWindow(_("AAS Viewer"), GlobalMainFrame().getWxTopLevelWindow(), true),
+	_dialogPanel(nullptr),
+	_controlContainer(nullptr)
+{
+	populateWindow();
+
+	InitialiseWindowPosition(135, 100, RKEY_WINDOW_STATE);
+    SetMinClientSize(wxSize(135, 100));
+
+	_mapEventSlot = GlobalMapModule().signal_mapEvent().connect(
+		sigc::mem_fun(*this, &AasControlDialog::onMapEvent));
+}
+
+void AasControlDialog::onMapEvent(IMap::MapEvent ev)
+{
+	switch (ev)
+	{
+	case IMap::MapEvent::MapLoaded:
+		refresh();
+		break;
+	case IMap::MapEvent::MapUnloading:
+		clearControls();
+		break;
+	default:
+		break;
+	};
+}
+
+void AasControlDialog::populateWindow()
+{
+	wxScrolledWindow* dialogPanel = new wxScrolledWindow(this, wxID_ANY);
+	dialogPanel->SetScrollRate(0, 15);
+
+	_dialogPanel = dialogPanel;
+	
+	_dialogPanel->SetSizer(new wxBoxSizer(wxVERTICAL));
+
+	_controlContainer = new wxFlexGridSizer(1, 2, 3, 3);
+	_controlContainer->AddGrowableCol(0);
+
+    _dialogPanel->GetSizer()->Add(_controlContainer, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 12);
+
+	createButtons();
+
+	_dialogPanel->FitInside(); // ask the sizer about the needed size
+}
+
+void AasControlDialog::createButtons()
+{
+	// Rescan button
+	_rescanButton = new wxButton(_dialogPanel, wxID_ANY, _("Search for AAS Files"));
+    _rescanButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& ev) { refresh(); });
+
+	// Bind the toggle button to the registry key
+	wxToggleButton* showNumbersButton = new wxToggleButton(_dialogPanel, wxID_ANY, _("Show Area Numbers"));
+	registry::bindWidget(showNumbersButton, map::RKEY_SHOW_AAS_AREA_NUMBERS);
+	
+	wxToggleButton* hideDistantAreasButton = new wxToggleButton(_dialogPanel, wxID_ANY, _("Hide distant Areas"));
+	registry::bindWidget(hideDistantAreasButton, map::RKEY_HIDE_DISTANT_AAS_AREAS);
+
+	_dialogPanel->GetSizer()->Add(showNumbersButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
+	_dialogPanel->GetSizer()->Add(hideDistantAreasButton, 0, wxEXPAND | wxLEFT | wxRIGHT, 12);
+	_dialogPanel->GetSizer()->Add(_rescanButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 12);
+}
+
+void AasControlDialog::clearControls()
+{
+	// Remove all previously allocated controls
+	_aasControls.clear();
+
+	// Delete all wxWidgets objects 
+	_controlContainer->Clear(true);
+}
+
+void AasControlDialog::refresh()
+{
+	clearControls();
+
+	std::map<std::string, AasControlPtr> sortedControls;
+
+	// Find all available AAS files for the current map
+    
+    std::list<map::AasFileInfo> aasFiles = GlobalAasFileManager().getAasFilesForMap(GlobalMap().getMapName());
+
+    for (map::AasFileInfo& info : aasFiles)
+    {
+        // Create a new control for each AAS type
+        // Store the object in a sorted container
+        sortedControls[info.type.fileExtension] = std::make_shared<AasControl>(_dialogPanel, info);
+    }
+
+    // Assign all controls to the target vector, alphabetically sorted
+    for (const auto& pair : sortedControls)
+    {
+        _aasControls.push_back(pair.second);
+    }
+
+	_controlContainer->SetRows(static_cast<int>(_aasControls.size()));
+
+	for (AasControls::iterator i = _aasControls.begin(); i != _aasControls.end(); ++i)
+	{
+		_controlContainer->Add((*i)->getToggle(), 1, wxEXPAND);
+		_controlContainer->Add((*i)->getButtons(), 0, wxEXPAND);
+
+        if (i == _aasControls.begin())
+        {
+            // Prevent setting the focus on the buttons at the bottom which lets the scrollbar 
+            // of the window jump around, set the focus on the first button.
+            (*i)->getToggle()->SetFocus();
+        }
+	}
+
+	_controlContainer->Layout();
+	_dialogPanel->FitInside(); // ask the sizer about the needed size
+
+	update();
+}
+
+void AasControlDialog::update()
+{
+	// Broadcast the update() call
+	for (const AasControlPtr& control : _aasControls)
+	{
+		control->update();
+	}
+}
+
+// TransientWindow callbacks
+void AasControlDialog::_preShow()
+{
+	TransientWindow::_preShow();
+
+	// Re-populate the dialog
+	refresh();
+}
+
+void AasControlDialog::OnRadiantStartup()
+{
+    // Lookup the stored window information in the registry
+    if (GlobalRegistry().getAttribute(RKEY_WINDOW_STATE, "visible") == "1")
+    {
+        // Show dialog
+        Instance().Show();
+    }
+}
+
+void AasControlDialog::onRadiantShutdown()
+{
+	rMessage() << "AasControlDialog shutting down." << std::endl;
+
+	_mapEventSlot.disconnect();
+
+	// Write the visibility status to the registry
+	GlobalRegistry().setAttribute(RKEY_WINDOW_STATE, "visible", IsShownOnScreen() ? "1" : "0");
+
+    // Hide the window and save its state
+    if (IsShownOnScreen())
+    {
+        Hide();
+    }
+
+	// Destroy the window (after it has been disconnected from the Eventmanager)
+	SendDestroyEvent();
+
+	// Final step: clear the instance pointer
+	InstancePtr().reset();
+}
+
+AasControlDialogPtr& AasControlDialog::InstancePtr()
+{
+	static AasControlDialogPtr _instancePtr;
+	return _instancePtr;
+}
+
+AasControlDialog& AasControlDialog::Instance()
+{
+	AasControlDialogPtr& instancePtr = InstancePtr();
+
+	if (instancePtr == NULL)
+	{
+		// Not yet instantiated, do it now
+		instancePtr.reset(new AasControlDialog);
+
+		// Register this instance with GlobalRadiant() at once
+		GlobalRadiant().signal_radiantShutdown().connect(
+            sigc::mem_fun(*instancePtr, &AasControlDialog::onRadiantShutdown)
+        );
+	}
+
+	return *instancePtr;
+}
+
+void AasControlDialog::Init()
+{
+    GlobalCommandSystem().addCommand("ToggleAasControlDialog", AasControlDialog::Toggle);
+	GlobalEventManager().addCommand("ToggleAasControlDialog", "ToggleAasControlDialog");
+
+    GlobalRadiant().signal_radiantStarted().connect(
+        sigc::ptr_fun(&AasControlDialog::OnRadiantStartup)
+    );
+}
+
+void AasControlDialog::Toggle(const cmd::ArgumentList& args)
+{
+	Instance().ToggleVisibility();
+}
+
+}
diff --git a/radiant/ui/aas/AasControlDialog.h b/radiant/ui/aas/AasControlDialog.h
new file mode 100644
index 0000000..c30f872
--- /dev/null
+++ b/radiant/ui/aas/AasControlDialog.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "wxutil/window/TransientWindow.h"
+#include "AasControl.h"
+#include "imap.h"
+#include "icommandsystem.h"
+#include <sigc++/connection.h>
+
+class wxFlexGridSizer;
+class wxButton;
+class wxPanel;
+
+namespace ui
+{
+
+class AasControlDialog;
+typedef std::shared_ptr<AasControlDialog> AasControlDialogPtr;
+
+class AasControlDialog :
+	public wxutil::TransientWindow
+{
+private:
+	typedef std::vector<AasControlPtr> AasControls;
+	AasControls _aasControls;
+
+	wxPanel* _dialogPanel;
+
+	wxFlexGridSizer* _controlContainer;
+    wxButton* _rescanButton;
+
+	sigc::connection _mapEventSlot;
+
+public:
+	AasControlDialog();
+
+    // Re-populates the window
+	void refresh();
+
+	// Updates the state of all controls
+	void update();
+
+	// Command target (registered in the event manager)
+	static void Toggle(const cmd::ArgumentList& args);
+
+	// Called during AAS module initialisation
+	static void Init();
+    static void OnRadiantStartup();
+
+	static AasControlDialog& Instance();
+
+private:
+	static AasControlDialogPtr& InstancePtr();
+
+	// TransientWindow events
+	void _preShow();
+
+	void populateWindow();
+    void createButtons();
+    void onRadiantShutdown();
+	void clearControls();
+
+	void onMapEvent(IMap::MapEvent ev);
+};
+
+}
diff --git a/radiant/ui/einspector/AnglePropertyEditor.h b/radiant/ui/einspector/AnglePropertyEditor.h
index acbb50b..fd87f5f 100644
--- a/radiant/ui/einspector/AnglePropertyEditor.h
+++ b/radiant/ui/einspector/AnglePropertyEditor.h
@@ -47,7 +47,7 @@ public:
     /* PropertyEditor implementation */
     IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
                                 const std::string& key,
-                                const std::string& options)
+                                const std::string& options) override
     {
         return PropertyEditorPtr(new AnglePropertyEditor(parent, entity, key));
     }
diff --git a/radiant/ui/einspector/BooleanPropertyEditor.cpp b/radiant/ui/einspector/BooleanPropertyEditor.cpp
index e37a9dc..4dee2da 100644
--- a/radiant/ui/einspector/BooleanPropertyEditor.cpp
+++ b/radiant/ui/einspector/BooleanPropertyEditor.cpp
@@ -31,13 +31,19 @@ BooleanPropertyEditor::BooleanPropertyEditor(wxWindow* parent, Entity* entity,
 	// Create the checkbox with correct initial state, and connect up the
 	// toggle callback
 	_checkBox = new wxCheckBox(mainVBox, wxID_ANY, name);
-	_checkBox->SetValue(_entity->getKeyValue(_key) == "1");
+
+	updateFromEntity();
 
 	_checkBox->Connect(wxEVT_CHECKBOX, wxCommandEventHandler(BooleanPropertyEditor::_onToggle), NULL, this);
 
 	mainVBox->GetSizer()->Add(_checkBox, 0, wxALIGN_CENTER_VERTICAL);
 }
 
+void BooleanPropertyEditor::updateFromEntity()
+{
+	_checkBox->SetValue(_entity->getKeyValue(_key) == "1");
+}
+
 void BooleanPropertyEditor::_onToggle(wxCommandEvent& ev)
 {
 	// Set the key based on the checkbutton state
diff --git a/radiant/ui/einspector/BooleanPropertyEditor.h b/radiant/ui/einspector/BooleanPropertyEditor.h
index 8bd1b10..406b344 100644
--- a/radiant/ui/einspector/BooleanPropertyEditor.h
+++ b/radiant/ui/einspector/BooleanPropertyEditor.h
@@ -36,10 +36,12 @@ public:
 	// PropertyEditorFactory
 	BooleanPropertyEditor();
 
+	void updateFromEntity() override;
+
 	// Create a new BooleanPropertyEditor
     virtual IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
     									const std::string& name,
-    									const std::string& options)
+    									const std::string& options) override
 	{
     	return PropertyEditorPtr(new BooleanPropertyEditor(parent, entity, name));
     }
diff --git a/radiant/ui/einspector/ClassnamePropertyEditor.cpp b/radiant/ui/einspector/ClassnamePropertyEditor.cpp
index dd4c3df..4a6567c 100644
--- a/radiant/ui/einspector/ClassnamePropertyEditor.cpp
+++ b/radiant/ui/einspector/ClassnamePropertyEditor.cpp
@@ -8,6 +8,7 @@
 #include <wx/panel.h>
 #include <wx/button.h>
 #include <wx/sizer.h>
+#include "wxutil/dialog/MessageBox.h"
 
 #include "selection/algorithm/Entity.h"
 #include "ui/entitychooser/EntityClassChooser.h"
@@ -38,8 +39,15 @@ void ClassnamePropertyEditor::onBrowseButtonClick()
 	{
 		UndoableCommand cmd("changeEntityClass");
 
-		// Apply the classname change to the entity, this requires some algorithm
-		selection::algorithm::setEntityClassname(selection);
+		try
+		{
+			// Apply the classname change to the entity, this requires some algorithm
+			selection::algorithm::setEntityClassname(selection);
+		}
+		catch (std::runtime_error& ex)
+		{
+			wxutil::Messagebox::ShowError(ex.what());
+		}
 	}
 }
 
diff --git a/radiant/ui/einspector/ClassnamePropertyEditor.h b/radiant/ui/einspector/ClassnamePropertyEditor.h
index d0cb635..467d5d3 100644
--- a/radiant/ui/einspector/ClassnamePropertyEditor.h
+++ b/radiant/ui/einspector/ClassnamePropertyEditor.h
@@ -37,7 +37,7 @@ public:
 	// Clone method for virtual construction
 	IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
 								const std::string& name,
-								const std::string& options)
+								const std::string& options) override
 	{
 		return PropertyEditorPtr(
 			new ClassnamePropertyEditor(parent, entity, name, options)
diff --git a/radiant/ui/einspector/ColourPropertyEditor.cpp b/radiant/ui/einspector/ColourPropertyEditor.cpp
index 56ae6a0..954fb76 100644
--- a/radiant/ui/einspector/ColourPropertyEditor.cpp
+++ b/radiant/ui/einspector/ColourPropertyEditor.cpp
@@ -40,8 +40,13 @@ ColourPropertyEditor::ColourPropertyEditor(wxWindow* parent, Entity* entity,
 
 	mainVBox->GetSizer()->Add(_colorButton, 1, wxEXPAND | wxALL, 15);
 
+	updateFromEntity();
+}
+
+void ColourPropertyEditor::updateFromEntity()
+{
 	// Set colour button's colour
-	setColourButton(_entity->getKeyValue(name));
+	setColourButton(_entity->getKeyValue(_key));
 }
 
 // Set displayed colour from the keyvalue
diff --git a/radiant/ui/einspector/ColourPropertyEditor.h b/radiant/ui/einspector/ColourPropertyEditor.h
index 45bb2b5..eb283e1 100644
--- a/radiant/ui/einspector/ColourPropertyEditor.h
+++ b/radiant/ui/einspector/ColourPropertyEditor.h
@@ -44,10 +44,12 @@ public:
 	/// Blank constructor for the PropertyEditorFactory
 	ColourPropertyEditor();
 
+	void updateFromEntity() override;
+
 	/// Create a new ColourPropertyEditor
     virtual IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
     									const std::string& name,
-    									const std::string& options)
+    									const std::string& options) override
 	{
     	return PropertyEditorPtr(new ColourPropertyEditor(parent, entity, name));
     }
diff --git a/radiant/ui/einspector/EntityInspector.cpp b/radiant/ui/einspector/EntityInspector.cpp
index e3bffc8..e3ac3f5 100644
--- a/radiant/ui/einspector/EntityInspector.cpp
+++ b/radiant/ui/einspector/EntityInspector.cpp
@@ -232,6 +232,20 @@ void EntityInspector::onKeyChange(const std::string& key,
 	row[_columns.name] = wxVariant(wxDataViewIconText(key, icon));
 	row[_columns.value] = value;
 
+	if (parms.type == "bool")
+	{
+		// Render a checkbox for boolean values (store an actual bool)
+		row[_columns.booleanValue] = value == "1";
+
+		// Column is enabled by default after assignment
+	}
+	else
+	{
+		// Store false to render the checkbox as unchecked
+		row[_columns.booleanValue] = false;
+		row[_columns.booleanValue].setEnabled(false);
+	}
+
 	// Text colour
 	row[_columns.name] = black;
 	row[_columns.value] = black;
@@ -261,6 +275,12 @@ void EntityInspector::onKeyChange(const std::string& key,
 	{
 		_valEntry->SetValue(value);
 	}
+
+	// Also update the property editor if the changed key is highlighted
+	if (_currentPropertyEditor && key == selectedKey)
+	{
+		_currentPropertyEditor->updateFromEntity();
+	}
 }
 
 void EntityInspector::onKeyErase(const std::string& key,
@@ -290,7 +310,8 @@ void EntityInspector::createContextMenu()
 
 	_contextMenu->addItem(
 		new wxutil::StockIconTextMenuItem(_("Add property..."), wxART_PLUS),
-		std::bind(&EntityInspector::_onAddKey, this)
+		std::bind(&EntityInspector::_onAddKey, this),
+		std::bind(&EntityInspector::_testAddKey, this)
 	);
 	_contextMenu->addItem(
 		new wxutil::StockIconTextMenuItem(_("Delete property"), wxART_MINUS),
@@ -301,22 +322,37 @@ void EntityInspector::createContextMenu()
 	_contextMenu->addSeparator();
 
 	_contextMenu->addItem(
-		new wxutil::StockIconTextMenuItem(_("Copy Spawnarg"), wxART_COPY),
+		new wxutil::StockIconTextMenuItem(_("Copy Spawnarg(s)"), wxART_COPY),
 		std::bind(&EntityInspector::_onCopyKey, this),
 		std::bind(&EntityInspector::_testCopyKey, this)
 	);
 	_contextMenu->addItem(
-		new wxutil::StockIconTextMenuItem(_("Cut Spawnarg"), wxART_CUT),
+		new wxutil::StockIconTextMenuItem(_("Cut Spawnarg(s)"), wxART_CUT),
 		std::bind(&EntityInspector::_onCutKey, this),
 		std::bind(&EntityInspector::_testCutKey, this)
 	);
 	_contextMenu->addItem(
-		new wxutil::StockIconTextMenuItem(_("Paste Spawnarg"), wxART_PASTE),
+		new wxutil::StockIconTextMenuItem(_("Paste Spawnarg(s)"), wxART_PASTE),
 		std::bind(&EntityInspector::_onPasteKey, this),
 		std::bind(&EntityInspector::_testPasteKey, this)
 	);
 }
 
+void EntityInspector::onRadiantStartup()
+{
+	// Add entity inspector to the group dialog
+	IGroupDialog::PagePtr page(new IGroupDialog::Page);
+
+	page->name = "entity";
+	page->windowLabel = _("Entity");
+	page->page = getWidget();
+	page->tabIcon = "cmenu_add_entity.png";
+	page->tabLabel = _("Entity");
+	page->position = IGroupDialog::Page::Position::EntityInspector;
+
+	GlobalGroupDialog().addPage(page);
+}
+
 void EntityInspector::onRadiantShutdown()
 {
     // Remove all previously stored pane information
@@ -355,7 +391,8 @@ const StringSet& EntityInspector::getDependencies() const
 {
 	static StringSet _dependencies;
 
-	if (_dependencies.empty()) {
+	if (_dependencies.empty())
+	{
 		_dependencies.insert(MODULE_XMLREGISTRY);
 		_dependencies.insert(MODULE_UIMANAGER);
 		_dependencies.insert(MODULE_SELECTIONSYSTEM);
@@ -373,6 +410,9 @@ void EntityInspector::initialiseModule(const ApplicationContext& ctx)
 {
 	construct();
 
+	GlobalRadiant().signal_radiantStarted().connect(
+		sigc::mem_fun(this, &EntityInspector::onRadiantStartup)
+	);
 	GlobalRadiant().signal_radiantShutdown().connect(
         sigc::mem_fun(this, &EntityInspector::onRadiantShutdown)
     );
@@ -422,12 +462,16 @@ wxWindow* EntityInspector::createTreeViewPane(wxWindow* parent)
 
 	_kvStore = new wxutil::TreeModel(_columns, true); // this is a list model
 
-	_keyValueTreeView = wxutil::TreeView::CreateWithModel(treeViewPanel, _kvStore, wxDV_SINGLE);
+	_keyValueTreeView = wxutil::TreeView::CreateWithModel(treeViewPanel, _kvStore, wxDV_MULTIPLE);
 
     // Search in both name and value columns
     _keyValueTreeView->AddSearchColumn(_columns.name);
     _keyValueTreeView->AddSearchColumn(_columns.value);
 
+	// Add the checkbox for boolean properties
+	_keyValueTreeView->AppendToggleColumn("", _columns.booleanValue.getColumnIndex(),
+		wxDATAVIEW_CELL_ACTIVATABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT);
+
 	// Create the Property column (has an icon)
 	_keyValueTreeView->AppendIconTextColumn(_("Property"), 
 		_columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, 
@@ -440,7 +484,7 @@ wxWindow* EntityInspector::createTreeViewPane(wxWindow* parent)
 
 	// Add the help icon
 	_helpColumn = _keyValueTreeView->AppendBitmapColumn(_("?"), 
-		_columns.helpIcon.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE);
+		_columns.helpIcon.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT);
 	_helpColumn->SetHidden(true);
 
 	// Used to update the help text
@@ -449,6 +493,11 @@ wxWindow* EntityInspector::createTreeViewPane(wxWindow* parent)
 	_keyValueTreeView->Connect(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, 
 		wxDataViewEventHandler(EntityInspector::_onContextMenu), NULL, this);
 
+	// When the toggle column is clicked to check/uncheck the box, the model's column value
+	// is directly changed by the wxWidgets event handlers. On model value change, this event is fired afterwards
+	_keyValueTreeView->Connect(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED,
+		wxDataViewEventHandler(EntityInspector::_onDataViewItemChanged), NULL, this);
+
 	wxBoxSizer* buttonHbox = new wxBoxSizer(wxHORIZONTAL);
 
 	// Pack in the key and value edit boxes
@@ -476,7 +525,14 @@ wxWindow* EntityInspector::createTreeViewPane(wxWindow* parent)
 
 std::string EntityInspector::getSelectedKey()
 {
-	wxDataViewItem item = _keyValueTreeView->GetSelection();
+	wxDataViewItemArray selectedItems;
+	if (_keyValueTreeView->GetSelections(selectedItems) != 1)
+	{
+		// Multiple or nothing selected, return empty string
+		return std::string();
+	}
+
+	const wxDataViewItem& item = selectedItems.front();
 
 	if (!item.IsOk()) return "";
 
@@ -488,7 +544,14 @@ std::string EntityInspector::getSelectedKey()
 
 std::string EntityInspector::getListSelection(const wxutil::TreeModel::Column& col)
 {
-	wxDataViewItem item = _keyValueTreeView->GetSelection();
+	wxDataViewItemArray selectedItems;
+	if (_keyValueTreeView->GetSelections(selectedItems) != 1)
+	{
+		// Multiple or nothing selected, return empty string
+		return std::string();
+	}
+
+	const wxDataViewItem& item = selectedItems.front();
 
 	if (!item.IsOk()) return "";
 
@@ -499,7 +562,14 @@ std::string EntityInspector::getListSelection(const wxutil::TreeModel::Column& c
 
 bool EntityInspector::getListSelectionBool(const wxutil::TreeModel::Column& col)
 {
-	wxDataViewItem item = _keyValueTreeView->GetSelection();
+	wxDataViewItemArray selectedItems;
+	if (_keyValueTreeView->GetSelections(selectedItems) != 1)
+	{
+		// Multiple or nothing selected, return false
+		return false;
+	}
+
+	const wxDataViewItem& item = selectedItems.front();
 
 	if (!item.IsOk()) return false;
 
@@ -533,7 +603,7 @@ void EntityInspector::updateGUIElements()
 
         // Disable the dialog and clear the TreeView
 		_editorFrame->Enable(false);
-        _keyValueTreeView->Enable(false);
+        _keyValueTreeView->Enable(true); // leave the treeview enabled
         _showInheritedCheckbox->Enable(false);
         _showHelpColumnCheckbox->Enable(false);
     }
@@ -550,70 +620,13 @@ void EntityInspector::selectionChanged(const scene::INodePtr& node, bool isCompo
     requestIdleCallback();
 }
 
-namespace
-{
-
-    // SelectionSystem visitor to set a keyvalue on each entity, checking for
-    // func_static-style name=model requirements
-    class EntityKeySetter
-    : public SelectionSystem::Visitor
-    {
-        // Key and value to set on all entities
-        std::string _key;
-        std::string _value;
-
-    public:
-
-        // Construct with key and value to set
-        EntityKeySetter(const std::string& k, const std::string& v)
-        : _key(k), _value(v)
-        { }
-
-        // Required visit function
-        void visit(const scene::INodePtr& node) const
-        {
-            Entity* entity = Node_getEntity(node);
-            if (entity)
-            {
-                // Check if we have a func_static-style entity
-                std::string name = entity->getKeyValue("name");
-                std::string model = entity->getKeyValue("model");
-                bool isFuncType = (!name.empty() && name == model);
-
-                // Set the actual value
-                entity->setKeyValue(_key, _value);
-
-                // Check for name key changes of func_statics
-                if (isFuncType && _key == "name")
-                {
-                    // Adapt the model key along with the name
-                    entity->setKeyValue("model", _value);
-				}
-            }
-			else if (Node_isPrimitive(node)) {
-				// We have a primitve node selected, check its parent
-				scene::INodePtr parent(node->getParent());
-
-				if (parent == NULL) return;
-
-				Entity* parentEnt = Node_getEntity(parent);
-
-				if (parentEnt != NULL) {
-					// We have child primitive of an entity selected, the change
-					// should go right into that parent entity
-					parentEnt->setKeyValue(_key, _value);
-				}
-			}
-        }
-    };
-}
-
 std::string EntityInspector::cleanInputString(const std::string &input)
 {
     std::string ret = input;
 
-    boost::algorithm::replace_all( ret, "\n", "" );
-    boost::algorithm::replace_all( ret, "\r", "" );
+    boost::algorithm::replace_all(ret, "\n", "");
+    boost::algorithm::replace_all(ret, "\r", "");
+
     return ret;
 }
 
@@ -628,44 +641,21 @@ void EntityInspector::setPropertyFromEntries()
     _keyEntry->SetValue(key);
 	_valEntry->SetValue(val);
 
+	UndoableCommand cmd("entitySetProperty");
+
 	// Pass the call to the specialised routine
 	applyKeyValueToSelection(key, val);
 }
 
 void EntityInspector::applyKeyValueToSelection(const std::string& key, const std::string& val)
 {
-	// greebo: Instantiate a scoped object to make this operation undoable
-	UndoableCommand command("entitySetProperty");
-
-	if (key.empty()) {
-		return;
-	}
-
-	if (key == "name") {
-		// Check the global namespace if this change is ok
-		scene::IMapRootNodePtr mapRoot = GlobalMapModule().getRoot();
-		if (mapRoot != NULL) {
-			INamespacePtr nspace = mapRoot->getNamespace();
-
-			if (nspace != NULL && nspace->nameExists(val))
-            {
-				// name exists, cancel the change
-				wxutil::Messagebox::ShowError(
-					(boost::format(_("The name %s already exists in this map!")) % val).str());
-				return;
-			}
-		}
-	}
-
-	// Detect classname changes
-    if (key == "classname") {
-		// Classname changes are handled in a special way
-		selection::algorithm::setEntityClassname(val);
+	try
+	{
+		selection::algorithm::setEntityKeyvalue(key, val);
 	}
-	else {
-		// Regular key change, use EntityKeySetter to set value on all selected entities
-		EntityKeySetter setter(key, val);
-		GlobalSelectionSystem().foreachSelected(setter);
+	catch (std::runtime_error& ex)
+	{
+		wxutil::Messagebox::ShowError(ex.what());
 	}
 }
 
@@ -716,6 +706,11 @@ void EntityInspector::_onAddKey()
     }
 }
 
+bool EntityInspector::_testAddKey()
+{
+	return !_selectedEntity.expired();
+}
+
 void EntityInspector::_onDeleteKey()
 {
     assert(!_selectedEntity.expired());
@@ -733,6 +728,10 @@ void EntityInspector::_onDeleteKey()
 
 bool EntityInspector::_testDeleteKey()
 {
+	if (_selectedEntity.expired()) return false;
+
+	if (getSelectedKey().empty()) return false;
+
 	// Make sure the Delete item is only available for explicit
 	// (non-inherited) properties
 	if (getListSelectionBool(_columns.isInherited) == false)
@@ -743,6 +742,25 @@ bool EntityInspector::_testDeleteKey()
 
 void EntityInspector::_onCopyKey()
 {
+	wxDataViewItemArray selectedItems;
+	_keyValueTreeView->GetSelections(selectedItems);
+
+	if (selectedItems.Count() == 0) return;
+
+	_clipBoard.clear();
+
+	for (const wxDataViewItem& item : selectedItems)
+	{
+		wxutil::TreeModel::Row row(item, *_kvStore);
+
+		wxDataViewIconText iconAndName = static_cast<wxDataViewIconText>(row[_columns.name]);
+
+		std::string key = iconAndName.GetText().ToStdString();
+		std::string value = row[_columns.value];
+
+		_clipBoard.push_back(std::make_pair(key, value));
+	}
+#if 0
 	std::string key = getSelectedKey();
     std::string value = getListSelection(_columns.value);
 
@@ -751,15 +769,47 @@ void EntityInspector::_onCopyKey()
 		_clipBoard.key = key;
 		_clipBoard.value = value;
 	}
+#endif
 }
 
 bool EntityInspector::_testCopyKey()
 {
-	return !getSelectedKey().empty();
+	return _keyValueTreeView->HasSelection();
 }
 
 void EntityInspector::_onCutKey()
 {
+	wxDataViewItemArray selectedItems;
+	_keyValueTreeView->GetSelections(selectedItems);
+
+	if (selectedItems.Count() == 0) return;
+
+	assert(!_selectedEntity.expired());
+	Entity* selectedEntity = Node_getEntity(_selectedEntity.lock());
+
+	_clipBoard.clear();
+
+	for (const wxDataViewItem& item : selectedItems)
+	{
+		wxutil::TreeModel::Row row(item, *_kvStore);
+
+		wxDataViewIconText iconAndName = static_cast<wxDataViewIconText>(row[_columns.name]);
+
+		std::string key = iconAndName.GetText().ToStdString();
+		std::string value = row[_columns.value];
+
+		_clipBoard.push_back(std::make_pair(key, value));
+
+		// We don't delete any inherited key values
+		if (row[_columns.isInherited].getBool()) continue;
+
+		UndoableCommand cmd("cutProperty");
+
+		// Clear the key after copying
+		selectedEntity->setKeyValue(key, "");
+	}
+
+#if 0
 	std::string key = getSelectedKey();
     std::string value = getListSelection(_columns.value);
 
@@ -776,10 +826,26 @@ void EntityInspector::_onCutKey()
 		// Clear the key after copying
         selectedEntity->setKeyValue(key, "");
 	}
+#endif
 }
 
 bool EntityInspector::_testCutKey()
 {
+	wxDataViewItemArray selectedItems;
+	_keyValueTreeView->GetSelections(selectedItems);
+
+	for (const wxDataViewItem& item : selectedItems)
+	{
+		wxutil::TreeModel::Row row(item, *_kvStore);
+
+		if (!row[_columns.isInherited].getBool())
+		{
+			return true; // we have at least one non-inherited value
+		}
+	}
+
+	return false;
+#if 0
 	// Make sure the Delete item is only available for explicit
 	// (non-inherited) properties
 	if (getListSelectionBool(_columns.isInherited) == false)
@@ -791,14 +857,21 @@ bool EntityInspector::_testCutKey()
 	{
 		return false;
 	}
+#endif
 }
 
 void EntityInspector::_onPasteKey()
 {
-	if (!_clipBoard.key.empty() && !_clipBoard.value.empty())
+	// greebo: Instantiate a scoped object to make this operation undoable
+	UndoableCommand command("entitySetProperties");
+
+	for (const KeyValuePair& kv : _clipBoard)
 	{
+		// skip empty entries
+		if (kv.first.empty() || kv.second.empty()) continue;
+
 		// Pass the call
-		applyKeyValueToSelection(_clipBoard.key, _clipBoard.value);
+		applyKeyValueToSelection(kv.first, kv.second);
 	}
 }
 
@@ -811,7 +884,7 @@ bool EntityInspector::_testPasteKey()
 	}
 
 	// Return true if the clipboard contains data
-	return !_clipBoard.key.empty() && !_clipBoard.value.empty();
+	return !_clipBoard.empty();
 }
 
 // wxWidget callbacks
@@ -821,6 +894,45 @@ void EntityInspector::_onContextMenu(wxDataViewEvent& ev)
 	_contextMenu->show(_keyValueTreeView);
 }
 
+void EntityInspector::_onDataViewItemChanged(wxDataViewEvent& ev)
+{
+	if (ev.GetDataViewColumn() != nullptr && 
+		static_cast<int>(ev.GetDataViewColumn()->GetModelColumn()) == _columns.booleanValue.getColumnIndex())
+	{
+		// Model value in the boolean column has changed, this means
+		// the user has clicked the checkbox, send the value to the entity/entities
+		wxutil::TreeModel::Row row(ev.GetItem(), *_kvStore);
+
+		wxDataViewIconText iconAndName = static_cast<wxDataViewIconText>(row[_columns.name]);
+
+		std::string key = iconAndName.GetText().ToStdString();
+		bool updatedValue = row[_columns.booleanValue].getBool();
+
+		UndoableCommand cmd("entitySetProperty");
+		applyKeyValueToSelection(key, updatedValue ? "1" : "0");
+		
+		// Check if the property was an inherited one.
+		// The applyKeyValue function produced a non-inherited entry
+		// which should be visible once we're done
+		// Note: selecting the non-inherited property instead of this one
+		// is not as easy as it may appear, since the user is yet to release
+		// the mouse button (we're in the middle of the click event here)
+		// and the MouseUp handler will select this row again
+		if (row[_columns.isInherited].getBool())
+		{
+			_kvStore->ForeachNode([&](wxutil::TreeModel::Row& row)
+			{
+				wxDataViewIconText nameVal = static_cast<wxDataViewIconText>(row[_columns.name]);
+
+				if (nameVal.GetText() == key && !row[_columns.isInherited].getBool())
+				{
+					_keyValueTreeView->EnsureVisible(row.getItem());
+				}
+			});
+		}
+	}
+}
+
 void EntityInspector::_onSetProperty(wxCommandEvent& ev)
 {
 	setPropertyFromEntries();
@@ -890,57 +1002,70 @@ void EntityInspector::updateHelpText(const wxutil::TreeModel::Row& row)
 // and making sure it refers to the currently-selected Entity.
 void EntityInspector::_onTreeViewSelectionChanged(wxDataViewEvent& ev)
 {
-	ev.Skip();
+    ev.Skip();
 
-	// Abort if called without a valid entity selection (may happen during
-	// various cleanup operations).
-	if (_selectedEntity.expired()) return;
+    // Abort if called without a valid entity selection (may happen during
+    // various cleanup operations).
+    if (_selectedEntity.expired()) return;
 
-	wxutil::TreeModel::Row row(ev.GetItem(), *_kvStore);
+    wxDataViewItemArray selectedItems;
+	_keyValueTreeView->GetSelections(selectedItems);
 
-	if (_showHelpColumnCheckbox->IsChecked())
-	{
-		updateHelpText(row);
-	}
+    if (selectedItems.Count() == 1)
+    {
+        wxDataViewItem selectedItem = selectedItems.front();
 
-	// Don't go further without a proper tree selection
-	if (!ev.GetItem().IsOk()) return;
+        wxutil::TreeModel::Row row(selectedItem, *_kvStore);
 
-    // Get the selected key and value in the tree view
-	std::string key = getSelectedKey();
-    std::string value = getListSelection(_columns.value);
+        if (_showHelpColumnCheckbox->IsChecked())
+        {
+            updateHelpText(row);
+        }
 
-    // Get the type for this key if it exists, and the options
-	PropertyParms parms = getPropertyParmsForKey(key);
+        // Don't go further without a proper tree selection
+        if (!selectedItem.IsOk()) return;
 
-    Entity* selectedEntity = Node_getEntity(_selectedEntity.lock());
+        // Get the selected key and value in the tree view
+        std::string key = getSelectedKey();
+        std::string value = getListSelection(_columns.value);
 
-    // If the type was not found, also try looking on the entity class
-    if (parms.type.empty())
-	{
-    	IEntityClassConstPtr eclass = selectedEntity->getEntityClass();
-		parms.type = eclass->getAttribute(key).getType();
-    }
+        // Get the type for this key if it exists, and the options
+        PropertyParms parms = getPropertyParmsForKey(key);
 
-    // Construct and add a new PropertyEditor
-    _currentPropertyEditor = PropertyEditorFactory::create(_editorFrame,
-		parms.type, selectedEntity, key, parms.options);
+        Entity* selectedEntity = Node_getEntity(_selectedEntity.lock());
 
-	if (_currentPropertyEditor)
-	{
-		// Don't use wxEXPAND to allow for horizontal centering, just add a 6 pixel border
-		// Using wxALIGN_CENTER_HORIZONTAL will position the property editor's panel in the middle
-		_editorFrame->GetSizer()->Add(_currentPropertyEditor->getWidget(), 1, wxALIGN_CENTER_HORIZONTAL | wxALL, 6);
-		_editorFrame->GetSizer()->Layout();
-	}
+        // If the type was not found, also try looking on the entity class
+        if (parms.type.empty())
+        {
+            IEntityClassConstPtr eclass = selectedEntity->getEntityClass();
+            parms.type = eclass->getAttribute(key).getType();
+        }
 
-    // Update key and value entry boxes, but only if there is a key value. If
-    // there is no selection we do not clear the boxes, to allow keyval copying
-    // between entities.
-	if (!key.empty())
+        // Construct and add a new PropertyEditor
+        _currentPropertyEditor = PropertyEditorFactory::create(_editorFrame,
+            parms.type, selectedEntity, key, parms.options);
+
+        if (_currentPropertyEditor)
+        {
+            // Don't use wxEXPAND to allow for horizontal centering, just add a 6 pixel border
+            // Using wxALIGN_CENTER_HORIZONTAL will position the property editor's panel in the middle
+            _editorFrame->GetSizer()->Add(_currentPropertyEditor->getWidget(), 1, wxALIGN_CENTER_HORIZONTAL | wxALL, 6);
+            _editorFrame->GetSizer()->Layout();
+        }
+
+        // Update key and value entry boxes, but only if there is a key value. If
+        // there is no selection we do not clear the boxes, to allow keyval copying
+        // between entities.
+        if (!key.empty())
+        {
+            _keyEntry->SetValue(key);
+            _valEntry->SetValue(value);
+        }
+    }
+	else if (selectedItems.Count() > 1)
 	{
-		_keyEntry->SetValue(key);
-		_valEntry->SetValue(value);
+		// When multiple items are selected, clear the property editor
+		_currentPropertyEditor.reset();
 	}
 }
 
@@ -986,6 +1111,17 @@ void EntityInspector::addClassAttribute(const EntityClassAttribute& a)
         row[_columns.name] = wxVariant(wxDataViewIconText(a.getName(), _emptyIcon)); 
         row[_columns.value] = a.getValue();
 
+		// Inherited values have an inactive checkbox, so assign a false value and disable
+		if (a.getType() == "bool")
+		{
+			row[_columns.booleanValue] = a.getValue() == "1";
+		}
+		else
+		{
+			row[_columns.booleanValue] = false;
+			row[_columns.booleanValue].setEnabled(false);
+		}
+
 		row[_columns.name] = grey;
         row[_columns.value] = grey;
 
diff --git a/radiant/ui/einspector/EntityInspector.h b/radiant/ui/einspector/EntityInspector.h
index 5d65818..afe667e 100644
--- a/radiant/ui/einspector/EntityInspector.h
+++ b/radiant/ui/einspector/EntityInspector.h
@@ -57,7 +57,8 @@ public:
 			value(add(wxutil::TreeModel::Column::String)),
 			isInherited(add(wxutil::TreeModel::Column::Boolean)),
 			helpIcon(add(wxutil::TreeModel::Column::Icon)),
-			hasHelpText(add(wxutil::TreeModel::Column::Boolean))
+			hasHelpText(add(wxutil::TreeModel::Column::Boolean)),
+			booleanValue(add(wxutil::TreeModel::Column::Boolean))
 		{}
 
 		wxutil::TreeModel::Column name;
@@ -65,6 +66,7 @@ public:
 		wxutil::TreeModel::Column isInherited;
 		wxutil::TreeModel::Column helpIcon;
 		wxutil::TreeModel::Column hasHelpText;
+		wxutil::TreeModel::Column booleanValue;
 	};
 
 private:
@@ -129,15 +131,9 @@ private:
 	IPropertyEditorPtr _currentPropertyEditor;
 
 	// The clipboard for spawnargs
-	struct ClipBoard
-	{
-		std::string key;
-		std::string value;
-
-		bool empty() const {
-			return key.empty();
-		}
-	} _clipBoard;
+	typedef std::pair<std::string, std::string> KeyValuePair;
+	typedef std::vector<KeyValuePair> ClipBoard;
+	ClipBoard _clipBoard;
 
 	// Data structure to store the type (vector3, text etc) and the options
 	// string for a single property.
@@ -174,6 +170,7 @@ private:
 	void _onCutKey();
 	void _onPasteKey();
 
+	bool _testAddKey();
 	bool _testDeleteKey();
 	bool _testCopyKey();
 	bool _testCutKey();
@@ -186,6 +183,7 @@ private:
 	void _onToggleShowHelpIcons(wxCommandEvent& ev);
 	void _onTreeViewSelectionChanged(wxDataViewEvent& ev);
 	void _onContextMenu(wxDataViewEvent& ev);
+	void _onDataViewItemChanged(wxDataViewEvent& ev);
 
 	void updateHelpText(const wxutil::TreeModel::Row& row);
     static std::string  cleanInputString( const std::string& );
@@ -237,6 +235,7 @@ public:
 	IPropertyEditorPtr getRegisteredPropertyEditor(const std::string& key);
 	void unregisterPropertyEditor(const std::string& key);
 
+	void onRadiantStartup();
 	void onRadiantShutdown();
 
 	// Gets called after an undo operation
diff --git a/radiant/ui/einspector/EntityPropertyEditor.cpp b/radiant/ui/einspector/EntityPropertyEditor.cpp
index 1aecef0..1b85f75 100644
--- a/radiant/ui/einspector/EntityPropertyEditor.cpp
+++ b/radiant/ui/einspector/EntityPropertyEditor.cpp
@@ -32,6 +32,11 @@ EntityPropertyEditor::EntityPropertyEditor(wxWindow* parent, Entity* entity, con
 		PropertyEditorFactory::getBitmapFor("entity"));
 }
 
+void EntityPropertyEditor::updateFromEntity()
+{
+	// nothing to do
+}
+
 void EntityPropertyEditor::onBrowseButtonClick()
 {
 	// Use a new dialog window to get a selection from the user
diff --git a/radiant/ui/einspector/EntityPropertyEditor.h b/radiant/ui/einspector/EntityPropertyEditor.h
index c62faea..eecd534 100644
--- a/radiant/ui/einspector/EntityPropertyEditor.h
+++ b/radiant/ui/einspector/EntityPropertyEditor.h
@@ -27,10 +27,12 @@ public:
     // PropertyEditorFactory
     EntityPropertyEditor();
 
+	void updateFromEntity() override;
+
     // Create a new EntityPropertyEditor
     virtual IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
     									const std::string& name,
-    									const std::string& options)
+    									const std::string& options) override
 	{
         return PropertyEditorPtr(new EntityPropertyEditor(parent, entity, name));
     }
diff --git a/radiant/ui/einspector/FloatPropertyEditor.cpp b/radiant/ui/einspector/FloatPropertyEditor.cpp
index 1078977..ca2eb90 100644
--- a/radiant/ui/einspector/FloatPropertyEditor.cpp
+++ b/radiant/ui/einspector/FloatPropertyEditor.cpp
@@ -19,7 +19,7 @@ namespace ui
 {
 
 FloatPropertyEditor::FloatPropertyEditor() :
-	_spinCtrl(NULL)
+	_spinCtrl(nullptr)
 {}
 
 // Main constructor
@@ -27,7 +27,7 @@ FloatPropertyEditor::FloatPropertyEditor(wxWindow* parent, Entity* entity,
 										 const std::string& key,
 										 const std::string& options)
 : PropertyEditor(entity),
-  _spinCtrl(NULL),
+  _spinCtrl(nullptr),
   _key(key)
 {
 	// Construct the main widget (will be managed by the base class)
@@ -65,14 +65,7 @@ FloatPropertyEditor::FloatPropertyEditor(wxWindow* parent, Entity* entity,
 	_spinCtrl->SetMinSize(wxSize(75, -1));
 
 	// Set the initial value if the entity has one
-	float value = 0;
-	try
-	{
-		value = boost::lexical_cast<float>(_entity->getKeyValue(_key));
-	}
-	catch (boost::bad_lexical_cast&) { }
-
-	_spinCtrl->SetValue(value);
+	updateFromEntity();
 
 	// Create and pack in the Apply button
 	wxButton* applyButton = new wxButton(mainVBox, wxID_APPLY, _("Apply..."));
@@ -82,6 +75,21 @@ FloatPropertyEditor::FloatPropertyEditor(wxWindow* parent, Entity* entity,
 	mainVBox->GetSizer()->Add(applyButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 6);
 }
 
+void FloatPropertyEditor::updateFromEntity()
+{
+	if (_spinCtrl == nullptr) return;
+
+	float value = 0;
+
+	try
+	{
+		value = boost::lexical_cast<float>(_entity->getKeyValue(_key));
+	}
+	catch (boost::bad_lexical_cast&) {}
+
+	_spinCtrl->SetValue(value);
+}
+
 void FloatPropertyEditor::_onApply(wxCommandEvent& ev)
 {
 	float value = static_cast<float>(_spinCtrl->GetValue());
diff --git a/radiant/ui/einspector/FloatPropertyEditor.h b/radiant/ui/einspector/FloatPropertyEditor.h
index 243621f..ffa94eb 100644
--- a/radiant/ui/einspector/FloatPropertyEditor.h
+++ b/radiant/ui/einspector/FloatPropertyEditor.h
@@ -38,12 +38,14 @@ public:
 	 */
 	FloatPropertyEditor(wxWindow* parent, Entity*, const std::string&, const std::string&);
 
+	void updateFromEntity() override;
+
 	/**
 	 * Virtual PropertyEditor clone method.
 	 */
 	IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
     							const std::string& name,
-    							const std::string& options)
+    							const std::string& options) override
 	{
 		return PropertyEditorPtr(
 			new FloatPropertyEditor(parent, entity, name, options)
diff --git a/radiant/ui/einspector/ModelPropertyEditor.h b/radiant/ui/einspector/ModelPropertyEditor.h
index 97d7756..ff8cf68 100644
--- a/radiant/ui/einspector/ModelPropertyEditor.h
+++ b/radiant/ui/einspector/ModelPropertyEditor.h
@@ -36,7 +36,7 @@ public:
 	// Clone method for virtual construction
 	IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
 								const std::string& name,
-								const std::string& options)
+								const std::string& options) override
 	{
 		return PropertyEditorPtr(
 			new ModelPropertyEditor(parent, entity, name, options)
diff --git a/radiant/ui/einspector/PropertyEditor.h b/radiant/ui/einspector/PropertyEditor.h
index 6575afa..f272c45 100644
--- a/radiant/ui/einspector/PropertyEditor.h
+++ b/radiant/ui/einspector/PropertyEditor.h
@@ -85,7 +85,12 @@ public:
 	virtual ~PropertyEditor();
 
 	// IPropertyEditor implementation
-	wxPanel* getWidget();
+	wxPanel* getWidget() override;
+
+	virtual void updateFromEntity()
+	{
+		// nothing by default, override in subclasses if needed
+	}
 };
 
 } // namespace
diff --git a/radiant/ui/einspector/SkinPropertyEditor.h b/radiant/ui/einspector/SkinPropertyEditor.h
index b8161f6..821901a 100644
--- a/radiant/ui/einspector/SkinPropertyEditor.h
+++ b/radiant/ui/einspector/SkinPropertyEditor.h
@@ -36,7 +36,7 @@ public:
 	// Clone method for virtual construction
 	IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
 								const std::string& name,
-								const std::string& options)
+								const std::string& options) override
 	{
 		return PropertyEditorPtr(new SkinPropertyEditor(parent, entity, name, options));
 	}
diff --git a/radiant/ui/einspector/SoundPropertyEditor.h b/radiant/ui/einspector/SoundPropertyEditor.h
index d1b53bc..fdf9e07 100644
--- a/radiant/ui/einspector/SoundPropertyEditor.h
+++ b/radiant/ui/einspector/SoundPropertyEditor.h
@@ -32,7 +32,7 @@ public:
 	// Clone method for virtual construction
 	IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
 								const std::string& name,
-								const std::string& options)
+								const std::string& options) override
 	{
 		return PropertyEditorPtr(
 			new SoundPropertyEditor(parent, entity, name, options)
diff --git a/radiant/ui/einspector/TexturePropertyEditor.h b/radiant/ui/einspector/TexturePropertyEditor.h
index 790ef89..9a4be74 100644
--- a/radiant/ui/einspector/TexturePropertyEditor.h
+++ b/radiant/ui/einspector/TexturePropertyEditor.h
@@ -38,7 +38,7 @@ public:
 	// Create a new TexturePropertyEditor
     virtual IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
     									const std::string& name,
-    									const std::string& options)
+    									const std::string& options) override
 	{
     	return PropertyEditorPtr(
     		new TexturePropertyEditor(parent, entity, name, options)
diff --git a/radiant/ui/einspector/Vector3PropertyEditor.cpp b/radiant/ui/einspector/Vector3PropertyEditor.cpp
index 1c1367d..3c9f8e5 100644
--- a/radiant/ui/einspector/Vector3PropertyEditor.cpp
+++ b/radiant/ui/einspector/Vector3PropertyEditor.cpp
@@ -65,7 +65,12 @@ Vector3PropertyEditor::Vector3PropertyEditor(wxWindow* parent, Entity* entity,
 	mainVBox->GetSizer()->Add(applyButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 6);
 
 	// Populate the spin boxes from the keyvalue
-	setWidgetsFromKey(_entity->getKeyValue(name));
+	updateFromEntity();
+}
+
+void Vector3PropertyEditor::updateFromEntity()
+{
+	setWidgetsFromKey(_entity->getKeyValue(_key));
 }
 
 void Vector3PropertyEditor::setWidgetsFromKey(const std::string& val)
diff --git a/radiant/ui/einspector/Vector3PropertyEditor.h b/radiant/ui/einspector/Vector3PropertyEditor.h
index 8d65837..69d2fbf 100644
--- a/radiant/ui/einspector/Vector3PropertyEditor.h
+++ b/radiant/ui/einspector/Vector3PropertyEditor.h
@@ -39,10 +39,12 @@ public:
 	// Construct a blank TextPropertyEditor for use in the PropertyEditorFactory
 	Vector3PropertyEditor();
 
+	void updateFromEntity() override;
+
 	// Create a new TextPropertyEditor
     virtual IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity,
     									const std::string& name,
-    									const std::string& options)
+    									const std::string& options) override
 	{
     	return PropertyEditorPtr(new Vector3PropertyEditor(parent, entity, name));
     }
diff --git a/radiant/ui/layers/LayerControl.cpp b/radiant/ui/layers/LayerControl.cpp
index ceb265e..db5a735 100644
--- a/radiant/ui/layers/LayerControl.cpp
+++ b/radiant/ui/layers/LayerControl.cpp
@@ -125,7 +125,7 @@ void LayerControl::onDelete(wxCommandEvent& ev)
 {
 	// Ask the about the deletion
 	std::string msg = _("Do you really want to delete this layer?");
-	msg += "\n<b>" + scene::getLayerSystem().getLayerName(_layerID) + "</b>";
+	msg += "\n" + scene::getLayerSystem().getLayerName(_layerID);
 
 	IDialogPtr box = GlobalDialogManager().createMessageBox(
 		_("Confirm Layer Deletion"), msg, IDialog::MESSAGE_ASK
diff --git a/radiant/ui/mainframe/EmbeddedLayout.cpp b/radiant/ui/mainframe/EmbeddedLayout.cpp
index 72e078f..9653009 100644
--- a/radiant/ui/mainframe/EmbeddedLayout.cpp
+++ b/radiant/ui/mainframe/EmbeddedLayout.cpp
@@ -77,6 +77,7 @@ void EmbeddedLayout::activate()
         page->page = textureBrowser;
         page->tabIcon = "icon_texture.png";
         page->tabLabel = _("Textures");
+		page->position = IGroupDialog::Page::Position::TextureBrowser;
 
         GlobalGroupDialog().addPage(page);
     }
diff --git a/radiant/ui/mainframe/FloatingLayout.cpp b/radiant/ui/mainframe/FloatingLayout.cpp
index 228b597..118dc1f 100644
--- a/radiant/ui/mainframe/FloatingLayout.cpp
+++ b/radiant/ui/mainframe/FloatingLayout.cpp
@@ -60,6 +60,7 @@ void FloatingLayout::activate()
 		page->page = textureBrowser;
 		page->tabIcon = "icon_texture.png";
 		page->tabLabel = _("Textures");
+		page->position = IGroupDialog::Page::Position::TextureBrowser;
 
 		GlobalGroupDialog().addPage(page);
 	}
diff --git a/radiant/ui/mainframe/MainFrame.cpp b/radiant/ui/mainframe/MainFrame.cpp
index 72e1ad1..3347637 100644
--- a/radiant/ui/mainframe/MainFrame.cpp
+++ b/radiant/ui/mainframe/MainFrame.cpp
@@ -2,35 +2,26 @@
 
 #include "i18n.h"
 #include "RadiantModule.h"
-#include "iuimanager.h"
-#include "idialogmanager.h"
 #include "igroupdialog.h"
 #include "ieventmanager.h"
+#include "iuimanager.h"
 #include "ipreferencesystem.h"
-#include "igrid.h"
 #include "ientityinspector.h"
 #include "iorthoview.h"
+#include "iregistry.h"
 
-#include "ui/splash/Splash.h"
-#include "ui/menu/FiltersMenu.h"
 #include "log/Console.h"
 #include "xyview/GlobalXYWnd.h"
 #include "ui/mediabrowser/MediaBrowser.h"
-#include "ui/texturebrowser/TextureBrowser.h"
-#include "ui/layers/LayerControlDialog.h"
-#include "ui/overlay/Overlay.h"
 #include "camera/GlobalCamera.h"
-#include "camera/CameraSettings.h"
-#include "selection/shaderclipboard/ShaderClipboard.h"
 
 #include "registry/registry.h"
-#include "map/AutoSaver.h"
-#include "brush/BrushModule.h"
 #include "wxutil/MultiMonitor.h"
 
 #include "ui/mainframe/ScreenUpdateBlocker.h"
 #include "ui/mainframe/EmbeddedLayout.h"
 #include "ui/mainframe/TopLevelFrame.h"
+#include "map/Map.h"
 
 #include "modulesystem/StaticModule.h"
 #include <functional>
@@ -57,7 +48,7 @@ namespace ui
 
 MainFrame::MainFrame() :
 	_topLevelWindow(NULL),
-	_screenUpdatesEnabled(true)
+	_screenUpdatesEnabled(false) // not enabled until constructed
 {}
 
 // RegisterableModule implementation
@@ -78,8 +69,8 @@ const StringSet& MainFrame::getDependencies() const
 		_dependencies.insert(MODULE_PREFERENCESYSTEM);
 		_dependencies.insert(MODULE_EVENTMANAGER);
 		_dependencies.insert(MODULE_COMMANDSYSTEM);
-		_dependencies.insert(MODULE_UIMANAGER);
 		_dependencies.insert(MODULE_ORTHOVIEWMANAGER);
+		_dependencies.insert(MODULE_CAMERA);
 	}
 
 	return _dependencies;
@@ -90,7 +81,7 @@ void MainFrame::initialiseModule(const ApplicationContext& ctx)
 	rMessage() << "MainFrame::initialiseModule called." << std::endl;
 
 	// Add another page for Multi-Monitor stuff
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Multi Monitor"));
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Multi Monitor"));
 
 	// Initialise the registry, if no key is set
 	if (GlobalRegistry().get(RKEY_MULTIMON_START_MONITOR).empty())
@@ -109,7 +100,7 @@ void MainFrame::initialiseModule(const ApplicationContext& ctx)
 		);
 	}
 
-	page->appendCombo(_("Start DarkRadiant on monitor"), RKEY_MULTIMON_START_MONITOR, list);
+	page.appendCombo(_("Start DarkRadiant on monitor"), RKEY_MULTIMON_START_MONITOR, list);
 
 	// Add the toggle max/min command for floating windows
 	GlobalCommandSystem().addCommand("ToggleFullScreenCamera",
@@ -128,9 +119,9 @@ void MainFrame::initialiseModule(const ApplicationContext& ctx)
 		if (dwmEnableComposition)
 		{
 			// Add a page for Desktop Composition stuff
-			PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Compatibility"));
+			IPreferencePage& compatPage = GlobalPreferenceSystem().getPage(_("Settings/Compatibility"));
 
-			page->appendCheckBox("", _("Disable Windows Desktop Composition"),
+			compatPage.appendCheckBox(_("Disable Windows Desktop Composition"),
 				RKEY_DISABLE_WIN_DESKTOP_COMP);
 
 			GlobalRegistry().signalForKey(RKEY_DISABLE_WIN_DESKTOP_COMP).connect(
@@ -149,6 +140,8 @@ void MainFrame::initialiseModule(const ApplicationContext& ctx)
 void MainFrame::shutdownModule()
 {
 	rMessage() << "MainFrame::shutdownModule called." << std::endl;
+
+	disableScreenUpdates();
 }
 
 void MainFrame::keyChanged()
@@ -226,6 +219,8 @@ void MainFrame::construct()
 	// register the commands
 	GlobalMainFrameLayoutManager().registerCommands();
 
+	enableScreenUpdates();
+
     updateAllWindows();
 }
 
@@ -253,12 +248,6 @@ void MainFrame::preDestructionCleanup()
 
 	// Broadcast shutdown event to RadiantListeners
 	radiant::getGlobalRadiant()->broadcastShutdownEvent();
-
-	// Destroy the Overlay instance
-	Overlay::destroyInstance();
-
-	// Stop the AutoSaver class from being called
-	map::AutoSaver().stopTimer();
 }
 
 void MainFrame::onTopLevelFrameClose(wxCloseEvent& ev)
@@ -373,17 +362,6 @@ void MainFrame::create()
 	/* Construct the Group Dialog. This is the tabbed window that contains
      * a number of pages - usually Entities, Textures and possibly Console.
      */
-    // Add entity inspector widget
-	IGroupDialog::PagePtr entityInspectorPage(new IGroupDialog::Page);
-
-	entityInspectorPage->name = "entity";
-	entityInspectorPage->windowLabel = _("Entity");
-	entityInspectorPage->page = GlobalEntityInspector().getWidget();
-	entityInspectorPage->tabIcon = "cmenu_add_entity.png";
-	entityInspectorPage->tabLabel = _("Entity");
-
-	GlobalGroupDialog().addPage(entityInspectorPage);
-
 	// Add the Media Browser page
 	IGroupDialog::PagePtr mediaBrowserPage(new IGroupDialog::Page);
 
@@ -392,6 +370,7 @@ void MainFrame::create()
 	mediaBrowserPage->page = MediaBrowser::getInstance().getWidget();
 	mediaBrowserPage->tabIcon = "folder16.png";
 	mediaBrowserPage->tabLabel = _("Media");
+	mediaBrowserPage->position = IGroupDialog::Page::Position::MediaBrowser;
 
 	GlobalGroupDialog().addPage(mediaBrowserPage);
 
@@ -403,21 +382,12 @@ void MainFrame::create()
 	consolePage->page = new Console(getWxTopLevelWindow());
 	consolePage->tabIcon = "iconConsole16.png";
 	consolePage->tabLabel = _("Console");
+	consolePage->position = IGroupDialog::Page::Position::Console;
 
 	GlobalGroupDialog().addPage(consolePage);
 
 	// Load the previous window settings from the registry
 	restoreWindowPosition();
-
-	_topLevelWindow->Show();
-
-	// Start the autosave timer so that it can periodically check the map for changes
-	map::AutoSaver().startTimer();
-
-	// Initialise the shaderclipboard
-	GlobalShaderClipboard().clear();
-
-	LayerControlDialog::init();
 }
 
 void MainFrame::saveWindowPosition()
@@ -449,6 +419,8 @@ void MainFrame::disableScreenUpdates() {
 
 void MainFrame::updateAllWindows(bool force)
 {
+	if (!_screenUpdatesEnabled) return;
+
     if (force)
     {
         GlobalCamera().forceDraw();
diff --git a/radiant/ui/mainframe/MainFrame.h b/radiant/ui/mainframe/MainFrame.h
index 2d2bcb8..adcf0c1 100644
--- a/radiant/ui/mainframe/MainFrame.h
+++ b/radiant/ui/mainframe/MainFrame.h
@@ -3,7 +3,6 @@
 #include <map>
 #include "icommandsystem.h"
 #include "imainframe.h"
-#include "iregistry.h"
 #include "imainframelayout.h"
 #include "wxutil/WindowPosition.h"
 
diff --git a/radiant/ui/mainframe/ScreenUpdateBlocker.cpp b/radiant/ui/mainframe/ScreenUpdateBlocker.cpp
index 1416d46..0b0ea89 100644
--- a/radiant/ui/mainframe/ScreenUpdateBlocker.cpp
+++ b/radiant/ui/mainframe/ScreenUpdateBlocker.cpp
@@ -1,7 +1,6 @@
 #include "ScreenUpdateBlocker.h"
 
 #include "iradiant.h"
-#include "map/AutoSaver.h"
 #include "imainframe.h"
 
 #include <wx/app.h>
@@ -42,10 +41,7 @@ ScreenUpdateBlocker::ScreenUpdateBlocker(const std::string& title, const std::st
 	Fit();
 	CenterOnParent();
 
-	// Stop the autosaver
-	map::AutoSaver().stopTimer();
-
-	// Set the "screen updates disabled" flag
+	// Set the "screen updates disabled" flag (also disables autosaver)
 	GlobalMainFrame().disableScreenUpdates();
 
 	// Connect the realize signal to remove the window decorations
@@ -90,9 +86,6 @@ ScreenUpdateBlocker::~ScreenUpdateBlocker()
 
     // Re-draw the scene to clear any artefacts in the buffer
     GlobalMainFrame().updateAllWindows();
-
-	// Start the autosaver again
-	map::AutoSaver().startTimer();
 }
 
 void ScreenUpdateBlocker::pulse()
diff --git a/radiant/ui/mainframe/SplitPaneLayout.cpp b/radiant/ui/mainframe/SplitPaneLayout.cpp
index a71da2b..e0d877c 100644
--- a/radiant/ui/mainframe/SplitPaneLayout.cpp
+++ b/radiant/ui/mainframe/SplitPaneLayout.cpp
@@ -112,6 +112,7 @@ void SplitPaneLayout::constructLayout()
 		page->page = textureBrowser;
 		page->tabIcon = "icon_texture.png";
 		page->tabLabel = _("Textures");
+		page->position = IGroupDialog::Page::Position::TextureBrowser;
 
 		GlobalGroupDialog().addPage(page);
 	}
diff --git a/radiant/ui/mediabrowser/MediaBrowser.cpp b/radiant/ui/mediabrowser/MediaBrowser.cpp
index cfe9de0..497c3a3 100644
--- a/radiant/ui/mediabrowser/MediaBrowser.cpp
+++ b/radiant/ui/mediabrowser/MediaBrowser.cpp
@@ -695,8 +695,8 @@ void MediaBrowser::toggle(const cmd::ArgumentList& args)
 void MediaBrowser::registerCommandsAndPreferences()
 {
 	// Add a page to the given group
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Media Browser"));
-	page->appendCheckBox("", _("Load media tree at startup"), RKEY_MEDIA_BROWSER_PRELOAD);
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Media Browser"));
+	page.appendCheckBox(_("Load media tree at startup"), RKEY_MEDIA_BROWSER_PRELOAD);
 
 	GlobalCommandSystem().addCommand("ToggleMediaBrowser", toggle);
 	GlobalEventManager().addCommand("ToggleMediaBrowser", "ToggleMediaBrowser");
diff --git a/radiant/ui/mru/MRU.cpp b/radiant/ui/mru/MRU.cpp
index ff547a4..478e54b 100644
--- a/radiant/ui/mru/MRU.cpp
+++ b/radiant/ui/mru/MRU.cpp
@@ -122,10 +122,10 @@ void MRU::keyChanged()
 // Construct the MRU preference page and add it to the given group
 void MRU::constructPreferences()
 {
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Map Files"));
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Map Files"));
 
-	page->appendEntry(_("Number of most recently used files"), RKEY_MRU_LENGTH);
-	page->appendCheckBox("", _("Open last map on startup"), RKEY_LOAD_LAST_MAP);
+	page.appendEntry(_("Number of most recently used files"), RKEY_MRU_LENGTH);
+	page.appendCheckBox(_("Open last map on startup"), RKEY_LOAD_LAST_MAP);
 }
 
 void MRU::initialise()
diff --git a/radiant/ui/ortho/OrthoContextMenu.cpp b/radiant/ui/ortho/OrthoContextMenu.cpp
index 0e0ea2a..7db828f 100644
--- a/radiant/ui/ortho/OrthoContextMenu.cpp
+++ b/radiant/ui/ortho/OrthoContextMenu.cpp
@@ -146,7 +146,7 @@ void OrthoContextMenu::analyseSelection()
         GlobalSelectionSystem().foreachSelected(walker);
 
         _selectionInfo.onlyGroupsSelected = walker.onlyGroupsAreSelected();
-        _selectionInfo.singleGroupSelected = walker.selectedGroupCount() == 1 && !node_is_worldspawn(walker.getFirstSelectedGroupNode());
+        _selectionInfo.singleGroupSelected = walker.selectedGroupCount() == 1 && !Node_isWorldspawn(walker.getFirstSelectedGroupNode());
 
         // Create a ModelFinder and check whether only models were selected
         selection::algorithm::ModelFinder visitor;
@@ -234,7 +234,7 @@ bool OrthoContextMenu::checkRevertToWorldspawnPartial()
         {
             scene::INodePtr parent = node->getParent();
 
-            return parent != NULL && scene::hasChildPrimitives(parent) && !node_is_worldspawn(parent);
+            return parent != NULL && scene::hasChildPrimitives(parent) && !Node_isWorldspawn(parent);
         }
     }
 
@@ -583,11 +583,12 @@ void OrthoContextMenu::constructMenu()
 
     addSectionItems(SECTION_CREATE, true); // no spacer for first category
     addSectionItems(SECTION_ACTION);
+	addSectionItems(SECTION_SELECTION_GROUPS);
     addSectionItems(SECTION_LAYER);
 
     // Add the rest of the sections
     for (MenuSections::const_iterator sec = _sections.lower_bound(SECTION_LAYER+1);
-         sec != _sections.end(); )
+         sec != _sections.end(); ++sec)
     {
         addSectionItems(sec->first);
     }
diff --git a/radiant/ui/overlay/Overlay.cpp b/radiant/ui/overlay/Overlay.cpp
index fd95e73..7a2f36b 100644
--- a/radiant/ui/overlay/Overlay.cpp
+++ b/radiant/ui/overlay/Overlay.cpp
@@ -52,6 +52,7 @@ void Overlay::observeKey(const std::string& key)
 void Overlay::onRadiantShutdown()
 {
 	_texture = TexturePtr();
+	destroyInstance();
 }
 
 void Overlay::destroyInstance() {
diff --git a/radiant/ui/patch/PatchInspector.cpp b/radiant/ui/patch/PatchInspector.cpp
index 28b4fdc..8c170b7 100644
--- a/radiant/ui/patch/PatchInspector.cpp
+++ b/radiant/ui/patch/PatchInspector.cpp
@@ -365,7 +365,7 @@ void PatchInspector::rescanSelection()
 			if (tess.x() == UINT_MAX)
             {
 				// Not initialised yet, take these values for starters
-				tessIsFixed = p.subdivionsFixed();
+				tessIsFixed = p.subdivisionsFixed();
 				tess = p.getSubdivisions();
 			}
 			else
@@ -373,7 +373,7 @@ void PatchInspector::rescanSelection()
 				// We already have a pair of divisions, compare
 				Subdivisions otherTess = p.getSubdivisions();
 
-				if (tessIsFixed != p.subdivionsFixed() || otherTess != tess)
+				if (tessIsFixed != p.subdivisionsFixed() || otherTess != tess)
 				{
 					// Our journey ends here, we cannot find a pair of tesselations
 					// for all selected patches or the same fixed/variable status
diff --git a/radiant/ui/prefabselector/PrefabSelector.cpp b/radiant/ui/prefabselector/PrefabSelector.cpp
index 39003b6..9df19c1 100644
--- a/radiant/ui/prefabselector/PrefabSelector.cpp
+++ b/radiant/ui/prefabselector/PrefabSelector.cpp
@@ -14,6 +14,7 @@
 #include <sstream>
 #include "string/convert.h"
 #include "registry/registry.h"
+#include "registry/Widgets.h"
 #include "os/path.h"
 
 #include <wx/button.h>
@@ -47,6 +48,7 @@ namespace
 
     const std::string RKEY_LAST_CUSTOM_PREFAB_PATH = RKEY_BASE + "lastPrefabPath";
     const std::string RKEY_RECENT_PREFAB_PATHS = RKEY_BASE + "recentPaths";
+	const std::string RKEY_INSERT_AS_GROUP = RKEY_BASE + "insertAsGroup";
 }
 
 // Constructor.
@@ -62,7 +64,8 @@ PrefabSelector::PrefabSelector() :
     _useCustomPath(nullptr),
     _useRecentPath(nullptr),
     _recentPathSelector(nullptr),
-    _customPath(nullptr)
+    _customPath(nullptr),
+	_insertAsGroupBox(nullptr)
 {
 	SetSizer(new wxBoxSizer(wxVERTICAL));
 
@@ -98,7 +101,16 @@ PrefabSelector::PrefabSelector() :
 	reloadButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(PrefabSelector::onRescanPrefabs), NULL, this);
 
 	buttonSizer->Prepend(reloadButton, 0, wxRIGHT, 32);
-	vbox->Add(buttonSizer, 0, wxALIGN_RIGHT | wxTOP, 12);
+
+	_insertAsGroupBox = new wxCheckBox(this, wxID_ANY, _("Create Group out of Prefab parts"));
+	registry::bindWidget(_insertAsGroupBox, RKEY_INSERT_AS_GROUP);
+
+	wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
+
+	hbox->Add(_insertAsGroupBox, 1, wxALL, 6);
+	hbox->Add(buttonSizer, 0);
+
+	vbox->Add(hbox, 0, wxEXPAND | wxTOP, 12);
 
 	// Set the default size of the window
 	_position.connect(this);
@@ -297,7 +309,7 @@ void PrefabSelector::onRadiantShutdown()
 	InstancePtr().reset();
 }
 
-std::string PrefabSelector::ChoosePrefab(const std::string& curPrefab)
+PrefabSelector::Result PrefabSelector::ChoosePrefab(const std::string& curPrefab)
 {
 	// Use the parameter only if it's not empty
 	if (!curPrefab.empty())
@@ -305,11 +317,15 @@ std::string PrefabSelector::ChoosePrefab(const std::string& curPrefab)
 		Instance()._lastPrefab = curPrefab;
 	}
 
-	std::string returnValue = "";
+	Result returnValue;
+
+	returnValue.insertAsGroup = false;
+	returnValue.prefabPath = "";
 
 	if (Instance().ShowModal() == wxID_OK)
 	{
-		returnValue = Instance().getSelectedValue(Instance()._columns.vfspath);
+		returnValue.prefabPath = Instance().getSelectedValue(Instance()._columns.vfspath);
+		returnValue.insertAsGroup = Instance().getInsertAsGroup();
 	}
 
 	Instance().Hide();
@@ -432,6 +448,11 @@ std::string PrefabSelector::getSelectedValue(const wxutil::TreeModel::Column& co
 	return row[col];
 }
 
+bool PrefabSelector::getInsertAsGroup()
+{
+	return _insertAsGroupBox->GetValue();
+}
+
 void PrefabSelector::clearPreview()
 {
     // NULLify the preview map root on failure
@@ -461,9 +482,9 @@ void PrefabSelector::handleSelectionChange()
 
     std::string prefabPath = row[_columns.vfspath];
 
-	_mapResource = GlobalMapResourceManager().capture(prefabPath);
+	_mapResource = GlobalMapResourceManager().loadFromPath(prefabPath);
 
-	if (_mapResource == NULL)
+	if (!_mapResource)
 	{
         clearPreview();
 		return;
@@ -475,14 +496,14 @@ void PrefabSelector::handleSelectionChange()
 	// getting stuck in the "drag filename" operation
 	registry::ScopedKeyChanger<bool> changer(
 		RKEY_MAP_SUPPRESS_LOAD_STATUS_DIALOG, true
-		);
+	);
 
 	if (_mapResource->load())
 	{
 		// Get the node from the resource
         scene::IMapRootNodePtr root = _mapResource->getNode();
 
-		assert(root != NULL);
+		assert(root);
 
 		// Set the new rootnode
 		_preview->setRootNode(root);
diff --git a/radiant/ui/prefabselector/PrefabSelector.h b/radiant/ui/prefabselector/PrefabSelector.h
index 6b5da16..ed2c829 100644
--- a/radiant/ui/prefabselector/PrefabSelector.h
+++ b/radiant/ui/prefabselector/PrefabSelector.h
@@ -15,6 +15,7 @@
 #include <string>
 #include <wx/combobox.h>
 
+class wxCheckBox;
 class wxSizer;
 class wxRadioButton;
 
@@ -46,6 +47,12 @@ public:
 		wxutil::TreeModel::Column isFolder;	// whether this is a folder
 	};
 
+	struct Result
+	{
+		std::string prefabPath; // VFS path of the prefab
+		bool insertAsGroup; // whether to insert the prefab as group
+	};
+
 private:
 	wxPanel* _dialogPanel;
 
@@ -80,6 +87,7 @@ private:
     wxRadioButton* _useRecentPath;
     wxComboBox* _recentPathSelector;
     wxutil::PathEntry* _customPath;
+	wxCheckBox* _insertAsGroupBox;
 
     std::list<std::string> _recentPaths;
 
@@ -108,6 +116,7 @@ private:
 
 	// Return the value from the selected column, or an empty string if nothing selected
 	std::string getSelectedValue(const wxutil::TreeModel::Column& col);
+	bool getInsertAsGroup();
 
     void handleSelectionChange();
 	void updateUsageInfo();
@@ -126,10 +135,10 @@ public:
 	* return the VFS path of the prefab selected by the user.
 	*
 	* @curPrefab: the name of the currently selected prefab the tree will browse to
-	*            Leave this empty to leave the treeview focus where it was when
-	*            the dialog was closed.
+	* Leave this empty to leave the treeview focus where it was when
+	* the dialog was closed.
 	*/
-	static std::string ChoosePrefab(const std::string& curPrefab = "");
+	static Result ChoosePrefab(const std::string& curPrefab = "");
 
 	void onRadiantShutdown();
 };
diff --git a/radiant/ui/prefdialog/PrefDialog.cpp b/radiant/ui/prefdialog/PrefDialog.cpp
index 2c64a62..61fae1b 100644
--- a/radiant/ui/prefdialog/PrefDialog.cpp
+++ b/radiant/ui/prefdialog/PrefDialog.cpp
@@ -9,110 +9,99 @@
 
 #include <wx/sizer.h>
 #include <wx/treebook.h>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/join.hpp>
 
-namespace ui
-{
+#include "settings/PreferenceSystem.h"
+#include "settings/PreferencePage.h"
 
-PrefDialog::PrefDialog() :
-	_dialog(NULL),
-	_notebook(NULL)
+namespace ui
 {
-	// There is no main application window by the time this constructor is called.
-	createDialog(NULL);
-
-	// Create the root element with the Notebook and Connector references
-	_root = PrefPagePtr(new PrefPage("", _notebook));
-
-	// Register this instance with GlobalRadiant() at once
-	GlobalRadiant().signal_radiantShutdown().connect(
-        sigc::mem_fun(*this, &PrefDialog::onRadiantShutdown)
-    );
-	GlobalRadiant().signal_radiantStarted().connect(
-        sigc::mem_fun(*this, &PrefDialog::onRadiantStartup)
-    );
-}
 
-void PrefDialog::createDialog(wxWindow* parent)
+PrefDialog::PrefDialog(wxWindow* parent) :
+	DialogBase(_("DarkRadiant Preferences"), parent),
+	_notebook(nullptr)
 {
-	wxutil::DialogBase* newDialog = new wxutil::DialogBase(_("DarkRadiant Preferences"), parent);
-	newDialog->SetSizer(new wxBoxSizer(wxVERTICAL));
-    newDialog->SetMinClientSize(wxSize(640, -1));
+	SetSizer(new wxBoxSizer(wxVERTICAL));
+	SetMinClientSize(wxSize(640, -1));
 
 	// 12-pixel spacer
-	wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL);
-	newDialog->GetSizer()->Add(vbox, 1, wxEXPAND | wxALL, 12);
+	wxBoxSizer* mainVbox = new wxBoxSizer(wxVERTICAL);
+	GetSizer()->Add(mainVbox, 1, wxEXPAND | wxALL, 12);
 
-	if (_notebook != NULL)
-	{
-		_notebook->Reparent(newDialog);
-	}
-	else
-	{
-		_notebook = new wxTreebook(newDialog, wxID_ANY);
-	}
-
-	vbox->Add(_notebook, 1, wxEXPAND);
-	vbox->Add(newDialog->CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT);
-
-    // Prevent the tree control from shrinking too much
-    _notebook->GetTreeCtrl()->SetMinClientSize(wxSize(200, -1));
-
-	// Destroy the old dialog window and use the new one
-	if (_dialog != NULL)
-	{
-		_dialog->Destroy();
-		_dialog = NULL;
-	}
+	mainVbox->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT);
+	
+	_notebook = new wxTreebook(this, wxID_ANY);
+	
+	// Prevent the tree control from shrinking too much
+	_notebook->GetTreeCtrl()->SetMinClientSize(wxSize(200, -1));
 
-	_dialog = newDialog;
-}
+	mainVbox->Prepend(_notebook, 1, wxEXPAND);
 
-PrefPagePtr PrefDialog::createOrFindPage(const std::string& path)
-{
-	// Pass the call to the root page
-	return _root->createOrFindPage(path);
+	// Create the page widgets
+	createPages();
 }
 
-void PrefDialog::onRadiantStartup()
+void PrefDialog::createPages()
 {
-	// greebo: Unfortunate step needed at least in Windows. Creating a modal
-	// dialog with a NULL parent (like we are doing) results in the dialog
-	// using the currently active top level window as parent, which in our case
-	// is the Splash window. The splash window is destroyed and this
-	// dialog will be affected. So recreate the dialog window and reparent the
-	// notebook to the new instance before the Splash screen goes the way of the dodo.
-	createDialog(GlobalMainFrame().getWxTopLevelWindow());
-}
+	// Now create all pages
+	GetPreferenceSystem().foreachPage([&](settings::PreferencePage& page)
+	{
+		// Create a page responsible for this settings::PreferencePage
+		PrefPage* pageWidget = new PrefPage(_notebook, page);
 
-void PrefDialog::onRadiantShutdown()
-{
-	rMessage() << "PrefDialog shutting down." << std::endl;
+		// Remember this page in our mapping
+		const std::string& pagePath = page.getPath();
 
-	if (_dialog->IsShownOnScreen())
-	{
-		_dialog->Hide();
-	}
+		_pages[pagePath] = pageWidget;
 
-	// Destroy the window and let it go
-	_dialog->Destroy();
-	_dialog = NULL;
-	_notebook = NULL;
+		std::vector<std::string> parts;
+		boost::algorithm::split(parts, pagePath, boost::algorithm::is_any_of("/"));
 
-	InstancePtr().reset();
+		if (parts.size() > 1)
+		{
+			parts.pop_back();
+			std::string parentPath = boost::algorithm::join(parts, "/");
+			
+			PageMap::const_iterator parent = _pages.find(parentPath);
+
+			if (parent != _pages.end())
+			{
+				// Find the index of the parent page to perform the insert
+				int pos = _notebook->FindPage(parent->second);
+				_notebook->InsertSubPage(pos, pageWidget, page.getName());
+			}
+			else
+			{
+				rError() << "Cannot insert page, unable to find parent path: " << parentPath << std::endl;
+			}
+		}
+		else
+		{
+			// Top-level page
+			// Append the panel as new page to the notebook
+			_notebook->AddPage(pageWidget, page.getName());
+		}
+	});
 }
 
-void PrefDialog::doShowModal(const std::string& requestedPage)
+void PrefDialog::showModal(const std::string& requestedPage)
 {
-	// Trigger a resize of the treebook's TreeCtrl
-	_notebook->ExpandNode(0, true); 
-
-	_dialog->FitToScreen(0.5f, 0.5f);
+	// Reset all values to the ones found in the registry
+	for (const PageMap::value_type& p : _pages)
+	{
+		p.second->resetValues();
+	}
 
-	// Discard any changes we got earlier
-	_root->foreachPage([&] (PrefPage& page)
+	// Trigger a resize of the treebook's TreeCtrl, do this by expanding all nodes 
+	// (one would be enough, but we want to show the whole tree anyway)
+	for (std::size_t page = 0; page < _notebook->GetPageCount(); ++page)
 	{
-		page.discardChanges();
-	});
+		_notebook->ExpandNode(page, true);
+	}
+
+	FitToScreen(0.5f, 0.5f);
 
 	// Is there a specific page display request?
 	if (!requestedPage.empty())
@@ -120,13 +109,13 @@ void PrefDialog::doShowModal(const std::string& requestedPage)
 		showPage(requestedPage);
 	}
 
-	if (_dialog->ShowModal() == wxID_OK)
+	if (ShowModal() == wxID_OK)
 	{
 		// Tell all pages to flush their buffer
-		_root->foreachPage([&] (PrefPage& page) 
+		for (const PageMap::value_type& p : _pages)
 		{ 
-			page.saveChanges(); 
-		});
+			p.second->saveChanges(); 
+		}
 
 		// greebo: Check if the mainframe module is already "existing". It might be
 		// uninitialised if this dialog is shown during DarkRadiant startup
@@ -135,67 +124,49 @@ void PrefDialog::doShowModal(const std::string& requestedPage)
 			GlobalMainFrame().updateAllWindows();
 		}
 	}
-	else
-	{
-		// Discard all changes
-		_root->foreachPage([&] (PrefPage& page)
-		{ 
-			page.discardChanges(); 
-		});
-	}
-}
-
-void PrefDialog::ShowDialog(const cmd::ArgumentList& args)
-{
-	Instance().ShowModal();
 }
 
-PrefDialogPtr& PrefDialog::InstancePtr()
+void PrefDialog::ShowPrefDialog(const cmd::ArgumentList& args)
 {
-	static PrefDialogPtr _instancePtr;
-	return _instancePtr;
+	ShowDialog();
 }
 
-PrefDialog& PrefDialog::Instance()
+void PrefDialog::showPage(const std::string& path)
 {
-	PrefDialogPtr& instancePtr = InstancePtr();
-
-	if (instancePtr == NULL)
+	if (!_notebook)
 	{
-		// Not yet instantiated, do it now
-		instancePtr.reset(new PrefDialog);
+		rError() << "Can't show requested page, as the wxNotebook is null" << std::endl;
+		return;
 	}
 
-	return *instancePtr;
-}
+	PageMap::const_iterator found = _pages.find(path);
 
-void PrefDialog::showPage(const std::string& path)
-{
-	_root->foreachPage([&] (PrefPage& page)
+	if (found != _pages.end())
 	{
-		// Check for a match
-		if (page.getPath() == path)
-		{
-			// Find the page number
-			int pagenum = _notebook->FindPage(page.getWidget());
+		// Find the page number
+		int pagenum = _notebook->FindPage(found->second);
 
-			// make it active
-			_notebook->SetSelection(pagenum);
-		}
-	});
+		// make it active
+		_notebook->SetSelection(pagenum);
+	}
 }
 
-void PrefDialog::ShowModal(const std::string& path)
+void PrefDialog::ShowDialog(const std::string& path)
 {
-	if (!Instance()._dialog->IsShownOnScreen())
-	{
-		Instance().doShowModal(path);
-	}
+	// greebo: Check if the mainframe module is already "existing". It might be
+	// uninitialised if this dialog is shown during DarkRadiant startup
+	wxWindow* parent = module::GlobalModuleRegistry().moduleExists(MODULE_MAINFRAME) ?
+		GlobalMainFrame().getWxTopLevelWindow() : nullptr;
+
+	PrefDialog* dialog = new PrefDialog(parent);
+
+	dialog->showModal(path);
+	dialog->Destroy();
 }
 
 void PrefDialog::ShowProjectSettings(const cmd::ArgumentList& args)
 {
-	ShowModal(_("Game"));
+	ShowDialog(_("Game"));
 }
 
 } // namespace ui
diff --git a/radiant/ui/prefdialog/PrefDialog.h b/radiant/ui/prefdialog/PrefDialog.h
index c1e6acb..7848ce2 100644
--- a/radiant/ui/prefdialog/PrefDialog.h
+++ b/radiant/ui/prefdialog/PrefDialog.h
@@ -1,63 +1,44 @@
 #pragma once
 
+#include <map>
 #include "iradiant.h"
 #include "icommandsystem.h"
 
 #include "PrefPage.h"
+#include "wxutil/dialog/DialogBase.h"
 
 class wxTreebook;
 
-namespace wxutil { class DialogBase; }
-
 namespace ui
 {
 
-class PrefDialog;
-typedef std::shared_ptr<PrefDialog> PrefDialogPtr;
-
-class PrefDialog
+class PrefDialog :
+	public wxutil::DialogBase
 {
 private:
-	// The actual dialog instance
-	wxutil::DialogBase* _dialog;
-
 	wxTreebook* _notebook;
 
-	// The root page
-	PrefPagePtr _root;
+	// Each notebook page is created and maintained by a PrefPage class
+	// Map the page path wo its widget
+	typedef std::map<std::string, PrefPage*> PageMap;
+	PageMap _pages;
 
-public:
-	PrefDialog();
-
-	// Retrieve a reference to the static instance of this dialog
-	static PrefDialog& Instance();
+	PrefDialog(wxWindow* parent);
 
+public:
 	/** greebo: Runs the modal dialog
 	 */
-	static void ShowDialog(const cmd::ArgumentList& args);
+	static void ShowPrefDialog(const cmd::ArgumentList& args);
 
 	/** greebo: Makes sure that the dialog is visible.
 	 * 			(does nothing if the dialog is already on screen)
 	 */
-	static void ShowModal(const std::string& path = "");
+	static void ShowDialog(const std::string& path = "");
 
 	/** greebo: The command target to show the Game settings preferences.
 	 */
 	static void ShowProjectSettings(const cmd::ArgumentList& args);
 
-	/** greebo: Looks up the page for the path and creates it
-	 * 			if necessary.
-	 */
-	PrefPagePtr createOrFindPage(const std::string& path);
-
-	// Reparent the preference dialog on startup
-	void onRadiantStartup();
-
-	/** greebo: A safe shutdown request that saves the window information
-	 * 			to the registry.
-	 */
-	void onRadiantShutdown();
-	
 	/** greebo: Displays the page with the specified path.
 	 *
 	 * @path: a string like "Settings/Patches"
@@ -65,12 +46,9 @@ public:
 	void showPage(const std::string& path);
 
 private:
-	void doShowModal(const std::string& requestedPage);
-
-	// This is where the static shared_ptr of the singleton instance is held.
-	static PrefDialogPtr& InstancePtr();
+	void showModal(const std::string& requestedPage);
 
-	void createDialog(wxWindow* parent);
+	void createPages();
 };
 
 } // namespace ui
diff --git a/radiant/ui/prefdialog/PrefPage.cpp b/radiant/ui/prefdialog/PrefPage.cpp
index 80802ab..4422d38 100644
--- a/radiant/ui/prefdialog/PrefPage.cpp
+++ b/radiant/ui/prefdialog/PrefPage.cpp
@@ -1,106 +1,40 @@
 #include "PrefPage.h"
 
 #include "i18n.h"
-#include "itextstream.h"
-#include "registry/buffer.h"
-#include "registry/registry.h"
-#include "registry/Widgets.h"
 
-#include "wxutil/PathEntry.h"
-
-#include <iostream>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/format.hpp>
-#include "modulesystem/ApplicationContextImpl.h"
-
-#include <wx/panel.h>
 #include <wx/sizer.h>
 #include <wx/stattext.h>
-#include <wx/treebook.h>
-#include <wx/slider.h>
-#include <wx/spinctrl.h>
-#include <wx/textctrl.h>
-#include <wx/scrolwin.h>
 
-namespace ui 
-{
+#include "settings/PreferenceItems.h"
+#include "PreferenceItem.h"
 
-namespace 
+namespace ui 
 {
-	typedef std::vector<std::string> StringVector;
-}
 
-PrefPage::PrefPage(const std::string& name,
-                   wxTreebook* notebook,
-				   const PrefPagePtr& parentPage) : 
-	_name(name), 
-	_notebook(notebook),
-	_pageWidget(NULL),
-	_titleLabel(NULL)
+PrefPage::PrefPage(wxWindow* parent, const settings::PreferencePage& settingsPage) :
+	wxScrolledWindow(parent, wxID_ANY),
+	_settingsPage(settingsPage)
 {
-	if (parentPage && !parentPage->getPath().empty())
-	{
-		_path = parentPage->getPath() + "/" + _name;
-	}
-	else
-	{
-		_path = _name;
-	}
-
-	if (!_name.empty())
-	{
-		// Create the overall panel
-        _pageWidget = new wxScrolledWindow(_notebook, wxID_ANY);
-        _pageWidget->SetScrollRate(0, 3);
-
-		_pageWidget->SetSizer(new wxBoxSizer(wxVERTICAL));
-
-		// 12 pixel border
-		wxBoxSizer* overallVBox = new wxBoxSizer(wxVERTICAL);
-		_pageWidget->GetSizer()->Add(overallVBox, 1, wxEXPAND | wxALL, 12);
+	// Create the overall panel
+	SetScrollRate(0, 3);
+	SetSizer(new wxBoxSizer(wxVERTICAL));
 
-		// Create the label
-		_titleLabel = new wxStaticText(_pageWidget, wxID_ANY, 
-			(boost::format("%s Settings") % _name).str()
-		);
-		_titleLabel->SetFont(_titleLabel->GetFont().Bold());
-		overallVBox->Add(_titleLabel, 0, wxBOTTOM, 12);
+	// 12 pixel border
+	wxBoxSizer* overallVBox = new wxBoxSizer(wxVERTICAL);
+	GetSizer()->Add(overallVBox, 1, wxEXPAND | wxALL, 12);
 
-		_table = new wxFlexGridSizer(1, 2, 6, 12);
-        overallVBox->Add(_table, 1, wxEXPAND | wxLEFT, 6); // another 12 pixels to the left
+	// Create the label
+	wxStaticText* titleLabel = new wxStaticText(this, wxID_ANY, _settingsPage.getTitle());
+	titleLabel->SetFont(titleLabel->GetFont().Bold());
+	overallVBox->Add(titleLabel, 0, wxBOTTOM, 12);
 
-		if (parentPage && !parentPage->getName().empty())
-		{
-			// Find the index of the parent page to perform the insert
-			int pos = _notebook->FindPage(parentPage->getWidget());
-			_notebook->InsertSubPage(pos, _pageWidget, name);
-		}
-		else if (!_name.empty())
-		{
-			// Append the panel as new page to the notebook
-			_notebook->AddPage(_pageWidget, name);
-		}
-	}
-}
+	_table = new wxFlexGridSizer(1, 2, 6, 12);
+	overallVBox->Add(_table, 1, wxEXPAND | wxLEFT, 6); // another 12 pixels to the left
 
-void PrefPage::setTitle(const std::string& title)
-{
-	if (_titleLabel != NULL)
+	settingsPage.foreachItem([&](const settings::PreferenceItemBasePtr& item)
 	{
-		_titleLabel->SetLabelText(title);
-	}
-}
-
-std::string PrefPage::getPath() const
-{
-	return _path;
-}
-
-std::string PrefPage::getName() const
-{
-	return _name;
+		createItemWidgets(item);
+	});
 }
 
 void PrefPage::saveChanges()
@@ -108,254 +42,80 @@ void PrefPage::saveChanges()
 	_registryBuffer.commitChanges();
 }
 
-void PrefPage::discardChanges()
+void PrefPage::resetValues()
 {
 	_registryBuffer.clear();
 
 	_resetValuesSignal();
 }
 
-wxWindow* PrefPage::getWidget()
-{
-	return _pageWidget;
-}
-
-void PrefPage::foreachPage(const std::function<void(PrefPage&)>& functor)
+void PrefPage::appendNamedWidget(const std::string& name, wxWindow* widget, bool useFullWidth)
 {
-	std::for_each(_children.begin(), _children.end(), [&] (const PrefPagePtr& page)
+	if (_table->GetItemCount() > 0)
 	{
-		// Visit this instance
-		functor(*page);
-
-		// Pass the visitor recursively
-		page->foreachPage(functor);
-	});
-}
-
-void PrefPage::appendCheckBox(const std::string& name,
-                              const std::string& flag,
-                              const std::string& registryKey)
-{
-	// Create a new checkbox with the given caption and display it
-	wxCheckBox* check = new wxCheckBox(_pageWidget, wxID_ANY, flag);
-
-	// Connect the registry key to this toggle button
-    registry::bindWidgetToBufferedKey(check, registryKey, _registryBuffer, _resetValuesSignal);
-
-	appendNamedWidget(name, check);
-}
-
-void PrefPage::appendSlider(const std::string& name, const std::string& registryKey, bool drawValue,
-                            double value, double lower, double upper, double step_increment, double page_increment, double page_size)
-{
-	// Since sliders are int only, we need to factor the values to support floats
-	int factor = static_cast<int>(1 / step_increment);
-
-	wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
-
-	wxSlider* slider = new wxSlider(_pageWidget, wxID_ANY, value * factor, lower * factor, upper * factor);
-	slider->SetPageSize(page_increment * factor);
-
-	// Add a text widget displaying the value
-	wxStaticText* valueText = new wxStaticText(_pageWidget, wxID_ANY, "");
-	slider->Bind(wxEVT_SCROLL_CHANGED, [=] (wxScrollEvent& ev)
-	{ 
-		valueText->SetLabelText(string::to_string(slider->GetValue())); 
-		ev.Skip();
-	});
-	slider->Bind(wxEVT_SCROLL_THUMBTRACK, [=] (wxScrollEvent& ev)
-	{ 
-		valueText->SetLabelText(string::to_string(slider->GetValue())); 
-		ev.Skip();
-	});
-	valueText->SetLabelText(string::to_string(value));
-
-	hbox->Add(valueText, 0, wxALIGN_CENTER_VERTICAL);
-	hbox->Add(slider, 1, wxEXPAND | wxLEFT, 6);
-
-	// Connect the registry key to this adjustment
-    registry::bindWidgetToBufferedKey(slider, registryKey, _registryBuffer, _resetValuesSignal, factor);
-
-	appendNamedSizer(name, hbox);
-}
-
-void PrefPage::appendCombo(const std::string& name,
-                           const std::string& registryKey,
-                           const ComboBoxValueList& valueList,
-                           bool storeValueNotIndex)
-{
-	wxChoice* choice = new wxChoice(_pageWidget, wxID_ANY);
-
-    // Add all the string values to the combo box
-    for (ComboBoxValueList::const_iterator i = valueList.begin();
-         i != valueList.end();
-         ++i)
-    {
-		choice->Append(*i);
-    }
-
-	registry::bindWidgetToBufferedKey(choice, registryKey, _registryBuffer, _resetValuesSignal, storeValueNotIndex);
-
-	// Add the widget to the dialog row
-	appendNamedWidget(name, choice, false);
-}
-
-void PrefPage::appendEntry(const std::string& name, const std::string& registryKey)
-{
-	wxTextCtrl* entry = new wxTextCtrl(_pageWidget, wxID_ANY);
-
-	int minChars = static_cast<int>(std::max(GlobalRegistry().get(registryKey).size(), std::size_t(30)));
-	entry->SetMinClientSize(wxSize(entry->GetCharWidth() * minChars, -1));
-
-	// Connect the registry key to the newly created input field
-    registry::bindWidgetToBufferedKey(entry, registryKey, _registryBuffer, _resetValuesSignal);
-
-	appendNamedWidget(name, entry);
-}
-
-void PrefPage::appendLabel(const std::string& caption)
-{
-	wxStaticText* label = new wxStaticText(_pageWidget, wxID_ANY, "");
-	label->SetLabelMarkup(caption);
-
-	appendNamedWidget("", label);
-}
-
-void PrefPage::appendPathEntry(const std::string& name, const std::string& registryKey, bool browseDirectories)
-{
-	wxutil::PathEntry* entry = new wxutil::PathEntry(_pageWidget, browseDirectories);
-
-	// Connect the registry key to the newly created input field
-    registry::bindWidgetToBufferedKey(entry->getEntryWidget(),
-                                registryKey, _registryBuffer, _resetValuesSignal);
-
-	int minChars = static_cast<int>(std::max(GlobalRegistry().get(registryKey).size(), std::size_t(30)));
-
-	entry->getEntryWidget()->SetMinClientSize(
-		wxSize(entry->getEntryWidget()->GetCharWidth() * minChars, -1));
-
-	// Initialize entry
-	entry->setValue(registry::getValue<std::string>(registryKey));
+		// Add another row
+		_table->SetRows(_table->GetRows() + 1);
+	}
 
-	appendNamedWidget(name, entry);
+	_table->Add(new wxStaticText(this, wxID_ANY, name), 0, wxALIGN_CENTRE_VERTICAL);
+	_table->Add(widget, useFullWidth ? 1 : 0, wxEXPAND);
 }
 
-void PrefPage::appendSpinner(const std::string& name, const std::string& registryKey,
-                                   double lower, double upper, int fraction)
+void PrefPage::createItemWidgets(const settings::PreferenceItemBasePtr& item)
 {
-	// Load the initial value (maybe unnecessary, as the value is loaded upon dialog show)
-	float value = registry::getValue<float>(registryKey);
-
-	double step = 1.0 / static_cast<double>(fraction);
-	unsigned int digits = 0;
+	// Construct a generic item and pass the common values
+	PreferenceItem widget(this, item->getRegistryKey(), _registryBuffer, _resetValuesSignal);
+	
+	// Switch on the item type
+	if (std::dynamic_pointer_cast<settings::PreferenceLabel>(item))
+	{
+		wxWindow* label = widget.createLabel(item->getLabel());
 
-	for (;fraction > 1; fraction /= 10)
+		appendNamedWidget("", label);
+	}
+	else if (std::dynamic_pointer_cast<settings::PreferenceEntry>(item))
 	{
-		++digits;
+		appendNamedWidget(item->getLabel(), widget.createEntry());
 	}
-
-	if (digits == 0)
+	else if (std::dynamic_pointer_cast<settings::PreferenceCheckbox>(item))
 	{
-		wxSpinCtrl* spinner = new wxSpinCtrl(_pageWidget, wxID_ANY);
-
-		spinner->SetRange(static_cast<int>(lower), static_cast<int>(upper));
-		spinner->SetValue(static_cast<int>(value));
+		wxWindow* checkbox = widget.createCheckbox(item->getLabel());
 
-		spinner->SetMinClientSize(wxSize(64, -1));
-
-		// Connect the registry key to the newly created input field
-		registry::bindWidgetToBufferedKey(spinner, registryKey, _registryBuffer, _resetValuesSignal);
-
-		appendNamedWidget(name, spinner);
+		appendNamedWidget("", checkbox);
 	}
-	else
+	else if (std::dynamic_pointer_cast<settings::PreferenceCombobox>(item))
 	{
-		wxSpinCtrlDouble* spinner = new wxSpinCtrlDouble(_pageWidget, wxID_ANY);
-
-		spinner->SetRange(lower, upper);
-		spinner->SetValue(value);
-		spinner->SetIncrement(step);
+		std::shared_ptr<settings::PreferenceCombobox> info = std::static_pointer_cast<settings::PreferenceCombobox>(item);
 
-		spinner->SetMinClientSize(wxSize(64, -1));
+		wxWindow* combobox = widget.createCombobox(info->getValues(), info->storeValueNotIndex());
 
-		// Connect the registry key to the newly created input field
-		registry::bindWidgetToBufferedKey(spinner, registryKey, _registryBuffer, _resetValuesSignal);
-
-		appendNamedWidget(name, spinner);
+		appendNamedWidget(item->getLabel(), combobox, false);
 	}
-}
-
-PrefPagePtr PrefPage::createOrFindPage(const std::string& path)
-{
-	// Split the path into parts
-	StringVector parts;
-	boost::algorithm::split(parts, path, boost::algorithm::is_any_of("/"));
-
-	if (parts.empty())
+	else if (std::dynamic_pointer_cast<settings::PreferencePathEntry>(item))
 	{
-        rConsole() << "Warning: Could not resolve preference path: " << path << std::endl;
-		return PrefPagePtr();
-	}
+		std::shared_ptr<settings::PreferencePathEntry> info = std::static_pointer_cast<settings::PreferencePathEntry>(item);
 
-	PrefPagePtr child;
+		wxWindow* pathEntry = widget.createPathEntry(info->browseDirectories());
 
-	// Try to lookup the page in the child list
-	for (std::size_t i = 0; i < _children.size(); ++i)
-	{
-		if (_children[i]->getName() == parts[0])
-		{
-			child = _children[i];
-			break;
-		}
+		appendNamedWidget(item->getLabel(), pathEntry);
 	}
-
-	if (child == NULL)
+	else if (std::dynamic_pointer_cast<settings::PreferenceSpinner>(item))
 	{
-		// No child found, create a new page and add it to the list
-		child = PrefPagePtr(new PrefPage(parts[0], _notebook, shared_from_this()));
-		_children.push_back(child);
-	}
+		std::shared_ptr<settings::PreferenceSpinner> info = std::static_pointer_cast<settings::PreferenceSpinner>(item);
 
-	// We now have a child with this name, do we have a leaf?
-	if (parts.size() > 1) {
-		// We have still more parts, split off the first part
-		std::string subPath("");
-		for (std::size_t i = 1; i < parts.size(); ++i)
-		{
-			subPath += (subPath.empty()) ? "" : "/";
-			subPath += parts[i];
-		}
-		// Pass the call to the child
-		return child->createOrFindPage(subPath);
-	}
-	else {
-		// We have found a leaf, return the child page
-		return child;
-	}
-}
+		wxWindow* spinner = widget.createSpinner(info->getLower(), info->getUpper(), info->getFraction());
 
-void PrefPage::appendNamedWidget(const std::string& name, wxWindow* widget, bool useFullWidth)
-{
-	if (_table->GetItemCount() > 0)
-	{
-		// Add another row
-		_table->SetRows(_table->GetRows() + 1);
+		appendNamedWidget(item->getLabel(), spinner);
 	}
+	else if (std::dynamic_pointer_cast<settings::PreferenceSlider>(item))
+	{
+		std::shared_ptr<settings::PreferenceSlider> info = std::static_pointer_cast<settings::PreferenceSlider>(item);
 
-	_table->Add(new wxStaticText(_pageWidget, wxID_ANY, name), 0, wxALIGN_CENTRE_VERTICAL);
-	_table->Add(widget, useFullWidth ? 1 : 0, wxEXPAND);
-}
+		wxWindow* slider = widget.createSlider(info->getLower(), info->getUpper(), 
+			info->getStepIncrement(), info->getPageIncrement());
 
-void PrefPage::appendNamedSizer(const std::string& name, wxSizer* sizer, bool useFullWidth)
-{
-	if (_table->GetItemCount() > 0)
-	{
-		// Add another row
-		_table->SetRows(_table->GetRows() + 1);
+		appendNamedWidget(item->getLabel(), slider);
 	}
-
-	_table->Add(new wxStaticText(_pageWidget, wxID_ANY, name), 0, wxALIGN_CENTRE_VERTICAL);
-	_table->Add(sizer, useFullWidth ? 1 : 0, wxEXPAND);
 }
 
 } // namespace ui
diff --git a/radiant/ui/prefdialog/PrefPage.h b/radiant/ui/prefdialog/PrefPage.h
index faa44ac..7a05ba1 100644
--- a/radiant/ui/prefdialog/PrefPage.h
+++ b/radiant/ui/prefdialog/PrefPage.h
@@ -1,26 +1,26 @@
 #pragma once
 
-#include "registry/buffer.h"
-#include <wx/panel.h>
+#include <wx/scrolwin.h>
 
-#include "ipreferencesystem.h"
+#include "registry/buffer.h"
+#include "settings/PreferencePage.h"
 
-class wxTreebook;
 class wxFlexGridSizer;
-class wxStaticText;
-class wxSizer;
-class wxScrolledWindow;
 
-namespace ui {
-
-class PrefPage;
-typedef std::shared_ptr<PrefPage> PrefPagePtr;
+namespace ui
+{
 
+/**
+ * A preference page as inserted into the PrefDialog's treebook control.
+ * Each PrefPage renders the items found in the assigned settings::PreferencePage.
+ */
 class PrefPage :
-	public PreferencesPage,
-	public std::enable_shared_from_this<PrefPage>
+	public wxScrolledWindow
 {
 private:
+	// The settings page we're representing
+	const settings::PreferencePage& _settingsPage;
+
 	// We're holding back any registry write operations until the user clicks OK
 	registry::Buffer _registryBuffer;
 
@@ -31,105 +31,20 @@ private:
 	// The table this page is adding the widgets to
 	wxFlexGridSizer* _table;
 
-  	// The list of child pages
-	std::vector<PrefPagePtr> _children;
-
-	// The name (caption) of this page
-	std::string _name;
-
-	// The full path of this object
-	std::string _path;
-
-	// The notebook this page is packed into
-	wxTreebook* _notebook;
-
-	// The actual page that gets attached to the notebook
-	wxScrolledWindow* _pageWidget;
-
-	wxStaticText* _titleLabel;
-
 public:
-	/** greebo: Constructor
-	 *
-	 * @name: The display caption of this prefpage
-	 * @parentPath: the path to the parent of this page
-	 * @notebook: The notebook widget this page is child of.
-	 */
-	PrefPage(const std::string& name,
-	         wxTreebook* notebook,
-			 const PrefPagePtr& parentPage = PrefPagePtr());
-
-	/** greebo: Sets the title caption that is displayed on the right.
-	 * 			Overrides the default title that is generated
-	 * 			on construction (the one with the " Settings" postfix).
-	 */
-	void setTitle(const std::string& title);
-
-	/** greebo: Returns the full path to this PrefPage
-	 */
-	std::string getPath() const;
+	PrefPage(wxWindow* parent, const settings::PreferencePage& settingsPage);
 
-	/** greebo: Returns the name (caption) of this Page (e.g. "Settings")
-	 */
-	std::string getName() const;
-
-	/**
-	 * Commit all pending registry write operations.
-	 */
+	// Commit all pending registry write operations.
 	void saveChanges();
 
-	/** 
-	 * Discard all pending registry write operations.
-	 */
-	void discardChanges();
-
-	/** greebo: Returns the widget that can be used to determine
-	 * the notebook page number.
-	 */
-	wxWindow* getWidget();
-
-	void foreachPage(const std::function<void(PrefPage&)>& functor);
-
-	// Appends a simple static label
-	void appendLabel(const std::string& caption);
-
-	/* greebo: This adds a checkbox and connects it to an XMLRegistry key.
-	 * @returns: the pointer to the created GtkWidget */
-	void appendCheckBox(const std::string& name, const std::string& flag, const std::string& registryKey);
-
-	/* greebo: This adds a horizontal slider to the internally referenced VBox and connects
-	 * it to the given registryKey. */
-	void appendSlider(const std::string& name, const std::string& registryKey, bool drawValue,
-	                  double value, double lower, double upper, double step_increment, double page_increment, double page_size) ;
-
-    void appendCombo(const std::string& name,
-                     const std::string& registryKey,
-                     const ComboBoxValueList& valueList,
-                     bool storeValueNotIndex = false);
-
-	/* greebo: Appends an entry field with <name> as caption which is connected to the given registryKey
-	 */
-	void appendEntry(const std::string& name, const std::string& registryKey);
-
-	// greebo: Adds a PathEntry to choose files or directories (depending on the given boolean)
-	void appendPathEntry(const std::string& name, const std::string& registryKey, bool browseDirectories);
-
-	/* greebo: Appends an entry field with spinner buttons which retrieves its value from the given
-	 * RegistryKey. The lower and upper values have to be passed as well.
-	 */
-	void appendSpinner(const std::string& name, const std::string& registryKey,
-	                         double lower, double upper, int fraction);
-
-	/** greebo: Performs a recursive lookup of the given path
-	 * 			and creates any items that do not exist.
-	 *
-	 * @returns: the shared_ptr to the PrefPage, can be empty on error.
-	 */
-	PrefPagePtr createOrFindPage(const std::string& path);
+	// Discard all pending registry write operations.
+	void resetValues();
 
 private:
 	void appendNamedWidget(const std::string& name, wxWindow* widget, bool useFullWidth = true);
-	void appendNamedSizer(const std::string& name, wxSizer* sizer, bool useFullWidth = true);
+
+	void createItemWidgets(const settings::PreferenceItemBasePtr& item);
 };
+typedef std::shared_ptr<PrefPage> PrefPagePtr;
 
 } // namespace ui
diff --git a/radiant/ui/prefdialog/PreferenceItem.cpp b/radiant/ui/prefdialog/PreferenceItem.cpp
new file mode 100644
index 0000000..fe931fe
--- /dev/null
+++ b/radiant/ui/prefdialog/PreferenceItem.cpp
@@ -0,0 +1,164 @@
+#include "PreferenceItem.h"
+
+#include <wx/stattext.h>
+#include <wx/checkbox.h>
+#include <wx/slider.h>
+#include <wx/sizer.h>
+#include <wx/combobox.h>
+#include <wx/choice.h>
+#include <wx/panel.h>
+#include <wx/scrolwin.h>
+#include "registry/Widgets.h"
+#include "wxutil/PathEntry.h"
+
+namespace ui
+{
+
+wxWindow* PreferenceItem::createLabel(const std::string& label)
+{
+	wxStaticText* labelWidget = new wxStaticText(_parent, wxID_ANY, "");
+	labelWidget->SetLabelMarkup(label);
+
+	return labelWidget;
+}
+
+wxWindow* PreferenceItem::createEntry()
+{
+	wxTextCtrl* entryWidget = new wxTextCtrl(_parent, wxID_ANY);
+
+	int minChars = static_cast<int>(std::max(_buffer.get(_registryKey).size(), std::size_t(30)));
+	entryWidget->SetMinClientSize(wxSize(entryWidget->GetCharWidth() * minChars, -1));
+
+	// Connect the registry key to the newly created input field
+	registry::bindWidgetToBufferedKey(entryWidget, _registryKey, _buffer, _resetSignal);
+
+	return entryWidget;
+}
+
+wxWindow* PreferenceItem::createCheckbox(const std::string& label)
+{
+	wxCheckBox* checkbox = new wxCheckBox(_parent, wxID_ANY, label);
+
+	// Connect the registry key to this checkbox
+	registry::bindWidgetToBufferedKey(checkbox, _registryKey, _buffer, _resetSignal);
+
+	return checkbox;
+}
+
+wxWindow* PreferenceItem::createCombobox(const ComboBoxValueList& values, bool storeValueNotIndex)
+{
+	wxChoice* choice = new wxChoice(_parent, wxID_ANY);
+
+	// Add all the string values to the combo box
+	for (const std::string& value : values)
+	{
+		choice->Append(value);
+	}
+
+	// Connect the registry key to this combo
+	registry::bindWidgetToBufferedKey(choice, _registryKey, _buffer, _resetSignal, storeValueNotIndex);
+
+	return choice;
+}
+
+wxWindow* PreferenceItem::createPathEntry(bool browseDirectories)
+{
+	wxutil::PathEntry* entry = new wxutil::PathEntry(_parent, browseDirectories);
+
+	// Connect the registry key to the newly created input field
+	registry::bindWidgetToBufferedKey(entry->getEntryWidget(), _registryKey, _buffer, _resetSignal);
+
+	int minChars = static_cast<int>(std::max(GlobalRegistry().get(_registryKey).size(), std::size_t(30)));
+
+	entry->getEntryWidget()->SetMinClientSize(
+		wxSize(entry->getEntryWidget()->GetCharWidth() * minChars, -1));
+
+	// Initialize entry
+	entry->setValue(registry::getValue<std::string>(_registryKey));
+
+	return entry;
+}
+
+wxWindow* PreferenceItem::createSpinner(double lower, double upper, int fractionInt)
+{
+	double fraction = static_cast<double>(fractionInt);
+	double step = 1.0 / fraction;
+	unsigned int digits = 0;
+
+	for (; fraction > 1; fraction /= 10)
+	{
+		++digits;
+	}
+
+	if (digits == 0)
+	{
+		wxSpinCtrl* spinner = new wxSpinCtrl(_parent, wxID_ANY);
+
+		spinner->SetRange(static_cast<int>(lower), static_cast<int>(upper));
+		spinner->SetMinClientSize(wxSize(64, -1));
+
+		// Connect the registry key to the newly created spinbutton
+		registry::bindWidgetToBufferedKey(spinner, _registryKey, _buffer, _resetSignal);
+
+		return spinner;
+	}
+	else
+	{
+		wxSpinCtrlDouble* spinner = new wxSpinCtrlDouble(_parent, wxID_ANY);
+
+		spinner->SetRange(lower, upper);
+		spinner->SetIncrement(step);
+		spinner->SetMinClientSize(wxSize(64, -1));
+
+		// Connect the registry key to the newly created spinbutton
+		registry::bindWidgetToBufferedKey(spinner, _registryKey, _buffer, _resetSignal);
+
+		return spinner;
+	}
+}
+
+wxWindow* PreferenceItem::createSlider(double lower, double upper, double stepIncrement, double pageIncrement)
+{
+	// Since sliders are int only, we need to factor the values to support floats
+	int factor = static_cast<int>(1 / stepIncrement);
+
+	wxPanel* panel = new wxPanel(_parent, wxID_ANY);
+
+	wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
+	panel->SetSizer(hbox);
+
+	// Get the current value from the registry
+	double value = registry::getValue<float>(_registryKey) * factor;
+
+	wxSlider* slider = new wxSlider(panel, wxID_ANY, value * factor, lower * factor, upper * factor);
+	slider->SetPageSize(pageIncrement * factor);
+
+	// Add a text widget displaying the value
+	wxStaticText* valueLabel = new wxStaticText(panel, wxID_ANY, "");
+	slider->Bind(wxEVT_SCROLL_CHANGED, [=](wxScrollEvent& ev)
+	{
+		valueLabel->SetLabelText(string::to_string(slider->GetValue()));
+		ev.Skip();
+	});
+
+	slider->Bind(wxEVT_SCROLL_THUMBTRACK, [=](wxScrollEvent& ev)
+	{
+		valueLabel->SetLabelText(string::to_string(slider->GetValue()));
+		ev.Skip();
+	});
+
+	valueLabel->SetLabelText(string::to_string(value));
+
+	hbox->Add(valueLabel, 0, wxALIGN_CENTER_VERTICAL);
+	hbox->Add(slider, 1, wxEXPAND | wxLEFT, 6);
+
+	// Connect the registry key to this slider
+	registry::bindWidgetToBufferedKey(slider, _registryKey, _buffer, _resetSignal, factor);
+
+	// Update the value text now, the wxSlider::SetValue() doesn't trigger the changed signal
+	valueLabel->SetLabelText(string::to_string(slider->GetValue()));
+
+	return panel;
+}
+
+} // namespace
diff --git a/radiant/ui/prefdialog/PreferenceItem.h b/radiant/ui/prefdialog/PreferenceItem.h
new file mode 100644
index 0000000..ba56a98
--- /dev/null
+++ b/radiant/ui/prefdialog/PreferenceItem.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <string>
+#include <sigc++/signal.h>
+
+#include "ipreferencesystem.h"
+#include "registry/buffer.h"
+
+class wxWindow;
+
+namespace ui
+{
+
+// Helper class creating and setting up various types of preference UI elements
+class PreferenceItem
+{
+protected:
+	wxWindow* _parent;
+	std::string _registryKey;
+	registry::Buffer& _buffer;
+	sigc::signal<void>& _resetSignal;
+
+public:
+	PreferenceItem(wxWindow* parent, const std::string& registryKey, 
+				   registry::Buffer& buffer, sigc::signal<void>& resetSignal) :
+		_parent(parent),
+		_registryKey(registryKey),
+		_buffer(buffer),
+		_resetSignal(resetSignal)
+	{}
+
+	wxWindow* createLabel(const std::string& label);
+
+	wxWindow* createEntry();
+
+	wxWindow* createCheckbox(const std::string& label);
+
+	wxWindow* createCombobox(const ComboBoxValueList& values, bool storeValueNotIndex);
+
+	wxWindow* createPathEntry(bool browseDirectories);
+
+	wxWindow* createSpinner(double lower, double upper, int fraction);
+
+	wxWindow* createSlider(double lower, double upper, double stepIncrement, double pageIncrement);
+};
+
+} // namespace
diff --git a/radiant/ui/texturebrowser/TextureBrowser.cpp b/radiant/ui/texturebrowser/TextureBrowser.cpp
index 3d240ea..baf3398 100644
--- a/radiant/ui/texturebrowser/TextureBrowser.cpp
+++ b/radiant/ui/texturebrowser/TextureBrowser.cpp
@@ -116,35 +116,35 @@ public:
         if ((position.y() - size.y() - FONT_HEIGHT() < _owner.getOriginY()) &&
             (position.y() > _owner.getOriginY() - _owner.getViewportHeight()))
         {
-            drawBorder(*material, position, size);
-            drawTextureQuad(texture->getGLTexNum(), position, size);
-            drawTextureName(*material, position, size);
+            drawBorder();
+            drawTextureQuad(texture->getGLTexNum());
+            drawTextureName();
         }
     }
 
 private:
-    void drawBorder(const Material& material, const Vector2i& pos, const Vector2i& size)
+    void drawBorder()
     {
         // borders rules:
         // if it's the current texture, draw a thick red line, else:
         // shaders have a white border, simple textures don't
         // if !texture_showinuse: (some textures displayed may not be in use)
         // draw an additional square around with 0.5 1 0.5 color
-        if (shader_equal(_owner.getSelectedShader(), material.getName()))
+        if (shader_equal(_owner.getSelectedShader(), material->getName()))
         {
             glLineWidth(3);
             glColor3f(1, 0, 0);
             glDisable(GL_TEXTURE_2D);
 
             glBegin(GL_LINE_LOOP);
-            glVertex2i(pos.x() - TILE_BORDER,
-                       pos.y() - FONT_HEIGHT() + TILE_BORDER);
-            glVertex2i(pos.x() - TILE_BORDER,
-                       pos.y() - FONT_HEIGHT() - size.y() - TILE_BORDER);
-            glVertex2i(pos.x() + TILE_BORDER + size.x(),
-                       pos.y() - FONT_HEIGHT() - size.y() - TILE_BORDER);
-            glVertex2i(pos.x() + TILE_BORDER + size.x(),
-                       pos.y() - FONT_HEIGHT() + TILE_BORDER);
+            glVertex2i(position.x() - TILE_BORDER,
+                       position.y() - FONT_HEIGHT() + TILE_BORDER);
+            glVertex2i(position.x() - TILE_BORDER,
+                       position.y() - FONT_HEIGHT() - size.y() - TILE_BORDER);
+            glVertex2i(position.x() + TILE_BORDER + size.x(),
+                       position.y() - FONT_HEIGHT() - size.y() - TILE_BORDER);
+            glVertex2i(position.x() + TILE_BORDER + size.x(),
+                       position.y() - FONT_HEIGHT() + TILE_BORDER);
             glEnd();
 
             glEnable(GL_TEXTURE_2D);
@@ -155,45 +155,45 @@ private:
             glLineWidth(1);
 
             // material border:
-            if (!material.IsDefault())
+            if (!material->IsDefault())
             {
                 glColor3f(1, 1, 1);
                 glDisable(GL_TEXTURE_2D);
 
                 glBegin(GL_LINE_LOOP);
-                glVertex2i(pos.x() - 1,
-                           pos.y() + 1 - FONT_HEIGHT());
-                glVertex2i(pos.x() - 1,
-                           pos.y() - size.y() - 1 - FONT_HEIGHT());
-                glVertex2i(pos.x() + 1 + size.x(),
-                           pos.y() - size.y() - 1 - FONT_HEIGHT());
-                glVertex2i(pos.x() + 1 + size.x(),
-                           pos.y() + 1 - FONT_HEIGHT());
+                glVertex2i(position.x() - 1,
+                           position.y() + 1 - FONT_HEIGHT());
+                glVertex2i(position.x() - 1,
+                           position.y() - size.y() - 1 - FONT_HEIGHT());
+                glVertex2i(position.x() + 1 + size.x(),
+                           position.y() - size.y() - 1 - FONT_HEIGHT());
+                glVertex2i(position.x() + 1 + size.x(),
+                           position.y() + 1 - FONT_HEIGHT());
                 glEnd();
                 glEnable(GL_TEXTURE_2D);
             }
 
             // highlight in-use textures
-            if (!_owner._hideUnused && material.IsInUse())
+            if (!_owner._hideUnused && material->IsInUse())
             {
                 glColor3f(0.5f, 1, 0.5f);
                 glDisable(GL_TEXTURE_2D);
                 glBegin(GL_LINE_LOOP);
-                glVertex2i(pos.x() - 3,
-                           pos.y() + 3 - FONT_HEIGHT());
-                glVertex2i(pos.x() - 3,
-                           pos.y() - size.y() - 3 - FONT_HEIGHT());
-                glVertex2i(pos.x() + 3 + size.x(),
-                           pos.y() - size.y() - 3 - FONT_HEIGHT());
-                glVertex2i(pos.x() + 3 + size.x(),
-                           pos.y() + 3 - FONT_HEIGHT());
+                glVertex2i(position.x() - 3,
+                           position.y() + 3 - FONT_HEIGHT());
+                glVertex2i(position.x() - 3,
+                           position.y() - size.y() - 3 - FONT_HEIGHT());
+                glVertex2i(position.x() + 3 + size.x(),
+                           position.y() - size.y() - 3 - FONT_HEIGHT());
+                glVertex2i(position.x() + 3 + size.x(),
+                           position.y() + 3 - FONT_HEIGHT());
                 glEnd();
                 glEnable(GL_TEXTURE_2D);
             }
         }
     }
 
-    void drawTextureQuad(GLuint num, const Vector2i& pos, const Vector2i& size)
+    void drawTextureQuad(GLuint num)
     {
         glBindTexture(GL_TEXTURE_2D, num);
         GlobalOpenGL().assertNoErrors();
@@ -201,26 +201,26 @@ private:
 
         glBegin(GL_QUADS);
         glTexCoord2i(0, 0);
-        glVertex2i(pos.x(), pos.y() - FONT_HEIGHT());
+        glVertex2i(position.x(), position.y() - FONT_HEIGHT());
         glTexCoord2i(1, 0);
-        glVertex2i(pos.x() + size.x(), pos.y() - FONT_HEIGHT());
+        glVertex2i(position.x() + size.x(), position.y() - FONT_HEIGHT());
         glTexCoord2i(1, 1);
-        glVertex2i(pos.x() + size.x(), pos.y() - FONT_HEIGHT() - size.y());
+        glVertex2i(position.x() + size.x(), position.y() - FONT_HEIGHT() - size.y());
         glTexCoord2i(0, 1);
-        glVertex2i(pos.x(), pos.y() - FONT_HEIGHT() - size.y());
+        glVertex2i(position.x(), position.y() - FONT_HEIGHT() - size.y());
         glEnd();
     }
 
-    void drawTextureName(const Material& material, const Vector2i& pos, const Vector2i& size)
+    void drawTextureName()
     {
         glDisable(GL_TEXTURE_2D);
         glColor3f(1, 1, 1);
 
         const static int FONT_OFFSET = 6;
-        glRasterPos2i(pos.x(), pos.y() - FONT_HEIGHT() + FONT_OFFSET);
+        glRasterPos2i(position.x(), position.y() - FONT_HEIGHT() + FONT_OFFSET);
 
         // don't draw the directory name
-        std::string name = material.getName();
+        std::string name = material->getName();
         name = name.substr(name.rfind("/") + 1);
 
         // Ellipsize the name if it's too long
@@ -819,7 +819,7 @@ void TextureBrowser::openContextMenu() {
         }
     }
 
-	_shaderLabel->SetText(shaderText);
+	_shaderLabel->SetItemLabel(shaderText);
 
     _popupMenu->show(_wxGLWidget);
 }
diff --git a/radiant/ui/texturebrowser/TextureBrowserManager.cpp b/radiant/ui/texturebrowser/TextureBrowserManager.cpp
index 0cdb303..6628b3d 100644
--- a/radiant/ui/texturebrowser/TextureBrowserManager.cpp
+++ b/radiant/ui/texturebrowser/TextureBrowserManager.cpp
@@ -58,14 +58,14 @@ void TextureBrowserManager::updateAllWindows()
 void TextureBrowserManager::registerPreferencePage()
 {
     // Add a page to the given group
-    PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Texture Browser"));
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Texture Browser"));
 
-    page->appendEntry(_("Uniform texture thumbnail size (pixels)"), RKEY_TEXTURE_UNIFORM_SIZE);
-    page->appendCheckBox("", _("Texture scrollbar"), RKEY_TEXTURE_SHOW_SCROLLBAR);
-    page->appendEntry(_("Mousewheel Increment"), RKEY_TEXTURE_MOUSE_WHEEL_INCR);
-    page->appendSpinner(_("Max shadername length"), RKEY_TEXTURE_MAX_NAME_LENGTH, 4, 100, 1);
+    page.appendEntry(_("Uniform texture thumbnail size (pixels)"), RKEY_TEXTURE_UNIFORM_SIZE);
+    page.appendCheckBox(_("Texture scrollbar"), RKEY_TEXTURE_SHOW_SCROLLBAR);
+    page.appendEntry(_("Mousewheel Increment"), RKEY_TEXTURE_MOUSE_WHEEL_INCR);
+    page.appendSpinner(_("Max shadername length"), RKEY_TEXTURE_MAX_NAME_LENGTH, 4, 100, 1);
 
-    page->appendCheckBox("", _("Show Texture Filter"), RKEY_TEXTURE_SHOW_FILTER);
+    page.appendCheckBox(_("Show Texture Filter"), RKEY_TEXTURE_SHOW_FILTER);
 }
 
 // Static command target
diff --git a/radiant/xyview/GlobalXYWnd.cpp b/radiant/xyview/GlobalXYWnd.cpp
index 7e472e8..74239fa 100644
--- a/radiant/xyview/GlobalXYWnd.cpp
+++ b/radiant/xyview/GlobalXYWnd.cpp
@@ -192,23 +192,23 @@ void XYWndManager::registerCommands() {
 
 void XYWndManager::constructPreferences()
 {
-	PreferencesPagePtr page = GlobalPreferenceSystem().getPage(_("Settings/Orthoview"));
-
-	page->appendCheckBox("", _("View chases Mouse Cursor during Drags"), RKEY_CHASE_MOUSE);
-    page->appendSlider(_("Maximum Chase Mouse Speed"), RKEY_CHASE_MOUSE_CAP, true, DEFAULT_CHASE_MOUSE_CAP, 0, 512, 1, 16, 16);
-	page->appendCheckBox("", _("Update Views on Camera Movement"), RKEY_CAMERA_XY_UPDATE);
-	page->appendCheckBox("", _("Show Crosshairs"), RKEY_SHOW_CROSSHAIRS);
-	page->appendCheckBox("", _("Show Grid"), RKEY_SHOW_GRID);
-	page->appendCheckBox("", _("Show Size Info"), RKEY_SHOW_SIZE_INFO);
-	page->appendCheckBox("", _("Show Entity Angle Arrow"), RKEY_SHOW_ENTITY_ANGLES);
-	page->appendCheckBox("", _("Show Entity Names"), RKEY_SHOW_ENTITY_NAMES);
-	page->appendCheckBox("", _("Show Blocks"), RKEY_SHOW_BLOCKS);
-	page->appendCheckBox("", _("Show Coordinates"), RKEY_SHOW_COORDINATES);
-	page->appendCheckBox("", _("Show Axes"), RKEY_SHOW_AXES);
-	page->appendCheckBox("", _("Show Window Outline"), RKEY_SHOW_OUTLINE);
-	page->appendCheckBox("", _("Show Workzone"), RKEY_SHOW_WORKZONE);
-	page->appendCheckBox("", _("Translate Manipulator always constrained to Axis"), RKEY_TRANSLATE_CONSTRAINED);
-	page->appendCheckBox("", _("Higher Selection Priority for Entities"), RKEY_HIGHER_ENTITY_PRIORITY);
+	IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Orthoview"));
+
+	page.appendCheckBox(_("View chases Mouse Cursor during Drags"), RKEY_CHASE_MOUSE);
+    page.appendSlider(_("Maximum Chase Mouse Speed"), RKEY_CHASE_MOUSE_CAP, 0, 512, 1, 16);
+	page.appendCheckBox(_("Update Views on Camera Movement"), RKEY_CAMERA_XY_UPDATE);
+	page.appendCheckBox(_("Show Crosshairs"), RKEY_SHOW_CROSSHAIRS);
+	page.appendCheckBox(_("Show Grid"), RKEY_SHOW_GRID);
+	page.appendCheckBox(_("Show Size Info"), RKEY_SHOW_SIZE_INFO);
+	page.appendCheckBox(_("Show Entity Angle Arrow"), RKEY_SHOW_ENTITY_ANGLES);
+	page.appendCheckBox(_("Show Entity Names"), RKEY_SHOW_ENTITY_NAMES);
+	page.appendCheckBox(_("Show Blocks"), RKEY_SHOW_BLOCKS);
+	page.appendCheckBox(_("Show Coordinates"), RKEY_SHOW_COORDINATES);
+	page.appendCheckBox(_("Show Axes"), RKEY_SHOW_AXES);
+	page.appendCheckBox(_("Show Window Outline"), RKEY_SHOW_OUTLINE);
+	page.appendCheckBox(_("Show Workzone"), RKEY_SHOW_WORKZONE);
+	page.appendCheckBox(_("Translate Manipulator always constrained to Axis"), RKEY_TRANSLATE_CONSTRAINED);
+	page.appendCheckBox(_("Higher Selection Priority for Entities"), RKEY_HIGHER_ENTITY_PRIORITY);
 }
 
 // Load/Reload the values from the registry
@@ -661,7 +661,8 @@ void XYWndManager::initialiseModule(const ApplicationContext& ctx)
 	GlobalUIManager().getStatusBarManager().addTextElement(
 		"XYZPos",
 		"",  // no icon
-		IStatusBarManager::POS_POSITION
+		IStatusBarManager::POS_POSITION,
+		_("Shows the mouse position in the orthoview")
 	);
 
 	XYWnd::captureStates();
diff --git a/radiant/xyview/XYRenderer.h b/radiant/xyview/XYRenderer.h
index 80d6587..6cce3fc 100644
--- a/radiant/xyview/XYRenderer.h
+++ b/radiant/xyview/XYRenderer.h
@@ -1,5 +1,4 @@
-#ifndef XYRENDERER_H_
-#define XYRENDERER_H_
+#pragma once
 
 #include "irenderable.h"
 
@@ -10,11 +9,14 @@ class XYRenderer :
 	struct State
 	{
 		bool highlightPrimitives;
+		bool highlightAsGroupMember;
 		Shader* shader;
 
 		// Constructor
-		State()
-		: highlightPrimitives(false), shader(NULL)
+		State() : 
+			highlightPrimitives(false), 
+			highlightAsGroupMember(false),
+			shader(nullptr)
 		{}
 	};
 
@@ -23,11 +25,13 @@ class XYRenderer :
 
 	// Shader to use for highlighted objects
 	Shader* _selectedShader;
+	Shader* _selectedShaderGroup;
 
 public:
-	XYRenderer(RenderStateFlags globalstate, Shader* selected) :
-			_globalstate(globalstate),
-			_selectedShader(selected)
+	XYRenderer(RenderStateFlags globalstate, Shader* selected, Shader* selectedGroup) :
+		_globalstate(globalstate),
+		_selectedShader(selected),
+		_selectedShaderGroup(selectedGroup)
 	{
 		// Reserve space in the vector to avoid reallocation delays
 		_stateStack.reserve(8);
@@ -58,11 +62,17 @@ public:
 		_stateStack.pop_back();
 	}
 
-    void highlightFaces(bool enable = true) { }
-
-    void highlightPrimitives(bool enable = true)
+	void setHighlightFlag(Highlight::Flags flags, bool enabled)
 	{
-        _stateStack.back().highlightPrimitives = enable;
+		if (flags & Highlight::Primitives)
+		{
+			_stateStack.back().highlightPrimitives = enabled;
+		}
+
+		if (flags & Highlight::GroupMember)
+		{
+			_stateStack.back().highlightAsGroupMember = enabled;
+		}
 	}
 
 	void addRenderable(const OpenGLRenderable& renderable,
@@ -70,9 +80,16 @@ public:
 	{
 		if (_stateStack.back().highlightPrimitives)
 		{
-			_selectedShader->addRenderable(renderable, localToWorld);
+			if (_stateStack.back().highlightAsGroupMember)
+			{
+				_selectedShaderGroup->addRenderable(renderable, localToWorld);
+			}
+			else
+			{
+				_selectedShader->addRenderable(renderable, localToWorld);
+			}
 		}
-		else if (_stateStack.back().shader != NULL)
+		else if (_stateStack.back().shader != nullptr)
 		{
 			_stateStack.back().shader->addRenderable(renderable, localToWorld);
 		}
@@ -84,9 +101,16 @@ public:
 	{
 		if (_stateStack.back().highlightPrimitives)
 		{
-			_selectedShader->addRenderable(renderable, localToWorld, entity);
+			if (_stateStack.back().highlightAsGroupMember)
+			{
+				_selectedShaderGroup->addRenderable(renderable, localToWorld, entity);
+			}
+			else
+			{
+				_selectedShader->addRenderable(renderable, localToWorld, entity);
+			}
 		}
-		else if (_stateStack.back().shader != NULL)
+		else if (_stateStack.back().shader != nullptr)
 		{
 			_stateStack.back().shader->addRenderable(renderable, localToWorld, entity);
 		}
@@ -97,5 +121,3 @@ public:
 		GlobalRenderSystem().render(_globalstate, modelview, projection);
 	}
 }; // class XYRenderer
-
-#endif /*XYRENDERER_H_*/
diff --git a/radiant/xyview/XYWnd.cpp b/radiant/xyview/XYWnd.cpp
index 01917fd..acd6321 100644
--- a/radiant/xyview/XYWnd.cpp
+++ b/radiant/xyview/XYWnd.cpp
@@ -22,6 +22,7 @@
 #include "ui/overlay/Overlay.h"
 #include "ui/texturebrowser/TextureBrowser.h"
 #include "map/RegionManager.h"
+#include "map/Map.h"
 #include "selection/algorithm/General.h"
 #include "selection/algorithm/Primitives.h"
 #include "registry/registry.h"
@@ -70,7 +71,6 @@ XYWnd::XYWnd(int id, wxWindow* parent) :
 	_id(id),
 	_wxGLWidget(new wxutil::GLWidget(parent, std::bind(&XYWnd::onRender, this), "XYWnd")),
     _drawing(false),
-	_deferredDraw(std::bind(&XYWnd::performDeferredDraw, this)),
 	_minWorldCoord(game::current::getValue<float>("/defaults/minWorldCoord")),
 	_maxWorldCoord(game::current::getValue<float>("/defaults/maxWorldCoord")),
 	_defaultCursor(wxCURSOR_DEFAULT),
@@ -125,10 +125,6 @@ XYWnd::XYWnd(int id, wxWindow* parent) :
 		wxutil::FreezePointer::MouseEventFunction(),
 		std::bind(&XYWnd::onGLMouseButtonRelease, this, std::placeholders::_1));
 
-    GlobalMap().signal_mapValidityChanged().connect(
-        sigc::mem_fun(_deferredDraw, &DeferredDraw::onMapValidChanged)
-    );
-
     updateProjection();
     updateModelview();
 
@@ -193,7 +189,7 @@ SelectionTestPtr XYWnd::createSelectionTestForPoint(const Vector2& point)
     float selectEpsilon = registry::getValue<float>(RKEY_SELECT_EPSILON);
 
     // Generate the epsilon
-    DeviceVector deviceEpsilon(selectEpsilon / getWidth(), selectEpsilon / getHeight());
+    Vector2 deviceEpsilon(selectEpsilon / getWidth(), selectEpsilon / getHeight());
 
     // Copy the current view and constrain it to a small rectangle
     render::View scissored(_view);
@@ -217,12 +213,16 @@ int XYWnd::getDeviceHeight() const
     return getHeight();
 }
 
-void XYWnd::captureStates() {
+void XYWnd::captureStates()
+{
     _selectedShader = GlobalRenderSystem().capture("$XY_OVERLAY");
+	_selectedShaderGroup = GlobalRenderSystem().capture("$XY_OVERLAY_GROUP");
 }
 
-void XYWnd::releaseStates() {
-    _selectedShader = ShaderPtr();
+void XYWnd::releaseStates()
+{
+	_selectedShader.reset();
+	_selectedShaderGroup.reset();
 }
 
 const std::string XYWnd::getViewTypeTitle(EViewType viewtype) {
@@ -268,7 +268,7 @@ void XYWnd::queueDraw()
         return; // deny redraw requests if we're currently drawing
     }
 
-    _deferredDraw.draw();
+	_wxGLWidget->Refresh(false);
 }
 
 void XYWnd::onSceneGraphChange() {
@@ -931,9 +931,10 @@ void XYWnd::drawGrid()
     }
 }
 
-void XYWnd::drawBlockGrid() {
-    if (GlobalMap().findWorldspawn() == NULL) {
-        return;
+void XYWnd::drawBlockGrid()
+{
+    if (GlobalMap().getWorldspawn() == NULL) {
+        return; // no worldspawn yet
     }
     // Set a default blocksize of 1024
     int blockSize = GlobalXYWnd().defaultBlockSize();
@@ -1306,7 +1307,7 @@ void XYWnd::draw()
 
     {
         // Construct the renderer and render the scene
-        XYRenderer renderer(flagsMask, _selectedShader.get());
+        XYRenderer renderer(flagsMask, _selectedShader.get(), _selectedShaderGroup.get());
 
         // First pass (scenegraph traversal)
         render::RenderableCollectionWalker::collectRenderablesInScene(renderer,
@@ -1514,11 +1515,6 @@ void XYWnd::onIdle(wxIdleEvent& ev)
 	}
 }
 
-void XYWnd::performDeferredDraw()
-{
-    _wxGLWidget->Refresh(false);
-}
-
 void XYWnd::onGLResize(wxSizeEvent& ev)
 {
 	const wxSize clientSize = _wxGLWidget->GetClientSize();
@@ -1533,7 +1529,7 @@ void XYWnd::onGLResize(wxSizeEvent& ev)
 
 void XYWnd::onRender()
 {
-	if (GlobalMap().isValid() && GlobalMainFrame().screenUpdatesEnabled())
+	if (GlobalMainFrame().screenUpdatesEnabled())
 	{
         util::ScopedBoolLock drawLock(_drawing);
 
@@ -1659,5 +1655,6 @@ IInteractiveView& XYWnd::getInteractiveView()
 
 /* STATICS */
 ShaderPtr XYWnd::_selectedShader;
+ShaderPtr XYWnd::_selectedShaderGroup;
 
 } // namespace 
diff --git a/radiant/xyview/XYWnd.h b/radiant/xyview/XYWnd.h
index 57644f1..48f919b 100644
--- a/radiant/xyview/XYWnd.h
+++ b/radiant/xyview/XYWnd.h
@@ -13,7 +13,6 @@
 #include <wx/cursor.h>
 #include <wx/stopwatch.h>
 
-#include "map/DeferredDraw.h"
 #include "camera/CameraObserver.h"
 #include "render/View.h"
 #include "imousetool.h"
@@ -37,8 +36,6 @@ protected:
     wxutil::GLWidget* _wxGLWidget;
     bool _drawing;
 
-    DeferredDraw _deferredDraw;
-
     // The maximum/minimum values of a coordinate
     double _minWorldCoord;
     double _maxWorldCoord;
@@ -60,6 +57,7 @@ protected:
 
     // Shader to use for selected items
     static ShaderPtr _selectedShader;
+	static ShaderPtr _selectedShaderGroup;
 
     Vector3 _mousePosition;
 
@@ -189,9 +187,6 @@ private:
     // Active mousetools might capture the mouse, this is handled here
     void handleGLCapturedMouseMotion(const MouseToolPtr& tool, int x, int y, unsigned int state);
 
-    // Is called by the DeferredDraw helper
-    void performDeferredDraw();
-
     // wxGLWidget-attached render method
     void onRender();
     void onGLResize(wxSizeEvent& ev);
diff --git a/tools/i18n/darkradiant.pot b/tools/i18n/darkradiant.pot
index b55290c..9f99a86 100644
--- a/tools/i18n/darkradiant.pot
+++ b/tools/i18n/darkradiant.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: greebo at angua.at\n"
-"POT-Creation-Date: 2016-01-05 11:54+0100\n"
+"POT-Creation-Date: 2016-11-18 18:32+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -79,11 +79,11 @@ msgstr ""
 msgid "Solid selection boxes"
 msgstr ""
 
-#: ..\..\radiant\camera\CameraSettings.cpp:75
+#: ..\..\radiant\camera\CameraSettings.cpp:74
 msgid "Show camera toolbar"
 msgstr ""
 
-#: ..\..\radiant\camera\FloatingCamWnd.cpp:18 xml_file_content.cpp:60
+#: ..\..\radiant\camera\FloatingCamWnd.cpp:18 xml_file_content.cpp:61
 msgid "Camera"
 msgstr ""
 
@@ -95,27 +95,31 @@ msgstr ""
 msgid "Jump to Object"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:97
+#: ..\..\radiant\camera\tools\PanViewTool.h:23
+msgid "Pan Camera View"
+msgstr ""
+
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:116
 msgid "Pick Shader"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:120
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:139
 msgid "Paste Shader Projected"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:138
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:158
 msgid "Paste Shader Natural"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:156
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:177
 msgid "Paste Texture Coordinates"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:174
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:196
 msgid "Paste Shader to all Brush Faces"
 msgstr ""
 
-#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:192
+#: ..\..\radiant\camera\tools\ShaderClipboardTools.h:215
 msgid "Paste Shader Name"
 msgstr ""
 
@@ -131,40 +135,40 @@ msgstr ""
 msgid "Caulk shader name"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:34
+#: ..\..\radiant\layers\LayerSystem.cpp:36
 msgid "Default"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:37
+#: ..\..\radiant\layers\LayerSystem.cpp:39
 msgid "Create Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:39
+#: ..\..\radiant\layers\LayerSystem.cpp:41
 msgid "Add to Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:40
+#: ..\..\radiant\layers\LayerSystem.cpp:42
 msgid "Move to Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:41
+#: ..\..\radiant\layers\LayerSystem.cpp:43
 msgid "Remove from Layer..."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:536
+#: ..\..\radiant\layers\LayerSystem.cpp:550
 #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1537
 msgid "Enter Name"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:537
+#: ..\..\radiant\layers\LayerSystem.cpp:551
 msgid "Enter Layer Name"
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:549
+#: ..\..\radiant\layers\LayerSystem.cpp:563
 msgid "Cannot create layer with empty name."
 msgstr ""
 
-#: ..\..\radiant\layers\LayerSystem.cpp:563
+#: ..\..\radiant\layers\LayerSystem.cpp:577
 msgid "This name already exists."
 msgstr ""
 
@@ -195,33 +199,39 @@ msgstr ""
 msgid "Loading entity %d\n"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:299
+#: ..\..\radiant\map\AutoSaver.cpp:276
 msgid "Settings/Autosave"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:302
+#: ..\..\radiant\map\AutoSaver.cpp:279
 msgid "Enable Autosave"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:303
+#: ..\..\radiant\map\AutoSaver.cpp:280
 msgid "Autosave Interval (in minutes)"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:305
+#: ..\..\radiant\map\AutoSaver.cpp:282
 msgid "Save Snapshots"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:306
+#: ..\..\radiant\map\AutoSaver.cpp:283
 msgid "Snapshot folder (relative to map folder)"
 msgstr ""
 
-#: ..\..\radiant\map\AutoSaver.cpp:307
+#: ..\..\radiant\map\AutoSaver.cpp:284
 msgid "Max Snapshot Folder size (MB)"
 msgstr ""
 
-#: ..\..\radiant\map\CounterManager.cpp:67
+#: ..\..\radiant\map\CounterManager.cpp:63
+msgid ""
+"Number of brushes/patches/entities in this map\n"
+"(Number of selected items shown in parentheses)"
+msgstr ""
+
+#: ..\..\radiant\map\CounterManager.cpp:81
 #, c-format
-msgid "Brushes: %lu Patches: %lu Entities: %lu"
+msgid "Brushes: %lu (%lu) Patches: %lu (%lu) Entities: %lu (%lu)"
 msgstr ""
 
 #: ..\..\radiant\map\FindMapElements.cpp:95
@@ -236,92 +246,92 @@ msgstr ""
 msgid "Brush Number:"
 msgstr ""
 
-#: ..\..\radiant\map\InfoFile.cpp:73
+#: ..\..\radiant\map\infofile\InfoFile.cpp:70
 msgid "Map Info File Version invalid"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:59
+#: ..\..\radiant\map\Map.cpp:62
 msgid "unnamed.map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:182 ..\..\radiant\map\Map.cpp:518
-#: ..\..\radiant\map\Map.cpp:610 ..\..\radiant\map\Map.cpp:638
-#: ..\..\radiant\RadiantModule.cpp:134
+#: ..\..\radiant\map\Map.cpp:106 ..\..\radiant\map\Map.cpp:431
+#: ..\..\radiant\map\Map.cpp:522 ..\..\radiant\map\Map.cpp:550
+#: ..\..\radiant\RadiantModule.cpp:135
 #: ..\..\radiant\referencecache\ModelCache.cpp:245
 #: ..\..\radiant\referencecache\ModelCache.cpp:261
-#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:319
+#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:321
 msgid "Processing..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:182
+#: ..\..\radiant\map\Map.cpp:106
 msgid "Loading textures..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:518
+#: ..\..\radiant\map\Map.cpp:431
 msgid "Saving Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:569
+#: ..\..\radiant\map\Map.cpp:481
 msgid "Importing..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:664
+#: ..\..\radiant\map\Map.cpp:576
 #, c-format
 msgid ""
 "Save changes to map \"%s\"\n"
 "before closing?"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:672
+#: ..\..\radiant\map\Map.cpp:584
 #, c-format
 msgid "%d minutes"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:676
+#: ..\..\radiant\map\Map.cpp:588
 #, c-format
 msgid "%d seconds"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:680
+#: ..\..\radiant\map\Map.cpp:592
 #, c-format
 msgid ""
 "If you don't save, changes from the last %s\n"
 "will be lost."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:738
+#: ..\..\radiant\map\Map.cpp:650
 msgid "Save Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:778
+#: ..\..\radiant\map\Map.cpp:690
 msgid "Save Copy As..."
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:854
+#: ..\..\radiant\map\Map.cpp:781
 msgid "New Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:865 ..\..\radiant\ui\mru\MRU.cpp:93
+#: ..\..\radiant\map\Map.cpp:790 ..\..\radiant\ui\mru\MRU.cpp:93
 msgid "Open Map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:869
+#: ..\..\radiant\map\Map.cpp:794
 msgid "Open map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:883
+#: ..\..\radiant\map\Map.cpp:808
 msgid "Import map"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:912
+#: ..\..\radiant\map\Map.cpp:837
 msgid "Export selection"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:927
+#: ..\..\radiant\map\Map.cpp:852
 msgid "Save selected as Prefab"
 msgstr ""
 
-#: ..\..\radiant\map\Map.cpp:1008
+#: ..\..\radiant\map\Map.cpp:933
 #, c-format
 msgid ""
 "Failure reading map from clipboard:\n"
@@ -332,39 +342,32 @@ msgstr ""
 msgid "Map"
 msgstr ""
 
-#: ..\..\radiant\map\MapFileManager.cpp:36 xml_file_content.cpp:89
+#: ..\..\radiant\map\MapFileManager.cpp:36 xml_file_content.cpp:90
 msgid "Region"
 msgstr ""
 
 #: ..\..\radiant\map\MapFileManager.cpp:37
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:327
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:343
 msgid "Prefab"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:273 ..\..\radiant\map\MapResource.cpp:641
+#: ..\..\radiant\map\MapResource.cpp:257 ..\..\radiant\map\MapResource.cpp:529
 #, c-format
 msgid "File is write-protected: %s"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:434
-#, c-format
-msgid ""
-"Failure opening map file:\n"
-"%s"
-msgstr ""
-
-#: ..\..\radiant\map\MapResource.cpp:476
+#: ..\..\radiant\map\MapResource.cpp:363
 #, c-format
 msgid ""
 "Could not determine map format of file:\n"
 "%s"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:602
+#: ..\..\radiant\map\MapResource.cpp:410
 msgid "Map loading cancelled"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:614
+#: ..\..\radiant\map\MapResource.cpp:422
 #, c-format
 msgid ""
 "Failure reading map file:\n"
@@ -373,40 +376,47 @@ msgid ""
 "%s"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:706
+#: ..\..\radiant\map\MapResource.cpp:485 ..\..\radiant\map\MapResource.cpp:504
+#, c-format
+msgid ""
+"Failure opening file:\n"
+"%s"
+msgstr ""
+
+#: ..\..\radiant\map\MapResource.cpp:594
 msgid "Map writing cancelled"
 msgstr ""
 
-#: ..\..\radiant\map\MapResource.cpp:721
+#: ..\..\radiant\map\MapResource.cpp:609
 msgid "Could not open output streams for writing"
 msgstr ""
 
-#: ..\..\radiant\map\PointFile.cpp:116
+#: ..\..\radiant\map\PointFile.cpp:126
 #, c-format
 msgid "Could not open pointfile: %s"
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:203
+#: ..\..\radiant\map\RegionManager.cpp:173
 msgid "Warning: Camera not within region, can't set info_player_start."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:293
+#: ..\..\radiant\map\RegionManager.cpp:261
 msgid "Could not set Region: XY Top View not found."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:320
+#: ..\..\radiant\map\RegionManager.cpp:288
 msgid "Could not set Region: please select a single Brush."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:345
+#: ..\..\radiant\map\RegionManager.cpp:313
 msgid "This command is not available in component mode."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:351
+#: ..\..\radiant\map\RegionManager.cpp:319
 msgid "Could not set Region: nothing selected."
 msgstr ""
 
-#: ..\..\radiant\map\RegionManager.cpp:368
+#: ..\..\radiant\map\RegionManager.cpp:336
 msgid "Export region"
 msgstr ""
 
@@ -423,7 +433,7 @@ msgstr ""
 msgid "Initialising Modules"
 msgstr ""
 
-#: ..\..\radiant\modulesystem\ModuleRegistry.cpp:165
+#: ..\..\radiant\modulesystem\ModuleRegistry.cpp:162
 msgid "Modules initialised"
 msgstr ""
 
@@ -455,7 +465,7 @@ msgstr ""
 msgid "Cannot create cylinder-cap, patch must have a width of 9."
 msgstr ""
 
-#: ..\..\radiant\patch\Patch.cpp:1667
+#: ..\..\radiant\patch\Patch.cpp:1639
 msgid "Sorry. Patch is not suitable for this kind of operation."
 msgstr ""
 
@@ -471,11 +481,11 @@ msgstr ""
 #: ..\..\radiant\settings\GameManager.cpp:97
 #: ..\..\radiant\settings\GameManager.cpp:323
 #: ..\..\radiant\settings\GameManager.cpp:344
-#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:198
+#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:169
 msgid "Game"
 msgstr ""
 
-#: ..\..\radiant\RadiantModule.cpp:238
+#: ..\..\radiant\RadiantModule.cpp:239
 msgid "Settings"
 msgstr ""
 
@@ -508,41 +518,82 @@ msgstr ""
 msgid "Can't convert curves - no entities with curves selected."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:62
+#: ..\..\radiant\selection\algorithm\Entity.cpp:82
+#, c-format
+msgid "The name %s already exists in this map!"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:106
+msgid "Cannot set classname to an empty string."
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:112
+msgid "Cannot change classname to worldspawn."
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Entity.cpp:131
 msgid "Cannot change classname of worldspawn entity."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:104
+#: ..\..\radiant\selection\algorithm\Entity.cpp:169
 msgid "Critical: Cannot find selected entities."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:109
-#: ..\..\radiant\selection\algorithm\Entity.cpp:125
+#: ..\..\radiant\selection\algorithm\Entity.cpp:174
+#: ..\..\radiant\selection\algorithm\Entity.cpp:190
 msgid "Exactly two entities must be selected for this operation."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Entity.cpp:171
+#: ..\..\radiant\selection\algorithm\Entity.cpp:236
 #, c-format
 msgid "Unable to create entity %s, no brushes selected."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Group.cpp:236
+#: ..\..\radiant\selection\algorithm\Group.cpp:234
 msgid ""
 "Cannot reparent primitives to entity. Please select at least one brush/patch "
 "and exactly one entity.(The entity has to be selected last.)"
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Group.cpp:386
+#: ..\..\radiant\selection\algorithm\Group.cpp:384
 msgid ""
 "Cannot merge entities, the selection must consist of func_* entities only.\n"
 "(The first selected entity will be preserved.)"
 msgstr ""
 
+#: ..\..\radiant\selection\algorithm\Group.cpp:394
+msgid "Groups can be formed in Primitive selection mode only"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:399
+msgid "Nothing selected, cannot group anything"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:404
+msgid "Select more than one element to form a group"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:430
+msgid "The selected elements already form a group"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:455
+msgid "Groups can be dissolved in Primitive selection mode only"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:460
+msgid "Nothing selected, cannot un-group anything"
+msgstr ""
+
+#: ..\..\radiant\selection\algorithm\Group.cpp:480
+msgid "The selected elements aren't part of any group"
+msgstr ""
+
 #: ..\..\radiant\selection\algorithm\Patch.cpp:60
 msgid "Cannot create caps, no patches selected."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Patch.cpp:208
+#: ..\..\radiant\selection\algorithm\Patch.cpp:197
 msgid "Cannot thicken patch. Nothing selected."
 msgstr ""
 
@@ -573,42 +624,52 @@ msgstr ""
 msgid "At least one brush must be selected for this operation."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:285
+#: ..\..\radiant\selection\algorithm\Shader.cpp:307
 msgid ""
 "Can't paste shader to entire brush.\n"
 "Target is not a brush."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:320
+#: ..\..\radiant\selection\algorithm\Shader.cpp:346
 msgid ""
 "Can't paste Texture Coordinates.\n"
 "Target patch dimensions must match."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:327
+#: ..\..\radiant\selection\algorithm\Shader.cpp:355
 msgid "Can't paste Texture Coordinates from patches to faces."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:332
+#: ..\..\radiant\selection\algorithm\Shader.cpp:361
 msgid "Can't paste Texture Coordinates from faces."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:388
+#: ..\..\radiant\selection\algorithm\Shader.cpp:417
 msgid "Can't copy Shader. Couldn't retrieve patch."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:398
+#: ..\..\radiant\selection\algorithm\Shader.cpp:427
 msgid "Can't copy Shader. Couldn't retrieve face."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Shader.cpp:404
+#: ..\..\radiant\selection\algorithm\Shader.cpp:433
 msgid "Can't copy Shader. Please select a single face or patch."
 msgstr ""
 
-#: ..\..\radiant\selection\algorithm\Transformation.cpp:55
+#: ..\..\radiant\selection\algorithm\Transformation.cpp:56
 msgid "Cannot scale by zero value."
 msgstr ""
 
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:81
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:97
+msgid "Ungroup Selection"
+msgstr ""
+
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:84
+#: ..\..\radiant\selection\group\SelectionGroupManager.cpp:91
+msgid "Group Selection"
+msgstr ""
+
 #: ..\..\radiant\selection\ManipulateMouseTool.cpp:28
 msgid "Manipulate"
 msgstr ""
@@ -617,31 +678,31 @@ msgstr ""
 msgid "Select"
 msgstr ""
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:168 xml_file_content.cpp:4
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:175 xml_file_content.cpp:4
 msgid "Select Faces"
 msgstr ""
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:187
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:194
 msgid "Cycle Selection"
 msgstr ""
 
-#: ..\..\radiant\selection\SelectionMouseTools.cpp:233
+#: ..\..\radiant\selection\SelectionMouseTools.cpp:247
 msgid "Cycle Face Selection"
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:84
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:104
 msgid "Selection Set: "
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:92
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:112
 msgid "Clear Selection Sets"
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:194
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:214
 msgid "Delete all selection sets?"
 msgstr ""
 
-#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:195
+#: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:215
 msgid ""
 "This will delete all set definitions. The actual map objects will not be "
 "affected by this step.\n"
@@ -669,20 +730,24 @@ msgid ""
 "scene."
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:87
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:24
+msgid "The name of the shader in the clipboard"
+msgstr ""
+
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:91
 #, c-format
 msgid "ShaderClipboard: %s"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:90
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:94
 msgid "Face"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:93
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:97
 msgid "Patch"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:96
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:100
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:359
 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:73
 #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:60
@@ -690,7 +755,7 @@ msgstr ""
 msgid "Shader"
 msgstr ""
 
-#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:100
+#: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:104
 msgid "ShaderClipboard is empty."
 msgstr ""
 
@@ -1487,10 +1552,35 @@ msgstr ""
 msgid "Zulu"
 msgstr ""
 
+#: ..\..\radiant\settings\PreferencePage.cpp:19
+#, c-format
+msgid "%s Settings"
+msgstr ""
+
 #: ..\..\radiant\textool\TexTool.cpp:38
 msgid "Texture Tool"
 msgstr ""
 
+#: ..\..\radiant\ui\aas\AasControl.cpp:36
+msgid "Reload AAS File"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:30
+msgid "AAS Viewer"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:80
+msgid "Search for AAS Files"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:84
+msgid "Show Area Numbers"
+msgstr ""
+
+#: ..\..\radiant\ui\aas\AasControlDialog.cpp:87
+msgid "Hide distant Areas"
+msgstr ""
+
 #: ..\..\radiant\ui\about\AboutDialog.cpp:24
 msgid "About DarkRadiant"
 msgstr ""
@@ -1521,7 +1611,7 @@ msgid "Renderer: %s"
 msgstr ""
 
 #: ..\..\radiant\ui\animationpreview\MD5AnimationViewer.cpp:16
-#: xml_file_content.cpp:139
+#: xml_file_content.cpp:140
 msgid "MD5 Animation Viewer"
 msgstr ""
 
@@ -1556,7 +1646,7 @@ msgid "Shortcut List"
 msgstr ""
 
 #: ..\..\radiant\ui\commandlist\CommandList.cpp:53
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:94
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:116
 #: xml_file_content.cpp:2
 msgid "Command"
 msgstr ""
@@ -1630,7 +1720,7 @@ msgstr ""
 
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:195
 #: ..\..\radiant\ui\common\ShaderSelector.cpp:225
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:437
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:481
 #: ..\..\plugins\eclasstree\EClassTree.cpp:143
 #: ..\..\libs\wxutil\KeyValueTable.cpp:44
 msgid "Value"
@@ -1678,7 +1768,7 @@ msgstr ""
 #: ..\..\radiant\ui\common\SoundChooser.cpp:189
 #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:290
 #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:340
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:380
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:396
 msgid "Loading..."
 msgstr ""
 
@@ -1711,12 +1801,12 @@ msgid "Custom properties defined for this entity class, if any"
 msgstr ""
 
 #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:76
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:432
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:476
 #: ..\..\plugins\eclasstree\EClassTree.cpp:140
 msgid "Property"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:25
+#: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:26
 msgid "Choose entity class..."
 msgstr ""
 
@@ -1728,41 +1818,42 @@ msgstr ""
 msgid "Show help"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:292
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:312
 msgid "Add property..."
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:296
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:317
 msgid "Delete property"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:304
-msgid "Copy Spawnarg"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:325
+msgid "Copy Spawnarg(s)"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:309
-msgid "Cut Spawnarg"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:330
+msgid "Cut Spawnarg(s)"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:314
-msgid "Paste Spawnarg"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:335
+msgid "Paste Spawnarg(s)"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:442
-msgid "?"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:347
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:350
+#: ..\..\plugins\uimanager\GroupDialog.cpp:28
+msgid "Entity"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:654
-#, c-format
-msgid "The name %s already exists in this map!"
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:486
+msgid "?"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1064
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1200
 #, c-format
 msgid "Entity %d"
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1076
+#: ..\..\radiant\ui\einspector\EntityInspector.cpp:1212
 #, c-format
 msgid "Entity %d, Primitive %d"
 msgstr ""
@@ -1771,7 +1862,7 @@ msgstr ""
 msgid "Choose target entity..."
 msgstr ""
 
-#: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:78
+#: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:71
 #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:62
 msgid "Apply..."
 msgstr ""
@@ -2033,59 +2124,53 @@ msgstr ""
 msgid "Window Layout"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:93
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:84
 msgid "Settings/Multi Monitor"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:112
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:103
 msgid "Start DarkRadiant on monitor"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:131
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:122
 msgid "Settings/Compatibility"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:133
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:124
 msgid "Disable Windows Desktop Composition"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:267
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:256
 msgid "Exit DarkRadiant"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:380
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:383
-#: ..\..\plugins\uimanager\GroupDialog.cpp:28
-msgid "Entity"
-msgstr ""
-
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:391
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:394
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:369
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:372
 msgid "Media"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:402
-#: ..\..\radiant\ui\mainframe\MainFrame.cpp:405
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:381
+#: ..\..\radiant\ui\mainframe\MainFrame.cpp:384
 msgid "Console"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:144
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:145
 msgid "Camera Position"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:147
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:148
 msgid "Top Left"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:149
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:150
 msgid "Top Right"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:151
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:152
 msgid "Bottom Left"
 msgstr ""
 
-#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:153
+#: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:154
 msgid "Bottom Right"
 msgstr ""
 
@@ -2381,7 +2466,7 @@ msgstr ""
 msgid "Merge Entities"
 msgstr ""
 
-#: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:74 xml_file_content.cpp:155
+#: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:74 xml_file_content.cpp:156
 msgid "Make Visportal"
 msgstr ""
 
@@ -2455,7 +2540,7 @@ msgid "Patch Inspector"
 msgstr ""
 
 #: ..\..\radiant\ui\patch\PatchInspector.cpp:29
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:55
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:56
 #: ..\..\radiant\ui\transform\TransformDialog.cpp:38
 msgid "Step:"
 msgstr ""
@@ -2464,135 +2549,139 @@ msgstr ""
 msgid "Patch Thicken"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:41
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:42
 msgid "Choose Prefab"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:97
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:100
 msgid "Rescan Prefab Folders"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:128
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:105
+msgid "Create Group out of Prefab parts"
+msgstr ""
+
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:140
 msgid "Browse mod resources"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:131
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:143
 msgid "Select recently used path:"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:133
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:145
 msgid "Browse custom path:"
 msgstr ""
 
-#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:532
+#: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:553
 msgid "<no description>"
 msgstr ""
 
-#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:37
+#: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:23
 msgid "DarkRadiant Preferences"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:38
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:39
 msgid "Surface Inspector"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:39
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:40
 msgid "Texture Properties"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:40
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:41
 msgid "Texture Operations"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:48
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:49
 msgid "Horiz. Shift:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:49
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:50
 msgid "Vert. Shift:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:50
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:51
 msgid "Horiz. Scale:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:51
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52
 msgid "Vert. Scale:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53
 msgid "Rotation:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:54
 #: xml_file_content.cpp:15
 msgid "Shader:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:57
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:58
 msgid "Fit Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:58
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:59
 msgid "Fit"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:60
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61
 msgid "Align Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62
 msgid "Top"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:63
 msgid "Bottom"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:63
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64
 #: xml_file_content.cpp:16
 msgid "Right"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:65
 #: xml_file_content.cpp:15
 msgid "Left"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:66
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67
 msgid "Flip Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68
 msgid "Flip Horizontal"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:69
 msgid "Flip Vertical"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:70
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71
 msgid "Modify Texture:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72
 msgid "Natural"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:73
 msgid "Normalise"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:74
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75
 msgid "Default Scale:"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:76
 #: xml_file_content.cpp:9
 msgid "Texture Lock"
 msgstr ""
 
-#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:634
+#: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:649
 msgid "Both fit values must be > 0."
 msgstr ""
 
@@ -2601,7 +2690,7 @@ msgid "Seek in Media Browser"
 msgstr ""
 
 #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:269
-#: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:811
+#: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:809
 msgid "No shader"
 msgstr ""
 
@@ -2701,7 +2790,7 @@ msgstr ""
 msgid "Show Entity Names"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:205 xml_file_content.cpp:79
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:205 xml_file_content.cpp:80
 msgid "Show Blocks"
 msgstr ""
 
@@ -2709,15 +2798,15 @@ msgstr ""
 msgid "Show Coordinates"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:207 xml_file_content.cpp:82
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:207 xml_file_content.cpp:83
 msgid "Show Axes"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:208 xml_file_content.cpp:81
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:208 xml_file_content.cpp:82
 msgid "Show Window Outline"
 msgstr ""
 
-#: ..\..\radiant\xyview\GlobalXYWnd.cpp:209 xml_file_content.cpp:83
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:209 xml_file_content.cpp:84
 msgid "Show Workzone"
 msgstr ""
 
@@ -2729,6 +2818,10 @@ msgstr ""
 msgid "Higher Selection Priority for Entities"
 msgstr ""
 
+#: ..\..\radiant\xyview\GlobalXYWnd.cpp:665
+msgid "Shows the mouse position in the orthoview"
+msgstr ""
+
 #: ..\..\radiant\xyview\tools\BrushCreatorTool.cpp:25
 msgid "Drag-create Brush"
 msgstr ""
@@ -2741,7 +2834,7 @@ msgstr ""
 msgid "Drag Camera"
 msgstr ""
 
-#: ..\..\radiant\xyview\tools\ClipperTool.cpp:21 xml_file_content.cpp:149
+#: ..\..\radiant\xyview\tools\ClipperTool.cpp:21 xml_file_content.cpp:150
 #: xml_file_content.cpp:12 xml_file_content.cpp:17
 msgid "Clipper"
 msgstr ""
@@ -2766,7 +2859,7 @@ msgstr ""
 msgid "YZ Side"
 msgstr ""
 
-#: ..\..\radiant\xyview\XYWnd.cpp:637
+#: ..\..\radiant\xyview\XYWnd.cpp:498
 #, c-format
 msgid "x: %6.1lf y: %6.1lf z: %6.1lf"
 msgstr ""
@@ -2785,7 +2878,7 @@ msgstr ""
 msgid "Conversation Editor"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationDialog.cpp:278
+#: ..\..\plugins\dm.conversation\ConversationDialog.cpp:280
 #, c-format
 msgid "Unable to create conversation Entity: class '%s' not found."
 msgstr ""
@@ -2794,35 +2887,35 @@ msgstr ""
 msgid "Edit Conversation"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:67
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:89
 msgid "Actor (click to edit)"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:92
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:114
 #: xml_file_content.cpp:1
 msgid "Actor"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:96
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:118
 msgid "Wait"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:201
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:223
 #, c-format
 msgid "Actor %d"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:203
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:225
 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206
 msgid "yes"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:203
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:225
 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206
 msgid "no"
 msgstr ""
 
-#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:369
+#: ..\..\plugins\dm.conversation\ConversationEditor.cpp:394
 msgid "New Actor"
 msgstr ""
 
@@ -3941,7 +4034,7 @@ msgstr ""
 #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:412
 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:403
 #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:95
-#: xml_file_content.cpp:179 xml_file_content.cpp:3 xml_file_content.cpp:7
+#: xml_file_content.cpp:180 xml_file_content.cpp:3 xml_file_content.cpp:7
 #: xml_file_content.cpp:9 xml_file_content.cpp:15 xml_file_content.cpp:2
 #: xml_file_content.cpp:5 xml_file_content.cpp:6 xml_file_content.cpp:18
 msgid "Delete"
@@ -4069,7 +4162,8 @@ msgstr ""
 msgid "Custom Stims"
 msgstr ""
 
-#: ..\..\plugins\eclassmgr\EClassManager.cpp:330
+#: ..\..\plugins\eclassmgr\EClassManager.cpp:327
+#: ..\..\plugins\eclassmgr\EClassManager.cpp:328
 msgid "Reloading Defs"
 msgstr ""
 
@@ -4113,47 +4207,51 @@ msgstr ""
 msgid "All Files"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:151
+#: ..\..\plugins\grid\Grid.cpp:55
+msgid "Current Grid Size"
+msgstr ""
+
+#: ..\..\plugins\grid\Grid.cpp:152
 msgid "Settings/Grid"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:153
+#: ..\..\plugins\grid\Grid.cpp:154
 msgid "Default Grid Size"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:157
+#: ..\..\plugins\grid\Grid.cpp:158
 msgid "Lines"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:158
+#: ..\..\plugins\grid\Grid.cpp:159
 msgid "Dotted Lines"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:159
+#: ..\..\plugins\grid\Grid.cpp:160
 msgid "More Dotted Lines"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:160
+#: ..\..\plugins\grid\Grid.cpp:161
 msgid "Crosses"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:161
+#: ..\..\plugins\grid\Grid.cpp:162
 msgid "Dots"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:162
+#: ..\..\plugins\grid\Grid.cpp:163
 msgid "Big Dots"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:163
+#: ..\..\plugins\grid\Grid.cpp:164
 msgid "Squares"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:165
+#: ..\..\plugins\grid\Grid.cpp:166
 msgid "Major Grid Style"
 msgstr ""
 
-#: ..\..\plugins\grid\Grid.cpp:166
+#: ..\..\plugins\grid\Grid.cpp:167
 msgid "Minor Grid Style"
 msgstr ""
 
@@ -4182,24 +4280,25 @@ msgid "Incorrect map version: required %f, found %f"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:144
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:94
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:102
 #, c-format
 msgid "Primitive #%d: parse error"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:154
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:104
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:112
 #, c-format
 msgid "Primitive #%d: parse exception %s"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:245
-#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:195
+#: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:203
 #, c-format
 msgid "Parsed invalid value '%s' for key '%s'"
 msgstr ""
 
 #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:125
+#: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:234
 #, c-format
 msgid "BrushDefParser: invalid token '%s'"
 msgstr ""
@@ -4269,23 +4368,23 @@ msgstr ""
 msgid "Cannot save particle, it has not been registered yet."
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:316
-#: ..\..\plugins\particles\ParticlesManager.cpp:344
+#: ..\..\plugins\particles\ParticlesManager.cpp:325
+#: ..\..\plugins\particles\ParticlesManager.cpp:353
 #, c-format
 msgid "Cannot open file for writing: %s"
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:357
+#: ..\..\plugins\particles\ParticlesManager.cpp:366
 #, c-format
 msgid "Cannot open file for reading: %s"
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:404
+#: ..\..\plugins\particles\ParticlesManager.cpp:413
 #, c-format
 msgid "Could not remove the file %s"
 msgstr ""
 
-#: ..\..\plugins\particles\ParticlesManager.cpp:418
+#: ..\..\plugins\particles\ParticlesManager.cpp:427
 #, c-format
 msgid "Could not rename the temporary file %s"
 msgstr ""
@@ -4299,7 +4398,7 @@ msgstr ""
 msgid "Reload Scripts"
 msgstr ""
 
-#: ..\..\plugins\script\ScriptMenu.cpp:51
+#: ..\..\plugins\script\ScriptMenu.cpp:60
 msgid "No scripts available"
 msgstr ""
 
@@ -4311,7 +4410,7 @@ msgstr ""
 msgid "Run Script"
 msgstr ""
 
-#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:319
+#: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:321
 msgid "Loading Shaders"
 msgstr ""
 
@@ -4350,20 +4449,16 @@ msgstr ""
 msgid "_Filters"
 msgstr ""
 
-#: ..\..\plugins\undo\UndoSystem.cpp:384
-msgid "Settings/Undo System"
+#: ..\..\plugins\uimanager\UIManager.cpp:115
+msgid "Describes available Mouse Commands"
 msgstr ""
 
-#: ..\..\plugins\undo\UndoSystem.cpp:385
-msgid "Undo Queue Size"
-msgstr ""
-
-#: ..\..\plugins\wavefront\WaveFrontModule.cpp:31
-msgid "Save as Obj"
+#: ..\..\plugins\undo\UndoSystem.cpp:398
+msgid "Settings/Undo System"
 msgstr ""
 
-#: ..\..\plugins\wavefront\WaveFrontModule.cpp:89
-msgid "Export Selection as OBJ..."
+#: ..\..\plugins\undo\UndoSystem.cpp:399
+msgid "Undo Queue Size"
 msgstr ""
 
 #: ..\..\libs\wxutil\dialog\MessageBox.cpp:20 xml_file_content.cpp:8
@@ -4502,7 +4597,7 @@ msgstr ""
 msgid "Reload S&kins"
 msgstr ""
 
-#: xml_file_content.cpp:16 xml_file_content.cpp:26
+#: xml_file_content.cpp:16 xml_file_content.cpp:28
 msgid "Reload Materials"
 msgstr ""
 
@@ -4662,496 +4757,496 @@ msgstr ""
 msgid "&Texture Tool"
 msgstr ""
 
-#: xml_file_content.cpp:61
-msgid "&Center"
+#: xml_file_content.cpp:60
+msgid "AAS Area Viewer"
 msgstr ""
 
 #: xml_file_content.cpp:62
-msgid "&Up Floor"
+msgid "&Center"
 msgstr ""
 
 #: xml_file_content.cpp:63
-msgid "&Down Floor"
+msgid "&Up Floor"
 msgstr ""
 
 #: xml_file_content.cpp:64
-msgid "Far Clip Plane In"
+msgid "&Down Floor"
 msgstr ""
 
 #: xml_file_content.cpp:65
-msgid "Far Clip Plane Out"
+msgid "Far Clip Plane In"
 msgstr ""
 
 #: xml_file_content.cpp:66
-msgid "Next leak spot"
+msgid "Far Clip Plane Out"
 msgstr ""
 
 #: xml_file_content.cpp:67
-msgid "Previous leak spot"
+msgid "Next leak spot"
 msgstr ""
 
 #: xml_file_content.cpp:68
-msgid "Orthographic"
+msgid "Previous leak spot"
 msgstr ""
 
 #: xml_file_content.cpp:69
-msgid "Next (XY, XZ, YZ)"
+msgid "Orthographic"
 msgstr ""
 
 #: xml_file_content.cpp:70
-msgid "XY (Top)"
+msgid "Next (XY, XZ, YZ)"
 msgstr ""
 
 #: xml_file_content.cpp:71
-msgid "YZ"
+msgid "XY (Top)"
 msgstr ""
 
 #: xml_file_content.cpp:72
-msgid "XZ"
+msgid "YZ"
 msgstr ""
 
 #: xml_file_content.cpp:73
-msgid "&XY 100%"
+msgid "XZ"
 msgstr ""
 
 #: xml_file_content.cpp:74
-msgid "&XY Zoom In"
+msgid "&XY 100%"
 msgstr ""
 
 #: xml_file_content.cpp:75
-msgid "&XY Zoom Out"
+msgid "&XY Zoom In"
 msgstr ""
 
 #: xml_file_content.cpp:76
-msgid "Show"
+msgid "&XY Zoom Out"
 msgstr ""
 
 #: xml_file_content.cpp:77
-msgid "Show &Angles"
+msgid "Show"
 msgstr ""
 
 #: xml_file_content.cpp:78
+msgid "Show &Angles"
+msgstr ""
+
+#: xml_file_content.cpp:79
 msgid "Show &Names"
 msgstr ""
 
-#: xml_file_content.cpp:80
+#: xml_file_content.cpp:81
 msgid "Show C&oordinates"
 msgstr ""
 
-#: xml_file_content.cpp:84
+#: xml_file_content.cpp:85
 msgid "Show size info"
 msgstr ""
 
-#: xml_file_content.cpp:85
+#: xml_file_content.cpp:86
 msgid "Hide/Show"
 msgstr ""
 
-#: xml_file_content.cpp:86
+#: xml_file_content.cpp:87
 msgid "Hide Selected"
 msgstr ""
 
-#: xml_file_content.cpp:87
+#: xml_file_content.cpp:88
 msgid "Hide Deselected"
 msgstr ""
 
-#: xml_file_content.cpp:88
+#: xml_file_content.cpp:89
 msgid "Show hidden"
 msgstr ""
 
-#: xml_file_content.cpp:90
+#: xml_file_content.cpp:91
 msgid "&Switch off"
 msgstr ""
 
-#: xml_file_content.cpp:91
+#: xml_file_content.cpp:92
 msgid "Set from &XY view"
 msgstr ""
 
-#: xml_file_content.cpp:92
+#: xml_file_content.cpp:93
 msgid "Set from &Brush"
 msgstr ""
 
-#: xml_file_content.cpp:93
+#: xml_file_content.cpp:94
 msgid "Set from Se&lection"
 msgstr ""
 
-#: xml_file_content.cpp:94
+#: xml_file_content.cpp:95
 msgid "Colours..."
 msgstr ""
 
-#: xml_file_content.cpp:95
+#: xml_file_content.cpp:96
 msgid "Background Image..."
 msgstr ""
 
-#: xml_file_content.cpp:96
+#: xml_file_content.cpp:97
 msgid "Mo&dify"
 msgstr ""
 
-#: xml_file_content.cpp:97 xml_file_content.cpp:16
+#: xml_file_content.cpp:98 xml_file_content.cpp:16
 msgid "Components"
 msgstr ""
 
-#: xml_file_content.cpp:98
+#: xml_file_content.cpp:99
 msgid "&Edges"
 msgstr ""
 
-#: xml_file_content.cpp:99
+#: xml_file_content.cpp:100
 msgid "&Vertices"
 msgstr ""
 
-#: xml_file_content.cpp:100
+#: xml_file_content.cpp:101
 msgid "&Faces"
 msgstr ""
 
-#: xml_file_content.cpp:101
+#: xml_file_content.cpp:102
 msgid "En&tities"
 msgstr ""
 
-#: xml_file_content.cpp:102
+#: xml_file_content.cpp:103
 msgid "Nudge"
 msgstr ""
 
-#: xml_file_content.cpp:103
+#: xml_file_content.cpp:104
 msgid "Nudge Left"
 msgstr ""
 
-#: xml_file_content.cpp:104
+#: xml_file_content.cpp:105
 msgid "Nudge Right"
 msgstr ""
 
-#: xml_file_content.cpp:105
+#: xml_file_content.cpp:106
 msgid "Nudge Up"
 msgstr ""
 
-#: xml_file_content.cpp:106
+#: xml_file_content.cpp:107
 msgid "Nudge Down"
 msgstr ""
 
-#: xml_file_content.cpp:107 xml_file_content.cpp:15
+#: xml_file_content.cpp:108 xml_file_content.cpp:15
 msgid "Rotate"
 msgstr ""
 
-#: xml_file_content.cpp:108
+#: xml_file_content.cpp:109
 msgid "Rotate X"
 msgstr ""
 
-#: xml_file_content.cpp:109
+#: xml_file_content.cpp:110
 msgid "Rotate Y"
 msgstr ""
 
-#: xml_file_content.cpp:110
+#: xml_file_content.cpp:111
 msgid "Rotate Z"
 msgstr ""
 
-#: xml_file_content.cpp:111
+#: xml_file_content.cpp:112
 msgid "Mirror"
 msgstr ""
 
-#: xml_file_content.cpp:112
+#: xml_file_content.cpp:113
 msgid "Mirror &X"
 msgstr ""
 
-#: xml_file_content.cpp:113
+#: xml_file_content.cpp:114
 msgid "Mirror &Y"
 msgstr ""
 
-#: xml_file_content.cpp:114
+#: xml_file_content.cpp:115
 msgid "Mirror &Z"
 msgstr ""
 
-#: xml_file_content.cpp:115 xml_file_content.cpp:10
+#: xml_file_content.cpp:116 xml_file_content.cpp:10
 msgid "Rotate Objects independently"
 msgstr ""
 
-#: xml_file_content.cpp:116
+#: xml_file_content.cpp:117
 msgid "Rotate and scale..."
 msgstr ""
 
-#: xml_file_content.cpp:117
+#: xml_file_content.cpp:118
 msgid "&Grid"
 msgstr ""
 
-#: xml_file_content.cpp:118
+#: xml_file_content.cpp:119
 msgid "Snap selected to grid"
 msgstr ""
 
-#: xml_file_content.cpp:119
+#: xml_file_content.cpp:120
 msgid "Grid0.125"
 msgstr ""
 
-#: xml_file_content.cpp:120
+#: xml_file_content.cpp:121
 msgid "Grid0.25"
 msgstr ""
 
-#: xml_file_content.cpp:121
+#: xml_file_content.cpp:122
 msgid "Grid0.5"
 msgstr ""
 
-#: xml_file_content.cpp:122
+#: xml_file_content.cpp:123
 msgid "Grid1"
 msgstr ""
 
-#: xml_file_content.cpp:123
+#: xml_file_content.cpp:124
 msgid "Grid2"
 msgstr ""
 
-#: xml_file_content.cpp:124
+#: xml_file_content.cpp:125
 msgid "Grid4"
 msgstr ""
 
-#: xml_file_content.cpp:125
+#: xml_file_content.cpp:126
 msgid "Grid8"
 msgstr ""
 
-#: xml_file_content.cpp:126
+#: xml_file_content.cpp:127
 msgid "Grid16"
 msgstr ""
 
-#: xml_file_content.cpp:127
+#: xml_file_content.cpp:128
 msgid "Grid32"
 msgstr ""
 
-#: xml_file_content.cpp:128
+#: xml_file_content.cpp:129
 msgid "Grid64"
 msgstr ""
 
-#: xml_file_content.cpp:129
+#: xml_file_content.cpp:130
 msgid "Grid128"
 msgstr ""
 
-#: xml_file_content.cpp:130
+#: xml_file_content.cpp:131
 msgid "Grid256"
 msgstr ""
 
-#: xml_file_content.cpp:131
+#: xml_file_content.cpp:132
 msgid "M&ap"
 msgstr ""
 
-#: xml_file_content.cpp:132
+#: xml_file_content.cpp:133
 msgid "Find brush..."
 msgstr ""
 
-#: xml_file_content.cpp:133
+#: xml_file_content.cpp:134
 msgid "Find and replace textures..."
 msgstr ""
 
-#: xml_file_content.cpp:134
+#: xml_file_content.cpp:135
 msgid "Map info..."
 msgstr ""
 
-#: xml_file_content.cpp:135
+#: xml_file_content.cpp:136
 msgid "E&ntity"
 msgstr ""
 
-#: xml_file_content.cpp:136
+#: xml_file_content.cpp:137
 msgid "&Revert group to worldspawn"
 msgstr ""
 
-#: xml_file_content.cpp:137
+#: xml_file_content.cpp:138
 msgid "&Connect selected entities"
 msgstr ""
 
-#: xml_file_content.cpp:138
+#: xml_file_content.cpp:139
 msgid "&Bind selected entities"
 msgstr ""
 
-#: xml_file_content.cpp:140
+#: xml_file_content.cpp:141
 msgid "B&rush"
 msgstr ""
 
-#: xml_file_content.cpp:141
+#: xml_file_content.cpp:142
 msgid "Prism..."
 msgstr ""
 
-#: xml_file_content.cpp:142
+#: xml_file_content.cpp:143
 msgid "Cone..."
 msgstr ""
 
-#: xml_file_content.cpp:143
+#: xml_file_content.cpp:144
 msgid "Sphere..."
 msgstr ""
 
-#: xml_file_content.cpp:144
+#: xml_file_content.cpp:145
 msgid "CSG"
 msgstr ""
 
-#: xml_file_content.cpp:145
+#: xml_file_content.cpp:146
 msgid "Make &Hollow"
 msgstr ""
 
-#: xml_file_content.cpp:146
+#: xml_file_content.cpp:147
 msgid "Make &Room"
 msgstr ""
 
-#: xml_file_content.cpp:147
+#: xml_file_content.cpp:148
 msgid "CSG &Subtract"
 msgstr ""
 
-#: xml_file_content.cpp:148
+#: xml_file_content.cpp:149
 msgid "CSG &Merge"
 msgstr ""
 
-#: xml_file_content.cpp:150
+#: xml_file_content.cpp:151
 msgid "Clip Selection"
 msgstr ""
 
-#: xml_file_content.cpp:151
+#: xml_file_content.cpp:152
 msgid "Split Selection"
 msgstr ""
 
-#: xml_file_content.cpp:152
+#: xml_file_content.cpp:153
 msgid "Flip Clip Orientation"
 msgstr ""
 
-#: xml_file_content.cpp:153
+#: xml_file_content.cpp:154
 msgid "Texture lock"
 msgstr ""
 
-#: xml_file_content.cpp:154
+#: xml_file_content.cpp:155
 msgid "Create Decal Patches"
 msgstr ""
 
-#: xml_file_content.cpp:156
+#: xml_file_content.cpp:157
 msgid "Make Detail"
 msgstr ""
 
-#: xml_file_content.cpp:157
+#: xml_file_content.cpp:158
 msgid "Make Structural"
 msgstr ""
 
-#: xml_file_content.cpp:158
+#: xml_file_content.cpp:159
 msgid "&Patch"
 msgstr ""
 
-#: xml_file_content.cpp:159
+#: xml_file_content.cpp:160
 msgid "Create Simple Patch Mesh"
 msgstr ""
 
-#: xml_file_content.cpp:160
+#: xml_file_content.cpp:161
 msgid "Create End cap"
 msgstr ""
 
-#: xml_file_content.cpp:161
+#: xml_file_content.cpp:162
 msgid "Create Bevel"
 msgstr ""
 
-#: xml_file_content.cpp:162
+#: xml_file_content.cpp:163
 msgid "Create Cone"
 msgstr ""
 
-#: xml_file_content.cpp:163
+#: xml_file_content.cpp:164
 msgid "Create Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:164
+#: xml_file_content.cpp:165
 msgid "Create Sphere"
 msgstr ""
 
-#: xml_file_content.cpp:165
+#: xml_file_content.cpp:166
 msgid "More cylinders"
 msgstr ""
 
-#: xml_file_content.cpp:166
+#: xml_file_content.cpp:167
 msgid "Create Dense Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:167
+#: xml_file_content.cpp:168
 msgid "Create Very Dense Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:168
+#: xml_file_content.cpp:169
 msgid "Create Square Cylinder"
 msgstr ""
 
-#: xml_file_content.cpp:169
+#: xml_file_content.cpp:170
 msgid "Insert"
 msgstr ""
 
-#: xml_file_content.cpp:170
+#: xml_file_content.cpp:171
 msgid "Insert 2 Columns at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:171
+#: xml_file_content.cpp:172
 msgid "Insert 2 Columns at the end"
 msgstr ""
 
-#: xml_file_content.cpp:172
+#: xml_file_content.cpp:173
 msgid "Insert 2 Rows at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:173
+#: xml_file_content.cpp:174
 msgid "Insert 2 Rows at the end"
 msgstr ""
 
-#: xml_file_content.cpp:174
+#: xml_file_content.cpp:175
 msgid "Append"
 msgstr ""
 
-#: xml_file_content.cpp:175
+#: xml_file_content.cpp:176
 msgid "Append 2 columns at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:176
+#: xml_file_content.cpp:177
 msgid "Append 2 columns at the end"
 msgstr ""
 
-#: xml_file_content.cpp:177
+#: xml_file_content.cpp:178
 msgid "Append 2 rows at the beginning"
 msgstr ""
 
-#: xml_file_content.cpp:178
+#: xml_file_content.cpp:179
 msgid "Append 2 rows at the end"
 msgstr ""
 
-#: xml_file_content.cpp:180
-msgid "Delete 2 columns from the beginning"
-msgstr ""
-
 #: xml_file_content.cpp:181
-msgid "Delete 2 columns from the end"
+msgid "Delete 2 columns from the beginning"
 msgstr ""
 
 #: xml_file_content.cpp:182
-msgid "Delete 2 rows from the beginning"
+msgid "Delete 2 columns from the end"
 msgstr ""
 
 #: xml_file_content.cpp:183
-msgid "Delete 2 rows from the end"
+msgid "Delete 2 rows from the beginning"
 msgstr ""
 
 #: xml_file_content.cpp:184
-msgid "Matrix"
+msgid "Delete 2 rows from the end"
 msgstr ""
 
 #: xml_file_content.cpp:185
-msgid "Invert"
+msgid "Matrix"
 msgstr ""
 
 #: xml_file_content.cpp:186
-msgid "Re-disperse"
+msgid "Invert"
 msgstr ""
 
 #: xml_file_content.cpp:187
-msgid "Rows"
+msgid "Re-disperse"
 msgstr ""
 
 #: xml_file_content.cpp:188
-msgid "Columns"
+msgid "Rows"
 msgstr ""
 
 #: xml_file_content.cpp:189
-msgid "Transpose"
+msgid "Columns"
 msgstr ""
 
 #: xml_file_content.cpp:190
-msgid "Thicken Selected Patches"
+msgid "Transpose"
 msgstr ""
 
 #: xml_file_content.cpp:191
-msgid "Cap Selection"
+msgid "Thicken Selected Patches"
 msgstr ""
 
 #: xml_file_content.cpp:192
-msgid "Cycle Cap Texture"
+msgid "Cap Selection"
 msgstr ""
 
 #: xml_file_content.cpp:193
@@ -5391,58 +5486,66 @@ msgid "Make Room"
 msgstr ""
 
 #: xml_file_content.cpp:20
-msgid "Put caps on the current patch"
+msgid "Group selected items"
 msgstr ""
 
 #: xml_file_content.cpp:21
-msgid "Creates a NURBS curve"
+msgid "Ungroup selected items"
 msgstr ""
 
 #: xml_file_content.cpp:22
-msgid "Convert the selected curve (NURBS <-> CatmullRom)"
+msgid "Put caps on the current patch"
 msgstr ""
 
 #: xml_file_content.cpp:23
-msgid "Appends a control point to the selected curves"
+msgid "Creates a NURBS curve"
 msgstr ""
 
 #: xml_file_content.cpp:24
-msgid "Inserts a curve control point before the selected ones"
+msgid "Convert the selected curve (NURBS <-> CatmullRom)"
 msgstr ""
 
 #: xml_file_content.cpp:25
-msgid "Removes the selected curve control points"
+msgid "Appends a control point to the selected curves"
+msgstr ""
+
+#: xml_file_content.cpp:26
+msgid "Inserts a curve control point before the selected ones"
 msgstr ""
 
 #: xml_file_content.cpp:27
+msgid "Removes the selected curve control points"
+msgstr ""
+
+#: xml_file_content.cpp:29
 msgid "Find & Replace"
 msgstr ""
 
-#: xml_file_content.cpp:28
+#: xml_file_content.cpp:30
 msgid "Decrease Grid Size"
 msgstr ""
 
-#: xml_file_content.cpp:29
+#: xml_file_content.cpp:31
 msgid "Increase Grid Size"
 msgstr ""
 
-#: xml_file_content.cpp:30
+#: xml_file_content.cpp:32
 msgid "Snap to Grid"
 msgstr ""
 
-#: xml_file_content.cpp:31
+#: xml_file_content.cpp:33
 msgid "Merge Selection"
 msgstr ""
 
-#: xml_file_content.cpp:32
+#: xml_file_content.cpp:34
 msgid "Flip Selection Horiz (S-Axis)"
 msgstr ""
 
-#: xml_file_content.cpp:33
+#: xml_file_content.cpp:35
 msgid "Flip Selection Vertical (T-Axis)"
 msgstr ""
 
-#: xml_file_content.cpp:34
+#: xml_file_content.cpp:36
 msgid "Select Related Items"
 msgstr ""
 
diff --git a/tools/innosetup/create_installer.x64.cmd b/tools/innosetup/create_installer.x64.cmd
deleted file mode 100644
index fbe43aa..0000000
--- a/tools/innosetup/create_installer.x64.cmd
+++ /dev/null
@@ -1,2 +0,0 @@
- at echo For this script to run, please make sure that InnoSetup's compil32 is found via the PATH environment variable
-compil32 /cc darkradiant.x64.iss
\ No newline at end of file
diff --git a/tools/innosetup/create_installer.x86.cmd b/tools/innosetup/create_installer.x86.cmd
deleted file mode 100644
index 8197c77..0000000
--- a/tools/innosetup/create_installer.x86.cmd
+++ /dev/null
@@ -1,2 +0,0 @@
- at echo For this script to run, please make sure that InnoSetup's compil32 is found via the PATH environment variable
-compil32 /cc darkradiant.iss
\ No newline at end of file
diff --git a/tools/innosetup/darkradiant.iss b/tools/innosetup/darkradiant.iss
index c3d4d83..7fd4a9c 100644
--- a/tools/innosetup/darkradiant.iss
+++ b/tools/innosetup/darkradiant.iss
@@ -3,15 +3,15 @@
 
 [Setup]
 AppName=DarkRadiant
-AppVerName=DarkRadiant 2.0.4 x86
+AppVerName=DarkRadiant 2.1.0 x86
 AppPublisher=The Dark Mod
 AppPublisherURL=http://www.thedarkmod.com
 AppSupportURL=http://www.thedarkmod.com
 AppUpdatesURL=http://www.thedarkmod.com
 DefaultDirName={pf}\DarkRadiant
-DefaultGroupName=DarkRadiant 2.0.4 x86
+DefaultGroupName=DarkRadiant 2.1.0 x86
 OutputDir=.
-OutputBaseFilename=darkradiant-2.0.4-x86
+OutputBaseFilename=darkradiant-2.1.0-x86
 Compression=lzma
 SolidCompression=yes
 ;ArchitecturesAllowed=x64
@@ -25,7 +25,10 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
 
 [Files]
 Source: "..\..\install\darkradiant.exe"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\..\install\*"; Excludes: "*.pdb,*.exp,*.lib,*.in,*.fbp"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "..\..\install\*"; Excludes: "*.pdb,*.exp,*.lib,*.in,*.fbp,*.iobj,*.ipdb"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvcp140.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\vcruntime140.dll"; DestDir: "{app}"; Flags: ignoreversion
+
 ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
 
 [Icons]
diff --git a/tools/innosetup/darkradiant.x64.iss b/tools/innosetup/darkradiant.x64.iss
index 972bcc8..bcc931e 100644
--- a/tools/innosetup/darkradiant.x64.iss
+++ b/tools/innosetup/darkradiant.x64.iss
@@ -3,15 +3,15 @@
 
 [Setup]
 AppName=DarkRadiant
-AppVerName=DarkRadiant 2.0.4 x64
+AppVerName=DarkRadiant 2.1.0 x64
 AppPublisher=The Dark Mod
 AppPublisherURL=http://www.thedarkmod.com
 AppSupportURL=http://www.thedarkmod.com
 AppUpdatesURL=http://www.thedarkmod.com
 DefaultDirName={pf}\DarkRadiant
-DefaultGroupName=DarkRadiant 2.0.4 x64
+DefaultGroupName=DarkRadiant 2.1.0 x64
 OutputDir=.
-OutputBaseFilename=darkradiant-2.0.4-x64
+OutputBaseFilename=darkradiant-2.1.0-x64
 Compression=lzma
 SolidCompression=yes
 ArchitecturesAllowed=x64
@@ -25,7 +25,9 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
 
 [Files]
 Source: "..\..\install\darkradiant.exe"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\..\install\*"; Excludes: "*.pdb,*.exp,*.lib,*.in,*.fbp"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "..\..\install\*"; Excludes: "*.pdb,*.exp,*.lib,*.in,*.fbp,*.iobj,*.ipdb"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+Source: "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.CRT\msvcp140.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.CRT\vcruntime140.dll"; DestDir: "{app}"; Flags: ignoreversion
 ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
 
 [Icons]
diff --git a/tools/msvc2013/DarkRadiant.sln b/tools/msvc2015/DarkRadiant.sln
similarity index 100%
rename from tools/msvc2013/DarkRadiant.sln
rename to tools/msvc2015/DarkRadiant.sln
diff --git a/tools/msvc2013/DarkRadiant.vcxproj b/tools/msvc2015/DarkRadiant.vcxproj
similarity index 95%
rename from tools/msvc2013/DarkRadiant.vcxproj
rename to tools/msvc2015/DarkRadiant.vcxproj
index 2186f97..65c0468 100644
--- a/tools/msvc2013/DarkRadiant.vcxproj
+++ b/tools/msvc2015/DarkRadiant.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>Application</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>false</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -115,7 +115,7 @@
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
-      <AdditionalOptions>/EHsc %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200 %(AdditionalOptions)</AdditionalOptions>
       <Optimization>Disabled</Optimization>
       <AdditionalIncludeDirectories>$(SolutionDir)/../../radiant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -171,7 +171,7 @@
       <TargetEnvironment>X64</TargetEnvironment>
     </Midl>
     <ClCompile>
-      <AdditionalOptions>/EHsc %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200 %(AdditionalOptions)</AdditionalOptions>
       <Optimization>Disabled</Optimization>
       <AdditionalIncludeDirectories>$(SolutionDir)/../../radiant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -223,7 +223,7 @@
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
-      <AdditionalOptions>/EHsc %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200 %(AdditionalOptions)</AdditionalOptions>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
@@ -278,7 +278,7 @@
       <TargetEnvironment>X64</TargetEnvironment>
     </Midl>
     <ClCompile>
-      <AdditionalOptions>/EHsc %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200 %(AdditionalOptions)</AdditionalOptions>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
@@ -333,13 +333,17 @@
   <ItemGroup>
     <ClCompile Include="..\..\radiant\brush\TextureMatrix.cpp" />
     <ClCompile Include="..\..\radiant\camera\CamRenderer.cpp" />
+    <ClCompile Include="..\..\radiant\layers\LayerInfoFileModule.cpp" />
     <ClCompile Include="..\..\radiant\main.cpp" />
+    <ClCompile Include="..\..\radiant\map\AasFileManager.cpp" />
     <ClCompile Include="..\..\radiant\map\algorithm\ChildPrimitives.cpp" />
-    <ClCompile Include="..\..\radiant\map\algorithm\InfoFileExporter.cpp" />
     <ClCompile Include="..\..\radiant\map\algorithm\MapExporter.cpp" />
     <ClCompile Include="..\..\radiant\map\algorithm\MapImporter.cpp" />
     <ClCompile Include="..\..\radiant\map\algorithm\Skins.cpp" />
-    <ClCompile Include="..\..\radiant\map\InfoFile.cpp" />
+    <ClCompile Include="..\..\radiant\map\infofile\InfoFile.cpp" />
+    <ClCompile Include="..\..\radiant\map\infofile\InfoFileExporter.cpp" />
+    <ClCompile Include="..\..\radiant\map\infofile\InfoFileManager.cpp" />
+    <ClCompile Include="..\..\radiant\map\RenderableAasFile.cpp" />
     <ClCompile Include="..\..\radiant\namespace\ComplexName.cpp" />
     <ClCompile Include="..\..\radiant\patch\algorithm\General.cpp" />
     <ClCompile Include="..\..\radiant\patch\algorithm\Prefab.cpp" />
@@ -362,9 +366,15 @@
     <ClCompile Include="..\..\radiant\render\View.cpp" />
     <ClCompile Include="..\..\radiant\selection\algorithm\Patch.cpp" />
     <ClCompile Include="..\..\radiant\selection\clipboard\Clipboard.cpp" />
+    <ClCompile Include="..\..\radiant\selection\group\SelectionGroupInfoFileModule.cpp" />
+    <ClCompile Include="..\..\radiant\selection\group\SelectionGroupManager.cpp" />
     <ClCompile Include="..\..\radiant\selection\ManipulateMouseTool.cpp" />
     <ClCompile Include="..\..\radiant\selection\SelectionMouseTools.cpp" />
+    <ClCompile Include="..\..\radiant\selection\selectionset\SelectionSetInfoFileModule.cpp" />
     <ClCompile Include="..\..\radiant\selection\shaderclipboard\ClosestTexturableFinder.cpp" />
+    <ClCompile Include="..\..\radiant\settings\PreferencePage.cpp" />
+    <ClCompile Include="..\..\radiant\ui\aas\AasControl.cpp" />
+    <ClCompile Include="..\..\radiant\ui\aas\AasControlDialog.cpp" />
     <ClCompile Include="..\..\radiant\ui\animationpreview\AnimationPreview.cpp" />
     <ClCompile Include="..\..\radiant\ui\animationpreview\MD5AnimationViewer.cpp" />
     <ClCompile Include="..\..\radiant\ui\mainframe\TopLevelFrame.cpp" />
@@ -412,7 +422,6 @@
     <ClCompile Include="..\..\radiant\namespace\Namespace.cpp" />
     <ClCompile Include="..\..\radiant\namespace\NamespaceFactory.cpp" />
     <ClCompile Include="..\..\radiant\patch\Patch.cpp" />
-    <ClCompile Include="..\..\radiant\patch\PatchBezier.cpp" />
     <ClCompile Include="..\..\radiant\patch\PatchModule.cpp" />
     <ClCompile Include="..\..\radiant\patch\PatchNode.cpp" />
     <ClCompile Include="..\..\radiant\patch\PatchRenderables.cpp" />
@@ -524,6 +533,7 @@
     <ClCompile Include="..\..\radiant\ui\prefabselector\PrefabPopulator.cpp" />
     <ClCompile Include="..\..\radiant\ui\prefabselector\PrefabSelector.cpp" />
     <ClCompile Include="..\..\radiant\ui\prefdialog\PrefDialog.cpp" />
+    <ClCompile Include="..\..\radiant\ui\prefdialog\PreferenceItem.cpp" />
     <ClCompile Include="..\..\radiant\ui\prefdialog\PrefPage.cpp" />
     <ClCompile Include="..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp" />
     <ClCompile Include="..\..\radiant\ui\transform\TransformDialog.cpp" />
@@ -567,14 +577,18 @@
     <ClInclude Include="..\..\radiant\camera\tools\CameraMouseToolEvent.h" />
     <ClInclude Include="..\..\radiant\camera\tools\FreeMoveTool.h" />
     <ClInclude Include="..\..\radiant\camera\tools\JumpToObjectTool.h" />
+    <ClInclude Include="..\..\radiant\camera\tools\PanViewTool.h" />
     <ClInclude Include="..\..\radiant\camera\tools\ShaderClipboardTools.h" />
+    <ClInclude Include="..\..\radiant\layers\LayerInfoFileModule.h" />
+    <ClInclude Include="..\..\radiant\map\AasFileManager.h" />
     <ClInclude Include="..\..\radiant\map\algorithm\ChildPrimitives.h" />
-    <ClInclude Include="..\..\radiant\map\algorithm\AssignLayerMappingWalker.h" />
-    <ClInclude Include="..\..\radiant\map\algorithm\InfoFileExporter.h" />
     <ClInclude Include="..\..\radiant\map\algorithm\MapExporter.h" />
     <ClInclude Include="..\..\radiant\map\algorithm\MapImporter.h" />
     <ClInclude Include="..\..\radiant\map\algorithm\Skins.h" />
-    <ClInclude Include="..\..\radiant\map\InfoFile.h" />
+    <ClInclude Include="..\..\radiant\map\infofile\InfoFile.h" />
+    <ClInclude Include="..\..\radiant\map\infofile\InfoFileExporter.h" />
+    <ClInclude Include="..\..\radiant\map\infofile\InfoFileManager.h" />
+    <ClInclude Include="..\..\radiant\map\RenderableAasFile.h" />
     <ClInclude Include="..\..\radiant\patch\algorithm\General.h" />
     <ClInclude Include="..\..\radiant\patch\algorithm\Prefab.h" />
     <ClInclude Include="..\..\radiant\precompiled.h" />
@@ -584,14 +598,24 @@
     <ClInclude Include="..\..\radiant\render\backend\OpenGLStateManager.h" />
     <ClInclude Include="..\..\radiant\render\frontend\RenderableCollectionWalker.h" />
     <ClInclude Include="..\..\radiant\render\View.h" />
+    <ClInclude Include="..\..\radiant\selection\algorithm\CommandNotAvailableException.h" />
     <ClInclude Include="..\..\radiant\selection\algorithm\Patch.h" />
     <ClInclude Include="..\..\radiant\selection\BasicSelectable.h" />
     <ClInclude Include="..\..\radiant\selection\clipboard\Clipboard.h" />
+    <ClInclude Include="..\..\radiant\selection\group\SelectionGroup.h" />
+    <ClInclude Include="..\..\radiant\selection\group\SelectionGroupInfoFileModule.h" />
+    <ClInclude Include="..\..\radiant\selection\group\SelectionGroupManager.h" />
     <ClInclude Include="..\..\radiant\selection\ManipulateMouseTool.h" />
     <ClInclude Include="..\..\radiant\selection\OccludeSelector.h" />
     <ClInclude Include="..\..\radiant\selection\Rectangle.h" />
     <ClInclude Include="..\..\radiant\selection\SelectionMouseTools.h" />
+    <ClInclude Include="..\..\radiant\selection\selectionset\SelectionSetInfoFileModule.h" />
     <ClInclude Include="..\..\radiant\selection\shaderclipboard\ClosestTexturableFinder.h" />
+    <ClInclude Include="..\..\radiant\settings\PreferenceItemBase.h" />
+    <ClInclude Include="..\..\radiant\settings\PreferenceItems.h" />
+    <ClInclude Include="..\..\radiant\settings\PreferencePage.h" />
+    <ClInclude Include="..\..\radiant\ui\aas\AasControl.h" />
+    <ClInclude Include="..\..\radiant\ui\aas\AasControlDialog.h" />
     <ClInclude Include="..\..\radiant\ui\animationpreview\AnimationPreview.h" />
     <ClInclude Include="..\..\radiant\ui\animationpreview\MD5AnimationViewer.h" />
     <ClInclude Include="..\..\radiant\ui\mainframe\TopLevelFrame.h" />
@@ -630,7 +654,6 @@
     <ClInclude Include="..\..\radiant\clipper\ClipPoint.h" />
     <ClInclude Include="..\..\radiant\map\AutoSaver.h" />
     <ClInclude Include="..\..\radiant\map\CounterManager.h" />
-    <ClInclude Include="..\..\radiant\map\DeferredDraw.h" />
     <ClInclude Include="..\..\radiant\map\EntityBreakdown.h" />
     <ClInclude Include="..\..\radiant\map\FindMapElements.h" />
     <ClInclude Include="..\..\radiant\map\Map.h" />
@@ -662,7 +685,6 @@
     <ClInclude Include="..\..\radiant\namespace\NamespaceFactory.h" />
     <ClInclude Include="..\..\radiant\namespace\UniqueNameSet.h" />
     <ClInclude Include="..\..\radiant\patch\Patch.h" />
-    <ClInclude Include="..\..\radiant\patch\PatchBezier.h" />
     <ClInclude Include="..\..\radiant\patch\PatchConstants.h" />
     <ClInclude Include="..\..\radiant\patch\PatchControl.h" />
     <ClInclude Include="..\..\radiant\patch\PatchControlInstance.h" />
@@ -803,6 +825,7 @@
     <ClInclude Include="..\..\radiant\ui\prefabselector\PrefabPopulator.h" />
     <ClInclude Include="..\..\radiant\ui\prefabselector\PrefabSelector.h" />
     <ClInclude Include="..\..\radiant\ui\prefdialog\PrefDialog.h" />
+    <ClInclude Include="..\..\radiant\ui\prefdialog\PreferenceItem.h" />
     <ClInclude Include="..\..\radiant\ui\prefdialog\PrefPage.h" />
     <ClInclude Include="..\..\radiant\ui\texturebrowser\TextureBrowserManager.h" />
     <ClInclude Include="..\..\radiant\ui\transform\TransformDialog.h" />
@@ -925,6 +948,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </Xml>
   </ItemGroup>
+  <ItemGroup>
+    <None Include="..\innosetup\darkradiant.iss" />
+    <None Include="..\innosetup\darkradiant.x64.iss" />
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
diff --git a/tools/msvc2013/DarkRadiant.vcxproj.filters b/tools/msvc2015/DarkRadiant.vcxproj.filters
similarity index 94%
rename from tools/msvc2013/DarkRadiant.vcxproj.filters
rename to tools/msvc2015/DarkRadiant.vcxproj.filters
index aac7434..0d02c33 100644
--- a/tools/msvc2013/DarkRadiant.vcxproj.filters
+++ b/tools/msvc2015/DarkRadiant.vcxproj.filters
@@ -185,6 +185,18 @@
     <Filter Include="src\ui\mousetool">
       <UniqueIdentifier>{569de02a-1a1c-46b7-9046-49bd1c575aba}</UniqueIdentifier>
     </Filter>
+    <Filter Include="innosetup">
+      <UniqueIdentifier>{cec4701a-4553-44d6-aec6-199afd8a38b3}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\ui\aas">
+      <UniqueIdentifier>{320a8b8c-db98-4f20-bb92-b66812bb5a53}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\map\infofile">
+      <UniqueIdentifier>{04d7b32b-b95b-41a1-93c1-3c623749b4eb}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\selection\group">
+      <UniqueIdentifier>{182783ec-2373-43f8-a310-0a3e51333300}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\radiant\main.cpp">
@@ -322,9 +334,6 @@
     <ClCompile Include="..\..\radiant\patch\Patch.cpp">
       <Filter>src\patch</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\radiant\patch\PatchBezier.cpp">
-      <Filter>src\patch</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\radiant\patch\PatchModule.cpp">
       <Filter>src\patch</Filter>
     </ClCompile>
@@ -748,12 +757,6 @@
     <ClCompile Include="..\..\radiant\map\algorithm\MapExporter.cpp">
       <Filter>src\map\algorithm</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\radiant\map\algorithm\InfoFileExporter.cpp">
-      <Filter>src\map\algorithm</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\radiant\map\InfoFile.cpp">
-      <Filter>src\map</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\radiant\map\algorithm\ChildPrimitives.cpp">
       <Filter>src\map\algorithm</Filter>
     </ClCompile>
@@ -850,6 +853,45 @@
     <ClCompile Include="..\..\radiant\brush\TextureMatrix.cpp">
       <Filter>src\brush</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\radiant\settings\PreferencePage.cpp">
+      <Filter>src\settings</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\ui\prefdialog\PreferenceItem.cpp">
+      <Filter>src\ui\prefdialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\ui\aas\AasControl.cpp">
+      <Filter>src\ui\aas</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\ui\aas\AasControlDialog.cpp">
+      <Filter>src\ui\aas</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\map\AasFileManager.cpp">
+      <Filter>src\map</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\map\RenderableAasFile.cpp">
+      <Filter>src\map</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\map\infofile\InfoFileManager.cpp">
+      <Filter>src\map\infofile</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\map\infofile\InfoFile.cpp">
+      <Filter>src\map\infofile</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\map\infofile\InfoFileExporter.cpp">
+      <Filter>src\map\infofile</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\selection\selectionset\SelectionSetInfoFileModule.cpp">
+      <Filter>src\selection\selectionset</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\layers\LayerInfoFileModule.cpp">
+      <Filter>src\layers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\selection\group\SelectionGroupManager.cpp">
+      <Filter>src\selection\group</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\radiant\selection\group\SelectionGroupInfoFileModule.cpp">
+      <Filter>src\selection\group</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\radiant\RadiantModule.h">
@@ -957,9 +999,6 @@
     <ClInclude Include="..\..\radiant\map\CounterManager.h">
       <Filter>src\map</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\radiant\map\DeferredDraw.h">
-      <Filter>src\map</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\radiant\map\EntityBreakdown.h">
       <Filter>src\map</Filter>
     </ClInclude>
@@ -1053,9 +1092,6 @@
     <ClInclude Include="..\..\radiant\patch\Patch.h">
       <Filter>src\patch</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\radiant\patch\PatchBezier.h">
-      <Filter>src\patch</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\radiant\patch\PatchConstants.h">
       <Filter>src\patch</Filter>
     </ClInclude>
@@ -1602,15 +1638,6 @@
     <ClInclude Include="..\..\radiant\map\algorithm\MapExporter.h">
       <Filter>src\map\algorithm</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\radiant\map\algorithm\InfoFileExporter.h">
-      <Filter>src\map\algorithm</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\radiant\map\InfoFile.h">
-      <Filter>src\map</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\radiant\map\algorithm\AssignLayerMappingWalker.h">
-      <Filter>src\map\algorithm</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\radiant\map\algorithm\ChildPrimitives.h">
       <Filter>src\map\algorithm</Filter>
     </ClInclude>
@@ -1734,6 +1761,60 @@
     <ClInclude Include="..\..\radiant\brush\TextureMatrix.h">
       <Filter>src\brush</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\radiant\settings\PreferencePage.h">
+      <Filter>src\settings</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\settings\PreferenceItemBase.h">
+      <Filter>src\settings</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\settings\PreferenceItems.h">
+      <Filter>src\settings</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\ui\prefdialog\PreferenceItem.h">
+      <Filter>src\ui\prefdialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\camera\tools\PanViewTool.h">
+      <Filter>src\camera\tools</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\ui\aas\AasControl.h">
+      <Filter>src\ui\aas</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\ui\aas\AasControlDialog.h">
+      <Filter>src\ui\aas</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\map\AasFileManager.h">
+      <Filter>src\map</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\map\RenderableAasFile.h">
+      <Filter>src\map</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\map\infofile\InfoFileManager.h">
+      <Filter>src\map\infofile</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\map\infofile\InfoFile.h">
+      <Filter>src\map\infofile</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\map\infofile\InfoFileExporter.h">
+      <Filter>src\map\infofile</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\selection\selectionset\SelectionSetInfoFileModule.h">
+      <Filter>src\selection\selectionset</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\layers\LayerInfoFileModule.h">
+      <Filter>src\layers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\selection\group\SelectionGroup.h">
+      <Filter>src\selection\group</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\selection\group\SelectionGroupManager.h">
+      <Filter>src\selection\group</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\selection\group\SelectionGroupInfoFileModule.h">
+      <Filter>src\selection\group</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\radiant\selection\algorithm\CommandNotAvailableException.h">
+      <Filter>src\selection\algorithm</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\radiant\darkradiant.rc" />
@@ -1758,4 +1839,12 @@
       <Filter>xml</Filter>
     </Xml>
   </ItemGroup>
+  <ItemGroup>
+    <None Include="..\innosetup\darkradiant.iss">
+      <Filter>innosetup</Filter>
+    </None>
+    <None Include="..\innosetup\darkradiant.x64.iss">
+      <Filter>innosetup</Filter>
+    </None>
+  </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/tools/msvc2013/archivezip.vcxproj b/tools/msvc2015/archivezip.vcxproj
similarity index 98%
rename from tools/msvc2013/archivezip.vcxproj
rename to tools/msvc2015/archivezip.vcxproj
index 5205d90..cbb4ac5 100644
--- a/tools/msvc2013/archivezip.vcxproj
+++ b/tools/msvc2015/archivezip.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/archivezip.vcxproj.filters b/tools/msvc2015/archivezip.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/archivezip.vcxproj.filters
rename to tools/msvc2015/archivezip.vcxproj.filters
diff --git a/tools/msvc2013/commandsystem.vcxproj b/tools/msvc2015/commandsystem.vcxproj
similarity index 98%
rename from tools/msvc2013/commandsystem.vcxproj
rename to tools/msvc2015/commandsystem.vcxproj
index af44562..e226cff 100644
--- a/tools/msvc2013/commandsystem.vcxproj
+++ b/tools/msvc2015/commandsystem.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/commandsystem.vcxproj.filters b/tools/msvc2015/commandsystem.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/commandsystem.vcxproj.filters
rename to tools/msvc2015/commandsystem.vcxproj.filters
diff --git a/tools/msvc2013/ddslib.vcxproj b/tools/msvc2015/ddslib.vcxproj
similarity index 97%
rename from tools/msvc2013/ddslib.vcxproj
rename to tools/msvc2015/ddslib.vcxproj
index 6b7891d..f52748c 100644
--- a/tools/msvc2013/ddslib.vcxproj
+++ b/tools/msvc2015/ddslib.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -25,19 +25,19 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/dm.conversation.vcxproj b/tools/msvc2015/dm.conversation.vcxproj
similarity index 98%
rename from tools/msvc2013/dm.conversation.vcxproj
rename to tools/msvc2015/dm.conversation.vcxproj
index 843d0bd..5d09e1b 100644
--- a/tools/msvc2013/dm.conversation.vcxproj
+++ b/tools/msvc2015/dm.conversation.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/dm.conversation.vcxproj.filters b/tools/msvc2015/dm.conversation.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/dm.conversation.vcxproj.filters
rename to tools/msvc2015/dm.conversation.vcxproj.filters
diff --git a/tools/msvc2013/dm.difficulty.vcxproj b/tools/msvc2015/dm.difficulty.vcxproj
similarity index 98%
rename from tools/msvc2013/dm.difficulty.vcxproj
rename to tools/msvc2015/dm.difficulty.vcxproj
index 20e97a0..e339928 100644
--- a/tools/msvc2013/dm.difficulty.vcxproj
+++ b/tools/msvc2015/dm.difficulty.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/dm.difficulty.vcxproj.filters b/tools/msvc2015/dm.difficulty.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/dm.difficulty.vcxproj.filters
rename to tools/msvc2015/dm.difficulty.vcxproj.filters
diff --git a/tools/msvc2013/dm.editing.vcxproj b/tools/msvc2015/dm.editing.vcxproj
similarity index 98%
rename from tools/msvc2013/dm.editing.vcxproj
rename to tools/msvc2015/dm.editing.vcxproj
index 52bf11c..c65becf 100644
--- a/tools/msvc2013/dm.editing.vcxproj
+++ b/tools/msvc2015/dm.editing.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/dm.editing.vcxproj.filters b/tools/msvc2015/dm.editing.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/dm.editing.vcxproj.filters
rename to tools/msvc2015/dm.editing.vcxproj.filters
diff --git a/tools/msvc2013/dm.gui.vcxproj b/tools/msvc2015/dm.gui.vcxproj
similarity index 98%
rename from tools/msvc2013/dm.gui.vcxproj
rename to tools/msvc2015/dm.gui.vcxproj
index 60a9fae..b0edaf9 100644
--- a/tools/msvc2013/dm.gui.vcxproj
+++ b/tools/msvc2015/dm.gui.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/dm.gui.vcxproj.filters b/tools/msvc2015/dm.gui.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/dm.gui.vcxproj.filters
rename to tools/msvc2015/dm.gui.vcxproj.filters
diff --git a/tools/msvc2013/dm.objectives.vcxproj b/tools/msvc2015/dm.objectives.vcxproj
similarity index 99%
rename from tools/msvc2013/dm.objectives.vcxproj
rename to tools/msvc2015/dm.objectives.vcxproj
index 7e5d324..39b0416 100644
--- a/tools/msvc2013/dm.objectives.vcxproj
+++ b/tools/msvc2015/dm.objectives.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/dm.objectives.vcxproj.filters b/tools/msvc2015/dm.objectives.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/dm.objectives.vcxproj.filters
rename to tools/msvc2015/dm.objectives.vcxproj.filters
diff --git a/tools/msvc2013/dm.stimresponse.vcxproj b/tools/msvc2015/dm.stimresponse.vcxproj
similarity index 98%
rename from tools/msvc2013/dm.stimresponse.vcxproj
rename to tools/msvc2015/dm.stimresponse.vcxproj
index 2cf9bc4..cec9e6d 100644
--- a/tools/msvc2013/dm.stimresponse.vcxproj
+++ b/tools/msvc2015/dm.stimresponse.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/dm.stimresponse.vcxproj.filters b/tools/msvc2015/dm.stimresponse.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/dm.stimresponse.vcxproj.filters
rename to tools/msvc2015/dm.stimresponse.vcxproj.filters
diff --git a/tools/msvc2013/eclassmgr.vcxproj b/tools/msvc2015/eclassmgr.vcxproj
similarity index 98%
rename from tools/msvc2013/eclassmgr.vcxproj
rename to tools/msvc2015/eclassmgr.vcxproj
index ad32384..ff499e5 100644
--- a/tools/msvc2013/eclassmgr.vcxproj
+++ b/tools/msvc2015/eclassmgr.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/eclassmgr.vcxproj.filters b/tools/msvc2015/eclassmgr.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/eclassmgr.vcxproj.filters
rename to tools/msvc2015/eclassmgr.vcxproj.filters
diff --git a/tools/msvc2013/eclasstree.vcxproj b/tools/msvc2015/eclasstree.vcxproj
similarity index 98%
rename from tools/msvc2013/eclasstree.vcxproj
rename to tools/msvc2015/eclasstree.vcxproj
index f657e2b..56e9cb4 100644
--- a/tools/msvc2013/eclasstree.vcxproj
+++ b/tools/msvc2015/eclasstree.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/eclasstree.vcxproj.filters b/tools/msvc2015/eclasstree.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/eclasstree.vcxproj.filters
rename to tools/msvc2015/eclasstree.vcxproj.filters
diff --git a/tools/msvc2013/entity.vcxproj b/tools/msvc2015/entity.vcxproj
similarity index 98%
rename from tools/msvc2013/entity.vcxproj
rename to tools/msvc2015/entity.vcxproj
index 933596f..b812ba9 100644
--- a/tools/msvc2013/entity.vcxproj
+++ b/tools/msvc2015/entity.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -117,7 +117,7 @@
       </Command>
     </CustomBuildStep>
     <ClCompile>
-      <AdditionalOptions>/EHsc</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200</AdditionalOptions>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
@@ -169,7 +169,7 @@
       <TargetEnvironment>X64</TargetEnvironment>
     </Midl>
     <ClCompile>
-      <AdditionalOptions>/EHsc</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200</AdditionalOptions>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
@@ -217,7 +217,7 @@
       </Command>
     </CustomBuildStep>
     <ClCompile>
-      <AdditionalOptions>/EHsc</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200</AdditionalOptions>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
@@ -271,7 +271,7 @@
       <TargetEnvironment>X64</TargetEnvironment>
     </Midl>
     <ClCompile>
-      <AdditionalOptions>/EHsc</AdditionalOptions>
+      <AdditionalOptions>/EHsc /Zm200</AdditionalOptions>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
diff --git a/tools/msvc2013/entity.vcxproj.filters b/tools/msvc2015/entity.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/entity.vcxproj.filters
rename to tools/msvc2015/entity.vcxproj.filters
diff --git a/tools/msvc2013/entitylist.vcxproj b/tools/msvc2015/entitylist.vcxproj
similarity index 98%
rename from tools/msvc2013/entitylist.vcxproj
rename to tools/msvc2015/entitylist.vcxproj
index 04c21bc..da9f2d9 100644
--- a/tools/msvc2013/entitylist.vcxproj
+++ b/tools/msvc2015/entitylist.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/entitylist.vcxproj.filters b/tools/msvc2015/entitylist.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/entitylist.vcxproj.filters
rename to tools/msvc2015/entitylist.vcxproj.filters
diff --git a/tools/msvc2013/eventmanager.vcxproj b/tools/msvc2015/eventmanager.vcxproj
similarity index 98%
rename from tools/msvc2013/eventmanager.vcxproj
rename to tools/msvc2015/eventmanager.vcxproj
index a94353b..dc84574 100644
--- a/tools/msvc2013/eventmanager.vcxproj
+++ b/tools/msvc2015/eventmanager.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/eventmanager.vcxproj.filters b/tools/msvc2015/eventmanager.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/eventmanager.vcxproj.filters
rename to tools/msvc2015/eventmanager.vcxproj.filters
diff --git a/tools/msvc2013/filetypes.vcxproj b/tools/msvc2015/filetypes.vcxproj
similarity index 98%
rename from tools/msvc2013/filetypes.vcxproj
rename to tools/msvc2015/filetypes.vcxproj
index 7065923..fda4c1c 100644
--- a/tools/msvc2013/filetypes.vcxproj
+++ b/tools/msvc2015/filetypes.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/filetypes.vcxproj.filters b/tools/msvc2015/filetypes.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/filetypes.vcxproj.filters
rename to tools/msvc2015/filetypes.vcxproj.filters
diff --git a/tools/msvc2013/filters.vcxproj b/tools/msvc2015/filters.vcxproj
similarity index 98%
rename from tools/msvc2013/filters.vcxproj
rename to tools/msvc2015/filters.vcxproj
index b4e7461..da2b340 100644
--- a/tools/msvc2013/filters.vcxproj
+++ b/tools/msvc2015/filters.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/filters.vcxproj.filters b/tools/msvc2015/filters.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/filters.vcxproj.filters
rename to tools/msvc2015/filters.vcxproj.filters
diff --git a/tools/msvc2013/fonts.vcxproj b/tools/msvc2015/fonts.vcxproj
similarity index 98%
rename from tools/msvc2013/fonts.vcxproj
rename to tools/msvc2015/fonts.vcxproj
index 56c48cd..8818bb0 100644
--- a/tools/msvc2013/fonts.vcxproj
+++ b/tools/msvc2015/fonts.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/fonts.vcxproj.filters b/tools/msvc2015/fonts.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/fonts.vcxproj.filters
rename to tools/msvc2015/fonts.vcxproj.filters
diff --git a/tools/msvc2013/grid.vcxproj b/tools/msvc2015/grid.vcxproj
similarity index 98%
rename from tools/msvc2013/grid.vcxproj
rename to tools/msvc2015/grid.vcxproj
index bb75f02..b7bf794 100644
--- a/tools/msvc2013/grid.vcxproj
+++ b/tools/msvc2015/grid.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/grid.vcxproj.filters b/tools/msvc2015/grid.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/grid.vcxproj.filters
rename to tools/msvc2015/grid.vcxproj.filters
diff --git a/tools/msvc2013/image.vcxproj b/tools/msvc2015/image.vcxproj
similarity index 98%
rename from tools/msvc2013/image.vcxproj
rename to tools/msvc2015/image.vcxproj
index 3003f8a..4b11d81 100644
--- a/tools/msvc2013/image.vcxproj
+++ b/tools/msvc2015/image.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/image.vcxproj.filters b/tools/msvc2015/image.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/image.vcxproj.filters
rename to tools/msvc2015/image.vcxproj.filters
diff --git a/tools/msvc2013/include.vcxproj b/tools/msvc2015/include.vcxproj
similarity index 92%
rename from tools/msvc2013/include.vcxproj
rename to tools/msvc2015/include.vcxproj
index f59fc06..e66df2a 100644
--- a/tools/msvc2013/include.vcxproj
+++ b/tools/msvc2015/include.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -24,19 +24,19 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -82,16 +82,16 @@
     <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)\..\..\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(IncludePath)</IncludePath>
+    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(W32DepsDir)\libsigc++\include;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(IncludePath)</IncludePath>
+    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(W32DepsDir)\libsigc++\include;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(IncludePath)</IncludePath>
+    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(W32DepsDir)\libsigc++\include;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(IncludePath)</IncludePath>
+    <IncludePath>$(DarkRadiantRoot)\libs;$(W32DepsDir)\boost;$(W32DepsDir)\libsigc++\include;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <Midl>
@@ -108,6 +108,7 @@
     <ClInclude Include="..\..\include\editable.h" />
     <ClInclude Include="..\..\include\GLProgramAttributes.h" />
     <ClInclude Include="..\..\include\i18n.h" />
+    <ClInclude Include="..\..\include\iaasfile.h" />
     <ClInclude Include="..\..\include\iarchive.h" />
     <ClInclude Include="..\..\include\ibrush.h" />
     <ClInclude Include="..\..\include\icamera.h" />
@@ -144,6 +145,7 @@
     <ClInclude Include="..\..\include\imap.h" />
     <ClInclude Include="..\..\include\imapcompiler.h" />
     <ClInclude Include="..\..\include\imapformat.h" />
+    <ClInclude Include="..\..\include\imapinfofile.h" />
     <ClInclude Include="..\..\include\imapresource.h" />
     <ClInclude Include="..\..\include\imd5anim.h" />
     <ClInclude Include="..\..\include\imd5model.h" />
@@ -175,6 +177,7 @@
     <ClInclude Include="..\..\include\iscenegraphfactory.h" />
     <ClInclude Include="..\..\include\iscript.h" />
     <ClInclude Include="..\..\include\iselectable.h" />
+    <ClInclude Include="..\..\include\iselectiongroup.h" />
     <ClInclude Include="..\..\include\iselectiontest.h" />
     <ClInclude Include="..\..\include\iselection.h" />
     <ClInclude Include="..\..\include\iselectionset.h" />
diff --git a/tools/msvc2013/libs.vcxproj b/tools/msvc2015/libs.vcxproj
similarity index 97%
rename from tools/msvc2013/libs.vcxproj
rename to tools/msvc2015/libs.vcxproj
index b983413..f575d07 100644
--- a/tools/msvc2013/libs.vcxproj
+++ b/tools/msvc2015/libs.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -26,26 +26,26 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <CharacterSet>NotSet</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <CharacterSet>NotSet</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>NotSet</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Utility</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>NotSet</CharacterSet>
   </PropertyGroup>
@@ -190,7 +190,6 @@
     <ClInclude Include="..\..\libs\render\VertexNT.h" />
     <ClInclude Include="..\..\libs\RGBAImage.h" />
     <ClInclude Include="..\..\libs\scenelib.h" />
-    <ClInclude Include="..\..\libs\SelectableNode.h" />
     <ClInclude Include="..\..\libs\selectionlib.h" />
     <ClInclude Include="..\..\libs\shaderlib.h" />
     <ClInclude Include="..\..\libs\stream\BufferInputStream.h" />
diff --git a/tools/msvc2013/libs.vcxproj.filters b/tools/msvc2015/libs.vcxproj.filters
similarity index 99%
rename from tools/msvc2013/libs.vcxproj.filters
rename to tools/msvc2015/libs.vcxproj.filters
index de10c1a..8a19ffc 100644
--- a/tools/msvc2013/libs.vcxproj.filters
+++ b/tools/msvc2015/libs.vcxproj.filters
@@ -135,7 +135,6 @@
     <ClInclude Include="..\..\libs\Transformable.h" />
     <ClInclude Include="..\..\libs\BasicUndoMemento.h" />
     <ClInclude Include="..\..\libs\ObservedUndoable.h" />
-    <ClInclude Include="..\..\libs\SelectableNode.h" />
     <ClInclude Include="..\..\libs\ObservedSelectable.h" />
     <ClInclude Include="..\..\libs\stream\ScopedArchiveBuffer.h">
       <Filter>stream</Filter>
diff --git a/tools/msvc2013/mapdoom3.vcxproj b/tools/msvc2015/mapdoom3.vcxproj
similarity index 96%
rename from tools/msvc2013/mapdoom3.vcxproj
rename to tools/msvc2015/mapdoom3.vcxproj
index 84cef96..2194bc6 100644
--- a/tools/msvc2013/mapdoom3.vcxproj
+++ b/tools/msvc2015/mapdoom3.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -311,6 +311,10 @@
     </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Doom3AasFile.h" />
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Doom3AasFileLoader.h" />
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Doom3AasFileSettings.h" />
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Util.h" />
     <ClInclude Include="..\..\plugins\mapdoom3\compiler\BspTree.h" />
     <ClInclude Include="..\..\plugins\mapdoom3\compiler\DebugRenderer.h" />
     <ClInclude Include="..\..\plugins\mapdoom3\compiler\Doom3MapCompiler.h" />
@@ -346,6 +350,9 @@
     <ClInclude Include="..\..\plugins\mapdoom3\primitivewriters\PatchDefExporter.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\..\plugins\mapdoom3\aas\Doom3AasFile.cpp" />
+    <ClCompile Include="..\..\plugins\mapdoom3\aas\Doom3AasFileLoader.cpp" />
+    <ClCompile Include="..\..\plugins\mapdoom3\aas\Doom3AasFileSettings.cpp" />
     <ClCompile Include="..\..\plugins\mapdoom3\compiler\Doom3MapCompiler.cpp" />
     <ClCompile Include="..\..\plugins\mapdoom3\compiler\OptIsland.cpp" />
     <ClCompile Include="..\..\plugins\mapdoom3\compiler\ProcCompiler.cpp" />
diff --git a/tools/msvc2013/mapdoom3.vcxproj.filters b/tools/msvc2015/mapdoom3.vcxproj.filters
similarity index 88%
rename from tools/msvc2013/mapdoom3.vcxproj.filters
rename to tools/msvc2015/mapdoom3.vcxproj.filters
index 560315c..a13e7a3 100644
--- a/tools/msvc2013/mapdoom3.vcxproj.filters
+++ b/tools/msvc2015/mapdoom3.vcxproj.filters
@@ -14,6 +14,9 @@
     <Filter Include="src\compiler">
       <UniqueIdentifier>{f6fd3c0c-b85a-44fd-9149-fa840615ac31}</UniqueIdentifier>
     </Filter>
+    <Filter Include="src\aas">
+      <UniqueIdentifier>{48df04ae-bfe5-475d-91e7-972f10239051}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\plugins\mapdoom3\Doom3MapFormat.h">
@@ -115,6 +118,18 @@
     <ClInclude Include="..\..\plugins\mapdoom3\primitivewriters\BrushDefExporter.h">
       <Filter>src\primitivewriters</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Doom3AasFile.h">
+      <Filter>src\aas</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Doom3AasFileLoader.h">
+      <Filter>src\aas</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Doom3AasFileSettings.h">
+      <Filter>src\aas</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\plugins\mapdoom3\aas\Util.h">
+      <Filter>src\aas</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\plugins\mapdoom3\Doom3MapFormat.cpp">
@@ -183,6 +198,15 @@
     <ClCompile Include="..\..\plugins\mapdoom3\Quake3MapReader.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\plugins\mapdoom3\aas\Doom3AasFileLoader.cpp">
+      <Filter>src\aas</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\plugins\mapdoom3\aas\Doom3AasFileSettings.cpp">
+      <Filter>src\aas</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\plugins\mapdoom3\aas\Doom3AasFile.cpp">
+      <Filter>src\aas</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\..\plugins\mapdoom3\mapdoom3.def">
diff --git a/tools/msvc2013/mathlib.vcxproj b/tools/msvc2015/mathlib.vcxproj
similarity index 97%
rename from tools/msvc2013/mathlib.vcxproj
rename to tools/msvc2015/mathlib.vcxproj
index 305af86..aa92ea9 100644
--- a/tools/msvc2013/mathlib.vcxproj
+++ b/tools/msvc2015/mathlib.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -24,19 +24,19 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/md5model.vcxproj b/tools/msvc2015/md5model.vcxproj
similarity index 98%
rename from tools/msvc2013/md5model.vcxproj
rename to tools/msvc2015/md5model.vcxproj
index 1231cbc..1388ff5 100644
--- a/tools/msvc2013/md5model.vcxproj
+++ b/tools/msvc2015/md5model.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/md5model.vcxproj.filters b/tools/msvc2015/md5model.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/md5model.vcxproj.filters
rename to tools/msvc2015/md5model.vcxproj.filters
diff --git a/tools/msvc2013/model.vcxproj b/tools/msvc2015/model.vcxproj
similarity index 98%
rename from tools/msvc2013/model.vcxproj
rename to tools/msvc2015/model.vcxproj
index fe0b037..0ed5dda 100644
--- a/tools/msvc2013/model.vcxproj
+++ b/tools/msvc2015/model.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/model.vcxproj.filters b/tools/msvc2015/model.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/model.vcxproj.filters
rename to tools/msvc2015/model.vcxproj.filters
diff --git a/tools/msvc2013/particles.vcxproj b/tools/msvc2015/particles.vcxproj
similarity index 98%
rename from tools/msvc2013/particles.vcxproj
rename to tools/msvc2015/particles.vcxproj
index 48c757c..bd523b9 100644
--- a/tools/msvc2013/particles.vcxproj
+++ b/tools/msvc2015/particles.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/particles.vcxproj.filters b/tools/msvc2015/particles.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/particles.vcxproj.filters
rename to tools/msvc2015/particles.vcxproj.filters
diff --git a/tools/msvc2013/picomodellib.vcxproj b/tools/msvc2015/picomodellib.vcxproj
similarity index 97%
rename from tools/msvc2013/picomodellib.vcxproj
rename to tools/msvc2015/picomodellib.vcxproj
index 623942d..0ba0b09 100644
--- a/tools/msvc2013/picomodellib.vcxproj
+++ b/tools/msvc2015/picomodellib.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -25,19 +25,19 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/picomodellib.vcxproj.filters b/tools/msvc2015/picomodellib.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/picomodellib.vcxproj.filters
rename to tools/msvc2015/picomodellib.vcxproj.filters
diff --git a/tools/msvc2013/post_build_event.cmd b/tools/msvc2015/post_build_event.cmd
similarity index 54%
rename from tools/msvc2013/post_build_event.cmd
rename to tools/msvc2015/post_build_event.cmd
index 2fff859..91c9d91 100644
--- a/tools/msvc2013/post_build_event.cmd
+++ b/tools/msvc2015/post_build_event.cmd
@@ -11,14 +11,14 @@ IF "%1" == "Debug" (SET WXLIB_SUFFIX=ud) ELSE (SET WXLIB_SUFFIX=u)
 
 @echo Copying wxWidgets binaries
 
-copy ..\..\w32deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_gl_%2.dll	   ..\..\install /Y
-copy ..\..\w32deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_html_%2.dll  ..\..\install /Y
-copy ..\..\w32deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_xrc_%2.dll   ..\..\install /Y
-copy ..\..\w32deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_stc_%2.dll   ..\..\install /Y
-copy ..\..\w32deps\wxWidgets\bin\wxbase30%WXLIB_SUFFIX%_%2.dll	   ..\..\install /Y
-copy ..\..\w32deps\wxWidgets\bin\wxbase30%WXLIB_SUFFIX%_xml_%2.dll  ..\..\install /Y
-copy ..\..\w32deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_adv_%2.dll   ..\..\install /Y
-copy ..\..\w32deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_core_%2.dll  ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_gl_%2.dll	   ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_html_%2.dll  ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_xrc_%2.dll   ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_stc_%2.dll   ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxbase310%WXLIB_SUFFIX%_%2.dll	   ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxbase310%WXLIB_SUFFIX%_xml_%2.dll  ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_adv_%2.dll   ..\..\install /Y
+copy ..\..\w32deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_core_%2.dll  ..\..\install /Y
 
 IF "%1" == "Debug" (SET DEBUG_SUFFIX=-d) ELSE (SET DEBUG_SUFFIX=)
 
diff --git a/tools/msvc2013/post_build_event_x64.cmd b/tools/msvc2015/post_build_event_x64.cmd
similarity index 53%
rename from tools/msvc2013/post_build_event_x64.cmd
rename to tools/msvc2015/post_build_event_x64.cmd
index 5e28465..25c4b59 100644
--- a/tools/msvc2013/post_build_event_x64.cmd
+++ b/tools/msvc2015/post_build_event_x64.cmd
@@ -11,14 +11,14 @@ IF "%1" == "Debug" (SET WXLIB_SUFFIX=ud) ELSE (SET WXLIB_SUFFIX=u)
 
 @echo Copying wxWidgets binaries
 
-copy ..\..\w64deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_gl_%2_x64.dll	   ..\..\install /Y
-copy ..\..\w64deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_html_%2_x64.dll  ..\..\install /Y
-copy ..\..\w64deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_xrc_%2_x64.dll   ..\..\install /Y
-copy ..\..\w64deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_stc_%2_x64.dll   ..\..\install /Y
-copy ..\..\w64deps\wxWidgets\bin\wxbase30%WXLIB_SUFFIX%_%2_x64.dll	   ..\..\install /Y
-copy ..\..\w64deps\wxWidgets\bin\wxbase30%WXLIB_SUFFIX%_xml_%2_x64.dll  ..\..\install /Y
-copy ..\..\w64deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_adv_%2_x64.dll   ..\..\install /Y
-copy ..\..\w64deps\wxWidgets\bin\wxmsw30%WXLIB_SUFFIX%_core_%2_x64.dll  ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_gl_%2_x64.dll	   ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_html_%2_x64.dll  ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_xrc_%2_x64.dll   ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_stc_%2_x64.dll   ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxbase310%WXLIB_SUFFIX%_%2_x64.dll	   ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxbase310%WXLIB_SUFFIX%_xml_%2_x64.dll  ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_adv_%2_x64.dll   ..\..\install /Y
+copy ..\..\w64deps\wxWidgets\bin\wxmsw310%WXLIB_SUFFIX%_core_%2_x64.dll  ..\..\install /Y
 
 IF "%1" == "Debug" (SET DEBUG_SUFFIX=-d) ELSE (SET DEBUG_SUFFIX=)
 
diff --git a/tools/msvc2013/properties/Boost.props b/tools/msvc2015/properties/Boost.props
similarity index 100%
rename from tools/msvc2013/properties/Boost.props
rename to tools/msvc2015/properties/Boost.props
diff --git a/tools/msvc2013/properties/DarkRadiant Base Debug Win32.props b/tools/msvc2015/properties/DarkRadiant Base Debug Win32.props
similarity index 100%
rename from tools/msvc2013/properties/DarkRadiant Base Debug Win32.props
rename to tools/msvc2015/properties/DarkRadiant Base Debug Win32.props
diff --git a/tools/msvc2013/properties/DarkRadiant Base Debug x64.props b/tools/msvc2015/properties/DarkRadiant Base Debug x64.props
similarity index 85%
rename from tools/msvc2013/properties/DarkRadiant Base Debug x64.props
rename to tools/msvc2015/properties/DarkRadiant Base Debug x64.props
index 66d3dc1..fcba6b5 100644
--- a/tools/msvc2013/properties/DarkRadiant Base Debug x64.props	
+++ b/tools/msvc2015/properties/DarkRadiant Base Debug x64.props	
@@ -12,6 +12,9 @@
   <ItemDefinitionGroup>
     <Link />
     <Link />
+    <ResourceCompile>
+      <PreprocessorDefinitions>_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ResourceCompile>
   </ItemDefinitionGroup>
   <ItemGroup>
     <BuildMacro Include="PlatformDepsDir">
diff --git a/tools/msvc2013/properties/DarkRadiant Base Release Win32.props b/tools/msvc2015/properties/DarkRadiant Base Release Win32.props
similarity index 100%
rename from tools/msvc2013/properties/DarkRadiant Base Release Win32.props
rename to tools/msvc2015/properties/DarkRadiant Base Release Win32.props
diff --git a/tools/msvc2013/properties/DarkRadiant Base Release x64.props b/tools/msvc2015/properties/DarkRadiant Base Release x64.props
similarity index 80%
rename from tools/msvc2013/properties/DarkRadiant Base Release x64.props
rename to tools/msvc2015/properties/DarkRadiant Base Release x64.props
index 6261d9f..f2cb49d 100644
--- a/tools/msvc2013/properties/DarkRadiant Base Release x64.props	
+++ b/tools/msvc2015/properties/DarkRadiant Base Release x64.props	
@@ -10,7 +10,11 @@
     <WxWidgetsLibDir>vc$(PlatformToolsetVersion)_x64_dll</WxWidgetsLibDir>
   </PropertyGroup>
   <PropertyGroup />
-  <ItemDefinitionGroup />
+  <ItemDefinitionGroup>
+    <ResourceCompile>
+      <PreprocessorDefinitions>_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ResourceCompile>
+  </ItemDefinitionGroup>
   <ItemGroup>
     <BuildMacro Include="PlatformDepsDir">
       <Value>$(PlatformDepsDir)</Value>
diff --git a/tools/msvc2013/properties/DarkRadiant Base.props b/tools/msvc2015/properties/DarkRadiant Base.props
similarity index 100%
rename from tools/msvc2013/properties/DarkRadiant Base.props
rename to tools/msvc2015/properties/DarkRadiant Base.props
diff --git a/tools/msvc2013/properties/GLEW.props b/tools/msvc2015/properties/GLEW.props
similarity index 100%
rename from tools/msvc2013/properties/GLEW.props
rename to tools/msvc2015/properties/GLEW.props
diff --git a/tools/msvc2013/properties/OpenAL + Vorbis.props b/tools/msvc2015/properties/OpenAL + Vorbis.props
similarity index 100%
rename from tools/msvc2013/properties/OpenAL + Vorbis.props
rename to tools/msvc2015/properties/OpenAL + Vorbis.props
diff --git a/tools/msvc2013/properties/Python.props b/tools/msvc2015/properties/Python.props
similarity index 100%
rename from tools/msvc2013/properties/Python.props
rename to tools/msvc2015/properties/Python.props
diff --git a/tools/msvc2013/properties/ftgl.props b/tools/msvc2015/properties/ftgl.props
similarity index 100%
rename from tools/msvc2013/properties/ftgl.props
rename to tools/msvc2015/properties/ftgl.props
diff --git a/tools/msvc2013/properties/libpng.props b/tools/msvc2015/properties/libpng.props
similarity index 100%
rename from tools/msvc2013/properties/libpng.props
rename to tools/msvc2015/properties/libpng.props
diff --git a/tools/msvc2013/properties/libxml2.props b/tools/msvc2015/properties/libxml2.props
similarity index 100%
rename from tools/msvc2013/properties/libxml2.props
rename to tools/msvc2015/properties/libxml2.props
diff --git a/tools/msvc2013/properties/win_iconv.props b/tools/msvc2015/properties/win_iconv.props
similarity index 100%
rename from tools/msvc2013/properties/win_iconv.props
rename to tools/msvc2015/properties/win_iconv.props
diff --git a/tools/msvc2013/properties/wxWidgets.props b/tools/msvc2015/properties/wxWidgets.props
similarity index 100%
rename from tools/msvc2013/properties/wxWidgets.props
rename to tools/msvc2015/properties/wxWidgets.props
diff --git a/tools/msvc2013/properties/zlib.props b/tools/msvc2015/properties/zlib.props
similarity index 100%
rename from tools/msvc2013/properties/zlib.props
rename to tools/msvc2015/properties/zlib.props
diff --git a/tools/msvc2013/scenegraph.vcxproj b/tools/msvc2015/scenegraph.vcxproj
similarity index 98%
rename from tools/msvc2013/scenegraph.vcxproj
rename to tools/msvc2015/scenegraph.vcxproj
index 1031a53..d5fbca8 100644
--- a/tools/msvc2013/scenegraph.vcxproj
+++ b/tools/msvc2015/scenegraph.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/scenegraph.vcxproj.filters b/tools/msvc2015/scenegraph.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/scenegraph.vcxproj.filters
rename to tools/msvc2015/scenegraph.vcxproj.filters
diff --git a/tools/msvc2013/scenelib.vcxproj b/tools/msvc2015/scenelib.vcxproj
similarity index 96%
rename from tools/msvc2013/scenelib.vcxproj
rename to tools/msvc2015/scenelib.vcxproj
index 668bcb4..f41b3c0 100644
--- a/tools/msvc2013/scenelib.vcxproj
+++ b/tools/msvc2015/scenelib.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -24,19 +24,19 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -135,6 +135,7 @@
   <ItemGroup>
     <ClCompile Include="..\..\libs\scene\InstanceWalkers.cpp" />
     <ClCompile Include="..\..\libs\scene\Node.cpp" />
+    <ClCompile Include="..\..\libs\scene\SelectableNode.cpp" />
     <ClCompile Include="..\..\libs\scene\TraversableNodeSet.cpp" />
   </ItemGroup>
   <ItemGroup>
@@ -142,6 +143,7 @@
     <ClInclude Include="..\..\libs\scene\InstanceWalkers.h" />
     <ClInclude Include="..\..\libs\scene\LayerValidityCheckWalker.h" />
     <ClInclude Include="..\..\libs\scene\Node.h" />
+    <ClInclude Include="..\..\libs\scene\SelectableNode.h" />
     <ClInclude Include="..\..\libs\scene\TraversableNodeSet.h" />
     <ClInclude Include="..\..\libs\scenelib.h" />
     <ClInclude Include="..\..\libs\selectionlib.h" />
diff --git a/tools/msvc2013/scenelib.vcxproj.filters b/tools/msvc2015/scenelib.vcxproj.filters
similarity index 85%
rename from tools/msvc2013/scenelib.vcxproj.filters
rename to tools/msvc2015/scenelib.vcxproj.filters
index c4e1f02..74bfe91 100644
--- a/tools/msvc2013/scenelib.vcxproj.filters
+++ b/tools/msvc2015/scenelib.vcxproj.filters
@@ -15,6 +15,9 @@
     <ClCompile Include="..\..\libs\scene\TraversableNodeSet.cpp">
       <Filter>scene</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\libs\scene\SelectableNode.cpp">
+      <Filter>scene</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\libs\scene\InstanceWalkers.h">
@@ -34,5 +37,8 @@
     <ClInclude Include="..\..\libs\scene\BasicRootNode.h">
       <Filter>scene</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\libs\scene\SelectableNode.h">
+      <Filter>scene</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/tools/msvc2013/script.vcxproj b/tools/msvc2015/script.vcxproj
similarity index 98%
rename from tools/msvc2013/script.vcxproj
rename to tools/msvc2015/script.vcxproj
index f933a15..bcc9fd7 100644
--- a/tools/msvc2013/script.vcxproj
+++ b/tools/msvc2015/script.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/script.vcxproj.filters b/tools/msvc2015/script.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/script.vcxproj.filters
rename to tools/msvc2015/script.vcxproj.filters
diff --git a/tools/msvc2013/shaders.vcxproj b/tools/msvc2015/shaders.vcxproj
similarity index 98%
rename from tools/msvc2013/shaders.vcxproj
rename to tools/msvc2015/shaders.vcxproj
index 3758966..de93b24 100644
--- a/tools/msvc2013/shaders.vcxproj
+++ b/tools/msvc2015/shaders.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/shaders.vcxproj.filters b/tools/msvc2015/shaders.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/shaders.vcxproj.filters
rename to tools/msvc2015/shaders.vcxproj.filters
diff --git a/tools/msvc2013/skins.vcxproj b/tools/msvc2015/skins.vcxproj
similarity index 98%
rename from tools/msvc2013/skins.vcxproj
rename to tools/msvc2015/skins.vcxproj
index c14dbdb..efa5572 100644
--- a/tools/msvc2013/skins.vcxproj
+++ b/tools/msvc2015/skins.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/skins.vcxproj.filters b/tools/msvc2015/skins.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/skins.vcxproj.filters
rename to tools/msvc2015/skins.vcxproj.filters
diff --git a/tools/msvc2013/sound.vcxproj b/tools/msvc2015/sound.vcxproj
similarity index 98%
rename from tools/msvc2013/sound.vcxproj
rename to tools/msvc2015/sound.vcxproj
index 3f4e527..c357dbb 100644
--- a/tools/msvc2013/sound.vcxproj
+++ b/tools/msvc2015/sound.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/sound.vcxproj.filters b/tools/msvc2015/sound.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/sound.vcxproj.filters
rename to tools/msvc2015/sound.vcxproj.filters
diff --git a/tools/msvc2013/uimanager.vcxproj b/tools/msvc2015/uimanager.vcxproj
similarity index 98%
rename from tools/msvc2013/uimanager.vcxproj
rename to tools/msvc2015/uimanager.vcxproj
index 4a4d9af..b6b8678 100644
--- a/tools/msvc2013/uimanager.vcxproj
+++ b/tools/msvc2015/uimanager.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/uimanager.vcxproj.filters b/tools/msvc2015/uimanager.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/uimanager.vcxproj.filters
rename to tools/msvc2015/uimanager.vcxproj.filters
diff --git a/tools/msvc2013/undo.vcxproj b/tools/msvc2015/undo.vcxproj
similarity index 98%
rename from tools/msvc2013/undo.vcxproj
rename to tools/msvc2015/undo.vcxproj
index 8ca8991..c0d0c59 100644
--- a/tools/msvc2013/undo.vcxproj
+++ b/tools/msvc2015/undo.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/undo.vcxproj.filters b/tools/msvc2015/undo.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/undo.vcxproj.filters
rename to tools/msvc2015/undo.vcxproj.filters
diff --git a/tools/msvc2013/vfspk3.vcxproj b/tools/msvc2015/vfspk3.vcxproj
similarity index 98%
rename from tools/msvc2013/vfspk3.vcxproj
rename to tools/msvc2015/vfspk3.vcxproj
index 01468e0..47e70b8 100644
--- a/tools/msvc2013/vfspk3.vcxproj
+++ b/tools/msvc2015/vfspk3.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/vfspk3.vcxproj.filters b/tools/msvc2015/vfspk3.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/vfspk3.vcxproj.filters
rename to tools/msvc2015/vfspk3.vcxproj.filters
diff --git a/tools/msvc2013/wavefront.vcxproj b/tools/msvc2015/wavefront.vcxproj
similarity index 100%
rename from tools/msvc2013/wavefront.vcxproj
rename to tools/msvc2015/wavefront.vcxproj
diff --git a/tools/msvc2013/wavefront.vcxproj.filters b/tools/msvc2015/wavefront.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/wavefront.vcxproj.filters
rename to tools/msvc2015/wavefront.vcxproj.filters
diff --git a/tools/msvc2013/wxutillib.vcxproj b/tools/msvc2015/wxutillib.vcxproj
similarity index 98%
rename from tools/msvc2013/wxutillib.vcxproj
rename to tools/msvc2015/wxutillib.vcxproj
index de3f7d5..f0996f7 100644
--- a/tools/msvc2013/wxutillib.vcxproj
+++ b/tools/msvc2015/wxutillib.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -26,22 +26,22 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
diff --git a/tools/msvc2013/wxutillib.vcxproj.filters b/tools/msvc2015/wxutillib.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/wxutillib.vcxproj.filters
rename to tools/msvc2015/wxutillib.vcxproj.filters
diff --git a/tools/msvc2013/xmlregistry.vcxproj b/tools/msvc2015/xmlregistry.vcxproj
similarity index 98%
rename from tools/msvc2013/xmlregistry.vcxproj
rename to tools/msvc2015/xmlregistry.vcxproj
index dd1324f..d7d701d 100644
--- a/tools/msvc2013/xmlregistry.vcxproj
+++ b/tools/msvc2015/xmlregistry.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -28,23 +28,23 @@
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
     <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/msvc2013/xmlregistry.vcxproj.filters b/tools/msvc2015/xmlregistry.vcxproj.filters
similarity index 100%
rename from tools/msvc2013/xmlregistry.vcxproj.filters
rename to tools/msvc2015/xmlregistry.vcxproj.filters
diff --git a/tools/msvc2013/xmlutillib.vcxproj b/tools/msvc2015/xmlutillib.vcxproj
similarity index 97%
rename from tools/msvc2013/xmlutillib.vcxproj
rename to tools/msvc2015/xmlutillib.vcxproj
index 60dd6e2..f9026c9 100644
--- a/tools/msvc2013/xmlutillib.vcxproj
+++ b/tools/msvc2015/xmlutillib.vcxproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -25,19 +25,19 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
diff --git a/tools/scripts/build_boost_libs.cmd b/tools/scripts/build_boost_libs.cmd
index 317c709..71b0761 100644
--- a/tools/scripts/build_boost_libs.cmd
+++ b/tools/scripts/build_boost_libs.cmd
@@ -4,45 +4,22 @@ rem Launch this file in the boost/ folder.
 rem Check if we're in the correct folder
 if not exist libs goto :error
 if not exist boost goto :error
+if not exist b2.exe goto :error
 
 mkdir stage
 
-cd libs\filesystem\build
-bjam --toolset=msvc link=static threading=multi release stage
-bjam --toolset=msvc link=static threading=multi debug stage
-copy stage\*.lib ..\..\..\stage
+b2 toolset=msvc variant=release,debug link=static threading=multi stage /boost/python /boost/filesystem /boost/regex /boost/system
 
-cd ..\..\..\libs\system\build
-bjam --toolset=msvc link=static threading=multi release stage
-bjam --toolset=msvc link=static threading=multi debug stage
-copy stage\*.lib ..\..\..\stage
-
-cd ..\..\..\libs\python\build
-bjam --toolset=msvc link=static threading=multi release stage
-bjam --toolset=msvc link=static threading=multi debug stage
-copy stage\*.lib ..\..\..\stage
-
-cd ..\..\..\libs\regex\build
-bjam --toolset=msvc link=static threading=multi release stage
-bjam --toolset=msvc link=static threading=multi debug stage
-copy stage\*.lib ..\..\..\stage
-
-rem cd ..\..\..\libs\signals\build
-rem bjam --toolset=msvc link=static threading=multi release stage
-rem bjam --toolset=msvc link=static threading=multi debug stage
-rem copy stage\*.lib ..\..\..\stage
-
-cd ..\..\..\stage
-start .
+start stage
 
 goto :success
 
 :error
 echo Please launch this file in the boost folder you downloaded and extracted from sourceforge.
-echo Note that you need to have the path to bjam.exe in your PATH environment variable.
-echo 
+echo Run the bootstrap.bat file to generate the b2.exe file needed for the build process.
+echo __________________________________________________________________________________________
 echo Example: 
-echo   cd c:\Downloads\boost_1_55_0\
+echo   cd c:\Downloads\boost_1_61_0\
 echo   c:\Games\DarkRadiant\tools\scripts\build_boost_libs.cmd
 goto :eof
 
diff --git a/tools/scripts/build_boost_libs.x64.cmd b/tools/scripts/build_boost_libs.x64.cmd
index 2935536..50acc81 100644
--- a/tools/scripts/build_boost_libs.x64.cmd
+++ b/tools/scripts/build_boost_libs.x64.cmd
@@ -4,46 +4,23 @@ rem Launch this file in the boost/ folder.
 rem Check if we're in the correct folder
 if not exist libs goto :error
 if not exist boost goto :error
+if not exist b2.exe goto :error
 
 mkdir stage
 
-cd libs\filesystem\build
-bjam --toolset=msvc address-model=64 threading=multi link=static release stage
-bjam --toolset=msvc address-model=64 threading=multi link=static debug stage
-copy stage\*.lib ..\..\..\stage
+b2 toolset=msvc variant=release,debug link=static threading=multi address-model=64 stage /boost/python /boost/filesystem /boost/regex /boost/system
 
-cd ..\..\..\libs\system\build
-bjam --toolset=msvc address-model=64 threading=multi link=static release stage
-bjam --toolset=msvc address-model=64 threading=multi link=static debug stage
-copy stage\*.lib ..\..\..\stage
-
-cd ..\..\..\libs\python\build
-bjam --toolset=msvc address-model=64 threading=multi link=static release stage
-bjam --toolset=msvc address-model=64 threading=multi link=static debug stage
-copy stage\*.lib ..\..\..\stage
-
-cd ..\..\..\libs\regex\build
-bjam --toolset=msvc address-model=64 threading=multi link=static release stage
-bjam --toolset=msvc address-model=64 threading=multi link=static debug stage
-copy stage\*.lib ..\..\..\stage
-
-rem cd ..\..\..\libs\signals\build
-rem bjam --toolset=msvc address-model=64 threading=multi link=static release stage
-rem bjam --toolset=msvc address-model=64 threading=multi link=static debug stage
-rem copy stage\*.lib ..\..\..\stage
-
-cd ..\..\..\stage
-start .
+start stage
 
 goto :success
 
 :error
 echo Please launch this file in the boost folder you downloaded and extracted from sourceforge.
-echo Note that you need to have the path to bjam.exe in your PATH environment variable.
+echo Run the bootstrap.bat file to generate the b2.exe file needed for the build process.
 echo __________________________________________________________________________________________
 echo Example: 
-echo   cd c:\Downloads\boost_1_55_0\
-echo   c:\Games\DarkRadiant\tools\scripts\build_boost_libs.cmd
+echo   cd c:\Downloads\boost_1_61_0\
+echo   c:\Games\DarkRadiant\tools\scripts\build_boost_libs.x64.cmd
 goto :eof
 
 :success
diff --git a/tools/scripts/compile_release_package.ps1 b/tools/scripts/compile_release_package.ps1
index 7e8783b..c312d03 100644
--- a/tools/scripts/compile_release_package.ps1
+++ b/tools/scripts/compile_release_package.ps1
@@ -8,6 +8,17 @@ if ($args.Count -eq 0 -or ($args[0] -ne "x64" -and $args[0] -ne "x86"))
     return
 }
 
+$skipbuild = $false
+
+foreach ($arg in $args)
+{
+	if ($arg -eq "skipbuild")
+	{
+		Write-Host "skipbuild: Will skip the build process."
+		$skipbuild = $true
+	}
+}
+
 # Check tool reachability
 if ((Get-Command "compil32" -ErrorAction SilentlyContinue) -eq $null)
 {
@@ -36,19 +47,53 @@ if ($target -eq "x86")
     $platform = "Win32"
     $copyFilesCmd = ".\copy_install_files.cmd"
     $issFile = "..\innosetup\darkradiant.iss"
-    $portableFilesFolder = "DarkRadiant_install"
+    $portablePath = "DarkRadiant_install"
 } 
 else
 {
     $platform = "x64"
     $copyFilesCmd = ".\copy_install_files.x64.cmd"
     $issFile = "..\innosetup\darkradiant.x64.iss"
-    $portableFilesFolder = "DarkRadiant_install.x64"
+    $portablePath = "DarkRadiant_install.x64"
 }
 
-Start-Process "msbuild" -ArgumentList ("..\msvc2013\DarkRadiant.sln", "/p:configuration=release", "/t:rebuild", "/p:platform=$platform", "/maxcpucount:4") -NoNewWindow -Wait
+if (-not $skipbuild)
+{
+	Start-Process "msbuild" -ArgumentList ("..\msvc2015\DarkRadiant.sln", "/p:configuration=release", "/t:rebuild", "/p:platform=$platform", "/maxcpucount:4") -NoNewWindow -Wait
+}
+
+# Copy files to portable files folder
+
+$pathToCheck = "..\..\..\$portablePath"
+$portableFilesFolder = Get-Item -Path $pathToCheck
+
+if ($portableFilesFolder -eq $null)
+{
+	$portableFilesFolder = New-Item -Path $pathToCheck -ItemType Directory -ErrorAction Stop
+}
 
-Start-Process $copyFilesCmd -NoNewWindow -Wait
+Write-Host ("Clearing output folder {0}" -f $portableFilesFolder)
+Get-ChildItem -Path $portableFilesFolder | Remove-Item -Recurse -Force
+
+Write-Host ("Copying files...")
+
+$installFolder = Get-Item "..\..\install"
+$excludes = @('*.exp', '*.lib', '*.iobj', '*.ipdb', '*.suo', '*.pgd', '*.fbp', 'darkradiant.desktop.in')
+
+Get-ChildItem $installFolder -Recurse -Exclude $excludes | Copy-Item -Destination { Join-Path $portableFilesFolder $_.FullName.Substring($installFolder.FullName.Length) }
+
+# Copy the VC++ redist files
+$vcFolder = Get-Item "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.CRT" -Erroraction SilentlyContinue
+
+if ($vcFolder -ne $null)
+{
+	Get-ChildItem "msvcp140.dll" -Path $vcFolder | Copy-Item -Destination $portableFilesFolder
+	Get-ChildItem "vcruntime140.dll" -Path $vcFolder | Copy-Item -Destination $portableFilesFolder
+}
+else
+{
+	Write-Host -ForegroundColor Yellow "Warning: cannot find the VC++ redist folder, won't copy runtime DLLs."
+}
 
 # Get the version from the innosetup file
 $content = Get-Content $issFile
@@ -76,11 +121,11 @@ if ((Get-ChildItem -Path $portableFilename -ErrorAction SilentlyContinue) -ne $n
 {
     Remove-Item -Path $portableFilename
 }
-Start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList ("a", "-r", "-x!*.pdb", "-mx9", "-mmt2", $portableFilename, "..\..\..\$portableFilesFolder\*.*")
+Start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList ("a", "-r", "-x!*.pdb", "-mx9", "-mmt2", $portableFilename, "..\..\..\$portablePath\*.*")
 
 # Compress Program Database Files
 if ((Get-ChildItem -Path $pdbFilename -ErrorAction SilentlyContinue) -ne $null)
 {
     Remove-Item -Path $pdbFilename
 }
-Start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList ("a", "-r", "-mx9", "-mmt2", $pdbFilename, "..\..\..\$portableFilesFolder\*.pdb") -Wait
+Start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList ("a", "-r", "-mx9", "-mmt2", $pdbFilename, "..\..\..\$portablePath\*.pdb") -Wait
diff --git a/tools/scripts/copy_install_files.cmd b/tools/scripts/copy_install_files.cmd
deleted file mode 100644
index f758f2b..0000000
--- a/tools/scripts/copy_install_files.cmd
+++ /dev/null
@@ -1,10 +0,0 @@
-md ..\..\..\DarkRadiant_install
-del ..\..\..\DarkRadiant_install\*.* /S /Q
-xcopy ..\..\install\*.* /s ..\..\..\DarkRadiant_install\
-del ..\..\..\DarkRadiant_install\*.exp /S /Q
-rem del ..\..\..\DarkRadiant_install\*.pdb /S /Q
-del ..\..\..\DarkRadiant_install\*.lib /S /Q
-del ..\..\..\DarkRadiant_install\*.suo /S /Q
-del ..\..\..\DarkRadiant_install\*.pgd /S /Q
-del ..\..\..\DarkRadiant_install\*.fbp /S /Q
-del ..\..\..\DarkRadiant_install\darkradiant.desktop.in
\ No newline at end of file
diff --git a/tools/scripts/copy_install_files.x64.cmd b/tools/scripts/copy_install_files.x64.cmd
deleted file mode 100644
index 6d9434d..0000000
--- a/tools/scripts/copy_install_files.x64.cmd
+++ /dev/null
@@ -1,10 +0,0 @@
-md ..\..\..\DarkRadiant_install.x64
-del ..\..\..\DarkRadiant_install.x64\*.* /S /Q
-xcopy ..\..\install\*.* /s ..\..\..\DarkRadiant_install.x64\
-del ..\..\..\DarkRadiant_install.x64\*.exp /S /Q
-rem del ..\..\..\DarkRadiant_install.x64\*.pdb /S /Q
-del ..\..\..\DarkRadiant_install.x64\*.lib /S /Q
-del ..\..\..\DarkRadiant_install.x64\*.suo /S /Q
-del ..\..\..\DarkRadiant_install.x64\*.pgd /S /Q
-del ..\..\..\DarkRadiant_install.x64\*.fbp /S /Q
-del ..\..\..\DarkRadiant_install.x64\darkradiant.desktop.in
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/darkradiant.git



More information about the Pkg-games-commits mailing list