2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "DeviceContext.h"
34 #include "UserInterfaceLocal.h"
35 #include "SliderWindow.h"
36 #include "EditWindow.h"
39 bool idEditWindow::ParseInternalVar( const char *_name, idParser *src ) {
40 if ( idStr::Icmp( _name, "maxchars" ) == 0) {
41 maxChars = src->ParseInt();
44 if ( idStr::Icmp( _name, "numeric" ) == 0) {
45 numeric = src->ParseBool();
48 if ( idStr::Icmp( _name, "wrap" ) == 0) {
49 wrap = src->ParseBool();
52 if ( idStr::Icmp( _name, "readonly" ) == 0) {
53 readonly = src->ParseBool();
56 if ( idStr::Icmp( _name, "forceScroll" ) == 0) {
57 forceScroll = src->ParseBool();
60 if ( idStr::Icmp( _name, "source" ) == 0) {
61 ParseString( src, sourceFile );
64 if ( idStr::Icmp( _name, "password" ) == 0 ) {
65 password = src->ParseBool();
68 if ( idStr::Icmp( _name, "cvarMax" ) == 0) {
69 cvarMax = src->ParseInt();
73 return idWindow::ParseInternalVar( _name, src );
76 idWinVar *idEditWindow::GetWinVarByName( const char *_name, bool fixup, drawWin_t** owner ) {
77 if ( idStr::Icmp( _name, "cvar" ) == 0 ) {
80 if ( idStr::Icmp( _name, "password" ) == 0 ) {
83 if ( idStr::Icmp( _name, "liveUpdate" ) == 0 ) {
86 if ( idStr::Icmp( _name, "cvarGroup" ) == 0 ) {
89 return idWindow::GetWinVarByName( _name, fixup, owner );
92 void idEditWindow::CommonInit() {
110 scroller = new idSliderWindow(dc, gui);
114 idEditWindow::idEditWindow( idDeviceContext *d, idUserInterfaceLocal *g ) : idWindow(d, g) {
120 idEditWindow::idEditWindow( idUserInterfaceLocal *g ) : idWindow(g) {
125 idEditWindow::~idEditWindow() {
129 void idEditWindow::GainFocus() {
130 cursorPos = text.Length();
131 EnsureCursorVisible();
134 void idEditWindow::Draw( int time, float x, float y ) {
135 idVec4 color = foreColor;
139 int len = text.Length();
140 if ( len != lastTextLength ) {
141 scroller->SetValue( 0.0f );
142 EnsureCursorVisible();
143 lastTextLength = len;
145 float scale = textScale;
150 const char* temp = text;
151 for ( ; *temp; temp++ ) {
159 if ( cursorPos > len ) {
163 idRectangle rect = textRect;
165 rect.x -= paintOffset;
166 rect.w += paintOffset;
168 if ( wrap && scroller->GetHigh() > 0.0f ) {
169 float lineHeight = GetMaxCharHeight( ) + 5;
170 rect.y -= scroller->GetValue() * lineHeight;
172 rect.h = ( breaks.Num() + 1 ) * lineHeight;
175 if ( hover && !noEvents && Contains(gui->CursorX(), gui->CursorY()) ) {
180 if ( flags & WIN_FOCUS ) {
184 dc->DrawText( buffer, scale, 0, color, rect, wrap, (flags & WIN_FOCUS) ? cursorPos : -1);
189 idEditWindow::HandleEvent
192 const char *idEditWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals) {
193 static char buffer[ MAX_EDITFIELD ];
194 const char *ret = "";
197 // need to call this to allow proper focus and capturing on embedded children
198 ret = idWindow::HandleEvent( event, updateVisuals );
204 if ( ( event->evType != SE_CHAR && event->evType != SE_KEY ) ) {
208 idStr::Copynz( buffer, text.c_str(), sizeof( buffer ) );
209 int key = event->evValue;
210 int len = text.Length();
212 if ( event->evType == SE_CHAR ) {
213 if ( event->evValue == Sys_GetConsoleKey( false ) || event->evValue == Sys_GetConsoleKey( true ) ) {
217 if ( updateVisuals ) {
218 *updateVisuals = true;
221 if ( maxChars && len > maxChars ) {
225 if ( ( key == K_ENTER || key == K_KP_ENTER ) && event->evValue2 ) {
226 RunScript( ON_ACTION );
227 RunScript( ON_ENTER );
231 if ( key == K_ESCAPE ) {
240 if ( key == 'h' - 'a' + 1 || key == K_BACKSPACE ) { // ctrl-h is backspace
241 if ( cursorPos > 0 ) {
242 if ( cursorPos >= len ) {
246 memmove( &buffer[ cursorPos - 1 ], &buffer[ cursorPos ], len + 1 - cursorPos);
252 RunScript( ON_ACTION );
259 // ignore any non printable chars (except enter when wrap is enabled)
261 if ( wrap && (key == K_ENTER || key == K_KP_ENTER) ) {
262 } else if ( !idStr::CharIsPrintable( key ) ) {
267 if ( ( key < '0' || key > '9' ) && key != '.' ) {
272 if ( dc->GetOverStrike() ) {
273 if ( maxChars && cursorPos >= maxChars ) {
277 if ( ( len == MAX_EDITFIELD - 1 ) || ( maxChars && len >= maxChars ) ) {
280 memmove( &buffer[ cursorPos + 1 ], &buffer[ cursorPos ], len + 1 - cursorPos );
283 buffer[ cursorPos ] = key;
287 RunScript( ON_ACTION );
289 if ( cursorPos < len + 1 ) {
292 EnsureCursorVisible();
294 } else if ( event->evType == SE_KEY && event->evValue2 ) {
296 if ( updateVisuals ) {
297 *updateVisuals = true;
300 if ( key == K_DEL ) {
304 if ( cursorPos < len ) {
305 memmove( &buffer[cursorPos], &buffer[cursorPos + 1], len - cursorPos);
308 RunScript( ON_ACTION );
313 if ( key == K_RIGHTARROW ) {
314 if ( cursorPos < len ) {
315 if ( idKeyInput::IsDown( K_CTRL ) ) {
317 while( ( cursorPos < len ) && ( buffer[ cursorPos ] != ' ' ) ) {
321 while( ( cursorPos < len ) && ( buffer[ cursorPos ] == ' ' ) ) {
325 if ( cursorPos < len ) {
331 EnsureCursorVisible();
336 if ( key == K_LEFTARROW ) {
337 if ( idKeyInput::IsDown( K_CTRL ) ) {
338 // skip to previous word
339 while( ( cursorPos > 0 ) && ( buffer[ cursorPos - 1 ] == ' ' ) ) {
343 while( ( cursorPos > 0 ) && ( buffer[ cursorPos - 1 ] != ' ' ) ) {
347 if ( cursorPos > 0 ) {
352 EnsureCursorVisible();
357 if ( key == K_HOME ) {
358 if ( idKeyInput::IsDown( K_CTRL ) || cursorLine <= 0 || ( cursorLine >= breaks.Num() ) ) {
361 cursorPos = breaks[cursorLine];
363 EnsureCursorVisible();
367 if ( key == K_END ) {
368 if ( idKeyInput::IsDown( K_CTRL ) || (cursorLine < -1) || ( cursorLine >= breaks.Num() - 1 ) ) {
371 cursorPos = breaks[cursorLine + 1] - 1;
373 EnsureCursorVisible();
377 if ( key == K_INS ) {
379 dc->SetOverStrike( !dc->GetOverStrike() );
384 if ( key == K_DOWNARROW ) {
385 if ( idKeyInput::IsDown( K_CTRL ) ) {
386 scroller->SetValue( scroller->GetValue() + 1.0f );
388 if ( cursorLine < breaks.Num() - 1 ) {
389 int offset = cursorPos - breaks[cursorLine];
390 cursorPos = breaks[cursorLine + 1] + offset;
391 EnsureCursorVisible();
396 if (key == K_UPARROW ) {
397 if ( idKeyInput::IsDown( K_CTRL ) ) {
398 scroller->SetValue( scroller->GetValue() - 1.0f );
400 if ( cursorLine > 0 ) {
401 int offset = cursorPos - breaks[cursorLine];
402 cursorPos = breaks[cursorLine - 1] + offset;
403 EnsureCursorVisible();
408 if ( key == K_ENTER || key == K_KP_ENTER ) {
409 RunScript( ON_ACTION );
410 RunScript( ON_ENTER );
414 if ( key == K_ESCAPE ) {
419 } else if ( event->evType == SE_KEY && !event->evValue2 ) {
420 if ( key == K_ENTER || key == K_KP_ENTER ) {
421 RunScript( ON_ENTERRELEASE );
424 RunScript( ON_ACTIONRELEASE );
431 void idEditWindow::PostParse() {
432 idWindow::PostParse();
434 if ( maxChars == 0 ) {
437 if ( sourceFile.Length() ) {
439 fileSystem->ReadFile( sourceFile, &buffer );
440 text = (char *) buffer;
441 fileSystem->FreeFile( buffer );
447 EnsureCursorVisible();
449 flags |= WIN_CANFOCUS;
454 idEditWindow::InitScroller
456 This is the same as in idListWindow
459 void idEditWindow::InitScroller( bool horizontal )
461 const char *thumbImage = "guis/assets/scrollbar_thumb.tga";
462 const char *barImage = "guis/assets/scrollbarv.tga";
463 const char *scrollerName = "_scrollerWinV";
466 barImage = "guis/assets/scrollbarh.tga";
467 scrollerName = "_scrollerWinH";
470 const idMaterial *mat = declManager->FindMaterial( barImage );
471 mat->SetSort( SS_GUI );
472 sizeBias = mat->GetImageWidth();
474 idRectangle scrollRect;
476 sizeBias = mat->GetImageHeight();
478 scrollRect.y = (clientRect.h - sizeBias);
479 scrollRect.w = clientRect.w;
480 scrollRect.h = sizeBias;
482 scrollRect.x = (clientRect.w - sizeBias);
484 scrollRect.w = sizeBias;
485 scrollRect.h = clientRect.h;
488 scroller->InitWithDefaults(scrollerName, scrollRect, foreColor, matColor, mat->GetName(), thumbImage, !horizontal, true);
489 InsertChild(scroller, NULL);
490 scroller->SetBuddy(this);
493 void idEditWindow::HandleBuddyUpdate( idWindow *buddy ) {
496 void idEditWindow::EnsureCursorVisible()
500 } else if ( maxChars == 1 ) {
512 cursorX = cursorPos * dc->CharWidth( '*', textScale );
515 while ( i < text.Length() && i < cursorPos ) {
516 if ( idStr::IsColor( &text[i] ) ) {
519 cursorX += dc->CharWidth( text[i], textScale );
524 int maxWidth = GetMaxCharWidth( );
525 int left = cursorX - maxWidth;
526 int right = ( cursorX - textRect.w ) + maxWidth;
528 if ( paintOffset > left ) {
529 // When we go past the left side, we want the text to jump 6 characters
530 paintOffset = left - maxWidth * 6;
532 if ( paintOffset < right) {
535 if ( paintOffset < 0 ) {
538 scroller->SetRange(0.0f, 0.0f, 1.0f);
544 idRectangle rect = textRect;
546 dc->DrawText(text, textScale, textAlign, colorWhite, rect, true, (flags & WIN_FOCUS) ? cursorPos : -1, true, &breaks );
548 int fit = textRect.h / (GetMaxCharHeight() + 5);
549 if ( fit < breaks.Num() + 1 ) {
550 scroller->SetRange(0, breaks.Num() + 1 - fit, 1);
552 // The text fits completely in the box
553 scroller->SetRange(0.0f, 0.0f, 1.0f);
557 scroller->SetValue( breaks.Num() - fit );
558 } else if ( readonly ) {
561 for ( int i = 1; i < breaks.Num(); i++ ) {
562 if ( cursorPos >= breaks[i] ) {
568 int topLine = idMath::FtoiFast( scroller->GetValue() );
569 if ( cursorLine < topLine ) {
570 scroller->SetValue( cursorLine );
571 } else if ( cursorLine >= topLine + fit) {
572 scroller->SetValue( ( cursorLine - fit ) + 1 );
578 void idEditWindow::Activate(bool activate, idStr &act) {
579 idWindow::Activate(activate, act);
581 UpdateCvar( true, true );
582 EnsureCursorVisible();
588 idEditWindow::InitCvar
591 void idEditWindow::InitCvar( ) {
592 if ( cvarStr[0] == '\0' ) {
593 if ( text.GetName() == NULL ) {
594 common->Warning( "idEditWindow::InitCvar: gui '%s' window '%s' has an empty cvar string", gui->GetSourceFile(), name.c_str() );
600 cvar = cvarSystem->Find( cvarStr );
602 common->Warning( "idEditWindow::InitCvar: gui '%s' window '%s' references undefined cvar '%s'", gui->GetSourceFile(), name.c_str(), cvarStr.c_str() );
609 idEditWindow::UpdateCvar
612 void idEditWindow::UpdateCvar( bool read, bool force ) {
613 if ( force || liveUpdate ) {
616 text = cvar->GetString();
618 cvar->SetString( text );
619 if ( cvarMax && ( cvar->GetInteger() > cvarMax ) ) {
620 cvar->SetInteger( cvarMax );
629 idEditWindow::RunNamedEvent
632 void idEditWindow::RunNamedEvent( const char* eventName ) {
635 if ( !idStr::Cmpn( eventName, "cvar read ", 10 ) ) {
637 group = event.Mid( 10, event.Length() - 10 );
638 if ( !group.Cmp( cvarGroup ) ) {
639 UpdateCvar( true, true );
641 } else if ( !idStr::Cmpn( eventName, "cvar write ", 11 ) ) {
643 group = event.Mid( 11, event.Length() - 11 );
644 if ( !group.Cmp( cvarGroup ) ) {
645 UpdateCvar( false, true );