1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "Interface.h"
26 #include "Variables.h"
27 #include "GameControl.h"
36 TextArea::TextArea(Color hitextcolor
, Color initcolor
, Color lowtextcolor
)
47 ResetEventHandler( TextAreaOnChange
);
48 ResetEventHandler( TextAreaOutOfText
);
50 palette
= core
->CreatePalette( hitextcolor
, lowtextcolor
);
51 initpalette
= core
->CreatePalette( initcolor
, lowtextcolor
);
53 hitextcolor
.b
, hitextcolor
.g
, hitextcolor
.r
, 0
55 selected
= core
->CreatePalette( tmp
, lowtextcolor
);
59 lineselpal
= core
->CreatePalette( tmp
, lowtextcolor
);
61 //Drop Capitals means initials on!
62 core
->GetDictionary()->Lookup("Drop Capitals", InternalFlags
);
64 InternalFlags
= TA_INITIALS
;
68 TextArea::~TextArea(void)
70 gamedata
->FreePalette( palette
);
71 gamedata
->FreePalette( initpalette
);
72 gamedata
->FreePalette( selected
);
73 gamedata
->FreePalette( lineselpal
);
74 core
->GetVideoDriver()->FreeSprite( Cursor
);
75 for (size_t i
= 0; i
< lines
.size(); i
++) {
80 void TextArea::RefreshSprite(const char *portrait
)
83 if (!strnicmp(PortraitResRef
, portrait
, 8) ) {
88 strnlwrcpy(PortraitResRef
, portrait
, 8);
89 if (!strnicmp(PortraitResRef
, "none", 8) ) {
92 ImageMgr
* im
= ( ImageMgr
* ) gamedata
->GetResource( PortraitResRef
, &ImageMgr::ID
);
97 SetAnimPicture ( im
->GetSprite2D() );
101 void TextArea::Draw(unsigned short x
, unsigned short y
)
103 /** Don't come back recursively */
104 if (InternalFlags
&TA_BITEMYTAIL
) {
109 Region
clip( tx
, ty
, Width
, Height
);
110 Video
*video
= core
->GetVideoDriver();
112 if (Flags
&IE_GUI_TEXTAREA_SPEAKER
) {
114 video
->BlitSprite(AnimPicture
, tx
,ty
, true, &clip
);
115 clip
.x
+=AnimPicture
->Width
;
116 clip
.w
-=AnimPicture
->Width
;
120 //this might look better in GlobalTimer
121 //or you might want to change the animated button to work like this
122 if (Flags
&IE_GUI_TEXTAREA_SMOOTHSCROLL
)
124 unsigned long thisTime
;
127 if (thisTime
>starttime
) {
128 starttime
= thisTime
+ticks
;
131 smooth
+=ftext
->maxHeight
;
137 /** Forcing redraw of whole screen before drawing text*/
139 InternalFlags
|= TA_BITEMYTAIL
;
141 InternalFlags
&= ~TA_BITEMYTAIL
;
145 if (!Changed
&& !(Owner
->Flags
&WF_FLOAT
) ) {
153 size_t linesize
= lines
.size();
158 //smooth vertical scrolling up
159 if (Flags
& IE_GUI_TEXTAREA_SMOOTHSCROLL
) {
164 //if textarea is 'selectable' it actually means, it is a listbox
165 //in this case the selected value equals the line number
166 //if it is 'not selectable' it can still have selectable lines
167 //but then it is like the dialog window in the main game screen:
168 //the selected value is encoded into the line
169 if (!(Flags
& IE_GUI_TEXTAREA_SELECTABLE
) ) {
170 char* Buffer
= (char *) malloc( 1 );
174 for (size_t i
= 0; i
< linesize
; i
++) {
175 if (strnicmp( "[s=", lines
[i
], 3 ) == 0) {
177 unsigned long idx
, acolor
, bcolor
;
179 idx
= strtoul( lines
[i
] + 3, &rest
, 0 );
182 acolor
= strtoul( rest
+ 1, &rest
, 16 );
185 bcolor
= strtoul( rest
+ 1, &rest
, 16 );
188 tlen
= (int)(strstr( rest
+ 1, "[/s]" ) - rest
- 1);
192 Buffer
= (char *) realloc( Buffer
, len
+ 2 );
193 if (seltext
== (int) i
) {
194 sprintf( Buffer
+ lastlen
, "[color=%6.6lX]%.*s[/color]",
195 acolor
, tlen
, rest
+ 1 );
197 sprintf( Buffer
+ lastlen
, "[color=%6.6lX]%.*s[/color]",
198 bcolor
, tlen
, rest
+ 1 );
202 len
+= ( int ) strlen( lines
[i
] ) + 1;
203 Buffer
= (char *) realloc( Buffer
, len
+ 2 );
204 memcpy( &Buffer
[lastlen
], lines
[i
], len
- lastlen
);
207 if (i
!= linesize
- 1) {
208 Buffer
[lastlen
- 1] = '\n';
212 video
->SetClipRect( &clip
);
216 if (startrow
==CurLine
) {
221 ftext
->PrintFromLine( startrow
, clip
,
222 ( unsigned char * ) Buffer
, palette
,
223 IE_FONT_ALIGN_LEFT
, finit
, Cursor
, pos
);
225 video
->SetClipRect( NULL
);
228 //the buffer is filled enough
231 if (core
->GetAudioDrv()->IsSpeaking() ) {
232 //the narrator is still talking
235 if (RunEventHandler( TextAreaOutOfText
)) {
238 if (linesize
==lines
.size()) {
239 ResetEventHandler( TextAreaOutOfText
);
245 // normal scrolling textarea
250 for (i
= 0; i
< linesize
; i
++) {
251 if (rc
+ lrows
[i
] <= sr
) {
257 if (seltext
== (int) i
)
263 ftext
->PrintFromLine( sr
, clip
,
264 ( unsigned char * ) lines
[i
], pal
,
265 IE_FONT_ALIGN_LEFT
, finit
, NULL
);
266 yl
= ftext
->size
[1].h
*(lrows
[i
]-sr
);
271 for (i
++; i
< linesize
; i
++) {
273 if (seltext
== (int) i
)
279 ftext
->Print( clip
, ( unsigned char * ) lines
[i
], pal
,
280 IE_FONT_ALIGN_LEFT
, true );
281 yl
= ftext
->size
[1].h
*lrows
[i
];
287 /** Sets the Scroll Bar Pointer. If 'ptr' is NULL no Scroll Bar will be linked
288 to this Text Area Control. */
289 int TextArea::SetScrollBar(Control
* ptr
)
291 int ret
= Control::SetScrollBar(ptr
);
296 /** Sets the Actual Text */
297 int TextArea::SetText(const char* text
, int pos
)
305 if (lines
.size() == 0) {
309 if (pos
>= ( int ) lines
.size()) {
312 int newlen
= ( int ) strlen( text
);
315 char* str
= (char *) malloc( newlen
+ 1 );
316 memcpy( str
, text
, newlen
+ 1 );
317 lines
.push_back( str
);
318 lrows
.push_back( 0 );
320 lines
[pos
] = (char *) realloc( lines
[pos
], newlen
+ 1 );
321 memcpy( lines
[pos
], text
, newlen
+ 1 );
324 CurLine
= lines
.size()-1;
329 void TextArea::SetMinRow(bool enable
)
332 minrow
= (int) lines
.size();
339 //drop lines scrolled out at the top.
340 //keeplines is the number of lines that should still be
341 //preserved (for scrollback history)
342 void TextArea::DiscardLines()
344 if (rows
<=keeplines
) {
347 int drop
= rows
-keeplines
;
348 PopLines(drop
, true);
351 static const char inserted_crap
[]="[/color][color=ffffff]";
352 #define CRAPLENGTH sizeof(inserted_crap)-1
354 /** Appends a String to the current Text */
355 int TextArea::AppendText(const char* text
, int pos
)
358 if (pos
>= ( int ) lines
.size()) {
361 int newlen
= ( int ) strlen( text
);
364 const char *note
= strstr(text
,"\r\n\r\nNOTE:");
367 str
= (char *) malloc( newlen
+1 );
368 memcpy(str
,text
, newlen
+1);
371 unsigned int notepos
= (unsigned int) (note
- text
);
372 str
= (char *) malloc( newlen
+ CRAPLENGTH
+1 );
373 memcpy(str
,text
,notepos
);
374 memcpy(str
+notepos
,inserted_crap
,CRAPLENGTH
);
375 memcpy(str
+notepos
+CRAPLENGTH
, text
+notepos
, newlen
-notepos
+1);
377 lines
.push_back( str
);
378 lrows
.push_back( 0 );
379 ret
=(int) (lines
.size() - 1);
381 int mylen
= ( int ) strlen( lines
[pos
] );
383 lines
[pos
] = (char *) realloc( lines
[pos
], mylen
+ newlen
+ 1 );
384 memcpy( lines
[pos
]+mylen
, text
, newlen
+ 1 );
388 //if the textarea is not a listbox, then discard scrolled out
390 if (Flags
&IE_GUI_TEXTAREA_HISTORY
) {
398 /** Deletes last or first `count' lines */
399 /** Probably not too optimal for many lines, but it isn't used */
400 /** for many lines */
401 void TextArea::PopLines(unsigned int count
, bool top
)
403 if (count
> lines
.size()) {
404 count
= (unsigned int) lines
.size();
409 int tmp
= lrows
.front();
410 if (minrow
|| (startrow
<tmp
) )
413 free(lines
.front() );
414 lines
.erase(lines
.begin());
415 lrows
.erase(lrows
.begin());
426 void TextArea::UpdateControls()
433 ScrollBar
* bar
= ( ScrollBar
* ) sb
;
434 if (Flags
& IE_GUI_TEXTAREA_AUTOSCROLL
)
435 pos
= rows
- ( ( Height
- 5 ) / ftext
->maxHeight
);
442 if (Flags
& IE_GUI_TEXTAREA_AUTOSCROLL
) {
443 pos
= rows
- ( ( Height
- 5 ) / ftext
->maxHeight
);
450 /** Sets the Fonts */
451 void TextArea::SetFonts(Font
* init
, Font
* text
)
458 /** Key Press Event */
459 void TextArea::OnKeyPress(unsigned char Key
, unsigned short /*Mod*/)
461 if (Flags
& IE_GUI_TEXTAREA_EDITABLE
) {
465 int len
= GetRowLength(CurLine
);
466 //printf("len: %d Before: %s\n",len, lines[CurLine]);
467 lines
[CurLine
] = (char *) realloc( lines
[CurLine
], len
+ 2 );
468 for (int i
= len
; i
> CurPos
; i
--) {
469 lines
[CurLine
][i
] = lines
[CurLine
][i
- 1];
471 lines
[CurLine
][CurPos
] = Key
;
472 lines
[CurLine
][len
+ 1] = 0;
474 //printf("pos: %d After: %s\n",CurPos, lines[CurLine]);
476 RunEventHandler( TextAreaOnChange
);
481 //Selectable=false for dialogs, rather unintuitive, but fact
482 if ((Flags
& IE_GUI_TEXTAREA_SELECTABLE
) || ( Key
< '1' ) || ( Key
> '9' ))
484 GameControl
*gc
= core
->GetGameControl();
485 if (gc
&& (gc
->GetDialogueFlags()&DF_IN_DIALOG
) ) {
488 if ((unsigned int) seltext
>=lines
.size()) {
491 for(int i
=0;i
<Key
-'0';i
++) {
494 if ((unsigned int) seltext
>=lines
.size()) {
498 while (strnicmp( lines
[seltext
], "[s=", 3 ) != 0 );
501 sscanf( lines
[seltext
], "[s=%d,", &idx
);
503 //this kills this object, don't use any more data!
507 gc
->DialogChoose( idx
);
511 /** Special Key Press */
512 void TextArea::OnSpecialKeyPress(unsigned char Key
)
517 if (!(Flags
&IE_GUI_TEXTAREA_EDITABLE
)) {
533 if (CurLine
<lines
.size()) {
538 CurLine
=lines
.size()-1;
539 CurPos
= GetRowLength((unsigned int) CurLine
);
547 CurPos
= GetRowLength(CurLine
);
552 len
= GetRowLength(CurLine
);
556 if(CurLine
<lines
.size()) {
563 len
= GetRowLength(CurLine
);
564 //printf("len: %d Before: %s\n",len, lines[CurLine]);
566 //TODO: merge next line
569 lines
[CurLine
] = (char *) realloc( lines
[CurLine
], len
);
570 for (i
= CurPos
; i
< len
; i
++) {
571 lines
[CurLine
][i
] = lines
[CurLine
][i
+ 1];
573 //printf("pos: %d After: %s\n",CurPos, lines[CurLine]);
576 len
= GetRowLength(CurLine
);
578 //printf("len: %d Before: %s\n",len, lines[CurLine]);
582 lines
[CurLine
] = (char *) realloc( lines
[CurLine
], len
);
583 for (i
= CurPos
; i
< len
; i
++) {
584 lines
[CurLine
][i
- 1] = lines
[CurLine
][i
];
586 lines
[CurLine
][len
- 1] = 0;
588 //printf("pos: %d After: %s\n",CurPos, lines[CurLine]);
592 int oldline
= CurLine
;
594 int old
= GetRowLength(CurLine
);
595 //printf("len: %d Before: %s\n",old, lines[CurLine]);
596 //printf("len: %d Before: %s\n",len, lines[oldline]);
597 lines
[CurLine
] = (char *) realloc (lines
[CurLine
], len
+old
);
598 memcpy(lines
[CurLine
]+old
, lines
[oldline
],len
);
599 free(lines
[oldline
]);
600 lines
[CurLine
][old
+len
]=0;
601 lines
.erase(lines
.begin()+oldline
);
602 lrows
.erase(lrows
.begin()+oldline
);
604 //printf("pos: %d len: %d After: %s\n",CurPos, GetRowLength(CurLine), lines[CurLine]);
609 //add an empty line after CurLine
610 //printf("pos: %d Before: %s\n",CurPos, lines[CurLine]);
611 lrows
.insert(lrows
.begin()+CurLine
, 0);
612 len
= GetRowLength(CurLine
);
613 //copy the text after the cursor into the new line
614 char *str
= (char *) malloc(len
-CurPos
+2);
615 memcpy(str
, lines
[CurLine
]+CurPos
, len
-CurPos
+1);
616 str
[len
-CurPos
+1] = 0;
617 lines
.insert(lines
.begin()+CurLine
+1, str
);
618 //truncate the current line
619 lines
[CurLine
] = (char *) realloc (lines
[CurLine
], CurPos
+1);
620 lines
[CurLine
][CurPos
]=0;
621 //move cursor to next line beginning
624 //printf("len: %d After: %s\n",GetRowLength(CurLine-1), lines[CurLine-1]);
625 //printf("len: %d After: %s\n",GetRowLength(CurLine), lines[CurLine]);
629 RunEventHandler( TextAreaOnChange
);
632 /** Returns Row count */
633 int TextArea::GetRowCount()
635 return ( int ) lines
.size();
638 int TextArea::GetRowLength(unsigned int row
)
640 if (lines
.size()<=row
) {
643 //this is just roughly the line size, escape sequences need to be removed
644 return strlen( lines
[row
] );
647 int TextArea::GetVisibleRowCount()
649 return (Height
-5) / ftext
->maxHeight
;
652 /** Returns top index */
653 int TextArea::GetTopIndex()
658 /** Set Starting Row */
659 void TextArea::SetRow(int row
)
667 void TextArea::CalcRowCount()
672 if (Flags
&IE_GUI_TEXTAREA_SPEAKER
) {
673 const char *portrait
= NULL
;
675 GameControl
*gc
= core
->GetGameControl();
677 actor
= gc
->GetTarget();
680 portrait
= actor
->GetPortrait(1);
683 RefreshSprite(portrait
);
686 w
-=AnimPicture
->Width
;
691 if (lines
.size() != 0) {
692 for (size_t i
= 0; i
< lines
.size(); i
++) {
695 int len
= ( int ) strlen( lines
[i
] );
696 char* tmp
= (char *) malloc( len
+ 1 );
697 memcpy( tmp
, lines
[i
], len
+ 1 );
698 ftext
->SetupString( tmp
, w
);
699 for (int p
= 0; p
<= len
; p
++) {
700 if (( ( unsigned char ) tmp
[p
] ) == '[') {
704 for (k
= 0; k
< 256; k
++) {
729 if (CurLine
>=lines
.size()) {
730 CurLine
=lines
.size()-1;
732 w
= strlen(lines
[CurLine
]);
744 ScrollBar
* bar
= ( ScrollBar
* ) sb
;
745 tr
= rows
- Height
/ftext
->size
[1].h
+ 1;
749 bar
->SetMax( (ieWord
) tr
);
751 /** Mouse Over Event */
752 void TextArea::OnMouseOver(unsigned short /*x*/, unsigned short y
)
754 int height
= ftext
->maxHeight
; //size[1].h;
758 for (size_t i
= 0; i
< lines
.size(); i
++) {
760 if (r
< ( row
- startrow
)) {
761 if (seltext
!= (int) i
)
764 //printf("CtrlId = 0x%08lx, seltext = %d, rows = %d, row = %d, r = %d\n", ControlID, i, rows, row, r);
772 //printf("CtrlId = 0x%08lx, seltext = %d, rows %d, row %d, r = %d\n", ControlID, seltext, rows, row, r);
775 /** Mouse Button Up */
776 void TextArea::OnMouseUp(unsigned short x
, unsigned short y
, unsigned short /*Button*/,
777 unsigned short /*Mod*/)
779 if (( x
<= Width
) && ( y
<= ( Height
- 5 ) ) && ( seltext
!= -1 )) {
780 Value
= (unsigned int) seltext
;
782 if (strnicmp( lines
[seltext
], "[s=", 3 ) == 0) {
783 if (minrow
> seltext
)
786 sscanf( lines
[seltext
], "[s=%d,", &idx
);
787 GameControl
* gc
= core
->GetGameControl();
788 if (gc
&& (gc
->GetDialogueFlags()&DF_IN_DIALOG
) ) {
790 //this kills this object, don't use any more data!
794 gc
->DialogChoose( idx
);
800 if (VarName
[0] != 0) {
801 core
->GetDictionary()->SetAt( VarName
, Value
);
803 RunEventHandler( TextAreaOnChange
);
806 /** Copies the current TextArea content to another TextArea control */
807 void TextArea::CopyTo(TextArea
* ta
)
810 for (size_t i
= 0; i
< lines
.size(); i
++) {
811 ta
->SetText( lines
[i
], -1 );
815 void TextArea::RedrawTextArea(const char* VariableName
, unsigned int Sum
)
817 if (strnicmp( VarName
, VariableName
, MAX_VARIABLE_LENGTH
)) {
824 const char* TextArea::QueryText()
826 if ( Value
<lines
.size() ) {
827 return ( const char * ) lines
[Value
];
829 return ( const char *) "";
832 bool TextArea::SetEvent(int eventType
, const char *handler
)
837 case IE_GUI_TEXTAREA_ON_CHANGE
:
838 SetEventHandler( TextAreaOnChange
, handler
);
840 case IE_GUI_TEXTAREA_OUT_OF_TEXT
:
841 SetEventHandler( TextAreaOutOfText
, handler
);
850 void TextArea::PadMinRow()
853 int i
=(int) (lines
.size()-1);
855 //minrow -2 ->npc text
856 while (i
>=minrow
-2 && i
>=0) {
860 row
= GetVisibleRowCount()-row
;
867 void TextArea::SetPreservedRow(int arg
)
870 Flags
|= IE_GUI_TEXTAREA_HISTORY
;
873 void TextArea::Clear()
875 for (size_t i
= 0; i
< lines
.size(); i
++) {
883 //setting up the textarea for smooth scrolling, the first
884 //TEXTAREA_OUTOFTEXT callback is called automatically
885 void TextArea::SetupScroll(unsigned long tck
)
888 smooth
= ftext
->maxHeight
;
891 //clearing the textarea
893 unsigned int i
= (unsigned int) (Height
/smooth
);
895 char *str
= (char *) malloc(1);
897 lines
.push_back(str
);
900 i
= (unsigned int) lines
.size();
901 Flags
|= IE_GUI_TEXTAREA_SMOOTHSCROLL
;
902 GetTime( starttime
);
903 if (RunEventHandler( TextAreaOutOfText
)) {
904 //event handler destructed this object?
907 if (i
==lines
.size()) {
908 ResetEventHandler( TextAreaOutOfText
);
915 void TextArea::OnMouseDown(unsigned short /*x*/, unsigned short /*y*/, unsigned short Button
,
916 unsigned short /*Mod*/)
919 ScrollBar
* scrlbr
= (ScrollBar
*) sb
;
922 Control
*ctrl
= Owner
->GetScrollControl();
923 if (ctrl
&& (ctrl
->ControlType
== IE_GUI_SCROLLBAR
)) {
924 scrlbr
= (ScrollBar
*) ctrl
;
933 case GEM_MB_SCRLDOWN
:
934 scrlbr
->ScrollDown();