/*****************************************************************************
The Dark Mod GPL Source Code

This file is part of the The Dark Mod Source Code, originally based
on the Doom 3 GPL Source Code as published in 2011.

The Dark Mod Source Code 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. For details, see LICENSE.TXT.

Project: The Dark Mod (http://www.thedarkmod.com/)

******************************************************************************/

#include "precompiled.h"
#pragma hdrstop



#include <io.h>

#include "../../sys/win32/rc/guied_resource.h"
#include "../../ui/DeviceContext.h"

#include "GEApp.h"
#include "GEOptionsDlg.h"
#include "GEViewer.h"

static const int IDM_WINDOWCHILD	= 1000;
static const int ID_GUIED_FILE_MRU1 = 10000;

static INT_PTR CALLBACK AboutDlg_WndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch ( msg )
	{
		case WM_COMMAND:
			EndDialog ( hwnd, 1 );
			break;			
	}
	
	return FALSE;
}


rvGEApp::rvGEApp ( )
{
	mMDIFrame			 = NULL;
	mMDIClient			 = NULL;
	mRecentFileMenu		 = NULL;
	mViewer				 = NULL;
	mRecentFileInsertPos = 0;
}

rvGEApp::~rvGEApp ( )
{
	DestroyAcceleratorTable ( mAccelerators );
}

/*
================
rvGEApp::Initialize

Initialize the gui editor application
================
*/
bool rvGEApp::Initialize ( void )
{
	mOptions.Init();

	// Mutually exclusive
	com_editors = EDITOR_GUI;

	Sys_GrabMouseCursor( false );
	
	// Load the options
	mOptions.Load ( );	

	mInstance = win32.hInstance;

	// Create the accelerators
	mAccelerators = LoadAccelerators ( mInstance, MAKEINTRESOURCE(IDR_GUIED_ACCELERATORS) );

	// Register the window classes for the main frame and the mdi child window	
	WNDCLASSEX wndClass;
	memset ( &wndClass, 0, sizeof(wndClass) );
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.lpszClassName	= "QUAKE4_GUIEDITOR_CLASS";		
	wndClass.lpfnWndProc	= FrameWndProc;
	wndClass.hbrBackground	= (HBRUSH) (COLOR_APPWORKSPACE + 1); 
	wndClass.hCursor		= LoadCursor((HINSTANCE) NULL, IDC_ARROW); 
	wndClass.lpszMenuName	= MAKEINTRESOURCE(IDR_GUIED_MAIN);
	wndClass.hInstance		= mInstance; 
	RegisterClassEx ( &wndClass );

	wndClass.lpszMenuName	= NULL;
	wndClass.lpfnWndProc	= MDIChildProc;
	wndClass.lpszClassName	= "QUAKE4_GUIEDITOR_CHILD_CLASS";
	wndClass.style			= CS_OWNDC|CS_DBLCLKS|CS_BYTEALIGNWINDOW|CS_VREDRAW|CS_HREDRAW;
	wndClass.hbrBackground	= (HBRUSH)GetStockObject( LTGRAY_BRUSH );  
	RegisterClassEx ( &wndClass );
	
	// Create the main window
	mMDIFrame = CreateWindow ( "QUAKE4_GUIEDITOR_CLASS", 
							  "Quake IV GUI Editor", 
							  WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS, 
							  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
							  NULL, NULL, mInstance, (LPVOID)this );

	if ( !mMDIFrame )
	{
		return false;
	}

	SetClassLongPtr( mMDIFrame, GCLP_HICON, ( LONG_PTR )LoadIcon( win32.hInstance, MAKEINTRESOURCE( IDI_GUIED ) ) );
	
	// Create the MDI window
	CLIENTCREATESTRUCT ccs;
	ccs.hWindowMenu = GetSubMenu ( GetMenu ( mMDIFrame ), 5 );
	ccs.idFirstChild = IDM_WINDOWCHILD;
	mMDIClient = CreateWindow ( "MDICLIENT", NULL, 
								WS_CHILDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 
								0, 0, 1000, 1000, 
								mMDIFrame, NULL, 
								mInstance, &ccs );

	if ( !mMDIClient )
	{
		DestroyWindow ( mMDIFrame );
		return false;
	}

	// hide the doom window by default
	::ShowWindow ( win32.hWnd, SW_HIDE );
	
	// Show both windows
	mOptions.GetWindowPlacement ( "mdiframe", mMDIFrame );
	ShowWindow ( mMDIFrame, SW_SHOW ); 
	UpdateWindow ( mMDIFrame );

	ShowWindow ( mMDIClient, SW_SHOW );
	UpdateWindow ( mMDIClient );
	
	return true;
}


/*
================
rvGEApp::GetActiveWorkspace

Retrieves the workspace pointer for the active workspace.  If there is no active
workspace then it will return NULL
================
*/
rvGEWorkspace* rvGEApp::GetActiveWorkspace ( HWND* ret )
{
    rvGEWorkspace*	workspace;
    HWND			active;
	
	workspace = NULL;
	active    = (HWND)SendMessage ( mMDIClient, WM_MDIGETACTIVE, 0, NULL );

	// Return the window handle if requested
	if ( ret )
	{
		*ret = active;
	}

	if ( !active )
	{
		return NULL;
	}
	
	return rvGEWorkspace::GetWorkspace ( active );
}

