/*
===========================================================================

Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").

Doom 3 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.

Doom 3 Source Code 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 Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "sys/platform.h"
#include "idlib/containers/HashTable.h"
#include "framework/UsercmdGen.h"
#include "framework/KeyInput.h"
#include "ui/DeviceContext.h"
#include "ui/UserInterfaceLocal.h"
#include "ui/EditWindow.h"
#include "ui/ChoiceWindow.h"
#include "ui/SliderWindow.h"
#include "ui/BindWindow.h"
#include "ui/ListWindow.h"
#include "ui/RenderWindow.h"
#include "ui/MarkerWindow.h"
#include "ui/FieldWindow.h"
#include "ui/GameSSDWindow.h"
#include "ui/GameBearShootWindow.h"
#include "ui/GameBustOutWindow.h"
//  gui editor is more integrated into the window now
#include "tools/guied/GEWindowWrapper.h"

#include "ui/Window.h"

bool idWindow::registerIsTemporary[MAX_EXPRESSION_REGISTERS];		// statics to assist during parsing
//float idWindow::shaderRegisters[MAX_EXPRESSION_REGISTERS];
//wexpOp_t idWindow::shaderOps[MAX_EXPRESSION_OPS];

idCVar idWindow::gui_debug( "gui_debug", "0", CVAR_GUI | CVAR_BOOL, "" );
idCVar idWindow::gui_edit( "gui_edit", "0", CVAR_GUI | CVAR_BOOL, "" );

extern idCVar r_skipGuiShaders;		// 1 = don't render any gui elements on surfaces
extern idCVar r_scaleMenusTo43;

//  made RegisterVars a member of idWindow
const idRegEntry idWindow::RegisterVars[] = {
	{ "forecolor", idRegister::VEC4 },
	{ "hovercolor", idRegister::VEC4 },
	{ "backcolor", idRegister::VEC4 },
	{ "bordercolor", idRegister::VEC4 },
	{ "rect", idRegister::RECTANGLE },
	{ "matcolor", idRegister::VEC4 },
	{ "scale", idRegister::VEC2 },
	{ "translate", idRegister::VEC2 },
	{ "rotate", idRegister::FLOAT },
	{ "textscale", idRegister::FLOAT },
	{ "visible", idRegister::BOOL },
	{ "noevents", idRegister::BOOL },
	{ "text", idRegister::STRING },
	{ "background", idRegister::STRING },
	{ "runscript", idRegister::STRING },
	{ "varbackground", idRegister::STRING },
	{ "cvar", idRegister::STRING },
	{ "choices", idRegister::STRING },
	{ "choiceVar", idRegister::STRING },
	{ "bind", idRegister::STRING },
	{ "modelRotate", idRegister::VEC4 },
	{ "modelOrigin", idRegister::VEC4 },
	{ "lightOrigin", idRegister::VEC4 },
	{ "lightColor", idRegister::VEC4 },
	{ "viewOffset", idRegister::VEC4 },
	{ "hideCursor", idRegister::BOOL}
};

const int idWindow::NumRegisterVars = sizeof(RegisterVars) / sizeof(idRegEntry);

const char *idWindow::ScriptNames[] = {
	"onMouseEnter",
	"onMouseExit",
	"onAction",
	"onActivate",
	"onDeactivate",
	"onESC",
	"onEvent",
	"onTrigger",
	"onActionRelease",
	"onEnter",
	"onEnterRelease"
};

/*
================
idWindow::CommonInit
================
*/
void idWindow::CommonInit() {
	childID = 0;
	flags = 0;
	lastTimeRun = 0;
	origin.Zero();
	fontNum = 0;
	timeLine = -1;
	xOffset = yOffset = 0.0;
	cursor = 0;
	forceAspectWidth = 640;
	forceAspectHeight = 480;
	matScalex = 1;
	matScaley = 1;
	borderSize = 0;
	noTime = false;
	visible = true;
	textAlign = 0;
	textAlignx = 0;
	textAligny = 0;
	noEvents = false;
	rotate = 0;
	shear.Zero();
	textScale = 0.35f;
	backColor.Zero();
	foreColor = idVec4(1, 1, 1, 1);
	hoverColor = idVec4(1, 1, 1, 1);
	matColor = idVec4(1, 1, 1, 1);
	borderColor.Zero();
	background = NULL;
	backGroundName = "";
	focusedChild = NULL;
	captureChild = NULL;
	overChild = NULL;
	parent = NULL;
	saveOps = NULL;
	saveRegs = NULL;
	timeLine = -1;
	textShadow = 0;
	hover = false;

	for (int i = 0; i < SCRIPT_COUNT; i++) {
		scripts[i] = NULL;
	}

	hideCursor = false;
}

/*
================
idWindow::Size
================
*/
size_t idWindow::Size() {
	int c = children.Num();
	int sz = 0;
	for (int i = 0; i < c; i++) {
		sz += children[i]->Size();
	}
	sz += sizeof(*this) + Allocated();
	return sz;
}

/*
================
idWindow::Allocated
================
*/
size_t idWindow::Allocated() {
	int i, c;
	int sz = name.Allocated();
	sz += text.Size();
	sz += backGroundName.Size();

	c = definedVars.Num();
	for (i = 0; i < c; i++) {
		sz += definedVars[i]->Size();
	}

	for (i = 0; i < SCRIPT_COUNT; i++) {
		if (scripts[i]) {
			sz += scripts[i]->Size();
		}
	}
	c = timeLineEvents.Num();
	for (i = 0; i < c; i++) {
		sz += timeLineEvents[i]->Size();
	}

	c = namedEvents.Num();
	for (i = 0; i < c; i++) {
		sz += namedEvents[i]->Size();
	}

	c = drawWindows.Num();
	for (i = 0; i < c; i++) {
		if (drawWindows[i].simp) {
			sz += drawWindows[i].simp->Size();
		}
	}

	return sz;
}

/*
================
idWindow::idWindow
================
*/
idWindow::idWindow(idUserInterfaceLocal *ui) {
	dc = NULL;
	gui = ui;
	CommonInit();
}

/*
================
idWindow::idWindow
================
*/
idWindow::idWindow(idDeviceContext *d, idUserInterfaceLocal *ui) {
	dc = d;
	gui = ui;
	CommonInit();
}

/*
================
idWindow::CleanUp
================
*/
void idWindow::CleanUp() {
	int i, c = drawWindows.Num();
	for (i = 0; i < c; i++) {
		delete drawWindows[i].simp;
	}

	// ensure the register list gets cleaned up
	regList.Reset ( );

	// Cleanup the named events
	namedEvents.DeleteContents(true);

	// Cleanup the operations and update vars
	// (if it is not fixed, orphane register references are possible)
	ops.Clear();
	updateVars.Clear();

	drawWindows.Clear();
	children.DeleteContents(true);
	definedVars.DeleteContents(true);
	timeLineEvents.DeleteContents(true);
	for (i = 0; i < SCRIPT_COUNT; i++) {
		delete scripts[i];
	}
	CommonInit();
}

/*
================
idWindow::~idWindow
================
*/
idWindow::~idWindow() {
	CleanUp();
}

/*
================
idWindow::Move
================
*/
void idWindow::Move(float x, float y) {
	idRectangle rct = rect;
	rct.x = x;
	rct.y = y;
	idRegister *reg = RegList()->FindReg("rect");
	if (reg) {
		reg->Enable(false);
	}
	rect = rct;
}

/*
================
idWindow::SetFont
================
*/
void idWindow::SetFont() {
	dc->SetFont(fontNum);
}

/*
================
idWindow::GetMaxCharHeight
================
*/
float idWindow::GetMaxCharHeight() {
	SetFont();
	return dc->MaxCharHeight(textScale);
}

/*
================
idWindow::GetMaxCharWidth
================
*/
float idWindow::GetMaxCharWidth() {
	SetFont();
	return dc->MaxCharWidth(textScale);
}

/*
================
idWindow::Draw
================
*/
void idWindow::Draw( int time, float x, float y ) {
	if ( text.Length() == 0 ) {
		return;
	}
	if ( textShadow ) {
		idStr shadowText = text;
		idRectangle shadowRect = textRect;

		shadowText.RemoveColors();
		shadowRect.x += textShadow;
		shadowRect.y += textShadow;

		dc->DrawText( shadowText, textScale, textAlign, colorBlack, shadowRect, !( flags & WIN_NOWRAP ), -1 );
	}
	dc->DrawText( text, textScale, textAlign, foreColor, textRect, !( flags & WIN_NOWRAP ), -1 );

	if ( gui_edit.GetBool() ) {
		dc->EnableClipping( false );
		dc->DrawText( va( "x: %i  y: %i", ( int )rect.x(), ( int )rect.y() ), 0.25, 0, dc->colorWhite, idRectangle( rect.x(), rect.y() - 15, 100, 20 ), false );
		dc->DrawText( va( "w: %i  h: %i", ( int )rect.w(), ( int )rect.h() ), 0.25, 0, dc->colorWhite, idRectangle( rect.x() + rect.w(), rect.w() + rect.h() + 5, 100, 20 ), false );
		dc->EnableClipping( true );
	}

}

/*
================
idWindow::BringToTop
================
*/
void idWindow::BringToTop(idWindow *w) {

	if (w && !(w->flags & WIN_MODAL)) {
		return;
	}

	int c = children.Num();
	for (int i = 0; i < c; i++) {
		if (children[i] == w) {
			// this is it move from i - 1 to 0 to i to 1 then shove this one into 0
			for (int j = i+1; j < c; j++) {
				children[j-1] = children[j];
			}
			children[c-1] = w;
			break;
		}
	}
}

/*
================
idWindow::Size
================
*/
void idWindow::Size(float x, float y, float w, float h) {
	idRectangle rct = rect;
	rct.x = x;
	rct.y = y;
	rct.w = w;
	rct.h = h;
	rect = rct;
	CalcClientRect(0,0);
}

/*
================
idWindow::MouseEnter
================
*/
void idWindow::MouseEnter() {

	if (noEvents) {
		return;
	}

	RunScript(ON_MOUSEENTER);
}

/*
================
idWindow::MouseExit
================
*/
void idWindow::MouseExit() {

	if (noEvents) {
		return;
	}

	RunScript(ON_MOUSEEXIT);
}


/*
================
idWindow::RouteMouseCoords
================
*/
const char *idWindow::RouteMouseCoords(float xd, float yd) {
	idStr str;
	if (GetCaptureChild()) {
		//FIXME: unkludge this whole mechanism
		return GetCaptureChild()->RouteMouseCoords(xd, yd);
	}

	if (xd == -2000 || yd == -2000) {
		return "";
	}

	int c = children.Num();
	while (c > 0) {
		idWindow *child = children[--c];
		if (child->visible && !child->noEvents && child->Contains(child->drawRect, gui->CursorX(), gui->CursorY())) {

			dc->SetCursor(child->cursor);
			child->hover = true;

			if (overChild != child) {
				if (overChild) {
					overChild->MouseExit();
					str = overChild->cmd;
					if (str.Length()) {
						gui->GetDesktop()->AddCommand(str);
						overChild->cmd = "";
					}
				}
				overChild = child;
				overChild->MouseEnter();
				str = overChild->cmd;
				if (str.Length()) {
					gui->GetDesktop()->AddCommand(str);
					overChild->cmd = "";
				}
			} else {
				if (!(child->flags & WIN_HOLDCAPTURE)) {
					child->RouteMouseCoords(xd, yd);
				}
			}
			return "";
		}
	}
	if (overChild) {
		overChild->MouseExit();
		str = overChild->cmd;
		if (str.Length()) {
			gui->GetDesktop()->AddCommand(str);
			overChild->cmd = "";
		}
		overChild = NULL;
	}
	return "";
}

/*
================
idWindow::Activate
================
*/
void idWindow::Activate( bool activate,	idStr &act ) {

	int n = (activate) ? ON_ACTIVATE : ON_DEACTIVATE;

	//  make sure win vars are updated before activation
	UpdateWinVars ( );

	RunScript(n);
	int c = children.Num();
	for (int i = 0; i < c; i++) {
		children[i]->Activate( activate, act );
	}

	if ( act.Length() ) {
		act += " ; ";
	}
}

/*
================
idWindow::Trigger
================
*/
void idWindow::Trigger() {
	RunScript( ON_TRIGGER );
	int c = children.Num();
	for ( int i = 0; i < c; i++ ) {
		children[i]->Trigger();
	}
	StateChanged( true );
}

/*
================
idWindow::StateChanged
================
*/
void idWindow::StateChanged( bool redraw ) {

	UpdateWinVars();

	if (expressionRegisters.Num() && ops.Num()) {
		EvalRegs();
	}

	int c = drawWindows.Num();
	for ( int i = 0; i < c; i++ ) {
		if ( drawWindows[i].win ) {
			drawWindows[i].win->StateChanged( redraw );
		} else {
			drawWindows[i].simp->StateChanged( redraw );
		}
	}

	if ( redraw ) {
		if ( flags & WIN_DESKTOP ) {
			Redraw( 0.0f, 0.0f );
		}
		if ( background && background->CinematicLength() ) {
			background->UpdateCinematic( gui->GetTime() );
		}
	}
}

/*
================
idWindow::SetCapture
================
*/
idWindow *idWindow::SetCapture(idWindow *w) {
	// only one child can have the focus

	idWindow *last = NULL;
	int c = children.Num();
	for (int i = 0; i < c; i++) {
		if ( children[i]->flags & WIN_CAPTURE ) {
			last = children[i];
			//last->flags &= ~WIN_CAPTURE;
			last->LoseCapture();
			break;
		}
	}

	w->flags |= WIN_CAPTURE;
	w->GainCapture();
	gui->GetDesktop()->captureChild = w;
	return last;
}

/*
================
idWindow::AddUpdateVar
================
*/
void idWindow::AddUpdateVar(idWinVar *var) {
	updateVars.AddUnique(var);
}

/*
================
idWindow::UpdateWinVars
================
*/
void idWindow::UpdateWinVars() {
	int c = updateVars.Num();
	for (int i = 0; i < c; i++) {
		updateVars[i]->Update();
	}
}

