123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #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();
- }
- int XMessageBox::alertAssert(const char *windowTitle, const char *message)
- {
- mMessage = message;
- mTitle = windowTitle;
- mButtons.clear();
- mButtons.push_back(XMessageBoxButton("Exit", OK));
- mButtons.push_back(XMessageBoxButton("Ignore", Cancel));
- mButtons.push_back(XMessageBoxButton("Ignore All", IgnoreAll));
- mButtons.push_back(XMessageBoxButton("Debug", Retry));
- 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
- XMapRaised(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;
- }
|