[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;
+}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/