#ifndef lint static char rcsid[] = "$Id: pico.c,v 4.95 1996/05/07 00:52:40 mikes Exp $"; #endif /* * Program: Main Pine Composer routines * * * Michael Seibel * Networks and Distributed Computing * Computing and Communications * University of Washington * Administration Builiding, AG-44 * Seattle, Washington, 98195, USA * Internet: mikes@cac.washington.edu * * Please address all bugs and comments to "pine-bugs@cac.washington.edu" * * * Pine and Pico are registered trademarks of the University of Washington. * No commercial use of these trademarks may be made without prior written * permission of the University of Washington. * * Pine, Pico, and Pilot software and its included text are Copyright * 1989-1996 by the University of Washington. * * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this distribution. * * * WEEMACS/PICO NOTES: * * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call * weemacs() and plugged into the Pine mailer. Lots of unused * MicroEmacs code laying around. * * 17 Jan 90 - weemacs() became weemacs() the composer. Header editing * functions added. * * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer. * */ /* * This program is in public domain; written by Dave G. Conroy. * This file contains the main driving routine, and some keyboard processing * code, for the MicroEMACS screen editor. * * REVISION HISTORY: * * 1.0 Steve Wilhite, 30-Nov-85 * - Removed the old LK201 and VT100 logic. Added code to support the * DEC Rainbow keyboard (which is a LK201 layout) using the the Level * 1 Console In ROM INT. See "rainbow.h" for the function key defs * Steve Wilhite, 1-Dec-85 * - massive cleanup on code in display.c and search.c * * 2.0 George Jones, 12-Dec-85 * - Ported to Amiga. * * 3.0 Daniel Lawrence, 29-Dec-85 * 16-apr-86 * - updated documentation and froze development for 3.6 net release */ #include #include #include /* make global definitions not external */ #define maindef #include "osdep.h" /* operating system dependent includes */ #include "pico.h" /* PIne COmposer definitions */ #include "estruct.h" /* global structures and defines */ #include "efunc.h" /* function declarations and name table */ #include "edef.h" /* global definitions */ #include "ebind.h" /* default key bindings */ #ifdef ANSI int func_init(void); void breplace(void *); int any_header_changes(void); #ifdef _WINDOWS int composer_file_drop(int, int, char *); #endif #else int func_init(); void breplace(); int any_header_changes(); #endif /* * function key mappings */ static int pfkm[12][2] = { { F1, (CTRL|'G')}, { F2, (CTRL|'C')}, { F3, (CTRL|'X')}, { F4, (CTRL|'J')}, { F5, (CTRL|'R')}, { F6, (CTRL|'W')}, { F7, (CTRL|'Y')}, { F8, (CTRL|'V')}, { F9, (CTRL|'K')}, { F10, (CTRL|'U')}, { F11, (CTRL|'O')}, #ifdef SPELLER { F12, (CTRL|'T')} #else { F12, (CTRL|'D')} #endif }; /* * flag for the various functions in pico() to set when ready * for pico() to return... */ int pico_all_done = 0; jmp_buf finstate; char *pico_anchor = NULL; extern struct headerentry *headents; /* * pico - the main routine for Pine's composer. * */ pico(pm) PICO *pm; { register int c; register int f; register int n; char bname[NBUFN]; /* buffer name of file to read */ extern struct on_display ods; int checkpointcnt = 0; char chkptfile[NLINE]; Pmaster = pm; gmode = MDWRAP; gmode |= pm->pine_flags; /* high 4 bits rsv'd for pine */ alt_speller = pm->alt_spell; pico_all_done = 0; km_popped = 0; if(!vtinit()) /* Init Displays. */ return(COMP_CANCEL); strcpy(bname, "main"); /* default buffer name */ edinit(bname); /* Buffers, windows. */ if(InitMailHeader(pm)) /* init mail header structure */ gmode &= ~(P_BODY | P_HEADEND); /* flip off special header stuff */ /* setup to process commands */ lastflag = 0; /* Fake last flags. */ curbp->b_mode |= gmode; /* and set default modes*/ pico_anchor = (char *)malloc((strlen(Pmaster->pine_anchor) + 1) * sizeof(char)); if(pico_anchor) strcpy(pico_anchor, Pmaster->pine_anchor); bindtokey(DEL, (gmode & P_DELRUBS) ? forwdel : backdel); if(pm->msgtext) breplace(pm->msgtext); #ifdef _WINDOWS mswin_allowpaste(MSWIN_PASTE_FULL); mswin_setscrollcallback (pico_scroll_callback); #endif pico_all_done = setjmp(finstate); /* jump out of HUP handler ? */ if(gmode & MDALTNOW){ while(!pico_all_done){ if(((gmode & P_BODY) || !Pmaster->headents) && alt_editor(0, 1) < 0) break; /* if problem, drop into pico */ if(Pmaster->headents){ update(); /* paint screen, n' start editing... */ HeaderEditor((gmode & (P_HEADEND | P_BODY)) ? 2 : 0, 0); gmode |= P_BODY; /* make sure we enter alt ed next */ } else pico_all_done = COMP_EXIT; } } else if(!pico_all_done){ if(gmode & P_BODY){ /* begin editing the header? */ ArrangeHeader(); /* line up pointers */ } else{ update(); /* paint screen, */ HeaderEditor((gmode & P_HEADEND) ? 2 : 0, 0); } /* prepare for checkpointing */ chkptfile[0] = '\0'; chkptinit((*Pmaster->ckptdir)(chkptfile, NLINE), NLINE); } while(1){ if(pico_all_done){ #ifdef _WINDOWS mswin_allowpaste(MSWIN_PASTE_DISABLE); mswin_setscrollcallback (NULL); #endif c = anycb() ? BUF_CHANGED : 0; switch(pico_all_done){ /* prepare for/handle final events */ case COMP_EXIT : /* already confirmed */ packheader(); c |= COMP_EXIT; break; case COMP_CANCEL : /* also already confirmed */ packheader(); c = COMP_CANCEL; break; case COMP_GOTHUP: /* * pack up and let caller know that we've received a SIGHUP */ if(ComposerEditing) /* expand addr if needed */ call_builder(&headents[ods.cur_e]); packheader(); c |= COMP_GOTHUP; break; case COMP_SUSPEND : default: /* safest if internal error */ packheader(); c |= COMP_SUSPEND; break; } free(pico_anchor); vttidy(); /* clean up tty modes */ zotdisplay(); /* blast display buffers */ zotedit(); unlink(chkptfile); Pmaster = NULL; /* blat global */ return(c); } if(km_popped){ km_popped--; if(km_popped == 0) /* cause bottom three lines to be repainted */ curwp->w_flag |= WFHARD; } if(km_popped){ /* temporarily change to cause menu to be painted */ term.t_mrow = 2; curwp->w_ntrows -= 2; curwp->w_flag |= WFMODE; movecursor(term.t_nrow-2, 0); /* clear status line, too */ peeol(); } update(); /* Fix up the screen */ if(km_popped){ term.t_mrow = 0; curwp->w_ntrows += 2; } #ifdef MOUSE #ifdef EX_MOUSE /* New mouse function for real mouse text seletion. */ register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow+1), term.t_ncol); #else mouse_in_content(K_MOUSE, -1, -1, -1, 0); register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1), term.t_ncol); #endif #endif #ifdef _WINDOWS mswin_setdndcallback (composer_file_drop); #endif c = GetKey(); #ifdef MOUSE #ifdef EX_MOUSE clear_mfunc(mouse_in_pico); #else clear_mfunc(mouse_in_content); #endif #endif #ifdef _WINDOWS mswin_cleardndcallback (); #endif if(c == NODATA || time_to_check()){ /* new mail ? */ if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){ if(km_popped){ term.t_mrow = 2; curwp->w_ntrows -= 2; curwp->w_flag |= WFHARD; km_popped = 0; } clearcursor(); mlerase(); (*Pmaster->showmsg)(c); mpresf = 1; } clearcursor(); movecursor(0, 0); } if(km_popped) switch(c){ case NODATA: case (CTRL|'L'): km_popped++; break; default: mlerase(); break; } if(c == NODATA) /* no op, getkey timed out */ continue; if (mpresf != FALSE) { /* message stay around only */ if (mpresf++ > NMMESSDELAY) /* so long! */ mlerase(); } f = FALSE; /* vestigial */ n = 1; /* Do it. */ execute(normalize_cmd(c, pfkm, 2), f, n); if(++checkpointcnt >= CHKPTDELAY){ checkpointcnt = 0; writeout(chkptfile); } } } /* * Initialize all of the buffers and windows. The buffer name is passed down * as an argument, because the main routine may have been told to read in a * file by default, and we want the buffer name to be right. */ /* * For the pine composer, we don't want to take over the whole screen * for editing. the first some odd lines are to be used for message * header information editing. */ edinit(bname) char bname[]; { register BUFFER *bp; register WINDOW *wp; if(Pmaster) func_init(); bp = bfind(bname, TRUE, BFWRAPOPEN); /* First buffer */ wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */ if (bp==NULL || wp==NULL){ if(Pmaster) return(0); else exit(1); } curbp = bp; /* Make this current */ wheadp = wp; curwp = wp; wp->w_wndp = NULL; /* Initialize window */ wp->w_bufp = bp; bp->b_nwnd = 1; /* Displayed. */ wp->w_linep = bp->b_linep; wp->w_dotp = bp->b_linep; wp->w_doto = 0; wp->w_markp = NULL; wp->w_marko = 0; bp->b_linecnt = -1; if(Pmaster){ term.t_mrow = Pmaster->menu_rows; wp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE; wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - term.t_mrow; fillcol = Pmaster->fillcolumn; if(Pmaster->oper_dir) strncpy(opertree, Pmaster->oper_dir, NLINE); } else{ if(sup_keyhelp) term.t_mrow = 0; else term.t_mrow = 2; wp->w_toprow = 2; wp->w_ntrows = term.t_nrow - 2 - term.t_mrow; if(userfillcol > 0) /* set fill column */ fillcol = userfillcol; else fillcol = term.t_ncol - 6; } /* * MDSCUR mode implies MDTREE mode with a opertree of home directory, * unless opertree has been set differently. */ if(gmode&MDSCUR && !*opertree) strncpy(opertree, gethomedir(NULL), NLINE); if(*opertree){ fixpath(opertree, NLINE); gmode |= MDTREE; } wp->w_force = 0; wp->w_flag = WFMODE|WFHARD; /* Full. */ } /* * This is the general command execution routine. It handles the fake binding * of all the keys to "self-insert". It also clears out the "thisflag" word, * and arranges to move it to the "lastflag", so that the next command can * look at it. Return the status of command. */ execute(c, f, n) int c, f, n; { register KEYTAB *ktp; register int status; register int i; ktp = (Pmaster) ? &keytab[0] : &pkeytab[0]; while (ktp->k_fp != NULL) { if (ktp->k_code == c) { if(lastflag&CFFILL){ curwp->w_flag |= WFMODE; if(Pmaster == NULL) sgarbk = TRUE; } thisflag = 0; status = (*ktp->k_fp)(f, n); if((lastflag & CFFILL) && (thisflag ^ CFFILL)) fdelete(); lastflag = thisflag; /* * Reset flag saying wrap should open a new line whenever * we execute a command (as opposed to just typing in text). * However, if that command leaves us in the same line on the * screen, then don't reset. */ if(curwp->w_flag & (WFMOVE | WFHARD)) curbp->b_flag |= BFWRAPOPEN; /* wrap should open new line */ return (status); } ++ktp; } if(lastflag & CFFILL) /* blat unusable fill data */ fdelete(); if (VALID_KEY(c)) { /* Self inserting. */ if (n <= 0) { /* Fenceposts. */ lastflag = 0; return (n<0 ? FALSE : TRUE); } thisflag = 0; /* For the future. */ /* if we are in overwrite mode, not at eol, and next char is not a tab or we are at a tab stop, delete a char forword */ if (curwp->w_bufp->b_mode & MDOVER && curwp->w_doto < curwp->w_dotp->l_used && (lgetc(curwp->w_dotp, curwp->w_doto).c != '\t' || (curwp->w_doto) % 8 == 7)) ldelete(1L, FALSE); /* do the appropriate insertion */ /* pico never does C mode, this is simple */ status = linsert(n, c); /* * Check to make sure we didn't go off of the screen * with that character. Take into account tab expansion. * If so wrap the line... */ if(curwp->w_bufp->b_mode & MDWRAP){ register int j; register int k; for(i = j = k = 0; j < llength(curwp->w_dotp); j++, k++) if(isspace((unsigned char)lgetc(curwp->w_dotp, j).c)){ if(lgetc(curwp->w_dotp, j).c == TAB) while(k+1 & 0x07) k++; } else if(k >= fillcol){ wrapword(); break; } } lastflag = thisflag; return (status); } if(c&CTRL) emlwrite("\007Unknown Command: ^%c", (void *)(c&0xff)); else emlwrite("\007Unknown Command", NULL); lastflag = 0; /* Fake last flags. */ return (FALSE); } /* * Fancy quit command, as implemented by Norm. If the any buffer has * changed do a write on that buffer and exit emacs, otherwise simply exit. */ quickexit(f, n) int f, n; { register BUFFER *bp; /* scanning pointer to buffers */ bp = bheadp; while (bp != NULL) { if ((bp->b_flag&BFCHG) != 0 /* Changed. */ && (bp->b_flag&BFTEMP) == 0) { /* Real. */ curbp = bp; /* make that buffer cur */ filesave(f, n); } bp = bp->b_bufp; /* on to the next buffer */ } wquit(f, n); /* conditionally quit */ } /* * abort_composer - ask the question here, then go quit or * return FALSE */ abort_composer(f, n) int f, n; { char *result; result = ""; if(Pmaster->canceltest){ if(((Pmaster->pine_flags & P_ABOOK && !any_header_changes()) || (!(Pmaster->pine_flags & P_ABOOK) && !anycb())) || (result = (*Pmaster->canceltest)())){ pico_all_done = COMP_CANCEL; emlwrite(result, NULL); return(TRUE); } else{ emlwrite("Cancel Cancelled", NULL); curwp->w_flag |= WFMODE; /* and modeline so we */ sgarbk = TRUE; /* redraw the keymenu */ pclear(term.t_nrow - 1, term.t_nrow + 1); return(FALSE); } } else switch(mlyesno(Pmaster->headents ? "Cancel message (answering \"Yes\" will abandon your mail message)" : (anycb() == FALSE) ? "Cancel Edit (and abandon changes)" : "Cancel Edit", FALSE)){ case TRUE: pico_all_done = COMP_CANCEL; return(TRUE); case ABORT: emlwrite("\007Cancel Cancelled", NULL); break; default: mlerase(); } return(FALSE); } /* * suspend_composer - return to pine with what's been edited so far */ suspend_composer(f, n) int f, n; { if(Pmaster && Pmaster->headents) pico_all_done = COMP_SUSPEND; else (*term.t_beep)(); } /* * Quit command. If an argument, always quit. Otherwise confirm if a buffer * has been changed and not written out. Normally bound to "C-X C-C". */ wquit(f, n) int f, n; { register int s; if(Pmaster){ char *prompt, *result; /* First, make sure there are no outstanding problems */ if(AttachError()){ emlwrite("\007Problem with attachments! Fix errors or delete attachments.", NULL); return(FALSE); } /* * if we're not in header, show some of it as we verify sending... */ display_for_send(); if((!(Pmaster->pine_flags & P_ABOOK) || any_header_changes()) && (result = (*Pmaster->exittest)())){ if(sgarbf) update(); lchange(WFHARD); /* set update flags... */ curwp->w_flag |= WFMODE; /* and modeline so we */ sgarbk = TRUE; /* redraw the keymenu */ pclear(term.t_nrow - 2, term.t_nrow + 1); emlwrite(result, NULL); } else{ pico_all_done = COMP_EXIT; return(TRUE); } } else{ if (f != FALSE /* Argument forces it. */ || anycb() == FALSE /* All buffers clean. */ /* User says it's OK. */ || (s=mlyesno("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)", -1)) == FALSE) { vttidy(); exit(0); } if(s == TRUE){ if(filewrite(0,1) == TRUE) wquit(1, 0); } else if(s == ABORT){ emlwrite("Exit cancelled", NULL); if(term.t_mrow == 0) curwp->w_flag |= WFHARD; /* cause bottom 3 lines to paint */ } return(s); } return(FALSE); } /* * Has any editing been done to headers? */ any_header_changes() { struct headerentry *he; for(he = Pmaster->headents; he->name != NULL; he++) if(he->dirty) break; return(he->name && he->dirty); } /* * Abort. * Beep the beeper. Kill off any keyboard macro, etc., that is in progress. * Sometimes called as a routine, to do general aborting of stuff. */ ctrlg(f, n) int f, n; { emlwrite("Cancelled", NULL); return (ABORT); } /* tell the user that this command is illegal while we are in * VIEW (read-only) mode */ rdonly() { (*term.t_beep)(); emlwrite("Key illegal in VIEW mode", NULL); return(FALSE); } /* * reset all globals to their initial values */ func_init() { extern int vtrow; extern int vtcol; extern int lbound; /* * re-initialize global buffer type variables .... */ fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6; eolexist = TRUE; revexist = FALSE; sgarbf = TRUE; mpresf = FALSE; mline_open = FALSE; ComposerEditing = FALSE; /* * re-initialize hardware display variables .... */ vtrow = vtcol = lbound = 0; clearcursor(); pat[0] = '\0'; } /* * pico_help - help function for standalone composer */ pico_help(text, title, i) char *text[], *title; int i; { register int numline = 0; char **p; p = text; while(*p++ != NULL) numline++; return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, text, numline)); } /* * zotedit() - kills the buffer and frees all lines associated with it!!! */ zotedit() { wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = NULL; bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL; free((char *) wheadp); /* clean up window */ wheadp = NULL; curwp = NULL; free((char *) bheadp); /* clean up buffers */ bheadp = NULL; curbp = NULL; zotheader(); /* blast header lines */ kdelete(); /* blast kill buffer */ } #ifdef MOUSE /* * Generic mouse handling functions */ MENUITEM menuitems[12]; /* key labels and functions */ MENUITEM *mfunc = NULL; /* list of regional functions */ mousehandler_t mtrack; /* mouse tracking handler */ /* last mouse position */ static int levent = 0, lrow = 0, lcol = 0, doubleclick, lbutton, lflags; #ifdef DOS static clock_t lastcalled = 0; #else static time_t lastcalled = 0; #endif static mousehandler_t lastf; /* * register_mfunc - register the given function to get called * on mouse events in the given display region */ register_mfunc(f, tlr, tlc, brr, brc) mousehandler_t f; int tlr, tlc, brr, brc; { MENUITEM **mp; if(!mouseexist()) return(FALSE); for(mp = &mfunc; *mp; mp = &(*mp)->next) ; *mp = (MENUITEM *)malloc(sizeof(MENUITEM)); memset(*mp, 0, sizeof(MENUITEM)); (*mp)->action = f; (*mp)->tl.r = tlr; (*mp)->br.r = brr; (*mp)->tl.c = tlc; (*mp)->br.c = brc; (*mp)->lbl.c = (*mp)->lbl.r = 0; (*mp)->label = ""; return(TRUE); } /* * clear_mfunc - clear any previously set mouse function */ void clear_mfunc(f) mousehandler_t f; { MENUITEM *mp, *tp; if(mp = mfunc){ if(mp->action == f) mfunc = mp->next; else for(tp = mp; tp->next; tp = tp->next) if(tp->next->action == f){ mp = tp->next; tp->next = tp->next->next; break; } if(mp){ mp->action = NULL; free(mp); } } } #ifdef EX_MOUSE void clear_mtrack () { mtrack = NULL; mswin_allowmousetrack (FALSE); } void register_mtrack (f) mousehandler_t f; { if (f) { mtrack = f; mswin_allowmousetrack (TRUE); } else clear_mtrack (); } static void move_dot_to (row, col) int row, col; { LINE *lp; int i; lp = curwp->w_linep; i = row - ((Pmaster) ? ComposerTopLine : 2); while(i-- && lp != curbp->b_linep) /* count from top */ lp = lforw(lp); curgoal = col; curwp->w_dotp = lp; /* to new dot. */ curwp->w_doto = getgoal(lp); curwp->w_flag |= WFMOVE; } /* * mouse_in_pico * * When the mouse goes down in the body we set the mark and start * tracking. * * As the mouse moves we update the dot and redraw the screen. * * If the mouse moves above or below the pico body region of the * screen we scroll the text and update the dot position. * * When the mouse comes up we clean up. If the mouse did not * move, then we clear the mark and turn off the selection. * * Most of the mouse processing is handled here. The exception is * mouse down in the header. Can't call HeaderEditor() from here so * we send up the K_MOUSE character, which gets dispatched to * mousepress(), which _can_ call HeaderEditor(). */ unsigned long mouse_in_pico(mevent, row, col, button, flags) int mevent; int row, col, button, flags; { unsigned long rv = 0; /* Our return value. */ int trow, tcol; /* translated row and col. */ static int lheader = FALSE; /* Mouse down was in header. */ /* * What's up. */ switch (mevent) { case M_EVENT_DOWN: /* Ignore mouse down if not in pico body region. */ if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) { clear_mtrack (); break; } /* Detect double clicks. Not that we do anything with em, just * detect them. */ #ifdef DOS #ifdef CLOCKS_PER_SEC doubleclick = (lrow == row && lcol == col && clock() < (lastcalled + CLOCKS_PER_SEC/2)); #else #ifdef CLK_TCK doubleclick = (lrow == row && lcol == col && clock() < (lastcalled + CLK_TCK/2)); #else doubleclick = FALSE; #endif #endif lastcalled = clock(); #else doubleclick = (lrow == row && lcol == col && time(0) < (lastcalled + 2)); lastcalled = time(0); #endif lheader = FALSE; /* Rember mouse down position. */ levent = mevent; lrow = row; lcol = col; lbutton = button; lflags = flags; /* Mouse down in body? */ if (row < (Pmaster ? ComposerTopLine : 2)) { /* Mouse down in message header -> no tracking, just remember * where */ lheader = TRUE; } else { /* Mouse down in message. * If no shift key and an existing mark -> clear the mark. * If shift key and no existing mark -> set mark before moving */ if (!(flags & M_KEY_SHIFT) && curwp->w_markp) setmark (0,1); /* this clears the mark. */ else if (flags & M_KEY_SHIFT && !curwp->w_markp) setmark (0,1); /* while this sets the mark. */ /* Reposition dot to mouse down. */ move_dot_to (row, col); /* Set the mark to dot if no existing mark. */ if (curwp->w_markp == NULL) setmark (0,1); /* Track mouse movement. */ register_mtrack (mouse_in_pico); update (); lheader = FALSE; /* Just to be sure. */ } break; case M_EVENT_TRACK: /* Mouse tracking. */ if (lheader) /* Ignore mouse movement in header. */ break; /* If above or below body, scroll body and adjust the row and col. */ if (row < (Pmaster ? ComposerTopLine : 2)) { /* Scroll text down screen and move dot to top left corner. */ scrollupline (0,1); trow = (Pmaster) ? ComposerTopLine : 2; tcol = 0; } else if (row > term.t_nrow - (term.t_mrow + 1)) { /* Scroll text up screen and move dot to bottom right corner. */ scrolldownline (0,1); trow = term.t_nrow - (term.t_mrow + 1); tcol = term.t_ncol; } else { trow = row; tcol = col; } /* Move dot to target column. */ move_dot_to (trow, tcol); /* Update screen. */ update (); break; case M_EVENT_UP: if (lheader) { lheader = FALSE; /* Last down in header. */ if (row == lrow && col == lcol) { /* Mouse up and down in same place in header. Means the * user want to edit the header. Return K_MOUSE which * will cause mousepress to be called, which will * call HeaderEditor. Can't call HeaderEditor from here * because that would mess up layering. */ if (curwp->w_marko) setmark (0,1); rv = ((unsigned long)K_MOUSE << 16) | TRUE; } } else { /* If up at same place, clear mark */ if (curwp->w_markp == curwp->w_dotp && curwp->w_marko == curwp->w_doto) { setmark (0,1); curwp->w_flag |= WFMOVE; } clear_mtrack (); update (); } break; } return(rv); } #endif /* * mouse_in_content - general mechanism used to pass recognized mouse * events in predefined region back thru the usual * keyboard input stream. The actual return value * passed back from this function is set dynamically * via the "down" argument which is read when both the * "row" and "col" arguments are negative. */ unsigned long mouse_in_content(mevent, row, col, button, flags) int mevent; int row, col, button, flags; { unsigned long rv = 0; static unsigned mouse_val = K_MOUSE; if(row == -1 && col == -1){ mouse_val = mevent; /* setting return value */ } else { /* A real event. */ levent = mevent; switch (mevent) { case M_EVENT_DOWN: /* Mouse down does not mean anything, just keep track of * where it went down and if this is a double click. */ #ifdef DOS #ifdef CLOCKS_PER_SEC doubleclick = (lrow == row && lcol == col && clock() < (lastcalled + CLOCKS_PER_SEC/2)); #else #ifdef CLK_TCK doubleclick = (lrow == row && lcol == col && clock() < (lastcalled + CLK_TCK/2)); #else doubleclick = FALSE; #endif #endif lastcalled = clock(); #else doubleclick = (lrow == row && lcol == col && time(0) < (lastcalled + 2)); lastcalled = time(0); #endif lrow = row; lcol = col; lbutton = button; lflags = flags; break; case M_EVENT_UP: /* Mouse up. If in the same position as it went down * then we return the value set above, which goes into * the character input stream, which gets processed as * a mouse event by some upper layer, which calls to * mouse_get_last(). */ if (lrow == row && lcol == col) { rv = mouse_val; rv = (rv << 16) | TRUE; } break; case M_EVENT_TRACK: break; } } return(rv); } /* * mouse_get_last - Get last mouse event. * */ void mouse_get_last(f, mp) mousehandler_t *f; MOUSEPRESS *mp; { if (f != NULL) *f = lastf; if (mp != NULL) { mp->mevent = levent; mp->row = lrow; mp->col = lcol; mp->doubleclick = doubleclick; mp->button = lbutton; mp->flags = lflags; } } /* * register_key - register the given keystroke to accept mouse events */ void register_key(i, rval, label, label_printer, row, col, len) int i; unsigned rval; char *label; void (*label_printer)(); int row, col, len; { if(i > 11) return; menuitems[i].val = rval; menuitems[i].tl.r = menuitems[i].br.r = row; menuitems[i].tl.c = col; menuitems[i].br.c = col + len; menuitems[i].lbl.r = menuitems[i].tl.r; menuitems[i].lbl.c = menuitems[i].tl.c; menuitems[i].label_hiliter = label_printer; if(menuitems[i].label){ free(menuitems[i].label); menuitems[i].label = NULL; } if(label && (menuitems[i].label =(char *)malloc((strlen(label)+1)*sizeof(char)))) strcpy(menuitems[i].label, label); } #endif /* MOUSE */ /* * Below are functions for use outside pico to manipulate text * in a pico's native format (circular linked list of lines). * * The idea is to streamline pico use by making it fairly easy * for outside programs to prepare text intended for pico's use. * The simple char * alternative is messy as it requires two copies * of the same text, and isn't very economic in limited memory * situations (THANKS BELLEVUE-BILLY.). */ typedef struct picotext { LINE *linep; LINE *dotp; short doto; short crinread; } PICOTEXT; #define PT(X) ((PICOTEXT *)(X)) /* * pico_get - return window struct pointer used as a handle * to the other pico_xxx routines. */ void * pico_get() { PICOTEXT *wp = NULL; LINE *lp = NULL; if(wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))){ wp->crinread = 0; if((lp = lalloc(0)) == NULL){ free(wp); return(NULL); } wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp; wp->doto = 0; } else emlwrite("Can't allocate space for text", NULL); return((void *)wp); } /* * pico_give - free resources and give up picotext struct */ void pico_give(w) void *w; { register LINE *lp; register LINE *fp; fp = lforw(PT(w)->linep); while((lp = fp) != PT(w)->linep){ fp = lforw(lp); free(lp); } free(PT(w)->linep); free((PICOTEXT *)w); } /* * pico_readc - return char at current point. Up to calling routines * to keep cumulative count of chars. */ int pico_readc(w, c) void *w; unsigned char *c; { int rv = 0; if(PT(w)->crinread){ *c = '\012'; /* return LF */ PT(w)->crinread = 0; rv++; } else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */ *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c; rv++; } else if(PT(w)->dotp != PT(w)->linep){ /* return line break */ PT(w)->dotp = lforw(PT(w)->dotp); PT(w)->doto = 0; #if defined(DOS) || defined(OS2) *c = '\015'; PT(w)->crinread++; #else *c = '\012'; /* return local eol! */ #endif rv++; } /* else no chars to return */ return(rv); } /* * pico_writec - write a char into picotext and advance pointers. * Up to calling routines to keep track of total chars * written */ int pico_writec(w, c) void *w; int c; { int rv = 0; if(c == '\r') /* ignore CR's */ rv++; /* so fake it */ else if(c == '\n'){ /* insert newlines on LF */ /* * OK, if there are characters on the current line or * dotp is pointing to the delimiter line, insert a newline * No here's the tricky bit; preserve the implicit EOF newline. */ if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){ PT(w)->dotp = PT(w)->linep; PT(w)->doto = 0; } else{ register LINE *lp; if((lp = lalloc(0)) == NULL){ emlwrite("Can't allocate space for more characters",NULL); return(0); } if(PT(w)->dotp == PT(w)->linep){ lforw(lp) = PT(w)->linep; lback(lp) = lback(PT(w)->linep); lforw(lback(lp)) = lback(PT(w)->linep) = lp; } else{ lforw(lp) = lforw(PT(w)->dotp); lback(lp) = PT(w)->dotp; lback(lforw(lp)) = lforw(PT(w)->dotp) = lp; PT(w)->dotp = lp; PT(w)->doto = 0; } } rv++; } else rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL); return((rv) ? 1 : 0); /* return number written */ } /* * pico_puts - just write the given string into the text */ int pico_puts(w, s) void *w; char *s; { while(*s != '\0') pico_writec(w, (int)*s++); } /* * pico_seek - position dotp and dot at requested location */ int pico_seek(w, offset, orig) void *w; long offset; int orig; { register LINE *lp; PT(w)->crinread = 0; switch(orig){ case 0 : /* SEEK_SET */ PT(w)->dotp = lforw(PT(w)->linep); PT(w)->doto = 0; case 1 : /* SEEK_CUR */ lp = PT(w)->dotp; while(lp != PT(w)->linep){ if(offset <= llength(lp)){ PT(w)->doto = (int)offset; PT(w)->dotp = lp; break; } offset -= ((long)llength(lp) #if defined(DOS) || defined(OS2) + 2L); #else + 1L); #endif lp = lforw(lp); } break; case 2 : /* SEEK_END */ PT(w)->dotp = lback(PT(w)->linep); PT(w)->doto = llength(PT(w)->dotp); break; default : return(-1); } return(0); } /* * breplace - replace the current window's text with the given * LINEs */ void breplace(w) void *w; { register LINE *lp; register LINE *fp; fp = lforw(curbp->b_linep); while((lp = fp) != curbp->b_linep){ /* blast old lines */ fp = lforw(lp); free(lp); } free(curbp->b_linep); curbp->b_linep = PT(w)->linep; /* arrange pointers */ curwp->w_linep = lforw(curbp->b_linep); curwp->w_dotp = lforw(curbp->b_linep); curwp->w_doto = 0; curwp->w_markp = curwp->w_imarkp = NULL; curwp->w_marko = curwp->w_imarko = 0; curbp->b_dotp = curwp->w_dotp; curbp->b_doto = curbp->b_marko = 0; curbp->b_markp = NULL; curbp->b_linecnt = -1; curwp->w_flag |= WFHARD; } #ifdef _WINDOWS /* * */ int composer_file_drop(x, y, filename) int x, y; char *filename; { int attached = 0; if((ComposerTopLine > 2 && x <= ComposerTopLine) || !LikelyASCII(filename)){ AppendAttachment(filename, NULL, NULL); attached++; } else{ setimark(FALSE, 1); ifile(filename); swapimark(FALSE, 1); } if(ComposerEditing){ /* update display */ PaintBody(0); } else{ refresh(0, 1); update(); } if(attached) emlwrite("Attached dropped file \"%s\"", filename); else emlwrite("Inserted dropped file \"%s\"", filename); if(ComposerEditing){ /* restore cursor */ HeaderPaintCursor(); } else{ curwp->w_flag |= WFHARD; update(); } return(1); } #endif