executor/src/aboutbox.c

822 lines
18 KiB
C

/* Copyright 1996 - 2000 by Abacus Research and
* Development, Inc. All rights reserved.
*/
#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_aboutbox[] =
"$Id: aboutbox.c 130 2006-05-10 17:21:35Z ctm $";
#endif
#include "rsys/common.h"
#include "rsys/aboutbox.h"
#include "rsys/mman.h"
#include "rsys/vdriver.h"
#include "rsys/string.h"
#include "rsys/cquick.h"
#include "rsys/quick.h"
#include "rsys/ctl.h"
#include "rsys/version.h"
#include "rsys/licensetext.h"
#include "CQuickDraw.h"
#include "Gestalt.h"
#include "ToolboxEvent.h"
#include "TextEdit.h"
#include "FontMgr.h"
#include "OSUtil.h"
#include <ctype.h>
#include <rsys/file.h>
#include <rsys/osutil.h>
#include <SegmentLdr.h>
#include <rsys/segment.h>
#include "rsys/notmac.h"
#include "rsys/custom.h"
#include "rsys/gestalt.h"
#include "rsys/osevent.h"
#define ABOUT_BOX_WIDTH 500
#define ABOUT_BOX_HEIGHT 300
#define BUTTON_WIDTH 85
#define BUTTON_HEIGHT 20
#define SCROLL_BAR_WIDTH 16
#define TE_MARGIN 4
#define TE_WIDTH (ABOUT_BOX_WIDTH - 20)
#define TE_HEIGHT (ABOUT_BOX_HEIGHT - 106)
#define TE_LEFT ((ABOUT_BOX_WIDTH - TE_WIDTH) / 2)
#define TE_RIGHT (TE_LEFT + TE_WIDTH)
#define TE_TOP 66
#define TE_BOTTOM (TE_TOP + TE_HEIGHT)
#define DONE_BUTTON_NAME "Accept"
#define LICENSE_BUTTON_NAME "License"
#define TIPS_BUTTON_NAME "Tips"
#define COPYRIGHT_STRING_1 "Copyright \251 ARDI 1986-2006"
#define COPYRIGHT_STRING_2 "All rights reserved."
static struct { char *name, *text; ControlHandle ctl; } about_box_buttons[] = {
{ LICENSE_BUTTON_NAME, NULL /* generated on the fly from licensetext.c */,
NULL
},
{ "Maker",
"ARDI\r"
"World Wide Web: <http://www.ardi.com>\r"
"FTP: <ftp://ftp.ardi.com/pub>\r",
NULL
},
{ "Credits",
"Bill Goldman \321 Browser, Testing\r"
"Mat Hostetter \321 Syn68k, Low Level Graphics, DOS port, more...\r"
#if defined (MSDOS)
"Joel Hunter \321 Low Level DOS Sound\r"
#endif
#if defined (CYGWIN32)
"Sam Lantinga \321 Win32 port\r"
#endif
"Patrick LoPresti \321 High Level Sound, Low Level Linux Sound\r"
"Cliff Matthews \321 this credit list (and most things not listed)\r"
"Cotton Seed \321 High Level Graphics, Apple Events, more...\r"
#if defined (MSDOS) || defined (CYGWIN32)
"Lauri Pesonen \321 Low Level Win32 CD-ROM access (Executor 2.1)\r"
#endif
#if defined (MSDOS)
"Samuel Vincent \321 Low Level DOS Serial Port Support\r"
#endif
"and all the engineers and testers who helped us build version 1.x\r"
"\r"
"Windows Appearance:\r"
"\r"
"The windows appearance option uses \"Jim's CDEFs\" copyright "
"Jim Stout and the "
"\"Infinity Windoid\" copyright Troy Gaul.\r"
"\r"
"Primary Pre-Beta Testers:\r"
"\r"
"Jon Abbott \321 Testing, Icon Design\r"
"Ziv Arazi \321 Testing\r"
"Edmund Ronald \321 Advice, Testing\r"
"K. Harrison Liang \321 Testing\r"
"Hugh Mclenaghan \321 Testing\r"
"Emilio Moreno \321 Testing, Spanish Translation + Keyboard, Icon Design\r"
"Ernst Oud \321 Documentation, Testing\r"
"\r"
"Some of the best development tools are Free and written by:\r"
"\r"
"The Free Software Foundation \321 the gcc compiler, more ...\r"
"Cygnus Support \321 the gdb debugger, the gnats bug tracking software\r"
"Linus Torvalds et al. \321 Linux, our favorite OS"
"\r"
#if defined (MSDOS)
"Executor/DOS was ported via DJGPP, DJ Delorie's port of gcc.\r"
"DJGPP's primary authors are:\r"
"\r"
"DJ Delorie \321 Ringleader\r"
"William Metzenthen \321 80387 emulator\r"
"Charles Sandmann \321 cwsdpmi, more...\r"
"Morten Welinder \321 misc.\r"
"Eli Zaretskii \321 Some DOS-related library functions\r"
"\rThis product includes software developed by "
"the University of California, Berkeley and its contributors\r"
#endif
, NULL
},
{
TIPS_BUTTON_NAME,
"Don't delete tips.txt. If you do, that will be your only tip.\r"
, NULL
},
{ DONE_BUTTON_NAME, "Internal error!", NULL }
};
static WindowPtr about_box;
static ControlHandle about_scrollbar;
static TEHandle about_te;
static ProcPtr scroll_bar_callback;
enum { THROTTLE_TICKS = 4 };
PRIVATE boolean_t
enough_time_has_passed (void)
{
static ULONGINT old_ticks;
ULONGINT new_ticks;
boolean_t retval;
new_ticks = TickCount ();
retval = new_ticks - old_ticks >= THROTTLE_TICKS;
if (retval)
old_ticks = new_ticks;
return retval;
}
/* Menu name for the about box. */
StringPtr about_box_menu_name_pstr = (StringPtr) "\022\000About Executor...";
static void
help_scroll (ControlHandle c, INTEGER part)
{
if (enough_time_has_passed ())
{
int page_size, old_value, new_value, delta;
int line_height;
old_value = GetCtlValue (c);
line_height = TE_LINE_HEIGHT (about_te);
page_size = RECT_HEIGHT (&CTL_RECT (c)) / line_height;
switch (part)
{
case inUpButton:
SetCtlValue (c, old_value - 1);
break;
case inDownButton:
SetCtlValue (c, old_value + 1);
break;
case inPageUp:
SetCtlValue (c, old_value - page_size);
break;
case inPageDown:
SetCtlValue (c, old_value + page_size);
break;
default:
break;
}
new_value = GetCtlValue (about_scrollbar);
delta = new_value - old_value;
if (delta != 0)
TEScroll (0, -delta * line_height, about_te);
}
}
static syn68k_addr_t
scroll_stub (syn68k_addr_t junk, void *junk2)
{
syn68k_addr_t retaddr;
uint32 ctl;
INTEGER part;
retaddr = POPADDR ();
part = POPUW ();
ctl = (uint32) SYN68K_TO_US (POPUL ());
help_scroll ((ControlHandle) ctl, part);
return retaddr;
}
static int
find_button (const char *button_name)
{
int i;
for (i = 0; i < (int) NELEM (about_box_buttons); i++)
if (!strcmp (about_box_buttons[i].name, button_name))
break;
gui_assert (i < (int) NELEM (about_box_buttons));
return i;
}
/* Returns the index of the button with name LICENSE_BUTTON_NAME. */
static int
find_license_button (void)
{
int retval;
retval = find_button (LICENSE_BUTTON_NAME);
return retval;
}
static void
create_license_text (void)
{
int b;
b = find_license_button ();
if (about_box_buttons[b].text == NULL)
{
long new_size;
char *license_text, *p, *q, *licensep;
new_size = 0;
/* Compute the length of the license string. */
for (licensep = ROMlib_licensep ? (char *) ROMlib_licensep->chars : "";
*licensep;)
{
int title_len;
int body_len;
title_len = strlen (licensep);
licensep += title_len + 1;
body_len = strlen (licensep);
licensep += body_len + 1;
new_size += title_len + body_len + 5;
}
/* Allocate and construct the license string. */
license_text = (char *) NewPtrSys (new_size + 1);
license_text[0] = '\0';
p = license_text;
for (licensep = ROMlib_licensep ? (char *) ROMlib_licensep->chars : "";
*licensep;)
{
const char *titlep;
const char *bodyp;
titlep = licensep;
licensep += strlen (licensep) + 1;
bodyp = licensep;
licensep += strlen (licensep) + 1;
p += sprintf (p, "%s\r\r%s\r\r\r", titlep, bodyp);
}
/* Nuke any trailing whitespace, and end with exactly one linefeed */
for (q = p - 1; q >= license_text && isspace (*q); q--)
*q = '\0';
if (q >= license_text)
strcpy (q + 1, "\r");
about_box_buttons[b].text = license_text;
}
}
/*
* get an upper bound on the number of tips that may be in the buffer
*/
static int
approximate_tips (char *p)
{
int retval;
retval = 0;
while (*p)
{
++retval;
while (*p && *p != '\n')
++p;
while (*p && *p == '\n')
++p;
}
return retval;
}
typedef struct
{
int tip_offset;
int tip_length;
}
tip_t;
static int
find_tips (tip_t *tips, const char *p)
{
const char *orig_p;
int retval;
retval = 0;
orig_p = p;
while (*p)
{
/* suck up any excess leading \n */
while (*p && *p == '\n')
++p;
if (*p)
{
tips->tip_offset = p - orig_p;
while (*p && (*p != '\n' || (p[1] && p[1] != '\n')))
++p;
tips->tip_length = p - orig_p - tips->tip_offset;
++retval;
++tips;
}
}
return retval;
}
static void
add_to_str (char **pp, char *ip, int n)
{
int i;
for (i = 0; i < n; ++i)
(*pp)[i] = ip[i] == '\n' ? ' ' : ip[i];
*pp += n;
}
static char *
parse_and_randomize_tips (char *buf)
{
int n_tips, chars_needed;
tip_t *tips;
boolean_t seen_tip;
char *retval;
char *p;
int i;
n_tips = approximate_tips (buf);
tips = alloca (n_tips * sizeof *tips);
n_tips = find_tips (tips, buf);
chars_needed = 0;
for (i = 0; i < n_tips; ++i)
chars_needed += tips[i].tip_length;
chars_needed += 2 * (n_tips - 1) + 1;
retval = malloc (chars_needed);
seen_tip = FALSE;
p = retval;
while (n_tips)
{
if (seen_tip)
add_to_str (&p, "\r\r", 2);
else
seen_tip = TRUE;
i = rand () % n_tips;
add_to_str (&p, buf+tips[i].tip_offset, tips[i].tip_length);
tips[i] = tips[n_tips-1];
--n_tips;
}
*p = 0;
return retval;
}
static char *orig_text;
static void
create_tips_text (void)
{
char *filename;
FILE *fp;
int b;
b = find_button (TIPS_BUTTON_NAME);
if (!orig_text)
{
srand (Ticks); /* NOTE: we don't care that rand/srand is not a
particular good way to generate random numbers */
orig_text = about_box_buttons[b].text;
}
if (about_box_buttons[b].text == orig_text)
{
#if !defined (LINUX)
filename = copystr ("+/tips.txt");
#else
filename = copystr ("/opt/executor/tips.txt");
#endif
fp = fopen (filename, "r");
free (filename);
if (fp)
{
char tempbuf[32768];
size_t nread;
nread = fread (tempbuf, 1, sizeof tempbuf -1, fp);
tempbuf[nread] = 0;
if (nread > 0)
about_box_buttons[b].text = parse_and_randomize_tips (tempbuf);
fclose (fp);
}
}
}
static void
dispose_license_text (void)
{
int b;
b = find_license_button ();
if (about_box_buttons[b].text != NULL)
{
DisposPtr ((Ptr) about_box_buttons[b].text);
about_box_buttons[b].text = NULL;
}
}
static void
dispose_tips_text (void)
{
int b;
b = find_button (TIPS_BUTTON_NAME);
if (about_box_buttons[b].text != orig_text)
{
free (about_box_buttons[b].text);
about_box_buttons[b].text = orig_text;
}
}
static void
create_about_box ()
{
static Rect scroll_bar_bounds = {
CWC (TE_TOP),
CWC (TE_RIGHT - SCROLL_BAR_WIDTH),
CWC (TE_BOTTOM),
CWC (TE_RIGHT)
};
static Rect te_bounds = {
CWC (TE_TOP + 1),
CWC (TE_LEFT + TE_MARGIN),
CWC (TE_BOTTOM - 1),
CWC (TE_RIGHT - TE_MARGIN - SCROLL_BAR_WIDTH)
};
Rect about_box_bounds;
int b;
create_license_text ();
create_tips_text ();
SetRect (&about_box_bounds,
(vdriver_width - ABOUT_BOX_WIDTH) / 2U,
(vdriver_height - ABOUT_BOX_HEIGHT) / 3U + 15,
(vdriver_width + ABOUT_BOX_WIDTH) / 2U,
(vdriver_height + 2 * ABOUT_BOX_HEIGHT) / 3U + 15);
/* Create the window. */
about_box = (WindowPtr) NewCWindow (NULL, &about_box_bounds,
(StringPtr) "\016About Executor",
FALSE, dBoxProc, (CWindowPtr) -1,
TRUE, /* go away flag */
-5 /* unused */);
THEPORT_SAVE_EXCURSION
(about_box,
{
/* Create the push buttons. */
for (b = 0; b < (int) NELEM (about_box_buttons); b++)
{
Str255 str;
Rect r;
/* Set up the rectangle enclosing each button. */
r.top = CWC (ABOUT_BOX_HEIGHT - 30);
r.bottom = CWC (ABOUT_BOX_HEIGHT - 30 + BUTTON_HEIGHT);
r.left = CW ((b * ABOUT_BOX_WIDTH / NELEM (about_box_buttons))
+ (ABOUT_BOX_WIDTH / NELEM (about_box_buttons)
- BUTTON_WIDTH) / 2);
r.right = CW (CW (r.left) + BUTTON_WIDTH);
str255_from_c_string (str, about_box_buttons[b].name);
about_box_buttons[b].ctl = NewControl (about_box, &r, str, TRUE, 0,
0, 1, pushButProc, b);
}
about_scrollbar = NewControl (about_box, &scroll_bar_bounds, NULL, TRUE,
0, 0, 100, scrollBarProc, -1);
about_te = TENew (&te_bounds, &te_bounds);
TESetJust (teFlushLeft, about_te);
});
}
/* Closes about box and frees up memory taken by it. */
static void
dispose_about_box (void)
{
C_DisposeWindow (about_box);
about_box = NULL;
about_scrollbar = NULL;
dispose_license_text ();
dispose_tips_text ();
}
/* Sets the text currently being displayed. */
static void
set_text (char *text)
{
TESetText ((Ptr) text, strlen (text), about_te);
SetCtlMax (about_scrollbar,
MAX (0, (TE_N_LINES (about_te)
- ((TE_HEIGHT - 2) / TE_LINE_HEIGHT (about_te)))));
SetCtlValue (about_scrollbar, 0);
TE_DEST_RECT (about_te) = TE_VIEW_RECT (about_te);
InvalRect (&TE_VIEW_RECT (about_te));
}
/* Specifies which button is currently pressed. */
static void
set_current_button (int button)
{
int i;
set_text (about_box_buttons[button].text);
for (i = 0; i < (int) NELEM (about_box_buttons); i++)
HiliteControl (about_box_buttons[i].ctl, (i == button) ? 255 : 0);
}
static void
draw_mem_string (char *s, int y)
{
MoveTo ((ABOUT_BOX_WIDTH - 9 - TextWidth ((Ptr) s, 0, strlen (s))), y);
DrawText_c_string (s);
}
static void
draw_status_info (boolean_t executor_p)
{
char total_ram_string[128];
char applzone_ram_string[128];
char syszone_ram_string[128];
boolean_t gestalt_success_p;
LONGINT total_ram;
const char *ram_tag, *system_ram_tag, *application_ram_tag;
if (executor_p)
{
ram_tag = "Emulated RAM: ";
system_ram_tag = "System RAM free: ";
application_ram_tag = "Application RAM free: ";
}
else
{
ram_tag = "";
system_ram_tag = "";
application_ram_tag = "";
}
/* Compute a string for total RAM. */
#define MB (1024 * 1024U)
gestalt_success_p = (C_GestaltTablesOnly (gestaltLogicalRAMSize, &total_ram)
== noErr);
total_ram = CL (total_ram);
if (gestalt_success_p)
sprintf (total_ram_string, "%s%u.%02u MB", ram_tag,
total_ram / MB, (total_ram % MB) * 100 / MB);
else
sprintf (total_ram_string, "%s??? MB", ram_tag);
/* Compute a string for ApplZone RAM. */
ZONE_SAVE_EXCURSION
(ApplZone,
{
sprintf (applzone_ram_string, "%s%lu KB / %u KB", application_ram_tag,
FreeMem () / 1024UL, ROMlib_applzone_size / 1024U);
});
/* Compute a string for SysZone RAM. */
sprintf (syszone_ram_string, "%s%lu KB / %u KB", system_ram_tag,
FreeMemSys () / 1024UL, ROMlib_syszone_size / 1024U);
draw_mem_string (total_ram_string, 20);
draw_mem_string (syszone_ram_string, 36);
draw_mem_string (applzone_ram_string, 52);
}
static void
event_loop (boolean_t executor_p)
{
static Rect frame_rect = {
CWC (TE_TOP),
CWC (TE_LEFT),
CWC (TE_BOTTOM),
CWC (TE_RIGHT - SCROLL_BAR_WIDTH + 1)
};
EventRecord evt;
boolean_t done_p;
int which_text;
Rect te_dest_rect;
int old_scroll_bar_value;
/* Make sure our drawing mode is sane. */
ForeColor (blackColor);
BackColor (whiteColor);
PenNormal ();
te_dest_rect = TE_DEST_RECT (about_te);
set_current_button (0);
which_text = 0;
InvalRect (&about_box->portRect);
old_scroll_bar_value = GetCtlValue (about_scrollbar);
for (done_p = FALSE; !done_p; )
{
GetNextEvent (( mDownMask | mUpMask
| keyDownMask | keyUpMask | autoKeyMask
| updateMask | activMask), &evt);
TEIdle (about_te);
switch (CW (evt.what))
{
case updateEvt:
BeginUpdate (about_box);
PenNormal ();
ForeColor (blackColor);
BackColor (whiteColor);
EraseRect (&about_box->portRect);
TextFont (helvetica);
TextSize (24);
MoveTo (TE_LEFT, 30);
if (executor_p)
DrawText_c_string ((char *) ROMlib_executor_full_name);
else
DrawText_c_string ((char *) "Carbonless Copies Runtime System");
TextSize (12);
MoveTo (TE_LEFT, 49);
DrawText_c_string (COPYRIGHT_STRING_1);
MoveTo (TE_LEFT, 62);
DrawText_c_string (COPYRIGHT_STRING_2);
draw_status_info (executor_p);
FrameRect (&frame_rect);
TEUpdate (&te_dest_rect, about_te);
DrawControls (about_box);
EndUpdate (about_box);
break;
case keyDown:
case autoKey:
{
char ch;
ch = CL (evt.message) & 0xFF;
switch (ch)
{
case '\r':
case NUMPAD_ENTER:
done_p = TRUE;
break;
default:
TEKey (ch, about_te);
break;
}
}
break;
#define _FindControl(arg0, arg1, arg2) \
({ \
int16 retval; \
HIDDEN_ControlHandle bogo_c; \
\
retval = FindControl (arg0, arg1, &bogo_c); \
*(arg2) = MR (bogo_c.p); \
\
retval; \
})
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, about_box, &c);
if (!control_p)
SysBeep (1);
else
{
if (c == about_scrollbar)
{
int new_val, delta;
INTEGER part;
part = TestControl (c, local_pt);
if (TrackControl (c, local_pt,
(part == inThumb
? (ProcPtr) -1
: scroll_bar_callback))
== inThumb)
{
new_val = GetCtlValue (about_scrollbar);
delta = new_val - old_scroll_bar_value;
if (delta != 0)
{
TEScroll (0, -delta * TE_LINE_HEIGHT (about_te),
about_te);
}
}
old_scroll_bar_value = GetCtlValue (about_scrollbar);
}
else if (TrackControl (c, local_pt, (ProcPtr) -1)
== inButton)
{
int new_text;
new_text = CTL_REF_CON (c);
if (new_text != which_text)
{
if (!strcmp (about_box_buttons[new_text].name,
DONE_BUTTON_NAME))
done_p = TRUE;
else
{
set_current_button (new_text);
which_text = new_text;
}
}
}
}
}
break;
case activateEvt:
case mouseUp:
case keyUp:
break;
}
}
}
void
do_about_box (void)
{
static boolean_t busy_p = FALSE;
if (!busy_p)
{
busy_p = TRUE; /* Only allow one about box at a time. */
if (scroll_bar_callback == 0)
scroll_bar_callback = (ProcPtr) SYN68K_TO_US(callback_install (scroll_stub, NULL));
ZONE_SAVE_EXCURSION
(SysZone,
{
create_about_box ();
THEPORT_SAVE_EXCURSION
(about_box,
{
C_ShowWindow (about_box);
event_loop (strncasecmp (ROMlib_appname, EXECUTOR_NAME,
sizeof EXECUTOR_NAME - 1) == 0);
});
TEDispose (about_te);
about_te = NULL;
dispose_about_box ();
});
busy_p = FALSE;
}
}