/*
================
rvGEApp::TranslateAccelerator

Translate any accelerators destined for this window
================
*/
bool rvGEApp::TranslateAccelerator ( LPMSG msg )
{
	HWND focus;

	if ( msg->message == WM_SYSCHAR )
	{
		SetFocus ( GetMDIClient ( ) );	
		msg->hwnd = GetMDIClient ( );	
	}

	if ( mViewer )
	{
		return false;
	}

	focus = GetActiveWindow ( );

	// Only use accelerators when on the main window or navigator window
	if ( focus == mMDIClient || focus == mMDIFrame ||
		 focus == GetNavigator().GetWindow ( ) )
	{
		if ( ::TranslateAccelerator ( mMDIFrame, mAccelerators, msg ) )
		{
			return true;
		}
	}
	
	if ( TranslateMDISysAccel ( mMDIClient, msg ) )
	{
		return true;
	}
	
	return false;
}

/*
================
rvGEApp::RunFrame

Runs the current frame which causes the active window to be redrawn
================
*/
void rvGEApp::RunFrame ( void )
{
	HWND			wnd;
	rvGEWorkspace*	workspace = GetActiveWorkspace ( &wnd );

	if ( workspace )
	{		
		// Render the workspace using a temp DC
		HDC hDC = GetDC ( wnd );		
		workspace->Render ( hDC );		
		ReleaseDC ( wnd, hDC );				
		
		if ( mViewer )
		{
			mViewer->RunFrame ( );
		}
	}
}

/*
================
rvGEApp::FrameWndProc

Main frame window procedure
================
*/
LRESULT CALLBACK rvGEApp::FrameWndProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	rvGEApp* app = (rvGEApp*) GetWindowLongPtr ( hWnd, GWLP_USERDATA );

	switch ( uMsg )
	{		
		case WM_SIZE:
		{
			RECT rStatus;
			RECT rClient;

			// Tell the status bar to resize 			
			app->mStatusBar.Resize ( LOWORD(lParam), HIWORD(lParam) );
				
			// Calculate the new client window rectangle (minus the status bar)
			GetWindowRect ( app->mStatusBar.GetWindow ( ), &rStatus );
			GetClientRect ( hWnd, &rClient );

			if ( app->mOptions.GetStatusBarVisible ( ) )
			{
				rClient.bottom -= (rStatus.bottom-rStatus.top);
			}
			MoveWindow ( app->mMDIClient, 0, 0, rClient.right-rClient.left, rClient.bottom-rClient.top, FALSE );
			
			return 0;
		}
		
		case WM_ENABLE:
		{	
			int i;

		    // Synchronise all toolwindows to the same state.
			for(i = 0; i < app->mToolWindows.Num(); i++)
			{
		        if(app->mToolWindows[i] != hWnd)
				{
					EnableWindow(app->mToolWindows[i], wParam);
				}
		    }
		    break;
		}		

		case WM_NCACTIVATE:
			return app->ToolWindowActivate ( hWnd, uMsg, wParam, lParam );

		case WM_ACTIVATE:
			common->ActivateTool( LOWORD( wParam ) != WA_INACTIVE );
			break;

		case WM_INITMENUPOPUP:
			app->HandleInitMenu ( wParam, lParam  );
			break;

		case WM_CLOSE:			
			while ( app->mWorkspaces.Num ( ) )
			{
				SendMessage ( app->mWorkspaces[0]->GetWindow ( ), WM_CLOSE, 0, 0 );
			}
			break;

		case WM_DESTROY:
			app->mOptions.SetWindowPlacement ( "mdiframe", hWnd );
			app->mOptions.Save ( );			
			ExitProcess(0);
			break;			

		case WM_COMMAND:
		{
			int result;
			assert ( app );
			result = app->HandleCommand ( wParam, lParam );
			if ( -1 != result )
			{
				return result;
			}			
			break;
		}

		case WM_CREATE:
		{	
			LPCREATESTRUCT		cs;
										
			cs = (LPCREATESTRUCT) lParam;
			app = (rvGEApp*)cs->lpCreateParams;

			assert ( app );
			
			SetWindowLongPtr ( hWnd, GWLP_USERDATA, (LONG_PTR)app );

			app->mMDIFrame = hWnd;
			
			app->InitRecentFiles ( );
			app->UpdateRecentFiles ( );
			app->mNavigator.Create ( hWnd, gApp.mOptions.GetNavigatorVisible ( ) );		
			app->mTransformer.Create ( hWnd, gApp.mOptions.GetTransformerVisible ( )  );
			app->mStatusBar.Create ( hWnd, 9999, gApp.mOptions.GetStatusBarVisible ( )  );
			app->mProperties.Create ( hWnd, gApp.mOptions.GetPropertiesVisible ( ) );

			// add all the tool windows to the tool window array
			app->mToolWindows.Append ( app->mMDIFrame );
			app->mToolWindows.Append ( app->mNavigator.GetWindow ( ) );
			app->mToolWindows.Append ( app->mProperties.GetWindow ( ) );
			app->mToolWindows.Append ( app->mTransformer.GetWindow ( ) );
			
			SendMessage ( app->mNavigator.GetWindow ( ), WM_NCACTIVATE, true, (LPARAM)-1 );
			SendMessage ( app->mProperties.GetWindow ( ), WM_NCACTIVATE, true, (LPARAM)-1 );
			SendMessage ( app->mTransformer.GetWindow ( ), WM_NCACTIVATE, true, (LPARAM)-1 );

			break;
		}
	}

	return DefFrameProc ( hWnd, app?app->mMDIClient:NULL, uMsg, wParam, lParam );
}

