eigenmath/MainOSX.cpp

1541 lines
32 KiB
C++

/* Eigenmath by George Weigt
This file does all the things required by the OS X API.
The starting point for a symbolic computation is in run.cpp
Input is scanned in scan.cpp
Expression evaluation is done in eval.cpp
Output is formatted in cmdisplay.cpp
The window display code is in window.cpp
*/
#include <Carbon/Carbon.h>
#include "YASTControl.h"
#define SMALL_FONT 1
#define DEFAULT_FONT 2
#define TIMES_FONT 3
#define ITALIC_TIMES_FONT 4
#define SYMBOL_FONT 5
#define ITALIC_SYMBOL_FONT 6
#define SMALL_TIMES_FONT 7
#define SMALL_ITALIC_TIMES_FONT 8
#define SMALL_SYMBOL_FONT 9
#define SMALL_ITALIC_SYMBOL_FONT 10
#if 1
#define DEFAULT_WIDTH 680
#define DEFAULT_HEIGHT 480
#else
#define DEFAULT_WIDTH 400
#define DEFAULT_HEIGHT 240
#endif
#define WINATTR (kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute)
int client_width = DEFAULT_WIDTH;
int client_height = DEFAULT_HEIGHT;
int line_height = 20;
int left_margin = 5;
int right_margin = 5;
int display_x;
int display_y;
int display_width;
int display_height;
int max_x;
int max_y;
int total_w;
int total_h;
int scroll_bar_dim = 15;
int grow_dim = 0; // set to 15 to remove overlap w/grow control
int input_control_height = 28;
int update_display_request;
extern int esc_flag;
static int running;
static unsigned int timer;
static int edit_mode;
struct text_metric {
int ascent, descent, width;
} text_metric[11];
#define NBUTTONS 8
char *button_cmd[NBUTTONS - 2] = {
"clear",
"draw",
"simplify",
"factor",
"derivative",
"integral",
};
char *button_name[NBUTTONS] = {
"Clear",
"Draw",
"Simplify",
"Factor",
"Derivative",
"Integral",
"Edit Script",
"Run Script",
};
#define YYFONT 0
#define YYSIZE 13
pascal OSStatus MainWindowCommandHandler( EventHandlerCallRef handlerRef, EventRef event, void *userData );
extern void do_up_arrow(void);
extern void do_down_arrow(void);
extern void printstr(char *);
extern void clear(void);
extern void draw_display(void);
extern void run(char *);
extern char *get_tty_buf(void);
void do_control_stuff(void);
void create_input_control(void);
void create_edit_control(void);
void create_graph_control(void);
void create_enter_button(void);
void create_run_button(void);
void create_graph_window(void);
void create_main_window(void);
void update_curr_cmd(char *);
static void file_svas(void);
static void do_save(void);
static void file_open(void);
static void update_edit_control(void);
static void select_font(int);
static void update_scroll_bars(void);
static void vscroll_f(ControlRef, ControlPartCode);
static void hscroll_f(ControlRef, ControlPartCode);
static void do_return_key(void);
static void do_button(char *);
static void draw_display_now(void);
static void send_user_event(void);
static void process_user_event(void);
static void get_script(void);
static void do_resize(void);
static void copy_display(void);
static void copy_tty(void);
static void update_display(void);
static void create_controls(void);
static void remove_controls(void);
static void change_modes(void);
static void create_task(char *);
static void go_to_calc_mode(void);
static void go_to_edit_mode(void);
static void activate_controls(void);
static void deactivate_controls(void);
WindowRef gwindow;
ControlHandle inputcontrol;
ControlHandle edit_control;
ControlHandle hscroll;
ControlHandle vscroll;
ControlHandle buttons[NBUTTONS];
char filename[1000], path[1000];
#define SCRIPT_BUF_LEN 100000
static char script_buf[SCRIPT_BUF_LEN + 1];
#define TMP_BUF_LEN 10000
static char tmp_buf[TMP_BUF_LEN + 1];
static char *inp;
#if 0
/* Register help book */
static void
register_help(void)
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
if (mainBundle)
{
CFURLRef bundleURL = NULL;
CFRetain(mainBundle);
bundleURL = CFBundleCopyBundleURL(mainBundle);
if (bundleURL)
{
FSRef bundleFSRef;
if (CFURLGetFSRef(bundleURL, &bundleFSRef)) {
FSRefMakePath(&bundleFSRef, (UInt8 *) path, 1000);
AHRegisterHelpBook(&bundleFSRef);
}
CFRelease(bundleURL);
}
CFRelease(mainBundle);
}
}
#endif
static void
timer_function(EventLoopTimerRef ref, void *data)
{
if (running)
send_user_event();
}
#define NEVENT 6
EventTypeSpec commSpec[NEVENT] = {
{kEventClassCommand, kEventProcessCommand},
{kEventClassKeyboard, kEventRawKeyDown},
{kEventClassKeyboard, 1234},
{kEventClassWindow, kEventWindowResizeCompleted},
{kEventClassWindow, kEventWindowZoomed},
{kEventClassWindow, kEventWindowClosed},
};
int main(int argc, char* argv[])
{
IBNibRef nibRef;
OSStatus err;
err = TXNInitTextension(NULL, 0, 0);
//require_noerr( err, CantGetNibRef );
// CreateStandardWindowMenu(0, NULL);
// Create a Nib reference passing the name of the nib file (without the .nib extension)
// CreateNibReference only searches into the application bundle.
err = CreateNibReference(CFSTR("main"), &nibRef);
require_noerr( err, CantGetNibRef );
// Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar
// object. This name is set in InterfaceBuilder when the nib is created.
err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar"));
require_noerr( err, CantSetMenuBar );
// We don't need the nib reference anymore.
DisposeNibReference(nibRef);
//register_help();
// The window was created hidden so show it.
//ShowWindow( window );
create_main_window();
//SetKeyboardFocus(gwindow, inputcontrol, kControlFocusNextPart);
InstallWindowEventHandler(
gwindow,
NewEventHandlerUPP(MainWindowCommandHandler),
NEVENT,
commSpec,
NULL,
NULL);
InstallEventLoopTimer(
GetMainEventLoop(),
1, // 1 second
1, // 1 second
NewEventLoopTimerUPP(timer_function),
NULL,
NULL);
RunApplicationEventLoop();
CantSetMenuBar:
CantGetNibRef:
return 0;
}
OSStatus kopencmd(void);
#define FILE_MENU_ID 2
#define FILE_NEW_ITEM 1
#define FILE_OPEN_ITEM 2
#define FILE_CLOSE_ITEM 4
#define FILE_SAVE_ITEM 5
#define FILE_SAVEAS_ITEM 6
#define EXAMPLES_MENU_ID 4
#define EXAMPLES_VECTOR_CALCULUS_ITEM 1
#if 0
static void
update_file_menu(void)
{
MenuRef m;
m = GetMenuHandle(FILE_MENU_ID);
if (script_window == NULL) {
EnableMenuItem(m, FILE_NEW_ITEM);
EnableMenuItem(m, FILE_OPEN_ITEM);
DisableMenuItem(m, FILE_CLOSE_ITEM);
DisableMenuItem(m, FILE_SAVE_ITEM);
DisableMenuItem(m, FILE_SAVEAS_ITEM);
} else {
DisableMenuItem(m, FILE_NEW_ITEM);
DisableMenuItem(m, FILE_OPEN_ITEM);
EnableMenuItem(m, FILE_CLOSE_ITEM);
EnableMenuItem(m, FILE_SAVE_ITEM);
EnableMenuItem(m, FILE_SAVEAS_ITEM);
}
}
#endif
pascal OSStatus
MainWindowCommandHandler(EventHandlerCallRef handlerRef, EventRef event, void *userData)
{
OSStatus err = noErr;
HICommand command;
int yclass, kind;
yclass = GetEventClass(event);
kind = GetEventKind(event);
//printf("class %c%c%c%c kind %d\n", class >> 24 & 0xff, class >> 16 & 0xff, class >> 8 & 0xff, class & 0xff, kind);
if (yclass == kEventClassKeyboard && kind == 1234) {
process_user_event();
return noErr;
}
if (yclass == kEventClassWindow) {
switch (kind) {
case kEventWindowResizeCompleted:
case kEventWindowZoomed:
do_resize();
break;
case kEventWindowClosed:
exit(0);
break;
default:
err = eventNotHandledErr;
break;
}
return err;
}
if (edit_mode == 0 && yclass == kEventClassKeyboard) {
char keycode;
GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &keycode);
if (keycode == 27) {
esc_flag = 1;
return noErr;
} else if (running)
return eventNotHandledErr;
else if (keycode == 13) {
do_return_key();
return noErr;
} else if (keycode == 30) {
do_up_arrow();
return noErr;
} else if (keycode == 31) {
do_down_arrow();
return noErr;
} else
return eventNotHandledErr;
}
// y = GetEventKind(event);
//printf("%c%c%c%c %08x\n", (x >> 24), (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff, y);
GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command );
//printf("%08x, %08x\n", command.attributes, command.commandID);
if ((command.commandID & 0xffff0000) == 0xcafe0000) {
do_button(button_cmd[command.commandID & 0xff]);
return noErr;
}
switch( command.commandID ) {
case 'PROG':
if (running)
break;
change_modes();
break;
case 'RUN ':
if (running)
break;
go_to_calc_mode();
deactivate_controls();
clear();
draw_display_now();
create_task(script_buf);
update_curr_cmd("Working...");
break;
// menu events
case 'abou':
if (running)
break;
go_to_calc_mode();
printstr("version 112 eigenmath.sourceforge.net\n");
update_display();
break;
case 'new ':
if (running)
break;
go_to_edit_mode();
*filename = 0;
*script_buf = 0;
update_edit_control();
break;
case 'open':
if (running)
break;
go_to_edit_mode();
file_open();
break;
case 'clos':
break;
case 'save':
if (*filename == 0)
file_svas();
else
do_save();
break;
case 'svas':
file_svas();
break;
// edit menu
case 'CPY1':
go_to_calc_mode();
copy_display();
break;
case 'CPY2':
go_to_calc_mode();
copy_tty();
break;
default:
err = eventNotHandledErr;
break;
}
return err;
}
// also called from history.cpp
char *
get_curr_cmd(void)
{
int i;
Size len;
char *s;
GetControlDataSize(inputcontrol, 0, kControlEditTextTextTag, &len);
s = (char *) malloc(len + 1);
if (s == NULL)
exit(1);
GetControlData(inputcontrol, 0, kControlEditTextTextTag, len, s, &len);
s[len] = 0;
// trim trailing spaces
for (i = len - 1; i >= 0; i--) {
if (isspace(s[i]))
s[i] = 0;
else
break;
}
return s;
}
void
update_curr_cmd(char *s)
{
SetControlData(inputcontrol, 0, kControlEditTextTextTag, strlen(s), s);
DrawOneControl(inputcontrol);
}
#define BLUE_SHIM 6
void
create_input_control(void)
{
Rect r;
r.left = 0;
r.top = display_height + scroll_bar_dim;
r.right = client_width;
r.bottom = r.top + input_control_height;
r.left += BLUE_SHIM;
r.top += BLUE_SHIM;
r.right -= BLUE_SHIM;
r.bottom -= BLUE_SHIM - 1;
CreateEditTextControl(gwindow, &r, NULL, FALSE, FALSE, NULL, &inputcontrol);
}
#define SHIM 2
#define AA (2 * line_height - 2 * SHIM)
void
create_buttons(void)
{
int i, j, k;
char *s;
Rect r;
CFStringRef str;
for (i = 0; i < NBUTTONS; i++) {
j = SHIM + i * (client_width - SHIM) / NBUTTONS;
k = (i + 1) * (client_width - SHIM) / NBUTTONS;
r.left = j;
r.top = client_height - 2 * line_height + SHIM;
r.right = k;
r.bottom = r.top + AA;
if (edit_mode && i == NBUTTONS - 2)
s = "<<";
else
s = button_name[i];
str = CFStringCreateWithCString(NULL, s, kCFStringEncodingMacRoman);
CreateBevelButtonControl(gwindow, &r, str, 0, 0, 0, 0, 0, 0, buttons + i);
CFRelease(str);
}
for (i = 0; i < NBUTTONS - 2; i++) {
SetControlCommandID(buttons[i], 0xcafe0000 + i);
if (edit_mode)
DisableControl(buttons[i]);
}
SetControlCommandID(buttons[NBUTTONS - 2], 'PROG');
SetControlCommandID(buttons[NBUTTONS - 1], 'RUN ');
}
extern OSStatus CreateYASTControl(WindowRef, Rect *, ControlRef *);
static void
do_font_metric(void)
{
FontInfo font;
select_font(SMALL_FONT);
GetFontInfo(&font);
text_metric[SMALL_FONT].ascent = font.ascent;
text_metric[SMALL_FONT].descent = font.descent;
text_metric[SMALL_FONT].width = 2 * CharWidth(' ');
select_font(DEFAULT_FONT);
GetFontInfo(&font);
text_metric[DEFAULT_FONT].ascent = font.ascent;
text_metric[DEFAULT_FONT].descent = font.descent;
text_metric[DEFAULT_FONT].width = 2 * CharWidth(' ');
select_font(TIMES_FONT);
GetFontInfo(&font);
text_metric[TIMES_FONT].ascent = font.ascent;
text_metric[TIMES_FONT].descent = font.descent;
text_metric[TIMES_FONT].width = 2 * CharWidth(' ');
select_font(ITALIC_TIMES_FONT);
GetFontInfo(&font);
text_metric[ITALIC_TIMES_FONT].ascent = font.ascent;
text_metric[ITALIC_TIMES_FONT].descent = font.descent;
text_metric[ITALIC_TIMES_FONT].width = 2 * CharWidth(' ');
select_font(SYMBOL_FONT);
GetFontInfo(&font);
text_metric[SYMBOL_FONT].ascent = font.ascent;
text_metric[SYMBOL_FONT].descent = font.descent;
text_metric[SYMBOL_FONT].width = 2 * CharWidth(' ');
select_font(ITALIC_SYMBOL_FONT);
GetFontInfo(&font);
text_metric[ITALIC_SYMBOL_FONT].ascent = font.ascent;
text_metric[ITALIC_SYMBOL_FONT].descent = font.descent;
text_metric[ITALIC_SYMBOL_FONT].width = 2 * CharWidth(' ');
select_font(SMALL_TIMES_FONT);
GetFontInfo(&font);
text_metric[SMALL_TIMES_FONT].ascent = font.ascent;
text_metric[SMALL_TIMES_FONT].descent = font.descent;
text_metric[SMALL_TIMES_FONT].width = 2 * CharWidth(' ');
select_font(SMALL_ITALIC_TIMES_FONT);
GetFontInfo(&font);
text_metric[SMALL_ITALIC_TIMES_FONT].ascent = font.ascent;
text_metric[SMALL_ITALIC_TIMES_FONT].descent = font.descent;
text_metric[SMALL_ITALIC_TIMES_FONT].width = 2 * CharWidth(' ');
select_font(SMALL_SYMBOL_FONT);
GetFontInfo(&font);
text_metric[SMALL_SYMBOL_FONT].ascent = font.ascent;
text_metric[SMALL_SYMBOL_FONT].descent = font.descent;
text_metric[SMALL_SYMBOL_FONT].width = 2 * CharWidth(' ');
select_font(SMALL_ITALIC_SYMBOL_FONT);
GetFontInfo(&font);
text_metric[SMALL_ITALIC_SYMBOL_FONT].ascent = font.ascent;
text_metric[SMALL_ITALIC_SYMBOL_FONT].descent = font.descent;
text_metric[SMALL_ITALIC_SYMBOL_FONT].width = 2 * CharWidth(' ');
}
static void
create_scroll_bar_controls(void)
{
Rect r;
// horizontal scroll bar
r.left = 0;
r.top = display_height;
r.right = display_width;
r.bottom = r.top + scroll_bar_dim;
CreateScrollBarControl(gwindow, &r, display_x, 0, max_x, display_width, TRUE, hscroll_f, &hscroll);
// vertical scroll bar
r.left = display_width;
r.top = 0;
r.right = r.left + scroll_bar_dim;
r.bottom = display_height;
CreateScrollBarControl(gwindow, &r, display_y, 0, max_y, display_height, TRUE, vscroll_f, &vscroll);
}
//#define WINATTR (kWindowFullZoomAttribute | kWindowCollapseBoxAttribute | kWindowResizableAttribute | kWindowStandardHandlerAttribute)
#define WINATTR (kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute)
void
create_main_window(void)
{
Rect r;
r.left = 20;
r.top = 44 + 16;
r.right = r.left + client_width + grow_dim;
r.bottom = r.top + client_height + grow_dim;
CreateNewWindow(6, WINATTR, &r, &gwindow);
SetWindowTitleWithCFString(gwindow, CFSTR("eigenmath.sourceforge.net"));
display_width = client_width - scroll_bar_dim;
display_height = client_height - 2 * line_height - input_control_height - scroll_bar_dim;
do_font_metric();
create_scroll_bar_controls();
create_input_control();
create_buttons();
ShowWindow(gwindow);
SetKeyboardFocus(gwindow, inputcontrol, kControlFocusNextPart);
}
#define DISPLAY_SIZE 24
#define DISPLAY_SCRIPT_SIZE 18
static void
select_font(int font)
{
switch (font) {
case SMALL_FONT: // for graph axis labels
TextFont(22);
TextSize(12);
TextFace(0);
break;
case DEFAULT_FONT: // for echoing user input
TextFont(YYFONT);
TextSize(YYSIZE);
TextFace(0);
break;
case TIMES_FONT:
TextFont(20);
TextSize(DISPLAY_SIZE);
TextFace(0);
break;
case ITALIC_TIMES_FONT:
TextFont(20);
TextSize(DISPLAY_SIZE);
TextFace(italic);
break;
case SYMBOL_FONT:
TextFont(23);
TextSize(DISPLAY_SIZE);
TextFace(0);
break;
case ITALIC_SYMBOL_FONT:
TextFont(23);
TextSize(DISPLAY_SIZE);
TextFace(italic);
break;
case SMALL_TIMES_FONT:
TextFont(20);
TextSize(DISPLAY_SCRIPT_SIZE);
TextFace(0);
break;
case SMALL_ITALIC_TIMES_FONT:
TextFont(20);
TextSize(DISPLAY_SCRIPT_SIZE);
TextFace(italic);
break;
case SMALL_SYMBOL_FONT:
TextFont(23);
TextSize(DISPLAY_SCRIPT_SIZE);
TextFace(0);
break;
case SMALL_ITALIC_SYMBOL_FONT:
TextFont(23);
TextSize(DISPLAY_SCRIPT_SIZE);
TextFace(italic);
break;
}
}
static void
update_edit_control(void)
{
int i, n;
YASTControlEditTextSelectionRec sel;
n = strlen(script_buf);
for (i = 0; i < n; i++)
if (script_buf[i] == '\n')
script_buf[i] = '\r';
HideControl(edit_control);
SetControlData(edit_control, 0, kControlEditTextTextTag, n, script_buf);
sel.selStart = 0;
sel.selEnd = 0;
SetControlData(edit_control, 0, kYASTControlSelectionRangeTag, sizeof sel, &sel);
ShowControl(edit_control);
}
static void
do_open(void)
{
int n;
FILE *f;
f = fopen(filename, "r");
if (f == NULL) {
*filename = 0;
*script_buf = 0;
} else {
n = fread(script_buf, 1, SCRIPT_BUF_LEN, f);
fclose(f);
script_buf[n] = 0;
fclose(f);
}
update_edit_control();
}
static void
do_save(void)
{
FILE *f;
if (edit_mode);
get_script();
f = fopen(filename, "w");
if (f == NULL)
return;
fwrite(script_buf, 1, strlen(script_buf), f);
fclose(f);
}
// "save as" dialog gives us directory path and file name separately
static pascal void
file_svas_callback(NavEventCallbackMessage msg, NavCBRecPtr p, void *o)
{
int n;
NavReplyRecord r;
AEDesc d;
FSRef f;
if (msg != kNavCBUserAction)
return;
if (NavDialogGetUserAction(p->context) != kNavUserActionSaveAs)
return;
NavDialogGetReply(p->context, &r);
AECoerceDesc(&r.selection, typeFSRef, &d);
AEGetDescData(&d, (void *) &f, sizeof (FSRef));
FSRefMakePath(&f, (UInt8 *) filename, 1000);
n = strlen(filename);
if (n && filename[n - 1] != '/')
filename[n++] = '/';
CFStringGetCString(r.saveFileName, filename + n, 1000 - n, 0);
do_save();
AEDisposeDesc(&d);
NavDisposeReply(&r);
}
static void
file_svas(void)
{
char *s, *t;
NavEventUPP upp;
NavDialogRef dialog;
NavDialogCreationOptions opt;
upp = NewNavEventUPP(file_svas_callback);
NavGetDefaultDialogCreationOptions(&opt);
// isolate file name
if (*filename) {
s = filename;
t = filename;
while (*t) {
if (*t == '/')
s = t + 1;
t++;
}
opt.saveFileName = CFStringCreateWithCString(NULL, s, 0);
}
NavCreatePutFileDialog(&opt, 0, 0, upp, NULL, &dialog);
NavDialogRun(dialog);
NavDialogDispose(dialog);
DisposeNavEventUPP(upp);
}
static pascal void
file_open_callback(NavEventCallbackMessage msg, NavCBRecPtr p, void *o)
{
NavReplyRecord r;
AEDesc d;
FSRef f;
if (msg != kNavCBUserAction)
return;
if (NavDialogGetUserAction(p->context) != kNavUserActionOpen)
return;
NavDialogGetReply(p->context, &r);
AECoerceDesc(&r.selection, typeFSRef, &d);
AEGetDescData(&d, (void *) &f, sizeof (FSRef));
FSRefMakePath(&f, (UInt8 *) filename, 1000);
do_open();
AEDisposeDesc(&d);
NavDisposeReply(&r);
}
static void
file_open(void)
{
NavEventUPP upp;
NavDialogRef dialog;
NavDialogCreationOptions opt;
upp = NewNavEventUPP(file_open_callback);
NavGetDefaultDialogCreationOptions(&opt);
NavCreateGetFileDialog(&opt, NULL, upp, NULL, NULL, NULL, &dialog);
NavDialogRun(dialog);
NavDialogDispose(dialog);
DisposeNavEventUPP(upp);
}
// mac draw func args are 16 bits so must clip
#define CLIP 32000
void
draw_text(int font, int x, int y, char *s, int len)
{
if (x < -CLIP || x > CLIP || y < -CLIP || y > CLIP)
return;
select_font(font);
MoveTo(x, y + text_metric[font].ascent);
DrawText(s, 0, len);
}
int
text_width(int font, char *s)
{
select_font(font);
return TextWidth(s, 0, strlen(s));
}
void
get_height_width(int *h, int *w, int font, char *s)
{
*h = text_metric[font].ascent + text_metric[font].descent;
select_font(font);
*w = TextWidth(s, 0, strlen(s));
}
// The Mac fills inside the rectangle:
//
// y - 1 ->
// |x|x|x|
// y ->
// |x|x|x|
// y + 1 ->
// |x|x|x|
// y + 2 ->
// ^ ^
// x x+2
void
draw_point(int x, int y)
{
Rect r;
r.left = x - 1;
r.top = y - 1;
r.right = x + 2;
r.bottom = y + 2;
PaintRect(&r);
}
void
draw_box(int x1, int y1, int x2, int y2)
{
RGBColor pen;
Rect r;
pen.red = (256 * 16 * 16) - 1;
pen.green = (256 * 16 * 16) - 1;
pen.blue = (256 * 16 * 15) - 1;
RGBForeColor(&pen);
r.left = x1;
r.top = y1;
r.right = x2 + 1;
r.bottom = y2 + 1;
PaintRect(&r);
pen.red = 0;
pen.green = 0;
pen.blue = 0;
RGBForeColor(&pen);
FrameRect(&r);
}
void
draw_line(int x1, int y1, int x2, int y2)
{
if (x1 < -CLIP) x1 = -CLIP;
if (x1 > +CLIP) x1 = +CLIP;
if (x2 < -CLIP) x2 = -CLIP;
if (x2 > +CLIP) x2 = +CLIP;
if (y1 < -CLIP) y1 = -CLIP;
if (y1 > +CLIP) y1 = +CLIP;
if (y2 < -CLIP) y2 = -CLIP;
if (y2 > +CLIP) y2 = +CLIP;
MoveTo(x1, y1);
LineTo(x2, y2);
}
// The Mac draws the last point:
//
// |x|x|x|x|x|
// ^ ^
// x x+w
//
// If w = 4 then 5 pixels are drawn.
//
// To draw an hrule w pixels wide, the endpoint is x + w - 1.
void
draw_hrule(int x, int y, int w)
{
draw_line(x, y, x + w - 1, y);
}
void
draw_left_bracket(int x, int y, int w, int h)
{
MoveTo(x + w - 1, y);
LineTo(x, y);
LineTo(x, y + h - 1);
LineTo(x + w - 1, y + h - 1);
}
void
draw_right_bracket(int x, int y, int w, int h)
{
MoveTo(x, y);
LineTo(x + w - 1, y);
LineTo(x + w - 1, y + h - 1);
LineTo(x, y + h - 1);
}
void
use_normal_pen()
{
}
void
use_graph_pen()
{
}
static void
update_scroll_bars(void)
{
SetControl32BitMaximum(hscroll, max_x);
SetControl32BitValue(hscroll, display_x);
SetControl32BitMaximum(vscroll, max_y);
SetControl32BitValue(vscroll, display_y);
}
static void
update_display(void)
{
if (update_display_request == 0)
return;
draw_display_now();
}
static void draw_display_now_f(void);
static void
draw_display_now(void)
{
RgnHandle rgn;
SetPortWindowPort(gwindow);
rgn = NewRgn();
GetClip(rgn);
draw_display_now_f();
SetClip(rgn);
DisposeRgn(rgn);
update_scroll_bars();
}
static void
draw_display_now_f(void)
{
Rect r;
r.left = 0;
r.top = 0;
r.right = display_width;
r.bottom = display_height;
ClipRect(&r);
EraseRect(&r);
draw_display();
select_font(DEFAULT_FONT);
}
static void
vscroll_f(ControlRef ref, ControlPartCode part)
{
int dy, y;
Rect r;
RgnHandle rgn, tmp;
y = display_y;
switch (part) {
case kControlUpButtonPart:
display_y -= line_height;
break;
case kControlDownButtonPart:
display_y += line_height;
break;
case kControlPageUpPart:
display_y -= display_height;
break;
case kControlPageDownPart:
display_y += display_height;
break;
case 129: // thumb
display_y = GetControl32BitValue(vscroll);
break;
}
if (display_y < 0)
display_y = 0;
if (display_y > max_y)
display_y = max_y;
dy = y - display_y;
if (dy == 0)
return;
SetPortWindowPort(gwindow);
rgn = NewRgn();
if (abs(dy) >= display_height) {
SetRectRgn(rgn, 0, 0, display_width, display_height);
EraseRgn(rgn);
} else {
r.left = 0;
r.top = 0;
r.right = display_width;
r.bottom = display_height;
ScrollRect(&r, 0, dy, rgn);
}
tmp = NewRgn();
GetClip(tmp);
SetClip(rgn);
DisposeRgn(rgn);
draw_display();
select_font(DEFAULT_FONT);
SetClip(tmp);
DisposeRgn(tmp);
update_scroll_bars();
}
static void
hscroll_f(ControlRef ref, ControlPartCode part)
{
int dx, x;
Rect r;
RgnHandle rgn, tmp;
x = display_x;
switch (part) {
case kControlUpButtonPart:
display_x -= line_height;
break;
case kControlDownButtonPart:
display_x += line_height;
break;
case kControlPageUpPart:
display_x -= display_width;
break;
case kControlPageDownPart:
display_x += display_width;
break;
case 129: // thumb
display_x = GetControl32BitValue(hscroll);
break;
}
if (display_x < 0)
display_x = 0;
if (display_x > max_x)
display_x = max_x;
dx = x - display_x;
if (dx == 0)
return;
SetPortWindowPort(gwindow);
rgn = NewRgn();
if (abs(dx) >= display_height) {
SetRectRgn(rgn, 0, 0, display_width, display_height);
EraseRgn(rgn);
} else {
r.left = 0;
r.top = 0;
r.right = display_width;
r.bottom = display_height;
ScrollRect(&r, dx, 0, rgn);
}
tmp = NewRgn();
GetClip(tmp);
SetClip(rgn);
DisposeRgn(rgn);
draw_display();
select_font(DEFAULT_FONT);
SetClip(tmp);
DisposeRgn(tmp);
update_scroll_bars();
}
static OSStatus
task(void *p)
{
run(inp);
running = 2;
send_user_event();
return noErr;
}
static void
create_task(char *s)
{
MPTaskID id;
inp = s;
timer = time(NULL);
running = 1;
MPCreateTask(
task,
NULL,
1024 * 1024,
NULL,
NULL,
NULL,
0,
&id);
}
extern void update_cmd_history(char *);
extern void echo_input(char *);
static void
do_return_key(void)
{
char *s;
if (running)
return;
s = get_curr_cmd();
strcpy(tmp_buf, s);
free(s);
update_cmd_history(tmp_buf); // reset history pointer no matter what
if (*tmp_buf == 0)
return;
echo_input(tmp_buf);
update_curr_cmd("");
create_task(tmp_buf);
}
static void
do_button(char *cmd)
{
char *s;
if (running)
return;
s = get_curr_cmd();
if (*s) {
if (strcmp(cmd, "derivative") == 0)
strcpy(tmp_buf, "d");
else
strcpy(tmp_buf, cmd);
strcat(tmp_buf, "(");
strcat(tmp_buf, s);
strcat(tmp_buf, ")");
} else
strcpy(tmp_buf, cmd);
free(s);
update_cmd_history(tmp_buf);
echo_input(tmp_buf);
update_curr_cmd("");
create_task(tmp_buf);
}
static int shunted;
static void
deactivate_controls(void)
{
int i;
if (shunted == 1)
return;
DeactivateControl(inputcontrol);
for (i = 0; i < NBUTTONS; i++)
DeactivateControl(buttons[i]);
shunted = 1;
}
static void
activate_controls(void)
{
int i;
if (shunted == 0)
return;
ActivateControl(inputcontrol);
for (i = 0; i < NBUTTONS; i++)
ActivateControl(buttons[i]);
shunted = 0;
}
static void
send_user_event(void)
{
EventRef event;
CreateEvent(NULL, kEventClassKeyboard, 1234, 0, kEventAttributeUserEvent, &event);
PostEventToQueue(GetMainEventQueue(), event, kEventPriorityStandard);
}
static void
process_user_event(void)
{
unsigned int dt;
static char buf[1000];
if (running == 0)
return;
if (running == 2) {
update_curr_cmd("");
activate_controls();
update_display();
running = 0;
return;
}
dt = time(NULL) - timer;
if (dt > 1) {
deactivate_controls();
sprintf(buf, "Working on it for %d seconds. Esc might interrupt, otherwise press \021Q to quit.", dt);
update_curr_cmd(buf);
update_display();
}
}
static void
get_script(void)
{
int i;
Size len;
GetControlData(edit_control, 0, kControlEditTextTextTag, SCRIPT_BUF_LEN, script_buf, &len);
script_buf[len] = 0;
for (i = 0; i < len; i++)
if (script_buf[i] == '\r')
script_buf[i] = '\n';
}
static void erase_window_f(void);
static void
erase_window(void)
{
RgnHandle rgn;
SetPortWindowPort(gwindow);
rgn = NewRgn();
GetClip(rgn);
erase_window_f();
SetClip(rgn);
DisposeRgn(rgn);
}
static void
erase_window_f(void)
{
Rect r;
r.left = 0;
r.top = 0;
r.right = client_width + grow_dim;
r.bottom = client_height + grow_dim;
ClipRect(&r);
EraseRect(&r);
}
static void
do_resize(void)
{
Rect r;
GetWindowBounds(gwindow, kWindowContentRgn, &r);
client_height = r.bottom - r.top - grow_dim;
client_width = r.right - r.left - grow_dim;
display_width = client_width - scroll_bar_dim;
display_height = client_height - 2 * line_height - input_control_height - scroll_bar_dim;
//KillControls(gwindow); // cannot get it to work, new controls are never drawn
remove_controls();
erase_window();
create_controls();
if (running) {
shunted = 0; // controls were redrawn so no longer shunted
deactivate_controls();
}
DrawControls(gwindow);
if (edit_mode)
update_edit_control();
else {
// display_y ranges from 0 to max_y
max_y = total_h - display_height;
if (max_y < 0)
max_y = 0;
if (display_y > max_y)
display_y = max_y;
draw_display_now();
}
}
static void
copy_display(void)
{
Rect r;
PicHandle pic;
RgnHandle rgn;
ScrapRef scrap;
r.top = 0;
r.left = 0;
r.right = display_width;
if (total_h - display_y < display_height)
r.bottom = total_h - display_y;
else
r.bottom = display_height;
SetPortWindowPort(gwindow);
rgn = NewRgn();
GetClip(rgn);
pic = OpenPicture(&r);
ClipRect(&r);
//EraseRect(&r);
draw_display();
select_font(DEFAULT_FONT);
ClosePicture();
SetClip(rgn);
DisposeRgn(rgn);
ClearCurrentScrap();
GetCurrentScrap(&scrap);
PutScrapFlavor(
scrap,
kScrapFlavorTypePicture,
kScrapFlavorMaskNone,
GetHandleSize((char **) pic),
*pic);
KillPicture(pic);
}
static void
copy_tty(void)
{
char *s;
ScrapRef scrap;
s = get_tty_buf();
if (s == NULL)
return;
ClearCurrentScrap();
GetCurrentScrap(&scrap);
PutScrapFlavor(
scrap,
kScrapFlavorTypeText,
kScrapFlavorMaskNone,
strlen(s),
s);
free(s);
}
static void create_calc_mode_controls(void);
static void create_edit_mode_controls(void);
static void
create_controls(void)
{
if (edit_mode)
create_edit_mode_controls();
else
create_calc_mode_controls();
}
static void
create_calc_mode_controls(void)
{
create_scroll_bar_controls();
create_input_control();
create_buttons();
SetKeyboardFocus(gwindow, inputcontrol, kControlFocusNextPart);
}
static void
create_edit_mode_controls(void)
{
Rect r;
create_buttons();
r.left = 3;
r.top = 3;
r.right = client_width - 3;
r.bottom = client_height - 2 * line_height - 3;
CreateYASTControl(gwindow, &r, &edit_control);
ShowControl(edit_control);
SetKeyboardFocus(gwindow, edit_control, kControlFocusNextPart);
}
static void remove_calc_mode_controls(void);
static void remove_edit_mode_controls(void);
static void
remove_controls(void)
{
if (edit_mode) {
get_script();
remove_edit_mode_controls();
} else
remove_calc_mode_controls();
}
static void
remove_calc_mode_controls(void)
{
int i;
DisposeControl(hscroll);
DisposeControl(vscroll);
DisposeControl(inputcontrol);
for (i = 0; i < NBUTTONS; i++)
DisposeControl(buttons[i]);
}
static void
remove_edit_mode_controls(void)
{
int i;
DisposeControl(edit_control);
for (i = 0; i < NBUTTONS; i++)
DisposeControl(buttons[i]);
}
static void
change_modes(void)
{
remove_controls();
erase_window();
edit_mode ^= 1;
create_controls();
DrawControls(gwindow);
if (edit_mode)
update_edit_control();
else
draw_display_now();
}
static void
go_to_calc_mode(void)
{
if (edit_mode == 1)
change_modes();
}
static void
go_to_edit_mode(void)
{
if (edit_mode == 0)
change_modes();
}