/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.

--- HISTORICAL COMMENTS FOLLOW ---

 * $Logfile: /DescentIII/Main/editor/editorView.cpp $
 * $Revision: 1.1.1.1 $
 * $Date: 2003-08-26 03:57:37 $
 * $Author: kevinb $
 *
 * Main Editor View which contains the different views of the mine
 *
 * $Log: not supported by cvs2svn $
 *
 * 72    10/14/99 4:21p Matt
 * Added code to remove duplicate faces from a room.
 *
 * 71    10/07/99 3:12p Matt
 * Added a function to flip the view 180 degrees.
 *
 * 70    9/15/99 1:55p Matt
 * Added the option to allow rooms or groups placed on the terrain to
 * either align with the terrain or with gravity.
 *
 * 69    9/07/99 12:10p Matt
 * Added a function to propagate a texture to all adjacent coplanar faces
 * in a room.
 *
 * 68    8/24/99 4:24p Matt
 * Added a function to rotate a placed room by 45 degrees.
 *
 * 67    5/08/99 6:39p Matt
 * Added a function to delete a face and all faces connected to it.
 *
 * 66    5/08/99 1:38a Matt
 * Fixed typo.
 *
 * 65    4/30/99 6:52p Matt
 * Added a function to merge an object's geometry into a room.
 *
 * 64    4/28/99 12:51a Matt
 * Added some funtions to edit face geometry.
 *
 * 63    4/19/99 12:10a Matt
 * Added a menu item to delete a vertex from a face.
 *
 * 62    4/06/99 10:23a Matt
 *
 * 61    3/31/99 12:58p Matt
 * Added snap-point-to-face
 *
 * 60    3/29/99 6:46p Matt
 * Added menu item to fix/remove degenerate faces
 *
 * 59    3/27/99 7:10p Matt
 * Strip leading & trailing spaces before check for duplicate names.
 *
 * 58    3/23/99 11:15p Matt
 * Added message when rooms combined.
 *
 * 57    3/23/99 5:12p Matt
 * Added function to combine rooms.
 *
 * 56    3/01/99 10:39p Matt
 * Added menu options to set & clear room center points
 *
 * 55    3/01/99 10:39a Matt
 * Strip leading and trailing spaces from object, trigger, and room names
 * on level load and when the names are entered.
 *
 * 54    2/28/99 10:40p Matt
 * Check for name already in use when entering a name.
 *
 * 53    2/28/99 6:31p Matt
 * Disable z-buffer button if OpenGL is being used in the editor.
 *
 * 52    2/03/99 1:10p Matt
 * Changed the paletted room current faces to be stored in a seperate
 * array, instead of in the room structure.
 *
 * 51    1/25/99 11:12a Matt
 * If the marked face is deleted, reset it.
 *
 * 50    1/21/99 11:15p Jeff
 * pulled out some structs and defines from header files and moved them
 * into seperate header files so that multiplayer dlls don't require major
 * game headers, just those new headers.  Side effect is a shorter build
 * time.  Also cleaned up some header file #includes that weren't needed.
 * This affected polymodel.h, object.h, player.h, vecmat.h, room.h,
 * manage.h and multi.h
 *
 * 49    1/15/99 10:50a Matt
 * Added rename room to Room menu & added shortcut key
 *
 * 48    12/30/98 6:49p Matt
 * When un-placing a door, free the room that the placed door was using
 *
 * 47    12/23/98 10:53a Matt
 * Added functions to create a face
 *
 * 46    10/05/98 9:45a Matt
 * Use windows function to get cursor position instead of ddio function.
 *
 * 45    10/03/98 11:21p Matt
 * Added system to seperately control outline mode for mine, terrain, sky,
 * & objects
 *
 * 44    10/03/98 8:31p Matt
 * Added Join Rooms Exact function.
 *
 * 43    9/18/98 1:27p Jason
 * cleaned up renderer initting
 *
 * 42    9/09/98 4:07p Matt
 * Added Smooth Bridge function
 *
 * 41    9/08/98 6:12p Matt
 * Added code to check for and fix duplicate and unused points.
 *
 * 40    9/08/98 12:06p Jason
 * added ReInit openGL button
 *
 * 39    9/07/98 10:58p Matt
 * Added snap point to point.
 *
 * 38    9/04/98 3:34p Matt
 * Added groovy vertex snap code
 *
 * 37    9/04/98 12:29p Matt
 * Added marked edge & vertex in the editor
 *
 * 36    8/31/98 4:36p Matt
 * Added status message
 *
 * 35    6/25/98 7:15p Matt
 * Added a function to delete a pair of portals.
 *
 * 34    6/23/98 2:45p Matt
 * Added option when propagating to all faces in a room to only do so to
 * ones with the same texture.
 *
 * 33    6/08/98 12:28p Matt
 * Added a function to triangulate a face
 *
 * 32    5/22/98 4:47p Matt
 * Added menu item to propagate a texture to all the faces in a room.
 *
 * 31    4/27/98 6:41p Matt
 * Added code to join all adjacent faces between two rooms
 *
 * 30    4/16/98 11:50a Matt
 * Made snap code work for placed groups.
 *
 * 29    2/16/98 1:27p Matt
 * Added function to snap the placed room to a vertex/edge on the base
 * room/face
 *
 * 28    2/11/98 12:39p Matt
 * Implemented Delete Face menu item.
 *
 * 27    1/29/98 2:15p Samir
 * Implemented ObjectListModeless and Toolbar button.
 *
 * 26    1/27/98 6:15p Samir
 * Added Object toolbar button.
 *
 * 25    1/20/98 4:08p Matt
 * Added function to swap current and marked room:face.  Added some
 * addition error checking to bridge function.
 *
 * 24    1/06/98 12:39p Matt
 * Added some error checking
 *
 * 23    11/05/97 7:13p Matt
 * Added join rooms function
 *
 * 22    10/03/97 3:37p Matt
 * Added menu item to place a building on the terrain
 *
 * 21    9/24/97 3:22p Matt
 * Added Drop Room function
 *
 * 20    9/17/97 1:24p Matt
 * Ripped out segment code
 *
 * 19    9/17/97 11:21a Matt
 * Ripped out segment code
 *
 * 18    9/16/97 4:09p Jason
 * implemented software zbuffer
 *
 * 17    9/14/97 11:06p Matt
 * Fixed up Room menu
 *
 * 16    9/11/97 5:46p Jason
 * first pass at getting doors to work with room engine
 *
 * 15    8/29/97 3:37p Samir
 * Took out segment tab constant.
 *
 * 14    8/22/97 9:32a Matt
 * Don't allow place room to a face that already has a room on it.
 *
 * 13    8/21/97 6:00p Matt
 * Added delete current room function
 *
 * 12    8/19/97 5:58p Samir
 * OLE additions
 *
 * 11    8/19/97 10:54a Matt
 * Added remove placed room option
 *
 * 10    8/18/97 6:59p Matt
 * Implemented Place Room/Attach room system
 *
 * 9     8/04/97 12:52p Matt
 * Added hooks for mark & bridge
 *
 * 8     8/01/97 6:10p Matt
 * Added hooks for attach room
 *
 * 7     7/28/97 11:29a Samir
 * Fixed editor->game goofiness.  Isolated GrWnd creation and destruction
 * to a couple of functions.
 *
 * 6     7/25/97 6:16p Samir
 * Fixed the tile-cascade switching/default madness.
 *
 * 5     7/22/97 7:07p Matt
 * Cleaned up D3EditState, moving some vars in and some out, and renaming
 * and changing a few others
 *
 * 4     7/22/97 10:33a Matt
 * Added functions for new room menu
 *
 * 29    6/30/97 1:29p Jason
 * added netherspace stuff
 *
 * 28    6/03/97 4:55p Mark
 *
 * 28	6/3/97	4:38p Jeff
 *	Added Context Sensitive Help for Keypads
 *
 * 27    5/27/97 3:30p Matt
 * Added editor button to toggle lighting
 *
 * 26    5/08/97 7:29p Matt
 * Made seperate viewers for mine & terrain, and cleaned up code the
 * switched between modes
 *
 * 25    5/06/97 3:49p Matt
 * Unlink/relink viewer object when switching between mine and terrain
 * views
 *
 * 24    5/06/97 1:17p Jason
 * checked in for matt
 *
 * 23    4/03/97 1:17p Matt
 * Took out support for toolbar movement mode toggle button, now that that
 * function has been moved to the object keypad.
 *
 * 22    4/02/97 8:07p Matt
 * Added variable & button to toggle object move state
 *
 * 21    3/31/97 5:57p Matt
 * Revamped mine update flags
 *
 * 20    3/21/97 5:02p Jason
 * make player_object switch the OF_OVER_TERRAIN flag to on when changing
 * to terrain view...temporary
 *
 * 19    3/17/97 7:41p Samir
 * Fixed editor interface problems with updating triggers and doorways in
 * keypads when loading new levels (must fix some more).
 *
 * 18    3/13/97 6:12p Jason
 * set default height above terrain
 *
 * 17    3/07/97 4:54p Jason
 * made editor save position of terrain/mine when switching between modes
 *
 *
 * 16    3/07/97 4:18p Jason
 * implemented terrain functionality in the editor
 *
 * 15    2/28/97 6:37p Matt
 * Added variable & toggle button for box selection mode
 *
 * 14    2/20/97 1:30p Samir
 * Defined main_view.
 *
 * 13    2/11/97 1:42p Samir
 * Moved file opening and closing to editorDoc where it should be.
 *
 * 12    2/10/97 5:40p Matt
 * Code to handle several File menu options
 *
 * 11    2/07/97 7:41p Matt
 * Hooked in next & prev segment & side funcs
 *
 * 10    1/23/97 4:09p Samir
 * Added stuff for toggling wireframe, texture windows, tiling or
 * cascading
 *
 * 9     1/21/97 12:51p Samir
 * GrWnds created based on D3EditState info
 *
 * $NoKeywords: $
 */

