Logo Search packages:      
Sourcecode: kball version File versions  Download package

qmenu.cpp

// -----------------------------------------------
// qmenu.cpp
// -----------------------------------------------
// Quick menu system for my games/etc
// ----------------------------------------------- 
// Developed By Kronoman - Copyright (c) 2004
// In loving memory of my father
// -----------------------------------------------

// Don't open this file until doomsday!!

#include "qmenu.h"
#include "gerror.h" // error reporting
// -------------------------------------------------------------------
// This are allegro timer routines, needed to keep the 
// menu manager calling the events at constant rate
// -------------------------------------------------------------------
#include <limits.h> // need this to know the max size of the counter...

static volatile int time_counter = 0; // first timer counter, counts in range from 0..BPS_OF_MENU_MANAGER
static volatile unsigned long int big_timer_counter = 0; // second timer, counts in range from 0..maximun int
static volatile int speed_counter = 0; // this speed counter is to keep the rate of drawing at constant fps

// how many times we have installed / erased the timer handler?
// really, the timer gets installed only once, this only counts how many calls are made
// when reaches <= 0, the timer is uninstalled and the 2 counter resets to zero
static int timers_installed = 0; 

// --------------------------------------------------------
// The timer routine
// --------------------------------------------------------
static void menu_manager_timer_handler() 
{ 
      time_counter++; 
      big_timer_counter++;
      speed_counter++;
      if (time_counter > BPS_OF_MENU_MANAGER) time_counter = 0;
      if (big_timer_counter > ULONG_MAX - 2 ) big_timer_counter = 0; 
}
END_OF_FUNCTION(menu_manager_timer_handler);

// --------------------------------------------------------
// This starts the global timer
// --------------------------------------------------------
static void menu_manager_start_global_timer()
{
      if (timers_installed == 0)
      {
            // Install timer  
            LOCK_VARIABLE(time_counter);
            LOCK_VARIABLE(big_timer_counter);
            LOCK_VARIABLE(speed_counter);
                  LOCK_FUNCTION((void *)menu_manager_timer_handler);
            
            // DEBUG - this SHOULD BE CHECKED FOR ERROR!!!
            if (install_int_ex(menu_manager_timer_handler, BPS_TO_TIMER(BPS_OF_MENU_MANAGER)))
                  raise_error("menu_manager_start_global_timer():\nERROR: Can't install timer at %d bps\n", BPS_OF_MENU_MANAGER);
      }
      
      timers_installed++;      // this counts how many calls to this function were made
}


// --------------------------------------------------------
// This ends the global timer
// --------------------------------------------------------
static void menu_manager_stop_global_timer()
{
      if (timers_installed == 0) return; // no timer installed
            
      timers_installed--;     
      
      if (timers_installed <= 0)
      {
            // remove and reset the timer
            remove_int(menu_manager_timer_handler);
            time_counter = 0;
            speed_counter = 0;
            big_timer_counter = 0;
            timers_installed = 0;
      }
}

// ===================================================================
// ===================================================================
// From here is the menu class itself
// ===================================================================
// ===================================================================

CQMenu::CQMenu()
{
      // set defaults
      menu_font_s = menu_font_ns = font;
      menu_fg_color_ns = makecol(128,128,128);
      menu_bg_color_ns = -1;
      
      menu_fg_color_s = makecol(255,255,255);
      menu_bg_color_s = makecol(128,0,0);
      
      to_bitmap = screen;
      back_bitmap = NULL;

      mx = my = 0;
      mw = 640;
      mh = 480;
      item_y_separation = 0;
      text_align = 0;   
      callback = NULL;

      // DEBUG - we use all available controllers by default
      control.set_use_mouse(true);
      control.set_use_joystick(true);
      control.set_use_keyboard(true);
      
      this->clear_menu_list();
}

CQMenu::~CQMenu()
{
      this->clear_menu_list();
}

// --------------------------------------------------------
// empty the menu item list
// --------------------------------------------------------
void CQMenu::clear_menu_list()
{
      menu_item_text.clear();
}

// --------------------------------------------------------
// adds a selectable item to the menu
// returns the index of the item added, or -1 on error (menu full)
// --------------------------------------------------------
int CQMenu::add_item_to_menu(string item)
{
      menu_item_text.push_back(item);
      
      return menu_item_text.size(); // done
}