/*
================
idWindow::RunTimeEvents
================
*/
bool idWindow::RunTimeEvents(int time) {

	if ( time - lastTimeRun < USERCMD_MSEC ) {
		//common->Printf("Skipping gui time events at %i\n", time);
		return false;
	}

	lastTimeRun = time;

	UpdateWinVars();

	if (expressionRegisters.Num() && ops.Num()) {
		EvalRegs();
	}

	if ( flags & WIN_INTRANSITION ) {
		Transition();
	}

	Time();

	// renamed ON_EVENT to ON_FRAME
	RunScript(ON_FRAME);

	int c = children.Num();
	for (int i = 0; i < c; i++) {
		children[i]->RunTimeEvents(time);
	}

	return true;
}

/*
================
idWindow::RunNamedEvent
================
*/
void idWindow::RunNamedEvent ( const char* eventName )
{
	int i;
	int c;

	// Find and run the event
	c = namedEvents.Num( );
	for ( i = 0; i < c; i ++ ) {
		if ( namedEvents[i]->mName.Icmp( eventName ) ) {
			continue;
		}

		UpdateWinVars();

		// Make sure we got all the current values for stuff
		if (expressionRegisters.Num() && ops.Num()) {
			EvalRegs(-1, true);
		}

		RunScriptList( namedEvents[i]->mEvent );

		break;
	}

	// Run the event in all the children as well
	c = children.Num();
	for ( i = 0; i < c; i++ ) {
		children[i]->RunNamedEvent ( eventName );
	}
}

/*
================
idWindow::Contains
================
*/
bool idWindow::Contains(const idRectangle &sr, float x, float y) {
	idRectangle r = sr;
	r.x += actualX - drawRect.x;
	r.y += actualY - drawRect.y;
	return r.Contains(x, y);
}

/*
================
idWindow::Contains
================
*/
bool idWindow::Contains(float x, float y) {
	idRectangle r = drawRect;
	r.x = actualX;
	r.y = actualY;
	return r.Contains(x, y);
}

/*
================
idWindow::AddCommand
================
*/
void idWindow::AddCommand(const char *_cmd) {
	idStr str = cmd;
	if (str.Length()) {
		str += " ; ";
		str += _cmd;
	} else {
		str = _cmd;
	}
	cmd = str;
}

/*
================
idWindow::HandleEvent
================
*/
const char *idWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals) {
	static bool actionDownRun;
	static bool actionUpRun;

	cmd = "";

	if ( flags & WIN_DESKTOP ) {
		actionDownRun = false;
		actionUpRun = false;
		if (expressionRegisters.Num() && ops.Num()) {
			EvalRegs();
		}
		RunTimeEvents(gui->GetTime());
		CalcRects(0,0);
		dc->SetCursor( idDeviceContext::CURSOR_ARROW );
	}

	if (visible && !noEvents) {

		if (event->evType == SE_KEY) {
			EvalRegs(-1, true);
			if (updateVisuals) {
				*updateVisuals = true;
			}

			if (event->evValue == K_MOUSE1) {

				if (!event->evValue2 && GetCaptureChild()) {
					GetCaptureChild()->LoseCapture();
					gui->GetDesktop()->captureChild = NULL;
					return "";
				}

				int c = children.Num();
				while (--c >= 0) {
					if (children[c]->visible && children[c]->Contains(children[c]->drawRect, gui->CursorX(), gui->CursorY()) && !(children[c]->noEvents)) {
						idWindow *child = children[c];
						if (event->evValue2) {
							BringToTop(child);
							SetFocus(child);
							if (child->flags & WIN_HOLDCAPTURE) {
								SetCapture(child);
							}
						}
						if (child->Contains(child->clientRect, gui->CursorX(), gui->CursorY())) {
							//if ((gui_edit.GetBool() && (child->flags & WIN_SELECTED)) || (!gui_edit.GetBool() && (child->flags & WIN_MOVABLE))) {
							//	SetCapture(child);
							//}
							SetFocus(child);
							const char *childRet = child->HandleEvent(event, updateVisuals);
							if (childRet && *childRet) {
								return childRet;
							}
							if (child->flags & WIN_MODAL) {
								return "";
							}
						} else {
							if (event->evValue2) {
								SetFocus(child);
								bool capture = true;
								if (capture && ((child->flags & WIN_MOVABLE) || gui_edit.GetBool())) {
									SetCapture(child);
								}
								return "";
							} else {
							}
						}
					}
				}
				if (event->evValue2 && !actionDownRun) {
					actionDownRun = RunScript( ON_ACTION );
				} else if (!actionUpRun) {
					actionUpRun = RunScript( ON_ACTIONRELEASE );
				}
			} else if (event->evValue == K_MOUSE2) {

				if (!event->evValue2 && GetCaptureChild()) {
					GetCaptureChild()->LoseCapture();
					gui->GetDesktop()->captureChild = NULL;
					return "";
				}

				int c = children.Num();
				while (--c >= 0) {
					if (children[c]->visible && children[c]->Contains(children[c]->drawRect, gui->CursorX(), gui->CursorY()) && !(children[c]->noEvents)) {
						idWindow *child = children[c];
						if (event->evValue2) {
							BringToTop(child);
							SetFocus(child);
						}
						if (child->Contains(child->clientRect,gui->CursorX(), gui->CursorY()) || GetCaptureChild() == child) {
							if ((gui_edit.GetBool() && (child->flags & WIN_SELECTED)) || (!gui_edit.GetBool() && (child->flags & WIN_MOVABLE))) {
								SetCapture(child);
							}
							const char *childRet = child->HandleEvent(event, updateVisuals);
							if (childRet && *childRet) {
								return childRet;
							}
							if (child->flags & WIN_MODAL) {
								return "";
							}
						}
					}
				}
			} else if (event->evValue == K_MOUSE3) {
				if (gui_edit.GetBool()) {
					int c = children.Num();
					for (int i = 0; i < c; i++) {
						if (children[i]->drawRect.Contains(gui->CursorX(), gui->CursorY())) {
							if (event->evValue2) {
								children[i]->flags ^= WIN_SELECTED;
								if (children[i]->flags & WIN_SELECTED) {
									flags &= ~WIN_SELECTED;
									return "childsel";
								}
							}
						}
					}
				}
			} else if (event->evValue == K_TAB && event->evValue2) {
				if (GetFocusedChild()) {
					const char *childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
					if (childRet && *childRet) {
						return childRet;
					}

					// If the window didn't handle the tab, then move the focus to the next window
					// or the previous window if shift is held down

					int direction = 1;
					if ( idKeyInput::IsDown( K_SHIFT ) ) {
						direction = -1;
					}

					idWindow *currentFocus = GetFocusedChild();
					idWindow *child = GetFocusedChild();
					idWindow *parent = child->GetParent();
					while ( parent ) {
						bool foundFocus = false;
						bool recurse = false;
						int index = 0;
						if ( child ) {
							index = parent->GetChildIndex( child ) + direction;
						} else if ( direction < 0 ) {
							index = parent->GetChildCount() - 1;
						}
						while ( index < parent->GetChildCount() && index >= 0) {
							idWindow *testWindow = parent->GetChild( index );
							if ( testWindow == currentFocus ) {
								// we managed to wrap around and get back to our starting window
								foundFocus = true;
								break;
							}
							if ( testWindow && !testWindow->noEvents && testWindow->visible ) {
								if ( testWindow->flags & WIN_CANFOCUS ) {
									SetFocus( testWindow );
									foundFocus = true;
									break;
								} else if ( testWindow->GetChildCount() > 0 ) {
									parent = testWindow;
									child = NULL;
									recurse = true;
									break;
								}
							}
							index += direction;
						}
						if ( foundFocus ) {
							// We found a child to focus on
							break;
						} else if ( recurse ) {
							// We found a child with children
							continue;
						} else {
							// We didn't find anything, so go back up to our parent
							child = parent;
							parent = child->GetParent();
							if ( parent == gui->GetDesktop() ) {
								// We got back to the desktop, so wrap around but don't actually go to the desktop
								parent = NULL;
								child = NULL;
							}
						}
					}
				}
			} else if (event->evValue == K_ESCAPE && event->evValue2) {
				if (GetFocusedChild()) {
					const char *childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
					if (childRet && *childRet) {
						return childRet;
					}
				}
				RunScript( ON_ESC );
			} else if (event->evValue == K_ENTER ) {
				if (GetFocusedChild()) {
					const char *childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
					if (childRet && *childRet) {
						return childRet;
					}
				}
				if ( flags & WIN_WANTENTER ) {
					if ( event->evValue2 ) {
						RunScript( ON_ACTION );
					} else {
						RunScript( ON_ACTIONRELEASE );
					}
				}
			} else {
				if (GetFocusedChild()) {
					const char *childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
					if (childRet && *childRet) {
						return childRet;
					}
				}
			}

		} else if (event->evType == SE_MOUSE) {
			if (updateVisuals) {
				*updateVisuals = true;
			}
			const char *mouseRet = RouteMouseCoords(event->evValue, event->evValue2);
			if (mouseRet && *mouseRet) {
				return mouseRet;
			}
		} else if (event->evType == SE_NONE) {
		} else if (event->evType == SE_CHAR) {
			if (GetFocusedChild()) {
				const char *childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
				if (childRet && *childRet) {
					return childRet;
				}
			}
		}
	}

	gui->GetReturnCmd() = cmd;
	if ( gui->GetPendingCmd().Length() ) {
		gui->GetReturnCmd() += " ; ";
		gui->GetReturnCmd() += gui->GetPendingCmd();
		gui->GetPendingCmd().Clear();
	}
	cmd = "";
	return gui->GetReturnCmd();
}

/*
================
idWindow::DebugDraw
================
*/
void idWindow::DebugDraw(int time, float x, float y) {
	static char buff[16384];
	if (dc) {
		dc->EnableClipping(false);
		if (gui_debug.GetInteger() == 1) {
			dc->DrawRect(drawRect.x, drawRect.y, drawRect.w, drawRect.h, 1, idDeviceContext::colorRed);
		} else if (gui_debug.GetInteger() == 2) {
			char out[1024];
			idStr str;
			str = text.c_str();

			if (str.Length()) {
				sprintf(buff, "%s\n", str.c_str());
			}

			sprintf(out, "Rect: %0.1f, %0.1f, %0.1f, %0.1f\n", rect.x(), rect.y(), rect.w(), rect.h());
			strcat(buff, out);
			sprintf(out, "Draw Rect: %0.1f, %0.1f, %0.1f, %0.1f\n", drawRect.x, drawRect.y, drawRect.w, drawRect.h);
			strcat(buff, out);
			sprintf(out, "Client Rect: %0.1f, %0.1f, %0.1f, %0.1f\n", clientRect.x, clientRect.y, clientRect.w, clientRect.h);
			strcat(buff, out);
			sprintf(out, "Cursor: %0.1f : %0.1f\n", gui->CursorX(), gui->CursorY());
			strcat(buff, out);


			//idRectangle tempRect = textRect;
			//tempRect.x += offsetX;
			//drawRect.y += offsetY;
			dc->DrawText(buff, textScale, textAlign, foreColor, textRect, true);
		}
		dc->EnableClipping(true);
	}
}

/*
================
idWindow::Transition
================
*/
void idWindow::Transition() {
	int i, c = transitions.Num();
	bool clear = true;

	for ( i = 0; i < c; i++ ) {
		idTransitionData *data = &transitions[i];
		idWinRectangle *r = NULL;
		idWinVec4 *v4 = dynamic_cast<idWinVec4*>(data->data);
		idWinFloat* val = NULL;
		if (v4 == NULL) {
			r = dynamic_cast<idWinRectangle*>(data->data);
			if ( !r ) {
				val = dynamic_cast<idWinFloat*>(data->data);
			}
		}
		if ( data->interp.IsDone( gui->GetTime() ) && data->data) {
			if (v4) {
				*v4 = data->interp.GetEndValue();
			} else if ( val ) {
				*val = data->interp.GetEndValue()[0];
			} else {
				*r = data->interp.GetEndValue();
			}
		} else {
			clear = false;
			if (data->data) {
				if (v4) {
					*v4 = data->interp.GetCurrentValue( gui->GetTime() );
				} else if ( val ) {
					*val = data->interp.GetCurrentValue( gui->GetTime() )[0];
				} else {
					*r = data->interp.GetCurrentValue( gui->GetTime() );
				}
			} else {
				common->Warning("Invalid transitional data for window %s in gui %s", GetName(), gui->GetSourceFile());
			}
		}
	}

	if ( clear ) {
		transitions.SetNum( 0, false );
		flags &= ~WIN_INTRANSITION;
	}
}

/*
================
idWindow::Time
================
*/
void idWindow::Time() {

	if ( noTime ) {
		return;
	}

	if ( timeLine == -1 ) {
		timeLine = gui->GetTime();
	}

	cmd = "";

	int c = timeLineEvents.Num();
	if ( c > 0 ) {
		for (int i = 0; i < c; i++) {
			if ( timeLineEvents[i]->pending && gui->GetTime() - timeLine >= timeLineEvents[i]->time ) {
				timeLineEvents[i]->pending = false;
				RunScriptList( timeLineEvents[i]->event );
			}
		}
	}
	if ( gui->Active() ) {
		gui->GetPendingCmd() += cmd;
	}
}

/*
================
idWindow::EvalRegs
================
*/
float idWindow::EvalRegs(int test, bool force) {
	static float regs[MAX_EXPRESSION_REGISTERS];
	static idWindow *lastEval = NULL;

	if (!force && test >= 0 && test < MAX_EXPRESSION_REGISTERS && lastEval == this) {
		return regs[test];
	}

	lastEval = this;

	if (expressionRegisters.Num()) {
		regList.SetToRegs(regs);
		EvaluateRegisters(regs);
		regList.GetFromRegs(regs);
	}

	if (test >= 0 && test < MAX_EXPRESSION_REGISTERS) {
		return regs[test];
	}

	return 0.0;
}

