[AD] al_alert with tab and newline support |
[ Thread Index |
Date Index
| More lists.liballeg.org/allegro-developers Archives
]
With the help of KittyCat in #allegro I reworked the alert functions to support any number of buttons, parse newlines, and use \t as an allignment specifier. I added a function al_alert which is much easier to use, its simplist form being:
al_alert(std_alert, "Some message");
Tab are used as alignment specifiers. That means if there are no tabs in the line, the line is centered. If there is a tab, everything to the left of it is left aligned and everything to the right of it is right aligned. So for example:
al_alert(std_alert, "Read Status:\tComplete");
would be drawn something like this:
__________________________
| Read Status: Complete |
|___________{Okay}________|
I made three different 'standard' button setups: std_alert, confirm_alert and cancel_confirm_alert. I'm not sure how much I like their names but here are some
descriptions:
std_alert: One "Okay" button
confirm_alert: One "Yes" button, One "No" button
cancel_confirm_alert: One "Yes" button, One "No" button, One "Cancel" button
Its easy to define your own button sets, heres an example of that:
ALERT_BUTTON should_save[] =
{
{ "&Save and Quit", 'S' },
{ "&Quit Without Save", 'Q' },
{ "&No", 'N' },
{ "&Shutdown Computer", 'S' },
{ NULL }
};
int action = "" "The file has been modified, are you sure you would like to quit?");
if(action == 1)
save_and_quit();
else if(action == 2)
quit_without_save();
else if(action == 3)
no();
else if(action == 4)
shutdown();
else if(action == -1)
escape_was_pressed();
else if(action == 0)
error_no_memory();
Sorr in advance for the patch setup, I couldn't get the cvs diff thing to work, so i must made my patches off of
guiold.h and guiold.c. Once the merge to svn is done I'll probably get better at this whole patch thing.
--- include/allegro/guiold.h 2005-12-06 15:54:50.000000000 -0800
+++ include/allegro/gui.h 2005-12-06 15:55:19.000000000 -0800
@@ -12,6 +12,8 @@
*
* By Shawn Hargreaves.
*
+ * Alert dialog additions by Dustin Dettmer.
+ *
* See readme.txt for copyright information.
*/
@@ -42,6 +44,13 @@
} DIALOG;
+/* Button information for al_alert */
+typedef struct ALERT_BUTTON {
+ AL_CONST char *msg; /* text on button */
+ int key; /* key to activate this button */
+} ALERT_BUTTON;
+
+
/* a popup menu */
typedef struct MENU
{
@@ -179,6 +188,9 @@
AL_VAR(DIALOG *, active_dialog);
AL_VAR(MENU *, active_menu);
+AL_VAR(ALERT_BUTTON, std_alert[]);
+AL_VAR(ALERT_BUTTON, confirm_alert[]);
+AL_VAR(ALERT_BUTTON, cancel_confirm_alert[]);
AL_VAR(int, gui_mouse_focus);
@@ -216,6 +228,7 @@
AL_FUNC(int, shutdown_menu, (MENU_PLAYER *player));
AL_FUNC(int, alert, (AL_CONST char *s1, AL_CONST char *s2, AL_CONST char *s3, AL_CONST char *b1, AL_CONST char *b2, int c1, int c2));
AL_FUNC(int, alert3, (AL_CONST char *s1, AL_CONST char *s2, AL_CONST char *s3, AL_CONST char *b1, AL_CONST char *b2, AL_CONST char *b3, int c1, int c2, int c3));
+AL_FUNC(int, al_alert, (AL_CONST ALERT_BUTTON *buttons, AL_CONST char *str));
AL_FUNC(int, file_select_ex, (AL_CONST char *message, char *path, AL_CONST char *ext, int size, int w, int h));
AL_FUNC(int, gfx_mode_select, (int *card, int *w, int *h));
--- src/guiold.c 2005-08-21 09:23:28.000000000 -0700
+++ src/gui.c 2005-12-06 15:50:14.000000000 -0800
@@ -20,11 +20,13 @@
*
* Elias Pschernig and Sven Sandberg improved the focus algorithm.
*
+ * Dustin Dettmer rewrote the alert dialog functions and made them nicer.
+ *
* See readme.txt for copyright information.
*/
-
#include <limits.h>
+#include <string.h>
#include "allegro.h"
#include "allegro/internal/aintern.h"
@@ -57,6 +59,29 @@
MENU *active_menu = NULL;
+/* Commonly used dialog button setups. */
+ALERT_BUTTON std_alert[] =
+{
+ { "&Okay", 'O' },
+ { NULL }
+};
+
+ALERT_BUTTON confirm_alert[] =
+{
+ { "&Yes", 'Y' },
+ { "&No", 'N' },
+ { NULL }
+};
+
+ALERT_BUTTON cancel_confirm_alert[] =
+{
+ { "&Yes", 'Y' },
+ { "&No", 'N' },
+ { "&Cancel", 'C' },
+ { NULL }
+};
+
+
static BITMAP *gui_screen = NULL;
@@ -2261,29 +2286,251 @@
}
-
-static DIALOG alert_dialog[] =
+/* _alert:
+ * Worker function for alert functions. Handles newlines,
+ * tabs and multiple buttons.
+ */
+static int _alert(char *str, AL_CONST ALERT_BUTTON *buttons)
{
- /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */
- { _gui_shadow_box_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
- { _gui_ctext_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
- { _gui_ctext_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
- { _gui_ctext_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
- { _gui_button_proc, 0, 0, 0, 0, 0, 0, 0, D_EXIT, 0, 0, NULL, NULL, NULL },
- { _gui_button_proc, 0, 0, 0, 0, 0, 0, 0, D_EXIT, 0, 0, NULL, NULL, NULL },
- { _gui_button_proc, 0, 0, 0, 0, 0, 0, 0, D_EXIT, 0, 0, NULL, NULL, NULL },
- { d_yield_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
- { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }
-};
-
+ int ret;
+ int lines = 1; /* Number of lines */
+ int width = 0; /* Width or length of longest line */
+ const int pad = 2; /* Padding between lines */
+ const int bpad = 10; /* Padding between buttons */
+ const int bh = text_height(font) + 8; /* Height of buttons */
+ char *lptr; /* Pointer for iterating lines */
+ const ALERT_BUTTON *bptr; /* For iterating */
+ DIALOG *d;
+ DIALOG *dptr; /* For iterating */
+ char *loc;
+ int size = 3; /* Size of array d */
+ int i;
+
+ /* Calculate number of buttons */
+ for(bptr = buttons; bptr->msg; bptr++)
+ size++;
+
+ lptr = str;
+
+ /* Count number of lines replacing newlines with '\0' */
+ while((lptr = strchr(lptr, '\n')) != NULL) {
+
+ lines++;
+ size += 2;
+
+ *(lptr++) = '\0';
+ }
+
+ lptr = str;
+
+ /* Calculate longest line length (width) */
+ for(i = 0; i < lines; i++) {
+
+ if(text_length(font, lptr) > width)
+ width = text_length(font, lptr);
+
+ lptr += strlen(lptr) + 1;
+ }
+
+ d = _al_malloc(sizeof(DIALOG) * (size + 1));
+
+ if(!d) {
+
+ *allegro_errno = ENOMEM;
+
+ return 0;
+ }
+
+ memset(d, 0, sizeof(DIALOG) * (size + 1));
+
+ /* Start filling in DIALOG elements */
+ dptr = d;
+
+ /* Set up background box */
+ dptr->proc = d_shadow_box_proc;
+ dptr->w = width + 2 * bpad;
+ dptr->h = lines * text_height(font) + lines * pad + bh * 2 + 8;
+ dptr->x = -dptr->w / 2;
+ dptr->y = -dptr->h / 2 + 8;
+ dptr->fg = gui_fg_color;
+ dptr->bg = gui_bg_color;
+
+ dptr++;
+
+ /* Setup buttons */
+ for(bptr = buttons; bptr->msg; bptr++) {
+
+ dptr->proc = d_button_proc;
+ dptr->w = gui_strlen(bptr->msg) + 10;
+ dptr->h = bh;
+ dptr->x = -dptr->w / 2;
+ dptr->y = d[0].y + d[0].h - bh - 8;
+ dptr->fg = gui_fg_color;
+ dptr->bg = gui_bg_color;
+ dptr->dp = (char*)bptr->msg;
+ dptr->key = bptr->key;
+ dptr->flags = D_EXIT;
+
+ /* Here we loop through all of the already placed buttons, moving
+ * them to the left to account for the new button. At the same
+ * time we move our new button to the right a value appropiate
+ * accounting for all the previous buttons' widths. */
+ for(i = buttons - bptr; i < 0; i++) {
+
+ dptr[i].x -= (bpad + dptr->w) / 2;
+ dptr->x += (bpad + dptr[i].w) / 2;
+
+ /* Adjust the width and x value for the background if the buttons wont fit
+ * accounting for the padding between the buttons and the edge */
+ d[0].x = -MAX(d[0].w, MAX(-dptr[i].x + bpad, dptr->x + dptr->w + bpad) * 2) / 2;
+ d[0].w = MAX(d[0].w, MAX(-dptr[i].x + bpad, dptr->x + dptr->w + bpad) * 2);
+ }
+
+ dptr++;
+ }
+
+ /* Make sure to yeild */
+ dptr->proc = d_yield_proc;
+
+ dptr++;
+
+ lptr = str;
+
+ /* Put lines onto dialog */
+ for(i = 0; i < lines; i++) {
+
+ loc = strchr(lptr, '\t');
+
+ if(loc == NULL) {
+
+ dptr->proc = d_ctext_proc;
+ dptr->x = 0;
+ dptr->dp = lptr;
+ dptr->y = text_height(font) * i + i * pad - lines * text_height(font) / 2 - lines * pad / 2;
+ dptr->dp2 = font;
+ dptr->fg = gui_fg_color;
+ dptr->bg = gui_bg_color;
+
+ dptr++;
+
+ dptr->proc = d_yield_proc;
+ }
+ else {
+
+ *loc = '\0';
+
+ dptr->proc = d_text_proc;
+ dptr->x = -d[0].w / 2 + bpad;
+ dptr->dp = lptr;
+ dptr->y = text_height(font) * i + i * pad - lines * text_height(font) / 2 - lines * pad / 2;
+ dptr->dp2 = font;
+ dptr->fg = gui_fg_color;
+ dptr->bg = gui_bg_color;
+
+ dptr++;
+
+ lptr += strlen(lptr) + 1;
+
+ dptr->proc = d_rtext_proc;
+ dptr->x = d[0].w / 2 - bpad;
+ dptr->dp = lptr;
+ dptr->y = text_height(font) * i + i * pad - lines * text_height(font) / 2 - lines * pad / 2;
+ dptr->dp2 = font;
+ dptr->fg = gui_fg_color;
+ dptr->bg = gui_bg_color;
+ }
+
+ dptr++;
+
+ lptr += strlen(lptr) + 1;
+ }
+
+ /* Set the final DIALOG proc in the array to null */
+ dptr->proc = NULL;
+
+ centre_dialog(d);
+
+ ret = popup_dialog(d, 1);
+
+ free(d);
+
+ return ret;
+}
-#define A_S1 1
-#define A_S2 2
-#define A_S3 3
-#define A_B1 4
-#define A_B2 5
-#define A_B3 6
+/* _concat_strs:
+ * Worker function for alert functions. Handles reallocating of
+ * up to 3 strings The returned string must be freed.
+ */
+static char *_concat_strs(AL_CONST char *s1, AL_CONST char *s2, AL_CONST char *s3)
+{
+ char *ret;
+ char *lptr; /* For iterating */
+ int len = 0; /* Length of new string */
+ int lines = 0; /* How many valid s# strings were passed */
+
+ const int tab = 4; /* Spaces per tab */
+
+ /* Calculate len and lines */
+ if(s1) {
+
+ len += strlen(s1) + 1;
+ lines++;
+ }
+
+ if(s2) {
+
+ len += strlen(s2) + 1;
+ lines++;
+ }
+
+ if(s3) {
+
+ len += strlen(s3) + 1;
+ lines++;
+ }
+
+ ret = _al_malloc(len + lines);
+
+ if(!ret) {
+
+ *allegro_errno = ENOMEM;
+
+ return 0;
+ }
+
+ lptr = ret;
+
+ /* Copy string to ret and postfix newline if nessicary */
+ if(s1) {
+
+ strcpy(lptr, s1);
+ lptr += strlen(lptr);
+
+ if(--lines) {
+
+ *lptr++ = '\n'; /* Written over \0 character */
+ }
+ }
+
+ /* Copy string to ret and postfix newline if nessicary */
+ if(s2) {
+
+ strcpy(lptr, s2);
+ lptr += strlen(lptr);
+
+ if(--lines) {
+
+ *lptr++ = '\n'; /* Written over \0 character */
+ }
+ }
+
+ /* Copy string to ret */
+ if(s3)
+ strcpy(lptr, s3);
+
+ return ret;
+}
/* alert3:
@@ -2291,114 +2538,79 @@
* and with either one, two, or three buttons. The text for these buttons
* is passed in b1, b2, and b3 (NULL for buttons which are not used), and
* the keyboard shortcuts in c1 and c2. Returns 1, 2, or 3 depending on
- * which button was selected.
+ * which button was selected, -1 if escape was pressed or 0 if an error
+ * occured.
*/
int alert3(AL_CONST char *s1, AL_CONST char *s2, AL_CONST char *s3, AL_CONST char *b1, AL_CONST char *b2, AL_CONST char *b3, int c1, int c2, int c3)
{
- char tmp[16];
- int avg_w, avg_h;
- int len1, len2, len3;
- int maxlen = 0;
- int buttons = 0;
- int b[3];
- int c;
-
- #define SORT_OUT_BUTTON(x) { \
- if (b##x) { \
- alert_dialog[A_B##x].flags &= ~D_HIDDEN; \
- alert_dialog[A_B##x].key = c##x; \
- alert_dialog[A_B##x].dp = (char *)b##x; \
- len##x = gui_strlen(b##x); \
- b[buttons++] = A_B##x; \
- } \
- else { \
- alert_dialog[A_B##x].flags |= D_HIDDEN; \
- len##x = 0; \
- } \
- }
-
- usetc(tmp+usetc(tmp, ' '), 0);
-
- avg_w = text_length(font, tmp);
- avg_h = text_height(font);
-
- alert_dialog[A_S1].dp = alert_dialog[A_S2].dp = alert_dialog[A_S3].dp =
- alert_dialog[A_B1].dp = alert_dialog[A_B2].dp = empty_string;
-
- if (s1) {
- alert_dialog[A_S1].dp = (char *)s1;
- maxlen = text_length(font, s1);
- }
-
- if (s2) {
- alert_dialog[A_S2].dp = (char *)s2;
- len1 = text_length(font, s2);
- if (len1 > maxlen)
- maxlen = len1;
- }
-
- if (s3) {
- alert_dialog[A_S3].dp = (char *)s3;
- len1 = text_length(font, s3);
- if (len1 > maxlen)
- maxlen = len1;
- }
-
- SORT_OUT_BUTTON(1);
- SORT_OUT_BUTTON(2);
- SORT_OUT_BUTTON(3);
-
- len1 = MAX(len1, MAX(len2, len3)) + avg_w*3;
- if (len1*buttons > maxlen)
- maxlen = len1*buttons;
-
- maxlen += avg_w*4;
- alert_dialog[0].w = maxlen;
- alert_dialog[A_S1].w = alert_dialog[A_S2].w = alert_dialog[A_S3].w = maxlen - avg_w*2;
- alert_dialog[A_S1].x = alert_dialog[A_S2].x = alert_dialog[A_S3].x =
- alert_dialog[0].x + avg_w;
-
- alert_dialog[A_B1].w = alert_dialog[A_B2].w = alert_dialog[A_B3].w = len1;
-
- alert_dialog[A_B1].x = alert_dialog[A_B2].x = alert_dialog[A_B3].x =
- alert_dialog[0].x + maxlen/2 - len1/2;
-
- if (buttons == 3) {
- alert_dialog[b[0]].x = alert_dialog[0].x + maxlen/2 - len1*3/2 - avg_w;
- alert_dialog[b[2]].x = alert_dialog[0].x + maxlen/2 + len1/2 + avg_w;
- }
- else if (buttons == 2) {
- alert_dialog[b[0]].x = alert_dialog[0].x + maxlen/2 - len1 - avg_w;
- alert_dialog[b[1]].x = alert_dialog[0].x + maxlen/2 + avg_w;
- }
-
- alert_dialog[0].h = avg_h*8;
- alert_dialog[A_S1].y = alert_dialog[0].y + avg_h;
- alert_dialog[A_S2].y = alert_dialog[0].y + avg_h*2;
- alert_dialog[A_S3].y = alert_dialog[0].y + avg_h*3;
- alert_dialog[A_S1].h = alert_dialog[A_S2].h = alert_dialog[A_S3].h = avg_h;
- alert_dialog[A_B1].y = alert_dialog[A_B2].y = alert_dialog[A_B3].y = alert_dialog[0].y + avg_h*5;
- alert_dialog[A_B1].h = alert_dialog[A_B2].h = alert_dialog[A_B3].h = avg_h*2;
-
- centre_dialog(alert_dialog);
- set_dialog_color(alert_dialog, gui_fg_color, gui_bg_color);
- for (c = 0; alert_dialog[c].proc; c++)
- if (alert_dialog[c].proc == _gui_ctext_proc)
- alert_dialog[c].bg = -1;
-
- clear_keybuf();
-
- do {
- } while (gui_mouse_b());
-
- c = popup_dialog(alert_dialog, A_B1);
-
- if (c == A_B1)
- return 1;
- else if (c == A_B2)
- return 2;
- else
- return 3;
+ int ret;
+ char *msg; /* New string containing s* seperated by newlines */
+ char *lptr; /* For iterating */
+ const char *cptr; /* For const iterating */
+ int len = 0; /* Length of new string */
+ int tabs = 0; /* Number of tabs in new string */
+ int lines = 0; /* How many valid s# strings were passed */
+ int numb = 0; /* The number of buttons passed (0 to 3) */
+ const int tab = 4; /* Spaces per tab */
+ ALERT_BUTTON *buttons; /* Array of buttons for passing to _alert */
+ ALERT_BUTTON *bptr; /* For iterating */
+
+ msg = _concat_strs(s1, s2, s3);
+
+ if(b1)
+ numb++;
+
+ if(b2)
+ numb++;
+
+ if(b3)
+ numb++;
+
+ buttons = _al_malloc(sizeof(ALERT_BUTTON) * (numb + 1));
+
+ if(!buttons) {
+
+ *allegro_errno = ENOMEM;
+
+ return -1;
+ }
+
+ /* put in buttons */
+ bptr = buttons;
+
+ if(b1) {
+
+ bptr->msg = b1;
+ bptr->key = c1;
+
+ bptr++;
+ }
+
+ if(b2) {
+
+ bptr->msg = b2;
+ bptr->key = c2;
+
+ bptr++;
+ }
+
+ if(b3) {
+
+ bptr->msg = b3;
+ bptr->key = c3;
+
+ bptr++;
+ }
+
+ bptr->msg = NULL;
+
+ /* pass to _alert to handle newlines, tabs and buttons */
+ ret = _alert(msg, buttons);
+
+ free(buttons);
+ free(msg);
+
+ return ret;
}
@@ -2407,7 +2619,8 @@
* Displays a simple alert box, containing three lines of text (s1-s3),
* and with either one or two buttons. The text for these buttons is passed
* in b1 and b2 (b2 may be null), and the keyboard shortcuts in c1 and c2.
- * Returns 1 or 2 depending on which button was selected.
+ * Returns 1 or 2 depending on which button was selected, -1 if escape was
+ * pressed or 0 on error.
*/
int alert(AL_CONST char *s1, AL_CONST char *s2, AL_CONST char *s3, AL_CONST char *b1, AL_CONST char *b2, int c1, int c2)
{
@@ -2420,3 +2633,30 @@
return ret;
}
+
+
+/* al_alert:
+ * Displays a simple alert box, containing multiple lines of text (seperated
+ * newline characters and with a number of buttons. The buttons should be
+ * passed as an array of ALERT_BUTTONs with the last button's msg attribute
+ * being NULL. Returns 1 if the first button in the array was pressed, 2 if
+ * the second was pressed and so on, -1 if escape was pressed or 0
+ * if an error occured.
+ */
+int al_alert(AL_CONST ALERT_BUTTON *buttons, AL_CONST char *str)
+{
+ int ret;
+ char *msg;
+
+ msg = _concat_strs(str, 0, 0);
+
+ if(!msg)
+ return 0;
+
+ /* pass to _alert to handle newlines, tabs and buttons */
+ ret = _alert(msg, buttons);
+
+ free(msg);
+
+ return ret;
+}