| 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 10XMessageBoxButton::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;}
 |