Code Search for Developers
 
 
  

guiproc.c from Allegro game programming library at Krugle


Show guiproc.c syntax highlighted

/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      The standard GUI dialog object procedures.
 *
 *      By Shawn Hargreaves.
 *
 *      Radio button, icon, and slider objects by Chris La Mantia.
 *
 *      Scrolling al_dialog_edit_proc by VolkerOth.
 *
 *      Text box object by Doug Eleveld.
 *
 *      al_dialog_text_list_proc by Andy Goth.
 *
 *      See readme.txt for copyright information.
 */


#include "allegro.h"
#include "allegro/internal/aintern.h"



/* typedef for the listbox callback functions */
typedef char *(*getfuncptr)(int, int *);



/* al_gui_put_text:
 *  Wrapper function for drawing text to the al_screen, which interprets the
 *  & character as an underbar for displaying keyboard shortcuts. Returns
 *  the width of the output string in pixels.
 */
int al_gui_put_text(AL_BITMAP *bmp, AL_CONST char *s, int x, int y, int color, int centre)
{
   char tmp[1024];
   int hline_pos = -1;
   int len = 0;
   int in_pos = 0;
   int out_pos = 0;
   int pix_len, c;

   while (((c = al_ugetc(s+in_pos)) != 0) && (out_pos<(int)(sizeof(tmp)-al_ucwidth(0)))) {
      if (c == '&') {
	 in_pos += al_uwidth(s+in_pos);
	 c = al_ugetc(s+in_pos);
	 if (c == '&') {
	    out_pos += al_usetc(tmp+out_pos, '&');
	    in_pos += al_uwidth(s+in_pos);
	    len++;
	 }
	 else
	    hline_pos = len;
      }
      else {
	 out_pos += al_usetc(tmp+out_pos, c);
	 in_pos += al_uwidth(s+in_pos);
	 len++;
      }
   }

   al_usetc(tmp+out_pos, 0);
   pix_len = al_text_length(al_font_8x8, tmp);

   if (centre)
      x -= pix_len / 2;

   if (bmp) {
      al_put_text(bmp, al_font_8x8, tmp, x, y, color);

      if (hline_pos >= 0) {
	 c = al_ugetat(tmp, hline_pos);
	 al_usetat(tmp, hline_pos, 0);
	 hline_pos = al_text_length(al_font_8x8, tmp);
	 c = al_usetc(tmp, c);
	 al_usetc(tmp+c, 0);
	 c = al_text_length(al_font_8x8, tmp);
	 al_draw_hline(bmp, x+hline_pos, y+al_text_height(al_font_8x8)-al_gui_font_baseline, x+hline_pos+c-1, color);
      }
   }

   return pix_len;
}



/* al_gui_strlen:
 *  Returns the length of a string in pixels, ignoring '&' characters.
 */
int al_gui_strlen(AL_CONST char *s)
{
   return al_gui_put_text(NULL, s, 0, 0, 0, 0);
}



/* dotted_rect:
 *  Draws a dotted rectangle, for showing an object has the input focus.
 */
static void dotted_rect(int x1, int y1, int x2, int y2, int fg, int bg)
{
   int x = ((x1+y1) & 1) ? 1 : 0;
   int c;

   /* two loops to avoid bank switches */
   for (c=x1; c<=x2; c++)
      al_put_pixel(al_screen, c, y1, (((c+y1) & 1) == x) ? fg : bg);
   for (c=x1; c<=x2; c++)
      al_put_pixel(al_screen, c, y2, (((c+y2) & 1) == x) ? fg : bg);

   for (c=y1+1; c<y2; c++) {
      al_put_pixel(al_screen, x1, c, (((c+x1) & 1) == x) ? fg : bg);
      al_put_pixel(al_screen, x2, c, (((c+x2) & 1) == x) ? fg : bg);
   }
}



/* al_dialog_yield_proc:
 *  Simple dialog procedure which just yields the timeslice when the dialog
 *  is idle.
 */
int al_dialog_yield_proc(int msg, AL_DIALOG *d, int c)
{
   if (msg == MSG_IDLE)
      al_yield_timeslice();

   return D_O_K;
}



/* al_dialog_clear_proc:
 *  Simple dialog procedure which just clears the al_screen. Useful as the
 *  first object in a dialog.
 */
int al_dialog_clear_proc(int msg, AL_DIALOG *d, int c)
{
   if (msg == MSG_DRAW) {
      al_set_clip(al_screen, 0, 0, AL_SCREEN_W-1, AL_SCREEN_H-1);
      al_clear_to_color(al_screen, d->bg);
   }

   return D_O_K;
}



/* al_dialog_box_proc:
 *  Simple dialog procedure: just draws a box.
 */
int al_dialog_box_proc(int msg, AL_DIALOG *d, int c)
{
   if (msg==MSG_DRAW) {
      int fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
      al_draw_rect_fill(al_screen, d->x+1, d->y+1, d->x+d->w-2, d->y+d->h-2, d->bg);
      al_draw_rect(al_screen, d->x, d->y, d->x+d->w-1, d->y+d->h-1, fg);
   }

   return D_O_K;
}



/* al_dialog_shadow_box_proc:
 *  Simple dialog procedure: draws a box with a shadow.
 */
int al_dialog_shadow_box_proc(int msg, AL_DIALOG *d, int c)
{
   if (msg==MSG_DRAW) {
      int fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
      int black = al_make_color(0,0,0);
      al_draw_rect_fill(al_screen, d->x+1, d->y+1, d->x+d->w-3, d->y+d->h-3, d->bg);
      al_draw_rect(al_screen, d->x, d->y, d->x+d->w-2, d->y+d->h-2, fg);
      al_draw_vline(al_screen, d->x+d->w-1, d->y+1, d->y+d->h-1, black);
      al_draw_hline(al_screen, d->x+1, d->y+d->h-1, d->x+d->w-1, black);
   }

   return D_O_K;
}



/* al_dialog_bitmap_proc:
 *  Simple dialog procedure: draws the bitmap which is pointed to by dp.
 */
int al_dialog_bitmap_proc(int msg, AL_DIALOG *d, int c)
{
   AL_BITMAP *b = (AL_BITMAP *)d->dp;

   if (msg==MSG_DRAW)
      al_blit(b, al_screen, 0, 0, d->x, d->y, d->w, d->h);

   return D_O_K;
}



/* al_dialog_text_proc:
 *  Simple dialog procedure: draws the text string which is pointed to by dp.
 */
int al_dialog_text_proc(int msg, AL_DIALOG *d, int c)
{
   if (msg==MSG_DRAW) {
      int rtm;
      int fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
      AL_FONT *oldfont = al_font_8x8;

      if (d->dp2)
	 al_font_8x8 = d->dp2;

      rtm = al_text_mode(d->bg);
      al_gui_put_text(al_screen, d->dp, d->x, d->y, fg, FALSE);
      al_text_mode(rtm);

      al_font_8x8 = oldfont;
   }

   return D_O_K;
}



/* al_dialog_ctext_proc:
 *  Simple dialog procedure: draws the text string which is pointed to by dp,
 *  centering it around the object's x coordinate.
 */
int al_dialog_ctext_proc(int msg, AL_DIALOG *d, int c)
{
   if (msg==MSG_DRAW) {
      int rtm;
      int fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
      AL_FONT *oldfont = al_font_8x8;

      if (d->dp2)
	 al_font_8x8 = d->dp2;

      rtm = al_text_mode(d->bg);
      al_gui_put_text(al_screen, d->dp, d->x, d->y, fg, TRUE);
      al_text_mode(rtm);

      al_font_8x8 = oldfont;
   }

   return D_O_K;
}



/* al_dialog_rtext_proc:
 *  Simple dialog procedure: draws the text string which is pointed to by dp,
 *  right aligning it.
 */
int al_dialog_rtext_proc(int msg, AL_DIALOG *d, int c)
{
   if (msg==MSG_DRAW) {
      int rtm;
      int fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
      AL_FONT *oldfont = al_font_8x8;

      if (d->dp2)
	 al_font_8x8 = d->dp2;

      rtm = al_text_mode(d->bg);
      al_gui_put_text(al_screen, d->dp, d->x + d->w - al_gui_strlen(d->dp), d->y, fg, FALSE);
      al_text_mode(rtm);

      al_font_8x8 = oldfont;
   }

   return D_O_K;
}



