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
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
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.
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
50 * 3.0 Daniel Lawrence, 29-Dec-85
52 * - updated documentation and froze development for 3.6 net release
55 /* make global definitions not external */
58 #include "../c-client/mail.h"
59 #include "../c-client/utf8.h"
62 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
67 #include "ebind.h" /* default key bindings */
68 #include "../pith/charconv/filesys.h"
71 void remove_directions_mark(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] = {
103 remove_directions_mark(void)
109 for(lp
= lforw(curbp
->b_linep
); lp
!= curbp
->b_linep
; lp
= lforw(lp
)){
110 for(i
= 0; i
< llength(lp
);){
112 if(c
== 0x200E || c
== 0x200F){
119 direction
= c
== 0x200E ? 0 : 1;
122 lgetc(lp
,i
++).d
= direction
;
125 curwp
->w_linep
= lforw(curbp
->b_linep
);
126 curwp
->w_dotp
= lforw(curbp
->b_linep
);
132 * flag for the various functions in pico() to set when ready
133 * for pico() to return...
135 int pico_all_done
= 0;
137 UCS
*pico_anchor
= NULL
;
138 extern struct headerentry
*headents
;
141 * pico - the main routine for Pine's composer.
150 char bname
[NBUFN
]; /* buffer name of file to read */
151 extern struct on_display ods
;
152 int checkpointcnt
= 0, input
= 0;
154 char chkptfile
[NLINE
];
161 gmode
|= pm
->pine_flags
; /* high 4 bits rsv'd for pine */
163 alt_speller
= pm
->alt_spell
;
165 dictionary
= pm
->dict
;
166 chosen_dict
= pm
->chosen_dict
;
167 #endif /* _WINDOWS */
171 if(!vtinit()) /* Init Displays. */
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
);
190 if(Pmaster
->quote_str
)
191 glo_quote_str
= utf8_to_ucs4_cpystr(Pmaster
->quote_str
);
193 glo_quote_str
= NULL
;
195 if(Pmaster
->wordseps
)
196 glo_wordseps
= ucs4_cpystr(Pmaster
->wordseps
);
200 bindtokey(DEL
, (gmode
& P_DELRUBS
) ? forwdel
: backdel
);
203 breplace(pm
->msgtext
);
205 remove_directions_mark();
208 cursor_shown
= mswin_showcaret(1); /* turn on for main window */
209 mswin_allowpaste(MSWIN_PASTE_FULL
);
210 mswin_setscrollcallback (pico_scroll_callback
);
213 /* prepare for checkpointing */
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 */
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
);
248 update(); /* paint screen, */
249 HeaderEditor((gmode
& P_HEADEND
) ? 2 : 0, 0);
259 mswin_allowpaste(MSWIN_PASTE_DISABLE
);
260 mswin_setscrollcallback (NULL
);
262 ret
= anycb() ? BUF_CHANGED
: 0;
263 switch(pico_all_done
){ /* prepare for/handle final events */
264 case COMP_EXIT
: /* already confirmed */
267 && (Pmaster
->strip_ws_before_send
268 || Pmaster
->allow_flowed_text
))
273 case COMP_CANCEL
: /* also already confirmed */
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
);
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
299 if(ComposerEditing
){ /* in the headers */
300 headents
[ods
.cur_e
].start_here
= 1;
301 Pmaster
->edit_offset
= ods
.p_ind
;
305 register long offset
;
307 for(clp
= lforw(curbp
->b_linep
), offset
= 0L;
308 clp
!= curwp
->w_dotp
;
310 offset
+= (llength(clp
) + 1);
312 Pmaster
->edit_offset
= offset
+ curwp
->w_doto
;
321 fs_give((void **) &pico_anchor
);
323 fs_give((void **) &glo_quote_str
);
325 fs_give((void **) &glo_wordseps
);
327 vttidy(); /* clean up tty modes */
328 zotdisplay(); /* blast display buffers */
330 our_unlink(chkptfile
);
331 Pmaster
= NULL
; /* blat global */
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 */
344 curwp
->w_ntrows
-= 2;
345 curwp
->w_flag
|= WFMODE
;
346 movecursor(term
.t_nrow
-2, 0); /* clear status line, too */
350 update(); /* Fix up the screen */
353 curwp
->w_ntrows
+= 2;
358 /* New mouse function for real mouse text selection. */
359 register_mfunc(mouse_in_pico
, 2, 0, term
.t_nrow
- (term
.t_mrow
+1),
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),
368 mswin_setdndcallback (composer_file_drop
);
369 mswin_mousetrackcallback(pico_cursor
);
372 if (term
.t_nrow
< 6 && c
!= NODATA
){
374 emlwrite(_("Please make the screen bigger."), NULL
);
380 clear_mfunc(mouse_in_pico
);
382 clear_mfunc(mouse_in_content
);
386 mswin_cleardndcallback ();
387 mswin_mousetrackcallback(NULL
);
389 if(c
== NODATA
|| time_to_check()){ /* new mail ? */
390 if((*Pmaster
->newmail
)(c
== NODATA
? 0 : 2, 1) >= 0){
395 curwp
->w_ntrows
-= 2;
396 curwp
->w_flag
|= WFHARD
;
402 rv
= (*Pmaster
->showmsg
)(c
);
404 picosigs(); /* restore altered handlers */
405 if(rv
) /* Did showmsg corrupt the display? */
406 PaintBody(0); /* Yes, repaint */
428 if(c
== NODATA
) /* no op, getkey timed out */
431 (*Pmaster
->keybinput
)();
433 if (mpresf
!= FALSE
) { /* message stay around only */
434 if (mpresf
++ > NMMESSDELAY
) /* so long! */
438 f
= FALSE
; /* vestigial */
441 execute(normalize_cmd(c
, pfkm
, 2), f
, n
);
442 if(++checkpointcnt
>= CHKPTDELAY
){
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.
469 bp
= bfind(bname
, TRUE
, BFWRAPOPEN
); /* First buffer */
470 wp
= (WINDOW
*) malloc(sizeof(WINDOW
)); /* First window */
472 if (bp
==NULL
|| wp
==NULL
){
479 curbp
= bp
; /* Make this current */
482 wp
->w_wndp
= NULL
; /* Initialize window */
484 bp
->b_nwnd
= 1; /* Displayed. */
485 wp
->w_linep
= bp
->b_linep
;
486 wp
->w_dotp
= bp
->b_linep
;
488 wp
->w_markp
= wp
->w_imarkp
= NULL
;
489 wp
->w_marko
= wp
->w_imarko
= 0;
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
;
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
;
510 wp
->w_ntrows
= term
.t_nrow
- 2 - term
.t_mrow
;
511 if(userfillcol
> 0) /* set fill column */
512 fillcol
= userfillcol
;
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';
527 fixpath(opertree
, sizeof(opertree
));
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
)
546 ktp
= (Pmaster
) ? &keytab
[0] : &pkeytab
[0];
548 while (ktp
->k_fp
!= NULL
) {
549 if (ktp
->k_code
== c
) {
552 curwp
->w_flag
|= WFMODE
;
558 status
= (*ktp
->k_fp
)(f
, n
);
559 if((lastflag
& CFFILL
) && !(thisflag
& CFFILL
))
561 if((lastflag
& CFFLBF
) && !(thisflag
& CFFLBF
))
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 */
580 if(lastflag
& CFFILL
) /* blat unusable fill data */
582 if(lastflag
& CFFLBF
)
585 if (VALID_KEY(c
)) { /* Self inserting. */
587 if (n
<= 0) { /* Fenceposts. */
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
){
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
){
617 ww
= wcellwidth((UCS
) lgetc(curwp
->w_dotp
, j
).c
);
618 wid
+= (ww
>= 0 ? ww
: 1);
632 lastflag
= 0; /* Fake last flags. */
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 */
649 if ((bp
->b_flag
&BFCHG
) != 0 /* Changed. */
650 && (bp
->b_flag
&BFTEMP
) == 0) { /* Real. */
651 curbp
= bp
; /* make that buffer cur */
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
666 abort_composer(int f
, int n
)
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
--;
682 /* TRANSLATORS: The user typed the Cancel command and was
683 asked to confirm that. Instead they canceled the cancel
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
--;
693 else switch(mlyesno_utf8(Pmaster
->headents
694 ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
696 ? _("Cancel Edit (and abandon changes)")
700 pico_all_done
= COMP_CANCEL
;
704 emlwwrite(_("Cancel Cancelled"), NULL
);
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
;
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".
743 /* First, make sure there are no outstanding problems */
745 emlwwrite(_("Problem with attachments! Fix errors or delete attachments."), NULL
);
750 if(Pmaster
->always_spell_check
)
751 if(spell(0, 0) == -1)
752 sleep(3); /* problem, show error */
755 * if we're not in header, show some of it as we verify sending...
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
,
765 Pmaster
->arm_winch_cleanup
--;
768 pico_all_done
= COMP_CANCEL
;
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
);
784 Pmaster
->arm_winch_cleanup
--;
785 pico_all_done
= COMP_EXIT
;
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
) {
796 #if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
803 if(filewrite(0,1) == TRUE
){
805 if(dictionary
!= NULL
){
807 for(i
= 0; dictionary
[i
] != NULL
; i
++)
808 fs_give((void **)&dictionary
[i
]);
809 fs_give((void **)dictionary
);
811 #endif /* _WINDOWS */
816 emlwrite(_("Exit cancelled"), NULL
);
818 curwp
->w_flag
|= WFHARD
; /* cause bottom 3 lines to paint */
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
++)
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
== '>')
849 else if(was_quote
&& lgetc(line
, i
).c
== ' '){
850 if(i
+1 < llength(line
) && ucs4_isspace(lgetc(line
,i
+1).c
))
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
)())
882 cursor_dotp
= curwp
->w_dotp
;
883 cursor_doto
= curwp
->w_doto
;
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
== ' ')
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
))
897 if(i
!= llength(*lp
)){
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
== ' '))
910 if(flow_line
&& i
&& lgetc(*lp
, i
).c
== ' '){
911 /* flowed line ending with space */
913 if(i
!= llength(*lp
)){
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*/
921 ldelete(llength(*lp
) - i
, NULL
);
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 */
932 Pmaster
->space_stuffed
= 1;
934 if(is_cursor_line
&& cursor_doto
)
943 /* put the cursor back where we found it */
945 curwp
->w_dotp
= cursor_dotp
;
946 curwp
->w_doto
= (cursor_doto
< llength(curwp
->w_dotp
))
947 ? cursor_doto
: llength(curwp
->w_dotp
) - 1;
953 * Remove all trailing white space from the text
956 stripwhitespace(void)
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
))
973 }while((cur_line
= lforw(cur_line
)) != curbp
->b_linep
);
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.
985 emlwrite(_("Cancelled"), NULL
);
990 /* tell the user that this command is illegal while we are in
991 * VIEW (read-only) mode
997 emlwrite("Key illegal in VIEW mode", NULL
);
1004 * reset all globals to their initial values
1014 * re-initialize global buffer type variables ....
1016 fillcol
= (term
.t_ncol
> 80) ? 77 : term
.t_ncol
- 6;
1020 ComposerEditing
= FALSE
;
1023 * re-initialize hardware display variables ....
1025 vtrow
= vtcol
= lbound
= 0;
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;
1049 return(wscrollw(COMPOSER_TOP_LINE
, term
.t_nrow
-1, utf8text
, numline
));
1055 * zotedit() - kills the buffer and frees all lines associated with it!!!
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 */
1067 free((char *) bheadp
); /* clean up buffers */
1071 zotheader(); /* blast header lines */
1073 kdelete(); /* blast kill buffer */
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
;
1090 static clock_t lastcalled
= 0;
1092 static time_t lastcalled
= 0;
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
)
1109 for(mp
= &mfunc
; *mp
; mp
= &(*mp
)->next
)
1112 *mp
= (MENUITEM
*)malloc(sizeof(MENUITEM
));
1113 memset(*mp
, 0, sizeof(MENUITEM
));
1120 (*mp
)->lbl
.c
= (*mp
)->lbl
.r
= 0;
1127 * clear_mfunc - clear any previously set mouse function
1130 clear_mfunc(mousehandler_t f
)
1134 if((mp
= mfunc
) != NULL
){
1138 for(tp
= mp
; tp
->next
; tp
= tp
->next
)
1139 if(tp
->next
->action
== f
){
1141 tp
->next
= tp
->next
->next
;
1160 mswin_allowmousetrack (FALSE
);
1165 register_mtrack(mousehandler_t f
)
1169 mswin_allowmousetrack (TRUE
);
1177 move_dot_to(int row
, int col
)
1182 lp
= curwp
->w_linep
;
1183 i
= row
- ((Pmaster
) ? ComposerTopLine
: 2);
1184 while(i
-- && lp
!= curbp
->b_linep
) /* count from top */
1187 curwp
->w_dotp
= lp
; /* to new dot. */
1188 curwp
->w_doto
= getgoal(lp
);
1189 curwp
->w_flag
|= WFMOVE
;
1196 * When the mouse goes down in the body we set the mark and start
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().
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. */
1226 if(button
!= M_BUTTON_LEFT
)
1229 /* Ignore mouse down if not in pico body region. */
1230 if (row
< 2 || row
> term
.t_nrow
- (term
.t_mrow
+1)) {
1235 /* Detect double clicks. Not that we do anything with em, just
1238 #ifdef CLOCKS_PER_SEC
1239 doubleclick
= (lrow
== row
&& lcol
== col
1240 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1243 doubleclick
= (lrow
== row
&& lcol
== col
1244 && clock() < (lastcalled
+ CLK_TCK
/2));
1246 doubleclick
= FALSE
;
1249 lastcalled
= clock();
1251 doubleclick
= (lrow
== row
&& lcol
== col
1252 && time(0) < (lastcalled
+ 2));
1253 lastcalled
= time(0);
1255 lheader
= FALSE
; /* Remember mouse down position. */
1262 /* Mouse down in body? */
1263 if (row
< (Pmaster
? ComposerTopLine
: 2)) {
1264 /* Mouse down in message header -> no tracking, just remember
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
)
1284 /* Track mouse movement. */
1285 register_mtrack (mouse_in_pico
);
1287 lheader
= FALSE
; /* Just to be sure. */
1293 /* Mouse tracking. */
1294 if (lheader
) /* Ignore mouse movement in header. */
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. */
1301 trow
= (Pmaster
) ? ComposerTopLine
: 2;
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);
1315 /* Move dot to target column. */
1316 move_dot_to (trow
, tcol
);
1318 /* Update screen. */
1324 if(button
== M_BUTTON_RIGHT
){
1330 else if(button
!= M_BUTTON_LEFT
)
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. */
1344 rv
= (unsigned long) KEY_MOUSE
;
1348 /* If up at same place, clear mark */
1349 if (curwp
->w_markp
== curwp
->w_dotp
&&
1350 curwp
->w_marko
== curwp
->w_doto
) {
1352 curwp
->w_flag
|= WFMOVE
;
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.
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 */
1388 /* Mouse down does not mean anything, just keep track of
1389 * where it went down and if this is a double click. */
1391 #ifdef CLOCKS_PER_SEC
1392 doubleclick
= (lrow
== row
&& lcol
== col
1393 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1396 doubleclick
= (lrow
== row
&& lcol
== col
1397 && clock() < (lastcalled
+ CLK_TCK
/2));
1399 doubleclick
= FALSE
;
1402 lastcalled
= clock();
1404 doubleclick
= (lrow
== row
&& lcol
== col
1405 && time(0) < (lastcalled
+ 2));
1406 lastcalled
= time(0);
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
) {
1435 * mouse_get_last - Get last mouse event.
1439 mouse_get_last(mousehandler_t
*f
, MOUSEPRESS
*mp
)
1444 mp
->mevent
= levent
;
1447 mp
->doubleclick
= doubleclick
;
1448 mp
->button
= lbutton
;
1456 * register_key - register the given keystroke to accept mouse events
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
)
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
);
1481 menuitems
[i
].kncp
= new_color_pair(kn
->fg
, kn
->bg
);
1483 menuitems
[i
].kncp
= NULL
;
1485 menuitems
[i
].klcp
= new_color_pair(kl
->fg
, kl
->bg
);
1487 menuitems
[i
].klcp
= NULL
;
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
)
1506 for(i
= 0; i
< 12; i
++)
1507 if(M_ACTIVE(row
, col
, &menuitems
[i
]))
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
{
1532 #define PT(X) ((PICOTEXT *)(X))
1535 * pico_get - return window struct pointer used as a handle
1536 * to the other pico_xxx routines.
1541 PICOTEXT
*wp
= NULL
;
1544 if((wp
= (PICOTEXT
*)malloc(sizeof(PICOTEXT
))) != NULL
){
1546 if((lp
= lalloc(0)) == NULL
){
1551 wp
->dotp
= wp
->linep
= lp
->l_fp
= lp
->l_bp
= lp
;
1555 emlwrite("Can't allocate space for text", NULL
);
1561 * pico_give - free resources and give up picotext struct
1569 fp
= lforw(PT(w
)->linep
);
1570 while((lp
= fp
) != 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
)
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
){
1599 if(obufpnext
>= obufpend
){
1608 if(PT(w
)->crinread
){
1609 *c
= '\012'; /* return LF */
1610 PT(w
)->crinread
= 0;
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
;
1620 ucs
= lgetc(PT(w
)->dotp
, (PT(w
)->doto
)++).c
;
1621 obufpend
= utf8_put(obuf
, (unsigned long) ucs
);
1623 if(obufpend
> obuf
){
1625 if(obufpnext
>= obufpend
){
1634 else if(PT(w
)->dotp
!= PT(w
)->linep
){ /* return line break */
1635 PT(w
)->dotp
= lforw(PT(w
)->dotp
);
1637 #if defined(DOS) || defined(OS2)
1641 *c
= '\012'; /* return local eol! */
1644 } /* else no chars to return */
1651 * pico_writec - write a char into picotext and advance pointers.
1652 * Up to calling routines to keep track of total chars
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
)
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
;
1678 if((lp
= lalloc(0)) == NULL
){
1679 emlwrite("Can't allocate space for more characters",NULL
);
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
;
1689 lforw(lp
) = lforw(PT(w
)->dotp
);
1690 lback(lp
) = PT(w
)->dotp
;
1691 lback(lforw(lp
)) = lforw(PT(w
)->dotp
) = lp
;
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
);
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
;
1721 *cbufp
++ = (unsigned char) c
;
1723 remaining_octets
= (cbufp
- cbuf
) * sizeof(unsigned char);
1724 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
1727 case U8G_ENDSTRG
: /* incomplete character, wait */
1728 case U8G_ENDSTRI
: /* incomplete character, wait */
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
++] = '?';
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
++] = '?';
1764 * A regular ucs character
1766 obuf
[outchars
++] = ucs
;
1769 /* update the input buffer */
1770 if(inputp
>= cbufp
) /* this should be the case */
1772 else{ /* extra chars for some reason? */
1773 unsigned char *q
, *newcbufp
;
1775 newcbufp
= (cbufp
- inputp
) + cbuf
;
1777 while(inputp
< cbufp
)
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
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
))
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
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
)
1825 while(rv
&& *s
!= '\0')
1826 if(!pico_writec(w
, (int)*s
++, flags
))
1830 return((rv
) ? 1 : 0);
1835 * pico_seek - position dotp and dot at requested location
1838 pico_seek(void *w
, long offset
, int orig
)
1842 PT(w
)->crinread
= 0;
1844 case 0 : /* SEEK_SET */
1845 PT(w
)->dotp
= lforw(PT(w
)->linep
);
1847 case 1 : /* SEEK_CUR */
1849 while(lp
!= PT(w
)->linep
){
1850 if(offset
<= llength(lp
)){
1851 PT(w
)->doto
= (int)offset
;
1856 offset
-= ((long)llength(lp
)
1857 #if defined(DOS) || defined(OS2)
1866 case 2 : /* SEEK_END */
1867 PT(w
)->dotp
= lback(PT(w
)->linep
);
1868 PT(w
)->doto
= llength(PT(w
)->dotp
);
1879 * breplace - replace the current window's text with the given
1888 fp
= lforw(curbp
->b_linep
);
1889 while((lp
= fp
) != curbp
->b_linep
){ /* blast old lines */
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
)){
1912 curwp
->w_linep
= lforw(curbp
->b_linep
);
1913 curwp
->w_dotp
= lforw(curbp
->b_linep
);
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
;
1927 free_pico_module_globals(void)
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
;
1952 composer_file_drop(int x
, int y
, char *filename
)
1957 if((ComposerTopLine
> 2 && x
<= ComposerTopLine
)
1958 || !LikelyASCII(filename
)){
1959 AppendAttachment(filename
, NULL
, NULL
);
1965 swapimark(FALSE
, 1);
1968 if(ComposerEditing
){ /* update display */
1978 emlwrite(_("Attached dropped file \"%s\""), &eml
);
1980 emlwrite(_("Inserted dropped file \"%s\""), &eml
);
1982 if(ComposerEditing
){ /* restore cursor */
1983 HeaderPaintCursor();
1986 curwp
->w_flag
|= WFHARD
;
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 */