Initial commit
This commit is contained in:
480
Torque/SDK/engine/platformX86UNIX/x86UNIXMessageBox.cc
Normal file
480
Torque/SDK/engine/platformX86UNIX/x86UNIXMessageBox.cc
Normal file
@@ -0,0 +1,480 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include "platformX86UNIX/x86UNIXMessageBox.h"
|
||||
|
||||
#define MessageBox_MaxWinWidth 800
|
||||
#define MessageBox_MaxWinHeight 600
|
||||
#define MessageBox_MinWinWidth 450
|
||||
|
||||
#define MessageBox_ButtonBoxWidth 60
|
||||
#define MessageBox_ButtonBoxHeight 22
|
||||
#define MessageBox_ButtonSpacer 20
|
||||
#define MessageBox_ButtonVMargin 10
|
||||
#define MessageBox_ButtonHMargin 10
|
||||
|
||||
#define MessageBox_LineSpacer 2
|
||||
#define MessageBox_LineVMargin 10
|
||||
#define MessageBox_LineHMargin 10
|
||||
|
||||
XMessageBoxButton::XMessageBoxButton()
|
||||
{
|
||||
strcpy(mLabel, "");
|
||||
mClickVal = -1;
|
||||
mLabelWidth = mX = mY = mWidth = mHeight = mMouseX = mMouseX = -1;
|
||||
mMouseDown = false;
|
||||
}
|
||||
|
||||
XMessageBoxButton::XMessageBoxButton(const char* label, int clickVal)
|
||||
{
|
||||
strncpy(mLabel, label, LabelSize);
|
||||
mClickVal = clickVal;
|
||||
mLabelWidth = mX = mY = mWidth = mHeight = mMouseX = mMouseX = -1;
|
||||
mMouseDown = false;
|
||||
}
|
||||
|
||||
XMessageBox::XMessageBox(Display* display)
|
||||
{
|
||||
mMessage = "";
|
||||
mFS = NULL;
|
||||
mDisplay = display;
|
||||
}
|
||||
|
||||
XMessageBox::~XMessageBox()
|
||||
{
|
||||
clearMessageLines();
|
||||
if (mDisplay != NULL)
|
||||
{
|
||||
mDisplay = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int XMessageBox::alertOK(const char *windowTitle, const char *message)
|
||||
{
|
||||
mMessage = message;
|
||||
mTitle = windowTitle;
|
||||
mButtons.clear();
|
||||
mButtons.push_back(XMessageBoxButton("OK", OK));
|
||||
return show();
|
||||
}
|
||||
|
||||
int XMessageBox::alertOKCancel(const char *windowTitle, const char *message)
|
||||
{
|
||||
mMessage = message;
|
||||
mTitle = windowTitle;
|
||||
mButtons.clear();
|
||||
mButtons.push_back(XMessageBoxButton("OK", OK));
|
||||
mButtons.push_back(XMessageBoxButton("Cancel", Cancel));
|
||||
return show();
|
||||
}
|
||||
|
||||
int XMessageBox::alertRetryCancel(const char *windowTitle, const char *message)
|
||||
{
|
||||
mMessage = message;
|
||||
mTitle = windowTitle;
|
||||
mButtons.clear();
|
||||
mButtons.push_back(XMessageBoxButton("Retry", Retry));
|
||||
mButtons.push_back(XMessageBoxButton("Cancel", Cancel));
|
||||
return show();
|
||||
}
|
||||
|
||||
void XMessageBox::repaint()
|
||||
{
|
||||
int white = WhitePixel(mDisplay, DefaultScreen(mDisplay));
|
||||
int black = BlackPixel(mDisplay, DefaultScreen(mDisplay));
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
// line V margin
|
||||
y = y + MessageBox_LineVMargin * 2;
|
||||
|
||||
// line H margin
|
||||
x = MessageBox_LineHMargin;
|
||||
|
||||
XSetForeground(mDisplay, mGC, black);
|
||||
for (unsigned int i = 0; i < mMessageLines.size(); ++i)
|
||||
{
|
||||
XDrawString(mDisplay, mWin, mGC, x, y, mMessageLines[i],
|
||||
strlen(mMessageLines[i]));
|
||||
if (i < (mMessageLines.size() - 1))
|
||||
y = y + MessageBox_LineSpacer + mFontHeight;
|
||||
}
|
||||
XFlush(mDisplay);
|
||||
|
||||
// line V margin
|
||||
y = y + MessageBox_LineVMargin;
|
||||
|
||||
int maxButWidth = MessageBox_ButtonBoxWidth;
|
||||
int maxButHeight = MessageBox_ButtonBoxHeight;
|
||||
|
||||
// compute size of text labels on buttons
|
||||
int fgColor, bgColor;
|
||||
|
||||
int fontDirection, fontAscent, fontDescent;
|
||||
Vector<XMessageBoxButton>::iterator iter;
|
||||
for (iter = mButtons.begin(); iter != mButtons.end(); ++iter)
|
||||
{
|
||||
XCharStruct strInfo;
|
||||
XTextExtents(mFS, iter->getLabel(), strlen(iter->getLabel()),
|
||||
&fontDirection, &fontAscent, &fontDescent,
|
||||
&strInfo);
|
||||
// if (maxButWidth < strInfo.width)
|
||||
// maxButWidth = strInfo.width;
|
||||
// if (maxButHeight < (strInfo.ascent + strInfo.descent))
|
||||
// maxButHeight = (strInfo.ascent + strInfo.descent);
|
||||
iter->setLabelWidth(strInfo.width);
|
||||
}
|
||||
int buttonBoxWidth = maxButWidth;
|
||||
int buttonBoxHeight = maxButHeight;
|
||||
|
||||
// draw buttons
|
||||
// button V margin
|
||||
y = y + MessageBox_ButtonVMargin;
|
||||
|
||||
// center the buttons
|
||||
x = MessageBox_ButtonHMargin + (mMBWidth - getButtonLineWidth()) / 2;
|
||||
|
||||
for (iter = mButtons.begin(); iter != mButtons.end(); ++iter)
|
||||
{
|
||||
if (iter->drawReverse())
|
||||
{
|
||||
fgColor = white;
|
||||
bgColor = black;
|
||||
}
|
||||
else
|
||||
{
|
||||
fgColor = black;
|
||||
bgColor = white;
|
||||
}
|
||||
|
||||
XSetForeground(mDisplay, mGC, bgColor);
|
||||
XFillRectangle(mDisplay, mWin, mGC, x, y,
|
||||
buttonBoxWidth, buttonBoxHeight);
|
||||
XSetForeground(mDisplay, mGC, fgColor);
|
||||
XDrawRectangle(mDisplay, mWin, mGC, x, y,
|
||||
buttonBoxWidth, buttonBoxHeight);
|
||||
XDrawString(mDisplay, mWin, mGC,
|
||||
x + ((buttonBoxWidth - iter->getLabelWidth()) / 2),
|
||||
y + mFontAscent + ((buttonBoxHeight - mFontAscent) / 2),
|
||||
iter->getLabel(),
|
||||
strlen(iter->getLabel()));
|
||||
iter->setButtonRect(x, y, buttonBoxWidth, buttonBoxHeight);
|
||||
x = x + buttonBoxWidth + MessageBox_ButtonSpacer;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
static inline Type max(Type v1, Type v2)
|
||||
{
|
||||
if (v1 <= v2)
|
||||
return v2;
|
||||
else
|
||||
return v1;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
static inline Type min(Type v1, Type v2)
|
||||
{
|
||||
if (v1 > v2)
|
||||
return v2;
|
||||
else
|
||||
return v1;
|
||||
}
|
||||
|
||||
void XMessageBox::clearMessageLines()
|
||||
{
|
||||
Vector<char*>::iterator iter;
|
||||
for (iter = mMessageLines.begin(); iter != mMessageLines.end(); ++iter)
|
||||
delete [] *iter;
|
||||
mMessageLines.clear();
|
||||
}
|
||||
|
||||
void XMessageBox::splitMessage()
|
||||
{
|
||||
clearMessageLines();
|
||||
if (mMessage == NULL || strlen(mMessage)==0)
|
||||
// JMQTODO: what to do with empty strings?
|
||||
return;
|
||||
|
||||
// need to break message up in to lines, and store lines in
|
||||
// mMessageLines
|
||||
|
||||
int numChars = strlen(mMessage);
|
||||
const int ScratchBufSize = 2048;
|
||||
char scratchBuf[ScratchBufSize];
|
||||
memset(scratchBuf, 0, ScratchBufSize);
|
||||
|
||||
int fontDirection, fontAscent, fontDescent;
|
||||
XCharStruct strInfo;
|
||||
|
||||
char *curChar = const_cast<char*>(mMessage);
|
||||
char *endChar;
|
||||
char *curWrapped = scratchBuf;
|
||||
int curWidth = 0;
|
||||
int maxWidth = mMaxWindowWidth - (MessageBox_LineHMargin);
|
||||
|
||||
while ( // while pointers are in range...
|
||||
(curChar - mMessage) < numChars &&
|
||||
(curWrapped - scratchBuf) < ScratchBufSize)
|
||||
{
|
||||
// look for next space in remaining string
|
||||
endChar = index(curChar, ' ');
|
||||
if (endChar == NULL)
|
||||
endChar = index(curChar, '\0');
|
||||
|
||||
if (endChar != NULL)
|
||||
// increment one char past the space to include it
|
||||
endChar++;
|
||||
else
|
||||
// otherwise, set the endchar to one char ahead
|
||||
endChar = curChar + 1;
|
||||
|
||||
// compute length of substr
|
||||
int len = endChar - curChar;
|
||||
XTextExtents(mFS, curChar, len,
|
||||
&fontDirection, &fontAscent, &fontDescent,
|
||||
&strInfo);
|
||||
// if its too big, time to add a new line...
|
||||
if ((curWidth + strInfo.width) > maxWidth)
|
||||
{
|
||||
// create a new block for the line and add it
|
||||
*curWrapped = '\0';
|
||||
int len = strlen(scratchBuf);
|
||||
char* line = new char[len+1];
|
||||
strncpy(line, scratchBuf, len+1); // +1 gets the null char
|
||||
mMessageLines.push_back(line);
|
||||
|
||||
// reset curWrapped to the beginning of the scratch buffer
|
||||
curWrapped = scratchBuf;
|
||||
curWidth = 0;
|
||||
}
|
||||
// copy the current string into curWrapped if we have enough room
|
||||
int bytesRemaining =
|
||||
ScratchBufSize - (curWrapped - scratchBuf);
|
||||
if (bytesRemaining >= len)
|
||||
strncpy(curWrapped, curChar, len);
|
||||
|
||||
curWrapped += len;
|
||||
curWidth += strInfo.width;
|
||||
curChar = endChar;
|
||||
}
|
||||
|
||||
// make a final line out of any leftover stuff in the scratch buffer
|
||||
if (curWrapped != scratchBuf)
|
||||
{
|
||||
*curWrapped = '\0';
|
||||
int len = strlen(scratchBuf);
|
||||
char* line = new char[len+1];
|
||||
strncpy(line, scratchBuf, len+1); // +1 gets the null char
|
||||
mMessageLines.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
int XMessageBox::loadFont()
|
||||
{
|
||||
// load the font
|
||||
mFS = XLoadQueryFont(mDisplay,
|
||||
"-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
|
||||
|
||||
if (mFS == NULL)
|
||||
mFS = XLoadQueryFont(mDisplay, "fixed");
|
||||
|
||||
if (mFS == NULL)
|
||||
return -1;
|
||||
|
||||
// dummy call to XTextExtents to get the font specs
|
||||
XCharStruct strInfo;
|
||||
|
||||
XTextExtents(mFS, "foo", 1,
|
||||
&mFontDirection, &mFontAscent, &mFontDescent,
|
||||
&strInfo);
|
||||
|
||||
mFontHeight = mFontAscent + mFontDescent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XMessageBox::getButtonLineWidth()
|
||||
{
|
||||
return mButtons.size() * MessageBox_ButtonBoxWidth +
|
||||
(mButtons.size() - 1) * MessageBox_ButtonSpacer +
|
||||
MessageBox_ButtonHMargin * 2;
|
||||
}
|
||||
|
||||
void XMessageBox::setDimensions()
|
||||
{
|
||||
mMBWidth = MessageBox_MaxWinWidth;
|
||||
mMBHeight = MessageBox_MaxWinHeight;
|
||||
|
||||
// determine width of button line
|
||||
int buttonWidth = getButtonLineWidth();
|
||||
|
||||
// if there is only one line, the desired width is the greater of the
|
||||
// line width and the buttonWidth, otherwise the lineWidth is the
|
||||
// max possible width which we already set.
|
||||
if (mMessageLines.size() == 1)
|
||||
{
|
||||
XCharStruct strInfo;
|
||||
int fontDirection, fontAscent, fontDescent;
|
||||
|
||||
XTextExtents(mFS, mMessageLines[0], strlen(mMessageLines[0]),
|
||||
&fontDirection, &fontAscent, &fontDescent,
|
||||
&strInfo);
|
||||
|
||||
mMBWidth = max(MessageBox_LineHMargin * 2 + strInfo.width,
|
||||
buttonWidth);
|
||||
mMBWidth = max(mMBWidth, MessageBox_MinWinWidth);
|
||||
}
|
||||
|
||||
// determine the height of the button line
|
||||
int buttonHeight = MessageBox_ButtonBoxHeight +
|
||||
MessageBox_ButtonVMargin * 2;
|
||||
|
||||
int lineHeight = mFontHeight * mMessageLines.size() +
|
||||
(mMessageLines.size() - 1) * MessageBox_LineSpacer +
|
||||
MessageBox_LineVMargin * 2;
|
||||
|
||||
mMBHeight = buttonHeight + lineHeight;
|
||||
}
|
||||
|
||||
int XMessageBox::show()
|
||||
{
|
||||
if (mDisplay == NULL)
|
||||
return -1;
|
||||
|
||||
int retVal = 0;
|
||||
retVal = loadFont();
|
||||
if (retVal < 0)
|
||||
return retVal;
|
||||
|
||||
// set the maximum window dimensions
|
||||
mScreenWidth = DisplayWidth(mDisplay, DefaultScreen(mDisplay));
|
||||
mScreenHeight = DisplayHeight(mDisplay, DefaultScreen(mDisplay));
|
||||
mMaxWindowWidth = min(mScreenWidth, MessageBox_MaxWinWidth);
|
||||
mMaxWindowHeight = min(mScreenHeight, MessageBox_MaxWinHeight);
|
||||
|
||||
// split the message into a vector of lines
|
||||
splitMessage();
|
||||
|
||||
// set the dialog dimensions
|
||||
setDimensions();
|
||||
|
||||
mWin = XCreateSimpleWindow(
|
||||
mDisplay,
|
||||
DefaultRootWindow(mDisplay),
|
||||
(mScreenWidth - mMBWidth) / 2, (mScreenHeight - mMBHeight) / 2,
|
||||
mMBWidth, mMBHeight,
|
||||
1,
|
||||
BlackPixel(mDisplay, DefaultScreen(mDisplay)),
|
||||
WhitePixel(mDisplay, DefaultScreen(mDisplay)));
|
||||
|
||||
mGC = XCreateGC(mDisplay, mWin, 0, 0);
|
||||
|
||||
XSetFont(mDisplay, mGC, mFS->fid);
|
||||
|
||||
// set input mask
|
||||
XSelectInput(mDisplay, mWin,
|
||||
ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
|
||||
|
||||
// set wm protocols in case they hit X
|
||||
Atom wm_delete_window =
|
||||
XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
|
||||
Atom wm_protocols =
|
||||
XInternAtom(mDisplay, "WM_PROTOCOLS", False);
|
||||
XSetWMProtocols (mDisplay, mWin, &wm_delete_window, 1);
|
||||
// set pop up dialog hint
|
||||
XSetTransientForHint(mDisplay, mWin, mWin);
|
||||
|
||||
// set title
|
||||
XTextProperty wtitle;
|
||||
wtitle.value = (unsigned char *)mTitle;
|
||||
wtitle.encoding = XA_STRING;
|
||||
wtitle.format = 8;
|
||||
wtitle.nitems = strlen(mTitle);
|
||||
XSetWMName(mDisplay, mWin, &wtitle);
|
||||
|
||||
// show window
|
||||
XMapWindow(mDisplay, mWin);
|
||||
// move it in case some bozo window manager repositioned it
|
||||
XMoveWindow(mDisplay, mWin,
|
||||
(mScreenWidth - mMBWidth) / 2, (mScreenHeight - mMBHeight) / 2);
|
||||
// raise it to top
|
||||
XRaiseWindow(mDisplay, mWin);
|
||||
|
||||
XMessageBoxButton* clickedButton = NULL;
|
||||
XEvent event;
|
||||
Vector<XMessageBoxButton>::iterator iter;
|
||||
bool done = false;
|
||||
while (!done)
|
||||
{
|
||||
XNextEvent(mDisplay, &event);
|
||||
switch (event.type)
|
||||
{
|
||||
case Expose:
|
||||
repaint();
|
||||
break;
|
||||
case MotionNotify:
|
||||
for (iter = mButtons.begin(); iter != mButtons.end(); ++iter)
|
||||
iter->setMouseCoordinates(event.xmotion.x, event.xmotion.y);
|
||||
break;
|
||||
case ButtonPress:
|
||||
for (iter = mButtons.begin(); iter != mButtons.end(); ++iter)
|
||||
{
|
||||
if (iter->pointInRect(event.xbutton.x, event.xbutton.y))
|
||||
{
|
||||
iter->setMouseDown(true);
|
||||
iter->setMouseCoordinates(event.xbutton.x, event.xbutton.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ButtonRelease:
|
||||
for (iter = mButtons.begin(); iter != mButtons.end(); ++iter)
|
||||
{
|
||||
if (iter->pointInRect(event.xbutton.x, event.xbutton.y) &&
|
||||
iter->isMouseDown())
|
||||
{
|
||||
// we got a winner!
|
||||
clickedButton = iter;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clickedButton == NULL)
|
||||
{
|
||||
// user released outside a button. clear the button states
|
||||
for (iter = mButtons.begin(); iter != mButtons.end(); ++iter)
|
||||
iter->setMouseDown(false);
|
||||
}
|
||||
break;
|
||||
case ClientMessage:
|
||||
if (event.xclient.message_type == wm_protocols &&
|
||||
event.xclient.data.l[0] == static_cast<long>(wm_delete_window))
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
XUnmapWindow(mDisplay, mWin);
|
||||
XDestroyWindow(mDisplay, mWin);
|
||||
XFreeGC(mDisplay, mGC);
|
||||
XFreeFont(mDisplay, mFS);
|
||||
|
||||
if (clickedButton != NULL)
|
||||
return clickedButton->getClickVal();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user