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 "defsounds.h"
27 #include "Interface.h"
29 #include "Variables.h"
34 Unpressed
= Pressed
= Selected
= Disabled
= NULL
;
35 State
= IE_GUI_BUTTON_UNPRESSED
;
36 ResetEventHandler( ButtonOnPress
);
37 ResetEventHandler( ButtonOnDoublePress
);
38 ResetEventHandler( ButtonOnShiftPress
);
39 ResetEventHandler( ButtonOnRightPress
);
40 ResetEventHandler( ButtonOnDragDrop
);
41 ResetEventHandler( ButtonOnDrag
);
42 ResetEventHandler( MouseEnterButton
);
43 ResetEventHandler( MouseLeaveButton
);
44 ResetEventHandler( MouseOverButton
);
45 //Text = ( char * ) calloc( 64, sizeof(char) );
48 font
= core
->GetButtonFont();
49 normal_palette
= NULL
;
50 disabled_palette
= font
->GetPalette()->Copy();
51 for (int i
= 0; i
< 256; i
++) {
52 disabled_palette
->col
[i
].r
= ( disabled_palette
->col
[i
].r
* 2 ) / 3;
53 disabled_palette
->col
[i
].g
= ( disabled_palette
->col
[i
].g
* 2 ) / 3;
54 disabled_palette
->col
[i
].b
= ( disabled_palette
->col
[i
].b
* 2 ) / 3;
56 Flags
= IE_GUI_BUTTON_NORMAL
;
60 memset(&SourceRGB
,0,sizeof(SourceRGB
));
61 memset(&DestRGB
,0,sizeof(DestRGB
));
62 memset( borders
, 0, sizeof( borders
));
67 Video
* video
= core
->GetVideoDriver();
68 video
->FreeSprite( Disabled
);
69 video
->FreeSprite( Selected
);
70 video
->FreeSprite( Pressed
);
71 video
->FreeSprite( Unpressed
);
72 video
->FreeSprite( Picture
);
77 gamedata
->FreePalette( normal_palette
);
78 gamedata
->FreePalette( disabled_palette
);
80 /** Sets the 'type' Image of the Button to 'img'.
81 'type' may assume the following values:
82 - IE_GUI_BUTTON_UNPRESSED
83 - IE_GUI_BUTTON_PRESSED
84 - IE_GUI_BUTTON_SELECTED
85 - IE_GUI_BUTTON_DISABLED */
86 void Button::SetImage(unsigned char type
, Sprite2D
* img
)
89 case IE_GUI_BUTTON_UNPRESSED
:
90 case IE_GUI_BUTTON_LOCKED
:
91 case IE_GUI_BUTTON_LOCKED_PRESSED
:
92 core
->GetVideoDriver()->FreeSprite( Unpressed
);
96 case IE_GUI_BUTTON_SECOND
:
97 case IE_GUI_BUTTON_PRESSED
:
98 core
->GetVideoDriver()->FreeSprite( Pressed
);
102 case IE_GUI_BUTTON_SELECTED
:
103 core
->GetVideoDriver()->FreeSprite( Selected
);
107 case IE_GUI_BUTTON_DISABLED
:
108 case IE_GUI_BUTTON_THIRD
:
109 core
->GetVideoDriver()->FreeSprite( Disabled
);
116 /** make SourceRGB go closer to DestRGB */
117 void Button::CloseUpColor()
119 if (!starttime
) return;
120 //using the realtime timer, because i don't want to
121 //handle Game at this point
122 unsigned long newtime
;
126 if (newtime
<starttime
) {
129 SourceRGB
.r
= (SourceRGB
.r
+ DestRGB
.r
) / 2;
130 SourceRGB
.g
= (SourceRGB
.g
+ DestRGB
.g
) / 2;
131 SourceRGB
.b
= (SourceRGB
.b
+ DestRGB
.b
) / 2;
132 SourceRGB
.a
= (SourceRGB
.a
+ DestRGB
.a
) / 2;
133 if (SourceRGB
.r
== DestRGB
.r
&&
134 SourceRGB
.g
== DestRGB
.g
&&
135 SourceRGB
.b
== DestRGB
.b
&&
136 SourceRGB
.a
== DestRGB
.a
) {
140 starttime
= newtime
+ 40;
143 /** Draws the Control on the Output Display */
144 void Button::Draw(unsigned short x
, unsigned short y
)
146 if (!Changed
&& !(Owner
->Flags
&WF_FLOAT
) ) {
150 if (XPos
== 65535 || Width
== 0) {
154 Video
* video
= core
->GetVideoDriver();
157 if (!( Flags
& IE_GUI_BUTTON_NO_IMAGE
)) {
158 Sprite2D
* Image
= NULL
;
161 case IE_GUI_BUTTON_UNPRESSED
:
162 case IE_GUI_BUTTON_LOCKED
:
163 case IE_GUI_BUTTON_LOCKED_PRESSED
:
167 case IE_GUI_BUTTON_SECOND
:
168 case IE_GUI_BUTTON_PRESSED
:
174 case IE_GUI_BUTTON_SELECTED
:
180 case IE_GUI_BUTTON_DISABLED
:
181 case IE_GUI_BUTTON_THIRD
:
188 // FIXME: maybe it's useless...
189 int xOffs
= ( Width
/ 2 ) - ( Image
->Width
/ 2 );
190 int yOffs
= ( Height
/ 2 ) - ( Image
->Height
/ 2 );
192 video
->BlitSprite( Image
, x
+ XPos
+ xOffs
, y
+ YPos
+ yOffs
, true );
196 if (State
== IE_GUI_BUTTON_PRESSED
) {
197 //shift the writing/border a bit
203 if (Picture
&& (Flags
& IE_GUI_BUTTON_PICTURE
) ) {
204 // Picture is drawn centered
205 int xOffs
= ( Width
/ 2 ) - ( Picture
->Width
/ 2 );
206 int yOffs
= ( Height
/ 2 ) - ( Picture
->Height
/ 2 );
207 if (Flags
& IE_GUI_BUTTON_HORIZONTAL
) {
208 xOffs
+= x
+ XPos
+ Picture
->XPos
;
209 yOffs
+= y
+ YPos
+ Picture
->YPos
;
210 video
->BlitSprite( Picture
, xOffs
, yOffs
, true );
211 Region r
= Region( xOffs
, yOffs
+ (int) (Picture
->Height
* Clipping
), Picture
->Width
, (int) (Picture
->Height
*(1.0 - Clipping
)) );
212 video
->DrawRect( r
, SourceRGB
, true );
216 Region
r( x
+ XPos
+ xOffs
, y
+ YPos
+ yOffs
, (int)(Picture
->Width
* Clipping
), Picture
->Height
);
217 video
->BlitSprite( Picture
, x
+ XPos
+ xOffs
+ Picture
->XPos
, y
+ YPos
+ yOffs
+ Picture
->YPos
, true, &r
);
221 // Composite pictures (paperdolls/description icons)
222 if (!PictureList
.empty() && (Flags
& IE_GUI_BUTTON_PICTURE
) ) {
223 std::list
<Sprite2D
*>::iterator iter
= PictureList
.begin();
224 int xOffs
= 0, yOffs
= 0;
225 if (Flags
& IE_GUI_BUTTON_CENTER_PICTURES
) {
226 // Center the hotspots of all pictures
229 } else if (Flags
& IE_GUI_BUTTON_BG1_PAPERDOLL
) {
234 // Center the first picture, and align the rest to that
235 xOffs
= Width
/2 - (*iter
)->Width
/2 + (*iter
)->XPos
;
236 yOffs
= Height
/2 - (*iter
)->Height
/2 + (*iter
)->YPos
;
239 for (; iter
!= PictureList
.end(); ++iter
) {
240 video
->BlitSprite( *iter
, x
+ XPos
+ xOffs
, y
+ YPos
+ yOffs
, true );
246 int xOffs
= ( Width
/ 2 ) - ( AnimPicture
->Width
/ 2 );
247 int yOffs
= ( Height
/ 2 ) - ( AnimPicture
->Height
/ 2 );
248 Region
r( x
+ XPos
+ xOffs
, y
+ YPos
+ yOffs
, (int)(AnimPicture
->Width
* Clipping
), AnimPicture
->Height
);
250 if (Flags
& IE_GUI_BUTTON_CENTER_PICTURES
) {
251 video
->BlitSprite( AnimPicture
, x
+ XPos
+ xOffs
+ AnimPicture
->XPos
, y
+ YPos
+ yOffs
+ AnimPicture
->YPos
, true, &r
);
253 video
->BlitSprite( AnimPicture
, x
+ XPos
+ xOffs
, y
+ YPos
+ yOffs
, true, &r
);
258 if (hasText
&& ! ( Flags
& IE_GUI_BUTTON_NO_TEXT
)) {
259 Palette
* ppoi
= normal_palette
;
262 if (State
== IE_GUI_BUTTON_DISABLED
)
263 ppoi
= disabled_palette
;
264 // FIXME: hopefully there's no button which sinks when selected
265 // AND has text label
266 //else if (State == IE_GUI_BUTTON_PRESSED || State == IE_GUI_BUTTON_SELECTED) {
268 if (Flags
& IE_GUI_BUTTON_ALIGN_LEFT
)
269 align
|= IE_FONT_ALIGN_LEFT
;
270 else if (Flags
& IE_GUI_BUTTON_ALIGN_RIGHT
)
271 align
|= IE_FONT_ALIGN_RIGHT
;
273 align
|= IE_FONT_ALIGN_CENTER
;
275 if (Flags
& IE_GUI_BUTTON_ALIGN_TOP
)
276 align
|= IE_FONT_ALIGN_TOP
;
277 else if (Flags
& IE_GUI_BUTTON_ALIGN_BOTTOM
)
278 align
|= IE_FONT_ALIGN_BOTTOM
;
280 align
|= IE_FONT_ALIGN_MIDDLE
;
282 if (! (Flags
& IE_GUI_BUTTON_MULTILINE
)) {
283 align
|= IE_FONT_SINGLE_LINE
;
285 font
->Print( Region( x
+ XPos
, y
+ YPos
, Width
- 2, Height
- 2),
286 ( unsigned char * ) Text
, ppoi
,
287 (ieByte
) align
, true );
290 if (! (Flags
&IE_GUI_BUTTON_NO_IMAGE
)) {
291 for (int i
= 0; i
< MAX_NUM_BORDERS
; i
++) {
292 ButtonBorder
*fr
= &borders
[i
];
293 if (! fr
->enabled
) continue;
295 Region r
= Region( x
+ XPos
+ fr
->dx1
, y
+ YPos
+ fr
->dy1
, Width
- (fr
->dx1
+ fr
->dx2
+ 1), Height
- (fr
->dy1
+ fr
->dy2
+ 1) );
296 video
->DrawRect( r
, fr
->color
, fr
->filled
);
300 /** Sets the Button State */
301 void Button::SetState(unsigned char state
)
303 if (state
> IE_GUI_BUTTON_LOCKED_PRESSED
) {// If wrong value inserted
306 if (State
!= state
) {
311 void Button::SetBorder(int index
, int dx1
, int dy1
, int dx2
, int dy2
, const Color
&color
, bool enabled
, bool filled
)
313 if (index
>= MAX_NUM_BORDERS
)
316 ButtonBorder
*fr
= &borders
[index
];
322 fr
->enabled
= enabled
;
327 void Button::EnableBorder(int index
, bool enabled
)
329 if (index
>= MAX_NUM_BORDERS
)
332 if (borders
[index
].enabled
!= enabled
) {
333 borders
[index
].enabled
= enabled
;
338 void Button::SetFont(Font
* newfont
)
342 /** Handling The default button (enter) */
343 void Button::OnSpecialKeyPress(unsigned char Key
)
345 if (State
!= IE_GUI_BUTTON_DISABLED
&& State
!= IE_GUI_BUTTON_LOCKED
) {
346 if (Key
== GEM_RETURN
) {
347 if (Flags
& IE_GUI_BUTTON_DEFAULT
) {
348 RunEventHandler( ButtonOnPress
);
352 else if (Key
== GEM_ESCAPE
) {
353 if (Flags
& IE_GUI_BUTTON_CANCEL
) {
354 RunEventHandler( ButtonOnPress
);
359 Control::OnSpecialKeyPress(Key
);
362 /** Mouse Button Down */
363 void Button::OnMouseDown(unsigned short x
, unsigned short y
,
364 unsigned short Button
, unsigned short Mod
)
366 if (State
== IE_GUI_BUTTON_DISABLED
) {
367 Control::OnMouseDown(x
,y
,Button
,Mod
);
371 if (core
->GetDraggedItem () && !ButtonOnDragDrop
) {
372 Control::OnMouseDown(x
,y
,Button
,Mod
);
376 ScrollBar
* scrlbr
= (ScrollBar
*) sb
;
378 Control
*ctrl
= Owner
->GetScrollControl();
379 if (ctrl
&& (ctrl
->ControlType
== IE_GUI_SCROLLBAR
)) {
380 scrlbr
= (ScrollBar
*) ctrl
;
384 //Button == 1 means Left Mouse Button
385 switch(Button
&GEM_MB_NORMAL
) {
387 // We use absolute screen position here, so drag_start
388 // remains valid even after window/control is moved
389 drag_start
.x
= Owner
->XPos
+ XPos
+ x
;
390 drag_start
.y
= Owner
->YPos
+ YPos
+ y
;
392 if (State
== IE_GUI_BUTTON_LOCKED
) {
393 SetState( IE_GUI_BUTTON_LOCKED_PRESSED
);
396 SetState( IE_GUI_BUTTON_PRESSED
);
397 if (Flags
& IE_GUI_BUTTON_SOUND
) {
398 core
->PlaySound( DS_BUTTON_PRESSED
);
400 if ((Button
& GEM_MB_DOUBLECLICK
) && ButtonOnDoublePress
) {
401 RunEventHandler( ButtonOnDoublePress
);
402 printMessage("Button","Doubleclick detected\n",GREEN
);
411 case GEM_MB_SCRLDOWN
:
413 scrlbr
->ScrollDown();
419 /** Mouse Button Up */
420 void Button::OnMouseUp(unsigned short x
, unsigned short y
,
421 unsigned short Button
, unsigned short Mod
)
423 if (State
== IE_GUI_BUTTON_DISABLED
) {
427 //what was just dropped?
429 if (core
->GetDraggedItem ()) dragtype
=1;
430 if (core
->GetDraggedPortrait ()) dragtype
=2;
432 //if something was dropped, but it isn't handled here: it didn't happen
433 if (dragtype
&& !ButtonOnDragDrop
)
437 case IE_GUI_BUTTON_PRESSED
:
439 SetState( IE_GUI_BUTTON_SELECTED
);
441 SetState( IE_GUI_BUTTON_UNPRESSED
);
444 case IE_GUI_BUTTON_LOCKED_PRESSED
:
445 SetState( IE_GUI_BUTTON_LOCKED
);
449 //in case of dragged/dropped portraits, allow the event to happen even
450 //when we are out of bound
452 if (( x
>= Width
) || ( y
>= Height
)) {
456 if (Flags
& IE_GUI_BUTTON_CHECKBOX
) {
458 ToggleState
= !ToggleState
;
460 SetState( IE_GUI_BUTTON_SELECTED
);
462 SetState( IE_GUI_BUTTON_UNPRESSED
);
463 if (VarName
[0] != 0) {
465 core
->GetDictionary()->Lookup( VarName
, tmp
);
467 core
->GetDictionary()->SetAt( VarName
, tmp
);
468 Owner
->RedrawControls( VarName
, tmp
);
471 if (Flags
& IE_GUI_BUTTON_RADIOBUTTON
) {
474 SetState( IE_GUI_BUTTON_SELECTED
);
476 if (VarName
[0] != 0) {
477 core
->GetDictionary()->SetAt( VarName
, Value
);
478 Owner
->RedrawControls( VarName
, Value
);
484 RunEventHandler( ButtonOnDragDrop
);
487 RunEventHandler( ButtonOnDragDropPortrait
);
491 if ((Button
&GEM_MB_NORMAL
) == GEM_MB_ACTION
) {
492 if ((Mod
& GEM_MOD_SHIFT
) && ButtonOnShiftPress
)
493 RunEventHandler( ButtonOnShiftPress
);
495 RunEventHandler( ButtonOnPress
);
497 if (Button
== GEM_MB_MENU
&& ButtonOnRightPress
)
498 RunEventHandler( ButtonOnRightPress
);
502 void Button::OnMouseOver(unsigned short x
, unsigned short y
)
504 if (State
== IE_GUI_BUTTON_DISABLED
) {
508 if ( RunEventHandler( MouseOverButton
)) {
509 //event handler destructed this object
513 if (State
== IE_GUI_BUTTON_LOCKED
) {
517 Owner
->Cursor
= IE_CURSOR_NORMAL
;
519 //portrait buttons are draggable and locked
520 if ((Flags
& IE_GUI_BUTTON_DRAGGABLE
) &&
521 (State
== IE_GUI_BUTTON_PRESSED
|| State
==IE_GUI_BUTTON_LOCKED_PRESSED
)) {
522 // We use absolute screen position here, so drag_start
523 // remains valid even after window/control is moved
524 int dx
= Owner
->XPos
+ XPos
+ x
- drag_start
.x
;
525 int dy
= Owner
->YPos
+ YPos
+ y
- drag_start
.y
;
526 core
->GetDictionary()->SetAt( "DragX", dx
);
527 core
->GetDictionary()->SetAt( "DragY", dy
);
528 drag_start
.x
= (ieWord
) (drag_start
.x
+ dx
);
529 drag_start
.y
= (ieWord
) (drag_start
.y
+ dy
);
530 RunEventHandler( ButtonOnDrag
);
534 void Button::OnMouseEnter(unsigned short /*x*/, unsigned short /*y*/)
536 if (State
== IE_GUI_BUTTON_DISABLED
) {
540 if (MouseEnterButton
!=0 && VarName
[0] != 0) {
541 core
->GetDictionary()->SetAt( VarName
, Value
);
544 RunEventHandler( MouseEnterButton
);
547 void Button::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/)
549 if (State
== IE_GUI_BUTTON_DISABLED
) {
553 if (MouseLeaveButton
!=0 && VarName
[0] != 0) {
554 core
->GetDictionary()->SetAt( VarName
, Value
);
557 RunEventHandler( MouseLeaveButton
);
561 /** Sets the Text of the current control */
562 int Button::SetText(const char* string
, int /*pos*/)
566 if (string
== NULL
) {
568 } else if (string
[0] == 0) {
571 Text
= strndup( string
, 255 );
572 if (Flags
&IE_GUI_BUTTON_LOWERCASE
)
574 else if (Flags
&IE_GUI_BUTTON_CAPS
)
582 /** Set Event Handler */
583 bool Button::SetEvent(int eventType
, EventHandler handler
)
588 case IE_GUI_BUTTON_ON_PRESS
:
589 ButtonOnPress
= handler
;
591 case IE_GUI_MOUSE_OVER_BUTTON
:
592 MouseOverButton
= handler
;
594 case IE_GUI_MOUSE_ENTER_BUTTON
:
595 MouseEnterButton
= handler
;
597 case IE_GUI_MOUSE_LEAVE_BUTTON
:
598 MouseLeaveButton
= handler
;
600 case IE_GUI_BUTTON_ON_SHIFT_PRESS
:
601 ButtonOnShiftPress
= handler
;
603 case IE_GUI_BUTTON_ON_RIGHT_PRESS
:
604 ButtonOnRightPress
= handler
;
606 case IE_GUI_BUTTON_ON_DRAG_DROP
:
607 ButtonOnDragDrop
= handler
;
609 case IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT
:
610 ButtonOnDragDropPortrait
= handler
;
612 case IE_GUI_BUTTON_ON_DRAG
:
613 ButtonOnDrag
= handler
;
615 case IE_GUI_BUTTON_ON_DOUBLE_PRESS
:
616 ButtonOnDoublePress
= handler
;
625 /** Redraws a button from a given radio button group */
626 void Button::RedrawButton(const char* VariableName
, unsigned int Sum
)
628 if (strnicmp( VarName
, VariableName
, MAX_VARIABLE_LENGTH
)) {
631 if (State
== IE_GUI_BUTTON_DISABLED
) {
634 if (Flags
& IE_GUI_BUTTON_RADIOBUTTON
) {
635 ToggleState
= ( Sum
== Value
);
636 } //radio button, exact value
637 else if (Flags
& IE_GUI_BUTTON_CHECKBOX
) {
638 ToggleState
= !!( Sum
& Value
);
639 } //checkbox, bitvalue
642 } //other buttons, nothing to redraw
644 SetState(IE_GUI_BUTTON_SELECTED
);
646 SetState(IE_GUI_BUTTON_UNPRESSED
);
649 /** Sets the Picture */
650 void Button::SetPicture(Sprite2D
* newpic
)
652 core
->GetVideoDriver()->FreeSprite( Picture
);
656 Flags
|= IE_GUI_BUTTON_PICTURE
;
660 /** Clears the list of Pictures */
661 void Button::ClearPictureList()
663 Video
* video
= core
->GetVideoDriver();
664 for (std::list
<Sprite2D
*>::iterator iter
= PictureList
.begin();
665 iter
!= PictureList
.end(); ++iter
)
666 video
->FreeSprite( *iter
);
672 /** Add picture to the end of the list of Pictures */
673 void Button::StackPicture(Sprite2D
* Picture
)
675 PictureList
.push_back(Picture
);
677 Flags
|= IE_GUI_BUTTON_PICTURE
;
681 bool Button::IsPixelTransparent(unsigned short x
, unsigned short y
)
683 // some buttons have hollow Image frame filled w/ Picture
684 // some buttons in BG2 are text only (if BAM == 'GUICTRL')
685 if (Picture
|| PictureList
.size() || ! Unpressed
) return false;
686 return Unpressed
->IsPixelTransparent(x
, y
);
689 // Set palette used for drawing button label in normal state
690 void Button::SetTextColor(const Color
&fore
, const Color
&back
)
692 gamedata
->FreePalette( normal_palette
);
693 normal_palette
= core
->CreatePalette( fore
, back
);
697 void Button::SetHorizontalOverlay(double clip
, const Color
&src
, const Color
&dest
)
699 if ((Clipping
>clip
) || !(Flags
&IE_GUI_BUTTON_HORIZONTAL
) ) {
700 Flags
|= IE_GUI_BUTTON_HORIZONTAL
;
703 GetTime( starttime
);