#include "stdafx.h"
#include "editor.h"

#include "editorDoc.h"
#include "editorView.h"
#include "MainFrm.h"
#include "TextureDialog.h"
#include "EditLineDialog.h"
#include "RoomKeypadDialog.h"
#include "game.h"

#include "d3edit.h"
#include "mono.h"

#include "render.h"
#include "HRoom.h"
#include "HFile.h"
#include "erooms.h"
#include "args.h"

#include "terrain.h"

#include "mem.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CEditorView

IMPLEMENT_DYNCREATE(CEditorView, CView)

BEGIN_MESSAGE_MAP(CEditorView, CView)
//{{AFX_MSG_MAP(CEditorView)
ON_WM_SIZE()
ON_WM_DESTROY()
ON_COMMAND(ID_BUTTON_OUTLINE, OnButtonOutline)
ON_UPDATE_COMMAND_UI(ID_BUTTON_OUTLINE, OnUpdateButtonOutline)
ON_UPDATE_COMMAND_UI(ID_VIEW_TEXTUREMINE, OnUpdateViewTexturemine)
ON_COMMAND(ID_VIEW_TEXTUREMINE, OnViewTexturemine)
ON_UPDATE_COMMAND_UI(ID_VIEW_WIREFRAMEMINE, OnUpdateViewWireframemine)
ON_COMMAND(ID_VIEW_WIREFRAMEMINE, OnViewWireframemine)
ON_WM_PARENTNOTIFY()
ON_COMMAND(ID_WINDOW_TILE, OnWindowTile)
ON_COMMAND(ID_WINDOW_CASCADE, OnWindowCascade)
ON_COMMAND(ID_BUTTON_WINDOWSELECTION, OnButtonWindowSelection)
ON_UPDATE_COMMAND_UI(ID_BUTTON_WINDOWSELECTION, OnUpdateButtonWindowSelection)
ON_COMMAND(ID_BUTTON_LIGHTING, OnButtonLighting)
ON_UPDATE_COMMAND_UI(ID_BUTTON_LIGHTING, OnUpdateButtonLighting)
ON_WM_HELPINFO()
ON_COMMAND(ID_ROOM_ADD, OnRoomAdd)
ON_COMMAND(ID_ROOM_GRABTEXTURE, OnRoomGrabTexture)
ON_COMMAND(ID_ROOM_NEXTFACE, OnRoomNextFace)
ON_COMMAND(ID_ROOM_PREVIOUSFACE, OnRoomPreviousFace)
ON_COMMAND(ID_ROOM_SELECTBYNUMBER, OnRoomSelectByNumber)
ON_COMMAND(ID_ROOM_ATTACHROOM, OnRoomAttachRoom)
ON_COMMAND(ID_ROOM_BUILDBRIDGE, OnRoomBuildBridge)
ON_COMMAND(ID_ROOM_MARK, OnRoomMark)
ON_COMMAND(ID_ROOM_PLACEROOM, OnRoomPlaceRoom)
ON_UPDATE_COMMAND_UI(ID_ROOM_ATTACHROOM, OnUpdateRoomAttachRoom)
ON_COMMAND(ID_ROOM_UNPLACEROOM, OnRoomUnPlaceRoom)
ON_UPDATE_COMMAND_UI(ID_ROOM_UNPLACEROOM, OnUpdateRoomUnPlaceRoom)
ON_COMMAND(ID_ROOM_DELETE, OnRoomDelete)
ON_COMMAND(ID_ROOM_SELECTFACEBYNUMBER, OnRoomSelectFaceByNumber)
ON_COMMAND(ID_ROOM_SAVECURRENTROOM, OnRoomSaveCurrentRoom)
ON_COMMAND(ID_ZBUTTON, OnZbutton)
ON_UPDATE_COMMAND_UI(ID_ZBUTTON, OnUpdateZbutton)
ON_COMMAND(ID_ROOM_DROPROOM, OnRoomDropRoom)
ON_COMMAND(ID_ROOM_PLACETERRAINROOM, OnRoomPlaceTerrainRoom)
ON_COMMAND(ID_ROOM_JOINROOMS, OnRoomJoinRooms)
ON_COMMAND(ID_ROOM_SWAPMAKEDANDCURRENTROOMFACE, OnRoomSwapMarkedAndCurrentRoomFace)
ON_COMMAND(ID_ROOM_DELETEFACE, OnRoomDeleteFace)
ON_COMMAND(ID_ROOM_SNAPPLACEDROOM, OnRoomSnapPlacedRoom)
ON_UPDATE_COMMAND_UI(ID_ROOM_SNAPPLACEDROOM, OnUpdateRoomSnapPlacedRoom)
ON_COMMAND(ID_ROOM_JOIN_ADJACENT_FACES, OnRoomJoinAdjacentFaces)
ON_COMMAND(ID_ROOM_PROPAGATETOALL, OnRoomPropagateToAll)
ON_COMMAND(ID_ROOM_SPLITFACE, OnRoomSplitFace)
ON_COMMAND(ID_ROOM_DELETEPORTAL, OnRoomDeletePortal)
ON_COMMAND(ID_ROOM_SNAPPOINTTOEDGE, OnRoomSnapPointToEdge)
ON_COMMAND(ID_ROOM_UNDOSNAP, OnRoomUndoSnap)
ON_UPDATE_COMMAND_UI(ID_ROOM_UNDOSNAP, OnUpdateRoomUndoSnap)
ON_COMMAND(ID_ROOM_SNAPPOINTTOPOINT, OnRoomSnapPointToPoint)
ON_COMMAND(ID_REINIT_OPENGL, OnReinitOpengl)
ON_UPDATE_COMMAND_UI(ID_REINIT_OPENGL, OnUpdateReinitOpengl)
ON_COMMAND(ID_FILE_REMOVEEXTRAPOINTS, OnFileRemoveExtraPoints)
ON_COMMAND(ID_ROOM_BUILDSMOOTHBRIDGE, OnRoomBuildSmoothBridge)
ON_COMMAND(ID_ROOM_JOINROOMSEXACT, OnRoomJoinRoomsExact)
ON_COMMAND(ID_ROOM_STARTNEWFACE, OnRoomStartNewFace)
ON_COMMAND(ID_ROOM_FINISHNEWFACE, OnRoomFinishNewFace)
ON_COMMAND(ID_ROOM_ADDVERTTONEWFACE, OnRoomAddVertToNewFace)
ON_COMMAND(ID_ROOM_RENAMEROOM, OnRoomRenameRoom)
ON_COMMAND(ID_ROOM_SETCENTERFROMVIEWER, OnRoomSetCenterFromViewer)
ON_COMMAND(ID_ROOM_CLEARCENTERPOINT, OnRoomClearCenterPoint)
ON_UPDATE_COMMAND_UI(ID_ROOM_CLEARCENTERPOINT, OnUpdateRoomClearCenterPoint)
ON_UPDATE_COMMAND_UI(ID_ROOM_SETCENTERFROMVIEWER, OnUpdateRoomSetCenterFromViewer)
ON_COMMAND(ID_ROOM_COMBINE, OnRoomCombine)
ON_COMMAND(ID_FILE_FIXDEGENERATEFACES, OnFileFixDegenerateFaces)
ON_COMMAND(ID_ROOM_SNAPPOINTTOFACE, OnRoomSnapPointToFace)
ON_COMMAND(ID_ROOM_LINKTONEWEXTERNAL, OnRoomLinkToNewExternal)
ON_COMMAND(ID_ROOM_DELETEVERT, OnRoomDeleteVert)
ON_COMMAND(ID_ROOM_FACE_ADDVERTTOEDGE, OnRoomFaceAddVertToEdge)
ON_COMMAND(ID_ROOM_FACE_MOVEVERTONEDGE, OnRoomFaceMoveVertOnEdge)
ON_COMMAND(ID_ROOM_FACE_SPLITFACE, OnRoomFaceSplitFace)
ON_COMMAND(ID_ROOM_FACE_DELETEVERTONEDGE, OnRoomFaceDeleteVertOnEdge)
ON_COMMAND(ID_ROOM_MERGEOBJECTINTOROOM, OnRoomMergeObjectIntoRoom)
ON_COMMAND(ID_ROOM_DELETECONNECTEDFACES, OnRoomDeleteConnectedFaces)
ON_COMMAND(ID_ROOM_ROTATEPLACEDROOM45DEGREES, OnRoomRotatePlacedRoom45Degrees)
ON_COMMAND(ID_ROOM_PROPAGATETOADJACENTCOPLANARFACES, OnRoomPropagateToAdjacentCoplanarFaces)
ON_COMMAND(ID_VIEW_FLIP, OnViewFlip)
ON_COMMAND(ID_FILE_REMOVEDUPLICATEFACESFROMCURRENTROOM, OnFileRemoveDuplicateFacesFromCurrentRoom)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

