From b31fda7ce89bc4e8e9183ce51e2cc832a5770a25 Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Sun, 15 Sep 2013 21:39:51 +0100
Subject: [PATCH] [Add] Input dialogues can now be cancelled and return NULL.

---
 src/land.c    | 10 ++++--
 src/menu.c    |  7 +++--
 src/player.c  | 30 ++++++++++++++----
 src/player.h  |  2 +-
 src/save.c    |  2 +-
 src/toolkit.c | 86 ++++++++++++++++++++++++++++++++++++++++++---------
 src/toolkit.h |  3 +-
 7 files changed, 111 insertions(+), 29 deletions(-)

diff --git a/src/land.c b/src/land.c
index a0ca39d..de3436d 100644
--- a/src/land.c
+++ b/src/land.c
@@ -247,8 +247,9 @@ static void outfits_open(void) {
   snprintf(buf, 128, "%s - Outfits", land_planet->name);
   secondary_wid = window_create(buf, -1, -1,
                                 OUTFITS_WIDTH, OUTFITS_HEIGHT);
+
   /* Will allow buying from keyboard. */
-  window_setFptr(secondary_wid, outfits_buy);
+  window_setAccept(secondary_wid, outfits_buy);
 
   /* Buttons. */
   window_addButton(secondary_wid, -20, 20,
@@ -674,8 +675,11 @@ static void shipyard_buy(char* str) {
                    "Do you really want to spend %s on a new ship?", buf)==0)
     return;
 
-  player_newShip(ship, player->solid->pos.x, player->solid->pos.y,
-                 0., 0., player->solid->dir);
+  if(player_newShip(ship, player->solid->pos.x, player->solid->pos.y,
+                 0., 0., player->solid->dir) != 0) {
+    /* Player actually oborted naming process. */
+    return;
+  }
 
   player->credits -= ship->price; /* Auch! Paying is hard! */
 
diff --git a/src/menu.c b/src/menu.c
index fc4e85a..3577771 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -161,6 +161,8 @@ void menu_small(void) {
 
   wid = window_create("Menu", -1, -1, MENU_WIDTH, MENU_HEIGHT);
 
+  window_setCancel(wid, menu_small_close);
+
   window_addButton(wid, 20, 20 + BUTTON_HEIGHT*2 + 20*2,
                    BUTTON_WIDTH, BUTTON_HEIGHT,
                    "btnResume", "Resume", menu_small_close);
@@ -176,9 +178,8 @@ void menu_small(void) {
 }
 
 static void menu_small_close(char* str) {
-  if(strcmp(str, "btnResume")==0)
-    window_destroy(window_get("Menu"));
-
+  (void)str;
+  window_destroy(window_get("Menu"));
   menu_Close(MENU_SMALL);
 }
 
diff --git a/src/player.c b/src/player.c
index 6e713cf..49c765e 100644
--- a/src/player.c
+++ b/src/player.c
@@ -216,6 +216,12 @@ void player_new(void) {
   player_name = dialogue_input("Player Name", 3, 20,
                                "Please tell me your name:");
 
+  /* Player cancelled dialogue. */
+  if(player_name == NULL) {
+    menu_main();
+    return;
+  }
+
   if(lfile_fileExists("saves/%s.ls", player_name)) {
     r = dialogue_YesNo("Overwrite",
         "You already have a pilot named %s. Overwrite?", player_name);
@@ -311,8 +317,12 @@ static void player_newMake(void) {
   player_message("Welcome to "APPNAME"!");
   player_message("v%d.%d.%d", VMAJOR, VMINOR, VREV);
 
-  /* Create the player and start the game. */
-  player_newShip(ship, x, y, 0., 0., RNG(0, 359)/180.*M_PI);
+  /* Try to create the pilot, if fails re-ask for player name. */
+  if(player_newShip(ship, x, y, 0., 0., RNG(0, 369)/180.*M_PI) != 0) {
+    player_new();
+    return;
+  }
+  
   space_init(sysname);
   free(sysname);
 
@@ -321,14 +331,15 @@ static void player_newMake(void) {
 }
 
 /**
- * @fn void player_newShip(Ship* ship, double px, double py,
+ * @fn int player_newShip(Ship* ship, double px, double py,
  *                        double vx, double vy, double dir)
  *
  * @brief Create a new ship for player.
+ *    @return 0 indicates success, -1 means dialogue was cancelled.
  *
  * @sa player_newShipMake
  */
-void player_newShip(Ship* ship, double px, double py,
+int player_newShip(Ship* ship, double px, double py,
                     double vx, double vy, double dir) {
 
   char* ship_name;
@@ -341,11 +352,18 @@ void player_newShip(Ship* ship, double px, double py,
   player_vy   = vy;
   player_dir  = dir;
 
-  ship_name = dialogue_input("Player Name", 3, 20,
+  ship_name = dialogue_input("Ship Name", 3, 20,
                              "Please name your shiny new %s", ship->name);
+
+  /* Dialogue cancelled. */
+  if(ship_name == NULL)
+    return -1;
+
   player_newShipMake(ship_name);
 
   free(ship_name);
+
+  return 0;
 }
 
 /**
@@ -357,7 +375,7 @@ static void player_newShipMake(char* name) {
   Vec2 vp, vv;
 
   /* Store the current ship if it exists. */
-  if(player) {
+  if(player != NULL) {
     player_stack = realloc(player_stack, sizeof(Pilot*)*(player_nstack+1));
     player_stack[player_nstack] = pilot_copy(player);
     player_lstack = realloc(player_lstack, sizeof(char*)*(player_nstack+1));
diff --git a/src/player.h b/src/player.h
index e43ba45..d440f3b 100644
--- a/src/player.h
+++ b/src/player.h
@@ -31,7 +31,7 @@ typedef enum RadarShape_ { RADAR_RECT, RADAR_CIRCLE } RadarShape;
 
 /* Creation/Cleanup. */
 void player_new(void);
-void player_newShip(Ship* ship, double px, double py,
+int player_newShip(Ship* ship, double px, double py,
                     double vx, double vy, double dir);
 void player_cleanup(void);
 int gui_load(const char* name);
diff --git a/src/save.c b/src/save.c
index 8cb003d..a1f03a7 100644
--- a/src/save.c
+++ b/src/save.c
@@ -144,7 +144,7 @@ void load_game_menu(void) {
       "btnDelete", "Del", load_menu_delete);
 
   /* Default action. */
-  window_setFptr(wid, load_menu_load);
+  window_setAccept(wid, load_menu_load);
 }
 
 static void load_menu_close(char* str) {
diff --git a/src/toolkit.c b/src/toolkit.c
index b3cce92..8525025 100644
--- a/src/toolkit.c
+++ b/src/toolkit.c
@@ -1,3 +1,9 @@
+/**
+ * @file toolkit.c
+ *
+ * @brief Handle window and widgets.
+ */
+
 #include <stdarg.h>
 
 #include "lephisto.h"
@@ -92,9 +98,10 @@ typedef struct Window_ {
   int hidden;  /* Is it hidden? */
   int focus;  /* Which window is focused. */
 
-  /* Pointer to a function to run if user hits 'enter' and no button is focused */
-  /* nor any other input thiny that catches 'enter'. */
-  void(*def_fptr)(char*);
+  void(*accept_fptr)(char*); /**< Triggered by hitting 'enter' with no widget
+                                  that catches the keypress. */
+  void(*cancel_fptr)(char*); /**< Triggered by hitting 'escape' with no
+                                  widget that catches the keypress. */
 
   double x,y; /* Position. */
   double w,h; /* Dimensions. */
@@ -158,6 +165,7 @@ static void dialogue_alertClose(char* str);
 static void dialogue_msgClose(char* str);
 static void dialogue_YesNoClose(char* str);
 static void dialogue_inputClose(char* str);
+static void dialogue_inputCancel(char* str);
 /* Secondary loop hack. */
 static int loop_done;
 static int toolkit_loop(void);
@@ -491,9 +499,10 @@ unsigned int window_create(char* name, const int x, const int y,
   wdw->id = wid;
   wdw->name = strdup(name);
 
-  wdw->hidden   = 0;
-  wdw->focus    = -1;
-  wdw->def_fptr = NULL;
+  wdw->hidden       = 0;
+  wdw->focus        = -1;
+  wdw->accept_fptr  = NULL;
+  wdw->cancel_fptr  = NULL;
 
   wdw->w = (double)w;
   wdw->h = (double)h;
@@ -530,12 +539,40 @@ unsigned int window_create(char* name, const int x, const int y,
   return wid;
 }
 
-/* Sets the window's default function. */
-void window_setFptr(const unsigned int wid, void(*fptr)(char*)) {
+/**
+ * @fn void window_setAccept(const unsigned int wid, void(*accept)(char*))
+ * 
+ * @brief Set the default accept function of the window.
+ *
+ * This function is called whenever 'enter' is pressed and the current widget
+ * does not catch it. NULL disables the accept function.
+ *    @param wid ID of the window to set the accept function.
+ *    @param accept Function to trigger when window is "accepted". Parameter
+ *                  passed is window name.
+ */
+void window_setAccept(const unsigned int wid, void(*accept)(char*)) {
   Window* wdw;
 
   wdw = window_wget(wid);
-  if(wdw != NULL) wdw->def_fptr = fptr;
+  if(wdw != NULL) wdw->accept_fptr = accept;
+}
+
+/**
+ * @fn void window_setCancel(const unsigned int wid, void(*cancel)(char*))
+ *
+ * @brief Set the default cancel function of the window.
+ *
+ * This function is called whenever 'escape' is hit and the current widget
+ * does not catch it. NULL disables the cancel function.
+ *    @param wid ID of the window to set cancel function.
+ *    @param cancel Function to trigger when window is "cancelled".
+ *                  Parameter passed is the window name.
+ */
+void window_setCancel(const unsigned int wid, void(*cancel)(char*)) {
+  Window* wdw;
+
+  wdw = window_wget(wid);
+  if(wdw != NULL) wdw->cancel_fptr = cancel;
 }
 
 /* Destroy a widget. */
@@ -1288,6 +1325,13 @@ static int toolkit_keyEvent(SDL_Event* event) {
     if(event->type == SDL_KEYDOWN)
       toolkit_triggerFocus();
     return 1;
+  case SDLK_ESCAPE:
+    if(event->type == SDL_KEYDOWN)
+      if(wdw->cancel_fptr != NULL) {
+        (*wdw->cancel_fptr)(wdw->name);
+        return 1;
+      }
+    return 0;
   case SDLK_UP:
     if(event->type == SDL_KEYDOWN) {
       toolkit_regKey(SDLK_UP);
@@ -1391,7 +1435,7 @@ static void toolkit_triggerFocus(void) {
         wgt->name, wdw->name);
     break;
   default:
-    if(wdw->def_fptr)(*wdw->def_fptr)(wgt->name);
+    if(wdw->accept_fptr)(*wdw->accept_fptr)(wgt->name);
     break;
   }
 }
@@ -1598,6 +1642,7 @@ static void dialogue_YesNoClose(char* str) {
 
 /* Toolkit input boxes, return input. */
 static unsigned int input_wid = 0;
+static int input_cancelled = 0;
 char* dialogue_input(char* title, int min, int max, const char* fmt, ...) {
   char msg[512], *input;
   va_list ap;
@@ -1613,12 +1658,16 @@ char* dialogue_input(char* title, int min, int max, const char* fmt, ...) {
     va_end(ap);
   }
 
+  /* Start out not cancelled. */
+  input_cancelled = 0;
+
   /* Get text height. */
   h = gl_printHeight(&gl_smallFont, 200, msg);
 
   /* Create the window. */
   input_wid = window_create(title, -1, -1, 240, h+140);
-  window_setFptr(input_wid, dialogue_inputClose);
+  window_setAccept(input_wid, dialogue_inputClose);
+  window_setCancel(input_wid, dialogue_inputCancel);
   /* Text. */
   window_addText(input_wid, 30, -30, 200, h, 0, "txtInput",
                  &gl_smallFont, &cDConsole, msg);
@@ -1632,7 +1681,8 @@ char* dialogue_input(char* title, int min, int max, const char* fmt, ...) {
 
   /* Tricky secondary loop. */
   input = NULL;
-  while(!input || ((int)strlen(input) < min)) {
+  while(!input_cancelled && (!input || 
+        ((int)strlen(input) < min))) { /* Must be longer then min. */
     /* Must be longer than min. */
     if(input) {
       dialogue_alert("Input must be at least %d characters long!", min);
@@ -1640,11 +1690,14 @@ char* dialogue_input(char* title, int min, int max, const char* fmt, ...) {
       input = NULL;
     }
 
-    if(toolkit_loop()) /* Error in loop -> quit. */
+    if(toolkit_loop() != 0) /* Error in loop -> quit. */
       return NULL;
 
     /* Save the input. */
-    input = strdup(window_getInput(input_wid, "inpInput"));
+    if(input_cancelled != 0)
+      input = NULL;
+    else
+      input = strdup(window_getInput(input_wid, "inpInput"));
   }
 
   /* Cleanup. */
@@ -1661,6 +1714,11 @@ static void dialogue_inputClose(char* str) {
   loop_done = 1;
 }
 
+static void dialogue_inputCancel(char* str) {
+  input_cancelled = 1;
+  dialogue_inputClose(str);
+}
+
 /* Init. */
 int toolkit_init(void) {
   windows = malloc(sizeof(Window)*MIN_WINDOWS);
diff --git a/src/toolkit.h b/src/toolkit.h
index 09957b5..077186e 100644
--- a/src/toolkit.h
+++ b/src/toolkit.h
@@ -53,7 +53,8 @@ int   dialogue_YesNo(char* caption, const char* fmt, ...);
 char* dialogue_input(char* title, int min, int max, const char* fmt, ...);
 
 /* Modification. */
-void window_setFptr(const unsigned int wid, void(*fptr)(char*));
+void window_setAccept(const unsigned int wid, void(*fptr)(char*));
+void window_setCancel(const unsigned int wid, void(*cancel)(char*));
 /* Text. */
 void window_modifyText(const unsigned int wid, char* name, char* newstring);
 /* Button. */