/* al_dialog_button_proc:
 *  A button object (the dp field points to the text string). This object
 *  can be selected by clicking on it with the mouse or by pressing its 
 *  keyboard shortcut. If the D_EXIT flag is set, selecting it will close 
 *  the dialog, otherwise it will toggle on and off.
 */
int al_dialog_button_proc(int msg, AL_DIALOG *d, int c)
{
   int state1, state2;
   int black;
   int swap;
   int g;
   int rtm;

   switch (msg) {

      case MSG_DRAW:
	 if (d->flags & D_SELECTED) {
	    g = 1;
	    state1 = d->bg;
	    state2 = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
	 }
	 else {
	    g = 0; 
	    state1 = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
	    state2 = d->bg;
	 }

	 al_draw_rect_fill(al_screen, d->x+1+g, d->y+1+g, d->x+d->w-3+g, d->y+d->h-3+g, state2);
	 al_draw_rect(al_screen, d->x+g, d->y+g, d->x+d->w-2+g, d->y+d->h-2+g, state1);
	 rtm = al_text_mode(-1);
	 al_gui_put_text(al_screen, d->dp, d->x+d->w/2+g, d->y+d->h/2-al_text_height(al_font_8x8)/2+g, state1, TRUE);
	 al_text_mode(rtm);

	 if (d->flags & D_SELECTED) {
	    al_draw_vline(al_screen, d->x, d->y, d->y+d->h-2, d->bg);
	    al_draw_hline(al_screen, d->x, d->y, d->x+d->w-2, d->bg);
	 }
	 else {
	    black = al_make_color(0,0,0);
	    al_draw_vline(al_screen, d->x+d->w-1, d->y+1, d->y+d->h-2, black);
	    al_draw_hline(al_screen, d->x+1, d->y+d->h-1, d->x+d->w-1, black);
	 }
	 if ((d->flags & D_GOTFOCUS) && 
	     (!(d->flags & D_SELECTED) || !(d->flags & D_EXIT)))
	    dotted_rect(d->x+1+g, d->y+1+g, d->x+d->w-3+g, d->y+d->h-3+g, state1, state2);
	 break;

      case MSG_WANTFOCUS:
	 return D_WANTFOCUS;

      case MSG_KEY:
	 /* close dialog? */
	 if (d->flags & D_EXIT) {
	    return D_CLOSE;
	 }

	 /* or just toggle */
	 d->flags ^= D_SELECTED;
	 al_scare_mouse();
	 al_object_message(d, MSG_DRAW, 0);
	 al_unscare_mouse();
	 break;

      case MSG_CLICK:
	 /* what state was the button originally in? */
	 state1 = d->flags & D_SELECTED;
	 if (d->flags & D_EXIT)
	    swap = FALSE;
	 else
	    swap = state1;

	 /* track the mouse until it is released */
	 while (al_gui_mouse_b()) {
	    state2 = ((al_gui_mouse_x() >= d->x) && (al_gui_mouse_y() >= d->y) &&
		      (al_gui_mouse_x() < d->x + d->w) && (al_gui_mouse_y() < d->y + d->h));
	    if (swap)
	       state2 = !state2;

	    /* redraw? */
	    if (((state1) && (!state2)) || ((state2) && (!state1))) {
	       d->flags ^= D_SELECTED;
	       state1 = d->flags & D_SELECTED;
	       al_scare_mouse();
	       al_object_message(d, MSG_DRAW, 0);
	       al_unscare_mouse();
	    }

	    /* let other objects continue to animate */
	    al_broadcast_dialog_message(MSG_IDLE, 0);
	 }

	 /* should we close the dialog? */
	 if ((d->flags & D_SELECTED) && (d->flags & D_EXIT)) {
	    d->flags ^= D_SELECTED;
	    return D_CLOSE;
	 }
	 break; 
   }

   return D_O_K;
}



/* al_dialog_check_proc:
 *  Who needs C++ after all? This is derived from al_dialog_button_proc, 
 *  but overrides the drawing routine to provide a check box.
 */
int al_dialog_check_proc(int msg, AL_DIALOG *d, int c)
{
   int x;
   int fg, bg;

   if (msg==MSG_DRAW) {
      int rtm;

      fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
      bg = (d->bg < 0) ? al_gui_bg_color : d->bg;

      rtm = al_text_mode(d->bg);

      x = d->x + ((d->d1) ? 0 : al_gui_put_text(al_screen, d->dp, d->x, d->y+(d->h-(al_text_height(al_font_8x8)-al_gui_font_baseline))/2, fg, FALSE) + al_text_height(al_font_8x8)/2);
      al_draw_rect_fill(al_screen, x+1, d->y+1, x+d->h-2, d->y+d->h-2, bg);
      al_draw_rect(al_screen, x, d->y, x+d->h-1, d->y+d->h-1, fg);
      if (d->d1)
	 al_gui_put_text(al_screen, d->dp, x+d->h-1+al_text_height(al_font_8x8)/2, d->y+(d->h-(al_text_height(al_font_8x8)-al_gui_font_baseline))/2, fg, FALSE);
      if (d->flags & D_SELECTED) {
	 al_draw_line(al_screen, x, d->y, x+d->h-1, d->y+d->h-1, fg);
	 al_draw_line(al_screen, x, d->y+d->h-1, x+d->h-1, d->y, fg);
      }
      if (d->flags & D_GOTFOCUS)
	 dotted_rect(x+1, d->y+1, x+d->h-2, d->y+d->h-2, fg, bg);

      al_text_mode(rtm);

      return D_O_K;
   } 

   return al_dialog_button_proc(msg, d, 0);
}



/* al_dialog_radio_proc:
 *  GUI procedure for radio buttons.
 *  Parameters: d1-button group number; d2-button style (0=al_draw_circle,1=square);
 *  dp-text to appear as label to the right of the button.
 */
int al_dialog_radio_proc(int msg, AL_DIALOG *d, int c)
{
   int x, center, r, ret, fg, bg;
   int rtm;

   switch(msg) {

      case MSG_DRAW:
	 fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
	 bg = (d->bg < 0) ? al_gui_bg_color : d->bg;

	 rtm = al_text_mode(d->bg);
	 al_gui_put_text(al_screen, d->dp, d->x+d->h-1+al_text_height(al_font_8x8), d->y+(d->h-(al_text_height(al_font_8x8)-al_gui_font_baseline))/2, fg, FALSE);
	 al_text_mode(rtm);

	 x = d->x;
	 r = d->h/2;

	 center = x+r;
	 al_draw_rect_fill(al_screen, x, d->y, x+d->h-1, d->y+d->h-1, bg);

	 switch (d->d2) {

	    case 1:
	       al_draw_rect(al_screen, x, d->y, x+d->h-1, d->y+d->h-1, fg);
	       if (d->flags & D_SELECTED)
		  al_draw_rect_fill(al_screen, x+r/2, d->y+r/2, x+d->h-1-r/2, d->y+d->h-1-r/2, fg);
	       break;

	    default:
	       al_draw_circle(al_screen, center, d->y+r, r, fg);
	       if (d->flags & D_SELECTED)
		  al_draw_circle_fill(al_screen, center, d->y+r, r/2, fg);
	       break;
	 }

	 if (d->flags & D_GOTFOCUS) {
	    if (d->d2 == 1)
	       dotted_rect(x+1, d->y+1, x+d->h-2, d->y+d->h-2, fg, bg);
	    else
	       dotted_rect(x, d->y, x+d->h-1, d->y+d->h-1, fg, bg);
	 }

	 return D_O_K;

      case MSG_KEY:
      case MSG_CLICK:
	 if (d->flags & D_SELECTED) {
	    return D_O_K;
	 }
      break;

      case MSG_RADIO:
	 if ((c == d->d1) && (d->flags & D_SELECTED)) {
	    d->flags &= ~D_SELECTED;
	    al_scare_mouse();
	    al_object_message(d, MSG_DRAW, 0);
	    al_unscare_mouse();
	 }
	 break;
   }

   ret = al_dialog_button_proc(msg, d, 0);

   if (((msg==MSG_KEY) || (msg==MSG_CLICK)) &&
       (d->flags & D_SELECTED) && (!(d->flags & D_EXIT))) {
      d->flags &= ~D_SELECTED;
      al_broadcast_dialog_message(MSG_RADIO, d->d1);
      d->flags |= D_SELECTED;
   }

   return ret;
}



