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-2016 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 emlwrite(_("\007Cancel 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 emlwrite(_("\007Problem 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
)
811 emlwrite(_("Exit cancelled"), NULL
);
813 curwp
->w_flag
|= WFHARD
; /* cause bottom 3 lines to paint */
823 * Has any editing been done to headers?
826 any_header_changes(void)
828 struct headerentry
*he
;
830 for(he
= Pmaster
->headents
; he
->name
!= NULL
; he
++)
834 return(he
->name
&& he
->dirty
);
838 isquotedspace(LINE
*line
)
840 int i
, was_quote
= 0;
841 for(i
= 0; i
< llength(line
); i
++){
842 if(lgetc(line
, i
).c
== '>')
844 else if(was_quote
&& lgetc(line
, i
).c
== ' '){
845 if(i
+1 < llength(line
) && ucs4_isspace(lgetc(line
,i
+1).c
))
857 * This function serves two purposes, 1) to strip white space when
858 * Pmaster asks that the composition have its trailing white space
859 * stripped, or 2) to prepare the text as flowed text, as Pmaster
860 * is telling us that we're working with flowed text.
862 * What flowed currently means to us is stripping all trailing white
863 * space, except for one space if the following line is a continuation
864 * of the paragraph. Also, we space-stuff all lines beginning
865 * with white-space, and leave siglines alone.
868 cleanwhitespace(void)
870 LINE
*cursor_dotp
= NULL
, **lp
= NULL
;
871 int i
= 0, cursor_doto
= 0, is_cursor_line
= 0;
872 int do_space_stuffing
= 0;
874 if(Pmaster
&& Pmaster
->allow_flowed_text
&& !(*Pmaster
->user_says_noflow
)())
877 cursor_dotp
= curwp
->w_dotp
;
878 cursor_doto
= curwp
->w_doto
;
881 for(lp
= &curwp
->w_dotp
; (*lp
) != curbp
->b_linep
; (*lp
) = lforw(*lp
)){
882 if(!(llength(*lp
) == 3
883 && lgetc(*lp
, 0).c
== '-'
884 && lgetc(*lp
, 1).c
== '-'
885 && lgetc(*lp
, 2).c
== ' ')
887 is_cursor_line
= (cursor_dotp
== (*lp
));
888 /* trim trailing whitespace, to be added back if flowing */
889 for(i
= llength(*lp
); i
; i
--)
890 if(!ucs4_isspace(lgetc(*lp
, i
- 1).c
))
892 if(i
!= llength(*lp
)){
895 if(Pmaster
&& !Pmaster
->strip_ws_before_send
896 && lforw(*lp
) != curbp
->b_linep
897 && llength(lforw(*lp
))
898 && !(ucs4_isspace(lgetc(lforw(*lp
), 0).c
)
899 || isquotedspace(lforw(*lp
)))
900 && !(llength(lforw(*lp
)) == 3
901 && lgetc(lforw(*lp
), 0).c
== '-'
902 && lgetc(lforw(*lp
), 1).c
== '-'
903 && lgetc(lforw(*lp
), 2).c
== ' '))
905 if(flow_line
&& i
&& lgetc(*lp
, i
).c
== ' '){
906 /* flowed line ending with space */
908 if(i
!= llength(*lp
)){
910 ldelete(llength(*lp
) - i
, NULL
);
913 else if(flow_line
&& i
&& ucs4_isspace(lgetc(*lp
, i
).c
)){
914 /* flowed line ending with whitespace other than space*/
916 ldelete(llength(*lp
) - i
, NULL
);
921 ldelete(llength(*lp
) - i
, NULL
);
924 if(do_space_stuffing
&& llength(*lp
) && ucs4_isspace(lgetc(*lp
, 0).c
)){
925 /* space-stuff only if flowed */
927 Pmaster
->space_stuffed
= 1;
929 if(is_cursor_line
&& cursor_doto
)
938 /* put the cursor back where we found it */
940 curwp
->w_dotp
= cursor_dotp
;
941 curwp
->w_doto
= (cursor_doto
< llength(curwp
->w_dotp
))
942 ? cursor_doto
: llength(curwp
->w_dotp
) - 1;
948 * Remove all trailing white space from the text
951 stripwhitespace(void)
954 LINE
*cur_line
= lforw(curbp
->b_linep
);
957 /* we gotta test for the sigdash case here */
958 if(!(cur_line
->l_used
== 3 &&
959 lgetc(cur_line
, 0).c
== '-' &&
960 lgetc(cur_line
, 1).c
== '-' &&
961 lgetc(cur_line
, 2).c
== ' ')){
962 for(i
= cur_line
->l_used
- 1; i
>= 0; i
--)
963 if(ucs4_isspace(lgetc(cur_line
, i
).c
))
968 }while((cur_line
= lforw(cur_line
)) != curbp
->b_linep
);
974 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
975 * Sometimes called as a routine, to do general aborting of stuff.
980 emlwrite(_("Cancelled"), NULL
);
985 /* tell the user that this command is illegal while we are in
986 * VIEW (read-only) mode
992 emlwrite("Key illegal in VIEW mode", NULL
);
999 * reset all globals to their initial values
1009 * re-initialize global buffer type variables ....
1011 fillcol
= (term
.t_ncol
> 80) ? 77 : term
.t_ncol
- 6;
1015 ComposerEditing
= FALSE
;
1018 * re-initialize hardware display variables ....
1020 vtrow
= vtcol
= lbound
= 0;
1023 pat
[0] = rpat
[0] = '\0';
1024 browse_dir
[0] = '\0';
1029 * pico_help - help function for standalone composer
1030 * Oops - looks like utf8title is unused!
1032 * This should be fixed to handle TAB characters.
1035 pico_help(char *utf8text
[], char *utf8title
, int i
)
1037 register int numline
= 0;
1044 return(wscrollw(COMPOSER_TOP_LINE
, term
.t_nrow
-1, utf8text
, numline
));
1050 * zotedit() - kills the buffer and frees all lines associated with it!!!
1055 wheadp
->w_linep
= wheadp
->w_dotp
= wheadp
->w_markp
= wheadp
->w_imarkp
= NULL
;
1056 bheadp
->b_linep
= bheadp
->b_dotp
= bheadp
->b_markp
= NULL
;
1058 free((char *) wheadp
); /* clean up window */
1062 free((char *) bheadp
); /* clean up buffers */
1066 zotheader(); /* blast header lines */
1068 kdelete(); /* blast kill buffer */
1075 * Generic mouse handling functions
1077 MENUITEM menuitems
[12]; /* key labels and functions */
1078 MENUITEM
*mfunc
= NULL
; /* list of regional functions */
1079 mousehandler_t mtrack
; /* mouse tracking handler */
1081 /* last mouse position */
1082 static unsigned long levent
= 0L;
1083 static int lrow
= 0, lcol
= 0, doubleclick
, lbutton
, lflags
;
1085 static clock_t lastcalled
= 0;
1087 static time_t lastcalled
= 0;
1089 static mousehandler_t lastf
;
1093 * register_mfunc - register the given function to get called
1094 * on mouse events in the given display region
1097 register_mfunc(mousehandler_t f
, int tlr
, int tlc
, int brr
, int brc
)
1104 for(mp
= &mfunc
; *mp
; mp
= &(*mp
)->next
)
1107 *mp
= (MENUITEM
*)malloc(sizeof(MENUITEM
));
1108 memset(*mp
, 0, sizeof(MENUITEM
));
1115 (*mp
)->lbl
.c
= (*mp
)->lbl
.r
= 0;
1122 * clear_mfunc - clear any previously set mouse function
1125 clear_mfunc(mousehandler_t f
)
1129 if((mp
= mfunc
) != NULL
){
1133 for(tp
= mp
; tp
->next
; tp
= tp
->next
)
1134 if(tp
->next
->action
== f
){
1136 tp
->next
= tp
->next
->next
;
1155 mswin_allowmousetrack (FALSE
);
1160 register_mtrack(mousehandler_t f
)
1164 mswin_allowmousetrack (TRUE
);
1172 move_dot_to(int row
, int col
)
1177 lp
= curwp
->w_linep
;
1178 i
= row
- ((Pmaster
) ? ComposerTopLine
: 2);
1179 while(i
-- && lp
!= curbp
->b_linep
) /* count from top */
1182 curwp
->w_dotp
= lp
; /* to new dot. */
1183 curwp
->w_doto
= getgoal(lp
);
1184 curwp
->w_flag
|= WFMOVE
;
1191 * When the mouse goes down in the body we set the mark and start
1194 * As the mouse moves we update the dot and redraw the screen.
1196 * If the mouse moves above or below the pico body region of the
1197 * screen we scroll the text and update the dot position.
1199 * When the mouse comes up we clean up. If the mouse did not
1200 * move, then we clear the mark and turn off the selection.
1202 * Most of the mouse processing is handled here. The exception is
1203 * mouse down in the header. Can't call HeaderEditor() from here so
1204 * we send up the KEY_MOUSE character, which gets dispatched to
1205 * mousepress(), which _can_ call HeaderEditor().
1208 mouse_in_pico(unsigned long mevent
, int row
, int col
, int button
, int flags
)
1210 unsigned long rv
= 0; /* Our return value. */
1211 int trow
, tcol
; /* translated row and col. */
1213 static int lheader
= FALSE
; /* Mouse down was in header. */
1221 if(button
!= M_BUTTON_LEFT
)
1224 /* Ignore mouse down if not in pico body region. */
1225 if (row
< 2 || row
> term
.t_nrow
- (term
.t_mrow
+1)) {
1230 /* Detect double clicks. Not that we do anything with em, just
1233 #ifdef CLOCKS_PER_SEC
1234 doubleclick
= (lrow
== row
&& lcol
== col
1235 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1238 doubleclick
= (lrow
== row
&& lcol
== col
1239 && clock() < (lastcalled
+ CLK_TCK
/2));
1241 doubleclick
= FALSE
;
1244 lastcalled
= clock();
1246 doubleclick
= (lrow
== row
&& lcol
== col
1247 && time(0) < (lastcalled
+ 2));
1248 lastcalled
= time(0);
1250 lheader
= FALSE
; /* Rember mouse down position. */
1257 /* Mouse down in body? */
1258 if (row
< (Pmaster
? ComposerTopLine
: 2)) {
1259 /* Mouse down in message header -> no tracking, just remember
1264 /* Mouse down in message.
1265 * If no shift key and an existing mark -> clear the mark.
1266 * If shift key and no existing mark -> set mark before moving */
1267 if (!(flags
& M_KEY_SHIFT
) && curwp
->w_markp
)
1268 setmark (0,1); /* this clears the mark. */
1269 else if (flags
& M_KEY_SHIFT
&& !curwp
->w_markp
)
1270 setmark (0,1); /* while this sets the mark. */
1272 /* Reposition dot to mouse down. */
1273 move_dot_to (row
, col
);
1275 /* Set the mark to dot if no existing mark. */
1276 if (curwp
->w_markp
== NULL
)
1279 /* Track mouse movement. */
1280 register_mtrack (mouse_in_pico
);
1282 lheader
= FALSE
; /* Just to be sure. */
1288 /* Mouse tracking. */
1289 if (lheader
) /* Ignore mouse movement in header. */
1292 /* If above or below body, scroll body and adjust the row and col. */
1293 if (row
< (Pmaster
? ComposerTopLine
: 2)) {
1294 /* Scroll text down screen and move dot to top left corner. */
1296 trow
= (Pmaster
) ? ComposerTopLine
: 2;
1299 else if (row
> term
.t_nrow
- (term
.t_mrow
+ 1)) {
1300 /* Scroll text up screen and move dot to bottom right corner. */
1301 scrolldownline (0,1);
1302 trow
= term
.t_nrow
- (term
.t_mrow
+ 1);
1310 /* Move dot to target column. */
1311 move_dot_to (trow
, tcol
);
1313 /* Update screen. */
1319 if(button
== M_BUTTON_RIGHT
){
1325 else if(button
!= M_BUTTON_LEFT
)
1330 /* Last down in header. */
1331 if (row
== lrow
&& col
== lcol
) {
1332 /* Mouse up and down in same place in header. Means the
1333 * user want to edit the header. Return KEY_MOUSE which
1334 * will cause mousepress to be called, which will
1335 * call HeaderEditor. Can't call HeaderEditor from here
1336 * because that would mess up layering. */
1339 rv
= (unsigned long) KEY_MOUSE
;
1343 /* If up at same place, clear mark */
1344 if (curwp
->w_markp
== curwp
->w_dotp
&&
1345 curwp
->w_marko
== curwp
->w_doto
) {
1347 curwp
->w_flag
|= WFMOVE
;
1362 * mouse_in_content - general mechanism used to pass recognized mouse
1363 * events in predefined region back thru the usual
1364 * keyboard input stream. The actual return value
1365 * passed back from this function is set dynamically
1366 * via the "down" argument which is read when both the
1367 * "row" and "col" arguments are negative.
1370 mouse_in_content(unsigned long mevent
, int row
, int col
, int button
, int flags
)
1372 unsigned long rv
= 0;
1373 static unsigned long mouse_val
= KEY_MOUSE
;
1375 if(row
== -1 && col
== -1){
1376 mouse_val
= mevent
; /* setting return value */
1383 /* Mouse down does not mean anything, just keep track of
1384 * where it went down and if this is a double click. */
1386 #ifdef CLOCKS_PER_SEC
1387 doubleclick
= (lrow
== row
&& lcol
== col
1388 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1391 doubleclick
= (lrow
== row
&& lcol
== col
1392 && clock() < (lastcalled
+ CLK_TCK
/2));
1394 doubleclick
= FALSE
;
1397 lastcalled
= clock();
1399 doubleclick
= (lrow
== row
&& lcol
== col
1400 && time(0) < (lastcalled
+ 2));
1401 lastcalled
= time(0);
1410 /* Mouse up. If in the same position as it went down
1411 * then we return the value set above, which goes into
1412 * the character input stream, which gets processed as
1413 * a mouse event by some upper layer, which calls to
1414 * mouse_get_last(). */
1415 if (lrow
== row
&& lcol
== col
) {
1430 * mouse_get_last - Get last mouse event.
1434 mouse_get_last(mousehandler_t
*f
, MOUSEPRESS
*mp
)
1439 mp
->mevent
= levent
;
1442 mp
->doubleclick
= doubleclick
;
1443 mp
->button
= lbutton
;
1451 * register_key - register the given keystroke to accept mouse events
1454 register_key(int i
, unsigned rval
, char *label
, void (*label_printer
)(),
1455 int row
, int col
, int len
, COLOR_PAIR
*kn
, COLOR_PAIR
*kl
)
1460 menuitems
[i
].val
= rval
;
1461 menuitems
[i
].tl
.r
= menuitems
[i
].br
.r
= row
;
1462 menuitems
[i
].tl
.c
= col
;
1463 menuitems
[i
].br
.c
= col
+ len
;
1464 menuitems
[i
].lbl
.r
= menuitems
[i
].tl
.r
;
1465 menuitems
[i
].lbl
.c
= menuitems
[i
].tl
.c
;
1466 menuitems
[i
].label_hiliter
= label_printer
;
1467 if(menuitems
[i
].label
){
1468 free(menuitems
[i
].label
);
1469 menuitems
[i
].label
= NULL
;
1471 if(menuitems
[i
].kncp
)
1472 free_color_pair(&menuitems
[i
].kncp
);
1473 if(menuitems
[i
].klcp
)
1474 free_color_pair(&menuitems
[i
].klcp
);
1476 menuitems
[i
].kncp
= new_color_pair(kn
->fg
, kn
->bg
);
1478 menuitems
[i
].kncp
= NULL
;
1480 menuitems
[i
].klcp
= new_color_pair(kl
->fg
, kl
->bg
);
1482 menuitems
[i
].klcp
= NULL
;
1487 len
= strlen(label
);
1488 if((menuitems
[i
].label
= (char *)malloc((len
+1)*sizeof(char))) != NULL
){
1489 strncpy(menuitems
[i
].label
, label
, len
);
1490 menuitems
[i
].label
[len
] = '\0';
1497 mouse_on_key(int row
, int col
)
1501 for(i
= 0; i
< 12; i
++)
1502 if(M_ACTIVE(row
, col
, &menuitems
[i
]))
1511 * Below are functions for use outside pico to manipulate text
1512 * in a pico's native format (circular linked list of lines).
1514 * The idea is to streamline pico use by making it fairly easy
1515 * for outside programs to prepare text intended for pico's use.
1516 * The simple char * alternative is messy as it requires two copies
1517 * of the same text, and isn't very economic in limited memory
1518 * situations (THANKS BELLEVUE-BILLY.).
1520 typedef struct picotext
{
1527 #define PT(X) ((PICOTEXT *)(X))
1530 * pico_get - return window struct pointer used as a handle
1531 * to the other pico_xxx routines.
1536 PICOTEXT
*wp
= NULL
;
1539 if((wp
= (PICOTEXT
*)malloc(sizeof(PICOTEXT
))) != NULL
){
1541 if((lp
= lalloc(0)) == NULL
){
1546 wp
->dotp
= wp
->linep
= lp
->l_fp
= lp
->l_bp
= lp
;
1550 emlwrite("Can't allocate space for text", NULL
);
1556 * pico_give - free resources and give up picotext struct
1564 fp
= lforw(PT(w
)->linep
);
1565 while((lp
= fp
) != PT(w
)->linep
){
1570 free((PICOTEXT
*)w
);
1574 * pico_readc - return char at current point. Up to calling routines
1575 * to keep cumulative count of chars.
1576 * The characters in PT are UCS-4 characters. The caller
1577 * of pico_readc is expecting UTF-8 chars. We convert
1578 * each UCS-4 character to a string of UTF-8 characters
1579 * and return them one at a time.
1582 pico_readc(void *w
, unsigned char *c
, int flags
)
1586 static unsigned char obuf
[6];
1587 static unsigned char *obufpend
= obuf
;
1588 static unsigned char *obufpnext
= obuf
;
1590 if(!(flags
& PICOREADC_NOUCS
)){
1591 if(obufpend
> obuf
){
1594 if(obufpnext
>= obufpend
){
1603 if(PT(w
)->crinread
){
1604 *c
= '\012'; /* return LF */
1605 PT(w
)->crinread
= 0;
1608 else if(PT(w
)->doto
< llength(PT(w
)->dotp
)){ /* normal char to return */
1609 if(flags
& PICOREADC_NOUCS
){
1610 *c
= (unsigned char) lgetc(PT(w
)->dotp
, (PT(w
)->doto
)++).c
;
1615 ucs
= lgetc(PT(w
)->dotp
, (PT(w
)->doto
)++).c
;
1616 obufpend
= utf8_put(obuf
, (unsigned long) ucs
);
1618 if(obufpend
> obuf
){
1620 if(obufpnext
>= obufpend
){
1629 else if(PT(w
)->dotp
!= PT(w
)->linep
){ /* return line break */
1630 PT(w
)->dotp
= lforw(PT(w
)->dotp
);
1632 #if defined(DOS) || defined(OS2)
1636 *c
= '\012'; /* return local eol! */
1639 } /* else no chars to return */
1646 * pico_writec - write a char into picotext and advance pointers.
1647 * Up to calling routines to keep track of total chars
1649 * Incoming text (c) is UTF-8 and written chars are UCS-4.
1650 * We need to collect up multiple chars to make a single
1651 * UCS-4 char, so there needs to be some state between calls.
1654 pico_writec(void *w
, int c
, int flags
)
1658 if(c
== '\r') /* ignore CR's */
1659 rv
++; /* so fake it */
1660 else if(c
== '\n'){ /* insert newlines on LF */
1662 * OK, if there are characters on the current line or
1663 * dotp is pointing to the delimiter line, insert a newline
1664 * No here's the tricky bit; preserve the implicit EOF newline.
1666 if(lforw(PT(w
)->dotp
) == PT(w
)->linep
&& PT(w
)->dotp
!= PT(w
)->linep
){
1667 PT(w
)->dotp
= PT(w
)->linep
;
1673 if((lp
= lalloc(0)) == NULL
){
1674 emlwrite("Can't allocate space for more characters",NULL
);
1678 if(PT(w
)->dotp
== PT(w
)->linep
){
1679 lforw(lp
) = PT(w
)->linep
;
1680 lback(lp
) = lback(PT(w
)->linep
);
1681 lforw(lback(lp
)) = lback(PT(w
)->linep
) = lp
;
1684 lforw(lp
) = lforw(PT(w
)->dotp
);
1685 lback(lp
) = PT(w
)->dotp
;
1686 lback(lforw(lp
)) = lforw(PT(w
)->dotp
) = lp
;
1695 if(flags
& PICOREADC_NOUCS
){
1697 * With this flag we're reverting to the old behavior where no
1698 * UTF-8 to UCS-4 translation takes place. We assume nothing
1699 * about the incoming byte stream. We just write a byte at a
1700 * time. So even though it's being stored in PT each
1701 * item is really limited to a single octet value.
1703 rv
= geninsert(&PT(w
)->dotp
, &PT(w
)->doto
, PT(w
)->linep
, c
, 0, 1, NULL
);
1706 static unsigned char cbuf
[6];
1707 static unsigned char *cbufp
= cbuf
;
1708 UCS obuf
[MAX(MB_LEN_MAX
,32)];
1709 int i
, outchars
= 0;
1711 if(cbufp
< cbuf
+sizeof(cbuf
)){
1712 unsigned char *inputp
;
1713 unsigned long remaining_octets
;
1716 *cbufp
++ = (unsigned char) c
;
1718 remaining_octets
= (cbufp
- cbuf
) * sizeof(unsigned char);
1719 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
1722 case U8G_ENDSTRG
: /* incomplete character, wait */
1723 case U8G_ENDSTRI
: /* incomplete character, wait */
1727 if(ucs
& U8G_ERROR
|| ucs
== UBOGON
){
1729 * None of these cases is supposed to happen. If it
1730 * does happen then the input stream isn't UTF-8
1731 * so something is wrong. Treat each character in the
1732 * input buffer as a separate error character and
1733 * print a '?' for each.
1735 for(inputp
= cbuf
; inputp
< cbufp
; inputp
++)
1736 obuf
[outchars
++] = '?';
1741 /* got a character */
1742 if(ucs
>= 0x80 && wcellwidth(ucs
) < 0){
1744 * This happens when we have a UTF-8 character that
1745 * we aren't able to print in our locale. For example,
1746 * if the locale is setup with the terminal
1747 * expecting ISO-8859-1 characters then there are
1748 * lots of UTF-8 characters that can't be printed.
1749 * Print a '?' instead.
1750 This may be the wrong thing to do. What happens if user
1751 is just forwarding and doesn't edit. We are going to lose
1752 the original value, aren't we? Maybe we do this only
1753 when printing to the screen instead.
1755 obuf
[outchars
++] = '?';
1759 * A regular ucs character
1761 obuf
[outchars
++] = ucs
;
1764 /* update the input buffer */
1765 if(inputp
>= cbufp
) /* this should be the case */
1767 else{ /* extra chars for some reason? */
1768 unsigned char *q
, *newcbufp
;
1770 newcbufp
= (cbufp
- inputp
) + cbuf
;
1772 while(inputp
< cbufp
)
1785 cbufp
= cbuf
; /* start over */
1789 * Unless we have trouble translating outchars will be
1790 * either 1 or 0. It is the UCS-4 character that we converted
1791 * the input to. It's an array just so it can hold ? when
1795 for(i
= 0; rv
&& i
< outchars
; i
++)
1796 if(!geninsert(&PT(w
)->dotp
, &PT(w
)->doto
, PT(w
)->linep
, obuf
[i
], 0, 1, NULL
))
1802 * Warning, this is no longer number written, because when we have a
1803 * multibyte UTF-8 character we won't write anything until we get all
1806 return((rv
) ? 1 : 0); /* return number written */
1811 * pico_puts - just write the given string into the text
1814 pico_puts(void *w
, char *s
, int flags
)
1820 while(rv
&& *s
!= '\0')
1821 if(!pico_writec(w
, (int)*s
++, flags
))
1825 return((rv
) ? 1 : 0);
1830 * pico_seek - position dotp and dot at requested location
1833 pico_seek(void *w
, long offset
, int orig
)
1837 PT(w
)->crinread
= 0;
1839 case 0 : /* SEEK_SET */
1840 PT(w
)->dotp
= lforw(PT(w
)->linep
);
1842 case 1 : /* SEEK_CUR */
1844 while(lp
!= PT(w
)->linep
){
1845 if(offset
<= llength(lp
)){
1846 PT(w
)->doto
= (int)offset
;
1851 offset
-= ((long)llength(lp
)
1852 #if defined(DOS) || defined(OS2)
1861 case 2 : /* SEEK_END */
1862 PT(w
)->dotp
= lback(PT(w
)->linep
);
1863 PT(w
)->doto
= llength(PT(w
)->dotp
);
1874 * breplace - replace the current window's text with the given
1883 fp
= lforw(curbp
->b_linep
);
1884 while((lp
= fp
) != curbp
->b_linep
){ /* blast old lines */
1888 free(curbp
->b_linep
);
1890 curbp
->b_linep
= PT(w
)->linep
; /* arrange pointers */
1893 * Undo space-stuffing that was done when we were preparing to send.
1894 * Some error happened so we're back to the composer.
1896 if(Pmaster
&& Pmaster
->space_stuffed
){
1897 Pmaster
->space_stuffed
= 0;
1898 for(lp
= lforw(curbp
->b_linep
); lp
!= curbp
->b_linep
; lp
= lforw(lp
)){
1899 if(llength(lp
) && ucs4_isspace(lgetc(lp
, 0).c
)){
1907 curwp
->w_linep
= lforw(curbp
->b_linep
);
1908 curwp
->w_dotp
= lforw(curbp
->b_linep
);
1910 curwp
->w_markp
= curwp
->w_imarkp
= NULL
;
1911 curwp
->w_marko
= curwp
->w_imarko
= 0;
1913 curbp
->b_dotp
= curwp
->w_dotp
;
1914 curbp
->b_doto
= curbp
->b_marko
= 0;
1915 curbp
->b_markp
= NULL
;
1916 curbp
->b_linecnt
= -1;
1918 curwp
->w_flag
|= WFHARD
;
1927 composer_file_drop(int x
, int y
, char *filename
)
1932 if((ComposerTopLine
> 2 && x
<= ComposerTopLine
)
1933 || !LikelyASCII(filename
)){
1934 AppendAttachment(filename
, NULL
, NULL
);
1940 swapimark(FALSE
, 1);
1943 if(ComposerEditing
){ /* update display */
1953 emlwrite(_("Attached dropped file \"%s\""), &eml
);
1955 emlwrite(_("Inserted dropped file \"%s\""), &eml
);
1957 if(ComposerEditing
){ /* restore cursor */
1958 HeaderPaintCursor();
1961 curwp
->w_flag
|= WFHARD
;
1970 pico_cursor(int col
, long row
)
1972 return((row
> 1 && row
<= term
.t_nrow
- (term
.t_mrow
+ 1))
1973 ? MSWIN_CURSOR_IBEAM
1974 : MSWIN_CURSOR_ARROW
);
1976 #endif /* _WINDOWS */