home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / mac / macwins.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  63.3 KB  |  2,354 lines  |  [TEXT/R*ch]

  1. /* Handling of assorted minor windows for the Mac interface to Xconq.
  2.    Copyright (C) 1992, 1993, 1994, 1995, 1996 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. extern int construction_possible PARAMS ((int u2));
  11. extern int can_build_or_help PARAMS ((Unit *unit));
  12. extern char *side_score_desc PARAMS ((char *buf, Side *side, Scorekeeper *sk));
  13. #include "macconq.h"
  14. extern void set_construction_run_length(int len);
  15.  
  16. #define DEFAULT_RUN (99)
  17.  
  18. static void adjust_construction_items(void);
  19.  
  20. static pascal void history_scroll_fn(ControlHandle control, short code);
  21. static pascal void notice_vscroll_fn(ControlHandle control, short code);
  22. static pascal void scores_vscroll_fn(ControlHandle control, short code);
  23.  
  24. /* Globals for the game window. */
  25.  
  26. WindowPtr gamewin = nil;
  27.  
  28. int gamewinw = 200;
  29. int gamedatehgt = 18;
  30. int gameclockoffset;
  31. int gameclockhgt = 15;
  32. int gamenoteoffset;
  33. int gamenotehgt = 15;
  34. int gametophgt;
  35.  
  36. int gamesidehgt = 24;
  37. int gamesideclockhgt = 15;
  38. int gamesideclockoffset;
  39. int gamesidescorehgt = 15;
  40. int gamesidescoreoffset;
  41.  
  42. int gamenumsides;
  43.  
  44. Handle aisicnhandle = nil;
  45. Handle facesicnhandle[3];
  46.  
  47. char *game_progress_str = "";
  48.  
  49. time_t lastnow;
  50.  
  51. /* Globals for the construction window. */
  52.  
  53. WindowPtr constructionwin = nil;
  54.  
  55. static ListHandle construction_unit_list = nil;
  56. static ListHandle construction_type_list = nil;
  57.  
  58. static int constructmargin = 5;
  59. static int constructtop = 32;
  60.  
  61. /* This is the vector of units that can do construction or research. */
  62.  
  63. static UnitVector *constructor_units = NULL;
  64.  
  65. static int numposstypes;
  66. static int *possibletypes = NULL;
  67.  
  68. int currunlength;
  69.  
  70. int editedrunlength = -1;
  71.  
  72. /* (this is referenced from the end-turn command, so can't make static) */
  73. ControlHandle constructbutton;
  74. static ControlHandle researchbutton;
  75.  
  76. TEHandle run_length_text = nil;
  77.  
  78. static Rect runlengthrect;
  79. static Rect unitlistrect;
  80. static Rect typelistrect;
  81.  
  82. /* Globals for unit closeup windows. */
  83.  
  84. int lastunitcloseuph = -1, lastunitcloseupv = -1;
  85.  
  86. static int m_per_row = 2;
  87.  
  88. static char *nummrows;
  89.  
  90. int closeupspacing = 16;
  91.  
  92. int closeupwinwid = 200;
  93.  
  94. /* Globals for the history window. */
  95.  
  96. WindowPtr historywin = nil;
  97.  
  98. HistEvent **histcontents = NULL;
  99.  
  100. int numhistcontents = 0;
  101.  
  102. ControlHandle histvscrollbar;
  103.  
  104. int history_line_spacing = 15; /* (should derive from font) */
  105.  
  106. int history_top_line_height = 25;
  107.  
  108. HistEvent *firstvisevt = NULL;
  109.  
  110. HistEvent *secondvisevt = NULL;
  111.  
  112. int numvishistlines;
  113.  
  114. int total_history_lines = 0;
  115.  
  116. ControlActionUPP history_scroll_proc;
  117.  
  118. /* Globals for the notice window. */
  119.  
  120. DialogPtr noticewin = nil;
  121.  
  122. static TEHandle notice_text = nil;
  123.  
  124. static ControlHandle notice_v_scrollbar;
  125.  
  126. ControlActionUPP notice_vscroll_proc;
  127.  
  128. /* Globals for the scores window. */
  129.  
  130. DialogPtr scoreswin = nil;
  131.  
  132. static TEHandle scores_text = nil;
  133.  
  134. static ControlHandle scores_v_scrollbar;
  135.  
  136. ControlActionUPP scores_vscroll_proc;
  137.  
  138. /* The game progress window. */
  139.  
  140. /* Create the game progress window. */
  141.  
  142. void
  143. create_game_window()
  144. {
  145.     int screenwidth;
  146.     extern int numscores;
  147.  
  148.     /* Create the window, color if possible, since emblems may be in color. */
  149.     if (hasColorQD) {    
  150.         gamewin = GetNewCWindow(wGame, NULL, (WindowPtr) -1L);
  151.     } else {
  152.         gamewin = GetNewWindow(wGame, NULL, (WindowPtr) -1L);
  153.     }
  154.     gametophgt = gamedatehgt;
  155.     if (g_rt_per_turn() > 0 || g_rt_for_game() > 0) {
  156.         gameclockoffset = gametophgt;
  157.         gametophgt += gameclockhgt;
  158.     }
  159.     gamenoteoffset = gametophgt;
  160.     gametophgt += gamenotehgt;
  161.     /* Add some space if sides have a per-turn and/or per-game clock. */
  162.     if (g_rt_per_side() > 0) {
  163.         gamesideclockoffset = gamesidehgt;
  164.         gamesidehgt += gamesideclockhgt;
  165.     }
  166.     /* Add additional space for each two scorekeepers. */
  167.     if (keeping_score()) {
  168.         gamesidescoreoffset = gamesidehgt;
  169.         gamesidehgt += gamesidescorehgt * ((numscorekeepers + 1) / 2);
  170.     }
  171.     /* This is not growable, so we have to ensure it's big enough to start with. */
  172.     /* (Record the current numsides so we can grow the window later.) */
  173.     gamenumsides = numsides;
  174.     SizeWindow(gamewin, gamewinw, gametophgt + gamenumsides * gamesidehgt, 1);
  175.     if (first_windows) {
  176.         get_main_screen_size(&screenwidth, NULL);
  177.         MoveWindow(gamewin, screenwidth - gamewinw - 3, 40, FALSE);
  178.     }
  179.     /* Get handles to useful sicns. */
  180.     aisicnhandle = GetNamedResource('SICN', "\pmplayer");
  181.     facesicnhandle[0] = GetNamedResource('SICN', "\phostile");
  182.     facesicnhandle[1] = GetNamedResource('SICN', "\pneutral");
  183.     facesicnhandle[2] = GetNamedResource('SICN', "\pfriendly");
  184. }
  185.  
  186. char *last_status;
  187. char *last_status_left;
  188. char *last_status_resv;
  189. char *last_status_fini;
  190. char **last_status_score1;
  191.  
  192. void
  193. draw_game()
  194. {
  195.     Side *side2;
  196.     GrafPtr oldport;
  197.  
  198.     if (gamewin == nil)
  199.       return;
  200.     /* The window might need to get bigger. */
  201.     if (numsides != gamenumsides) {
  202.         last_status = NULL;
  203.         last_status_left = NULL;
  204.         last_status_resv = NULL;
  205.         last_status_fini = NULL;
  206.         last_status_score1 = NULL;
  207.         gamenumsides = numsides;
  208.         SizeWindow(gamewin, gamewinw, gametophgt + gamenumsides * gamesidehgt, 1);
  209.     }
  210.     if (last_status == NULL)
  211.       last_status = xmalloc(numsides + 1);
  212.     if (last_status_left == NULL)
  213.       last_status_left = xmalloc(numsides + 1);
  214.     if (last_status_resv == NULL)
  215.       last_status_resv = xmalloc(numsides + 1);
  216.     if (last_status_fini == NULL)
  217.       last_status_fini = xmalloc(numsides + 1);
  218.     if (last_status_score1 == NULL) {
  219.         int i;
  220.         last_status_score1 = (char **) xmalloc((numsides + 1) * sizeof(char *));
  221.         for (i = 0; i <= numsides; ++i)
  222.           last_status_score1[i] = xmalloc(100);
  223.     }
  224.     GetPort(&oldport);
  225.     SetPort(gamewin);
  226.     draw_game_date();
  227.     draw_game_progress();
  228.     /* Draw a solid separating line between date info and side list. */
  229.     MoveTo(0, gametophgt);
  230.     Line(gamewinw, 0);
  231.     for_all_sides(side2)
  232.       draw_game_side(side2);
  233.     SetPort(oldport);
  234. }
  235.  
  236. /* Display the current time and date and any realtime countdowns. */
  237.  
  238. void
  239. draw_game_date()
  240. {
  241.     Rect tmprect;
  242.  
  243.     SetRect(&tmprect, 0, 0, gamewinw, gamedatehgt - 1);
  244.     EraseRect(&tmprect);
  245.     MoveTo(tmprect.left + 10, tmprect.top + 12);
  246.     TextFace(bold);
  247.     DrawString((unsigned char *) curdatestr);
  248.     TextFace(0);
  249.     /* (should draw season name here somewhere?) */
  250.     draw_game_clocks();
  251.     if (endofgame) {
  252.         gray_out_rect(&tmprect);
  253.     }
  254. #ifdef DEBUGGING
  255.     /* Indicate the state of all the debug flags. */
  256.     if (Debug || DebugM || DebugG) {
  257.         sprintf(spbuf, "%c%c%c%c",
  258.                 (Debug ? 'D' : ' '), (DebugM ? 'M' : ' '), (DebugG ? 'G' : ' '),
  259.                 (Profile ? 'P' : ' '));
  260.         MoveTo(tmprect.right - 30, tmprect.top + 12);
  261.         DrawText(spbuf, 0, strlen(spbuf));
  262.     }
  263. #endif /* DEBUGGING */
  264. }
  265.  
  266. void
  267. draw_game_clocks()
  268. {
  269.     int elapsed, s2, sy;
  270.     time_t now;
  271.     Rect tmprect;
  272.     Side *side2;
  273.  
  274.     /* Draw per-turn and per-game time limits that for the game as a whole. */
  275.     SetRect(&tmprect, 0, gamedatehgt, gamewinw / 2, gamedatehgt + gameclockhgt - 1);
  276.     if (g_rt_per_turn() > 0) {
  277.         time(&now);
  278.         elapsed = (int) difftime(now, turn_play_start_in_real_time);
  279.         time_desc(spbuf, g_rt_per_turn() - elapsed, g_rt_per_turn());
  280.         EraseRect(&tmprect);
  281.         MoveTo(tmprect.left + 20, tmprect.top + 10);
  282.         DrawText(spbuf, 0, strlen(spbuf));
  283.         lastnow = now;
  284.     }
  285.     OffsetRect(&tmprect, 100, 0);
  286.     if (g_rt_for_game() > 0) {
  287.         time(&now);
  288.         elapsed = (int) difftime(now, game_start_in_real_time);
  289.         time_desc(spbuf, g_rt_for_game() - elapsed, g_rt_for_game());
  290.         EraseRect(&tmprect);
  291.         MoveTo(tmprect.left + 10, tmprect.top + 10);
  292.         DrawText(spbuf, 0, strlen(spbuf));
  293.         lastnow = now;
  294.     }
  295.     /* Draw per-side clocks if any limits defined. */
  296.     if (g_rt_per_side() > 0) {
  297.         for_all_sides(side2) {
  298.             if (side2->ingame) {
  299.                 s2 = side_number(side2);
  300.                 sy = gametophgt + (s2 - 1) * gamesidehgt + gamesideclockoffset;
  301.                 elapsed = 0;
  302.                 if (!side2->finishedturn)
  303.                   elapsed = (int) difftime(now, turn_play_start_in_real_time); /* should be side start */
  304.                 time_desc(spbuf, g_rt_per_side() - side2->totaltimeused - elapsed, g_rt_per_side());
  305.                 SetRect(&tmprect, 0, sy, gamewinw, sy + gamesideclockhgt - 1);
  306.                 EraseRect(&tmprect);
  307.                 MoveTo(tmprect.left + 20, tmprect.top + 10);
  308.                 DrawText(spbuf, 0, strlen(spbuf));
  309.                 /* (should draw per-turn side usage) */
  310.             }
  311.         }
  312.     }
  313. }
  314.  
  315. void
  316. draw_game_progress()
  317. {
  318.     Rect tmprect;
  319.  
  320.     SetRect(&tmprect, 0, gamenoteoffset, gamewinw, gamenoteoffset + gamenotehgt - 1);
  321.     EraseRect(&tmprect);
  322.     MoveTo(1, gamenoteoffset + 12);
  323.     DrawText(game_progress_str, 0, strlen(game_progress_str));
  324. }
  325.  
  326. /* Draw info about a given side. */
  327.  
  328. void
  329. draw_game_side(Side *side2)
  330. {
  331.     int s2 = side_number(side2);
  332.     int sx = 20, sy = gametophgt + (s2 - 1) * gamesidehgt;
  333.  
  334.     draw_side_emblem(gamewin, 2, sy + 4, 16, 16, s2, shadow_emblem);
  335.     strcpy(spbuf, short_side_title(side2));
  336.     MoveTo(sx, sy + 12);
  337.     /* Put the name of our side in boldface. */
  338.     TextFace((side2 == dside ? bold : 0));
  339.     DrawText(spbuf, 0, strlen(spbuf));
  340.     TextFace(0);
  341.     if (side_has_ai(side2) && side2->ingame) {
  342.         /* Show that the side is run by an AI. */
  343.         plot_sicn(gamewin, 182, sy + 2, aisicnhandle, 0, TRUE, srcOr);
  344.     }
  345.     if (side2 != dside
  346.         && side2->ingame
  347.         && (side_has_ai(side2) || side_has_display(side2))) {
  348.         /* Indicate attitude of other side. */
  349.         plot_sicn(gamewin, 164, sy + 2,
  350.             facesicnhandle[feeling_towards(side2, dside)], 0, TRUE, srcOr);
  351.     }
  352.     last_status[s2] = -1;
  353.     last_status_left[s2] = -1;
  354.     last_status_resv[s2] = -1;
  355.     last_status_fini[s2] = -1;
  356.     *(last_status_score1[s2]) = '\0';
  357.     draw_side_status(side2);
  358.     /* Draw a separating line. */
  359.     PenPat(QDPat(gray));
  360.     MoveTo(0, sy + gamesidehgt);
  361.     Line(gamewinw, 0);
  362.     PenNormal();
  363. }
  364.  
  365. /* (should make this more generic) */
  366.  
  367. int
  368. feeling_towards(Side *side, Side *side2)
  369. {
  370.     if (trusted_side(side2, side)) {
  371.         return 2;
  372.     } else if (side_has_ai(side) && should_try_to_win(side)) {
  373.         return 0;
  374.     } else {
  375.         return 1;
  376.     }
  377. }
  378.  
  379. /* Draw the current details about a side. */
  380.  
  381. void
  382. draw_side_status(Side *side2)
  383. {
  384.     int s2 = side_number(side2);
  385.     int sx, sy, i;
  386.     int totacp, resvacp, acpleft, percentleft, percentresv;
  387.     int newstatus;
  388.     char *scoredesc;
  389.     Rect siderect, tmprect, progressrect;
  390.     Scorekeeper *sk;
  391.     extern int curpriority;
  392.  
  393.     /* Be safe.  It's probably not great to be passing through here before the game
  394.        window is actually set up, but it's easier to just ignore the attempt than
  395.        to figure out why it's attempting... */
  396.     if (last_status == NULL)
  397.       return;
  398.     sy = gametophgt + (s2 - 1) * gamesidehgt;
  399.     newstatus = last_status[s2];
  400.     percentleft = 0;
  401.     SetRect(&siderect, 0, sy + 1, gamewinw, sy + gamesidehgt);
  402.     /* Set up the area where we show progress. */
  403.     SetRect(&progressrect, 20, sy + 12 + 4, 20 + 100, sy + 12 + 4 + 7);
  404.     if (!side2->ingame || endofgame) {
  405.         if (last_status[s2] != 0) {
  406.             gray_out_rect(&siderect);
  407.             if (side_won(side2)) {
  408.                 /* (should) Indicate that this side has won. */
  409.                 /* draw like a trophy or flourishes or some such?) */
  410.             } else if (side_lost(side2)) {
  411.                 /* Draw a (solid) line crossing out the loser.  Simple and obvious. */
  412.                 MoveTo(1, sy + 8);
  413.                 Line(gamewin->portRect.right - 3, 0);
  414.             }
  415.             newstatus = 0;
  416.         }
  417.     } else {
  418.         if (!g_use_side_priority() || curpriority == side2->priority) {
  419.             /* Show the current acp totals/progress of the side. */
  420.             /* This is not quite the security hole it might seem,
  421.                you don't get much advantage out of seeing how far along each side is,
  422.                and it gives you a feel for how the turn is progressing. */
  423.             totacp = side_initacp(side2);
  424.             if (totacp > 0) {
  425.                 acpleft = side_acp(side2);
  426.                 resvacp = side_acp_reserved(side2);
  427.                 if (totacp > 0) {
  428.                     percentleft = (100 * acpleft) / totacp;
  429.                     percentleft = max(0, min(99, percentleft));
  430.                     percentresv = (100 * resvacp) / totacp;
  431.                     /* Acp in reserve should be less than acp total. */
  432.                     percentresv = max(0, min(percentleft, percentresv));
  433.                 } else {
  434.                     percentleft = percentresv = 0;
  435.                 }
  436.                 /* Only draw if there's been any actual change. */
  437.                 if (last_status[s2] != 1
  438.                     || last_status_left[s2] != percentleft
  439.                     || last_status_resv[s2] != percentresv) {
  440.                     if (percentleft > 0) {
  441.                         tmprect = progressrect;
  442.                         InsetRect(&tmprect, 1, 1);
  443.                         EraseRect(&tmprect);
  444.                         tmprect.right = tmprect.left + percentleft;
  445.                         FillRect(&tmprect, QDPat(black));
  446.                     }
  447.                     if (percentresv > 0) {
  448.                         tmprect = progressrect;
  449.                         InsetRect(&tmprect, 1, 1);
  450.                         tmprect.right = tmprect.left + percentresv;
  451.                         FillRect(&tmprect, QDPat(dkGray));
  452.                     }
  453.                     last_status_left[s2] = percentleft;
  454.                     last_status_resv[s2] = percentresv;
  455.                 }
  456.                 newstatus = 1;
  457.             } else {
  458.                 if (last_status[s2] != 2) {
  459.                     tmprect = progressrect;
  460.                     InsetRect(&tmprect, 1, 1);
  461.                     EraseRect(&tmprect);
  462.                     newstatus = 2;
  463.                 }
  464.             }
  465.         } else if (g_use_side_priority() && curpriority != side2->priority) {
  466.             newstatus = 0;
  467.         }
  468.         /* (should this be a generic kernel test?) */
  469.         if (side2->finishedturn || !(side_has_ai(side2) || side_has_display(side2))) {
  470.             if (last_status_fini[s2] != 1) {
  471.                 tmprect = progressrect;
  472.                 InsetRect(&tmprect, 1, 1);
  473.                 EraseRect(&tmprect);
  474.                 tmprect.right = tmprect.left + percentleft;
  475.                 FillRect(&tmprect, QDPat(gray));
  476.                 last_status_fini[s2] = 1;
  477.             }
  478.         } else if (!side2->finishedturn) {
  479.             last_status_fini[s2] = 0;
  480.         }
  481.     }
  482.     /* Decide how to frame the progress bar - each shade indicates something. */
  483.     if (newstatus != last_status[s2]) {
  484.         if (newstatus == 0) {
  485.             EraseRect(&progressrect);
  486.             PenPat(QDPat(white));
  487.         } else if (newstatus == 1) {
  488.             PenPat(QDPat(black));
  489.         } else if (newstatus == 2) {
  490.             PenPat(QDPat(gray));
  491.         } else {
  492.             PenPat(QDPat(ltGray));
  493.         }
  494.         FrameRect(&progressrect);
  495.         PenNormal();
  496.         last_status[s2] = newstatus;
  497.     }
  498.     /* Always draw the score, and always ungrayed. */
  499.     if (keeping_score()) {
  500.         siderect.top += gamesidescoreoffset;
  501.         siderect.bottom = siderect.top + gamesidescorehgt - 1;
  502.         i = 0;
  503.         for_all_scorekeepers(sk) {
  504.             scoredesc = side_score_desc(spbuf, side2, sk);
  505.             if ((i == 0 && strcmp(scoredesc, last_status_score1[s2]) != 0)
  506.                 || i > 0) {
  507.                 if ((i & 1) == 0)
  508.                   EraseRect(&siderect);
  509.                 /* Draw two scorekeepers per line. */
  510.                 sx = (((i & 1) == 1) ? gamewinw / 2 : 0);
  511.                 /* Draw the scorekeeper's status. */
  512.                 MoveTo(sx + 10, siderect.top + 10);
  513.                 DrawText(scoredesc, 0, strlen(scoredesc));
  514.                 if (i == 0)
  515.                   strcpy(last_status_score1[s2], scoredesc);
  516.             }
  517.             ++i;
  518.             /* Offset rectangle to next row. */
  519.             if ((i & 1) == 0)
  520.               OffsetRect(&siderect, 0, gamesidescorehgt);
  521.         }
  522.     }
  523. }
  524.  
  525. void
  526. do_mouse_down_game(Point mouse, int mods)
  527. {
  528.     beep();
  529. }
  530.  
  531. /* The construction planning window. */
  532.  
  533. void
  534. create_construction_window()
  535. {
  536.     int done = FALSE, mainheight;
  537.     Point cellsize;
  538.     Rect listrect, tmprect;
  539.  
  540.     if (hasColorQD) {
  541.         constructionwin = GetNewCWindow(wConstruction, NULL, (WindowPtr) -1L);
  542.     } else {
  543.         constructionwin = GetNewWindow(wConstruction, NULL, (WindowPtr) -1L);
  544.     }
  545.     constructbutton = GetNewControl(cConstructButton, constructionwin);
  546.     researchbutton = GetNewControl(cResearchButton, constructionwin);
  547.     SetPort(constructionwin);
  548.     calc_construction_rects();
  549.     run_length_text = TENew(&runlengthrect, &runlengthrect);
  550.     set_construction_run_length(DEFAULT_RUN);
  551.     editedrunlength = -1;
  552.     /* Switch to a font for the lists. */
  553.     TextFont(monaco);
  554.     TextSize(9);
  555.     /* Set up the list of all constructing units. */
  556.     tmprect = unitlistrect;
  557.     tmprect.right -= sbarwid;
  558.     SetRect(&listrect, 0, 0, 1, 0);
  559.     SetPt(&cellsize, 300, 12);
  560.     /* Create the list of units itself. */
  561.     construction_unit_list =
  562.         LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
  563.              FALSE, FALSE, FALSE, TRUE);
  564.     /* Now set up the list of types. */
  565.     tmprect = typelistrect;
  566.     tmprect.right -= sbarwid;
  567.     SetRect(&listrect, 0, 0, 1, 0);
  568.     /* (should calc this from the desired font) */
  569.     SetPt(&cellsize, 300, 12);
  570.     construction_type_list =
  571.         LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
  572.              FALSE, FALSE, FALSE, TRUE);
  573.     init_construction_lists();
  574.     if (1 /* position construction at bottom of main screen */) {
  575.         get_main_screen_size(NULL, &mainheight);
  576.         tmprect = constructionwin->portRect;
  577.         MoveWindow(constructionwin,
  578.                    4,
  579.                    mainheight - (tmprect.bottom - tmprect.top) - 3,
  580.                    FALSE);
  581.     }
  582.     ShowWindow(constructionwin);
  583. }
  584.  
  585. /* Build the list of constructing units and the list of constructible types. */
  586.  
  587. void
  588. init_construction_lists()
  589. {
  590.     int u;
  591.     Unit *unit;
  592.     Cell tmpcell;
  593.  
  594.     /* Update the list of units. */
  595.     LDoDraw(0, construction_unit_list);
  596.     LDelRow(0, 0, construction_unit_list);
  597.     SetPt(&tmpcell, 0, 0);
  598.     /* Create the vector of constructing units, at a reasonable initial size. */
  599.     if (constructor_units == NULL) {
  600.         constructor_units = make_unit_vector(max(50, numunits));
  601.     }
  602.     clear_unit_vector(constructor_units);
  603.     for_all_side_units(dside, unit) {
  604.         maybe_add_unit_to_construction_list(unit);
  605.     }
  606.     LDoDraw(1, construction_unit_list);
  607.     /* Update the list of types. */
  608.     LDoDraw(0, construction_type_list);
  609.     LDelRow(0, 0, construction_type_list);
  610.     SetPt(&tmpcell, 0, 0);
  611.     if (possibletypes == NULL)
  612.       possibletypes = (int *) xmalloc(numutypes * sizeof(int));
  613.     numposstypes = 0;
  614.     for_all_unit_types(u) {
  615.         if (construction_possible(u) && type_allowed_on_side(u, dside)) {
  616.             LAddRow(1, tmpcell.v, construction_type_list);
  617.             constructible_desc(spbuf, dside, u, NULL);
  618.             LSetCell(spbuf, strlen(spbuf), tmpcell, construction_type_list);
  619.             ++tmpcell.v;
  620.             possibletypes[numposstypes++] = u;
  621.         }
  622.     }
  623.     LDoDraw(1, construction_type_list);
  624.     adjust_construction_controls();
  625. }
  626.  
  627. void
  628. reinit_construction_lists()
  629. {
  630.     init_construction_lists();
  631. }
  632.  
  633. void
  634. set_construction_run_length(int len)
  635. {
  636.     /* Do nothing if no change. */
  637.     if (len == currunlength)
  638.       return;
  639.     currunlength = len;
  640.     TESetSelect(0, 32767, run_length_text);
  641.     TEDelete(run_length_text);
  642.     sprintf(tmpbuf, "%d", len);
  643.     TEInsert(tmpbuf, strlen(tmpbuf), run_length_text);
  644. }
  645.  
  646. /* Draw the construction window by updating the lists and framing them. */
  647.  
  648. void
  649. draw_construction()
  650. {
  651.     Rect tmprect;
  652.  
  653.     calc_construction_rects();
  654.     TEUpdate(&(constructionwin->portRect), run_length_text);
  655.     tmprect = runlengthrect;
  656.     InsetRect(&tmprect, -1, -1);
  657.     FrameRect(&tmprect);
  658.     LUpdate(constructionwin->visRgn, construction_unit_list);
  659.     tmprect = unitlistrect;
  660.     InsetRect(&tmprect, -1, -1);
  661.     FrameRect(&tmprect);
  662.     LUpdate(constructionwin->visRgn, construction_type_list);
  663.     tmprect = typelistrect;
  664.     InsetRect(&tmprect, -1, -1);
  665.     FrameRect(&tmprect);
  666.     /* Maybe show the construct button as the default. */
  667.     draw_construction_default();
  668. }
  669.  
  670. /* Draw a heavy outline around the construction button. */
  671.  
  672. void
  673. draw_construction_default()
  674. {
  675.     Rect tmprect;
  676.     GrafPtr oldport;
  677.  
  678.     GetPort(&oldport);
  679.     SetPort(constructionwin);
  680.     tmprect = (*constructbutton)->contrlRect;
  681.     PenSize(3, 3);
  682.     InsetRect(&tmprect, -4, -4);
  683.     if ((*constructbutton)->contrlHilite != 0) {
  684.         PenMode(patBic);
  685.     }
  686.     FrameRoundRect(&tmprect, 16, 16);
  687.     PenNormal();
  688.     SetPort(oldport);
  689. }
  690.  
  691. /* Figure out how to subdivide the construction window for the two lists. */
  692.  
  693. void
  694. calc_construction_rects()
  695. {
  696.     int wid, hgt, divide;
  697.     Rect tmprect;
  698.  
  699.     tmprect = constructionwin->portRect;
  700.     runlengthrect = tmprect;
  701.     runlengthrect.left = runlengthrect.right - 100;  runlengthrect.top = 5;
  702.     runlengthrect.right -= 20;  runlengthrect.bottom = 25;
  703.     wid = tmprect.right - tmprect.left - sbarwid;
  704.     hgt = tmprect.bottom - tmprect.top - sbarwid;
  705.     if (wid / 2 > 220 /* maxtypewid */) {
  706.         divide = wid - 220;
  707.     } else {
  708.         divide = wid / 2;
  709.     }
  710.     SetRect(&unitlistrect, 0, constructtop, divide, hgt);
  711.     InsetRect(&unitlistrect, constructmargin, constructmargin);
  712.     SetRect(&typelistrect, divide, constructtop, wid, hgt);
  713.     InsetRect(&typelistrect, constructmargin, constructmargin);
  714. }
  715.  
  716. void
  717. activate_construction(int activate)
  718. {
  719.     if (activate)
  720.       TEActivate(run_length_text);
  721.     else
  722.       TEDeactivate(run_length_text);
  723.     LActivate(activate, construction_unit_list);
  724.     LActivate(activate, construction_type_list);
  725. }
  726.  
  727. Unit *
  728. get_selected_construction_unit()
  729. {
  730.     Point tmpcell;
  731.     Unit *unit;
  732.  
  733.     SetPt(&tmpcell, 0, 0);
  734.     if (LGetSelect(TRUE, &tmpcell, construction_unit_list)) {                
  735.         if (tmpcell.v < constructor_units->numunits) {
  736.             unit = (constructor_units->units)[tmpcell.v].unit;
  737.             if (is_acting(unit))
  738.               return unit;
  739.         }
  740.     }
  741.     return NULL;
  742. }
  743.  
  744. int
  745. get_selected_construction_type()
  746. {
  747.     Point tmpcell;
  748.  
  749.     SetPt(&tmpcell, 0, 0);
  750.     if (LGetSelect(TRUE, &tmpcell, construction_type_list)) {                
  751.         if (tmpcell.v < numposstypes) {
  752.             return possibletypes[tmpcell.v];
  753.         }
  754.     }
  755.     return NONUTYPE;
  756. }
  757.  
  758. void
  759. scroll_to_selected_construction_unit()
  760. {
  761.     Unit *unit;
  762.  
  763.     /* Beep and return if there are no maps open currently. */
  764.     if (maplist == NULL) {
  765.         beep();
  766.         return;
  767.     }
  768.     unit = get_selected_construction_unit();
  769.     if (unit != NULL && inside_area(unit->x, unit->y))
  770.       scroll_best_map_to_unit(unit);
  771. }
  772.  
  773. /* Handle a click anywhere within the construction window. */
  774.  
  775. void
  776. do_mouse_down_construction(Point mouse, int mods)
  777. {
  778.     ControlHandle control;
  779.     short part;
  780.     int u;
  781.     Unit *unit;
  782.     extern int modal_construction;
  783.     extern WindowPtr window_behind_construction;
  784.  
  785.     part = FindControl(mouse, constructionwin, &control);
  786.     if (control == constructbutton) {
  787.         unit = get_selected_construction_unit();
  788.         if (unit != NULL) {
  789.             u = get_selected_construction_type();
  790.             if (u != NONUTYPE) {
  791.                 push_build_task(unit, u, currunlength);
  792.                 execute_task(unit);
  793.                 update_construction_unit_list(unit);
  794.                 if (modal_construction && window_behind_construction != nil)
  795.                   SelectWindow(window_behind_construction);
  796.                 window_behind_construction = NULL;
  797.                 modal_construction = FALSE;
  798.                 return;
  799.             }
  800.         }
  801.     } else if (control == researchbutton) {
  802.         unit = get_selected_construction_unit();
  803.         if (unit != NULL) {
  804.             u = get_selected_construction_type();
  805.             if (u != NONUTYPE) {
  806.                 push_research_task(unit, u, u_tech_to_build(u));
  807.                 execute_task(unit);
  808.                 update_construction_unit_list(unit);
  809.                 if (modal_construction && window_behind_construction != nil)
  810.                   SelectWindow(window_behind_construction);
  811.                 window_behind_construction = NULL;
  812.                 modal_construction = FALSE;
  813.                 return;
  814.             }
  815.         }
  816.     } else if (PtInRect(mouse, &runlengthrect)) {
  817.         TEClick(mouse, mods, run_length_text);
  818.         /* (should switch this to be current item) */
  819.     } else if (PtInRect(mouse, &unitlistrect)) {
  820.         LClick(mouse, mods, construction_unit_list);
  821.         /* Update the type list to show what could be built and in how long. */
  822.         update_type_list_for_unit(get_selected_construction_unit());
  823.     } else if (PtInRect(mouse, &typelistrect)) {
  824.         LClick(mouse, mods, construction_type_list);
  825.         /* Update the unit list to show what could build the type */
  826.         update_unit_list_for_type(get_selected_construction_type());
  827.     } else {
  828.         /* Click was not in any useful part of the window. */ 
  829.     }
  830. }
  831.  
  832. int
  833. do_key_down_construction(key)
  834. int key;
  835. {
  836.     int len, runlength, i;
  837.     char buffer[10];
  838.     CharsHandle text;
  839.  
  840.     if (isdigit(key)) {
  841.         TEKey(key, run_length_text);
  842.         text = TEGetText(run_length_text);
  843.         /* Pick out only the initial digits (up to 9). */
  844.         len = min((*run_length_text)->teLength, 9);
  845.         strncpy(buffer, *text, len);
  846.         buffer[len] = '\0';
  847.         runlength = atoi(buffer);
  848.         if (between(1, runlength, 32767)) {
  849.             currunlength = runlength;
  850.             editedrunlength = runlength;
  851.             return TRUE;
  852.         }
  853.     } else if (key == 13 || key == 3) {
  854.         /* Pass enters and returns back to Dialog Manager. */
  855.         return FALSE;
  856.     } else {
  857.         for (i = 0; i < numposstypes; ++i) {
  858.             if (key == unitchars[possibletypes[i]]
  859.                 /* Skip over types that the selected unit can't build. */
  860.                 && est_completion_time(get_selected_construction_unit(), possibletypes[i]) >= 0) {
  861.                 select_type_in_construction_window(possibletypes[i]);
  862.                 return TRUE;
  863.             }
  864.         }
  865.         /* If not recognized as a unit type shortcut, hand it to the
  866.            run length field. */
  867.         if (isalpha(key)) {
  868.             beep();
  869.             return FALSE;
  870.         }
  871.         TEKey(key, run_length_text);
  872.  
  873.     }
  874.     return FALSE;
  875. }
  876.  
  877. /* Highlight exactly one specific unit in the construction window, and unhighlight
  878.    any others. */
  879.  
  880. void
  881. select_unit_in_construction_window(Unit *unit)
  882. {
  883.     int i;
  884.     Point tmpcell;
  885.  
  886.     for (i = 0; i < constructor_units->numunits; ++i) {
  887.         SetPt(&tmpcell, 0, i);
  888.         LSetSelect((unit == (constructor_units->units)[i].unit), tmpcell, construction_unit_list);
  889.         LAutoScroll(construction_unit_list);
  890.     }
  891.     update_type_list_for_unit(get_selected_construction_unit());
  892. }
  893.  
  894. void
  895. select_type_in_construction_window(int u)
  896. {
  897.     int i;
  898.     Point tmpcell;
  899.  
  900.     for (i = 0; i < numposstypes; ++i) {
  901.         SetPt(&tmpcell, 0, i);
  902.         LSetSelect((u == possibletypes[i]), tmpcell, construction_type_list);
  903.         LAutoScroll(construction_type_list);
  904.     }
  905.     if (u == NONUTYPE)
  906.       return;
  907.     update_unit_list_for_type(get_selected_construction_type());
  908. }
  909.  
  910. /* Given a unit (which may be any unit), update the list of constructing units. */
  911.  
  912. void
  913. update_construction_unit_list(Unit *unit)
  914. {
  915.     int i, u;
  916.     Point tmpcell;
  917.  
  918.     if (constructionwin == nil)
  919.       return;
  920.     u = get_selected_construction_type();
  921.     /* We need to look for it even if it might not be ours, since it might
  922.        have been captured or otherwise lost, and needs to be removed. */
  923.     for (i = 0; i < constructor_units->numunits; ++i) {
  924.         if (unit == (constructor_units->units)[i].unit) {
  925.             SetPt(&tmpcell, 0, i);
  926.             if (is_active(unit)
  927.                 && can_build_or_help(unit)
  928.                 && side_controls_unit(dside, unit)) {
  929.                 construction_desc(spbuf, unit, u);
  930.                 LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
  931.             } else {
  932.                 remove_unit_from_vector(constructor_units, unit, i);
  933.                 LDelRow(1, tmpcell.v, construction_unit_list);
  934.             }
  935.             return;
  936.         }
  937.     }
  938.     /* Unit was not found, try to add it to the list. */
  939.     maybe_add_unit_to_construction_list(unit);
  940. }
  941.  
  942. void
  943. maybe_add_unit_to_construction_list(Unit *unit)
  944. {
  945.     Point tmpcell;
  946.  
  947.     if (is_acting(unit)
  948.         && can_build_or_help(unit)
  949.         && side_controls_unit(dside, unit)) {
  950.         /* Add this unit to the vector of constructing units. */
  951.         constructor_units = add_unit_to_vector(constructor_units, unit, 0);
  952.         /* (should sort and maybe rearrange list here) */
  953.         /* Add a row at the end of the list. */
  954.         SetPt(&tmpcell, 0, constructor_units->numunits - 1);
  955.         LAddRow(1, constructor_units->numunits - 1, construction_unit_list);
  956.         construction_desc(spbuf, unit, get_selected_construction_type());
  957.         LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
  958.     }
  959. }
  960.  
  961. void
  962. update_unit_list_for_type(int u)
  963. {
  964.     int i;
  965.     Point tmpcell;
  966.     Unit *unit;
  967.  
  968.     for (i = 0; i < constructor_units->numunits; ++i) {
  969.         unit = (constructor_units->units)[i].unit;
  970.         if (unit != NULL) {
  971.             SetPt(&tmpcell, 0, i);
  972.             if (is_acting(unit) && unit->side == dside) {
  973.                 construction_desc(spbuf, unit, u);
  974.                 LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
  975.             } else {
  976. /*                LDelRow(1, tmpcell.v, construction_unit_list); */
  977.                 LSetCell("", 0, tmpcell, construction_unit_list);
  978.             }
  979.         }
  980.     }
  981.     adjust_construction_controls();
  982. }
  983.  
  984. void
  985. update_construction_type_list()
  986. {
  987.     int u;
  988.  
  989.     if (constructionwin == nil)
  990.       return;
  991.     u = get_selected_construction_type();
  992.     update_type_list_for_unit(get_selected_construction_unit());
  993. }
  994.  
  995. void
  996. update_type_list_for_unit(Unit *unit)
  997. {
  998.     int i;
  999.     Point tmpcell;
  1000.  
  1001.     for (i = 0; i < numposstypes; ++i) {
  1002.         constructible_desc(spbuf, dside, possibletypes[i], get_selected_construction_unit());
  1003.         SetPt(&tmpcell, 0, i);
  1004.         LSetCell(spbuf, strlen(spbuf), tmpcell, construction_type_list);
  1005.     }
  1006.     adjust_construction_controls();
  1007. }
  1008.  
  1009. /* Enable/disable controls according to whether the selected list elements can
  1010.    do construction activities. */
  1011.  
  1012. void
  1013. adjust_construction_controls()
  1014. {
  1015.     int u, canconstruct = FALSE, canresearch = FALSE, len;
  1016.     Unit *unit;
  1017.  
  1018.     unit = get_selected_construction_unit();
  1019.     if (unit != NULL) {
  1020.         u = get_selected_construction_type();
  1021.         if (u != NONUTYPE) {
  1022.             if (uu_acp_to_create(unit->type, u) > 0)
  1023.               canconstruct = TRUE;
  1024.             if (uu_acp_to_research(unit->type, u) > 0)
  1025.               canresearch = TRUE;
  1026.         }
  1027.     }
  1028.     HiliteControl(constructbutton, (canconstruct ? 0 : 255));
  1029.     HiliteControl(researchbutton, (canresearch ? 0 : 255));
  1030.     draw_construction_default();
  1031.     /* If there is doctrine on construction run length, use it to seed the
  1032.        run length in the dialog. */
  1033.     if (editedrunlength < 0 && unit != NULL && u != NONUTYPE) {
  1034.         len = DEFAULT_RUN;
  1035.         if (dside->udoctrine[unit->type] != NULL
  1036.             && dside->udoctrine[unit->type]->construction_run != NULL
  1037.             && dside->udoctrine[unit->type]->construction_run[u] > 0) {
  1038.             len = (dside->udoctrine[unit->type])->construction_run[u];
  1039.         }
  1040.         set_construction_run_length(len);
  1041.     }
  1042. }
  1043.  
  1044. /* Resize the construction window to the given size. */
  1045.  
  1046. void
  1047. grow_construction(int h, int v)
  1048. {
  1049.     EraseRect(&constructionwin->portRect);
  1050.     SizeWindow(constructionwin, h, v, 1);
  1051.     adjust_construction_items();
  1052.     /* This will force a full redraw at the next update. */
  1053.     InvalRect(&constructionwin->portRect);
  1054. }                    
  1055.  
  1056. /* Zooming "rightsizes" the window. */
  1057.  
  1058. void
  1059. zoom_construction(int part)
  1060. {
  1061.     int titleh, vislinesavail;
  1062.     Rect zoomrect;
  1063.     GDHandle zoomgd;
  1064.  
  1065.     EraseRect(&constructionwin->portRect);
  1066.     if (part == inZoomOut) {
  1067.         if (hasColorQD) {
  1068.             zoomgd = best_zoom_screen(&constructionwin->portRect);
  1069.             zoomrect = (*zoomgd)->gdRect;
  1070.             if (zoomgd == GetMainDevice()) {
  1071.                 zoomrect.top += GetMBarHeight();
  1072.             }
  1073.         } else {
  1074.             /* If no Color QD, then there is only the one screen. */
  1075.             zoomrect = QD(screenBits).bounds;
  1076.             zoomrect.top += GetMBarHeight();
  1077.         }
  1078.         titleh = 20; /* (should calc) */
  1079.         zoomrect.top += titleh;
  1080.         InsetRect(&zoomrect, 4, 4);
  1081.         /* If not many units or types, shrink the zoomed window to fit. */
  1082.         vislinesavail = (zoomrect.bottom - zoomrect.top - sbarwid) / 15;
  1083.         if (0) {
  1084.             zoomrect.bottom = zoomrect.top + 20  * 15 + sbarwid;
  1085.         }
  1086.         (*((WStateDataHandle) ((WindowPeek) constructionwin)->dataHandle))->stdState = zoomrect;
  1087.     }
  1088.     ZoomWindow(constructionwin, part, (constructionwin == FrontWindow()));
  1089.     adjust_construction_items();
  1090.     /* This will force a full redraw at the next update. */
  1091.     InvalRect(&constructionwin->portRect);
  1092. }
  1093.  
  1094. /* Move and resize the list and text objects in the construction window. */
  1095.  
  1096. static void
  1097. adjust_construction_items()
  1098. {
  1099.     /* Recalculate size and position. */
  1100.     calc_construction_rects();
  1101.     /* Resize the run length text item. */
  1102.     (*run_length_text)->viewRect = runlengthrect;
  1103.     (*run_length_text)->destRect = runlengthrect;
  1104.     TECalText(run_length_text);
  1105.     /* Resize the unit list. */
  1106.     LSize(unitlistrect.right - unitlistrect.left - sbarwid,
  1107.           unitlistrect.bottom - unitlistrect.top,
  1108.           construction_unit_list);
  1109.     /* Move the type list (is this the approved way to do it?) */
  1110.     (*construction_type_list)->rView.left = typelistrect.left;
  1111.     LSize(typelistrect.right - typelistrect.left - sbarwid,
  1112.           typelistrect.bottom - typelistrect.top,
  1113.           construction_type_list);
  1114. }
  1115.  
  1116. /* The side renaming dialog includes places for all the different name-related
  1117.    properties of a side. */
  1118.  
  1119. void
  1120. side_rename_dialog(Side *side)
  1121. {
  1122.     short done = FALSE, changed = TRUE, ditem;
  1123.     Str255 tmpstr;
  1124.     DialogPtr win;
  1125.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1126.  
  1127.     win = GetNewDialog(dSideRename, NULL, (DialogPtr) -1L);
  1128.     while (!done) {
  1129.         if (changed) {
  1130.             /* Seed the items with the current side names. */
  1131.             GetDItem(win, diSideRenameName, &itemtype, &itemhandle, &itemrect);
  1132.             c2p((side->name ? side->name : ""), tmpstr);
  1133.             SetIText(itemhandle, tmpstr);
  1134.             GetDItem(win, diSideRenameFullName, &itemtype, &itemhandle, &itemrect);
  1135.             c2p((side->longname ? side->longname : ""), tmpstr);
  1136.             SetIText(itemhandle, tmpstr);
  1137.             GetDItem(win, diSideRenameAcronym, &itemtype, &itemhandle, &itemrect);
  1138.             c2p((side->shortname ? side->shortname : ""), tmpstr);
  1139.             SetIText(itemhandle, tmpstr);
  1140.             GetDItem(win, diSideRenameNoun, &itemtype, &itemhandle, &itemrect);
  1141.             c2p((side->noun ? side->noun : ""), tmpstr);
  1142.             SetIText(itemhandle, tmpstr);
  1143.             GetDItem(win, diSideRenamePluralNoun, &itemtype, &itemhandle, &itemrect);
  1144.             c2p((side->pluralnoun ? side->pluralnoun : ""), tmpstr);
  1145.             SetIText(itemhandle, tmpstr);
  1146.             GetDItem(win, diSideRenameAdjective, &itemtype, &itemhandle, &itemrect);
  1147.             c2p((side->adjective ? side->adjective : ""), tmpstr);
  1148.             SetIText(itemhandle, tmpstr);
  1149.             GetDItem(win, diSideRenameEmblemName, &itemtype, &itemhandle, &itemrect);
  1150.             c2p((side->emblemname ? side->emblemname : ""), tmpstr);
  1151.             SetIText(itemhandle, tmpstr);
  1152.             GetDItem(win, diSideRenameColorScheme, &itemtype, &itemhandle, &itemrect);
  1153.             c2p((side->colorscheme ? side->colorscheme : ""), tmpstr);
  1154.             SetIText(itemhandle, tmpstr);
  1155.             ShowWindow(win);
  1156.             changed = FALSE;
  1157.         }
  1158.         draw_default_button(win, diSideRenameOK);
  1159.         SetCursor(&QD(arrow));
  1160.         ModalDialog(NULL, &ditem);
  1161.         switch (ditem) {
  1162.             case diSideRenameOK:
  1163.                 /* Actually change the side's slots. */
  1164.                 /* (should all go through networkable calls!) */
  1165.                 GetDItem(win, diSideRenameName, &itemtype, &itemhandle, &itemrect);
  1166.                 set_side_name(dside, dside, get_string_from_item(itemhandle));
  1167.                 GetDItem(win, diSideRenameFullName, &itemtype, &itemhandle, &itemrect);
  1168.                 side->longname = get_string_from_item(itemhandle);
  1169.                 GetDItem(win, diSideRenameAcronym, &itemtype, &itemhandle, &itemrect);
  1170.                 side->shortname = get_string_from_item(itemhandle);
  1171.                 GetDItem(win, diSideRenameNoun, &itemtype, &itemhandle, &itemrect);
  1172.                 side->noun = get_string_from_item(itemhandle);
  1173.                 GetDItem(win, diSideRenamePluralNoun, &itemtype, &itemhandle, &itemrect);
  1174.                 side->pluralnoun = get_string_from_item(itemhandle);
  1175.                 GetDItem(win, diSideRenameAdjective, &itemtype, &itemhandle, &itemrect);
  1176.                 side->adjective = get_string_from_item(itemhandle);
  1177.                 GetDItem(win, diSideRenameEmblemName, &itemtype, &itemhandle, &itemrect);
  1178.                 side->emblemname = get_string_from_item(itemhandle);
  1179.                 GetDItem(win, diSideRenameColorScheme, &itemtype, &itemhandle, &itemrect);
  1180.                 side->colorscheme = get_string_from_item(itemhandle);
  1181.                 /* Tweak the side menu. */
  1182.                 update_side_menu(dside);
  1183.                 /* Force redisplay of everything that might use any side names. */
  1184.                 force_overall_update();
  1185.                 /* Fall into next case. */
  1186.             case diSideRenameCancel:
  1187.                 done = TRUE;
  1188.                 break;
  1189.             case diSideRenameRandom:
  1190.                 side->name = NULL;
  1191.                 side->noun = NULL;
  1192.                 /* always need to clear this cache before renaming... */
  1193.                 side->pluralnoun = NULL;
  1194.                 side->adjective = NULL;
  1195.                 make_up_side_name(side);
  1196.                 init_emblem_images(side);
  1197.                 changed = TRUE;
  1198.                 break;
  1199.         }
  1200.     }
  1201.     DisposDialog(win);
  1202. }
  1203.  
  1204. /* Unit naming/renaming. */
  1205.  
  1206. int
  1207. unit_rename_dialog(Unit *unit)
  1208. {
  1209.     short done = FALSE, ditem;
  1210.     char *newname;
  1211.     char *namer = unit_namer(unit);
  1212.     Str255 tmpstr;
  1213.     DialogPtr win;
  1214.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1215.  
  1216.     if (unit == NULL)
  1217.       return FALSE;
  1218.     win = GetNewDialog(dRename, NULL, (DialogPtr) -1L);
  1219.     /* Seed the text item with the original name. */
  1220.     newname = unit->name;
  1221.     if (newname == NULL)
  1222.       newname = "";
  1223.     GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1224.     c2p(newname, tmpstr);
  1225.     SetIText(itemhandle, tmpstr);
  1226.     /* Gray out the random renaming button if no namers available. */
  1227.     GetDItem(win, diRenameRandom, &itemtype, &itemhandle, &itemrect);
  1228.     HiliteControl((ControlHandle) itemhandle, (!empty_string(namer) ? 0 : 255));
  1229.     ShowWindow(win);
  1230.     while (!done) {
  1231.         draw_default_button(win, diRenameOK);
  1232.         SetCursor(&QD(arrow));
  1233.         ModalDialog(NULL, &ditem);
  1234.         switch (ditem) {
  1235.             case diRenameOK:
  1236.                 GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1237.                 set_unit_name(dside, unit, get_string_from_item(itemhandle));
  1238.                 /* Fall into the next case. */
  1239.             case diRenameCancel:
  1240.                 done = TRUE;
  1241.                 break;
  1242.             case diRenameRandom:
  1243.                 newname = propose_unit_name(unit);
  1244.                 if (!empty_string(newname)) {
  1245.                     GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1246.                      c2p(newname, tmpstr);
  1247.                     SetIText(itemhandle, tmpstr);
  1248.                 }
  1249.                 break;
  1250.         }
  1251.     }
  1252.     DisposDialog(win);
  1253.     return TRUE;
  1254. }
  1255.  
  1256. /* Unit closeups. */
  1257.  
  1258. UnitCloseup *
  1259. find_unit_closeup(Unit *unit)
  1260. {
  1261.     UnitCloseup *unitcloseup;
  1262.  
  1263.     for_all_unit_closeups(unitcloseup) {
  1264.         if (unitcloseup->unit == unit
  1265.             && unitcloseup->window
  1266.             && ((WindowPeek) unitcloseup->window)->visible)
  1267.           return unitcloseup;
  1268.     }
  1269.     return NULL;
  1270. }
  1271.  
  1272. void
  1273. create_unit_closeup(Unit *unit)
  1274. {
  1275.     int u, w, h;
  1276.     WindowPtr win;
  1277.     UnitCloseup *unitcloseup = (UnitCloseup *) xmalloc(sizeof(UnitCloseup));
  1278.  
  1279.     if (!active_display(dside) || unit == NULL)
  1280.       return;
  1281.     DGprintf("Creating a closeup of %s\n", unit_desig(unit));
  1282.     u = unit->type;
  1283.     unitcloseup->unit = unit;
  1284.     if (hasColorQD) {
  1285.         win = GetNewCWindow(wUnitCloseup, nil, (WindowPtr) -1L);
  1286.     } else {
  1287.         win = GetNewWindow(wUnitCloseup, nil, (WindowPtr) -1L);
  1288.     }
  1289.     unitcloseup->window = win;
  1290.     stagger_window(unitcloseup->window, &lastunitcloseuph, &lastunitcloseupv);
  1291.     preferred_closeup_size(u, &w, &h);
  1292.     SizeWindow(win, w, h, 1);
  1293.     sprintf(spbuf, "Closeup on %s", short_unit_handle(unit));
  1294.     add_window_menu_item(spbuf, win);
  1295.     unitcloseup->next = unitcloseuplist;
  1296.     unitcloseuplist = unitcloseup;
  1297.     /* We're now ready to show this closeup to the world. */
  1298.     ShowWindow(win);
  1299. }
  1300.  
  1301. /* Compute the right size for a unit's closeup.  More complicated units
  1302.    with more state get bigger closeups. */
  1303.  
  1304. void
  1305. preferred_closeup_size(int u, int *widp, int *hgtp)
  1306. {
  1307.     int wid = closeupwinwid, hgt, m, u2, count;
  1308.  
  1309.     hgt = 4 + 32 + 4 + 3 * closeupspacing;
  1310.     if (nummrows == NULL) {
  1311.         /* Compute and cache the space needed to display each unit's supply. */
  1312.         nummrows = xmalloc(numutypes);
  1313.         for_all_unit_types(u2) {
  1314.             nummrows[u2] = count = 0;
  1315.             for_all_material_types(m) {
  1316.                 if (um_storage_x(u2, m) > 0)
  1317.                     ++count;
  1318.               }
  1319.               if (count > 0) {
  1320.                   nummrows[u2] = max(1, (count + (m_per_row - 1)) / m_per_row);
  1321.               }
  1322.         }
  1323.     }
  1324.     hgt += nummrows[u] * closeupspacing;
  1325.     if (u_acp(u) > 0)
  1326.       hgt += 5 * closeupspacing;
  1327.     /* (should make some room for tooling state) */
  1328.     *widp = wid;  *hgtp = hgt;
  1329. }
  1330.  
  1331. /* Draw all the fields and displays in a unit closeup. */
  1332.  
  1333. void
  1334. draw_unit_closeup(UnitCloseup *unitcloseup)
  1335. {
  1336.     int u, m, sx = 4, sy, count, sx2, sy2;
  1337.     char tmpbuf[BUFSIZE];
  1338.     Rect tmprect;
  1339.     GrafPtr oldport;
  1340.     WindowPtr win = unitcloseup->window;
  1341.     Unit *unit = unitcloseup->unit;
  1342.  
  1343.     if (!active_display(dside))
  1344.       return;
  1345.     if (!in_play(unit) || !(side_controls_unit(dside, unit) || endofgame)) {
  1346.         /* If the unit is no longer alive and ours, shut down the window. */
  1347.         remove_window_menu_item(win);
  1348.         destroy_unit_closeup(unitcloseup);
  1349.         HideWindow(win);
  1350.         return;
  1351.     }
  1352.     GetPort(&oldport);
  1353.     SetPort(win);
  1354.     /* Switch to a small fixed-width font. */
  1355.     TextFont(monaco);
  1356.     TextSize(9);
  1357.     EraseRect(&win->portRect);
  1358.     u = unit->type;
  1359.     /* Draw the unit's image. */
  1360.     SetRect(&tmprect, sx, sx, sx + 32, sx + 32); 
  1361.     EraseRect(&tmprect);
  1362.     draw_unit_image(win, tmprect.left, tmprect.top,
  1363.                     tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
  1364.                     u, side_number(unit->side), !completed(unit));
  1365.     /* Draw the unit's name. */
  1366.     name_or_number(unit, tmpbuf);
  1367.     MoveTo(44, 4 + closeupspacing);
  1368.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1369.     if (Debug || DebugG || DebugM) {
  1370.         sprintf(tmpbuf, " %d ", unit->id);
  1371.         MoveTo(closeupwinwid - 30, 15);
  1372.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1373.     }
  1374.     /* Draw the unit's side and type. */
  1375.     side_and_type_name(tmpbuf, dside, u, unit->side);
  1376.     MoveTo(44, 4 + closeupspacing * 2);
  1377.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1378.     /* Draw the unit's location. */
  1379.     if (unit->z != 0) {
  1380.         sprintf(tmpbuf, "; alt %d", unit->z);
  1381.     } else {
  1382.         strcpy(tmpbuf, "");
  1383.     }
  1384.     sprintf(spbuf, "at %d,%d%s", unit->x, unit->y, tmpbuf);
  1385.     if (unit->transport != NULL) {
  1386.         sprintf(tmpbuf, "In %s (%s)", short_unit_handle(unit->transport), spbuf);
  1387.     } else {
  1388.         strcpy(tmpbuf, spbuf);
  1389.     }
  1390.     sy = 4 + 32 + 4 + closeupspacing;
  1391.     MoveTo(sx, sy);
  1392.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1393.     /* Draw the unit's hit points. */
  1394.     hp_desc(tmpbuf, unit, TRUE);
  1395.     sy += closeupspacing;
  1396.     MoveTo(sx, sy);
  1397.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1398.     /* Draw the unit's current ACP, if applicable. */
  1399.     if (u_acp(u) > 0) {
  1400.         acp_desc(tmpbuf, unit, TRUE);
  1401.         sy += closeupspacing;
  1402.         MoveTo(sx, sy);
  1403.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1404.     }
  1405.     /* Draw the unit's supplies. */
  1406.     count = 0;
  1407.     if (nummrows[u] > 0)
  1408.       sy += closeupspacing;
  1409.     for_all_material_types(m) {
  1410.         if (um_storage_x(u, m) > 0) {
  1411.             strcpy(tmpbuf, m_type_name(m));
  1412.             sx2 = sx + (count % m_per_row) * (closeupwinwid / m_per_row);
  1413.             sy2 = sy + (count / m_per_row) * closeupspacing;
  1414.             MoveTo(sx2, sy2);
  1415.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1416.             sprintf(tmpbuf, "%d/%d", unit->supply[m], um_storage_x(u, m));
  1417.             MoveTo(sx2 + (closeupwinwid / m_per_row - 4 - 4 - TextWidth(tmpbuf, 0, strlen(tmpbuf))), sy2);
  1418.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1419.             ++count;
  1420.         }
  1421.     }
  1422.     if (nummrows[u] > 0)
  1423.       sy += (nummrows[u] - 1) * closeupspacing; 
  1424.     /* Draw the unit's plan, if it has one. */
  1425.     if (unit->plan) {
  1426.         Task *task;
  1427.         extern char *plantypenames[];
  1428.         Plan *plan = unit->plan;
  1429.         
  1430.         sprintf(tmpbuf, "Plan: %s", plantypenames[plan->type]);
  1431.         if (plan->waitingfortasks)
  1432.           strcat(tmpbuf, " [wait]");
  1433.         if (plan->delayed) 
  1434.           strcat(tmpbuf, " [delay]");
  1435.         if (plan->reserve)
  1436.           strcat(tmpbuf, " [resv]");
  1437.         if (plan->asleep)
  1438.           strcat(tmpbuf, " [aslp]");
  1439.         if (plan->aicontrol)
  1440.           strcat(tmpbuf, " [dlgt]");
  1441.         if (plan->supply_is_low)
  1442.           strcat(tmpbuf, " [low]");
  1443.         if (plan->supply_alarm)
  1444.           strcat(tmpbuf, " [alrm]");
  1445.         sy += closeupspacing;
  1446.         MoveTo(sx, sy);
  1447.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1448.         if (plan->maingoal) {
  1449.             /* (should use a "goal_desc" routine) */
  1450.             strcpy(tmpbuf, goal_desig(plan->maingoal));
  1451.             sy += closeupspacing;            
  1452.             MoveTo(sx, sy);
  1453.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1454.         }
  1455.         if (plan->formation) {
  1456.             /* (should use a "goal_desc" routine) */
  1457.             strcpy(tmpbuf, goal_desig(plan->formation));
  1458.             sy += closeupspacing;            
  1459.             MoveTo(sx, sy);
  1460.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1461.         }
  1462.         for_all_tasks(plan, task) {
  1463.             task_desc(tmpbuf, task);
  1464.             sy += closeupspacing;            
  1465.             MoveTo(sx, sy);
  1466.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1467.         }
  1468.     }
  1469.     SetPort(oldport);
  1470. }
  1471.  
  1472.  
  1473. UnitCloseup *
  1474. unit_closeup_from_window(WindowPtr win)
  1475. {
  1476.     UnitCloseup *unitcloseup;
  1477.     
  1478.     for_all_unit_closeups(unitcloseup) {
  1479.         if (unitcloseup->window == win)
  1480.           return unitcloseup;
  1481.     }
  1482.     return NULL;
  1483. }
  1484.  
  1485. int
  1486. do_mouse_down_unit_closeup(UnitCloseup *unitcloseup, Point mouse, int mods)
  1487. {
  1488.     ControlHandle control;
  1489.     short part;
  1490.     WindowPtr window = unitcloseup->window;
  1491.  
  1492.     part = FindControl(mouse, window, &control);
  1493.     if (0 /* some control */) {
  1494.     } else {
  1495.         /* This just forces a redraw of the window - kind of crude. */
  1496.         update_unit_display(dside, unitcloseup->unit, TRUE);
  1497.         return TRUE;
  1498.     }
  1499. }
  1500.  
  1501. void
  1502. destroy_unit_closeup(UnitCloseup *unitcloseup)
  1503. {
  1504.     UnitCloseup *unitcloseup2;
  1505.     
  1506.     if (unitcloseuplist == unitcloseup) {
  1507.         unitcloseuplist = unitcloseup->next;
  1508.     } else {
  1509.         for_all_unit_closeups(unitcloseup2) {
  1510.             if (unitcloseup2->next == unitcloseup) {
  1511.                 unitcloseup2->next = unitcloseup->next;
  1512.             }
  1513.         }
  1514.     }
  1515.     /* (should destroy substructs) */
  1516.     free(unitcloseup);
  1517. }
  1518.  
  1519. /* History window. */
  1520.  
  1521. int maxvishistlines = 200;
  1522.  
  1523. void
  1524. create_history_window()
  1525. {
  1526.     Rect vscrollrect;
  1527.  
  1528.     historywin = GetNewWindow(wHistory, NULL, (WindowPtr) -1L);
  1529.     /* (should calc max based on size of font and height of screen) */
  1530.     histcontents = (HistEvent **) xmalloc(maxvishistlines * sizeof(HistEvent *));
  1531.     vscrollrect = historywin->portRect;
  1532.     vscrollrect.top -= 1;
  1533.     vscrollrect.bottom -= sbarwid - 1;
  1534.     vscrollrect.left = vscrollrect.right - sbarwid;
  1535.     vscrollrect.right += 1;
  1536.     histvscrollbar = NewControl(historywin, &vscrollrect, "\p", TRUE,
  1537.                                  0, 0, 100, scrollBarProc, 0L);
  1538.     firstvisevt = history->next;
  1539.     secondvisevt = firstvisevt->next;
  1540.     update_total_hist_lines();
  1541.     set_history_scrollbar();
  1542.     ShowWindow(historywin);
  1543. }
  1544.  
  1545. void
  1546. calc_history_layout()
  1547. {
  1548.     update_total_hist_lines();
  1549.     set_history_scrollbar();
  1550. }
  1551.  
  1552. void
  1553. update_total_hist_lines()
  1554. {
  1555.     HistEvent *hevt;
  1556.     
  1557.     total_history_lines = 0;
  1558.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  1559.         if (side_in_set(dside, hevt->observers)) {
  1560.             if (hevt->startdate != hevt->prev->startdate) ++total_history_lines;
  1561.             ++total_history_lines;
  1562.         }
  1563.     }
  1564. }
  1565.  
  1566. void
  1567. set_history_scrollbar()
  1568. {
  1569.     int hgt, oldmax;
  1570.     HistEvent *nexthevt;
  1571.  
  1572.     hgt = historywin->portRect.bottom - historywin->portRect.top;
  1573.     numvishistlines = (hgt - history_line_spacing - sbarwid) / history_line_spacing;
  1574.     oldmax = GetCtlMax(histvscrollbar);
  1575.     SetCtlMax(histvscrollbar, max(0, total_history_lines - numvishistlines + 1));
  1576.     HiliteControl(histvscrollbar, (numvishistlines < total_history_lines ? 0 : 255));
  1577.     /* If the thumb was at max, move it to the new max. */
  1578.     if (GetCtlValue(histvscrollbar) == oldmax) {
  1579.         SetCtlValue(histvscrollbar, GetCtlMax(histvscrollbar));
  1580.         if (GetCtlValue(histvscrollbar) == 0) {
  1581.             firstvisevt = history->next;
  1582.             secondvisevt = firstvisevt->next;
  1583.         } else {
  1584.             firstvisevt = get_nth_history_line(dside, GetCtlValue(histvscrollbar), &nexthevt);
  1585.             secondvisevt = nexthevt;
  1586.         }
  1587.     }
  1588. }
  1589.  
  1590. void
  1591. draw_history()
  1592. {
  1593.     int i, headdate;
  1594.     HistEvent *hevt;
  1595.     int numchars;
  1596.     char *datestr, hdatebuf[100];
  1597.  
  1598.     if (!active_display(dside) || historywin == nil)
  1599.       return;
  1600.     /* Build up the array of events and dates to draw. */
  1601.     numhistcontents = 0;
  1602.     hevt = firstvisevt;
  1603.     if (hevt == NULL) {
  1604.         histcontents[numhistcontents++] = NULL;
  1605.         hevt = secondvisevt;
  1606.     }
  1607.     for (; hevt != history; hevt = hevt->next) {
  1608.         if (numhistcontents >= numvishistlines)
  1609.           break;
  1610.         if (side_in_set(dside, hevt->observers)) {
  1611.             if (numhistcontents > 0
  1612.                 && histcontents[numhistcontents - 1] != NULL
  1613.                 && hevt->startdate != histcontents[numhistcontents - 1]->startdate) {
  1614.                 histcontents[numhistcontents++] = NULL;
  1615.             }
  1616.             histcontents[numhistcontents++] = hevt;
  1617.         }
  1618.     }
  1619.     /* Draw the header line. */
  1620.     MoveTo(2, 10);
  1621.     headdate = (firstvisevt ? firstvisevt : secondvisevt)->startdate; 
  1622.     /* (should be relative) */
  1623.     datestr = absolute_date_string(headdate);
  1624.     sprintf(hdatebuf, "(%s)", datestr);
  1625.     /* (should clip to drawing only visible chars) */
  1626.     numchars = strlen(hdatebuf);
  1627.     DrawText(hdatebuf, 0, numchars);
  1628.     /* Now draw each event or date. */
  1629.     for (i = 0; i < numhistcontents; ++i) {
  1630.         if (histcontents[i] != NULL) {
  1631.             draw_historical_event(histcontents[i], i);
  1632.         } else {
  1633.             draw_historical_date(histcontents[i+1], i);
  1634.         }
  1635.     }
  1636. }
  1637.  
  1638. void
  1639. draw_historical_event(HistEvent *hevt, int y)
  1640. {
  1641.     int hgt, pos, numchars;
  1642.     char hevtbuf[500];
  1643.  
  1644.     if (hevt == NULL)
  1645.       return;
  1646.     pos = history_line_spacing * y + history_top_line_height;
  1647.     hgt = historywin->portRect.bottom - historywin->portRect.top;
  1648.     /* Don't draw the line if it will intrude on the bottom scrollbar. */
  1649.     if (pos + history_line_spacing > hgt - sbarwid)
  1650.       return;
  1651.     MoveTo(20, pos);
  1652.     historical_event_desc(dside, hevt, hevtbuf);
  1653.     /* (should clip to drawing only visible chars) */
  1654.     numchars = strlen(hevtbuf);
  1655.     DrawText(hevtbuf, 0, numchars);
  1656. }
  1657.  
  1658. void
  1659. draw_historical_date(HistEvent *hevt, int y)
  1660. {
  1661.     int numchars;
  1662.     char *datestr, hdatebuf[100];
  1663.  
  1664.     if (hevt == NULL)
  1665.       return;
  1666.     MoveTo(2, history_line_spacing * y + history_top_line_height);
  1667.     /* (should be relative) */
  1668.     datestr = absolute_date_string(hevt->startdate);
  1669.     strcpy(hdatebuf, datestr);
  1670.     /* (should clip to drawing only visible chars) */
  1671.     numchars = strlen(hdatebuf);
  1672.     DrawText(hdatebuf, 0, numchars);
  1673. }
  1674.  
  1675. void
  1676. update_history_window(HistEvent *hevt)
  1677. {
  1678.     HistEvent *prevfirst, *prevsecond;
  1679.  
  1680.     prevfirst = firstvisevt;
  1681.     prevsecond = secondvisevt;
  1682.     SetPort(historywin);
  1683.     calc_history_layout();
  1684.     if (firstvisevt != prevfirst
  1685.         || secondvisevt != prevsecond
  1686.         || numvishistlines > total_history_lines) {
  1687.         force_update(historywin);
  1688.     }
  1689. }
  1690.  
  1691. static pascal void
  1692. history_scroll_fn(ControlHandle control, short code)
  1693. {
  1694.     int curvalue, maxvalue, pagesize, jump;
  1695.  
  1696.     curvalue = GetCtlValue(control);
  1697.     maxvalue = GetCtlMax(control);
  1698.     pagesize = numvishistlines;
  1699.     switch (code) {
  1700.         case inPageDown:
  1701.             jump = max(1, pagesize - 2);
  1702.             break;
  1703.         case inDownButton:
  1704.             jump = 1;
  1705.             break;
  1706.         case inPageUp:
  1707.             jump = min(-1, - (pagesize - 2));
  1708.             break;
  1709.         case inUpButton:
  1710.             jump = -1;
  1711.             break;
  1712.         default:
  1713.             jump = 0;
  1714.             break;
  1715.     }
  1716.     curvalue = max(min(curvalue + jump, maxvalue), 0);
  1717.     SetCtlValue(control, curvalue);
  1718. }
  1719.  
  1720. void
  1721. do_mouse_down_history(Point mouse, int mods)
  1722. {
  1723.     HistEvent *prevfirst, *prevsecond, *nexthevt;
  1724.     ControlHandle control;
  1725.     short part, value;
  1726.  
  1727.     if (history_scroll_proc == NULL)
  1728.       history_scroll_proc = NewControlActionProc(history_scroll_fn);
  1729.  
  1730.     part = FindControl(mouse, historywin, &control);
  1731.     if (control == histvscrollbar) {
  1732.         prevfirst = firstvisevt;
  1733.         prevsecond = secondvisevt;
  1734.         switch (part) {
  1735.             case inThumb:
  1736.                 part = TrackControl(control, mouse, NULL);
  1737.                 break;
  1738.             default:
  1739.                 part = TrackControl(control, mouse, history_scroll_proc);
  1740.                 break;
  1741.         }
  1742.         value = GetCtlValue(control);
  1743.         firstvisevt = get_nth_history_line(dside, value, &nexthevt);
  1744.         secondvisevt = nexthevt;
  1745.         if (firstvisevt != prevfirst || secondvisevt != prevsecond) {
  1746.             force_update(historywin);
  1747.         }
  1748.     } else {
  1749.         /* anything to do here? */
  1750.     }
  1751. }
  1752.  
  1753. /* Grow/shrink the history window to the given size. */
  1754.  
  1755. void
  1756. grow_history(int h, int v)
  1757. {
  1758.     EraseRect(&historywin->portRect);
  1759.     SizeWindow(historywin, h, v, 1);
  1760.     move_history_scrollbar(h, v);
  1761.     /* This will force a full redraw at the next update. */
  1762.     InvalRect(&historywin->portRect);
  1763. }                    
  1764.  
  1765. /* Zoom the history window to its best maximal size. */
  1766.  
  1767. void
  1768. zoom_history(int part)
  1769. {
  1770.     int titleh, vislinesavail;
  1771.     Rect zoomrect;
  1772.     GDHandle zoomgd;
  1773.  
  1774.     EraseRect(&historywin->portRect);
  1775.     if (part == inZoomOut) {
  1776.         if (hasColorQD) {
  1777.             zoomgd = best_zoom_screen(&historywin->portRect);
  1778.             zoomrect = (*zoomgd)->gdRect;
  1779.             if (zoomgd == GetMainDevice()) {
  1780.                 zoomrect.top += GetMBarHeight();
  1781.             }
  1782.         } else {
  1783.             /* If no Color QD, then there is only one screen. */
  1784.             zoomrect = QD(screenBits).bounds;
  1785.             zoomrect.top += GetMBarHeight();
  1786.         }
  1787.         titleh = 20; /* (should calc) */
  1788.         zoomrect.top += titleh;
  1789.         InsetRect(&zoomrect, 4, 4);
  1790.         /* If not much history, shrink the zoomed window to fit. */
  1791.         vislinesavail = (zoomrect.bottom - zoomrect.top - sbarwid) / history_line_spacing;
  1792.         update_total_hist_lines();
  1793.         if (vislinesavail > total_history_lines) {
  1794.             zoomrect.bottom = zoomrect.top + total_history_lines * history_line_spacing + sbarwid;
  1795.         }
  1796.         (*((WStateDataHandle) ((WindowPeek) historywin)->dataHandle))->stdState = zoomrect;
  1797.     }
  1798.     ZoomWindow(historywin, part, (historywin == FrontWindow()));
  1799.     move_history_scrollbar(window_width(historywin), window_height(historywin));
  1800.     /* This will force a full redraw at the next update. */
  1801.     InvalRect(&historywin->portRect);
  1802. }
  1803.  
  1804. void
  1805. move_history_scrollbar(int h, int v)
  1806. {
  1807.     MoveControl(histvscrollbar, h - sbarwid, 0);
  1808.     SizeControl(histvscrollbar, sbarwid + 1, v - sbarwid + 1);
  1809.     set_history_scrollbar();
  1810. }
  1811.  
  1812. /* notice window. */
  1813.  
  1814. /* This is the top-level access to bring up the notice window, can be called
  1815.    anywhere, anytime. */
  1816.  
  1817. void
  1818. notice_dialog()
  1819. {
  1820.     if (noticewin == nil) {
  1821.         create_notice_window();
  1822.     }
  1823.     ShowWindow(noticewin);
  1824.     SelectWindow(noticewin);
  1825. }
  1826.  
  1827. void
  1828. create_notice_window()
  1829. {
  1830.     int h, v, mainheight;
  1831.     Rect destrect, viewrect, vscrollrect, tmprect;
  1832.  
  1833.     /* Create the window, color if possible, since images may be in color. */
  1834.     if (hasColorQD) {    
  1835.         noticewin = GetNewCWindow(wNotice, NULL, (WindowPtr) -1L);
  1836.     } else {
  1837.         noticewin = GetNewWindow(wNotice, NULL, (WindowPtr) -1L);
  1838.     }
  1839.     SetPort(noticewin);
  1840.     /* All text will be in Times. */
  1841.     /* (should these be choosable?) */
  1842.     TextFont(times);
  1843.     /* Set up the notice text. */
  1844.     TextSize(14);
  1845.     h = window_width(noticewin);  v = window_height(noticewin);
  1846.     SetRect(&viewrect, 5, 5, h - sbarwid, v - sbarwid); 
  1847.     destrect = viewrect;
  1848.     notice_text = TENew(&destrect, &destrect);
  1849.     /* Set up a vertical scrollbar. */
  1850.     vscrollrect = noticewin->portRect;
  1851.     vscrollrect.top = 5;
  1852.     vscrollrect.bottom -= sbarwid - 1;
  1853.     vscrollrect.left = vscrollrect.right - sbarwid;
  1854.     vscrollrect.right += 1;
  1855.     notice_v_scrollbar =
  1856.         NewControl(noticewin, &vscrollrect, "\p", TRUE, 0, 0, 0, scrollBarProc, 0L);
  1857.     HiliteControl(notice_v_scrollbar, 0);
  1858.     add_window_menu_item("Notices", noticewin);
  1859.     if (1 /* position notices at bottom of main screen */) {
  1860.         get_main_screen_size(NULL, &mainheight);
  1861.         tmprect = noticewin->portRect;
  1862.         MoveWindow(noticewin,
  1863.                    4,
  1864.                    mainheight - (tmprect.bottom - tmprect.top) - 3,
  1865.                    FALSE);
  1866.     }
  1867. }
  1868.  
  1869. void
  1870. append_notice(char *str)
  1871. {
  1872.     /* Delete old notices. */
  1873.     if (((*notice_text)->teLength) > 30000) {
  1874.         TESetSelect(0, (*notice_text)->teLength - 30000, notice_text);
  1875.         TEDelete(notice_text);
  1876.     }
  1877. #ifdef THINK_C
  1878.     /* Hack up newlines so that TextEdit recognizes them. */
  1879.     {
  1880.         int i;
  1881.     
  1882.         for (i = 0; i < strlen(str); ++i) {
  1883.             if (str[i] == '\n')
  1884.               str[i] = '\r';
  1885.         }
  1886.     }
  1887. #endif
  1888.     TESetSelect(32767, 32767, notice_text);
  1889.     TEInsert(str, strlen(str), notice_text);
  1890.     TEInsert("\r", strlen("\r"), notice_text);
  1891.     TESetSelect(32767, 32767, notice_text);
  1892.     (*notice_text)->destRect = (*notice_text)->viewRect;
  1893.     /* Update on the screen. */
  1894.     adjust_notice_scrollbar();
  1895.     draw_notice();
  1896. }
  1897.  
  1898. void
  1899. draw_notice()
  1900. {
  1901.     Rect tmprect;
  1902.     GrafPtr oldport;
  1903.  
  1904.     GetPort(&oldport);
  1905.     SetPort(noticewin);
  1906.     SetRect(&tmprect, 5, 40, 5 + 32, 40 + 32);
  1907.     EraseRect(&tmprect);
  1908.     TextSize(14);
  1909.     TEUpdate(&(noticewin->portRect), notice_text);
  1910.     SetPort(oldport);
  1911.     adjust_notice_scrollbar();
  1912. }
  1913.  
  1914. void
  1915. adjust_notice_scrollbar()
  1916. {
  1917.     int lines, oldmax, newmax, oldvalue, newvalue;
  1918.  
  1919.     oldvalue = GetCtlValue(notice_v_scrollbar);
  1920.     oldmax = GetCtlMax(notice_v_scrollbar);
  1921.     lines = (*notice_text)->nLines;
  1922.     /* Account for a return at the end of the text. */
  1923.     if (*(*(*notice_text)->hText + (*notice_text)->teLength - 1) == 0x0d)
  1924.       ++lines;
  1925.     newmax = lines - (((*notice_text)->viewRect.bottom - (*notice_text)->viewRect.top)
  1926.                      / (*notice_text)->lineHeight);
  1927.     if (newmax < 0)
  1928.       newmax = 0;
  1929.     SetCtlMax(notice_v_scrollbar, newmax);
  1930.     if (oldvalue == oldmax) {
  1931.         /* If the thumb was at max, move it to the new max. */
  1932.         newvalue = newmax;
  1933.     } else {
  1934.         /* Otherwise adjust it proportionally. */
  1935.         newvalue = ((*notice_text)->viewRect.top - (*notice_text)->destRect.top)
  1936.                     / (*notice_text)->lineHeight;
  1937.         if (newvalue < 0)
  1938.           newvalue = 0;
  1939.         if (newvalue > newmax)
  1940.           newvalue = newmax;
  1941.     }
  1942.     SetCtlValue(notice_v_scrollbar, newvalue);
  1943.     TEScroll(0, ((*notice_text)->viewRect.top - (*notice_text)->destRect.top)
  1944.                  - (GetCtlValue(notice_v_scrollbar) * (*notice_text)->lineHeight),
  1945.              notice_text);
  1946. }
  1947.  
  1948. void
  1949. activate_notice(int activate)
  1950. {
  1951.     HiliteControl(notice_v_scrollbar, (activate ? 0 : 255));
  1952.     if (activate)
  1953.       TEActivate(notice_text);
  1954.     else
  1955.       TEDeactivate(notice_text);
  1956. }
  1957.  
  1958. static pascal void
  1959. notice_vscroll_fn(ControlHandle control, short code)
  1960. {
  1961.     int oldvalue, curvalue, minvalue, maxvalue, pagesize, jump;
  1962.  
  1963.     curvalue = GetCtlValue(control);
  1964.     minvalue = GetCtlMin(control);
  1965.     maxvalue = GetCtlMax(control);
  1966.     pagesize = ((*notice_text)->viewRect.bottom - (*notice_text)->viewRect.top) /
  1967.                 (*notice_text)->lineHeight;
  1968.     if (pagesize > 1)
  1969.       pagesize -= 1;
  1970.     switch (code) {
  1971.         case inPageDown:
  1972.             jump = pagesize;
  1973.             break;
  1974.         case inDownButton:
  1975.             jump = 1;
  1976.             break;
  1977.         case inPageUp:
  1978.             jump = - pagesize;
  1979.             break;
  1980.         case inUpButton:
  1981.             jump = -1;
  1982.             break;
  1983.         default:
  1984.             jump = 0;
  1985.             break;
  1986.     }
  1987.     oldvalue = curvalue;
  1988.     curvalue = max(min(curvalue + jump, maxvalue), minvalue);
  1989.     SetCtlValue(control, curvalue);
  1990.     /* Calculate the actual jump and use it to adjust the text. */
  1991.     jump = curvalue - oldvalue;
  1992.     if (jump != 0)
  1993.       TEScroll(0, - jump * (*notice_text)->lineHeight, notice_text);
  1994. }
  1995.  
  1996. /* Respond to an event occurring in the notice window. */
  1997.  
  1998. void
  1999. do_mouse_down_notice(Point mouse, int mods)
  2000. {
  2001.     ControlHandle control;
  2002.     short part, value;
  2003.  
  2004.     if (notice_vscroll_proc == NULL)
  2005.       notice_vscroll_proc = NewControlActionProc(notice_vscroll_fn);
  2006.  
  2007.     part = FindControl(mouse, noticewin, &control);
  2008.     if (control == notice_v_scrollbar) {
  2009.         if (part != 0) {
  2010.             switch (part) {
  2011.                 case inPageDown:
  2012.                 case inDownButton:
  2013.                 case inPageUp:
  2014.                 case inUpButton:
  2015.                     value = TrackControl(control, mouse, notice_vscroll_proc);
  2016.                     break;
  2017.                 case inThumb:
  2018.                     value = GetCtlValue(control);
  2019.                     if ((part = TrackControl(control, mouse, nil)) != 0) {
  2020.                         value -= GetCtlValue(control);
  2021.                         if (value != 0) {
  2022.                             TEScroll(0, value * (*notice_text)->lineHeight, notice_text);
  2023.                         }
  2024.                     }
  2025.                     break;
  2026.             }
  2027.         }
  2028.     } else if (PtInRect(mouse, &((*notice_text)->viewRect))) {
  2029.         TEClick(mouse, 0, notice_text);
  2030.     }
  2031. }
  2032.  
  2033. void
  2034. grow_notice(int h, int v)
  2035. {
  2036.     EraseRect(¬icewin->portRect);
  2037.     SizeWindow(noticewin, h, v, 1);
  2038.     MoveControl(notice_v_scrollbar, h - sbarwid, 5);
  2039.     SizeControl(notice_v_scrollbar, sbarwid + 1, v - 5 - sbarwid + 1);
  2040.     (*notice_text)->viewRect.right = h - sbarwid;
  2041.     (*notice_text)->viewRect.bottom = v - sbarwid;
  2042.     (*notice_text)->destRect.right = h - sbarwid;
  2043.     TECalText(notice_text);
  2044.     InvalRect(¬icewin->portRect);
  2045. }                    
  2046.  
  2047. void
  2048. zoom_notice(int part)
  2049. {
  2050.     int titleh, h, v;
  2051.     Rect zoomrect;
  2052.     GDHandle gd, zoomgd;
  2053.  
  2054.     EraseRect(¬icewin->portRect);
  2055.     if (part == inZoomOut) {
  2056.         if (hasColorQD) {
  2057.             zoomgd = best_zoom_screen(¬icewin->portRect);
  2058.             zoomrect = (*zoomgd)->gdRect;
  2059.             if (zoomgd == GetMainDevice()) {
  2060.                 zoomrect.top += GetMBarHeight();
  2061.             }
  2062.             InsetRect(&zoomrect, 3, 3);
  2063.         } else {
  2064.             /* If no Color QD, then there is only the one screen. */
  2065.             zoomrect = QD(screenBits).bounds;
  2066.             zoomrect.top += GetMBarHeight();
  2067.             InsetRect(&zoomrect, 4, 4);
  2068.         }
  2069.         titleh = 20; /* (should calc) */
  2070.         zoomrect.top += titleh;
  2071.         (*((WStateDataHandle) ((WindowPeek) noticewin)->dataHandle))->stdState = zoomrect;
  2072.     }
  2073.     ZoomWindow(noticewin, part, (noticewin == FrontWindow()));
  2074.     h = window_width(noticewin);  v = window_height(noticewin);
  2075.     MoveControl(notice_v_scrollbar, h - sbarwid, 0);
  2076.     SizeControl(notice_v_scrollbar, sbarwid + 1, v - sbarwid + 1);
  2077.     adjust_notice_scrollbar();
  2078.     (*notice_text)->viewRect.right = h - sbarwid;
  2079.     (*notice_text)->viewRect.bottom = v - sbarwid;
  2080.     (*notice_text)->destRect.right = h - sbarwid;
  2081.     TECalText(notice_text);
  2082.     /* This will force a full redraw at the next update. */
  2083.     InvalRect(¬icewin->portRect);
  2084. }
  2085.  
  2086. /* scores window. */
  2087.  
  2088. /* This is the top-level access to bring up the scores window, can be called
  2089.    anywhere, anytime. */
  2090.  
  2091. void
  2092. scores_dialog()
  2093. {
  2094.     extern char *get_scores(Side *side);
  2095.  
  2096.     if (scoreswin == nil) {
  2097.         create_scores_window();
  2098.         append_scores(get_scores(dside));
  2099.     }
  2100.     ShowWindow(scoreswin);
  2101.     SelectWindow(scoreswin);
  2102. }
  2103.  
  2104. void
  2105. create_scores_window()
  2106. {
  2107.     int h, v;
  2108.     Rect destrect, viewrect, vscrollrect;
  2109.  
  2110.     /* Create the window, color if possible, since images may be in color. */
  2111.     if (hasColorQD) {    
  2112.         scoreswin = GetNewCWindow(wScores, NULL, (WindowPtr) -1L);
  2113.     } else {
  2114.         scoreswin = GetNewWindow(wScores, NULL, (WindowPtr) -1L);
  2115.     }
  2116.     SetPort(scoreswin);
  2117.     /* All text will be in Times. */
  2118.     /* (should these be choosable?) */
  2119.     TextFont(times);
  2120.     /* Set up the scores text. */
  2121.     TextSize(14);
  2122.     h = window_width(scoreswin);  v = window_height(scoreswin);
  2123.     SetRect(&viewrect, 5, 5, h - sbarwid, v - sbarwid); 
  2124.     destrect = viewrect;
  2125.     scores_text = TENew(&destrect, &destrect);
  2126.     /* Set up a vertical scrollbar. */
  2127.     vscrollrect = scoreswin->portRect;
  2128.     vscrollrect.top = 5;
  2129.     vscrollrect.bottom -= sbarwid - 1;
  2130.     vscrollrect.left = vscrollrect.right - sbarwid;
  2131.     vscrollrect.right += 1;
  2132.     scores_v_scrollbar =
  2133.         NewControl(scoreswin, &vscrollrect, "\p", TRUE, 0, 0, 0, scrollBarProc, 0L);
  2134.     HiliteControl(scores_v_scrollbar, 0);
  2135.     add_window_menu_item("scores", scoreswin);
  2136. }
  2137.  
  2138. void
  2139. append_scores(char *str)
  2140. {
  2141.     /* Delete old scoress. */
  2142.     if (((*scores_text)->teLength) > 30000) {
  2143.         TESetSelect(0, (*scores_text)->teLength - 30000, scores_text);
  2144.         TEDelete(scores_text);
  2145.     }
  2146. #ifdef THINK_C
  2147.     /* Hack up newlines so that TextEdit recognizes them. */
  2148.     {
  2149.         int i;
  2150.     
  2151.         for (i = 0; i < strlen(str); ++i) {
  2152.             if (str[i] == '\n')
  2153.               str[i] = '\r';
  2154.         }
  2155.     }
  2156. #endif
  2157.     TESetSelect(32767, 32767, scores_text);
  2158.     TEInsert(str, strlen(str), scores_text);
  2159.     TEInsert("\r", strlen("\r"), scores_text);
  2160.     TESetSelect(32767, 32767, scores_text);
  2161.     (*scores_text)->destRect = (*scores_text)->viewRect;
  2162.     /* Update on the screen. */
  2163.     adjust_scores_scrollbar();
  2164.     draw_scores();
  2165. }
  2166.  
  2167. void
  2168. draw_scores()
  2169. {
  2170.     Rect tmprect;
  2171.     GrafPtr oldport;
  2172.  
  2173.     GetPort(&oldport);
  2174.     SetPort(scoreswin);
  2175.     SetRect(&tmprect, 5, 40, 5 + 32, 40 + 32);
  2176.     EraseRect(&tmprect);
  2177.     TextSize(14);
  2178.     TEUpdate(&(scoreswin->portRect), scores_text);
  2179.     SetPort(oldport);
  2180.     adjust_scores_scrollbar();
  2181. }
  2182.  
  2183. void
  2184. adjust_scores_scrollbar()
  2185. {
  2186.     int lines, oldmax, newmax, oldvalue, newvalue;
  2187.  
  2188.     oldvalue = GetCtlValue(scores_v_scrollbar);
  2189.     oldmax = GetCtlMax(scores_v_scrollbar);
  2190.     lines = (*scores_text)->nLines;
  2191.     /* Account for a return at the end of the text. */
  2192.     if (*(*(*scores_text)->hText + (*scores_text)->teLength - 1) == 0x0d)
  2193.       ++lines;
  2194.     newmax = lines - (((*scores_text)->viewRect.bottom - (*scores_text)->viewRect.top)
  2195.                      / (*scores_text)->lineHeight);
  2196.     if (newmax < 0)
  2197.       newmax = 0;
  2198.     SetCtlMax(scores_v_scrollbar, newmax);
  2199.     if (oldvalue == oldmax) {
  2200.         /* If the thumb was at max, move it to the new max. */
  2201.         newvalue = newmax;
  2202.     } else {
  2203.         /* Otherwise adjust it proportionally. */
  2204.         newvalue = ((*scores_text)->viewRect.top - (*scores_text)->destRect.top)
  2205.                     / (*scores_text)->lineHeight;
  2206.         if (newvalue < 0)
  2207.           newvalue = 0;
  2208.         if (newvalue > newmax)
  2209.           newvalue = newmax;
  2210.     }
  2211.     SetCtlValue(scores_v_scrollbar, newvalue);
  2212.     TEScroll(0, ((*scores_text)->viewRect.top - (*scores_text)->destRect.top)
  2213.                  - (GetCtlValue(scores_v_scrollbar) * (*scores_text)->lineHeight),
  2214.              scores_text);
  2215. }
  2216.  
  2217. void
  2218. activate_scores(int activate)
  2219. {
  2220.     HiliteControl(scores_v_scrollbar, (activate ? 0 : 255));
  2221.     if (activate)
  2222.       TEActivate(scores_text);
  2223.     else
  2224.       TEDeactivate(scores_text);
  2225. }
  2226.  
  2227. static pascal void
  2228. scores_vscroll_fn(ControlHandle control, short code)
  2229. {
  2230.     int oldvalue, curvalue, minvalue, maxvalue, pagesize, jump;
  2231.  
  2232.     curvalue = GetCtlValue(control);
  2233.     minvalue = GetCtlMin(control);
  2234.     maxvalue = GetCtlMax(control);
  2235.     pagesize = ((*scores_text)->viewRect.bottom - (*scores_text)->viewRect.top) /
  2236.                 (*scores_text)->lineHeight;
  2237.     if (pagesize > 1)
  2238.       pagesize -= 1;
  2239.     switch (code) {
  2240.         case inPageDown:
  2241.             jump = pagesize;
  2242.             break;
  2243.         case inDownButton:
  2244.             jump = 1;
  2245.             break;
  2246.         case inPageUp:
  2247.             jump = - pagesize;
  2248.             break;
  2249.         case inUpButton:
  2250.             jump = -1;
  2251.             break;
  2252.         default:
  2253.             jump = 0;
  2254.             break;
  2255.     }
  2256.     oldvalue = curvalue;
  2257.     curvalue = max(min(curvalue + jump, maxvalue), minvalue);
  2258.     SetCtlValue(control, curvalue);
  2259.     /* Calculate the actual jump and use it to adjust the text. */
  2260.     jump = curvalue - oldvalue;
  2261.     if (jump != 0)
  2262.       TEScroll(0, - jump * (*scores_text)->lineHeight, scores_text);
  2263. }
  2264.  
  2265. /* Respond to an event occurring in the scores window. */
  2266.  
  2267. void
  2268. do_mouse_down_scores(Point mouse, int mods)
  2269. {
  2270.     ControlHandle control;
  2271.     short part, value;
  2272.  
  2273.     if (scores_vscroll_proc == NULL)
  2274.       scores_vscroll_proc = NewControlActionProc(scores_vscroll_fn);
  2275.  
  2276.     part = FindControl(mouse, scoreswin, &control);
  2277.     if (control == scores_v_scrollbar) {
  2278.         if (part != 0) {
  2279.             switch (part) {
  2280.                 case inPageDown:
  2281.                 case inDownButton:
  2282.                 case inPageUp:
  2283.                 case inUpButton:
  2284.                     value = TrackControl(control, mouse, scores_vscroll_proc);
  2285.                     break;
  2286.                 case inThumb:
  2287.                     value = GetCtlValue(control);
  2288.                     if ((part = TrackControl(control, mouse, nil)) != 0) {
  2289.                         value -= GetCtlValue(control);
  2290.                         if (value != 0) {
  2291.                             TEScroll(0, value * (*scores_text)->lineHeight, scores_text);
  2292.                         }
  2293.                     }
  2294.                     break;
  2295.             }
  2296.         }
  2297.     } else if (PtInRect(mouse, &((*scores_text)->viewRect))) {
  2298.         TEClick(mouse, 0, scores_text);
  2299.     }
  2300. }
  2301.  
  2302. void
  2303. grow_scores(int h, int v)
  2304. {
  2305.     EraseRect(&scoreswin->portRect);
  2306.     SizeWindow(scoreswin, h, v, 1);
  2307.     MoveControl(scores_v_scrollbar, h - sbarwid, 5);
  2308.     SizeControl(scores_v_scrollbar, sbarwid + 1, v - 5 - sbarwid + 1);
  2309.     (*scores_text)->viewRect.right = h - sbarwid;
  2310.     (*scores_text)->viewRect.bottom = v - sbarwid;
  2311.     (*scores_text)->destRect.right = h - sbarwid;
  2312.     TECalText(scores_text);
  2313.     InvalRect(&scoreswin->portRect);
  2314. }                    
  2315.  
  2316. void
  2317. zoom_scores(int part)
  2318. {
  2319.     int titleh, h, v;
  2320.     Rect zoomrect;
  2321.     GDHandle gd, zoomgd;
  2322.  
  2323.     EraseRect(&scoreswin->portRect);
  2324.     if (part == inZoomOut) {
  2325.         if (hasColorQD) {
  2326.             zoomgd = best_zoom_screen(&scoreswin->portRect);
  2327.             zoomrect = (*zoomgd)->gdRect;
  2328.             if (zoomgd == GetMainDevice()) {
  2329.                 zoomrect.top += GetMBarHeight();
  2330.             }
  2331.             InsetRect(&zoomrect, 3, 3);
  2332.         } else {
  2333.             /* If no Color QD, then there is only the one screen. */
  2334.             zoomrect = QD(screenBits).bounds;
  2335.             zoomrect.top += GetMBarHeight();
  2336.             InsetRect(&zoomrect, 4, 4);
  2337.         }
  2338.         titleh = 20; /* (should calc) */
  2339.         zoomrect.top += titleh;
  2340.         (*((WStateDataHandle) ((WindowPeek) scoreswin)->dataHandle))->stdState = zoomrect;
  2341.     }
  2342.     ZoomWindow(scoreswin, part, (scoreswin == FrontWindow()));
  2343.     h = window_width(scoreswin);  v = window_height(scoreswin);
  2344.     MoveControl(scores_v_scrollbar, h - sbarwid, 0);
  2345.     SizeControl(scores_v_scrollbar, sbarwid + 1, v - sbarwid + 1);
  2346.     adjust_scores_scrollbar();
  2347.     (*scores_text)->viewRect.right = h - sbarwid;
  2348.     (*scores_text)->viewRect.bottom = v - sbarwid;
  2349.     (*scores_text)->destRect.right = h - sbarwid;
  2350.     TECalText(scores_text);
  2351.     /* This will force a full redraw at the next update. */
  2352.     InvalRect(&scoreswin->portRect);
  2353. }
  2354.