/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
// infobox.c
// Two-letter Module Descriptor: ib
// ****************************************************************************
// Copyright (C) B. Cameron Lesiuk, 1999. All rights reserved.
// Permission to use/copy this code is granted for non-commercial use only.
// B. Cameron Lesiuk
// Victoria, BC, Canada
// wi961@freenet.victoria.bc.ca
// ****************************************************************************

/* This module contains code to make and operate an information box, much
 * like the standard windows created with the MessageBox function.
 * I did this so that I had better control of what the message box looks
 * like.
 */

#include<windows.h>
#include<windowsx.h> /* for message crackers */
#include<stdio.h>
#include<ctl3d.h>
#include"ctl3dl.h"
#include<string.h>
#include"molerc.h"
#include"molem.h"
#include"infobox.h"

/* local typedefs */
typedef struct IBPARAMtag {
  unsigned long style;
	LPCSTR title;
	LPSTR text;
	LPCSTR icon;
	DWORD help;
  HGLOBAL global;
  } IBPARAM;

/* local defines */
#define IB_NUM_BUTTONS 8 // how many buttons do we service here?
#define IB_NUM_INFOBOX_POSITIONS 4 // number of infobox positions on main screen - this affects the positioning of the infobox
#define IB_MAX_BUTTON_WIDTH 80 // button width max
#define IB_MIN_BUTTON_WIDTH 70 // minimum button width
#define IB_MAX_SPACE_WIDTH  20 // space between the buttons max
#define IB_MIN_SPACE_WIDTH  2  // space between the buttons min
#define IB_MIN_TEXT_SPACE  6  // space between the buttons min

/* Globals */
/* Note this module doesn't use any state-dependent globals;
 * it is designed to be re-entrant. */
/* g_ibBoxNum keeps track of which new-box position to use. Yes,
 * there is the possibility of this variable getting mucked up
 * by an interruption at "just the right moment", but hey, it
 * just means that two infoboxes will be a little out of kilter,
 * with a possible complete overlap. This is definately non-fatal! */
int g_ibBoxNum=0;
/* All buttons are recorded (flags & values) here. The order is
 * important: the earlier buttons get the focus and are placed
 * before the later buttons! IE: IDYES and IDNO are both visible-
 * IDYES is to the left of IDNO in the box, and IDYES gets the
 * initial focus, because it's located earlier in these lists!!! */
int g_ibButValue[IB_NUM_BUTTONS]={IDYES ,IDNO ,IDOK ,IDCANCEL ,IDABORT,
  IDRETRY ,IDIGNORE ,IDHELP };
long g_ibButFlag[IB_NUM_BUTTONS]={IB_YES,IB_NO,IB_OK,IB_CANCEL,IB_ABORT,
  IB_RETRY,IB_IGNORE,IB_HELP};

/* ibInfoBox
 * Creates a pop-up window to ask the user a quick question. The
 * p_icon is supposed to be a resource identifier of a bitmap resource
 * to put beside the text.
 */
int ibInfoBox(HWND p_hWnd,LPCSTR p_text,LPCSTR p_title,unsigned long p_style,
  LPCSTR p_icon,DWORD p_help)
  {
  IBPARAM l_param;
  char l_buf[2]=" ";
  int l_rc;

  l_param.global=NULL;
  if (p_text)
    if (p_text[0])
      l_param.global=GlobalAlloc(GHND|GMEM_NOCOMPACT,strlen(p_text)+1);

  if (l_param.global)
    {
    l_param.text=(LPSTR)GlobalLock(l_param.global);
    if (!l_param.text)
      l_param.text=l_buf;
    else
      strcpy(l_param.text,p_text);
    }
  else
    l_param.text=l_buf;

  l_param.style=p_style;
  l_param.title=p_title;
  l_param.icon=p_icon;
  l_param.help=p_help;

  l_rc=DialogBoxParam(g_aahInst,MAKEINTRESOURCE(DIALOG_INFOBOX),p_hWnd,ibInfoBoxProc,(LPARAM)(&l_param));
  if (l_param.global)
    {
	  GlobalUnlock(l_param.global);
	  GlobalFree(l_param.global);
    }
  return l_rc;
  }