// --------------------------------------------------------
// Starts the menu loop until the user selects one
// Return the index of the item selected by the user (starts at 0).
// selected is the item to let selected by default
// --------------------------------------------------------
int CQMenu::do_the_menu(int selected)
{
      bool d_done = false; // dialog done?
      int mis = 0; // mis == Menu Item Selected (current selected item by user)
      int cret = 0; // controller return value
      int mhi = 0; // menu item height (measured in items)
      int msd = 0, med = 0, mdy = 0; // menu start drawing, menu end drawing, menu draw Y
      int last_hit = 0; // timer to last hit of a key, to take a small delay; if not, the menu scrolls too fast :(
      BITMAP *dbuf = NULL; // double buffer
      
      if (menu_item_text.size() < 1) raise_error("CQMenu::do_the_menu()\nERROR: no items in menu\n");
            
      if (to_bitmap == NULL) raise_error("CQMenu::do_the_menu()\nERROR: no destination bitmap to render the menu\n");

      menu_manager_start_global_timer(); // we need a timer running
      
      dbuf = create_bitmap(to_bitmap->w, to_bitmap->h); // double buffer
      if (dbuf == NULL) raise_error("CQMenu::do_the_menu()\nERROR: unable to create doble buffer bitmap!\n");
      
      mhi = mh / (text_height(menu_font_ns)+item_y_separation); // how many items can we fit vertically?
      
      // ------------- Main loop --------------
      clear_keybuf();
      rest(25);
      clear_keybuf();
      
      show_mouse(NULL); // go to hell, little mouse cursor :P
      mis = selected;
      if (mis < 0 || (unsigned)mis > menu_item_text.size()-1) mis = 0;
      
      while (!d_done)
      {
            while (speed_counter > 0)
            {
                  // update menu logic here

                  cret = control.do_input_poll();
                  
                  // if the last hit was very close (1/6 of second), ignore input
                  if (abs((int)big_timer_counter - last_hit) < BPS_OF_MENU_MANAGER/10)
                  {
                        key[KEY_UP] = key[KEY_DOWN] = key[KEY_ENTER] = key[KEY_SPACE]=0;
                        clear_keybuf();
                        cret = 0;
                  }
                  
                  if ((cret & KC_UP) || (cret & KC_LEFT) || key[KEY_UP])
                  {
                        mis--;
                        // take a little delay to let the guy release the key (if any)
                        clear_keybuf();
                        while (keypressed()) readkey();
                        last_hit = big_timer_counter;
                  }
                  
                  if ((cret & KC_DOWN) || (cret & KC_RIGHT) || key[KEY_DOWN])
                  {
                        mis++;
                        // take a little delay to let the guy release the key (if any)
                        clear_keybuf();
                        while (keypressed()) readkey();
                        last_hit = big_timer_counter;
                  }
                  
                  // don't let the selection go out of bounds
                  if (mis < 0) mis = menu_item_text.size()-1;
                  if ((unsigned)mis > menu_item_text.size()-1) mis = 0; 
                  
                  if ((cret & KC_BTN1) || (cret & KC_BTN2) || (cret & KC_BTN3) || key[KEY_ENTER] || key[KEY_SPACE]) d_done = true; // done, item selected wow!
                        
                  if (callback != NULL) callback(*this, true); // call the callback if needed, to update logic
                  speed_counter--;
                  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)
                  
                  //if (key[KEY_ESC]) d_done = true; // emergency key :^O
            }
            
            // here update screen
            if (back_bitmap) 
                  { blit(back_bitmap, dbuf, 0,0,0,0,back_bitmap->w, back_bitmap->h);}
            else
                  { clear(dbuf); }
            
            if (callback != NULL) callback(*this, false); // call the callback if needed, to update screen
            
            // Here we draw the menu
            msd = mis - mhi/2 - 1; // menu start drawing from here
            if (msd < 0) msd = 0;
            med = msd + mhi; // menu drawing ends here
            if ((unsigned)med > menu_item_text.size()) med = menu_item_text.size();
            mdy = my;
            for (int i = msd; i < med; i ++)
            {
                  if (i == mis) // is selected or unselected item?
                  {
                        text_mode(menu_bg_color_s);
                        switch (text_align) // wich text align to use?
                        {
                              case 1:
                                    textout_right(dbuf, menu_font_s, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_s);
                              break;
                              case 2:
                                    textout_centre(dbuf, menu_font_s, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_s);
                              break;
                              case 3:
                                    textout_justify(dbuf, menu_font_s, menu_item_text[i].c_str(), mx,mx+mw, mdy, mw, menu_fg_color_s);
                              break;
                              default:
                                    textout(dbuf, menu_font_s, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_s);
                              break;
                        }
                        mdy += text_height(menu_font_s);
                  }
                  else
                  {
                        text_mode(menu_bg_color_ns);
                        switch (text_align)
                        {
                              case 1:
                                    textout_right(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_ns);
                              break;
                              case 2:
                                    textout_centre(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_ns);
                              break;
                              case 3:
                                    textout_justify(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx,mx+mw, mdy, mw, menu_fg_color_ns);
                              break;
                              default:
                                    textout(dbuf, menu_font_ns, menu_item_text[i].c_str(), mx, mdy, menu_fg_color_ns);
                              break;
                        }
                        mdy += text_height(menu_font_ns);
                  }
                  mdy += item_y_separation;
            }
            
            // show it
            blit(dbuf, to_bitmap, 0,0,0,0, dbuf->w, dbuf->h);
      } // end of while !d_done

      // ------------- End of main loop -------
      menu_manager_stop_global_timer(); // we finish the timer 
      text_mode(-1);

      clear_keybuf();
      rest(25);
      clear_keybuf();
      
      return mis; // return menu item selected
}