END_MESSAGE_MAP()

//	ON_COMMAND(ID_FILE_SAVE, OnFileSave)
//	ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)

/////////////////////////////////////////////////////////////////////////////
// CEditorView construction/destruction

CEditorView::CEditorView() {
  // TODO: add construction code here
  m_grwndCreated = FALSE;
  m_grwndActive = FALSE;
}

CEditorView::~CEditorView() {}

BOOL CEditorView::PreCreateWindow(CREATESTRUCT &cs) {
  // TODO: Modify the Window class or styles here by modifying
  //  the CREATESTRUCT cs

  m_grwndActive = TRUE; // This should be moved to OnCreate or something like it

  return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CEditorView printing

BOOL CEditorView::OnPreparePrinting(CPrintInfo *pInfo) {
  // default preparation
  return DoPreparePrinting(pInfo);
}

void CEditorView::OnBeginPrinting(CDC * /*pDC*/, CPrintInfo * /*pInfo*/) {
  // TODO: add extra initialization before printing
}

void CEditorView::OnEndPrinting(CDC * /*pDC*/, CPrintInfo * /*pInfo*/) {
  // TODO: add cleanup after printing
}

/////////////////////////////////////////////////////////////////////////////
// OLE Server support

// The following command handler provides the standard keyboard
//  user interface to cancel an in-place editing session.  Here,
//  the server (not the container) causes the deactivation.
void CEditorView::OnCancelEditSrvr() { GetDocument()->OnDeactivateUI(FALSE); }

/////////////////////////////////////////////////////////////////////////////
// CEditorView drawing

void CEditorView::OnDraw(CDC *pDC) {
  CEditorDoc *pDoc = GetDocument();
  ASSERT_VALID(pDoc);

  // TODO: add draw code for native data here
  if (!theApp.paused()) {
    if (theApp.wireframe_view)
      m_grwndWireframe.Render();
    if (theApp.textured_view)
      m_grwndTexture.Render();
  }
}

/////////////////////////////////////////////////////////////////////////////
// CEditorView diagnostics

#ifdef _DEBUG
void CEditorView::AssertValid() const { CView::AssertValid(); }

void CEditorView::Dump(CDumpContext &dc) const { CView::Dump(dc); }

CEditorDoc *CEditorView::GetDocument() // non-debug version is inline
{
  ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CEditorDoc)));
  return (CEditorDoc *)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CEditorView message handlers

