x86UNIXMessageBox.client.cpp
Engine/source/platformX86UNIX/x86UNIXMessageBox.client.cpp
Public Defines
define
define
define
define
define
define
define
define
define
MessageBox_MaxWinHeight() 600
define
MessageBox_MaxWinWidth() 800
define
MessageBox_MinWinWidth() 450
Detailed Description
Public Defines
MessageBox_ButtonBoxHeight() 22
MessageBox_ButtonBoxWidth() 60
MessageBox_ButtonHMargin() 10
MessageBox_ButtonSpacer() 20
MessageBox_ButtonVMargin() 10
MessageBox_LineHMargin() 10
MessageBox_LineSpacer() 2
MessageBox_LineVMargin() 10
MessageBox_MaxWinHeight() 600
MessageBox_MaxWinWidth() 800
MessageBox_MinWinWidth() 450
Public Functions
max(Type v1, Type v2)
min(Type v1, Type v2)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27 28#include <X11/Xlib.h> 29#include <X11/Xutil.h> 30#include <X11/Xatom.h> 31 32#include "platformX86UNIX/x86UNIXMessageBox.h" 33 34#define MessageBox_MaxWinWidth 800 35#define MessageBox_MaxWinHeight 600 36#define MessageBox_MinWinWidth 450 37 38#define MessageBox_ButtonBoxWidth 60 39#define MessageBox_ButtonBoxHeight 22 40#define MessageBox_ButtonSpacer 20 41#define MessageBox_ButtonVMargin 10 42#define MessageBox_ButtonHMargin 10 43 44#define MessageBox_LineSpacer 2 45#define MessageBox_LineVMargin 10 46#define MessageBox_LineHMargin 10 47 48XMessageBoxButton::XMessageBoxButton() 49{ 50 strcpy(mLabel, ""); 51 mClickVal = -1; 52 mLabelWidth = mX = mY = mWidth = mHeight = mMouseX = mMouseX = -1; 53 mMouseDown = false; 54} 55 56XMessageBoxButton::XMessageBoxButton(const char* label, int clickVal) 57{ 58 strncpy(mLabel, label, LabelSize); 59 mClickVal = clickVal; 60 mLabelWidth = mX = mY = mWidth = mHeight = mMouseX = mMouseX = -1; 61 mMouseDown = false; 62} 63 64XMessageBox::XMessageBox(Display* display) 65{ 66 mMessage = ""; 67 mFS = NULL; 68 mDisplay = display; 69} 70 71XMessageBox::~XMessageBox() 72{ 73 clearMessageLines(); 74 if (mDisplay != NULL) 75 { 76 mDisplay = NULL; 77 } 78} 79 80int XMessageBox::alertOK(const char *windowTitle, const char *message) 81{ 82 mMessage = message; 83 mTitle = windowTitle; 84 mButtons.clear(); 85 mButtons.push_back(XMessageBoxButton("OK", OK)); 86 return show(); 87} 88 89int XMessageBox::alertOKCancel(const char *windowTitle, const char *message) 90{ 91 mMessage = message; 92 mTitle = windowTitle; 93 mButtons.clear(); 94 mButtons.push_back(XMessageBoxButton("OK", OK)); 95 mButtons.push_back(XMessageBoxButton("Cancel", Cancel)); 96 return show(); 97} 98 99int XMessageBox::alertRetryCancel(const char *windowTitle, const char *message) 100{ 101 mMessage = message; 102 mTitle = windowTitle; 103 mButtons.clear(); 104 mButtons.push_back(XMessageBoxButton("Retry", Retry)); 105 mButtons.push_back(XMessageBoxButton("Cancel", Cancel)); 106 return show(); 107} 108 109int XMessageBox::alertAssert(const char *windowTitle, const char *message) 110{ 111 mMessage = message; 112 mTitle = windowTitle; 113 mButtons.clear(); 114 mButtons.push_back(XMessageBoxButton("Exit", OK)); 115 mButtons.push_back(XMessageBoxButton("Ignore", Cancel)); 116 mButtons.push_back(XMessageBoxButton("Ignore All", IgnoreAll)); 117 mButtons.push_back(XMessageBoxButton("Debug", Retry)); 118 return show(); 119} 120 121void XMessageBox::repaint() 122{ 123 int white = WhitePixel(mDisplay, DefaultScreen(mDisplay)); 124 int black = BlackPixel(mDisplay, DefaultScreen(mDisplay)); 125 126 int x = 0; 127 int y = 0; 128 129 // line V margin 130 y = y + MessageBox_LineVMargin * 2; 131 132 // line H margin 133 x = MessageBox_LineHMargin; 134 135 XSetForeground(mDisplay, mGC, black); 136 for (unsigned int i = 0; i < mMessageLines.size(); ++i) 137 { 138 XDrawString(mDisplay, mWin, mGC, x, y, mMessageLines[i], 139 strlen(mMessageLines[i])); 140 if (i < (mMessageLines.size() - 1)) 141 y = y + MessageBox_LineSpacer + mFontHeight; 142 } 143 XFlush(mDisplay); 144 145 // line V margin 146 y = y + MessageBox_LineVMargin; 147 148 int maxButWidth = MessageBox_ButtonBoxWidth; 149 int maxButHeight = MessageBox_ButtonBoxHeight; 150 151 // compute size of text labels on buttons 152 int fgColor, bgColor; 153 154 int fontDirection, fontAscent, fontDescent; 155 Vector<XMessageBoxButton>::iterator iter; 156 for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) 157 { 158 XCharStruct strInfo; 159 XTextExtents(mFS, iter->getLabel(), strlen(iter->getLabel()), 160 &fontDirection, &fontAscent, &fontDescent, 161 &strInfo); 162// if (maxButWidth < strInfo.width) 163// maxButWidth = strInfo.width; 164// if (maxButHeight < (strInfo.ascent + strInfo.descent)) 165// maxButHeight = (strInfo.ascent + strInfo.descent); 166 iter->setLabelWidth(strInfo.width); 167 } 168 int buttonBoxWidth = maxButWidth; 169 int buttonBoxHeight = maxButHeight; 170 171 // draw buttons 172 // button V margin 173 y = y + MessageBox_ButtonVMargin; 174 175 // center the buttons 176 x = MessageBox_ButtonHMargin + (mMBWidth - getButtonLineWidth()) / 2; 177 178 for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) 179 { 180 if (iter->drawReverse()) 181 { 182 fgColor = white; 183 bgColor = black; 184 } 185 else 186 { 187 fgColor = black; 188 bgColor = white; 189 } 190 191 XSetForeground(mDisplay, mGC, bgColor); 192 XFillRectangle(mDisplay, mWin, mGC, x, y, 193 buttonBoxWidth, buttonBoxHeight); 194 XSetForeground(mDisplay, mGC, fgColor); 195 XDrawRectangle(mDisplay, mWin, mGC, x, y, 196 buttonBoxWidth, buttonBoxHeight); 197 XDrawString(mDisplay, mWin, mGC, 198 x + ((buttonBoxWidth - iter->getLabelWidth()) / 2), 199 y + mFontAscent + ((buttonBoxHeight - mFontAscent) / 2), 200 iter->getLabel(), 201 strlen(iter->getLabel())); 202 iter->setButtonRect(x, y, buttonBoxWidth, buttonBoxHeight); 203 x = x + buttonBoxWidth + MessageBox_ButtonSpacer; 204 } 205} 206 207template <class Type> 208static inline Type max(Type v1, Type v2) 209{ 210 if (v1 <= v2) 211 return v2; 212 else 213 return v1; 214} 215 216template <class Type> 217static inline Type min(Type v1, Type v2) 218{ 219 if (v1 > v2) 220 return v2; 221 else 222 return v1; 223} 224 225void XMessageBox::clearMessageLines() 226{ 227 Vector<char*>::iterator iter; 228 for (iter = mMessageLines.begin(); iter != mMessageLines.end(); ++iter) 229 delete [] *iter; 230 mMessageLines.clear(); 231} 232 233void XMessageBox::splitMessage() 234{ 235 clearMessageLines(); 236 if (mMessage == NULL || strlen(mMessage)==0) 237 // JMQTODO: what to do with empty strings? 238 return; 239 240 // need to break message up in to lines, and store lines in 241 // mMessageLines 242 243 int numChars = strlen(mMessage); 244 const int ScratchBufSize = 2048; 245 char scratchBuf[ScratchBufSize]; 246 memset(scratchBuf, 0, ScratchBufSize); 247 248 int fontDirection, fontAscent, fontDescent; 249 XCharStruct strInfo; 250 251 char *curChar = const_cast<char*>(mMessage); 252 char *endChar; 253 char *curWrapped = scratchBuf; 254 int curWidth = 0; 255 int maxWidth = mMaxWindowWidth - (MessageBox_LineHMargin); 256 257 while ( // while pointers are in range... 258 (curChar - mMessage) < numChars && 259 (curWrapped - scratchBuf) < ScratchBufSize) 260 { 261 // look for next space in remaining string 262 endChar = index(curChar, ' '); 263 if (endChar == NULL) 264 endChar = index(curChar, '\0'); 265 266 if (endChar != NULL) 267 // increment one char past the space to include it 268 endChar++; 269 else 270 // otherwise, set the endchar to one char ahead 271 endChar = curChar + 1; 272 273 // compute length of substr 274 int len = endChar - curChar; 275 XTextExtents(mFS, curChar, len, 276 &fontDirection, &fontAscent, &fontDescent, 277 &strInfo); 278 // if its too big, time to add a new line... 279 if ((curWidth + strInfo.width) > maxWidth) 280 { 281 // create a new block for the line and add it 282 *curWrapped = '\0'; 283 int len = strlen(scratchBuf); 284 char* line = new char[len+1]; 285 strncpy(line, scratchBuf, len+1); // +1 gets the null char 286 mMessageLines.push_back(line); 287 288 // reset curWrapped to the beginning of the scratch buffer 289 curWrapped = scratchBuf; 290 curWidth = 0; 291 } 292 // copy the current string into curWrapped if we have enough room 293 int bytesRemaining = 294 ScratchBufSize - (curWrapped - scratchBuf); 295 if (bytesRemaining >= len) 296 strncpy(curWrapped, curChar, len); 297 298 curWrapped += len; 299 curWidth += strInfo.width; 300 curChar = endChar; 301 } 302 303 // make a final line out of any leftover stuff in the scratch buffer 304 if (curWrapped != scratchBuf) 305 { 306 *curWrapped = '\0'; 307 int len = strlen(scratchBuf); 308 char* line = new char[len+1]; 309 strncpy(line, scratchBuf, len+1); // +1 gets the null char 310 mMessageLines.push_back(line); 311 } 312} 313 314int XMessageBox::loadFont() 315{ 316 // load the font 317 mFS = XLoadQueryFont(mDisplay, 318 "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"); 319 320 if (mFS == NULL) 321 mFS = XLoadQueryFont(mDisplay, "fixed"); 322 323 if (mFS == NULL) 324 return -1; 325 326 // dummy call to XTextExtents to get the font specs 327 XCharStruct strInfo; 328 329 XTextExtents(mFS, "foo", 1, 330 &mFontDirection, &mFontAscent, &mFontDescent, 331 &strInfo); 332 333 mFontHeight = mFontAscent + mFontDescent; 334 return 0; 335} 336 337int XMessageBox::getButtonLineWidth() 338{ 339 return mButtons.size() * MessageBox_ButtonBoxWidth + 340 (mButtons.size() - 1) * MessageBox_ButtonSpacer + 341 MessageBox_ButtonHMargin * 2; 342} 343 344void XMessageBox::setDimensions() 345{ 346 mMBWidth = MessageBox_MaxWinWidth; 347 mMBHeight = MessageBox_MaxWinHeight; 348 349 // determine width of button line 350 int buttonWidth = getButtonLineWidth(); 351 352 // if there is only one line, the desired width is the greater of the 353 // line width and the buttonWidth, otherwise the lineWidth is the 354 // max possible width which we already set. 355 if (mMessageLines.size() == 1) 356 { 357 XCharStruct strInfo; 358 int fontDirection, fontAscent, fontDescent; 359 360 XTextExtents(mFS, mMessageLines[0], strlen(mMessageLines[0]), 361 &fontDirection, &fontAscent, &fontDescent, 362 &strInfo); 363 364 mMBWidth = max(MessageBox_LineHMargin * 2 + strInfo.width, 365 buttonWidth); 366 mMBWidth = max(mMBWidth, MessageBox_MinWinWidth); 367 } 368 369 // determine the height of the button line 370 int buttonHeight = MessageBox_ButtonBoxHeight + 371 MessageBox_ButtonVMargin * 2; 372 373 int lineHeight = mFontHeight * mMessageLines.size() + 374 (mMessageLines.size() - 1) * MessageBox_LineSpacer + 375 MessageBox_LineVMargin * 2; 376 377 mMBHeight = buttonHeight + lineHeight; 378} 379 380int XMessageBox::show() 381{ 382 if (mDisplay == NULL) 383 return -1; 384 385 int retVal = 0; 386 retVal = loadFont(); 387 if (retVal < 0) 388 return retVal; 389 390 // set the maximum window dimensions 391 mScreenWidth = DisplayWidth(mDisplay, DefaultScreen(mDisplay)); 392 mScreenHeight = DisplayHeight(mDisplay, DefaultScreen(mDisplay)); 393 mMaxWindowWidth = min(mScreenWidth, MessageBox_MaxWinWidth); 394 mMaxWindowHeight = min(mScreenHeight, MessageBox_MaxWinHeight); 395 396 // split the message into a vector of lines 397 splitMessage(); 398 399 // set the dialog dimensions 400 setDimensions(); 401 402 mWin = XCreateSimpleWindow( 403 mDisplay, 404 DefaultRootWindow(mDisplay), 405 (mScreenWidth - mMBWidth) / 2, (mScreenHeight - mMBHeight) / 2, 406 mMBWidth, mMBHeight, 407 1, 408 BlackPixel(mDisplay, DefaultScreen(mDisplay)), 409 WhitePixel(mDisplay, DefaultScreen(mDisplay))); 410 411 mGC = XCreateGC(mDisplay, mWin, 0, 0); 412 413 XSetFont(mDisplay, mGC, mFS->fid); 414 415 // set input mask 416 XSelectInput(mDisplay, mWin, 417 ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask); 418 419 // set wm protocols in case they hit X 420 Atom wm_delete_window = 421 XInternAtom(mDisplay, "WM_DELETE_WINDOW", False); 422 Atom wm_protocols = 423 XInternAtom(mDisplay, "WM_PROTOCOLS", False); 424 XSetWMProtocols (mDisplay, mWin, &wm_delete_window, 1); 425 // set pop up dialog hint 426 XSetTransientForHint(mDisplay, mWin, mWin); 427 428 // set title 429 XTextProperty wtitle; 430 wtitle.value = (unsigned char *)mTitle; 431 wtitle.encoding = XA_STRING; 432 wtitle.format = 8; 433 wtitle.nitems = strlen(mTitle); 434 XSetWMName(mDisplay, mWin, &wtitle); 435 436 // show window 437 XMapRaised(mDisplay, mWin); 438 // move it in case some bozo window manager repositioned it 439 XMoveWindow(mDisplay, mWin, 440 (mScreenWidth - mMBWidth) / 2, (mScreenHeight - mMBHeight) / 2); 441 // raise it to top 442 XRaiseWindow(mDisplay, mWin); 443 444 XMessageBoxButton* clickedButton = NULL; 445 XEvent event; 446 Vector<XMessageBoxButton>::iterator iter; 447 bool done = false; 448 while (!done) 449 { 450 XNextEvent(mDisplay, &event); 451 switch (event.type) 452 { 453 case Expose: 454 repaint(); 455 break; 456 case MotionNotify: 457 for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) 458 iter->setMouseCoordinates(event.xmotion.x, event.xmotion.y); 459 break; 460 case ButtonPress: 461 for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) 462 { 463 if (iter->pointInRect(event.xbutton.x, event.xbutton.y)) 464 { 465 iter->setMouseDown(true); 466 iter->setMouseCoordinates(event.xbutton.x, event.xbutton.y); 467 break; 468 } 469 } 470 break; 471 case ButtonRelease: 472 for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) 473 { 474 if (iter->pointInRect(event.xbutton.x, event.xbutton.y) && 475 iter->isMouseDown()) 476 { 477 // we got a winner! 478 clickedButton = iter; 479 done = true; 480 break; 481 } 482 } 483 if (clickedButton == NULL) 484 { 485 // user released outside a button. clear the button states 486 for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) 487 iter->setMouseDown(false); 488 } 489 break; 490 case ClientMessage: 491 if (event.xclient.message_type == wm_protocols && 492 event.xclient.data.l[0] == static_cast<long>(wm_delete_window)) 493 done = true; 494 break; 495 } 496 repaint(); 497 } 498 499 XUnmapWindow(mDisplay, mWin); 500 XDestroyWindow(mDisplay, mWin); 501 XFreeGC(mDisplay, mGC); 502 XFreeFont(mDisplay, mFS); 503 504 if (clickedButton != NULL) 505 return clickedButton->getClickVal(); 506 else 507 return -1; 508} 509