/* al_dialog_icon_proc:
 *  Allows graphic icons to be used as buttons.
 * 
 *  Parameters:
 *    fg = color dotted al_draw_line showing focus will be drawn in
 *    bg = shadow color used to fill in top and left sides of
 *         button when "pressed"
 *    d1 = "push depth": number of pixels icon will be shifted
 *         to right and down when selected (default=2) if there is
 *         no "selected" image
 *    d2 = distance dotted al_draw_line showing focus is indented (default=2)
 *    dp = pointer to a bitmap for the icon
 *    dp2 = pointer to a "selected" bitmap for the icon (OPTIONAL)
 *    dp3 = pointer to a "disabled" bitmap for the icon (OPTIONAL)
 */
int al_dialog_icon_proc(int msg, AL_DIALOG *d, int c)
{
   AL_BITMAP *butimage = (AL_BITMAP *)d->dp;
   int butx;
   int buty;
   int index;
   int indent;
   int depth;

   if ((msg == MSG_DRAW) && (!(d->flags & D_HIDDEN))) {
      depth = 0;
      if ((d->dp2 == NULL) && (d->flags & D_SELECTED)) {
	 depth = d->d1;
	 if (depth<1)
	    depth = 2;
      }
      if ((d->dp2 != NULL) && (d->flags & D_SELECTED)) {
	 butimage = (AL_BITMAP *)d->dp2;
      }
      if ((d->dp3 != NULL) && (d->flags & D_DISABLED)) {
	 butimage = (AL_BITMAP *)d->dp3;
      }
      indent = d->d2;
      if (indent==0)
	 indent = 2;

      /* put the graphic on al_screen, scaled as needed */
      butx = butimage->w;
      buty = butimage->h;
      al_stretch(butimage, al_screen, 0, 0, butx-depth, buty-depth, 
		   d->x+depth, d->y+depth, d->w-depth, d->h-depth);

      if ((d->flags & D_GOTFOCUS) &&
	  (!(d->flags & D_SELECTED) || !(d->flags & D_EXIT))) {
	 /* draw focus lines */
	 for (index=indent; index<d->w-(indent+1); index+=2) {
	    al_put_pixel(al_screen, d->x+index+depth, d->y+indent+depth,d->fg);
	    al_put_pixel(al_screen, d->x+index+depth, d->y+d->h-(indent+1)+depth, d->fg);
	 }
	 for (index=indent; index<d->h-(indent+1); index+=2) {
	    al_put_pixel(al_screen, d->x+indent+depth, d->y+index+depth, d->fg);
	    al_put_pixel(al_screen, d->x+d->w-(indent+1)+depth, d->y+index+depth, d->fg);
	 }
      }

      /* draw shadowing */
      for (index=0; index<depth; index++) {
	  al_draw_hline(al_screen, d->x, d->y+index, d->x+d->w-1, d->bg);
	  al_draw_vline(al_screen, d->x+index, d->y, d->y+d->h-1, d->bg);
      }

      return D_O_K;
   }

   return al_dialog_button_proc(msg, d, c);
}



/* al_dialog_keyboard_proc:
 *  Invisible object for implementing keyboard shortcuts. When its al_key
 *  is pressed, it calls the function pointed to by dp. This should return
 *  an integer, which will be passed back to the dialog manager. The al_key
 *  can be specified by putting an ASCII code in the al_key field or by
 *  putting scancodes in d1 and d2.
 */
int al_dialog_keyboard_proc(int msg, AL_DIALOG *d, int c)
{
   int (*proc)();
   int ret = D_O_K;

   switch (msg) {

      case MSG_START:
	 d->w = d->h = 0;
	 break;

      case MSG_XCHAR:
	 if (((c>>8) != d->d1) && ((c>>8) != d->d2))
	    break;

	 ret |= D_USED_CHAR;
	 /* fall through */

      case MSG_KEY:
	 proc = d->dp;
	 ret |= (*proc)();
	 break;
   }

   return ret;
}



/* al_dialog_edit_proc:
 *  An editable text object (the dp field points to the string). When it
 *  has the input focus (obtained by clicking on it with the mouse), text
 *  can be typed into this object. The d1 field specifies the maximum
 *  number of characters that it will accept, and d2 is the text cursor 
 *  position within the string.
 */
int al_dialog_edit_proc(int msg, AL_DIALOG *d, int c)
{
   static int ignore_next_uchar = FALSE;
   int last_was_space, new_pos, i, k;
   int f, l, p, w, x, fg, b, scroll;
   char buf[16];
   char *s, *t;
   int rtm;

   s = d->dp;
   l = al_ustrlen(s);
   if (d->d2 > l) 
      d->d2 = l;

   /* calculate maximal number of displayable characters */
   if (d->d2 == l)  {
      al_usetc(buf+al_usetc(buf, ' '), 0);
      x = al_text_length(al_font_8x8, buf);
   }
   else
      x = 0;

   b = 0;

   for (p=d->d2; p>=0; p--) {
      al_usetc(buf+al_usetc(buf, al_ugetat(s, p)), 0);
      x += al_text_length(al_font_8x8, buf);
      b++;
      if (x > d->w) 
	 break;
   }

   if (x <= d->w) {
      b = l; 
      scroll = FALSE;
   }
   else {
      b--; 
      scroll = TRUE;
   }

   switch (msg) {

      case MSG_START:
	 d->d2 = l;
	 break;

      case MSG_DRAW:
	 fg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;
	 x = 0;

	 if (scroll) {
	    p = d->d2-b+1; 
	    b = d->d2; 
	 }
	 else 
	    p = 0; 

	 for (; p<=b; p++) {
	    f = al_ugetat(s, p);
	    al_usetc(buf+al_usetc(buf, (f) ? f : ' '), 0);
	    w = al_text_length(al_font_8x8, buf);
	    if (x+w > d->w) 
	       break;
	    f = ((p == d->d2) && (d->flags & D_GOTFOCUS));
	    rtm = al_text_mode((f) ? fg : d->bg);
	    al_put_text(al_screen, al_font_8x8, buf, d->x+x, d->y, (f) ? d->bg : fg);
	    al_text_mode(rtm);
	    x += w;
	 }
	 if (x < d->w)
	    al_draw_rect_fill(al_screen, d->x+x, d->y, d->x+d->w-1, d->y+al_text_height(al_font_8x8)-1, d->bg);
	 break;

      case MSG_CLICK:
	 x = d->x;

	 if (scroll) {
	    p = d->d2-b+1; 
	    b = d->d2; 
	 }
	 else
	    p = 0; 

	 for (; p<b; p++) {
	    al_usetc(buf+al_usetc(buf, al_ugetat(s, p)), 0);
	    x += al_text_length(al_font_8x8, buf);
	    if (x > al_gui_mouse_x()) 
	       break;
	 }
	 d->d2 = MID(0, p, l);
	 al_scare_mouse();
	 al_object_message(d, MSG_DRAW, 0);
	 al_unscare_mouse();
	 break;

      case MSG_WANTFOCUS:
      case MSG_LOSTFOCUS:
      case MSG_KEY:
	 return D_WANTFOCUS;

      case MSG_CHAR:
	 ignore_next_uchar = FALSE;

	 if ((c >> 8) == AL_KEY_LEFT) {
	    if (d->d2 > 0) {
	       if (al_key_shifts & KB_CTRL_FLAG) {
		  last_was_space = TRUE;
		  new_pos = 0;
		  t = s;
		  for (i = 0; i < d->d2; i++) {
		     k = al_ugetx(&t);
		     if (al_uisspace(k))
			last_was_space = TRUE;
		     else if (last_was_space) {
			last_was_space = FALSE;
			new_pos = i;
		     }
		  }
		  d->d2 = new_pos;
	       }
	       else
		  d->d2--;
	    }
	 }
	 else if ((c >> 8) == AL_KEY_RIGHT) {
	    if (d->d2 < l) {
	       if (al_key_shifts & KB_CTRL_FLAG) {
		  t = s + al_uoffset(s, d->d2);
		  for (k = al_ugetx(&t); al_uisspace(k); k = al_ugetx(&t))
		     d->d2++;
		  for (; k && !al_uisspace(k); k = al_ugetx(&t))
		     d->d2++;
	       }
	       else
		  d->d2++;
	    }
	 }
	 else if ((c >> 8) == AL_KEY_HOME) {
	    d->d2 = 0;
	 }
	 else if ((c >> 8) == AL_KEY_END) {
	    d->d2 = l;
	 }
	 else if ((c >> 8) == AL_KEY_DEL) {
	    if (d->d2 < l)
	       al_uremove(s, d->d2);
	 }
	 else if ((c >> 8) == AL_KEY_BACKSPACE) {
	    if (d->d2 > 0) {
	       d->d2--;
	       al_uremove(s, d->d2);
	    }
	 }
	 else if ((c >> 8) == AL_KEY_ENTER) {
	    if (d->flags & D_EXIT) {
	       al_scare_mouse();
	       al_object_message(d, MSG_DRAW, 0);
	       al_unscare_mouse();
	       return D_CLOSE;
	    }
	    else
	       return D_O_K;
	 }
	 else if ((c >> 8) == AL_KEY_TAB) {
	    ignore_next_uchar = TRUE;
	    return D_O_K;
	 }
	 else {
	    /* don't process regular keys here: MSG_UCHAR will do that */
	    break;
	 }
	 al_scare_mouse();
	 al_object_message(d, MSG_DRAW, 0);
	 al_unscare_mouse();
	 return D_USED_CHAR;

      case MSG_UCHAR:
	 if ((c >= ' ') && (al_uisok(c)) && (!ignore_next_uchar)) {
	    if (l < d->d1) {
	       al_uinsert(s, d->d2, c);
	       d->d2++;

	       al_scare_mouse();
	       al_object_message(d, MSG_DRAW, 0);
	       al_unscare_mouse();
	    }
	    return D_USED_CHAR;
	 }
	 break;
   }

   return D_O_K;
}