void CEditorView::OnInitialUpdate() {
  CView::OnInitialUpdate();

  // TODO: Add your specialized code here and/or call the base class
  theApp.main_view = this;

  CWnd *wnd = GetParent();

  wnd->Invalidate(FALSE);

  mprintf(0, "CEditorView::OnInitialUpdate.\n");
}

void CEditorView::OnSize(UINT nType, int cx, int cy) {
  CView::OnSize(nType, cx, cy);

  // TODO: Add your message handler code here
  //	ReCreate wireframe and texture screen/windows
  if (!m_grwndActive)
    return;

  KillChildViews();

  if (cx && cy) {
    CreateChildViews();
  }
}

void CEditorView::OnDestroy() {
  theApp.main_view = NULL;
  CView::OnDestroy();
}

void CEditorView::DeactivateView() {
  KillChildViews();
  m_grwndActive = FALSE; // Tell system that this view should not function
}

void CEditorView::ActivateView() {
  CreateChildViews();
  m_grwndActive = TRUE; // This view is active and functional
}

/*	Since this code is shared by a couple of functions, and is critical that the code
        is the same for both functions, I place all the code here.
*/
void CEditorView::KillChildViews() {
  if (m_grwndCreated) {
    if (theApp.wireframe_view)
      m_grwndWireframe.DestroyWindow();
    if (theApp.textured_view)
      m_grwndTexture.DestroyWindow();
    m_grwndCreated = FALSE;
  }
}

void CEditorView::CreateChildViews() {
  RECT view_rect, wire_rect, texture_rect;

  if (m_grwndCreated)
    return;

  GetClientRect(&view_rect);
  SetRect(&texture_rect, 0, 0, 0, 0);

  //	if we tile windows, then texture window will be reset to default width, height
  //	wireframe window will be sized depending on the existance of the texture view.
  if (D3EditState.texscr_visible) {
    if (D3EditState.tile_views)
      SetRect(&texture_rect, view_rect.left, view_rect.top, view_rect.left + CALC_PIXELS_WITH_ASPECTX(TEXSCREEN_WIDTH),
              view_rect.top + CALC_PIXELS_WITH_ASPECTY(TEXSCREEN_HEIGHT));
    else
      SetRect(&texture_rect, D3EditState.texscr_x, D3EditState.texscr_y, D3EditState.texscr_x + D3EditState.texscr_w,
              D3EditState.texscr_y + D3EditState.texscr_h);
    m_grwndTexture.Create(texture_rect, !D3EditState.tile_views, this);
  }

  if (D3EditState.wirescr_visible) {
    if (D3EditState.tile_views)
      SetRect(&wire_rect, view_rect.left, view_rect.top + texture_rect.bottom + 2, view_rect.right, view_rect.bottom);
    else
      SetRect(&wire_rect, D3EditState.wirescr_x, D3EditState.wirescr_y, D3EditState.wirescr_x + D3EditState.wirescr_w,
              D3EditState.wirescr_y + D3EditState.wirescr_h);
    m_grwndWireframe.Create(wire_rect, !D3EditState.tile_views, this);
  }

  m_grwndCreated = TRUE;
}

#include "ddio.h"

void CEditorView::OnButtonOutline() {
  CMenu popup;

  if (popup.CreatePopupMenu())
    if (!SetupPopup(&popup))
      Int3();

  POINT pnt;
  GetCursorPos(&pnt);

  if (!popup.TrackPopupMenu(TPM_LEFTALIGN, pnt.x - 20, pnt.y - 10, this, NULL))
    mprintf(0, "TrackPopupMenu error!\n");

  //	Outline_mode ^= OM_ON;
  //	EditorStatus("Outline mode turned %s",OUTLINE_ON(0)?"ON":"OFF");
  //	State_changed = 1;
}

void CEditorView::OnUpdateButtonOutline(CCmdUI *pCmdUI) { pCmdUI->SetCheck(OUTLINE_ON(0)); }

void CEditorView::OnUpdateViewTexturemine(CCmdUI *pCmdUI) { pCmdUI->SetCheck(D3EditState.texscr_visible); }

void CEditorView::OnViewTexturemine() {
  RECT texture_rect;

  D3EditState.texscr_visible = !D3EditState.texscr_visible;

  if (!D3EditState.texscr_visible) {
    if (D3EditState.tile_views) {
      DeactivateView();
      ActivateView();
    } else if (theApp.textured_view)
      m_grwndTexture.DestroyWindow();
  } else {
    if (D3EditState.tile_views) { // for tiling, reinit the views.
      DeactivateView();
      ActivateView();
    } else {
      SetRect(&texture_rect, D3EditState.texscr_x, D3EditState.texscr_y, D3EditState.texscr_x + D3EditState.texscr_w,
              D3EditState.texscr_y + D3EditState.texscr_h);
      m_grwndTexture.Create(texture_rect, TRUE, this);
    }
  }
}

void CEditorView::OnUpdateViewWireframemine(CCmdUI *pCmdUI) { pCmdUI->SetCheck(D3EditState.wirescr_visible); }

void CEditorView::OnViewWireframemine() {
  RECT wire_rect;

  D3EditState.wirescr_visible = !D3EditState.wirescr_visible;

  if (!D3EditState.wirescr_visible) {
    if (D3EditState.tile_views) {
      DeactivateView();
      ActivateView();
    } else if (theApp.wireframe_view)
      m_grwndWireframe.DestroyWindow();
  } else {
    if (D3EditState.tile_views) { // for tiling, reinit the views
      DeactivateView();
      ActivateView();
    } else {
      SetRect(&wire_rect, D3EditState.wirescr_x, D3EditState.wirescr_y, D3EditState.wirescr_x + D3EditState.wirescr_w,
              D3EditState.wirescr_y + D3EditState.wirescr_h);
      m_grwndWireframe.Create(wire_rect, TRUE, this);
    }
  }
}

//	 We do this so that we can tell if the texture or wireframe views were closed by the user
//
void CEditorView::OnParentNotify(UINT message, LPARAM lParam) {
  CView::OnParentNotify(message, lParam);

  //	Is the texture or wireframe window closing?
  switch (message) {
  case WM_CREATE:
    if (HIWORD(lParam) == IDC_TEXTURE_WND) {
      GetParent()->GetMenu()->CheckMenuItem(ID_VIEW_TEXTUREMINE, MF_BYCOMMAND | MF_CHECKED);
    } else if (HIWORD(lParam) == IDC_WIREFRAME_WND) {
      GetParent()->GetMenu()->CheckMenuItem(ID_VIEW_WIREFRAMEMINE, MF_BYCOMMAND | MF_CHECKED);
    }
    break;

  case WM_DESTROY:
    if (HIWORD(lParam) == IDC_TEXTURE_WND) {
      GetParent()->GetMenu()->CheckMenuItem(ID_VIEW_TEXTUREMINE, MF_BYCOMMAND | MF_UNCHECKED);
    } else if (HIWORD(lParam) == IDC_WIREFRAME_WND) {
      GetParent()->GetMenu()->CheckMenuItem(ID_VIEW_WIREFRAMEMINE, MF_BYCOMMAND | MF_UNCHECKED);
    }
    break;
  }
}

