594 lines
14 KiB
C++
594 lines
14 KiB
C++
/**
|
|
@file prompt.cpp
|
|
|
|
@author Morgan McGuire, http://graphics.cs.williams.edu
|
|
@cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com
|
|
@cite Font setting code by Kurt Miller, kurt@flipcode.com
|
|
|
|
@created 2000-08-26
|
|
@edited 2005-01-14
|
|
*/
|
|
|
|
#include "G3D/prompt.h"
|
|
#include "G3D/platform.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef G3D_WINDOWS
|
|
# include <sstream>
|
|
# include <conio.h>
|
|
#else
|
|
# define _getch getchar
|
|
#endif
|
|
|
|
#if 0 /* G3DFIX: exclude GUI prompt code */
|
|
#ifdef G3D_OSX
|
|
|
|
/*#ifdef __LP64__
|
|
# undef __LP64__
|
|
#endif
|
|
*/
|
|
|
|
# include "G3D/prompt_cocoa.h"
|
|
|
|
/*
|
|
#ifdef G3D_64BIT
|
|
# define __LP64__
|
|
#endif
|
|
*/
|
|
|
|
#endif
|
|
#endif /* G3DFIX: exclude GUI prompt code */
|
|
|
|
namespace G3D {
|
|
|
|
#if 0 /* G3DFIX: exclude GUI prompt code */
|
|
#ifdef G3D_WINDOWS
|
|
|
|
namespace _internal {
|
|
/**
|
|
Generic Win32 dialog facility.
|
|
@author Max McGuire
|
|
*/
|
|
class DialogTemplate {
|
|
public:
|
|
|
|
DialogTemplate(LPCSTR caption, DWORD style,
|
|
int x, int y, int w, int h,
|
|
LPCSTR font = NULL, WORD fontSize = 8) {
|
|
|
|
usedBufferLength = sizeof(DLGTEMPLATE);
|
|
totalBufferLength = usedBufferLength;
|
|
|
|
dialogTemplate = (DLGTEMPLATE*)malloc(totalBufferLength);
|
|
|
|
dialogTemplate->style = style;
|
|
|
|
if (font != NULL) {
|
|
dialogTemplate->style |= DS_SETFONT;
|
|
}
|
|
|
|
dialogTemplate->x = (short)x;
|
|
dialogTemplate->y = (short)y;
|
|
dialogTemplate->cx = (short)w;
|
|
dialogTemplate->cy = (short)h;
|
|
dialogTemplate->cdit = 0;
|
|
|
|
dialogTemplate->dwExtendedStyle = 0;
|
|
|
|
// The dialog box doesn't have a menu or a special class
|
|
AppendData("\0", 2);
|
|
AppendData("\0", 2);
|
|
|
|
// Add the dialog's caption to the template
|
|
|
|
AppendString(caption);
|
|
|
|
if (font != NULL) {
|
|
AppendData(&fontSize, sizeof(WORD));
|
|
AppendString(font);
|
|
}
|
|
}
|
|
|
|
void AddComponent(LPCSTR type, LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
|
|
|
|
DLGITEMTEMPLATE item;
|
|
|
|
item.style = style;
|
|
item.x = (short)x;
|
|
item.y = (short)y;
|
|
item.cx = (short)w;
|
|
item.cy = (short)h;
|
|
item.id = id;
|
|
|
|
item.dwExtendedStyle = exStyle;
|
|
|
|
AppendData(&item, sizeof(DLGITEMTEMPLATE));
|
|
|
|
AppendString(type);
|
|
AppendString(caption);
|
|
|
|
WORD creationDataLength = 0;
|
|
AppendData(&creationDataLength, sizeof(WORD));
|
|
|
|
// Increment the component count
|
|
dialogTemplate->cdit++;
|
|
|
|
}
|
|
|
|
|
|
void AddButton(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
|
|
|
|
AddStandardComponent(0x0080, caption, style, exStyle, x, y, w, h, id);
|
|
|
|
WORD creationDataLength = 0;
|
|
AppendData(&creationDataLength, sizeof(WORD));
|
|
|
|
}
|
|
|
|
|
|
void AddEditBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
|
|
|
|
AddStandardComponent(0x0081, caption, style, exStyle, x, y, w, h, id);
|
|
|
|
WORD creationDataLength = 0;
|
|
AppendData(&creationDataLength, sizeof(WORD));
|
|
|
|
}
|
|
|
|
|
|
void AddStatic(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
|
|
|
|
AddStandardComponent(0x0082, caption, style, exStyle, x, y, w, h, id);
|
|
|
|
WORD creationDataLength = 0;
|
|
AppendData(&creationDataLength, sizeof(WORD));
|
|
|
|
}
|
|
|
|
|
|
void AddListBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
|
|
|
|
AddStandardComponent(0x0083, caption, style, exStyle, x, y, w, h, id);
|
|
|
|
WORD creationDataLength = sizeof(WORD) + 5 * sizeof(WCHAR);
|
|
AppendData(&creationDataLength, sizeof(WORD));
|
|
|
|
AppendString("TEST");
|
|
|
|
}
|
|
|
|
|
|
void AddScrollBar(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
|
|
|
|
AddStandardComponent(0x0084, caption, style, exStyle, x, y, w, h, id);
|
|
|
|
WORD creationDataLength = 0;
|
|
AppendData(&creationDataLength, sizeof(WORD));
|
|
|
|
}
|
|
|
|
|
|
void AddComboBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
|
|
|
|
AddStandardComponent(0x0085, caption, style, exStyle, x, y, w, h, id);
|
|
|
|
WORD creationDataLength = 0;
|
|
AppendData(&creationDataLength, sizeof(WORD));
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Returns a pointer to the Win32 dialog template which the object
|
|
* represents. This pointer may become invalid if additional components
|
|
* are added to the template.
|
|
*
|
|
*/
|
|
operator const DLGTEMPLATE*() const {
|
|
return dialogTemplate;
|
|
}
|
|
|
|
virtual ~DialogTemplate() {
|
|
free(dialogTemplate);
|
|
}
|
|
|
|
protected:
|
|
|
|
void AddStandardComponent(WORD type, LPCSTR caption, DWORD style, DWORD exStyle,
|
|
int x, int y, int w, int h, WORD id, LPSTR font = NULL, WORD fontSize = 8) {
|
|
|
|
DLGITEMTEMPLATE item;
|
|
|
|
// DWORD align the beginning of the component data
|
|
|
|
AlignData(sizeof(DWORD));
|
|
|
|
item.style = style;
|
|
if (font != NULL) {
|
|
item.style |= DS_SETFONT;
|
|
}
|
|
item.x = (short)x;
|
|
item.y = (short)y;
|
|
item.cx = (short)w;
|
|
item.cy = (short)h;
|
|
item.id = id;
|
|
|
|
item.dwExtendedStyle = exStyle;
|
|
|
|
AppendData(&item, sizeof(DLGITEMTEMPLATE));
|
|
|
|
WORD preType = 0xFFFF;
|
|
|
|
AppendData(&preType, sizeof(WORD));
|
|
AppendData(&type, sizeof(WORD));
|
|
|
|
AppendString(caption);
|
|
|
|
if (font != NULL) {
|
|
AppendData(&fontSize, sizeof(WORD));
|
|
AppendString(font);
|
|
}
|
|
|
|
// Increment the component count
|
|
dialogTemplate->cdit++;
|
|
}
|
|
|
|
|
|
void AlignData(int size) {
|
|
|
|
int paddingSize = usedBufferLength % size;
|
|
|
|
if (paddingSize != 0) {
|
|
EnsureSpace(paddingSize);
|
|
usedBufferLength += paddingSize;
|
|
}
|
|
|
|
}
|
|
|
|
void AppendString(LPCSTR string) {
|
|
|
|
int length = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0);
|
|
|
|
WCHAR* wideString = (WCHAR*)malloc(sizeof(WCHAR) * length);
|
|
MultiByteToWideChar(CP_ACP, 0, string, -1, wideString, length);
|
|
|
|
AppendData(wideString, length * sizeof(WCHAR));
|
|
free(wideString);
|
|
|
|
}
|
|
|
|
void AppendData(const void* data, int dataLength) {
|
|
|
|
EnsureSpace(dataLength);
|
|
|
|
memcpy((char*)dialogTemplate + usedBufferLength, data, dataLength);
|
|
usedBufferLength += dataLength;
|
|
|
|
}
|
|
|
|
void EnsureSpace(int length) {
|
|
if (length + usedBufferLength > totalBufferLength) {
|
|
totalBufferLength += length * 2;
|
|
|
|
void* newBuffer = malloc(totalBufferLength);
|
|
memcpy(newBuffer, dialogTemplate, usedBufferLength);
|
|
|
|
free(dialogTemplate);
|
|
dialogTemplate = (DLGTEMPLATE*)newBuffer;
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
DLGTEMPLATE* dialogTemplate;
|
|
|
|
int totalBufferLength;
|
|
int usedBufferLength;
|
|
|
|
};
|
|
|
|
|
|
struct PromptParams {
|
|
const char* message;
|
|
const char* title;
|
|
};
|
|
|
|
/**
|
|
* Constants for controls.
|
|
*/
|
|
#define IDC_MESSAGE 1000
|
|
#define IDC_BUTTON0 2000
|
|
|
|
INT_PTR CALLBACK PromptDlgProc(HWND hDlg, UINT msg,
|
|
WPARAM wParam, LPARAM lParam) {
|
|
switch(msg) {
|
|
case WM_INITDIALOG:
|
|
{
|
|
PromptParams *params = (PromptParams*)lParam;
|
|
::SetWindowTextA(::GetDlgItem(hDlg, IDC_MESSAGE), params->message);
|
|
|
|
::SetFocus(::GetDlgItem(hDlg, IDC_BUTTON0));
|
|
|
|
SetWindowTextA(hDlg, params->title);
|
|
|
|
HFONT hfont =
|
|
CreateFontA(16, 0, 0, 0, FW_NORMAL,
|
|
FALSE, FALSE, FALSE,
|
|
ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
|
|
PROOF_QUALITY, FIXED_PITCH | FF_MODERN, "Courier New");
|
|
|
|
SendDlgItemMessage(hDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE,0));
|
|
|
|
|
|
break;
|
|
}
|
|
case WM_COMMAND:
|
|
{
|
|
int choiceNumber = LOWORD(wParam) - IDC_BUTTON0;
|
|
if ((choiceNumber >= 0) && (choiceNumber < 10)) {
|
|
EndDialog(hDlg, choiceNumber);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
// Under SDL 1.2.6 we get a NCDESTROY message for no reason and the
|
|
// window is immediately closed. This is here to debug the problem.
|
|
(void)0;
|
|
break;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}; // namespace _internal
|
|
|
|
|
|
using namespace _internal;
|
|
|
|
/**
|
|
* Show a dialog prompt.
|
|
*/
|
|
static int guiPrompt(
|
|
const char* windowTitle,
|
|
const char* prompt,
|
|
const char** choice,
|
|
int numChoices) {
|
|
|
|
int width = 340;
|
|
int height = 220;
|
|
|
|
const int buttonSpacing = 2;
|
|
const int buttonWidth =
|
|
(width - buttonSpacing * 2 -
|
|
buttonSpacing * (numChoices - 1)) / numChoices;
|
|
const int buttonHeight = 13;
|
|
|
|
|
|
DialogTemplate dialogTemplate(
|
|
windowTitle,
|
|
WS_CAPTION | DS_CENTER | WS_SYSMENU,
|
|
10, 10, width, height,
|
|
"Tahoma");
|
|
|
|
dialogTemplate.AddEditBox(
|
|
"Edit", WS_VISIBLE | ES_READONLY | ES_OEMCONVERT | ES_MULTILINE | WS_TABSTOP, WS_EX_STATICEDGE,
|
|
2, 2, width - 4, height - buttonHeight - 7, IDC_MESSAGE);
|
|
|
|
int i;
|
|
for (i = 0; i < numChoices; ++i) {
|
|
|
|
int x = buttonSpacing + i * (buttonWidth + buttonSpacing);
|
|
int y = height - buttonHeight - buttonSpacing;
|
|
|
|
dialogTemplate.AddButton(choice[i], WS_VISIBLE | WS_TABSTOP, 0,
|
|
x, y, buttonWidth, buttonHeight, IDC_BUTTON0 + (WORD)i);
|
|
|
|
}
|
|
|
|
// Convert all single \n characters to \r\n for proper printing
|
|
int strLen = 0;
|
|
const char* pStr = prompt;
|
|
|
|
while (*pStr != '\0') {
|
|
if ((*pStr == '\n') && (pStr != prompt)) {
|
|
if (*(pStr - 1) != '\r') {
|
|
++strLen;
|
|
}
|
|
}
|
|
++strLen;
|
|
++pStr;
|
|
}
|
|
|
|
char* newStr = (char*)malloc(strLen + 1);
|
|
|
|
const char* pStr2 = prompt;
|
|
char* pNew = newStr;
|
|
|
|
while (*pStr2 != '\0') {
|
|
if ((*pStr2 == '\n') && (pStr2 != prompt)) {
|
|
if (*(pStr2 - 1) != '\r') {
|
|
*pNew = '\r';
|
|
++pNew;
|
|
}
|
|
}
|
|
*pNew = *pStr2;
|
|
++pNew;
|
|
++pStr2;
|
|
}
|
|
|
|
*pNew = '\0';
|
|
|
|
PromptParams params;
|
|
params.message = newStr;;
|
|
params.title = windowTitle;
|
|
|
|
HMODULE module = GetModuleHandle(0);
|
|
int ret = (int)DialogBoxIndirectParam(module, dialogTemplate, NULL, (DLGPROC) PromptDlgProc, (DWORD)¶ms);
|
|
|
|
free(newStr);
|
|
|
|
/*
|
|
For debugging when DialogBoxIndirectParam fails:
|
|
|
|
// The last error value. (Which is preserved across the call).
|
|
DWORD lastErr = GetLastError();
|
|
|
|
// The decoded message from FormatMessage
|
|
LPTSTR formatMsg = NULL;
|
|
|
|
if (NULL == formatMsg) {
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
lastErr,
|
|
0,
|
|
(LPTSTR)&formatMsg,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
// Make sure the message got translated into something.
|
|
LPTSTR realLastErr;
|
|
if (NULL != formatMsg) {
|
|
realLastErr = formatMsg;
|
|
} else {
|
|
realLastErr = "Last error code does not exist.";
|
|
}
|
|
|
|
// Get rid of the allocated memory from FormatMessage.
|
|
if (NULL != formatMsg) {
|
|
LocalFree((LPVOID)formatMsg);
|
|
}
|
|
*/
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
#endif /* G3DFIX: exclude GUI prompt code */
|
|
|
|
|
|
/**
|
|
* Show a prompt on stdout
|
|
*/
|
|
static int textPrompt(
|
|
const char* windowTitle,
|
|
const char* prompt,
|
|
const char** choice,
|
|
int numChoices) {
|
|
|
|
printf("\n___________________________________________________\n");
|
|
printf("%s\n", windowTitle);
|
|
printf("%s", prompt);
|
|
|
|
if (numChoices > 10) {
|
|
numChoices = 10;
|
|
}
|
|
|
|
int c = -1;
|
|
if (numChoices > 1) {
|
|
printf("\n");
|
|
printf("Choose an option by number:");
|
|
|
|
while ((c < 0) || (c >= numChoices)) {
|
|
printf("\n");
|
|
|
|
for (int i = 0; i < numChoices; ++i) {
|
|
if (numChoices <= 3) {
|
|
printf(" (%d) %s ", i, choice[i]);
|
|
} else {
|
|
printf(" (%d) %s\n", i, choice[i]);
|
|
}
|
|
}
|
|
|
|
printf("\n> ");
|
|
c = _getch() - '0';
|
|
|
|
if ((c < 0) || (c >= numChoices)) {
|
|
printf("'%d' is not a valid choice.", c);
|
|
} else {
|
|
printf("%d", c);
|
|
}
|
|
}
|
|
|
|
} else if (numChoices == 1) {
|
|
|
|
printf("\nPress any key for '%s'...", choice[0]);
|
|
_getch();
|
|
c = 0;
|
|
|
|
} else {
|
|
|
|
printf("\nPress any key...");
|
|
_getch();
|
|
c = 0;
|
|
}
|
|
|
|
printf("\n___________________________________________________\n");
|
|
return c;
|
|
}
|
|
|
|
#if 0 /* G3DFIX: exclude GUI prompt code */
|
|
#ifdef G3D_OSX
|
|
|
|
static int guiPrompt
|
|
(const char* windowTitle,
|
|
const char* prompt,
|
|
const char** choice,
|
|
int numChoices) {
|
|
|
|
return prompt_cocoa(windowTitle, prompt, choice, numChoices);
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif /* G3DFIX: exclude GUI prompt code */
|
|
int prompt(
|
|
const char* windowTitle,
|
|
const char* prompt,
|
|
const char** choice,
|
|
int numChoices,
|
|
bool useGui) {
|
|
#if 0 /* G3DFIX: exclude GUI prompt code */
|
|
#ifdef G3D_WINDOWS
|
|
if (useGui) {
|
|
// Build the message box
|
|
return guiPrompt(windowTitle, prompt, choice, numChoices);
|
|
}
|
|
#endif
|
|
|
|
#ifdef G3D_OSX
|
|
if (useGui){
|
|
//Will default to text prompt if numChoices > 4
|
|
int result = guiPrompt(windowTitle, prompt, choice, numChoices);
|
|
fprintf(stderr, "%d\n", result);
|
|
return result;
|
|
}
|
|
#endif
|
|
#endif /* G3DFIX: exclude GUI prompt code */
|
|
return textPrompt(windowTitle, prompt, choice, numChoices);
|
|
}
|
|
|
|
|
|
void msgBox(
|
|
const std::string& message,
|
|
const std::string& title) {
|
|
|
|
const char *choice[] = {"Ok"};
|
|
prompt(title.c_str(), message.c_str(), choice, 1, true);
|
|
}
|
|
|
|
#ifndef G3D_WINDOWS
|
|
#undef _getch
|
|
#endif
|
|
|
|
}// namespace
|
|
|