/*
================
idWindow::DrawBackground
================
*/
void idWindow::DrawBackground(const idRectangle &drawRect) {
	if ( backColor.w() ) {
		dc->DrawFilledRect(drawRect.x, drawRect.y, drawRect.w, drawRect.h, backColor);
	}

	if ( background && matColor.w() ) {
		float scalex, scaley;
		if ( flags & WIN_NATURALMAT ) {
			scalex = drawRect.w / background->GetImageWidth();
			scaley = drawRect.h / background->GetImageHeight();
		} else {
			scalex = matScalex;
			scaley = matScaley;
		}
		dc->DrawMaterial(drawRect.x, drawRect.y, drawRect.w, drawRect.h, background, matColor, scalex, scaley);
	}
}

/*
================
idWindow::DrawBorderAndCaption
================
*/
void idWindow::DrawBorderAndCaption(const idRectangle &drawRect) {
	if ( flags & WIN_BORDER && borderSize && borderColor.w() ) {
		dc->DrawRect(drawRect.x, drawRect.y, drawRect.w, drawRect.h, borderSize, borderColor);
	}
}

/*
================
idWindow::SetupTransforms
================
*/
void idWindow::SetupTransforms(float x, float y) {
	static idMat3 trans;
	static idVec3 org;

	trans.Identity();
	org.Set( origin.x + x, origin.y + y, 0 );

	if ( rotate ) {
		static idRotation rot;
		static idVec3 vec(0, 0, 1);
		rot.Set( org, vec, rotate );
		trans = rot.ToMat3();
	}

	if ( shear.x || shear.y ) {
		static idMat3 smat;
		smat.Identity();
		smat[0][1] = shear.x;
		smat[1][0] = shear.y;
		trans *= smat;
	}

	if ( !trans.IsIdentity() ) {
		dc->SetTransformInfo( org, trans );
	}
}

/*
================
idWindow::CalcRects
================
*/
void idWindow::CalcRects(float x, float y) {
	CalcClientRect(0, 0);
	drawRect.Offset(x, y);
	clientRect.Offset(x, y);
	actualX = drawRect.x;
	actualY = drawRect.y;
	int c = drawWindows.Num();
	for (int i = 0; i < c; i++) {
		if (drawWindows[i].win) {
			drawWindows[i].win->CalcRects(clientRect.x + xOffset, clientRect.y + yOffset);
		}
	}
	drawRect.Offset(-x, -y);
	clientRect.Offset(-x, -y);
}

/*
================
idWindow::Redraw
================
*/
void idWindow::Redraw(float x, float y) {
	idStr str;

	if (r_skipGuiShaders.GetInteger() == 1 || dc == NULL ) {
		return;
	}

	int time = gui->GetTime();

	if ( flags & WIN_DESKTOP && r_skipGuiShaders.GetInteger() != 3 ) {
		RunTimeEvents( time );
	}

	if ( r_skipGuiShaders.GetInteger() == 2 ) {
		return;
	}

	// DG: allow scaling menus to 4:3
	bool fixupFor43 = false;
	if ( flags & WIN_DESKTOP ) {
		// only scale desktop windows (will automatically scale its sub-windows)
		// that EITHER have the scaleto43 flag set OR are fullscreen menus and r_scaleMenusTo43 is 1
		if( (flags & WIN_SCALETO43) ||
			((flags & WIN_MENUGUI) && r_scaleMenusTo43.GetBool()) )
		{
			fixupFor43 = true;
			dc->SetMenuScaleFix(true);
		}
	}

	if ( flags & WIN_SHOWTIME ) {
		dc->DrawText(va(" %0.1f seconds\n%s", (float)(time - timeLine) / 1000, gui->State().GetString("name")), 0.35f, 0, dc->colorWhite, idRectangle(100, 0, 80, 80), false);
	}

	if ( flags & WIN_SHOWCOORDS ) {
		dc->EnableClipping(false);
		sprintf(str, "x: %i y: %i  cursorx: %i cursory: %i", (int)rect.x(), (int)rect.y(), (int)gui->CursorX(), (int)gui->CursorY());
		dc->DrawText(str, 0.25f, 0, dc->colorWhite, idRectangle(0, 0, 100, 20), false);
		dc->EnableClipping(true);
	}

	if (!visible) {
		if (fixupFor43) { // DG: gotta reset that before returning this function
			dc->SetMenuScaleFix(false);
		}
		return;
	}

	CalcClientRect(0, 0);

	SetFont();
	//if (flags & WIN_DESKTOP) {
		// see if this window forces a new aspect ratio
		dc->SetSize(forceAspectWidth, forceAspectHeight);
	//}

	//FIXME: go to screen coord tracking
	drawRect.Offset(x, y);
	clientRect.Offset(x, y);
	textRect.Offset(x, y);
	actualX = drawRect.x;
	actualY = drawRect.y;

	idVec3	oldOrg;
	idMat3	oldTrans;

	dc->GetTransformInfo( oldOrg, oldTrans );

	SetupTransforms(x, y);
	DrawBackground(drawRect);
	DrawBorderAndCaption(drawRect);

	if ( !( flags & WIN_NOCLIP) ) {
		dc->PushClipRect(clientRect);
	}

	if ( r_skipGuiShaders.GetInteger() < 5 ) {
		Draw(time, x, y);
	}

	if ( gui_debug.GetInteger() ) {
		DebugDraw(time, x, y);
	}

	int c = drawWindows.Num();
	for ( int i = 0; i < c; i++ ) {
		if ( drawWindows[i].win ) {
			drawWindows[i].win->Redraw( clientRect.x + xOffset, clientRect.y + yOffset );
		} else {
			drawWindows[i].simp->Redraw( clientRect.x + xOffset, clientRect.y + yOffset );
		}
	}

	// Put transforms back to what they were before the children were processed
	dc->SetTransformInfo(oldOrg, oldTrans);

	if ( ! ( flags & WIN_NOCLIP ) ) {
		dc->PopClipRect();
	}

	if (gui_edit.GetBool()  || (flags & WIN_DESKTOP && !( flags & WIN_NOCURSOR )  && !hideCursor && (gui->Active() || ( flags & WIN_MENUGUI ) ))) {
		dc->SetTransformInfo(vec3_origin, mat3_identity);
		gui->DrawCursor();
	}

	if (gui_debug.GetInteger() && flags & WIN_DESKTOP) {
		dc->EnableClipping(false);
		sprintf(str, "x: %1.f y: %1.f",  gui->CursorX(), gui->CursorY());
		dc->DrawText(str, 0.25, 0, dc->colorWhite, idRectangle(0, 0, 100, 20), false);
		dc->DrawText(gui->GetSourceFile(), 0.25, 0, dc->colorWhite, idRectangle(0, 20, 300, 20), false);
		dc->EnableClipping(true);
	}

	if (fixupFor43) { // DG: gotta reset that before returning this function
		dc->SetMenuScaleFix(false);
	}

	drawRect.Offset(-x, -y);
	clientRect.Offset(-x, -y);
	textRect.Offset(-x, -y);
}

/*
================
idWindow::SetDC
================
*/
void idWindow::SetDC(idDeviceContext *d) {
	dc = d;
	//if (flags & WIN_DESKTOP) {
		dc->SetSize(forceAspectWidth, forceAspectHeight);
	//}
	int c = children.Num();
	for (int i = 0; i < c; i++) {
		children[i]->SetDC(d);
	}
}

/*
================
idWindow::ArchiveToDictionary
================
*/
void idWindow::ArchiveToDictionary(idDict *dict, bool useNames) {
	//FIXME: rewrite without state
	int c = children.Num();
	for (int i = 0; i < c; i++) {
		children[i]->ArchiveToDictionary(dict);
	}
}

/*
================
idWindow::InitFromDictionary
================
*/
void idWindow::InitFromDictionary(idDict *dict, bool byName) {
	//FIXME: rewrite without state
	int c = children.Num();
	for (int i = 0; i < c; i++) {
		children[i]->InitFromDictionary(dict);
	}
}

/*
================
idWindow::CalcClientRect
================
*/
void idWindow::CalcClientRect(float xofs, float yofs) {
	drawRect = rect;

	if ( flags & WIN_INVERTRECT ) {
		drawRect.x = rect.x() - rect.w();
		drawRect.y = rect.y() - rect.h();
	}

	if (flags & (WIN_HCENTER | WIN_VCENTER) && parent) {
		// in this case treat xofs and yofs as absolute top left coords
		// and ignore the original positioning
		if (flags & WIN_HCENTER) {
			drawRect.x = (parent->rect.w() - rect.w()) / 2;
		} else {
			drawRect.y = (parent->rect.h() - rect.h()) / 2;
		}
	}

	drawRect.x += xofs;
	drawRect.y += yofs;

	clientRect = drawRect;
	if (rect.h() > 0.0 && rect.w() > 0.0) {

		if (flags & WIN_BORDER && borderSize != 0.0) {
			clientRect.x += borderSize;
			clientRect.y += borderSize;
			clientRect.w -= borderSize;
			clientRect.h -= borderSize;
		}

		textRect = clientRect;
		textRect.x += 2.0;
		textRect.w -= 2.0;
		textRect.y += 2.0;
		textRect.h -= 2.0;

		textRect.x += textAlignx;
		textRect.y += textAligny;

	}
	origin.Set( rect.x() + (rect.w() / 2 ), rect.y() + ( rect.h() / 2 ) );

}

/*
================
idWindow::SetupBackground
================
*/
void idWindow::SetupBackground() {
	if (backGroundName.Length()) {
		background = declManager->FindMaterial(backGroundName);
		background->SetImageClassifications( 1 );	// just for resource tracking
		if ( background && !background->TestMaterialFlag( MF_DEFAULTED ) ) {
			background->SetSort(SS_GUI );
		}
	}
	backGroundName.SetMaterialPtr(&background);
}

/*
================
idWindow::SetupFromState
================
*/
void idWindow::SetupFromState() {
	idStr str;
	background = NULL;

	SetupBackground();

	if (borderSize) {
		flags |= WIN_BORDER;
	}

	if (regList.FindReg("rotate") || regList.FindReg("shear")) {
		flags |= WIN_TRANSFORM;
	}

	CalcClientRect(0,0);
	if ( scripts[ ON_ACTION ] ) {
		cursor = idDeviceContext::CURSOR_HAND;
		flags |= WIN_CANFOCUS;
	}
}

/*
================
idWindow::Moved
================
*/
void idWindow::Moved() {
}

/*
================
idWindow::Sized
================
*/
void idWindow::Sized() {
}

/*
================
idWindow::GainFocus
================
*/
void idWindow::GainFocus() {
}

/*
================
idWindow::LoseFocus
================
*/
void idWindow::LoseFocus() {
}

/*
================
idWindow::GainCapture
================
*/
void idWindow::GainCapture() {
}

/*
================
idWindow::LoseCapture
================
*/
void idWindow::LoseCapture() {
	flags &= ~WIN_CAPTURE;
}

/*
================
idWindow::SetFlag
================
*/
void idWindow::SetFlag(unsigned int f) {
	flags |= f;
}

/*
================
idWindow::ClearFlag
================
*/
void idWindow::ClearFlag(unsigned int f) {
	flags &= ~f;
}


/*
================
idWindow::SetParent
================
*/
void idWindow::SetParent(idWindow *w) {
	parent = w;
}

/*
================
idWindow::GetCaptureChild
================
*/
idWindow *idWindow::GetCaptureChild() {
	if (flags & WIN_DESKTOP) {
		return gui->GetDesktop()->captureChild;
	}
	return NULL;
}

/*
================
idWindow::GetFocusedChild
================
*/
idWindow *idWindow::GetFocusedChild() {
	if (flags & WIN_DESKTOP) {
		return gui->GetDesktop()->focusedChild;
	}
	return NULL;
}


/*
================
idWindow::SetFocus
================
*/
idWindow *idWindow::SetFocus(idWindow *w, bool scripts) {
	// only one child can have the focus
	idWindow *lastFocus = NULL;
	if (w->flags & WIN_CANFOCUS) {
		lastFocus = gui->GetDesktop()->focusedChild;
		if ( lastFocus ) {
			lastFocus->flags &= ~WIN_FOCUS;
			lastFocus->LoseFocus();
		}

		//  call on lose focus
		if ( scripts && lastFocus ) {
			// calling this broke all sorts of guis
			// lastFocus->RunScript(ON_MOUSEEXIT);
		}
		//  call on gain focus
		if ( scripts && w ) {
			// calling this broke all sorts of guis
			// w->RunScript(ON_MOUSEENTER);
		}

		w->flags |= WIN_FOCUS;
		w->GainFocus();
		gui->GetDesktop()->focusedChild = w;
	}

	return lastFocus;
}

/*
================
idWindow::ParseScript
================
*/
bool idWindow::ParseScript(idParser *src, idGuiScriptList &list, int *timeParm, bool elseBlock ) {

	bool	ifElseBlock = false;

	idToken token;

	// scripts start with { ( unless parm is true ) and have ; separated command lists.. commands are command,
	// arg.. basically we want everything between the { } as it will be interpreted at
	// run time

	if ( elseBlock ) {
		src->ReadToken ( &token );

		if ( !token.Icmp ( "if" ) ) {
			ifElseBlock = true;
		}

		src->UnreadToken ( &token );

		if ( !ifElseBlock && !src->ExpectTokenString( "{" ) ) {
			return false;
		}
	}
	else if ( !src->ExpectTokenString( "{" ) ) {
		return false;
	}

	int nest = 0;

	while (1) {
		if ( !src->ReadToken(&token) ) {
			src->Error( "Unexpected end of file" );
			return false;
		}

		if ( token == "{" ) {
			nest++;
		}

		if ( token == "}" ) {
			if (nest-- <= 0) {
				return true;
			}
		}

		idGuiScript *gs = new idGuiScript();
		if (token.Icmp("if") == 0) {
			gs->conditionReg = ParseExpression(src);
			gs->ifList = new idGuiScriptList();
			ParseScript(src, *gs->ifList, NULL);
			if (src->ReadToken(&token)) {
				if (token == "else") {
					gs->elseList = new idGuiScriptList();
					// pass true to indicate we are parsing an else condition
					ParseScript(src, *gs->elseList, NULL, true );
				} else {
					src->UnreadToken(&token);
				}
			}

			list.Append(gs);

			// if we are parsing an else if then return out so
			// the initial "if" parser can handle the rest of the tokens
			if ( ifElseBlock ) {
				return true;
			}
			continue;
		} else {
			src->UnreadToken(&token);
		}

		// empty { } is not allowed
		if ( token == "{" ) {
			 src->Error ( "Unexpected {" );
			 delete gs;
			 return false;
		}

		gs->Parse(src);
		list.Append(gs);
	}

}