/*
================
rvGEApp::MDIChildProc

MDI Child window procedure
================
*/
LRESULT CALLBACK rvGEApp::MDIChildProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	rvGEWorkspace* workspace = (rvGEWorkspace*)GetWindowLongPtr ( hWnd, GWLP_USERDATA );	

	// Give the active workspace a chance to play with it
	if ( workspace )
	{
		workspace->HandleMessage ( uMsg, wParam, lParam );
	}

	switch ( uMsg )
	{
		case WM_CLOSE:
			workspace->GetApplication ( )->mWorkspaces.Remove ( workspace );
			break;

		case WM_CREATE:
		{	
			LPMDICREATESTRUCT	mdics;
			LPCREATESTRUCT		cs;
					
			// MDI windows have their creation params buried two levels deep, extract
			// that param since it is the workspace pointer
			cs = (LPCREATESTRUCT) lParam;
			mdics = (LPMDICREATESTRUCT) cs->lpCreateParams;
			
			// Attach the workspace to the window
			workspace = (rvGEWorkspace*) mdics->lParam;
			workspace->Attach ( hWnd );

			workspace->GetApplication ( )->mWorkspaces.Append ( workspace );				
				
			break;
		}

		case WM_MDIACTIVATE:
			assert ( workspace );
			if ( (HWND)lParam == hWnd )
			{				
				workspace->GetApplication ( )->GetNavigator().SetWorkspace(workspace);
				workspace->GetApplication ( )->GetTransformer().SetWorkspace(workspace);
				workspace->GetApplication ( )->GetProperties().SetWorkspace(workspace);
				gApp.GetStatusBar ( ).SetSimple ( false );
			}
			else if ( lParam == NULL )
			{
				gApp.GetStatusBar ( ).SetSimple ( true );
			}
			break;		

		case WM_DESTROY:
			assert ( workspace );
			workspace->Detach ( );
			delete workspace;
			break;

		case WM_SETCURSOR:
			return 1;

		case WM_ERASEBKGND:
			return TRUE;

		case WM_PAINT:
		{
			HDC			dc;
			PAINTSTRUCT	ps;
			
			dc = BeginPaint(hWnd, &ps); 
			
			if ( workspace )
			{
				workspace->Render ( dc );
			}
			
            EndPaint(hWnd, &ps); 
           
			break;
		}
	}
	
	return DefMDIChildProc ( hWnd, uMsg, wParam, lParam );
}

/*
================
rvGEApp::HandleCommandSave

Handles the ID_GUIED_FILE_SAVE and ID_GUIED_FILE_SAVEAS commands
================
*/
void rvGEApp::HandleCommandSave ( rvGEWorkspace* workspace, const char* filename )
{
	idStr realFilename;
	
	// See if we need to browse for a filename
	if ( workspace->IsNew ( ) || filename == NULL )
	{
		OPENFILENAME ofn;
		char		 szFile[MAX_PATH];

		strcpy ( szFile, workspace->GetFilename ( ) );

		// Initialize OPENFILENAME
		ZeroMemory(&ofn, sizeof(OPENFILENAME));
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = mMDIFrame;
		ofn.lpstrFile = szFile;
		ofn.nMaxFile = sizeof(szFile);
		ofn.lpstrFilter = "GUI Files\0*.GUI\0All Files\0*.*\0";
		ofn.nFilterIndex = 1;
		ofn.Flags = OFN_PATHMUSTEXIST;

		// Display the save dialog box. 
		if ( !GetSaveFileName(&ofn) ) 
		{
			return;
		}

		realFilename = ofn.lpstrFile;
		realFilename.StripFileExtension ( );
		realFilename.Append ( ".gui" );

		// If the file already exists then warn about overwriting it
		if ( _taccess ( realFilename, 0 ) == 0 )
		{
			if ( IDNO == MessageBox ( va("File '%s' already exists. Do you want to replace it?", realFilename.c_str()), MB_YESNO|MB_ICONQUESTION ) )
			{
				return;
			}
		}
	}
	else
	{
		realFilename = filename;
	}	

	// Now performe the file save
	if ( workspace->SaveFile ( realFilename ) )
	{
		mOptions.AddRecentFile ( workspace->GetFilename ( ) );
		UpdateRecentFiles ( );
	}
	else
	{
		MessageBox ( va("Could not write file '%s'", workspace->GetFilename()), MB_OK|MB_ICONERROR );
	}
}