/* _handle_scrollable_click:
 *  Helper to process a click on a scrollable object.
 */
void _handle_scrollable_scroll_click(AL_DIALOG *d, int listsize, int *offset, int height)
{
   int xx, yy;
   int hh = d->h - 5;

   while (al_gui_mouse_b()) {
      int i = (hh * height + listsize/2) / listsize;
      int len = (hh * (*offset) + listsize/2) / listsize + 2;

      if ((al_gui_mouse_y() >= d->y+len) && (al_gui_mouse_y() <= d->y+len+i)) {
	 xx = al_gui_mouse_y() - len + 2;
	 while (al_gui_mouse_b()) {
	    yy = (listsize * (al_gui_mouse_y() - xx) + hh/2) / hh;
	    if (yy > listsize-height) 
	       yy = listsize-height;

	    if (yy < 0) 
	       yy = 0;

	    if (yy != *offset) {
	       *offset = yy;
	       al_scare_mouse();
	       al_object_message(d, MSG_DRAW, 0);
	       al_unscare_mouse();
	    }

	    /* let other objects continue to animate */
	    al_broadcast_dialog_message(MSG_IDLE, 0);
	 }
      }
      else {
	 if (al_gui_mouse_y() <= d->y+len) 
	    yy = *offset - height;
	 else 
	    yy = *offset + height;

	 if (yy > listsize-height) 
	    yy = listsize-height;

	 if (yy < 0) 
	    yy = 0;

	 if (yy != *offset) {
	    *offset = yy;
	    al_scare_mouse();
	    al_object_message(d, MSG_DRAW, 0);
	    al_unscare_mouse();
	 }
      }

      /* let other objects continue to animate */
      al_broadcast_dialog_message(MSG_IDLE, 0);
   }
}



/* _handle_scrollable_scroll:
 *  Helper function to scroll through a scrollable object.
 */
void _handle_scrollable_scroll(AL_DIALOG *d, int listsize, int *index, int *offset)
{
   int height = (d->h-4) / al_text_height(al_font_8x8);

   if (listsize <= 0) {
      *index = *offset = 0;
      return;
   }

   /* check selected item */
   if (*index < 0) 
      *index = 0;
   else if (*index >= listsize)
      *index = listsize - 1;

   /* check scroll position */
   while ((*offset > 0) && (*offset + height > listsize))
      (*offset)--;

   if (*offset >= *index) {
      if (*index < 0) 
	 *offset = 0;
      else
	 *offset = *index;
   }
   else {
      while ((*offset + height - 1) < *index)
	 (*offset)++;
   }
}



/* idle_cb:
 *  al_rest_callback() routine to keep dialogs animating nice and smoothly.
 */
static void idle_cb(void)
{
   al_broadcast_dialog_message(MSG_IDLE, 0);
}



/* _handle_listbox_click:
 *  Helper to process a click on a listbox, doing hit-testing and moving
 *  the selection.
 */
void _handle_listbox_click(AL_DIALOG *d)
{
   char *sel = d->dp2;
   int listsize, height;
   int i, j;

   (*(getfuncptr)d->dp)(-1, &listsize);
   if (!listsize)
      return;

   height = (d->h-4) / al_text_height(al_font_8x8);

   i = MID(0, ((al_gui_mouse_y() - d->y - 2) / al_text_height(al_font_8x8)), 
	      ((d->h-4) / al_text_height(al_font_8x8) - 1));
   i += d->d2;
   if (i < d->d2)
      i = d->d2;
   else {
      if (i > d->d2 + height-1)
	 i = d->d2 + height-1;
      if (i >= listsize)
	 i = listsize-1;
   }

   if (al_gui_mouse_y() <= d->y)
      i = MAX(i-1, 0);
   else if (al_gui_mouse_y() >= d->y+d->h-1)
      i = MIN(i+1, listsize-1);

   if (i != d->d1) {
      if (sel) {
	 if (al_key_shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG)) {
	    if ((al_key_shifts & KB_SHIFT_FLAG) || (d->flags & D_INTERNAL)) {
	       for (j=MIN(i, d->d1); j<=MAX(i, d->d1); j++)
		  sel[j] = TRUE;
	    }
	    else
	       sel[i] = !sel[i];
	 }
      }

      d->d1 = i;
      i = d->d2;
      _handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);

      d->flags |= D_DIRTY;

      if (i != d->d2)
	 al_rest_callback(MID(10, al_text_height(al_font_8x8)*16-d->h-1, 100), idle_cb);
   }
}



/* _draw_scrollable_frame:
 *  Helper function to draw a frame for all objects with vertical scrollbars.
 */
void _draw_scrollable_frame(AL_DIALOG *d, int listsize, int offset, int height, int fg_color, int bg)
{
   int i, len;
   AL_BITMAP *pattern;
   int xx, yy;

   /* draw frame */
   al_draw_rect(al_screen, d->x, d->y, d->x+d->w-1, d->y+d->h-1, fg_color);

   /* possibly draw scrollbar */
   if (listsize > height) {
      al_draw_vline(al_screen, d->x+d->w-13, d->y+1, d->y+d->h-2, fg_color);

      /* scrollbar with focus */ 
      if (d->flags & D_GOTFOCUS) {
	 dotted_rect(d->x+1, d->y+1, d->x+d->w-14, d->y+d->h-2, fg_color, bg);
	 dotted_rect(d->x+d->w-12, d->y+1, d->x+d->w-2, d->y+d->h-2, fg_color, bg);
      }
      else {
	 al_draw_rect(al_screen, d->x+1, d->y+1, d->x+d->w-14, d->y+d->h-2, bg);
	 al_draw_rect(al_screen, d->x+d->w-12, d->y+1, d->x+d->w-2, d->y+d->h-2, bg);
      }

      /* create and draw the scrollbar */
      pattern = al_create_bitmap(2, 2);
      i = ((d->h-5) * height + listsize/2) / listsize;
      xx = d->x+d->w-11;
      yy = d->y+2;

      al_put_pixel(pattern, 0, 1, bg);
      al_put_pixel(pattern, 1, 0, bg);
      al_put_pixel(pattern, 0, 0, fg_color);
      al_put_pixel(pattern, 1, 1, fg_color);

      if (offset > 0) {
	 len = (((d->h-5) * offset) + listsize/2) / listsize;
	 al_draw_rect_fill(al_screen, xx, yy, xx+8, yy+len, bg);
	 yy += len;
      }
      if (yy+i < d->y+d->h-3) {
	 al_drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0);
	 al_draw_rect_fill(al_screen, xx, yy, xx+8, yy+i, 0);
	 al_drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
	 yy += i+1;
	 al_draw_rect_fill(al_screen, xx, yy, xx+8, d->y+d->h-3, bg);
      }
      else {
	 al_drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0);
	 al_draw_rect_fill(al_screen, xx, yy, xx+8, d->y+d->h-3, 0);
	 al_drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      }
      al_destroy_bitmap(pattern);
   }
   else {
      /* no scrollbar necessary */
      if (d->flags & D_GOTFOCUS)
	 dotted_rect(d->x+1, d->y+1, d->x+d->w-2, d->y+d->h-2, fg_color, bg);
      else
	 al_draw_rect(al_screen, d->x+1, d->y+1, d->x+d->w-2, d->y+d->h-2, bg);
   }
}