// ----------------------------------------------------------------------------
//	window menu options

void CEditorView::OnWindowTile() {
  // TODO: Add your command handler code here
  D3EditState.tile_views = 1;
  ((CMainFrame *)GetParentFrame())->DockKeypad(TRUE);
  DeactivateView();
  ActivateView();
}

void CEditorView::OnWindowCascade() {
  // TODO: Add your command handler code here
  D3EditState.tile_views = 0;
  ((CMainFrame *)GetParentFrame())->DockKeypad(FALSE);
  DeactivateView();
  ActivateView();
}

void CEditorView::OnButtonWindowSelection() {
  D3EditState.box_selection_mode = -(D3EditState.box_selection_mode - IN_WINDOW - ACROSS_EDGE);
  EditorStatus("Box selection set to %s", (D3EditState.box_selection_mode == IN_WINDOW) ? "IN WINDOW" : "ACROSS EDGE");
}

void CEditorView::OnUpdateButtonWindowSelection(CCmdUI *pCmdUI) {
  pCmdUI->SetCheck(D3EditState.box_selection_mode == ACROSS_EDGE);
}

void CEditorView::OnButtonLighting() {
  Lighting_on = !Lighting_on;
  EditorStatus("Lighting turned %s", Lighting_on ? "ON" : "OFF");
  State_changed = 1;
}

void CEditorView::OnUpdateButtonLighting(CCmdUI *pCmdUI) { pCmdUI->SetCheck(Lighting_on); }

BOOL CEditorView::OnHelpInfo(HELPINFO *pHelpInfo) {
  // TODO: Add your message handler code here and/or call default
  switch (D3EditState.keypad_current) {
  case TAB_TEXTURE_KEYPAD:
    WinHelp(HID_TEXTURETAB, HELP_CONTEXT);
    break;

  case TAB_OBJECTS_KEYPAD:
    WinHelp(HID_OBJECTTAB, HELP_CONTEXT);
    break;

  case TAB_LIGHTING_KEYPAD:
    WinHelp(HID_LIGHTINGTAB, HELP_CONTEXT);
    break;

  case TAB_PATHS_KEYPAD:
    WinHelp(HID_PATHSTAB, HELP_CONTEXT);
    break;

  case TAB_TRIGGER_KEYPAD:
    WinHelp(HID_TRIGGERTAB, HELP_CONTEXT);
    break;

  case TAB_DOORWAY_KEYPAD:
    WinHelp(HID_DOORWAYTAB, HELP_CONTEXT);
    break;

  case TAB_TERRAIN_KEYPAD:
    WinHelp(HID_TERRAINTAB, HELP_CONTEXT);
    break;

  case TAB_MEGACELL_KEYPAD:
    WinHelp(HID_MEGACELLTAB, HELP_CONTEXT);
    break;

  case TAB_ROOM_KEYPAD:
    WinHelp(HID_ROOMTAB, HELP_CONTEXT);
    break;

  default:
    WinHelp(HID_DEFAULT, HELP_CONTEXT);
    // return CView::OnHelpInfo(pHelpInfo);
  }
  return TRUE;
}

void CEditorView::OnRoomAdd() { AddRoom(); }

void CEditorView::OnRoomDelete() {
  if (OutrageMessageBox(MBOX_YESNO, "Are you sure you want to delete Room %d?", ROOMNUM(Curroomp)) != IDYES)
    return;

  DeleteRoomFromMine(Curroomp);

  World_changed = 1;
}

void CEditorView::OnRoomGrabTexture() { theApp.main_frame->m_TextureDialog->OnTexpadGrab(); }

void CEditorView::OnRoomNextFace() {
  SelectNextFace();

  EditorStatus("Face %d selected.", Curface);
}

void CEditorView::OnRoomPreviousFace() {
  SelectPrevFace();

  EditorStatus("Face %d selected.", Curface);
}

void CEditorView::OnRoomSelectByNumber() {
  int n;

  if (InputNumber(&n, "Select Room", "Enter room number to select", this)) {

    if ((n > Highest_room_index) || !Rooms[n].used) {
      OutrageMessageBox("Invalid room number.");
      return;
    }

    Curroomp = &Rooms[n];
    Curface = Curedge = Curvert = 0;
    Curportal = -1;

    EditorStatus("Room %d selected.", n);

    State_changed = 1;
  }
}

void CEditorView::OnRoomPlaceRoom() {
  if (D3EditState.current_room == -1) {
    OutrageMessageBox("You must have a current room for this operation");
    return;
  }

  if (Curroomp->faces[Curface].portal_num != -1) {
    OutrageMessageBox("There's already a connection at the current room:face.");
    return;
  }

  PlaceRoom(Curroomp, Curface, D3EditState.current_room, Current_faces[D3EditState.current_room - FIRST_PALETTE_ROOM],
            -1);
}

void CEditorView::OnRoomAttachRoom() {
  ASSERT(Placed_room != -1);

  AttachRoom();
}

void CEditorView::OnRoomBuildBridge() {
  if ((Markedroomp == NULL) || (Markedface == -1)) {
    OutrageMessageBox("You must have a marked face to use this function.");
    return;
  }

  if ((Markedroomp == Curroomp) && (Markedface == Curface)) {
    OutrageMessageBox("The marked room:face must be different from the current room:face.");
    return;
  }

  BuildBridge(Curroomp, Curface, Markedroomp, Markedface);
}

void CEditorView::OnRoomJoinRooms() {
  if ((Markedroomp == NULL) || (Markedface == -1)) {
    OutrageMessageBox("You must have a marked face to use this function.");
    return;
  }

  JoinRooms(Curroomp, Curface, Markedroomp, Markedface);
}

void CEditorView::OnRoomMark() { SetMarkedRoom(); }

void CEditorView::OnUpdateRoomAttachRoom(CCmdUI *pCmdUI) { pCmdUI->Enable(Placed_room != -1); }

void CEditorView::OnRoomUnPlaceRoom() {
  if (Placed_door != -1) {
    FreeRoom(&Rooms[Placed_room]);
    Placed_door = -1;
  }

  Placed_room = -1;
  Placed_group = NULL;

  State_changed = 1;
}