/*
================
rvGEApp::HandleCommand

Handles the WM_COMMAND message
================
*/
int rvGEApp::HandleCommand ( WPARAM wParam, LPARAM lParam )
{
	HWND		   active;
	rvGEWorkspace* workspace = GetActiveWorkspace ( &active );

	// The recent file list needs to be handled specially
	if ( LOWORD(wParam) >= ID_GUIED_FILE_MRU1 && LOWORD(wParam) < ID_GUIED_FILE_MRU1 + rvGEOptions::MAX_MRU_SIZE )
	{
		OpenFile ( mOptions.GetRecentFile ( mOptions.GetRecentFileCount() - (LOWORD(wParam)-ID_GUIED_FILE_MRU1) - 1 ) );
		return 0;
	}
					
	switch ( LOWORD ( wParam ) )
	{		
		case ID_GUIED_SOURCECONTROL_CHECKIN:
			assert ( workspace );
			HandleCommandSave ( workspace, workspace->GetFilename ( ) );
			workspace->CheckIn ( );
			break;
			
		case ID_GUIED_SOURCECONTROL_CHECKOUT:
			assert ( workspace );
			workspace->CheckOut ( );
			break;

		case ID_GUIED_SOURCECONTROL_UNDOCHECKOUT:
			assert ( workspace );
			if ( IDYES == MessageBox ( va("Are you sure you want to undo the checkout of the file '%s'?",workspace->GetFilename()), MB_YESNO|MB_ICONQUESTION) )
			{
				workspace->UndoCheckout ( );
			}
			break;
			
		case ID_GUIED_TOOLS_RELOADMATERIALS:
			SetCursor ( LoadCursor ( NULL, IDC_WAIT ) );
			cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadImages\n" );
			cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadMaterials\n" );
			SetCursor ( LoadCursor ( NULL, IDC_ARROW ) );
			break;
			
		case ID_GUIED_EDIT_COPY:
			assert ( workspace );
			workspace->Copy  ( );
			break;

		case ID_GUIED_EDIT_PASTE:
			assert ( workspace );
			workspace->Paste  ( );
			break;
	
		case ID_GUIED_HELP_ABOUT:
			DialogBox ( GetInstance(), MAKEINTRESOURCE(IDD_GUIED_ABOUT), mMDIFrame, AboutDlg_WndProc );
			break;
	
		case ID_GUIED_TOOLS_VIEWER:
		{
			if ( mViewer )
			{
				break;
			}
			
			mViewer = new rvGEViewer;
			if ( !mViewer->Create ( mMDIFrame ) )
			{
				delete mViewer;
				mViewer = NULL;
			}
						
			if ( workspace )
			{				
				if ( !workspace->IsModified () || HandleCommand ( MAKELONG(ID_GUIED_FILE_SAVE,0), 0 ) )
				{
					mViewer->OpenFile ( workspace->GetFilename ( ) );
				}
			}
			
			SetActiveWindow ( mViewer->GetWindow ( ) );
			break;
		}
		
		case ID_GUIED_ITEM_MAKESAMESIZEWIDTH:
			assert ( workspace );
			workspace->MakeSelectedSameSize ( true, false );
			break;

		case ID_GUIED_ITEM_MAKESAMESIZEBOTH:
			assert ( workspace );
			workspace->MakeSelectedSameSize ( true, true );
			break;

		case ID_GUIED_ITEM_MAKESAMESIZEHEIGHT:
			assert ( workspace );
			workspace->MakeSelectedSameSize ( false, true );
			break;
	
		case ID_GUIED_ITEM_ALIGNLEFTS:
			assert ( workspace );
			workspace->AlignSelected ( rvGEWorkspace::ALIGN_LEFTS );
			break;

		case ID_GUIED_ITEM_ALIGNCENTERS:
			assert ( workspace );
			workspace->AlignSelected ( rvGEWorkspace::ALIGN_CENTERS );
			break;

		case ID_GUIED_ITEM_ALIGNRIGHTS:
			assert ( workspace );
			workspace->AlignSelected ( rvGEWorkspace::ALIGN_RIGHTS );
			break;

		case ID_GUIED_ITEM_ALIGNTOPS:
			assert ( workspace );
			workspace->AlignSelected ( rvGEWorkspace::ALIGN_TOPS );
			break;

		case ID_GUIED_ITEM_ALIGNMIDDLES:
			assert ( workspace );
			workspace->AlignSelected ( rvGEWorkspace::ALIGN_MIDDLES );
			break;

		case ID_GUIED_ITEM_ALIGNBOTTOMS:
			assert ( workspace );
			workspace->AlignSelected ( rvGEWorkspace::ALIGN_BOTTOMS );
			break;
			
		case ID_GUIED_ITEM_ARRANGESENDBACKWARD:
			assert ( workspace );
			workspace->SendSelectedBackward ( );
			break;			

		case ID_GUIED_ITEM_ARRANGESENDTOBACK:
			assert ( workspace );
			workspace->SendSelectedToBack( );
			break;			

		case ID_GUIED_ITEM_ARRANGEBRINGFORWARD:
			assert ( workspace );
			workspace->BringSelectedForward ( );
			break;			

		case ID_GUIED_ITEM_ARRANGEBRINGTOFRONT:
			assert ( workspace );
			workspace->BringSelectedToFront ( );
			break;			
			
		case ID_GUIED_ITEM_ARRANGEMAKECHILD:
			assert ( workspace );
			workspace->MakeSelectedAChild ( );
			break;
	
		case ID_GUIED_ITEM_PROPERTIES:
			assert ( workspace );
			workspace->EditSelectedProperties ( );
			break;

		case ID_GUIED_ITEM_SCRIPTS:
			assert ( workspace );
			workspace->EditSelectedScripts ( );
			break;
			
		case ID_GUIED_ITEM_NEWWINDOWDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_NORMAL );
			break;

		case ID_GUIED_ITEM_NEWEDITDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_EDIT );
			break;

		case ID_GUIED_ITEM_NEWHTMLDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_HTML );
			break;

		case ID_GUIED_ITEM_NEWCHOICEDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_CHOICE );
			break;

		case ID_GUIED_ITEM_NEWSLIDERDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_SLIDER );
			break;

		case ID_GUIED_ITEM_NEWLISTDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_LIST );
			break;

		case ID_GUIED_ITEM_NEWBINDDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_BIND );
			break;

		case ID_GUIED_ITEM_NEWRENDERDEF:
			assert ( workspace );
			workspace->AddWindow ( rvGEWindowWrapper::WT_RENDER );
			break;
	
		case ID_GUIED_WINDOW_TILE:
			SendMessage ( mMDIClient, WM_MDITILE, 0, 0 );
			break;	

		case ID_GUIED_WINDOW_CASCADE:
			SendMessage ( mMDIClient, WM_MDICASCADE, 0, 0 );
			break;	

		case ID_GUIED_VIEW_STATUSBAR:
		{
			RECT rWindow;
			
			mStatusBar.Show ( mOptions.GetStatusBarVisible()?false:true );
			
			GetWindowRect ( mMDIFrame, &rWindow );
			SendMessage ( mMDIFrame, WM_SIZE, 0, MAKELONG ( rWindow.right-rWindow.left, rWindow.bottom-rWindow.top ) );			
			break;
		}

		case ID_GUIED_WINDOW_SHOWNAVIGATOR:
			mNavigator.Show ( mOptions.GetNavigatorVisible()?false:true );
			break;					
			
		case ID_GUIED_WINDOW_SHOWPROPERTIES:
			mProperties.Show ( mOptions.GetPropertiesVisible()?false:true );
			break;
			
		case ID_GUIED_WINDOW_SHOWTRANSFORMER:
			mTransformer.Show ( mOptions.GetTransformerVisible()?false:true  );
			break;					
			
		case ID_GUIED_EDIT_DELETE:
			assert ( workspace );
			workspace->DeleteSelected ( );
			break;

		case ID_GUIED_VIEW_HIDESELECTED:
			assert ( workspace );
			workspace->HideSelected ( );
			break;

		case ID_GUIED_VIEW_UNHIDESELECTED:
			assert ( workspace );
			workspace->UnhideSelected ( );
			break;
		
		case ID_GUIED_VIEW_SHOWHIDDEN:
			assert ( workspace );
			workspace->ShowHidden ( );
			break;
			
		case ID_GUIED_EDIT_UNDO:
			assert ( workspace );
			workspace->GetModifierStack().Undo ( );
			mNavigator.Update ( );
			mTransformer.Update ( );
			break;

		case ID_GUIED_EDIT_REDO:
			assert ( workspace );
			workspace->GetModifierStack().Redo ( );
			mNavigator.Update ( );
			mTransformer.Update ( );
			break;
	
		case ID_GUIED_VIEW_OPTIONS:
			GEOptionsDlg_DoModal ( mMDIFrame );
			break;
	
		case ID_GUIED_VIEW_SHOWGRID:
			mOptions.SetGridVisible ( mOptions.GetGridVisible()?false:true );				
			break;

		case ID_GUIED_VIEW_SNAPTOGRID:
			mOptions.SetGridSnap ( mOptions.GetGridSnap ()?false:true );				
			break;
	
		case ID_GUIED_VIEW_ZOOMIN:
			assert ( workspace );
			workspace->ZoomIn ( );
			break;
			
		case ID_GUIED_VIEW_ZOOMOUT:
			assert ( workspace );
			workspace->ZoomOut ( );
			break;
	
		case ID_GUIED_FILE_EXIT:
			DestroyWindow ( mMDIFrame );
			break;

		case ID_GUIED_FILE_CLOSE:
			if ( active )
			{
				assert ( workspace );
				SendMessage ( active, WM_CLOSE, 0, 0 );
			}
			break;
			
		case ID_GUIED_FILE_NEW:
			NewFile ( );
			break;

		case ID_GUIED_FILE_SAVE:
			assert ( workspace );
			HandleCommandSave ( workspace, workspace->GetFilename ( ) );
			break;

		case ID_GUIED_FILE_SAVEAS:
			assert ( workspace );
			HandleCommandSave ( workspace, NULL );
			break;			
			
		case ID_GUIED_FILE_OPEN:
		{
			OPENFILENAME ofn;
			char		 szFile[MAX_PATH] = "";

			// Initialize OPENFILENAME
			ZeroMemory(&ofn, sizeof(OPENFILENAME));
			ofn.lStructSize = sizeof(OPENFILENAME);
			ofn.hwndOwner = mMDIFrame;
			ofn.lpstrFile = szFile;
			ofn.nMaxFile = sizeof(szFile);
			ofn.lpstrFilter = "GUI Files\0*.GUI\0All Files\0*.*\0";
			ofn.nFilterIndex = 1;
			ofn.lpstrFileTitle = NULL;
			ofn.nMaxFileTitle = 0;
			ofn.lpstrInitialDir = NULL;
			ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

			// Display the Open dialog box. 
			if (GetOpenFileName(&ofn)==TRUE) 
			{			
				OpenFile ( ofn.lpstrFile );
			}
			break;
		}			
	}
	
	return -1;
}