/* draw_listbox:
 *  Helper function to draw a listbox object.
 */
void _draw_listbox(AL_DIALOG *d)
{
   int height, listsize, i, len, bar, x, y, w;
   int fg_color, fg, bg;
   char *sel = d->dp2;
   char s[1024];
   int rtm;

   (*(getfuncptr)d->dp)(-1, &listsize);
   height = (d->h-4) / al_text_height(al_font_8x8);
   bar = (listsize > height);
   w = (bar ? d->w-15 : d->w-3);
   fg_color = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;

   /* draw box contents */
   for (i=0; i<height; i++) {
      if (d->d2+i < listsize) {
	 if (d->d2+i == d->d1) {
	    fg = d->bg;
	    bg = fg_color;
	 } 
	 else if ((sel) && (sel[d->d2+i])) { 
	    fg = d->bg;
	    bg = al_gui_mg_color;
	 }
	 else {
	    fg = fg_color;
	    bg = d->bg;
	 }
	 al_ustrzcpy(s, sizeof(s), (*(getfuncptr)d->dp)(i+d->d2, NULL));
	 x = d->x + 2;
	 y = d->y + 2 + i*al_text_height(al_font_8x8);
	 rtm = al_text_mode(bg);
	 al_draw_rect_fill(al_screen, x, y, x+7, y+al_text_height(al_font_8x8)-1, bg); 
	 x += 8;
	 len = al_ustrlen(s);
	 while (al_text_length(al_font_8x8, s) >= MAX(d->w - 1 - (bar ? 22 : 10), 1)) {
	    len--;
	    al_usetat(s, len, 0);
	 }
	 al_put_text(al_screen, al_font_8x8, s, x, y, fg);
	 al_text_mode(rtm);
	 x += al_text_length(al_font_8x8, s);
	 if (x <= d->x+w) 
	    al_draw_rect_fill(al_screen, x, y, d->x+w, y+al_text_height(al_font_8x8)-1, bg);
      }
      else {
	 al_draw_rect_fill(al_screen, d->x+2,  d->y+2+i*al_text_height(al_font_8x8), 
		  d->x+w, d->y+1+(i+1)*al_text_height(al_font_8x8), d->bg);
      }
   }

   if (d->y+2+i*al_text_height(al_font_8x8) <= d->y+d->h-3)
      al_draw_rect_fill(al_screen, d->x+2, d->y+2+i*al_text_height(al_font_8x8), 
				       d->x+w, d->y+d->h-3, d->bg);

   /* draw frame, maybe with scrollbar */
   _draw_scrollable_frame(d, listsize, d->d2, height, fg_color, d->bg);
}



/* al_dialog_list_proc:
 *  A list box object. The dp field points to a function which it will call
 *  to obtain information about the list. This should follow the form:
 *     const char *<list_func_name> (int index, int *list_size);
 *  If index is zero or positive, the function should return a pointer to
 *  the string which is to be displayed at position index in the list. If
 *  index is  negative, it should return null and list_size should be set
 *  to the number of items in the list. The list box object will allow the
 *  user to scroll through the list and to select items list by clicking
 *  on them, and if it has the input focus also by using the arrow keys. If 
 *  the D_EXIT flag is set, double clicking on a list item will cause it to 
 *  close the dialog. The index of the selected item is held in the d1 
 *  field, and d2 is used to store how far it has scrolled through the list.
 */
int al_dialog_list_proc(int msg, AL_DIALOG *d, int c)
{
   int listsize, i, bottom, height, bar, orig;
   char *sel = d->dp2;
   int redraw = FALSE;

   switch (msg) {

      case MSG_START:
	 (*(getfuncptr)d->dp)(-1, &listsize);
	 _handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);
	 break;

      case MSG_DRAW:
	 _draw_listbox(d);
	 break;

      case MSG_CLICK:
	 (*(getfuncptr)d->dp)(-1, &listsize);
	 height = (d->h-4) / al_text_height(al_font_8x8);
	 bar = (listsize > height);
	 if ((!bar) || (al_gui_mouse_x() < d->x+d->w-13)) {
	    if ((sel) && (!(al_key_shifts & KB_CTRL_FLAG))) {
	       for (i=0; i<listsize; i++) {
		  if (sel[i]) {
		     redraw = TRUE;
		     sel[i] = FALSE;
		  }
	       }
	       if (redraw) {
		  al_scare_mouse();
		  al_object_message(d, MSG_DRAW, 0);
		  al_unscare_mouse();
	       }
	    }
	    _handle_listbox_click(d);
	    while (al_gui_mouse_b()) {
	       al_broadcast_dialog_message(MSG_IDLE, 0);
	       d->flags |= D_INTERNAL;
	       _handle_listbox_click(d);
	       d->flags &= ~D_INTERNAL;
	    }
	 }
	 else {
	    _handle_scrollable_scroll_click(d, listsize, &d->d2, height);
	 }
	 break;

      case MSG_DCLICK:
	 (*(getfuncptr)d->dp)(-1, &listsize);
	 height = (d->h-4) / al_text_height(al_font_8x8);
	 bar = (listsize > height);
	 if ((!bar) || (al_gui_mouse_x() < d->x+d->w-13)) {
	    if (d->flags & D_EXIT) {
	       if (listsize) {
		  i = d->d1;
		  al_object_message(d, MSG_CLICK, 0);
		  if (i == d->d1) 
		     return D_CLOSE;
	       }
	    }
	 }
	 break;

      case MSG_WHEEL:
	 (*(getfuncptr)d->dp)(-1, &listsize);
	 height = (d->h-4) / al_text_height(al_font_8x8);
	 if (height < listsize) {
	    int delta = (height > 3) ? 3 : 1;
	    if (c > 0) 
	       i = MAX(0, d->d2-delta);
	    else
	       i = MIN(listsize-height, d->d2+delta);
	    if (i != d->d2) {
	       d->d2 = i;
	       al_scare_mouse();
	       al_object_message(d, MSG_DRAW, 0);
	       al_unscare_mouse(); 
	    }
	 }
	 break;

      case MSG_KEY:
	 (*(getfuncptr)d->dp)(-1, &listsize);
	 if ((listsize) && (d->flags & D_EXIT))
	    return D_CLOSE;
	 break;

      case MSG_WANTFOCUS:
	 return D_WANTFOCUS;

      case MSG_CHAR:
	 (*(getfuncptr)d->dp)(-1, &listsize);

	 if (listsize) {
	    c >>= 8;

	    bottom = d->d2 + (d->h-4)/al_text_height(al_font_8x8) - 1;
	    if (bottom >= listsize-1)
	       bottom = listsize-1;

	    orig = d->d1;

	    if (c == AL_KEY_UP)
	       d->d1--;
	    else if (c == AL_KEY_DOWN)
	       d->d1++;
	    else if (c == AL_KEY_HOME)
	       d->d1 = 0;
	    else if (c == AL_KEY_END)
	       d->d1 = listsize-1;
	    else if (c == AL_KEY_PGUP) {
	       if (d->d1 > d->d2)
		  d->d1 = d->d2;
	       else
		  d->d1 -= (bottom - d->d2) ? bottom - d->d2 : 1;
	    }
	    else if (c == AL_KEY_PGDN) {
	       if (d->d1 < bottom)
		  d->d1 = bottom;
	       else
		  d->d1 += (bottom - d->d2) ? bottom - d->d2 : 1;
	    } 
	    else 
	       return D_O_K;

	    if (sel) {
	       if (!(al_key_shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG))) {
		  for (i=0; i<listsize; i++)
		     sel[i] = FALSE;
	       }
	       else if (al_key_shifts & KB_SHIFT_FLAG) {
		  for (i=MIN(orig, d->d1); i<=MAX(orig, d->d1); i++) {
		     if (al_key_shifts & KB_CTRL_FLAG)
			sel[i] = (i != d->d1);
		     else
			sel[i] = TRUE;
		  }
	       }
	    }

	    /* if we changed something, better redraw... */ 
	    _handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);
	    d->flags |= D_DIRTY;
	    return D_USED_CHAR;
	 }
	 break;
   }

   return D_O_K;
}



