OSDN Git Service

shrink mine
[nethackexpress/trunk.git] / sys / mac / macwin.c
1 /*      SCCS Id: @(#)macwin.c   3.4     1996/01/15      */
2 /* Copyright (c) Jon W{tte, Hao-Yang Wang, Jonathan Handler 1992. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "hack.h"
6 #include "func_tab.h"
7 #include "macwin.h"
8 #include "mactty.h"
9 #include "wintty.h"
10
11 #if !TARGET_API_MAC_CARBON
12 #include <LowMem.h>
13 #include <AppleEvents.h>
14 #include <Gestalt.h>
15 #include <TextUtils.h>
16 #include <DiskInit.h>
17 #include <ControlDefinitions.h>
18 #endif
19
20 NhWindow *theWindows = (NhWindow *) 0;
21 Cursor qdarrow;
22
23 /* Borrowed from the Mac tty port */
24 extern WindowPtr _mt_window;
25
26 /* Some useful #defines for the scroll bar width and height */
27 #define         SBARWIDTH       15
28 #define         SBARHEIGHT      15
29
30 /*
31  * We put a TE on the message window for the "top line" queries.
32  * top_line is the TE that holds both the query and the user's
33  * response.  The first topl_query_len characters in top_line are
34  * the query, the rests are the response.  topl_resp is the valid
35  * response to a yn query, while topl_resp[topl_def_idx] is the
36  * default response to a yn query.
37  */
38 static TEHandle top_line = (TEHandle) nil;
39 static int      topl_query_len;
40 static int      topl_def_idx = -1;
41 static char     topl_resp[10] = "";
42
43 #define CHAR_ANY '\n'
44
45 /*
46  * inSelect means we have a menu window up for selection or
47  * something similar. It makes the window with win number ==
48  * inSelect a movable modal (unfortunately without the border)
49  * and clicking the close box forces an RET into the key
50  * buffer. Don't forget to set inSelect to WIN_ERR when you're
51  * done...
52  */
53 static winid inSelect = WIN_ERR;
54
55 /*
56  * The key queue ring buffer where Read is where to take from, 
57  * Write is where next char goes and count is queue depth.
58  */
59 static unsigned char keyQueue [QUEUE_LEN];
60 static int keyQueueRead = 0,
61         keyQueueWrite = 0,
62         keyQueueCount = 0;
63
64 static Boolean gClickedToMove = 0;      /* For ObscureCursor */
65
66 static Point clicked_pos;       /* For nh_poskey */
67 static int clicked_mod;
68 static Boolean cursor_locked = false;
69
70 static ControlActionUPP MoveScrollUPP;          /* scrolling callback, init'ed in InitMac */
71
72 void
73 lock_mouse_cursor(Boolean new_cursor_locked) {
74         cursor_locked = new_cursor_locked;
75 }
76
77
78 /*
79  * Add key to input queue, force means flush left and replace if full
80  */
81 void
82 AddToKeyQueue (unsigned char ch, Boolean force) {
83         if (keyQueueCount < QUEUE_LEN) {
84                 keyQueue [keyQueueWrite++] = ch;
85                 keyQueueCount++;
86         }
87         else if (force) {
88                 keyQueue [keyQueueWrite++] = ch;
89                 keyQueueRead++;
90                 if (keyQueueRead >= QUEUE_LEN)
91                         keyQueueRead = 0;
92                 keyQueueCount = QUEUE_LEN;
93         }
94         if (keyQueueWrite >= QUEUE_LEN)
95                 keyQueueWrite = 0;
96 }
97
98
99 /*
100  * Get key from queue
101  */
102 unsigned char
103 GetFromKeyQueue (void) {
104         unsigned char ret;
105
106         if (keyQueueCount) {
107                 ret = keyQueue [keyQueueRead++];
108                 keyQueueCount--;
109                 if (keyQueueRead >= QUEUE_LEN)
110                         keyQueueRead = 0;
111         }
112         else
113                 ret = 0;
114         return ret;
115 }
116
117
118 /*
119  * Cursor movement
120  */
121 static RgnHandle gMouseRgn = (RgnHandle) 0;
122
123 /*
124  * _Gestalt madness - we rely heavily on the _Gestalt glue, since we
125  * don't check for the trap...
126  */
127 MacFlags macFlags;
128
129 /*
130  * The screen layouts on the small 512x342 screen need special cares.
131  */
132 Boolean small_screen = 0;
133
134 #ifdef NHW_BASE
135 # undef NHW_BASE
136 #endif
137 #define NHW_BASE 0
138
139 static int FDECL(filter_scroll_key,(const int, NhWindow *));
140
141 static void FDECL(GeneralKey, (EventRecord *, WindowPtr));
142 static void FDECL(macKeyMenu, (EventRecord *, WindowPtr));
143 static void FDECL(macKeyText, (EventRecord *, WindowPtr));
144
145 static void FDECL(macClickMessage, (EventRecord *, WindowPtr));
146 static void FDECL(macClickTerm, (EventRecord *, WindowPtr));
147 static void FDECL(macClickMenu, (EventRecord *, WindowPtr));
148 static void FDECL(macClickText, (EventRecord *, WindowPtr));
149
150 static short FDECL(macDoNull, (EventRecord *, WindowPtr));
151 static short FDECL(macUpdateMessage, (EventRecord *, WindowPtr));
152 static short FDECL(macUpdateMenu, (EventRecord *, WindowPtr));
153 static short FDECL(GeneralUpdate, (EventRecord *, WindowPtr));
154
155 static void FDECL(macCursorTerm, (EventRecord *, WindowPtr, RgnHandle));
156 static void FDECL(GeneralCursor, (EventRecord *, WindowPtr, RgnHandle));
157
158 static void FDECL(DoScrollBar,(Point, short, ControlHandle, NhWindow *));
159 static pascal void FDECL(MoveScrollBar, (ControlHandle, short));
160
161 typedef void (*CbFunc) (EventRecord *, WindowPtr);
162 typedef short (*CbUpFunc) (EventRecord *, WindowPtr);
163 typedef void (*CbCursFunc) (EventRecord *, WindowPtr, RgnHandle);
164
165 #define NUM_FUNCS 6
166 static const CbFunc winKeyFuncs [NUM_FUNCS] = {
167         GeneralKey, GeneralKey, GeneralKey, GeneralKey, macKeyMenu, macKeyText
168 };
169
170 static const CbFunc winClickFuncs [NUM_FUNCS] = {
171         (CbFunc)macDoNull, macClickMessage, macClickTerm, macClickTerm, macClickMenu,
172         macClickText
173 };
174
175 static const CbUpFunc winUpdateFuncs [NUM_FUNCS] = {
176         macDoNull, macUpdateMessage, image_tty, image_tty,
177         macUpdateMenu, GeneralUpdate
178 };
179
180 static const CbCursFunc winCursorFuncs [NUM_FUNCS] = {
181         (CbCursFunc) macDoNull, GeneralCursor, macCursorTerm, macCursorTerm,
182         GeneralCursor, GeneralCursor
183 };
184
185
186 static NhWindow *
187 GetNhWin(WindowPtr mac_win) {
188         if (mac_win == _mt_window)      /* term window is still maintained by both systems, and */
189                 return theWindows;              /* WRefCon still refers to tty struct, so we have to map it */
190         else {
191                 NhWindow *aWin = (NhWindow *)GetWRefCon (mac_win);
192                 if (aWin >= theWindows && aWin < &theWindows[NUM_MACWINDOWS])
193                         return aWin;
194         }
195         return ((NhWindow *) nil);
196 }
197
198
199 Boolean CheckNhWin (WindowPtr mac_win) {
200         return GetNhWin (mac_win) != nil;
201 }
202
203
204 static pascal OSErr
205 AppleEventHandler (const AppleEvent* inAppleEvent, AppleEvent* outAEReply, long inRefCon) {
206 #if defined(__SC__) || defined(__MRC__)
207 # pragma unused(outAEReply,inRefCon)
208 #endif
209         Size     actualSize;
210         DescType typeCode;
211         AEEventID EventID;
212         OSErr    err;
213
214         /* Get Event ID */
215         err = AEGetAttributePtr (inAppleEvent, keyEventIDAttr, typeType, &typeCode,
216                                                                 &EventID, sizeof (EventID), &actualSize);
217         if (err == noErr) {
218                 switch (EventID) {
219                         default :
220                         case kAEOpenApplication :
221                                 macFlags.gotOpen = 1;
222                                 /* fall through */
223                         case kAEPrintDocuments :
224                                 err = errAEEventNotHandled;
225                                 break;
226                         case kAEQuitApplication :
227                                 /* Flush key queue */
228                                 keyQueueCount = keyQueueWrite = keyQueueRead = 0;
229                                 AddToKeyQueue ('S', 1);
230                                 break;
231                         case kAEOpenDocuments : {
232                                 FSSpec      fss;
233                                 FInfo fndrInfo;
234                                 AEKeyword   keywd;
235                                 AEDescList  docList;
236                                 long     index, itemsInList;
237
238                                 if((err = AEGetParamDesc(inAppleEvent, keyDirectObject, typeAEList, &docList)) != noErr ||
239                                         (err = AECountItems(&docList, &itemsInList)) != noErr){
240                                         if (err == errAEDescNotFound)
241                                                 itemsInList = 0;
242                                         else
243                                                 break;
244                                 }
245
246                                 for(index = 1; index <= itemsInList; index++){
247                                         err = AEGetNthPtr(&docList, index, typeFSS, &keywd, &typeCode, (Ptr)&fss,
248                                                                 sizeof(FSSpec), &actualSize);
249                                         if(noErr != err)
250                                                 break;
251
252                                         err = FSpGetFInfo (&fss, &fndrInfo);
253                                         if (noErr != err)
254                                                 break;
255
256                                         if (fndrInfo.fdType != SAVE_TYPE) 
257                                                 continue;       /* only look at save files */
258
259                                         process_openfile (fss.vRefNum, fss.parID, fss.name, fndrInfo.fdType);
260                                         if (macFlags.gotOpen)
261                                                 break;  /* got our save file */
262                                 }
263                                 err = AEDisposeDesc(&docList);
264                                 break;
265                         }
266                 }
267         }                       
268
269         /* Check to see if all required parameters for this type of event are present */
270         if (err == noErr) {
271                 err = AEGetAttributePtr (inAppleEvent, keyMissedKeywordAttr, 
272                                                                 typeWildCard, &typeCode, NULL, 0, &actualSize);
273                 if (err == errAEDescNotFound)                                                                                    
274                         err = noErr;            /* got all the required parameters */
275                 else if (err == noErr)  /* missed a required parameter */
276                         err = errAEEventNotHandled;
277         }
278
279         return err;
280 }
281
282
283 short win_fonts [NHW_TEXT + 1];
284
285 void
286 InitMac(void) {
287         short i;
288         long l;
289         Str255 volName;
290
291
292 #if !TARGET_API_MAC_CARBON
293         if (LMGetDefltStack() < 50 * 1024L) {
294                 SetApplLimit ((void *) ((long) LMGetCurStackBase() - (50 * 1024L)));
295         }
296         MaxApplZone ();
297         for (i = 0; i < 5; i ++)
298                 MoreMasters ();
299
300         InitGraf (&qd.thePort);
301         InitFonts ();
302         InitWindows ();
303         InitMenus ();
304         InitDialogs (0L);
305         TEInit ();
306 #endif
307
308         memset (&macFlags, 0, sizeof(macFlags));
309         if (!Gestalt (gestaltOSAttr, & l)) {
310                 macFlags.processes = (l & (1 << gestaltLaunchControl)) ? 1 : 0;
311                 macFlags.tempMem = (l & (1 << gestaltRealTempMemory)) ? 1 : 0;
312                 macFlags.hasDebugger = (l & (1 << gestaltSysDebuggerSupport)) ? 1 : 0;
313         }
314         if (!Gestalt (gestaltQuickdrawVersion, & l))
315                 macFlags.color = (l >= gestalt8BitQD) ? 1 : 0;
316
317         if (!Gestalt (gestaltFindFolderAttr, & l))
318                 macFlags.folders = (l & (1 << gestaltFindFolderPresent)) ? 1 : 0;
319                 
320         if (!Gestalt (gestaltHelpMgrAttr, & l))
321                 macFlags.help = (l & (1 << gestaltHelpMgrPresent)) ? 1 : 0;
322
323         if (!Gestalt (gestaltFSAttr, & l))
324                 macFlags.fsSpec = (l & (1 << gestaltHasFSSpecCalls)) ? 1 : 0;
325
326         if (!Gestalt (gestaltFontMgrAttr, & l))
327                 macFlags.trueType = (l & (1 << gestaltOutlineFonts)) ? 1 : 0;
328
329         if (!Gestalt (gestaltAUXVersion, & l))
330                 macFlags.aux = (l >= 0x200) ? 1 : 0;
331
332         if (!Gestalt (gestaltAliasMgrAttr, & l))
333                 macFlags.alias = (l & (1 << gestaltAliasMgrPresent)) ? 1 : 0;
334
335         if (!Gestalt (gestaltStandardFileAttr, & l))
336                 macFlags.standardFile = (l & (1 << gestaltStandardFile58)) ? 1 : 0;
337
338         gMouseRgn = NewRgn ();
339         InitCursor();
340         GetQDGlobalsArrow(&qdarrow);
341         ObscureCursor ();
342         
343         MoveScrollUPP = NewControlActionUPP(MoveScrollBar);
344
345         /* Set up base fonts for all window types */
346         GetFNum ("\pHackFont", &i);
347         if (i == 0)
348                 i = kFontIDMonaco;
349         win_fonts [NHW_BASE] = win_fonts [NHW_MAP] = win_fonts [NHW_STATUS] = i;
350         GetFNum ("\pPSHackFont", &i);
351         if (i == 0)
352                 i = kFontIDGeneva;
353         win_fonts [NHW_MESSAGE] = i;
354         win_fonts [NHW_TEXT] = kFontIDGeneva;
355
356         macFlags.hasAE = 0;
357         if(!Gestalt(gestaltAppleEventsAttr, &l) && (l & (1L << gestaltAppleEventsPresent))){
358                 if (AEInstallEventHandler (kCoreEventClass, typeWildCard,
359                                                         NewAEEventHandlerUPP(AppleEventHandler),
360                                                         0,
361                                                         FALSE) == noErr)
362                         macFlags.hasAE = 1;
363         }
364
365 #if TARGET_API_MAC_CARBON
366         HGetVol(volName, &theDirs.dataRefNum, &theDirs.dataDirID);
367 #else
368         /*
369          * We should try to get this data from a rsrc, in the profile file
370          * the user double-clicked...  This data should be saved with the
371          * save file in the resource fork, AND be saveable in "stationary"
372          */
373         GetVol (volName, &theDirs.dataRefNum );
374         GetWDInfo (theDirs.dataRefNum, &theDirs.dataRefNum, &theDirs.dataDirID, &l);
375 #endif
376         if (volName [0] > 31) volName [0] = 31;
377         for (l = 1; l <= volName [0]; l++) {
378                 if (volName [l] == ':') {
379                         volName [l] = 0;
380                         volName [0] = l - 1;
381                         break;
382                 }
383         }
384         BlockMove (volName, theDirs.dataName, l);
385         BlockMove (volName, theDirs.saveName, l);
386         BlockMove (volName, theDirs.levelName, l);
387         theDirs.saveRefNum = theDirs.levelRefNum = theDirs.dataRefNum;
388         theDirs.saveDirID = theDirs.levelDirID = theDirs.dataDirID;
389
390         /* Create the "record" file, if necessary */
391         check_recordfile("");
392         return;
393 }
394
395
396 /*
397  * Change default window fonts.
398  */
399 short
400 set_tty_font_name (int window_type, char *font_name) {
401         short fnum;
402         Str255 new_font;
403         
404         if (window_type < NHW_BASE || window_type > NHW_TEXT)
405                 return general_failure;
406                 
407         C2P (font_name, new_font);
408         GetFNum (new_font, &(fnum));
409         if (!fnum)
410                 return general_failure;
411         win_fonts [window_type] = fnum;
412         return noErr;
413 }
414
415
416 static void
417 DrawScrollbar (NhWindow *aWin)
418 {
419         WindowPtr theWindow = aWin->its_window;
420         Rect crect, wrect;
421         Boolean vis;
422         short val, lin, win_height;
423
424
425         if (!aWin->scrollBar)
426                 return;
427         GetControlBounds(aWin->scrollBar, &crect);
428         GetWindowBounds(aWin->its_window, kWindowContentRgn, &wrect);
429         OffsetRect(&wrect, -wrect.left, -wrect.top);
430         win_height = wrect.bottom - wrect.top;
431
432         if (crect.top != wrect.top - 1 ||
433                  crect.left != wrect.right - SBARWIDTH) {
434                 MoveControl (aWin->scrollBar, wrect.right - SBARWIDTH, wrect.top - 1);
435         }
436         if (crect.bottom != wrect.bottom - SBARHEIGHT ||
437                  crect.right != wrect.right + 1) {
438                 SizeControl (aWin->scrollBar, SBARWIDTH+1, win_height - SBARHEIGHT + 2);
439         }
440         vis = (win_height > (50 + SBARHEIGHT));
441         if (vis != IsControlVisible(aWin->scrollBar)) {
442                 /* current status != control */
443                 if (vis)/* if visible, show */
444                         ShowControl (aWin->scrollBar);
445                 else    /* else hide */
446                         HideControl (aWin->scrollBar);
447         }
448         lin = aWin->y_size;
449         if (aWin == theWindows + WIN_MESSAGE) {
450                 /* calculate how big scroll bar is for message window */
451                 lin -= (win_height - SBARHEIGHT) / aWin->row_height;
452                 if (lin < 0)
453                         lin = 0;                
454                 val = 0;                        /* always have message scrollbar active */
455         }
456         else {
457                 /* calculate how big scroll bar is for other windows */
458                 lin -= win_height / aWin->row_height;
459                 if (lin < 0)
460                         lin = 0;
461                 if (lin)        val = 0;        /* if there are 1+ screen lines, activate scrollbar */
462                 else            val = 255;      /* else grey it out */
463         }
464         SetControlMaximum (aWin->scrollBar, lin);
465         HiliteControl (aWin->scrollBar, val);
466         val = GetControlValue (aWin->scrollBar);
467         if (val != aWin->scrollPos) {
468                 InvalWindowRect(theWindow, &wrect);
469                 aWin->scrollPos = val;
470         }
471 }
472
473
474 #define MAX_HEIGHT 100
475 #define MIN_HEIGHT 50
476 #define MIN_WIDTH 300
477
478 /*
479  * This function could be overloaded with any amount of intelligence...
480  */
481 int
482 SanePositions (void)
483 {
484 #if TARGET_API_MAC_CARBON
485         ConstrainWindowToScreen(_mt_window, kWindowStructureRgn,
486                 kWindowConstrainMoveRegardlessOfFit, NULL, NULL);
487 #else
488         short left, top, width, height;
489         int ix, numText = 0, numMenu = 0;
490         int mbar_height = GetMBarHeight();
491         BitMap qbitmap;
492         Rect screenArea;
493         WindowPtr theWindow;
494         NhWindow *nhWin;
495
496
497         screenArea = GetQDGlobalsScreenBits(&qbitmap)->bounds;
498         OffsetRect (&screenArea, - screenArea.left, - screenArea.top);
499
500 /* Map Window */
501         height = _mt_window->portRect.bottom - _mt_window->portRect.top;
502         width = _mt_window->portRect.right - _mt_window->portRect.left;
503
504         if (!RetrievePosition (kMapWindow, &top, &left)) {
505                 top = mbar_height + (small_screen ? 2 : 20);
506                 left = (screenArea.right - width) / 2;
507         }
508         MoveWindow (_mt_window, left, top, 1);
509
510 /* Message Window */
511         if (!RetrievePosition (kMessageWindow, &top, &left)) {
512                 top += height;
513                 if (!small_screen)
514                         top += 20;
515         }
516
517         if (!RetrieveSize (kMessageWindow, top, left, &height, &width)) {
518                 height = screenArea.bottom - top - (small_screen ? 2-SBARHEIGHT : 2);
519                 if (height > MAX_HEIGHT) {
520                         height = MAX_HEIGHT;
521                 } else if (height < MIN_HEIGHT) {
522                         height = MIN_HEIGHT;
523                         width = MIN_WIDTH;
524                         left = screenArea.right - width;
525                         top = screenArea.bottom - MIN_HEIGHT;
526                 }
527         }
528
529 /* Move these windows */
530         nhWin = theWindows + WIN_MESSAGE;
531         theWindow = nhWin->its_window;
532
533         MoveWindow (theWindow, left, top, 1);
534         SizeWindow (theWindow, width, height, 1);
535         if (nhWin->scrollBar)
536                 DrawScrollbar (nhWin);
537
538         /* Handle other windows */
539         for (ix = 0; ix < NUM_MACWINDOWS; ix ++) {
540                 if (ix != WIN_STATUS && ix != WIN_MESSAGE && ix != WIN_MAP && ix != BASE_WINDOW) {
541                         theWindow = theWindows [ix].its_window;
542                         if (theWindow && ((WindowPeek) theWindow)->visible) {
543                                 int shift;
544                                 if (((WindowPeek)theWindow)->windowKind == WIN_BASE_KIND + NHW_MENU) {
545                                         if (!RetrievePosition (kMenuWindow, &top, &left)) {
546                                                 top = mbar_height * 2;
547                                                 left = 2;
548                                         }
549                                         top += (numMenu * mbar_height);
550                                         numMenu++;
551                                         shift = 20;
552                                 } else {
553                                         if (!RetrievePosition (kTextWindow, &top, &left)) {
554                                                 top = mbar_height * 2;
555                                                 left = screenArea.right - 3 - 
556                                                         (theWindow->portRect.right - theWindow->portRect.left);
557                                         }
558                                         top += (numText * mbar_height);
559                                         numText++;
560                                         shift = -20;
561                                 }
562                                 while (top > screenArea.bottom - MIN_HEIGHT) {
563                                         top -= screenArea.bottom - mbar_height * 2;
564                                         left += shift;
565                                 }
566                                 MoveWindow (theWindow, left, top, 1);
567                         }
568                 }
569         }
570 #endif
571         return (0);
572 }
573
574
575 winid
576 mac_create_nhwindow (int kind) {
577         int i;
578         NhWindow *aWin;
579         FontInfo fi;
580
581         if (kind < NHW_BASE || kind > NHW_TEXT) {
582                 error ("cre_win: Invalid kind %d.", kind);
583                 return WIN_ERR;
584         }
585
586         for (i = 0; i < NUM_MACWINDOWS; i ++) {
587                 if (!theWindows [i].its_window)
588                         break;
589         }
590         if (i >= NUM_MACWINDOWS) {
591                 error ("cre_win: Win full; freeing extras");
592                 for (i = 0; i < NUM_MACWINDOWS; i ++) {
593                         if (IsWindowVisible(theWindows [i].its_window) || i == WIN_INVEN ||
594                                 GetWindowKind(theWindows [i].its_window) != WIN_BASE_KIND + NHW_MENU &&
595                                 GetWindowKind(theWindows [i].its_window) != WIN_BASE_KIND + NHW_TEXT)
596                                 continue;
597                         mac_destroy_nhwindow(i);
598                         goto got1;
599                 }
600                 error ("cre_win: Out of ids!");
601                 return WIN_ERR;
602         }
603
604 got1 :
605         aWin = &theWindows [i];
606         aWin->windowTextLen = 0L;
607         aWin->scrollBar = (ControlHandle) 0;
608         aWin->menuInfo = 0;
609         aWin->menuSelected = 0;
610         aWin->miLen = 0;
611         aWin->miSize = 0;
612         aWin->menuChar = 'a';
613         
614         dprintf ("cre_win: New kind %d", kind);
615
616         if (kind == NHW_BASE || kind == NHW_MAP || kind == NHW_STATUS) {
617                 short x_sz, x_sz_p, y_sz, y_sz_p;
618                 if (kind != NHW_BASE) {
619                         if (i != tty_create_nhwindow(kind)) {
620                                 dprintf ("cre_win: error creating kind %d", kind);
621                         }
622                         if (kind == NHW_MAP) {
623                                 wins[i]->offy = 0;      /* the message box is in a separate window */
624                         }
625                 }
626                 aWin->its_window = _mt_window;
627                 get_tty_metrics(aWin->its_window, &x_sz, &y_sz, &x_sz_p, &y_sz_p,
628                                          &aWin->font_number, &aWin->font_size,
629                                          &aWin->char_width, &aWin->row_height);
630                 return i;
631         }
632
633         aWin->its_window = GetNewWindow (WIN_BASE_RES + kind, (WindowPtr) 0L, (WindowPtr) -1L);
634         SetWindowKind(aWin->its_window, WIN_BASE_KIND + kind);
635         SetWRefCon(aWin->its_window, (long) aWin);
636         if (!(aWin->windowText = NewHandle (TEXT_BLOCK))) {
637                 error ("cre_win: NewHandle fail(%ld)", (long) TEXT_BLOCK);
638                 DisposeWindow (aWin->its_window);
639                 aWin->its_window = (WindowPtr) 0;
640                 return WIN_ERR;
641         }
642         aWin->x_size = aWin->y_size = 0;
643         aWin->x_curs = aWin->y_curs = 0;
644         aWin->drawn = TRUE;
645         mac_clear_nhwindow (i);
646
647         SetPortWindowPort(aWin->its_window);
648
649         if (kind == NHW_MESSAGE) {
650                 aWin->font_number = win_fonts [NHW_MESSAGE];
651                 aWin->font_size = iflags.wc_fontsiz_message? iflags.wc_fontsiz_message :
652                         iflags.large_font ? 12 : 9;
653                 if (!top_line) {
654                         const Rect out_of_scr = {10000, 10000, 10100, 10100};
655                         TextFont(aWin->font_number);
656                         TextSize(aWin->font_size);
657                         TextFace(bold);
658                         top_line = TENew(&out_of_scr, &out_of_scr);
659                         TEActivate(top_line);
660                         TextFace(normal);
661                 }
662         } else {
663                 aWin->font_number = win_fonts [NHW_TEXT];
664                 aWin->font_size = iflags.wc_fontsiz_text ? iflags.wc_fontsiz_text : 9;
665         }
666
667         TextFont (aWin->font_number); 
668         TextSize (aWin->font_size);
669
670         GetFontInfo (&fi);
671         aWin->ascent_height = fi.ascent + fi.leading;
672         aWin->row_height = aWin->ascent_height + fi.descent;
673         aWin->char_width = fi.widMax;
674
675         if (kind == NHW_MENU || kind == NHW_TEXT || kind == NHW_MESSAGE) {
676                 Rect r;
677
678                 GetWindowBounds(aWin->its_window, kWindowContentRgn, &r);
679                 r.right -= (r.left - 1);
680                 r.left = r.right - SBARWIDTH;
681                 r.bottom -= (r.top + SBARHEIGHT);
682                 r.top = -1;
683                 aWin->scrollBar = NewControl (aWin->its_window, &r, "\p", (r.bottom > r.top + 50), 0, 0, 0, 16, 0L);
684                 aWin->scrollPos = 0;
685         }
686         return i;
687 }
688
689
690 void
691 mac_init_nhwindows (int *argcp, char **argv)
692 {
693 #if !TARGET_API_MAC_CARBON
694         Rect scr = (*GetGrayRgn())->rgnBBox;
695         small_screen = scr.bottom - scr.top <= (iflags.large_font ? 12*40 : 9*40);
696 #endif
697         Rect r;
698
699
700         InitMenuRes ();
701
702         theWindows = (NhWindow *) NewPtrClear (NUM_MACWINDOWS * sizeof (NhWindow));
703         if (MemError())
704                 error("mac_init_nhwindows: Couldn't allocate memory for windows.");
705
706         DimMenuBar ();
707
708         tty_init_nhwindows(argcp, argv);
709         iflags.window_inited = TRUE;
710
711         /* Some ugly hacks to make both interfaces happy:
712          * Mac port uses both tty interface (for main map) and extra windows.  The winids need to
713          * be kept in synch for both interfaces to map.  Also, the "blocked" display_nhwindow case
714          * for the map automatically calls the tty interface for the message box, so some version
715          * of the message box has to exist in the tty world to prevent a meltdown, even though most
716          * messages are handled in mac window.
717          */
718         mac_create_nhwindow(NHW_BASE);
719         tty_create_nhwindow(NHW_MESSAGE);
720         RetrievePosition(kMessageWindow, &r.top, &r.left);
721         RetrieveSize(kMessageWindow, r.top, r.left, &r.bottom, &r.right);
722         MoveWindow(theWindows[NHW_MESSAGE].its_window, r.left, r.top, false);
723         SizeWindow(theWindows[NHW_MESSAGE].its_window, r.right, r.bottom, true);
724         ConstrainWindowToScreen(theWindows[NHW_MESSAGE].its_window, kWindowStructureRgn,
725                 kWindowConstrainMoveRegardlessOfFit, NULL, NULL);
726         return;
727 }
728
729
730 void
731 mac_clear_nhwindow (winid win) {
732         long l;
733         Rect r;
734         NhWindow *aWin = &theWindows [win];
735         WindowPtr theWindow = aWin->its_window;
736
737         if (win < 0 || win >= NUM_MACWINDOWS || !theWindow) {
738                 error ("clr_win: Invalid win %d.", win);
739                 return;
740         }
741         if (theWindow == _mt_window) {
742                 tty_clear_nhwindow(win);
743                 return;
744         }
745         if (!aWin->drawn)
746                 return;
747
748         SetPortWindowPort(theWindow);
749         GetWindowBounds(theWindow, kWindowContentRgn, &r);
750         OffsetRect(&r, -r.left, -r.top);
751         if (aWin->scrollBar)
752                 r.right -= SBARWIDTH;
753
754         switch (GetWindowKind(theWindow) - WIN_BASE_KIND) {
755         case NHW_MESSAGE :
756                 if (aWin->scrollPos == aWin->y_size - 1)        /* if no change since last clear */
757                         return;                                                                 /* don't bother with redraw */
758                 r.bottom -= SBARHEIGHT;
759                 for (l = 0; aWin->y_size > iflags.msg_history;) {
760                         const char cr = CHAR_CR;
761                         l = Munger(aWin->windowText, l, &cr, 1, nil, 0) + 1;
762                         --aWin->y_size;
763                 }
764                 if (l) {
765                         aWin->windowTextLen -= l;
766                         BlockMove(*aWin->windowText + l, *aWin->windowText, aWin->windowTextLen);
767                 }
768                 aWin->last_more_lin = aWin->y_size;
769                 aWin->save_lin  = aWin->y_size;
770                 aWin->scrollPos = aWin->y_size ? aWin->y_size - 1 : 0;
771                 break;
772         case NHW_MENU:
773                 if (aWin->menuInfo) {
774                         DisposeHandle((Handle)aWin->menuInfo);
775                         aWin->menuInfo = NULL;
776                 }
777                 if (aWin->menuSelected) {
778                         DisposeHandle((Handle)aWin->menuSelected);
779                         aWin->menuSelected = NULL;
780                 }
781                 aWin->menuChar = 'a';
782                 aWin->miSelLen = 0;
783                 aWin->miLen = 0;
784                 aWin->miSize = 0;
785                 /* Fall-Through */
786         default :
787                 SetHandleSize (aWin->windowText, TEXT_BLOCK);
788                 aWin->windowTextLen = 0L;
789                 aWin->x_size = 0;
790                 aWin->y_size = 0;
791                 aWin->scrollPos = 0;
792                 break;
793         }
794         if (aWin->scrollBar) {
795                 SetControlMaximum (aWin->scrollBar, aWin->y_size);
796                 SetControlValue(aWin->scrollBar, aWin->scrollPos);
797         }
798         aWin->y_curs = 0;
799         aWin->x_curs = 0;
800         aWin->drawn = FALSE;
801         InvalWindowRect(theWindow, &r);
802 }
803
804
805 static Boolean
806 ClosingWindowChar(const int c) {
807         return c == CHAR_ESC || c == CHAR_BLANK || c == CHAR_LF || c == CHAR_CR ||
808                         c == 'q';
809 }
810
811
812 static Boolean
813 in_topl_mode(void)
814 {
815         Rect rect;
816
817
818         GetWindowBounds(theWindows[WIN_MESSAGE].its_window, kWindowContentRgn, &rect);
819         OffsetRect(&rect, -rect.left, -rect.top);
820         return (WIN_MESSAGE != WIN_ERR && top_line &&
821                 (*top_line)->viewRect.left < rect.right);
822 }
823
824
825 #define BTN_IND 2
826 #define BTN_W   40
827 #define BTN_H   (SBARHEIGHT-3)
828
829 static void
830 topl_resp_rect(int resp_idx, Rect *r)
831 {
832         Rect rect;
833
834
835         GetWindowBounds(theWindows[WIN_MESSAGE].its_window, kWindowContentRgn, &rect);
836         OffsetRect(&rect, -rect.left, -rect.top);
837         r->left   = (BTN_IND + BTN_W) * resp_idx + BTN_IND;
838         r->right  = r->left + BTN_W;
839         r->bottom = rect.bottom - 1;
840         r->top    = r->bottom - BTN_H;
841         return;
842 }
843
844
845 void
846 enter_topl_mode(char *query) {
847         if (in_topl_mode())
848                 return;
849
850         putstr(WIN_MESSAGE, ATR_BOLD, query);
851
852         topl_query_len = strlen(query);
853         (*top_line)->selStart = topl_query_len;
854         (*top_line)->selEnd = topl_query_len;
855         (*top_line)->viewRect.left = 0;
856         PtrToXHand(query, (*top_line)->hText, topl_query_len);
857         TECalText(top_line);
858
859         DimMenuBar();
860         mac_display_nhwindow(WIN_MESSAGE, FALSE);
861 }
862
863
864 void
865 leave_topl_mode(char *answer) {
866         unsigned char *ap, *bp;
867
868         int ans_len = (*top_line)->teLength - topl_query_len;
869         NhWindow *aWin = theWindows + WIN_MESSAGE;
870
871         if (!in_topl_mode())
872                 return;
873
874         /* Cap length of reply */
875         if (ans_len >= BUFSZ)
876                 ans_len = BUFSZ-1;
877
878         /* remove unprintables from the answer */
879         for (ap = *(*top_line)->hText + topl_query_len, bp = answer; ans_len > 0; ans_len--, ap++) {
880                 if (*ap >= ' ' && *ap < 128) {
881                         *bp++ = *ap;
882                 }
883         }
884         *bp = 0;
885         
886         if (aWin->windowTextLen && (*aWin->windowText)[aWin->windowTextLen-1] == CHAR_CR) {
887                 -- aWin->windowTextLen;
888                 -- aWin->y_size;
889         }
890         putstr(WIN_MESSAGE, ATR_BOLD, answer);
891
892         (*top_line)->viewRect.left += 10000;
893         UndimMenuBar();
894 }
895
896 /*
897  * TESetSelect flushes out all the pending key strokes.  I hate it.
898  */
899 static void
900 topl_set_select(short selStart, short selEnd) {
901         TEDeactivate(top_line);
902         (*top_line)->selStart   = selStart;
903         (*top_line)->selEnd     = selEnd;
904         TEActivate(top_line);
905 }
906
907
908 static void
909 topl_replace(char *new_ans) {
910         topl_set_select(topl_query_len, (*top_line)->teLength);
911         TEDelete(top_line);
912         TEInsert(new_ans, strlen(new_ans), top_line);
913 }
914
915
916 Boolean
917 topl_key(unsigned char ch, Boolean ext) {
918         switch (ch) {
919                 case CHAR_ESC:
920                         topl_replace("\x1b");
921                 case CHAR_ENTER: case CHAR_CR: case CHAR_LF:
922                         return false;
923
924                 case 0x1f & 'P':
925                         mac_doprev_message();
926                         return true;
927                 case '\x1e'/* up arrow */:
928                         topl_replace ("");
929                         return true;
930                 case CHAR_BS: case '\x1c'/* left arrow */:
931                         if ((*top_line)->selEnd <= topl_query_len)
932                                 return true;
933                         else if (ext) {
934                                 topl_replace ("");
935                                 return true;
936                         }
937                 default:
938                         TEKey(ch, top_line);
939                         if (ext) {
940                                 int com_index = -1, oindex = 0;
941                                 while(extcmdlist[oindex].ef_txt != (char *)0) {
942                                         if(!strncmpi(*(*top_line)->hText + topl_query_len,
943                                                                  extcmdlist[oindex].ef_txt,
944                                                                  (*top_line)->teLength - topl_query_len)) {
945                                                 if(com_index == -1) /* No matches yet*/
946                                                         com_index = oindex;
947                                                 else /* More than 1 match */ {
948                                                         com_index = -2;
949                                                         break;
950                                                 }
951                                         }
952                                         oindex++;
953                                 }
954                                 if(com_index >= 0)
955                                         topl_replace((char *) extcmdlist[com_index].ef_txt);
956                         }
957                         return true;
958         }
959 }
960
961
962 static void
963 topl_flash_resp(int resp_idx) {
964         unsigned long dont_care;
965         Rect frame;
966         SetPortWindowPort(theWindows[WIN_MESSAGE].its_window);
967         topl_resp_rect(resp_idx, &frame);
968         InsetRect(&frame, 1, 1);
969         InvertRect(&frame);
970         Delay(GetDblTime() / 2, &dont_care);
971         InvertRect(&frame);
972 }
973
974
975 static void
976 topl_set_def(int new_def_idx) {
977         Rect frame;
978         SetPortWindowPort(theWindows[WIN_MESSAGE].its_window);
979         topl_resp_rect(topl_def_idx, &frame);
980         InvalWindowRect(theWindows[WIN_MESSAGE].its_window, &frame);
981         topl_def_idx = new_def_idx;
982         topl_resp_rect(new_def_idx, &frame);
983         InvalWindowRect(theWindows[WIN_MESSAGE].its_window, &frame);
984 }
985
986
987 void
988 topl_set_resp(char *resp, char def) {
989         char *loc;
990         Rect frame;
991         int r_len, r_len1;
992
993         if (!resp) {
994                 const char any_str[2] = {CHAR_ANY, '\0'};
995                 resp = (char *) any_str;
996                 def = CHAR_ANY;
997         }
998
999         SetPortWindowPort(theWindows[WIN_MESSAGE].its_window);
1000         r_len1 = strlen(resp);
1001         r_len  = strlen(topl_resp);
1002         if (r_len < r_len1)
1003                 r_len = r_len1;
1004         topl_resp_rect(0, &frame);
1005         frame.right = (BTN_IND + BTN_W) * r_len;
1006         InvalWindowRect(theWindows[WIN_MESSAGE].its_window, &frame);
1007
1008         strcpy(topl_resp, resp);
1009         loc = strchr (resp, def);
1010         topl_def_idx = loc ? loc - resp : -1;
1011 }
1012
1013
1014 static char
1015 topl_resp_key(char ch) {
1016         if (strlen(topl_resp) > 0) {
1017                 char *loc = strchr(topl_resp, ch);
1018
1019                 if (!loc) {
1020                         if (ch == '\x9'/* tab */) {
1021                                 topl_set_def(topl_def_idx<=0 ? strlen(topl_resp)-1 : topl_def_idx-1);
1022                                 ch = '\0';
1023                         } else if (ch == CHAR_ESC) {
1024                                 loc = strchr(topl_resp, 'q');
1025                                 if (!loc) {
1026                                         loc = strchr(topl_resp, 'n');
1027                                         if (!loc && topl_def_idx >= 0)
1028                                                 loc = topl_resp + topl_def_idx;
1029                                 }
1030                         } else if (ch == (0x1f & 'P')) {
1031                                 mac_doprev_message();
1032                                 ch = '\0';
1033                         } else if (topl_def_idx >= 0) {
1034                                 if (ch == CHAR_ENTER || ch == CHAR_CR || ch == CHAR_LF ||
1035                                          ch == CHAR_BLANK || topl_resp[topl_def_idx] == CHAR_ANY)
1036                                         loc = topl_resp + topl_def_idx;
1037
1038                                 else if (strchr(topl_resp, '#')) {
1039                                         if (digit(ch)) {
1040                                                 topl_set_def(strchr(topl_resp, '#') - topl_resp);
1041                                                 TEKey(ch, top_line);
1042                                                 ch = '\0';
1043
1044                                         } else if (topl_resp[topl_def_idx] == '#') {
1045                                                 if (ch == '\x1e'/* up arrow */) {
1046                                                         topl_set_select(topl_query_len, topl_query_len);
1047                                                         ch = '\0';
1048                                                 } else if (ch == '\x1d'/* right arrow */ ||
1049                                                            ch == '\x1f'/* down arrow */ ||
1050                                                            ch == CHAR_BS || ch == '\x1c'/* left arrow */ &&
1051                                                            (*top_line)->selEnd > topl_query_len) {
1052                                                         TEKey(ch, top_line);
1053                                                         ch = '\0';
1054                                                 }
1055                                         }
1056                                 }
1057                         }
1058                 }
1059
1060                 if (loc) {
1061                         topl_flash_resp(loc - topl_resp);
1062                         if (*loc != CHAR_ANY)
1063                                 ch = *loc;
1064                         TEKey(ch, top_line);
1065                 }
1066         }
1067
1068         return ch;
1069 }
1070
1071
1072 static void
1073 adjust_window_pos(NhWindow *aWin, short width, short height)
1074 {
1075         WindowRef theWindow = aWin->its_window;
1076 #if TARGET_API_MAC_CARBON
1077         Rect r;
1078
1079
1080         GetWindowBounds(theWindow, kWindowContentRgn, &r);
1081         RetrieveWinPos(theWindow, &r.top, &r.left);
1082         MoveWindow(theWindow, r.left, r.top, false);
1083         SizeWindow(theWindow, width, height, true);
1084         ConstrainWindowToScreen(theWindow, kWindowStructureRgn,
1085                 kWindowConstrainMoveRegardlessOfFit, NULL, NULL);
1086 #else
1087         Rect scr_r = (*GetGrayRgn())->rgnBBox;
1088         const Rect win_ind = {2, 2, 3, 3};
1089         const short     min_w = theWindow->portRect.right - theWindow->portRect.left,
1090                                 max_w = scr_r.right - scr_r.left - win_ind.left - win_ind.right;
1091         Point pos;
1092         short max_h;
1093
1094         SetPortWindowPort(theWindow);
1095         if (!RetrieveWinPos(theWindow, &pos.v, &pos.h)) {
1096                 pos.v = 0;      /* take window's existing position */
1097                 pos.h = 0;
1098                 LocalToGlobal(&pos);
1099         }
1100
1101         max_h = scr_r.bottom - win_ind.bottom - pos.v;
1102         if (height > max_h)             height = max_h;
1103         if (height < MIN_HEIGHT)        height = MIN_HEIGHT;
1104         if (width < min_w)              width = min_w;
1105         if (width > max_w)              width = max_w;
1106         SizeWindow(theWindow, width, height, true);
1107
1108         if (pos.v + height + win_ind.bottom > scr_r.bottom)
1109                 pos.v = scr_r.bottom - height - win_ind.bottom;
1110         if (pos.h + width + win_ind.right > scr_r.right)
1111                 pos.h = scr_r.right      - width - win_ind.right;
1112         MoveWindow(theWindow, pos.h, pos.v, false);
1113         if (aWin->scrollBar)    
1114                 DrawScrollbar (aWin);
1115 #endif
1116         return;
1117 }
1118
1119
1120 /*
1121  * display/select/update the window.
1122  * If f is true, this window should be "modal" - don't return
1123  * until presumed seen.
1124  */
1125 void
1126 mac_display_nhwindow (winid win, BOOLEAN_P f) {
1127         NhWindow *aWin = &theWindows [win];
1128         WindowPtr theWindow = aWin->its_window;
1129
1130         if (win < 0 || win >= NUM_MACWINDOWS || !theWindow) {
1131                 error ("disp_win: Invalid window %d.", win);
1132                 return;
1133         }
1134
1135         if (theWindow == _mt_window) {
1136                 tty_display_nhwindow(win, f);
1137                 return;
1138         }
1139
1140         if (f && inSelect == WIN_ERR && win == WIN_MESSAGE) {
1141                 topl_set_resp ((char *)0, 0);
1142                 if (aWin->windowTextLen > 0 &&
1143                          (*aWin->windowText) [aWin->windowTextLen - 1] == CHAR_CR) {
1144                         -- aWin->windowTextLen;
1145                         -- aWin->y_size;
1146                 }
1147                 putstr (win, flags.standout ? ATR_INVERSE : ATR_NONE, " --More--");
1148         }
1149
1150         if (!IsWindowVisible(theWindow)) {
1151                 if (win != WIN_MESSAGE)
1152                         adjust_window_pos(aWin, aWin->x_size + SBARWIDTH+1, aWin->y_size *aWin->row_height);
1153
1154                 SelectWindow (theWindow);
1155                 ShowWindow (theWindow);
1156         }
1157
1158         if (f && inSelect == WIN_ERR) {
1159                 int ch;
1160
1161                 DimMenuBar();
1162                 inSelect = win;
1163                 do {
1164                         ch = mac_nhgetch ();
1165                 } while (!ClosingWindowChar (ch));
1166                 inSelect = WIN_ERR;
1167                 UndimMenuBar();
1168
1169                 if (win == WIN_MESSAGE)
1170                         topl_set_resp ("", '\0');
1171                 else
1172                         HideWindow (theWindow);
1173
1174         }
1175 }
1176
1177
1178 void
1179 mac_destroy_nhwindow (winid win) {
1180         WindowPtr theWindow;
1181         NhWindow *aWin = &theWindows [win];
1182         int kind;
1183
1184         if (win < 0 || win >= NUM_MACWINDOWS) {
1185                 if (iflags.window_inited) error ("dest_win: Invalid win %d.", win);
1186                 return;
1187         }
1188         theWindow = aWin->its_window;
1189         if (!theWindow) {
1190                 error ("dest_win: Not allocated win %d.", win);
1191                 return;
1192         }
1193
1194         /*
1195          * Check special windows.  The base window should never go away.
1196          * Other "standard" windows should not go away unless we've exitted nhwindows.
1197          */
1198         if (theWindow == _mt_window) {
1199                 return;
1200         }
1201         if (win == WIN_INVEN || win == WIN_MESSAGE) {
1202                 if (iflags.window_inited) {
1203                         if (flags.tombstone && killer) {
1204                                 /* Prepare for the coming of the tombstone window. */
1205                                 win_fonts [NHW_TEXT] = kFontIDMonaco;
1206                         }
1207                         return;
1208                 }
1209                 if (win == WIN_MESSAGE)
1210                         WIN_MESSAGE = WIN_ERR;
1211         }
1212
1213         kind = GetWindowKind(theWindow) - WIN_BASE_KIND;
1214
1215         if ((!IsWindowVisible(theWindow) || (kind != NHW_MENU && kind != NHW_TEXT))) {
1216                 DisposeWindow (theWindow);
1217                 if (aWin->windowText) {
1218                         DisposeHandle(aWin->windowText);
1219                 }
1220                 aWin->its_window = (WindowPtr) 0;
1221                 aWin->windowText = (Handle) 0;
1222         }
1223 }
1224
1225
1226 void
1227 mac_number_pad (int pad) {
1228         iflags.num_pad = pad;
1229 }
1230
1231
1232 void
1233 trans_num_keys(EventRecord *theEvent) {
1234 #if defined(__SC__) || defined(__MRC__)
1235 # pragma unused(theEvent)
1236 #endif
1237 /* KMH -- Removed this translation.
1238  * Number pad keys should always emit digit characters.
1239  * That's consistent with the default MacOS behavior.
1240  * The number_pad option controls how digits are interpreted.
1241  */
1242 #if 0
1243         if (iflags.num_pad) {
1244                 Handle h = GetResource('Nump', theEvent->modifiers & shiftKey ? 129 : 128);
1245                 if (h) {
1246                         short inkey = (theEvent->message & keyCodeMask), *ab = (short *)*h;
1247                         int i = ab[0];
1248                         for (; i; i--) {
1249                                 if (inkey == (ab[i] & keyCodeMask)) {
1250                                         theEvent->message = ab[i];
1251                                         break;
1252                                 }
1253                         }
1254                 }
1255         }
1256 #endif
1257 }
1258
1259
1260 /*
1261  * Note; theWindow may very well be null here, since keyDown may call
1262  * it when theres no window !!!
1263  */
1264 static void
1265 GeneralKey (EventRecord *theEvent, WindowPtr theWindow) {
1266 #if defined(__SC__) || defined(__MRC__)
1267 # pragma unused(theWindow)
1268 #endif
1269 #if 0
1270         trans_num_keys (theEvent);
1271 #endif
1272         AddToKeyQueue (topl_resp_key (theEvent->message & 0xff), TRUE);
1273 }
1274
1275
1276 /*
1277  * Routine used to select and de-select elements in a menu window, used by KeyMenu,
1278  * ClickMenu, and UpdateMenu.  Takes the NhWindow and a line ref relative to the scrollbar.
1279  */
1280 static void ToggleMenuSelect (NhWindow *aWin, int line) {
1281         Rect r;
1282
1283
1284         GetWindowBounds(aWin->its_window, kWindowContentRgn, &r);
1285         OffsetRect(&r, -r.left, -r.top);
1286         if (aWin->scrollBar)
1287                 r.right -= SBARWIDTH;                   
1288         r.top = line * aWin->row_height;
1289         r.bottom = r.top + aWin->row_height;
1290
1291         LMSetHiliteMode((UInt8) (LMGetHiliteMode() & 0x7F));
1292         InvertRect(&r);
1293 }
1294
1295 /*
1296  * Check to see if given item is selected, return index if it is
1297  */
1298 static int
1299 ListItemSelected (NhWindow *aWin, int item) {
1300         int             i;
1301
1302         HLock ((char**)aWin->menuSelected);
1303         /* Find item in selection list */
1304         for (i = aWin->miSelLen - 1; i >= 0; i--) {
1305                 if ((*aWin->menuSelected) [i] == item)
1306                         break;
1307         }
1308         HUnlock ((char**)aWin->menuSelected);
1309         return i;
1310 }
1311
1312 /*
1313  * Add item to selection list if it's not selected already
1314  * If it is selected already, remove it from the list.
1315  */
1316 static void
1317 ToggleMenuListItemSelected (NhWindow *aWin, short item) {
1318         int i = ListItemSelected (aWin, item);
1319
1320         HLock ((char**)aWin->menuSelected);
1321         if (i < 0) {    /* not there, so add */
1322                 (*aWin->menuSelected) [aWin->miSelLen] = item;
1323                 aWin->miSelLen++;
1324         }
1325         else {                  /* there, so remove */
1326                 short *mi = &(*aWin->menuSelected)[i];
1327                 aWin->miSelLen --;
1328                 memcpy (mi, mi + 1, (aWin->miSelLen - i)*sizeof(short));
1329         }
1330         HUnlock ((char**)aWin->menuSelected);
1331 }
1332
1333
1334 /*
1335  * Find menu item in list given a line number on the window
1336  */
1337 static short
1338 ListCoordinateToItem (NhWindow *aWin, short Row) {
1339         int                                     i, item = -1;
1340         MacMHMenuItem *         mi;
1341
1342         HLock ((char**)aWin->menuInfo);
1343         for (i = 0, mi = *aWin->menuInfo; i < aWin->miLen; i++, mi++) {
1344                 if (mi->line == Row + aWin->scrollPos) {
1345                         item = i;
1346                         break;
1347                 }
1348         }
1349         HUnlock ((char**)aWin->menuInfo);
1350         return item;
1351 }
1352
1353
1354 static void
1355 macKeyMenu (EventRecord *theEvent, WindowPtr theWindow) {
1356         NhWindow *aWin = GetNhWin(theWindow);
1357         MacMHMenuItem *mi;
1358         int l, ch = theEvent->message & 0xff;
1359
1360         if (aWin && aWin->menuInfo) {
1361                 HLock ((char**)aWin->menuInfo);
1362                 for (l = 0, mi = *aWin->menuInfo; l < aWin->miLen; l++, mi++) {
1363                         if (mi->accelerator == ch) {
1364                                 ToggleMenuListItemSelected (aWin, l);
1365                                 if (mi->line >= aWin->scrollPos && mi->line <= aWin->y_size) {
1366                                         SetPortWindowPort(theWindow);
1367                                         ToggleMenuSelect (aWin, mi->line - aWin->scrollPos);
1368                                 }                               
1369                                 /* Dismiss window if only picking one item */
1370                                 if (aWin->how != PICK_ANY)
1371                                         AddToKeyQueue(CHAR_CR, 1);
1372                                 break;
1373                         }
1374                 }
1375                 HUnlock ((char**)aWin->menuInfo);
1376                 /* add key if didn't find it in menu and not filtered */
1377                 if (l == aWin->miLen && filter_scroll_key (ch, aWin))
1378                         GeneralKey (theEvent, theWindow);
1379         }
1380 }
1381
1382
1383 static void
1384 macClickMenu (EventRecord *theEvent, WindowRef theWindow) {
1385         Point p;
1386         NhWindow *aWin = GetNhWin(theWindow);
1387         Rect wrect;
1388
1389
1390         GetWindowBounds(theWindow, kWindowContentRgn, &wrect);
1391         OffsetRect(&wrect, -wrect.left, -wrect.top);
1392         if (aWin->scrollBar && IsControlVisible(aWin->scrollBar)) {
1393                 short code;
1394                 ControlHandle theBar;
1395
1396                 p = theEvent->where;
1397                 GlobalToLocal (&p);
1398                 code = FindControl (p, theWindow, &theBar);
1399                 if (code) {
1400                         DoScrollBar (p, code, theBar, aWin);
1401                         return;
1402                 }
1403         }
1404         if (inSelect != WIN_ERR && aWin->how != PICK_NONE) {
1405                 short           currentRow = -1, previousRow = -1;
1406                 short           previousItem = -1, item = -1;
1407                 Boolean         majorSelectState, firstRow = TRUE;
1408
1409                 do {
1410 #if !TARGET_API_MAC_CARBON
1411                         SystemTask ();
1412 #endif
1413                         GetMouse (&p);
1414                         currentRow = p.v / aWin->row_height;
1415                         if (p.h < wrect.left || p.h > wrect.right ||
1416                                 p.v < 0 || p.v > wrect.bottom || currentRow >= aWin->y_size) {
1417                                 continue;       /* not in window range */
1418                         }
1419
1420                         item = ListCoordinateToItem (aWin, currentRow);
1421
1422                         if (item != previousItem) {
1423                                 /* Implement typical Mac multiple-selection behavior
1424                                  * (ie, not the UI implemented by the Finder)
1425                                  */
1426                                 Boolean itemIsSelected = (ListItemSelected (aWin,item) >= 0);
1427
1428                                 if (firstRow) {
1429                                         /* this is first valid row, so major state is opposite of what this row is */
1430                                         majorSelectState = !itemIsSelected;
1431                                         firstRow = FALSE;
1432                                 }
1433
1434                                 if (aWin->how == PICK_ONE && previousItem != -1) {
1435                                         /* if previous row was selected and we're only selecting one object,
1436                                          * deselect previous row!
1437                                          */
1438                                         ToggleMenuListItemSelected (aWin, previousItem);
1439                                         ToggleMenuSelect (aWin, previousRow);
1440                                         previousItem = -1;
1441                                 }
1442
1443                                 if (item == -1)
1444                                         continue;       /* header line */
1445                                         
1446                                 if (majorSelectState != itemIsSelected) {
1447                                         ToggleMenuListItemSelected (aWin, item);
1448                                         ToggleMenuSelect (aWin, currentRow);
1449                                 }
1450
1451                                 previousRow             = currentRow;
1452                                 previousItem    = item;
1453                         }
1454                 } while (StillDown ());
1455
1456                 /* Dismiss window if only picking one item */
1457                 if (aWin->how == PICK_ONE)
1458                         AddToKeyQueue(CHAR_CR, 1);
1459         }
1460 }
1461
1462
1463 static void
1464 macKeyText (EventRecord *theEvent, WindowPtr theWindow) {
1465         NhWindow *aWin = GetNhWin (theWindow);
1466         char c = filter_scroll_key (theEvent->message & 0xff, aWin);
1467         if (c) {
1468                 if (inSelect == WIN_ERR && ClosingWindowChar (c)) {
1469                         HideWindow (theWindow);
1470                         mac_destroy_nhwindow (aWin - theWindows);
1471                 } else {
1472                         GeneralKey (theEvent, theWindow);
1473                 }
1474         }
1475 }
1476
1477
1478 static void
1479 macClickText (EventRecord *theEvent, WindowPtr theWindow) {
1480         NhWindow *aWin = GetNhWin (theWindow);
1481
1482         if (aWin->scrollBar && IsControlVisible(aWin->scrollBar)) {
1483                 short code;
1484                 Point p = theEvent->where;
1485                 ControlHandle theBar;
1486
1487                 GlobalToLocal (&p);
1488                 code = FindControl (p, theWindow, &theBar);
1489                 if (code) {
1490                         DoScrollBar (p, code, theBar, aWin);
1491                 }
1492         }
1493 }
1494
1495
1496 static void
1497 macClickMessage (EventRecord *theEvent, WindowPtr theWindow) {
1498         int r_idx = 0;
1499         Point mouse = theEvent->where;
1500
1501         GlobalToLocal(&mouse);
1502         while (topl_resp[r_idx]) {
1503                 Rect frame;
1504                 topl_resp_rect(r_idx, &frame);
1505                 InsetRect(&frame, 1, 1);
1506                 if (PtInRect(mouse, &frame)) {
1507                         Boolean in_btn = true;
1508
1509                         InvertRect(&frame);
1510                         while (WaitMouseUp()) {
1511 #if !TARGET_API_MAC_CARBON
1512                                 SystemTask();
1513 #endif
1514                                 GetMouse(&mouse);
1515                                 if (PtInRect(mouse, &frame) != in_btn) {
1516                                         in_btn = !in_btn;
1517                                         InvertRect(&frame);
1518                                 }
1519                         }
1520                         if (in_btn) {
1521                                 InvertRect(&frame);
1522                                 AddToKeyQueue (topl_resp [r_idx], 1);
1523                         }
1524                         return;
1525
1526                 }
1527                 ++r_idx;
1528         }
1529
1530         macClickText(theEvent, theWindow);
1531 }
1532
1533
1534 static void
1535 macClickTerm (EventRecord *theEvent, WindowPtr theWindow) {
1536         NhWindow *nhw = GetNhWin(theWindow);
1537         Point where = theEvent->where;
1538
1539         GlobalToLocal(&where);
1540         where.h = where.h / nhw->char_width + 1;
1541         where.v = where.v / nhw->row_height;
1542         clicked_mod = (theEvent->modifiers & shiftKey) ? CLICK_2 : CLICK_1;
1543
1544         if (strchr(topl_resp, *click_to_cmd(where.h, where.v, clicked_mod)))
1545                 nhbell();
1546         else {
1547 #if !TARGET_API_MAC_CARBON
1548                 if (cursor_locked)
1549                         while (WaitMouseUp())
1550                                 SystemTask();
1551 #endif
1552
1553                 gClickedToMove = TRUE;
1554                 clicked_pos = where;
1555         }
1556 }
1557
1558 static pascal void
1559 MoveScrollBar (ControlHandle theBar, short part) {
1560         EventRecord fake;
1561         Rect r;
1562         RgnHandle rgn;
1563         int now, amtToScroll;
1564         WindowPtr theWin;
1565         NhWindow *winToScroll;
1566         
1567         if (!part)
1568                 return;
1569
1570         theWin = GetControlOwner(theBar);
1571         GetWindowBounds(theWin, kWindowContentRgn, &r);
1572         OffsetRect(&r, -r.left, -r.top);
1573         winToScroll = (NhWindow*)(GetWRefCon(theWin));
1574         now = GetControlValue (theBar);
1575
1576         if (part == kControlPageUpPart || part == kControlPageDownPart) 
1577                 amtToScroll = (r.bottom - r.top) / winToScroll->row_height;
1578         else
1579                 amtToScroll = 1;
1580
1581         if (part == kControlPageUpPart || part == kControlUpButtonPart) {
1582                 int bound = GetControlMinimum (theBar);
1583                 if (now - bound < amtToScroll)
1584                         amtToScroll = now - bound;
1585                 amtToScroll = -amtToScroll;
1586         } else {
1587                 int bound = GetControlMaximum (theBar);
1588                 if (bound - now < amtToScroll)
1589                         amtToScroll = bound - now;
1590         }
1591         
1592         if (!amtToScroll)
1593                 return;
1594
1595         SetControlValue (theBar, now + amtToScroll);
1596         winToScroll->scrollPos = now + amtToScroll;
1597         r.right -= SBARWIDTH;
1598         if (winToScroll == theWindows + WIN_MESSAGE)
1599                 r.bottom -= SBARHEIGHT;
1600         rgn = NewRgn ();
1601         ScrollRect (&r, 0, -amtToScroll * winToScroll->row_height, rgn);
1602         if (rgn) {
1603                 InvalWindowRgn(theWin, rgn);
1604                 BeginUpdate(theWin);
1605         }
1606         winUpdateFuncs [GetWindowKind(theWin) - WIN_BASE_KIND] (&fake, theWin);
1607         if (rgn) {
1608                 EndUpdate(theWin);
1609                 DisposeRgn(rgn);
1610         }
1611 }
1612
1613
1614 static void
1615 DoScrollBar (Point p, short code, ControlHandle theBar, NhWindow *aWin)
1616 {
1617         ControlActionUPP func = NULL;
1618         Rect rect;
1619
1620         if (code == kControlUpButtonPart || code == kControlPageUpPart ||
1621                 code == kControlDownButtonPart || code == kControlPageDownPart)
1622                 func = MoveScrollUPP;
1623         (void) TrackControl(theBar, p, func);
1624         if (!func) {
1625                 if (aWin->scrollPos != GetControlValue (theBar)) {
1626                         aWin->scrollPos = GetControlValue (theBar);
1627                         GetWindowBounds(aWin->its_window, kWindowContentRgn, &rect);
1628                         OffsetRect(&rect, -rect.left, -rect.top);
1629                         InvalWindowRect(aWin->its_window, &rect);
1630                 }
1631         }
1632 }
1633
1634
1635 static int
1636 filter_scroll_key(const int ch, NhWindow *aWin) {
1637         if (aWin->scrollBar && GetControlValue(aWin->scrollBar) < GetControlMaximum(aWin->scrollBar)) {
1638                 short part = 0;
1639                 if (ch == CHAR_BLANK) {
1640                         part = kControlPageDownPart;
1641                 }
1642                 else if (ch == CHAR_CR || ch == CHAR_LF) {
1643                         part = kControlDownButtonPart;
1644                 }
1645                 if (part) {
1646                         SetPortWindowPort(aWin->its_window);
1647                         MoveScrollBar(aWin->scrollBar, part);
1648                         return 0;
1649                 }
1650         }
1651         return ch;
1652 }
1653
1654
1655 int
1656 mac_doprev_message(void) {
1657         if (WIN_MESSAGE) {
1658                 NhWindow *winToScroll = &theWindows[WIN_MESSAGE];
1659                 mac_display_nhwindow(WIN_MESSAGE, FALSE);
1660                 SetPortWindowPort(winToScroll->its_window);
1661                 MoveScrollBar(winToScroll->scrollBar, kControlUpButtonPart);
1662         }       
1663         return 0;
1664 }
1665
1666
1667 static short
1668 macDoNull (EventRecord *theEvent, WindowPtr theWindow) {
1669         return 0;
1670 }
1671
1672
1673 static void
1674 draw_growicon_vert_only(WindowPtr wind)
1675 {
1676         GrafPtr org_port;
1677         RgnHandle org_clip = NewRgn();
1678         Rect r;
1679
1680         GetPort(&org_port);
1681         SetPortWindowPort(wind);
1682         GetClip(org_clip);
1683         GetWindowBounds(wind, kWindowContentRgn, &r);
1684         OffsetRect(&r, -r.left, -r.top);
1685         r.left = r.right - SBARWIDTH;
1686         ClipRect(&r);
1687         DrawGrowIcon(wind);
1688         SetClip(org_clip);
1689         DisposeRgn(org_clip);
1690         SetPort(org_port);
1691 }
1692
1693
1694 static short
1695 macUpdateMessage (EventRecord *theEvent, WindowPtr theWindow)
1696 {
1697         RgnHandle org_clip = NewRgn(), clip = NewRgn();
1698         Rect r;
1699         NhWindow *aWin = GetNhWin(theWindow);
1700         int l;
1701
1702         if (!theEvent)
1703                 return 0;
1704
1705         GetClip(org_clip);
1706         GetWindowBounds(theWindow, kWindowContentRgn, &r);
1707         OffsetRect(&r, -r.left, -r.top);
1708
1709         DrawControls(theWindow);
1710         DrawGrowIcon(theWindow);
1711
1712         for (l = 0; topl_resp[l]; l++) {
1713                 StringPtr name;
1714                 unsigned char tmp[2];
1715                 FontInfo font;
1716                 Rect frame;
1717                 topl_resp_rect(l, &frame);
1718                 switch (topl_resp[l]) {
1719                         case 'y':
1720                                 name = "\pyes";
1721                                 break;
1722                         case 'n':
1723                                 name = "\pno";
1724                                 break;
1725                         case 'N':
1726                                 name = "\pNone";
1727                                 break;
1728                         case 'a':
1729                                 name = "\pall";
1730                                 break;
1731                         case 'q':
1732                                 name = "\pquit";
1733                                 break;
1734                         case CHAR_ANY:
1735                                 name = "\pany key";
1736                                 break;
1737                         default:
1738                                 tmp[0] = 1;
1739                                 tmp[1] = topl_resp[l];
1740                                 name = tmp;
1741                                 break;
1742                 }
1743                 TextFont(kFontIDGeneva);
1744                 TextSize(9);
1745                 GetFontInfo(&font);
1746                 MoveTo ((frame.left + frame.right - StringWidth(name)) / 2,
1747                         (frame.top + frame.bottom + font.ascent-font.descent-font.leading-1) / 2);
1748                 DrawString(name);
1749                 PenNormal();
1750                 if (l == topl_def_idx)
1751                         PenSize(2, 2);
1752                 FrameRoundRect(&frame, 4, 4);
1753         }
1754
1755         r.right -= SBARWIDTH;
1756         r.bottom -= SBARHEIGHT;
1757         /* Clip to the portrect - scrollbar/growicon *before* adjusting the rect
1758                 to be larger than the size of the window (!) */
1759         RectRgn(clip, &r);
1760         SectRgn(clip, org_clip, clip);
1761         if (r.right < MIN_RIGHT)
1762                 r.right = MIN_RIGHT;
1763         r.top -= aWin->scrollPos * aWin->row_height;
1764
1765 #if 0
1766         /* If you enable this band of code (and disable the next band), you will get
1767            fewer flickers but a slower performance while drawing the dot line. */
1768         {       RgnHandle dotl_rgn = NewRgn();
1769                 Rect dotl;
1770                 dotl.left       = r.left;
1771                 dotl.right      = r.right;
1772                 dotl.bottom = r.top + aWin->save_lin * aWin->row_height;
1773                 dotl.top        = dotl.bottom - 1;
1774                 FillRect(&dotl, &qd.gray);
1775                 RectRgn(dotl_rgn, &dotl);
1776                 DiffRgn(clip, dotl_rgn, clip);
1777                 DisposeRgn(dotl_rgn);
1778                 SetClip(clip);
1779         }
1780 #endif
1781
1782         if (in_topl_mode()) {
1783                 RgnHandle topl_rgn = NewRgn();
1784                 Rect topl_r = r;
1785                 topl_r.top += (aWin->y_size - 1) * aWin->row_height;
1786                 l = (*top_line)->destRect.right - (*top_line)->destRect.left;
1787                 (*top_line)->viewRect = topl_r;
1788                 (*top_line)->destRect = topl_r;
1789                 if (l != topl_r.right - topl_r.left)
1790                         TECalText(top_line);
1791                 TEUpdate(&topl_r, top_line);
1792                 RectRgn(topl_rgn, &topl_r);
1793                 DiffRgn(clip, topl_rgn, clip);
1794                 DisposeRgn(topl_rgn);
1795                 SetClip(clip);
1796         }
1797
1798         DisposeRgn(clip);
1799
1800         TextFont (aWin->font_number);
1801         TextSize (aWin->font_size);
1802         HLock (aWin->windowText);
1803         TETextBox (*aWin->windowText, aWin->windowTextLen, &r, teJustLeft);
1804         HUnlock (aWin->windowText);
1805
1806 #if !TARGET_API_MAC_CARBON
1807         r.bottom = r.top + aWin->save_lin * aWin->row_height;
1808         r.top    = r.bottom - 1;
1809         FillRect(&r, (void *) &qd.gray);
1810 #endif
1811
1812         SetClip(org_clip);
1813         DisposeRgn(org_clip);
1814         return 0;
1815 }
1816
1817
1818 static short 
1819 macUpdateMenu (EventRecord *theEvent, WindowPtr theWindow) {
1820         NhWindow *aWin = GetNhWin (theWindow);
1821         int i, line;
1822         MacMHMenuItem *mi;
1823         
1824         GeneralUpdate (theEvent, theWindow);
1825         HLock ((char**)aWin->menuInfo);
1826         HLock ((char**)aWin->menuSelected);
1827         for (i = 0; i < aWin->miSelLen; i++) {
1828                 mi = &(*aWin->menuInfo) [(*aWin->menuSelected) [i]];
1829                 line = mi->line;
1830                 if (line > aWin->scrollPos && line <= aWin->y_size)
1831                         ToggleMenuSelect (aWin, line - aWin->scrollPos);
1832         }
1833         HUnlock ((char**)aWin->menuInfo);
1834         HUnlock ((char**)aWin->menuSelected);
1835         return 0;
1836 }
1837
1838
1839 static short
1840 GeneralUpdate (EventRecord *theEvent, WindowPtr theWindow) {
1841         Rect r, r2;
1842         NhWindow *aWin = GetNhWin (theWindow);
1843         RgnHandle h;
1844         Boolean vis;
1845
1846
1847         if (!theEvent)
1848                 return 0;
1849
1850         GetWindowBounds(theWindow, kWindowContentRgn, &r);
1851         OffsetRect(&r, -r.left, -r.top);
1852         r2 = r;
1853         r2.left = r2.right - SBARWIDTH;
1854         r2.right += 1;
1855         r2.top -= 1;
1856         vis = (r2.bottom > r2.top + 50);
1857
1858         draw_growicon_vert_only(theWindow);
1859         DrawControls (theWindow);
1860
1861         h = (RgnHandle) 0;
1862         if (vis && (h = NewRgn ())) {
1863                 RgnHandle tmp = NewRgn ();
1864                 if (!tmp) {
1865                         DisposeRgn (h);
1866                         h = (RgnHandle) 0;
1867                 } else {
1868                         GetClip (h);
1869                         RectRgn (tmp, &r2);
1870                         DiffRgn (h, tmp, tmp);
1871                         SetClip (tmp);
1872                         DisposeRgn (tmp);
1873                 }
1874         }
1875         if (r.right < MIN_RIGHT)
1876                 r.right = MIN_RIGHT;
1877         r.top -= aWin->scrollPos * aWin->row_height;
1878         r.right -= SBARWIDTH;
1879         HLock (aWin->windowText);
1880         TETextBox (*aWin->windowText, aWin->windowTextLen, &r, teJustLeft);
1881         HUnlock (aWin->windowText);
1882         if (h) {
1883                 SetClip (h);
1884                 DisposeRgn (h);
1885         }
1886         return 0;
1887 }
1888
1889
1890 static void
1891 macCursorTerm (EventRecord *theEvent, WindowPtr theWindow, RgnHandle mouseRgn) {
1892         char *dir_bas, *dir;
1893         CursHandle ch;
1894         GrafPtr gp;
1895         NhWindow *nhw = GetNhWin (theWindow);
1896         Rect r = {0, 0, 1, 1};
1897
1898         GetPort (&gp);
1899         SetPortWindowPort(theWindow);
1900
1901         if (cursor_locked)
1902                 dir = (char *)0;
1903         else {
1904                 Point where = theEvent->where;
1905
1906                 GlobalToLocal (&where);
1907                 dir_bas = iflags.num_pad ? (char *) ndir : (char *) sdir;
1908                 dir = strchr (dir_bas, *click_to_cmd (where.h / nhw->char_width + 1 ,
1909                                                         where.v / nhw->row_height, CLICK_1));
1910         }
1911         ch = GetCursor (dir ? dir - dir_bas + 513 : 512);
1912         if (ch) {
1913                 HLock ((Handle) ch);
1914                 SetCursor (*ch);
1915                 HUnlock ((Handle) ch);
1916
1917         } else {
1918                 SetCursor(&qdarrow);
1919         }
1920         OffsetRect (&r, theEvent->where.h, theEvent->where.v);
1921         RectRgn (mouseRgn, &r);
1922         SetPort (gp);
1923 }
1924
1925
1926 static void
1927 GeneralCursor (EventRecord *theEvent, WindowPtr theWindow, RgnHandle mouseRgn) {
1928 #if defined(__SC__) || defined(__MRC__)
1929 # pragma unused(theWindow)
1930 #endif
1931         Rect r = {-1, -1, 2, 2};
1932
1933         SetCursor(&qdarrow);
1934         OffsetRect (&r, theEvent->where.h, theEvent->where.v);
1935         RectRgn (mouseRgn, &r);
1936 }
1937
1938
1939 static void
1940 HandleKey (EventRecord *theEvent) {
1941         WindowPtr theWindow = FrontWindow ();
1942
1943         if (theEvent->modifiers & cmdKey) {
1944                 if (theEvent->message & 0xff == '.') {
1945                         /* Flush key queue */
1946                         keyQueueCount = keyQueueWrite = keyQueueRead = 0;
1947                         theEvent->message = '\033';
1948                         goto dispatchKey;
1949                 } else {
1950                         UndimMenuBar ();
1951                         DoMenuEvt (MenuKey (theEvent->message & 0xff));
1952                 }
1953         } else {
1954
1955 dispatchKey :
1956                 if (theWindow) {
1957                         int kind = GetWindowKind(theWindow) - WIN_BASE_KIND;
1958                         winKeyFuncs [kind] (theEvent, theWindow);
1959                 } else {
1960                         GeneralKey (theEvent, (WindowPtr) 0);
1961                 }
1962         }
1963 }
1964
1965
1966 static void
1967 WindowGoAway (EventRecord *theEvent, WindowPtr theWindow) {
1968         NhWindow *aWin = GetNhWin(theWindow);
1969
1970         if (!theEvent || TrackGoAway (theWindow, theEvent->where)) {
1971                 if (aWin - theWindows == BASE_WINDOW && !iflags.window_inited) {
1972                         AddToKeyQueue ('\033', 1);
1973                 } else {
1974                         HideWindow (theWindow);
1975                         if (aWin - theWindows != inSelect)
1976                                 mac_destroy_nhwindow (aWin - theWindows);
1977                         else                                                            /* if this IS the inSelect window put a close char */
1978                                 AddToKeyQueue (CHAR_CR, 1);             /* in queue to exit and maintain inSelect */
1979                 }
1980         }
1981 }
1982
1983
1984 static void
1985 HandleClick (EventRecord *theEvent) {
1986         int code;
1987         unsigned long l;
1988         WindowPtr theWindow;
1989         NhWindow *aWin;
1990         Rect r;
1991         Boolean not_inSelect;
1992
1993         InsetRect(GetRegionBounds(GetGrayRgn(), &r), 4, 4);
1994
1995         code = FindWindow (theEvent->where, &theWindow);
1996         aWin = GetNhWin (theWindow);
1997         not_inSelect = (inSelect == WIN_ERR || aWin - theWindows == inSelect);
1998         
1999         switch (code) {
2000         case inContent :
2001                 if (not_inSelect) {
2002                         int kind = GetWindowKind(theWindow) - WIN_BASE_KIND;
2003                         winCursorFuncs [kind] (theEvent, theWindow, gMouseRgn);
2004                         SelectWindow (theWindow);
2005                         SetPortWindowPort(theWindow);
2006                         winClickFuncs [kind] (theEvent, theWindow);
2007                 } else {
2008                         nhbell ();
2009                 }
2010                 break;
2011
2012         case inDrag :
2013                 if (not_inSelect) {
2014                         SetCursor(&qdarrow);
2015                         DragWindow (theWindow, theEvent->where, &r);
2016                         SaveWindowPos(theWindow);
2017                 } else {
2018                         nhbell ();
2019                 }
2020                 break;
2021
2022         case inGrow :
2023                 if (not_inSelect) {
2024                         SetCursor(&qdarrow);
2025                         SetRect (&r, 80, 2 * aWin->row_height + 1, r.right, r.bottom);
2026                         if (aWin == theWindows + WIN_MESSAGE)
2027                                 r.top += SBARHEIGHT;
2028                         l = GrowWindow (theWindow, theEvent->where, &r);
2029                         SizeWindow (theWindow, l & 0xffff, l >> 16, FALSE);
2030                         SaveWindowSize(theWindow);
2031                         SetPortWindowPort(theWindow);
2032                         GetWindowBounds(theWindow, kWindowContentRgn, &r);
2033                         OffsetRect(&r, -r.left, -r.top);
2034                         InvalWindowRect(theWindow, &r);
2035                         if (aWin->scrollBar) {
2036                                 DrawScrollbar (aWin);
2037                         }
2038                 } else {
2039                         nhbell ();
2040                 }
2041                 break;
2042
2043         case inGoAway :
2044                 WindowGoAway(theEvent, theWindow);
2045                 break;
2046
2047         case inMenuBar :
2048                 DoMenuEvt (MenuSelect (theEvent->where));
2049                 break;
2050
2051 #if !TARGET_API_MAC_CARBON
2052         case inSysWindow :
2053                 SystemClick(theEvent, theWindow);
2054 #endif
2055         default :
2056                 break;
2057         }
2058 }
2059
2060
2061 static void
2062 HandleUpdate (EventRecord *theEvent) {
2063         WindowPtr theWindow = (WindowPtr) theEvent->message;
2064         NhWindow *aWin = GetNhWin (theWindow);
2065         Rect r;
2066
2067
2068         char existing_update_region = FALSE;
2069         Rect rect;
2070         
2071         if (theWindow == _mt_window) {
2072                 existing_update_region = (get_invalid_region (theWindow, &rect) == noErr);
2073         }
2074         BeginUpdate (theWindow);
2075         SetPortWindowPort(theWindow);
2076         GetWindowBounds(theWindow, kWindowContentRgn, &r);
2077         OffsetRect(&r, -r.left, -r.top);
2078         EraseRect(&r);
2079         winUpdateFuncs [GetWindowKind(theWindow) - WIN_BASE_KIND] 
2080                                 (theEvent, theWindow);
2081
2082         if (theWindow == _mt_window && existing_update_region) {
2083                 set_invalid_region (theWindow, &rect);
2084         }
2085         aWin->drawn = TRUE;
2086         EndUpdate (theWindow);
2087 }
2088
2089
2090 static void
2091 DoOsEvt (EventRecord *theEvent) {
2092         WindowRef win;
2093         short code;
2094
2095         if ((theEvent->message & 0xff000000) == 0xfa000000) {
2096                 /* Mouse Moved */
2097
2098                 code = FindWindow (theEvent->where, &win);
2099                 if (code != inContent) {
2100                         Rect r = {-1, -1, 2, 2};
2101
2102                         SetCursor(&qdarrow);
2103                         OffsetRect (&r, theEvent->where.h, theEvent->where.v);
2104                         RectRgn (gMouseRgn, &r);
2105                 } else {
2106                         int kind = GetWindowKind(win) - WIN_BASE_KIND;
2107                         if (kind >= 0 && kind <= NHW_TEXT) {
2108                                 winCursorFuncs [kind] (theEvent, win, gMouseRgn);
2109                         }
2110                 }
2111         }
2112 }
2113
2114
2115 void
2116 HandleEvent (EventRecord *theEvent) {
2117         switch (theEvent->what) {
2118         case autoKey :
2119         case keyDown :
2120                 HandleKey (theEvent);
2121                 break;
2122         case updateEvt :
2123                 HandleUpdate (theEvent);
2124                 break;
2125         case mouseDown :
2126                 HandleClick (theEvent);
2127                 break;
2128 #if !TARGET_API_MAC_CARBON
2129         case diskEvt :
2130                 if ((theEvent->message & 0xffff0000) != 0) {
2131                         Point p = {150, 150};
2132                         (void) DIBadMount (p, theEvent->message);
2133                 }
2134                 break;
2135 #endif
2136         case osEvt :
2137                 DoOsEvt (theEvent);
2138                 break;
2139         case kHighLevelEvent:
2140                 AEProcessAppleEvent(theEvent);
2141         default :
2142                 break;
2143         }
2144 }
2145
2146
2147 void
2148 mac_get_nh_event(void) {
2149         EventRecord anEvent;
2150
2151         /* KMH -- Don't proceed if the window system isn't set up */
2152         if (!iflags.window_inited)
2153                 return;
2154
2155         (void) WaitNextEvent (everyEvent, &anEvent, -1, gMouseRgn);
2156         HandleEvent(&anEvent);
2157 }
2158
2159
2160 int
2161 mac_nhgetch(void) {
2162         int ch;
2163         long doDawdle;
2164         EventRecord anEvent;
2165
2166         /* We want to take care of keys in the buffer as fast as
2167          * possible
2168          */
2169         if (keyQueueCount)
2170                 doDawdle = 0L;
2171         else {
2172                 long total, contig;
2173                 static char warn = 0;
2174
2175                 doDawdle = (in_topl_mode() ? GetCaretTime () : 120L);
2176                 /* Since we have time, check memory */
2177                 PurgeSpace (&total, &contig);
2178                 if (contig < 25000L || total < 50000L) {
2179                         if (!warn) {
2180                                 pline ("Low Memory!");
2181                                 warn = 1;
2182                         }
2183                 } else {
2184                         warn = 0;
2185                 }
2186         }
2187
2188         do {
2189                 (void) WaitNextEvent (everyEvent, &anEvent, doDawdle, gMouseRgn);
2190                 HandleEvent (&anEvent);
2191                 ch = GetFromKeyQueue ();
2192         } while (!ch && !gClickedToMove);
2193
2194         if (!gClickedToMove)
2195                 ObscureCursor ();
2196         else
2197                 gClickedToMove = 0;
2198
2199 #ifdef THINK_C
2200         if (ch == '\r') ch = '\n';
2201 #endif
2202
2203         return ch;
2204 }
2205
2206
2207 void
2208 mac_delay_output(void) {
2209         long destTicks = TickCount () + 1;
2210
2211         while (TickCount () < destTicks) {
2212                 mac_get_nh_event ();
2213         }
2214 }
2215
2216
2217 #ifdef CLIPPING
2218 static void
2219 mac_cliparound (int x, int y) {
2220 #if defined(__SC__) || defined(__MRC__)
2221 # pragma unused(x,y)
2222 #endif
2223         /* TODO */
2224 }
2225 #endif
2226
2227 void
2228 mac_exit_nhwindows (const char *s) {
2229         clear_screen ();
2230         tty_exit_nhwindows (s);
2231         mac_destroy_nhwindow (WIN_MESSAGE);
2232         mac_destroy_nhwindow (WIN_INVEN);
2233 }
2234
2235
2236 /*
2237  * Don't forget to decrease in_putstr before returning...
2238  */
2239 void
2240 mac_putstr (winid win, int attr, const char *str) {
2241         long len, slen;
2242         NhWindow *aWin = &theWindows [win];
2243         static char in_putstr = 0;
2244         short newWidth, maxWidth;
2245         Rect r;
2246         char *src, *sline, *dst, ch;
2247
2248         if (win < 0 || win >= NUM_MACWINDOWS || !aWin->its_window) {
2249                 error ("putstr: Invalid win %d (Max %d).", win, NUM_MACWINDOWS, attr);
2250                 return;
2251         }
2252
2253         if (aWin->its_window == _mt_window) {
2254                 tty_putstr(win, attr, str);
2255                 return;
2256         }
2257
2258         if (in_putstr > 3)
2259                 return;
2260
2261         in_putstr ++;
2262         slen = strlen (str);
2263
2264         SetPortWindowPort(aWin->its_window);
2265         GetWindowBounds(aWin->its_window, kWindowContentRgn, &r);
2266         OffsetRect(&r, -r.left, -r.top);
2267         if (win == WIN_MESSAGE) {
2268                 r.right  -= SBARWIDTH;
2269                 r.bottom -= SBARHEIGHT;
2270                 if (flags.page_wait && 
2271                         aWin->last_more_lin <= aWin->y_size - (r.bottom - r.top) / aWin->row_height) {
2272                         aWin->last_more_lin = aWin->y_size;
2273                         mac_display_nhwindow(win, TRUE);
2274                 }
2275         }
2276
2277         /*
2278          * A "default" text window - uses TETextBox
2279          * We just add the text, without attributes for now
2280          */
2281         len = GetHandleSize (aWin->windowText);
2282         while (aWin->windowTextLen + slen + 1 > len) {
2283                 len = (len > 2048) ? (len + 2048) : (len * 2);
2284                 SetHandleSize (aWin->windowText, len);
2285                 if (MemError ()) {
2286                         error ("putstr: SetHandleSize");
2287                         aWin->windowTextLen = 0L;
2288                         aWin->save_lin = 0;
2289                         aWin->y_curs = 0;
2290                         aWin->y_size = 0;
2291                 }
2292         }
2293         
2294         len = aWin->windowTextLen;
2295         dst = *(aWin->windowText) + len;
2296         sline = src = (char *)str;
2297         maxWidth = newWidth = 0;
2298         for (ch = *src; ch; ch = *src) {
2299                 if (ch == CHAR_LF)
2300                         ch = CHAR_CR;
2301                 *dst++ = ch;
2302                 if (ch == CHAR_CR) {
2303                         aWin->y_curs ++;
2304                         aWin->y_size ++;
2305                         aWin->x_curs = 0;
2306                         newWidth = TextWidth (sline, 0, src - sline);
2307                         if (newWidth > maxWidth) {
2308                                 maxWidth = newWidth;
2309                         }
2310                         sline = src+1;  /* keep track of where new line begins */
2311                 }
2312                 else
2313                         aWin->x_curs ++;
2314                 src++;
2315         }
2316
2317         newWidth = TextWidth (sline, 0, src - sline);
2318         if (newWidth > maxWidth) {
2319                 maxWidth = newWidth;
2320         }
2321
2322         aWin->windowTextLen += slen;
2323         
2324         if (ch != CHAR_CR) {
2325                 (*(aWin->windowText)) [len + slen] = CHAR_CR;
2326                 aWin->windowTextLen ++;
2327                 aWin->y_curs ++;
2328                 aWin->y_size ++;
2329                 aWin->x_curs = 0;
2330         }
2331
2332         if (win == WIN_MESSAGE) {
2333                 short min = aWin->y_size - (r.bottom - r.top) / aWin->row_height;
2334                 if (aWin->scrollPos < min) {
2335                         aWin->scrollPos = min;
2336                         SetControlMaximum (aWin->scrollBar, aWin->y_size);
2337                         SetControlValue(aWin->scrollBar, min);
2338                 }
2339                 InvalWindowRect(aWin->its_window, &r);
2340         }
2341         else    /* Message has a fixed width, other windows base on content */
2342                 if (maxWidth > aWin->x_size)
2343                         aWin->x_size = maxWidth;
2344         in_putstr --;
2345 }
2346
2347
2348 void
2349 mac_curs (winid win, int x, int y) {
2350         NhWindow *aWin = &theWindows [win];
2351
2352         if (aWin->its_window == _mt_window) {
2353                 tty_curs(win, x, y);
2354                 return;
2355         }
2356
2357         SetPortWindowPort(aWin->its_window);
2358         MoveTo (x * aWin->char_width, (y * aWin->row_height) + aWin->ascent_height);
2359         aWin->x_curs = x;
2360         aWin->y_curs = y;
2361 }
2362
2363
2364 int
2365 mac_nh_poskey (int *a, int *b, int *c) {
2366         int ch = mac_nhgetch();
2367         *a = clicked_pos.h;
2368         *b = clicked_pos.v;
2369         *c = clicked_mod;
2370         return ch;
2371 }
2372
2373
2374 void
2375 mac_start_menu (winid win) {
2376         HideWindow (theWindows [win].its_window);
2377         mac_clear_nhwindow (win);
2378 }
2379
2380
2381 void
2382 mac_add_menu (winid win, int glyph, const anything *any, CHAR_P menuChar, CHAR_P groupAcc, int attr, const char *inStr, int preselected) {
2383 #if defined(__SC__) || defined(__MRC__)
2384 # pragma unused(glyph)
2385 #endif
2386         NhWindow *aWin = &theWindows [win];
2387         const char *str;
2388         char locStr[4+BUFSZ];
2389         MacMHMenuItem *item;
2390
2391         if (!inStr) return;
2392
2393         if (any->a_void != 0) {
2394
2395 #define kMenuSizeBump 26
2396                 if (!aWin->miSize) {
2397                         aWin->menuInfo = (MacMHMenuItem **)NewHandle(sizeof(MacMHMenuItem) * kMenuSizeBump);
2398                         if (!aWin->menuInfo) {
2399                                 error("Can't alloc menu handle");
2400                                 return;
2401                         }
2402                         aWin->menuSelected = (short **)NewHandle(sizeof(short) * kMenuSizeBump);
2403                         if (!aWin->menuSelected) {
2404                                 error("Can't alloc menu select handle");
2405                                 return;
2406                         }
2407                         aWin->miSize = kMenuSizeBump;
2408                 }
2409
2410                 if (aWin->miLen >= aWin->miSize) {
2411                         SetHandleSize((Handle)aWin->menuInfo, sizeof(MacMHMenuItem) * (aWin->miLen+kMenuSizeBump));
2412                         if (MemError()) {
2413                                 error("Can't resize menu handle");
2414                                 return;
2415                         }
2416                         SetHandleSize((Handle)aWin->menuSelected, sizeof(short) * (aWin->miLen+kMenuSizeBump));
2417                         if (MemError()) {
2418                                 error("Can't resize menu select handle");
2419                                 return;
2420                         }
2421                         aWin->miSize += kMenuSizeBump;
2422                 }
2423
2424                 if (menuChar == 0) {    
2425                         if (('a' <= aWin->menuChar && aWin->menuChar <= 'z') ||
2426                                 ('A' <= aWin->menuChar && aWin->menuChar <= 'Z')) {
2427                                 menuChar = aWin->menuChar++;
2428                                 if (menuChar == 'z')
2429                                         aWin->menuChar = 'A';
2430                         }
2431                 }
2432                 
2433                 Sprintf(locStr, "%c - %s", (menuChar ? menuChar : ' '), inStr);
2434                 str = locStr;
2435                 HLock ((char**)aWin->menuInfo);
2436                 HLock ((char**)aWin->menuSelected);
2437                 (*aWin->menuSelected)[aWin->miLen] = preselected;
2438                 item = &(*aWin->menuInfo)[aWin->miLen];
2439                 aWin->miLen++;
2440                 item->id = *any;
2441                 item->accelerator = menuChar;
2442                 item->groupAcc = groupAcc;
2443                 item->line = aWin->y_size;
2444                 HUnlock ((char**)aWin->menuInfo);
2445                 HUnlock ((char**)aWin->menuSelected);
2446         } else
2447                 str = inStr;
2448
2449         putstr (win, attr, str);
2450 }
2451
2452
2453 /*
2454  * End a menu in this window, window must a type NHW_MENU.
2455  * str is a list of cancel characters (values that may be input)
2456  * morestr is a prompt to display, rather than the default.
2457  * str and morestr might be ignored by some ports.
2458  */
2459 void
2460 mac_end_menu (winid win, const char *morestr) {
2461         Str255 buf;
2462         NhWindow *aWin = &theWindows [win];
2463
2464         buf [0] = 0;
2465         if (morestr) 
2466                 C2P (morestr, buf);
2467         SetWTitle (aWin->its_window, buf);
2468 }
2469
2470
2471 int
2472 mac_select_menu (winid win, int how, menu_item **selected_list) {
2473         int c;
2474         NhWindow *aWin = &theWindows [win];
2475         WindowPtr theWin = aWin->its_window;
2476
2477         inSelect = win;
2478
2479         mac_display_nhwindow (win, FALSE);
2480
2481         aWin->how = (short) how;
2482         for (;;) {
2483                 c = map_menu_cmd (mac_nhgetch());
2484                 if (c == CHAR_ESC) {
2485                         /* deselect everything */
2486                         aWin->miSelLen = 0;
2487                         break;
2488                 } else if (ClosingWindowChar(c)) {
2489                         break;
2490                 } else {
2491                         nhbell();
2492                 }
2493         }
2494
2495         HideWindow (theWin);
2496
2497         if (aWin->miSelLen) {
2498                 menu_item *mp;
2499                 MacMHMenuItem *mi;
2500                 *selected_list = mp = (menu_item *) alloc(aWin->miSelLen * sizeof(menu_item));
2501                 HLock ((char**)aWin->menuInfo);
2502                 HLock ((char**)aWin->menuSelected);
2503                 for (c = 0; c < aWin->miSelLen; c++) {
2504                         mi = &(*aWin->menuInfo)[(*aWin->menuSelected) [c]];
2505                         mp->item = mi->id;
2506                         mp->count = -1L;
2507                         mp++;
2508                 }
2509                 HUnlock ((char**)aWin->menuInfo);
2510                 HUnlock ((char**)aWin->menuSelected);
2511         } else
2512                 *selected_list = 0;
2513
2514         inSelect = WIN_ERR;
2515
2516         return aWin->miSelLen;
2517 }
2518
2519 #include "dlb.h"
2520
2521 static void
2522 mac_display_file (name, complain)
2523 const char *name;       /* not ANSI prototype because of boolean parameter */
2524 boolean complain;
2525 {
2526         Ptr buf;
2527         int win;
2528         dlb *fp = dlb_fopen(name, "r");
2529         
2530         if (fp) {
2531                 long l = dlb_fseek(fp, 0, SEEK_END);
2532                 (void) dlb_fseek(fp, 0, 0L);
2533                 buf = NewPtr(l+1);
2534                 if (buf) {
2535                         l = dlb_fread(buf, 1, l, fp);
2536                         if (l > 0) {
2537                                 buf[l] = '\0';
2538                                 win = create_nhwindow(NHW_TEXT);
2539                                 if (WIN_ERR == win) {
2540                                         if (complain) error ("Cannot make window.");
2541                                 } else {
2542                                         putstr(win, 0, buf);
2543                                         display_nhwindow(win, FALSE);
2544                                 }
2545                         }
2546                         DisposePtr(buf);
2547                 }
2548                 dlb_fclose(fp);
2549         } else if (complain)
2550                 error("Cannot open %s.", name);
2551 }
2552
2553
2554 void
2555 port_help () {
2556         display_file (PORT_HELP, TRUE);
2557 }
2558
2559
2560 static void
2561 mac_unimplemented (void) {
2562 }
2563
2564
2565 static void
2566 mac_suspend_nhwindows (const char *foo) {
2567 #if defined(__SC__) || defined(__MRC__)
2568 # pragma unused(foo)
2569 #endif
2570         /*      Can't really do that :-)                */
2571 }
2572
2573
2574 int
2575 try_key_queue (char *bufp) {
2576         if (keyQueueCount) {
2577                 char ch;
2578                 for (ch = GetFromKeyQueue(); ; ch = GetFromKeyQueue()) {
2579                         if (ch == CHAR_LF || ch == CHAR_CR)
2580                                 ch = 0;
2581                         *bufp++ = ch;
2582                         if (ch == 0)
2583                                 break;
2584                 }
2585                 return 1;
2586         }
2587         return 0;
2588 }
2589
2590 /* Interface definition, for windows.c */
2591 struct window_procs mac_procs = {
2592         "mac",
2593         WC_COLOR | WC_HILITE_PET |
2594         WC_FONT_MAP | WC_FONT_MENU | WC_FONT_MESSAGE | WC_FONT_STATUS | WC_FONT_TEXT |
2595         WC_FONTSIZ_MAP | WC_FONTSIZ_MENU | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_TEXT,
2596         0L,
2597         mac_init_nhwindows,
2598         mac_unimplemented,      /* see macmenu.c:mac_askname() for player selection */
2599         mac_askname,
2600         mac_get_nh_event,
2601         mac_exit_nhwindows,
2602         mac_suspend_nhwindows,
2603         mac_unimplemented,
2604         mac_create_nhwindow,
2605         mac_clear_nhwindow,
2606         mac_display_nhwindow,
2607         mac_destroy_nhwindow,
2608         mac_curs,
2609         mac_putstr,
2610         mac_display_file,
2611         mac_start_menu,
2612         mac_add_menu,
2613         mac_end_menu,
2614         mac_select_menu,
2615         genl_message_menu,
2616         mac_unimplemented,
2617         mac_get_nh_event,
2618         mac_get_nh_event,
2619 #ifdef CLIPPING
2620         mac_cliparound,
2621 #endif
2622 #ifdef POSITIONBAR
2623         donull,
2624 #endif
2625         tty_print_glyph,
2626         tty_raw_print,
2627         tty_raw_print_bold,
2628         mac_nhgetch,
2629         mac_nh_poskey,
2630         tty_nhbell,
2631         mac_doprev_message,
2632         mac_yn_function,
2633         mac_getlin,
2634         mac_get_ext_cmd,
2635         mac_number_pad,
2636         mac_delay_output,
2637 #ifdef CHANGE_COLOR
2638         tty_change_color,
2639         tty_change_background,
2640         set_tty_font_name,
2641         tty_get_color_string,
2642 #endif
2643 /* other defs that really should go away (they're tty specific) */
2644         0, //    mac_start_screen,
2645         0, //    mac_end_screen,
2646         genl_outrip,
2647         genl_preference_update,
2648 };
2649
2650 /*macwin.c*/