void CEditorView::OnUpdateRoomUnPlaceRoom(CCmdUI *pCmdUI) {
  if (Placed_room != -1) {
    pCmdUI->SetText((Placed_door != -1) ? "Un-place Door" : "Un-place Room");
    pCmdUI->Enable(1);
  } else if (Placed_group != NULL) {
    pCmdUI->SetText("Un-place Group");
    pCmdUI->Enable(1);
  } else
    pCmdUI->Enable(0);
}

void CEditorView::OnRoomSelectFaceByNumber() {
  int n;

  if (InputNumber(&n, "Select Face", "Enter face number to select", this)) {

    if (n >= Curroomp->num_faces) {
      OutrageMessageBox("Invalid face number.");
      return;
    }

    Curface = n;
    Curedge = Curvert = 0;

    EditorStatus("Face %d selected.", Curface);

    State_changed = 1;
  }
}

void CEditorView::OnRoomSaveCurrentRoom() { theApp.main_frame->m_RoomDialog->OnSaveRoomLocally(); }

void CEditorView::OnZbutton() {
  // TODO: Add your command handler code here
  Use_software_zbuffer = !Use_software_zbuffer;
  // tex_SetZBufferState (Use_software_zbuffer); // LGT: not defined
  State_changed = 1;
}

void CEditorView::OnUpdateZbutton(CCmdUI *pCmdUI) {
  static bool first_time = 1, opengl;
  if (first_time) {
    first_time = 0;
    opengl = (FindArg("-WindowGL") != 0);
  }

  pCmdUI->Enable(!opengl);
  pCmdUI->SetCheck(opengl || Use_software_zbuffer);
}

void CEditorView::OnRoomDropRoom() {
  if (D3EditState.current_room == -1) {
    OutrageMessageBox("You must have a current room for this operation");
    return;
  }

  DropRoom(Curroomp, Curface, D3EditState.current_room);
}

void CEditorView::OnRoomPlaceTerrainRoom() {
  int cellnum;

  if (D3EditState.current_room == -1) {
    OutrageMessageBox("You must have a current room for this operation");
    return;
  }

  if (Current_faces[D3EditState.current_room - FIRST_PALETTE_ROOM] == -1) {
    OutrageMessageBox("You must have a face selected on the current room for this operation");
    return;
  }

  if (Num_terrain_selected != 1) {
    OutrageMessageBox("You must have one and only one cell selected this operation");
    return;
  } else {
    for (cellnum = 0; cellnum < TERRAIN_WIDTH * TERRAIN_DEPTH; cellnum++)
      if (TerrainSelected[cellnum])
        break;

    ASSERT(cellnum != TERRAIN_WIDTH * TERRAIN_DEPTH);
  }

  int result = OutrageMessageBox(MBOX_YESNOCANCEL, "Do you want to align to the terrain? NO will align with gravity.");

  if (result == IDCANCEL)
    return;

  PlaceExternalRoom(cellnum, D3EditState.current_room, Current_faces[D3EditState.current_room - FIRST_PALETTE_ROOM],
                    (result == IDYES));
}

void CEditorView::OnRoomSwapMarkedAndCurrentRoomFace() {
  if (Markedroomp == NULL)
    return;

  room *troomp = Markedroomp;
  int tface = Markedface, tedge = Markededge, tvert = Markedvert;

  Markedroomp = Curroomp;
  Markedface = Curface;
  Markededge = Curedge;
  Markedvert = Curvert;

  Curroomp = troomp;
  Curface = tface;
  Curedge = tedge;
  Curvert = tvert;

  State_changed = 1;

  EditorStatus("Current room:face set to %d:%d; marked room:face set to %d:%d", ROOMNUM(Curroomp), Curface,
               ROOMNUM(Markedroomp), Markedface);
}

void CEditorView::OnRoomDeleteFace() {
  if (Curroomp->faces[Curface].portal_num != -1) {
    OutrageMessageBox("You cannot delete a face that is part of a portal.");
    return;
  }

  if (Curroomp->num_faces == 1) {
    OutrageMessageBox("You cannot delete the only face in a room.");
    return;
  }

  if (OutrageMessageBox(MBOX_YESNO, "Are you sure you want to delete Face %d from Room %d?", Curface,
                        ROOMNUM(Curroomp)) != IDYES)
    return;

  DeleteRoomFace(Curroomp, Curface);

  EditorStatus("Face %d deleted from room %d.", ROOMNUM(Curroomp), Curface);

  if (Curface == Curroomp->num_faces)
    Curface--;

  if (Markedface == Curroomp->num_faces)
    Markedface--;

  World_changed = 1;
}

void CEditorView::OnRoomSnapPlacedRoom() {
  ASSERT((Placed_room != -1) || (Placed_group != NULL));

  if ((Curroomp != Placed_baseroomp) || (Curface != Placed_baseface)) {
    OutrageMessageBox("The current room & face must be the same as the base room & face for this operation.");
    return;
  }

  SnapRoom(Curvert);
}

void CEditorView::OnUpdateRoomSnapPlacedRoom(CCmdUI *pCmdUI) {
  pCmdUI->Enable(((Placed_room != -1) || (Placed_group != NULL)) && (Placed_baseroomp != NULL));

  if (Placed_room != -1) {
    pCmdUI->SetText((Placed_door != -1) ? "Snap Placed Door" : "Snap Placed Room");
    pCmdUI->Enable(1);
  } else if (Placed_group != NULL) {
    pCmdUI->SetText("Snap Placed Group");
    pCmdUI->Enable(1);
  } else
    pCmdUI->Enable(0);
}

void CEditorView::OnRoomJoinAdjacentFaces() {
  if (Markedroomp == NULL) {
    OutrageMessageBox("You must have a Marked Room for this operation.");
    return;
  }

  if (Markedroomp == Curroomp) {
    OutrageMessageBox("The Marked room cannot be the same as the Current room for this operation.");
    return;
  }

  JoinAllAdjacentFaces(Curroomp, Markedroomp);
}

void CEditorView::OnRoomPropagateToAll() {
  int answer;

  answer = OutrageMessageBox(
      MBOX_YESNOCANCEL,
      "Propagate only to faces with the same texture?\n(Answering No will propagate to all faces in the room.)");

  if (answer == IDCANCEL)
    return;

  PropagateToAllFaces(Curroomp, Curface, (answer == IDYES));
}

void CEditorView::OnRoomSplitFace() {
  if (OutrageMessageBox(MBOX_YESNO, "Are you sure you want to triangulate the current face?") == IDYES)
    TriangulateFace(Curroomp, Curface, Curvert);
}

void CEditorView::OnRoomDeletePortal() {
  if (Curportal == -1) {
    OutrageMessageBox("You must have a current portal for this operation.");
    return;
  }

  if (OutrageMessageBox(MBOX_YESNO, "Are you sure you want to delete portal %d from room %d (& its connecting portal)?",
                        Curportal, ROOMNUM(Curroomp)) == IDYES) {
    DeletePortalPair(Curroomp, Curportal);
    Curportal = -1;
  }
}

