359 lines
8.7 KiB
C
359 lines
8.7 KiB
C
/**
|
|
* @file dialogue.c.
|
|
*
|
|
* @brief Is a hight level api around toolkit.c for easy window creation.
|
|
*
|
|
* Only one dialogue may be open at once or behaviour is unspecified.
|
|
*
|
|
* All these dialogues use what I'm calling the secondary main loop hack.
|
|
* Basically they spawn another main lopp identical to the primary whose only
|
|
* difference is that it breaks on loop_done. Therefore this loop hijacks
|
|
* the main lopp until it's over, making these functions seem to be blocking
|
|
* without really being blocking.
|
|
*
|
|
* @todo Make dialogue system more flexible.
|
|
*
|
|
* @sa toolkit.c
|
|
*/
|
|
#include <stdarg.h>
|
|
#include "lephisto.h"
|
|
#include "log.h"
|
|
#include "toolkit.h"
|
|
#include "pause.h"
|
|
#include "opengl.h"
|
|
#include "input.h"
|
|
#include "dialogue.h"
|
|
|
|
int dialogue_open; /**< Number of dialogues open. */
|
|
|
|
/* Extern. */
|
|
extern void main_loop(void); /* From lephisto.c */
|
|
|
|
/* Dialogues. */
|
|
static glFont* dialogue_getSize(char* msg, int* w, int* h);
|
|
static void dialogue_alertClose(unsigned int wid, char* str);
|
|
static void dialogue_msgClose(unsigned int wid, char* str);
|
|
static void dialogue_YesNoClose(unsigned int wid, char* str);
|
|
static void dialogue_inputClose(unsigned int wid, char* str);
|
|
static void dialogue_inputCancel(unsigned int wid, char* str);
|
|
|
|
/* Secondary loop hack. */
|
|
static int loop_done; /**< Used to indicate the secondary loop is finished. */
|
|
static int toolkit_loop(void);
|
|
|
|
/**
|
|
* @brief Check to see if a dialogue is open.
|
|
*/
|
|
int dialogue_isOpen(void) {
|
|
return !!dialogue_open;
|
|
}
|
|
|
|
/**
|
|
* @brief Display an alert popup with only an ok button and a message.
|
|
* @param fmt Printf stype message to display.
|
|
*/
|
|
void dialogue_alert(const char* fmt, ...) {
|
|
char msg[512];
|
|
va_list ap;
|
|
unsigned int wdw;
|
|
int h;
|
|
|
|
if(fmt == NULL) return;
|
|
else { /* Get the message. */
|
|
va_start(ap, fmt);
|
|
vsnprintf(msg, 512, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
h = gl_printHeight(&gl_smallFont, 260, msg);
|
|
|
|
wdw = window_create("Warning", -1, -1, 300, 90+h);
|
|
window_addText(wdw, 20, -30, 260, h, 0, "txtAlert",
|
|
&gl_smallFont, &cBlack, msg);
|
|
window_addButton(wdw, 135, 20, 50, 30, "btnOK", "OK",
|
|
dialogue_alertClose);
|
|
|
|
dialogue_open++;
|
|
}
|
|
|
|
/**
|
|
* @brief Closes the alert dialogue.
|
|
* @param wid Window being closed.
|
|
* @param str Unused.
|
|
*/
|
|
static void dialogue_alertClose(unsigned int wid, char* str) {
|
|
(void)str;
|
|
window_destroy(wid);
|
|
dialogue_open--;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the size needed for the dialogue.
|
|
* @param msg Message of the dialogue.
|
|
* @param[out] width Get the width needed.
|
|
* @param[out] height Get the height needed.
|
|
* @return The font that matches the size.
|
|
*/
|
|
static glFont* dialogue_getSize(char* msg, int* width, int* height) {
|
|
glFont* font;
|
|
double w, h, d;
|
|
int len;
|
|
|
|
w = 300; /* Default width to try. */
|
|
len = strlen(msg);
|
|
|
|
/* First we split by text length. */
|
|
if(len < 50) {
|
|
font = &gl_defFont;
|
|
h = gl_printHeight(font, w-40, msg);
|
|
} else {
|
|
/* Now we look at proportion. */
|
|
font = &gl_smallFont;
|
|
h = gl_printHeight(font, w-40, msg);
|
|
|
|
d = ((double)w/(double)h) * (2./4.); /* Deformation factor. */
|
|
if(fabs(d) > 0.3) {
|
|
if(h > w)
|
|
w = h;
|
|
h = gl_printHeight(font, w-40, msg);
|
|
}
|
|
}
|
|
|
|
/* Set values. */
|
|
(*width) = w;
|
|
(*height) = h;
|
|
|
|
return font;
|
|
}
|
|
|
|
/**
|
|
* @brief Open a dialogue window with an OK button and a message.
|
|
* @param caption Window title.
|
|
* @param fmt Printf syle message to display.
|
|
*/
|
|
void dialogue_msg(char* caption, const char* fmt, ...) {
|
|
char msg[4096];
|
|
va_list ap;
|
|
int w, h;
|
|
glFont* font;
|
|
|
|
unsigned int msg_wid;
|
|
|
|
if(fmt == NULL) return;
|
|
else {
|
|
va_start(ap, fmt);
|
|
vsnprintf(msg, 4096, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
font = dialogue_getSize(msg, &w, &h);
|
|
|
|
/* Create the window. */
|
|
msg_wid = window_create(caption, -1, -1, w, 110 + h);
|
|
window_addText(msg_wid, 20, -40, w-40, h, 0, "txtMsg",
|
|
font, &cBlack, msg);
|
|
window_addButton(msg_wid, (w-50)/2, 20, 50, 30, "btnOK", "OK",
|
|
dialogue_msgClose);
|
|
|
|
dialogue_open++;
|
|
toolkit_loop();
|
|
}
|
|
|
|
/**
|
|
* @brief Closes a message dialogue.
|
|
* @param wid Window being closed.
|
|
* @param str Unused.
|
|
*/
|
|
static void dialogue_msgClose(unsigned int wid, char* str) {
|
|
(void)str;
|
|
window_destroy(wid);
|
|
loop_done = 1;
|
|
dialogue_open--;
|
|
}
|
|
|
|
/* Run a dialogue with a Yes and No button, return 1 if yes, 0 for no. */
|
|
static int yesno_result;
|
|
static unsigned int yesno_wid = 0;
|
|
int dialogue_YesNo(char* caption, const char* fmt, ...) {
|
|
char msg[4096];
|
|
va_list ap;
|
|
int w, h;
|
|
glFont* font;
|
|
|
|
if(yesno_wid) return -1;
|
|
|
|
if(fmt == NULL) return -1;
|
|
else { /* Get the message. */
|
|
va_start(ap, fmt);
|
|
vsnprintf(msg, 4096, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
font = dialogue_getSize(msg, &w, &h);
|
|
|
|
/* Create the window. */
|
|
yesno_wid = window_create(caption, -1, -1, w, h+110);
|
|
|
|
/* Text. */
|
|
window_addText(yesno_wid, 20, -40, w-40, h, 0, "txtYesNo",
|
|
font, &cBlack, msg);
|
|
|
|
/* Buttons. */
|
|
window_addButton(yesno_wid, w/2-50-10, 20, 50, 30, "btnYes", "Yes",
|
|
dialogue_YesNoClose);
|
|
window_addButton(yesno_wid, w/2+10, 20, 50, 30, "btnNo", "No",
|
|
dialogue_YesNoClose);
|
|
|
|
/* Tricky secondary loop. */
|
|
dialogue_open++;
|
|
toolkit_loop();
|
|
|
|
/* Return the result. */
|
|
return yesno_result;
|
|
}
|
|
|
|
/**
|
|
* @brief Closes a yesno dialogue.
|
|
* @param wid Window being closed.
|
|
* @param str Unused.
|
|
*/
|
|
static void dialogue_YesNoClose(unsigned int wid, char* str) {
|
|
/* Store the result. */
|
|
if(strcmp(str, "btnYes")==0) yesno_result = 1;
|
|
else if(strcmp(str, "btnNo")==0) yesno_result = 0;
|
|
|
|
/* Destroy the window. */
|
|
window_destroy(wid);
|
|
yesno_wid = 0;
|
|
|
|
loop_done = 1;
|
|
dialogue_open--;
|
|
}
|
|
|
|
static unsigned int input_wid = 0;
|
|
static int input_cancelled = 0;
|
|
/**
|
|
* @brief Create a dialogue that allows the player to write a message.
|
|
*
|
|
* You must free the result if it's not null.
|
|
* @param title Title of the dialogue window.
|
|
* @param min Minimum length of the message (must be non-zero).
|
|
* @param max Maximum length of the message (must be non-zero).
|
|
* @param fmt Printf style message to display on the dialogue.
|
|
* @return The message the player typed or NULL if it was cancelled.
|
|
*/
|
|
char* dialogue_input(char* title, int min, int max, const char* fmt, ...) {
|
|
char msg[512], *input;
|
|
va_list ap;
|
|
int h;
|
|
|
|
if(input_wid) return NULL;
|
|
|
|
if(fmt == NULL) return NULL;
|
|
else { /* Get the message. */
|
|
va_start(ap, fmt);
|
|
vsnprintf(msg, 512, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/* Start out not cancelled. */
|
|
input_cancelled = 0;
|
|
|
|
/* Get text height. */
|
|
h = gl_printHeight(&gl_smallFont, 200, msg);
|
|
|
|
/* Create Window. */
|
|
input_wid = window_create(title, -1, -1, 240, h+140);
|
|
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);
|
|
|
|
/* Input. */
|
|
window_addInput(input_wid, 20, -50-h, 200, 20, "inpInput", max, 1);
|
|
/* Button. */
|
|
window_addButton(input_wid, -20, 20, 80, 30,
|
|
"btnClose", "Done", dialogue_inputClose);
|
|
|
|
/* Tricky secondary. */
|
|
dialogue_open++;
|
|
input = NULL;
|
|
while(!input_cancelled && (!input ||
|
|
((int) strlen(input) < min))) { /* Must be longer than min. */
|
|
|
|
if(input) {
|
|
dialogue_alert("Input must be at least %d characters long!", min);
|
|
free(input);
|
|
input = NULL;
|
|
}
|
|
|
|
if(toolkit_loop() != 0) /* Error in loop -> quit. */
|
|
return NULL;
|
|
|
|
if(input_cancelled != 0)
|
|
input = NULL;
|
|
else
|
|
input = strdup(window_getInput(input_wid, "inpInput"));
|
|
}
|
|
|
|
/* Cleanup plox. */
|
|
window_destroy(input_wid);
|
|
input_wid = 0;
|
|
dialogue_open--;
|
|
|
|
/* Return the result. */
|
|
return input;
|
|
}
|
|
|
|
/**
|
|
* @brief Closes an input dialogue.
|
|
* @param wid Unused.
|
|
* @param str Unused.
|
|
*/
|
|
static void dialogue_inputClose(unsigned int wid, char* str) {
|
|
(void)str;
|
|
(void)wid;
|
|
|
|
/* Break the loop. */
|
|
loop_done = 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Cancels an input dialogue.
|
|
* @param wid Window being closed.
|
|
* @param str Unused.
|
|
*/
|
|
static void dialogue_inputCancel(unsigned int wid, char* str) {
|
|
input_cancelled = 1;
|
|
dialogue_inputClose(wid, str);
|
|
}
|
|
|
|
/**
|
|
* @brief Spawns a secondary loop that only works until the toolkit dies,
|
|
* alot like the main while loop in lephisto.c
|
|
* @return 0 on success.
|
|
*/
|
|
static int toolkit_loop(void) {
|
|
SDL_Event event;
|
|
|
|
loop_done = 0;
|
|
while(!loop_done && toolkit) {
|
|
/* Loop first so exit condition is checked before next iteration. */
|
|
main_loop();
|
|
|
|
while(SDL_PollEvent(&event)) { /* Event loop. */
|
|
if(event.type == SDL_QUIT) { /* Pass quit event to main engine. */
|
|
loop_done = 1;
|
|
SDL_PushEvent(&event);
|
|
return -1;
|
|
}
|
|
input_handle(&event); /* Handles all the events and player keybinds. */
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|