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 * ========================================================================
14 * Program: Display functions
19 * The functions in this file handle redisplay. There are two halves, the
20 * ones that update the virtual display screen, and the ones that make the
21 * physical display screen the same as the virtual display screen. These
22 * functions use hints that are left in the windows by the commands.
26 #include "../c-client/mail.h"
27 #include "../c-client/utf8.h"
30 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
35 #include "../pith/charconv/filesys.h"
36 #include "../pith/charconv/utf8.h"
39 void vtmove(int, int);
42 void updateline(int, CELL
*, CELL
*, short *, int);
43 void updatelinecolor(int, CELL
*, CELL
*, short *, int);
45 void mlputi(int, int);
46 void pprints(int, int);
47 void mlputli(long, int);
48 void showCompTitle(void);
50 int dumbroot(int, int);
51 int dumblroot(long, int);
52 unsigned cellwidth_ptr_to_ptr(CELL
*pstart
, CELL
*pend
);
53 unsigned vcellwidth_a_to_b(int row
, int a
, int b
);
54 int window_signature_block(WINDOW
*wp
);
56 void pico_config_menu_items (KEYMENU
*);
57 int update_scroll (void);
62 * Standard pico keymenus...
64 static KEYMENU menu_pico
[] = {
65 {"^G", N_("Get Help"), KS_SCREENHELP
}, {"^O", N_("WriteOut"), KS_SAVEFILE
},
66 {"^R", N_("Read File"), KS_READFILE
}, {"^Y", N_("Prev Pg"), KS_PREVPAGE
},
67 {"^K", N_("Cut Text"), KS_NONE
}, {"^C", N_("Cur Pos"), KS_CURPOSITION
},
68 {"^X", N_("Exit"), KS_EXIT
}, {"^J", N_("Justify"), KS_JUSTIFY
},
69 {"^W", N_("Where is"), KS_WHEREIS
}, {"^V", N_("Next Pg"), KS_NEXTPAGE
},
70 {"^U", NULL
, KS_NONE
},
72 {"^T", N_("To Spell"), KS_SPELLCHK
}
74 {"^D", N_("Del Char"), KS_NONE
}
80 static KEYMENU menu_compose
[] = {
81 {"^G", N_("Get Help"), KS_SCREENHELP
}, {"^X", NULL
, KS_SEND
},
82 {"^R", N_("Read File"), KS_READFILE
}, {"^Y", N_("Prev Pg"), KS_PREVPAGE
},
83 {"^K", N_("Cut Text"), KS_NONE
}, {"^O", N_("Postpone"), KS_POSTPONE
},
84 /* TRANSLATORS: Justify is to reformat a paragraph automatically */
85 {"^C", N_("Cancel"), KS_CANCEL
}, {"^J", N_("Justify"), KS_JUSTIFY
},
86 {NULL
, NULL
, KS_NONE
}, {"^V", N_("Next Pg"), KS_NEXTPAGE
},
87 {"^U", NULL
, KS_NONE
},
89 {"^T", N_("To Spell"), KS_SPELLCHK
}
91 {"^D", N_("Del Char"), KS_NONE
}
100 * Definition's for pico's modeline
102 #define PICO_TITLE " UW PICO %s"
103 #define PICO_MOD_MSG "Modified"
104 #define PICO_NEWBUF_MSG "New Buffer"
106 #define WFDEBUG 0 /* Window flag debug. */
108 #define VFCHG 0x0001 /* Changed flag */
109 #define VFEXT 0x0002 /* extended (beyond column 80) */
110 #define VFSIG 0x0004 /* in signature block */
112 int vtrow
= 0; /* Row location of SW cursor */
113 int vtcol
= 0; /* Column location of SW cursor */
114 int vtind
= 0; /* Index into row array of SW cursor */
115 int ttrow
= FARAWAY
; /* Row location of HW cursor */
116 int ttcol
= FARAWAY
; /* Column location of HW cursor */
117 int lbound
= 0; /* leftmost column of current line
120 VIDEO
**vscreen
; /* Virtual screen. */
121 VIDEO
**pscreen
; /* Physical screen. */
125 * Initialize the data structures used by the display code. The edge vectors
126 * used to access the screens are set up. The operating system's terminal I/O
127 * channel is set up. All the other things get initialized at compile time.
128 * The original window has "WFCHG" set, so that it will get completely
129 * redrawn on the first call to "update".
142 vtterminalinfo(gmode
& MDTCAPWINS
);
146 (*term
.t_rev
)(FALSE
);
147 vscreen
= (VIDEO
**) malloc((term
.t_nrow
+1)*sizeof(VIDEO
*));
148 memset(vscreen
, 0, (term
.t_nrow
+1)*sizeof(VIDEO
*));
149 if (vscreen
== NULL
){
150 emlwrite("Allocating memory for virtual display failed.", NULL
);
154 pscreen
= (VIDEO
**) malloc((term
.t_nrow
+1)*sizeof(VIDEO
*));
155 memset(pscreen
, 0, (term
.t_nrow
+1)*sizeof(VIDEO
*));
156 if (pscreen
== NULL
){
157 free((void *)vscreen
);
158 emlwrite("Allocating memory for physical display failed.", NULL
);
163 for (i
= 0; i
<= term
.t_nrow
; ++i
) {
164 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
167 free((void *)vscreen
);
168 free((void *)pscreen
);
169 emlwrite("Allocating memory for virtual display lines failed.",
174 for(j
= 0; j
< term
.t_ncol
; j
++)
181 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
184 free((void *)vscreen
[i
]);
186 free((void *)vscreen
[i
]);
187 free((void *)pscreen
[i
]);
190 free((void *)vscreen
);
191 free((void *)pscreen
);
192 emlwrite("Allocating memory for physical display lines failed.",
197 for(j
= 0; j
< term
.t_ncol
; j
++)
209 vtterminalinfo(int termcap_wins
)
211 return((term
.t_terminalinfo
) ? (*term
.t_terminalinfo
)(termcap_wins
)
212 : (Pmaster
? 0 : TRUE
));
217 * Clean up the virtual terminal system, in anticipation for a return to the
218 * operating system. Move down to the last line and clear it out (the next
219 * system prompt will be written in the line). Shut down the channel to the
225 movecursor(term
.t_nrow
-1, 0);
227 movecursor(term
.t_nrow
, 0);
234 * Set the virtual cursor to the specified row and column on the virtual
235 * screen. There is no checking for nonsense values; this might be a good
236 * idea during the early stages.
239 vtmove(int row
, int col
)
250 * This is unused so don't worry about it.
258 * Write a character to the virtual screen. The virtual row and column are
259 * updated. If the line is too long put a "$" in the last column. This routine
260 * only puts printing characters into the virtual terminal buffers. Only
261 * column overflow is checked.
275 if (vtcol
>= term
.t_ncol
) {
277 * What's this supposed to be doing? This sets vtcol
278 * to the even multiple of 8 >= vtcol. Why are we doing that?
279 * Must make tab work correctly.
287 vtcol
= (vtcol
+ 0x07) & ~0x07;
291 * If we get to here that means that there must be characters
292 * past the right hand edge, so we want to put a $ character
293 * in the last visible character. It would be nice to replace
294 * the last visible character by a double-$ if it is double-width
295 * but we aren't doing that because we'd have to add up the widths
296 * starting at the left hand margin each time through.
298 if(vtind
> 0 && vtind
<= term
.t_ncol
)
299 vp
->v_text
[vtind
-1] = ac
;
301 else if (c
.c
== '\t') {
305 while (((vtcol
+ (vtrow
==currow
? lbound
: 0)) & 0x07) != 0 && vtcol
< term
.t_ncol
);
307 else if (ISCONTROL(c
.c
)){
310 ac
.c
= ((c
.c
& 0x7f) | 0x40);
316 * Have to worry about what happens if we skip over 0
317 * with a double-width character. There may be a better
318 * place to be setting vtind, or maybe we could make do
322 w
= wcellwidth((UCS
) c
.c
);
323 w
= (w
>= 0 ? w
: 1);
325 if(vtcol
== 0 || (vtcol
< 0 && vtcol
+ w
== 1)){
332 * Double-width character overlaps right edge.
333 * Replace it with a $.
335 if(vtcol
+ w
> term
.t_ncol
){
341 if(vtind
>= 0 && vtind
< term
.t_ncol
)
342 vp
->v_text
[vtind
++] = c
;
350 * Erase from the end of the software cursor to the end of the line on which
351 * the software cursor is located.
364 while (vtind
< term
.t_ncol
)
365 vp
->v_text
[vtind
++] = c
;
371 window_signature_block(WINDOW
*wp
)
374 int in_sig
, is_sig_start
= 0;
378 lp
= lforw(wp
->w_bufp
->b_linep
);
381 in_sig
= lback(lp
) == wp
->w_bufp
->b_linep
? 0 : lback(lp
)->l_sig
;
383 if(llength(lp
) == 3){
384 if(lgetc(lp
, 0).c
== '-'
385 && lgetc(lp
, 1).c
== '-'
386 && lgetc(lp
, 2).c
== ' '){
393 if(is_sig_start
== 0) in_sig
= 0;
396 if(lp
->l_sig
!= in_sig
)
400 } while(lp
!= wp
->w_bufp
->b_linep
);
407 * Make sure that the display is right. This is a three part process. First,
408 * scan through all of the windows looking for dirty ones. Check the framing,
409 * and refresh the screen. Second, make sure that "currow" and "curcol" are
410 * correct for the current window. Third, make the virtual and physical
425 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
433 /* This tells our MS Windows module to not bother updating the
434 * cursor position while a massive screen update is in progress.
436 mswin_beginupdate ();
440 * BUG: setting and unsetting whole region at a time is dumb. fix this.
450 /* Look at any window with update flags set on. */
452 if(pcolors
&& (repaint
= window_signature_block(wp
))){
454 wp
->w_flag
|= WFEDIT
| WFHARD
;
456 if (wp
->w_flag
!= 0){
457 /* If not force reframe, check the framing. */
459 if ((wp
->w_flag
& WFFORCE
) == 0){
462 for (i
= 0; i
< wp
->w_ntrows
; ++i
){
463 if (lp
== wp
->w_dotp
)
466 if (lp
== wp
->w_bufp
->b_linep
)
473 /* Not acceptable, better compute a new value for the line at the
474 * top of the window. Then set the "WFHARD" flag to force full
482 if (i
>= wp
->w_ntrows
)
491 else if(TERM_OPTIMIZE
){
493 * find dotp, if its been moved just above or below the
494 * window, use scrollxxx() to facilitate quick redisplay...
496 lp
= lforw(wp
->w_dotp
);
497 if(lp
!= wp
->w_dotp
){
498 if(lp
== wp
->w_linep
&& lp
!= wp
->w_bufp
->b_linep
){
503 for(j
=0;j
< wp
->w_ntrows
; ++j
){
504 if(lp
!= wp
->w_bufp
->b_linep
)
509 if(lp
== wp
->w_dotp
&& j
== wp
->w_ntrows
)
513 j
= i
= wp
->w_ntrows
/2;
520 while (i
!= 0 && lback(lp
) != wp
->w_bufp
->b_linep
){
526 * this is supposed to speed things up by using tcap sequences
527 * to efficiently scroll the terminal screen. the thinking here
528 * is that its much faster to update pscreen[] than to actually
529 * write the stuff to the screen...
533 case 1: /* scroll text down */
534 j
= j
-i
+1; /* add one for dot line */
536 * do we scroll down the header as well? Well, only
537 * if we're not editing the header, we've backed up
538 * to the top, and the composer is not being
541 if(Pmaster
&& Pmaster
->headents
&& !ComposerEditing
542 && (lback(lp
) == wp
->w_bufp
->b_linep
)
543 && (ComposerTopLine
== COMPOSER_TOP_LINE
))
544 j
+= entry_line(1000, TRUE
); /* Never > 1000 headers */
546 scrolldown(wp
, -1, j
);
548 case 2: /* scroll text up */
549 j
= wp
->w_ntrows
- (j
-i
); /* we chose new top line! */
552 * do we scroll down the header as well? Well, only
553 * if we're not editing the header, we've backed up
554 * to the top, and the composer is not being
558 && (ComposerTopLine
!= COMPOSER_TOP_LINE
))
559 scrollup(wp
, COMPOSER_TOP_LINE
,
560 j
+entry_line(1000, TRUE
));
573 wp
->w_flag
|= WFHARD
; /* Force full. */
576 * if the line at the top of the page is the top line
577 * in the body, show the header...
579 if(Pmaster
&& Pmaster
->headents
&& !ComposerEditing
){
580 if(lback(wp
->w_linep
) == wp
->w_bufp
->b_linep
){
581 if(ComposerTopLine
== COMPOSER_TOP_LINE
){
582 i
= term
.t_nrow
- 2 - term
.t_mrow
- HeaderLen();
583 if(i
> 0 && nlforw() >= i
) { /* room for header ? */
584 if((i
= nlforw()/2) == 0 && term
.t_nrow
&1)
586 while(wp
->w_linep
!= wp
->w_bufp
->b_linep
&& i
--)
587 wp
->w_linep
= lforw(wp
->w_linep
);
595 if(ComposerTopLine
!= COMPOSER_TOP_LINE
)
596 ToggleHeader(0); /* hide it ! */
600 /* Try to use reduced update. Mode line update has its own special
601 * flag. The fast update is used if the only thing to do is within
607 if ((wp
->w_flag
& ~WFMODE
) == WFEDIT
){
608 while (lp
!= wp
->w_dotp
){
612 vscreen
[i
]->v_flag
|= (lp
->l_sig
? VFSIG
: 0)| VFCHG
;
613 /* compute physical length of line in screen */
614 vscreen
[i
]->v_length
= 0;
615 for (j
= 0; vscreen
[i
]->v_length
< term
.t_ncol
616 && j
< llength(lp
); ++j
){
619 vscreen
[i
]->v_length
|= 0x07;
620 vscreen
[i
]->v_length
++;
622 else if(ISCONTROL(c
.c
)){
623 vscreen
[i
]->v_length
+= 2;
628 w
= wcellwidth((UCS
) c
.c
);
629 vscreen
[i
]->v_length
+= (w
>= 0 ? w
: 1);
634 for (j
= 0; j
< llength(lp
); ++j
)
635 vtputc(lgetc(lp
, j
));
638 else if ((wp
->w_flag
& (WFEDIT
| WFHARD
)) != 0){
639 while (i
< wp
->w_toprow
+wp
->w_ntrows
){
640 vscreen
[i
]->v_flag
|= (lp
->l_sig
? VFSIG
: 0 )| VFCHG
;
641 /* compute physical length of line in screen */
642 vscreen
[i
]->v_length
= 0;
643 for (j
= 0; vscreen
[i
]->v_length
< term
.t_ncol
644 && j
< llength(lp
); ++j
){
647 vscreen
[i
]->v_length
|= 0x07;
648 vscreen
[i
]->v_length
++;
650 else if(ISCONTROL(c
.c
)){
651 vscreen
[i
]->v_length
+= 2;
656 w
= wcellwidth((UCS
) c
.c
);
657 vscreen
[i
]->v_length
+= (w
>= 0 ? w
: 1);
662 /* if line has been changed */
663 if (lp
!= wp
->w_bufp
->b_linep
){
664 for (j
= 0; j
< llength(lp
); ++j
)
665 vtputc(lgetc(lp
, j
));
675 if ((wp
->w_flag
&WFMODE
) != 0)
688 /* and onward to the next window */
692 /* Always recompute the row and column number of the hardware cursor. This
693 * is the only update for simple moves.
696 currow
= curwp
->w_toprow
;
698 while (lp
!= curwp
->w_dotp
){
706 while (i
< curwp
->w_doto
){
713 else if(ISCONTROL(c
.c
)){
719 w
= wcellwidth((UCS
) c
.c
);
720 curcol
+= (w
>= 0 ? w
: 1);
724 if (curcol
>= term
.t_ncol
) { /* extended line. */
725 /* flag we are extended and changed */
726 vscreen
[currow
]->v_flag
|= VFEXT
| VFCHG
;
727 updext(); /* and output extended line */
729 lbound
= 0; /* not extended line */
731 /* make sure no lines need to be de-extended because the cursor is
741 while (i
< wp
->w_toprow
+ wp
->w_ntrows
) {
742 if (vscreen
[i
]->v_flag
& VFEXT
) {
743 /* always flag extended lines as changed */
744 vscreen
[i
]->v_flag
|= VFCHG
;
745 if ((wp
!= curwp
) || (lp
!= wp
->w_dotp
) ||
746 (curcol
< term
.t_ncol
)) {
748 for (j
= 0; j
< llength(lp
); ++j
)
749 vtputc(lgetc(lp
, j
));
752 /* this line no longer is extended */
753 vscreen
[i
]->v_flag
&= ~VFEXT
;
759 /* and onward to the next window */
763 /* Special hacking if the screen is garbage. Clear the hardware screen,
764 * and update your copy to agree with it. Set all the virtual screen
765 * change bits, to force a full update.
768 if (sgarbf
!= FALSE
){
774 if(ComposerTopLine
!= COMPOSER_TOP_LINE
){
775 UpdateHeader(0); /* arrange things */
776 PaintHeader(COMPOSER_TOP_LINE
, TRUE
);
780 * since we're using only a portion of the screen and only
781 * one buffer, only clear enough screen for the current window
782 * which is to say the *only* window.
784 for(i
=wheadp
->w_toprow
;i
<=term
.t_nrow
; i
++){
787 vscreen
[i
]->v_flag
|= VFCHG
;
789 rv
= (*Pmaster
->showmsg
)('X' & 0x1f); /* ctrl-L */
791 picosigs(); /* restore altered handlers */
792 if(rv
) /* Did showmsg corrupt the display? */
793 PaintBody(0); /* Yes, repaint */
794 movecursor(wheadp
->w_toprow
, 0);
799 for (i
= 0; i
< term
.t_nrow
-term
.t_mrow
; i
++){
800 vscreen
[i
]->v_flag
|= VFCHG
;
802 for (j
= 0; j
< term
.t_ncol
; j
++)
810 movecursor(0, 0); /* Erase the screen. */
815 sgarbf
= FALSE
; /* Erase-page clears */
816 mpresf
= FALSE
; /* the message area. */
821 sgarbk
= TRUE
; /* fix the keyhelp as well...*/
824 /* Make sure that the physical and virtual displays agree. Unlike before,
825 * the "updateline" code is only called with a line that has been updated
833 if (term
.t_nrow
> term
.t_mrow
)
834 c
.c
= term
.t_nrow
- term
.t_mrow
;
838 for (; i
< (int)c
.c
; ++i
){
842 /* for each line that needs to be updated, or that needs its
843 reverse video status changed, call the line updater */
857 updateline(i
, &vp1
->v_text
[0], &vp2
->v_text
[0], &vp1
->v_flag
, vp1
->v_length
);
865 movecursor(term
.t_nrow
-1, 0);
867 movecursor(term
.t_nrow
, 0);
872 /* TRANSLATORS: UnJustify means undo the previous
874 menu_pico
[UNCUT_KEY
].label
= N_("UnJustify");
875 if(!(lastflag
&CFFLBF
)){
876 emlwrite(_("Can now UnJustify!"), NULL
);
877 mpresf
= FARAWAY
; /* remove this after next keystroke! */
881 menu_pico
[UNCUT_KEY
].label
= N_("UnCut Text");
889 emlwrite(_("Can now UnJustify!"), NULL
);
890 mpresf
= FARAWAY
; /* remove this after next keystroke! */
893 /* Finally, update the hardware cursor and flush out buffers. */
895 movecursor(currow
, curcol
- lbound
);
900 * Update the scroll bars. This function is where curbp->b_linecnt
901 * is really managed. See update_scroll.
909 /* updext - update the extended line which the cursor is currently
910 * on at a column greater than the terminal width. The line
911 * will be scrolled right or left to let the user see where
917 int rcursor
; /* real cursor location */
918 LINE
*lp
; /* pointer to current line */
919 int j
; /* index into line */
924 * Calculate what column the real cursor will end up in.
925 * The cursor will be in the rcursor'th column. So if we're
926 * counting columns 0 1 2 3 and rcursor is 8, then rcursor
927 * will be over cell 7.
929 * What this effectively does is to scroll the screen as we're
930 * moving to the right when the cursor first passes off the
931 * screen's right edge. It would be nice if it did the same
932 * thing coming back to the left. Instead, in order that the
933 * screen's display depends only on the curcol and not on
934 * how we got there, the screen scrolls when we pass the
935 * t_margin column. It's also kind of funky that you can't
936 * see the character under the $ but you can delete it.
938 rcursor
= ((curcol
- term
.t_ncol
) % (term
.t_ncol
- term
.t_margin
+ 1)) + term
.t_margin
;
939 lbound
= curcol
- rcursor
+ 1;
942 * Make sure lbound is set so that a double-width character does
943 * not straddle the boundary. If it does, move over one cell.
945 lp
= curwp
->w_dotp
; /* line to output */
946 for (j
=0; j
<llength(lp
) && w
< lbound
; ++j
){
947 ww
= wcellwidth((UCS
) lgetc(lp
, j
).c
);
948 w
+= (ww
>= 0 ? ww
: 1);
955 /* scan through the line outputting characters to the virtual screen
956 * once we reach the left edge
958 vtmove(currow
, -lbound
); /* start scanning offscreen */
959 for (j
=0; j
<llength(lp
); ++j
) /* until the end-of-line */
960 vtputc(lgetc(lp
, j
));
962 /* truncate the virtual line */
965 /* and put a '$' in column 1, may have to adjust curcol */
966 w
= wcellwidth((UCS
) vscreen
[currow
]->v_text
[0].c
);
967 vscreen
[currow
]->v_text
[0].c
= '$';
968 vscreen
[currow
]->v_text
[0].a
= 0;
971 * We want to put $ in the first two columns so that it
972 * takes up the right amount of space, but that means we
973 * have to scoot the real characters over one slot.
975 for (j
= term
.t_ncol
-1; j
>= 2; --j
)
976 vscreen
[currow
]->v_text
[j
] = vscreen
[currow
]->v_text
[j
-1];
978 vscreen
[currow
]->v_text
[1].c
= '$';
979 vscreen
[currow
]->v_text
[1].a
= 0;
983 /* update line color, to be executed to update lines when color is on */
985 updatelinecolor (int row
, CELL vline
[], CELL pline
[], short *flags
, int len
)
987 CELL
*cp1
, *cp2
, *cp3
, *cp4
, *cp5
;
988 int nbflag
; /* non-blanks to the right flag? */
991 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
992 COLOR_PAIR
*lastc
= NULL
, *pcolor
= NULL
;
993 int first
= 1, lastattr
= -1, change
= 0;
996 updateline(row
, vline
, pline
, flags
, len
);
1001 lastc
= pico_get_cur_color();
1003 /* set up pointers to virtual and physical lines */
1006 cp3
= &vline
[term
.t_ncol
];
1007 cp4
= &pline
[term
.t_ncol
];
1009 if(cellwidth_ptr_to_ptr(cp1
, cp3
) == cellwidth_ptr_to_ptr(cp2
, cp4
))
1010 while (cp3
!= cp1
&& cp3
[-1].c
== cp4
[-1].c
&& cp3
[-1].a
== cp4
[-1].a
) {
1013 if (cp3
[0].c
!= ' ' || cp3
[0].a
!= 0) /* Note if any nonblank */
1014 nbflag
= TRUE
; /* in right match. */
1019 if (nbflag
== FALSE
&& TERM_EOLEXIST
) { /* Erase to EOL ? */
1020 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1023 if (cp3
-cp5
<= 3) /* Use only if erase is */
1024 cp5
= cp3
; /* fewer characters. */
1027 /* go to start of line */
1033 w1
= cellwidth_ptr_to_ptr(cp1
, cp3
);
1034 w2
= cellwidth_ptr_to_ptr(cp2
, cp2
+ (cp3
-cp1
));
1036 if(w1
< w2
|| (nbflag
&& w1
!= w2
)){
1040 * Draw all of the characters starting with cp1
1041 * until we get to all spaces, then clear to the end of
1042 * line from there. Watch out we don't run over the
1043 * right hand edge, which shouldn't happen.
1045 * Set cp5 to the first of the repeating spaces.
1047 cp5
= &vline
[term
.t_ncol
];
1048 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1053 * In the !nbflag case we want spaces from cp5 on.
1054 * Setting cp3 to something different from cp5 triggers
1055 * the clear to end of line below.
1057 if(cellwidth_ptr_to_ptr(&vline
[0], cp5
) < term
.t_ncol
)
1064 * No peeol so draw all the way to the edge whether they
1065 * are spaces or not.
1068 for(w
= 0; w
< term
.t_ncol
&& cp3
< &vline
[term
.t_ncol
]; cp3
++){
1071 ww
= wcellwidth((UCS
) cp3
->c
);
1072 w
+= (ww
>= 0 ? ww
: 1);
1080 if(row
!= 0 && len
< term
.t_ncol
)
1085 while (cp1
!= cp5
){ /* Ordinary. */
1093 change
= lastattr
!= cp1
->a
;
1097 pico_set_colorp(pcolors
->tbcp
, PSC_NONE
);
1098 else if(row
< term
.t_nrow
- 2)
1099 pcolor
= (*flags
& VFSIG
) ? pcolors
->sbcp
: pcolors
->ntcp
;
1101 if(cp1
->c
!= '>' && cp1
->c
!= ' ')
1103 else if (in_quote
&& cp1
->c
== '>' && pcolors
!= NULL
)
1104 level
= (level
+ 1) % 3;
1106 if(level
== 0) pcolor
= pcolors
->qlcp
;
1107 else if(level
== 1) pcolor
= pcolors
->qllcp
;
1108 else if(level
== 2) pcolor
= pcolors
->qlllcp
;
1111 pcolor
= pcolors
->rtcp
; /* pcolor = proposed color */
1113 pico_set_colorp(pcolor
, PSC_NONE
);
1115 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1118 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1119 (*term
.t_putchar
)(cp1
->c
);
1121 ww
= wcellwidth((UCS
) cp1
->c
);
1122 ttcol
+= (ww
>= 0 ? ww
: 1);
1128 (void)pico_set_colorp(lastc
, PSC_NONE
);
1129 free_color_pair(&lastc
);
1132 (*term
.t_rev
)(0); /* turn off inverse anyway! */
1134 if (cp5
!= cp3
|| cleartoeol
) /* Erase. */
1137 *flags
&= ~(VFCHG
|VFSIG
); /* flag this line is changed */
1143 * Update a single line. This does not know how to use insert or delete
1144 * character sequences; we are using VT52 functionality. Update the physical
1145 * row and column variables.
1148 updateline(int row
, /* row on screen */
1149 CELL vline
[], /* what we want it to end up as */
1150 CELL pline
[], /* what it looks like now */
1154 CELL
*cp1
, *cp2
, *cp3
, *cp4
, *cp5
, *cp6
, *cp7
;
1156 int nbflag
; /* non-blanks to the right flag? */
1159 if(row
< 0 || row
> term
.t_nrow
)
1162 if((Pmaster
&& Pmaster
->colors
) || Pcolors
){
1163 updatelinecolor(row
, vline
, pline
, flags
, len
);
1167 /* set up pointers to virtual and physical lines */
1170 cp3
= &vline
[term
.t_ncol
];
1172 /* advance past any common chars at the left */
1173 while (cp1
!= cp3
&& cp1
[0].c
== cp2
[0].c
&& cp1
[0].a
== cp2
[0].a
) {
1178 /* This can still happen, even though we only call this routine on changed
1179 * lines. A hard update is always done when a line splits, a massive
1180 * change is done, or a buffer is displayed twice. This optimizes out most
1181 * of the excess updating. A lot of computes are used, but these tend to
1182 * be hard operations that do a lot of update, so I don't really care.
1184 /* if both lines are the same, no update needs to be done */
1186 *flags
&= ~VFCHG
; /* mark it clean */
1190 /* find out if there is a match on the right */
1192 cp3
= &vline
[term
.t_ncol
];
1193 cp4
= &pline
[term
.t_ncol
];
1195 if(cellwidth_ptr_to_ptr(cp1
, cp3
) == cellwidth_ptr_to_ptr(cp2
, cp4
))
1196 while (cp3
[-1].c
== cp4
[-1].c
&& cp3
[-1].a
== cp4
[-1].a
) {
1199 if (cp3
[0].c
!= ' ' || cp3
[0].a
!= 0) /* Note if any nonblank */
1200 nbflag
= TRUE
; /* in right match. */
1205 if (nbflag
== FALSE
&& TERM_EOLEXIST
) { /* Erase to EOL ? */
1206 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1209 if (cp3
-cp5
<= 3) /* Use only if erase is */
1210 cp5
= cp3
; /* fewer characters. */
1213 /* go to start of differences */
1214 movecursor(row
, cellwidth_ptr_to_ptr(&vline
[0], cp1
));
1216 if (!nbflag
) { /* use insert or del char? */
1221 &&(cp7
!=cp2
&& cp6
[0].c
==cp7
[-1].c
&& cp6
[0].a
==cp7
[-1].a
)){
1222 while (cp7
!= cp2
&& cp6
[0].c
==cp7
[-1].c
&& cp6
[0].a
==cp7
[-1].a
){
1227 if (cp7
==cp2
&& cp4
-cp2
> 3){
1230 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1231 o_insert((UCS
) cp1
->c
); /* insert the char */
1232 ww
= wcellwidth((UCS
) cp1
->c
);
1233 ttcol
+= (ww
>= 0 ? ww
: 1);
1234 display
= FALSE
; /* only do it once!! */
1237 else if(TERM_DELCHAR
&& cp3
!= cp1
&& cp7
[0].c
== cp6
[-1].c
1238 && cp7
[0].a
== cp6
[-1].a
){
1239 while (cp6
!= cp1
&& cp7
[0].c
==cp6
[-1].c
&& cp7
[0].a
==cp6
[-1].a
){
1244 if (cp6
==cp1
&& cp5
-cp6
> 3){
1247 w
= wcellwidth((UCS
) cp7
[0].c
);
1248 w
= (w
>= 0 ? w
: 1);
1249 while(w
-- > 0) /* in case double-width char */
1250 o_delete(); /* delete the char */
1251 display
= FALSE
; /* only do it once!! */
1256 if(cp1
!= cp5
&& display
){
1260 * If we need to copy characters from cp1 to cp2 and
1261 * we need to display them, then we have to worry about
1262 * the characters that we are replacing being of a different
1263 * width than the new characters, else the display may be
1266 * If the new width (w1) is less than the old width, that means
1267 * we will leave behind some old remnants if we aren't careful.
1268 * If the new width is larger than the old width, we have to
1269 * make sure we draw the characters all the way to the end
1270 * in order to get it right. Take advantage of clear to end
1271 * of line if we have it.
1273 w1
= cellwidth_ptr_to_ptr(cp1
, cp3
);
1274 w2
= cellwidth_ptr_to_ptr(cp2
, cp2
+ (cp3
-cp1
));
1276 if(w1
< w2
|| (nbflag
&& w1
!= w2
)){
1280 * Draw all of the characters starting with cp1
1281 * until we get to all spaces, then clear to the end of
1282 * line from there. Watch out we don't run over the
1283 * right hand edge, which shouldn't happen.
1285 * Set cp5 to the first of the repeating spaces.
1287 cp5
= &vline
[term
.t_ncol
];
1288 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1293 * In the !nbflag case we want spaces from cp5 on.
1294 * Setting cp3 to something different from cp5 triggers
1295 * the clear to end of line below.
1297 if(cellwidth_ptr_to_ptr(&vline
[0], cp5
) < term
.t_ncol
)
1304 * No peeol so draw all the way to the edge whether they
1305 * are spaces or not.
1308 for(w
= 0; w
< term
.t_ncol
; cp3
++){
1311 ww
= wcellwidth((UCS
) cp3
->c
);
1312 w
+= (ww
>= 0 ? ww
: 1);
1320 while (cp1
!= cp5
) { /* Ordinary. */
1324 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1325 (*term
.t_putchar
)(cp1
->c
);
1328 ww
= wcellwidth((UCS
) cp1
->c
);
1329 ttcol
+= (ww
>= 0 ? ww
: 1);
1334 (*term
.t_rev
)(0); /* turn off inverse anyway! */
1336 if (cp5
!= cp3
|| cleartoeol
) { /* Erase. */
1344 *flags
&= ~VFCHG
; /* flag this line is changed */
1349 * Redisplay the mode line for the window pointed to by the "wp". This is the
1350 * only routine that has any idea of how the modeline is formatted. You can
1351 * change the modeline format by hacking at this routine. Called by "update"
1352 * any time there is a dirty window.
1355 modeline(WINDOW
*wp
)
1361 menu_compose
[EXIT_KEY
].label
= (Pmaster
->headents
)
1362 ? N_("Send") :N_("Exit");
1363 menu_compose
[PSTPN_KEY
].name
= (Pmaster
->headents
)
1365 menu_compose
[PSTPN_KEY
].label
= (Pmaster
->headents
)
1366 ? N_("Postpone") : NULL
;
1367 menu_compose
[WHERE_KEY
].name
= (Pmaster
->alt_ed
) ? "^_" : "^W";
1368 menu_compose
[WHERE_KEY
].label
= (Pmaster
->alt_ed
) ? N_("Alt Edit")
1370 KS_OSDATASET(&menu_compose
[WHERE_KEY
],
1371 (Pmaster
->alt_ed
) ? KS_ALTEDITOR
: KS_WHEREIS
);
1372 menu_compose
[UNCUT_KEY
].label
= (thisflag
&CFFILL
) ? N_("UnJustify")
1374 wkeyhelp(menu_compose
);
1379 char t1
[NLINE
], t2
[NLINE
], t3
[NLINE
], tline
[NLINE
];
1380 int w1
, w2
, w3
, w1_to_2
, w2_to_3
, w3_to_r
;
1385 vscreen
[0]->v_flag
|= VFCHG
; /* Redraw next time. */
1386 vtmove(0, 0); /* Seek to right line. */
1388 snprintf(t1
, sizeof(t1
), PICO_TITLE
, version
); /* write version */
1391 if(bp
->b_fname
[0]) /* File name? */
1392 snprintf(t2
, sizeof(t2
), "File: %.*s", (int) sizeof(t2
) - 7, bp
->b_fname
);
1394 strncpy(t2
, PICO_NEWBUF_MSG
, sizeof(t2
));
1395 t2
[sizeof(t2
)-1] = '\0';
1398 if(bp
->b_flag
&BFCHG
){ /* "MOD" if changed. */
1399 strncpy(t3
, PICO_MOD_MSG
, sizeof(t3
));
1400 t3
[sizeof(t3
)-1] = '\0';
1405 #define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
1406 #define ALLBUTSPACE (w1+w2+w3+w3_to_r)
1408 w1
= utf8_width(t1
);
1409 w2
= utf8_width(t2
);
1410 w3
= utf8_width(t3
);
1411 w1_to_2
= w2_to_3
= 1; /* min values for separation */
1414 if(ALLOFTHEM
<= term
.t_ncol
){ /* everything fits */
1415 w1_to_2
= (term
.t_ncol
- ALLBUTSPACE
)/2;
1416 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1421 if(ALLOFTHEM
<= term
.t_ncol
)
1422 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1424 w1
= w1_to_2
= w3_to_r
= 0;
1425 if(ALLOFTHEM
<= term
.t_ncol
)
1426 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1429 snprintf(t2
, sizeof(t2
), "%.*s", (int) sizeof(t2
) - 1, bp
->b_fname
);
1430 w2
= utf8_width(t2
);
1433 if(ALLOFTHEM
<= term
.t_ncol
)
1434 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1437 if(bp
->b_fname
[0] && ALLOFTHEM
<= term
.t_ncol
){
1438 /* reduce size of file */
1439 w2
= term
.t_ncol
- (ALLOFTHEM
- w2
);
1440 t2
[0] = t2
[1] = t2
[2] = '.';
1441 utf8_to_width_rhs(t2
+3, bp
->b_fname
, sizeof(t2
)-3, w2
-3);
1442 w2
= utf8_width(t2
);
1445 w2
= utf8_width(t2
);
1447 if(ALLOFTHEM
<= term
.t_ncol
)
1448 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1450 w1
= w1_to_2
= w2
= w2_to_3
= w3_to_r
= 0;
1451 if(ALLOFTHEM
<= term
.t_ncol
)
1452 w2_to_3
= term
.t_ncol
- ALLBUTSPACE
;
1461 utf8_snprintf(tline
, sizeof(tline
),
1462 "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
1464 w1_to_2
, w1_to_2
, "",
1466 w2_to_3
, w2_to_3
, "",
1468 w3_to_r
, w3_to_r
, "");
1471 if(utf8_width(tline
) <= term
.t_ncol
)
1472 ucs
= utf8_to_ucs4_cpystr(tline
);
1480 while((c
.c
= CELLMASK
& *ucsp
++))
1483 fs_give((void **) &ucs
);
1491 * Send a command to the terminal to move the hardware cursor to row "row"
1492 * and column "col". The row and column arguments are origin 0. Optimize out
1493 * random calls. Update "ttrow" and "ttcol".
1496 movecursor(int row
, int col
)
1498 if (row
!=ttrow
|| col
!=ttcol
) {
1501 (*term
.t_move
)(MIN(MAX(row
,0),term
.t_nrow
), MIN(MAX(col
,0),term
.t_ncol
-1));
1507 * Erase any sense we have of the cursor's HW location...
1512 ttrow
= ttcol
= FARAWAY
;
1516 get_cursor(int *row
, int *col
)
1526 * Erase the message line. This is a special routine because the message line
1527 * is not considered to be part of the virtual screen. It always works
1528 * immediately; the terminal buffer is flushed via a call to the flusher.
1533 if (term
.t_nrow
< term
.t_mrow
)
1536 movecursor(term
.t_nrow
- term
.t_mrow
, 0);
1538 if (TERM_EOLEXIST
== TRUE
)
1541 if(ttrow
== term
.t_nrow
){
1542 while(ttcol
++ < term
.t_ncol
-1)
1543 (*term
.t_putchar
)(' ');
1546 while(ttcol
++ < term
.t_ncol
) /* track's ttcol */
1547 (*term
.t_putchar
)(' ');
1555 /* returns the chosen dictionary. If one was already chosen
1559 speller_choice(char **sp_list
, int *choice
)
1564 if(sp_list
== NULL
|| sp_list
[0] == NULL
|| sp_list
[0][0] == '\0')
1567 if(choice
&& *choice
>= 0)
1568 return sp_list
[*choice
];
1570 for(cnt
= 0; sp_list
[cnt
] != NULL
&& sp_list
[cnt
][0] != '\0'; cnt
++)
1573 if(cnt
> 10) /* only the first 10 dictionaries */
1576 if(cnt
== 1) /* only one dictionary? choose it! */
1579 if(ch_dict
> cnt
- 1) /* choose again in case something changed */
1582 if(ch_dict
< 0){ /* not a choice yet? do one now! */
1585 EXTRAKEYS menu_dictionary
[] = {
1598 for(i
= 0; i
< cnt
; i
++)
1599 menu_dictionary
[i
].label
= sp_list
[i
];
1602 menu_dictionary
[cnt
].name
= NULL
;
1604 /* write the prompt in utf8, and let internal functions translate it to ucs4 */
1605 ucs4_prompt
= utf8_to_ucs4_cpystr(_("Choose Dictionary: "));
1607 i
= mlchoose(ucs4_prompt
, menu_dictionary
);
1609 if(i
>= '0' && i
<= '9')
1612 if (i
== -2) /* user cancelled */
1616 fs_give((void **)&ucs4_prompt
);
1623 return ch_dict
>= 0 ? sp_list
[ch_dict
] : NULL
;
1626 /* just like mlreplyd, but user cannot fill a prompt */
1628 mlchoose(UCS
*prompt
, EXTRAKEYS
*extras
)
1634 KEYMENU menu_choose
[12];
1635 COLOR_PAIR
*lastc
= NULL
;
1637 for(i
= 0; i
< 12; i
++){
1638 menu_choose
[i
].name
= NULL
;
1639 KS_OSDATASET(&menu_choose
[i
], KS_NONE
);
1642 menu_choose
[0].name
= "^G";
1643 menu_choose
[0].label
= N_("Get Help");
1644 KS_OSDATASET(&menu_choose
[0], KS_SCREENHELP
);
1646 menu_choose
[6].name
= "^C";
1647 menu_choose
[6].label
= N_("Cancel");
1648 KS_OSDATASET(&menu_choose
[6], KS_NONE
);
1650 for(i
= 0; i
< 10; i
++){
1652 menu_choose
[i
/ 2 + 1].name
= extras
[i
].name
;
1653 menu_choose
[i
/ 2 + 1].label
= extras
[i
].label
;
1656 menu_choose
[(i
+ 13) / 2].name
= extras
[i
].name
;
1657 menu_choose
[(i
+ 13) / 2].label
= extras
[i
].label
;
1660 wkeyhelp(menu_choose
); /* paint generic menu */
1661 sgarbk
= TRUE
; /* mark menu dirty */
1662 if(Pmaster
&& curwp
)
1663 curwp
->w_flag
|= WFMODE
;
1665 ucs4_strncpy(buf
, prompt
, NLINE
);
1666 buf
[NLINE
-1] = '\0';
1668 if(Pmaster
&& Pmaster
->colors
&& Pmaster
->colors
->prcp
1669 && pico_is_good_colorpair(Pmaster
->colors
->prcp
)){
1670 lastc
= pico_get_cur_color();
1671 (void) pico_set_colorp(Pmaster
->colors
->prcp
, PSC_NONE
);
1679 for(i
= 0; i
< 10 && extras
[i
].name
!= NULL
&& extras
[i
].key
!= c
; i
++)
1681 if(i
< 10 && extras
[i
].name
)
1684 case (CTRL
|'C') : /* Bail out! */
1686 pputs_utf8(_("Cancel"), 1);
1691 if(term
.t_mrow
== 0 && km_popped
== 0){
1692 movecursor(term
.t_nrow
-2, 0);
1696 (void) pico_set_colorp(lastc
, PSC_NONE
);
1697 free_color_pair(&lastc
);
1702 wkeyhelp(menu_choose
); /* paint generic menu */
1704 if(Pmaster
&& Pmaster
->colors
&& Pmaster
->colors
->prcp
1705 && pico_is_good_colorpair(Pmaster
->colors
->prcp
)){
1706 lastc
= pico_get_cur_color();
1707 (void) pico_set_colorp(Pmaster
->colors
->prcp
, PSC_NONE
);
1712 sgarbk
= TRUE
; /* mark menu dirty */
1716 /* else fall through */
1725 if (return_val
!= -1){ /* abort sets rv = -2, other return values are positive */
1727 (void) pico_set_colorp(lastc
, PSC_NONE
);
1728 free_color_pair(&lastc
);
1735 movecursor(term
.t_nrow
, 0);
1748 mlyesno_utf8(char *utf8prompt
, int dflt
)
1753 prompt
= utf8_to_ucs4_cpystr(utf8prompt
? utf8prompt
: "");
1755 ret
= mlyesno(prompt
, dflt
);
1758 fs_give((void **) &prompt
);
1765 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
1766 * ABORT. The ABORT status is returned if the user bumps out of the question
1767 * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
1771 mlyesno(UCS
*prompt
, int dflt
)
1774 UCS buf
[NLINE
], lbuf
[10];
1775 KEYMENU menu_yesno
[12];
1776 COLOR_PAIR
*lastc
= NULL
;
1777 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
1780 if (mswin_usedialog ())
1781 switch (mswin_yesno (prompt
)) {
1783 case 0: return (ABORT
);
1784 case 1: return (TRUE
);
1785 case 2: return (FALSE
);
1789 for(rv
= 0; rv
< 12; rv
++){
1790 menu_yesno
[rv
].name
= NULL
;
1791 KS_OSDATASET(&menu_yesno
[rv
], KS_NONE
);
1794 menu_yesno
[1].name
= "Y";
1795 menu_yesno
[1].label
= (dflt
== TRUE
) ? "[" N_("Yes") "]" : N_("Yes");
1796 menu_yesno
[6].name
= "^C";
1797 menu_yesno
[6].label
= N_("Cancel");
1798 menu_yesno
[7].name
= "N";
1799 menu_yesno
[7].label
= (dflt
== FALSE
) ? "[" N_("No") "]" : N_("No");
1800 wkeyhelp(menu_yesno
); /* paint generic menu */
1801 sgarbk
= TRUE
; /* mark menu dirty */
1802 if(Pmaster
&& curwp
)
1803 curwp
->w_flag
|= WFMODE
;
1805 ucs4_strncpy(buf
, prompt
, NLINE
);
1806 buf
[NLINE
-1] = '\0';
1807 lbuf
[0] = ' '; lbuf
[1] = '?'; lbuf
[2] = ' '; lbuf
[3] = '\0';
1808 ucs4_strncat(buf
, lbuf
, NLINE
- ucs4_strlen(buf
) - 1);
1809 buf
[NLINE
-1] = '\0';
1811 if(pcolors
&& pcolors
->prcp
1812 && pico_is_good_colorpair(pcolors
->prcp
)){
1813 lastc
= pico_get_cur_color();
1814 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
1821 case (CTRL
|'M') : /* default */
1823 pputs_utf8((dflt
) ? _("Yes") : _("No"), 1);
1831 case (CTRL
|'C') : /* Bail out! */
1833 pputs_utf8(_("ABORT"), 1);
1840 pputs_utf8(_("Yes"), 1);
1847 pputs_utf8(_("No"), 1);
1852 if(term
.t_mrow
== 0 && km_popped
== 0){
1853 movecursor(term
.t_nrow
-2, 0);
1857 (void) pico_set_colorp(lastc
, PSC_NONE
);
1858 free_color_pair(&lastc
);
1863 wkeyhelp(menu_yesno
); /* paint generic menu */
1865 if(pcolors
&& pcolors
->prcp
1866 && pico_is_good_colorpair(pcolors
->prcp
)){
1867 lastc
= pico_get_cur_color();
1868 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
1873 sgarbk
= TRUE
; /* mark menu dirty */
1877 /* else fall through */
1888 (void) pico_set_colorp(lastc
, PSC_NONE
);
1889 free_color_pair(&lastc
);
1896 movecursor(term
.t_nrow
, 0);
1909 * Write a prompt into the message line, then read back a response. Keep
1910 * track of the physical position of the cursor. If we are in a keyboard
1911 * macro throw the prompt away, and return the remembered response. This
1912 * lets macros run at full speed. The reply is always terminated by a carriage
1913 * return. Handle erase, kill, and abort keys.
1916 mlreply_utf8(char *utf8prompt
, char *utf8buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
1918 return(mlreplyd_utf8(utf8prompt
, utf8buf
, nbuf
, flg
|QDEFLT
, extras
));
1923 mlreply(UCS
*prompt
, UCS
*buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
1925 return(mlreplyd(prompt
, buf
, nbuf
, flg
|QDEFLT
, extras
));
1930 * function key mappings
1932 static UCS rfkm
[12][2] = {
1949 mlreplyd_utf8(char *utf8prompt
, char *utf8buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
1956 buf
= (UCS
*) fs_get(nbuf
* sizeof(*b
));
1957 b
= utf8_to_ucs4_cpystr(utf8buf
);
1959 ucs4_strncpy(buf
, b
, nbuf
);
1961 fs_give((void **) &b
);
1964 prompt
= utf8_to_ucs4_cpystr(utf8prompt
? utf8prompt
: "");
1966 ret
= mlreplyd(prompt
, buf
, nbuf
, flg
, extras
);
1968 utf8
= ucs4_to_utf8_cpystr(buf
);
1970 strncpy(utf8buf
, utf8
, nbuf
);
1971 utf8buf
[nbuf
-1] = '\0';
1972 fs_give((void **) &utf8
);
1976 fs_give((void **) &buf
);
1979 fs_give((void **) &prompt
);
1993 * mlreplyd - write the prompt to the message line along with a default
1994 * answer already typed in. Carriage return accepts the
1995 * default. answer returned in buf which also holds the initial
1996 * default, nbuf is its length, def set means use default value.
1997 * In order to be able to eliminate keys from a menu, EXTRAKEYS
1998 * always has size 10.
2001 mlreplyd(UCS
*prompt
, UCS
*buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
2003 UCS c
; /* current char */
2004 UCS
*b
; /* pointer in buf */
2007 int changed
= FALSE
;
2009 KEYMENU menu_mlreply
[12];
2011 struct display_line dline
;
2012 COLOR_PAIR
*lastc
= NULL
;
2013 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
2016 if(mswin_usedialog()){
2017 MDlgButton btn_list
[12];
2018 LPTSTR free_names
[12];
2019 LPTSTR free_labels
[12];
2022 memset(&free_names
, 0, sizeof(LPTSTR
) * 12);
2023 memset(&free_labels
, 0, sizeof(LPTSTR
) * 12);
2024 memset(&btn_list
, 0, sizeof (MDlgButton
) * 12);
2026 for(i
= 0; extras
&& extras
[i
].name
!= NULL
; ++i
) {
2027 if(extras
[i
].label
[0] != '\0') {
2028 if((extras
[i
].key
& CTRL
) == CTRL
)
2029 btn_list
[j
].ch
= (extras
[i
].key
& ~CTRL
) - '@';
2031 btn_list
[j
].ch
= extras
[i
].key
;
2033 btn_list
[j
].rval
= extras
[i
].key
;
2034 free_names
[j
] = utf8_to_lptstr(extras
[i
].name
);
2035 btn_list
[j
].name
= free_names
[j
];
2036 free_labels
[j
] = utf8_to_lptstr(extras
[i
].label
);
2037 btn_list
[j
].label
= free_labels
[j
];
2042 btn_list
[j
].ch
= -1;
2044 return_val
= mswin_dialog(prompt
, buf
, nbuf
, ((flg
&QDEFLT
) > 0),
2045 FALSE
, btn_list
, NULL
, 0);
2048 return_val
= HELPCH
;
2050 for(i
= 0; i
< 12; i
++){
2052 fs_give((void **) &free_names
[i
]);
2054 fs_give((void **) &free_labels
[i
]);
2061 memset(&menu_mlreply
, 0, 12*sizeof(KEYMENU
));
2062 menu_mlreply
[0].name
= "^G";
2063 menu_mlreply
[0].label
= N_("Get Help");
2064 KS_OSDATASET(&menu_mlreply
[0], KS_SCREENHELP
);
2065 for(j
= 0, i
= 1; i
< 6; i
++){ /* insert odd extras */
2066 menu_mlreply
[i
].name
= NULL
;
2067 KS_OSDATASET(&menu_mlreply
[i
], KS_NONE
);
2072 rfkm
[2*i
][1] = extras
[j
].key
;
2073 menu_mlreply
[i
].name
= extras
[j
].name
;
2074 menu_mlreply
[i
].label
= extras
[j
].label
;
2075 KS_OSDATASET(&menu_mlreply
[i
], KS_OSDATAGET(&extras
[j
]));
2080 menu_mlreply
[6].name
= "^C";
2081 menu_mlreply
[6].label
= N_("Cancel");
2082 KS_OSDATASET(&menu_mlreply
[6], KS_NONE
);
2083 for(j
= 0, i
= 7; i
< 12; i
++){ /* insert even extras */
2084 menu_mlreply
[i
].name
= NULL
;
2085 rfkm
[2*(i
-6)+1][1] = 0;
2089 rfkm
[2*(i
-6)+1][1] = extras
[j
].key
;
2090 menu_mlreply
[i
].name
= extras
[j
].name
;
2091 menu_mlreply
[i
].label
= extras
[j
].label
;
2092 KS_OSDATASET(&menu_mlreply
[i
], KS_OSDATAGET(&extras
[j
]));
2097 /* set up what to watch for and return values */
2098 memset(extra_v
, 0, sizeof(extra_v
));
2099 for(i
= 0, j
= 0; i
< 12 && extras
&& extras
[i
].name
; i
++)
2100 extra_v
[j
++] = extras
[i
].key
;
2102 plen
= mlwrite(prompt
, NULL
); /* paint prompt */
2107 dline
.vused
= ucs4_strlen(buf
);
2108 dline
.dwid
= term
.t_ncol
- plen
;
2109 dline
.row
= term
.t_nrow
- term
.t_mrow
;
2112 dline
.dlen
= 2 * dline
.dwid
+ 100;
2114 dline
.dl
= (UCS
*) fs_get(dline
.dlen
* sizeof(UCS
));
2115 dline
.olddl
= (UCS
*) fs_get(dline
.dlen
* sizeof(UCS
));
2116 memset(dline
.dl
, 0, dline
.dlen
* sizeof(UCS
));
2117 memset(dline
.olddl
, 0, dline
.dlen
* sizeof(UCS
));
2119 dline
.movecursor
= movecursor
;
2120 dline
.writechar
= writeachar
;
2123 dline
.vlen
= nbuf
-1;
2126 b
= &buf
[(flg
& QBOBUF
) ? 0 : ucs4_strlen(buf
)];
2128 wkeyhelp(menu_mlreply
); /* paint generic menu */
2130 sgarbk
= 1; /* mark menu dirty */
2132 if(pcolors
&& pcolors
->prcp
2133 && pico_is_good_colorpair(pcolors
->prcp
)){
2134 lastc
= pico_get_cur_color();
2135 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
2142 line_paint(b
-buf
, &dline
, NULL
);
2146 mouse_in_content(KEY_MOUSE
, -1, -1, 0x5, 0);
2147 register_mfunc(mouse_in_content
,
2148 term
.t_nrow
- term
.t_mrow
, plen
,
2149 term
.t_nrow
- term
.t_mrow
, term
.t_ncol
-1);
2152 mswin_allowpaste(MSWIN_PASTE_LINE
);
2154 while((c
= GetKey()) == NODATA
)
2158 clear_mfunc(mouse_in_content
);
2161 mswin_allowpaste(MSWIN_PASTE_DISABLE
);
2164 switch(c
= normalize_cmd(c
, rfkm
, 1)){
2165 case (CTRL
|'A') : /* CTRL-A beginning */
2170 case (CTRL
|'B') : /* CTRL-B back a char */
2179 case (CTRL
|'C') : /* CTRL-C abort */
2180 pputs_utf8(_("ABORT"), 1);
2185 case (CTRL
|'E') : /* CTRL-E end of line */
2187 b
= &buf
[ucs4_strlen(buf
)];
2190 case (CTRL
|'F') : /* CTRL-F forward a char*/
2199 case (CTRL
|'G') : /* CTRL-G help */
2200 if(term
.t_mrow
== 0 && km_popped
== 0){
2201 movecursor(term
.t_nrow
-2, 0);
2203 sgarbk
= 1; /* mark menu dirty */
2207 (void) pico_set_colorp(lastc
, PSC_NONE
);
2208 free_color_pair(&lastc
);
2213 wkeyhelp(menu_mlreply
); /* paint generic menu */
2214 plen
= mlwrite(prompt
, NULL
); /* paint prompt */
2215 if(pcolors
&& pcolors
->prcp
2216 && pico_is_good_colorpair(pcolors
->prcp
)){
2217 lastc
= pico_get_cur_color();
2218 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
2227 pputs_utf8(_("HELP"), 1);
2228 return_val
= HELPCH
;
2231 case (CTRL
|'H') : /* CTRL-H backspace */
2232 case 0x7f : /* rubout */
2240 case (CTRL
|'D') : /* CTRL-D delete char */
2250 do /* blat out left char */
2252 while(b
[i
++] != '\0');
2255 case (CTRL
|'L') : /* CTRL-L redraw */
2256 return_val
= (CTRL
|'L');
2259 case (CTRL
|'K') : /* CTRL-K kill line */
2266 case F1
: /* sort of same thing */
2267 return_val
= HELPCH
;
2270 case (CTRL
|'M') : /* newline */
2271 return_val
= changed
;
2279 mouse_get_last (NULL
, &mp
);
2281 /* The clicked line have anything special on it? */
2283 case M_BUTTON_LEFT
: /* position cursor */
2284 mp
.col
-= plen
; /* normalize column */
2285 if(mp
.col
>= 0 && mp
.col
<= ucs4_strlen(buf
))
2290 case M_BUTTON_RIGHT
:
2292 mswin_allowpaste(MSWIN_PASTE_LINE
);
2293 mswin_paste_popup();
2294 mswin_allowpaste(MSWIN_PASTE_DISABLE
);
2298 case M_BUTTON_MIDDLE
: /* NO-OP for now */
2299 default: /* just ignore */
2309 /* look for match in extra_v */
2310 for(i
= 0; i
< 12; i
++)
2311 if(c
&& c
== extra_v
[i
]){
2318 if(c
& (CTRL
| FUNC
)){ /* bag ctrl_special chars */
2323 if(flg
&QNODQT
){ /* reject double quotes? */
2330 if(dline
.vused
>= nbuf
-1){
2335 do /* blat out left char */
2347 (void) pico_set_colorp(lastc
, PSC_NONE
);
2348 free_color_pair(&lastc
);
2357 movecursor(term
.t_nrow
, 0);
2364 fs_give((void **) &dline
.dl
);
2367 fs_give((void **) &dline
.olddl
);
2374 emlwwrite(char *utf8message
, EML
*eml
)
2377 emlwrite(utf8message
, eml
);
2381 emlwrite(char *utf8message
, EML
*eml
)
2385 message
= utf8_to_ucs4_cpystr(utf8message
? utf8message
: "");
2387 emlwrite_ucs4(message
, eml
);
2390 fs_give((void **) &message
);
2395 * emlwrite() - write the message string to the error half of the screen
2396 * center justified. much like mlwrite (which is still used
2397 * to paint the line for prompts and such), except it center
2401 emlwrite_ucs4(UCS
*message
, EML
*eml
)
2405 COLOR_PAIR
*lastc
= NULL
;
2406 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
2410 if(!(message
&& *message
) || term
.t_nrow
< 2)
2411 return; /* nothing to write or no space to write, bag it */
2415 width
= ucs4_str_width(message
);
2418 * next, figure out where the to move the cursor so the message
2419 * comes out centered
2421 if((ap
=ucs4_strchr(message
, '%')) != NULL
){
2426 width
+= (eml
&& eml
->c
) ? wcellwidth(eml
->c
) : 1;
2429 width
+= dumbroot(eml
? eml
->d
: 0, 10);
2432 width
+= dumblroot(eml
? eml
->l
: 0L, 10);
2435 width
+= dumbroot(eml
? eml
->d
: 0, 8);
2438 width
+= dumbroot(eml
? eml
->d
: 0, 16);
2440 case 's': /* string arg is UTF-8 */
2441 width
+= (eml
&& eml
->s
) ? utf8_width(eml
->s
) : 2;
2446 if(width
+4 <= term
.t_ncol
)
2447 movecursor(term
.t_nrow
-term
.t_mrow
, (term
.t_ncol
- (width
+ 4))/2);
2449 movecursor(term
.t_nrow
-term
.t_mrow
, 0);
2451 if(pcolors
&& pcolors
->stcp
2452 && pico_is_good_colorpair(pcolors
->stcp
)){
2453 lastc
= pico_get_cur_color();
2454 (void) pico_set_colorp(pcolors
->stcp
, PSC_NONE
);
2459 pputs_utf8("[ ", 1);
2460 while (*bufp
!= '\0' && ttcol
< term
.t_ncol
-2){
2463 else if(*bufp
== '%'){
2469 pputs_utf8("%c", 0);
2473 mlputi(eml
? eml
->d
: 0, 10);
2476 mlputli(eml
? eml
->l
: 0L, 10);
2479 mlputi(eml
? eml
->d
: 0, 16);
2482 mlputi(eml
? eml
->d
: 0, 8);
2485 pputs_utf8((eml
&& eml
->s
) ? eml
->s
: "%s", 0);
2498 pputs_utf8(" ]", 1);
2501 (void) pico_set_colorp(lastc
, PSC_NONE
);
2502 free_color_pair(&lastc
);
2514 mlwrite_utf8(char *utf8fmt
, void *arg
)
2519 fmt
= utf8_to_ucs4_cpystr(utf8fmt
? utf8fmt
: "");
2520 ret
= mlwrite(fmt
, arg
);
2522 fs_give((void **) &fmt
);
2529 * Write a message into the message line. Keep track of the physical cursor
2530 * position. A small class of printf like format items is handled. Assumes the
2531 * stack grows down; this assumption is made by the "++" in the argument scan
2532 * loop. Set the "message line" flag TRUE.
2535 mlwrite(UCS
*fmt
, void *arg
)
2540 COLOR_PAIR
*lastc
= NULL
;
2541 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
2544 * the idea is to only highlight if there is something to show
2547 movecursor(ttrow
, 0);
2549 if(pcolors
&& pcolors
->prcp
2550 && pico_is_good_colorpair(pcolors
->prcp
)){
2551 lastc
= pico_get_cur_color();
2552 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
2559 while ((c
= *fmt
++) != 0) {
2567 mlputi(*(int *)ap
, 10);
2572 mlputi(*(int *)ap
, 8);
2577 mlputi(*(int *)ap
, 16);
2582 mlputli(*(long *)ap
, 10);
2587 pputs_utf8(*(char **)ap
, 1);
2588 ap
+= sizeof(char *);
2594 ttcol
+= (ww
>= 0 ? ww
: 1);
2600 while(ttcol
< term
.t_ncol
)
2603 movecursor(term
.t_nrow
- term
.t_mrow
, ret
);
2606 (void) pico_set_colorp(lastc
, PSC_NONE
);
2607 free_color_pair(&lastc
);
2620 * Write out an integer, in the specified radix. Update the physical cursor
2621 * position. This will not handle any negative numbers; maybe it should.
2624 mlputi(int i
, int r
)
2627 static char hexdigits
[] = "0123456789ABCDEF";
2639 pputc(hexdigits
[i
%r
], 1);
2644 * do the same except as a long integer.
2647 mlputli(long l
, int r
)
2661 pputc((int)(l
%r
)+'0', 1);
2666 unknown_command(UCS c
)
2668 char buf
[10], ch
, *s
;
2677 else if(c
& CTRL
&& c
>= (CTRL
|'@') && c
<= (CTRL
|'_')){
2678 ch
= c
- (CTRL
|'@') + '@';
2679 snprintf(s
, sizeof(buf
), "^%c", ch
);
2683 case ' ' : s
= "SPACE"; break;
2684 case '\033' : s
= "ESC"; break;
2685 case '\177' : s
= "DEL"; break;
2686 case ctrl('I') : s
= "TAB"; break;
2687 case ctrl('J') : s
= "LINEFEED"; break;
2688 case ctrl('M') : s
= "RETURN"; break;
2689 case ctrl('Q') : s
= "XON"; break;
2690 case ctrl('S') : s
= "XOFF"; break;
2691 case KEY_UP
: s
= "Up Arrow"; break;
2692 case KEY_DOWN
: s
= "Down Arrow"; break;
2693 case KEY_RIGHT
: s
= "Right Arrow"; break;
2694 case KEY_LEFT
: s
= "Left Arrow"; break;
2695 case CTRL
|KEY_UP
: s
= "Ctrl-Up Arrow"; break;
2696 case CTRL
|KEY_DOWN
: s
= "Ctrl-Down Arrow"; break;
2697 case CTRL
|KEY_RIGHT
: s
= "Ctrl-Right Arrow"; break;
2698 case CTRL
|KEY_LEFT
: s
= "Ctrl-Left Arrow"; break;
2699 case KEY_PGUP
: s
= "Prev Page"; break;
2700 case KEY_PGDN
: s
= "Next Page"; break;
2701 case KEY_HOME
: s
= "Home"; break;
2702 case KEY_END
: s
= "End"; break;
2703 case KEY_DEL
: s
= "Delete"; break; /* Not necessary DEL! */
2716 snprintf(s
, sizeof(buf
), "F%ld", (long) (c
- PF1
+ 1));
2721 utf8_put((unsigned char *) s
, (unsigned long) c
);
2727 emlwrite("Unknown Command: %s", &eml
);
2733 * scrolldown - use stuff to efficiently move blocks of text on the
2734 * display, and update the pscreen array to reflect those
2737 * wp is the window to move in
2738 * r is the row at which to begin scrolling
2739 * n is the number of lines to scrol
2742 scrolldown(WINDOW
*wp
, int r
, int n
)
2747 register VIDEO
*vp1
;
2748 register VIDEO
*vp2
;
2758 if(r
> wp
->w_toprow
)
2759 vscreen
[r
-1]->v_flag
|= VFCHG
;
2760 l
= wp
->w_toprow
+wp
->w_ntrows
-r
;
2765 for(i
=l
-n
-1; i
>= 0; i
--){
2767 vp2
= pscreen
[r
+i
+n
];
2768 memcpy(vp2
, vp1
, term
.t_ncol
* sizeof(CELL
));
2773 #endif /* TERMCAP */
2778 * scrollup - use tcap stuff to efficiently move blocks of text on the
2779 * display, and update the pscreen array to reflect those
2783 scrollup(WINDOW
*wp
, int r
, int n
)
2787 register VIDEO
*vp1
;
2788 register VIDEO
*vp2
;
2801 if(!(r
+i
+n
< wp
->w_toprow
+wp
->w_ntrows
))
2805 if(!((i
< wp
->w_ntrows
-n
)&&(r
+i
+n
< wp
->w_toprow
+wp
->w_ntrows
)))
2808 vp1
= pscreen
[r
+i
+n
];
2810 memcpy(vp2
, vp1
, term
.t_ncol
* sizeof(CELL
));
2813 pprints(wp
->w_toprow
+wp
->w_ntrows
-n
, wp
->w_toprow
+wp
->w_ntrows
-1);
2816 #endif /* TERMCAP */
2821 * print spaces in the physical screen starting from row abs(n) working in
2822 * either the positive or negative direction (depending on sign of n).
2825 pprints(int x
, int y
)
2831 for(i
= x
;i
<= y
; ++i
){
2832 for(j
= 0; j
< term
.t_ncol
; j
++){
2833 pscreen
[i
]->v_text
[j
].c
= ' ';
2834 pscreen
[i
]->v_text
[j
].a
= 0;
2839 for(i
= x
;i
>= y
; --i
){
2840 for(j
= 0; j
< term
.t_ncol
; j
++){
2841 pscreen
[i
]->v_text
[j
].c
= ' ';
2842 pscreen
[i
]->v_text
[j
].a
= 0;
2852 * doton - return the physical line number that the dot is on in the
2853 * current window, and by side effect the number of lines remaining
2856 doton(int *r
, unsigned *chs
)
2859 register LINE
*lp
= curwp
->w_linep
;
2862 assert(r
!= NULL
&& chs
!= NULL
);
2865 while(i
++ < curwp
->w_ntrows
){
2866 if(lp
== curwp
->w_dotp
)
2869 if(lp
== curwp
->w_bufp
->b_linep
){
2874 (*chs
) += llength(lp
);
2876 *r
= i
- l
- term
.t_mrow
;
2877 return(l
+curwp
->w_toprow
);
2883 * resize_pico - given new window dimensions, allocate new resources
2886 resize_pico(int row
, int col
)
2888 int old_nrow
, old_ncol
;
2892 old_nrow
= term
.t_nrow
;
2893 old_ncol
= term
.t_ncol
;
2898 if (old_ncol
== term
.t_ncol
&& old_nrow
== term
.t_nrow
)
2902 curwp
->w_toprow
= 2;
2903 curwp
->w_ntrows
= term
.t_nrow
- curwp
->w_toprow
- term
.t_mrow
;
2907 fillcol
= Pmaster
->fillcolumn
;
2908 (*Pmaster
->resize
)();
2910 else if(userfillcol
> 0)
2911 fillcol
= userfillcol
;
2913 fillcol
= term
.t_ncol
- 6; /* we control the fill column */
2916 * free unused screen space ...
2918 for(i
=term
.t_nrow
+1; i
<= old_nrow
; ++i
){
2919 free((char *) vscreen
[i
]);
2920 free((char *) pscreen
[i
]);
2924 * realloc new space for screen ...
2926 if((vscreen
=(VIDEO
**)realloc(vscreen
,(term
.t_nrow
+1)*sizeof(VIDEO
*))) == NULL
){
2933 if((pscreen
=(VIDEO
**)realloc(pscreen
,(term
.t_nrow
+1)*sizeof(VIDEO
*))) == NULL
){
2940 for (i
= 0; i
<= term
.t_nrow
; ++i
) {
2942 vp
= (VIDEO
*) realloc(vscreen
[i
], sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2944 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2950 if(old_ncol
< term
.t_ncol
){ /* don't let any garbage in */
2952 vtcol
= (i
< old_nrow
) ? old_ncol
: 0;
2957 vp
= (VIDEO
*) realloc(pscreen
[i
], sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2959 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2968 if(!ResizeBrowser()){
2969 if(Pmaster
&& Pmaster
->headents
){
2973 curwp
->w_flag
|= (WFHARD
| WFMODE
);
2974 pico_refresh(0, 1); /* redraw whole enchilada. */
2975 update(); /* do it */
2983 redraw_pico_for_callback(void)
2991 * showCompTitle - display the anchor line passed in from pine
2998 extern UCS
*pico_anchor
;
2999 COLOR_PAIR
*lastc
= NULL
;
3001 if((bufp
= pico_anchor
) == NULL
)
3004 movecursor(COMPOSER_TITLE_LINE
, 0);
3005 if (Pmaster
->colors
&& Pmaster
->colors
->tbcp
&&
3006 pico_is_good_colorpair(Pmaster
->colors
->tbcp
)){
3007 lastc
= pico_get_cur_color();
3008 (void)pico_set_colorp(Pmaster
->colors
->tbcp
, PSC_NONE
);
3013 while (ttcol
< term
.t_ncol
)
3020 (void)pico_set_colorp(lastc
, PSC_NONE
);
3021 free_color_pair(&lastc
);
3026 movecursor(COMPOSER_TITLE_LINE
+ 1, 0);
3034 * zotdisplay - blast malloc'd space created for display maps
3041 for (i
= 0; i
<= term
.t_nrow
; ++i
){ /* free screens */
3042 free((char *) vscreen
[i
]);
3043 free((char *) pscreen
[i
]);
3046 free((char *) vscreen
);
3047 free((char *) pscreen
);
3053 * nlforw() - returns the number of lines from the top to the dot
3059 register LINE
*lp
= curwp
->w_linep
;
3061 while(lp
!= curwp
->w_dotp
){
3071 * pputc - output the given char, keep track of it on the physical screen
3072 * array, and keep track of the cursor
3075 pputc(UCS c
, /* char to write */
3076 int a
) /* and its attribute */
3078 int ind
, width
, printable_ascii
= 0;
3081 * This is necessary but not sufficient to allow us to draw. Note that
3082 * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
3083 * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
3085 if((ttcol
>= 0 && ttcol
< term
.t_ncol
) && (ttrow
>= 0 && ttrow
<= term
.t_nrow
)){
3088 * Width is the number of screen columns a character will occupy.
3090 if(c
< 0x80 && isprint(c
)){
3095 width
= wcellwidth(c
);
3098 width
= 1; /* will be a '?' */
3100 if(ttcol
+ width
<= term
.t_ncol
){ /* it fits */
3102 * Some terminals scroll when you write in the lower right corner
3103 * of the screen, so don't write there.
3105 if(!(ttrow
== term
.t_nrow
&& ttcol
+width
== term
.t_ncol
)){
3106 (*term
.t_putchar
)(c
); /* write it */
3107 ind
= index_from_col(ttrow
, ttcol
);
3108 pscreen
[ttrow
]->v_text
[ind
].c
= c
; /* keep track of it */
3109 pscreen
[ttrow
]->v_text
[ind
].a
= a
; /* keep track of it */
3114 * Character overlaps right edge of screen. Hopefully the higher
3115 * layers will prevent this but we're making sure.
3117 * We may want to do something like writing a space character
3118 * into the cells that are on the screen. We'll see.
3122 ttcol
= MIN(term
.t_ncol
, ttcol
+width
);
3128 * pputs - print a string and keep track of the cursor
3131 pputs(UCS
*s
, /* string to write */
3132 int a
) /* and its attribute */
3140 pputs_utf8(char *s
, int a
)
3145 ucsstr
= utf8_to_ucs4_cpystr(s
);
3148 fs_give((void **) &ucsstr
);
3155 * peeol - physical screen array erase to end of the line. remember to
3161 int i
, width
= 0, ww
;
3164 if(ttrow
< 0 || ttrow
> term
.t_nrow
)
3171 * Don't clear if we think we are sitting past the last column,
3172 * that erases the last column if we just wrote it.
3174 if(ttcol
< term
.t_ncol
)
3178 * Because the characters are variable width it's a little tricky
3179 * to erase the rest of the line. What we do is add up the
3180 * widths of the characters until we reach ttcol
3181 * then set the rest to the space character.
3183 for(i
= 0; i
< term
.t_ncol
&& width
< ttcol
; i
++){
3184 ww
= wcellwidth((UCS
) pscreen
[ttrow
]->v_text
[i
].c
);
3185 width
+= (ww
>= 0 ? ww
: 1);
3188 while(i
< term
.t_ncol
)
3189 pscreen
[ttrow
]->v_text
[i
++] = cl
;
3194 * pscr - return the character cell on the physical screen map on the
3195 * given line, l, and offset, o.
3200 if((l
>= 0 && l
<= term
.t_nrow
) && (o
>= 0 && o
< term
.t_ncol
))
3201 return(&(pscreen
[l
]->v_text
[o
]));
3208 * pclear() - clear the physical screen from row x through row y (inclusive)
3209 * row is zero origin, min row = 0 max row = t_nrow
3210 * Clear whole screen -- pclear(0, term.t_nrow)
3211 * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
3212 * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
3215 pclear(int x
, int y
)
3219 x
= MIN(MAX(0, x
), term
.t_nrow
);
3220 y
= MIN(MAX(0, y
), term
.t_nrow
);
3222 for(i
=x
; i
<= y
; i
++){
3230 * dumbroot - just get close
3233 dumbroot(int x
, int b
)
3238 return(dumbroot(x
/b
, b
) + 1);
3243 * dumblroot - just get close
3246 dumblroot(long x
, int b
)
3251 return(dumblroot(x
/b
, b
) + 1);
3256 * pinsertc - use optimized insert, fixing physical screen map.
3257 * returns true if char written, false otherwise
3265 if(ttrow
< 0 || ttrow
> term
.t_nrow
)
3268 if(o_insert((UCS
) c
.c
)){ /* if we've got it, use it! */
3269 p
= pscreen
[ttrow
]->v_text
; /* then clean up physical screen */
3271 ind
= index_from_col(ttrow
, ttcol
);
3273 for(i
= term
.t_ncol
-1; i
> ind
; i
--)
3274 p
[i
] = p
[i
-1]; /* shift right */
3276 p
[ind
] = c
; /* insert new char */
3278 ww
= wcellwidth((UCS
) c
.c
);
3279 ttcol
+= (ww
>= 0 ? ww
: 1);
3289 * pdel - use optimized delete to rub out the current char and
3290 * fix the physical screen array.
3291 * returns true if optimized the delete, false otherwise
3299 if(ttrow
< 0 || ttrow
> term
.t_nrow
)
3302 if(TERM_DELCHAR
){ /* if we've got it, use it! */
3303 p
= pscreen
[ttrow
]->v_text
;
3304 ind
= index_from_col(ttrow
, ttcol
);
3308 w
= wcellwidth((UCS
) p
[ind
].c
);
3309 w
= (w
>= 0 ? w
: 1);
3312 for(i
= 0; i
< w
; i
++){
3313 (*term
.t_putchar
)('\b'); /* move left a char */
3314 o_delete(); /* and delete it */
3317 /* then clean up physical screen */
3318 for(i
=ind
; i
< term
.t_ncol
-1; i
++)
3334 * wstripe - write out the given string at the given location, and reverse
3335 * video on flagged characters. Does the same thing as pine's
3338 * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
3339 * only if you want to put the tildes before multi-byte chars.
3342 wstripe(int line
, int column
, char *utf8pmt
, int key
)
3348 COLOR_PAIR
*lastc
= NULL
;
3349 COLOR_PAIR
*kncp
= NULL
;
3350 COLOR_PAIR
*klcp
= NULL
;
3352 if(line
< 0 || line
> term
.t_nrow
)
3355 if (Pmaster
&& Pmaster
->colors
){
3356 if(pico_is_good_colorpair(Pmaster
->colors
->klcp
))
3357 klcp
= Pmaster
->colors
->klcp
;
3359 if(klcp
&& pico_is_good_colorpair(Pmaster
->colors
->kncp
))
3360 kncp
= Pmaster
->colors
->kncp
;
3363 klcp
= Pcolors
->klcp
;
3364 kncp
= Pcolors
->kncp
;
3367 lastc
= pico_get_cur_color();
3368 ucs4pmt
= utf8_to_ucs4_cpystr(utf8pmt
);
3369 l
= ucs4_strlen(ucs4pmt
);
3371 if(i
>= term
.t_ncol
|| col
>= term
.t_ncol
|| j
>= l
){
3372 if(lastc
) free_color_pair(&lastc
);
3373 if(ucs4pmt
) fs_give((void **) &ucs4pmt
);
3374 return; /* equal strings */
3377 if(ucs4pmt
[j
] == (UCS
) key
)
3380 if (pscr(line
, i
) == NULL
){
3381 if(lastc
) free_color_pair(&lastc
);
3382 if(ucs4pmt
) fs_give((void **) &ucs4pmt
);
3386 if(pscr(line
, i
)->c
!= ucs4pmt
[j
]){
3387 if(j
>= 1 && ucs4pmt
[j
-1] == (UCS
) key
)
3392 ww
= wcellwidth((UCS
) pscr(line
, i
)->c
);
3393 col
+= (ww
>= 0 ? ww
: 1);
3398 movecursor(line
, column
+col
);
3399 if(klcp
) (void)pico_set_colorp(klcp
, PSC_NONE
);
3402 if(*u
== (UCS
) key
){
3405 (void)pico_set_colorp(kncp
, PSC_NONE
);
3407 (void)(*term
.t_rev
)(1);
3411 (void)pico_set_colorp(klcp
, PSC_NONE
);
3413 (void)(*term
.t_rev
)(0);
3419 while(*++u
!= '\0');
3422 fs_give((void **) &ucs4pmt
);
3426 (void)pico_set_colorp(lastc
, PSC_NONE
);
3427 free_color_pair(&lastc
);
3435 * wkeyhelp - paint list of possible commands on the bottom
3436 * of the display (yet another pine clone)
3437 * NOTE: function key mode is handled here since all the labels
3440 * The KEYMENU definitions have names and labels defined as UTF-8 strings,
3441 * and wstripe expects UTF-8.
3444 wkeyhelp(KEYMENU
*keymenu
)
3446 char *obufp
, *p
, fkey
[4];
3447 char linebuf
[2*NLINE
]; /* "2" is for space for invert tokens */
3448 int row
, slot
, tspace
, adjusted_tspace
, nspace
[6], index
, n
;
3454 pico_config_menu_items (keymenu
);
3457 if(term
.t_mrow
== 0)
3464 * Calculate amount of space for the names column by column...
3466 for(index
= 0; index
< 6; index
++)
3467 if(!(gmode
&MDFKEY
)){
3468 nspace
[index
] = (keymenu
[index
].name
)
3469 ? utf8_width(keymenu
[index
].name
) : 0;
3470 if(keymenu
[index
+6].name
3471 && (n
= utf8_width(keymenu
[index
+6].name
)) > nspace
[index
])
3477 nspace
[index
] = (index
< 4) ? 3 : 4;
3479 tspace
= term
.t_ncol
/6; /* total space for each item */
3482 * Avoid writing in bottom right corner so we won't scroll screens that
3483 * scroll when you do that. The way this is setup, we won't do that
3484 * unless the number of columns is evenly divisible by 6.
3486 adjusted_tspace
= (6 * tspace
== term
.t_ncol
) ? tspace
- 1 : tspace
;
3489 for(row
= 0; row
<= 1; row
++){
3491 obufp
= &linebuf
[0];
3492 for(slot
= 0; slot
< 6; slot
++){
3493 if(keymenu
[index
].name
&& keymenu
[index
].label
){
3495 char this_label
[200], tmp_label
[200];
3497 if(keymenu
[index
].label
[0] == '[' && keymenu
[index
].label
[(l
=strlen(keymenu
[index
].label
))-1] == ']' && l
> 2){
3498 strncpy(tmp_label
, &keymenu
[index
].label
[1], MIN(sizeof(tmp_label
),l
-2));
3499 tmp_label
[MIN(sizeof(tmp_label
)-1,l
-2)] = '\0';
3500 snprintf(this_label
, sizeof(this_label
), "[%s]", _(tmp_label
));
3503 strncpy(this_label
, _(keymenu
[index
].label
), sizeof(this_label
));
3505 this_label
[sizeof(this_label
)-1] = '\0';
3509 snprintf(fkey
, sizeof(fkey
), "F%d", (2 * slot
) + row
+ 1);
3512 p
= keymenu
[index
].name
;
3514 snprintf(nbuf
, sizeof(nbuf
), "%.*s %s", nspace
[slot
], p
, this_label
);
3516 (gmode
&MDFKEY
) ? F1
+ (2 * slot
) + row
:
3517 (keymenu
[index
].name
[0] == '^')
3518 ? (CTRL
| keymenu
[index
].name
[1])
3519 : (keymenu
[index
].name
[0] == 'S'
3520 && !strcmp(keymenu
[index
].name
, "Spc"))
3522 : keymenu
[index
].name
[0],
3524 term
.t_nrow
- 1 + row
, (slot
* tspace
),
3526 (Pmaster
&& Pmaster
->colors
)
3527 ? Pmaster
->colors
->kncp
: NULL
,
3528 (Pmaster
&& Pmaster
->colors
)
3529 ? Pmaster
->colors
->klcp
: NULL
);
3533 while(p
&& *p
&& n
--){
3534 *obufp
++ = '~'; /* insert "invert" token */
3542 n
= ((slot
== 5 && row
== 1) ? adjusted_tspace
3543 : tspace
) - nspace
[slot
];
3544 while(p
&& *p
&& n
-- > 0)
3551 n
= (slot
== 5 && row
== 1) ? adjusted_tspace
: tspace
;
3556 register_key(index
, NODATA
, "", NULL
, 0, 0, 0, NULL
, NULL
);
3564 wstripe(term
.t_nrow
- 1 + row
, 0, linebuf
, '~');
3570 * This returns the screen width between pstart (inclusive) and
3571 * pend (exclusive) where the pointers point into an array of CELLs.
3574 cellwidth_ptr_to_ptr(CELL
*pstart
, CELL
*pend
)
3581 for(p
= pstart
; p
< pend
; p
++){
3582 ww
= wcellwidth((UCS
) p
->c
);
3583 width
+= (ww
>= 0 ? ww
: 1);
3591 * This returns the virtual screen width in row from index a to b (exclusive).
3594 vcellwidth_a_to_b(int row
, int a
, int b
)
3596 CELL
*pstart
, *pend
;
3599 if(row
< 0 || row
> term
.t_nrow
)
3605 a
= MIN(MAX(0, a
), term
.t_ncol
-1);
3606 b
= MIN(MAX(0, a
), term
.t_ncol
); /* b is past where we stop */
3609 pstart
= &vp
->v_text
[a
];
3610 pend
= &vp
->v_text
[b
];
3612 return(cellwidth_ptr_to_ptr(pstart
, pend
));
3617 * This returns the physical screen width in row from index a to b (exclusive).
3620 pcellwidth_a_to_b(int row
, int a
, int b
)
3622 CELL
*pstart
, *pend
;
3625 if(row
< 0 || row
> term
.t_nrow
)
3631 a
= MIN(MAX(0, a
), term
.t_ncol
-1);
3632 b
= MIN(MAX(0, a
), term
.t_ncol
); /* b is past where we stop */
3635 pstart
= &vp
->v_text
[a
];
3636 pend
= &vp
->v_text
[b
];
3638 return(cellwidth_ptr_to_ptr(pstart
, pend
));
3643 index_from_col(int row
, int col
)
3645 CELL
*p_start
, *p_end
, *p_limit
;
3646 int w_consumed
= 0, w
, done
= 0;
3648 if(row
< 0 || row
> term
.t_nrow
)
3651 p_end
= p_start
= pscreen
[row
]->v_text
;
3652 p_limit
= p_start
+ term
.t_ncol
;
3655 while(!done
&& p_end
< p_limit
&& p_end
->c
&& w_consumed
<= col
){
3656 w
= wcellwidth((UCS
) p_end
->c
);
3657 w
= (w
>= 0 ? w
: 1);
3658 if(w_consumed
+ w
<= col
){
3666 /* MIN and MAX just to be sure */
3667 return(MIN(MAX(0, p_end
- p_start
), term
.t_ncol
-1));
3673 pico_config_menu_items (KEYMENU
*keymenu
)
3679 mswin_menuitemclear ();
3681 /* keymenu's seem to be hardcoded at 12 entries. */
3682 for (i
= 0, k
= keymenu
; i
< 12; ++i
, ++k
) {
3683 if (k
->name
!= NULL
&& k
->label
!= NULL
&&
3684 k
->menuitem
!= KS_NONE
) {
3686 if (k
->name
[0] == '^')
3687 key
= CTRL
| k
->name
[1];
3688 else if (strcmp(k
->name
, "Ret") == 0)
3693 mswin_menuitemadd (key
, k
->label
, k
->menuitem
, 0);
3699 * Update the scroll range and position. (exported)
3701 * This is where curbp->b_linecnt is really managed. With out this function
3702 * to count the number of lines when needed curbp->b_linecnt will never
3703 * really be correct. BUT, this function is only compiled into the
3704 * windows version, so b_linecnt will only ever be right in the windows
3705 * version. OK for now because that is the only version that
3706 * looks at b_linecnt.
3709 update_scroll (void)
3714 static LINE
*last_top_line
= NULL
;
3715 static long last_scroll_pos
= -1;
3718 if (ComposerEditing
) {
3719 /* Editing header - don't allow scroll bars. */
3720 mswin_setscrollrange (0, 0);
3726 * Count the number of lines in the current buffer. Done when:
3728 * when told to recount: curbp->b_linecnt == -1
3729 * when the top line changed: curwp->w_linep != last_top_line
3730 * when we don't know the scroll pos: last_scroll_pos == -1
3732 * The first line in the list is a "place holder" line and is not
3733 * counted. The list is circular, when we return the to place
3734 * holder we have reached the end.
3736 if(curbp
->b_linecnt
== -1 || curwp
->w_linep
!= last_top_line
3737 || last_scroll_pos
== -1) {
3740 for (lp
= lforw (curbp
->b_linep
); lp
!= curbp
->b_linep
;
3742 if (lp
== curwp
->w_linep
)
3743 scr_pos
= scr_range
;
3748 curbp
->b_linecnt
= scr_range
;
3749 last_scroll_pos
= scr_pos
;
3750 last_top_line
= curwp
->w_linep
;
3754 * Set new scroll range and position.
3756 mswin_setscrollrange (curwp
->w_ntrows
- 2, curbp
->b_linecnt
- 1);
3757 mswin_setscrollpos (last_scroll_pos
);
3760 #endif /* _WINDOWS */