/*
================
rvGEApp::HandleInitMenu

Handles the initialization of the main menu
================
*/
int rvGEApp::HandleInitMenu ( WPARAM wParam, LPARAM lParam ) 
{ 
    int				cMenuItems = GetMenuItemCount((HMENU)wParam); 
    int				nPos; 
    int				id; 
    UINT			flags; 
    rvGEWorkspace*	workspace;
    HMENU			hmenu;
 
	hmenu     = (HMENU) wParam;
	workspace = GetActiveWorkspace ( );

	// Run through all the menu items in the menu and see if any of them need
	// modification in any way
    for (nPos = 0; nPos < cMenuItems; nPos++) 
    { 
        id    = GetMenuItemID(hmenu, nPos); 
        flags = 0;
 
		// Handle popup menus too
		if ( id < 0 )
		{
			HMENU sub = GetSubMenu ( hmenu, nPos );
			if ( sub )
			{
				HandleInitMenu ( (WPARAM) sub, 0 );
				continue;
			}
		}
	
		// Menu items that are completely unrelated to the workspace
		switch ( id )
		{
			case ID_GUIED_VIEW_STATUSBAR:
				flags = MF_BYCOMMAND | (mOptions.GetStatusBarVisible()?MF_CHECKED:MF_UNCHECKED);
				CheckMenuItem ( hmenu, id, flags );
				break;				

			case ID_GUIED_WINDOW_SHOWNAVIGATOR:
				flags = MF_BYCOMMAND | (mOptions.GetNavigatorVisible()?MF_CHECKED:MF_UNCHECKED);
				CheckMenuItem ( hmenu, id, flags );
				break;				
			
			case ID_GUIED_WINDOW_SHOWPROPERTIES:
				flags = MF_BYCOMMAND | (mOptions.GetPropertiesVisible()?MF_CHECKED:MF_UNCHECKED);
				CheckMenuItem ( hmenu, id, flags );
				break;				

			case ID_GUIED_WINDOW_SHOWTRANSFORMER:
				flags = MF_BYCOMMAND | (mOptions.GetTransformerVisible()?MF_CHECKED:MF_UNCHECKED);
				CheckMenuItem ( hmenu, id, flags );
				break;				
		}
 
		// Handle the basic case where an item is disabled because
		// there is no workspace available
		if ( !workspace )
		{
			switch ( id )
			{
				case ID_GUIED_EDIT_UNDO:
				case ID_GUIED_EDIT_REDO:
				case ID_GUIED_VIEW_SHOWGRID:
				case ID_GUIED_VIEW_SNAPTOGRID:
				case ID_GUIED_VIEW_HIDESELECTED:
				case ID_GUIED_VIEW_UNHIDESELECTED:
				case ID_GUIED_EDIT_DELETE:
				case ID_GUIED_WINDOW_TILE:
				case ID_GUIED_WINDOW_CASCADE:
				case ID_GUIED_ITEM_NEWWINDOWDEF:
				case ID_GUIED_ITEM_NEWEDITDEF:
				case ID_GUIED_ITEM_NEWHTMLDEF:
				case ID_GUIED_ITEM_ARRANGEBRINGTOFRONT:
				case ID_GUIED_ITEM_ARRANGEBRINGFORWARD:
				case ID_GUIED_ITEM_ARRANGESENDTOBACK:
				case ID_GUIED_ITEM_ARRANGESENDBACKWARD:
				case ID_GUIED_ITEM_PROPERTIES:
				case ID_GUIED_ITEM_SCRIPTS:
				case ID_GUIED_VIEW_ZOOMIN:
				case ID_GUIED_VIEW_ZOOMOUT:
				case ID_GUIED_ITEM_ALIGNLEFTS:
				case ID_GUIED_ITEM_ALIGNCENTERS:
				case ID_GUIED_ITEM_ALIGNRIGHTS:
				case ID_GUIED_ITEM_ALIGNBOTTOMS:
				case ID_GUIED_ITEM_ALIGNMIDDLES:
				case ID_GUIED_ITEM_ALIGNTOPS:
				case ID_GUIED_ITEM_MAKESAMESIZEHEIGHT:
				case ID_GUIED_ITEM_MAKESAMESIZEWIDTH:
				case ID_GUIED_ITEM_MAKESAMESIZEBOTH:
				case ID_GUIED_FILE_SAVE:
				case ID_GUIED_FILE_SAVEAS:
				case ID_GUIED_EDIT_COPY:
				case ID_GUIED_EDIT_PASTE:		
				case ID_GUIED_ITEM_ARRANGEMAKECHILD:
				case ID_GUIED_SOURCECONTROL_GETLATESTVERSION:
				case ID_GUIED_SOURCECONTROL_CHECKIN:
				case ID_GUIED_SOURCECONTROL_CHECKOUT:
				case ID_GUIED_SOURCECONTROL_UNDOCHECKOUT:
				case ID_GUIED_FILE_CLOSE:
					EnableMenuItem ( hmenu, nPos, MF_GRAYED|MF_BYPOSITION );
					break;
			}

			continue;
		}

        switch (id) 
        { 
			// Undo is greyed out when there is noting to undo and the text is
			// modified to include the name of the modifier that will be undone
			case ID_GUIED_EDIT_UNDO:
			{
				MENUITEMINFO info;
				idStr		 undo;

				info.cbSize = sizeof(info);
				info.fMask = MIIM_STATE|MIIM_TYPE;
				info.fType = MFT_STRING;
				
				if ( !workspace->GetModifierStack().CanUndo ( ) )
				{				
					undo = "Undo\tCtrl+Z";
					info.fState = MFS_GRAYED;
				}
				else
				{
					undo = "Undo ";
					undo.Append ( workspace->GetModifierStack().GetUndoModifier()->GetName ( ) );
					undo.Append ( "\tCtrl+Z" );
					info.fState = MFS_ENABLED;
				}

				info.dwTypeData = (LPSTR)undo.c_str();
				info.cch = undo.Length ( );
									
				SetMenuItemInfo ( hmenu, id, FALSE, &info );

				break;
			}

			case ID_GUIED_EDIT_REDO:
			{
				MENUITEMINFO info;
				idStr		 undo;

				info.cbSize = sizeof(info);
				info.fMask = MIIM_STATE|MIIM_TYPE;
				info.fType = MFT_STRING;
				
				if ( !workspace || !workspace->GetModifierStack().CanRedo ( ) )
				{				
					undo = "Redo\tCtrl+Y";
					info.fState = MFS_GRAYED;
				}
				else
				{
					undo = "Redo ";
					undo.Append ( workspace->GetModifierStack().GetRedoModifier()->GetName ( ) );
					undo.Append ( "\tCtrl+Y" );
					info.fState = MFS_ENABLED;
				}

				info.dwTypeData = (LPSTR)undo.c_str();
				info.cch = undo.Length ( );
									
				SetMenuItemInfo ( hmenu, id, FALSE, &info );

				break;
			}
				
			case ID_GUIED_VIEW_SHOWGRID:
				flags = MF_BYCOMMAND | (mOptions.GetGridVisible()?MF_CHECKED:MF_UNCHECKED);
				CheckMenuItem ( hmenu, id, flags );
				break;				

			case ID_GUIED_VIEW_SNAPTOGRID:
				flags = MF_BYCOMMAND | (mOptions.GetGridSnap()?MF_CHECKED:MF_UNCHECKED);
				CheckMenuItem ( hmenu, id, flags );
				break;				
			
			// All menu items that are greyed out when there is no workspace
			case ID_GUIED_WINDOW_TILE:
			case ID_GUIED_WINDOW_CASCADE:
			case ID_GUIED_ITEM_NEWWINDOWDEF:
			case ID_GUIED_ITEM_NEWEDITDEF:
			case ID_GUIED_ITEM_NEWHTMLDEF:
			case ID_GUIED_VIEW_ZOOMIN:
			case ID_GUIED_VIEW_ZOOMOUT:
			case ID_GUIED_FILE_SAVE:
			case ID_GUIED_FILE_SAVEAS:
			case ID_GUIED_FILE_CLOSE:
				EnableMenuItem ( hmenu, nPos, MF_ENABLED|MF_BYPOSITION);
				break;

			// All menu items that are greyed out unless an item is selected
			case ID_GUIED_VIEW_HIDESELECTED:
			case ID_GUIED_VIEW_UNHIDESELECTED:
			case ID_GUIED_EDIT_DELETE:
			case ID_GUIED_EDIT_COPY:
				EnableMenuItem ( hmenu, nPos, MF_BYPOSITION|(workspace->GetSelectionMgr().Num()>0?MF_ENABLED:MF_GRAYED) );
				break;

			// Enable paste if the clipboard has something in it
			case ID_GUIED_EDIT_PASTE:		
				EnableMenuItem ( hmenu, nPos, MF_BYPOSITION|(workspace->GetClipboard().Num()>0?MF_ENABLED:MF_GRAYED) );
				break;
			
			// All menu items that are greyed out unless a single item is selected
			case ID_GUIED_ITEM_ARRANGEBRINGTOFRONT:
			case ID_GUIED_ITEM_ARRANGEBRINGFORWARD:
			case ID_GUIED_ITEM_ARRANGESENDTOBACK:
			case ID_GUIED_ITEM_ARRANGESENDBACKWARD:
			case ID_GUIED_ITEM_PROPERTIES:
			case ID_GUIED_ITEM_SCRIPTS:
				EnableMenuItem ( hmenu, nPos, MF_BYPOSITION|(workspace->GetSelectionMgr().Num()==1?MF_ENABLED:MF_GRAYED) );
				break;

			// All menu items that are greyed out unless multiple itmes are selected
			case ID_GUIED_ITEM_ALIGNLEFTS:
			case ID_GUIED_ITEM_ALIGNCENTERS:
			case ID_GUIED_ITEM_ALIGNRIGHTS:
			case ID_GUIED_ITEM_ALIGNBOTTOMS:
			case ID_GUIED_ITEM_ALIGNMIDDLES:
			case ID_GUIED_ITEM_ALIGNTOPS:
			case ID_GUIED_ITEM_MAKESAMESIZEHEIGHT:
			case ID_GUIED_ITEM_MAKESAMESIZEBOTH:
			case ID_GUIED_ITEM_MAKESAMESIZEWIDTH:
			case ID_GUIED_ITEM_ARRANGEMAKECHILD:
				EnableMenuItem ( hmenu, nPos, MF_BYPOSITION|(workspace->GetSelectionMgr().Num()>1?MF_ENABLED:MF_GRAYED) );
				break;

			case ID_GUIED_SOURCECONTROL_CHECKIN:
			case ID_GUIED_SOURCECONTROL_UNDOCHECKOUT:
				EnableMenuItem ( hmenu, nPos, MF_BYPOSITION|((workspace->GetSourceControlState()==rvGEWorkspace::SCS_CHECKEDOUT)?MF_ENABLED:MF_GRAYED) );
				break;

			case ID_GUIED_SOURCECONTROL_CHECKOUT:
				EnableMenuItem ( hmenu, nPos, MF_BYPOSITION|((workspace->GetSourceControlState()==rvGEWorkspace::SCS_CHECKEDIN)?MF_ENABLED:MF_GRAYED) );
				break;
			
			default:
				continue;
		}				
	}
	
	return 0;
} 

