/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Quake4Doom source code by Justin Marshall.
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 .
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 "precompiled.h"
#pragma hdrstop
#include "Window.h"
#include "UserInterfaceLocal.h"
#include "EditWindow.h"
#include "ChoiceWindow.h"
#include "SliderWindow.h"
#include "BindWindow.h"
#include "ListWindow.h"
#include "RenderWindow.h"
#include "MinigameWindow.h"
#include "MarkerWindow.h"
#include "FieldWindow.h"
//
// gui editor is more integrated into the window now
#include "../tools/guied/GEWindowWrapper.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
// 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);
#ifdef QUAKE4
const char* idWindow::ScriptNames[] = {
"onMouseEnter",
"onMouseExit",
"onAction",
"onBackAction",
"onActivate",
"onDeactivate",
"onESC",
"onEvent",
"onTrigger",
"onActionRelease",
"onEnter",
"onEnterRelease",
"onTabRelease",
"onGainFocus",
"onLoseFocus",
"onSelChange",
"onInit",
"onJoyStart",
"onJoySelect",
"onJoyBack",
"onJoyLShoulder",
"onJoyRShoulder",
"onJoyUp",
"onJoyDown",
"onJoyLeft",
"onJoyRight",
"onJoyButton1",
"onJoyButton2"
};
#else
const char* idWindow::ScriptNames[] = {
"onMouseEnter",
"onMouseExit",
"onAction",
"onActivate",
"onDeactivate",
"onESC",
"onEvent",
"onTrigger",
"onActionRelease",
"onEnter",
"onEnterRelease"
};
#endif
/*
================
idWindow::CommonInit
================
*/
void idWindow::CommonInit() {
childID = 0;
flags = 0;
lastTimeRun = 0;
origin.Zero();
fontNum = 0;
timeLine = -1;
xOffset = yOffset = 0.0f;
cursor = 0;
#ifdef QUAKE4
forceAspectWidth = 640.0f;
forceAspectHeight = 480.0f;
#else
forceAspectWidth = SCREEN_WIDTH;
forceAspectHeight = SCREEN_HEIGHT;
#endif
matScalex = 1.0f;
matScaley = 1.0f;
borderSize = 0.0f;
noTime = false;
visible = true;
textAlign = 0;
textAlignx = 0.0f;
textAligny = 0.0f;
textShadow = 0;
#ifdef QUAKE4
textStyle = 0;
textSpacing = 0.0f;
#endif
noEvents = false;
rotate = 0.0f;
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 = "";
text = "";
focusedChild = NULL;
captureChild = NULL;
overChild = NULL;
parent = NULL;
#ifdef QUAKE4
startChildSearch = 0;
currentChild = NULL;
#endif
saveOps = NULL;
saveRegs = NULL;
hover = false;
hideCursor = false;
for (int i = 0; i < SCRIPT_COUNT; i++) {
scripts[i] = NULL;
}
}
/*
================
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() {
for (int i = 0; i < drawWindows.Num(); i++) {
delete drawWindows[i].simp;
}
regList.Reset();
namedEvents.DeleteContents(true);
drawWindows.Clear();
children.DeleteContents(true);
definedVars.DeleteContents(true);
updateVars.Clear();
timeLineEvents.DeleteContents(true);
transitions.Clear();
#ifdef QUAKE4
transitionSubs.Clear();
#endif
ops.Clear();
expressionRegisters.Clear();
for (int i = 0; i < SCRIPT_COUNT; i++) {
delete scripts[i];
scripts[i] = NULL;
}
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);
}
// Quake 4 extends idDeviceContext with textStyle / textSpacing aware drawing.
// The stock Doom 3 API does not expose that overload, so we preserve the parsed
// state and fall back to classic DrawText here unless the renderer is extended.
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.25f, 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.25f, 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) {
idWindow* last = NULL;
for (int i = 0; i < children.Num(); i++) {
if (children[i]->flags & WIN_CAPTURE) {
last = children[i];
last->LoseCapture();
break;
}
}
if (w) {
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 (lastTimeRun > time) {
lastTimeRun = time;
}
#ifdef QUAKE4
if (!(flags & WIN_ALWAYSTHINK) && time - lastTimeRun < USERCMD_MSEC) {
#else
if (time - lastTimeRun < USERCMD_MSEC) {
#endif
return false;
}
lastTimeRun = time;
UpdateWinVars();
if (expressionRegisters.Num() && ops.Num()) {
EvalRegs();
}
if (flags & WIN_INTRANSITION) {
Transition();
}
Time();
RunScript(ON_FRAME);
for (int i = 0; i < children.Num(); 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 "";
}
}
}
}
#ifdef QUAKE4
if (event->evValue2 && !actionDownRun) {
actionDownRun = RunScript(ON_ACTIONRIGHT);
}
#endif
}
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);
}
}
}
#ifdef QUAKE4
else if (event->evValue == K_JOY3 && event->evValue2) {
if (GetFocusedChild()) {
const char* childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
if (childRet && *childRet) {
return childRet;
}
if (!actionDownRun) {
actionDownRun = GetFocusedChild()->RunScript(ON_ACTION);
AddCommand(GetFocusedChild()->cmd.c_str());
}
}
RunControllerScript(ON_JOYACTION, event, updateVisuals);
}
else if (event->evValue == K_JOY4 && event->evValue2) {
if (!RunControllerScript(ON_JOYBACK, event, updateVisuals)) {
if (GetFocusedChild()) {
const char* childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
if (childRet && *childRet) {
return childRet;
}
}
RunScript(ON_ESC);
}
}
else if (event->evValue == K_JOY1 && event->evValue2) {
RunControllerScript(ON_JOYLSHOULDER, event, updateVisuals);
}
else if (event->evValue == K_JOY2 && event->evValue2) {
RunControllerScript(ON_JOYRSHOULDER, event, updateVisuals);
}
else if (event->evValue == K_JOY5 && event->evValue2) {
RunControllerScript(ON_JOYBUTTON2, event, updateVisuals);
}
else if (event->evValue == K_JOY6 && event->evValue2) {
RunControllerScript(ON_JOYBUTTON1, event, updateVisuals);
}
else if (event->evValue == K_JOY7 && event->evValue2) {
RunControllerScript(ON_JOYBUTTON3, event, updateVisuals);
}
else if (event->evValue == K_JOY9 && event->evValue2) {
RunControllerScript(ON_JOYLEFT, event, updateVisuals);
}
else if (event->evValue == K_JOY10 && event->evValue2) {
RunControllerScript(ON_JOYRIGHT, event, updateVisuals);
}
else if (event->evValue == K_JOY11 && event->evValue2) {
RunControllerScript(ON_JOYUP, event, updateVisuals);
}
else if (event->evValue == K_JOY12 && event->evValue2) {
RunControllerScript(ON_JOYDOWN, event, updateVisuals);
}
#endif
else {
if (GetFocusedChild()) {
const char* childRet = GetFocusedChild()->HandleEvent(event, updateVisuals);
if (childRet && *childRet) {
return childRet;
}
}
}
}
#ifdef QUAKE4
else if (event->evType == SE_JOYSTICK_AXIS) {
if (updateVisuals) {
*updateVisuals = true;
}
RouteJoystickChange(event->evValue, event->evValue2);
}
#endif
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(data->data);
idWinFloat* val = NULL;
#ifdef QUAKE4
idWinFloatMember* member = NULL;
#endif
if (v4 == NULL) {
r = dynamic_cast(data->data);
if (!r) {
val = dynamic_cast(data->data);
if (!val) {
#ifdef QUAKE4
member = dynamic_cast(data->data);
#endif
}
}
}
if (data->interp.IsDone(gui->GetTime()) && data->data) {
if (v4) {
*v4 = data->interp.GetEndValue();
}
else if (val) {
*val = data->interp.GetEndValue()[0];
}
#ifdef QUAKE4
else if (member) {
member->Set(va("%g", data->interp.GetEndValue()[0]));
}
#endif
else if (r) {
*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];
}
#ifdef QUAKE4
else if (member) {
member->Set(va("%g", data->interp.GetCurrentValue(gui->GetTime())[0]));
}
#endif
else if (r) {
*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;
}
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) {
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);
}
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() {
background = NULL;
SetupBackground();
if (borderSize) {
flags |= WIN_BORDER;
}
if (regList.FindReg("rotate") || regList.FindReg("shear")) {
flags |= WIN_TRANSFORM;
}
CalcClientRect(0, 0);
#ifdef QUAKE4
if (scripts[ON_ACTION] || scripts[ON_ACTIONRIGHT] || scripts[ON_JOYACTION]) {
#else
if (scripts[ON_ACTION]) {
#endif
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) {
idWindow* lastFocus = NULL;
if (!w || !(w->flags & WIN_CANFOCUS)) {
return NULL;
}
lastFocus = gui->GetDesktop()->focusedChild;
if (lastFocus) {
lastFocus->flags &= ~WIN_FOCUS;
lastFocus->LoseFocus();
}
#ifdef QUAKE4
if (scripts && lastFocus) {
lastFocus->RunScript(ON_LOSEFOCUS);
}
if (scripts && w) {
w->RunScript(ON_GAINFOCUS);
}
#endif
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 = (intptr_t) & ((idWindow*)0)->rect;
}
if (wv == &backColor) {
ret = (intptr_t) & ((idWindow*)0)->backColor;
}
if (wv == &matColor) {
ret = (intptr_t) & ((idWindow*)0)->matColor;
}
if (wv == &foreColor) {
ret = (intptr_t) & ((idWindow*)0)->foreColor;
}
if (wv == &hoverColor) {
ret = (intptr_t) & ((idWindow*)0)->hoverColor;
}
if (wv == &borderColor) {
ret = (intptr_t) & ((idWindow*)0)->borderColor;
}
if (wv == &textScale) {
ret = (intptr_t) & ((idWindow*)0)->textScale;
}
if (wv == &rotate) {
ret = (intptr_t) & ((idWindow*)0)->rotate;
}
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 = ▭
}
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);
for (int i = 0; i < definedVars.Num(); 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;
}
if (key.Length() > 5 && guiVar) {
idWinVar* var = new idWinStr;
var->Init(_name, this);
definedVars.Append(var);
return var;
}
if (fixup) {
int n = key.Find("::");
if (n > 0) {
idStr winName = key.Left(n);
idStr varName = key.Right(key.Length() - n - 2);
drawWin_t* win = GetGui()->GetDesktop()->FindChildByName(winName);
if (win) {
if (win->win) {
return win->win->GetWinVarByName(varName, false, owner);
}
else {
if (owner) {
*owner = win;
}
return win->simp->GetWinVarByName(varName);
}
}
}
}
#ifdef QUAKE4
static bool preventRecursion = false;
char* underscore = strrchr(const_cast(_name), '_');
if (underscore && underscore[1] && underscore[2] == '\0' && !preventRecursion) {
idStr temp = _name;
temp.CapLength( (int)( underscore - _name ) );
int index = -1;
const char suffix = (char)tolower(underscore[1]);
preventRecursion = true;
idWinVar* base = GetWinVarByName(temp.c_str(), fixup, owner);
preventRecursion = false;
if (dynamic_cast(base)) {
switch (suffix) {
case 'x': index = 0; break;
case 'y': index = 1; break;
case 'z': index = 2; break;
case 'w': index = 3; break;
}
}
else if (dynamic_cast(base)) {
switch (suffix) {
case 'x': index = 0; break;
case 'y': index = 1; break;
case 'w': index = 2; break;
case 'h': index = 3; break;
}
}
if (index != -1) {
idWinFloatMember* member = new idWinFloatMember(base, index);
member->SetName(_name);
definedVars.Append(member);
return member;
}
}
#endif
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;
}
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;
}
#ifdef QUAKE4
if (idStr::Icmp(_name, "textSpacing") == 0) {
textSpacing = src->ParseFloat();
return true;
}
if (idStr::Icmp(_name, "textStyle") == 0) {
textStyle = src->ParseInt();
return true;
}
#endif
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;
}
#ifdef QUAKE4
if (idStr::Icmp(_name, "alwaysThink") == 0) {
if (src->ParseBool()) {
flags |= WIN_ALWAYSTHINK;
}
return true;
}
if (idStr::Icmp(_name, "chatWindow") == 0) {
if (src->ParseBool()) {
flags |= WIN_CHATWINDOW;
}
return true;
}
#endif
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, NULL);
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;
#ifdef QUAKE4
forceAspectWidth = 640.0;
forceAspectHeight = 480.0;
#else
forceAspectWidth = SCREEN_WIDTH;
forceAspectHeight = SCREEN_HEIGHT;
#endif
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 == "minigameDef") {
idMinigameWindow* win = new idMinigameWindow(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;
}
if (token == "+")
{
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();
#ifdef QUAKE4
int start = startChildSearch;
#else
int start = 0;
#endif
if (start >= c) {
start = 0;
}
for (int phase = 0; phase < 2; phase++) {
int i = (phase == 0) ? start : 0;
int end = (phase == 0) ? c : start;
for (; i < end; i++) {
if (drawWindows[i].win) {
if (idStr::Icmp(drawWindows[i].win->name, _name) == 0) {
#ifdef QUAKE4
startChildSearch = i;
#endif
return &drawWindows[i];
}
drawWin_t* win = drawWindows[i].win->FindChildByName(_name);
if (win) {
#ifdef QUAKE4
startChildSearch = i;
#endif
return win;
}
}
else if (drawWindows[i].simp) {
if (idStr::Icmp(drawWindows[i].simp->name, _name) == 0) {
#ifdef QUAKE4
startChildSearch = i;
#endif
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;
for (int i = 0; i < timeLineEvents.Num(); i++) {
if (timeLineEvents[i]->time >= t) {
timeLineEvents[i]->pending = true;
}
}
noTime = false;
for (int i = 0; i < transitions.Num(); i++) {
idTransitionData* data = &transitions[i];
if (data->interp.IsDone(gui->GetTime()) && data->data) {
transitions.RemoveIndex(i);
#ifdef QUAKE4
#ifdef QUAKE4
if (i < transitionSubs.Num()) {
transitionSubs.RemoveIndex(i);
}
#endif
#endif
i--;
}
}
}
/*
================
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());
}
const idDeclTable* table = static_cast(declManager->FindType(DECL_TABLE, token.c_str(), false));
if (table) {
a = table->Index();
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;
var->Init(token, this);
b = component;
if (dynamic_cast(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(var)) {
return EmitOp(a, b, WOP_TYPE_VARF);
}
#ifdef QUAKE4
else if (dynamic_cast(var)) {
return EmitOp(a, b, WOP_TYPE_VARFM);
}
#endif
else if (dynamic_cast(var)) {
return EmitOp(a, b, WOP_TYPE_VARI);
}
else if (dynamic_cast(var)) {
return EmitOp(a, b, WOP_TYPE_VARB);
}
else if (dynamic_cast(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 {
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(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;
#ifdef QUAKE4
case WOP_TYPE_VARFM:
if (op->a) {
idWinFloatMember* var = (idWinFloatMember*)(op->a);
registers[op->c] = var->x();
}
else {
registers[op->c] = 0;
}
break;
#endif
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;
idWinVar* targetVar = trans.data;
#ifdef QUAKE4
int subIndex = -1;
if (idWinFloatMember* member = dynamic_cast(trans.data)) {
targetVar = member->GetOwner();
subIndex = member->GetComponent();
}
#endif
int offset = gui->GetDesktop()->GetWinVarOffset(targetVar, &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));
#ifdef QUAKE4
savefile->Write(&subIndex, sizeof(subIndex)); // savegame version >= 18
#endif
}
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));
#ifdef QUAKE4
int subIndex = -1;
if (session->GetSaveGameVersion() >= 18) {
savefile->Read(&subIndex, sizeof(subIndex));
}
transitionSubs.Append(subIndex);
#endif
trans.data = NULL;
trans.offset = offset;
if (winName.Length()) {
idWinStr* strVar = new idWinStr();
strVar->Set(winName);
trans.data = dynamic_cast(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));
#ifdef QUAKE4
savefile->Write(&textStyle, sizeof(textStyle)); // savegame version >= 18
savefile->Write(&textSpacing, sizeof(textSpacing)); // savegame version >= 18
#endif
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();
#ifdef QUAKE4
transitionSubs.Clear();
#endif
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));
#ifdef QUAKE4
if (session->GetSaveGameVersion() >= 18) {
savefile->Read(&textStyle, sizeof(textStyle));
savefile->Read(&textSpacing, sizeof(textSpacing));
}
else {
textStyle = 0;
textSpacing = 0.0f;
}
#endif
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() {
for (int i = 0; i < transitions.Num(); 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 == (int)&((idWindow*)0)->rect) {
transitions[i].data = &dw->win->rect;
}
else if (transitions[i].offset == (int)&((idWindow*)0)->backColor) {
transitions[i].data = &dw->win->backColor;
}
else if (transitions[i].offset == (int)&((idWindow*)0)->matColor) {
transitions[i].data = &dw->win->matColor;
}
else if (transitions[i].offset == (int)&((idWindow*)0)->foreColor) {
transitions[i].data = &dw->win->foreColor;
}
else if (transitions[i].offset == (int)&((idWindow*)0)->borderColor) {
transitions[i].data = &dw->win->borderColor;
}
else if (transitions[i].offset == (int)&((idWindow*)0)->textScale) {
transitions[i].data = &dw->win->textScale;
}
else if (transitions[i].offset == (int)&((idWindow*)0)->rotate) {
transitions[i].data = &dw->win->rotate;
}
}
else {
if (transitions[i].offset == (int)&((idSimpleWindow*)0)->rect) {
transitions[i].data = &dw->simp->rect;
}
else if (transitions[i].offset == (int)&((idSimpleWindow*)0)->backColor) {
transitions[i].data = &dw->simp->backColor;
}
else if (transitions[i].offset == (int)&((idSimpleWindow*)0)->matColor) {
transitions[i].data = &dw->simp->matColor;
}
else if (transitions[i].offset == (int)&((idSimpleWindow*)0)->foreColor) {
transitions[i].data = &dw->simp->foreColor;
}
else if (transitions[i].offset == (int)&((idSimpleWindow*)0)->borderColor) {
transitions[i].data = &dw->simp->borderColor;
}
else if (transitions[i].offset == (int)&((idSimpleWindow*)0)->textScale) {
transitions[i].data = &dw->simp->textScale;
}
else if (transitions[i].offset == (int)&((idSimpleWindow*)0)->rotate) {
transitions[i].data = &dw->simp->rotate;
}
}
}
#ifdef QUAKE4
if (transitions[i].data && i < transitionSubs.Num() && transitionSubs[i] != -1) {
transitions[i].data = new idWinFloatMember(transitions[i].data, transitionSubs[i]);
}
#endif
if (transitions[i].data == NULL) {
transitions.RemoveIndex(i);
#ifdef QUAKE4
#ifdef QUAKE4
if (i < transitionSubs.Num()) {
transitionSubs.RemoveIndex(i);
}
#endif
#endif
i--;
}
}
for (int 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;
}
#ifdef QUAKE4
if (forceAspectWidth != 640.0f || forceAspectHeight != 480.0f) {
return false;
}
#endif
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) {
matScalex = 1.0f;
matScaley = 1.0f;
#ifdef QUAKE4
forceAspectWidth = 640.0f;
forceAspectHeight = 480.0f;
#else
forceAspectWidth = SCREEN_WIDTH;
forceAspectHeight = SCREEN_HEIGHT;
#endif
borderSize = 0.0f;
noTime = false;
visible = true;
textAlign = 0;
textAlignx = 0.0f;
textAligny = 0.0f;
textShadow = 0;
#ifdef QUAKE4
#ifdef QUAKE4
textStyle = 0;
textSpacing = 0.0f;
#endif
#endif
noEvents = false;
rotate = 0.0f;
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();
#ifdef QUAKE4
transitionSubs.Clear();
#endif
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;
}
#ifdef QUAKE4
/*
================
idWindow::IsVisible
================
*/
bool idWindow::IsVisible() const {
for (const idWindow* w = this; w; w = w->parent) {
if (!w->visible) {
return false;
}
}
return true;
}
/*
================
idWindow::Init
================
*/
void idWindow::Init() {
RunScript(ON_INIT);
for (int i = 0; i < children.Num(); i++) {
children[i]->Init();
}
}
/*
================
idWindow::FindBestChild_r
================
*/
void idWindow::FindBestChild_r(int xd, int yd, int& startX, int& startY, int& bestX, int& bestY, idWindow* current, idWindow*& best) {
for (int i = children.Num() - 1; i >= 0; i--) {
idWindow* child = children[i];
if (!child->visible) {
continue;
}
if (child->noEvents || !child->scripts[ON_MOUSEENTER] || child == current) {
child->FindBestChild_r(xd, yd, startX, startY, bestX, bestY, current, best);
continue;
}
const int x = (int)child->actualX;
const int y = (int)child->actualY;
if (yd > 0) {
if (y > startY && (!best || y < bestY)) {
best = child;
bestY = y;
}
}
else if (yd < 0) {
if (y < startY && (!best || y > bestY)) {
best = child;
bestY = y;
}
}
else if (y > startY && (!best || y < bestY)) {
best = child;
bestY = y;
}
if (xd > 0) {
if (x > startX && (!best || x < bestX)) {
best = child;
bestX = x;
}
}
else if (xd < 0) {
if (x < startX && (!best || x > bestX)) {
best = child;
bestX = x;
}
}
}
}
/*
================
idWindow::RunControllerScript
================
*/
bool idWindow::RunControllerScript(int script, const sysEvent_t* event, bool* updateVisuals) {
if (!(flags & WIN_DESKTOP)) {
return false;
}
idWindow* focus = gui->GetDesktop()->focusedChild;
if (!focus) {
focus = overChild;
}
if (!focus || !focus->IsVisible()) {
return false;
}
if (!focus->RunScript(script)) {
return false;
}
// Mirror Q4 behaviour: if focus and hover diverge, move hover to the focused child
// so controller-only menus still receive the same enter/exit side effects as mouse menus.
idWindow* newOver = gui->GetDesktop()->focusedChild;
if (newOver && newOver != overChild) {
if (overChild) {
overChild->MouseExit();
if (overChild->cmd.Length()) {
AddCommand(overChild->cmd.c_str());
overChild->cmd = "";
}
}
overChild = newOver;
dc->SetCursor(newOver->cursor);
newOver->hover = true;
newOver->MouseEnter();
if (newOver->cmd.Length()) {
AddCommand(newOver->cmd.c_str());
newOver->cmd = "";
}
}
return true;
}
/*
================
idWindow::RouteJoystickChange
================
*/
bool idWindow::RouteJoystickChange(int xd, int yd) {
if (GetCaptureChild()) {
return GetCaptureChild()->RouteJoystickChange(xd, yd);
}
idWindow* best = NULL;
int startX = 0;
int startY = 0;
int bestX = 0;
int bestY = 0;
if (overChild && overChild->IsVisible() && !overChild->noEvents && overChild->scripts[ON_MOUSEENTER]) {
if (xd == 0 && yd == 0) {
SetFocus(overChild, true);
dc->SetCursor(overChild->cursor);
overChild->hover = true;
overChild->MouseEnter();
if (overChild->cmd.Length()) {
AddCommand(overChild->cmd.c_str());
overChild->cmd = "";
}
return true;
}
startX = (int)overChild->actualX;
startY = (int)overChild->actualY;
}
FindBestChild_r(xd, yd, startX, startY, bestX, bestY, overChild, best);
if (!best) {
for (int i = 0; i < children.Num(); i++) {
if (children[i]->RouteJoystickChange(xd, yd)) {
return true;
}
}
return false;
}
SetFocus(best, true);
dc->SetCursor(best->cursor);
best->hover = true;
if (overChild && overChild != best) {
overChild->MouseExit();
if (overChild->cmd.Length()) {
AddCommand(overChild->cmd.c_str());
overChild->cmd = "";
}
}
overChild = best;
best->MouseEnter();
if (best->cmd.Length()) {
AddCommand(best->cmd.c_str());
best->cmd = "";
}
return true;
}
/*
================
idWindow::ClearTransitions
================
*/
void idWindow::ClearTransitions() {
transitions.Clear();
#ifdef QUAKE4
transitionSubs.Clear();
#endif
flags &= ~WIN_INTRANSITION;
}
/*
================
idWindow::SetWrapper
================
*/
void idWindow::SetWrapper(int wrapper) {
idWinVar* existing = GetWinVarByName("guied_wrapper");
if (idWinInt* intVar = dynamic_cast(existing)) {
*intVar = wrapper;
intVar->SetEval(false);
return;
}
idWinInt* var = new idWinInt();
*var = wrapper;
var->SetEval(false);
var->SetName("guied_wrapper");
definedVars.Append(var);
}
#endif // QUAKE4