DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

ListWindow.cpp (16477B)


      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 "SliderWindow.h"
     36 #include "ListWindow.h"
     37 
     38 // Number of pixels above the text that the rect starts
     39 static const int pixelOffset = 3;
     40 
     41 // number of pixels between columns
     42 static const int tabBorder = 4;
     43 
     44 // Time in milliseconds between clicks to register as a double-click
     45 static const int doubleClickSpeed = 300;
     46 
     47 void idListWindow::CommonInit() {
     48 	typed = "";
     49 	typedTime = 0;
     50 	clickTime = 0;
     51 	currentSel.Clear();
     52 	top = 0;
     53 	sizeBias = 0;
     54 	horizontal = false;
     55 	scroller = new (TAG_OLD_UI) idSliderWindow(gui);
     56 	multipleSel = false;
     57 }
     58 
     59 idListWindow::idListWindow(idUserInterfaceLocal *g) : idWindow(g) {
     60 	gui = g;
     61 	CommonInit();
     62 }
     63 
     64 void idListWindow::SetCurrentSel( int sel ) {
     65 	currentSel.Clear();
     66 	currentSel.Append( sel );
     67 }
     68 
     69 void idListWindow::ClearSelection( int sel ) {
     70 	int cur = currentSel.FindIndex( sel );
     71 	if ( cur >= 0 ) {
     72 		currentSel.RemoveIndex( cur );
     73 	}
     74 }
     75 
     76 void idListWindow::AddCurrentSel( int sel ) {
     77 	currentSel.Append( sel );
     78 }
     79 
     80 int idListWindow::GetCurrentSel() {
     81 	return ( currentSel.Num() ) ? currentSel[0] : 0;
     82 }
     83 
     84 bool idListWindow::IsSelected( int index ) {
     85 	return ( currentSel.FindIndex( index ) >= 0 );
     86 }
     87 
     88 const char *idListWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals) {
     89 	// need to call this to allow proper focus and capturing on embedded children
     90 	const char *ret = idWindow::HandleEvent(event, updateVisuals);
     91 
     92 	float vert = GetMaxCharHeight();
     93 	int numVisibleLines = textRect.h / vert;
     94 
     95 	int key = event->evValue;
     96 
     97 	if ( event->evType == SE_KEY ) {
     98 		if ( !event->evValue2 ) {
     99 			// We only care about key down, not up
    100 			return ret;
    101 		}
    102 
    103 		if ( key == K_MOUSE1 || key == K_MOUSE2 ) {
    104 			// If the user clicked in the scroller, then ignore it
    105 			if ( scroller->Contains(gui->CursorX(), gui->CursorY()) ) {
    106 				return ret;
    107 			}
    108 		}
    109 
    110 		if ( ( key == K_ENTER || key == K_KP_ENTER ) ) {
    111 			RunScript( ON_ENTER );
    112 			return cmd;
    113 		}
    114 
    115 		if ( key == K_MWHEELUP ) {
    116 			key = K_UPARROW;
    117 		} else if ( key == K_MWHEELDOWN ) {
    118 			key = K_DOWNARROW;
    119 		}
    120 
    121 		if ( key == K_MOUSE1) {
    122 			if (Contains(gui->CursorX(), gui->CursorY())) {
    123 				int cur = ( int )( ( gui->CursorY() - actualY - pixelOffset ) / vert ) + top;
    124 				if ( cur >= 0 && cur < listItems.Num() ) {
    125 					if ( multipleSel && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) {
    126 						if ( IsSelected( cur ) ) {
    127 							ClearSelection( cur );
    128 						} else {
    129 							AddCurrentSel( cur );
    130 						}
    131 					} else {
    132 						if ( IsSelected( cur ) && ( gui->GetTime() < clickTime + doubleClickSpeed ) ) {
    133 							// Double-click causes ON_ENTER to get run
    134 							RunScript(ON_ENTER);
    135 							return cmd;
    136 						}
    137 						SetCurrentSel( cur );
    138 
    139 						clickTime = gui->GetTime();
    140 					}
    141 				} else {
    142 					SetCurrentSel( listItems.Num() - 1 );
    143 				}
    144 			}
    145 		} else if ( key == K_UPARROW || key == K_PGUP || key == K_DOWNARROW || key == K_PGDN ) {
    146 			int numLines = 1;
    147 
    148 			if ( key == K_PGUP || key == K_PGDN ) {
    149 				numLines = numVisibleLines / 2;
    150 			}
    151 
    152 			if ( key == K_UPARROW || key == K_PGUP ) {
    153 				numLines = -numLines;
    154 			}
    155 
    156 			if ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) {
    157 				top += numLines;
    158 			} else {
    159 				SetCurrentSel( GetCurrentSel() + numLines );
    160 			}
    161 		} else {
    162 			return ret;
    163 		}
    164 	} else if ( event->evType == SE_CHAR ) {
    165 		if ( !idStr::CharIsPrintable(key) ) {
    166 			return ret;
    167 		}
    168 
    169 		if ( gui->GetTime() > typedTime + 1000 ) {
    170 			typed = "";
    171 		}
    172 		typedTime = gui->GetTime();
    173 		typed.Append( key );
    174 
    175 		for ( int i=0; i<listItems.Num(); i++ ) {
    176 			if ( idStr::Icmpn( typed, listItems[i], typed.Length() ) == 0 ) {
    177 				SetCurrentSel( i );
    178 				break;
    179 			}
    180 		}
    181 
    182 	} else {
    183 		return ret;
    184 	}
    185 
    186 	if ( GetCurrentSel() < 0 ) {
    187 		SetCurrentSel( 0 );
    188 	}
    189 
    190 	if ( GetCurrentSel() >= listItems.Num() ) {
    191 		SetCurrentSel( listItems.Num() - 1 );
    192 	}
    193 
    194 	if ( scroller->GetHigh() > 0.0f ) {
    195 		if ( !idKeyInput::IsDown( K_LCTRL ) && !idKeyInput::IsDown( K_RCTRL ) ) {
    196 			if ( top > GetCurrentSel() - 1 ) {
    197 				top = GetCurrentSel() - 1;
    198 			}
    199 			if ( top < GetCurrentSel() - numVisibleLines + 2 ) {
    200 				top = GetCurrentSel() - numVisibleLines + 2;
    201 			}
    202 		}
    203 
    204 		if ( top > listItems.Num() - 2 ) {
    205 			top = listItems.Num() - 2;
    206 		}
    207 		if ( top < 0 ) {
    208 			top = 0;
    209 		}
    210 		scroller->SetValue(top);
    211 	} else {
    212 		top = 0;
    213 		scroller->SetValue(0.0f);
    214 	}
    215 
    216 	if ( key != K_MOUSE1 ) {
    217 		// Send a fake mouse click event so onAction gets run in our parents
    218 		const sysEvent_t ev = sys->GenerateMouseButtonEvent( 1, true );
    219 		idWindow::HandleEvent(&ev, updateVisuals);
    220 	}
    221 
    222 	if ( currentSel.Num() > 0 ) {
    223 		for ( int i = 0; i < currentSel.Num(); i++ ) {
    224 			gui->SetStateInt( va( "%s_sel_%i", listName.c_str(), i ), currentSel[i] );
    225 		}
    226 	} else {
    227 		gui->SetStateInt( va( "%s_sel_0", listName.c_str() ), 0 );
    228 	}
    229 	gui->SetStateInt( va( "%s_numsel", listName.c_str() ), currentSel.Num() );
    230 
    231 	return ret;
    232 }
    233 
    234 
    235 bool idListWindow::ParseInternalVar(const char *_name, idTokenParser *src) {
    236 	if (idStr::Icmp(_name, "horizontal") == 0) {
    237 		horizontal = src->ParseBool();
    238 		return true;
    239 	}
    240 	if (idStr::Icmp(_name, "listname") == 0) {
    241 		ParseString(src, listName);
    242 		return true;
    243 	}
    244 	if (idStr::Icmp(_name, "tabstops") == 0) {
    245 		ParseString(src, tabStopStr);
    246 		return true;
    247 	}
    248 	if (idStr::Icmp(_name, "tabaligns") == 0) {
    249 		ParseString(src, tabAlignStr);
    250 		return true;
    251 	}
    252 	if (idStr::Icmp(_name, "multipleSel") == 0) {
    253 		multipleSel = src->ParseBool();
    254 		return true;
    255 	}
    256 	if(idStr::Icmp(_name, "tabvaligns") == 0) {
    257 		ParseString(src, tabVAlignStr);
    258 		return true;
    259 	}
    260 	if(idStr::Icmp(_name, "tabTypes") == 0) {
    261 		ParseString(src, tabTypeStr);
    262 		return true;
    263 	}
    264 	if(idStr::Icmp(_name, "tabIconSizes") == 0) {
    265 		ParseString(src, tabIconSizeStr);
    266 		return true;
    267 	}
    268 	if(idStr::Icmp(_name, "tabIconVOffset") == 0) {
    269 		ParseString(src, tabIconVOffsetStr);
    270 		return true;
    271 	}
    272 	
    273 	idStr strName = _name;
    274 	if(idStr::Icmp(strName.Left(4), "mtr_") == 0) {
    275 		idStr matName;
    276 		const idMaterial* mat;
    277 
    278 		ParseString(src, matName);
    279 		mat = declManager->FindMaterial(matName);
    280 		if ( mat != NULL && !mat->TestMaterialFlag( MF_DEFAULTED ) ) {
    281 			mat->SetSort(SS_GUI );
    282 		}
    283 		iconMaterials.Set(_name, mat);
    284 		return true;
    285 	}
    286 
    287 	return idWindow::ParseInternalVar(_name, src);
    288 }
    289 
    290 idWinVar *idListWindow::GetWinVarByName(const char *_name, bool fixup, drawWin_t** owner) {
    291 	return idWindow::GetWinVarByName(_name, fixup, owner);
    292 }
    293 
    294 void idListWindow::PostParse() {
    295 	idWindow::PostParse();
    296 
    297 	InitScroller(horizontal);
    298 
    299 	idList<int> tabStops;
    300 	idList<int> tabAligns;
    301 	if (tabStopStr.Length()) {
    302 		idParser src(tabStopStr, tabStopStr.Length(), "tabstops", LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS);
    303 		idToken tok;
    304 		while (src.ReadToken(&tok)) {
    305 			if (tok == ",") {
    306 				continue;
    307 			}
    308 			tabStops.Append(atoi(tok));
    309 		}
    310 	}
    311 	if (tabAlignStr.Length()) {
    312 		idParser src(tabAlignStr, tabAlignStr.Length(), "tabaligns", LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS);
    313 		idToken tok;
    314 		while (src.ReadToken(&tok)) {
    315 			if (tok == ",") {
    316 				continue;
    317 			}
    318 			tabAligns.Append(atoi(tok));
    319 		}
    320 	}
    321 	idList<int> tabVAligns;
    322 	if (tabVAlignStr.Length()) {
    323 		idParser src(tabVAlignStr, tabVAlignStr.Length(), "tabvaligns", LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS);
    324 		idToken tok;
    325 		while (src.ReadToken(&tok)) {
    326 			if (tok == ",") {
    327 				continue;
    328 			}
    329 			tabVAligns.Append(atoi(tok));
    330 		}
    331 	}
    332 
    333 	idList<int> tabTypes;
    334 	if (tabTypeStr.Length()) {
    335 		idParser src(tabTypeStr, tabTypeStr.Length(), "tabtypes", LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS);
    336 		idToken tok;
    337 		while (src.ReadToken(&tok)) {
    338 			if (tok == ",") {
    339 				continue;
    340 			}
    341 			tabTypes.Append(atoi(tok));
    342 		}
    343 	}
    344 	idList<idVec2> tabSizes;
    345 	if (tabIconSizeStr.Length()) {
    346 		idParser src(tabIconSizeStr, tabIconSizeStr.Length(), "tabiconsizes", LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS);
    347 		idToken tok;
    348 		while (src.ReadToken(&tok)) {
    349 			if (tok == ",") {
    350 				continue;
    351 			}
    352 			idVec2 size;
    353 			size.x = atoi(tok);
    354 			
    355 			src.ReadToken(&tok);	//","
    356 			src.ReadToken(&tok);
    357 
    358 			size.y = atoi(tok);
    359 			tabSizes.Append(size);
    360 		}
    361 	}
    362 
    363 	idList<float> tabIconVOffsets;
    364 	if (tabIconVOffsetStr.Length()) {
    365 		idParser src(tabIconVOffsetStr, tabIconVOffsetStr.Length(), "tabiconvoffsets", LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS);
    366 		idToken tok;
    367 		while (src.ReadToken(&tok)) {
    368 			if (tok == ",") {
    369 				continue;
    370 			}
    371 			tabIconVOffsets.Append(atof(tok));
    372 		}
    373 	}
    374 
    375 	int c = tabStops.Num();
    376 	bool doAligns = (tabAligns.Num() == tabStops.Num());
    377 	for (int i = 0; i < c; i++) {
    378 		idTabRect r;
    379 		r.x = tabStops[i];
    380 		r.w = (i < c - 1) ? tabStops[i+1] - r.x - tabBorder : -1;
    381 		r.align = (doAligns) ? tabAligns[i] : 0;
    382 		if(tabVAligns.Num() > 0) {
    383 			r.valign = tabVAligns[i];
    384 		} else {
    385 			r.valign = 0;
    386 		}
    387 		if(tabTypes.Num() > 0) {
    388 			r.type = tabTypes[i];
    389 		} else {
    390 			r.type = TAB_TYPE_TEXT;
    391 		}
    392 		if(tabSizes.Num() > 0) {
    393 			r.iconSize = tabSizes[i];
    394 		} else {
    395 			r.iconSize.Zero();
    396 		}
    397 		if(tabIconVOffsets.Num() > 0 ) {
    398 			r.iconVOffset = tabIconVOffsets[i];
    399 		} else {
    400 			r.iconVOffset = 0;
    401 		}
    402 		tabInfo.Append(r);
    403 	}
    404 	flags |= WIN_CANFOCUS;
    405 }
    406 
    407 /*
    408 ================
    409 idListWindow::InitScroller
    410 
    411 This is the same as in idEditWindow
    412 ================
    413 */
    414 void idListWindow::InitScroller( bool horizontal )
    415 {
    416 	const char *thumbImage = "guis/assets/scrollbar_thumb.tga";
    417 	const char *barImage = "guis/assets/scrollbarv.tga";
    418 	const char *scrollerName = "_scrollerWinV";
    419 
    420 	if (horizontal) {
    421 		barImage = "guis/assets/scrollbarh.tga";
    422 		scrollerName = "_scrollerWinH";
    423 	}
    424 
    425 	const idMaterial *mat = declManager->FindMaterial( barImage );
    426 	mat->SetSort( SS_GUI );
    427 	sizeBias = mat->GetImageWidth();
    428 
    429 	idRectangle scrollRect;
    430 	if (horizontal) {
    431 		sizeBias = mat->GetImageHeight();
    432 		scrollRect.x = 0;
    433 		scrollRect.y = (clientRect.h - sizeBias);
    434 		scrollRect.w = clientRect.w;
    435 		scrollRect.h = sizeBias;
    436 	} else {
    437 		scrollRect.x = (clientRect.w - sizeBias);
    438 		scrollRect.y = 0;
    439 		scrollRect.w = sizeBias;
    440 		scrollRect.h = clientRect.h;
    441 	}
    442 
    443 	scroller->InitWithDefaults(scrollerName, scrollRect, foreColor, matColor, mat->GetName(), thumbImage, !horizontal, true);
    444 	InsertChild(scroller, NULL);
    445 	scroller->SetBuddy(this);
    446 }
    447 
    448 void idListWindow::Draw(int time, float x, float y) {
    449 	idVec4 color;
    450 	idStr work;
    451 	int count = listItems.Num();
    452 	idRectangle rect = textRect;
    453 	float scale = textScale;
    454 	float lineHeight = GetMaxCharHeight();
    455 
    456 	float bottom = textRect.Bottom();
    457 	float width = textRect.w;
    458 
    459 	if ( scroller->GetHigh() > 0.0f ) {
    460 		if ( horizontal ) {
    461 			bottom -= sizeBias;
    462 		} else {
    463 			width -= sizeBias;
    464 			rect.w = width;
    465 		}
    466 	}
    467 
    468 	if ( noEvents || !Contains(gui->CursorX(), gui->CursorY()) ) {
    469 		hover = false;
    470 	}
    471 
    472 	for (int i = top; i < count; i++) {
    473 		if ( IsSelected( i ) ) {
    474 			rect.h = lineHeight;
    475 			dc->DrawFilledRect(rect.x, rect.y + pixelOffset, rect.w, rect.h, borderColor);
    476 			if ( flags & WIN_FOCUS ) {
    477 				idVec4 color = borderColor;
    478 				color.w = 1.0f;
    479 				dc->DrawRect(rect.x, rect.y + pixelOffset, rect.w, rect.h, 1.0f, color );
    480 			}
    481 		}
    482 		rect.y ++;
    483 		rect.h = lineHeight - 1;
    484 		if ( hover && !noEvents && Contains(rect, gui->CursorX(), gui->CursorY()) ) {
    485 			color = hoverColor;
    486 		} else {
    487 			color = foreColor;
    488 		}
    489 		rect.h = lineHeight + pixelOffset;
    490 		rect.y --;
    491 
    492 		if ( tabInfo.Num() > 0 ) {
    493 			int start = 0;
    494 			int tab = 0;
    495 			int stop = listItems[i].Find('\t', 0);
    496 			while ( start < listItems[i].Length() ) {
    497 				if ( tab >= tabInfo.Num() ) {
    498 					common->Warning( "idListWindow::Draw: gui '%s' window '%s' tabInfo.Num() exceeded", gui->GetSourceFile(), name.c_str() );
    499 					break;
    500 				}
    501 				listItems[i].Mid(start, stop - start, work);
    502 
    503 				rect.x = textRect.x + tabInfo[tab].x;
    504 				rect.w = (tabInfo[tab].w == -1) ? width - tabInfo[tab].x : tabInfo[tab].w;
    505 				dc->PushClipRect( rect );
    506 
    507 				if ( tabInfo[tab].type == TAB_TYPE_TEXT ) {
    508 					dc->DrawText(work, scale, tabInfo[tab].align, color, rect, false, -1);
    509 				} else if (tabInfo[tab].type == TAB_TYPE_ICON) {
    510 					
    511 					const idMaterial	**hashMat;
    512 					const idMaterial	*iconMat;
    513 
    514 					// leaving the icon name empty doesn't draw anything
    515 					if ( work[0] != '\0' ) {
    516 
    517 						if ( iconMaterials.Get(work, &hashMat) == false ) {
    518 							iconMat = declManager->FindMaterial("_default");
    519 						} else {
    520 							iconMat = *hashMat;
    521 						}
    522 
    523 						idRectangle iconRect;
    524 						iconRect.w = tabInfo[tab].iconSize.x;
    525 						iconRect.h = tabInfo[tab].iconSize.y;
    526 
    527 						if(tabInfo[tab].align == idDeviceContext::ALIGN_LEFT) {
    528 							iconRect.x = rect.x;
    529 						} else if (tabInfo[tab].align == idDeviceContext::ALIGN_CENTER) {
    530 							iconRect.x = rect.x + rect.w/2.0f - iconRect.w/2.0f;
    531 						} else if (tabInfo[tab].align == idDeviceContext::ALIGN_RIGHT) {
    532 							iconRect.x  = rect.x + rect.w - iconRect.w;
    533 						}
    534 
    535 						if(tabInfo[tab].valign == 0) { //Top
    536 							iconRect.y = rect.y + tabInfo[tab].iconVOffset;
    537 						} else if(tabInfo[tab].valign == 1) { //Center
    538 							iconRect.y = rect.y + rect.h/2.0f - iconRect.h/2.0f + tabInfo[tab].iconVOffset;
    539 						} else if(tabInfo[tab].valign == 2) { //Bottom
    540 							iconRect.y = rect.y + rect.h - iconRect.h + tabInfo[tab].iconVOffset;
    541 						}
    542 
    543 						dc->DrawMaterial(iconRect.x, iconRect.y, iconRect.w, iconRect.h, iconMat, idVec4(1.0f,1.0f,1.0f,1.0f), 1.0f, 1.0f);
    544 
    545 					}
    546 				}
    547 
    548 				dc->PopClipRect();
    549 
    550 				start = stop + 1;
    551 				stop = listItems[i].Find('\t', start);
    552 				if ( stop < 0 ) {
    553 					stop = listItems[i].Length();
    554 				}
    555 				tab++;
    556 			}
    557 			rect.x = textRect.x;
    558 			rect.w = width;
    559 		} else {
    560 			dc->DrawText(listItems[i], scale, 0, color, rect, false, -1);
    561 		}
    562 		rect.y += lineHeight;
    563 		if ( rect.y > bottom ) {
    564 			break;
    565 		}
    566 	}
    567 }
    568 
    569 void idListWindow::Activate(bool activate, idStr &act) {
    570 	idWindow::Activate(activate, act);
    571 
    572 	if ( activate ) {
    573 		UpdateList();
    574 	}
    575 }
    576 
    577 void idListWindow::HandleBuddyUpdate(idWindow *buddy) {
    578 	top = scroller->GetValue();
    579 }
    580 
    581 void idListWindow::UpdateList() {
    582 	idStr str, strName;
    583 	listItems.Clear();
    584 	for (int i = 0; i < MAX_LIST_ITEMS; i++) {
    585 		if (gui->State().GetString( va("%s_item_%i", listName.c_str(), i), "", str) ) {
    586 			if ( str.Length() ) {
    587 				listItems.Append(str);
    588 			}
    589 		} else {
    590 			break;
    591 		}
    592 	}
    593 	float vert = GetMaxCharHeight();
    594 	int fit = textRect.h / vert;
    595 	if ( listItems.Num() < fit ) {
    596 		scroller->SetRange(0.0f, 0.0f, 1.0f);
    597 	} else {
    598 		scroller->SetRange(0.0f, (listItems.Num() - fit) + 1.0f, 1.0f);
    599 	}
    600 
    601 	SetCurrentSel( gui->State().GetInt( va( "%s_sel_0", listName.c_str() ) ) );
    602 
    603 	float value = scroller->GetValue();
    604 	if ( value > listItems.Num() - 1 ) {
    605 		value = listItems.Num() - 1;
    606 	}
    607 	if ( value < 0.0f ) {
    608 		value = 0.0f;
    609 	}
    610 	scroller->SetValue(value);
    611 	top = value;
    612 
    613 	typedTime = 0;
    614 	clickTime = 0;
    615 	typed = "";
    616 }
    617 
    618 void idListWindow::StateChanged( bool redraw ) {
    619 	UpdateList();
    620 }
    621