/* al_dialog_text_list_proc:
 *  Like al_dialog_list_proc, but allows the user to type in the first few characters
 *  of a listbox entry in order to select it. Uses dp3 internally, so you
 *  mustn't store anything important there yourself.
 */
int al_dialog_text_list_proc(int msg, AL_DIALOG *d, int c)
{
   int listsize, i, a, failure;
   char *selected, *thisitem;
   char *sel = d->dp2;

   switch (msg) {

      case MSG_START:
      case MSG_CLICK:
      case MSG_DCLICK:
      case MSG_WANTFOCUS:
      case MSG_LOSTFOCUS:
	 d->dp3 = 0;
	 break;

      case MSG_CHAR:
	 if ((c & 0xFF) < ' ')
	    d->dp3 = 0;
	 break;

      case MSG_UCHAR:
	 (*(getfuncptr)d->dp)(-1, &listsize);

	 if (listsize) {
	    if (c >= ' ') {
	       selected = (*(getfuncptr)d->dp)(d->d1, NULL);

	       i = d->d1;

	       do {
		  thisitem = (*(getfuncptr)d->dp)(i, NULL);
		  failure = FALSE;

		  if ((int)d->dp3 < al_ustrlen(thisitem)) {
		     for (a=0; a<(int)d->dp3; a++) {
			if (al_utolower(al_ugetat(thisitem, a)) != al_utolower(al_ugetat(selected, a))) {
			   failure = TRUE;
			   break;
			}
		     }

		     if ((!failure) && (al_utolower(al_ugetat(thisitem, (int)d->dp3)) == al_utolower(c))) {
			d->d1 = i;
			d->dp3 = (void *)((int)d->dp3 + 1);

			if (sel) {
			   for (i=0; i<listsize; i++)
			      sel[i] = FALSE;
			}

			_handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);
			al_scare_mouse();
			al_object_message(d, MSG_DRAW, 0);
			al_unscare_mouse();
			return D_USED_CHAR;
		     }
		  }

		  i++;
		  if (i >= listsize)
		     i = 0;

	       } while (i != d->d1);

	       if (d->dp3) {
		  d->dp3 = 0;
		  return al_dialog_text_list_proc(msg, d, c);
	       }
	    }
	 }
	 break;
   }

   return al_dialog_list_proc(msg, d, c);
}



/* _draw_textbox:
 *  Helper function to draw a textbox object.
 */
void _draw_textbox(char *thetext, int *listsize, int draw, int offset,
		   int wword, int tabsize, int x, int y, int w, int h,
		   int disabled, int fore, int deselect, int disable)
{
   int fg = fore;
   int y1 = y+4;
   int x1;
   int len;
   int ww = w-6;
   char s[16];
   char text[16];
   char space[16];
   char *printed = text;
   char *scanned = text;
   char *oldscan = text;
   char *ignore = NULL;
   char *tmp, *ptmp;
   int width;
   int al_draw_line = 0;
   int i = 0;
   int noignore;
   int rtm;

   al_usetc(s+al_usetc(s, '.'), 0);
   al_usetc(text+al_usetc(text, ' '), 0);
   al_usetc(space+al_usetc(space, ' '), 0);

   /* find the correct text */
   if (thetext != NULL) {
      printed = thetext;
      scanned = thetext;
   }

   /* do some drawing setup */
   if (draw) {
      /* initial start blanking at the top */
      al_draw_rect_fill(al_screen, x+2, y+2, x+w-3, y1-1, deselect);
   }

   /* choose the text color */
   if (disabled) 
      fg = disable;

   rtm = al_text_mode(deselect);

   /* loop over the entire string */
   while (1) {
      width = 0;

      /* find the next break */
      while (al_ugetc(scanned)) {
	 /* check for a forced break */
	 if (al_ugetc(scanned) == '\n') {
	    scanned += al_uwidth(scanned);

	    /* we are done parsing the al_draw_line end */
	    break;
	 }

	 /* the next character length */
	 al_usetc(s+al_usetc(s, al_ugetc(scanned)), 0);
	 len = al_text_length(al_font_8x8, s);

	 /* modify length if its a tab */
	 if (al_ugetc(s) == '\t') 
	    len = tabsize * al_text_length(al_font_8x8, space);

	 /* check for the end of a al_draw_line by excess width of next char */
	 if (width+len >= ww) {
	    /* we have reached end of al_draw_line do we go back to find start */
	    if (wword) {
	       /* remember where we were */
	       oldscan = scanned;
	       noignore = FALSE;

	       /* go backwards looking for start of word */
	       while (!al_uisspace(al_ugetc(scanned))) {
		  /* don't wrap too far */
		  if (scanned == printed) {
		     /* the whole al_draw_line is filled, so stop here */
		     tmp = ptmp = scanned;
		     while (ptmp != oldscan) {
			ptmp = tmp;
			tmp += al_uwidth(tmp);
		     }
		     scanned = ptmp;
		     noignore = TRUE;
		     break;
		  }
		  /* look further backwards to wrap */
		  tmp = ptmp = printed;
		  while (tmp < scanned) {
		     ptmp = tmp;
		     tmp += al_uwidth(tmp);
		  }
		  scanned = ptmp;
	       }
	       /* put the space at the end of the al_draw_line */
	       if (!noignore) {
		  ignore = scanned;
		  scanned += al_uwidth(scanned);
	       }
	       else
		  ignore = NULL;

	       /* check for endline at the convenient place */
	       if (al_ugetc(scanned) == '\n') 
		  scanned += al_uwidth(scanned);
	    }
	    /* we are done parsing the al_draw_line end */
	    break;
	 }

	 /* the character can be added */
	 scanned += al_uwidth(scanned);
	 width += len;
      }

      /* check if we are to print it */
      if ((draw) && (al_draw_line >= offset) && (y1+al_text_height(al_font_8x8) < (y+h-3))) {
	 x1 = x+4;

	 /* the initial blank bit */
	 al_draw_rect_fill(al_screen, x+2, y1, x1-1, y1+al_text_height(al_font_8x8), deselect);

	 /* print up to the marked character */
	 while (printed != scanned) {
	    /* do special stuff for each charater */
	    switch (al_ugetc(printed)) {

	       case '\r':
	       case '\n':
		  /* don't print endlines in the text */
		  break;

	       /* possibly expand the tabs */
	       case '\t':
		  for (i=0; i<tabsize; i++) {
		     al_usetc(s+al_usetc(s, ' '), 0);
		     al_put_text(al_screen, al_font_8x8, s, x1, y1, fg);
		     x1 += al_text_length(al_font_8x8, s);
		  }
		  break;

	       /* print a normal character */
	       default:
		  if (printed != ignore) {
		     al_usetc(s+al_usetc(s, al_ugetc(printed)), 0);
		     al_put_text(al_screen, al_font_8x8, s, x1, y1, fg);
		     x1 += al_text_length(al_font_8x8, s);
		  }
	    }

	    /* goto the next character */
	    printed += al_uwidth(printed);
	 }
	 /* the last blank bit */
	 if (x1 <= x+w-3) 
	    al_draw_rect_fill(al_screen, x1, y1, x+w-3, y1+al_text_height(al_font_8x8)-1, deselect);

	 /* print the al_draw_line end */
	 y1 += al_text_height(al_font_8x8);
      }
      printed = scanned;

      /* we have done a al_draw_line */
      al_draw_line++;

      /* check if we are at the end of the string */
      if (!al_ugetc(printed)) {
	 /* the under blank bit */
	 if (draw) 
	    al_draw_rect_fill(al_screen, x+1, y1, x+w-3, y+h-1, deselect);

	 /* tell how many lines we found */
	 *listsize = al_draw_line;
	 al_text_mode(rtm);
	 return;
      }
   }

   al_text_mode(rtm);
}