/*
================
idWindow::SaveExpressionParseState
================
*/
void idWindow::SaveExpressionParseState() {
	saveTemps = (bool*)Mem_Alloc(MAX_EXPRESSION_REGISTERS * sizeof(bool));
	memcpy(saveTemps, registerIsTemporary, MAX_EXPRESSION_REGISTERS * sizeof(bool));
}

/*
================
idWindow::RestoreExpressionParseState
================
*/
void idWindow::RestoreExpressionParseState() {
	memcpy(registerIsTemporary, saveTemps, MAX_EXPRESSION_REGISTERS * sizeof(bool));
	Mem_Free(saveTemps);
}

/*
================
idWindow::ParseScriptEntry
================
*/
bool idWindow::ParseScriptEntry(const char *name, idParser *src) {
	for (int i = 0; i < SCRIPT_COUNT; i++) {
		if (idStr::Icmp(name, ScriptNames[i]) == 0) {
			delete scripts[i];
			scripts[i] = new idGuiScriptList;
			return ParseScript(src, *scripts[i]);
		}
	}
	return false;
}

/*
================
idWindow::DisableRegister
================
*/
void idWindow::DisableRegister(const char *_name) {
	idRegister *reg = RegList()->FindReg(_name);
	if (reg) {
		reg->Enable(false);
	}
}

/*
================
idWindow::PostParse
================
*/
void idWindow::PostParse() {
}

/*
================
idWindow::GetWinVarOffset
================
*/
intptr_t idWindow::GetWinVarOffset( idWinVar *wv, drawWin_t* owner) {
	intptr_t ret = -1;

	if ( wv == &rect ) {
		ret = (ptrdiff_t)&this->rect - (ptrdiff_t)this;
	}

	if ( wv == &backColor ) {
		ret = (ptrdiff_t)&this->backColor - (ptrdiff_t)this;
	}

	if ( wv == &matColor ) {
		ret = (ptrdiff_t)&this->matColor - (ptrdiff_t)this;
	}

	if ( wv == &foreColor ) {
		ret = (ptrdiff_t)&this->foreColor - (ptrdiff_t)this;
	}

	if ( wv == &hoverColor ) {
		ret = (ptrdiff_t)&this->hoverColor - (ptrdiff_t)this;
	}

	if ( wv == &borderColor ) {
		ret = (ptrdiff_t)&this->borderColor - (ptrdiff_t)this;
	}

	if ( wv == &textScale ) {
		ret = (ptrdiff_t)&this->textScale - (ptrdiff_t)this;
	}

	if ( wv == &rotate ) {
		ret = (ptrdiff_t)&this->rotate - (ptrdiff_t)this;
	}

	if ( ret != -1 ) {
		owner->win = this;
		return ret;
	}

	for ( int i = 0; i < drawWindows.Num(); i++ ) {
		if ( drawWindows[i].win ) {
			ret = drawWindows[i].win->GetWinVarOffset( wv, owner );
		} else {
			ret = drawWindows[i].simp->GetWinVarOffset( wv, owner );
		}
		if ( ret != -1 ) {
			break;
		}
	}

	return ret;
}

/*
================
idWindow::GetWinVarByName
================
*/
idWinVar *idWindow::GetWinVarByName(const char *_name, bool fixup, drawWin_t** owner) {
	idWinVar *retVar = NULL;

	if ( owner ) {
		*owner = NULL;
	}

	if (idStr::Icmp(_name, "notime") == 0) {
		retVar = &noTime;
	}
	if (idStr::Icmp(_name, "background") == 0) {
		retVar = &backGroundName;
	}
	if (idStr::Icmp(_name, "visible") == 0) {
		retVar = &visible;
	}
	if (idStr::Icmp(_name, "rect") == 0) {
		retVar = &rect;
	}
	if (idStr::Icmp(_name, "backColor") == 0) {
		retVar = &backColor;
	}
	if (idStr::Icmp(_name, "matColor") == 0) {
		retVar = &matColor;
	}
	if (idStr::Icmp(_name, "foreColor") == 0) {
		retVar = &foreColor;
	}
	if (idStr::Icmp(_name, "hoverColor") == 0) {
		retVar = &hoverColor;
	}
	if (idStr::Icmp(_name, "borderColor") == 0) {
		retVar = &borderColor;
	}
	if (idStr::Icmp(_name, "textScale") == 0) {
		retVar = &textScale;
	}
	if (idStr::Icmp(_name, "rotate") == 0) {
		retVar = &rotate;
	}
	if (idStr::Icmp(_name, "noEvents") == 0) {
		retVar = &noEvents;
	}
	if (idStr::Icmp(_name, "text") == 0) {
		retVar = &text;
	}
	if (idStr::Icmp(_name, "backGroundName") == 0) {
		retVar = &backGroundName;
	}
	if (idStr::Icmp(_name, "hidecursor") == 0) {
		retVar = &hideCursor;
	}

	idStr key = _name;
	bool guiVar = (key.Find(VAR_GUIPREFIX) >= 0);
	int c = definedVars.Num();
	for (int i = 0; i < c; i++) {
		if (idStr::Icmp(_name, (guiVar) ? va("%s",definedVars[i]->GetName()) : definedVars[i]->GetName()) == 0) {
			retVar = definedVars[i];
			break;
		}
	}

	if (retVar) {
		if (fixup && *_name != '$') {
			DisableRegister(_name);
		}

		if ( owner && parent ) {
			*owner = parent->FindChildByName ( name );
		}

		return retVar;
	}

	int len = key.Length();
	if ( len > 5 && guiVar ) {
		idWinVar *var = new idWinStr;
		var->Init(_name, this);
		definedVars.Append(var);
		return var;
	} else if (fixup) {
		int n = key.Find("::");
		if (n > 0) {
			idStr winName = key.Left(n);
			idStr var = key.Right(key.Length() - n - 2);
			drawWin_t *win = GetGui()->GetDesktop()->FindChildByName(winName);
			if (win) {
				if (win->win) {
					return win->win->GetWinVarByName(var, false, owner);
				} else {
					if ( owner ) {
						*owner = win;
					}
					return win->simp->GetWinVarByName(var);
				}
			}
		}
	}
	return NULL;
}

/*
================
idWindow::ParseString
================
*/
void idWindow::ParseString(idParser *src, idStr &out) {
	idToken tok;
	if (src->ReadToken(&tok)) {
		out = tok;
	}
}

/*
================
idWindow::ParseVec4
================
*/
void idWindow::ParseVec4(idParser *src, idVec4 &out) {
	idToken tok;
	src->ReadToken(&tok);
	out.x = atof(tok);
	src->ExpectTokenString(",");
	src->ReadToken(&tok);
	out.y = atof(tok);
	src->ExpectTokenString(",");
	src->ReadToken(&tok);
	out.z = atof(tok);
	src->ExpectTokenString(",");
	src->ReadToken(&tok);
	out.w = atof(tok);
}

/*
================
idWindow::ParseInternalVar
================
*/
bool idWindow::ParseInternalVar(const char *_name, idParser *src) {

	if (idStr::Icmp(_name, "showtime") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_SHOWTIME;
		}
		return true;
	}
	if (idStr::Icmp(_name, "showcoords") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_SHOWCOORDS;
		}
		return true;
	}
	// DG: added this window flag for Windows that should be scaled to 4:3
	//     (with "empty" bars left/right or above/below)
	if (idStr::Icmp(_name, "scaleto43") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_SCALETO43;
		}
		return true;
	}
	// DG end
	if (idStr::Icmp(_name, "forceaspectwidth") == 0) {
		forceAspectWidth = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "forceaspectheight") == 0) {
		forceAspectHeight = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "matscalex") == 0) {
		matScalex = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "matscaley") == 0) {
		matScaley = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "bordersize") == 0) {
		borderSize = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "nowrap") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_NOWRAP;
		}
		return true;
	}
	if (idStr::Icmp(_name, "shadow") == 0) {
		textShadow = src->ParseInt();
		return true;
	}
	if (idStr::Icmp(_name, "textalign") == 0) {
		textAlign = src->ParseInt();
		return true;
	}
	if (idStr::Icmp(_name, "textalignx") == 0) {
		textAlignx = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "textaligny") == 0) {
		textAligny = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "shear") == 0) {
		shear.x = src->ParseFloat();
		idToken tok;
		src->ReadToken( &tok );
		if ( tok.Icmp( "," ) ) {
			src->Error( "Expected comma in shear definiation" );
			return false;
		}
		shear.y = src->ParseFloat();
		return true;
	}
	if (idStr::Icmp(_name, "wantenter") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_WANTENTER;
		}
		return true;
	}
	if (idStr::Icmp(_name, "naturalmatscale") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_NATURALMAT;
		}
		return true;
	}
	if (idStr::Icmp(_name, "noclip") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_NOCLIP;
		}
		return true;
	}
	if (idStr::Icmp(_name, "nocursor") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_NOCURSOR;
		}
		return true;
	}
	if (idStr::Icmp(_name, "menugui") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_MENUGUI;
		}
		return true;
	}
	if (idStr::Icmp(_name, "modal") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_MODAL;
		}
		return true;
	}
	if (idStr::Icmp(_name, "invertrect") == 0) {
		if ( src->ParseBool() ) {
			flags |= WIN_INVERTRECT;
		}
		return true;
	}
	if (idStr::Icmp(_name, "name") == 0) {
		ParseString(src, name);
		return true;
	}
	if (idStr::Icmp(_name, "play") == 0) {
		common->Warning( "play encountered during gui parse.. see Robert\n" );
		idStr playStr;
		ParseString(src, playStr);
		return true;
	}
	if (idStr::Icmp(_name, "comment") == 0) {
		ParseString(src, comment);
		return true;
	}
	if ( idStr::Icmp( _name, "font" ) == 0 ) {
		idStr fontStr;
		ParseString( src, fontStr );
		fontNum = dc->FindFont( fontStr );
		return true;
	}
	return false;
}

/*
================
idWindow::ParseRegEntry
================
*/
bool idWindow::ParseRegEntry(const char *name, idParser *src) {
	idStr work;
	work = name;
	work.ToLower();

	idWinVar *var = GetWinVarByName(work, false);
	if ( var ) {
		for (int i = 0; i < NumRegisterVars; i++) {
			if (idStr::Icmp(work, RegisterVars[i].name) == 0) {
				regList.AddReg(work, RegisterVars[i].type, src, this, var);
				return true;
			}
		}
	}

	// not predefined so just read the next token and add it to the state
	idToken tok;
	idVec4 v;
	idWinInt *vari;
	idWinFloat *varf;
	idWinStr *vars;
	if (src->ReadToken(&tok)) {
		if (var) {
			var->Set(tok);
			return true;
		}
		switch (tok.type) {
			case TT_NUMBER :
				if (tok.subtype & TT_INTEGER) {
					vari = new idWinInt();
					*vari = atoi(tok);
					vari->SetName(work);
					definedVars.Append(vari);
				} else if (tok.subtype & TT_FLOAT) {
					varf = new idWinFloat();
					*varf = atof(tok);
					varf->SetName(work);
					definedVars.Append(varf);
				} else {
					vars = new idWinStr();
					*vars = tok;
					vars->SetName(work);
					definedVars.Append(vars);
				}
				break;
			default :
				vars = new idWinStr();
				*vars = tok;
				vars->SetName(work);
				definedVars.Append(vars);
				break;
		}
	}

	return true;
}

/*
================
idWindow::SetInitialState
================
*/
void idWindow::SetInitialState(const char *_name) {
	name = _name;
	matScalex = 1.0;
	matScaley = 1.0;
	forceAspectWidth = 640.0;
	forceAspectHeight = 480.0;
	noTime = false;
	visible = true;
	flags = 0;
}