BOOL CALLBACK _export ibInfoBoxProc(HWND p_hWnd, UINT p_message,
											WPARAM p_wParam, LPARAM p_lParam)
	{
	switch (p_message)
		{
		case WM_INITDIALOG:
			{
			IBPARAM *l_param;
			int l_i,l_j,l_default;
			HWND l_hWnd;
			RECT l_rect;
			int l_numbuttons,l_centreX,l_buttonX,l_spaceX,l_tempX;
			int l_positionY,l_buttonY;
			int l_longsize;
			POINT l_point,l_textsize;
			HDC l_hDC;
			char *l_p1,*l_p2,l_oldp2;

			/* Subclass this box so we appear 3-D. whoah. */
			Ctl3dSubclassDlgEx(p_hWnd,CTL3D_ALL);

			/* note: icon is currently ignored */
			l_param=(IBPARAM *)p_lParam;
			SetWindowText(p_hWnd,l_param->title);
			SetDlgItemText(p_hWnd,IDC_INFOBOX_TEXT,l_param->text);
			SetDlgItemInt(p_hWnd,IDC_INFOBOX_HELP,LOWORD(l_param->help),FALSE);

			/* First, count the buttons */
			l_numbuttons=0;
			for (l_i=0;l_i<IB_NUM_BUTTONS;l_i++)
				if (l_param->style & g_ibButFlag[l_i])
					l_numbuttons++;

			/* now resize the dialog box according to how big our
			 * text string is. */
			GetClientRect(l_hWnd=GetDlgItem(p_hWnd,IDC_INFOBOX_TEXT),&l_rect); // get original text box size - this is our MAX size
			l_point.x=l_point.y=0; // get our initial Y position
			MapWindowPoints(l_hWnd,p_hWnd,&l_point,1);
			l_positionY=l_point.y; // l_positionY is the Y coord for the top of the text box.
			l_hDC=GetDC(l_hWnd);
			/* note: from this point on, l_point.x and l_point.y represent our text box
			 * x and y dimentions (ie width and height). */
			/* let's find our longest line */
			l_longsize=0;
			l_p1=l_p2=(char *)l_param->text;
			do
				{
				if ((*l_p2=='\r')||(*l_p2==0)) // carridge return
					{
					l_oldp2=*l_p2;
					*l_p2=0;
					GetTextExtentPoint(l_hDC,l_p1,strlen(l_p1),(SIZE *)&l_textsize);
					*l_p2=l_oldp2;
					if (l_textsize.x>l_longsize)
						l_longsize=l_textsize.x;
					l_p1=l_p2+1;
					}
				l_p2++;
				}
			while (*(l_p2-1)!=0);
			/* ok, l_longsize is our longest size. Now, shove this through our MAX WIDTH filter */
			l_point.x=l_longsize;
			/* also use a left-over l_textsize.y value to record our line height */
			l_point.y=l_textsize.y;

			l_i=l_numbuttons*IB_MIN_BUTTON_WIDTH+
				(l_numbuttons-1)*IB_MIN_SPACE_WIDTH; // absolute min window width - to fit buttons in!
			if (l_point.x>l_rect.right) // NOTE: this would be l_rect.right-l_rect.left, but GetClientRect returns l_rect.top=l_rect.left=0! Cool.
				l_point.x=l_rect.right;
			if (l_point.x<l_i) // make sure we have enough room for buttons!!
				l_point.x=l_i;   // note: buttons can override max width!
			/* ok, now comes the height. We want to move the window down so that the
			 * text is centred vertically. However, to do this properly, we need to
			 * know how many lines there are. l_point.y currently gives us the height
			 * of a single line of text. We have to check each line of text, though, for
			 * wrap. */
			l_i=0; // number of rows (lines) of text we have, ACCOUNTING FOR TEXT WRAP
			l_p1=l_p2=(char *)l_param->text;
			do
				{
				if ((*l_p2=='\r')||(*l_p2==0)) // carridge return
					{
					l_oldp2=*l_p2;
					*l_p2=0;
					GetTextExtentPoint(l_hDC,l_p1,strlen(l_p1),(SIZE *)&l_textsize);
					*l_p2=l_oldp2;
					l_i+=1+((l_textsize.x-1)/l_point.x); // add 1 for this line, + 1 for each wrapped line
					l_p1=l_p2+1;
					}
				l_p2++;
				}
			while (*(l_p2-1)!=0);

			/* ok. l_i holds the number of lines.*/
			l_point.y*=l_i; // multiply # lines (l_i) times height per line (l_point.y) and
											// this becomes our text box raw height.
			if (l_point.y>l_rect.bottom) // update our Y height - check make sure not too long
				l_point.y=l_rect.bottom;
			l_positionY+=(l_rect.bottom-l_point.y)/2; // and our Y pos - centred nicely
			l_point.y=l_rect.bottom-l_positionY; // drop bottom as far as it will go, anyways

			/* Ok, we now have our width, our height, and our Y position. */
			/* Adjust our dialog box width according to our new calculations
			 * and move our window in the Y position to centre
			 * the text vertically */
			/* first, move & resize the text box */
			SetWindowPos(l_hWnd,HWND_TOP,IB_MIN_TEXT_SPACE,l_positionY,
				l_point.x,l_point.y,SWP_NOACTIVATE|SWP_NOZORDER);
			/* and secondly, resize the dialog box (x-direction only) */
			GetWindowRect(p_hWnd,&l_rect);
			SetWindowPos(p_hWnd,HWND_TOP,0,0,l_point.x+2*(IB_MIN_TEXT_SPACE+
				GetSystemMetrics(SM_CXDLGFRAME)),l_rect.bottom-l_rect.top,
				SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
			ReleaseDC(l_hWnd,l_hDC);

			/* next, position this dialog box in the centre of the screen,
			 * with a slight offset between info boxes kept in track by
			 * g_ibBoxNum */
			l_i=g_ibBoxNum; // do this as fast as possible to avoid interruption
			g_ibBoxNum++;
			if (g_ibBoxNum>IB_NUM_INFOBOX_POSITIONS)
				g_ibBoxNum=0;
			GetWindowRect(GetDesktopWindow(),&l_rect); // find middle of screen
			l_point.x=(l_rect.right-l_rect.left)/2;
			l_point.y=(l_rect.bottom-l_rect.top)/3; // l_point is now the scrn middle
				 // NOTE: we divide l_point.y by **3** to achieve the "visual"
				 // centre of the screen - ie where everyone naturally looks!!!
				 // If we plop the window in the real centre, it "appears" too
				 // low to the natural human eye.
			l_j=GetSystemMetrics(SM_CYCAPTION); // get header height
			GetWindowRect(p_hWnd,&l_rect); // get dimentions of this dialog box
			l_point.x-=(l_rect.right-l_rect.left)/2; // get top-left of centred dialog box
			l_point.y-=(l_rect.bottom-l_rect.top)/2;
			l_point.x-=l_j*(IB_NUM_INFOBOX_POSITIONS/2); // find top-left of zero-th dialog box
			l_point.y-=l_j*(IB_NUM_INFOBOX_POSITIONS/2);
			l_point.x+=l_i*l_j; // find top-left of THIS dialog box
			l_point.y+=l_i*l_j;
			/* and now do the great re-positioning deed (without resize) */
			SetWindowPos(p_hWnd,HWND_TOP,l_point.x,l_point.y,0,0,SWP_NOACTIVATE|SWP_NOSIZE);

			/* Check we've got at least one functional button or the user
			 * can't exit!! The special case examined here is for the only
			 * button being IB_HELP with IB_HELPDEF, which would give the
			 * user a single button - the help button - and they still
			 * couldn't exit... but they could get help on how they
			 * can't exit! */
			if ((!l_numbuttons)||((l_numbuttons==1)&&
				(l_param->style&IB_HELP)&&(l_param->style&IB_HELPDEF)))
				{
				l_numbuttons++;
				l_param->style|=g_ibButFlag[0];
				}

			/* next, calculate button sizes  */
			GetClientRect(p_hWnd,&l_rect);
			l_tempX=l_rect.right-l_rect.left;  // get width of our window
			l_centreX=l_tempX/2;  // we position buttons from the centre
														// out so that round-off errors result
														// in a nicely even and centred button
														// array, as opposed to a button array
														// which is positioned a little to one
														// side.
			l_tempX-=2*IB_MIN_SPACE_WIDTH; // leave space at left & right edge
			l_tempX=l_tempX/l_numbuttons; // rough space per button
			l_buttonX=l_tempX-IB_MIN_SPACE_WIDTH; // rough button width
			l_spaceX=IB_MIN_SPACE_WIDTH;          // rough space width (space between buttons)
			if (l_buttonX>IB_MAX_BUTTON_WIDTH)    // finalize button width
				{
				l_buttonX=IB_MAX_BUTTON_WIDTH;
				l_spaceX=l_tempX-l_buttonX;         // re-rough space width
				}
			if (l_spaceX>IB_MAX_SPACE_WIDTH)      // finalize button space
				l_spaceX=IB_MAX_SPACE_WIDTH;

			/* We've got our button width, our space between the button width,
			 * and our window centre mark. That's all we need!
			 * Let's position our buttons (boogey time!)                 */
			/* First, l_tempX is our starting button position. We don't, at
			 * this point, have to worry about round-off problems because
			 * that's all taken care of now that we've finalized our
			 * width calculations. We may be a single pixel off, but that's
			 * close enough for our purposes. */
			if (l_numbuttons>1)
				l_tempX=l_centreX-
					((l_numbuttons*l_buttonX)+((l_numbuttons-1)*l_spaceX))/2;
			else
				l_tempX=l_centreX-l_buttonX/2;
			/* we also need the Y value for our buttons */
			GetWindowRect(l_hWnd=GetDlgItem(p_hWnd,g_ibButValue[0]),&l_rect);
			l_buttonY=l_rect.bottom-l_rect.top; // record height - don't change
			l_point.x=l_rect.left;
			l_point.y=l_rect.top;
			ScreenToClient(p_hWnd,&l_point);
			l_positionY=l_point.y; // record Y position - don't change
			for (l_i=0;l_i<IB_NUM_BUTTONS;l_i++) // go through, positioning buttons
				if (l_param->style & g_ibButFlag[l_i])
					{
					MoveWindow(GetDlgItem(p_hWnd,g_ibButValue[l_i]),l_tempX,
						l_positionY,l_buttonX,l_buttonY,FALSE);
					l_tempX+=l_buttonX+l_spaceX;
					}

			/* now, based on p_style, enable buttons */
			for (l_i=IB_NUM_BUTTONS-1;l_i>=0;l_i--)
				{
				if (l_param->style & g_ibButFlag[l_i])
					{
					EnableWindow(l_hWnd=GetDlgItem(p_hWnd,g_ibButValue[l_i]),TRUE);
					ShowWindow(l_hWnd,SW_SHOWNA);
					l_default=l_i;
					}
				}
			SetFocus(GetDlgItem(p_hWnd,g_ibButValue[l_default]));

			if (l_param->style & IB_HELPDEF)
				SetDlgItemInt(p_hWnd,IDC_INFOBOX_HELP,(UINT)l_param->help,FALSE);
			else
				SetDlgItemInt(p_hWnd,IDC_INFOBOX_HELP,0,FALSE);

			return TRUE;
			}
		case WM_COMMAND:
			{
			switch (GET_WM_COMMAND_ID(p_wParam,p_lParam))
				{
				case IDYES:
				case IDNO:
				case IDOK:
				case IDCANCEL:
				case IDABORT:
				case IDRETRY:
				case IDIGNORE:
					EndDialog(p_hWnd,GET_WM_COMMAND_ID(p_wParam,p_lParam));
					return TRUE;
				case IDHELP:
					{
					unsigned int l_i;
					BOOL l_bool;
					if ((l_i=GetDlgItemInt(p_hWnd,IDC_INFOBOX_HELP,&l_bool,FALSE))!=0)
						WinHelp(g_aahWnd,g_aaHelpFile,HELP_CONTEXT,(DWORD)l_i);
					else
						EndDialog(p_hWnd,GET_WM_COMMAND_ID(p_wParam,p_lParam));
					return TRUE;
					}
				default:
					break;
				}
			}
			break;
		default:
			break;
		}
	return d3DlgMessageCheck(p_hWnd,p_message,p_wParam,p_lParam);
	}