1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: pico.c 921 2008-01-31 02:09:25Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * Program: Main Pine Composer routines
25 * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call
26 * weemacs() and plugged into the Pine mailer. Lots of unused
27 * MicroEmacs code laying around.
29 * 17 Jan 90 - weemacs() became weemacs() the composer. Header editing
32 * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer.
38 * This program is in public domain; written by Dave G. Conroy.
39 * This file contains the main driving routine, and some keyboard processing
40 * code, for the MicroEMACS screen editor.
44 * 1.0 Steve Wilhite, 30-Nov-85
45 * - Removed the old LK201 and VT100 logic. Added code to support the
46 * DEC Rainbow keyboard (which is a LK201 layout) using the the Level
47 * 1 Console In ROM INT. See "rainbow.h" for the function key defs
48 * Steve Wilhite, 1-Dec-85
49 * - massive cleanup on code in display.c and search.c
51 * 2.0 George Jones, 12-Dec-85
54 * 3.0 Daniel Lawrence, 29-Dec-85
56 * - updated documentation and froze development for 3.6 net release
59 /* make global definitions not external */
62 #include "../c-client/mail.h"
63 #include "../c-client/utf8.h"
66 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
71 #include "ebind.h" /* default key bindings */
72 #include "../pith/charconv/filesys.h"
75 void remove_directions_mark(void);
77 void breplace(void *w
);
78 int any_header_changes(void);
79 int cleanwhitespace(void);
80 int isquotedspace(LINE
*);
84 * function key mappings
86 static UCS pfkm
[12][2] = {
107 remove_directions_mark(void)
113 for(lp
= lforw(curbp
->b_linep
); lp
!= curbp
->b_linep
; lp
= lforw(lp
)){
114 for(i
= 0; i
< llength(lp
);){
116 if(c
== 0x200E || c
== 0x200F){
123 direction
= c
== 0x200E ? 0 : 1;
126 lgetc(lp
,i
++).d
= direction
;
129 curwp
->w_linep
= lforw(curbp
->b_linep
);
130 curwp
->w_dotp
= lforw(curbp
->b_linep
);
136 * flag for the various functions in pico() to set when ready
137 * for pico() to return...
139 int pico_all_done
= 0;
141 UCS
*pico_anchor
= NULL
;
142 extern struct headerentry
*headents
;
145 * pico - the main routine for Pine's composer.
154 char bname
[NBUFN
]; /* buffer name of file to read */
155 extern struct on_display ods
;
156 int checkpointcnt
= 0, input
= 0;
158 char chkptfile
[NLINE
];
165 gmode
|= pm
->pine_flags
; /* high 4 bits rsv'd for pine */
167 alt_speller
= pm
->alt_spell
;
169 dictionary
= pm
->dict
;
170 chosen_dict
= pm
->chosen_dict
;
171 #endif /* _WINDOWS */
175 if(!vtinit()) /* Init Displays. */
178 strncpy(bname
, "main", sizeof(bname
)); /* default buffer name */
179 bname
[sizeof(bname
)-1] = '\0';
180 edinit(bname
); /* Buffers, windows. */
182 if(InitMailHeader(pm
)) /* init mail header structure */
183 gmode
&= ~(P_BODY
| P_HEADEND
); /* flip off special header stuff */
185 /* setup to process commands */
186 lastflag
= 0; /* Fake last flags. */
187 curbp
->b_mode
|= gmode
; /* and set default modes*/
189 if(Pmaster
->pine_anchor
)
190 pico_anchor
= utf8_to_ucs4_cpystr(Pmaster
->pine_anchor
);
194 if(Pmaster
->quote_str
)
195 glo_quote_str
= utf8_to_ucs4_cpystr(Pmaster
->quote_str
);
197 glo_quote_str
= NULL
;
199 if(Pmaster
->wordseps
)
200 glo_wordseps
= ucs4_cpystr(Pmaster
->wordseps
);
204 bindtokey(DEL
, (gmode
& P_DELRUBS
) ? forwdel
: backdel
);
207 breplace(pm
->msgtext
);
209 remove_directions_mark();
212 cursor_shown
= mswin_showcaret(1); /* turn on for main window */
213 mswin_allowpaste(MSWIN_PASTE_FULL
);
214 mswin_setscrollcallback (pico_scroll_callback
);
217 /* prepare for checkpointing */
219 chkptinit((*Pmaster
->ckptdir
)(chkptfile
, sizeof(chkptfile
)), sizeof(chkptfile
));
220 if(gmode
& P_CHKPTNOW
)
221 writeout(chkptfile
, TRUE
);
223 pico_all_done
= setjmp(finstate
); /* jump out of HUP handler ? */
225 if(gmode
& MDALTNOW
){
226 while(!pico_all_done
){
227 if(((gmode
& P_BODY
) || !Pmaster
->headents
)
228 && alt_editor(0, 1) < 0)
229 break; /* if problem, drop into pico */
231 if(Pmaster
->headents
){
232 update(); /* paint screen, n' start editing... */
233 HeaderEditor((gmode
& (P_HEADEND
| P_BODY
)) ? 2 : 0, 0);
234 gmode
|= P_BODY
; /* make sure we enter alt ed next */
237 pico_all_done
= COMP_EXIT
;
240 else if(!pico_all_done
){
241 if(gmode
& P_BODY
){ /* begin editing the header? */
242 ArrangeHeader(); /* line up pointers */
244 * Move to the offset pine asked us to move to.
245 * Perhaps we should be checking to see if this is
246 * a reasonable number before moving.
248 if(Pmaster
&& Pmaster
->edit_offset
)
249 forwchar(FALSE
, Pmaster
->edit_offset
);
252 update(); /* paint screen, */
253 HeaderEditor((gmode
& P_HEADEND
) ? 2 : 0, 0);
263 mswin_allowpaste(MSWIN_PASTE_DISABLE
);
264 mswin_setscrollcallback (NULL
);
266 ret
= anycb() ? BUF_CHANGED
: 0;
267 switch(pico_all_done
){ /* prepare for/handle final events */
268 case COMP_EXIT
: /* already confirmed */
271 && (Pmaster
->strip_ws_before_send
272 || Pmaster
->allow_flowed_text
))
277 case COMP_CANCEL
: /* also already confirmed */
284 * pack up and let caller know that we've received a SIGHUP
286 if(ComposerEditing
) /* expand addr if needed */
287 call_builder(&headents
[ods
.cur_e
], NULL
, NULL
);
294 default: /* safest if internal error */
296 * If we're in the headers mark the current header line
297 * with start_here bit so caller knows where to reset.
298 * Also set the edit_offset, which is either the offset
299 * into this header line or the offset into the body.
300 * Packheader will adjust edit_offset for multi-line
303 if(ComposerEditing
){ /* in the headers */
304 headents
[ods
.cur_e
].start_here
= 1;
305 Pmaster
->edit_offset
= ods
.p_ind
;
309 register long offset
;
311 for(clp
= lforw(curbp
->b_linep
), offset
= 0L;
312 clp
!= curwp
->w_dotp
;
314 offset
+= (llength(clp
) + 1);
316 Pmaster
->edit_offset
= offset
+ curwp
->w_doto
;
325 fs_give((void **) &pico_anchor
);
327 fs_give((void **) &glo_quote_str
);
329 fs_give((void **) &glo_wordseps
);
331 vttidy(); /* clean up tty modes */
332 zotdisplay(); /* blast display buffers */
334 our_unlink(chkptfile
);
335 Pmaster
= NULL
; /* blat global */
342 if(km_popped
== 0) /* cause bottom three lines to be repainted */
343 curwp
->w_flag
|= WFHARD
;
346 if(km_popped
){ /* temporarily change to cause menu to be painted */
348 curwp
->w_ntrows
-= 2;
349 curwp
->w_flag
|= WFMODE
;
350 movecursor(term
.t_nrow
-2, 0); /* clear status line, too */
354 update(); /* Fix up the screen */
357 curwp
->w_ntrows
+= 2;
362 /* New mouse function for real mouse text seletion. */
363 register_mfunc(mouse_in_pico
, 2, 0, term
.t_nrow
- (term
.t_mrow
+1),
366 mouse_in_content(KEY_MOUSE
, -1, -1, -1, 0);
367 register_mfunc(mouse_in_content
, 2, 0, term
.t_nrow
- (term
.t_mrow
+ 1),
372 mswin_setdndcallback (composer_file_drop
);
373 mswin_mousetrackcallback(pico_cursor
);
376 if (term
.t_nrow
< 6 && c
!= NODATA
){
378 emlwrite(_("Please make the screen bigger."), NULL
);
384 clear_mfunc(mouse_in_pico
);
386 clear_mfunc(mouse_in_content
);
390 mswin_cleardndcallback ();
391 mswin_mousetrackcallback(NULL
);
393 if(c
== NODATA
|| time_to_check()){ /* new mail ? */
394 if((*Pmaster
->newmail
)(c
== NODATA
? 0 : 2, 1) >= 0){
399 curwp
->w_ntrows
-= 2;
400 curwp
->w_flag
|= WFHARD
;
406 rv
= (*Pmaster
->showmsg
)(c
);
408 picosigs(); /* restore altered handlers */
409 if(rv
) /* Did showmsg corrupt the display? */
410 PaintBody(0); /* Yes, repaint */
432 if(c
== NODATA
) /* no op, getkey timed out */
435 (*Pmaster
->keybinput
)();
437 if (mpresf
!= FALSE
) { /* message stay around only */
438 if (mpresf
++ > NMMESSDELAY
) /* so long! */
442 f
= FALSE
; /* vestigial */
445 execute(normalize_cmd(c
, pfkm
, 2), f
, n
);
446 if(++checkpointcnt
>= CHKPTDELAY
){
448 writeout(chkptfile
, TRUE
);
454 * Initialize all of the buffers and windows. The buffer name is passed down
455 * as an argument, because the main routine may have been told to read in a
456 * file by default, and we want the buffer name to be right.
460 * For the pine composer, we don't want to take over the whole screen
461 * for editing. the first some odd lines are to be used for message
462 * header information editing.
473 bp
= bfind(bname
, TRUE
, BFWRAPOPEN
); /* First buffer */
474 wp
= (WINDOW
*) malloc(sizeof(WINDOW
)); /* First window */
476 if (bp
==NULL
|| wp
==NULL
){
483 curbp
= bp
; /* Make this current */
486 wp
->w_wndp
= NULL
; /* Initialize window */
488 bp
->b_nwnd
= 1; /* Displayed. */
489 wp
->w_linep
= bp
->b_linep
;
490 wp
->w_dotp
= bp
->b_linep
;
492 wp
->w_markp
= wp
->w_imarkp
= NULL
;
493 wp
->w_marko
= wp
->w_imarko
= 0;
497 term
.t_mrow
= Pmaster
->menu_rows
;
498 wp
->w_toprow
= ComposerTopLine
= COMPOSER_TOP_LINE
;
499 wp
->w_ntrows
= term
.t_nrow
- COMPOSER_TOP_LINE
- term
.t_mrow
;
500 fillcol
= Pmaster
->fillcolumn
;
502 (Pmaster
->oper_dir
&& strlen(Pmaster
->oper_dir
) < NLINE
)
503 ? Pmaster
->oper_dir
: "", sizeof(opertree
));
504 opertree
[sizeof(opertree
)-1] = '\0';
505 input_cs
= Pmaster
->input_cs
;
514 wp
->w_ntrows
= term
.t_nrow
- 2 - term
.t_mrow
;
515 if(userfillcol
> 0) /* set fill column */
516 fillcol
= userfillcol
;
518 fillcol
= term
.t_ncol
- 6;
522 * MDSCUR mode implies MDTREE mode with a opertree of home directory,
523 * unless opertree has been set differently.
525 if((gmode
& MDSCUR
) && !opertree
[0]){
526 strncpy(opertree
, gethomedir(NULL
), sizeof(opertree
));
527 opertree
[sizeof(opertree
)-1] = '\0';
531 fixpath(opertree
, sizeof(opertree
));
534 wp
->w_flag
= WFMODE
|WFHARD
; /* Full. */
539 * This is the general command execution routine. It handles the fake binding
540 * of all the keys to "self-insert". It also clears out the "thisflag" word,
541 * and arranges to move it to the "lastflag", so that the next command can
542 * look at it. Return the status of command.
545 execute(UCS c
, int f
, int n
)
550 ktp
= (Pmaster
) ? &keytab
[0] : &pkeytab
[0];
552 while (ktp
->k_fp
!= NULL
) {
553 if (ktp
->k_code
== c
) {
556 curwp
->w_flag
|= WFMODE
;
562 status
= (*ktp
->k_fp
)(f
, n
);
563 if((lastflag
& CFFILL
) && !(thisflag
& CFFILL
))
565 if((lastflag
& CFFLBF
) && !(thisflag
& CFFLBF
))
571 * Reset flag saying wrap should open a new line whenever
572 * we execute a command (as opposed to just typing in text).
573 * However, if that command leaves us in the same line on the
574 * screen, then don't reset.
576 if(curwp
->w_flag
& (WFMOVE
| WFHARD
))
577 curbp
->b_flag
|= BFWRAPOPEN
; /* wrap should open new line */
584 if(lastflag
& CFFILL
) /* blat unusable fill data */
586 if(lastflag
& CFFLBF
)
589 if (VALID_KEY(c
)) { /* Self inserting. */
591 if (n
<= 0) { /* Fenceposts. */
593 return (n
<0 ? FALSE
: TRUE
);
595 thisflag
= 0; /* For the future. */
597 /* do the appropriate insertion */
598 /* pico never does C mode, this is simple */
599 status
= linsert(n
, c
);
602 * Check to make sure we didn't go off of the screen
603 * with that character. Take into account tab expansion.
604 * If so wrap the line...
606 if(curwp
->w_bufp
->b_mode
& MDWRAP
){
610 for(j
= 0; j
< llength(curwp
->w_dotp
); j
++)
611 if(ucs4_isspace(lgetc(curwp
->w_dotp
, j
).c
)){
612 if(lgetc(curwp
->w_dotp
, j
).c
== TAB
){
621 ww
= wcellwidth((UCS
) lgetc(curwp
->w_dotp
, j
).c
);
622 wid
+= (ww
>= 0 ? ww
: 1);
636 lastflag
= 0; /* Fake last flags. */
643 * Fancy quit command, as implemented by Norm. If the any buffer has
644 * changed do a write on that buffer and exit emacs, otherwise simply exit.
647 quickexit(int f
, int n
)
649 register BUFFER
*bp
; /* scanning pointer to buffers */
653 if ((bp
->b_flag
&BFCHG
) != 0 /* Changed. */
654 && (bp
->b_flag
&BFTEMP
) == 0) { /* Real. */
655 curbp
= bp
; /* make that buffer cur */
658 bp
= bp
->b_bufp
; /* on to the next buffer */
660 return(wquit(f
, n
)); /* conditionally quit */
666 * abort_composer - ask the question here, then go quit or
670 abort_composer(int f
, int n
)
676 Pmaster
->arm_winch_cleanup
++;
677 if(Pmaster
->canceltest
){
678 if(((Pmaster
->pine_flags
& MDHDRONLY
) && !any_header_changes())
679 || (result
= (*Pmaster
->canceltest
)(redraw_pico_for_callback
))){
680 pico_all_done
= COMP_CANCEL
;
681 emlwrite(result
, NULL
);
682 Pmaster
->arm_winch_cleanup
--;
686 /* TRANSLATORS: The user typed the Cancel command and was
687 asked to confirm that. Instead they canceled the cancel
689 emlwrite(_("Cancel Cancelled"), NULL
);
690 curwp
->w_flag
|= WFMODE
; /* and modeline so we */
691 sgarbk
= TRUE
; /* redraw the keymenu */
692 pclear(term
.t_nrow
-1, term
.t_nrow
);
693 Pmaster
->arm_winch_cleanup
--;
697 else switch(mlyesno_utf8(Pmaster
->headents
698 ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
700 ? _("Cancel Edit (and abandon changes)")
704 pico_all_done
= COMP_CANCEL
;
708 emlwwrite(_("Cancel Cancelled"), NULL
);
719 * suspend_composer - return to pine with what's been edited so far
722 suspend_composer(int f
, int n
)
724 if(Pmaster
&& Pmaster
->headents
)
725 pico_all_done
= COMP_SUSPEND
;
735 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
736 * has been changed and not written out. Normally bound to "C-X C-C".
747 /* First, make sure there are no outstanding problems */
749 emlwwrite(_("Problem with attachments! Fix errors or delete attachments."), NULL
);
754 if(Pmaster
->always_spell_check
)
755 if(spell(0, 0) == -1)
756 sleep(3); /* problem, show error */
759 * if we're not in header, show some of it as we verify sending...
763 Pmaster
->arm_winch_cleanup
++;
764 if((!(Pmaster
->pine_flags
& MDHDRONLY
) || any_header_changes())
765 && (ret
= (*Pmaster
->exittest
)(Pmaster
->headents
,
766 redraw_pico_for_callback
,
767 Pmaster
->allow_flowed_text
,
769 Pmaster
->arm_winch_cleanup
--;
772 pico_all_done
= COMP_CANCEL
;
778 lchange(WFHARD
); /* set update flags... */
779 curwp
->w_flag
|= WFMODE
; /* and modeline so we */
780 sgarbk
= TRUE
; /* redraw the keymenu */
781 pclear(term
.t_nrow
-2, term
.t_nrow
);
784 if(result
&& *result
)
785 emlwrite(result
, NULL
);
788 Pmaster
->arm_winch_cleanup
--;
789 pico_all_done
= COMP_EXIT
;
794 if (f
!= FALSE
/* Argument forces it. */
795 || anycb() == FALSE
/* All buffers clean. */
796 /* User says it's OK. */
797 /* TRANSLATORS: buffer is the in-memory copy of a file */
798 || (s
=mlyesno_utf8(_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"), -1)) == FALSE
) {
800 #if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
807 if(filewrite(0,1) == TRUE
){
809 if(dictionary
!= NULL
){
811 for(i
= 0; dictionary
[i
] != NULL
; i
++)
812 fs_give((void **)&dictionary
[i
]);
813 fs_give((void **)dictionary
);
815 #endif /* _WINDOWS */
820 emlwrite(_("Exit cancelled"), NULL
);
822 curwp
->w_flag
|= WFHARD
; /* cause bottom 3 lines to paint */
832 * Has any editing been done to headers?
835 any_header_changes(void)
837 struct headerentry
*he
;
839 for(he
= Pmaster
->headents
; he
->name
!= NULL
; he
++)
843 return(he
->name
&& he
->dirty
);
847 isquotedspace(LINE
*line
)
849 int i
, was_quote
= 0;
850 for(i
= 0; i
< llength(line
); i
++){
851 if(lgetc(line
, i
).c
== '>')
853 else if(was_quote
&& lgetc(line
, i
).c
== ' '){
854 if(i
+1 < llength(line
) && ucs4_isspace(lgetc(line
,i
+1).c
))
866 * This function serves two purposes, 1) to strip white space when
867 * Pmaster asks that the composition have its trailing white space
868 * stripped, or 2) to prepare the text as flowed text, as Pmaster
869 * is telling us that we're working with flowed text.
871 * What flowed currently means to us is stripping all trailing white
872 * space, except for one space if the following line is a continuation
873 * of the paragraph. Also, we space-stuff all lines beginning
874 * with white-space, and leave siglines alone.
877 cleanwhitespace(void)
879 LINE
*cursor_dotp
= NULL
, **lp
= NULL
;
880 int i
= 0, cursor_doto
= 0, is_cursor_line
= 0;
881 int do_space_stuffing
= 0;
883 if(Pmaster
&& Pmaster
->allow_flowed_text
&& !(*Pmaster
->user_says_noflow
)())
886 cursor_dotp
= curwp
->w_dotp
;
887 cursor_doto
= curwp
->w_doto
;
890 for(lp
= &curwp
->w_dotp
; (*lp
) != curbp
->b_linep
; (*lp
) = lforw(*lp
)){
891 if(!(llength(*lp
) == 3
892 && lgetc(*lp
, 0).c
== '-'
893 && lgetc(*lp
, 1).c
== '-'
894 && lgetc(*lp
, 2).c
== ' ')
896 is_cursor_line
= (cursor_dotp
== (*lp
));
897 /* trim trailing whitespace, to be added back if flowing */
898 for(i
= llength(*lp
); i
; i
--)
899 if(!ucs4_isspace(lgetc(*lp
, i
- 1).c
))
901 if(i
!= llength(*lp
)){
904 if(Pmaster
&& !Pmaster
->strip_ws_before_send
905 && lforw(*lp
) != curbp
->b_linep
906 && llength(lforw(*lp
))
907 && !(ucs4_isspace(lgetc(lforw(*lp
), 0).c
)
908 || isquotedspace(lforw(*lp
)))
909 && !(llength(lforw(*lp
)) == 3
910 && lgetc(lforw(*lp
), 0).c
== '-'
911 && lgetc(lforw(*lp
), 1).c
== '-'
912 && lgetc(lforw(*lp
), 2).c
== ' '))
914 if(flow_line
&& i
&& lgetc(*lp
, i
).c
== ' '){
915 /* flowed line ending with space */
917 if(i
!= llength(*lp
)){
919 ldelete(llength(*lp
) - i
, NULL
);
922 else if(flow_line
&& i
&& ucs4_isspace(lgetc(*lp
, i
).c
)){
923 /* flowed line ending with whitespace other than space*/
925 ldelete(llength(*lp
) - i
, NULL
);
930 ldelete(llength(*lp
) - i
, NULL
);
933 if(do_space_stuffing
&& llength(*lp
) && ucs4_isspace(lgetc(*lp
, 0).c
)){
934 /* space-stuff only if flowed */
936 Pmaster
->space_stuffed
= 1;
938 if(is_cursor_line
&& cursor_doto
)
947 /* put the cursor back where we found it */
949 curwp
->w_dotp
= cursor_dotp
;
950 curwp
->w_doto
= (cursor_doto
< llength(curwp
->w_dotp
))
951 ? cursor_doto
: llength(curwp
->w_dotp
) - 1;
957 * Remove all trailing white space from the text
960 stripwhitespace(void)
963 LINE
*cur_line
= lforw(curbp
->b_linep
);
966 /* we gotta test for the sigdash case here */
967 if(!(cur_line
->l_used
== 3 &&
968 lgetc(cur_line
, 0).c
== '-' &&
969 lgetc(cur_line
, 1).c
== '-' &&
970 lgetc(cur_line
, 2).c
== ' ')){
971 for(i
= cur_line
->l_used
- 1; i
>= 0; i
--)
972 if(ucs4_isspace(lgetc(cur_line
, i
).c
))
977 }while((cur_line
= lforw(cur_line
)) != curbp
->b_linep
);
983 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
984 * Sometimes called as a routine, to do general aborting of stuff.
989 emlwrite(_("Cancelled"), NULL
);
994 /* tell the user that this command is illegal while we are in
995 * VIEW (read-only) mode
1001 emlwrite("Key illegal in VIEW mode", NULL
);
1008 * reset all globals to their initial values
1018 * re-initialize global buffer type variables ....
1020 fillcol
= (term
.t_ncol
> 80) ? 77 : term
.t_ncol
- 6;
1024 ComposerEditing
= FALSE
;
1027 * re-initialize hardware display variables ....
1029 vtrow
= vtcol
= lbound
= 0;
1032 pat
[0] = rpat
[0] = '\0';
1033 browse_dir
[0] = '\0';
1038 * pico_help - help function for standalone composer
1039 * Oops - looks like utf8title is unused!
1041 * This should be fixed to handle TAB characters.
1044 pico_help(char *utf8text
[], char *utf8title
, int i
)
1046 register int numline
= 0;
1053 return(wscrollw(COMPOSER_TOP_LINE
, term
.t_nrow
-1, utf8text
, numline
));
1059 * zotedit() - kills the buffer and frees all lines associated with it!!!
1064 wheadp
->w_linep
= wheadp
->w_dotp
= wheadp
->w_markp
= wheadp
->w_imarkp
= NULL
;
1065 bheadp
->b_linep
= bheadp
->b_dotp
= bheadp
->b_markp
= NULL
;
1067 free((char *) wheadp
); /* clean up window */
1071 free((char *) bheadp
); /* clean up buffers */
1075 zotheader(); /* blast header lines */
1077 kdelete(); /* blast kill buffer */
1084 * Generic mouse handling functions
1086 MENUITEM menuitems
[12]; /* key labels and functions */
1087 MENUITEM
*mfunc
= NULL
; /* list of regional functions */
1088 mousehandler_t mtrack
; /* mouse tracking handler */
1090 /* last mouse position */
1091 static unsigned long levent
= 0L;
1092 static int lrow
= 0, lcol
= 0, doubleclick
, lbutton
, lflags
;
1094 static clock_t lastcalled
= 0;
1096 static time_t lastcalled
= 0;
1098 static mousehandler_t lastf
;
1102 * register_mfunc - register the given function to get called
1103 * on mouse events in the given display region
1106 register_mfunc(mousehandler_t f
, int tlr
, int tlc
, int brr
, int brc
)
1113 for(mp
= &mfunc
; *mp
; mp
= &(*mp
)->next
)
1116 *mp
= (MENUITEM
*)malloc(sizeof(MENUITEM
));
1117 memset(*mp
, 0, sizeof(MENUITEM
));
1124 (*mp
)->lbl
.c
= (*mp
)->lbl
.r
= 0;
1131 * clear_mfunc - clear any previously set mouse function
1134 clear_mfunc(mousehandler_t f
)
1138 if((mp
= mfunc
) != NULL
){
1142 for(tp
= mp
; tp
->next
; tp
= tp
->next
)
1143 if(tp
->next
->action
== f
){
1145 tp
->next
= tp
->next
->next
;
1164 mswin_allowmousetrack (FALSE
);
1169 register_mtrack(mousehandler_t f
)
1173 mswin_allowmousetrack (TRUE
);
1181 move_dot_to(int row
, int col
)
1186 lp
= curwp
->w_linep
;
1187 i
= row
- ((Pmaster
) ? ComposerTopLine
: 2);
1188 while(i
-- && lp
!= curbp
->b_linep
) /* count from top */
1191 curwp
->w_dotp
= lp
; /* to new dot. */
1192 curwp
->w_doto
= getgoal(lp
);
1193 curwp
->w_flag
|= WFMOVE
;
1200 * When the mouse goes down in the body we set the mark and start
1203 * As the mouse moves we update the dot and redraw the screen.
1205 * If the mouse moves above or below the pico body region of the
1206 * screen we scroll the text and update the dot position.
1208 * When the mouse comes up we clean up. If the mouse did not
1209 * move, then we clear the mark and turn off the selection.
1211 * Most of the mouse processing is handled here. The exception is
1212 * mouse down in the header. Can't call HeaderEditor() from here so
1213 * we send up the KEY_MOUSE character, which gets dispatched to
1214 * mousepress(), which _can_ call HeaderEditor().
1217 mouse_in_pico(unsigned long mevent
, int row
, int col
, int button
, int flags
)
1219 unsigned long rv
= 0; /* Our return value. */
1220 int trow
, tcol
; /* translated row and col. */
1222 static int lheader
= FALSE
; /* Mouse down was in header. */
1230 if(button
!= M_BUTTON_LEFT
)
1233 /* Ignore mouse down if not in pico body region. */
1234 if (row
< 2 || row
> term
.t_nrow
- (term
.t_mrow
+1)) {
1239 /* Detect double clicks. Not that we do anything with em, just
1242 #ifdef CLOCKS_PER_SEC
1243 doubleclick
= (lrow
== row
&& lcol
== col
1244 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1247 doubleclick
= (lrow
== row
&& lcol
== col
1248 && clock() < (lastcalled
+ CLK_TCK
/2));
1250 doubleclick
= FALSE
;
1253 lastcalled
= clock();
1255 doubleclick
= (lrow
== row
&& lcol
== col
1256 && time(0) < (lastcalled
+ 2));
1257 lastcalled
= time(0);
1259 lheader
= FALSE
; /* Remember mouse down position. */
1266 /* Mouse down in body? */
1267 if (row
< (Pmaster
? ComposerTopLine
: 2)) {
1268 /* Mouse down in message header -> no tracking, just remember
1273 /* Mouse down in message.
1274 * If no shift key and an existing mark -> clear the mark.
1275 * If shift key and no existing mark -> set mark before moving */
1276 if (!(flags
& M_KEY_SHIFT
) && curwp
->w_markp
)
1277 setmark (0,1); /* this clears the mark. */
1278 else if (flags
& M_KEY_SHIFT
&& !curwp
->w_markp
)
1279 setmark (0,1); /* while this sets the mark. */
1281 /* Reposition dot to mouse down. */
1282 move_dot_to (row
, col
);
1284 /* Set the mark to dot if no existing mark. */
1285 if (curwp
->w_markp
== NULL
)
1288 /* Track mouse movement. */
1289 register_mtrack (mouse_in_pico
);
1291 lheader
= FALSE
; /* Just to be sure. */
1297 /* Mouse tracking. */
1298 if (lheader
) /* Ignore mouse movement in header. */
1301 /* If above or below body, scroll body and adjust the row and col. */
1302 if (row
< (Pmaster
? ComposerTopLine
: 2)) {
1303 /* Scroll text down screen and move dot to top left corner. */
1305 trow
= (Pmaster
) ? ComposerTopLine
: 2;
1308 else if (row
> term
.t_nrow
- (term
.t_mrow
+ 1)) {
1309 /* Scroll text up screen and move dot to bottom right corner. */
1310 scrolldownline (0,1);
1311 trow
= term
.t_nrow
- (term
.t_mrow
+ 1);
1319 /* Move dot to target column. */
1320 move_dot_to (trow
, tcol
);
1322 /* Update screen. */
1328 if(button
== M_BUTTON_RIGHT
){
1334 else if(button
!= M_BUTTON_LEFT
)
1339 /* Last down in header. */
1340 if (row
== lrow
&& col
== lcol
) {
1341 /* Mouse up and down in same place in header. Means the
1342 * user want to edit the header. Return KEY_MOUSE which
1343 * will cause mousepress to be called, which will
1344 * call HeaderEditor. Can't call HeaderEditor from here
1345 * because that would mess up layering. */
1348 rv
= (unsigned long) KEY_MOUSE
;
1352 /* If up at same place, clear mark */
1353 if (curwp
->w_markp
== curwp
->w_dotp
&&
1354 curwp
->w_marko
== curwp
->w_doto
) {
1356 curwp
->w_flag
|= WFMOVE
;
1371 * mouse_in_content - general mechanism used to pass recognized mouse
1372 * events in predefined region back thru the usual
1373 * keyboard input stream. The actual return value
1374 * passed back from this function is set dynamically
1375 * via the "down" argument which is read when both the
1376 * "row" and "col" arguments are negative.
1379 mouse_in_content(unsigned long mevent
, int row
, int col
, int button
, int flags
)
1381 unsigned long rv
= 0;
1382 static unsigned long mouse_val
= KEY_MOUSE
;
1384 if(row
== -1 && col
== -1){
1385 mouse_val
= mevent
; /* setting return value */
1392 /* Mouse down does not mean anything, just keep track of
1393 * where it went down and if this is a double click. */
1395 #ifdef CLOCKS_PER_SEC
1396 doubleclick
= (lrow
== row
&& lcol
== col
1397 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1400 doubleclick
= (lrow
== row
&& lcol
== col
1401 && clock() < (lastcalled
+ CLK_TCK
/2));
1403 doubleclick
= FALSE
;
1406 lastcalled
= clock();
1408 doubleclick
= (lrow
== row
&& lcol
== col
1409 && time(0) < (lastcalled
+ 2));
1410 lastcalled
= time(0);
1419 /* Mouse up. If in the same position as it went down
1420 * then we return the value set above, which goes into
1421 * the character input stream, which gets processed as
1422 * a mouse event by some upper layer, which calls to
1423 * mouse_get_last(). */
1424 if (lrow
== row
&& lcol
== col
) {
1439 * mouse_get_last - Get last mouse event.
1443 mouse_get_last(mousehandler_t
*f
, MOUSEPRESS
*mp
)
1448 mp
->mevent
= levent
;
1451 mp
->doubleclick
= doubleclick
;
1452 mp
->button
= lbutton
;
1460 * register_key - register the given keystroke to accept mouse events
1463 register_key(int i
, unsigned rval
, char *label
, void (*label_printer
)(),
1464 int row
, int col
, int len
, COLOR_PAIR
*kn
, COLOR_PAIR
*kl
)
1469 menuitems
[i
].val
= rval
;
1470 menuitems
[i
].tl
.r
= menuitems
[i
].br
.r
= row
;
1471 menuitems
[i
].tl
.c
= col
;
1472 menuitems
[i
].br
.c
= col
+ len
;
1473 menuitems
[i
].lbl
.r
= menuitems
[i
].tl
.r
;
1474 menuitems
[i
].lbl
.c
= menuitems
[i
].tl
.c
;
1475 menuitems
[i
].label_hiliter
= label_printer
;
1476 if(menuitems
[i
].label
){
1477 free(menuitems
[i
].label
);
1478 menuitems
[i
].label
= NULL
;
1480 if(menuitems
[i
].kncp
)
1481 free_color_pair(&menuitems
[i
].kncp
);
1482 if(menuitems
[i
].klcp
)
1483 free_color_pair(&menuitems
[i
].klcp
);
1485 menuitems
[i
].kncp
= new_color_pair(kn
->fg
, kn
->bg
);
1487 menuitems
[i
].kncp
= NULL
;
1489 menuitems
[i
].klcp
= new_color_pair(kl
->fg
, kl
->bg
);
1491 menuitems
[i
].klcp
= NULL
;
1496 len
= strlen(label
);
1497 if((menuitems
[i
].label
= (char *)malloc((len
+1)*sizeof(char))) != NULL
){
1498 strncpy(menuitems
[i
].label
, label
, len
);
1499 menuitems
[i
].label
[len
] = '\0';
1506 mouse_on_key(int row
, int col
)
1510 for(i
= 0; i
< 12; i
++)
1511 if(M_ACTIVE(row
, col
, &menuitems
[i
]))
1520 * Below are functions for use outside pico to manipulate text
1521 * in a pico's native format (circular linked list of lines).
1523 * The idea is to streamline pico use by making it fairly easy
1524 * for outside programs to prepare text intended for pico's use.
1525 * The simple char * alternative is messy as it requires two copies
1526 * of the same text, and isn't very economic in limited memory
1527 * situations (THANKS BELLEVUE-BILLY.).
1529 typedef struct picotext
{
1536 #define PT(X) ((PICOTEXT *)(X))
1539 * pico_get - return window struct pointer used as a handle
1540 * to the other pico_xxx routines.
1545 PICOTEXT
*wp
= NULL
;
1548 if((wp
= (PICOTEXT
*)malloc(sizeof(PICOTEXT
))) != NULL
){
1550 if((lp
= lalloc(0)) == NULL
){
1555 wp
->dotp
= wp
->linep
= lp
->l_fp
= lp
->l_bp
= lp
;
1559 emlwrite("Can't allocate space for text", NULL
);
1565 * pico_give - free resources and give up picotext struct
1573 fp
= lforw(PT(w
)->linep
);
1574 while((lp
= fp
) != PT(w
)->linep
){
1579 free((PICOTEXT
*)w
);
1583 * pico_readc - return char at current point. Up to calling routines
1584 * to keep cumulative count of chars.
1585 * The characters in PT are UCS-4 characters. The caller
1586 * of pico_readc is expecting UTF-8 chars. We convert
1587 * each UCS-4 character to a string of UTF-8 characters
1588 * and return them one at a time.
1591 pico_readc(void *w
, unsigned char *c
, int flags
)
1595 static unsigned char obuf
[6];
1596 static unsigned char *obufpend
= obuf
;
1597 static unsigned char *obufpnext
= obuf
;
1599 if(!(flags
& PICOREADC_NOUCS
)){
1600 if(obufpend
> obuf
){
1603 if(obufpnext
>= obufpend
){
1612 if(PT(w
)->crinread
){
1613 *c
= '\012'; /* return LF */
1614 PT(w
)->crinread
= 0;
1617 else if(PT(w
)->doto
< llength(PT(w
)->dotp
)){ /* normal char to return */
1618 if(flags
& PICOREADC_NOUCS
){
1619 *c
= (unsigned char) lgetc(PT(w
)->dotp
, (PT(w
)->doto
)++).c
;
1624 ucs
= lgetc(PT(w
)->dotp
, (PT(w
)->doto
)++).c
;
1625 obufpend
= utf8_put(obuf
, (unsigned long) ucs
);
1627 if(obufpend
> obuf
){
1629 if(obufpnext
>= obufpend
){
1638 else if(PT(w
)->dotp
!= PT(w
)->linep
){ /* return line break */
1639 PT(w
)->dotp
= lforw(PT(w
)->dotp
);
1641 #if defined(DOS) || defined(OS2)
1645 *c
= '\012'; /* return local eol! */
1648 } /* else no chars to return */
1655 * pico_writec - write a char into picotext and advance pointers.
1656 * Up to calling routines to keep track of total chars
1658 * Incoming text (c) is UTF-8 and written chars are UCS-4.
1659 * We need to collect up multiple chars to make a single
1660 * UCS-4 char, so there needs to be some state between calls.
1663 pico_writec(void *w
, int c
, int flags
)
1667 if(c
== '\r') /* ignore CR's */
1668 rv
++; /* so fake it */
1669 else if(c
== '\n'){ /* insert newlines on LF */
1671 * OK, if there are characters on the current line or
1672 * dotp is pointing to the delimiter line, insert a newline
1673 * No here's the tricky bit; preserve the implicit EOF newline.
1675 if(lforw(PT(w
)->dotp
) == PT(w
)->linep
&& PT(w
)->dotp
!= PT(w
)->linep
){
1676 PT(w
)->dotp
= PT(w
)->linep
;
1682 if((lp
= lalloc(0)) == NULL
){
1683 emlwrite("Can't allocate space for more characters",NULL
);
1687 if(PT(w
)->dotp
== PT(w
)->linep
){
1688 lforw(lp
) = PT(w
)->linep
;
1689 lback(lp
) = lback(PT(w
)->linep
);
1690 lforw(lback(lp
)) = lback(PT(w
)->linep
) = lp
;
1693 lforw(lp
) = lforw(PT(w
)->dotp
);
1694 lback(lp
) = PT(w
)->dotp
;
1695 lback(lforw(lp
)) = lforw(PT(w
)->dotp
) = lp
;
1704 if(flags
& PICOREADC_NOUCS
){
1706 * With this flag we're reverting to the old behavior where no
1707 * UTF-8 to UCS-4 translation takes place. We assume nothing
1708 * about the incoming byte stream. We just write a byte at a
1709 * time. So even though it's being stored in PT each
1710 * item is really limited to a single octet value.
1712 rv
= geninsert(&PT(w
)->dotp
, &PT(w
)->doto
, PT(w
)->linep
, c
, 0, 1, NULL
);
1715 static unsigned char cbuf
[6];
1716 static unsigned char *cbufp
= cbuf
;
1717 UCS obuf
[MAX(MB_LEN_MAX
,32)];
1718 int i
, outchars
= 0;
1720 if(cbufp
< cbuf
+sizeof(cbuf
)){
1721 unsigned char *inputp
;
1722 unsigned long remaining_octets
;
1725 *cbufp
++ = (unsigned char) c
;
1727 remaining_octets
= (cbufp
- cbuf
) * sizeof(unsigned char);
1728 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
1731 case U8G_ENDSTRG
: /* incomplete character, wait */
1732 case U8G_ENDSTRI
: /* incomplete character, wait */
1736 if(ucs
& U8G_ERROR
|| ucs
== UBOGON
){
1738 * None of these cases is supposed to happen. If it
1739 * does happen then the input stream isn't UTF-8
1740 * so something is wrong. Treat each character in the
1741 * input buffer as a separate error character and
1742 * print a '?' for each.
1744 for(inputp
= cbuf
; inputp
< cbufp
; inputp
++)
1745 obuf
[outchars
++] = '?';
1750 /* got a character */
1751 if(ucs
>= 0x80 && wcellwidth(ucs
) < 0){
1753 * This happens when we have a UTF-8 character that
1754 * we aren't able to print in our locale. For example,
1755 * if the locale is setup with the terminal
1756 * expecting ISO-8859-1 characters then there are
1757 * lots of UTF-8 characters that can't be printed.
1758 * Print a '?' instead.
1759 This may be the wrong thing to do. What happens if user
1760 is just forwarding and doesn't edit. We are going to lose
1761 the original value, aren't we? Maybe we do this only
1762 when printing to the screen instead.
1764 obuf
[outchars
++] = '?';
1768 * A regular ucs character
1770 obuf
[outchars
++] = ucs
;
1773 /* update the input buffer */
1774 if(inputp
>= cbufp
) /* this should be the case */
1776 else{ /* extra chars for some reason? */
1777 unsigned char *q
, *newcbufp
;
1779 newcbufp
= (cbufp
- inputp
) + cbuf
;
1781 while(inputp
< cbufp
)
1794 cbufp
= cbuf
; /* start over */
1798 * Unless we have trouble translating outchars will be
1799 * either 1 or 0. It is the UCS-4 character that we converted
1800 * the input to. It's an array just so it can hold ? when
1804 for(i
= 0; rv
&& i
< outchars
; i
++)
1805 if(!geninsert(&PT(w
)->dotp
, &PT(w
)->doto
, PT(w
)->linep
, obuf
[i
], 0, 1, NULL
))
1811 * Warning, this is no longer number written, because when we have a
1812 * multibyte UTF-8 character we won't write anything until we get all
1815 return((rv
) ? 1 : 0); /* return number written */
1820 * pico_puts - just write the given string into the text
1823 pico_puts(void *w
, char *s
, int flags
)
1829 while(rv
&& *s
!= '\0')
1830 if(!pico_writec(w
, (int)*s
++, flags
))
1834 return((rv
) ? 1 : 0);
1839 * pico_seek - position dotp and dot at requested location
1842 pico_seek(void *w
, long offset
, int orig
)
1846 PT(w
)->crinread
= 0;
1848 case 0 : /* SEEK_SET */
1849 PT(w
)->dotp
= lforw(PT(w
)->linep
);
1851 case 1 : /* SEEK_CUR */
1853 while(lp
!= PT(w
)->linep
){
1854 if(offset
<= llength(lp
)){
1855 PT(w
)->doto
= (int)offset
;
1860 offset
-= ((long)llength(lp
)
1861 #if defined(DOS) || defined(OS2)
1870 case 2 : /* SEEK_END */
1871 PT(w
)->dotp
= lback(PT(w
)->linep
);
1872 PT(w
)->doto
= llength(PT(w
)->dotp
);
1883 * breplace - replace the current window's text with the given
1892 fp
= lforw(curbp
->b_linep
);
1893 while((lp
= fp
) != curbp
->b_linep
){ /* blast old lines */
1897 free(curbp
->b_linep
);
1899 curbp
->b_linep
= PT(w
)->linep
; /* arrange pointers */
1902 * Undo space-stuffing that was done when we were preparing to send.
1903 * Some error happened so we're back to the composer.
1905 if(Pmaster
&& Pmaster
->space_stuffed
){
1906 Pmaster
->space_stuffed
= 0;
1907 for(lp
= lforw(curbp
->b_linep
); lp
!= curbp
->b_linep
; lp
= lforw(lp
)){
1908 if(llength(lp
) && ucs4_isspace(lgetc(lp
, 0).c
)){
1916 curwp
->w_linep
= lforw(curbp
->b_linep
);
1917 curwp
->w_dotp
= lforw(curbp
->b_linep
);
1919 curwp
->w_markp
= curwp
->w_imarkp
= NULL
;
1920 curwp
->w_marko
= curwp
->w_imarko
= 0;
1922 curbp
->b_dotp
= curwp
->w_dotp
;
1923 curbp
->b_doto
= curbp
->b_marko
= 0;
1924 curbp
->b_markp
= NULL
;
1925 curbp
->b_linecnt
= -1;
1927 curwp
->w_flag
|= WFHARD
;
1931 free_pico_module_globals(void)
1936 for(i
= 0; i
< 12; i
++){
1937 if(menuitems
[i
].kncp
)
1938 free_color_pair(&menuitems
[i
].kncp
);
1939 if(menuitems
[i
].klcp
)
1940 free_color_pair(&menuitems
[i
].klcp
);
1941 if(menuitems
[i
].label
){
1942 free((void *) menuitems
[i
].label
);
1943 menuitems
[i
].label
= NULL
;
1956 composer_file_drop(int x
, int y
, char *filename
)
1961 if((ComposerTopLine
> 2 && x
<= ComposerTopLine
)
1962 || !LikelyASCII(filename
)){
1963 AppendAttachment(filename
, NULL
, NULL
);
1969 swapimark(FALSE
, 1);
1972 if(ComposerEditing
){ /* update display */
1982 emlwrite(_("Attached dropped file \"%s\""), &eml
);
1984 emlwrite(_("Inserted dropped file \"%s\""), &eml
);
1986 if(ComposerEditing
){ /* restore cursor */
1987 HeaderPaintCursor();
1990 curwp
->w_flag
|= WFHARD
;
1999 pico_cursor(int col
, long row
)
2001 return((row
> 1 && row
<= term
.t_nrow
- (term
.t_mrow
+ 1))
2002 ? MSWIN_CURSOR_IBEAM
2003 : MSWIN_CURSOR_ARROW
);
2005 #endif /* _WINDOWS */