1 /*
2 * XFrisk - The classic board game for X
3 * Copyright (C) 1993-1999 Elan Feingold (elan@aetherworks.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: utils.c,v 1.6 1999/11/13 21:58:32 morphy Exp $
20 */
21
22 #include <X11/X.h>
23 #include <X11/Intrinsic.h>
24 #include <X11/cursorfont.h>
25 #include <X11/StringDefs.h>
26 #include <X11/Shell.h>
27
28 #include <X11/Xaw/AsciiText.h>
29 #include <X11/Xaw/List.h>
30 #include <X11/Xaw/Form.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36
37 #include "utils.h"
38 #include "network.h"
39 #include "riskgame.h"
40 #include "gui-vars.h"
41 #include "client.h"
42 #include "colormap.h"
43 #include "callbacks.h"
44 #include "game.h"
45 #include "dice.h"
46 #include "debug.h"
47
48 #define MAX_LINES 5
49
50 #define MAX_DIGITS 3
51 #define MAX_STRING "888"
52
53 /* Globals */
54 static Int32 iQueryResult;
55 static Cursor cursorWait=0, cursorPlay=0;
56
57 /* Private functions */
58 void UTIL_SetCountryBrightness(Int32 iCountry, Boolean fLight);
59
60 /************************************************************************
61 * FUNCTION: UTIL_PrintArmies
62 * HISTORY:
63 * 01.28.94 ESF Created.
64 * 02.05.94 ESF Added number centering (finally!)
65 * 03.30.94 ESF Changed EXTRA_Y to be smaller.
66 * 04.02.94 ESF Fixed font problem, not XSetFont'ing.
67 * 05.05.94 ESF Fixed so that 0 armies doesn't print anything.
68 * PURPOSE:
69 * NOTES:
70 ************************************************************************/
UTIL_PrintArmies(Int32 iCountry,Int32 iNumArmies,Int32 iColor)71 void UTIL_PrintArmies(Int32 iCountry, Int32 iNumArmies, Int32 iColor)
72 {
73 Int32 x, y, iWidth, iHeight;
74 Char strNumber[4];
75
76 /* Is number of armies right? */
77 if (iNumArmies>999)
78 {
79 #ifdef ENGLISH
80 (void)UTIL_PopupDialog("Warning", "UTIL: Too many armies",
81 1, "Ok", NULL, NULL);
82 #endif
83 #ifdef FRENCH
84 (void)UTIL_PopupDialog("Attention", "UTIL: Trop d'arm�es",
85 1, "Oui", NULL, NULL);
86 #endif
87 return;
88 }
89
90 /* Setup what we want to print (could be empty) */
91 if (iNumArmies>0)
92 snprintf(strNumber, sizeof(strNumber), "%d", iNumArmies);
93 else
94 snprintf(strNumber, sizeof(strNumber), " ");
95
96 /* Erase the old number, assume three digits */
97 x = RISK_GetTextXOfCountry(iCountry);
98 y = RISK_GetTextYOfCountry(iCountry);
99 iHeight = (pFont->max_bounds.ascent + pFont->max_bounds.descent);
100 iWidth = XTextWidth(pFont, MAX_STRING, MAX_DIGITS);
101
102 /* Draw erasing rectangle on screen and pixmap */
103 XSetForeground(hDisplay, hGC, COLOR_QueryColor(COLOR_CountryToColor(iCountry)));
104 XFillRectangle(hDisplay, hWindow, hGC, x, y-iHeight,
105 iWidth, iHeight+1);
106 XFillRectangle(hDisplay, pixMapImage, hGC, x, y-iHeight,
107 iWidth, iHeight+1);
108
109 /* Center the number, based on the number of digits */
110 if (strlen(strNumber)==1)
111 x += iWidth/3;
112 else if (strlen(strNumber)==2)
113 x += iWidth/4;
114
115 /* Finally draw the text in color requested. */
116 XSetForeground(hDisplay, hGC, iColor);
117 XSetFont(hDisplay, hGC, pFont->fid);
118
119 if (iNumArmies>=0)
120 {
121 XDrawString(hDisplay, hWindow, hGC, x, y, strNumber,
122 strlen(strNumber));
123 XDrawString(hDisplay, pixMapImage, hGC, x, y, strNumber,
124 strlen(strNumber));
125 }
126
127 /* Flush the queue of requests */
128 XFlush(hDisplay);
129 }
130
131
132 /************************************************************************
133 * FUNCTION: UTIL_DisplayMessage
134 * HISTORY:
135 * 02.04.94 ESF Created.
136 * 01.15.95 ESF Fixed memory leak.
137 * 04.30.95 ESF Got rid of newlines in incoming message.
138 * 04.30.95 ESF Improved multiline message handling.
139 * 29.08.95 JC Put the newline in the beginning of the message.
140 * 29.08.95 JC strFrom -> iFrom, added iTo.
141 * PURPOSE:
142 * NOTES:
143 ************************************************************************/
UTIL_DisplayMessage(Int32 iFrom,Int32 iTo,CString strMessage)144 void UTIL_DisplayMessage(Int32 iFrom, Int32 iTo, CString strMessage)
145 {
146 static Flag fFirst = TRUE;
147 Int32 i, iCount;
148 CString strOldMessage, strNewMessage;
149 const char *strFrom = NULL, *strNewLine = "", *strTo = "";
150 int has_a_to = 1;
151 size_t len;
152
153 #ifdef ENGLISH
154 const char *strToHead = "To ";
155 #endif
156 #ifdef FRENCH
157 const char *strToHead = "� ";
158 #endif
159
160 /* Get the old text */
161 XtVaGetValues(wMsgText, XtNstring, &strOldMessage, NULL);
162
163 /* Remove any newlines from new message */
164 for (i=0, iCount=strlen(strMessage); i!=iCount; i++)
165 if (strMessage[i]=='\n')
166 strMessage[i]=' ';
167
168 /* Build up the new message */
169 if (fFirst)
170 fFirst = FALSE;
171 else
172 strNewLine = "\n";
173
174 switch (iFrom)
175 {
176 case FROM_SERVER:
177 #ifdef ENGLISH
178 strFrom = "Server";
179 #endif
180 #ifdef FRENCH
181 strFrom = "Serveur";
182 #endif
183 break;
184 case FROM_UNKNOW:
185 #ifdef ENGLISH
186 strFrom = "Unknown";
187 #endif
188 #ifdef FRENCH
189 strFrom = "Inconnu";
190 #endif
191 break;
192 default:
193 D_Assert(iFrom >= -1 && iFrom < MAX_PLAYERS, "Bogus sender!");
194 strFrom = RISK_GetNameOfPlayer(iFrom);
195 }
196
197 switch (iTo)
198 {
199 case DST_ALLPLAYERS:
200 #ifdef ENGLISH
201 strTo = "Everybody";
202 #endif
203 #ifdef FRENCH
204 strTo = "tous";
205 #endif
206 break;
207 case DST_OTHER:
208 has_a_to = 0;
209 break;
210 default:
211 strTo = RISK_GetNameOfPlayer(iTo);
212 }
213
214 len = strlen(strOldMessage) + strlen(strNewLine) + strlen(strFrom) + 2
215 + 1 + strlen(strToHead) + strlen(strTo) + 4 + strlen(strMessage) + 1;
216 strNewMessage = (CString)MEM_Alloc(len);
217
218 snprintf(strNewMessage, len,
219 "%s%s%s: %s%s%s%s%s%s", strOldMessage, strNewLine, strFrom,
220 has_a_to ? "(" : "",
221 has_a_to ? strToHead : "",
222 has_a_to ? strTo : "",
223 has_a_to ? ") \"" : "",
224 strMessage,
225 has_a_to ? "\"" : "");
226
227 /* Set the new message, go to the end */
228 XtVaSetValues(wMsgText, XtNstring, strNewMessage, NULL);
229 XawTextSetInsertionPoint(wMsgText, strlen(strNewMessage));
230
231 MEM_Free(strNewMessage);
232 }
233
234
235 /************************************************************************
236 * FUNCTION: UTIL_DisplayComment
237 * HISTORY:
238 * 02.07.94 ESF Created.
239 * PURPOSE:
240 * NOTES:
241 ************************************************************************/
UTIL_DisplayComment(CString strComment)242 void UTIL_DisplayComment(CString strComment)
243 {
244 Arg pArgs[1];
245 Int32 iCount;
246
247 iCount = 0;
248 XtSetArg(pArgs[iCount], XtNlabel, strComment); iCount++;
249 XtSetValues(wCommentLabel, pArgs, iCount);
250 }
251
252
253
254 /************************************************************************
255 * FUNCTION: UTIL_SetPlayerTurnIndicator
256 * HISTORY:
257 * 03.03.94 ESF Created.
258 * PURPOSE:
259 * NOTES:
260 ************************************************************************/
UTIL_SetPlayerTurnIndicator(Int32 iPlayer)261 void UTIL_SetPlayerTurnIndicator(Int32 iPlayer)
262 {
263 UNUSED(iPlayer);
264
265 D_Assert(FALSE, "Unimplemented!");
266 }
267
268
269 /************************************************************************
270 * FUNCTION: UTIL_DisplayActionCString
271 * HISTORY:
272 * 03.03.94 ESF Created.
273 * 03.29.94 ESF Added player attack mode history.
274 * 03.30.94 ESF Fixed bug, not erasing iAttackSrc.
275 * 04.02.94 ESF Added clearing of error.
276 * 05.07.94 ESF Removed clearing of error.
277 * PURPOSE:
278 * NOTES:
279 ************************************************************************/
UTIL_DisplayActionCString(Int32 iState,Int32 iPlayer)280 void UTIL_DisplayActionCString(Int32 iState, Int32 iPlayer)
281 {
282 char buf[256];
283 switch (iState)
284 {
285 case STATE_REGISTER:
286 #ifdef ENGLISH
287 snprintf(buf, sizeof(buf), "Waiting for all clients to register...");
288 #endif
289 #ifdef FRENCH
290 snprintf(buf, sizeof(buf), "Attent que tous les clients aient fini de s'enregistrer...");
291 #endif
292 break;
293
294 case STATE_FORTIFY:
295 #ifdef ENGLISH
296 snprintf(buf, sizeof(buf), "%s to place an army (%d remaining)...",
297 #endif
298 #ifdef FRENCH
299 snprintf(buf, sizeof(buf), "%s place une arm�e (reste %d)...",
300 #endif
301 RISK_GetNameOfPlayer(iPlayer),
302 RISK_GetNumArmiesOfPlayer(iPlayer));
303 XawListHighlight(wActionList, iActionState = ACTION_PLACE);
304 if (UTIL_PlayerIsLocal(iPlayer))
305 {
306 XawListHighlight(wAttackList,
307 RISK_GetDiceModeOfPlayer(iCurrentPlayer));
308 XawListHighlight(wMsgDestList,
309 RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
310 }
311 break;
312
313
314 case STATE_PLACE:
315 #ifdef ENGLISH
316 snprintf(buf, sizeof(buf), "%s placing armies (%d remaining)...",
317 #endif
318 #ifdef FRENCH
319 snprintf(buf, sizeof(buf), "%s place des arm�es (reste %d)...",
320 #endif
321 RISK_GetNameOfPlayer(iPlayer),
322 RISK_GetNumArmiesOfPlayer(iPlayer));
323 XawListHighlight(wActionList, iActionState = ACTION_PLACE);
324 if (UTIL_PlayerIsLocal(iPlayer))
325 {
326 XawListHighlight(wAttackList,
327 RISK_GetDiceModeOfPlayer(iCurrentPlayer));
328 XawListHighlight(wMsgDestList,
329 RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
330 }
331 break;
332
333 case STATE_ATTACK:
334 #ifdef ENGLISH
335 snprintf(buf, sizeof(buf), "%s attacking...",
336 #endif
337 #ifdef FRENCH
338 snprintf(buf, sizeof(buf), "%s attaque...",
339 #endif
340 RISK_GetNameOfPlayer(iPlayer));
341 iActionState = RISK_GetAttackModeOfPlayer(iCurrentPlayer);
342 XawListHighlight(wActionList,
343 RISK_GetAttackModeOfPlayer(iCurrentPlayer));
344 if (UTIL_PlayerIsLocal(iPlayer))
345 {
346 XawListHighlight(wAttackList,
347 RISK_GetDiceModeOfPlayer(iCurrentPlayer));
348 XawListHighlight(wMsgDestList,
349 RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
350 }
351 break;
352
353 case STATE_MOVE:
354 if (GAME_AttackFrom() >= 0)
355 {
356 UTIL_DarkenCountry(GAME_AttackFrom());
357 GAME_SetAttackSrc(-1);
358 }
359
360 #ifdef ENGLISH
361 snprintf(buf, sizeof(buf), "%s executing a free move...",
362 #endif
363 #ifdef FRENCH
364 snprintf(buf, sizeof(buf), "%s d�place...",
365 #endif
366 RISK_GetNameOfPlayer(iPlayer));
367 XawListHighlight(wActionList, iActionState = ACTION_MOVE);
368 if (UTIL_PlayerIsLocal(iPlayer))
369 {
370 XawListHighlight(wAttackList,
371 RISK_GetDiceModeOfPlayer(iCurrentPlayer));
372 XawListHighlight(wMsgDestList,
373 RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
374 }
375 break;
376
377 default:
378 D_Assert(FALSE, "Shouldn't be here!");
379 }
380
381 UTIL_DisplayComment(buf);
382 }
383
384
385 /************************************************************************
386 * FUNCTION: UTIL_DisplayError
387 * HISTORY:
388 * 03.04.94 ESF Created.
389 * PURPOSE:
390 * NOTES:
391 ************************************************************************/
392 void UTIL_DisplayError(CString strError)
393 {
394 Arg pArgs[1];
395 Int32 iCount;
396
397 iCount=0;
398 XtSetArg(pArgs[iCount], XtNlabel, strError); iCount++;
399 XtSetValues(wErrorLabel, pArgs, iCount);
400 }
401
402
403 /************************************************************************
404 * FUNCTION: UTIL_ServerEnterState
405 * HISTORY:
406 * 03.05.94 ESF Created.
407 * 11.16.94 ESF Added to send messages to other clients.
408 * PURPOSE:
409 * NOTES:
410 ************************************************************************/
411 void UTIL_ServerEnterState(Int32 iNewState)
412 {
413 MsgEnterState m;
414 MsgNetMessage msgNetMessage;
415 char buf[256];
416
417 switch (iNewState)
418 {
419 case STATE_REGISTER:
420 break;
421 case STATE_PLACE:
422 #ifdef ENGLISH
423 snprintf(buf, sizeof(buf), "%s is placing %s.",
424 RISK_GetNameOfPlayer(iCurrentPlayer),
425 (RISK_GetNumArmiesOfPlayer(iCurrentPlayer) > 1 ?
426 "armies" :
427 "an army"));
428 #endif
429 #ifdef FRENCH
430 snprintf(buf, sizeof(buf), "%s place %s arm�e%s.",
431 RISK_GetNameOfPlayer(iCurrentPlayer),
432 (RISK_GetNumArmiesOfPlayer(iCurrentPlayer)>1)?"des":"une",
433 (RISK_GetNumArmiesOfPlayer(iCurrentPlayer)>1)?"s":"");
434 #endif
435 break;
436 case STATE_FORTIFY:
437 #ifdef ENGLISH
438 snprintf(buf, sizeof(buf), "%s is fortifying territories.",
439 #endif
440 #ifdef FRENCH
441 snprintf(buf, sizeof(buf), "%s fortifie des territoires.",
442 #endif
443 RISK_GetNameOfPlayer(iCurrentPlayer));
444 break;
445 case STATE_ATTACK:
446 #ifdef ENGLISH
447 snprintf(buf, sizeof(buf), "%s is attacking.",
448 #endif
449 #ifdef FRENCH
450 snprintf(buf, sizeof(buf), "%s attaque.",
451 #endif
452 RISK_GetNameOfPlayer(iCurrentPlayer));
453 break;
454 case STATE_MOVE:
455 #ifdef ENGLISH
456 snprintf(buf, sizeof(buf), "%s is moving armie(s).",
457 #endif
458 #ifdef FRENCH
459 snprintf(buf, sizeof(buf), "%s d�place des arm�e(s).",
460 #endif
461 RISK_GetNameOfPlayer(iCurrentPlayer));
462 break;
463 default:
464 D_Assert(0, "Bogus state!");
465 }
466
467 msgNetMessage.strMessage = buf;
468 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
469 MSG_NETMESSAGE, &msgNetMessage);
470
471 /* Change states and let the server know about it */
472 m.iState = iNewState;
473 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
474 MSG_ENTERSTATE, &m);
475 }
476
477
478 /* Utility for the next function */
479 #define UTIL_CloseArmyDialog() \
480 UTIL_DisplayError(""); \
481 XtSetKeyboardFocus(wToplevel, wToplevel); \
482 XtRemoveGrab(wArmiesShell); \
483 XtUnrealizeWidget(wArmiesShell);
484
485 /************************************************************************
486 * FUNCTION: UTIL_GetArmyNumber
487 * HISTORY:
488 * 03.16.94 ESF Created.
489 * 03.28.94 ESF Fixed minor printing bug.
490 * 04.01.94 ESF Fixed bug in centering shell, had XtNy with an x.
491 * PURPOSE:
492 * NOTES:
493 ************************************************************************/
494 Int32 UTIL_GetArmyNumber(Int32 iMinArmies, Int32 iMaxArmies, Flag fLetCancel)
495 {
496 Int32 x, y, iNumArmies;
497 XEvent xEvent;
498 CString strBuffer;
499 char buf[256];
500
501 /* Make sure that the range is valid */
502 D_Assert(iMinArmies<=iMaxArmies, "Invalid range for army values!");
503
504 UTIL_CenterShell(wArmiesShell, wToplevel, &x, &y);
505 XtVaSetValues(wArmiesShell,
506 XtNallowShellResize, False,
507 XtNx, x,
508 XtNy, y,
509 XtNborderWidth, 1,
510 XtNtitle, "Frisk",
511 NULL);
512
513 /* Set the default number of armies as the maximum */
514 snprintf(buf, sizeof(buf), "%d", iMaxArmies);
515 XtVaSetValues(wArmiesText, XtNstring, buf, NULL);
516
517 /* Don't display the cancel button if there is no chance of this */
518 XtVaSetValues(wCancelArmiesButton, XtNsensitive,
519 fLetCancel ? True : False, NULL);
520
521 /* Pop it up */
522 XtMapWidget(wArmiesShell);
523 XtAddGrab(wArmiesShell, True, True);
524 XtSetKeyboardFocus(wToplevel, wArmiesShell);
525 XtSetKeyboardFocus(wArmiesShell, wArmiesText);
526
527 keep_going:
528 iQueryResult = QUERY_INPROGRESS;
529 while (iQueryResult == QUERY_INPROGRESS)
530 {
531 /* pass events */
532 XNextEvent(hDisplay, &xEvent);
533 XtDispatchEvent(&xEvent);
534 }
535
536 /* User must have selected one of the buttons */
537 if (!fLetCancel && iQueryResult == QUERY_NO)
538 goto keep_going;
539 else if (iQueryResult == QUERY_NO)
540 {
541 UTIL_CloseArmyDialog();
542 return(0);
543 }
544
545 /* Get number of armies */
546 XtVaGetValues(wArmiesText, XtNstring, &strBuffer, NULL);
547 iNumArmies = atoi(strBuffer);
548
549 if (iNumArmies<iMinArmies || iNumArmies>iMaxArmies)
550 {
551 if (iNumArmies < iMinArmies)
552 #ifdef ENGLISH
553 snprintf(buf, sizeof(buf), "You must move at least %d armie%s.",
554 #endif
555 #ifdef FRENCH
556 snprintf(buf, sizeof(buf), "Il faut d�placer au moins %d arm�e%s.",
557 #endif
558 iMinArmies, (iMinArmies>1)?"s":"");
559 else
560 #ifdef ENGLISH
561 snprintf(buf, sizeof(buf), "You can't move more than %d armie%s.",
562 #endif
563 #ifdef FRENCH
564 snprintf(buf, sizeof(buf), "Impossible de d�placer plus de %d arm�e%s.",
565 #endif
566 iMaxArmies, (iMaxArmies>1)?"s":"");
567 UTIL_DisplayError(buf);
568
569 goto keep_going;
570 }
571
572 UTIL_CloseArmyDialog();
573 return(iNumArmies);
574 }
575
576
577 void UTIL_QueryYes(Widget w, XtPointer pData, XtPointer pCalldata) {
578 UNUSED(w);
579 UNUSED(pData);
580 UNUSED(pCalldata);
581
582 iQueryResult = QUERY_YES;
583 }
584
585 void UTIL_QueryNo(Widget w, XtPointer pData, XtPointer pCalldata) {
586 UNUSED(w);
587 UNUSED(pData);
588 UNUSED(pCalldata);
589
590 iQueryResult = QUERY_NO;
591 }
592
593 void UTIL_QueryCancel(Widget w, XtPointer pData, XtPointer pCalldata) {
594 UNUSED(w);
595 UNUSED(pData);
596 UNUSED(pCalldata);
597
598 iQueryResult = QUERY_CANCEL;
599 }
600
601
602 /************************************************************************
603 * FUNCTION: UTIL_CenterShell
604 * HISTORY:
605 * 03.16.94 ESF Created.
606 * PURPOSE:
607 * NOTES:
608 ************************************************************************/
609 void UTIL_CenterShell(Widget wCenter, Widget wBase, Int32 *x, Int32 *y)
610 {
611 Dimension dimWidth, dimHeight, dimTopWidth, dimTopHeight;
612 Window hDummyWindow;
613
614 /* Make sure the two widgets are realized */
615 if (!XtIsRealized(wCenter))
616 XtRealizeWidget(wCenter);
617 if (!XtIsRealized(wBase))
618 XtRealizeWidget(wBase);
619
620 /* Get the dimensions of the shells and center the popup. */
621 XtVaGetValues(wCenter,
622 XtNwidth, &dimWidth,
623 XtNheight, &dimHeight, NULL);
624 XtVaGetValues(wBase,
625 XtNwidth, &dimTopWidth,
626 XtNheight, &dimTopHeight, NULL);
627
628 XTranslateCoordinates(hDisplay, XtWindow(wBase),
629 XDefaultRootWindow(hDisplay),
630 (Int32)(dimTopWidth-dimWidth)/2,
631 (Int32)(dimTopHeight-dimHeight)/2,
632 x, y, &hDummyWindow);
633 }
634
635
636 /************************************************************************
637 * FUNCTION: UTIL_LightCountry
638 * HISTORY:
639 * 03.28.94 ESF Created.
640 * PURPOSE:
641 * NOTES:
642 ************************************************************************/
643 void UTIL_LightCountry(Int32 iCountry)
644 {
645 UTIL_PrintArmies(iCountry,
646 RISK_GetNumArmiesOfCountry(iCountry),
647 WhitePixel(hDisplay, 0));
648 }
649
650
651 /************************************************************************
652 * FUNCTION: UTIL_DarkenCountry
653 * HISTORY:
654 * 03.28.94 ESF Created.
655 * PURPOSE:
656 * NOTES:
657 ************************************************************************/
658 void UTIL_DarkenCountry(Int32 iCountry)
659 {
660 UTIL_PrintArmies(iCountry,
661 RISK_GetNumArmiesOfCountry(iCountry),
662 BlackPixel(hDisplay, 0));
663 }
664
665
666 /************************************************************************
667 * FUNCTION: UTIL_PopupDialog
668 * HISTORY:
669 * 04.02.94 ESF Created.
670 * 05.12.94 ESF Revamped.
671 * 10.30.94 ESF Added sanity check for hDisplay.
672 * 01.25.95 ESF Added code to center buttons.
673 * 01.25.95 ESF Changed the default title to "Frisk".
674 * 04.30.95 ESF Fixed a bug with resizing of the label.
675 * PURPOSE:
676 * NOTES:
677 ************************************************************************/
678 Int32 UTIL_PopupDialog(CString strTitle, CString strLabel, Int32 iNumOptions,
679 CString strOption1, CString strOption2,
680 CString strOption3)
681 {
682 CString pstrOptions[3];
683 Int32 i, x, y, iWidth;
684 XEvent xEvent;
685 static XFontStruct *pLabelFont=NULL;
686
687 /* If this got called before the display is set up */
688 if (!hDisplay)
689 {
690 printf("%s: %s\n", strTitle, strLabel);
691 return 0;
692 }
693
694 /* Initialize these for later */
695 pstrOptions[0] = strOption1;
696 pstrOptions[1] = strOption2;
697 pstrOptions[2] = strOption3;
698
699 /* Get the font if necessary */
700 if (pLabelFont==NULL)
701 if ((pLabelFont=XLoadQueryFont(hDisplay, "*helvetica-b*-o-*14*"))==NULL)
702 {
703 printf("Can't find the font \"*helvetica-b*-o-*14*\"!!\n");
704 UTIL_ExitProgram(-1);
705 }
706
707 /* Sanity check */
708 if (iNumOptions>3 || iNumOptions<1)
709 {
710 (void)UTIL_PopupDialog("Warning", "UTIL: Wacked # of options",
711 1, "Ok", NULL, NULL);
712 return(-1);
713 }
714
715 /* Make sure the dialog is realized */
716 if(!XtIsRealized(wDialogShell))
717 XtRealizeWidget(wDialogShell);
718
719 /* Set the title */
720 XtVaSetValues(wDialogShell,
721 XtNtitle, strTitle == NULL ? "Frisk" : strTitle,
722 XtNallowShellResize, True,
723 NULL);
724
725 /* Make sure that the label is big enough to hold the string, and
726 * if the length of the button strings are larger, make it bigger.
727 * Let there be a minimum width of 200... There are a few magic
728 * numbers here, excuse them :)
729 */
730
731 iWidth = MAX(200,
732 MAX(XTextWidth(pLabelFont, strLabel, strlen(strLabel))+50,
733 XTextWidth(pLabelFont, strOption1,
734 strOption1 ? strlen(strOption1) : 0)+
735 XTextWidth(pLabelFont, strOption2,
736 strOption2 ? strlen(strOption2) : 0)+
737 XTextWidth(pLabelFont, strOption3,
738 strOption3 ? strlen(strOption3) : 0)+
739 140));
740
741 /* Set the button resources */
742 for (i=0; i!=3; i++)
743 if (iNumOptions>i)
744 {
745 XtMapWidget(wDialogButton[i]);
746 XtVaSetValues(wDialogButton[i],
747 XtNlabel, pstrOptions[i]==NULL ? "Null": pstrOptions[i],
748 XtNfromHoriz, i>0 ? wDialogButton[i-1] : NULL,
749 NULL);
750 }
751 else
752 {
753 XtUnmapWidget(wDialogButton[i]);
754 XtVaSetValues(wDialogButton[i],
755 XtNfromHoriz, 0,
756 NULL);
757 }
758
759 /* We need to unrealize it so that the changes will take place (?) */
760 XtUnrealizeWidget(wDialogShell);
761
762 /* Center and resize the shell, and then actually pop the dialog up */
763 XtVaSetValues(wDialogShell, XtNwidth, iWidth, NULL);
764 XtVaSetValues(wDialogLabel,
765 XtNlabel, strLabel,
766 XtNwidth, iWidth,
767 NULL);
768
769 UTIL_CenterShell(wDialogShell, wToplevel, &x, &y);
770 XtVaSetValues(wDialogShell,
771 XtNx, x,
772 XtNy, y,
773 XtNborderWidth, 1,
774 NULL);
775
776 XtPopup(wDialogShell, XtGrabExclusive);
777
778 iQueryResult = QUERY_INPROGRESS;
779 while (iQueryResult == QUERY_INPROGRESS)
780 {
781 /* pass events */
782 XNextEvent(hDisplay, &xEvent);
783 XtDispatchEvent(&xEvent);
784 }
785
786 /* User must have selected one of the buttons */
787 XtPopdown(wDialogShell);
788 return(iQueryResult);
789 }
790
791
792 /************************************************************************
793 * FUNCTION: UTIL_PlayerIsLocal
794 * HISTORY:
795 * 04.11.94 ESF Created.
796 * PURPOSE:
797 * NOTES:
798 ************************************************************************/
799 Flag UTIL_PlayerIsLocal(Int32 iPlayer)
800 {
801 return (RISK_GetClientOfPlayer(iPlayer) == CLNT_GetThisClientID() ? TRUE : FALSE);
802 }
803
804
805 /************************************************************************
806 * FUNCTION: UTIL_NumPlayersAtClient
807 * HISTORY:
808 * 05.04.94 ESF Created.
809 * 05.13.95 ESF Fixed bug, wasn't checking if player was allocated.
810 * PURPOSE:
811 * NOTES:
812 ************************************************************************/
813 Int32 UTIL_NumPlayersAtClient(Int32 iClient)
814 {
815 Int32 i, iCount;
816
817 /* It's an error to call this with an out of range number */
818 D_Assert(iClient>=0 && iClient<MAX_CLIENTS, "Client is bogus!");
819
820 for (i=iCount=0; i!=MAX_PLAYERS; i++)
821 if (RISK_GetAllocationStateOfPlayer(i) == ALLOC_COMPLETE &&
822 RISK_GetClientOfPlayer(i) == iClient)
823 iCount++;
824
825 return iCount;
826 }
827
828
829 /************************************************************************
830 * FUNCTION:
831 * HISTORY:
832 * 05.06.94 ESF Created.
833 * PURPOSE:
834 * NOTES:
835 ************************************************************************/
836 void UTIL_RefreshMsgDest(Int32 iNumCStrings)
837 {
838 /* Update the listbox */
839 XtVaSetValues(wMsgDestList,
840 XtNlist, pstrMsgDstCString,
841 XtNnumberStrings, iNumCStrings,
842 NULL);
843 }
844
845
846 /************************************************************************
847 * FUNCTION: UTIL_ExitProgram
848 * HISTORY:
849 * 06.16.94 ESF Created.
850 * 08.03.94 ESF Fixed to send server message before exiting.
851 * 08.28.94 ESF Changed to close sockets.
852 * 10.02.94 ESF Changed to not close sockets.
853 * 25.08.95 JC Changed to not send server message if closed.
854 * PURPOSE:
855 * NOTES:
856 ************************************************************************/
857 void UTIL_ExitProgram(Int32 iExitValue)
858 {
859 if (CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()) != -1)
860 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
861 MSG_DEREGISTERCLIENT, NULL);
862 MEM_TheEnd();
863 exit(iExitValue);
864 }
865
866
867 /************************************************************************
868 * FUNCTION: UTIL_SetCursorShape
869 * HISTORY:
870 * 10.01.94 ESF Created.
871 * PURPOSE:
872 * NOTES:
873 ************************************************************************/
874 void UTIL_SetCursorShape(Int32 iShape)
875 {
876 /* If the cursors haven't been created then create them */
877 if (cursorPlay==0)
878 cursorPlay = XCreateFontCursor(hDisplay, XC_crosshair);
879 if (cursorWait==0)
880 cursorWait = XCreateFontCursor(hDisplay, XC_watch);
881
882 /* Switch to the desired cursor */
883 switch (iShape)
884 {
885 case CURSOR_WAIT:
886 XDefineCursor(hDisplay, hWindow, cursorWait);
887 break;
888
889 case CURSOR_PLAY:
890 XDefineCursor(hDisplay, hWindow, cursorPlay);
891 break;
892
893 default:
894 D_Assert(FALSE, "Bogus cursor being passed in.");
895 }
896 }
897
898
899 /************************************************************************
900 * FUNCTION: UTIL_PlaceNotification
901 * HISTORY:
902 * 10.30.94 ESF Created.
903 * 11.06.94 ESF Completed.
904 * 01.15.95 ESF Fixed memory leak.
905 * PURPOSE:
906 * NOTES:
907 ************************************************************************/
908 void UTIL_PlaceNotification(XtPointer msgMess, XtIntervalId *pId)
909 {
910 MsgPlaceNotify *msg = (MsgPlaceNotify *)msgMess;
911 UTIL_SetCountryBrightness(msg->iCountry, pId == NULL ? TRUE : FALSE);
912
913 /* If it's the second time we are being called then there will
914 * be a pId and we can free the memory taken by it.
915 */
916
917 if (pId != NULL)
918 MEM_Free(msg);
919 }
920
921
922 /************************************************************************
923 * FUNCTION: UTIL_AttackNotification
924 * HISTORY:
925 * 10.30.94 ESF Created.
926 * 11.16.94 ESF Finished.
927 * 01.15.95 ESF Fixed memory leak.
928 * PURPOSE:
929 * NOTES:
930 ************************************************************************/
931 void UTIL_AttackNotification(XtPointer msgMess, XtIntervalId *pId)
932 {
933 MsgAttackNotify *msg = (MsgAttackNotify *)msgMess;
934 UTIL_SetCountryBrightness(msg->iSrcCountry, pId == NULL ? TRUE : FALSE);
935 UTIL_DrawNiceLine(msg->iSrcCountry, msg->iDstCountry);
936
937 /* If it's the second time we are being called then there will
938 * be a pId and we can free the memory taken by it.
939 */
940
941 if (pId != NULL)
942 MEM_Free(msg);
943 }
944
945
946 /************************************************************************
947 * FUNCTION: UTIL_MoveNotification
948 * HISTORY:
949 * 10.30.94 ESF Created.
950 * 11.16.94 ESF Finished.
951 * 01.15.95 ESF Fixed memory leak.
952 * PURPOSE:
953 * NOTES:
954 ************************************************************************/
955 void UTIL_MoveNotification(XtPointer msgMess, XtIntervalId *pId)
956 {
957 MsgMoveNotify *msg = (MsgMoveNotify *)msgMess;
958 UTIL_SetCountryBrightness(msg->iSrcCountry, pId == NULL ? TRUE : FALSE);
959 UTIL_DrawNiceLine(msg->iSrcCountry, msg->iDstCountry);
960
961 /* If it's the second time we are being called then there will
962 * be a pId and we can free the memory taken by it.
963 */
964
965 if (pId != NULL)
966 MEM_Free(msg);
967 }
968
969 #define BORDER 2
970
971 /************************************************************************
972 * FUNCTION: UTIL_DrawNiceLine
973 * HISTORY:
974 * 11.06.94 ESF Created.
975 * 11.16.94 ESF Finished.
976 * PURPOSE:
977 * NOTES:
978 ************************************************************************/
979 void UTIL_DrawNiceLine(Int32 iSrcCountry, Int32 iDstCountry)
980 {
981 Int32 x1, y1, x2, y2, iHalfHeight, iHalfWidth;
982 Int32 iWidth, iHeight, ixDist, iyDist;
983
984 /* Get the two endpoints of the line */
985 x1 = RISK_GetTextXOfCountry(iSrcCountry)-BORDER;
986 y1 = RISK_GetTextYOfCountry(iSrcCountry)+BORDER;
987 x2 = RISK_GetTextXOfCountry(iDstCountry)-BORDER;
988 y2 = RISK_GetTextYOfCountry(iDstCountry)+BORDER;
989
990 /* Get the width and height of the box containing the text. */
991 iHeight = (pFont->max_bounds.ascent + pFont->max_bounds.descent)+2*BORDER;
992 iWidth = XTextWidth(pFont, MAX_STRING, MAX_DIGITS)+2*BORDER;
993
994 /* Depending on where the source and destination of the line is,
995 * draw it coming and going from the middle of one of the sides,
996 * of from the corners. Thus, the line can come and go from eight
997 * different points, which should be enough to make it look good.
998 */
999
1000 ixDist = (x1-x2<0) ? (x2-x1) : (x1-x2);
1001 iyDist = (y1-y2<0) ? (y2-y1) : (y1-y2);
1002
1003 /* The compiler should do this (CSE), but I'm distrustful by nature */
1004 iHalfHeight = iHeight / 2;
1005 iHalfWidth = iWidth / 2;
1006
1007 if (ixDist > iyDist)
1008 {
1009 /* Use the middles of the sides */
1010 if (x2 > x1)
1011 x1+=iWidth, y1-=iHalfHeight, y2-=iHalfHeight;
1012 else
1013 x2+=iWidth, y1-=iHalfHeight, y2-=iHalfHeight;
1014 }
1015 else
1016 {
1017 /* Use the middles of the top and bottom */
1018 if (y2 > y1)
1019 x1+=iHalfWidth, x2+=iHalfWidth, y2-=iHeight;
1020 else
1021 x1+=iHalfWidth, x2+=iHalfWidth, y1-=iHeight;
1022 }
1023
1024 if (!COLOR_IsTrueColor())
1025 XDrawLine(hDisplay, hWindow, hGC_XOR, x1, y1, x2, y2);
1026 XFlush(hDisplay);
1027 }
1028
1029
1030 /************************************************************************
1031 * FUNCTION: UTIL_SetCountryBrightness
1032 * HISTORY:
1033 * 11.16.94 ESF Created.
1034 * PURPOSE:
1035 * NOTES:
1036 ************************************************************************/
1037 void UTIL_SetCountryBrightness(Int32 iCountry, Boolean fLight)
1038 {
1039 Int32 iLightRefCount;
1040
1041 iLightRefCount = CLNT_GetLightCountOfCountry(iCountry);
1042
1043 /* Light up or darken country, using reference counting. */
1044 if (fLight)
1045 {
1046 if (iLightRefCount == 0)
1047 UTIL_LightCountry(iCountry);
1048 CLNT_SetLightCountOfCountry(iCountry, iLightRefCount + 1);
1049 }
1050 else
1051 {
1052 if (iLightRefCount == 1)
1053 UTIL_DarkenCountry(iCountry);
1054 CLNT_SetLightCountOfCountry(iCountry, iLightRefCount - 1);
1055 }
1056 }
1057
1058
1059 /************************************************************************
1060 * FUNCTION: UTIL_OpenFile
1061 * HISTORY:
1062 * 12.31.94 ESF Created.
1063 * PURPOSE:
1064 * NOTES:
1065 ************************************************************************/
1066 FILE *UTIL_OpenFile(CString strName, CString strOptions)
1067 {
1068 FILE *hFile;
1069 CString strNewName = NULL;
1070 Int32 i;
1071
1072 /* First just try to open the complete file that was passed */
1073 if ((hFile = fopen(strName, strOptions)) != NULL)
1074 return hFile;
1075
1076 /* Construct an alternate name that is the same file
1077 * but in the current directory. Try to open this.
1078 */
1079
1080 /* Alloc some memory, with room for '\0' and './' */
1081 strNewName = (CString)MEM_Alloc(strlen(strName)+1+2);
1082
1083 /* Find the last occurance of '/' in the filename */
1084 for (i=strlen(strName)-1; i>=0 && strName[i]!='/'; i--)
1085 ; /* TwiddleThumbs() */
1086
1087 /* Create the start of the new pathname */
1088 strcpy(strNewName, "./");
1089
1090 /* Move the pointer along to the last '/', but don't run off the end */
1091 strName = strName + MIN(i+1, (Int32)strlen(strName));
1092 strcat(strNewName, strName);
1093
1094 /* Try to open this file */
1095 hFile = fopen(strName, strOptions);
1096
1097 /* Free the memory */
1098 MEM_Free(strNewName);
1099
1100 /* Return the handle */
1101 return hFile;
1102 }
1103