1 // -----------------------------------------------
3 // -----------------------------------------------
4 // Quick menu system for my games/etc
5 // -----------------------------------------------
6 // Developed By Kronoman - Copyright (c) 2004
7 // In loving memory of my father
8 // -----------------------------------------------
10 // Don't open this file until doomsday!!
13 #include "gerror.h" // error reporting
14 // -------------------------------------------------------------------
15 // This are allegro timer routines, needed to keep the
16 // menu manager calling the events at constant rate
17 // -------------------------------------------------------------------
18 #include <limits.h> // need this to know the max size of the counter...
20 static volatile int time_counter
= 0; // first timer counter, counts in range from 0..BPS_OF_MENU_MANAGER
21 static volatile unsigned long int big_timer_counter
= 0; // second timer, counts in range from 0..maximun int
22 static volatile int speed_counter
= 0; // this speed counter is to keep the rate of drawing at constant fps
24 // how many times we have installed / erased the timer handler?
25 // really, the timer gets installed only once, this only counts how many calls are made
26 // when reaches <= 0, the timer is uninstalled and the 2 counter resets to zero
27 static int timers_installed
= 0;
29 // --------------------------------------------------------
31 // --------------------------------------------------------
32 static void menu_manager_timer_handler()
37 if (time_counter
> BPS_OF_MENU_MANAGER
) time_counter
= 0;
38 if (big_timer_counter
> ULONG_MAX
- 2 ) big_timer_counter
= 0;
40 END_OF_FUNCTION(menu_manager_timer_handler
);
42 // --------------------------------------------------------
43 // This starts the global timer
44 // --------------------------------------------------------
45 static void menu_manager_start_global_timer()
47 if (timers_installed
== 0)
50 LOCK_VARIABLE(time_counter
);
51 LOCK_VARIABLE(big_timer_counter
);
52 LOCK_VARIABLE(speed_counter
);
53 LOCK_FUNCTION((void *)menu_manager_timer_handler
);
55 // DEBUG - this SHOULD BE CHECKED FOR ERROR!!!
56 if (install_int_ex(menu_manager_timer_handler
, BPS_TO_TIMER(BPS_OF_MENU_MANAGER
)))
57 raise_error("menu_manager_start_global_timer():\nERROR: Can't install timer at %d bps\n", BPS_OF_MENU_MANAGER
);
60 timers_installed
++; // this counts how many calls to this function were made
64 // --------------------------------------------------------
65 // This ends the global timer
66 // --------------------------------------------------------
67 static void menu_manager_stop_global_timer()
69 if (timers_installed
== 0) return; // no timer installed
73 if (timers_installed
<= 0)
75 // remove and reset the timer
76 remove_int(menu_manager_timer_handler
);
79 big_timer_counter
= 0;
84 // ===================================================================
85 // ===================================================================
86 // From here is the menu class itself
87 // ===================================================================
88 // ===================================================================
93 menu_font_s
= menu_font_ns
= font
;
94 menu_fg_color_ns
= makecol(128,128,128);
95 menu_bg_color_ns
= -1;
97 menu_fg_color_s
= makecol(255,255,255);
98 menu_bg_color_s
= makecol(128,0,0);
106 item_y_separation
= 0;
110 // DEBUG - we use all available controllers by default
111 control
.set_use_mouse(true);
112 control
.set_use_joystick(true);
113 control
.set_use_keyboard(true);
115 this->clear_menu_list();
120 this->clear_menu_list();
123 // --------------------------------------------------------
124 // empty the menu item list
125 // --------------------------------------------------------
126 void CQMenu::clear_menu_list()
128 menu_item_text
.clear();
131 // --------------------------------------------------------
132 // adds a selectable item to the menu
133 // returns the index of the item added, or -1 on error (menu full)
134 // --------------------------------------------------------
135 int CQMenu::add_item_to_menu(string item
)
137 menu_item_text
.push_back(item
);
139 return menu_item_text
.size(); // done
142 // --------------------------------------------------------
143 // Starts the menu loop until the user selects one
144 // Return the index of the item selected by the user (starts at 0).
145 // selected is the item to let selected by default
146 // --------------------------------------------------------
147 int CQMenu::do_the_menu(int selected
)
149 bool d_done
= false; // dialog done?
150 int mis
= 0; // mis == Menu Item Selected (current selected item by user)
151 int cret
= 0; // controller return value
152 int mhi
= 0; // menu item height (measured in items)
153 int msd
= 0, med
= 0, mdy
= 0; // menu start drawing, menu end drawing, menu draw Y
154 int last_hit
= 0; // timer to last hit of a key, to take a small delay; if not, the menu scrolls too fast :(
155 BITMAP
*dbuf
= NULL
; // double buffer
157 if (menu_item_text
.size() < 1) raise_error("CQMenu::do_the_menu()\nERROR: no items in menu\n");
159 if (to_bitmap
== NULL
) raise_error("CQMenu::do_the_menu()\nERROR: no destination bitmap to render the menu\n");
161 menu_manager_start_global_timer(); // we need a timer running
163 dbuf
= create_bitmap(to_bitmap
->w
, to_bitmap
->h
); // double buffer
164 if (dbuf
== NULL
) raise_error("CQMenu::do_the_menu()\nERROR: unable to create doble buffer bitmap!\n");
166 mhi
= mh
/ (text_height(menu_font_ns
)+item_y_separation
); // how many items can we fit vertically?
168 // ------------- Main loop --------------
173 show_mouse(NULL
); // go to hell, little mouse cursor :P
175 if (mis
< 0 || (unsigned)mis
> menu_item_text
.size()-1) mis
= 0;
179 while (speed_counter
> 0)
181 // update menu logic here
183 cret
= control
.do_input_poll();
185 // if the last hit was very close (1/6 of second), ignore input
186 if (abs((int)big_timer_counter
- last_hit
) < BPS_OF_MENU_MANAGER
/10)
188 key
[KEY_UP
] = key
[KEY_DOWN
] = key
[KEY_ENTER
] = key
[KEY_SPACE
]=0;
193 if ((cret
& KC_UP
) || (cret
& KC_LEFT
) || key
[KEY_UP
])
196 // take a little delay to let the guy release the key (if any)
198 while (keypressed()) readkey();
199 last_hit
= big_timer_counter
;
202 if ((cret
& KC_DOWN
) || (cret
& KC_RIGHT
) || key
[KEY_DOWN
])
205 // take a little delay to let the guy release the key (if any)
207 while (keypressed()) readkey();
208 last_hit
= big_timer_counter
;
211 // don't let the selection go out of bounds
212 if (mis
< 0) mis
= menu_item_text
.size()-1;
213 if ((unsigned)mis
> menu_item_text
.size()-1) mis
= 0;
215 if ((cret
& KC_BTN1
) || (cret
& KC_BTN2
) || (cret
& KC_BTN3
) || key
[KEY_ENTER
] || key
[KEY_SPACE
]) d_done
= true; // done, item selected wow!
217 if (callback
!= NULL
) callback(*this, true); // call the callback if needed, to update logic
219 if (time_counter
> BPS_OF_MENU_MANAGER
- 5) yield_timeslice(); // we play nice with the OS multitask, when we can (when the timer is about to reach peak)
221 //if (key[KEY_ESC]) d_done = true; // emergency key :^O
224 // here update screen
226 { blit(back_bitmap
, dbuf
, 0,0,0,0,back_bitmap
->w
, back_bitmap
->h
);}
230 if (callback
!= NULL
) callback(*this, false); // call the callback if needed, to update screen
232 // Here we draw the menu
233 msd
= mis
- mhi
/2 - 1; // menu start drawing from here
234 if (msd
< 0) msd
= 0;
235 med
= msd
+ mhi
; // menu drawing ends here
236 if ((unsigned)med
> menu_item_text
.size()) med
= menu_item_text
.size();
238 for (int i
= msd
; i
< med
; i
++)
240 if (i
== mis
) // is selected or unselected item?
242 text_mode(menu_bg_color_s
);
243 switch (text_align
) // wich text align to use?
246 textout_right(dbuf
, menu_font_s
, menu_item_text
[i
].c_str(), mx
, mdy
, menu_fg_color_s
);
249 textout_centre(dbuf
, menu_font_s
, menu_item_text
[i
].c_str(), mx
, mdy
, menu_fg_color_s
);
252 textout_justify(dbuf
, menu_font_s
, menu_item_text
[i
].c_str(), mx
,mx
+mw
, mdy
, mw
, menu_fg_color_s
);
255 textout(dbuf
, menu_font_s
, menu_item_text
[i
].c_str(), mx
, mdy
, menu_fg_color_s
);
258 mdy
+= text_height(menu_font_s
);
262 text_mode(menu_bg_color_ns
);
266 textout_right(dbuf
, menu_font_ns
, menu_item_text
[i
].c_str(), mx
, mdy
, menu_fg_color_ns
);
269 textout_centre(dbuf
, menu_font_ns
, menu_item_text
[i
].c_str(), mx
, mdy
, menu_fg_color_ns
);
272 textout_justify(dbuf
, menu_font_ns
, menu_item_text
[i
].c_str(), mx
,mx
+mw
, mdy
, mw
, menu_fg_color_ns
);
275 textout(dbuf
, menu_font_ns
, menu_item_text
[i
].c_str(), mx
, mdy
, menu_fg_color_ns
);
278 mdy
+= text_height(menu_font_ns
);
280 mdy
+= item_y_separation
;
284 blit(dbuf
, to_bitmap
, 0,0,0,0, dbuf
->w
, dbuf
->h
);
285 } // end of while !d_done
287 // ------------- End of main loop -------
288 menu_manager_stop_global_timer(); // we finish the timer
295 return mis
; // return menu item selected
298 // --------------------------------------------------------
300 // --------------------------------------------------------
302 void CQMenu::set_font_s(FONT
*f
) { menu_font_s
= f
; }
303 void CQMenu::set_font_ns(FONT
*f
) { menu_font_ns
= f
; }
304 void CQMenu::set_fg_color_ns(int fg
) { menu_fg_color_ns
= fg
; }
305 void CQMenu::set_bg_color_ns(int bg
) { menu_bg_color_ns
= bg
; }
306 void CQMenu::set_fg_color_s(int fg
) { menu_fg_color_s
= fg
; }
307 void CQMenu::set_bg_color_s(int bg
) { menu_bg_color_s
= bg
; }
309 void CQMenu::set_xywh(int x
, int y
, int w
, int h
) { mx
= x
; my
= y
; mw
= w
; mh
= h
; }
311 void CQMenu::set_to_bitmap(BITMAP
*b
) { to_bitmap
= b
; }
312 void CQMenu::set_back_bitmap(BITMAP
*b
) {back_bitmap
= b
; }
314 void CQMenu::set_callback_drawer(void (*c
)(CQMenu
&d
, bool do_logic
))
319 // --------------------------------------------------------
321 // --------------------------------------------------------
322 FONT
*CQMenu::get_font_s() { return menu_font_s
; }
323 FONT
*CQMenu::get_font_ns(){ return menu_font_ns
; }
324 int CQMenu::get_fg_color_ns() { return menu_fg_color_ns
; }
325 int CQMenu::get_bg_color_ns() { return menu_bg_color_ns
; }
326 int CQMenu::get_fg_color_s() { return menu_fg_color_s
; }
327 int CQMenu::get_bg_color_s() { return menu_bg_color_s
; }
328 int CQMenu::get_menu_item_count() { return menu_item_text
.size(); }
329 int CQMenu::get_x() { return mx
; }
330 int CQMenu::get_y() { return my
; }
331 int CQMenu::get_w() { return mw
; }
332 int CQMenu::get_h() { return mh
; }
333 BITMAP
*CQMenu::get_to_bitmap() { return to_bitmap
; }
334 BITMAP
*CQMenu::get_back_bitmap() { return back_bitmap
; }
336 string
CQMenu::get_menu_item_text(int item_index
)
338 if (item_index
< 0 || (unsigned)item_index
> menu_item_text
.size()-1) return menu_item_text
[0];
339 return menu_item_text
[item_index
];
342 // --------------------------------------------------------
343 // Small timer, goes in range 0 to BPS_OF_MENU_MANAGER
344 // --------------------------------------------------------
345 int CQMenu::get_time_counter()
350 // --------------------------------------------------------
351 // Big timer, goes in range 0 to ULONG_MAX
352 // --------------------------------------------------------
353 unsigned long int CQMenu::get_big_timer_counter()
355 return big_timer_counter
;