/*
================
rvGEApp::NewFile

Creates a new file and opens a window for it
================
*/
bool rvGEApp::NewFile ( void )
{
	rvGEWorkspace* workspace = new rvGEWorkspace ( this );
	if ( workspace->NewFile ( ) )
	{
		HWND child;
		
		child = CreateMDIWindow("QUAKE4_GUIEDITOR_CHILD_CLASS",
								"Untitled.gui",
								WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_HSCROLL|WS_VSCROLL|WS_MAXIMIZE,
								CW_USEDEFAULT,
								CW_USEDEFAULT,
								640,
								480,
								mMDIClient,
								mInstance,
								(LPARAM)workspace );
														
		ShowWindow ( child, SW_SHOW );
	}

	return true;
}

/*
================
rvGEApp::OpenFile

Opens the given file and will fail if its already open or could not
be opened for some reason
================
*/
bool rvGEApp::OpenFile ( const char* filename )
{
	int i;
	bool result = false;
	idStr error;

	// See if the file is already open and if so just make it active
	for ( i = 0; i < mWorkspaces.Num(); i ++ )
	{
		if ( !idStr::Icmp ( mWorkspaces[i]->GetFilename(), filename ) )
		{
			SendMessage ( mMDIClient, WM_MDIACTIVATE, (WPARAM)mWorkspaces[i]->GetWindow ( ), 0 );
			return false;
		}
	}

	SetCursor ( LoadCursor ( NULL, IDC_WAIT ) );

	// Setup the default error.
	error = va("Failed to parse '%s'", filename );

	rvGEWorkspace* workspace = new rvGEWorkspace ( this );
	if ( workspace->LoadFile ( filename, &error ) )
	{
		HWND child;
			
		child = CreateMDIWindow("QUAKE4_GUIEDITOR_CHILD_CLASS",
								"Unamed.gui",
								WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_HSCROLL|WS_VSCROLL|WS_MAXIMIZE,
								CW_USEDEFAULT,
								CW_USEDEFAULT,
								640,
								480,
								mMDIClient,
								mInstance,
								(LPARAM)workspace );
														
		ShowWindow ( child, SW_SHOW );
		
		mOptions.AddRecentFile ( filename ); 
		UpdateRecentFiles ( );

		result = true;
	}
	else
	{
		MessageBox ( error, MB_OK|MB_ICONERROR );
	}

	SetCursor ( LoadCursor ( NULL, IDC_ARROW ) );
	
	return result;;
}