/*
================
idWindow::Parse
================
*/
bool idWindow::Parse( idParser *src, bool rebuild) {
	idToken token, token2, token3, token4, token5, token6, token7;
	idStr work;

	if (rebuild) {
		CleanUp();
	}

	drawWin_t dwt;

	timeLineEvents.Clear();
	transitions.Clear();

	namedEvents.DeleteContents( true );

	src->ExpectTokenType( TT_NAME, 0, &token );

	SetInitialState(token);

	src->ExpectTokenString( "{" );
	src->ExpectAnyToken( &token );

	bool ret = true;

	// attach a window wrapper to the window if the gui editor is running
#ifdef ID_ALLOW_TOOLS
	if ( com_editors & EDITOR_GUI ) {
		new rvGEWindowWrapper ( this, rvGEWindowWrapper::WT_NORMAL );
	}
#endif

	while( token != "}" ) {
		// track what was parsed so we can maintain it for the guieditor
		src->SetMarker ( );

		if ( token == "windowDef" || token == "animationDef" ) {
			if (token == "animationDef") {
				visible = false;
				rect = idRectangle(0,0,0,0);
			}
			src->ExpectTokenType( TT_NAME, 0, &token );
			token2 = token;
			src->UnreadToken(&token);
			drawWin_t *dw = FindChildByName(token2.c_str());
			if (dw && dw->win) {
				SaveExpressionParseState();
				dw->win->Parse(src, rebuild);
				RestoreExpressionParseState();
			} else {
				idWindow *win = new idWindow(dc, gui);
				SaveExpressionParseState();
				win->Parse(src, rebuild);
				RestoreExpressionParseState();
				win->SetParent(this);
				dwt.simp = NULL;
				dwt.win = NULL;
				if (win->IsSimple()) {
					idSimpleWindow *simple = new idSimpleWindow(win);
					dwt.simp = simple;
					drawWindows.Append(dwt);
					delete win;
				} else {
					AddChild(win);
					SetFocus(win,false);
					dwt.win = win;
					drawWindows.Append(dwt);
				}
			}
		}
		else if ( token == "editDef" ) {
			idEditWindow *win = new idEditWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "choiceDef" ) {
			idChoiceWindow *win = new idChoiceWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "sliderDef" ) {
			idSliderWindow *win = new idSliderWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "markerDef" ) {
			idMarkerWindow *win = new idMarkerWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "bindDef" ) {
			idBindWindow *win = new idBindWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "listDef" ) {
			idListWindow *win = new idListWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "fieldDef" ) {
			idFieldWindow *win = new idFieldWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "renderDef" ) {
			idRenderWindow *win = new idRenderWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "gameSSDDef" ) {
			idGameSSDWindow *win = new idGameSSDWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "gameBearShootDef" ) {
			idGameBearShootWindow *win = new idGameBearShootWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
		else if ( token == "gameBustOutDef" ) {
			idGameBustOutWindow *win = new idGameBustOutWindow(dc, gui);
			SaveExpressionParseState();
			win->Parse(src, rebuild);
			RestoreExpressionParseState();
			AddChild(win);
			win->SetParent(this);
			dwt.simp = NULL;
			dwt.win = win;
			drawWindows.Append(dwt);
		}
//
//  added new onEvent
		else if ( token == "onNamedEvent" ) {
			// Read the event name
			if ( !src->ReadToken(&token) ) {
				src->Error( "Expected event name" );
				return false;
			}

			rvNamedEvent* ev = new rvNamedEvent ( token );

			src->SetMarker ( );

			if ( !ParseScript ( src, *ev->mEvent ) ) {
				ret = false;
				break;
			}

			// If we are in the gui editor then add the internal var to the
			// the wrapper
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;
				idStr out;

				// Grab the string from the last marker
				src->GetStringFromMarker ( str, false );

				// Parse it one more time to knock unwanted tabs out
				idLexer src2( str, str.Length(), "", src->GetFlags() );
				src2.ParseBracedSectionExact ( out, 1);

				// Save the script
				rvGEWindowWrapper::GetWrapper ( this )->GetScriptDict().Set ( va("onEvent %s", token.c_str()), out );
			}
#endif
			namedEvents.Append(ev);
		}
		else if ( token == "onTime" ) {
			idTimeLineEvent *ev = new idTimeLineEvent;

			if ( !src->ReadToken(&token) ) {
				src->Error( "Unexpected end of file" );
				return false;
			}
			ev->time = atoi(token.c_str());

			// reset the mark since we dont want it to include the time
			src->SetMarker ( );

			if (!ParseScript(src, *ev->event, &ev->time)) {
				ret = false;
				break;
			}

			// add the script to the wrappers script list
			// If we are in the gui editor then add the internal var to the
			// the wrapper
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;
				idStr out;

				// Grab the string from the last marker
				src->GetStringFromMarker ( str, false );

				// Parse it one more time to knock unwanted tabs out
				idLexer src2( str, str.Length(), "", src->GetFlags() );
				src2.ParseBracedSectionExact ( out, 1);

				// Save the script
				rvGEWindowWrapper::GetWrapper ( this )->GetScriptDict().Set ( va("onTime %d", ev->time), out );
			}
#endif
			// this is a timeline event
			ev->pending = true;
			timeLineEvents.Append(ev);
		}
		else if ( token == "definefloat" ) {
			src->ReadToken(&token);
			work = token;
			work.ToLower();
			idWinFloat *varf = new idWinFloat();
			varf->SetName(work);
			definedVars.Append(varf);

			// add the float to the editors wrapper dict
			// Set the marker after the float name
			src->SetMarker ( );

			// Read in the float
			regList.AddReg(work, idRegister::FLOAT, src, this, varf);

			// If we are in the gui editor then add the float to the defines
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;

				// Grab the string from the last marker and save it in the wrapper
				src->GetStringFromMarker ( str, true );
				rvGEWindowWrapper::GetWrapper ( this )->GetVariableDict().Set ( va("definefloat\t\"%s\"",token.c_str()), str );
			}
#endif
		}
		else if ( token == "definevec4" ) {
			src->ReadToken(&token);
			work = token;
			work.ToLower();
			idWinVec4 *var = new idWinVec4();
			var->SetName(work);

			// set the marker so we can determine what was parsed
			// set the marker after the vec4 name
			src->SetMarker ( );

			// FIXME: how about we add the var to the desktop instead of this window so it won't get deleted
			//        when this window is destoyed which even happens during parsing with simple windows ?
			//definedVars.Append(var);
			gui->GetDesktop()->definedVars.Append( var );
			gui->GetDesktop()->regList.AddReg( work, idRegister::VEC4, src, gui->GetDesktop(), var );

			// store the original vec4 for the editor
			// If we are in the gui editor then add the float to the defines
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;

				// Grab the string from the last marker and save it in the wrapper
				src->GetStringFromMarker ( str, true );
				rvGEWindowWrapper::GetWrapper ( this )->GetVariableDict().Set ( va("definevec4\t\"%s\"",token.c_str()), str );
			}
#endif
		}
		else if ( token == "float" ) {
			src->ReadToken(&token);
			work = token;
			work.ToLower();
			idWinFloat *varf = new idWinFloat();
			varf->SetName(work);
			definedVars.Append(varf);

			// add the float to the editors wrapper dict
			// set the marker to after the float name
			src->SetMarker ( );

			// Parse the float
			regList.AddReg(work, idRegister::FLOAT, src, this, varf);

			// If we are in the gui editor then add the float to the defines
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;

				// Grab the string from the last marker and save it in the wrapper
				src->GetStringFromMarker ( str, true );
				rvGEWindowWrapper::GetWrapper ( this )->GetVariableDict().Set ( va("float\t\"%s\"",token.c_str()), str );
			}
#endif
		}
		else if (ParseScriptEntry(token, src)) {
			// add the script to the wrappers script list
			// If we are in the gui editor then add the internal var to the
			// the wrapper
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;
				idStr out;

				// Grab the string from the last marker
				src->GetStringFromMarker ( str, false );

				// Parse it one more time to knock unwanted tabs out
				idLexer src2( str, str.Length(), "", src->GetFlags() );
				src2.ParseBracedSectionExact ( out, 1);

				// Save the script
				rvGEWindowWrapper::GetWrapper ( this )->GetScriptDict().Set ( token, out );
			}
#endif
		} else if (ParseInternalVar(token, src)) {
			// gui editor support
			// If we are in the gui editor then add the internal var to the
			// the wrapper
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;
				src->GetStringFromMarker ( str );
				rvGEWindowWrapper::GetWrapper ( this )->SetStateKey ( token, str, false );
			}
#endif
		}
		else {
			ParseRegEntry(token, src);
			// hook into the main window parsing for the gui editor
			// If we are in the gui editor then add the internal var to the
			// the wrapper
#ifdef ID_ALLOW_TOOLS
			if ( com_editors & EDITOR_GUI ) {
				idStr str;
				src->GetStringFromMarker ( str );
				rvGEWindowWrapper::GetWrapper ( this )->SetStateKey ( token, str, false );
			}
#endif
		}
		if ( !src->ReadToken( &token ) ) {
			src->Error( "Unexpected end of file" );
			ret = false;
			break;
		}
	}

	if (ret) {
		EvalRegs(-1, true);
	}

	SetupFromState();
	PostParse();

	// hook into the main window parsing for the gui editor
	// If we are in the gui editor then add the internal var to the
	// the wrapper
#ifdef ID_ALLOW_TOOLS
	if ( com_editors & EDITOR_GUI ) {
		rvGEWindowWrapper::GetWrapper ( this )->Finish ( );
	}
#endif

	return ret;
}

/*
================
idWindow::FindSimpleWinByName
================
*/
idSimpleWindow *idWindow::FindSimpleWinByName(const char *_name) {
	int c = drawWindows.Num();
	for (int i = 0; i < c; i++) {
		if (drawWindows[i].simp == NULL) {
			continue;
		}
		if ( idStr::Icmp(drawWindows[i].simp->name, _name) == 0 ) {
			return drawWindows[i].simp;
		}
	}
	return NULL;
}

/*
================
idWindow::FindChildByName
================
*/
drawWin_t *idWindow::FindChildByName(const char *_name) {
	static drawWin_t dw;
	if (idStr::Icmp(name,_name) == 0) {
		dw.simp = NULL;
		dw.win = this;
		return &dw;
	}
	int c = drawWindows.Num();
	for (int i = 0; i < c; i++) {
		if (drawWindows[i].win) {
			if (idStr::Icmp(drawWindows[i].win->name, _name) == 0) {
				return &drawWindows[i];
			}
			drawWin_t *win = drawWindows[i].win->FindChildByName(_name);
			if (win) {
				return win;
			}
		} else {
			if (idStr::Icmp(drawWindows[i].simp->name, _name) == 0) {
				return &drawWindows[i];
			}
		}
	}
	return NULL;
}

/*
================
idWindow::GetStrPtrByName
================
*/
idStr* idWindow::GetStrPtrByName(const char *_name) {
	return NULL;
}

/*
================
idWindow::AddTransition
================
*/
void idWindow::AddTransition(idWinVar *dest, idVec4 from, idVec4 to, int time, float accelTime, float decelTime) {
	idTransitionData data;
	data.data = dest;
	data.interp.Init(gui->GetTime(), accelTime * time, decelTime * time, time, from, to);
	transitions.Append(data);
}


/*
================
idWindow::StartTransition
================
*/
void idWindow::StartTransition() {
	flags |= WIN_INTRANSITION;
}

/*
================
idWindow::ResetCinematics
================
*/
void idWindow::ResetCinematics() {
	if ( background ) {
		background->ResetCinematicTime( gui->GetTime() );
	}
}

/*
================
idWindow::ResetTime
================
*/
void idWindow::ResetTime(int t) {

	timeLine = gui->GetTime() - t;

	int i, c = timeLineEvents.Num();
	for ( i = 0; i < c; i++ ) {
		if ( timeLineEvents[i]->time >= t ) {
			timeLineEvents[i]->pending = true;
		}
	}

	noTime = false;

	c = transitions.Num();
	for ( i = 0; i < c; i++ ) {
		idTransitionData *data = &transitions[i];
		if ( data->interp.IsDone( gui->GetTime() ) && data->data ) {
			transitions.RemoveIndex( i );
			i--;
			c--;
		}
	}

}


/*
================
idWindow::RunScriptList
================
*/
bool idWindow::RunScriptList(idGuiScriptList *src) {
	if (src == NULL) {
		return false;
	}
	src->Execute(this);
	return true;
}

/*
================
idWindow::RunScript
================
*/
bool idWindow::RunScript(int n) {
	if (n >= ON_MOUSEENTER && n < SCRIPT_COUNT) {
		return RunScriptList(scripts[n]);
	}
	return false;
}

/*
================
idWindow::ExpressionConstant
================
*/
int idWindow::ExpressionConstant(float f) {
	int		i;

	for ( i = WEXP_REG_NUM_PREDEFINED ; i < expressionRegisters.Num() ; i++ ) {
		if ( !registerIsTemporary[i] && expressionRegisters[i] == f ) {
			return i;
		}
	}
	if ( expressionRegisters.Num() == MAX_EXPRESSION_REGISTERS ) {
		common->Warning( "expressionConstant: gui %s hit MAX_EXPRESSION_REGISTERS", gui->GetSourceFile() );
		return 0;
	}

	int c = expressionRegisters.Num();
	if (i > c) {
		while (i > c) {
			expressionRegisters.Append(-9999999);
			i--;
		}
	}

	i = expressionRegisters.Append(f);
	registerIsTemporary[i] = false;
	return i;
}

/*
================
idWindow::ExpressionTemporary
================
*/
int idWindow::ExpressionTemporary() {
	if ( expressionRegisters.Num() == MAX_EXPRESSION_REGISTERS ) {
		common->Warning( "expressionTemporary: gui %s hit MAX_EXPRESSION_REGISTERS", gui->GetSourceFile());
		return 0;
	}
	int i = expressionRegisters.Num();
	registerIsTemporary[i] = true;
	i = expressionRegisters.Append(0);
	return i;
}

/*
================
idWindow::ExpressionOp
================
*/
wexpOp_t *idWindow::ExpressionOp() {
	if ( ops.Num() == MAX_EXPRESSION_OPS ) {
		common->Warning( "expressionOp: gui %s hit MAX_EXPRESSION_OPS", gui->GetSourceFile());
		return &ops[0];
	}
	wexpOp_t wop;
	memset(&wop, 0, sizeof(wexpOp_t));
	int i = ops.Append(wop);
	return &ops[i];
}

/*
================
idWindow::EmitOp
================
*/

intptr_t idWindow::EmitOp( intptr_t a, intptr_t b, wexpOpType_t opType, wexpOp_t **opp ) {
	wexpOp_t *op;
/*
	// optimize away identity operations
	if ( opType == WOP_TYPE_ADD ) {
		if ( !registerIsTemporary[a] && shaderRegisters[a] == 0 ) {
			return b;
		}
		if ( !registerIsTemporary[b] && shaderRegisters[b] == 0 ) {
			return a;
		}
		if ( !registerIsTemporary[a] && !registerIsTemporary[b] ) {
			return ExpressionConstant( shaderRegisters[a] + shaderRegisters[b] );
		}
	}
	if ( opType == WOP_TYPE_MULTIPLY ) {
		if ( !registerIsTemporary[a] && shaderRegisters[a] == 1 ) {
			return b;
		}
		if ( !registerIsTemporary[a] && shaderRegisters[a] == 0 ) {
			return a;
		}
		if ( !registerIsTemporary[b] && shaderRegisters[b] == 1 ) {
			return a;
		}
		if ( !registerIsTemporary[b] && shaderRegisters[b] == 0 ) {
			return b;
		}
		if ( !registerIsTemporary[a] && !registerIsTemporary[b] ) {
			return ExpressionConstant( shaderRegisters[a] * shaderRegisters[b] );
		}
	}
*/
	op = ExpressionOp();

	op->opType = opType;
	op->a = a;
	op->b = b;
	op->c = ExpressionTemporary();

	if (opp) {
		*opp = op;
	}
	return op->c;
}