void CEditorView::OnRoomSnapPointToEdge() {
  if (Markedroomp == NULL) {
    OutrageMessageBox("You must have a marked vertex to use this function.");
    return;
  }

  SnapPointToEdge(
      Markedroomp, Markedroomp->faces[Markedface].face_verts[Markedvert],
      &Curroomp->verts[Curroomp->faces[Curface].face_verts[Curedge]],
      &Curroomp->verts[Curroomp->faces[Curface].face_verts[(Curedge + 1) % Curroomp->faces[Curface].num_verts]]);
}

void CEditorView::OnRoomUndoSnap() { UndoSnap(); }

void CEditorView::OnUpdateRoomUndoSnap(CCmdUI *pCmdUI) { pCmdUI->Enable(Snap_roomnum != -1); }

void CEditorView::OnRoomSnapPointToPoint() {
  if (Markedroomp == NULL) {
    OutrageMessageBox("You must have a marked vertex to use this function.");
    return;
  }

  SnapPointToPoint(Markedroomp, Markedroomp->faces[Markedface].face_verts[Markedvert], Curroomp,
                   Curroomp->faces[Curface].face_verts[Curvert]);
}

void CEditorView::OnReinitOpengl() {
  // TODO: Add your command handler code here
  if (FindArg("-WindowGL")) {
    EditorStatus("Reinitting OpenGL");
    rend_Close();
    rend_Init(RENDERER_OPENGL, Descent, &Render_preferred_state);
  } else {
    EditorStatus("OpenGL command line argument not set.");
  }
}

void CEditorView::OnUpdateReinitOpengl(CCmdUI *pCmdUI) {}

void CEditorView::OnFileRemoveExtraPoints() { RemoveAllDuplicateAndUnusedPoints(); }

void CEditorView::OnRoomBuildSmoothBridge() {
  if ((Markedroomp == NULL) || (Markedface == -1)) {
    OutrageMessageBox("You must have a marked face to use this function.");
    return;
  }

  if ((Markedroomp == Curroomp) && (Markedface == Curface)) {
    OutrageMessageBox("The marked room:face must be different from the current room:face.");
    return;
  }

  BuildSmoothBridge(Markedroomp, Markedface, Curroomp, Curface);
}

void CEditorView::OnRoomJoinRoomsExact() {
  if ((Markedroomp == NULL) || (Markedface == -1)) {
    OutrageMessageBox("You must have a marked face to use this function.");
    return;
  }

  JoinRoomsExact(Curroomp, Curface, Markedroomp, Markedface);
}

#define POPUP_OUTLINE_ON 0x60
#define POPUP_OUTLINE_MINE 0x61
#define POPUP_OUTLINE_TERRAIN 0x62
#define POPUP_OUTLINE_SKY 0x63
#define POPUP_OUTLINE_OBJECTS 0x64

bool CEditorView::SetupPopup(CMenu *popup) {
  bool ret;
  ret = true;
  int grayed = (Outline_mode & OM_ON) ? 0 : MF_GRAYED;

  if (!popup->AppendMenu((Outline_mode & OM_ON) ? MF_CHECKED : MF_UNCHECKED, POPUP_OUTLINE_ON, "On"))
    ret = false;
  if (!popup->AppendMenu(grayed + ((Outline_mode & OM_MINE) ? MF_CHECKED : MF_UNCHECKED), POPUP_OUTLINE_MINE, "Mine"))
    ret = false;
  if (!popup->AppendMenu(grayed + ((Outline_mode & OM_TERRAIN) ? MF_CHECKED : MF_UNCHECKED), POPUP_OUTLINE_TERRAIN,
                         "Terrain"))
    ret = false;
  if (!popup->AppendMenu(grayed + ((Outline_mode & OM_SKY) ? MF_CHECKED : MF_UNCHECKED), POPUP_OUTLINE_SKY, "Sky"))
    ret = false;
  if (!popup->AppendMenu(grayed + ((Outline_mode & OM_OBJECTS) ? MF_CHECKED : MF_UNCHECKED), POPUP_OUTLINE_OBJECTS,
                         "Objects"))
    ret = false;

  return ret;
}

BOOL CEditorView::OnCommand(WPARAM wParam, LPARAM lParam) {
  int old_outline_mode = Outline_mode;

  if (HIWORD(wParam) == 0) {
    switch (LOWORD(wParam)) {
    case POPUP_OUTLINE_ON:
      Outline_mode ^= OM_ON;
      EditorStatus("Outline mode turned %s", OUTLINE_ON(0) ? "ON" : "OFF");
      break;
    case POPUP_OUTLINE_MINE:
      Outline_mode ^= OM_MINE;
      EditorStatus("Mine outline turned %s", OUTLINE_ON(OM_MINE) ? "ON" : "OFF");
      break;
    case POPUP_OUTLINE_TERRAIN:
      Outline_mode ^= OM_TERRAIN;
      EditorStatus("Terrain outline turned %s", OUTLINE_ON(OM_TERRAIN) ? "ON" : "OFF");
      break;
    case POPUP_OUTLINE_SKY:
      Outline_mode ^= OM_SKY;
      EditorStatus("Sky outline turned %s", OUTLINE_ON(OM_SKY) ? "ON" : "OFF");
      break;
    case POPUP_OUTLINE_OBJECTS:
      Outline_mode ^= OM_OBJECTS;
      EditorStatus("Object outline turned %s", OUTLINE_ON(OM_OBJECTS) ? "ON" : "OFF");
      break;
    }
  }

  State_changed = (Outline_mode != old_outline_mode);

  return CView::OnCommand(wParam, lParam);
}

void CEditorView::OnRoomStartNewFace() { StartNewFace(); }

void CEditorView::OnRoomFinishNewFace() { EndNewFace(); }

void CEditorView::OnRoomAddVertToNewFace() { AddNewFaceVert(); }

#include "osiris_predefs.h"
extern bool StripLeadingTrailingSpaces(char *s);

void RenameRoom() {
  char tempname[ROOM_NAME_LEN + 1] = "";

  if (Curroomp->name) {
    ASSERT(strlen(Curroomp->name) <= ROOM_NAME_LEN);
    strcpy(tempname, Curroomp->name);
  }

try_again:;
  if (!InputString(tempname, ROOM_NAME_LEN, "Room Name", "Enter a new name:"))
    return;

  if (StripLeadingTrailingSpaces(tempname))
    EditorMessageBox("Note: Leading and/or trailing spaces have been removed from this name (\"%s\")", tempname);

  int n = osipf_FindRoomName(tempname);
  if ((n != -1) && (n != ROOMNUM(Curroomp))) {
    EditorMessageBox("Room %d already has this name.", n);
    goto try_again;
  }

  if (Curroomp->name) {
    mem_free(Curroomp->name);
    Curroomp->name = NULL;
  }

  if (strlen(tempname)) {
    Curroomp->name = (char *)mem_malloc(strlen(tempname) + 1);
    strcpy(Curroomp->name, tempname);
  }

  World_changed = 1;
}

void CEditorView::OnRoomRenameRoom() { RenameRoom(); }