/*
================
rvGEApp::InitRecentFiles

Finds the file menu and the location within it where the MRU should
be added.
================
*/
bool rvGEApp::InitRecentFiles ( void )
{
	int	i;
	int count;

	mRecentFileMenu = GetSubMenu ( GetMenu(mMDIFrame), 0 );
	count			= GetMenuItemCount ( mRecentFileMenu );
	
	for ( i = 0; i < count; i ++ )
	{
		if ( GetMenuItemID ( mRecentFileMenu, i ) == ID_GUIED_FILE_MRU )
		{
			mRecentFileInsertPos = i;
			DeleteMenu ( mRecentFileMenu, mRecentFileInsertPos, MF_BYPOSITION );
			return true;
		}
	}
	
	return false;
}

/*
================
rvGEApp::UpdateRecentFiles

Updates the mru in the menu
================
*/
void rvGEApp::UpdateRecentFiles ( void )
{
	int i;
	int j;

	// Make sure everything is initialized
	if ( !mRecentFileMenu )	
	{	
		InitRecentFiles ( );
	}

	// Delete all the old recent files from the menu's
	for ( i = 0; i < rvGEOptions::MAX_MRU_SIZE; i ++ )
	{
		DeleteMenu ( mRecentFileMenu, ID_GUIED_FILE_MRU1 + i, MF_BYCOMMAND );
	}	

	// Make sure there is a separator after the recent files
	if ( mOptions.GetRecentFileCount() )
	{
		MENUITEMINFO info;
		ZeroMemory ( &info, sizeof(info) );
		info.cbSize = sizeof(info);
		info.fMask = MIIM_FTYPE;
		GetMenuItemInfo ( mRecentFileMenu, mRecentFileInsertPos+1,TRUE, &info );
		if ( !(info.fType & MFT_SEPARATOR ) )
		{
			InsertMenu ( mRecentFileMenu, mRecentFileInsertPos, MF_BYPOSITION|MF_SEPARATOR|MF_ENABLED, 0, NULL );
		}
	}

	// Add the recent files to the menu now
	for ( j = 0, i = mOptions.GetRecentFileCount ( ) - 1; i >= 0; i --, j++ )
	{
		UINT id = ID_GUIED_FILE_MRU1 + j;
		idStr str = va("&%d ", j+1);
		str.Append ( mOptions.GetRecentFile ( i ) );
		InsertMenu ( mRecentFileMenu, mRecentFileInsertPos+j+1, MF_BYPOSITION|MF_STRING|MF_ENABLED, id, str );
	}		
}