/*
================
idWindow::ParseEmitOp
================
*/
intptr_t idWindow::ParseEmitOp( idParser *src, intptr_t a, wexpOpType_t opType, int priority, wexpOp_t **opp ) {
	intptr_t b = ParseExpressionPriority( src, priority );
	return EmitOp( a, b, opType, opp );
}


/*
================
idWindow::ParseTerm

Returns a register index
=================
*/
intptr_t idWindow::ParseTerm( idParser *src,	idWinVar *var, intptr_t component ) {
	idToken token;
	intptr_t a, b;

	src->ReadToken( &token );

	if ( token == "(" ) {
		a = ParseExpression( src );
		src->ExpectTokenString(")");
		return a;
	}

	if ( !token.Icmp( "time" ) ) {
		return WEXP_REG_TIME;
	}

	// parse negative numbers
	if ( token == "-" ) {
		src->ReadToken( &token );
		if ( token.type == TT_NUMBER || token == "." ) {
			return ExpressionConstant( -(float) token.GetFloatValue() );
		}
		src->Warning( "Bad negative number '%s'", token.c_str() );
		return 0;
	}

	if ( token.type == TT_NUMBER || token == "." || token == "-" ) {
		return ExpressionConstant( (float) token.GetFloatValue() );
	}

	// see if it is a table name
	const idDeclTable *table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, token.c_str(), false ) );
	if ( table ) {
		a = table->Index();
		// parse a table expression
		src->ExpectTokenString("[");
		b = ParseExpression(src);
		src->ExpectTokenString("]");
		return EmitOp( a, b, WOP_TYPE_TABLE );
	}

	if (var == NULL) {
		var = GetWinVarByName(token, true);
	}
	if (var) {
		a = (intptr_t)var;
		//assert(dynamic_cast<idWinVec4*>(var));
		var->Init(token, this);
		b = component;
		if (dynamic_cast<idWinVec4*>(var)) {
			if (src->ReadToken(&token)) {
				if (token == "[") {
					b = ParseExpression(src);
					src->ExpectTokenString("]");
				} else {
					src->UnreadToken(&token);
				}
			}
			return EmitOp(a, b, WOP_TYPE_VAR);
		} else if (dynamic_cast<idWinFloat*>(var)) {
			return EmitOp(a, b, WOP_TYPE_VARF);
		} else if (dynamic_cast<idWinInt*>(var)) {
			return EmitOp(a, b, WOP_TYPE_VARI);
		} else if (dynamic_cast<idWinBool*>(var)) {
			return EmitOp(a, b, WOP_TYPE_VARB);
		} else if (dynamic_cast<idWinStr*>(var)) {
			return EmitOp(a, b, WOP_TYPE_VARS);
		} else {
			src->Warning("Var expression not vec4, float or int '%s'", token.c_str());
		}
		return 0;
	} else {
		// ugly but used for post parsing to fixup named vars
		char *p = new char[token.Length()+1];
		strcpy(p, token);
		a = (intptr_t)p;
		b = -2;
		return EmitOp(a, b, WOP_TYPE_VAR);
	}

}

/*
=================
idWindow::ParseExpressionPriority

Returns a register index
=================
*/
#define	TOP_PRIORITY 4
intptr_t idWindow::ParseExpressionPriority( idParser *src, int priority, idWinVar *var, intptr_t component ) {
	idToken token;
	intptr_t a;

	if ( priority == 0 ) {
		return ParseTerm( src, var, component );
	}

	a = ParseExpressionPriority( src, priority - 1, var, component );

	if ( !src->ReadToken( &token ) ) {
		// we won't get EOF in a real file, but we can
		// when parsing from generated strings
		return a;
	}

	if ( priority == 1 && token == "*" ) {
		return ParseEmitOp( src, a, WOP_TYPE_MULTIPLY, priority );
	}
	if ( priority == 1 && token == "/" ) {
		return ParseEmitOp( src, a, WOP_TYPE_DIVIDE, priority );
	}
	if ( priority == 1 && token == "%" ) {	// implied truncate both to integer
		return ParseEmitOp( src, a, WOP_TYPE_MOD, priority );
	}
	if ( priority == 2 && token == "+" ) {
		return ParseEmitOp( src, a, WOP_TYPE_ADD, priority );
	}
	if ( priority == 2 && token == "-" ) {
		return ParseEmitOp( src, a, WOP_TYPE_SUBTRACT, priority );
	}
	if ( priority == 3 && token == ">" ) {
		return ParseEmitOp( src, a, WOP_TYPE_GT, priority );
	}
	if ( priority == 3 && token == ">=" ) {
		return ParseEmitOp( src, a, WOP_TYPE_GE, priority );
	}
	if ( priority == 3 && token == "<" ) {
		return ParseEmitOp( src, a, WOP_TYPE_LT, priority );
	}
	if ( priority == 3 && token == "<=" ) {
		return ParseEmitOp( src, a, WOP_TYPE_LE, priority );
	}
	if ( priority == 3 && token == "==" ) {
		return ParseEmitOp( src, a, WOP_TYPE_EQ, priority );
	}
	if ( priority == 3 && token == "!=" ) {
		return ParseEmitOp( src, a, WOP_TYPE_NE, priority );
	}
	if ( priority == 4 && token == "&&" ) {
		return ParseEmitOp( src, a, WOP_TYPE_AND, priority );
	}
	if ( priority == 4 && token == "||" ) {
		return ParseEmitOp( src, a, WOP_TYPE_OR, priority );
	}
	if ( priority == 4 && token == "?" ) {
		wexpOp_t *oop = NULL;
		intptr_t o = ParseEmitOp( src, a, WOP_TYPE_COND, priority, &oop );
		if ( !src->ReadToken( &token ) ) {
			return o;
		}
		if (token == ":") {
			a = ParseExpressionPriority( src, priority - 1, var );
			oop->d = a;
		}
		return o;
	}

	// assume that anything else terminates the expression
	// not too robust error checking...

	src->UnreadToken( &token );

	return a;
}

/*
================
idWindow::ParseExpression

Returns a register index
================
*/
intptr_t idWindow::ParseExpression(idParser *src, idWinVar *var, intptr_t component) {
	return ParseExpressionPriority( src, TOP_PRIORITY, var );
}

/*
================
idWindow::ParseBracedExpression
================
*/
void idWindow::ParseBracedExpression(idParser *src) {
	src->ExpectTokenString("{");
	ParseExpression(src);
	src->ExpectTokenString("}");
}

/*
===============
idWindow::EvaluateRegisters

Parameters are taken from the localSpace and the renderView,
then all expressions are evaluated, leaving the shader registers
set to their apropriate values.
===============
*/
void idWindow::EvaluateRegisters(float *registers) {
	int		i, b;
	wexpOp_t	*op;
	idVec4 v;

	int erc = expressionRegisters.Num();
	int oc = ops.Num();
	// copy the constants
	for ( i = WEXP_REG_NUM_PREDEFINED ; i < erc ; i++ ) {
		registers[i] = expressionRegisters[i];
	}

	// copy the local and global parameters
	registers[WEXP_REG_TIME] = gui->GetTime();

	for ( i = 0 ; i < oc ; i++ ) {
		op = &ops[i];
		if (op->b == -2) {
			continue;
		}
		switch( op->opType ) {
		case WOP_TYPE_ADD:
			registers[op->c] = registers[op->a] + registers[op->b];
			break;
		case WOP_TYPE_SUBTRACT:
			registers[op->c] = registers[op->a] - registers[op->b];
			break;
		case WOP_TYPE_MULTIPLY:
			registers[op->c] = registers[op->a] * registers[op->b];
			break;
		case WOP_TYPE_DIVIDE:
			if ( registers[op->b] == 0.0f ) {
				common->Warning( "Divide by zero in window '%s' in %s", GetName(), gui->GetSourceFile() );
				registers[op->c] = registers[op->a];
			} else {
				registers[op->c] = registers[op->a] / registers[op->b];
			}
			break;
		case WOP_TYPE_MOD:
			b = (int)registers[op->b];
			b = b != 0 ? b : 1;
			registers[op->c] = (int)registers[op->a] % b;
			break;
		case WOP_TYPE_TABLE:
			{
				const idDeclTable *table = static_cast<const idDeclTable *>( declManager->DeclByIndex( DECL_TABLE, op->a ) );
				registers[op->c] = table->TableLookup( registers[op->b] );
			}
			break;
		case WOP_TYPE_GT:
			registers[op->c] = registers[ op->a ] > registers[op->b];
			break;
		case WOP_TYPE_GE:
			registers[op->c] = registers[ op->a ] >= registers[op->b];
			break;
		case WOP_TYPE_LT:
			registers[op->c] = registers[ op->a ] < registers[op->b];
			break;
		case WOP_TYPE_LE:
			registers[op->c] = registers[ op->a ] <= registers[op->b];
			break;
		case WOP_TYPE_EQ:
			registers[op->c] = registers[ op->a ] == registers[op->b];
			break;
		case WOP_TYPE_NE:
			registers[op->c] = registers[ op->a ] != registers[op->b];
			break;
		case WOP_TYPE_COND:
			registers[op->c] = (registers[ op->a ]) ? registers[op->b] : registers[op->d];
			break;
		case WOP_TYPE_AND:
			registers[op->c] = registers[ op->a ] && registers[op->b];
			break;
		case WOP_TYPE_OR:
			registers[op->c] = registers[ op->a ] || registers[op->b];
			break;
		case WOP_TYPE_VAR:
			if ( !op->a ) {
				registers[op->c] = 0.0f;
				break;
			}
			if ( op->b >= 0 && registers[op->b] >= 0 && registers[op->b] < 4 ) {
				// grabs vector components
				idWinVec4 *var = (idWinVec4 *)( op->a );
				registers[op->c] = ((idVec4&)var)[registers[op->b]];
			} else {
				registers[op->c] = ((idWinVar*)(op->a))->x();
			}
			break;
		case WOP_TYPE_VARS:
			if (op->a) {
				idWinStr *var = (idWinStr*)(op->a);
				registers[op->c] = atof(var->c_str());
			} else {
				registers[op->c] = 0;
			}
			break;
		case WOP_TYPE_VARF:
			if (op->a) {
				idWinFloat *var = (idWinFloat*)(op->a);
				registers[op->c] = *var;
			} else {
				registers[op->c] = 0;
			}
			break;
		case WOP_TYPE_VARI:
			if (op->a) {
				idWinInt *var = (idWinInt*)(op->a);
				registers[op->c] = *var;
			} else {
				registers[op->c] = 0;
			}
			break;
		case WOP_TYPE_VARB:
			if (op->a) {
				idWinBool *var = (idWinBool*)(op->a);
				registers[op->c] = *var;
			} else {
				registers[op->c] = 0;
			}
			break;
		default:
			common->FatalError( "R_EvaluateExpression: bad opcode" );
		}
	}

}

/*
================
idWindow::ReadFromDemoFile
================
*/
void idWindow::ReadFromDemoFile( class idDemoFile *f, bool rebuild ) {

	// should never hit unless we re-enable WRITE_GUIS
#ifndef WRITE_GUIS
	assert( false );
#else

	if (rebuild) {
		CommonInit();
	}

	f->SetLog(true, "window1");
	backGroundName = f->ReadHashString();
	f->SetLog(true, backGroundName);
	if ( backGroundName[0] ) {
		background = declManager->FindMaterial(backGroundName);
	} else {
		background = NULL;
	}
	f->ReadUnsignedChar( cursor );
	f->ReadUnsignedInt( flags );
	f->ReadInt( timeLine );
	f->ReadInt( lastTimeRun );
	idRectangle rct = rect;
	f->ReadFloat( rct.x );
	f->ReadFloat( rct.y );
	f->ReadFloat( rct.w );
	f->ReadFloat( rct.h );
	f->ReadFloat( drawRect.x );
	f->ReadFloat( drawRect.y );
	f->ReadFloat( drawRect.w );
	f->ReadFloat( drawRect.h );
	f->ReadFloat( clientRect.x );
	f->ReadFloat( clientRect.y );
	f->ReadFloat( clientRect.w );
	f->ReadFloat( clientRect.h );
	f->ReadFloat( textRect.x );
	f->ReadFloat( textRect.y );
	f->ReadFloat( textRect.w );
	f->ReadFloat( textRect.h );
	f->ReadFloat( xOffset);
	f->ReadFloat( yOffset);
	int i, c;

	idStr work;
	if (rebuild) {
		f->SetLog(true, (work + "-scripts"));
		for (i = 0; i < SCRIPT_COUNT; i++) {
			bool b;
			f->ReadBool( b );
			if (b) {
				delete scripts[i];
				scripts[i] = new idGuiScriptList;
				scripts[i]->ReadFromDemoFile(f);
			}
		}

		f->SetLog(true, (work + "-timelines"));
		f->ReadInt( c );
		for (i = 0; i < c; i++) {
			idTimeLineEvent *tl = new idTimeLineEvent;
			f->ReadInt( tl->time );
			f->ReadBool( tl->pending );
			tl->event->ReadFromDemoFile(f);
			if (rebuild) {
				timeLineEvents.Append(tl);
			} else {
				assert(i < timeLineEvents.Num());
				timeLineEvents[i]->time = tl->time;
				timeLineEvents[i]->pending = tl->pending;
			}
		}
	}

	f->SetLog(true, (work + "-transitions"));
	f->ReadInt( c );
	for (i = 0; i < c; i++) {
		idTransitionData td;
		td.data = NULL;
		f->ReadInt ( td.offset );

		float startTime, accelTime, linearTime, decelTime;
		idVec4 startValue, endValue;
		f->ReadFloat( startTime );
		f->ReadFloat( accelTime );
		f->ReadFloat( linearTime );
		f->ReadFloat( decelTime );
		f->ReadVec4( startValue );
		f->ReadVec4( endValue );
		td.interp.Init( startTime, accelTime, decelTime, accelTime + linearTime + decelTime, startValue, endValue );

		// read this for correct data padding with the win32 savegames
		// the extrapolate is correctly initialized through the above Init call
		int extrapolationType;
		float duration;
		idVec4 baseSpeed, speed;
		float currentTime;
		idVec4 currentValue;
		f->ReadInt( extrapolationType );
		f->ReadFloat( startTime );
		f->ReadFloat( duration );
		f->ReadVec4( startValue );
		f->ReadVec4( baseSpeed );
		f->ReadVec4( speed );
		f->ReadFloat( currentTime );
		f->ReadVec4( currentValue );

		transitions.Append(td);
	}

	f->SetLog(true, (work + "-regstuff"));
	if (rebuild) {
		f->ReadInt( c );
		for (i = 0; i < c; i++) {
			wexpOp_t w;
			f->ReadInt( (int&)w.opType );
			f->ReadInt( w.a );
			f->ReadInt( w.b );
			f->ReadInt( w.c );
			f->ReadInt( w.d );
			ops.Append(w);
		}

		f->ReadInt( c );
		for (i = 0; i < c; i++) {
			float ff;
			f->ReadFloat( ff );
			expressionRegisters.Append(ff);
		}

		regList.ReadFromDemoFile(f);

	}
	f->SetLog(true, (work + "-children"));
	f->ReadInt( c );
	for (i = 0; i < c; i++) {
		if (rebuild) {
			idWindow *win = new idWindow(dc, gui);
			win->ReadFromDemoFile(f);
			AddChild(win);
		} else {
			for (int j = 0; j < c; j++) {
				if (children[j]->childID == i) {
					children[j]->ReadFromDemoFile(f,rebuild);
					break;
				} else {
					continue;
				}
			}
		}
	}
#endif /* WRITE_GUIS */
}