void CEditorView::OnUpdateRoomSetCenterFromViewer(CCmdUI *pCmdUI) { pCmdUI->Enable(!OBJECT_OUTSIDE(Viewer_object)); }

void CEditorView::OnRoomSetCenterFromViewer() {
  ASSERT(!OBJECT_OUTSIDE(Viewer_object));

  if (Viewer_object->roomnum != ROOMNUM(Curroomp)) {
    EditorMessageBox("The viewer must be in the current room for this operation.");
    return;
  }

  room *rp = &Rooms[Viewer_object->roomnum];

  rp->path_pnt = Viewer_object->pos;
  rp->flags |= RF_MANUAL_PATH_PNT;

  EditorStatus("Center point set for room %d", ROOMNUM(Curroomp));

  World_changed = 1;
}

void CEditorView::OnUpdateRoomClearCenterPoint(CCmdUI *pCmdUI) {
  pCmdUI->Enable((Editor_view_mode == VM_MINE) && ((Curroomp->flags & RF_MANUAL_PATH_PNT) != 0));
}

void CEditorView::OnRoomClearCenterPoint() {
  ASSERT(Curroomp->flags & RF_MANUAL_PATH_PNT);

  Curroomp->flags &= ~RF_MANUAL_PATH_PNT;

  EditorStatus("Center point cleared for room %d", ROOMNUM(Curroomp));

  World_changed = 1;
}

void CEditorView::OnRoomCombine() {
  if (Markedroomp == NULL) {
    OutrageMessageBox("You must have a Marked Room for this operation.");
    return;
  }

  int marked_roomnum = ROOMNUM(Markedroomp);

  if (CombineRooms(Curroomp, Markedroomp))
    EditorStatus("Room %d merged into room %d\n", marked_roomnum, ROOMNUM(Curroomp));
}

void FixDegenerateFaces();

void CEditorView::OnFileFixDegenerateFaces() { FixDegenerateFaces(); }

void CEditorView::OnRoomSnapPointToFace() {
  if (Markedroomp == NULL) {
    OutrageMessageBox("You must have a marked vertex to use this function.");
    return;
  }

  SnapPointToFace(Markedroomp, Markedroomp->faces[Markedface].face_verts[Markedvert],
                  &Curroomp->verts[Curroomp->faces[Curface].face_verts[Curedge]], &Curroomp->faces[Curface].normal);
}

#define MARK_TEXTURE 1

void CEditorView::OnRoomLinkToNewExternal() {
  void LinkToExternalRoom(room * rp, int nfaces, int *facenums);
  int n = 0, facelist[MAX_FACES_PER_ROOM];

  for (int i = 0; i < Curroomp->num_faces; i++) {
    if ((Curroomp->faces[i].portal_num == -1) && (Curroomp->faces[i].tmap == MARK_TEXTURE))
      facelist[n++] = i;
  }

  if (n) {
    if (OutrageMessageBox(MBOX_YESNO, "Do you want to create a new room linked to the %d selected faces in room %d?", n,
                          ROOMNUM(Curroomp)) != IDYES)
      return;

    LinkToExternalRoom(Curroomp, n, facelist);
  } else
    OutrageMessageBox("You have no faces selected in room %d.\n\nTo select a face, texture it with Texture %d, \"%s\".",
                      ROOMNUM(Curroomp), MARK_TEXTURE, GameTextures[MARK_TEXTURE].name);
}

void CEditorView::OnRoomDeleteVert() {
  if (Curroomp->faces[Curface].num_verts <= 3) {
    OutrageMessageBox("You cannot delete a vertex from a face with three or fewer vertices.");
    return;
  }

  if (OutrageMessageBox(MBOX_YESNO, "Are you sure you want to delete vertex %d (%d) from room %d face %d?", Curvert,
                        Curroomp->faces[Curface].face_verts[Curvert], ROOMNUM(Curroomp), Curface) == IDYES) {
    DeletePointFromFace(Curroomp, Curface, Curvert);
    Curvert = 0;
  }
}

void CEditorView::OnRoomFaceAddVertToEdge() {
  void SplitEdge(room * rp, int facenum, int edgenum, float position);

  char buf[10] = "0.5";

  if (InputString(buf, sizeof(buf), "New point position",
                  "Please enter the position of the new point, from 0.0 to 1.0")) {
    SplitEdge(Curroomp, Curface, Curedge, atof(buf));
    Curvert = Curedge + 1;
  }
}

void CEditorView::OnRoomFaceMoveVertOnEdge() {
  void MoveVert(room * rp, int facenum, int vertnum, float new_position);

  char buf[10] = "0.5";

  if (InputString(buf, sizeof(buf), "New point position",
                  "Please enter the new position of the point, from 0.0 to 1.0")) {
    MoveVert(Curroomp, Curface, Curvert, atof(buf));
  }
}

void CEditorView::OnRoomFaceSplitFace() {
  void SplitFace(room * rp, int facenum, int v0, int v1);

  if ((Curroomp != Markedroomp) || (Curface != Markedface)) {
    OutrageMessageBox("Cannot split face: the Marked & Current faces must be the same for this operation.");
    return;
  }

  int n_faces = Curroomp->num_faces;

  SplitFace(Curroomp, Curface, Curvert, Markedvert);

  Curface = n_faces - 1;
  Curedge = Curvert = 0;
  Markedroomp = NULL;
}

void CEditorView::OnRoomFaceDeleteVertOnEdge() {
  void DeleteVert(room * rp, int facenum, int vertnum);

  DeleteVert(Curroomp, Curface, Curvert);
}

void CEditorView::OnRoomMergeObjectIntoRoom() {
  if (MergeObjectIntoRoom(Curroomp, Cur_object_index))
    Cur_object_index = -1;
}

void CEditorView::OnRoomDeleteConnectedFaces() {
  if (OutrageMessageBox(
          MBOX_YESNO,
          "This function will delete all faces connected to room %d face %d.  This could be very dangerous.\n\n"
          "Are you sure you want to continue?",
          ROOMNUM(Curroomp), Curface) != IDYES)
    return;

  DeleteAllConnectedFaces(Curroomp, Curface);

  Curface = 0;

  if (Markedroomp == Curroomp)
    Markedroomp = NULL;
}

void CEditorView::OnRoomRotatePlacedRoom45Degrees() {
  Placed_room_angle += 65536 / 8;

  ComputePlacedRoomMatrix();

  State_changed = 1;
}

void CEditorView::OnRoomPropagateToAdjacentCoplanarFaces() {
  extern void PropagateToConnectedFaces(room * rp, int facenum);

  PropagateToConnectedFaces(Curroomp, Curface);
}

void CEditorView::OnViewFlip() {
  Viewer_object->orient.fvec = -Viewer_object->orient.fvec;
  Viewer_object->orient.rvec = -Viewer_object->orient.rvec;

  Viewer_moved = 1;
}

void CEditorView::OnFileRemoveDuplicateFacesFromCurrentRoom() { RemoveDuplicateFaces(Curroomp); }
