/* Copyright 1995 by Abacus Research and * Development, Inc. All rights reserved. */ #if !defined (OMIT_RCSID_STRINGS) char ROMlib_rcsid_system_error[] = "$Id: system_error.c 88 2005-05-25 03:59:37Z ctm $"; #endif #include "rsys/common.h" #include "WindowMgr.h" #include "ControlMgr.h" #include "QuickDraw.h" #include "CQuickDraw.h" #include "TextEdit.h" #include "EventMgr.h" #include "ToolboxEvent.h" #include "OSEvent.h" #include "rsys/system_error.h" #include "rsys/redrawscreen.h" #include "rsys/cquick.h" #include "rsys/osevent.h" #include "rsys/options.h" #define N_BUTTONS (3) /* sanity defines */ #define _NewCWindow(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ ((WindowPtr) NewCWindow (arg0, arg1, arg2, arg3, arg4, \ (CWindowPtr) (arg5), arg6, arg7)) #define _FindControl(arg0, arg1, arg2) \ ({ \ int16 retval; \ HIDDEN_ControlHandle bogo_c; \ \ retval = FindControl (arg0, arg1, &bogo_c); \ *(arg2) = MR (bogo_c.p); \ \ retval; \ }) struct button { ControlHandle ctl; const char *text; system_error_callback_t func; boolean_t hilite_p; Rect hilite_rect; int oval; int index; }; static struct button buttons[3]; static WindowPtr msg_window; static const char *message; static int default_button; static Rect message_rect; static struct button * event_loop (void) { EventRecord evt; Point where; for (;;) { GetNextEvent (( mDownMask | updateMask | keyDownMask | autoKeyMask), &evt); where = SWAP_POINT (evt.where); switch (CW (evt.what)) { case mouseDown: { Point local_pt; boolean_t control_p; ControlHandle c; local_pt = evt.where; GlobalToLocal (&local_pt); local_pt = SWAP_POINT (local_pt); control_p = _FindControl (local_pt, msg_window, &c); if (control_p) { int release_part; release_part = TrackControl (c, local_pt, (ProcPtr) -1); if (release_part == inButton) { int i; for (i = 0; i < N_BUTTONS; i ++) if (c == buttons[i].ctl) return &buttons[i]; } } } case updateEvt: { int i; BeginUpdate (msg_window); DrawControls (msg_window); PenSize (3, 3); for (i = 0; i < N_BUTTONS; i ++) if (buttons[i].hilite_p) { if (!(ROMlib_options & ROMLIB_RECT_SCREEN_BIT)) FrameRoundRect (&buttons[i].hilite_rect, buttons[i].oval, buttons[i].oval); else FrameRect (&buttons[i].hilite_rect); } /* ### `TextBox ()' should be const */ TextBox ((Ptr) message, strlen (message), &message_rect, teFlushLeft); EndUpdate (msg_window); break; } case keyDown: case autoKey: { char ch; ch = CL (evt.message) & 0xFF; if (ch == '\r' || ch == NUMPAD_ENTER) { int i; for (i = 0; i < N_BUTTONS; i ++) if (buttons[i].hilite_p) return &buttons[i]; } break; } } } } static int int_sqrt (int x) { int i; gui_assert (x >= 0); for (i = 0; i * i <= x; i++) ; return i - 1; } int system_error (const char *_message, int _default_button, const char *button0, const char *button1, const char *button2, system_error_callback_t func0, system_error_callback_t func1, system_error_callback_t func2) { static Rect dummy_rect; struct button *retval; message = _message; default_button = _default_button; buttons[0].text = button0; buttons[0].func = func0; buttons[0].index = 0; buttons[1].text = button1; buttons[1].func = func1; buttons[1].index = 1; buttons[2].text = button2; buttons[2].func = func2; buttons[2].index = 2; #if 1 /* NOTE: when system_error() is called from InitWindows() the colors are messed up. I don't know what the proper solution is, but this works for now. */ redraw_screen (); /* NOTE: initializing the cursor here is real rude, and makes it so that we really can't call system_error at random times (but then again, we never really could). But since system_error is primarily used to print messages from InitWindows(), it's important that we somehow manage to get the cursor visible, since people are given the option of exiting but with no way to click on the button. */ InitCursor (); #endif /* allocate the message window with a bogus size; the text settings for this window will determine it's eventual size, at which point it will be resized and made visible */ msg_window = _NewCWindow (NULL, &dummy_rect, /* no title */ NULL, /* invisible */ FALSE, dBoxProc, (WindowPtr) -1, FALSE, /* dummy */ -1); THEPORT_SAVE_EXCURSION (msg_window, { FontInfo font_info; int total_message_width; int text_height; int line_count; int message_width; int message_height; int button_span; int width; int height; int max_button_width; int i; /* compute the geometry of the various dialog components so we can determine the dialog's size */ total_message_width = TextWidth ((Ptr) &message[0], 0, strlen (message)); GetFontInfo (&font_info); text_height = ( CW (font_info.ascent) + CW (font_info.descent) + CW (font_info.leading)); line_count /* must be at least one line of text, otherwise we'll get hit with a div by zero below */ = MAX (int_sqrt (total_message_width / (4 * text_height)), 1); max_button_width = -1; for (i = 0; i < N_BUTTONS; i ++) if (buttons[i].text != NULL) max_button_width = MAX (max_button_width, /* ### `TextWidth ()' should be const */ TextWidth ((Ptr) buttons[i].text, 0, strlen (buttons[i].text))); /* padding for button outline, etc */ max_button_width += 16; message_width = total_message_width / line_count; message_height = (line_count + 2) * text_height; button_span = (max_button_width + 13) * N_BUTTONS - 13; if (message_width < button_span) message_width = button_span; width = message_width + 20; height = message_height + 20 + 20 + 13; SizeWindow (msg_window, width, height, FALSE); /* compute top/left based on screen dimensions */ { Rect *gd_rect; int gd_width; int gd_height; int top; int left; gd_rect = &GD_RECT (MR (MainDevice)); gd_width = RECT_WIDTH (gd_rect); gd_height = RECT_HEIGHT (gd_rect); /* centered horizontally, with a third of the space above the window, and two-thirds below */ top = CW (gd_rect->top) + (gd_height - height) / 3; left = CW (gd_rect->left) + (gd_width - width) / 2; MoveWindow (msg_window, left, top, TRUE); } ShowWindow (msg_window); message_rect.top = CWC (10); message_rect.left = CWC (10); message_rect.bottom = CW (message_height + 10); message_rect.right = CW (message_width + 10); for (i = 0; i < N_BUTTONS; i ++) if (buttons[i].text != NULL) { Rect ctl_rect; unsigned char buf[256]; ctl_rect.top = CW (height - 10 - 20); ctl_rect.bottom = CW (height - 10); ctl_rect.right = CW ( width - 10 - i * (max_button_width + 13)); ctl_rect.left = CW ( width - 10 - i * (max_button_width + 13) - max_button_width); *buf = (unsigned char) strlen (buttons[i].text); strcpy ((char *) &buf[1], buttons[i].text); buttons[i].ctl = NewControl (msg_window, &ctl_rect, /* ### `NewControl ()' should be const */ buf, /* visible */ TRUE, 0, 0, 1, pushButProc, /* dummy */ -1); if (i == default_button) { buttons[i].hilite_rect = ctl_rect; InsetRect (&buttons[i].hilite_rect, -4, -4); buttons[i].oval = RECT_HEIGHT (&buttons[i].hilite_rect) / 2 - 4; buttons[i].hilite_p = TRUE; } else buttons[i].hilite_p = FALSE; } retval = event_loop (); }); DisposeWindow (msg_window); if (retval->func) (*retval->func) (); return retval->index; }