/* al_dialog_textbox_proc:
 *  A text box object. The dp field points to a char * which is the text
 *  to be displayed in the text box. If the text is long, there will be
 *  a vertical scrollbar on the right hand side of the object which can
 *  be used to scroll through the text. The default is to print the text
 *  with word wrapping, but if the D_SELECTED flag is set, the text will
 *  be printed with character wrapping. The d1 field is used internally
 *  to store the number of lines of text, and d2 is used to store how far
 *  it has scrolled through the text.
 */
int al_dialog_textbox_proc(int msg, AL_DIALOG *d, int c)
{
   int height, bar, ret = D_O_K;
   int start, top, bottom, l;
   int used, delta;
   int fg_color = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;

   /* calculate the actual height */
   height = (d->h-8) / al_text_height(al_font_8x8);

   switch (msg) {

      case MSG_START:
	 /* measure how many lines of text we contain */
	 _draw_textbox(d->dp, &d->d1, 
		       0, /* DONT DRAW anything */
		       d->d2, !(d->flags & D_SELECTED), 8,
		       d->x, d->y, d->w, d->h,
		       (d->flags & D_DISABLED),
		       0, 0, 0);
	 break;

      case MSG_DRAW:
	 /* tell the object to sort of draw, but only calculate the listsize */
	 _draw_textbox(d->dp, &d->d1, 
		       0, /* DONT DRAW anything */
		       d->d2, !(d->flags & D_SELECTED), 8,
		       d->x, d->y, d->w, d->h,
		       (d->flags & D_DISABLED),
		       0, 0, 0);

	 if (d->d1 > height) {
	    bar = 12;
	 }
	 else {
	    bar = 0;
	    d->d2 = 0;
	 }

	 /* now do the actual drawing */
	 _draw_textbox(d->dp, &d->d1, 1, d->d2,
		       !(d->flags & D_SELECTED), 8,
		       d->x, d->y, d->w-bar, d->h,
		       (d->flags & D_DISABLED),
		       fg_color, d->bg, al_gui_mg_color);

	 /* draw the frame around */
	 _draw_scrollable_frame(d, d->d1, d->d2, height, fg_color, d->bg);
	 break;

      case MSG_CLICK:
	 /* figure out if it's on the text or the scrollbar */
	 bar = (d->d1 > height);

	 if ((!bar) || (al_gui_mouse_x() < d->x+d->w-13)) {
	    /* clicked on the text area */
	    ret = D_O_K;
	 }
	 else {
	    /* clicked on the scroll area */
	    _handle_scrollable_scroll_click(d, d->d1, &d->d2, height);
	 }
	 break;

      case MSG_CHAR:
	 start = d->d2;
	 used = D_USED_CHAR;

	 if (d->d1 > 0) {
	    if (d->d2 > 0) 
	       top = d->d2+1;
	    else 
	       top = 0;

	    l = (d->h-8)/al_text_height(al_font_8x8);

	    bottom = d->d2 + l - 1;
	    if (bottom >= d->d1-1) 
	       bottom = d->d1-1;
	    else 
	       bottom--;

	    if ((c>>8) == AL_KEY_UP) 
	       d->d2--;
	    else if ((c>>8) == AL_KEY_DOWN) 
	       d->d2++;
	    else if ((c>>8) == AL_KEY_HOME) 
	       d->d2 = 0;
	    else if ((c>>8) == AL_KEY_END) 
	       d->d2 = d->d1-l;
	    else if ((c>>8) == AL_KEY_PGUP) 
	       d->d2 -= (bottom-top) ? bottom-top : 1;
	    else if ((c>>8) == AL_KEY_PGDN) 
	       d->d2 += (bottom-top) ? bottom-top : 1;
	    else 
	       used = D_O_K;

	    /* make sure that the list stays in bounds */
	    if (d->d2 > d->d1-l) 
	       d->d2 = d->d1-l;
	    if (d->d2 < 0) 
	       d->d2 = 0;
	 }
	 else 
	    used = D_O_K;

	 /* if we changed something, better redraw... */
	 if (d->d2 != start)
	    d->flags |= D_DIRTY;

	 ret = used;
	 break;
      
      case MSG_WHEEL:
	 l = (d->h-8)/al_text_height(al_font_8x8);
	 delta = (l > 3) ? 3 : 1;

	 /* scroll, making sure that the list stays in bounds */
	 start = d->d2;
	 d->d2 = (c > 0) ? MAX(0, d->d2-delta) : MIN(d->d1-l, d->d2+delta);

	 /* if we changed something, better redraw... */
	 if (d->d2 != start)
	    d->flags |= D_DIRTY;

	 ret = D_O_K;
	 break;

      case MSG_WANTFOCUS:
	 /* if we don't have a scrollbar we can't do anything with the focus */
	 if (d->d1 > height) 
	    ret = D_WANTFOCUS;
	 break;

      default:
	 ret = D_O_K;
   }

   return ret;
}



/* al_dialog_slider_proc:
 *  A slider control object. This object returns a value in d2, in the
 *  range from 0 to d1. It will display as a vertical slider if h is
 *  greater than or equal to w; otherwise, it will display as a horizontal
 *  slider. dp can contain an optional bitmap to use for the slider handle; 
 *  dp2 can contain an optional callback function, which is called each 
 *  time d2 changes. The callback function should have the following
 *  prototype:
 *
 *  int function(void *dp3, int d2);
 *
 *  The al_dialog_slider_proc object will return the value of the callback function.
 */
