FS2_Open
Open source remastering of the Freespace 2 engine
button.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "gamesnd/gamesnd.h"
13 #include "globalincs/alphacolors.h"
14 #include "io/timer.h"
15 #include "ui/ui.h"
16 #include "ui/uidefs.h"
17 
18 // ---------------------------------------------------------------------------------------
19 // input:
20 // do_repeat => property of button, set to 1 to allow pressed events if mouse
21 // pointer is held over button with left mouse button down,
22 // otherwise 0 (useful for buttons that scroll items)
23 // ignore_focus => whether to allow Enter/Spacebar to affect pressed state when
24 // control has focus
25 //
26 void UI_BUTTON::create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _w, int _h, int do_repeat, int ignore_focus)
27 {
28  text = NULL;
29 
30  if (_text) {
31  if ( _text[0] != '\0' ) {
32  text = vm_strdup(_text);
33  }
34  }
35 
36  // register gadget with UI window
37  base_create( wnd, UI_KIND_BUTTON, _x, _y, _w, _h );
38 
39  // initialize variables
40  m_flags = 0;
41  next_repeat = 0;
42  m_just_highlighted_function = NULL; // assume there is no callback
43  m_disabled_function = NULL; // ditto
44  if (do_repeat) {
46  next_repeat = 1;
47  }
48 
49  m_press_linger = 1;
50  first_callback = 1;
51 
52  hotkey_if_focus = KEY_SPACEBAR;
53 
54  if (ignore_focus){
56  }
57 
58  custom_cursor_bmap = -1;
59  previous_cursor_bmap = -1;
60 }
61 
62 void UI_BUTTON::destroy()
63 {
64  if (text) {
65  vm_free(text);
66  text = NULL;
67  }
68 
69  UI_GADGET::destroy(); // call base as well
70 }
71 
72 // sets a hotkey for button that works only when it had focus (or derived focus)
74 {
75  hotkey_if_focus = key;
76 }
77 
79 {
82  m_flags &= ~BF_DOWN;
85  m_flags &= ~BF_CLICKED;
86 }
87 
88 // reset anything that needs to be at the start of a new frame before processing
89 void UI_BUTTON::frame_reset()
90 {
93  m_flags &= ~BF_DOWN;
96  m_flags &= ~BF_CLICKED;
99 
100  restore_previous_cursor();
101 }
102 
103 // Force button to draw a specified frame
104 void UI_BUTTON::draw_forced(int frame_num)
105 {
106  if (uses_bmaps) {
107  if (bmap_ids[frame_num] >= 0) {
108  gr_set_bitmap(bmap_ids[frame_num]);
110 
111  // my_wnd->draw_tooltip();
112 
113  // redraw any associated xstr
114  my_wnd->draw_XSTR_forced(this, frame_num);
115  }
116  }
117 }
118 
119 // Render button. How it draws exactly depends on it's current state.
121 {
122  int offset, frame_num = -1;
123 
124  if (uses_bmaps) {
125  gr_reset_clip();
126  // if button is down, draw it that way
127  if (button_down()) {
128  if (bmap_ids[B_PRESSED] >= 0){
129  frame_num = B_PRESSED;
130  }
131  // otherwise if button is disabled, draw it that way
132  } else if (disabled_flag) {
133  if (bmap_ids[B_DISABLED] >= 0){
134  frame_num = B_DISABLED;
135  }
136  // otherwise, if button is highlighted (mouse is over it, but mouse buttons not down) draw it that way
137  } else if (m_flags & BF_HIGHLIGHTED) {
138  if (bmap_ids[B_HIGHLIGHT] >= 0){
139  frame_num = B_HIGHLIGHT;
140  }
141  // otherwise, just draw it normally
142  } else {
143  if (bmap_ids[B_NORMAL] >= 0){
144  frame_num = B_NORMAL;
145  }
146  }
147 
148  if (frame_num >= 0) {
149  gr_set_bitmap(bmap_ids[frame_num]);
151  }
152  } else {
154  gr_set_clip( x, y, w, h, GR_RESIZE_MENU );
155 
156  // draw the button's box
157  if (button_down()) {
158  ui_draw_box_in( 0, 0, w-1, h-1 );
159  offset = 1;
160 
161  } else {
162  ui_draw_box_out( 0, 0, w-1, h-1 );
163  offset = 0;
164  }
165 
166  // now draw the button's text
167  if (disabled_flag){
169  } else if (my_wnd->selected_gadget == this){
171  } else {
173  }
174 
175  if (text){
176  ui_string_centered( Middle(w) + offset, Middle(h) + offset, text );
177  }
178 
179  gr_reset_clip();
180  }
181 }
182 
183 // process() is called to process the button, which amounts to:
184 // If mouse is over button, hilight it
185 // If highlighted and mouse button down, flag button as down
186 // If hotkey pressed, flag button as down
187 // If hotkey_if_focus pressed, and button has focus, flag button as down
188 // Set various BF_JUST_* flags if events changed from last frame
189 //
190 void UI_BUTTON::process(int focus)
191 {
192  int mouse_on_me, old_flags;
193 
194  old_flags = m_flags;
195  frame_reset();
196 
197  // check mouse over control and handle hilighting state
198  mouse_on_me = is_mouse_on();
199 
200  // if gadget is disabled, force button up and return
201  if (disabled_flag) {
202  if (old_flags & BF_DOWN){
204  }
205 
207  if (mouse_on_me && B1_JUST_PRESSED){
209  }
210 
211  if ( (hotkey >= 0) && (my_wnd->keypress == hotkey) ){
213  }
214  }
215 
216  // do callback if the button is disabled
217  if (mouse_on_me && B1_JUST_PRESSED){
218  if (m_disabled_function != NULL) {
219  m_disabled_function();
220  }
221  }
222 
223  return;
224  }
225 
226  // check focus and derived focus with one variable
227  if (my_wnd->selected_gadget == this) {
228  focus = 1;
229  }
230 
231  // show alternate cursor, perhaps?
232  maybe_show_custom_cursor();
233 
234  if ( !mouse_on_me ) {
235  next_repeat = 0;
236  } else {
238  if ( !(old_flags & BF_HIGHLIGHTED) ) {
239  int do_callback = 1;
241  // if a callback exists, call it
242  if (m_just_highlighted_function) {
243 
245  if ( first_callback ) {
246  do_callback = 0;
247  }
248  }
249 
250  first_callback = 0;
251  if ( do_callback ) {
252  m_just_highlighted_function();
253  }
254  }
255  }
256  }
257 
258  // check if mouse is pressed
259  if ( B1_PRESSED && mouse_on_me ) {
260  m_flags |= BF_DOWN;
261  capture_mouse();
262  }
263 
264  // check if hotkey is down or not
265  if ( (hotkey >= 0) && (my_wnd->keypress == hotkey) ) {
266  m_flags |= BF_DOWN | BF_CLICKED;
267  }
268 
269  // only check for space/enter keystrokes if we are not ignoring the focus (this is the
270  // default behavior)
271  if ( !(m_flags & BF_IGNORE_FOCUS) ) {
272  if ( focus && (hotkey_if_focus >= 0) ) {
273  if (my_wnd->keypress == hotkey_if_focus)
274  m_flags |= BF_DOWN | BF_CLICKED;
275 
276  if ( (hotkey_if_focus == KEY_SPACEBAR) && (my_wnd->keypress == KEY_ENTER) )
277  m_flags |= BF_DOWN | BF_CLICKED;
278  }
279  }
280 
281  // handler for button not down
282  if ( !(m_flags & BF_DOWN) ) {
283  next_repeat = 0;
284  if ( (old_flags & BF_DOWN) && !(old_flags & BF_CLICKED) ) // check for release of mouse, not hotkey
286 
287  // non-repeating buttons behave sort of uniquely.. They activate when released over button
288  if (!(m_flags & BF_REPEATS)) {
289  if ( (m_flags & BF_JUST_RELEASED) && (m_flags & BF_HIGHLIGHTED) )
290  m_flags |= BF_CLICKED;
291  }
292 
293  return;
294  }
295 
296  // check if button just went down this frame
297  if ( !(old_flags & BF_DOWN) ) {
299  m_press_linger = timestamp(100);
300  if (user_function)
301  user_function();
302 
303  if (m_flags & BF_REPEATS) {
304  next_repeat = timestamp(B_REPEAT_TIME * 3);
305  m_flags |= BF_CLICKED;
306  }
307  }
308 
309  // check if a repeat event should occur
310  if ( timestamp_elapsed(next_repeat) && (m_flags & BF_REPEATS) ) {
311  next_repeat = timestamp(B_REPEAT_TIME);
312  m_flags |= BF_CLICKED;
313  m_press_linger = timestamp(100);
314  }
315 
316  // check for double click occurance
317  if (B1_DOUBLE_CLICKED && mouse_on_me) {
319  m_press_linger = timestamp(100);
320  }
321 }
322 
323 // Check if button should do it's function in life (trigger the event)
324 //
326 {
327  if (m_flags & BF_CLICKED)
328  return TRUE;
329 
330  return FALSE;
331 }
332 
334 {
335  if ( m_flags & BF_DOUBLE_CLICKED )
336  return TRUE;
337  else
338  return FALSE;
339 }
340 
342 {
343  if ( m_flags & BF_JUST_PRESSED )
344  return TRUE;
345  else
346  return FALSE;
347 }
348 
350 {
352  return TRUE;
353  else
354  return FALSE;
355 }
356 
357 // ----------------------------------------------------------------------------------
358 // Checks if button is down (or up). This checks the button instead, rather than any
359 // events that may have caused it to be down. Buttons also stay down for a certain amount
360 // of time minimum, to make sure it's long enough for the user to see it has went down (since
361 // one frame is probably far to quick for users to notice it). Basically, this indicates
362 // how the button is being drawn, if you want to think of it that way.
364 {
365  if ( (m_flags & BF_DOWN) || !timestamp_elapsed(m_press_linger) )
366  return TRUE;
367  else
368  return FALSE;
369 }
370 
371 // ------------------------------------------------------------
372 // set the callback function for when the mouse first goes over
373 // a button
374 //
375 void UI_BUTTON::set_highlight_action( void (*_user_function)(void) )
376 {
377  m_just_highlighted_function = _user_function;
378 }
379 
380 void UI_BUTTON::set_disabled_action( void (*_user_function)(void) )
381 {
382  m_disabled_function = _user_function;
383 }
384 
385 
386 // Is the mouse over this button?
388 {
389  return m_flags & BF_HIGHLIGHTED;
390 }
391 
392 // Is the mouse over this button?
394 {
396 }
397 
398 // Force button to get pressed
400 {
401  if ( !disabled_flag ) {
402  m_flags |= BF_DOWN | BF_CLICKED;
403  //m_flags |= BF_JUST_PRESSED;
404  }
405 }
406 
407 // reset the "pressed" timestamps
409 {
410  m_press_linger = 1;
411  next_repeat = 0;
412 }
413 
415 {
417  first_callback = 1;
418 }
419 
421 {
422  if(yes){
423  m_flags |= BF_REPEATS;
424  next_repeat = 1;
425  } else {
426  m_flags &= ~(BF_REPEATS);
427  next_repeat = 0;
428  }
429 }
430 
431 void UI_BUTTON::maybe_show_custom_cursor()
432 {
433  if (disabled_flag)
434  return;
435 
436  // set the mouseover cursor
437  if (is_mouse_on()) {
438  if ((custom_cursor_bmap >= 0) && (previous_cursor_bmap < 0)) {
439  previous_cursor_bmap = gr_get_cursor_bitmap();
440  gr_set_cursor_bitmap(custom_cursor_bmap, GR_CURSOR_LOCK); // set and lock
441  }
442  }
443 }
444 
445 void UI_BUTTON::restore_previous_cursor()
446 {
447  if (previous_cursor_bmap >= 0) {
448  gr_set_cursor_bitmap(previous_cursor_bmap, GR_CURSOR_UNLOCK); // restore and unlock
449  previous_cursor_bmap = -1;
450  }
451 }
void set_highlight_action(void(*_user_function)(void))
Definition: button.cpp:375
int timestamp(int delta_ms)
Definition: timer.cpp:226
#define vm_free(ptr)
Definition: pstypes.h:548
int just_pressed()
Definition: button.cpp:341
int button_hilighted()
Definition: button.cpp:387
void reset_status()
Definition: button.cpp:78
void set_button_hilighted()
Definition: button.cpp:393
int hotkey
Definition: ui.h:78
GLfloat GLfloat GLfloat GLfloat h
Definition: Glext.h:7280
int is_mouse_on()
Definition: gadget.cpp:371
#define GR_RESIZE_MENU
Definition: 2d.h:684
UI_WINDOW * my_wnd
Definition: ui.h:111
int bmap_ids[MAX_BMAPS_PER_GADGET]
Definition: ui.h:118
int double_clicked()
Definition: button.cpp:333
virtual void draw()
Definition: button.cpp:120
general failure sound for any event
Definition: gamesnd.h:297
void base_create(UI_WINDOW *wnd, int _kind, int _x, int _y, int _w, int _h)
Definition: gadget.cpp:244
#define TRUE
Definition: pstypes.h:399
void gr_set_color_fast(color *dst)
Definition: 2d.cpp:1197
int key
void ui_draw_box_in(int x1, int y1, int x2, int y2)
Definition: uidraw.cpp:80
void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha)
Definition: 2d.cpp:2105
void ui_draw_box_out(int x1, int y1, int x2, int y2)
Definition: uidraw.cpp:69
#define UI_KIND_BUTTON
Definition: ui.h:18
#define B1_JUST_PRESSED
Definition: uidefs.h:49
__inline void gr_set_clip(int x, int y, int w, int h, int resize_mode=GR_RESIZE_FULL)
Definition: 2d.h:741
void draw_forced(int frame_num)
Definition: button.cpp:104
#define gr_reset_clip
Definition: 2d.h:745
void press_button()
Definition: button.cpp:399
void skip_first_highlight_callback()
Definition: button.cpp:414
virtual void destroy()
Definition: gadget.cpp:160
GLintptr offset
Definition: Glext.h:5497
void set_hotkey_if_focus(int key)
Definition: button.cpp:73
#define BF_DOWN
Definition: ui.h:183
int pressed()
Definition: button.cpp:325
#define KEY_ENTER
Definition: key.h:125
#define BF_SKIP_FIRST_HIGHLIGHT_CALLBACK
Definition: ui.h:193
#define CDARK_GRAY
Definition: uidefs.h:24
#define vm_strdup(ptr)
Definition: pstypes.h:549
int use_hack_to_get_around_stupid_problem_flag
Definition: ui.h:652
#define CBLACK
Definition: uidefs.h:20
#define B1_DOUBLE_CLICKED
Definition: uidefs.h:51
#define BF_CLICKED
Definition: ui.h:186
GLint GLint GLint GLint GLint x
Definition: Glext.h:5182
int uses_bmaps
Definition: ui.h:93
#define CBRIGHT_GREEN
Definition: uidefs.h:22
void reset_timestamps()
Definition: button.cpp:408
int gr_get_cursor_bitmap()
Definition: 2d.cpp:1297
void capture_mouse()
Definition: gadget.cpp:225
#define B1_PRESSED
Definition: uidefs.h:47
int just_highlighted()
Definition: button.cpp:349
int hidden
Definition: ui.h:86
int disabled_flag
Definition: ui.h:82
void gr_set_cursor_bitmap(int n, int lock)
Definition: 2d.cpp:1242
#define BF_JUST_PRESSED
Definition: ui.h:184
void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _w, int _h, int do_repeat=0, int ignore_focus=0)
Definition: button.cpp:26
GLubyte GLubyte GLubyte GLubyte w
Definition: Glext.h:5679
#define BF_JUST_HIGHLIGHTED
Definition: ui.h:189
int button_down()
Definition: button.cpp:363
int m_flags
Definition: ui.h:80
Definition: ui.h:584
#define BF_REPEATS
Definition: ui.h:192
#define timestamp_elapsed(stamp)
Definition: timer.h:102
void draw_XSTR_forced(UI_GADGET *owner, int frame)
Definition: window.cpp:278
#define KEY_SPACEBAR
Definition: key.h:128
void gr_bitmap(int _x, int _y, int resize_mode)
Definition: 2d.cpp:1303
#define BF_HOTKEY_JUST_PRESSED
Definition: ui.h:191
void ui_string_centered(int x, int y, char *s)
Definition: uidraw.cpp:28
void(* user_function)(void)
Definition: ui.h:81
#define BF_HIGHLIGHTED
Definition: ui.h:188
void set_disabled_action(void(*_user_function)(void))
Definition: button.cpp:380
void gamesnd_play_iface(int n)
Definition: gamesnd.cpp:260
void gr_set_font(int fontnum)
Definition: font.cpp:566
#define GR_CURSOR_LOCK
Definition: 2d.h:720
#define BF_DOUBLE_CLICKED
Definition: ui.h:187
#define FALSE
Definition: pstypes.h:400
int keypress
Definition: ui.h:622
int f_id
Definition: ui.h:603
#define GR_CURSOR_UNLOCK
Definition: 2d.h:721
void repeatable(int yes)
Definition: button.cpp:420
#define BF_JUST_RELEASED
Definition: ui.h:185
GLint y
Definition: Gl.h:1505
#define Middle(x)
Definition: uidefs.h:74
#define BF_IGNORE_FOCUS
Definition: ui.h:190
UI_GADGET * selected_gadget
Definition: ui.h:610