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-2014 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"
76 void breplace(void *w
);
77 int any_header_changes(void);
78 int cleanwhitespace(void);
79 int isquotedspace(LINE
*);
83 * function key mappings
85 static UCS pfkm
[12][2] = {
106 * flag for the various functions in pico() to set when ready
107 * for pico() to return...
109 int pico_all_done
= 0;
111 UCS
*pico_anchor
= NULL
;
112 extern struct headerentry
*headents
;
115 * pico - the main routine for Pine's composer.
124 char bname
[NBUFN
]; /* buffer name of file to read */
125 extern struct on_display ods
;
126 int checkpointcnt
= 0, input
= 0;
128 char chkptfile
[NLINE
];
135 gmode
|= pm
->pine_flags
; /* high 4 bits rsv'd for pine */
137 alt_speller
= pm
->alt_spell
;
141 if(!vtinit()) /* Init Displays. */
144 strncpy(bname
, "main", sizeof(bname
)); /* default buffer name */
145 bname
[sizeof(bname
)-1] = '\0';
146 edinit(bname
); /* Buffers, windows. */
148 if(InitMailHeader(pm
)) /* init mail header structure */
149 gmode
&= ~(P_BODY
| P_HEADEND
); /* flip off special header stuff */
151 /* setup to process commands */
152 lastflag
= 0; /* Fake last flags. */
153 curbp
->b_mode
|= gmode
; /* and set default modes*/
155 if(Pmaster
->pine_anchor
)
156 pico_anchor
= utf8_to_ucs4_cpystr(Pmaster
->pine_anchor
);
160 if(Pmaster
->quote_str
)
161 glo_quote_str
= utf8_to_ucs4_cpystr(Pmaster
->quote_str
);
163 glo_quote_str
= NULL
;
165 if(Pmaster
->wordseps
)
166 glo_wordseps
= ucs4_cpystr(Pmaster
->wordseps
);
170 bindtokey(DEL
, (gmode
& P_DELRUBS
) ? forwdel
: backdel
);
173 breplace(pm
->msgtext
);
176 cursor_shown
= mswin_showcaret(1); /* turn on for main window */
177 mswin_allowpaste(MSWIN_PASTE_FULL
);
178 mswin_setscrollcallback (pico_scroll_callback
);
181 /* prepare for checkpointing */
183 chkptinit((*Pmaster
->ckptdir
)(chkptfile
, sizeof(chkptfile
)), sizeof(chkptfile
));
184 if(gmode
& P_CHKPTNOW
)
185 writeout(chkptfile
, TRUE
);
187 pico_all_done
= setjmp(finstate
); /* jump out of HUP handler ? */
189 if(gmode
& MDALTNOW
){
190 while(!pico_all_done
){
191 if(((gmode
& P_BODY
) || !Pmaster
->headents
)
192 && alt_editor(0, 1) < 0)
193 break; /* if problem, drop into pico */
195 if(Pmaster
->headents
){
196 update(); /* paint screen, n' start editing... */
197 HeaderEditor((gmode
& (P_HEADEND
| P_BODY
)) ? 2 : 0, 0);
198 gmode
|= P_BODY
; /* make sure we enter alt ed next */
201 pico_all_done
= COMP_EXIT
;
204 else if(!pico_all_done
){
205 if(gmode
& P_BODY
){ /* begin editing the header? */
206 ArrangeHeader(); /* line up pointers */
208 * Move to the offset pine asked us to move to.
209 * Perhaps we should be checking to see if this is
210 * a reasonable number before moving.
212 if(Pmaster
&& Pmaster
->edit_offset
)
213 forwchar(FALSE
, Pmaster
->edit_offset
);
216 update(); /* paint screen, */
217 HeaderEditor((gmode
& P_HEADEND
) ? 2 : 0, 0);
227 mswin_allowpaste(MSWIN_PASTE_DISABLE
);
228 mswin_setscrollcallback (NULL
);
230 ret
= anycb() ? BUF_CHANGED
: 0;
231 switch(pico_all_done
){ /* prepare for/handle final events */
232 case COMP_EXIT
: /* already confirmed */
235 && (Pmaster
->strip_ws_before_send
236 || Pmaster
->allow_flowed_text
))
241 case COMP_CANCEL
: /* also already confirmed */
248 * pack up and let caller know that we've received a SIGHUP
250 if(ComposerEditing
) /* expand addr if needed */
251 call_builder(&headents
[ods
.cur_e
], NULL
, NULL
);
258 default: /* safest if internal error */
260 * If we're in the headers mark the current header line
261 * with start_here bit so caller knows where to reset.
262 * Also set the edit_offset, which is either the offset
263 * into this header line or the offset into the body.
264 * Packheader will adjust edit_offset for multi-line
267 if(ComposerEditing
){ /* in the headers */
268 headents
[ods
.cur_e
].start_here
= 1;
269 Pmaster
->edit_offset
= ods
.p_ind
;
273 register long offset
;
275 for(clp
= lforw(curbp
->b_linep
), offset
= 0L;
276 clp
!= curwp
->w_dotp
;
278 offset
+= (llength(clp
) + 1);
280 Pmaster
->edit_offset
= offset
+ curwp
->w_doto
;
289 fs_give((void **) &pico_anchor
);
291 fs_give((void **) &glo_quote_str
);
293 fs_give((void **) &glo_wordseps
);
295 vttidy(); /* clean up tty modes */
296 zotdisplay(); /* blast display buffers */
298 our_unlink(chkptfile
);
299 Pmaster
= NULL
; /* blat global */
306 if(km_popped
== 0) /* cause bottom three lines to be repainted */
307 curwp
->w_flag
|= WFHARD
;
310 if(km_popped
){ /* temporarily change to cause menu to be painted */
312 curwp
->w_ntrows
-= 2;
313 curwp
->w_flag
|= WFMODE
;
314 movecursor(term
.t_nrow
-2, 0); /* clear status line, too */
318 update(); /* Fix up the screen */
321 curwp
->w_ntrows
+= 2;
326 /* New mouse function for real mouse text seletion. */
327 register_mfunc(mouse_in_pico
, 2, 0, term
.t_nrow
- (term
.t_mrow
+1),
330 mouse_in_content(KEY_MOUSE
, -1, -1, -1, 0);
331 register_mfunc(mouse_in_content
, 2, 0, term
.t_nrow
- (term
.t_mrow
+ 1),
336 mswin_setdndcallback (composer_file_drop
);
337 mswin_mousetrackcallback(pico_cursor
);
340 if (term
.t_nrow
< 6 && c
!= NODATA
){
342 emlwrite(_("Please make the screen bigger."), NULL
);
348 clear_mfunc(mouse_in_pico
);
350 clear_mfunc(mouse_in_content
);
354 mswin_cleardndcallback ();
355 mswin_mousetrackcallback(NULL
);
357 if(c
== NODATA
|| time_to_check()){ /* new mail ? */
358 if((*Pmaster
->newmail
)(c
== NODATA
? 0 : 2, 1) >= 0){
363 curwp
->w_ntrows
-= 2;
364 curwp
->w_flag
|= WFHARD
;
370 rv
= (*Pmaster
->showmsg
)(c
);
372 picosigs(); /* restore altered handlers */
373 if(rv
) /* Did showmsg corrupt the display? */
374 PaintBody(0); /* Yes, repaint */
396 if(c
== NODATA
) /* no op, getkey timed out */
399 (*Pmaster
->keybinput
)();
401 if (mpresf
!= FALSE
) { /* message stay around only */
402 if (mpresf
++ > NMMESSDELAY
) /* so long! */
406 f
= FALSE
; /* vestigial */
409 execute(normalize_cmd(c
, pfkm
, 2), f
, n
);
410 if(++checkpointcnt
>= CHKPTDELAY
){
412 writeout(chkptfile
, TRUE
);
418 * Initialize all of the buffers and windows. The buffer name is passed down
419 * as an argument, because the main routine may have been told to read in a
420 * file by default, and we want the buffer name to be right.
424 * For the pine composer, we don't want to take over the whole screen
425 * for editing. the first some odd lines are to be used for message
426 * header information editing.
437 bp
= bfind(bname
, TRUE
, BFWRAPOPEN
); /* First buffer */
438 wp
= (WINDOW
*) malloc(sizeof(WINDOW
)); /* First window */
440 if (bp
==NULL
|| wp
==NULL
){
447 curbp
= bp
; /* Make this current */
450 wp
->w_wndp
= NULL
; /* Initialize window */
452 bp
->b_nwnd
= 1; /* Displayed. */
453 wp
->w_linep
= bp
->b_linep
;
454 wp
->w_dotp
= bp
->b_linep
;
456 wp
->w_markp
= wp
->w_imarkp
= NULL
;
457 wp
->w_marko
= wp
->w_imarko
= 0;
461 term
.t_mrow
= Pmaster
->menu_rows
;
462 wp
->w_toprow
= ComposerTopLine
= COMPOSER_TOP_LINE
;
463 wp
->w_ntrows
= term
.t_nrow
- COMPOSER_TOP_LINE
- term
.t_mrow
;
464 fillcol
= Pmaster
->fillcolumn
;
466 (Pmaster
->oper_dir
&& strlen(Pmaster
->oper_dir
) < NLINE
)
467 ? Pmaster
->oper_dir
: "", sizeof(opertree
));
468 opertree
[sizeof(opertree
)-1] = '\0';
469 input_cs
= Pmaster
->input_cs
;
478 wp
->w_ntrows
= term
.t_nrow
- 2 - term
.t_mrow
;
479 if(userfillcol
> 0) /* set fill column */
480 fillcol
= userfillcol
;
482 fillcol
= term
.t_ncol
- 6;
486 * MDSCUR mode implies MDTREE mode with a opertree of home directory,
487 * unless opertree has been set differently.
489 if((gmode
& MDSCUR
) && !opertree
[0]){
490 strncpy(opertree
, gethomedir(NULL
), sizeof(opertree
));
491 opertree
[sizeof(opertree
)-1] = '\0';
495 fixpath(opertree
, sizeof(opertree
));
498 wp
->w_flag
= WFMODE
|WFHARD
; /* Full. */
503 * This is the general command execution routine. It handles the fake binding
504 * of all the keys to "self-insert". It also clears out the "thisflag" word,
505 * and arranges to move it to the "lastflag", so that the next command can
506 * look at it. Return the status of command.
509 execute(UCS c
, int f
, int n
)
514 ktp
= (Pmaster
) ? &keytab
[0] : &pkeytab
[0];
516 while (ktp
->k_fp
!= NULL
) {
517 if (ktp
->k_code
== c
) {
520 curwp
->w_flag
|= WFMODE
;
526 status
= (*ktp
->k_fp
)(f
, n
);
527 if((lastflag
& CFFILL
) && !(thisflag
& CFFILL
))
529 if((lastflag
& CFFLBF
) && !(thisflag
& CFFLBF
))
535 * Reset flag saying wrap should open a new line whenever
536 * we execute a command (as opposed to just typing in text).
537 * However, if that command leaves us in the same line on the
538 * screen, then don't reset.
540 if(curwp
->w_flag
& (WFMOVE
| WFHARD
))
541 curbp
->b_flag
|= BFWRAPOPEN
; /* wrap should open new line */
548 if(lastflag
& CFFILL
) /* blat unusable fill data */
550 if(lastflag
& CFFLBF
)
553 if (VALID_KEY(c
)) { /* Self inserting. */
555 if (n
<= 0) { /* Fenceposts. */
557 return (n
<0 ? FALSE
: TRUE
);
559 thisflag
= 0; /* For the future. */
561 /* do the appropriate insertion */
562 /* pico never does C mode, this is simple */
563 status
= linsert(n
, c
);
566 * Check to make sure we didn't go off of the screen
567 * with that character. Take into account tab expansion.
568 * If so wrap the line...
570 if(curwp
->w_bufp
->b_mode
& MDWRAP
){
574 for(j
= 0; j
< llength(curwp
->w_dotp
); j
++)
575 if(ucs4_isspace(lgetc(curwp
->w_dotp
, j
).c
)){
576 if(lgetc(curwp
->w_dotp
, j
).c
== TAB
){
585 ww
= wcellwidth((UCS
) lgetc(curwp
->w_dotp
, j
).c
);
586 wid
+= (ww
>= 0 ? ww
: 1);
600 lastflag
= 0; /* Fake last flags. */
607 * Fancy quit command, as implemented by Norm. If the any buffer has
608 * changed do a write on that buffer and exit emacs, otherwise simply exit.
611 quickexit(int f
, int n
)
613 register BUFFER
*bp
; /* scanning pointer to buffers */
617 if ((bp
->b_flag
&BFCHG
) != 0 /* Changed. */
618 && (bp
->b_flag
&BFTEMP
) == 0) { /* Real. */
619 curbp
= bp
; /* make that buffer cur */
622 bp
= bp
->b_bufp
; /* on to the next buffer */
624 return(wquit(f
, n
)); /* conditionally quit */
630 * abort_composer - ask the question here, then go quit or
634 abort_composer(int f
, int n
)
640 Pmaster
->arm_winch_cleanup
++;
641 if(Pmaster
->canceltest
){
642 if(((Pmaster
->pine_flags
& MDHDRONLY
) && !any_header_changes())
643 || (result
= (*Pmaster
->canceltest
)(redraw_pico_for_callback
))){
644 pico_all_done
= COMP_CANCEL
;
645 emlwrite(result
, NULL
);
646 Pmaster
->arm_winch_cleanup
--;
650 /* TRANSLATORS: The user typed the Cancel command and was
651 asked to confirm that. Instead they canceled the cancel
653 emlwrite(_("Cancel Cancelled"), NULL
);
654 curwp
->w_flag
|= WFMODE
; /* and modeline so we */
655 sgarbk
= TRUE
; /* redraw the keymenu */
656 pclear(term
.t_nrow
-1, term
.t_nrow
);
657 Pmaster
->arm_winch_cleanup
--;
661 else switch(mlyesno_utf8(Pmaster
->headents
662 ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
664 ? _("Cancel Edit (and abandon changes)")
668 pico_all_done
= COMP_CANCEL
;
672 emlwrite(_("\007Cancel Cancelled"), NULL
);
683 * suspend_composer - return to pine with what's been edited so far
686 suspend_composer(int f
, int n
)
688 if(Pmaster
&& Pmaster
->headents
)
689 pico_all_done
= COMP_SUSPEND
;
699 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
700 * has been changed and not written out. Normally bound to "C-X C-C".
711 /* First, make sure there are no outstanding problems */
713 emlwrite(_("\007Problem with attachments! Fix errors or delete attachments."), NULL
);
718 if(Pmaster
->always_spell_check
)
719 if(spell(0, 0) == -1)
720 sleep(3); /* problem, show error */
723 * if we're not in header, show some of it as we verify sending...
727 Pmaster
->arm_winch_cleanup
++;
728 if((!(Pmaster
->pine_flags
& MDHDRONLY
) || any_header_changes())
729 && (ret
= (*Pmaster
->exittest
)(Pmaster
->headents
,
730 redraw_pico_for_callback
,
731 Pmaster
->allow_flowed_text
,
733 Pmaster
->arm_winch_cleanup
--;
736 pico_all_done
= COMP_CANCEL
;
742 lchange(WFHARD
); /* set update flags... */
743 curwp
->w_flag
|= WFMODE
; /* and modeline so we */
744 sgarbk
= TRUE
; /* redraw the keymenu */
745 pclear(term
.t_nrow
-2, term
.t_nrow
);
748 if(result
&& *result
)
749 emlwrite(result
, NULL
);
752 Pmaster
->arm_winch_cleanup
--;
753 pico_all_done
= COMP_EXIT
;
758 if (f
!= FALSE
/* Argument forces it. */
759 || anycb() == FALSE
/* All buffers clean. */
760 /* User says it's OK. */
761 /* TRANSLATORS: buffer is the in-memory copy of a file */
762 || (s
=mlyesno_utf8(_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"), -1)) == FALSE
) {
764 #if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
771 if(filewrite(0,1) == TRUE
)
775 emlwrite(_("Exit cancelled"), NULL
);
777 curwp
->w_flag
|= WFHARD
; /* cause bottom 3 lines to paint */
787 * Has any editing been done to headers?
790 any_header_changes(void)
792 struct headerentry
*he
;
794 for(he
= Pmaster
->headents
; he
->name
!= NULL
; he
++)
798 return(he
->name
&& he
->dirty
);
802 isquotedspace(LINE
*line
)
804 int i
, was_quote
= 0;
805 for(i
= 0; i
< llength(line
); i
++){
806 if(lgetc(line
, i
).c
== '>')
808 else if(was_quote
&& lgetc(line
, i
).c
== ' '){
809 if(i
+1 < llength(line
) && ucs4_isspace(lgetc(line
,i
+1).c
))
821 * This function serves two purposes, 1) to strip white space when
822 * Pmaster asks that the composition have its trailing white space
823 * stripped, or 2) to prepare the text as flowed text, as Pmaster
824 * is telling us that we're working with flowed text.
826 * What flowed currently means to us is stripping all trailing white
827 * space, except for one space if the following line is a continuation
828 * of the paragraph. Also, we space-stuff all lines beginning
829 * with white-space, and leave siglines alone.
832 cleanwhitespace(void)
834 LINE
*cursor_dotp
= NULL
, **lp
= NULL
;
835 int i
= 0, cursor_doto
= 0, is_cursor_line
= 0;
836 int do_space_stuffing
= 0;
838 if(Pmaster
&& Pmaster
->allow_flowed_text
&& !(*Pmaster
->user_says_noflow
)())
841 cursor_dotp
= curwp
->w_dotp
;
842 cursor_doto
= curwp
->w_doto
;
845 for(lp
= &curwp
->w_dotp
; (*lp
) != curbp
->b_linep
; (*lp
) = lforw(*lp
)){
846 if(!(llength(*lp
) == 3
847 && lgetc(*lp
, 0).c
== '-'
848 && lgetc(*lp
, 1).c
== '-'
849 && lgetc(*lp
, 2).c
== ' ')
851 is_cursor_line
= (cursor_dotp
== (*lp
));
852 /* trim trailing whitespace, to be added back if flowing */
853 for(i
= llength(*lp
); i
; i
--)
854 if(!ucs4_isspace(lgetc(*lp
, i
- 1).c
))
856 if(i
!= llength(*lp
)){
859 if(Pmaster
&& !Pmaster
->strip_ws_before_send
860 && lforw(*lp
) != curbp
->b_linep
861 && llength(lforw(*lp
))
862 && !(ucs4_isspace(lgetc(lforw(*lp
), 0).c
)
863 || isquotedspace(lforw(*lp
)))
864 && !(llength(lforw(*lp
)) == 3
865 && lgetc(lforw(*lp
), 0).c
== '-'
866 && lgetc(lforw(*lp
), 1).c
== '-'
867 && lgetc(lforw(*lp
), 2).c
== ' '))
869 if(flow_line
&& i
&& lgetc(*lp
, i
).c
== ' '){
870 /* flowed line ending with space */
872 if(i
!= llength(*lp
)){
874 ldelete(llength(*lp
) - i
, NULL
);
877 else if(flow_line
&& i
&& ucs4_isspace(lgetc(*lp
, i
).c
)){
878 /* flowed line ending with whitespace other than space*/
880 ldelete(llength(*lp
) - i
, NULL
);
885 ldelete(llength(*lp
) - i
, NULL
);
888 if(do_space_stuffing
&& llength(*lp
) && ucs4_isspace(lgetc(*lp
, 0).c
)){
889 /* space-stuff only if flowed */
891 Pmaster
->space_stuffed
= 1;
893 if(is_cursor_line
&& cursor_doto
)
902 /* put the cursor back where we found it */
904 curwp
->w_dotp
= cursor_dotp
;
905 curwp
->w_doto
= (cursor_doto
< llength(curwp
->w_dotp
))
906 ? cursor_doto
: llength(curwp
->w_dotp
) - 1;
912 * Remove all trailing white space from the text
915 stripwhitespace(void)
918 LINE
*cur_line
= lforw(curbp
->b_linep
);
921 /* we gotta test for the sigdash case here */
922 if(!(cur_line
->l_used
== 3 &&
923 lgetc(cur_line
, 0).c
== '-' &&
924 lgetc(cur_line
, 1).c
== '-' &&
925 lgetc(cur_line
, 2).c
== ' '))
926 for(i
= cur_line
->l_used
- 1; i
>= 0; i
--)
927 if(ucs4_isspace(lgetc(cur_line
, i
).c
))
931 }while((cur_line
= lforw(cur_line
)) != curbp
->b_linep
);
937 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
938 * Sometimes called as a routine, to do general aborting of stuff.
943 emlwrite(_("Cancelled"), NULL
);
948 /* tell the user that this command is illegal while we are in
949 * VIEW (read-only) mode
955 emlwrite("Key illegal in VIEW mode", NULL
);
962 * reset all globals to their initial values
972 * re-initialize global buffer type variables ....
974 fillcol
= (term
.t_ncol
> 80) ? 77 : term
.t_ncol
- 6;
978 ComposerEditing
= FALSE
;
981 * re-initialize hardware display variables ....
983 vtrow
= vtcol
= lbound
= 0;
986 pat
[0] = rpat
[0] = '\0';
987 browse_dir
[0] = '\0';
992 * pico_help - help function for standalone composer
993 * Oops - looks like utf8title is unused!
995 * This should be fixed to handle TAB characters.
998 pico_help(char *utf8text
[], char *utf8title
, int i
)
1000 register int numline
= 0;
1007 return(wscrollw(COMPOSER_TOP_LINE
, term
.t_nrow
-1, utf8text
, numline
));
1013 * zotedit() - kills the buffer and frees all lines associated with it!!!
1018 wheadp
->w_linep
= wheadp
->w_dotp
= wheadp
->w_markp
= wheadp
->w_imarkp
= NULL
;
1019 bheadp
->b_linep
= bheadp
->b_dotp
= bheadp
->b_markp
= NULL
;
1021 free((char *) wheadp
); /* clean up window */
1025 free((char *) bheadp
); /* clean up buffers */
1029 zotheader(); /* blast header lines */
1031 kdelete(); /* blast kill buffer */
1038 * Generic mouse handling functions
1040 MENUITEM menuitems
[12]; /* key labels and functions */
1041 MENUITEM
*mfunc
= NULL
; /* list of regional functions */
1042 mousehandler_t mtrack
; /* mouse tracking handler */
1044 /* last mouse position */
1045 static unsigned long levent
= 0L;
1046 static int lrow
= 0, lcol
= 0, doubleclick
, lbutton
, lflags
;
1048 static clock_t lastcalled
= 0;
1050 static time_t lastcalled
= 0;
1052 static mousehandler_t lastf
;
1056 * register_mfunc - register the given function to get called
1057 * on mouse events in the given display region
1060 register_mfunc(mousehandler_t f
, int tlr
, int tlc
, int brr
, int brc
)
1067 for(mp
= &mfunc
; *mp
; mp
= &(*mp
)->next
)
1070 *mp
= (MENUITEM
*)malloc(sizeof(MENUITEM
));
1071 memset(*mp
, 0, sizeof(MENUITEM
));
1078 (*mp
)->lbl
.c
= (*mp
)->lbl
.r
= 0;
1085 * clear_mfunc - clear any previously set mouse function
1088 clear_mfunc(mousehandler_t f
)
1092 if((mp
= mfunc
) != NULL
){
1096 for(tp
= mp
; tp
->next
; tp
= tp
->next
)
1097 if(tp
->next
->action
== f
){
1099 tp
->next
= tp
->next
->next
;
1118 mswin_allowmousetrack (FALSE
);
1123 register_mtrack(mousehandler_t f
)
1127 mswin_allowmousetrack (TRUE
);
1135 move_dot_to(int row
, int col
)
1140 lp
= curwp
->w_linep
;
1141 i
= row
- ((Pmaster
) ? ComposerTopLine
: 2);
1142 while(i
-- && lp
!= curbp
->b_linep
) /* count from top */
1145 curwp
->w_dotp
= lp
; /* to new dot. */
1146 curwp
->w_doto
= getgoal(lp
);
1147 curwp
->w_flag
|= WFMOVE
;
1154 * When the mouse goes down in the body we set the mark and start
1157 * As the mouse moves we update the dot and redraw the screen.
1159 * If the mouse moves above or below the pico body region of the
1160 * screen we scroll the text and update the dot position.
1162 * When the mouse comes up we clean up. If the mouse did not
1163 * move, then we clear the mark and turn off the selection.
1165 * Most of the mouse processing is handled here. The exception is
1166 * mouse down in the header. Can't call HeaderEditor() from here so
1167 * we send up the KEY_MOUSE character, which gets dispatched to
1168 * mousepress(), which _can_ call HeaderEditor().
1171 mouse_in_pico(unsigned long mevent
, int row
, int col
, int button
, int flags
)
1173 unsigned long rv
= 0; /* Our return value. */
1174 int trow
, tcol
; /* translated row and col. */
1176 static int lheader
= FALSE
; /* Mouse down was in header. */
1184 if(button
!= M_BUTTON_LEFT
)
1187 /* Ignore mouse down if not in pico body region. */
1188 if (row
< 2 || row
> term
.t_nrow
- (term
.t_mrow
+1)) {
1193 /* Detect double clicks. Not that we do anything with em, just
1196 #ifdef CLOCKS_PER_SEC
1197 doubleclick
= (lrow
== row
&& lcol
== col
1198 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1201 doubleclick
= (lrow
== row
&& lcol
== col
1202 && clock() < (lastcalled
+ CLK_TCK
/2));
1204 doubleclick
= FALSE
;
1207 lastcalled
= clock();
1209 doubleclick
= (lrow
== row
&& lcol
== col
1210 && time(0) < (lastcalled
+ 2));
1211 lastcalled
= time(0);
1213 lheader
= FALSE
; /* Rember mouse down position. */
1220 /* Mouse down in body? */
1221 if (row
< (Pmaster
? ComposerTopLine
: 2)) {
1222 /* Mouse down in message header -> no tracking, just remember
1227 /* Mouse down in message.
1228 * If no shift key and an existing mark -> clear the mark.
1229 * If shift key and no existing mark -> set mark before moving */
1230 if (!(flags
& M_KEY_SHIFT
) && curwp
->w_markp
)
1231 setmark (0,1); /* this clears the mark. */
1232 else if (flags
& M_KEY_SHIFT
&& !curwp
->w_markp
)
1233 setmark (0,1); /* while this sets the mark. */
1235 /* Reposition dot to mouse down. */
1236 move_dot_to (row
, col
);
1238 /* Set the mark to dot if no existing mark. */
1239 if (curwp
->w_markp
== NULL
)
1242 /* Track mouse movement. */
1243 register_mtrack (mouse_in_pico
);
1245 lheader
= FALSE
; /* Just to be sure. */
1251 /* Mouse tracking. */
1252 if (lheader
) /* Ignore mouse movement in header. */
1255 /* If above or below body, scroll body and adjust the row and col. */
1256 if (row
< (Pmaster
? ComposerTopLine
: 2)) {
1257 /* Scroll text down screen and move dot to top left corner. */
1259 trow
= (Pmaster
) ? ComposerTopLine
: 2;
1262 else if (row
> term
.t_nrow
- (term
.t_mrow
+ 1)) {
1263 /* Scroll text up screen and move dot to bottom right corner. */
1264 scrolldownline (0,1);
1265 trow
= term
.t_nrow
- (term
.t_mrow
+ 1);
1273 /* Move dot to target column. */
1274 move_dot_to (trow
, tcol
);
1276 /* Update screen. */
1282 if(button
== M_BUTTON_RIGHT
){
1288 else if(button
!= M_BUTTON_LEFT
)
1293 /* Last down in header. */
1294 if (row
== lrow
&& col
== lcol
) {
1295 /* Mouse up and down in same place in header. Means the
1296 * user want to edit the header. Return KEY_MOUSE which
1297 * will cause mousepress to be called, which will
1298 * call HeaderEditor. Can't call HeaderEditor from here
1299 * because that would mess up layering. */
1302 rv
= (unsigned long) KEY_MOUSE
;
1306 /* If up at same place, clear mark */
1307 if (curwp
->w_markp
== curwp
->w_dotp
&&
1308 curwp
->w_marko
== curwp
->w_doto
) {
1310 curwp
->w_flag
|= WFMOVE
;
1325 * mouse_in_content - general mechanism used to pass recognized mouse
1326 * events in predefined region back thru the usual
1327 * keyboard input stream. The actual return value
1328 * passed back from this function is set dynamically
1329 * via the "down" argument which is read when both the
1330 * "row" and "col" arguments are negative.
1333 mouse_in_content(unsigned long mevent
, int row
, int col
, int button
, int flags
)
1335 unsigned long rv
= 0;
1336 static unsigned long mouse_val
= KEY_MOUSE
;
1338 if(row
== -1 && col
== -1){
1339 mouse_val
= mevent
; /* setting return value */
1346 /* Mouse down does not mean anything, just keep track of
1347 * where it went down and if this is a double click. */
1349 #ifdef CLOCKS_PER_SEC
1350 doubleclick
= (lrow
== row
&& lcol
== col
1351 && clock() < (lastcalled
+ CLOCKS_PER_SEC
/2));
1354 doubleclick
= (lrow
== row
&& lcol
== col
1355 && clock() < (lastcalled
+ CLK_TCK
/2));
1357 doubleclick
= FALSE
;
1360 lastcalled
= clock();
1362 doubleclick
= (lrow
== row
&& lcol
== col
1363 && time(0) < (lastcalled
+ 2));
1364 lastcalled
= time(0);
1373 /* Mouse up. If in the same position as it went down
1374 * then we return the value set above, which goes into
1375 * the character input stream, which gets processed as
1376 * a mouse event by some upper layer, which calls to
1377 * mouse_get_last(). */
1378 if (lrow
== row
&& lcol
== col
) {
1393 * mouse_get_last - Get last mouse event.
1397 mouse_get_last(mousehandler_t
*f
, MOUSEPRESS
*mp
)
1402 mp
->mevent
= levent
;
1405 mp
->doubleclick
= doubleclick
;
1406 mp
->button
= lbutton
;
1414 * register_key - register the given keystroke to accept mouse events
1417 register_key(int i
, unsigned rval
, char *label
, void (*label_printer
)(),
1418 int row
, int col
, int len
, COLOR_PAIR
*kn
, COLOR_PAIR
*kl
)
1423 menuitems
[i
].val
= rval
;
1424 menuitems
[i
].tl
.r
= menuitems
[i
].br
.r
= row
;
1425 menuitems
[i
].tl
.c
= col
;
1426 menuitems
[i
].br
.c
= col
+ len
;
1427 menuitems
[i
].lbl
.r
= menuitems
[i
].tl
.r
;
1428 menuitems
[i
].lbl
.c
= menuitems
[i
].tl
.c
;
1429 menuitems
[i
].label_hiliter
= label_printer
;
1430 if(menuitems
[i
].label
){
1431 free(menuitems
[i
].label
);
1432 menuitems
[i
].label
= NULL
;
1434 if(menuitems
[i
].kncp
)
1435 free_color_pair(&menuitems
[i
].kncp
);
1436 if(menuitems
[i
].klcp
)
1437 free_color_pair(&menuitems
[i
].klcp
);
1439 menuitems
[i
].kncp
= new_color_pair(kn
->fg
, kn
->bg
);
1441 menuitems
[i
].kncp
= NULL
;
1443 menuitems
[i
].klcp
= new_color_pair(kl
->fg
, kl
->bg
);
1445 menuitems
[i
].klcp
= NULL
;
1450 len
= strlen(label
);
1451 if((menuitems
[i
].label
= (char *)malloc((len
+1)*sizeof(char))) != NULL
){
1452 strncpy(menuitems
[i
].label
, label
, len
);
1453 menuitems
[i
].label
[len
] = '\0';
1460 mouse_on_key(int row
, int col
)
1464 for(i
= 0; i
< 12; i
++)
1465 if(M_ACTIVE(row
, col
, &menuitems
[i
]))
1474 * Below are functions for use outside pico to manipulate text
1475 * in a pico's native format (circular linked list of lines).
1477 * The idea is to streamline pico use by making it fairly easy
1478 * for outside programs to prepare text intended for pico's use.
1479 * The simple char * alternative is messy as it requires two copies
1480 * of the same text, and isn't very economic in limited memory
1481 * situations (THANKS BELLEVUE-BILLY.).
1483 typedef struct picotext
{
1490 #define PT(X) ((PICOTEXT *)(X))
1493 * pico_get - return window struct pointer used as a handle
1494 * to the other pico_xxx routines.
1499 PICOTEXT
*wp
= NULL
;
1502 if((wp
= (PICOTEXT
*)malloc(sizeof(PICOTEXT
))) != NULL
){
1504 if((lp
= lalloc(0)) == NULL
){
1509 wp
->dotp
= wp
->linep
= lp
->l_fp
= lp
->l_bp
= lp
;
1513 emlwrite("Can't allocate space for text", NULL
);
1519 * pico_give - free resources and give up picotext struct
1527 fp
= lforw(PT(w
)->linep
);
1528 while((lp
= fp
) != PT(w
)->linep
){
1533 free((PICOTEXT
*)w
);
1537 * pico_readc - return char at current point. Up to calling routines
1538 * to keep cumulative count of chars.
1539 * The characters in PT are UCS-4 characters. The caller
1540 * of pico_readc is expecting UTF-8 chars. We convert
1541 * each UCS-4 character to a string of UTF-8 characters
1542 * and return them one at a time.
1545 pico_readc(void *w
, unsigned char *c
, int flags
)
1549 static unsigned char obuf
[6];
1550 static unsigned char *obufpend
= obuf
;
1551 static unsigned char *obufpnext
= obuf
;
1553 if(!(flags
& PICOREADC_NOUCS
)){
1554 if(obufpend
> obuf
){
1557 if(obufpnext
>= obufpend
){
1566 if(PT(w
)->crinread
){
1567 *c
= '\012'; /* return LF */
1568 PT(w
)->crinread
= 0;
1571 else if(PT(w
)->doto
< llength(PT(w
)->dotp
)){ /* normal char to return */
1572 if(flags
& PICOREADC_NOUCS
){
1573 *c
= (unsigned char) lgetc(PT(w
)->dotp
, (PT(w
)->doto
)++).c
;
1578 ucs
= lgetc(PT(w
)->dotp
, (PT(w
)->doto
)++).c
;
1579 obufpend
= utf8_put(obuf
, (unsigned long) ucs
);
1581 if(obufpend
> obuf
){
1583 if(obufpnext
>= obufpend
){
1592 else if(PT(w
)->dotp
!= PT(w
)->linep
){ /* return line break */
1593 PT(w
)->dotp
= lforw(PT(w
)->dotp
);
1595 #if defined(DOS) || defined(OS2)
1599 *c
= '\012'; /* return local eol! */
1602 } /* else no chars to return */
1609 * pico_writec - write a char into picotext and advance pointers.
1610 * Up to calling routines to keep track of total chars
1612 * Incoming text (c) is UTF-8 and written chars are UCS-4.
1613 * We need to collect up multiple chars to make a single
1614 * UCS-4 char, so there needs to be some state between calls.
1617 pico_writec(void *w
, int c
, int flags
)
1621 if(c
== '\r') /* ignore CR's */
1622 rv
++; /* so fake it */
1623 else if(c
== '\n'){ /* insert newlines on LF */
1625 * OK, if there are characters on the current line or
1626 * dotp is pointing to the delimiter line, insert a newline
1627 * No here's the tricky bit; preserve the implicit EOF newline.
1629 if(lforw(PT(w
)->dotp
) == PT(w
)->linep
&& PT(w
)->dotp
!= PT(w
)->linep
){
1630 PT(w
)->dotp
= PT(w
)->linep
;
1636 if((lp
= lalloc(0)) == NULL
){
1637 emlwrite("Can't allocate space for more characters",NULL
);
1641 if(PT(w
)->dotp
== PT(w
)->linep
){
1642 lforw(lp
) = PT(w
)->linep
;
1643 lback(lp
) = lback(PT(w
)->linep
);
1644 lforw(lback(lp
)) = lback(PT(w
)->linep
) = lp
;
1647 lforw(lp
) = lforw(PT(w
)->dotp
);
1648 lback(lp
) = PT(w
)->dotp
;
1649 lback(lforw(lp
)) = lforw(PT(w
)->dotp
) = lp
;
1658 if(flags
& PICOREADC_NOUCS
){
1660 * With this flag we're reverting to the old behavior where no
1661 * UTF-8 to UCS-4 translation takes place. We assume nothing
1662 * about the incoming byte stream. We just write a byte at a
1663 * time. So even though it's being stored in PT each
1664 * item is really limited to a single octet value.
1666 rv
= geninsert(&PT(w
)->dotp
, &PT(w
)->doto
, PT(w
)->linep
, c
, 0, 1, NULL
);
1669 static unsigned char cbuf
[6];
1670 static unsigned char *cbufp
= cbuf
;
1671 UCS obuf
[MAX(MB_LEN_MAX
,32)];
1672 int i
, outchars
= 0;
1674 if(cbufp
< cbuf
+sizeof(cbuf
)){
1675 unsigned char *inputp
;
1676 unsigned long remaining_octets
;
1679 *cbufp
++ = (unsigned char) c
;
1681 remaining_octets
= (cbufp
- cbuf
) * sizeof(unsigned char);
1682 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
1685 case U8G_ENDSTRG
: /* incomplete character, wait */
1686 case U8G_ENDSTRI
: /* incomplete character, wait */
1690 if(ucs
& U8G_ERROR
|| ucs
== UBOGON
){
1692 * None of these cases is supposed to happen. If it
1693 * does happen then the input stream isn't UTF-8
1694 * so something is wrong. Treat each character in the
1695 * input buffer as a separate error character and
1696 * print a '?' for each.
1698 for(inputp
= cbuf
; inputp
< cbufp
; inputp
++)
1699 obuf
[outchars
++] = '?';
1704 /* got a character */
1705 if(ucs
>= 0x80 && wcellwidth(ucs
) < 0){
1707 * This happens when we have a UTF-8 character that
1708 * we aren't able to print in our locale. For example,
1709 * if the locale is setup with the terminal
1710 * expecting ISO-8859-1 characters then there are
1711 * lots of UTF-8 characters that can't be printed.
1712 * Print a '?' instead.
1713 This may be the wrong thing to do. What happens if user
1714 is just forwarding and doesn't edit. We are going to lose
1715 the original value, aren't we? Maybe we do this only
1716 when printing to the screen instead.
1718 obuf
[outchars
++] = '?';
1722 * A regular ucs character
1724 obuf
[outchars
++] = ucs
;
1727 /* update the input buffer */
1728 if(inputp
>= cbufp
) /* this should be the case */
1730 else{ /* extra chars for some reason? */
1731 unsigned char *q
, *newcbufp
;
1733 newcbufp
= (cbufp
- inputp
) + cbuf
;
1735 while(inputp
< cbufp
)
1748 cbufp
= cbuf
; /* start over */
1752 * Unless we have trouble translating outchars will be
1753 * either 1 or 0. It is the UCS-4 character that we converted
1754 * the input to. It's an array just so it can hold ? when
1758 for(i
= 0; rv
&& i
< outchars
; i
++)
1759 if(!geninsert(&PT(w
)->dotp
, &PT(w
)->doto
, PT(w
)->linep
, obuf
[i
], 0, 1, NULL
))
1765 * Warning, this is no longer number written, because when we have a
1766 * multibyte UTF-8 character we won't write anything until we get all
1769 return((rv
) ? 1 : 0); /* return number written */
1774 * pico_puts - just write the given string into the text
1777 pico_puts(void *w
, char *s
, int flags
)
1783 while(rv
&& *s
!= '\0')
1784 if(!pico_writec(w
, (int)*s
++, flags
))
1788 return((rv
) ? 1 : 0);
1793 * pico_seek - position dotp and dot at requested location
1796 pico_seek(void *w
, long offset
, int orig
)
1800 PT(w
)->crinread
= 0;
1802 case 0 : /* SEEK_SET */
1803 PT(w
)->dotp
= lforw(PT(w
)->linep
);
1805 case 1 : /* SEEK_CUR */
1807 while(lp
!= PT(w
)->linep
){
1808 if(offset
<= llength(lp
)){
1809 PT(w
)->doto
= (int)offset
;
1814 offset
-= ((long)llength(lp
)
1815 #if defined(DOS) || defined(OS2)
1824 case 2 : /* SEEK_END */
1825 PT(w
)->dotp
= lback(PT(w
)->linep
);
1826 PT(w
)->doto
= llength(PT(w
)->dotp
);
1837 * breplace - replace the current window's text with the given
1846 fp
= lforw(curbp
->b_linep
);
1847 while((lp
= fp
) != curbp
->b_linep
){ /* blast old lines */
1851 free(curbp
->b_linep
);
1853 curbp
->b_linep
= PT(w
)->linep
; /* arrange pointers */
1856 * Undo space-stuffing that was done when we were preparing to send.
1857 * Some error happened so we're back to the composer.
1859 if(Pmaster
&& Pmaster
->space_stuffed
){
1860 Pmaster
->space_stuffed
= 0;
1861 for(lp
= lforw(curbp
->b_linep
); lp
!= curbp
->b_linep
; lp
= lforw(lp
)){
1862 if(llength(lp
) && ucs4_isspace(lgetc(lp
, 0).c
)){
1870 curwp
->w_linep
= lforw(curbp
->b_linep
);
1871 curwp
->w_dotp
= lforw(curbp
->b_linep
);
1873 curwp
->w_markp
= curwp
->w_imarkp
= NULL
;
1874 curwp
->w_marko
= curwp
->w_imarko
= 0;
1876 curbp
->b_dotp
= curwp
->w_dotp
;
1877 curbp
->b_doto
= curbp
->b_marko
= 0;
1878 curbp
->b_markp
= NULL
;
1879 curbp
->b_linecnt
= -1;
1881 curwp
->w_flag
|= WFHARD
;
1890 composer_file_drop(int x
, int y
, char *filename
)
1895 if((ComposerTopLine
> 2 && x
<= ComposerTopLine
)
1896 || !LikelyASCII(filename
)){
1897 AppendAttachment(filename
, NULL
, NULL
);
1903 swapimark(FALSE
, 1);
1906 if(ComposerEditing
){ /* update display */
1916 emlwrite(_("Attached dropped file \"%s\""), &eml
);
1918 emlwrite(_("Inserted dropped file \"%s\""), &eml
);
1920 if(ComposerEditing
){ /* restore cursor */
1921 HeaderPaintCursor();
1924 curwp
->w_flag
|= WFHARD
;
1933 pico_cursor(int col
, long row
)
1935 return((row
> 1 && row
<= term
.t_nrow
- (term
.t_mrow
+ 1))
1936 ? MSWIN_CURSOR_IBEAM
1937 : MSWIN_CURSOR_ARROW
);
1939 #endif /* _WINDOWS */