int al_dialog_slider_proc(int msg, AL_DIALOG *d, int c)
{
   AL_BITMAP *slhan = NULL;
   int oldpos, newpos;
   int sfg;                /* slider foreground color */
   int vert = TRUE;        /* flag: is slider vertical? */
   int hh = 7;             /* handle height (width for horizontal sliders) */
   int hmar;               /* handle margin */
   int slp;                /* slider position */
   int mp;                 /* mouse position */
   int irange;
   int slx, sly, slh, slw;
   int msx, msy;
   int retval = D_O_K;
   int upkey, downkey;
   int pgupkey, pgdnkey;
   int homekey, endkey;
   int delta;
   fixed slratio, slmax, slpos;
   int (*proc)(void *cbpointer, int d2value);
   int oldval;

   /* check for slider direction */
   if (d->h < d->w)
      vert = FALSE;

   /* set up the metrics for the control */
   if (d->dp != NULL) {
      slhan = (AL_BITMAP *)d->dp;
      if (vert)
	 hh = slhan->h;
      else
	 hh = slhan->w;
   }

   hmar = hh/2;
   irange = (vert) ? d->h : d->w;
   slmax = al_int_to_fix(irange-hh);
   slratio = slmax / (d->d1);
   slpos = slratio * d->d2;
   slp = al_fix_to_int(slpos);

   switch (msg) {

      case MSG_DRAW:
	 sfg = (d->flags & D_DISABLED) ? al_gui_mg_color : d->fg;

	 if (vert) {
	    al_draw_rect_fill(al_screen, d->x, d->y, d->x+d->w/2-2, d->y+d->h-1, d->bg);
	    al_draw_rect_fill(al_screen, d->x+d->w/2-1, d->y, d->x+d->w/2+1, d->y+d->h-1, sfg);
	    al_draw_rect_fill(al_screen, d->x+d->w/2+2, d->y, d->x+d->w-1, d->y+d->h-1, d->bg);
	 }
	 else {
	    al_draw_rect_fill(al_screen, d->x, d->y, d->x+d->w-1, d->y+d->h/2-2, d->bg);
	    al_draw_rect_fill(al_screen, d->x, d->y+d->h/2-1, d->x+d->w-1, d->y+d->h/2+1, sfg);
	    al_draw_rect_fill(al_screen, d->x, d->y+d->h/2+2, d->x+d->w-1, d->y+d->h-1, d->bg);
	 }

	 /* okay, background and slot are drawn, now draw the handle */
	 if (slhan) {
	    if (vert) {
	       slx = d->x+(d->w/2)-(slhan->w/2);
	       sly = d->y+(d->h-1)-(hh+slp);
	    } 
	    else {
	       slx = d->x+slp;
	       sly = d->y+(d->h/2)-(slhan->h/2);
	    }
	    al_draw_sprite(al_screen, slhan, slx, sly);
	 } 
	 else {
	    /* draw default handle */
	    if (vert) {
	       slx = d->x;
	       sly = d->y+(d->h)-(hh+slp);
	       slw = d->w-1;
	       slh = hh-1;
	    } else {
	       slx = d->x+slp;
	       sly = d->y;
	       slw = hh-1;
	       slh = d->h-1;
	    }

	    /* draw body */
	    al_draw_rect_fill(al_screen, slx+2, sly, slx+(slw-2), sly+slh, sfg);
	    al_draw_vline(al_screen, slx+1, sly+1, sly+slh-1, sfg);
	    al_draw_vline(al_screen, slx+slw-1, sly+1, sly+slh-1, sfg);
	    al_draw_vline(al_screen, slx, sly+2, sly+slh-2, sfg);
	    al_draw_vline(al_screen, slx+slw, sly+2, sly+slh-2, sfg);
	    al_draw_vline(al_screen, slx+1, sly+2, sly+slh-2, d->bg);
	    al_draw_hline(al_screen, slx+2, sly+1, slx+slw-2, d->bg);
	    al_put_pixel(al_screen, slx+2, sly+2, d->bg);
	 }

	 if (d->flags & D_GOTFOCUS)
	    dotted_rect(d->x, d->y, d->x+d->w-1, d->y+d->h-1, sfg, d->bg);
	 break;

      case MSG_WANTFOCUS:
      case MSG_LOSTFOCUS:
	 return D_WANTFOCUS;

      case MSG_KEY:
	 if (!(d->flags & D_GOTFOCUS))
	    return D_WANTFOCUS;
	 else
	    return D_O_K;

      case MSG_CHAR:
	 /* handle movement keys to move slider */
	 c >>= 8;

	 if (vert) {
	    upkey = AL_KEY_UP;
	    downkey = AL_KEY_DOWN;
	    pgupkey = AL_KEY_PGUP;
	    pgdnkey = AL_KEY_PGDN;
	    homekey = AL_KEY_END;
	    endkey = AL_KEY_HOME;
	 } 
	 else {
	    upkey = AL_KEY_RIGHT;
	    downkey = AL_KEY_LEFT;
	    pgupkey = AL_KEY_PGDN;
	    pgdnkey = AL_KEY_PGUP;
	    homekey = AL_KEY_HOME;
	    endkey = AL_KEY_END;
	 }

	 if (c == upkey)
	    delta = 1;
	 else if (c == downkey)
	    delta = -1;
	 else if (c == pgdnkey)
	    delta = -d->d1 / 16;
	 else if (c == pgupkey)
	    delta = d->d1 / 16;
	 else if (c == homekey)
	    delta = -d->d2;
	 else if (c == endkey)
	    delta = d->d1 - d->d2;
	 else
	    delta = 0;

	 if (delta) {
	    oldpos = slp;
	    oldval = d->d2;

	    while (1) {
	       d->d2 = d->d2+delta;
	       slpos = slratio*d->d2;
	       slp = al_fix_to_int(slpos);
	       if ((slp != oldpos) || (d->d2 <= 0) || (d->d2 >= d->d1))
		  break;
	    }

	    if (d->d2 < 0)
	       d->d2 = 0;
	    if (d->d2 > d->d1)
	       d->d2 = d->d1;

	    retval = D_USED_CHAR;

	    if (d->d2 != oldval) {
	       /* call callback function here */
	       if (d->dp2) {
		  proc = d->dp2;
		  retval |= (*proc)(d->dp3, d->d2);
	       }

	       al_scare_mouse();
	       al_object_message(d, MSG_DRAW, 0);
	       al_unscare_mouse();
	    }
	 }
	 break;
      
      case MSG_WHEEL: 
	 oldval = d->d2;
	 d->d2 = MID(0, d->d2+c, d->d1);
	 if (d->d2 != oldval) {
	    /* call callback function here */
	    if (d->dp2) {
	       proc = d->dp2;
	       retval |= (*proc)(d->dp3, d->d2);
	    }

	    al_scare_mouse();
	    al_object_message(d, MSG_DRAW, 0);
	    al_unscare_mouse();
	 }
	 break;

      case MSG_CLICK:
	 /* track the mouse until it is released */
	 mp = slp;

	 while (al_gui_mouse_b()) {
	    msx = al_gui_mouse_x();
	    msy = al_gui_mouse_y();
	    oldval = d->d2;
	    if (vert)
	       mp = (d->y+d->h-hmar)-msy;
	    else
	       mp = msx-(d->x+hmar);
	    if (mp < 0)
	       mp = 0;
	    if (mp > irange-hh)
	       mp = irange-hh;
	    slpos = al_int_to_fix(mp);
	    slmax = al_fix_div(slpos, slratio);
	    newpos = al_fix_to_int(slmax);
	    if (newpos != oldval) {
	       d->d2 = newpos;

	       /* call callback function here */
	       if (d->dp2 != NULL) {
		  proc = d->dp2;
		  retval |= (*proc)(d->dp3, d->d2);
	       }

	       al_scare_mouse();
	       al_object_message(d, MSG_DRAW, 0);
	       al_unscare_mouse();
	    }

	    /* let other objects continue to animate */
	    al_broadcast_dialog_message(MSG_IDLE, 0);
	 }
	 break;
   }

   return retval;
}



/* Overridable procedures used by standard GUI dialogs.  */

#define MAKE_PROC(proc, default)                                   \
int (*al_##proc)(int, AL_DIALOG *, int);                           \
int _##proc(int msg, AL_DIALOG *d, int c)                          \
{                                                                  \
    return al_##proc ? al_##proc(msg, d, c) : default(msg, d, c);  \
}

MAKE_PROC(gui_shadow_box_proc, al_dialog_shadow_box_proc);
MAKE_PROC(gui_ctext_proc, al_dialog_ctext_proc);
MAKE_PROC(gui_button_proc, al_dialog_button_proc);
MAKE_PROC(gui_edit_proc, al_dialog_edit_proc);
MAKE_PROC(gui_list_proc, al_dialog_text_list_proc);
MAKE_PROC(gui_text_list_proc, al_dialog_text_list_proc);




See more files for this project here

Allegro game programming library

Allegro is a cross-platform library intended for use in computer games and other types of multimedia programming.

Project homepage: http://sourceforge.net/projects/alleg
Programming language(s): Assembly,C,Shell Script
License: other

  beos/
    baccel.cpp
    bclasses.cpp
    bdispsw.cpp
    bgfx.c
    bgfxapi.cpp
    bgfxdrv.c
    bjoy.c
    bjoyapi.cpp
    bjoydrv.c
    bkey.c
    bkeyapi.cpp
    bkeydrv.c
    bmidi.c
    bmidiapi.cpp
    bmididrv.c
    bmousapi.cpp
    bmousdrv.c
    bmouse.c
    bsnd.c
    bsndapi.cpp
    bsnddrv.c
    bswitch.s
    bsysapi.cpp
    bsysdrv.c
    bsystem.c
    btimeapi.cpp
    btimedrv.c
    btimer.c
  c/
    cblit.h
    cblit16.c
    cblit24.c
    cblit32.c
  dos/
  i386/
  linux/
  mac/
  misc/
  ppc/
  qnx/
  unix/
  win/
  x/
  allegro.c
  blit.c
  bmp.c
  clip3d.c
  clip3df.c
  colblend.c
  color.c
  config.c
  datafile.c
  dataregi.c
  digmid.c
  dispsw.c
  dither.c
  drvlist.c
  file.c
  fli.c
  flood.c
  font.c
  fsel.c
  gfx.c
  glyph.c
  graphics.c
  gsprite.c
  gui.c
  guiproc.c
  inline.c
  joystick.c
  keyboard.c
  lbm.c
  libc.c
  math.c
  math3d.c
  midi.c
  mixer.c
  modesel.c
  mouse.c
  pcx.c
  poly3d.c
  polygon.c
  quantize.c
  quat.c
  readbmp.c
  rle.c
  rotate.c
  scene3d.c
  sound.c
  spline.c
  stream.c
  text.c
  tga.c
  timer.c
  unicode.c
  vtable.c
  vtable15.c
  vtable16.c
  vtable24.c
  vtable32.c
  vtable8.c