/*
================
idWindow::WriteToDemoFile
================
*/
void idWindow::WriteToDemoFile( class idDemoFile *f ) {
	// should never hit unless we re-enable WRITE_GUIS
#ifndef WRITE_GUIS
	assert( false );
#else

	f->SetLog(true, "window");
	f->WriteHashString(backGroundName);
	f->SetLog(true, backGroundName);
	f->WriteUnsignedChar( cursor );
	f->WriteUnsignedInt( flags );
	f->WriteInt( timeLine );
	f->WriteInt( lastTimeRun );
	idRectangle rct = rect;
	f->WriteFloat( rct.x );
	f->WriteFloat( rct.y );
	f->WriteFloat( rct.w );
	f->WriteFloat( rct.h );
	f->WriteFloat( drawRect.x );
	f->WriteFloat( drawRect.y );
	f->WriteFloat( drawRect.w );
	f->WriteFloat( drawRect.h );
	f->WriteFloat( clientRect.x );
	f->WriteFloat( clientRect.y );
	f->WriteFloat( clientRect.w );
	f->WriteFloat( clientRect.h );
	f->WriteFloat( textRect.x );
	f->WriteFloat( textRect.y );
	f->WriteFloat( textRect.w );
	f->WriteFloat( textRect.h );
	f->WriteFloat( xOffset );
	f->WriteFloat( yOffset );
	idStr work;
	f->SetLog(true, work);

	int i, c;

	f->SetLog(true, (work + "-transitions"));
	c = transitions.Num();
	f->WriteInt( c );
	for (i = 0; i < c; i++) {
		f->WriteInt( 0 );
		f->WriteInt( transitions[i].offset );

		f->WriteFloat( transitions[i].interp.GetStartTime() );
		f->WriteFloat( transitions[i].interp.GetAccelTime() );
		f->WriteFloat( transitions[i].interp.GetLinearTime() );
		f->WriteFloat( transitions[i].interp.GetDecelTime() );
		f->WriteVec4( transitions[i].interp.GetStartValue() );
		f->WriteVec4( transitions[i].interp.GetEndValue() );

		// write to keep win32 render demo format compatiblity - we don't actually read them back anymore
		f->WriteInt( transitions[i].interp.GetExtrapolate()->GetExtrapolationType() );
		f->WriteFloat( transitions[i].interp.GetExtrapolate()->GetStartTime() );
		f->WriteFloat( transitions[i].interp.GetExtrapolate()->GetDuration() );
		f->WriteVec4( transitions[i].interp.GetExtrapolate()->GetStartValue() );
		f->WriteVec4( transitions[i].interp.GetExtrapolate()->GetBaseSpeed() );
		f->WriteVec4( transitions[i].interp.GetExtrapolate()->GetSpeed() );
		f->WriteFloat( transitions[i].interp.GetExtrapolate()->GetCurrentTime() );
		f->WriteVec4( transitions[i].interp.GetExtrapolate()->GetCurrentValue() );
	}

	f->SetLog(true, (work + "-regstuff"));

	f->SetLog(true, (work + "-children"));
	c = children.Num();
	f->WriteInt( c );
	for (i = 0; i < c; i++) {
		for (int j = 0; j < c; j++) {
			if (children[j]->childID == i) {
				children[j]->WriteToDemoFile(f);
				break;
			} else {
				continue;
			}
		}
	}
#endif /* WRITE_GUIS */
}

/*
===============
idWindow::WriteString
===============
*/
void idWindow::WriteSaveGameString( const char *string, idFile *savefile ) {
	int len = strlen( string );

	savefile->Write( &len, sizeof( len ) );
	savefile->Write( string, len );
}

/*
===============
idWindow::WriteSaveGameTransition
===============
*/
void idWindow::WriteSaveGameTransition( idTransitionData &trans, idFile *savefile ) {
	drawWin_t dw, *fdw;
	idStr winName("");
	dw.simp = NULL;
	dw.win = NULL;
	int offset = gui->GetDesktop()->GetWinVarOffset( trans.data, &dw );
	if ( dw.win || dw.simp ) {
		winName = ( dw.win ) ? dw.win->GetName() : dw.simp->name.c_str();
	}
	fdw = gui->GetDesktop()->FindChildByName( winName );
	if ( offset != -1 && fdw && ( fdw->win || fdw->simp ) ) {
		savefile->Write( &offset, sizeof( offset ) );
		WriteSaveGameString( winName, savefile );
		savefile->Write( &trans.interp, sizeof( trans.interp ) );
	} else {
		offset = -1;
		savefile->Write( &offset, sizeof( offset ) );
	}
}

/*
===============
idWindow::ReadSaveGameTransition
===============
*/
void idWindow::ReadSaveGameTransition( idTransitionData &trans, idFile *savefile ) {
	int offset;

	savefile->Read( &offset, sizeof( offset ) );
	if ( offset != -1 ) {
		idStr winName;
		ReadSaveGameString( winName, savefile );
		savefile->Read( &trans.interp, sizeof( trans.interp ) );
		trans.data = NULL;
		trans.offset = offset;
		if ( winName.Length() ) {
			idWinStr *strVar = new idWinStr();
			strVar->Set( winName );
			trans.data = dynamic_cast< idWinVar* >( strVar );
		}
	}
}

/*
===============
idWindow::WriteToSaveGame
===============
*/
void idWindow::WriteToSaveGame( idFile *savefile ) {
	int i;

	WriteSaveGameString( cmd, savefile );

	savefile->Write( &actualX, sizeof( actualX ) );
	savefile->Write( &actualY, sizeof( actualY ) );
	savefile->Write( &childID, sizeof( childID ) );
	savefile->Write( &flags, sizeof( flags ) );
	savefile->Write( &lastTimeRun, sizeof( lastTimeRun ) );
	savefile->Write( &drawRect, sizeof( drawRect ) );
	savefile->Write( &clientRect, sizeof( clientRect ) );
	savefile->Write( &origin, sizeof( origin ) );
	savefile->Write( &fontNum, sizeof( fontNum ) );
	savefile->Write( &timeLine, sizeof( timeLine ) );
	savefile->Write( &xOffset, sizeof( xOffset ) );
	savefile->Write( &yOffset, sizeof( yOffset ) );
	savefile->Write( &cursor, sizeof( cursor ) );
	savefile->Write( &forceAspectWidth, sizeof( forceAspectWidth ) );
	savefile->Write( &forceAspectHeight, sizeof( forceAspectHeight ) );
	savefile->Write( &matScalex, sizeof( matScalex ) );
	savefile->Write( &matScaley, sizeof( matScaley ) );
	savefile->Write( &borderSize, sizeof( borderSize ) );
	savefile->Write( &textAlign, sizeof( textAlign ) );
	savefile->Write( &textAlignx, sizeof( textAlignx ) );
	savefile->Write( &textAligny, sizeof( textAligny ) );
	savefile->Write( &textShadow, sizeof( textShadow ) );
	savefile->Write( &shear, sizeof( shear ) );

	WriteSaveGameString( name, savefile );
	WriteSaveGameString( comment, savefile );

	// WinVars
	noTime.WriteToSaveGame( savefile );
	visible.WriteToSaveGame( savefile );
	rect.WriteToSaveGame( savefile );
	backColor.WriteToSaveGame( savefile );
	matColor.WriteToSaveGame( savefile );
	foreColor.WriteToSaveGame( savefile );
	hoverColor.WriteToSaveGame( savefile );
	borderColor.WriteToSaveGame( savefile );
	textScale.WriteToSaveGame( savefile );
	noEvents.WriteToSaveGame( savefile );
	rotate.WriteToSaveGame( savefile );
	text.WriteToSaveGame( savefile );
	backGroundName.WriteToSaveGame( savefile );
	hideCursor.WriteToSaveGame(savefile);

	// Defined Vars
	for ( i = 0; i < definedVars.Num(); i++ ) {
		definedVars[i]->WriteToSaveGame( savefile );
	}

	savefile->Write( &textRect, sizeof( textRect ) );

	// Window pointers saved as the child ID of the window
	int winID;

	winID = focusedChild ? focusedChild->childID : -1 ;
	savefile->Write( &winID, sizeof( winID ) );

	winID = captureChild ? captureChild->childID : -1 ;
	savefile->Write( &winID, sizeof( winID ) );

	winID = overChild ? overChild->childID : -1 ;
	savefile->Write( &winID, sizeof( winID ) );


	// Scripts
	for ( i = 0; i < SCRIPT_COUNT; i++ ) {
		if ( scripts[i] ) {
			scripts[i]->WriteToSaveGame( savefile );
		}
	}

	// TimeLine Events
	for ( i = 0; i < timeLineEvents.Num(); i++ ) {
		if ( timeLineEvents[i] ) {
			savefile->Write( &timeLineEvents[i]->pending, sizeof( timeLineEvents[i]->pending ) );
			savefile->Write( &timeLineEvents[i]->time, sizeof( timeLineEvents[i]->time ) );
			if ( timeLineEvents[i]->event ) {
				timeLineEvents[i]->event->WriteToSaveGame( savefile );
			}
		}
	}

	// Transitions
	int num = transitions.Num();

	savefile->Write( &num, sizeof( num ) );
	for ( i = 0; i < transitions.Num(); i++ ) {
		WriteSaveGameTransition( transitions[ i ], savefile );
	}


	// Named Events
	for ( i = 0; i < namedEvents.Num(); i++ ) {
		if ( namedEvents[i] ) {
			WriteSaveGameString( namedEvents[i]->mName, savefile );
			if ( namedEvents[i]->mEvent ) {
				namedEvents[i]->mEvent->WriteToSaveGame( savefile );
			}
		}
	}

	// regList
	regList.WriteToSaveGame( savefile );


	// Save children
	for ( i = 0; i < drawWindows.Num(); i++ ) {
		drawWin_t	window = drawWindows[i];

		if ( window.simp ) {
			window.simp->WriteToSaveGame( savefile );
		} else if ( window.win ) {
			window.win->WriteToSaveGame( savefile );
		}
	}
}

/*
===============
idWindow::ReadSaveGameString
===============
*/
void idWindow::ReadSaveGameString( idStr &string, idFile *savefile ) {
	int len;

	savefile->Read( &len, sizeof( len ) );
	if ( len < 0 ) {
		common->Warning( "idWindow::ReadSaveGameString: invalid length" );
	}

	string.Fill( ' ', len );
	savefile->Read( &string[0], len );
}