// --------------------------------------------------------
// Set functions
// --------------------------------------------------------

void CQMenu::set_font_s(FONT *f) { menu_font_s = f; }
void CQMenu::set_font_ns(FONT *f) { menu_font_ns = f; }
void CQMenu::set_fg_color_ns(int fg) { menu_fg_color_ns = fg; }
void CQMenu::set_bg_color_ns(int bg) { menu_bg_color_ns = bg; }
void CQMenu::set_fg_color_s(int fg) { menu_fg_color_s = fg; }
void CQMenu::set_bg_color_s(int bg) { menu_bg_color_s = bg; }

void CQMenu::set_xywh(int x, int y, int w, int h) { mx = x; my = y; mw = w; mh = h; }

void CQMenu::set_to_bitmap(BITMAP *b) { to_bitmap = b; }
void CQMenu::set_back_bitmap(BITMAP *b) {back_bitmap = b; }

void CQMenu::set_callback_drawer(void (*c)(CQMenu &d, bool do_logic))
{
      callback = c;     
}

// --------------------------------------------------------
// Get functions
// --------------------------------------------------------
FONT *CQMenu::get_font_s() { return menu_font_s; }
FONT *CQMenu::get_font_ns(){ return menu_font_ns; }
int CQMenu::get_fg_color_ns() { return menu_fg_color_ns; }
int CQMenu::get_bg_color_ns() { return menu_bg_color_ns; }
int CQMenu::get_fg_color_s() { return menu_fg_color_s; }
int CQMenu::get_bg_color_s() { return menu_bg_color_s; }
int CQMenu::get_menu_item_count() { return menu_item_text.size(); }
int CQMenu::get_x() { return mx; }
int CQMenu::get_y() { return my; }
int CQMenu::get_w() { return mw; }
int CQMenu::get_h() { return mh; }
BITMAP *CQMenu::get_to_bitmap() { return to_bitmap; }
BITMAP *CQMenu::get_back_bitmap() { return back_bitmap; }

string CQMenu::get_menu_item_text(int item_index)
{
      if (item_index < 0 || (unsigned)item_index > menu_item_text.size()-1) return menu_item_text[0];
      return menu_item_text[item_index];  
} 

// --------------------------------------------------------
// Small timer, goes in range 0 to BPS_OF_MENU_MANAGER
// --------------------------------------------------------
int CQMenu::get_time_counter()
{
      return time_counter;
}

// --------------------------------------------------------
// Big timer, goes in range 0 to ULONG_MAX
// --------------------------------------------------------
unsigned long int CQMenu::get_big_timer_counter()
{
      return big_timer_counter;
}


Generated by  Doxygen 1.6.0   Back to index