ChoiceWindow.cpp (10271B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 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. 25 26 =========================================================================== 27 */ 28 29 #pragma hdrstop 30 #include "../idlib/precompiled.h" 31 32 #include "DeviceContext.h" 33 #include "Window.h" 34 #include "UserInterfaceLocal.h" 35 #include "ChoiceWindow.h" 36 37 /* 38 ============ 39 idChoiceWindow::InitVars 40 ============ 41 */ 42 void idChoiceWindow::InitVars( ) { 43 if ( cvarStr.Length() ) { 44 cvar = cvarSystem->Find( cvarStr ); 45 if ( !cvar ) { 46 common->Warning( "idChoiceWindow::InitVars: gui '%s' window '%s' references undefined cvar '%s'", gui->GetSourceFile(), name.c_str(), cvarStr.c_str() ); 47 return; 48 } 49 updateStr.Append( &cvarStr ); 50 } 51 if ( guiStr.Length() ) { 52 updateStr.Append( &guiStr ); 53 } 54 updateStr.SetGuiInfo( gui->GetStateDict() ); 55 updateStr.Update(); 56 } 57 58 /* 59 ============ 60 idChoiceWindow::CommonInit 61 ============ 62 */ 63 void idChoiceWindow::CommonInit() { 64 currentChoice = 0; 65 choiceType = 0; 66 cvar = NULL; 67 liveUpdate = true; 68 choices.Clear(); 69 } 70 71 idChoiceWindow::idChoiceWindow(idUserInterfaceLocal *g) : idWindow(g) { 72 gui = g; 73 CommonInit(); 74 } 75 76 idChoiceWindow::~idChoiceWindow() { 77 78 } 79 80 void idChoiceWindow::RunNamedEvent( const char* eventName ) { 81 idStr event, group; 82 83 if ( !idStr::Cmpn( eventName, "cvar read ", 10 ) ) { 84 event = eventName; 85 group = event.Mid( 10, event.Length() - 10 ); 86 if ( !group.Cmp( updateGroup ) ) { 87 UpdateVars( true, true ); 88 } 89 } else if ( !idStr::Cmpn( eventName, "cvar write ", 11 ) ) { 90 event = eventName; 91 group = event.Mid( 11, event.Length() - 11 ); 92 if ( !group.Cmp( updateGroup ) ) { 93 UpdateVars( false, true ); 94 } 95 } 96 } 97 98 void idChoiceWindow::UpdateVars( bool read, bool force ) { 99 if ( force || liveUpdate ) { 100 if ( cvar && cvarStr.NeedsUpdate() ) { 101 if ( read ) { 102 cvarStr.Set( cvar->GetString() ); 103 } else { 104 cvar->SetString( cvarStr.c_str() ); 105 } 106 } 107 if ( !read && guiStr.NeedsUpdate() ) { 108 guiStr.Set( va( "%i", currentChoice ) ); 109 } 110 } 111 } 112 113 const char *idChoiceWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals) { 114 int key; 115 bool runAction = false; 116 bool runAction2 = false; 117 118 if ( event->evType == SE_KEY ) { 119 key = event->evValue; 120 121 if ( key == K_RIGHTARROW || key == K_KP_6 || key == K_MOUSE1) { 122 // never affects the state, but we want to execute script handlers anyway 123 if ( !event->evValue2 ) { 124 RunScript( ON_ACTIONRELEASE ); 125 return cmd; 126 } 127 currentChoice++; 128 if (currentChoice >= choices.Num()) { 129 currentChoice = 0; 130 } 131 runAction = true; 132 } 133 134 if ( key == K_LEFTARROW || key == K_KP_4 || key == K_MOUSE2) { 135 // never affects the state, but we want to execute script handlers anyway 136 if ( !event->evValue2 ) { 137 RunScript( ON_ACTIONRELEASE ); 138 return cmd; 139 } 140 currentChoice--; 141 if (currentChoice < 0) { 142 currentChoice = choices.Num() - 1; 143 } 144 runAction = true; 145 } 146 147 if ( !event->evValue2 ) { 148 // is a key release with no action catch 149 return ""; 150 } 151 152 } else if ( event->evType == SE_CHAR ) { 153 154 key = event->evValue; 155 156 int potentialChoice = -1; 157 for ( int i = 0; i < choices.Num(); i++ ) { 158 if ( toupper(key) == toupper(choices[i][0]) ) { 159 if ( i < currentChoice && potentialChoice < 0 ) { 160 potentialChoice = i; 161 } else if ( i > currentChoice ) { 162 potentialChoice = -1; 163 currentChoice = i; 164 break; 165 } 166 } 167 } 168 if ( potentialChoice >= 0 ) { 169 currentChoice = potentialChoice; 170 } 171 172 runAction = true; 173 runAction2 = true; 174 175 } else { 176 return ""; 177 } 178 179 if ( runAction ) { 180 RunScript( ON_ACTION ); 181 } 182 183 if ( choiceType == 0 ) { 184 cvarStr.Set( va( "%i", currentChoice ) ); 185 } else if ( values.Num() ) { 186 cvarStr.Set( values[ currentChoice ] ); 187 } else { 188 cvarStr.Set( choices[ currentChoice ] ); 189 } 190 191 UpdateVars( false ); 192 193 if ( runAction2 ) { 194 RunScript( ON_ACTIONRELEASE ); 195 } 196 197 return cmd; 198 } 199 200 void idChoiceWindow::ValidateChoice() { 201 if ( currentChoice < 0 || currentChoice >= choices.Num() ) { 202 currentChoice = 0; 203 } 204 if ( choices.Num() == 0 ) { 205 choices.Append( "No Choices Defined" ); 206 } 207 } 208 209 void idChoiceWindow::UpdateChoice() { 210 if ( !updateStr.Num() ) { 211 return; 212 } 213 UpdateVars( true ); 214 updateStr.Update(); 215 if ( choiceType == 0 ) { 216 // ChoiceType 0 stores current as an integer in either cvar or gui 217 // If both cvar and gui are defined then cvar wins, but they are both updated 218 if ( updateStr[ 0 ]->NeedsUpdate() ) { 219 currentChoice = atoi( updateStr[ 0 ]->c_str() ); 220 } 221 ValidateChoice(); 222 } else { 223 // ChoiceType 1 stores current as a cvar string 224 int c = ( values.Num() ) ? values.Num() : choices.Num(); 225 int i; 226 for ( i = 0; i < c; i++ ) { 227 if ( idStr::Icmp( cvarStr.c_str(), ( values.Num() ) ? values[i] : choices[i] ) == 0 ) { 228 break; 229 } 230 } 231 if (i == c) { 232 i = 0; 233 } 234 currentChoice = i; 235 ValidateChoice(); 236 } 237 } 238 239 bool idChoiceWindow::ParseInternalVar(const char *_name, idTokenParser *src) { 240 if (idStr::Icmp(_name, "choicetype") == 0) { 241 choiceType = src->ParseInt(); 242 return true; 243 } 244 if (idStr::Icmp(_name, "currentchoice") == 0) { 245 currentChoice = src->ParseInt(); 246 return true; 247 } 248 return idWindow::ParseInternalVar(_name, src); 249 } 250 251 252 idWinVar *idChoiceWindow::GetWinVarByName(const char *_name, bool fixup, drawWin_t** owner) { 253 if ( idStr::Icmp( _name, "choices" ) == 0 ) { 254 return &choicesStr; 255 } 256 if ( idStr::Icmp( _name, "values" ) == 0 ) { 257 return &choiceVals; 258 } 259 if ( idStr::Icmp( _name, "cvar" ) == 0 ) { 260 return &cvarStr; 261 } 262 if ( idStr::Icmp( _name, "gui" ) == 0 ) { 263 return &guiStr; 264 } 265 if ( idStr::Icmp( _name, "liveUpdate" ) == 0 ) { 266 return &liveUpdate; 267 } 268 if ( idStr::Icmp( _name, "updateGroup" ) == 0 ) { 269 return &updateGroup; 270 } 271 272 return idWindow::GetWinVarByName(_name, fixup, owner); 273 } 274 275 // update the lists whenever the WinVar have changed 276 void idChoiceWindow::UpdateChoicesAndVals() { 277 idToken token; 278 idStr str2, str3; 279 idLexer src; 280 281 if ( latchedChoices.Icmp( choicesStr ) ) { 282 choices.Clear(); 283 src.FreeSource(); 284 src.SetFlags( LEXFL_NOFATALERRORS | LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT ); 285 src.LoadMemory( choicesStr, choicesStr.Length(), "<ChoiceList>" ); 286 if ( src.IsLoaded() ) { 287 while( src.ReadToken( &token ) ) { 288 if ( token == ";" ) { 289 if ( str2.Length() ) { 290 str2.StripTrailingWhitespace(); 291 str2 = idLocalization::GetString( str2 ); 292 choices.Append(str2); 293 str2 = ""; 294 } 295 continue; 296 } 297 str2 += token; 298 str2 += " "; 299 } 300 if ( str2.Length() ) { 301 str2.StripTrailingWhitespace(); 302 choices.Append( str2 ); 303 } 304 } 305 latchedChoices = choicesStr.c_str(); 306 } 307 if ( choiceVals.Length() && latchedVals.Icmp( choiceVals ) ) { 308 values.Clear(); 309 src.FreeSource(); 310 src.SetFlags( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT ); 311 src.LoadMemory( choiceVals, choiceVals.Length(), "<ChoiceVals>" ); 312 str2 = ""; 313 bool negNum = false; 314 if ( src.IsLoaded() ) { 315 while( src.ReadToken( &token ) ) { 316 if (token == "-") { 317 negNum = true; 318 continue; 319 } 320 if (token == ";") { 321 if (str2.Length()) { 322 str2.StripTrailingWhitespace(); 323 values.Append( str2 ); 324 str2 = ""; 325 } 326 continue; 327 } 328 if ( negNum ) { 329 str2 += "-"; 330 negNum = false; 331 } 332 str2 += token; 333 str2 += " "; 334 } 335 if ( str2.Length() ) { 336 str2.StripTrailingWhitespace(); 337 values.Append( str2 ); 338 } 339 } 340 if ( choices.Num() != values.Num() ) { 341 common->Warning( "idChoiceWindow:: gui '%s' window '%s' has value count unequal to choices count", gui->GetSourceFile(), name.c_str()); 342 } 343 latchedVals = choiceVals.c_str(); 344 } 345 } 346 347 void idChoiceWindow::PostParse() { 348 idWindow::PostParse(); 349 UpdateChoicesAndVals(); 350 351 InitVars(); 352 UpdateChoice(); 353 UpdateVars(false); 354 355 flags |= WIN_CANFOCUS; 356 } 357 358 void idChoiceWindow::Draw(int time, float x, float y) { 359 idVec4 color = foreColor; 360 361 UpdateChoicesAndVals(); 362 UpdateChoice(); 363 364 // FIXME: It'd be really cool if textAlign worked, but a lot of the guis have it set wrong because it used to not work 365 textAlign = 0; 366 367 if ( textShadow ) { 368 idStr shadowText = choices[currentChoice]; 369 idRectangle shadowRect = textRect; 370 371 shadowText.RemoveColors(); 372 shadowRect.x += textShadow; 373 shadowRect.y += textShadow; 374 375 dc->DrawText( shadowText, textScale, textAlign, colorBlack, shadowRect, false, -1 ); 376 } 377 378 if ( hover && !noEvents && Contains(gui->CursorX(), gui->CursorY()) ) { 379 color = hoverColor; 380 } else { 381 hover = false; 382 } 383 if ( flags & WIN_FOCUS ) { 384 color = hoverColor; 385 } 386 387 dc->DrawText( choices[currentChoice], textScale, textAlign, color, textRect, false, -1 ); 388 } 389 390 void idChoiceWindow::Activate( bool activate, idStr &act ) { 391 idWindow::Activate( activate, act ); 392 if ( activate ) { 393 // sets the gui state based on the current choice the window contains 394 UpdateChoice(); 395 } 396 }