/* =========================================================================== 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 "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); } // // 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