Store levels edited by the user in $HOME
[kball.git] / src / qmenu.cpp
blob8c11f642a224376dfb3e58ffc1ae7bae52fb7986
1 // -----------------------------------------------
2 // qmenu.cpp
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!!
12 #include "qmenu.h"
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 // --------------------------------------------------------
30 // The timer routine
31 // --------------------------------------------------------
32 static void menu_manager_timer_handler()
34 time_counter++;
35 big_timer_counter++;
36 speed_counter++;
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)
49 // Install timer
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
71 timers_installed--;
73 if (timers_installed <= 0)
75 // remove and reset the timer
76 remove_int(menu_manager_timer_handler);
77 time_counter = 0;
78 speed_counter = 0;
79 big_timer_counter = 0;
80 timers_installed = 0;
84 // ===================================================================
85 // ===================================================================
86 // From here is the menu class itself
87 // ===================================================================
88 // ===================================================================
90 CQMenu::CQMenu()
92 // set defaults
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);
100 to_bitmap = screen;
101 back_bitmap = NULL;
103 mx = my = 0;
104 mw = 640;
105 mh = 480;
106 item_y_separation = 0;
107 text_align = 0;
108 callback = NULL;
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();
118 CQMenu::~CQMenu()
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 --------------
169 clear_keybuf();
170 rest(25);
171 clear_keybuf();
173 show_mouse(NULL); // go to hell, little mouse cursor :P
174 mis = selected;
175 if (mis < 0 || (unsigned)mis > menu_item_text.size()-1) mis = 0;
177 while (!d_done)
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;
189 clear_keybuf();
190 cret = 0;
193 if ((cret & KC_UP) || (cret & KC_LEFT) || key[KEY_UP])
195 mis--;
196 // take a little delay to let the guy release the key (if any)
197 clear_keybuf();
198 while (keypressed()) readkey();
199 last_hit = big_timer_counter;
202 if ((cret & KC_DOWN) || (cret & KC_RIGHT) || key[KEY_DOWN])
204 mis++;
205 // take a little delay to let the guy release the key (if any)
206 clear_keybuf();
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
218 speed_counter--;
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
225 if (back_bitmap)
226 { blit(back_bitmap, dbuf, 0,0,0,0,back_bitmap->w, back_bitmap->h);}
227 else
228 { clear(dbuf); }
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();
237 mdy = my;
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?
245 case 1:
246 textout_right(dbuf, menu_font_s, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_s);
247 break;
248 case 2:
249 textout_centre(dbuf, menu_font_s, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_s);
250 break;
251 case 3:
252 textout_justify(dbuf, menu_font_s, menu_item_text[i].c_str(), mx,mx+mw, mdy, mw, menu_fg_color_s);
253 break;
254 default:
255 textout(dbuf, menu_font_s, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_s);
256 break;
258 mdy += text_height(menu_font_s);
260 else
262 text_mode(menu_bg_color_ns);
263 switch (text_align)
265 case 1:
266 textout_right(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_ns);
267 break;
268 case 2:
269 textout_centre(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_ns);
270 break;
271 case 3:
272 textout_justify(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx,mx+mw, mdy, mw, menu_fg_color_ns);
273 break;
274 default:
275 textout(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_ns);
276 break;
278 mdy += text_height(menu_font_ns);
280 mdy += item_y_separation;
283 // show it
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
289 text_mode(-1);
291 clear_keybuf();
292 rest(25);
293 clear_keybuf();
295 return mis; // return menu item selected
298 // --------------------------------------------------------
299 // Set functions
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))
316 callback = c;
319 // --------------------------------------------------------
320 // Get functions
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()
347 return 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;