/*
===============
idWindow::ReadFromSaveGame
===============
*/
void idWindow::ReadFromSaveGame( idFile *savefile ) {
	int i;

	transitions.Clear();

	ReadSaveGameString( cmd, savefile );

	savefile->Read( &actualX, sizeof( actualX ) );
	savefile->Read( &actualY, sizeof( actualY ) );
	savefile->Read( &childID, sizeof( childID ) );
	savefile->Read( &flags, sizeof( flags ) );
	savefile->Read( &lastTimeRun, sizeof( lastTimeRun ) );
	savefile->Read( &drawRect, sizeof( drawRect ) );
	savefile->Read( &clientRect, sizeof( clientRect ) );
	savefile->Read( &origin, sizeof( origin ) );
	savefile->Read( &fontNum, sizeof( fontNum ) );
	savefile->Read( &timeLine, sizeof( timeLine ) );
	savefile->Read( &xOffset, sizeof( xOffset ) );
	savefile->Read( &yOffset, sizeof( yOffset ) );
	savefile->Read( &cursor, sizeof( cursor ) );
	savefile->Read( &forceAspectWidth, sizeof( forceAspectWidth ) );
	savefile->Read( &forceAspectHeight, sizeof( forceAspectHeight ) );
	savefile->Read( &matScalex, sizeof( matScalex ) );
	savefile->Read( &matScaley, sizeof( matScaley ) );
	savefile->Read( &borderSize, sizeof( borderSize ) );
	savefile->Read( &textAlign, sizeof( textAlign ) );
	savefile->Read( &textAlignx, sizeof( textAlignx ) );
	savefile->Read( &textAligny, sizeof( textAligny ) );
	savefile->Read( &textShadow, sizeof( textShadow ) );
	savefile->Read( &shear, sizeof( shear ) );

	ReadSaveGameString( name, savefile );
	ReadSaveGameString( comment, savefile );

	// WinVars
	noTime.ReadFromSaveGame( savefile );
	visible.ReadFromSaveGame( savefile );
	rect.ReadFromSaveGame( savefile );
	backColor.ReadFromSaveGame( savefile );
	matColor.ReadFromSaveGame( savefile );
	foreColor.ReadFromSaveGame( savefile );
	hoverColor.ReadFromSaveGame( savefile );
	borderColor.ReadFromSaveGame( savefile );
	textScale.ReadFromSaveGame( savefile );
	noEvents.ReadFromSaveGame( savefile );
	rotate.ReadFromSaveGame( savefile );
	text.ReadFromSaveGame( savefile );
	backGroundName.ReadFromSaveGame( savefile );

	if ( session->GetSaveGameVersion() >= 17 ) {
		hideCursor.ReadFromSaveGame(savefile);
	} else {
		hideCursor = false;
	}

	// Defined Vars
	for ( i = 0; i < definedVars.Num(); i++ ) {
		definedVars[i]->ReadFromSaveGame( savefile );
	}

	savefile->Read( &textRect, sizeof( textRect ) );

	// Window pointers saved as the child ID of the window
	int winID = -1;

	savefile->Read( &winID, sizeof( winID ) );
	for ( i = 0; i < children.Num(); i++ ) {
		if ( children[i]->childID == winID ) {
			focusedChild = children[i];
		}
	}
	savefile->Read( &winID, sizeof( winID ) );
	for ( i = 0; i < children.Num(); i++ ) {
		if ( children[i]->childID == winID ) {
			captureChild = children[i];
		}
	}
	savefile->Read( &winID, sizeof( winID ) );
	for ( i = 0; i < children.Num(); i++ ) {
		if ( children[i]->childID == winID ) {
			overChild = children[i];
		}
	}

	// Scripts
	for ( i = 0; i < SCRIPT_COUNT; i++ ) {
		if ( scripts[i] ) {
			scripts[i]->ReadFromSaveGame( savefile );
		}
	}

	// TimeLine Events
	for ( i = 0; i < timeLineEvents.Num(); i++ ) {
		if ( timeLineEvents[i] ) {
			savefile->Read( &timeLineEvents[i]->pending, sizeof( timeLineEvents[i]->pending ) );
			savefile->Read( &timeLineEvents[i]->time, sizeof( timeLineEvents[i]->time ) );
			if ( timeLineEvents[i]->event ) {
				timeLineEvents[i]->event->ReadFromSaveGame( savefile );
			}
		}
	}


	// Transitions
	int num;
	savefile->Read( &num, sizeof( num ) );
	for ( i = 0; i < num; i++ ) {
		idTransitionData trans;
		trans.data = NULL;
		ReadSaveGameTransition( trans, savefile );
		if ( trans.data ) {
			transitions.Append( trans );
		}
	}


	// Named Events
	for ( i = 0; i < namedEvents.Num(); i++ ) {
		if ( namedEvents[i] ) {
			ReadSaveGameString( namedEvents[i]->mName, savefile );
			if ( namedEvents[i]->mEvent ) {
				namedEvents[i]->mEvent->ReadFromSaveGame( savefile );
			}
		}
	}

	// regList
	regList.ReadFromSaveGame( savefile );

	// Read children
	for ( i = 0; i < drawWindows.Num(); i++ ) {
		drawWin_t	window = drawWindows[i];

		if ( window.simp ) {
			window.simp->ReadFromSaveGame( savefile );
		} else if ( window.win ) {
			window.win->ReadFromSaveGame( savefile );
		}
	}

	if ( flags & WIN_DESKTOP ) {
		FixupTransitions();
	}
}

/*
===============
idWindow::NumTransitions
===============
*/
int idWindow::NumTransitions() {
	int c = transitions.Num();
	for ( int i = 0; i < children.Num(); i++ ) {
		c += children[i]->NumTransitions();
	}
	return c;
}


/*
===============
idWindow::FixupTransitions
===============
*/
void idWindow::FixupTransitions() {
	int i, c = transitions.Num();
	for ( i = 0; i < c; i++ ) {
		drawWin_t *dw = gui->GetDesktop()->FindChildByName( ( ( idWinStr* )transitions[i].data )->c_str() );
		delete transitions[i].data;
		transitions[i].data = NULL;
		if ( dw && ( dw->win || dw->simp ) ){
			if ( dw->win ) {
				if ( transitions[i].offset == (ptrdiff_t)&this->rect - (ptrdiff_t)this ) {
					transitions[i].data = &dw->win->rect;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->backColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->win->backColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->matColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->win->matColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->foreColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->win->foreColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->borderColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->win->borderColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->textScale - (ptrdiff_t)this ) {
					transitions[i].data = &dw->win->textScale;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->rotate - (ptrdiff_t)this ) {
					transitions[i].data = &dw->win->rotate;
				}
			} else {
				if ( transitions[i].offset == (ptrdiff_t)&this->rect - (ptrdiff_t)this ) {
					transitions[i].data = &dw->simp->rect;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->backColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->simp->backColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->matColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->simp->matColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->foreColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->simp->foreColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->borderColor - (ptrdiff_t)this ) {
					transitions[i].data = &dw->simp->borderColor;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->textScale - (ptrdiff_t)this ) {
					transitions[i].data = &dw->simp->textScale;
				} else if ( transitions[i].offset == (ptrdiff_t)&this->rotate - (ptrdiff_t)this ) {
					transitions[i].data = &dw->simp->rotate;
				}
			}
		}
		if ( transitions[i].data == NULL ) {
			transitions.RemoveIndex( i );
			i--;
			c--;
		}
	}
	for ( c = 0; c < children.Num(); c++ ) {
		children[c]->FixupTransitions();
	}
}


/*
===============
idWindow::AddChild
===============
*/
void idWindow::AddChild(idWindow *win) {
	win->childID = children.Append(win);
}

/*
================
idWindow::FixupParms
================
*/
void idWindow::FixupParms() {
	int i;
	int c = children.Num();
	for (i = 0; i < c; i++) {
		children[i]->FixupParms();
	}
	for (i = 0; i < SCRIPT_COUNT; i++) {
		if (scripts[i]) {
			scripts[i]->FixupParms(this);
		}
	}

	c = timeLineEvents.Num();
	for (i = 0; i < c; i++) {
		timeLineEvents[i]->event->FixupParms(this);
	}

	c = namedEvents.Num();
	for (i = 0; i < c; i++) {
		namedEvents[i]->mEvent->FixupParms(this);
	}

	c = ops.Num();
	for (i = 0; i < c; i++) {
		if (ops[i].b == -2) {
			// need to fix this up
			const char *p = (const char*)(ops[i].a);
			idWinVar *var = GetWinVarByName(p, true);
			delete []p;
			ops[i].a = (intptr_t)var;
			ops[i].b = -1;
		}
	}


	if (flags & WIN_DESKTOP) {
		CalcRects(0,0);
	}

}

/*
================
idWindow::IsSimple
================
*/
bool idWindow::IsSimple() {

	// dont do simple windows when in gui editor
	if ( com_editors & EDITOR_GUI ) {
		return false;
	}

	if (ops.Num()) {
		return false;
	}
	if (flags & (WIN_HCENTER | WIN_VCENTER)) {
		return false;
	}
	if (children.Num() || drawWindows.Num()) {
		return false;
	}
	for (int i = 0; i < SCRIPT_COUNT; i++) {
		if (scripts[i]) {
			return false;
		}
	}
	if (timeLineEvents.Num()) {
		return false;
	}

	if ( namedEvents.Num() ) {
		return false;
	}

	return true;
}

/*
================
idWindow::ContainsStateVars
================
*/
bool idWindow::ContainsStateVars() {
	if ( updateVars.Num() ) {
		return true;
	}
	int c = children.Num();
	for (int i = 0; i < c; i++) {
		if ( children[i]->ContainsStateVars() ) {
			return true;
		}
	}
	return false;
}

/*
================
idWindow::Interactive
================
*/
bool idWindow::Interactive() {
	if ( scripts[ ON_ACTION ] ) {
		return true;
	}
	int c = children.Num();
	for (int i = 0; i < c; i++) {
		if (children[i]->Interactive()) {
			return true;
		}
	}
	return false;
}

/*
================
idWindow::SetChildWinVarVal
================
*/
void idWindow::SetChildWinVarVal(const char *name, const char *var, const char *val) {
	drawWin_t *dw = FindChildByName(name);
	idWinVar *wv = NULL;
	if (dw && dw->simp) {
		wv = dw->simp->GetWinVarByName(var);
	} else if (dw && dw->win) {
		wv = dw->win->GetWinVarByName(var);
	}
	if (wv) {
		wv->Set(val);
		wv->SetEval(false);
	}
}


/*
================
idWindow::FindChildByPoint

Finds the window under the given point
================
*/
idWindow* idWindow::FindChildByPoint ( float x, float y, idWindow** below ) {
	int c = children.Num();

	// If we are looking for a window below this one then
	// the next window should be good, but this one wasnt it
	if ( *below == this ) {
		*below = NULL;
		return NULL;
	}

	if ( !Contains ( drawRect, x, y ) ) {
		return NULL;
	}

	for (int i = c - 1; i >= 0 ; i-- ) {
		idWindow* found = children[i]->FindChildByPoint ( x, y, below );
		if ( found ) {
			if ( *below ) {
				continue;
			}

			return found;
		}
	}

	return this;
}

/*
================
idWindow::FindChildByPoint
================
*/
idWindow* idWindow::FindChildByPoint ( float x, float y, idWindow* below )
{
	return FindChildByPoint ( x, y, &below );
}

/*
================
idWindow::GetChildCount

Returns the number of children
================
*/
int idWindow::GetChildCount ( void )
{
	return drawWindows.Num ( );
}

/*
================
idWindow::GetChild

Returns the child window at the given index
================
*/
idWindow* idWindow::GetChild ( int index )
{
	return drawWindows[index].win;
}

/*
================
idWindow::GetChildIndex

Returns the index of the given child window
================
*/
int idWindow::GetChildIndex ( idWindow* window ) {
	int find;
	for ( find = 0; find < drawWindows.Num(); find ++ ) {
		if ( drawWindows[find].win == window ) {
			return find;
		}
	}
	return -1;
}

/*
================
idWindow::RemoveChild

Removes the child from the list of children.   Note that the child window being
removed must still be deallocated by the caller
================
*/
void idWindow::RemoveChild ( idWindow *win ) {
	int find;

	// Remove the child window
	children.Remove ( win );

	for ( find = 0; find < drawWindows.Num(); find ++ )
	{
		if ( drawWindows[find].win == win )
		{
			drawWindows.RemoveIndex ( find );
			break;
		}
	}
}

/*
================
idWindow::InsertChild

Inserts the given window as a child into the given location in the zorder.
================
*/
bool idWindow::InsertChild ( idWindow *win, idWindow* before )
{
	AddChild ( win );

	win->parent = this;

	drawWin_t dwt;
	dwt.simp = NULL;
	dwt.win = win;

	// If not inserting before anything then just add it at the end
	if ( before ) {
		int index;
		index = GetChildIndex ( before );
		if ( index != -1 ) {
			drawWindows.Insert ( dwt, index );
			return true;
		}
	}

	drawWindows.Append ( dwt );
	return true;
}

/*
================
idWindow::ScreenToClient
================
*/
void idWindow::ScreenToClient ( idRectangle* r ) {
	int		  x;
	int		  y;
	idWindow* p;

	for ( p = this, x = 0, y = 0; p; p = p->parent ) {
		x += p->rect.x();
		y += p->rect.y();
	}

	r->x -= x;
	r->y -= y;
}

/*
================
idWindow::ClientToScreen
================
*/
void idWindow::ClientToScreen ( idRectangle* r ) {
	int		  x;
	int		  y;
	idWindow* p;

	for ( p = this, x = 0, y = 0; p; p = p->parent ) {
		x += p->rect.x();
		y += p->rect.y();
	}

	r->x += x;
	r->y += y;
}

/*
================
idWindow::SetDefaults

Set the window do a default window with no text, no background and
default colors, etc..
================
*/
void idWindow::SetDefaults ( void ) {
	forceAspectWidth = 640.0f;
	forceAspectHeight = 480.0f;
	matScalex = 1;
	matScaley = 1;
	borderSize = 0;
	noTime = false;
	visible = true;
	textAlign = 0;
	textAlignx = 0;
	textAligny = 0;
	noEvents = false;
	rotate = 0;
	shear.Zero();
	textScale = 0.35f;
	backColor.Zero();
	foreColor = idVec4(1, 1, 1, 1);
	hoverColor = idVec4(1, 1, 1, 1);
	matColor = idVec4(1, 1, 1, 1);
	borderColor.Zero();
	text = "";

	background = NULL;
	backGroundName = "";
}

/*
================
idWindow::UpdateFromDictionary

The editor only has a dictionary to work with so the easiest way to push the
values of the dictionary onto the window is for the window to interpret the
dictionary as if were a file being parsed.
================
*/
bool idWindow::UpdateFromDictionary ( idDict& dict ) {
	const idKeyValue*	kv;
	int					i;

	SetDefaults ( );

	// Clear all registers since they will get recreated
	regList.Reset ( );
	expressionRegisters.Clear ( );
	ops.Clear ( );

	for ( i = 0; i < dict.GetNumKeyVals(); i ++ ) {
		kv = dict.GetKeyVal ( i );

		// Special case name
		if ( !kv->GetKey().Icmp ( "name" ) ) {
			name = kv->GetValue();
			continue;
		}

		idParser src( kv->GetValue().c_str(), kv->GetValue().Length(), "",
					  LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
		if ( !ParseInternalVar ( kv->GetKey(), &src ) ) {
			// Kill the old register since the parse reg entry will add a new one
			if ( !ParseRegEntry ( kv->GetKey(), &src ) ) {
				continue;
			}
		}
	}

	EvalRegs(-1, true);

	SetupFromState();
	PostParse();

	return true;
}
