]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/ui/EditWindow.cpp
hello world
[icculus/iodoom3.git] / neo / ui / EditWindow.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
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 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "DeviceContext.h"
33 #include "Window.h"
34 #include "UserInterfaceLocal.h"
35 #include "SliderWindow.h"
36 #include "EditWindow.h"
37
38
39 bool idEditWindow::ParseInternalVar( const char *_name, idParser *src ) {
40         if ( idStr::Icmp( _name, "maxchars" ) == 0) {
41                 maxChars = src->ParseInt();
42                 return true;
43         }
44         if ( idStr::Icmp( _name, "numeric" ) == 0) {
45                 numeric = src->ParseBool();
46                 return true;
47         }
48         if ( idStr::Icmp( _name, "wrap" ) == 0) {
49                 wrap = src->ParseBool();
50                 return true;
51         }
52         if ( idStr::Icmp( _name, "readonly" ) == 0) {
53                 readonly = src->ParseBool();
54                 return true;
55         }
56         if ( idStr::Icmp( _name, "forceScroll" ) == 0) {
57                 forceScroll = src->ParseBool();
58                 return true;
59         }
60         if ( idStr::Icmp( _name, "source" ) == 0) {
61                 ParseString( src, sourceFile );
62                 return true;
63         }
64         if ( idStr::Icmp( _name, "password" ) == 0 ) { 
65                 password = src->ParseBool();
66                 return true;
67         }
68         if ( idStr::Icmp( _name, "cvarMax" ) == 0) {
69                 cvarMax = src->ParseInt();
70                 return true;
71         }
72
73         return idWindow::ParseInternalVar( _name, src );
74 }
75
76 idWinVar *idEditWindow::GetWinVarByName( const char *_name, bool fixup, drawWin_t** owner ) {
77         if ( idStr::Icmp( _name, "cvar" ) == 0 ) {
78                 return &cvarStr;
79         }
80         if ( idStr::Icmp( _name, "password" ) == 0 ) {
81                 return &password;
82         }
83         if ( idStr::Icmp( _name, "liveUpdate" ) == 0 ) {
84                 return &liveUpdate;
85         }
86         if ( idStr::Icmp( _name, "cvarGroup" ) == 0 ) {
87                 return &cvarGroup;
88         }
89         return idWindow::GetWinVarByName( _name, fixup, owner );
90 }
91
92 void idEditWindow::CommonInit() {
93         maxChars = 128;
94         numeric = false;
95         paintOffset = 0;
96         cursorPos = 0;
97         cursorLine = 0;
98         cvarMax = 0;
99         wrap = false;
100         sourceFile = "";
101         scroller = NULL;
102         sizeBias = 0;
103         lastTextLength = 0;
104         forceScroll = false;
105         password = false;
106         cvar = NULL;
107         liveUpdate = true;
108         readonly = false;
109
110         scroller = new idSliderWindow(dc, gui);
111 }
112
113
114 idEditWindow::idEditWindow( idDeviceContext *d, idUserInterfaceLocal *g ) : idWindow(d, g) {
115         dc = d;
116         gui = g;
117         CommonInit();
118 }
119
120 idEditWindow::idEditWindow( idUserInterfaceLocal *g ) : idWindow(g) {
121         gui = g;
122         CommonInit();
123 }
124
125 idEditWindow::~idEditWindow() {
126
127 }
128
129 void idEditWindow::GainFocus() {
130         cursorPos = text.Length();
131         EnsureCursorVisible();
132 }
133
134 void idEditWindow::Draw( int time, float x, float y ) {
135         idVec4 color = foreColor;
136
137         UpdateCvar( true );
138
139         int len = text.Length();
140         if ( len != lastTextLength ) {
141                 scroller->SetValue( 0.0f );
142                 EnsureCursorVisible();
143                 lastTextLength = len;
144         }
145         float scale = textScale;
146
147         idStr           pass;
148         const char* buffer;
149         if ( password ) {               
150                 const char* temp = text;
151                 for ( ; *temp; temp++ ) {
152                         pass += "*";
153                 }
154                 buffer = pass;
155         } else {
156                 buffer = text;
157         }
158
159         if ( cursorPos > len ) {
160                 cursorPos = len;
161         }
162
163         idRectangle rect = textRect;
164
165         rect.x -= paintOffset;
166         rect.w += paintOffset;
167
168         if ( wrap && scroller->GetHigh() > 0.0f ) {
169                 float lineHeight = GetMaxCharHeight( ) + 5;
170                 rect.y -= scroller->GetValue() * lineHeight;
171                 rect.w -= sizeBias;
172                 rect.h = ( breaks.Num() + 1 ) * lineHeight;
173         }
174
175         if ( hover && !noEvents && Contains(gui->CursorX(), gui->CursorY()) ) {
176                 color = hoverColor;
177         } else {
178                 hover = false;
179         }
180         if ( flags & WIN_FOCUS ) {
181                 color = hoverColor;
182         }
183
184         dc->DrawText( buffer, scale, 0, color, rect, wrap, (flags & WIN_FOCUS) ? cursorPos : -1);
185 }
186
187 /*
188 =============
189 idEditWindow::HandleEvent
190 =============
191 */
192 const char *idEditWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals) {
193         static char buffer[ MAX_EDITFIELD ];
194         const char *ret = "";
195
196         if ( wrap ) {
197                 // need to call this to allow proper focus and capturing on embedded children
198                 ret = idWindow::HandleEvent( event, updateVisuals );
199                 if ( ret && *ret ) {
200                         return ret;
201                 }
202         }
203
204         if ( ( event->evType != SE_CHAR && event->evType != SE_KEY ) ) {
205                 return ret;
206         }
207
208         idStr::Copynz( buffer, text.c_str(), sizeof( buffer ) );
209         int key = event->evValue;
210         int len = text.Length();
211
212         if ( event->evType == SE_CHAR ) {
213                 if ( event->evValue == Sys_GetConsoleKey( false ) || event->evValue == Sys_GetConsoleKey( true ) ) {
214                         return "";
215                 }
216
217                 if ( updateVisuals ) {
218                         *updateVisuals = true;
219                 }
220
221                 if ( maxChars && len > maxChars ) {
222                         len = maxChars;
223                 }
224         
225                 if ( ( key == K_ENTER || key == K_KP_ENTER ) && event->evValue2 ) {
226                         RunScript( ON_ACTION );
227                         RunScript( ON_ENTER );
228                         return cmd;
229                 }
230
231                 if ( key == K_ESCAPE ) {
232                         RunScript( ON_ESC );
233                         return cmd;
234                 }
235
236                 if ( readonly ) {
237                         return "";
238                 }
239
240                 if ( key == 'h' - 'a' + 1 || key == K_BACKSPACE ) {     // ctrl-h is backspace
241                         if ( cursorPos > 0 ) {
242                                 if ( cursorPos >= len ) {
243                                         buffer[len - 1] = 0;
244                                         cursorPos = len - 1;
245                                 } else {
246                                         memmove( &buffer[ cursorPos - 1 ], &buffer[ cursorPos ], len + 1 - cursorPos);
247                                         cursorPos--;
248                                 }
249
250                                 text = buffer;
251                                 UpdateCvar( false );
252                                 RunScript( ON_ACTION );
253                         }
254
255                         return "";
256                 }
257
258                 //
259                 // ignore any non printable chars (except enter when wrap is enabled)
260                 //
261                 if ( wrap && (key == K_ENTER || key == K_KP_ENTER) ) {
262                 } else if ( !idStr::CharIsPrintable( key ) ) {
263                         return "";
264                 }
265
266                 if ( numeric ) {
267                         if ( ( key < '0' || key > '9' ) && key != '.' ) {
268                         return "";
269                         }
270                 }
271
272                 if ( dc->GetOverStrike() ) {
273                         if ( maxChars && cursorPos >= maxChars ) {
274                         return "";
275                         }
276                 } else {
277                         if ( ( len == MAX_EDITFIELD - 1 ) || ( maxChars && len >= maxChars ) ) {
278                         return "";
279                         }
280                         memmove( &buffer[ cursorPos + 1 ], &buffer[ cursorPos ], len + 1 - cursorPos );
281                 }
282
283                 buffer[ cursorPos ] = key;
284
285                 text = buffer;
286                 UpdateCvar( false );
287                 RunScript( ON_ACTION );
288
289                 if ( cursorPos < len + 1 ) {
290                         cursorPos++;
291                 }
292                 EnsureCursorVisible();
293
294         } else if ( event->evType == SE_KEY && event->evValue2 ) {
295
296                 if ( updateVisuals ) {
297                         *updateVisuals = true;
298                 }
299
300                 if ( key == K_DEL ) {
301                         if ( readonly ) {
302                                 return ret;
303                         }
304                         if ( cursorPos < len ) {
305                                 memmove( &buffer[cursorPos], &buffer[cursorPos + 1], len - cursorPos);
306                                 text = buffer;
307                                 UpdateCvar( false );
308                                 RunScript( ON_ACTION );
309                         }
310                         return ret;
311                 }
312
313                 if ( key == K_RIGHTARROW )  {
314                         if ( cursorPos < len ) {
315                                 if ( idKeyInput::IsDown( K_CTRL ) ) {
316                                         // skip to next word
317                                         while( ( cursorPos < len ) && ( buffer[ cursorPos ] != ' ' ) ) {
318                                                 cursorPos++;
319                                         }
320
321                                         while( ( cursorPos < len ) && ( buffer[ cursorPos ] == ' ' ) ) {
322                                                 cursorPos++;
323                                         }
324                                 } else {
325                                         if ( cursorPos < len ) {
326                                                 cursorPos++;
327                                         }
328                                 }
329                         } 
330
331                         EnsureCursorVisible();
332
333                         return ret;
334                 }
335
336                 if ( key == K_LEFTARROW ) {
337                         if ( idKeyInput::IsDown( K_CTRL ) ) {
338                                 // skip to previous word
339                                 while( ( cursorPos > 0 ) && ( buffer[ cursorPos - 1 ] == ' ' ) ) {
340                                         cursorPos--;
341                                 }
342
343                                 while( ( cursorPos > 0 ) && ( buffer[ cursorPos - 1 ] != ' ' ) ) {
344                                         cursorPos--;
345                                 }
346                         } else {
347                                 if ( cursorPos > 0 ) {
348                                         cursorPos--;
349                                 }
350                         }
351
352                         EnsureCursorVisible();
353
354                         return ret;
355                 }
356
357                 if ( key == K_HOME ) {
358                         if ( idKeyInput::IsDown( K_CTRL ) || cursorLine <= 0 || ( cursorLine >= breaks.Num() ) ) {
359                 cursorPos = 0;
360                         } else {
361                                 cursorPos = breaks[cursorLine];
362                         }
363                         EnsureCursorVisible();
364                         return ret;
365                 }
366
367                 if ( key == K_END )  {
368                         if ( idKeyInput::IsDown( K_CTRL ) || (cursorLine < -1) || ( cursorLine >= breaks.Num() - 1 ) ) {
369                                 cursorPos = len;
370                         } else {
371                                 cursorPos = breaks[cursorLine + 1] - 1;
372                         }
373                         EnsureCursorVisible();
374                         return ret;
375                 }
376
377                 if ( key == K_INS ) {
378                         if ( !readonly ) {
379                                 dc->SetOverStrike( !dc->GetOverStrike() );
380                         }
381                         return ret;
382                 }
383
384                 if ( key == K_DOWNARROW ) {
385                         if ( idKeyInput::IsDown( K_CTRL ) ) {
386                                 scroller->SetValue( scroller->GetValue() + 1.0f );
387                         } else {
388                                 if ( cursorLine < breaks.Num() - 1 ) {
389                                         int offset = cursorPos - breaks[cursorLine];
390                                         cursorPos = breaks[cursorLine + 1] + offset;
391                                         EnsureCursorVisible();
392                                 }
393                         }
394                 }
395
396                 if (key == K_UPARROW ) {
397                         if ( idKeyInput::IsDown( K_CTRL ) ) {
398                                 scroller->SetValue( scroller->GetValue() - 1.0f );
399                         } else {
400                                 if ( cursorLine > 0 ) {
401                                         int offset = cursorPos - breaks[cursorLine];
402                                         cursorPos = breaks[cursorLine - 1] + offset;
403                                         EnsureCursorVisible();
404                                 }
405                         }
406                 }
407
408                 if ( key == K_ENTER || key == K_KP_ENTER ) {
409                         RunScript( ON_ACTION );
410                         RunScript( ON_ENTER );
411                         return cmd;
412                 }
413
414                 if ( key == K_ESCAPE ) {
415                         RunScript( ON_ESC );
416                         return cmd;
417                 }
418
419         } else if ( event->evType == SE_KEY && !event->evValue2 ) {
420                 if ( key == K_ENTER || key == K_KP_ENTER ) {
421                         RunScript( ON_ENTERRELEASE );
422                         return cmd;
423                 } else {
424                         RunScript( ON_ACTIONRELEASE );
425                 }
426         }
427
428         return ret;
429 }
430
431 void idEditWindow::PostParse() {
432         idWindow::PostParse();
433
434         if ( maxChars == 0 ) {
435                 maxChars = 10;
436         }
437         if ( sourceFile.Length() ) {
438                 void *buffer;
439                 fileSystem->ReadFile( sourceFile, &buffer );
440                 text = (char *) buffer;
441                 fileSystem->FreeFile( buffer );
442         }
443
444         InitCvar();
445         InitScroller(false);
446
447         EnsureCursorVisible();
448
449         flags |= WIN_CANFOCUS;
450 }
451
452 /*
453 ================
454 idEditWindow::InitScroller
455
456 This is the same as in idListWindow
457 ================
458 */
459 void idEditWindow::InitScroller( bool horizontal )
460 {
461         const char *thumbImage = "guis/assets/scrollbar_thumb.tga";
462         const char *barImage = "guis/assets/scrollbarv.tga";
463         const char *scrollerName = "_scrollerWinV";
464
465         if (horizontal) {
466                 barImage = "guis/assets/scrollbarh.tga";
467                 scrollerName = "_scrollerWinH";
468         }
469
470         const idMaterial *mat = declManager->FindMaterial( barImage );
471         mat->SetSort( SS_GUI );
472         sizeBias = mat->GetImageWidth();
473
474         idRectangle scrollRect;
475         if (horizontal) {
476                 sizeBias = mat->GetImageHeight();
477                 scrollRect.x = 0;
478                 scrollRect.y = (clientRect.h - sizeBias);
479                 scrollRect.w = clientRect.w;
480                 scrollRect.h = sizeBias;
481         } else {
482                 scrollRect.x = (clientRect.w - sizeBias);
483                 scrollRect.y = 0;
484                 scrollRect.w = sizeBias;
485                 scrollRect.h = clientRect.h;
486         }
487
488         scroller->InitWithDefaults(scrollerName, scrollRect, foreColor, matColor, mat->GetName(), thumbImage, !horizontal, true);
489         InsertChild(scroller, NULL);
490         scroller->SetBuddy(this);
491 }
492
493 void idEditWindow::HandleBuddyUpdate( idWindow *buddy ) {
494 }
495
496 void idEditWindow::EnsureCursorVisible()
497 {
498         if ( readonly ) {
499                 cursorPos = -1;
500         } else if ( maxChars == 1 ) {
501                 cursorPos = 0;
502         }
503
504         if ( !dc ) {
505                 return;
506         }
507
508         SetFont();
509         if ( !wrap ) {
510                 int cursorX = 0;
511                 if ( password ) {
512                         cursorX = cursorPos * dc->CharWidth( '*', textScale );
513                 } else {
514                         int i = 0;
515                         while ( i < text.Length() && i < cursorPos ) {
516                                 if ( idStr::IsColor( &text[i] ) ) {
517                                         i += 2;
518                                 } else {
519                                         cursorX += dc->CharWidth( text[i], textScale );
520                                         i++;
521                                 }
522                         }
523                 }
524                 int maxWidth = GetMaxCharWidth( );
525                 int left = cursorX - maxWidth;
526                 int right = ( cursorX - textRect.w ) + maxWidth;
527
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;
531                 }
532                 if ( paintOffset <  right) {
533                         paintOffset = right;
534                 }
535                 if ( paintOffset < 0 ) {
536                         paintOffset = 0;
537                 }
538                 scroller->SetRange(0.0f, 0.0f, 1.0f);
539
540         } else {
541                 // Word wrap
542
543                 breaks.Clear();
544                 idRectangle rect = textRect;
545                 rect.w -= sizeBias;
546                 dc->DrawText(text, textScale, textAlign, colorWhite, rect, true, (flags & WIN_FOCUS) ? cursorPos : -1, true, &breaks );
547
548                 int fit = textRect.h / (GetMaxCharHeight() + 5);
549                 if ( fit < breaks.Num() + 1 ) {
550                         scroller->SetRange(0, breaks.Num() + 1 - fit, 1);
551                 } else {
552                         // The text fits completely in the box
553                         scroller->SetRange(0.0f, 0.0f, 1.0f);
554                 }
555
556                 if ( forceScroll ) {
557                         scroller->SetValue( breaks.Num() - fit );
558                 } else if ( readonly ) {
559                 } else {
560                         cursorLine = 0;
561                         for ( int i = 1; i < breaks.Num(); i++ ) {
562                                 if ( cursorPos >= breaks[i] ) {
563                                         cursorLine = i;
564                                 } else {
565                                         break;
566                                 }
567                         }
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 );
573                         }
574                 }
575         }
576 }
577
578 void idEditWindow::Activate(bool activate, idStr &act) {
579         idWindow::Activate(activate, act);
580         if ( activate ) {
581                 UpdateCvar( true, true );
582                 EnsureCursorVisible();
583         }
584 }
585
586 /*
587 ============
588 idEditWindow::InitCvar
589 ============
590 */
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() );
595                 }
596                 cvar = NULL;
597                 return;
598         }
599
600         cvar = cvarSystem->Find( cvarStr );
601         if ( !cvar ) {
602                 common->Warning( "idEditWindow::InitCvar: gui '%s' window '%s' references undefined cvar '%s'", gui->GetSourceFile(), name.c_str(), cvarStr.c_str() );
603                 return;
604         }
605 }
606
607 /*
608 ============
609 idEditWindow::UpdateCvar
610 ============
611 */
612 void idEditWindow::UpdateCvar( bool read, bool force ) {
613         if ( force || liveUpdate ) {
614                 if ( cvar ) {
615                         if ( read ) {
616                                 text = cvar->GetString();
617                         } else {
618                                 cvar->SetString( text );
619                                 if ( cvarMax && ( cvar->GetInteger() > cvarMax ) ) {
620                                         cvar->SetInteger( cvarMax );
621                                 }
622                         }
623                 }
624         }
625 }
626
627 /*
628 ============
629 idEditWindow::RunNamedEvent
630 ============
631 */
632 void idEditWindow::RunNamedEvent( const char* eventName ) {
633         idStr event, group;
634         
635         if ( !idStr::Cmpn( eventName, "cvar read ", 10 ) ) {
636                 event = eventName;
637                 group = event.Mid( 10, event.Length() - 10 );
638                 if ( !group.Cmp( cvarGroup ) ) {
639                         UpdateCvar( true, true );
640                 }
641         } else if ( !idStr::Cmpn( eventName, "cvar write ", 11 ) ) {
642                 event = eventName;
643                 group = event.Mid( 11, event.Length() - 11 );
644                 if ( !group.Cmp( cvarGroup ) ) {
645                         UpdateCvar( false, true );
646                 }
647         }
648 }