* New version 2.26
[alpine.git] / pico / pico.c
blobbb1376fa35670dff98cd505045191579012a01f4
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * Program: Main Pine Composer routines
19 * WEEMACS/PICO NOTES:
21 * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call
22 * weemacs() and plugged into the Pine mailer. Lots of unused
23 * MicroEmacs code laying around.
25 * 17 Jan 90 - weemacs() became weemacs() the composer. Header editing
26 * functions added.
28 * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer.
34 * This program is in public domain; written by Dave G. Conroy.
35 * This file contains the main driving routine, and some keyboard processing
36 * code, for the MicroEMACS screen editor.
38 * REVISION HISTORY:
40 * 1.0 Steve Wilhite, 30-Nov-85
41 * - Removed the old LK201 and VT100 logic. Added code to support the
42 * DEC Rainbow keyboard (which is a LK201 layout) using the the Level
43 * 1 Console In ROM INT. See "rainbow.h" for the function key defs
44 * Steve Wilhite, 1-Dec-85
45 * - massive cleanup on code in display.c and search.c
47 * 2.0 George Jones, 12-Dec-85
48 * - Ported to Amiga.
50 * 3.0 Daniel Lawrence, 29-Dec-85
51 * 16-apr-86
52 * - updated documentation and froze development for 3.6 net release
55 /* make global definitions not external */
56 #define maindef
58 #include "../c-client/mail.h"
59 #include "../c-client/utf8.h"
61 #ifdef _WINDOWS
62 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
63 #undef ERROR
65 #endif
66 #include "headers.h"
67 #include "ebind.h" /* default key bindings */
68 #include "../pith/charconv/filesys.h"
71 void remove_directions_mark(void);
72 void func_init(void);
73 void breplace(void *w);
74 int any_header_changes(void);
75 int cleanwhitespace(void);
76 int isquotedspace(LINE *);
80 * function key mappings
82 static UCS pfkm[12][2] = {
83 { F1, (CTRL|'G')},
84 { F2, (CTRL|'C')},
85 { F3, (CTRL|'X')},
86 { F4, (CTRL|'J')},
87 { F5, (CTRL|'R')},
88 { F6, (CTRL|'W')},
89 { F7, (CTRL|'Y')},
90 { F8, (CTRL|'V')},
91 { F9, (CTRL|'K')},
92 { F10, (CTRL|'U')},
93 { F11, (CTRL|'O')},
94 #ifdef SPELLER
95 { F12, (CTRL|'T')}
96 #else
97 { F12, (CTRL|'D')}
98 #endif
102 void
103 remove_directions_mark(void)
105 LINE *lp;
106 int i;
107 UCS c;
109 for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
110 for(i = 0; i < llength(lp);){
111 c = lgetc(lp, i).c;
112 if(c == 0x200E || c == 0x200F){
113 curwp->w_dotp = lp;
114 curwp->w_doto = i;
115 if(i < llength(lp))
116 forwdel(FALSE, 1);
117 else
118 llength(lp)--;
119 direction = c == 0x200E ? 0 : 1;
121 else
122 lgetc(lp,i++).d = direction;
125 curwp->w_linep = lforw(curbp->b_linep);
126 curwp->w_dotp = lforw(curbp->b_linep);
127 curwp->w_doto = 0;
132 * flag for the various functions in pico() to set when ready
133 * for pico() to return...
135 int pico_all_done = 0;
136 jmp_buf finstate;
137 UCS *pico_anchor = NULL;
138 extern struct headerentry *headents;
141 * pico - the main routine for Pine's composer.
145 pico(PICO *pm)
147 UCS c;
148 register int f;
149 register int n;
150 char bname[NBUFN]; /* buffer name of file to read */
151 extern struct on_display ods;
152 int checkpointcnt = 0, input = 0;
153 int ret;
154 char chkptfile[NLINE];
155 #ifdef _WINDOWS
156 int cursor_shown;
157 #endif
159 Pmaster = pm;
160 gmode = MDWRAP;
161 gmode |= pm->pine_flags; /* high 4 bits rsv'd for pine */
163 alt_speller = pm->alt_spell;
164 #ifdef _WINDOWS
165 dictionary = pm->dict;
166 chosen_dict = pm->chosen_dict;
167 #endif /* _WINDOWS */
168 pico_all_done = 0;
169 km_popped = 0;
171 if(!vtinit()) /* Init Displays. */
172 return(COMP_CANCEL);
174 strncpy(bname, "main", sizeof(bname)); /* default buffer name */
175 bname[sizeof(bname)-1] = '\0';
176 edinit(bname); /* Buffers, windows. */
178 if(InitMailHeader(pm)) /* init mail header structure */
179 gmode &= ~(P_BODY | P_HEADEND); /* flip off special header stuff */
181 /* setup to process commands */
182 lastflag = 0; /* Fake last flags. */
183 curbp->b_mode |= gmode; /* and set default modes*/
185 if(Pmaster->pine_anchor)
186 pico_anchor = utf8_to_ucs4_cpystr(Pmaster->pine_anchor);
187 else
188 pico_anchor = NULL;
190 if(Pmaster->quote_str)
191 glo_quote_str = utf8_to_ucs4_cpystr(Pmaster->quote_str);
192 else
193 glo_quote_str = NULL;
195 if(Pmaster->wordseps)
196 glo_wordseps = ucs4_cpystr(Pmaster->wordseps);
197 else
198 glo_wordseps = NULL;
200 bindtokey(DEL, (gmode & P_DELRUBS) ? forwdel : backdel);
202 if(pm->msgtext)
203 breplace(pm->msgtext);
205 remove_directions_mark();
207 #ifdef _WINDOWS
208 cursor_shown = mswin_showcaret(1); /* turn on for main window */
209 mswin_allowpaste(MSWIN_PASTE_FULL);
210 mswin_setscrollcallback (pico_scroll_callback);
211 #endif
213 /* prepare for checkpointing */
214 chkptfile[0] = '\0';
215 chkptinit((*Pmaster->ckptdir)(chkptfile, sizeof(chkptfile)), sizeof(chkptfile));
216 if(gmode & P_CHKPTNOW)
217 writeout(chkptfile, TRUE);
219 pico_all_done = setjmp(finstate); /* jump out of HUP handler ? */
221 if(gmode & MDALTNOW){
222 while(!pico_all_done){
223 if(((gmode & P_BODY) || !Pmaster->headents)
224 && alt_editor(0, 1) < 0)
225 break; /* if problem, drop into pico */
227 if(Pmaster->headents){
228 update(); /* paint screen, n' start editing... */
229 HeaderEditor((gmode & (P_HEADEND | P_BODY)) ? 2 : 0, 0);
230 gmode |= P_BODY; /* make sure we enter alt ed next */
232 else
233 pico_all_done = COMP_EXIT;
236 else if(!pico_all_done){
237 if(gmode & P_BODY){ /* begin editing the header? */
238 ArrangeHeader(); /* line up pointers */
240 * Move to the offset pine asked us to move to.
241 * Perhaps we should be checking to see if this is
242 * a reasonable number before moving.
244 if(Pmaster && Pmaster->edit_offset)
245 forwchar(FALSE, Pmaster->edit_offset);
247 else{
248 update(); /* paint screen, */
249 HeaderEditor((gmode & P_HEADEND) ? 2 : 0, 0);
253 while(1){
254 if(pico_all_done){
255 #ifdef _WINDOWS
256 if(!cursor_shown)
257 mswin_showcaret(0);
259 mswin_allowpaste(MSWIN_PASTE_DISABLE);
260 mswin_setscrollcallback (NULL);
261 #endif
262 ret = anycb() ? BUF_CHANGED : 0;
263 switch(pico_all_done){ /* prepare for/handle final events */
264 case COMP_EXIT : /* already confirmed */
265 packheader();
266 if(Pmaster
267 && (Pmaster->strip_ws_before_send
268 || Pmaster->allow_flowed_text))
269 cleanwhitespace();
270 ret |= COMP_EXIT;
271 break;
273 case COMP_CANCEL : /* also already confirmed */
274 packheader();
275 ret = COMP_CANCEL;
276 break;
278 case COMP_GOTHUP:
280 * pack up and let caller know that we've received a SIGHUP
282 if(ComposerEditing) /* expand addr if needed */
283 call_builder(&headents[ods.cur_e], NULL, NULL);
285 packheader();
286 ret |= COMP_GOTHUP;
287 break;
289 case COMP_SUSPEND :
290 default: /* safest if internal error */
292 * If we're in the headers mark the current header line
293 * with start_here bit so caller knows where to reset.
294 * Also set the edit_offset, which is either the offset
295 * into this header line or the offset into the body.
296 * Packheader will adjust edit_offset for multi-line
297 * headers.
299 if(ComposerEditing){ /* in the headers */
300 headents[ods.cur_e].start_here = 1;
301 Pmaster->edit_offset = ods.p_ind;
303 else{
304 register LINE *clp;
305 register long offset;
307 for(clp = lforw(curbp->b_linep), offset = 0L;
308 clp != curwp->w_dotp;
309 clp = lforw(clp))
310 offset += (llength(clp) + 1);
312 Pmaster->edit_offset = offset + curwp->w_doto;
315 packheader();
316 ret |= COMP_SUSPEND;
317 break;
320 if(pico_anchor)
321 fs_give((void **) &pico_anchor);
322 if(glo_quote_str)
323 fs_give((void **) &glo_quote_str);
324 if(glo_wordseps)
325 fs_give((void **) &glo_wordseps);
327 vttidy(); /* clean up tty modes */
328 zotdisplay(); /* blast display buffers */
329 zotedit();
330 our_unlink(chkptfile);
331 Pmaster = NULL; /* blat global */
333 return(ret);
336 if(km_popped){
337 km_popped--;
338 if(km_popped == 0) /* cause bottom three lines to be repainted */
339 curwp->w_flag |= WFHARD;
342 if(km_popped){ /* temporarily change to cause menu to be painted */
343 term.t_mrow = 2;
344 curwp->w_ntrows -= 2;
345 curwp->w_flag |= WFMODE;
346 movecursor(term.t_nrow-2, 0); /* clear status line, too */
347 peeol();
350 update(); /* Fix up the screen */
351 if(km_popped){
352 term.t_mrow = 0;
353 curwp->w_ntrows += 2;
356 #ifdef MOUSE
357 #ifdef EX_MOUSE
358 /* New mouse function for real mouse text selection. */
359 register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow+1),
360 term.t_ncol);
361 #else
362 mouse_in_content(KEY_MOUSE, -1, -1, -1, 0);
363 register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1),
364 term.t_ncol);
365 #endif
366 #endif
367 #ifdef _WINDOWS
368 mswin_setdndcallback (composer_file_drop);
369 mswin_mousetrackcallback(pico_cursor);
370 #endif
371 c = GetKey();
372 if (term.t_nrow < 6 && c != NODATA){
373 (*term.t_beep)();
374 emlwrite(_("Please make the screen bigger."), NULL);
375 continue;
378 #ifdef MOUSE
379 #ifdef EX_MOUSE
380 clear_mfunc(mouse_in_pico);
381 #else
382 clear_mfunc(mouse_in_content);
383 #endif
384 #endif
385 #ifdef _WINDOWS
386 mswin_cleardndcallback ();
387 mswin_mousetrackcallback(NULL);
388 #endif
389 if(c == NODATA || time_to_check()){ /* new mail ? */
390 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
391 int rv;
393 if(km_popped){
394 term.t_mrow = 2;
395 curwp->w_ntrows -= 2;
396 curwp->w_flag |= WFHARD;
397 km_popped = 0;
400 clearcursor();
401 mlerase();
402 rv = (*Pmaster->showmsg)(c);
403 ttresize();
404 picosigs(); /* restore altered handlers */
405 if(rv) /* Did showmsg corrupt the display? */
406 PaintBody(0); /* Yes, repaint */
408 mpresf = 1;
409 input = 0;
412 clearcursor();
413 movecursor(0, 0);
416 if(km_popped)
417 switch(c){
418 case NODATA:
419 case (CTRL|'L'):
420 km_popped++;
421 break;
423 default:
424 mlerase();
425 break;
428 if(c == NODATA) /* no op, getkey timed out */
429 continue;
430 else if(!input++)
431 (*Pmaster->keybinput)();
433 if (mpresf != FALSE) { /* message stay around only */
434 if (mpresf++ > NMMESSDELAY) /* so long! */
435 mlerase();
438 f = FALSE; /* vestigial */
439 n = 1;
440 /* Do it. */
441 execute(normalize_cmd(c, pfkm, 2), f, n);
442 if(++checkpointcnt >= CHKPTDELAY){
443 checkpointcnt = 0;
444 writeout(chkptfile, TRUE);
450 * Initialize all of the buffers and windows. The buffer name is passed down
451 * as an argument, because the main routine may have been told to read in a
452 * file by default, and we want the buffer name to be right.
456 * For the pine composer, we don't want to take over the whole screen
457 * for editing. the first some odd lines are to be used for message
458 * header information editing.
460 void
461 edinit(char bname[])
463 register BUFFER *bp;
464 register WINDOW *wp;
466 if(Pmaster)
467 func_init();
469 bp = bfind(bname, TRUE, BFWRAPOPEN); /* First buffer */
470 wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
472 if (bp==NULL || wp==NULL){
473 if(Pmaster)
474 return;
475 else
476 exit(1);
479 curbp = bp; /* Make this current */
480 wheadp = wp;
481 curwp = wp;
482 wp->w_wndp = NULL; /* Initialize window */
483 wp->w_bufp = bp;
484 bp->b_nwnd = 1; /* Displayed. */
485 wp->w_linep = bp->b_linep;
486 wp->w_dotp = bp->b_linep;
487 wp->w_doto = 0;
488 wp->w_markp = wp->w_imarkp = NULL;
489 wp->w_marko = wp->w_imarko = 0;
490 bp->b_linecnt = -1;
492 if(Pmaster){
493 term.t_mrow = Pmaster->menu_rows;
494 wp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
495 wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - term.t_mrow;
496 fillcol = Pmaster->fillcolumn;
497 strncpy(opertree,
498 (Pmaster->oper_dir && strlen(Pmaster->oper_dir) < NLINE)
499 ? Pmaster->oper_dir : "", sizeof(opertree));
500 opertree[sizeof(opertree)-1] = '\0';
501 input_cs = Pmaster->input_cs;
503 else{
504 if(sup_keyhelp)
505 term.t_mrow = 0;
506 else
507 term.t_mrow = 2;
509 wp->w_toprow = 2;
510 wp->w_ntrows = term.t_nrow - 2 - term.t_mrow;
511 if(userfillcol > 0) /* set fill column */
512 fillcol = userfillcol;
513 else
514 fillcol = term.t_ncol - 6;
518 * MDSCUR mode implies MDTREE mode with a opertree of home directory,
519 * unless opertree has been set differently.
521 if((gmode & MDSCUR) && !opertree[0]){
522 strncpy(opertree, gethomedir(NULL), sizeof(opertree));
523 opertree[sizeof(opertree)-1] = '\0';
526 if(*opertree)
527 fixpath(opertree, sizeof(opertree));
529 wp->w_force = 0;
530 wp->w_flag = WFMODE|WFHARD; /* Full. */
535 * This is the general command execution routine. It handles the fake binding
536 * of all the keys to "self-insert". It also clears out the "thisflag" word,
537 * and arranges to move it to the "lastflag", so that the next command can
538 * look at it. Return the status of command.
541 execute(UCS c, int f, int n)
543 KEYTAB *ktp;
544 int status, ww;
546 ktp = (Pmaster) ? &keytab[0] : &pkeytab[0];
548 while (ktp->k_fp != NULL) {
549 if (ktp->k_code == c) {
551 if(lastflag&CFFILL){
552 curwp->w_flag |= WFMODE;
553 if(Pmaster == NULL)
554 sgarbk = TRUE;
557 thisflag = 0;
558 status = (*ktp->k_fp)(f, n);
559 if((lastflag & CFFILL) && !(thisflag & CFFILL))
560 fdelete();
561 if((lastflag & CFFLBF) && !(thisflag & CFFLBF))
562 kdelete();
564 lastflag = thisflag;
567 * Reset flag saying wrap should open a new line whenever
568 * we execute a command (as opposed to just typing in text).
569 * However, if that command leaves us in the same line on the
570 * screen, then don't reset.
572 if(curwp->w_flag & (WFMOVE | WFHARD))
573 curbp->b_flag |= BFWRAPOPEN; /* wrap should open new line */
575 return (status);
577 ++ktp;
580 if(lastflag & CFFILL) /* blat unusable fill data */
581 fdelete();
582 if(lastflag & CFFLBF)
583 kdelete();
585 if (VALID_KEY(c)) { /* Self inserting. */
587 if (n <= 0) { /* Fenceposts. */
588 lastflag = 0;
589 return (n<0 ? FALSE : TRUE);
591 thisflag = 0; /* For the future. */
593 /* do the appropriate insertion */
594 /* pico never does C mode, this is simple */
595 status = linsert(n, c);
598 * Check to make sure we didn't go off of the screen
599 * with that character. Take into account tab expansion.
600 * If so wrap the line...
602 if(curwp->w_bufp->b_mode & MDWRAP){
603 int j, wid;
605 wid = 0;
606 for(j = 0; j < llength(curwp->w_dotp); j++)
607 if(ucs4_isspace(lgetc(curwp->w_dotp, j).c)){
608 if(lgetc(curwp->w_dotp, j).c == TAB){
609 ++wid;
610 while(wid & 0x07)
611 ++wid;
613 else
614 ++wid;
616 else{
617 ww = wcellwidth((UCS) lgetc(curwp->w_dotp, j).c);
618 wid += (ww >= 0 ? ww : 1);
619 if(wid > fillcol){
620 wrapword();
621 break;
626 lastflag = thisflag;
627 return (status);
630 unknown_command(c);
632 lastflag = 0; /* Fake last flags. */
633 return (FALSE);
639 * Fancy quit command, as implemented by Norm. If the any buffer has
640 * changed do a write on that buffer and exit emacs, otherwise simply exit.
643 quickexit(int f, int n)
645 register BUFFER *bp; /* scanning pointer to buffers */
647 bp = bheadp;
648 while (bp != NULL) {
649 if ((bp->b_flag&BFCHG) != 0 /* Changed. */
650 && (bp->b_flag&BFTEMP) == 0) { /* Real. */
651 curbp = bp; /* make that buffer cur */
652 filesave(f, n);
654 bp = bp->b_bufp; /* on to the next buffer */
656 return(wquit(f, n)); /* conditionally quit */
662 * abort_composer - ask the question here, then go quit or
663 * return FALSE
666 abort_composer(int f, int n)
668 char *result;
670 result = "";
672 Pmaster->arm_winch_cleanup++;
673 if(Pmaster->canceltest){
674 if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes())
675 || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){
676 pico_all_done = COMP_CANCEL;
677 emlwrite(result, NULL);
678 Pmaster->arm_winch_cleanup--;
679 return(TRUE);
681 else{
682 /* TRANSLATORS: The user typed the Cancel command and was
683 asked to confirm that. Instead they canceled the cancel
684 command. */
685 emlwrite(_("Cancel Cancelled"), NULL);
686 curwp->w_flag |= WFMODE; /* and modeline so we */
687 sgarbk = TRUE; /* redraw the keymenu */
688 pclear(term.t_nrow-1, term.t_nrow);
689 Pmaster->arm_winch_cleanup--;
690 return(FALSE);
693 else switch(mlyesno_utf8(Pmaster->headents
694 ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
695 : (anycb() == FALSE)
696 ? _("Cancel Edit (and abandon changes)")
697 : _("Cancel Edit"),
698 FALSE)){
699 case TRUE:
700 pico_all_done = COMP_CANCEL;
701 return(TRUE);
703 case ABORT:
704 emlwwrite(_("Cancel Cancelled"), NULL);
705 break;
707 default:
708 mlerase();
710 return(FALSE);
715 * suspend_composer - return to pine with what's been edited so far
718 suspend_composer(int f, int n)
720 if(Pmaster && Pmaster->headents)
721 pico_all_done = COMP_SUSPEND;
722 else
723 (*term.t_beep)();
725 return(TRUE);
731 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
732 * has been changed and not written out. Normally bound to "C-X C-C".
735 wquit(int f, int n)
737 register int s;
739 if(Pmaster){
740 char *result = NULL;
741 int ret;
743 /* First, make sure there are no outstanding problems */
744 if(AttachError()){
745 emlwwrite(_("Problem with attachments! Fix errors or delete attachments."), NULL);
746 return(FALSE);
749 #ifdef SPELLER
750 if(Pmaster->always_spell_check)
751 if(spell(0, 0) == -1)
752 sleep(3); /* problem, show error */
753 #endif
755 * if we're not in header, show some of it as we verify sending...
757 display_for_send();
758 packheader();
759 Pmaster->arm_winch_cleanup++;
760 if((!(Pmaster->pine_flags & MDHDRONLY) || any_header_changes())
761 && (ret = (*Pmaster->exittest)(Pmaster->headents,
762 redraw_pico_for_callback,
763 Pmaster->allow_flowed_text,
764 &result))){
765 Pmaster->arm_winch_cleanup--;
767 if(ret == -1){
768 pico_all_done = COMP_CANCEL;
770 else{
771 if(sgarbf)
772 update();
774 lchange(WFHARD); /* set update flags... */
775 curwp->w_flag |= WFMODE; /* and modeline so we */
776 sgarbk = TRUE; /* redraw the keymenu */
777 pclear(term.t_nrow-2, term.t_nrow);
780 if(result && *result)
781 emlwrite(result, NULL);
783 else{
784 Pmaster->arm_winch_cleanup--;
785 pico_all_done = COMP_EXIT;
786 return(TRUE);
789 else{
790 if (f != FALSE /* Argument forces it. */
791 || anycb() == FALSE /* All buffers clean. */
792 /* User says it's OK. */
793 /* TRANSLATORS: buffer is the in-memory copy of a file */
794 || (s=mlyesno_utf8(_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"), -1)) == FALSE) {
795 vttidy();
796 #if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
797 kbdestroy(kbesc);
798 #endif
799 exit(0);
802 if(s == TRUE){
803 if(filewrite(0,1) == TRUE){
804 #ifdef _WINDOWS
805 if(dictionary != NULL){
806 int i;
807 for(i = 0; dictionary[i] != NULL; i++)
808 fs_give((void **)&dictionary[i]);
809 fs_give((void **)dictionary);
811 #endif /* _WINDOWS */
812 wquit(1, 0);
815 else if(s == ABORT){
816 emlwrite(_("Exit cancelled"), NULL);
817 if(term.t_mrow == 0)
818 curwp->w_flag |= WFHARD; /* cause bottom 3 lines to paint */
820 return(s);
823 return(FALSE);
828 * Has any editing been done to headers?
831 any_header_changes(void)
833 struct headerentry *he;
835 for(he = Pmaster->headents; he->name != NULL; he++)
836 if(he->dirty)
837 break;
839 return(he->name && he->dirty);
843 isquotedspace(LINE *line)
845 int i, was_quote = 0;
846 for(i = 0; i < llength(line); i++){
847 if(lgetc(line, i).c == '>')
848 was_quote = 1;
849 else if(was_quote && lgetc(line, i).c == ' '){
850 if(i+1 < llength(line) && ucs4_isspace(lgetc(line,i+1).c))
851 return 1;
852 else
853 return 0;
855 else
856 return 0;
858 return 0;
862 * This function serves two purposes, 1) to strip white space when
863 * Pmaster asks that the composition have its trailing white space
864 * stripped, or 2) to prepare the text as flowed text, as Pmaster
865 * is telling us that we're working with flowed text.
867 * What flowed currently means to us is stripping all trailing white
868 * space, except for one space if the following line is a continuation
869 * of the paragraph. Also, we space-stuff all lines beginning
870 * with white-space, and leave siglines alone.
873 cleanwhitespace(void)
875 LINE *cursor_dotp = NULL, **lp = NULL;
876 int i = 0, cursor_doto = 0, is_cursor_line = 0;
877 int do_space_stuffing = 0;
879 if(Pmaster && Pmaster->allow_flowed_text && !(*Pmaster->user_says_noflow)())
880 do_space_stuffing++;
882 cursor_dotp = curwp->w_dotp;
883 cursor_doto = curwp->w_doto;
884 gotobob(FALSE, 1);
886 for(lp = &curwp->w_dotp; (*lp) != curbp->b_linep; (*lp) = lforw(*lp)){
887 if(!(llength(*lp) == 3
888 && lgetc(*lp, 0).c == '-'
889 && lgetc(*lp, 1).c == '-'
890 && lgetc(*lp, 2).c == ' ')
891 && llength(*lp)){
892 is_cursor_line = (cursor_dotp == (*lp));
893 /* trim trailing whitespace, to be added back if flowing */
894 for(i = llength(*lp); i; i--)
895 if(!ucs4_isspace(lgetc(*lp, i - 1).c))
896 break;
897 if(i != llength(*lp)){
898 int flow_line = 0;
900 if(Pmaster && !Pmaster->strip_ws_before_send
901 && lforw(*lp) != curbp->b_linep
902 && llength(lforw(*lp))
903 && !(ucs4_isspace(lgetc(lforw(*lp), 0).c)
904 || isquotedspace(lforw(*lp)))
905 && !(llength(lforw(*lp)) == 3
906 && lgetc(lforw(*lp), 0).c == '-'
907 && lgetc(lforw(*lp), 1).c == '-'
908 && lgetc(lforw(*lp), 2).c == ' '))
909 flow_line = 1;
910 if(flow_line && i && lgetc(*lp, i).c == ' '){
911 /* flowed line ending with space */
912 i++;
913 if(i != llength(*lp)){
914 curwp->w_doto = i;
915 ldelete(llength(*lp) - i, NULL);
918 else if(flow_line && i && ucs4_isspace(lgetc(*lp, i).c)){
919 /* flowed line ending with whitespace other than space*/
920 curwp->w_doto = i;
921 ldelete(llength(*lp) - i, NULL);
922 linsert(1, ' ');
924 else{
925 curwp->w_doto = i;
926 ldelete(llength(*lp) - i, NULL);
929 if(do_space_stuffing && llength(*lp) && ucs4_isspace(lgetc(*lp, 0).c)){
930 /* space-stuff only if flowed */
931 if(Pmaster)
932 Pmaster->space_stuffed = 1;
933 curwp->w_doto = 0;
934 if(is_cursor_line && cursor_doto)
935 cursor_doto++;
936 linsert(1, ' ');
938 if(is_cursor_line)
939 cursor_dotp = (*lp);
943 /* put the cursor back where we found it */
944 gotobob(FALSE, 1);
945 curwp->w_dotp = cursor_dotp;
946 curwp->w_doto = (cursor_doto < llength(curwp->w_dotp))
947 ? cursor_doto : llength(curwp->w_dotp) - 1;
949 return(0);
953 * Remove all trailing white space from the text
956 stripwhitespace(void)
958 int i;
959 LINE *cur_line = lforw(curbp->b_linep);
962 /* we gotta test for the sigdash case here */
963 if(!(cur_line->l_used == 3 &&
964 lgetc(cur_line, 0).c == '-' &&
965 lgetc(cur_line, 1).c == '-' &&
966 lgetc(cur_line, 2).c == ' ')){
967 for(i = cur_line->l_used - 1; i >= 0; i--)
968 if(ucs4_isspace(lgetc(cur_line, i).c))
969 cur_line->l_used--;
970 else
971 break;
973 }while((cur_line = lforw(cur_line)) != curbp->b_linep);
974 return 0;
978 * Abort.
979 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
980 * Sometimes called as a routine, to do general aborting of stuff.
983 ctrlg(int f, int n)
985 emlwrite(_("Cancelled"), NULL);
986 return (ABORT);
990 /* tell the user that this command is illegal while we are in
991 * VIEW (read-only) mode
994 rdonly(void)
996 (*term.t_beep)();
997 emlwrite("Key illegal in VIEW mode", NULL);
998 return(FALSE);
1004 * reset all globals to their initial values
1006 void
1007 func_init(void)
1009 extern int vtrow;
1010 extern int vtcol;
1011 extern int lbound;
1014 * re-initialize global buffer type variables ....
1016 fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6;
1017 sgarbf = TRUE;
1018 mpresf = FALSE;
1019 mline_open = FALSE;
1020 ComposerEditing = FALSE;
1023 * re-initialize hardware display variables ....
1025 vtrow = vtcol = lbound = 0;
1026 clearcursor();
1028 pat[0] = rpat[0] = '\0';
1029 browse_dir[0] = '\0';
1034 * pico_help - help function for standalone composer
1035 * Oops - looks like utf8title is unused!
1037 * This should be fixed to handle TAB characters.
1040 pico_help(char *utf8text[], char *utf8title, int i)
1042 register int numline = 0;
1043 char **p;
1045 p = utf8text;
1046 while(*p++ != NULL)
1047 numline++;
1049 return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, utf8text, numline));
1055 * zotedit() - kills the buffer and frees all lines associated with it!!!
1057 void
1058 zotedit(void)
1060 wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = wheadp->w_imarkp = NULL;
1061 bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL;
1063 free((char *) wheadp); /* clean up window */
1064 wheadp = NULL;
1065 curwp = NULL;
1067 free((char *) bheadp); /* clean up buffers */
1068 bheadp = NULL;
1069 curbp = NULL;
1071 zotheader(); /* blast header lines */
1073 kdelete(); /* blast kill buffer */
1078 #ifdef MOUSE
1080 * Generic mouse handling functions
1082 MENUITEM menuitems[12]; /* key labels and functions */
1083 MENUITEM *mfunc = NULL; /* list of regional functions */
1084 mousehandler_t mtrack; /* mouse tracking handler */
1086 /* last mouse position */
1087 static unsigned long levent = 0L;
1088 static int lrow = 0, lcol = 0, doubleclick, lbutton, lflags;
1089 #ifdef DOS
1090 static clock_t lastcalled = 0;
1091 #else
1092 static time_t lastcalled = 0;
1093 #endif
1094 static mousehandler_t lastf;
1098 * register_mfunc - register the given function to get called
1099 * on mouse events in the given display region
1102 register_mfunc(mousehandler_t f, int tlr, int tlc, int brr, int brc)
1104 MENUITEM **mp;
1106 if(!mouseexist())
1107 return(FALSE);
1109 for(mp = &mfunc; *mp; mp = &(*mp)->next)
1112 *mp = (MENUITEM *)malloc(sizeof(MENUITEM));
1113 memset(*mp, 0, sizeof(MENUITEM));
1115 (*mp)->action = f;
1116 (*mp)->tl.r = tlr;
1117 (*mp)->br.r = brr;
1118 (*mp)->tl.c = tlc;
1119 (*mp)->br.c = brc;
1120 (*mp)->lbl.c = (*mp)->lbl.r = 0;
1121 (*mp)->label = "";
1122 return(TRUE);
1127 * clear_mfunc - clear any previously set mouse function
1129 void
1130 clear_mfunc(mousehandler_t f)
1132 MENUITEM *mp, *tp;
1134 if((mp = mfunc) != NULL){
1135 if(mp->action == f)
1136 mfunc = mp->next;
1137 else
1138 for(tp = mp; tp->next; tp = tp->next)
1139 if(tp->next->action == f){
1140 mp = tp->next;
1141 tp->next = tp->next->next;
1142 break;
1145 if(mp){
1146 mp->action = NULL;
1147 free(mp);
1154 #ifdef EX_MOUSE
1156 void
1157 clear_mtrack(void)
1159 mtrack = NULL;
1160 mswin_allowmousetrack (FALSE);
1164 void
1165 register_mtrack(mousehandler_t f)
1167 if (f) {
1168 mtrack = f;
1169 mswin_allowmousetrack (TRUE);
1171 else
1172 clear_mtrack ();
1176 static void
1177 move_dot_to(int row, int col)
1179 LINE *lp;
1180 int i;
1182 lp = curwp->w_linep;
1183 i = row - ((Pmaster) ? ComposerTopLine : 2);
1184 while(i-- && lp != curbp->b_linep) /* count from top */
1185 lp = lforw(lp);
1186 curgoal = col;
1187 curwp->w_dotp = lp; /* to new dot. */
1188 curwp->w_doto = getgoal(lp);
1189 curwp->w_flag |= WFMOVE;
1194 * mouse_in_pico
1196 * When the mouse goes down in the body we set the mark and start
1197 * tracking.
1199 * As the mouse moves we update the dot and redraw the screen.
1201 * If the mouse moves above or below the pico body region of the
1202 * screen we scroll the text and update the dot position.
1204 * When the mouse comes up we clean up. If the mouse did not
1205 * move, then we clear the mark and turn off the selection.
1207 * Most of the mouse processing is handled here. The exception is
1208 * mouse down in the header. Can't call HeaderEditor() from here so
1209 * we send up the KEY_MOUSE character, which gets dispatched to
1210 * mousepress(), which _can_ call HeaderEditor().
1212 unsigned long
1213 mouse_in_pico(unsigned long mevent, int row, int col, int button, int flags)
1215 unsigned long rv = 0; /* Our return value. */
1216 int trow, tcol; /* translated row and col. */
1218 static int lheader = FALSE; /* Mouse down was in header. */
1222 * What's up.
1224 switch (mevent) {
1225 case M_EVENT_DOWN:
1226 if(button != M_BUTTON_LEFT)
1227 break;
1229 /* Ignore mouse down if not in pico body region. */
1230 if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) {
1231 clear_mtrack ();
1232 break;
1235 /* Detect double clicks. Not that we do anything with em, just
1236 * detect them. */
1237 #ifdef DOS
1238 #ifdef CLOCKS_PER_SEC
1239 doubleclick = (lrow == row && lcol == col
1240 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1241 #else
1242 #ifdef CLK_TCK
1243 doubleclick = (lrow == row && lcol == col
1244 && clock() < (lastcalled + CLK_TCK/2));
1245 #else
1246 doubleclick = FALSE;
1247 #endif
1248 #endif
1249 lastcalled = clock();
1250 #else
1251 doubleclick = (lrow == row && lcol == col
1252 && time(0) < (lastcalled + 2));
1253 lastcalled = time(0);
1254 #endif
1255 lheader = FALSE; /* Remember mouse down position. */
1256 levent = mevent;
1257 lrow = row;
1258 lcol = col;
1259 lbutton = button;
1260 lflags = flags;
1262 /* Mouse down in body? */
1263 if (row < (Pmaster ? ComposerTopLine : 2)) {
1264 /* Mouse down in message header -> no tracking, just remember
1265 * where */
1266 lheader = TRUE;
1268 else {
1269 /* Mouse down in message.
1270 * If no shift key and an existing mark -> clear the mark.
1271 * If shift key and no existing mark -> set mark before moving */
1272 if (!(flags & M_KEY_SHIFT) && curwp->w_markp)
1273 setmark (0,1); /* this clears the mark. */
1274 else if (flags & M_KEY_SHIFT && !curwp->w_markp)
1275 setmark (0,1); /* while this sets the mark. */
1277 /* Reposition dot to mouse down. */
1278 move_dot_to (row, col);
1280 /* Set the mark to dot if no existing mark. */
1281 if (curwp->w_markp == NULL)
1282 setmark (0,1);
1284 /* Track mouse movement. */
1285 register_mtrack (mouse_in_pico);
1286 update ();
1287 lheader = FALSE; /* Just to be sure. */
1289 break;
1292 case M_EVENT_TRACK:
1293 /* Mouse tracking. */
1294 if (lheader) /* Ignore mouse movement in header. */
1295 break;
1297 /* If above or below body, scroll body and adjust the row and col. */
1298 if (row < (Pmaster ? ComposerTopLine : 2)) {
1299 /* Scroll text down screen and move dot to top left corner. */
1300 scrollupline (0,1);
1301 trow = (Pmaster) ? ComposerTopLine : 2;
1302 tcol = 0;
1304 else if (row > term.t_nrow - (term.t_mrow + 1)) {
1305 /* Scroll text up screen and move dot to bottom right corner. */
1306 scrolldownline (0,1);
1307 trow = term.t_nrow - (term.t_mrow + 1);
1308 tcol = term.t_ncol;
1310 else {
1311 trow = row;
1312 tcol = col;
1315 /* Move dot to target column. */
1316 move_dot_to (trow, tcol);
1318 /* Update screen. */
1319 update ();
1320 break;
1323 case M_EVENT_UP:
1324 if(button == M_BUTTON_RIGHT){
1325 #ifdef _WINDOWS
1326 pico_popup();
1327 #endif
1328 break;
1330 else if(button != M_BUTTON_LEFT)
1331 break;
1333 if (lheader) {
1334 lheader = FALSE;
1335 /* Last down in header. */
1336 if (row == lrow && col == lcol) {
1337 /* Mouse up and down in same place in header. Means the
1338 * user want to edit the header. Return KEY_MOUSE which
1339 * will cause mousepress to be called, which will
1340 * call HeaderEditor. Can't call HeaderEditor from here
1341 * because that would mess up layering. */
1342 if (curwp->w_marko)
1343 setmark (0,1);
1344 rv = (unsigned long) KEY_MOUSE;
1347 else {
1348 /* If up at same place, clear mark */
1349 if (curwp->w_markp == curwp->w_dotp &&
1350 curwp->w_marko == curwp->w_doto) {
1351 setmark (0,1);
1352 curwp->w_flag |= WFMOVE;
1354 clear_mtrack ();
1355 update ();
1357 break;
1360 return(rv);
1362 #endif
1367 * mouse_in_content - general mechanism used to pass recognized mouse
1368 * events in predefined region back thru the usual
1369 * keyboard input stream. The actual return value
1370 * passed back from this function is set dynamically
1371 * via the "down" argument which is read when both the
1372 * "row" and "col" arguments are negative.
1374 unsigned long
1375 mouse_in_content(unsigned long mevent, int row, int col, int button, int flags)
1377 unsigned long rv = 0;
1378 static unsigned long mouse_val = KEY_MOUSE;
1380 if(row == -1 && col == -1){
1381 mouse_val = mevent; /* setting return value */
1383 else {
1384 /* A real event. */
1385 levent = mevent;
1386 switch (mevent) {
1387 case M_EVENT_DOWN:
1388 /* Mouse down does not mean anything, just keep track of
1389 * where it went down and if this is a double click. */
1390 #ifdef DOS
1391 #ifdef CLOCKS_PER_SEC
1392 doubleclick = (lrow == row && lcol == col
1393 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1394 #else
1395 #ifdef CLK_TCK
1396 doubleclick = (lrow == row && lcol == col
1397 && clock() < (lastcalled + CLK_TCK/2));
1398 #else
1399 doubleclick = FALSE;
1400 #endif
1401 #endif
1402 lastcalled = clock();
1403 #else
1404 doubleclick = (lrow == row && lcol == col
1405 && time(0) < (lastcalled + 2));
1406 lastcalled = time(0);
1407 #endif
1408 lrow = row;
1409 lcol = col;
1410 lbutton = button;
1411 lflags = flags;
1412 break;
1414 case M_EVENT_UP:
1415 /* Mouse up. If in the same position as it went down
1416 * then we return the value set above, which goes into
1417 * the character input stream, which gets processed as
1418 * a mouse event by some upper layer, which calls to
1419 * mouse_get_last(). */
1420 if (lrow == row && lcol == col) {
1421 rv = mouse_val;
1423 break;
1425 case M_EVENT_TRACK:
1426 break;
1430 return(rv);
1435 * mouse_get_last - Get last mouse event.
1438 void
1439 mouse_get_last(mousehandler_t *f, MOUSEPRESS *mp)
1441 if (f != NULL)
1442 *f = lastf;
1443 if (mp != NULL) {
1444 mp->mevent = levent;
1445 mp->row = lrow;
1446 mp->col = lcol;
1447 mp->doubleclick = doubleclick;
1448 mp->button = lbutton;
1449 mp->flags = lflags;
1456 * register_key - register the given keystroke to accept mouse events
1458 void
1459 register_key(int i, unsigned rval, char *label, void (*label_printer)(),
1460 int row, int col, int len, COLOR_PAIR *kn, COLOR_PAIR *kl)
1462 if(i > 11)
1463 return;
1465 menuitems[i].val = rval;
1466 menuitems[i].tl.r = menuitems[i].br.r = row;
1467 menuitems[i].tl.c = col;
1468 menuitems[i].br.c = col + len;
1469 menuitems[i].lbl.r = menuitems[i].tl.r;
1470 menuitems[i].lbl.c = menuitems[i].tl.c;
1471 menuitems[i].label_hiliter = label_printer;
1472 if(menuitems[i].label){
1473 free(menuitems[i].label);
1474 menuitems[i].label = NULL;
1476 if(menuitems[i].kncp)
1477 free_color_pair(&menuitems[i].kncp);
1478 if(menuitems[i].klcp)
1479 free_color_pair(&menuitems[i].klcp);
1480 if(kn)
1481 menuitems[i].kncp = new_color_pair(kn->fg, kn->bg);
1482 else
1483 menuitems[i].kncp = NULL;
1484 if(kl)
1485 menuitems[i].klcp = new_color_pair(kl->fg, kl->bg);
1486 else
1487 menuitems[i].klcp = NULL;
1489 if(label){
1490 size_t len;
1492 len = strlen(label);
1493 if((menuitems[i].label = (char *)malloc((len+1)*sizeof(char))) != NULL){
1494 strncpy(menuitems[i].label, label, len);
1495 menuitems[i].label[len] = '\0';
1502 mouse_on_key(int row, int col)
1504 int i;
1506 for(i = 0; i < 12; i++)
1507 if(M_ACTIVE(row, col, &menuitems[i]))
1508 return(TRUE);
1510 return(FALSE);
1512 #endif /* MOUSE */
1516 * Below are functions for use outside pico to manipulate text
1517 * in a pico's native format (circular linked list of lines).
1519 * The idea is to streamline pico use by making it fairly easy
1520 * for outside programs to prepare text intended for pico's use.
1521 * The simple char * alternative is messy as it requires two copies
1522 * of the same text, and isn't very economic in limited memory
1523 * situations (THANKS BELLEVUE-BILLY.).
1525 typedef struct picotext {
1526 LINE *linep;
1527 LINE *dotp;
1528 int doto;
1529 short crinread;
1530 } PICOTEXT;
1532 #define PT(X) ((PICOTEXT *)(X))
1535 * pico_get - return window struct pointer used as a handle
1536 * to the other pico_xxx routines.
1538 void *
1539 pico_get(void)
1541 PICOTEXT *wp = NULL;
1542 LINE *lp = NULL;
1544 if((wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))) != NULL){
1545 wp->crinread = 0;
1546 if((lp = lalloc(0)) == NULL){
1547 free(wp);
1548 return(NULL);
1551 wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp;
1552 wp->doto = 0;
1554 else
1555 emlwrite("Can't allocate space for text", NULL);
1557 return((void *)wp);
1561 * pico_give - free resources and give up picotext struct
1563 void
1564 pico_give(void *w)
1566 register LINE *lp;
1567 register LINE *fp;
1569 fp = lforw(PT(w)->linep);
1570 while((lp = fp) != PT(w)->linep){
1571 fp = lforw(lp);
1572 free(lp);
1574 free(PT(w)->linep);
1575 free((PICOTEXT *)w);
1579 * pico_readc - return char at current point. Up to calling routines
1580 * to keep cumulative count of chars.
1581 * The characters in PT are UCS-4 characters. The caller
1582 * of pico_readc is expecting UTF-8 chars. We convert
1583 * each UCS-4 character to a string of UTF-8 characters
1584 * and return them one at a time.
1587 pico_readc(void *w, unsigned char *c, int flags)
1589 int rv = 0;
1590 UCS ucs;
1591 static unsigned char obuf[6];
1592 static unsigned char *obufpend = obuf;
1593 static unsigned char *obufpnext = obuf;
1595 if(!(flags & PICOREADC_NOUCS)){
1596 if(obufpend > obuf){
1597 *c = *obufpnext++;
1598 rv++;
1599 if(obufpnext >= obufpend){
1600 obufpend = obuf;
1601 obufpnext = obuf;
1604 return(rv);
1608 if(PT(w)->crinread){
1609 *c = '\012'; /* return LF */
1610 PT(w)->crinread = 0;
1611 rv++;
1613 else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */
1614 if(flags & PICOREADC_NOUCS){
1615 *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1616 rv++;
1618 else{
1619 rv++;
1620 ucs = lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1621 obufpend = utf8_put(obuf, (unsigned long) ucs);
1622 obufpnext = obuf;
1623 if(obufpend > obuf){
1624 *c = *obufpnext++;
1625 if(obufpnext >= obufpend){
1626 obufpend = obuf;
1627 obufpnext = obuf;
1630 else
1631 *c = '?';
1634 else if(PT(w)->dotp != PT(w)->linep){ /* return line break */
1635 PT(w)->dotp = lforw(PT(w)->dotp);
1636 PT(w)->doto = 0;
1637 #if defined(DOS) || defined(OS2)
1638 *c = '\015';
1639 PT(w)->crinread++;
1640 #else
1641 *c = '\012'; /* return local eol! */
1642 #endif
1643 rv++;
1644 } /* else no chars to return */
1646 return(rv);
1651 * pico_writec - write a char into picotext and advance pointers.
1652 * Up to calling routines to keep track of total chars
1653 * written.
1654 * Incoming text (c) is UTF-8 and written chars are UCS-4.
1655 * We need to collect up multiple chars to make a single
1656 * UCS-4 char, so there needs to be some state between calls.
1659 pico_writec(void *w, int c, int flags)
1661 int rv = 0;
1663 if(c == '\r') /* ignore CR's */
1664 rv++; /* so fake it */
1665 else if(c == '\n'){ /* insert newlines on LF */
1667 * OK, if there are characters on the current line or
1668 * dotp is pointing to the delimiter line, insert a newline
1669 * No here's the tricky bit; preserve the implicit EOF newline.
1671 if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){
1672 PT(w)->dotp = PT(w)->linep;
1673 PT(w)->doto = 0;
1675 else{
1676 register LINE *lp;
1678 if((lp = lalloc(0)) == NULL){
1679 emlwrite("Can't allocate space for more characters",NULL);
1680 return(0);
1683 if(PT(w)->dotp == PT(w)->linep){
1684 lforw(lp) = PT(w)->linep;
1685 lback(lp) = lback(PT(w)->linep);
1686 lforw(lback(lp)) = lback(PT(w)->linep) = lp;
1688 else{
1689 lforw(lp) = lforw(PT(w)->dotp);
1690 lback(lp) = PT(w)->dotp;
1691 lback(lforw(lp)) = lforw(PT(w)->dotp) = lp;
1692 PT(w)->dotp = lp;
1693 PT(w)->doto = 0;
1697 rv++;
1699 else{
1700 if(flags & PICOREADC_NOUCS){
1702 * With this flag we're reverting to the old behavior where no
1703 * UTF-8 to UCS-4 translation takes place. We assume nothing
1704 * about the incoming byte stream. We just write a byte at a
1705 * time. So even though it's being stored in PT each
1706 * item is really limited to a single octet value.
1708 rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL);
1710 else{
1711 static unsigned char cbuf[6];
1712 static unsigned char *cbufp = cbuf;
1713 UCS obuf[MAX(MB_LEN_MAX,32)];
1714 int i, outchars = 0;
1716 if(cbufp < cbuf+sizeof(cbuf)){
1717 unsigned char *inputp;
1718 unsigned long remaining_octets;
1719 UCS ucs;
1721 *cbufp++ = (unsigned char) c;
1722 inputp = cbuf;
1723 remaining_octets = (cbufp - cbuf) * sizeof(unsigned char);
1724 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
1726 switch(ucs){
1727 case U8G_ENDSTRG: /* incomplete character, wait */
1728 case U8G_ENDSTRI: /* incomplete character, wait */
1729 break;
1731 default:
1732 if(ucs & U8G_ERROR || ucs == UBOGON){
1734 * None of these cases is supposed to happen. If it
1735 * does happen then the input stream isn't UTF-8
1736 * so something is wrong. Treat each character in the
1737 * input buffer as a separate error character and
1738 * print a '?' for each.
1740 for(inputp = cbuf; inputp < cbufp; inputp++)
1741 obuf[outchars++] = '?';
1743 cbufp = cbuf;
1745 else{
1746 /* got a character */
1747 if(ucs >= 0x80 && wcellwidth(ucs) < 0){
1749 * This happens when we have a UTF-8 character that
1750 * we aren't able to print in our locale. For example,
1751 * if the locale is setup with the terminal
1752 * expecting ISO-8859-1 characters then there are
1753 * lots of UTF-8 characters that can't be printed.
1754 * Print a '?' instead.
1755 This may be the wrong thing to do. What happens if user
1756 is just forwarding and doesn't edit. We are going to lose
1757 the original value, aren't we? Maybe we do this only
1758 when printing to the screen instead.
1760 obuf[outchars++] = '?';
1762 else{
1764 * A regular ucs character
1766 obuf[outchars++] = ucs;
1769 /* update the input buffer */
1770 if(inputp >= cbufp) /* this should be the case */
1771 cbufp = cbuf;
1772 else{ /* extra chars for some reason? */
1773 unsigned char *q, *newcbufp;
1775 newcbufp = (cbufp - inputp) + cbuf;
1776 q = cbuf;
1777 while(inputp < cbufp)
1778 *q++ = *inputp++;
1780 cbufp = newcbufp;
1784 break;
1787 else{ /* error */
1788 obuf[0] = '?';
1789 outchars = 1;
1790 cbufp = cbuf; /* start over */
1794 * Unless we have trouble translating outchars will be
1795 * either 1 or 0. It is the UCS-4 character that we converted
1796 * the input to. It's an array just so it can hold ? when
1797 * there is trouble.
1799 rv = 1;
1800 for(i = 0; rv && i < outchars; i++)
1801 if(!geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, obuf[i], 0, 1, NULL))
1802 rv = 0;
1807 * Warning, this is no longer number written, because when we have a
1808 * multibyte UTF-8 character we won't write anything until we get all
1809 * the bytes.
1811 return((rv) ? 1 : 0); /* return number written */
1816 * pico_puts - just write the given string into the text
1819 pico_puts(void *w, char *s, int flags)
1821 int rv = 0;
1823 if(*s != '\0'){
1824 rv = 1;
1825 while(rv && *s != '\0')
1826 if(!pico_writec(w, (int)*s++, flags))
1827 rv = 0;
1830 return((rv) ? 1 : 0);
1835 * pico_seek - position dotp and dot at requested location
1838 pico_seek(void *w, long offset, int orig)
1840 register LINE *lp;
1842 PT(w)->crinread = 0;
1843 switch(orig){
1844 case 0 : /* SEEK_SET */
1845 PT(w)->dotp = lforw(PT(w)->linep);
1846 PT(w)->doto = 0;
1847 case 1 : /* SEEK_CUR */
1848 lp = PT(w)->dotp;
1849 while(lp != PT(w)->linep){
1850 if(offset <= llength(lp)){
1851 PT(w)->doto = (int)offset;
1852 PT(w)->dotp = lp;
1853 break;
1856 offset -= ((long)llength(lp)
1857 #if defined(DOS) || defined(OS2)
1858 + 2L);
1859 #else
1860 + 1L);
1861 #endif
1862 lp = lforw(lp);
1864 break;
1866 case 2 : /* SEEK_END */
1867 PT(w)->dotp = lback(PT(w)->linep);
1868 PT(w)->doto = llength(PT(w)->dotp);
1869 break;
1870 default :
1871 return(-1);
1874 return(0);
1879 * breplace - replace the current window's text with the given
1880 * LINEs
1882 void
1883 breplace(void *w)
1885 register LINE *lp;
1886 register LINE *fp;
1888 fp = lforw(curbp->b_linep);
1889 while((lp = fp) != curbp->b_linep){ /* blast old lines */
1890 fp = lforw(lp);
1891 free(lp);
1893 free(curbp->b_linep);
1895 curbp->b_linep = PT(w)->linep; /* arrange pointers */
1898 * Undo space-stuffing that was done when we were preparing to send.
1899 * Some error happened so we're back to the composer.
1901 if(Pmaster && Pmaster->space_stuffed){
1902 Pmaster->space_stuffed = 0;
1903 for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
1904 if(llength(lp) && ucs4_isspace(lgetc(lp, 0).c)){
1905 curwp->w_dotp = lp;
1906 curwp->w_doto = 0;
1907 forwdel(FALSE,1);
1912 curwp->w_linep = lforw(curbp->b_linep);
1913 curwp->w_dotp = lforw(curbp->b_linep);
1914 curwp->w_doto = 0;
1915 curwp->w_markp = curwp->w_imarkp = NULL;
1916 curwp->w_marko = curwp->w_imarko = 0;
1918 curbp->b_dotp = curwp->w_dotp;
1919 curbp->b_doto = curbp->b_marko = 0;
1920 curbp->b_markp = NULL;
1921 curbp->b_linecnt = -1;
1923 curwp->w_flag |= WFHARD;
1926 void
1927 free_pico_module_globals(void)
1929 int i;
1931 #ifdef MOUSE
1932 for(i = 0; i < 12; i++){
1933 if(menuitems[i].kncp)
1934 free_color_pair(&menuitems[i].kncp);
1935 if(menuitems[i].klcp)
1936 free_color_pair(&menuitems[i].klcp);
1937 if(menuitems[i].label){
1938 free((void *) menuitems[i].label);
1939 menuitems[i].label = NULL;
1942 #endif /* MOUSE */
1947 #ifdef _WINDOWS
1952 composer_file_drop(int x, int y, char *filename)
1954 int attached = 0;
1955 EML eml;
1957 if((ComposerTopLine > 2 && x <= ComposerTopLine)
1958 || !LikelyASCII(filename)){
1959 AppendAttachment(filename, NULL, NULL);
1960 attached++;
1962 else{
1963 setimark(FALSE, 1);
1964 ifile(filename);
1965 swapimark(FALSE, 1);
1968 if(ComposerEditing){ /* update display */
1969 PaintBody(0);
1971 else{
1972 pico_refresh(0, 1);
1973 update();
1976 eml.s = filename;
1977 if(attached)
1978 emlwrite(_("Attached dropped file \"%s\""), &eml);
1979 else
1980 emlwrite(_("Inserted dropped file \"%s\""), &eml);
1982 if(ComposerEditing){ /* restore cursor */
1983 HeaderPaintCursor();
1985 else{
1986 curwp->w_flag |= WFHARD;
1987 update();
1990 return(1);
1995 pico_cursor(int col, long row)
1997 return((row > 1 && row <= term.t_nrow - (term.t_mrow + 1))
1998 ? MSWIN_CURSOR_IBEAM
1999 : MSWIN_CURSOR_ARROW);
2001 #endif /* _WINDOWS */