/* 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 #include "YASTControl.h" #include "help.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 700 #define DEFAULT_HEIGHT 500 #else #define DEFAULT_WIDTH 400 #define DEFAULT_HEIGHT 240 #endif static void do_help(char **s, int n); #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", "float", "derivative", "integral", }; char *button_name[NBUTTONS] = { "Clear", "Draw", "Simplify", "Float", "Derivative", "Integral", "Edit Script", "Run Script", }; #define YYFONT 22 #define YYSIZE 16 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 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_enter(void); static void do_button(char *); static void run_script(void); 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 goto_calc_mode(void); static void goto_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]; 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_enter(); 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 ': run_script(); break; // file menu case 'abou': if (running) break; goto_calc_mode(); printstr("version 133 eigenmath.sf.net\n"); update_display(); break; case 'new ': if (running) break; goto_edit_mode(); *filename = 0; *script_buf = 0; update_edit_control(); break; case 'open': if (running) break; goto_edit_mode(); file_open(); break; case 'clos': break; case 'save': if (running) break; goto_edit_mode(); if (*filename == 0) file_svas(); else do_save(); break; case 'svas': if (running) break; goto_edit_mode(); file_svas(); break; // edit menu case 'CPY1': goto_calc_mode(); copy_display(); break; case 'CPY2': goto_calc_mode(); copy_tty(); break; // help menu case 'arg ': HELP(help_arg); break; case 'conj': HELP(help_conj); break; case 'imag': HELP(help_imag); break; case 'mag ': HELP(help_mag); break; case 'pola': HELP(help_polar); break; case 'real': HELP(help_real); break; case 'rect': HELP(help_rect); break; case 'coef': HELP(help_coeff); break; case 'deg ': HELP(help_deg); break; case 'quot': HELP(help_quotient); break; case 'adj ': HELP(help_adj); break; case 'cofa': HELP(help_cofactor); break; case 'cont': HELP(help_contract); break; case 'det ': HELP(help_det); break; case 'dot ': HELP(help_dot); break; case 'inv ': HELP(help_inv); break; case 'oute': HELP(help_outer); break; case 'tran': HELP(help_transpose); break; case 'unit': HELP(help_unit); break; case 'zero': HELP(help_zero); break; case 'deri': HELP(help_derivative); break; case 'grad': HELP(help_gradient); break; case 'inte': HELP(help_integral); break; case 'tayl': HELP(help_taylor); break; case 'circ': HELP(help_circexp); break; case 'exp ': HELP(help_exp); break; case 'expc': HELP(help_expcos); break; case 'exps': HELP(help_expsin); break; case 'log ': HELP(help_log); break; case 'arcc': HELP(help_arccos); break; case 'arcs': HELP(help_arcsin); break; case 'arct': HELP(help_arctan); break; case 'cos ': HELP(help_cos); break; case 'sin ': HELP(help_sin); break; case 'tan ': HELP(help_tan); break; case 'argc': HELP(help_arccosh); break; case 'args': HELP(help_arcsinh); break; case 'argt': HELP(help_arctanh); break; case 'cosh': HELP(help_cosh); break; case 'sinh': HELP(help_sinh); break; case 'tanh': HELP(help_tanh); break; case 'bess': HELP(help_besselj); break; case 'herm': HELP(help_hermite); break; case 'lagu': HELP(help_laguerre); break; case 'lege': HELP(help_legendre); break; case 'abs ': HELP(help_abs); break; case 'choo': HELP(help_choose); break; case 'deno': HELP(help_denominator); break; case 'erf ': HELP(help_erf); break; case 'erfc': HELP(help_erfc); break; case 'eval': HELP(help_eval); break; case 'fact': HELP(help_factor); break; case '! ': HELP(help_factorial); break; case 'for ': HELP(help_for); break; case 'nume': HELP(help_numerator); break; case 'prod': HELP(help_product); break; case 'sqrt': HELP(help_sqrt); break; case 'sum ': HELP(help_sum); break; case 'nroo': HELP(help_nroots); break; case 'facp': HELP(help_factor_polynomial); break; case 'root': HELP(help_roots); break; case 'defi': HELP(help_defint); break; // none of the above 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; CreateEditUnicodeTextControl(gwindow, &r, NULL, 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 130")); 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; 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 dx, int y, int dy) { Rect r; r.left = x + dx - 1; r.top = y + dy - 1; r.right = x + dx + 2; r.bottom = y + dy + 2; if (dx == 0) r.left++; if (dy == 0) r.top++; if (dx == 300) r.right--; if (dy == 300) r.bottom--; 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(void) { MPTaskID id; timer = time(NULL); running = 1; MPCreateTask( task, NULL, 1024 * 1024, NULL, NULL, NULL, 0, &id); } 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, "Esc key to stop (%d)", 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 goto_calc_mode(void) { if (edit_mode == 1) change_modes(); } static void goto_edit_mode(void) { if (edit_mode == 0) change_modes(); } extern void update_cmd_history(char *); extern void echo_input(char *); static void do_special(char *s) { if (inp && inp != script_buf) free(inp); inp = strdup(s); update_cmd_history(inp); echo_input(inp); update_curr_cmd(""); run(inp); } static void do_help(char **s, int n) { int i; if (running) return; goto_calc_mode(); do_special("clear"); for (i = 0; i < n; i++) do_special(s[i]); update_display(); } // evaluate the command line static void do_enter(void) { if (running || edit_mode) return; if (inp && inp != script_buf) free(inp); inp = get_curr_cmd(); update_cmd_history(inp); if (*inp == 0) return; echo_input(inp); update_curr_cmd(""); create_task(); } static void do_button(char *s) { char *tmp; if (running || edit_mode) return; if (inp && inp != script_buf) free(inp); inp = get_curr_cmd(); update_cmd_history(inp); if (*inp == 0) { free(inp); inp = (char *) malloc(strlen(s) + 1); strcpy(inp, s); } else { tmp = (char *) malloc(strlen(s) + strlen(inp) + 3); if (strcmp(s, "derivative") == 0) strcpy(tmp, "d"); else strcpy(tmp, s); strcat(tmp, "("); strcat(tmp, inp); strcat(tmp, ")"); free(inp); inp = tmp; } update_cmd_history(inp); echo_input(inp); update_curr_cmd(""); create_task(); } static void run_script(void) { if (running) return; goto_calc_mode(); // this updates script_buf if leaving edit mode // if there is anything on the command line then put it in history if (inp && inp != script_buf) free(inp); inp = get_curr_cmd(); update_cmd_history(inp); free(inp); inp = script_buf; deactivate_controls(); run("clear"); update_display(); create_task(); update_curr_cmd(""); }