/* Copyright 1996 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* -> c.Edit * Title: Edit * Purpose: main driving program for text editor. * History: * 13-Mar-88: WRS: history started. * 1-Jun-90: NDR: Check for Message_DataOpen with estimated size <= -2, and load untitled * 7-Mar-91: PJC: Extended to support BASIC tokenising and detokenising * 19-Mar-91: PJC: Removed check for SHIFT+double-click * 5-Apr-91: PJC: Turn wordwrap off when creating non-text objects * 07-Jun-91: IDJ: don't show info box on click on menu entry * 11-Jun-91: made filename comparison case-insensitive * 14-Jun-91: IDJ: setlocale to get dft territory * 14-Jun-91: IDJ: added menu maker to get help on iconbar menu * 28-Jun-91: IDJ: removed task window from iconbar menu * 01-Jul-91: PJC: fix BASIC support code (line no. inc. and strip flag) * 02-Jul-91: IDJ: look for messages/templates file on Edit$Path * 15-Aug-91: IDJ: initialise filetypebuff to null * 29-Aug-91: IDJ: (my birthday) put nul in filetypebuff to ensure termination * 13-Jan-92: ECN: Set fixed size stack to 7.5*1024+4*1024 * 12-Mar-92: OSS: Split into two directories to allow ROM app names to * be translated in localised systems. * 13-Mar-92: OSS: Finish above. */ #define BOOL int #define TRUE 1 #define FALSE 0 #define BASIC TRUE #define PRINT 1 #define CTL FALSE /* Not using the ctl module yet. */ #include #include #include #include #include #include #include #include #include #include "kernel.h" #include "akbd.h" #include "help.h" #include "flex.h" #include "os.h" #include "swis.h" #include "wimp.h" #include "sprite.h" #include "wimpt.h" #include "werr.h" #include "win.h" #include "menu.h" #include "template.h" #include "dbox.h" #include "txt.h" #include "txtscrap.h" #include "typdat.h" #include "txtedit.h" #include "event.h" #include "res.h" #include "resspr.h" #include "trace.h" #include "dboxquery.h" #include "editv.h" #include "baricon.h" #include "taskwin.h" #include "xferrecv.h" #include "msgs.h" #include "visdelay.h" #include "pointer.h" #if CTL #include "ctl.h" #endif #if BASIC static int Edit_Strip = TRUE; static int Edit_Incrm = 10; static char incrmbuff[10]; static menu Edit_Options; #endif static char filetypebuff[10] = ""; static menu Edit_Menu; static void save_one_txt(txt, int); static int cistrcmp(const char *s1, const char *s2) { int ch1, ch2; for (;;) { ch1 = *s1++; ch2 = *s2++; /* care here for portability... don't rely on ANSI spec */ if (isupper(ch1)) ch1 = tolower(ch1); if (isupper(ch2)) ch2 = tolower(ch2); if (ch1 != ch2) return ch1-ch2; if (ch1 == 0) return 0; } } /* -------- Icon Bar Icon. -------- */ /* On the icon bar we provide a text file icon. Clicking on it gets a blank document. Menu on it provides a cut-down menu. */ static void arcedit__clickproc(wimp_i i) { i=i; txtedit_new("",0xfff); } #define MProg 1 #define MCreate 2 #if BASIC #define MOptions 3 #define MQuit 4 #define MStrip 1 #define MIncrm 2 #else #define MQuit 3 #endif #if FALSE #define MTask 1 #endif #define MText 1 #define MBASIC 2 #define MObey 3 #define MExec 4 #define MWrite 5 static void arcedit__iconmenuproc(void *handle, char *hit) { txtedit_state *s; #if CTL ctl_decode_state state; state.index = 1; while (ctl_decode("IconControl", wimpt_last_event(), &state)) { #if FALSE switch ctl_switch("IconControl", "go think find foo fum", action, TRUE) { 1: /+ go +/ ... logical action for action "go" ... break; 2: /+ think +/ ... logical action for action "think" ... ... other actions ... default: /+ error message already produced by ctl_switch +/ }; #else werr(FALSE, "edit: ctl suggests event %i", state.code); #endif }; if (state.index != 0) /* otherwise, try old handlers. */ #endif { handle=handle; switch (hit[0]) { case MProg: if (hit[1] != 0) { tracef0("about Edit\n"); arceditv_infoaboutprogram(); } break; #if BASIC case MOptions: switch (hit[1]) { case MStrip: Edit_Strip = !Edit_Strip; txtedit_setBASICstrip(Edit_Strip); menu_setflags(Edit_Options, MStrip, Edit_Strip, FALSE); break; case MIncrm: { int save_Edit_Incrm = atoi(incrmbuff); if (save_Edit_Incrm == 0) { save_Edit_Incrm = Edit_Incrm; werr(FALSE, msgs_lookup("BA1")); sprintf(incrmbuff, "%i", Edit_Incrm); } Edit_Incrm = save_Edit_Incrm; txtedit_setBASICincrement(Edit_Incrm); } break; } break; #endif case MCreate: switch (hit[1]) { #if FALSE case MTask: if (wimpt_complain( os_cli( "RMEnsure TaskWindow 0 RMLoad .Task")) == 0) { message_taskwindow("ShellCLI_Task"); }; break; #endif case MText: s = txtedit_new("", 0xfff); break; case MBASIC: s = txtedit_new("", 0xffb); break; case MObey: s = txtedit_new("", 0xfeb); break; case MExec: s = txtedit_new("", 0xffe); break; case MWrite: { os_regset r; char *p = filetypebuff; while (*p >= ' ' && p-filetypebuff < 9) p++; *p = 0; r.r[0] = 31; r.r[1] = (int)filetypebuff; if (wimpt_complain(os_swix(OS_FSControl, &r)) == 0) { wimp_create_menu((wimp_menustr*)-1, 0, 0); s = txtedit_new("", r.r[2]); } } }; break; case MQuit: tracef0("quit\n"); #if FALSE if (dboxquery("Quit Edit: are you sure?") == dboxquery_YES) { exit(0); }; #else if (txtedit_mayquit()) exit(0); #endif }; }; } static menu edit__main_menu_maker(void *handle) { menu create_submenu; #if BASIC menu incrm_submenu; #endif if (Edit_Menu == 0) { #if CTL Edit_Menu = ctl_menu("IconControl"); #else #if BASIC Edit_Menu = menu_new(msgs_lookup("ED1"), msgs_lookup("ED2a")); Edit_Options = menu_new(msgs_lookup("ED2b"), msgs_lookup("ED2c")); incrm_submenu = menu_new(msgs_lookup("ED2d"),msgs_lookup("ED2e")); menu_make_writeable(incrm_submenu, 1, incrmbuff, 5, "a0-9"); menu_submenu(Edit_Options, MIncrm, incrm_submenu); menu_submenu(Edit_Menu, MOptions, Edit_Options); menu_setflags(Edit_Options, MStrip, Edit_Strip, FALSE); sprintf(incrmbuff, "%d", Edit_Incrm); #else Edit_Menu = menu_new(msgs_lookup("ED1"), msgs_lookup("ED2")); #endif create_submenu = menu_new(msgs_lookup("ED3"), msgs_lookup("ED4")); menu_make_writeable(create_submenu,MWrite,filetypebuff,9,"a~."); { wimp_menuhdr *sizemenu; wimp_menuitem *sizeitem; sizemenu = (wimp_menuhdr*) menu_syshandle(create_submenu); sizeitem = (wimp_menuitem*) (sizemenu+1); sizeitem += MWrite-1; sizeitem->iconflags &= ~wimp_IHCENTRE; } menu_submenu(Edit_Menu, MCreate, create_submenu); #endif } help_register_handler(help_simplehandler, "IHELP"); return Edit_Menu; } static void arcedit__createicon(void) { /* OSS Look sprite name up in Messages file. */ baricon(msgs_lookup("BarIcon"), 1, arcedit__clickproc); tracef0("menu created.\n"); event_attachmenumaker(win_ICONBAR, edit__main_menu_maker, arcedit__iconmenuproc, 0); } /* -------- Hot-key selection copy in background. -------- */ #if FALSE static wimp_w edit__hotcopy; static wimp_w edit__prev_idle_claimer; /* both initialised when idle events are claimed. */ static int edit__selptr; char edit__hot_copy_nextchar(void) { /* 0 assumed to mean end of useful chars. */ txt t = txtscrap_selectowner(); if (t == 0) return 0; if (edit__selptr >= (txt_selectend(t) - txt_selectstart(t))) return 0; return (txt_charat(t, txt_selectstart(t) + (edit__selptr++))); } /* 26-June-89 !!!! This bit will probably NOT work after a change to win_claim_idle_events: need to do something like: event_setmask(event_getmask() & ~wimp_EMNULL) before claiming idle events,and event_setmask(event_getmask() | wimp_EMNULL) when you don't want idle events any more */ void edit__hot_copy(wimp_eventstr *e, void *handle) { handle=handle; if (e->e == wimp_ENULL) { char c = edit__hot_copy_nextchar(); if (c == 0) { win_claim_idle_events(edit__prev_idle_claimer); } else { wimpt_noerr(wimp_processkey(c)); }; }; } #endif /* -------- Desktop saving. -------- */ static os_error * save_desktop(int handle) { char buffer[300]; char buffer2[256]; char *ptr = buffer; os_read_var_val("Edit$Options", buffer2, sizeof(buffer2)); if (buffer2[0] != 0) { sprintf(ptr, "Set Edit$Options %s\n", buffer2); ptr += strlen(ptr); } os_read_var_val("Edit$Dir",buffer2,sizeof(buffer2)); sprintf(ptr, "Run %s\n",buffer2); for (ptr=buffer; *ptr; ptr++) { os_error * error = os_swi2(OS_BPut, *ptr, handle); if (error) return(error); } return NULL; } /* -------- Background sink for all unclaimed events. -------- */ static void arcedit__bkg_events(wimp_eventstr *e, void *handle) { tracef1("arcedit__bkg_events %i.\n", e->e); handle=handle; switch(e->e) { case wimp_ESEND: case wimp_ESENDWANTACK: tracef0("it's a message.\n"); if (e->data.msg.hdr.action == wimp_MPREQUIT) { tracef0("pre-quit message.\n"); txtedit_prequit(); } else if (e->data.msg.hdr.action == wimp_MHELPREQUEST && e->data.msg.data.helprequest.m.w == -2 ) { e->data.msg.hdr.your_ref = e->data.msg.hdr.my_ref; e->data.msg.hdr.action = wimp_MHELPREPLY; e->data.msg.hdr.size = 256; /* be generous! */ sprintf(e->data.msg.data.helpreply.text, msgs_lookup("ED5")); /* "This is the Edit icon.|MClick SELECT to create a new Text window." */ wimpt_noerr( wimp_sendmessage(wimp_ESEND, &e->data.msg, e->data.msg.hdr.task)); } else if (help_process(e)) { /* already done by help_process */ ; } else if (e->data.msg.hdr.action == wimp_SAVEDESK) { if (wimpt_complain(save_desktop(e->data.msg.data.savedesk.filehandle)) != 0) { /* Things went wrong (e.g. disc full on file output): ack the message, to stop the closedown sequence. */ e->data.msg.hdr.your_ref = e->data.msg.hdr.my_ref; wimpt_complain(wimp_sendmessage(wimp_EACK, &e->data.msg, e->data.msg.hdr.task)); }; } #if PRINT #if BASIC else if (e->data.msg.hdr.action == wimp_MPrintTypeOdd) { txtedit_state *s = 0; BOOL newtext = FALSE; os_regset r; if (e->data.msg.data.print.type != 0xffb /* not basic */) return; /* --- reply --- */ e->data.msg.hdr.action = wimp_MPrintTypeKnown; e->data.msg.hdr.your_ref = e->data.msg.hdr.my_ref; if (wimpt_complain(wimp_sendmessage(wimp_ESEND, &e->data.msg, e->data.msg.hdr.task))) return; /* --- load the file --- */ /* look for it first */ s = txtedit_getstates(); while (s != 0) { if (s != 0 && !cistrcmp(s->filename, e->data.msg.data.print.name)) break; s = s->next; } if (s == 0 || (s != 0 && (txt_charoptions(s->t) & txt_UPDATED))) { if ((s = txtedit_new(e->data.msg.data.print.name, 0xfff)) == 0) return; txt_hide(s->t); newtext = TRUE; } /* --- save to Printer$Temp --- */ r.r[0] = 0x83; /* Open out, no path */ r.r[1] = (int)""; if (os_find(&r) == NULL) { int h = r.r[0]; save_one_txt(s->t, h); r.r[0] = 0; /* Close */ r.r[1] = h; os_find(&r); os_swix3(OS_File, 18, "", 0xfff); } if (newtext) txtedit_dispose(s); } #endif #endif else { int estsize; int filetype = xferrecv_checkimport(&estsize); if (filetype != -1) { /* drag icon in-store to icon bar. Create window and insert. */ txtedit_state *s = txtedit_new("", filetype); if (s != 0) { /* 14-Dec-88 WRS: check inserted. */ if (! txtedit_doimport(s, filetype, estsize)) { txtedit_dispose(s); }; }; } else { char *filename; BOOL generalload = wimpt_last_event()->data.msg.hdr.action == wimp_MDATAOPEN; BOOL untitledgeneral = generalload && wimpt_last_event()->data.msg.data.dataload.size <= -2; /* note that the Filer in RISC OS 2.00 supplies estsize = -1, so we check for -2 */ filetype = xferrecv_checkinsert(&filename); if (filetype == 0xfff || (filetype != -1 && !generalload)) { if (xferrecv_file_is_safe() && !untitledgeneral) { /* it's a "real" file, with estsize > -2 */ txtedit_new(filename, filetype); xferrecv_insertfileok(); } else { txtedit_state *s = txtedit_new("", filetype); if (s != 0) { txtedit_doinsertfile(s, filename, FALSE); xferrecv_insertfileok(); }; }; }; }; }; break; default: tracef1("edit: unrecognised event type %i.\n", e->e); }; } #if FALSE /* these actually end up with other dud events above. */ /* -------- Hot key definitions. -------- */ /* BOOL edit__hotkeys(dbox d, void *event, void *handle) { */ void edit__hotkeys(wimp_eventstr *e, void *handle) { /* wimp_eventstr *e = (wimp_eventstr*) event; */ tracef1("hot key event: type %i.\n", e->e); werr(FALSE, "hot key %i %i!", e->e, e->data.key.chcode); /* test message. */ if (e->e == wimp_EKEY) { if (e->data.key.chcode == akbd_Fn + 7) { werr(FALSE, "hot key select copy!"); /* test message. */ #if FALSE #endif } else { /* not recognised: pass it on. */ wimpt_noerr(wimp_processkey(e->data.key.chcode)); }; }; /* return TRUE; */ } #endif /* -------- Main Program. -------- */ static const wimp_msgaction edit_messages[] = { wimp_MDATASAVE, wimp_MDATASAVEOK, wimp_MDATALOAD, wimp_MDATALOADOK, wimp_MDATAOPEN, wimp_MRAMFETCH, wimp_MRAMTRANSMIT, wimp_MPREQUIT, wimp_SAVEDESK, wimp_MDATASAVED, wimp_MCLAIMENTITY, wimp_MDATAREQUEST, wimp_MMENUWARN, wimp_MMODECHANGE, wimp_MHELPREQUEST, wimp_MHELPREPLY, wimp_MPrintError, wimp_MPrintSave, wimp_MPrintFile, wimp_MWillPrint, wimp_MPrintTypeOdd, wimp_MPrintTypeKnown, wimp_MSetPrinter, message_input, /* Parent has input for child */ message_output, /* Child has output for parent */ message_ego, /* Child tells parent its name */ message_morio, /* Child dies */ message_morite, /* Parent kills child */ message_newtask, /* External agent requests task window */ message_suspend, /* Parent halts child */ message_resume, /* Parent continues child */ wimp_MCLOSEDOWN }; #define N_SIGNALS 11 static jmp_buf SigBuf; static void edit_signal_handler(int signal) { longjmp(SigBuf, signal); } static void save_one_txt(txt t, int handle) { size_t p = 0; char *a; int n; do { /* Not sure where the dot is, so locate each segment in turn */ txt_arrayseg(t, p, &a, &n); if (n) os_swix4(OS_GBPB, 2, handle, a, n); p += n; } while(n); } static void save_edits(int s, char *buff) { _kernel_oserror *e; os_error error; os_regset r; BOOL reported = FALSE; txtedit_state *txtedits; int f; int key; e = _kernel_last_oserror(); pointer_reset_shape(); if (!_kernel_getenv("Wimp$ScrapDir", buff, 256)) { strcat(buff, ".Edit"); s = strlen(buff); txtedits = txtedit_getstates(); error.errnum = 0; f = 0; while (txtedits) { if (txt_charoptions(txtedits->t) & txt_UPDATED) { if (!reported) { sprintf(error.errmess, msgs_lookup("EditX"), e->errmess, buff); os_swi3r(Wimp_ReportError, &error, 3, "Edit", NULL, &key, NULL); reported = TRUE; if (key == 2) break; os_swi5(OS_File, 8, buff, 0, 0, 0); } buff[s] = '.'; buff[s+1] = f / 10 + '0'; buff[s+2] = f % 10 + '0'; buff[s+3] = 0; r.r[0] = 0x83; /* Open out, no path */ r.r[1] = (int)buff; if (os_find(&r) == NULL) { int h = r.r[0]; os_swix4(OS_GBPB, 2, h, "> ", 2); os_swix4(OS_GBPB, 2, h, txtedits->filename, strlen(txtedits->filename)); os_swix2(OS_BPut, '\n', h); save_one_txt(txtedits->t, h); r.r[0] = 0; /* Close */ r.r[1] = h; os_find(&r); os_swix3(OS_File, 18, buff, (txtedits->ty.ld >> 8) & 0xfff); } f++; } txtedits = txtedits->next; } } if (!reported) { wimp_reporterror((os_error *)e, (wimp_errflags)0, "Edit"); } } /* Fixed stack size !!! * 7.5k is the max required for find [0-9], replace with P?0. * 0.5k is a bodge safety factor. */ int __root_stack_size = 7*1024+512+512; extern int disable_stack_extension; int main(int argc, char *argv[]) { int s; char buff[256]; argc=argc; disable_stack_extension = 1; wimpt_install_signal_handlers(); setlocale(LC_ALL, ""); visdelay_begin(); wimpt_wimpversion(300); wimpt_messages(edit_messages); res_init("Edit"); msgs_init(); wimpt_init(msgs_lookup("ED1")); flex_init(); template_init(); dbox_init(); dboxquery_close(0); /* init */ dboxquery_quit(0); txt_init(); txtedit_init(); for (s = 0; s < N_SIGNALS; s++) { if (s != SIGINT) signal(s, edit_signal_handler); } s = setjmp(SigBuf); if (s) { for (s = 0; s < N_SIGNALS; s++) { signal(s, SIG_DFL); } save_edits(s, buff); return 1; } #if BASIC { int tok, detok; os_read_var_val("Edit$Detokenise", buff, 255); detok = atoi(buff); os_read_var_val("Edit$Tokenise", buff, 255); tok = atoi(buff); txtedit_setBASICaddresses(tok, detok); txtedit_setBASICstrip(Edit_Strip); /* ensure txtedit is in sync with our flag */ txtedit_setBASICincrement(Edit_Incrm); } #endif #if CTL trace_on(); ctl_init(); #endif #if FALSE /* Need to be able to move the hot keys template off the screen, dbox prevents this at the moment... */ { dbox hotkeys = dbox_new("hotkeys"); dbox_showstatic(hotkeys); edit__hotcopy = dbox_syshandle(hotkeys); win_register_event_handler(edit__hotcopy, edit__hot_copy, 0); }; #endif arcedit__createicon(); { char *filename; #if TRACE filename = strcmp(argv[0], "dbug") == 0 ? "" : argv[1]; #else filename = argv[1]; #endif if (filename != NULL) txtedit_new(filename, 0); }; win_register_event_handler(win_ICONBARLOAD, arcedit__bkg_events, 0); win_claim_unknown_events(win_ICONBARLOAD); message_init(); atexit(killalltasks); visdelay_end(); while (1) { #if TRACE if (win_unknown_event_claimer() != -99) { tracef1("unknown event claimer is %i.\n", win_unknown_event_claimer()); }; #endif event_process(); }; return 0; }