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