/*
================
rvGEApp::CloseViewer

Closes the gui viewer
================
*/
void rvGEApp::CloseViewer ( void )
{
	if ( !mViewer )
	{
		return;
	}
	
	mViewer->Destroy ( );
	delete mViewer;
	mViewer = NULL;
}

/*
================
rvGEApp::ToolWindowActivate

Handles the nc activate message for all tool windows
================
*/
int	rvGEApp::ToolWindowActivate ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	bool	keepActive;
	bool	syncOthers;
	int		i;
	
	keepActive = ( wParam != 0 );
	syncOthers = true;
	
	for ( i = 0; i < mToolWindows.Num (); i ++ )
	{
		if ( (HWND)lParam == mToolWindows[i] ) 
		{
			keepActive = true;
			syncOthers = false;
			break;
		}
	}
	
	if ( lParam == -1 )
	{
		return DefWindowProc ( hwnd, WM_NCACTIVATE, keepActive, 0 );
	}

	if ( syncOthers )
	{
		for ( i = 0; i < mToolWindows.Num(); i ++ )
		{
			if ( mToolWindows[i] != hwnd &&	mToolWindows[i] != (HWND) lParam )
			{
				SendMessage ( mToolWindows[i], WM_NCACTIVATE, keepActive, (LPARAM)-1 );
			}
		}
	}
	
	return DefWindowProc ( hwnd, WM_NCACTIVATE, keepActive, lParam );
}

/*
================
rvGEApp::MessageBox

Displays a modal message box 
================
*/
int rvGEApp::MessageBox ( const char* text, int flags )
{
	return ::MessageBox ( mMDIFrame, text, "Quake 4 GUI Editor", flags );
}

