* Color support for the default composer and Pico. If users have
[alpine.git] / pico / display.c
blob7cd309092715cb751382f21632db3423a9e43983
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: display.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 * Program: Display functions
23 * The functions in this file handle redisplay. There are two halves, the
24 * ones that update the virtual display screen, and the ones that make the
25 * physical display screen the same as the virtual display screen. These
26 * functions use hints that are left in the windows by the commands.
30 #include "../c-client/mail.h"
31 #include "../c-client/utf8.h"
33 #ifdef _WINDOWS
34 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
35 #undef ERROR
36 #endif
38 #include "headers.h"
39 #include "../pith/charconv/filesys.h"
40 #include "../pith/charconv/utf8.h"
43 void vtmove(int, int);
44 void vtputc(CELL);
45 void vteeol(void);
46 void updateline(int, CELL *, CELL *, short *);
47 void updext(void);
48 void mlputi(int, int);
49 void pprints(int, int);
50 void mlputli(long, int);
51 void showCompTitle(void);
52 int nlforw(void);
53 int dumbroot(int, int);
54 int dumblroot(long, int);
55 unsigned cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend);
56 unsigned vcellwidth_a_to_b(int row, int a, int b);
57 int window_signature_block(WINDOW *wp);
58 #ifdef _WINDOWS
59 void pico_config_menu_items (KEYMENU *);
60 int update_scroll (void);
61 #endif /* _WINDOWS */
65 * Standard pico keymenus...
67 static KEYMENU menu_pico[] = {
68 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^O", N_("WriteOut"), KS_SAVEFILE},
69 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
70 {"^K", N_("Cut Text"), KS_NONE}, {"^C", N_("Cur Pos"), KS_CURPOSITION},
71 {"^X", N_("Exit"), KS_EXIT}, {"^J", N_("Justify"), KS_JUSTIFY},
72 {"^W", N_("Where is"), KS_WHEREIS}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
73 {"^U", NULL, KS_NONE},
74 #ifdef SPELLER
75 {"^T", N_("To Spell"), KS_SPELLCHK}
76 #else
77 {"^D", N_("Del Char"), KS_NONE}
78 #endif
80 #define UNCUT_KEY 10
83 static KEYMENU menu_compose[] = {
84 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^X", NULL, KS_SEND},
85 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
86 {"^K", N_("Cut Text"), KS_NONE}, {"^O", N_("Postpone"), KS_POSTPONE},
87 /* TRANSLATORS: Justify is to reformat a paragraph automatically */
88 {"^C", N_("Cancel"), KS_CANCEL}, {"^J", N_("Justify"), KS_JUSTIFY},
89 {NULL, NULL, KS_NONE}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
90 {"^U", NULL, KS_NONE},
91 #ifdef SPELLER
92 {"^T", N_("To Spell"), KS_SPELLCHK}
93 #else
94 {"^D", N_("Del Char"), KS_NONE}
95 #endif
97 #define EXIT_KEY 1
98 #define PSTPN_KEY 5
99 #define WHERE_KEY 8
103 * Definition's for pico's modeline
105 #define PICO_TITLE " UW PICO %s"
106 #define PICO_MOD_MSG "Modified"
107 #define PICO_NEWBUF_MSG "New Buffer"
109 #define WFDEBUG 0 /* Window flag debug. */
111 #define VFCHG 0x0001 /* Changed flag */
112 #define VFEXT 0x0002 /* extended (beyond column 80) */
113 #define VFREV 0x0004 /* reverse video status */
114 #define VFREQ 0x0008 /* reverse video request */
115 #define VFSIG 0x0010 /* in signature block */
116 #define VFNOR 0x0020 /* in not signature block */
117 #define VFQUO 0x0040 /* in quoted text */
119 int vtrow = 0; /* Row location of SW cursor */
120 int vtcol = 0; /* Column location of SW cursor */
121 int vtind = 0; /* Index into row array of SW cursor */
122 int ttrow = FARAWAY; /* Row location of HW cursor */
123 int ttcol = FARAWAY; /* Column location of HW cursor */
124 int lbound = 0; /* leftmost column of current line
125 being displayed */
127 VIDEO **vscreen; /* Virtual screen. */
128 VIDEO **pscreen; /* Physical screen. */
132 * Initialize the data structures used by the display code. The edge vectors
133 * used to access the screens are set up. The operating system's terminal I/O
134 * channel is set up. All the other things get initialized at compile time.
135 * The original window has "WFCHG" set, so that it will get completely
136 * redrawn on the first call to "update".
139 vtinit(void)
141 int i, j;
142 VIDEO *vp;
143 CELL ac;
144 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
146 ac.c = '\0';
147 ac.a = 0;
149 if(Pmaster == NULL)
150 vtterminalinfo(gmode & MDTCAPWINS);
152 (*term.t_open)();
154 (*term.t_rev)(FALSE);
155 vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
156 memset(vscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
157 if (vscreen == NULL){
158 emlwrite("Allocating memory for virtual display failed.", NULL);
159 return(FALSE);
162 pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
163 memset(pscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
164 if (pscreen == NULL){
165 free((void *)vscreen);
166 emlwrite("Allocating memory for physical display failed.", NULL);
167 return(FALSE);
171 for (i = 0; i <= term.t_nrow; ++i) {
172 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
174 if (vp == NULL){
175 free((void *)vscreen);
176 free((void *)pscreen);
177 emlwrite("Allocating memory for virtual display lines failed.",
178 NULL);
179 return(FALSE);
181 else
182 for(j = 0; j < term.t_ncol; j++)
183 vp->v_text[j] = ac;
185 vp->v_flag = 0;
186 vscreen[i] = vp;
188 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
190 if (vp == NULL){
191 free((void *)vscreen[i]);
192 while(--i >= 0){
193 free((void *)vscreen[i]);
194 free((void *)pscreen[i]);
197 free((void *)vscreen);
198 free((void *)pscreen);
199 emlwrite("Allocating memory for physical display lines failed.",
200 NULL);
201 return(FALSE);
203 else
204 for(j = 0; j < term.t_ncol; j++)
205 vp->v_text[j] = ac;
207 vp->v_flag = 0;
208 pscreen[i] = vp;
211 return(TRUE);
215 vtterminalinfo(int termcap_wins)
217 return((term.t_terminalinfo) ? (*term.t_terminalinfo)(termcap_wins)
218 : (Pmaster ? 0 : TRUE));
223 * Clean up the virtual terminal system, in anticipation for a return to the
224 * operating system. Move down to the last line and clear it out (the next
225 * system prompt will be written in the line). Shut down the channel to the
226 * terminal.
228 void
229 vttidy(void)
231 movecursor(term.t_nrow-1, 0);
232 peeol();
233 movecursor(term.t_nrow, 0);
234 peeol();
235 (*term.t_close)();
240 * Set the virtual cursor to the specified row and column on the virtual
241 * screen. There is no checking for nonsense values; this might be a good
242 * idea during the early stages.
244 void
245 vtmove(int row, int col)
247 vtrow = row;
248 vtcol = col;
250 if(vtcol < 0)
251 vtind = -1;
252 else if(vtcol == 0)
253 vtind = vtcol;
254 else{
256 * This is unused so don't worry about it.
258 assert(0);
264 * Write a character to the virtual screen. The virtual row and column are
265 * updated. If the line is too long put a "$" in the last column. This routine
266 * only puts printing characters into the virtual terminal buffers. Only
267 * column overflow is checked.
269 void
270 vtputc(CELL c)
272 VIDEO *vp;
273 CELL ac;
274 int w;
276 vp = vscreen[vtrow];
277 ac.c = '\0';
278 ac.a = c.a;
279 ac.d = c.d;
281 if (vtcol >= term.t_ncol) {
283 * What's this supposed to be doing? This sets vtcol
284 * to the even multiple of 8 >= vtcol. Why are we doing that?
285 * Must make tab work correctly.
286 * 24 -> 24
287 * 25 -> 32
288 * ...
289 * 31 -> 32
290 * 32 -> 32
291 * 33 -> 40
293 vtcol = (vtcol + 0x07) & ~0x07;
294 ac.c = '$';
297 * If we get to here that means that there must be characters
298 * past the right hand edge, so we want to put a $ character
299 * in the last visible character. It would be nice to replace
300 * the last visible character by a double-$ if it is double-width
301 * but we aren't doing that because we'd have to add up the widths
302 * starting at the left hand margin each time through.
304 if(vtind > 0 && vtind <= term.t_ncol)
305 vp->v_text[vtind-1] = ac;
307 else if (c.c == '\t') {
308 ac.c = ' ';
309 do {
310 vtputc(ac);
312 while (((vtcol + (vtrow==currow ? lbound : 0)) & 0x07) != 0 && vtcol < term.t_ncol);
314 else if (ISCONTROL(c.c)){
315 ac.c = '^';
316 vtputc(ac);
317 ac.c = ((c.c & 0x7f) | 0x40);
318 vtputc(ac);
320 else{
323 * Have to worry about what happens if we skip over 0
324 * with a double-width character. There may be a better
325 * place to be setting vtind, or maybe we could make do
326 * without it.
329 w = wcellwidth((UCS) c.c);
330 w = (w >= 0 ? w : 1);
332 if(vtcol == 0 || (vtcol < 0 && vtcol + w == 1)){
333 vtind = 0;
334 if(vtcol < 0)
335 vtcol = 0;
339 * Double-width character overlaps right edge.
340 * Replace it with a $.
342 if(vtcol + w > term.t_ncol){
343 ac.c = '$';
344 c = ac;
345 w = 1;
348 if(vtind >= 0 && vtind < term.t_ncol)
349 vp->v_text[vtind++] = c;
351 vtcol += w;
357 * Erase from the end of the software cursor to the end of the line on which
358 * the software cursor is located.
360 void
361 vteeol(void)
363 register VIDEO *vp;
364 CELL c;
366 c.c = '\0';
367 c.a = 0;
368 vp = vscreen[vtrow];
370 if(vtind >= 0)
371 while (vtind < term.t_ncol)
372 vp->v_text[vtind++] = c;
374 vtcol = term.t_ncol;
378 window_signature_block(WINDOW *wp)
380 LINE *lp, *llp;
381 int in_sig, is_sig_start;
382 int change = 0;
384 llp = wp->w_linep;
385 lp = lforw(wp->w_bufp->b_linep);
387 do {
388 in_sig = lback(lp) == wp->w_bufp->b_linep ? 0 : lback(lp)->l_sig;
389 if(in_sig == 0){
390 if(llength(lp) == 3){
391 if(lgetc(lp, 0).c == '-'
392 && lgetc(lp, 1).c == '-'
393 && lgetc(lp, 2).c == ' '){
394 in_sig = 1;
395 is_sig_start = 1;
398 } else {
399 if(lisblank(lp))
400 if(is_sig_start == 0) in_sig = 0;
401 is_sig_start = 0;
403 if(lp->l_sig != in_sig)
404 change++;
405 lp->l_sig = in_sig;
406 lp = lforw(lp);
407 } while(lp != wp->w_bufp->b_linep);
408 wp->w_linep = llp;
409 return change;
414 * Make sure that the display is right. This is a three part process. First,
415 * scan through all of the windows looking for dirty ones. Check the framing,
416 * and refresh the screen. Second, make sure that "currow" and "curcol" are
417 * correct for the current window. Third, make the virtual and physical
418 * screens the same.
420 void
421 update(void)
423 LINE *lp;
424 WINDOW *wp;
425 VIDEO *vp1;
426 VIDEO *vp2;
427 int i;
428 int j;
429 int scroll = 0;
430 int repaint= 0;
431 int quoted;
432 CELL c;
433 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
435 #if TYPEAH
436 if (typahead())
437 return;
438 #endif
440 #ifdef _WINDOWS
441 /* This tells our MS Windows module to not bother updating the
442 * cursor position while a massive screen update is in progress.
444 mswin_beginupdate ();
445 #endif
448 * BUG: setting and unsetting whole region at a time is dumb. fix this.
450 if(curwp->w_markp){
451 unmarkbuffer();
452 markregion(1);
455 wp = wheadp;
457 while (wp != NULL){
458 /* Look at any window with update flags set on. */
460 if(pcolors)
461 repaint = window_signature_block(wp);
462 if (wp->w_flag != 0){
463 /* If not force reframe, check the framing. */
465 if ((wp->w_flag & WFFORCE) == 0){
466 lp = wp->w_linep;
468 for (i = 0; i < wp->w_ntrows; ++i){
469 if (lp == wp->w_dotp)
470 goto out;
472 if (lp == wp->w_bufp->b_linep)
473 break;
475 lp = lforw(lp);
479 /* Not acceptable, better compute a new value for the line at the
480 * top of the window. Then set the "WFHARD" flag to force full
481 * redraw.
483 i = wp->w_force;
485 if (i > 0){
486 --i;
488 if (i >= wp->w_ntrows)
489 i = wp->w_ntrows-1;
491 else if (i < 0){
492 i += wp->w_ntrows;
494 if (i < 0)
495 i = 0;
497 else if(TERM_OPTIMIZE){
499 * find dotp, if its been moved just above or below the
500 * window, use scrollxxx() to facilitate quick redisplay...
502 lp = lforw(wp->w_dotp);
503 if(lp != wp->w_dotp){
504 if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
505 scroll = 1;
507 else {
508 lp = wp->w_linep;
509 for(j=0;j < wp->w_ntrows; ++j){
510 if(lp != wp->w_bufp->b_linep)
511 lp = lforw(lp);
512 else
513 break;
515 if(lp == wp->w_dotp && j == wp->w_ntrows)
516 scroll = 2;
519 j = i = wp->w_ntrows/2;
521 else
522 i = wp->w_ntrows/2;
524 lp = wp->w_dotp;
526 while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
527 --i;
528 lp = lback(lp);
532 * this is supposed to speed things up by using tcap sequences
533 * to efficiently scroll the terminal screen. the thinking here
534 * is that its much faster to update pscreen[] than to actually
535 * write the stuff to the screen...
537 if(TERM_OPTIMIZE){
538 switch(scroll){
539 case 1: /* scroll text down */
540 j = j-i+1; /* add one for dot line */
542 * do we scroll down the header as well? Well, only
543 * if we're not editing the header, we've backed up
544 * to the top, and the composer is not being
545 * displayed...
547 if(Pmaster && Pmaster->headents && !ComposerEditing
548 && (lback(lp) == wp->w_bufp->b_linep)
549 && (ComposerTopLine == COMPOSER_TOP_LINE))
550 j += entry_line(1000, TRUE); /* Never > 1000 headers */
552 scrolldown(wp, -1, j);
553 break;
554 case 2: /* scroll text up */
555 j = wp->w_ntrows - (j-i); /* we chose new top line! */
556 if(Pmaster && j){
558 * do we scroll down the header as well? Well, only
559 * if we're not editing the header, we've backed up
560 * to the top, and the composer is not being
561 * displayed...
563 if(!ComposerEditing
564 && (ComposerTopLine != COMPOSER_TOP_LINE))
565 scrollup(wp, COMPOSER_TOP_LINE,
566 j+entry_line(1000, TRUE));
567 else
568 scrollup(wp, -1, j);
570 else
571 scrollup(wp, -1, j);
572 break;
573 default :
574 break;
578 wp->w_linep = lp;
579 wp->w_flag |= WFHARD; /* Force full. */
580 out:
582 * if the line at the top of the page is the top line
583 * in the body, show the header...
585 if(Pmaster && Pmaster->headents && !ComposerEditing){
586 if(lback(wp->w_linep) == wp->w_bufp->b_linep){
587 if(ComposerTopLine == COMPOSER_TOP_LINE){
588 i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
589 if(i > 0 && nlforw() >= i) { /* room for header ? */
590 if((i = nlforw()/2) == 0 && term.t_nrow&1)
591 i = 1;
592 while(wp->w_linep != wp->w_bufp->b_linep && i--)
593 wp->w_linep = lforw(wp->w_linep);
596 else
597 ToggleHeader(1);
600 else{
601 if(ComposerTopLine != COMPOSER_TOP_LINE)
602 ToggleHeader(0); /* hide it ! */
606 /* Try to use reduced update. Mode line update has its own special
607 * flag. The fast update is used if the only thing to do is within
608 * the line editing.
610 lp = wp->w_linep;
611 i = wp->w_toprow;
613 if ((wp->w_flag & ~WFMODE) == WFEDIT){
614 /* if the signature separator was modified (or created)
615 * do the other side below
617 if(repaint) goto other;
618 while (lp != wp->w_dotp){
619 ++i;
620 lp = lforw(lp);
623 for (j = 0, quoted = -1; j < llength(lp); ++j){
624 if(quoted < 0){
625 if(lgetc(lp,j).c != '>' && lgetc(lp, j).c != ' ')
626 quoted = 0;
627 else if(lgetc(lp,j).c == '>')
628 quoted = 1;
632 vscreen[i]->v_flag &= ~(VFSIG|VFNOR|VFQUO);
633 vscreen[i]->v_flag |= (quoted > 0 ? VFQUO : 0)|(lp->l_sig ? VFSIG : 0)| VFCHG;
634 vtmove(i, 0);
636 for (j = 0; j < llength(lp); ++j)
637 vtputc(lgetc(lp, j));
638 vteeol();
640 else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
641 other:
642 while (i < wp->w_toprow+wp->w_ntrows){
643 int flag = 0;
644 for (j = 0, quoted = -1; j < llength(lp) && quoted < 0; ++j){
645 if(quoted < 0){
646 if(lgetc(lp,j).c != '>' && lgetc(lp, j).c != ' ')
647 quoted = 0;
648 else if(lgetc(lp,j).c == '>')
649 quoted = 1;
652 if(repaint && (vscreen[i]->v_flag & VFSIG) && lp->l_sig == 0)
653 flag |= VFNOR;
654 if(quoted > 0) flag |= VFQUO;
655 vscreen[i]->v_flag &= ~(VFSIG | VFNOR | VFQUO);
656 vscreen[i]->v_flag |= flag | (lp->l_sig ? VFSIG : 0 )| VFCHG;
657 vtmove(i, 0);
659 /* if line has been changed */
660 if (lp != wp->w_bufp->b_linep){
661 for (j = 0; j < llength(lp); ++j)
662 vtputc(lgetc(lp, j));
664 lp = lforw(lp);
667 vteeol();
668 ++i;
671 #if ~WFDEBUG
672 if ((wp->w_flag&WFMODE) != 0)
673 modeline(wp);
675 wp->w_flag = 0;
676 wp->w_force = 0;
677 #endif
679 #if WFDEBUG
680 modeline(wp);
681 wp->w_flag = 0;
682 wp->w_force = 0;
683 #endif
685 /* and onward to the next window */
686 wp = wp->w_wndp;
689 /* Always recompute the row and column number of the hardware cursor. This
690 * is the only update for simple moves.
692 lp = curwp->w_linep;
693 currow = curwp->w_toprow;
695 while (lp != curwp->w_dotp){
696 ++currow;
697 lp = lforw(lp);
700 curcol = 0;
701 i = 0;
703 while (i < curwp->w_doto){
704 c = lgetc(lp, i++);
706 if(c.c == '\t'){
707 curcol |= 0x07;
708 ++curcol;
710 else if(ISCONTROL(c.c)){
711 curcol += 2;
713 else{
714 int w;
716 w = wcellwidth((UCS) c.c);
717 curcol += (w >= 0 ? w : 1);
721 if (curcol >= term.t_ncol) { /* extended line. */
722 /* flag we are extended and changed */
723 vscreen[currow]->v_flag |= VFEXT | VFCHG;
724 updext(); /* and output extended line */
725 } else
726 lbound = 0; /* not extended line */
728 /* make sure no lines need to be de-extended because the cursor is
729 * no longer on them
732 wp = wheadp;
734 while (wp != NULL) {
735 lp = wp->w_linep;
736 i = wp->w_toprow;
738 while (i < wp->w_toprow + wp->w_ntrows) {
739 if (vscreen[i]->v_flag & VFEXT) {
740 /* always flag extended lines as changed */
741 vscreen[i]->v_flag |= VFCHG;
742 if ((wp != curwp) || (lp != wp->w_dotp) ||
743 (curcol < term.t_ncol)) {
744 vtmove(i, 0);
745 for (j = 0; j < llength(lp); ++j)
746 vtputc(lgetc(lp, j));
747 vteeol();
749 /* this line no longer is extended */
750 vscreen[i]->v_flag &= ~VFEXT;
753 lp = lforw(lp);
754 ++i;
756 /* and onward to the next window */
757 wp = wp->w_wndp;
760 /* Special hacking if the screen is garbage. Clear the hardware screen,
761 * and update your copy to agree with it. Set all the virtual screen
762 * change bits, to force a full update.
765 if (sgarbf != FALSE){
766 if(Pmaster){
767 int rv;
769 showCompTitle();
771 if(ComposerTopLine != COMPOSER_TOP_LINE){
772 UpdateHeader(0); /* arrange things */
773 PaintHeader(COMPOSER_TOP_LINE, TRUE);
777 * since we're using only a portion of the screen and only
778 * one buffer, only clear enough screen for the current window
779 * which is to say the *only* window.
781 for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
782 movecursor(i, 0);
783 peeol();
784 vscreen[i]->v_flag |= VFCHG;
786 rv = (*Pmaster->showmsg)('X' & 0x1f); /* ctrl-L */
787 ttresize();
788 picosigs(); /* restore altered handlers */
789 if(rv) /* Did showmsg corrupt the display? */
790 PaintBody(0); /* Yes, repaint */
791 movecursor(wheadp->w_toprow, 0);
793 else{
794 c.c = '\0';
795 c.a = 0;
796 for (i = 0; i < term.t_nrow-term.t_mrow; i++){
797 vscreen[i]->v_flag |= VFCHG;
798 vp1 = pscreen[i];
799 for (j = 0; j < term.t_ncol; j++)
800 vp1->v_text[j] = c;
801 if(sgarbf == FALSE){
802 movecursor(i, 0);
803 term.t_eeol();
806 if(sgarbf != FALSE){
807 movecursor(0, 0); /* Erase the screen. */
808 (*term.t_eeop)();
812 sgarbf = FALSE; /* Erase-page clears */
813 mpresf = FALSE; /* the message area. */
815 if(Pmaster)
816 modeline(curwp);
817 else
818 sgarbk = TRUE; /* fix the keyhelp as well...*/
821 /* Make sure that the physical and virtual displays agree. Unlike before,
822 * the "updateline" code is only called with a line that has been updated
823 * for sure.
825 if(Pmaster)
826 i = curwp->w_toprow;
827 else
828 i = 0;
830 if (term.t_nrow > term.t_mrow)
831 c.c = term.t_nrow - term.t_mrow;
832 else
833 c.c = 0;
835 for (; i < (int)c.c; ++i){
837 vp1 = vscreen[i];
839 /* for each line that needs to be updated, or that needs its
840 reverse video status changed, call the line updater */
841 j = vp1->v_flag;
842 if (j & VFCHG){
844 #if TYPEAH
845 if (typahead()){
846 #ifdef _WINDOWS
847 mswin_endupdate ();
848 #endif
849 return;
851 #endif
852 vp2 = pscreen[i];
854 updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
858 if(Pmaster == NULL){
860 if(sgarbk != FALSE){
861 if(term.t_mrow > 0){
862 movecursor(term.t_nrow-1, 0);
863 peeol();
864 movecursor(term.t_nrow, 0);
865 peeol();
868 if(lastflag&CFFILL){
869 /* TRANSLATORS: UnJustify means undo the previous
870 Justify command. */
871 menu_pico[UNCUT_KEY].label = N_("UnJustify");
872 if(!(lastflag&CFFLBF)){
873 emlwrite(_("Can now UnJustify!"), NULL);
874 mpresf = FARAWAY; /* remove this after next keystroke! */
877 else
878 menu_pico[UNCUT_KEY].label = N_("UnCut Text");
880 wkeyhelp(menu_pico);
881 sgarbk = FALSE;
885 if(lastflag&CFFLBF){
886 emlwrite(_("Can now UnJustify!"), NULL);
887 mpresf = FARAWAY; /* remove this after next keystroke! */
890 /* Finally, update the hardware cursor and flush out buffers. */
892 movecursor(currow, curcol - lbound);
893 #ifdef _WINDOWS
894 mswin_endupdate ();
897 * Update the scroll bars. This function is where curbp->b_linecnt
898 * is really managed. See update_scroll.
900 update_scroll ();
901 #endif
902 (*term.t_flush)();
906 /* updext - update the extended line which the cursor is currently
907 * on at a column greater than the terminal width. The line
908 * will be scrolled right or left to let the user see where
909 * the cursor is
911 void
912 updext(void)
914 int rcursor; /* real cursor location */
915 LINE *lp; /* pointer to current line */
916 int j; /* index into line */
917 int w = 0;
918 int ww;
921 * Calculate what column the real cursor will end up in.
922 * The cursor will be in the rcursor'th column. So if we're
923 * counting columns 0 1 2 3 and rcursor is 8, then rcursor
924 * will be over cell 7.
926 * What this effectively does is to scroll the screen as we're
927 * moving to the right when the cursor first passes off the
928 * screen's right edge. It would be nice if it did the same
929 * thing coming back to the left. Instead, in order that the
930 * screen's display depends only on the curcol and not on
931 * how we got there, the screen scrolls when we pass the
932 * t_margin column. It's also kind of funky that you can't
933 * see the character under the $ but you can delete it.
935 rcursor = ((curcol - term.t_ncol) % (term.t_ncol - term.t_margin + 1)) + term.t_margin;
936 lbound = curcol - rcursor + 1;
939 * Make sure lbound is set so that a double-width character does
940 * not straddle the boundary. If it does, move over one cell.
942 lp = curwp->w_dotp; /* line to output */
943 for (j=0; j<llength(lp) && w < lbound; ++j){
944 ww = wcellwidth((UCS) lgetc(lp, j).c);
945 w += (ww >= 0 ? ww : 1);
948 if(w > lbound)
949 lbound = w;
952 /* scan through the line outputing characters to the virtual screen
953 * once we reach the left edge
955 vtmove(currow, -lbound); /* start scanning offscreen */
956 for (j=0; j<llength(lp); ++j) /* until the end-of-line */
957 vtputc(lgetc(lp, j));
959 /* truncate the virtual line */
960 vteeol();
962 /* and put a '$' in column 1, may have to adjust curcol */
963 w = wcellwidth((UCS) vscreen[currow]->v_text[0].c);
964 vscreen[currow]->v_text[0].c = '$';
965 vscreen[currow]->v_text[0].a = 0;
966 if(w == 2){
968 * We want to put $ in the first two columns so that it
969 * takes up the right amount of space, but that means we
970 * have to scoot the real characters over one slot.
972 for (j = term.t_ncol-1; j >= 2; --j)
973 vscreen[currow]->v_text[j] = vscreen[currow]->v_text[j-1];
975 vscreen[currow]->v_text[1].c = '$';
976 vscreen[currow]->v_text[1].a = 0;
982 * Update a single line. This does not know how to use insert or delete
983 * character sequences; we are using VT52 functionality. Update the physical
984 * row and column variables.
986 void
987 updateline(int row, /* row on screen */
988 CELL vline[], /* what we want it to end up as */
989 CELL pline[], /* what it looks like now */
990 short *flags)
992 CELL *cp1, *cp2, *cp3, *cp4, *cp5, *cp6, *cp7;
993 int display = TRUE;
994 int nbflag; /* non-blanks to the right flag? */
995 int cleartoeol = 0;
996 int in_quote, quote_found = 0, level;
997 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
998 COLOR_PAIR *qcolor = NULL, *lastc = NULL;
999 int first = 1;
1000 int x; /* bit to indicate if we should eXecute a block below */
1002 if(row < 0 || row > term.t_nrow)
1003 return;
1005 /* set up pointers to virtual and physical lines */
1006 cp1 = &vline[0];
1007 cp2 = &pline[0];
1008 cp3 = &vline[term.t_ncol];
1010 /* advance past any common chars at the left */
1011 if(!(*flags & (VFSIG|VFNOR|VFQUO))
1012 && (pcolors == NULL || vline[0].c != ' '))
1013 while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
1014 ++cp1;
1015 ++cp2;
1018 /* This can still happen, even though we only call this routine on changed
1019 * lines. A hard update is always done when a line splits, a massive
1020 * change is done, or a buffer is displayed twice. This optimizes out most
1021 * of the excess updating. A lot of computes are used, but these tend to
1022 * be hard operations that do a lot of update, so I don't really care.
1024 /* if both lines are the same, no update needs to be done */
1025 if (cp1 == cp3){
1026 *flags &= ~VFCHG; /* mark it clean */
1027 return;
1030 /* find out if there is a match on the right */
1031 nbflag = FALSE;
1032 cp3 = &vline[term.t_ncol];
1033 cp4 = &pline[term.t_ncol];
1035 if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
1036 while (cp3 != cp1 && cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
1037 --cp3;
1038 --cp4;
1039 if (cp3[0].c != '\0' || cp3[0].a != 0) /* Note if any nonblank */
1040 nbflag = TRUE; /* in right match. */
1043 cp5 = cp3;
1045 if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
1046 while (cp5 != cp1 && cp5[-1].c == '\0' && cp5[-1].a == 0)
1047 --cp5;
1049 if (cp3-cp5 <= 3) /* Use only if erase is */
1050 cp5 = cp3; /* fewer characters. */
1053 /* go to start of differences */
1054 movecursor(row, cellwidth_ptr_to_ptr(&vline[0], cp1));
1056 /* the next code inserts a character or deletes one in the middle
1057 * of a line. However, we do not need this code to work when we
1058 * are using colors that depend on what is inserted/deleted, so
1059 * we defer this work to the code below
1061 x = pcolors && (pcolors->qlcp || pcolors->qllcp || pcolors->qlllcp);
1062 if (!nbflag && x == 0) { /* use insert or del char? */
1063 cp6 = cp3;
1064 cp7 = cp4;
1066 if(TERM_INSCHAR
1067 &&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
1068 while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
1069 --cp7;
1070 --cp6;
1073 if (cp7==cp2 && cp4-cp2 > 3){
1074 int ww;
1076 (*term.t_rev)(cp1->a); /* set inverse for this char */
1077 o_insert((UCS) cp1->c); /* insert the char */
1078 ww = wcellwidth((UCS) cp1->c);
1079 ttcol += (ww >= 0 ? ww : 1);
1080 display = FALSE; /* only do it once!! */
1083 else if(TERM_DELCHAR && cp3 != cp1 && cp7[0].c == cp6[-1].c
1084 && cp7[0].a == cp6[-1].a){
1085 while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
1086 --cp7;
1087 --cp6;
1090 if (cp6==cp1 && cp5-cp6 > 3){
1091 int w;
1093 w = wcellwidth((UCS) cp7[0].c);
1094 w = (w >= 0 ? w : 1);
1095 while(w-- > 0) /* in case double-width char */
1096 o_delete(); /* delete the char */
1097 display = FALSE; /* only do it once!! */
1102 if(cp1 == cp5 && (*flags & (VFSIG|VFNOR|VFQUO)))
1103 while(cp5 < &vline[term.t_ncol] && cp5->c != '\0')
1104 cp5++;
1106 if(cp1 != cp5 && display){
1107 int w1, w2;
1109 if(pcolors){
1110 lastc = pico_get_cur_color();
1111 if(row == 0)
1112 pico_set_colorp(pcolors->tbcp, PSC_NONE);
1113 else if(row < term.t_nrow - 2)
1114 pico_set_colorp((*flags & VFSIG) ? pcolors->sbcp : pcolors->ntcp, PSC_NONE);
1117 * If we need to copy characters from cp1 to cp2 and
1118 * we need to display them, then we have to worry about
1119 * the characters that we are replacing being of a different
1120 * width than the new characters, else the display may be
1121 * messed up.
1123 * If the new width (w1) is less than the old width, that means
1124 * we will leave behind some old remnants if we aren't careful.
1125 * If the new width is larger than the old width, we have to
1126 * make sure we draw the characters all the way to the end
1127 * in order to get it right. Take advantage of clear to end
1128 * of line if we have it.
1130 w1 = cellwidth_ptr_to_ptr(cp1, cp3);
1131 w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
1133 if(w1 < w2 || (nbflag && w1 != w2)){
1134 if(TERM_EOLEXIST){
1135 if(nbflag){
1137 * Draw all of the characters starting with cp1
1138 * until we get to all spaces, then clear to the end of
1139 * line from there. Watch out we don't run over the
1140 * right hand edge, which shouldn't happen.
1142 * Set cp5 to the first of the repeating spaces.
1144 cp5 = &vline[term.t_ncol];
1145 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1146 --cp5;
1150 * In the !nbflag case we want spaces from cp5 on.
1151 * Setting cp3 to something different from cp5 triggers
1152 * the clear to end of line below.
1154 if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
1155 cleartoeol++;
1157 else{
1158 int w;
1161 * No peeol so draw all the way to the edge whether they
1162 * are spaces or not.
1164 cp3 = &vline[0];
1165 for(w = 0; w < term.t_ncol; cp3++){
1166 int ww;
1168 ww = wcellwidth((UCS) cp3->c);
1169 w += (ww >= 0 ? ww : 1);
1172 cp5 = cp3;
1177 in_quote = 1;
1178 level = -1;
1179 while (cp1 != cp5) { /* Ordinary. */
1180 int ww;
1182 if(display){
1183 if(pcolors){
1184 if(cp1->c != '>' && cp1->c != ' '){
1185 in_quote = 0;
1186 } else if (in_quote && cp1->c == '>' && pcolors != NULL)
1187 level = (level + 1) % 3;
1188 if(level >= 0){
1189 if(level == 0) qcolor = pcolors->qlcp;
1190 else if(level == 1) qcolor = pcolors->qllcp;
1191 else if(level == 2) qcolor = pcolors->qlllcp;
1192 pico_set_colorp(qcolor, PSC_NONE);
1196 /* cp1->c could be null because we set it up that way by default. Initialization
1197 * is made with null characters. We did this because otherwise Pico does not
1198 * color trailing spaces in lines, so this gives a way for pico to distinguish
1199 * trailing spaces in lines from its own old default initialization of cells
1200 * using spaces. So when we get a null character we output the corresponding
1201 * space that would have been output in the past. If you want to see what
1202 * would happen if we output a null character, rewrite the code below.
1204 (*term.t_rev)(cp1->a); /* set inverse for this char */
1205 (*term.t_putchar)(cp1->c || pcolors == NULL ? cp1->c : ' ');
1208 ww = wcellwidth((UCS) cp1->c);
1209 ttcol += (ww >= 0 ? ww : 1);
1211 *cp2++ = *cp1++;
1214 if (pcolors && lastc){
1215 (void)pico_set_colorp(lastc, PSC_NONE);
1216 free_color_pair(&lastc);
1219 (*term.t_rev)(0); /* turn off inverse anyway! */
1221 if (cp5 != cp3 || cleartoeol) { /* Erase. */
1222 if(display)
1223 peeol();
1224 else
1225 while (cp1 != cp3)
1226 *cp2++ = *cp1++;
1229 *flags &= ~VFCHG; /* flag this line is changed */
1234 * Redisplay the mode line for the window pointed to by the "wp". This is the
1235 * only routine that has any idea of how the modeline is formatted. You can
1236 * change the modeline format by hacking at this routine. Called by "update"
1237 * any time there is a dirty window.
1239 void
1240 modeline(WINDOW *wp)
1242 if(Pmaster){
1243 if(ComposerEditing)
1244 ShowPrompt();
1245 else{
1246 menu_compose[EXIT_KEY].label = (Pmaster->headents)
1247 ? N_("Send") :N_("Exit");
1248 menu_compose[PSTPN_KEY].name = (Pmaster->headents)
1249 ? "^O" : NULL;
1250 menu_compose[PSTPN_KEY].label = (Pmaster->headents)
1251 ? N_("Postpone") : NULL;
1252 menu_compose[WHERE_KEY].name = (Pmaster->alt_ed) ? "^_" : "^W";
1253 menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? N_("Alt Edit")
1254 : N_("Where is");
1255 KS_OSDATASET(&menu_compose[WHERE_KEY],
1256 (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
1257 menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? N_("UnJustify")
1258 : N_("UnCut Text");
1259 wkeyhelp(menu_compose);
1262 else{
1263 BUFFER *bp;
1264 char t1[NLINE], t2[NLINE], t3[NLINE], tline[NLINE];
1265 int w1, w2, w3, w1_to_2, w2_to_3, w3_to_r;
1266 UCS *ucs;
1268 vtmove(1, 0);
1269 vteeol();
1270 vscreen[0]->v_flag |= VFCHG; /* Redraw next time. */
1271 vtmove(0, 0); /* Seek to right line. */
1273 snprintf(t1, sizeof(t1), PICO_TITLE, version); /* write version */
1275 bp = wp->w_bufp;
1276 if(bp->b_fname[0]) /* File name? */
1277 snprintf(t2, sizeof(t2), "File: %s", bp->b_fname);
1278 else{
1279 strncpy(t2, PICO_NEWBUF_MSG, sizeof(t2));
1280 t2[sizeof(t2)-1] = '\0';
1283 if(bp->b_flag&BFCHG){ /* "MOD" if changed. */
1284 strncpy(t3, PICO_MOD_MSG, sizeof(t3));
1285 t3[sizeof(t3)-1] = '\0';
1287 else
1288 t3[0] = '\0';
1290 #define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
1291 #define ALLBUTSPACE (w1+w2+w3+w3_to_r)
1293 w1 = utf8_width(t1);
1294 w2 = utf8_width(t2);
1295 w3 = utf8_width(t3);
1296 w1_to_2 = w2_to_3 = 1; /* min values for separation */
1297 w3_to_r = 2;
1299 if(ALLOFTHEM <= term.t_ncol){ /* everything fits */
1300 w1_to_2 = (term.t_ncol - ALLBUTSPACE)/2;
1301 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1303 else{
1304 w1 = 2;
1305 w1_to_2 = 0;
1306 if(ALLOFTHEM <= term.t_ncol)
1307 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1308 else{
1309 w1 = w1_to_2 = w3_to_r = 0;
1310 if(ALLOFTHEM <= term.t_ncol)
1311 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1312 else{
1313 if(bp->b_fname[0]){
1314 snprintf(t2, sizeof(t2), "%s", bp->b_fname);
1315 w2 = utf8_width(t2);
1318 if(ALLOFTHEM <= term.t_ncol)
1319 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1320 else{
1321 w2 = 8;
1322 if(bp->b_fname[0] && ALLOFTHEM <= term.t_ncol){
1323 /* reduce size of file */
1324 w2 = term.t_ncol - (ALLOFTHEM - w2);
1325 t2[0] = t2[1] = t2[2] = '.';
1326 utf8_to_width_rhs(t2+3, bp->b_fname, sizeof(t2)-3, w2-3);
1327 w2 = utf8_width(t2);
1329 else
1330 w2 = utf8_width(t2);
1332 if(ALLOFTHEM <= term.t_ncol)
1333 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1334 else{
1335 w1 = w1_to_2 = w2 = w2_to_3 = w3_to_r = 0;
1336 if(ALLOFTHEM <= term.t_ncol)
1337 w2_to_3 = term.t_ncol - ALLBUTSPACE;
1338 else
1339 w3 = 0;
1346 utf8_snprintf(tline, sizeof(tline),
1347 "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
1348 w1, w1, t1,
1349 w1_to_2, w1_to_2, "",
1350 w2, w2, t2,
1351 w2_to_3, w2_to_3, "",
1352 w3, w3, t3,
1353 w3_to_r, w3_to_r, "");
1355 ucs = NULL;
1356 if(utf8_width(tline) <= term.t_ncol)
1357 ucs = utf8_to_ucs4_cpystr(tline);
1359 if(ucs){
1360 UCS *ucsp;
1361 CELL c;
1363 c.a = 1;
1364 ucsp = ucs;
1365 while((c.c = CELLMASK & *ucsp++))
1366 vtputc(c);
1368 fs_give((void **) &ucs);
1376 * Send a command to the terminal to move the hardware cursor to row "row"
1377 * and column "col". The row and column arguments are origin 0. Optimize out
1378 * random calls. Update "ttrow" and "ttcol".
1380 void
1381 movecursor(int row, int col)
1383 if (row!=ttrow || col!=ttcol) {
1384 ttrow = row;
1385 ttcol = col;
1386 (*term.t_move)(MIN(MAX(row,0),term.t_nrow), MIN(MAX(col,0),term.t_ncol-1));
1392 * Erase any sense we have of the cursor's HW location...
1394 void
1395 clearcursor(void)
1397 ttrow = ttcol = FARAWAY;
1400 void
1401 get_cursor(int *row, int *col)
1403 if(row)
1404 *row = ttrow;
1405 if(col)
1406 *col = ttcol;
1411 * Erase the message line. This is a special routine because the message line
1412 * is not considered to be part of the virtual screen. It always works
1413 * immediately; the terminal buffer is flushed via a call to the flusher.
1415 void
1416 mlerase(void)
1418 if (term.t_nrow < term.t_mrow)
1419 return;
1421 movecursor(term.t_nrow - term.t_mrow, 0);
1422 (*term.t_rev)(0);
1423 if (TERM_EOLEXIST == TRUE)
1424 peeol();
1425 else{
1426 if(ttrow == term.t_nrow){
1427 while(ttcol++ < term.t_ncol-1)
1428 (*term.t_putchar)(' ');
1430 else{
1431 while(ttcol++ < term.t_ncol) /* track's ttcol */
1432 (*term.t_putchar)(' ');
1436 (*term.t_flush)();
1437 mpresf = FALSE;
1440 /* returns the chosen dictionary. If one was already chosen
1441 * return that one
1443 char *
1444 speller_choice(char **sp_list, int *choice)
1446 int ch_dict = -1;
1447 int cnt;
1449 if(sp_list == NULL || sp_list[0] == NULL || sp_list[0][0] == '\0')
1450 return NULL;
1452 if(choice && *choice >= 0)
1453 return sp_list[*choice];
1455 for(cnt = 0; sp_list[cnt] != NULL && sp_list[cnt][0] != '\0'; cnt++)
1458 if(cnt > 10) /* only the first 10 dictionaries */
1459 cnt = 10;
1461 if(cnt == 1) /* only one dictionary? choose it! */
1462 ch_dict = 0;
1464 if(ch_dict > cnt - 1) /* choose again in case something changed */
1465 ch_dict = -1;
1467 if(ch_dict < 0){ /* not a choice yet? do one now! */
1468 UCS buf[128];
1469 int i;
1470 char *utf8_prompt;
1471 UCS *ucs4_prompt;
1472 EXTRAKEYS menu_dictionary[] = {
1473 {"0", NULL, '0'},
1474 {"1", NULL, '1'},
1475 {"2", NULL, '2'},
1476 {"3", NULL, '3'},
1477 {"4", NULL, '4'},
1478 {"5", NULL, '5'},
1479 {"6", NULL, '6'},
1480 {"7", NULL, '7'},
1481 {"8", NULL, '8'},
1482 {"9", NULL, '9'}
1485 for(i = 0; i < cnt; i++)
1486 menu_dictionary[i].label = sp_list[i];
1488 if(cnt < 10)
1489 menu_dictionary[cnt].name = NULL;
1491 buf[0] = '\0';
1492 /* write the prompt in utf8, and let internal functions translate it to ucs4 */
1493 ucs4_prompt = utf8_to_ucs4_cpystr(_("Choose Dictionary: "));
1495 i = mlchoose(ucs4_prompt, menu_dictionary);
1497 if(i >= '0' && i <= '9')
1498 ch_dict = i - '0';
1500 if (i == -2) /* user cancelled */
1501 ch_dict = -2;
1503 if(ucs4_prompt)
1504 fs_give((void **)&ucs4_prompt);
1506 else ch_dict = -1;
1508 if(choice)
1509 *choice = ch_dict;
1511 return ch_dict >= 0 ? sp_list[ch_dict] : NULL;
1514 /* just like mlreplyd, but user cannot fill a prompt */
1516 mlchoose(UCS *prompt, EXTRAKEYS *extras)
1518 UCS c;
1519 UCS buf[NLINE];
1520 int i;
1521 int changed = FALSE;
1522 int return_val = 0;
1523 KEYMENU menu_choose[12];
1524 COLOR_PAIR *lastc = NULL;
1526 for(i = 0; i < 12; i++){
1527 menu_choose[i].name = NULL;
1528 KS_OSDATASET(&menu_choose[i], KS_NONE);
1531 menu_choose[0].name = "^G";
1532 menu_choose[0].label = N_("Get Help");
1533 KS_OSDATASET(&menu_choose[0], KS_SCREENHELP);
1535 menu_choose[6].name = "^C";
1536 menu_choose[6].label = N_("Cancel");
1537 KS_OSDATASET(&menu_choose[6], KS_NONE);
1539 for(i = 0; i < 10; i++){
1540 if((i % 2) == 0){
1541 menu_choose[i / 2 + 1].name = extras[i].name;
1542 menu_choose[i / 2 + 1].label = extras[i].label;
1544 else{
1545 menu_choose[(i + 13) / 2].name = extras[i].name;
1546 menu_choose[(i + 13) / 2].label = extras[i].label;
1549 wkeyhelp(menu_choose); /* paint generic menu */
1550 sgarbk = TRUE; /* mark menu dirty */
1551 if(Pmaster && curwp)
1552 curwp->w_flag |= WFMODE;
1554 ucs4_strncpy(buf, prompt, NLINE);
1555 buf[NLINE-1] = '\0';
1556 mlwrite(buf, NULL);
1557 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1558 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1559 lastc = pico_get_cur_color();
1560 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1562 else
1563 (*term.t_rev)(1);
1565 return_val = -1;
1566 while(1){
1567 c = GetKey();
1568 for(i = 0; i < 10 && extras[i].name != NULL && extras[i].key != c; i++)
1570 if(i < 10 && extras[i].name)
1571 return_val = c;
1572 else switch(c){
1573 case (CTRL|'C') : /* Bail out! */
1574 case F2 :
1575 pputs_utf8(_("Cancel"), 1);
1576 return_val = -2;
1577 break;
1579 case (CTRL|'G') :
1580 if(term.t_mrow == 0 && km_popped == 0){
1581 movecursor(term.t_nrow-2, 0);
1582 peeol();
1583 term.t_mrow = 2;
1584 if(lastc){
1585 (void) pico_set_colorp(lastc, PSC_NONE);
1586 free_color_pair(&lastc);
1588 else
1589 (*term.t_rev)(0);
1591 wkeyhelp(menu_choose); /* paint generic menu */
1592 mlwrite(buf, NULL);
1593 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1594 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1595 lastc = pico_get_cur_color();
1596 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1598 else
1599 (*term.t_rev)(1);
1601 sgarbk = TRUE; /* mark menu dirty */
1602 km_popped++;
1603 break;
1605 /* else fall through */
1607 default:
1608 (*term.t_beep)();
1609 case NODATA :
1610 break;
1613 (*term.t_flush)();
1614 if (return_val != -1){ /* abort sets rv = -2, other return values are positive */
1615 if(lastc){
1616 (void) pico_set_colorp(lastc, PSC_NONE);
1617 free_color_pair(&lastc);
1619 else
1620 (*term.t_rev)(0);
1622 if(km_popped){
1623 term.t_mrow = 0;
1624 movecursor(term.t_nrow, 0);
1625 peeol();
1626 sgarbf = 1;
1627 km_popped = 0;
1630 return(return_val);
1637 mlyesno_utf8(char *utf8prompt, int dflt)
1639 int ret;
1640 UCS *prompt;
1642 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1644 ret = mlyesno(prompt, dflt);
1646 if(prompt)
1647 fs_give((void **) &prompt);
1649 return(ret);
1654 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
1655 * ABORT. The ABORT status is returned if the user bumps out of the question
1656 * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
1657 * is no default.
1660 mlyesno(UCS *prompt, int dflt)
1662 int rv;
1663 UCS buf[NLINE], lbuf[10];
1664 KEYMENU menu_yesno[12];
1665 COLOR_PAIR *lastc = NULL;
1666 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
1668 #ifdef _WINDOWS
1669 if (mswin_usedialog ())
1670 switch (mswin_yesno (prompt)) {
1671 default:
1672 case 0: return (ABORT);
1673 case 1: return (TRUE);
1674 case 2: return (FALSE);
1676 #endif
1678 for(rv = 0; rv < 12; rv++){
1679 menu_yesno[rv].name = NULL;
1680 KS_OSDATASET(&menu_yesno[rv], KS_NONE);
1683 menu_yesno[1].name = "Y";
1684 menu_yesno[1].label = (dflt == TRUE) ? "[" N_("Yes") "]" : N_("Yes");
1685 menu_yesno[6].name = "^C";
1686 menu_yesno[6].label = N_("Cancel");
1687 menu_yesno[7].name = "N";
1688 menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No");
1689 wkeyhelp(menu_yesno); /* paint generic menu */
1690 sgarbk = TRUE; /* mark menu dirty */
1691 if(Pmaster && curwp)
1692 curwp->w_flag |= WFMODE;
1694 ucs4_strncpy(buf, prompt, NLINE);
1695 buf[NLINE-1] = '\0';
1696 lbuf[0] = ' '; lbuf[1] = '?'; lbuf[2] = ' '; lbuf[3] = '\0';
1697 ucs4_strncat(buf, lbuf, NLINE - ucs4_strlen(buf) - 1);
1698 buf[NLINE-1] = '\0';
1699 mlwrite(buf, NULL);
1700 if(pcolors && pcolors->prcp
1701 && pico_is_good_colorpair(pcolors->prcp)){
1702 lastc = pico_get_cur_color();
1703 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
1704 } else
1705 (*term.t_rev)(1);
1707 rv = -1;
1708 while(1){
1709 switch(GetKey()){
1710 case (CTRL|'M') : /* default */
1711 if(dflt >= 0){
1712 pputs_utf8((dflt) ? _("Yes") : _("No"), 1);
1713 rv = dflt;
1715 else
1716 (*term.t_beep)();
1718 break;
1720 case (CTRL|'C') : /* Bail out! */
1721 case F2 :
1722 pputs_utf8(_("ABORT"), 1);
1723 rv = ABORT;
1724 break;
1726 case 'y' :
1727 case 'Y' :
1728 case F3 :
1729 pputs_utf8(_("Yes"), 1);
1730 rv = TRUE;
1731 break;
1733 case 'n' :
1734 case 'N' :
1735 case F4 :
1736 pputs_utf8(_("No"), 1);
1737 rv = FALSE;
1738 break;
1740 case (CTRL|'G') :
1741 if(term.t_mrow == 0 && km_popped == 0){
1742 movecursor(term.t_nrow-2, 0);
1743 peeol();
1744 term.t_mrow = 2;
1745 if(lastc){
1746 (void) pico_set_colorp(lastc, PSC_NONE);
1747 free_color_pair(&lastc);
1749 else
1750 (*term.t_rev)(0);
1752 wkeyhelp(menu_yesno); /* paint generic menu */
1753 mlwrite(buf, NULL);
1754 if(pcolors && pcolors->prcp
1755 && pico_is_good_colorpair(pcolors->prcp)){
1756 lastc = pico_get_cur_color();
1757 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
1759 else
1760 (*term.t_rev)(1);
1762 sgarbk = TRUE; /* mark menu dirty */
1763 km_popped++;
1764 break;
1766 /* else fall through */
1768 default:
1769 (*term.t_beep)();
1770 case NODATA :
1771 break;
1774 (*term.t_flush)();
1775 if(rv != -1){
1776 if(lastc){
1777 (void) pico_set_colorp(lastc, PSC_NONE);
1778 free_color_pair(&lastc);
1780 else
1781 (*term.t_rev)(0);
1783 if(km_popped){
1784 term.t_mrow = 0;
1785 movecursor(term.t_nrow, 0);
1786 peeol();
1787 sgarbf = 1;
1788 km_popped = 0;
1791 return(rv);
1798 * Write a prompt into the message line, then read back a response. Keep
1799 * track of the physical position of the cursor. If we are in a keyboard
1800 * macro throw the prompt away, and return the remembered response. This
1801 * lets macros run at full speed. The reply is always terminated by a carriage
1802 * return. Handle erase, kill, and abort keys.
1805 mlreply_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1807 return(mlreplyd_utf8(utf8prompt, utf8buf, nbuf, flg|QDEFLT, extras));
1812 mlreply(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
1814 return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
1819 * function key mappings
1821 static UCS rfkm[12][2] = {
1822 { F1, (CTRL|'G')},
1823 { F2, (CTRL|'C')},
1824 { F3, 0 },
1825 { F4, 0 },
1826 { F5, 0 },
1827 { F6, 0 },
1828 { F7, 0 },
1829 { F8, 0 },
1830 { F9, 0 },
1831 { F10, 0 },
1832 { F11, 0 },
1833 { F12, 0 }
1838 mlreplyd_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1840 int ret;
1841 UCS *b, *buf;
1842 char *utf8;
1843 UCS *prompt;
1845 buf = (UCS *) fs_get(nbuf * sizeof(*b));
1846 b = utf8_to_ucs4_cpystr(utf8buf);
1847 if(b){
1848 ucs4_strncpy(buf, b, nbuf);
1849 buf[nbuf-1] = '\0';
1850 fs_give((void **) &b);
1853 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1855 ret = mlreplyd(prompt, buf, nbuf, flg, extras);
1857 utf8 = ucs4_to_utf8_cpystr(buf);
1858 if(utf8){
1859 strncpy(utf8buf, utf8, nbuf);
1860 utf8buf[nbuf-1] = '\0';
1861 fs_give((void **) &utf8);
1864 if(buf)
1865 fs_give((void **) &buf);
1867 if(prompt)
1868 fs_give((void **) &prompt);
1870 return(ret);
1874 void
1875 writeachar(UCS ucs)
1877 pputc(ucs, 0);
1882 * mlreplyd - write the prompt to the message line along with a default
1883 * answer already typed in. Carriage return accepts the
1884 * default. answer returned in buf which also holds the initial
1885 * default, nbuf is its length, def set means use default value.
1886 * In order to be able to eliminate keys from a menu, EXTRAKEYS
1887 * always has size 10.
1890 mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
1892 UCS c; /* current char */
1893 UCS *b; /* pointer in buf */
1894 int i, j;
1895 int plen;
1896 int changed = FALSE;
1897 int return_val = 0;
1898 KEYMENU menu_mlreply[12];
1899 UCS extra_v[12];
1900 struct display_line dline;
1901 COLOR_PAIR *lastc = NULL;
1902 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
1904 #ifdef _WINDOWS
1905 if(mswin_usedialog()){
1906 MDlgButton btn_list[12];
1907 LPTSTR free_names[12];
1908 LPTSTR free_labels[12];
1909 int i, j;
1911 memset(&free_names, 0, sizeof(LPTSTR) * 12);
1912 memset(&free_labels, 0, sizeof(LPTSTR) * 12);
1913 memset(&btn_list, 0, sizeof (MDlgButton) * 12);
1914 j = 0;
1915 for(i = 0; extras && extras[i].name != NULL; ++i) {
1916 if(extras[i].label[0] != '\0') {
1917 if((extras[i].key & CTRL) == CTRL)
1918 btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
1919 else
1920 btn_list[j].ch = extras[i].key;
1922 btn_list[j].rval = extras[i].key;
1923 free_names[j] = utf8_to_lptstr(extras[i].name);
1924 btn_list[j].name = free_names[j];
1925 free_labels[j] = utf8_to_lptstr(extras[i].label);
1926 btn_list[j].label = free_labels[j];
1927 j++;
1931 btn_list[j].ch = -1;
1933 return_val = mswin_dialog(prompt, buf, nbuf, ((flg&QDEFLT) > 0),
1934 FALSE, btn_list, NULL, 0);
1936 if(return_val == 3)
1937 return_val = HELPCH;
1939 for(i = 0; i < 12; i++){
1940 if(free_names[i])
1941 fs_give((void **) &free_names[i]);
1942 if(free_labels[i])
1943 fs_give((void **) &free_labels[i]);
1946 return(return_val);
1948 #endif
1950 memset(&menu_mlreply, 0, 12*sizeof(KEYMENU));
1951 menu_mlreply[0].name = "^G";
1952 menu_mlreply[0].label = N_("Get Help");
1953 KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
1954 for(j = 0, i = 1; i < 6; i++){ /* insert odd extras */
1955 menu_mlreply[i].name = NULL;
1956 KS_OSDATASET(&menu_mlreply[i], KS_NONE);
1957 rfkm[2*i][1] = 0;
1958 if(extras){
1959 j = 2*(i-1);
1960 if(extras[j].name){
1961 rfkm[2*i][1] = extras[j].key;
1962 menu_mlreply[i].name = extras[j].name;
1963 menu_mlreply[i].label = extras[j].label;
1964 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
1969 menu_mlreply[6].name = "^C";
1970 menu_mlreply[6].label = N_("Cancel");
1971 KS_OSDATASET(&menu_mlreply[6], KS_NONE);
1972 for(j = 0, i = 7; i < 12; i++){ /* insert even extras */
1973 menu_mlreply[i].name = NULL;
1974 rfkm[2*(i-6)+1][1] = 0;
1975 if(extras){
1976 j = 2*(i-6) - 1;
1977 if(extras[j].name){
1978 rfkm[2*(i-6)+1][1] = extras[j].key;
1979 menu_mlreply[i].name = extras[j].name;
1980 menu_mlreply[i].label = extras[j].label;
1981 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
1986 /* set up what to watch for and return values */
1987 memset(extra_v, 0, sizeof(extra_v));
1988 for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
1989 extra_v[j++] = extras[i].key;
1991 plen = mlwrite(prompt, NULL); /* paint prompt */
1993 if(!(flg&QDEFLT))
1994 *buf = '\0';
1996 dline.vused = ucs4_strlen(buf);
1997 dline.dwid = term.t_ncol - plen;
1998 dline.row = term.t_nrow - term.t_mrow;
1999 dline.col = plen;
2001 dline.dlen = 2 * dline.dwid + 100;
2003 dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
2004 dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
2005 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
2006 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
2008 dline.movecursor = movecursor;
2009 dline.writechar = writeachar;
2011 dline.vl = buf;
2012 dline.vlen = nbuf-1;
2013 dline.vbase = 0;
2015 b = &buf[(flg & QBOBUF) ? 0 : ucs4_strlen(buf)];
2017 wkeyhelp(menu_mlreply); /* paint generic menu */
2019 sgarbk = 1; /* mark menu dirty */
2021 if(pcolors && pcolors->prcp
2022 && pico_is_good_colorpair(pcolors->prcp)){
2023 lastc = pico_get_cur_color();
2024 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2026 else
2027 (*term.t_rev)(1);
2029 for(;;){
2031 line_paint(b-buf, &dline, NULL);
2032 (*term.t_flush)();
2034 #ifdef MOUSE
2035 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
2036 register_mfunc(mouse_in_content,
2037 term.t_nrow - term.t_mrow, plen,
2038 term.t_nrow - term.t_mrow, term.t_ncol-1);
2039 #endif
2040 #ifdef _WINDOWS
2041 mswin_allowpaste(MSWIN_PASTE_LINE);
2042 #endif
2043 while((c = GetKey()) == NODATA)
2046 #ifdef MOUSE
2047 clear_mfunc(mouse_in_content);
2048 #endif
2049 #ifdef _WINDOWS
2050 mswin_allowpaste(MSWIN_PASTE_DISABLE);
2051 #endif
2053 switch(c = normalize_cmd(c, rfkm, 1)){
2054 case (CTRL|'A') : /* CTRL-A beginning */
2055 case KEY_HOME :
2056 b = buf;
2057 continue;
2059 case (CTRL|'B') : /* CTRL-B back a char */
2060 case KEY_LEFT:
2061 if(b <= buf)
2062 (*term.t_beep)();
2063 else
2064 b--;
2066 continue;
2068 case (CTRL|'C') : /* CTRL-C abort */
2069 pputs_utf8(_("ABORT"), 1);
2070 ctrlg(FALSE, 0);
2071 return_val = ABORT;
2072 goto ret;
2074 case (CTRL|'E') : /* CTRL-E end of line */
2075 case KEY_END :
2076 b = &buf[ucs4_strlen(buf)];
2077 continue;
2079 case (CTRL|'F') : /* CTRL-F forward a char*/
2080 case KEY_RIGHT :
2081 if(*b == '\0')
2082 (*term.t_beep)();
2083 else
2084 b++;
2086 continue;
2088 case (CTRL|'G') : /* CTRL-G help */
2089 if(term.t_mrow == 0 && km_popped == 0){
2090 movecursor(term.t_nrow-2, 0);
2091 peeol();
2092 sgarbk = 1; /* mark menu dirty */
2093 km_popped++;
2094 term.t_mrow = 2;
2095 if(lastc){
2096 (void) pico_set_colorp(lastc, PSC_NONE);
2097 free_color_pair(&lastc);
2099 else
2100 (*term.t_rev)(0);
2102 wkeyhelp(menu_mlreply); /* paint generic menu */
2103 plen = mlwrite(prompt, NULL); /* paint prompt */
2104 if(pcolors && pcolors->prcp
2105 && pico_is_good_colorpair(pcolors->prcp)){
2106 lastc = pico_get_cur_color();
2107 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2109 else
2110 (*term.t_rev)(1);
2112 pputs(buf, 1);
2113 break;
2116 pputs_utf8(_("HELP"), 1);
2117 return_val = HELPCH;
2118 goto ret;
2120 case (CTRL|'H') : /* CTRL-H backspace */
2121 case 0x7f : /* rubout */
2122 if (b <= buf){
2123 (*term.t_beep)();
2124 break;
2126 else
2127 b--;
2129 case (CTRL|'D') : /* CTRL-D delete char */
2130 case KEY_DEL :
2131 if (!*b){
2132 (*term.t_beep)();
2133 break;
2136 changed=TRUE;
2137 i = 0;
2138 dline.vused--;
2139 do /* blat out left char */
2140 b[i] = b[i+1];
2141 while(b[i++] != '\0');
2142 break;
2144 case (CTRL|'L') : /* CTRL-L redraw */
2145 return_val = (CTRL|'L');
2146 goto ret;
2148 case (CTRL|'K') : /* CTRL-K kill line */
2149 changed=TRUE;
2150 buf[0] = '\0';
2151 dline.vused = 0;
2152 b = buf;
2153 break;
2155 case F1 : /* sort of same thing */
2156 return_val = HELPCH;
2157 goto ret;
2159 case (CTRL|'M') : /* newline */
2160 return_val = changed;
2161 goto ret;
2163 #ifdef MOUSE
2164 case KEY_MOUSE :
2166 MOUSEPRESS mp;
2168 mouse_get_last (NULL, &mp);
2170 /* The clicked line have anything special on it? */
2171 switch(mp.button){
2172 case M_BUTTON_LEFT : /* position cursor */
2173 mp.col -= plen; /* normalize column */
2174 if(mp.col >= 0 && mp.col <= ucs4_strlen(buf))
2175 b = buf + mp.col;
2177 break;
2179 case M_BUTTON_RIGHT :
2180 #ifdef _WINDOWS
2181 mswin_allowpaste(MSWIN_PASTE_LINE);
2182 mswin_paste_popup();
2183 mswin_allowpaste(MSWIN_PASTE_DISABLE);
2184 break;
2185 #endif
2187 case M_BUTTON_MIDDLE : /* NO-OP for now */
2188 default: /* just ignore */
2189 break;
2193 continue;
2194 #endif
2196 default :
2198 /* look for match in extra_v */
2199 for(i = 0; i < 12; i++)
2200 if(c && c == extra_v[i]){
2201 return_val = c;
2202 goto ret;
2205 changed=TRUE;
2207 if(c & (CTRL | FUNC)){ /* bag ctrl_special chars */
2208 (*term.t_beep)();
2210 else{
2211 i = ucs4_strlen(b);
2212 if(flg&QNODQT){ /* reject double quotes? */
2213 if(c == '"'){
2214 (*term.t_beep)();
2215 continue;
2219 if(dline.vused >= nbuf-1){
2220 (*term.t_beep)();
2221 continue;
2224 do /* blat out left char */
2225 b[i+1] = b[i];
2226 while(i-- > 0);
2228 dline.vused++;
2229 *b++ = c;
2234 ret:
2235 if(lastc){
2236 (void) pico_set_colorp(lastc, PSC_NONE);
2237 free_color_pair(&lastc);
2239 else
2240 (*term.t_rev)(0);
2242 (*term.t_flush)();
2244 if(km_popped){
2245 term.t_mrow = 0;
2246 movecursor(term.t_nrow, 0);
2247 peeol();
2248 sgarbf = 1;
2249 km_popped = 0;
2252 if(dline.dl)
2253 fs_give((void **) &dline.dl);
2255 if(dline.olddl)
2256 fs_give((void **) &dline.olddl);
2258 return(return_val);
2262 void
2263 emlwrite(char *utf8message, EML *eml)
2265 UCS *message;
2267 message = utf8_to_ucs4_cpystr(utf8message ? utf8message : "");
2269 emlwrite_ucs4(message, eml);
2271 if(message)
2272 fs_give((void **) &message);
2277 * emlwrite() - write the message string to the error half of the screen
2278 * center justified. much like mlwrite (which is still used
2279 * to paint the line for prompts and such), except it center
2280 * the text.
2282 void
2283 emlwrite_ucs4(UCS *message, EML *eml)
2285 UCS *bufp, *ap;
2286 int width;
2287 COLOR_PAIR *lastc = NULL;
2288 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2290 mlerase();
2292 if(!(message && *message) || term.t_nrow < 2)
2293 return; /* nothing to write or no space to write, bag it */
2295 bufp = message;
2297 width = ucs4_str_width(message);
2300 * next, figure out where the to move the cursor so the message
2301 * comes out centered
2303 if((ap=ucs4_strchr(message, '%')) != NULL){
2304 width -= 2;
2305 switch(ap[1]){
2306 case '%':
2307 case 'c':
2308 width += (eml && eml->c) ? wcellwidth(eml->c) : 1;
2309 break;
2310 case 'd':
2311 width += dumbroot(eml ? eml->d : 0, 10);
2312 break;
2313 case 'D':
2314 width += dumblroot(eml ? eml->l : 0L, 10);
2315 break;
2316 case 'o':
2317 width += dumbroot(eml ? eml->d : 0, 8);
2318 break;
2319 case 'x':
2320 width += dumbroot(eml ? eml->d : 0, 16);
2321 break;
2322 case 's': /* string arg is UTF-8 */
2323 width += (eml && eml->s) ? utf8_width(eml->s) : 2;
2324 break;
2328 if(width+4 <= term.t_ncol)
2329 movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (width + 4))/2);
2330 else
2331 movecursor(term.t_nrow-term.t_mrow, 0);
2333 if(pcolors && pcolors->stcp
2334 && pico_is_good_colorpair(pcolors->stcp)){
2335 lastc = pico_get_cur_color();
2336 (void) pico_set_colorp(pcolors->stcp, PSC_NONE);
2338 else
2339 (*term.t_rev)(1);
2341 pputs_utf8("[ ", 1);
2342 while (*bufp != '\0' && ttcol < term.t_ncol-2){
2343 if(*bufp == '\007')
2344 (*term.t_beep)();
2345 else if(*bufp == '%'){
2346 switch(*++bufp){
2347 case 'c':
2348 if(eml && eml->c)
2349 pputc(eml->c, 0);
2350 else {
2351 pputs_utf8("%c", 0);
2353 break;
2354 case 'd':
2355 mlputi(eml ? eml->d : 0, 10);
2356 break;
2357 case 'D':
2358 mlputli(eml ? eml->l : 0L, 10);
2359 break;
2360 case 'o':
2361 mlputi(eml ? eml->d : 0, 16);
2362 break;
2363 case 'x':
2364 mlputi(eml ? eml->d : 0, 8);
2365 break;
2366 case 's':
2367 pputs_utf8((eml && eml->s) ? eml->s : "%s", 0);
2368 break;
2369 case '%':
2370 default:
2371 pputc(*bufp, 0);
2372 break;
2375 else
2376 pputc(*bufp, 0);
2377 bufp++;
2380 pputs_utf8(" ]", 1);
2382 if(lastc){
2383 (void) pico_set_colorp(lastc, PSC_NONE);
2384 free_color_pair(&lastc);
2386 else
2387 (*term.t_rev)(0);
2389 (*term.t_flush)();
2391 mpresf = TRUE;
2396 mlwrite_utf8(char *utf8fmt, void *arg)
2398 UCS *fmt;
2399 int ret;
2401 fmt = utf8_to_ucs4_cpystr(utf8fmt ? utf8fmt : "");
2402 ret = mlwrite(fmt, arg);
2403 if(fmt)
2404 fs_give((void **) &fmt);
2406 return(ret);
2411 * Write a message into the message line. Keep track of the physical cursor
2412 * position. A small class of printf like format items is handled. Assumes the
2413 * stack grows down; this assumption is made by the "++" in the argument scan
2414 * loop. Set the "message line" flag TRUE.
2417 mlwrite(UCS *fmt, void *arg)
2419 int ret, ww;
2420 UCS c;
2421 char *ap;
2422 COLOR_PAIR *lastc = NULL;
2423 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2426 * the idea is to only highlight if there is something to show
2428 mlerase();
2429 movecursor(ttrow, 0);
2431 if(pcolors && pcolors->prcp
2432 && pico_is_good_colorpair(pcolors->prcp)){
2433 lastc = pico_get_cur_color();
2434 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2436 else
2437 (*term.t_rev)(1);
2439 ap = (char *) &arg;
2441 while ((c = *fmt++) != 0) {
2442 if (c != '%') {
2443 pputc(c, 1);
2445 else {
2446 c = *fmt++;
2447 switch (c){
2448 case 'd':
2449 mlputi(*(int *)ap, 10);
2450 ap += sizeof(int);
2451 break;
2453 case 'o':
2454 mlputi(*(int *)ap, 8);
2455 ap += sizeof(int);
2456 break;
2458 case 'x':
2459 mlputi(*(int *)ap, 16);
2460 ap += sizeof(int);
2461 break;
2463 case 'D':
2464 mlputli(*(long *)ap, 10);
2465 ap += sizeof(long);
2466 break;
2468 case 's':
2469 pputs_utf8(*(char **)ap, 1);
2470 ap += sizeof(char *);
2471 break;
2473 default:
2474 pputc(c, 1);
2475 ww = wcellwidth(c);
2476 ttcol += (ww >= 0 ? ww : 1);
2481 ret = ttcol;
2482 while(ttcol < term.t_ncol)
2483 pputc(' ', 0);
2485 movecursor(term.t_nrow - term.t_mrow, ret);
2487 if(lastc){
2488 (void) pico_set_colorp(lastc, PSC_NONE);
2489 free_color_pair(&lastc);
2491 else
2492 (*term.t_rev)(0);
2494 (*term.t_flush)();
2495 mpresf = TRUE;
2497 return(ret);
2502 * Write out an integer, in the specified radix. Update the physical cursor
2503 * position. This will not handle any negative numbers; maybe it should.
2505 void
2506 mlputi(int i, int r)
2508 register int q;
2509 static char hexdigits[] = "0123456789ABCDEF";
2511 if (i < 0){
2512 i = -i;
2513 pputc('-', 1);
2516 q = i/r;
2518 if (q != 0)
2519 mlputi(q, r);
2521 pputc(hexdigits[i%r], 1);
2526 * do the same except as a long integer.
2528 void
2529 mlputli(long l, int r)
2531 register long q;
2533 if (l < 0){
2534 l = -l;
2535 pputc('-', 1);
2538 q = l/r;
2540 if (q != 0)
2541 mlputli(q, r);
2543 pputc((int)(l%r)+'0', 1);
2547 void
2548 unknown_command(UCS c)
2550 char buf[10], ch, *s;
2551 EML eml;
2553 buf[0] = '\0';
2554 s = buf;
2556 if(!c){
2557 /* fall through */
2559 else if(c & CTRL && c >= (CTRL|'@') && c <= (CTRL|'_')){
2560 ch = c - (CTRL|'@') + '@';
2561 snprintf(s, sizeof(buf), "^%c", ch);
2563 else
2564 switch(c){
2565 case ' ' : s = "SPACE"; break;
2566 case '\033' : s = "ESC"; break;
2567 case '\177' : s = "DEL"; break;
2568 case ctrl('I') : s = "TAB"; break;
2569 case ctrl('J') : s = "LINEFEED"; break;
2570 case ctrl('M') : s = "RETURN"; break;
2571 case ctrl('Q') : s = "XON"; break;
2572 case ctrl('S') : s = "XOFF"; break;
2573 case KEY_UP : s = "Up Arrow"; break;
2574 case KEY_DOWN : s = "Down Arrow"; break;
2575 case KEY_RIGHT : s = "Right Arrow"; break;
2576 case KEY_LEFT : s = "Left Arrow"; break;
2577 case CTRL|KEY_UP : s = "Ctrl-Up Arrow"; break;
2578 case CTRL|KEY_DOWN : s = "Ctrl-Down Arrow"; break;
2579 case CTRL|KEY_RIGHT : s = "Ctrl-Right Arrow"; break;
2580 case CTRL|KEY_LEFT : s = "Ctrl-Left Arrow"; break;
2581 case KEY_PGUP : s = "Prev Page"; break;
2582 case KEY_PGDN : s = "Next Page"; break;
2583 case KEY_HOME : s = "Home"; break;
2584 case KEY_END : s = "End"; break;
2585 case KEY_DEL : s = "Delete"; break; /* Not necessary DEL! */
2586 case F1 :
2587 case F2 :
2588 case F3 :
2589 case F4 :
2590 case F5 :
2591 case F6 :
2592 case F7 :
2593 case F8 :
2594 case F9 :
2595 case F10 :
2596 case F11 :
2597 case F12 :
2598 snprintf(s, sizeof(buf), "F%ld", (long) (c - PF1 + 1));
2599 break;
2601 default:
2602 if(c < CTRL)
2603 utf8_put((unsigned char *) s, (unsigned long) c);
2605 break;
2608 eml.s = s;
2609 emlwrite("Unknown Command: %s", &eml);
2610 (*term.t_beep)();
2615 * scrolldown - use stuff to efficiently move blocks of text on the
2616 * display, and update the pscreen array to reflect those
2617 * moves...
2619 * wp is the window to move in
2620 * r is the row at which to begin scrolling
2621 * n is the number of lines to scrol
2623 void
2624 scrolldown(WINDOW *wp, int r, int n)
2626 #ifdef TERMCAP
2627 register int i;
2628 register int l;
2629 register VIDEO *vp1;
2630 register VIDEO *vp2;
2632 if(!n)
2633 return;
2635 if(r < 0){
2636 r = wp->w_toprow;
2637 l = wp->w_ntrows;
2639 else{
2640 if(r > wp->w_toprow)
2641 vscreen[r-1]->v_flag |= VFCHG;
2642 l = wp->w_toprow+wp->w_ntrows-r;
2645 o_scrolldown(r, n);
2647 for(i=l-n-1; i >= 0; i--){
2648 vp1 = pscreen[r+i];
2649 vp2 = pscreen[r+i+n];
2650 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2652 pprints(r+n-1, r);
2653 ttrow = FARAWAY;
2654 ttcol = FARAWAY;
2655 #endif /* TERMCAP */
2660 * scrollup - use tcap stuff to efficiently move blocks of text on the
2661 * display, and update the pscreen array to reflect those
2662 * moves...
2664 void
2665 scrollup(WINDOW *wp, int r, int n)
2667 #ifdef TERMCAP
2668 register int i;
2669 register VIDEO *vp1;
2670 register VIDEO *vp2;
2672 if(!n)
2673 return;
2675 if(r < 0)
2676 r = wp->w_toprow;
2678 o_scrollup(r, n);
2680 i = 0;
2681 while(1){
2682 if(Pmaster){
2683 if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
2684 break;
2686 else{
2687 if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
2688 break;
2690 vp1 = pscreen[r+i+n];
2691 vp2 = pscreen[r+i];
2692 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2693 i++;
2695 pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
2696 ttrow = FARAWAY;
2697 ttcol = FARAWAY;
2698 #endif /* TERMCAP */
2703 * print spaces in the physical screen starting from row abs(n) working in
2704 * either the positive or negative direction (depending on sign of n).
2706 void
2707 pprints(int x, int y)
2709 register int i;
2710 register int j;
2712 if(x < y){
2713 for(i = x;i <= y; ++i){
2714 for(j = 0; j < term.t_ncol; j++){
2715 pscreen[i]->v_text[j].c = ' ';
2716 pscreen[i]->v_text[j].a = 0;
2720 else{
2721 for(i = x;i >= y; --i){
2722 for(j = 0; j < term.t_ncol; j++){
2723 pscreen[i]->v_text[j].c = ' ';
2724 pscreen[i]->v_text[j].a = 0;
2728 ttrow = y;
2729 ttcol = 0;
2734 * doton - return the physical line number that the dot is on in the
2735 * current window, and by side effect the number of lines remaining
2738 doton(int *r, unsigned *chs)
2740 register int i = 0;
2741 register LINE *lp = curwp->w_linep;
2742 int l = -1;
2744 assert(r != NULL && chs != NULL);
2746 *chs = 0;
2747 while(i++ < curwp->w_ntrows){
2748 if(lp == curwp->w_dotp)
2749 l = i-1;
2750 lp = lforw(lp);
2751 if(lp == curwp->w_bufp->b_linep){
2752 i++;
2753 break;
2755 if(l >= 0)
2756 (*chs) += llength(lp);
2758 *r = i - l - term.t_mrow;
2759 return(l+curwp->w_toprow);
2765 * resize_pico - given new window dimensions, allocate new resources
2768 resize_pico(int row, int col)
2770 int old_nrow, old_ncol;
2771 register int i;
2772 register VIDEO *vp;
2774 old_nrow = term.t_nrow;
2775 old_ncol = term.t_ncol;
2777 term.t_nrow = row;
2778 term.t_ncol = col;
2780 if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
2781 return(TRUE);
2783 if(curwp){
2784 curwp->w_toprow = 2;
2785 curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
2788 if(Pmaster){
2789 fillcol = Pmaster->fillcolumn;
2790 (*Pmaster->resize)();
2792 else if(userfillcol > 0)
2793 fillcol = userfillcol;
2794 else
2795 fillcol = term.t_ncol - 6; /* we control the fill column */
2798 * free unused screen space ...
2800 for(i=term.t_nrow+1; i <= old_nrow; ++i){
2801 free((char *) vscreen[i]);
2802 free((char *) pscreen[i]);
2806 * realloc new space for screen ...
2808 if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2809 if(Pmaster)
2810 return(-1);
2811 else
2812 exit(1);
2815 if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2816 if(Pmaster)
2817 return(-1);
2818 else
2819 exit(1);
2822 for (i = 0; i <= term.t_nrow; ++i) {
2823 if(i <= old_nrow)
2824 vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2825 else
2826 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2828 if (vp == NULL)
2829 exit(1);
2830 vp->v_flag = VFCHG;
2831 vscreen[i] = vp;
2832 if(old_ncol < term.t_ncol){ /* don't let any garbage in */
2833 vtrow = i;
2834 vtcol = (i < old_nrow) ? old_ncol : 0;
2835 vteeol();
2838 if(i <= old_nrow)
2839 vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2840 else
2841 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2843 if (vp == NULL)
2844 exit(1);
2846 vp->v_flag = VFCHG;
2847 pscreen[i] = vp;
2850 if(!ResizeBrowser()){
2851 if(Pmaster && Pmaster->headents){
2852 ResizeHeader();
2854 else{
2855 curwp->w_flag |= (WFHARD | WFMODE);
2856 pico_refresh(0, 1); /* redraw whole enchilada. */
2857 update(); /* do it */
2861 return(TRUE);
2864 void
2865 redraw_pico_for_callback(void)
2867 pico_refresh(0, 1);
2868 update();
2873 * showCompTitle - display the anchor line passed in from pine
2875 void
2876 showCompTitle(void)
2878 if(Pmaster){
2879 UCS *bufp;
2880 extern UCS *pico_anchor;
2881 COLOR_PAIR *lastc = NULL;
2883 if((bufp = pico_anchor) == NULL)
2884 return;
2886 movecursor(COMPOSER_TITLE_LINE, 0);
2887 if (Pmaster->colors && Pmaster->colors->tbcp &&
2888 pico_is_good_colorpair(Pmaster->colors->tbcp)){
2889 lastc = pico_get_cur_color();
2890 (void)pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2892 else
2893 (*term.t_rev)(1);
2895 while (ttcol < term.t_ncol)
2896 if(*bufp != '\0')
2897 pputc(*bufp++, 1);
2898 else
2899 pputc(' ', 1);
2901 if (lastc){
2902 (void)pico_set_colorp(lastc, PSC_NONE);
2903 free_color_pair(&lastc);
2905 else
2906 (*term.t_rev)(0);
2908 movecursor(COMPOSER_TITLE_LINE + 1, 0);
2909 peeol();
2916 * zotdisplay - blast malloc'd space created for display maps
2918 void
2919 zotdisplay(void)
2921 register int i;
2923 for (i = 0; i <= term.t_nrow; ++i){ /* free screens */
2924 free((char *) vscreen[i]);
2925 free((char *) pscreen[i]);
2928 free((char *) vscreen);
2929 free((char *) pscreen);
2935 * nlforw() - returns the number of lines from the top to the dot
2938 nlforw(void)
2940 register int i = 0;
2941 register LINE *lp = curwp->w_linep;
2943 while(lp != curwp->w_dotp){
2944 lp = lforw(lp);
2945 i++;
2947 return(i);
2953 * pputc - output the given char, keep track of it on the physical screen
2954 * array, and keep track of the cursor
2956 void
2957 pputc(UCS c, /* char to write */
2958 int a) /* and its attribute */
2960 int ind, width, printable_ascii = 0;
2963 * This is necessary but not sufficient to allow us to draw. Note that
2964 * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
2965 * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
2967 if((ttcol >= 0 && ttcol < term.t_ncol) && (ttrow >= 0 && ttrow <= term.t_nrow)){
2970 * Width is the number of screen columns a character will occupy.
2972 if(c < 0x80 && isprint(c)){
2973 printable_ascii++;
2974 width = 1;
2976 else
2977 width = wcellwidth(c);
2979 if(width < 0)
2980 width = 1; /* will be a '?' */
2982 if(ttcol + width <= term.t_ncol){ /* it fits */
2984 * Some terminals scroll when you write in the lower right corner
2985 * of the screen, so don't write there.
2987 if(!(ttrow == term.t_nrow && ttcol+width == term.t_ncol)){
2988 (*term.t_putchar)(c); /* write it */
2989 ind = index_from_col(ttrow, ttcol);
2990 pscreen[ttrow]->v_text[ind].c = c; /* keep track of it */
2991 pscreen[ttrow]->v_text[ind].a = a; /* keep track of it */
2994 else{
2996 * Character overlaps right edge of screen. Hopefully the higher
2997 * layers will prevent this but we're making sure.
2999 * We may want to do something like writing a space character
3000 * into the cells that are on the screen. We'll see.
3004 ttcol = MIN(term.t_ncol, ttcol+width);
3010 * pputs - print a string and keep track of the cursor
3012 void
3013 pputs(UCS *s, /* string to write */
3014 int a) /* and its attribute */
3016 while (*s != '\0')
3017 pputc(*s++, a);
3021 void
3022 pputs_utf8(char *s, int a)
3024 UCS *ucsstr = NULL;
3026 if(s && *s){
3027 ucsstr = utf8_to_ucs4_cpystr(s);
3028 if(ucsstr){
3029 pputs(ucsstr, a);
3030 fs_give((void **) &ucsstr);
3037 * peeol - physical screen array erase to end of the line. remember to
3038 * track the cursor.
3040 void
3041 peeol(void)
3043 int i, width = 0, ww;
3044 CELL cl;
3046 if(ttrow < 0 || ttrow > term.t_nrow)
3047 return;
3049 cl.c = '\0';
3050 cl.a = 0;
3053 * Don't clear if we think we are sitting past the last column,
3054 * that erases the last column if we just wrote it.
3056 if(ttcol < term.t_ncol)
3057 (*term.t_eeol)();
3060 * Because the characters are variable width it's a little tricky
3061 * to erase the rest of the line. What we do is add up the
3062 * widths of the characters until we reach ttcol
3063 * then set the rest to the space character.
3065 for(i = 0; i < term.t_ncol && width < ttcol; i++){
3066 ww = wcellwidth((UCS) pscreen[ttrow]->v_text[i].c);
3067 width += (ww >= 0 ? ww : 1);
3070 while(i < term.t_ncol)
3071 pscreen[ttrow]->v_text[i++] = cl;
3076 * pscr - return the character cell on the physical screen map on the
3077 * given line, l, and offset, o.
3079 CELL *
3080 pscr(int l, int o)
3082 if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
3083 return(&(pscreen[l]->v_text[o]));
3084 else
3085 return(NULL);
3090 * pclear() - clear the physical screen from row x through row y (inclusive)
3091 * row is zero origin, min row = 0 max row = t_nrow
3092 * Clear whole screen -- pclear(0, term.t_nrow)
3093 * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
3094 * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
3096 void
3097 pclear(int x, int y)
3099 register int i;
3101 x = MIN(MAX(0, x), term.t_nrow);
3102 y = MIN(MAX(0, y), term.t_nrow);
3104 for(i=x; i <= y; i++){
3105 movecursor(i, 0);
3106 peeol();
3112 * dumbroot - just get close
3115 dumbroot(int x, int b)
3117 if(x < b)
3118 return(1);
3119 else
3120 return(dumbroot(x/b, b) + 1);
3125 * dumblroot - just get close
3128 dumblroot(long x, int b)
3130 if(x < b)
3131 return(1);
3132 else
3133 return(dumblroot(x/b, b) + 1);
3138 * pinsertc - use optimized insert, fixing physical screen map.
3139 * returns true if char written, false otherwise
3142 pinsert(CELL c)
3144 int i, ind = 0, ww;
3145 CELL *p;
3147 if(ttrow < 0 || ttrow > term.t_nrow)
3148 return(0);
3150 if(o_insert((UCS) c.c)){ /* if we've got it, use it! */
3151 p = pscreen[ttrow]->v_text; /* then clean up physical screen */
3153 ind = index_from_col(ttrow, ttcol);
3155 for(i = term.t_ncol-1; i > ind; i--)
3156 p[i] = p[i-1]; /* shift right */
3158 p[ind] = c; /* insert new char */
3160 ww = wcellwidth((UCS) c.c);
3161 ttcol += (ww >= 0 ? ww : 1);
3163 return(1);
3166 return(0);
3171 * pdel - use optimized delete to rub out the current char and
3172 * fix the physical screen array.
3173 * returns true if optimized the delete, false otherwise
3176 pdel(void)
3178 int i, ind = 0, w;
3179 CELL *p;
3181 if(ttrow < 0 || ttrow > term.t_nrow)
3182 return(0);
3184 if(TERM_DELCHAR){ /* if we've got it, use it! */
3185 p = pscreen[ttrow]->v_text;
3186 ind = index_from_col(ttrow, ttcol);
3188 if(ind > 0){
3189 --ind;
3190 w = wcellwidth((UCS) p[ind].c);
3191 w = (w >= 0 ? w : 1);
3192 ttcol -= w;
3194 for(i = 0; i < w; i++){
3195 (*term.t_putchar)('\b'); /* move left a char */
3196 o_delete(); /* and delete it */
3199 /* then clean up physical screen */
3200 for(i=ind; i < term.t_ncol-1; i++)
3201 p[i] = p[i+1];
3203 p[i].c = ' ';
3204 p[i].a = 0;
3207 return(1);
3210 return(0);
3216 * wstripe - write out the given string at the given location, and reverse
3217 * video on flagged characters. Does the same thing as pine's
3218 * stripe.
3220 * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
3221 * only if you want to put the tildes before multi-byte chars.
3223 void
3224 wstripe(int line, int column, char *utf8pmt, int key)
3226 UCS *ucs4pmt, *u;
3227 int i = 0, col = 0;
3228 int j = 0;
3229 int l, ww;
3230 COLOR_PAIR *lastc = NULL;
3231 COLOR_PAIR *kncp = NULL;
3232 COLOR_PAIR *klcp = NULL;
3234 if(line < 0 || line > term.t_nrow)
3235 return;
3237 if (Pmaster && Pmaster->colors){
3238 if(pico_is_good_colorpair(Pmaster->colors->klcp))
3239 klcp = Pmaster->colors->klcp;
3241 if(klcp && pico_is_good_colorpair(Pmaster->colors->kncp))
3242 kncp = Pmaster->colors->kncp;
3244 else if(Pcolors){
3245 klcp = Pcolors->klcp;
3246 kncp = Pcolors->kncp;
3249 lastc = pico_get_cur_color();
3250 ucs4pmt = utf8_to_ucs4_cpystr(utf8pmt);
3251 l = ucs4_strlen(ucs4pmt);
3252 while(1){
3253 if(i >= term.t_ncol || col >= term.t_ncol || j >= l)
3254 return; /* equal strings */
3256 if(ucs4pmt[j] == (UCS) key)
3257 j++;
3259 if (pscr(line, i) == NULL)
3260 return;
3262 if(pscr(line, i)->c != ucs4pmt[j]){
3263 if(j >= 1 && ucs4pmt[j-1] == (UCS) key)
3264 j--;
3265 break;
3268 ww = wcellwidth((UCS) pscr(line, i)->c);
3269 col += (ww >= 0 ? ww : 1);
3270 j++;
3271 i++;
3274 movecursor(line, column+col);
3275 if(klcp) (void)pico_set_colorp(klcp, PSC_NONE);
3276 u = &ucs4pmt[j];
3278 if(*u == (UCS) key){
3279 u++;
3280 if(kncp)
3281 (void)pico_set_colorp(kncp, PSC_NONE);
3282 else
3283 (void)(*term.t_rev)(1);
3285 pputc(*u, 1);
3286 if(kncp)
3287 (void)pico_set_colorp(klcp, PSC_NONE);
3288 else
3289 (void)(*term.t_rev)(0);
3291 else{
3292 pputc(*u, 0);
3295 while(*++u != '\0');
3297 if(ucs4pmt)
3298 fs_give((void **) &ucs4pmt);
3300 peeol();
3301 if (lastc){
3302 (void)pico_set_colorp(lastc, PSC_NONE);
3303 free_color_pair(&lastc);
3305 (*term.t_flush)();
3311 * wkeyhelp - paint list of possible commands on the bottom
3312 * of the display (yet another pine clone)
3313 * NOTE: function key mode is handled here since all the labels
3314 * are the same...
3316 * The KEYMENU definitions have names and labels defined as UTF-8 strings,
3317 * and wstripe expects UTF-8.
3319 void
3320 wkeyhelp(KEYMENU *keymenu)
3322 char *obufp, *p, fkey[4];
3323 char linebuf[2*NLINE]; /* "2" is for space for invert tokens */
3324 int row, slot, tspace, adjusted_tspace, nspace[6], index, n;
3325 #ifdef MOUSE
3326 char nbuf[NLINE];
3327 #endif
3329 #ifdef _WINDOWS
3330 pico_config_menu_items (keymenu);
3331 #endif
3333 if(term.t_mrow == 0)
3334 return;
3336 if(term.t_nrow < 1)
3337 return;
3340 * Calculate amount of space for the names column by column...
3342 for(index = 0; index < 6; index++)
3343 if(!(gmode&MDFKEY)){
3344 nspace[index] = (keymenu[index].name)
3345 ? utf8_width(keymenu[index].name) : 0;
3346 if(keymenu[index+6].name
3347 && (n = utf8_width(keymenu[index+6].name)) > nspace[index])
3348 nspace[index] = n;
3350 nspace[index]++;
3352 else
3353 nspace[index] = (index < 4) ? 3 : 4;
3355 tspace = term.t_ncol/6; /* total space for each item */
3358 * Avoid writing in bottom right corner so we won't scroll screens that
3359 * scroll when you do that. The way this is setup, we won't do that
3360 * unless the number of columns is evenly divisible by 6.
3362 adjusted_tspace = (6 * tspace == term.t_ncol) ? tspace - 1 : tspace;
3364 index = 0;
3365 for(row = 0; row <= 1; row++){
3366 linebuf[0] = '\0';
3367 obufp = &linebuf[0];
3368 for(slot = 0; slot < 6; slot++){
3369 if(keymenu[index].name && keymenu[index].label){
3370 size_t l;
3371 char this_label[200], tmp_label[200];
3373 if(keymenu[index].label[0] == '[' && keymenu[index].label[(l=strlen(keymenu[index].label))-1] == ']' && l > 2){
3374 strncpy(tmp_label, &keymenu[index].label[1], MIN(sizeof(tmp_label),l-2));
3375 tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
3376 snprintf(this_label, sizeof(this_label), "[%s]", _(tmp_label));
3378 else
3379 strncpy(this_label, _(keymenu[index].label), sizeof(this_label));
3381 this_label[sizeof(this_label)-1] = '\0';
3383 if(gmode&MDFKEY){
3384 p = fkey;
3385 snprintf(fkey, sizeof(fkey), "F%d", (2 * slot) + row + 1);
3387 else
3388 p = keymenu[index].name;
3389 #ifdef MOUSE
3390 snprintf(nbuf, sizeof(nbuf), "%.*s %s", nspace[slot], p, this_label);
3391 register_key(index,
3392 (gmode&MDFKEY) ? F1 + (2 * slot) + row:
3393 (keymenu[index].name[0] == '^')
3394 ? (CTRL | keymenu[index].name[1])
3395 : (keymenu[index].name[0] == 'S'
3396 && !strcmp(keymenu[index].name, "Spc"))
3397 ? ' '
3398 : keymenu[index].name[0],
3399 nbuf, invert_label,
3400 term.t_nrow - 1 + row, (slot * tspace),
3401 strlen(nbuf),
3402 (Pmaster && Pmaster->colors)
3403 ? Pmaster->colors->kncp: NULL,
3404 (Pmaster && Pmaster->colors)
3405 ? Pmaster->colors->klcp: NULL);
3406 #endif
3408 n = nspace[slot];
3409 while(p && *p && n--){
3410 *obufp++ = '~'; /* insert "invert" token */
3411 *obufp++ = *p++;
3414 while(n-- > 0)
3415 *obufp++ = ' ';
3417 p = this_label;
3418 n = ((slot == 5 && row == 1) ? adjusted_tspace
3419 : tspace) - nspace[slot];
3420 while(p && *p && n-- > 0)
3421 *obufp++ = *p++;
3423 while(n-- > 0)
3424 *obufp++ = ' ';
3426 else{
3427 n = (slot == 5 && row == 1) ? adjusted_tspace : tspace;
3428 while(n--)
3429 *obufp++ = ' ';
3431 #ifdef MOUSE
3432 register_key(index, NODATA, "", NULL, 0, 0, 0, NULL, NULL);
3433 #endif
3436 *obufp = '\0';
3437 index++;
3440 wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
3446 * This returns the screen width between pstart (inclusive) and
3447 * pend (exclusive) where the pointers point into an array of CELLs.
3449 unsigned
3450 cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend)
3452 CELL *p;
3453 unsigned width = 0;
3454 int ww;
3456 if(pstart)
3457 for(p = pstart; p < pend; p++){
3458 ww = wcellwidth((UCS) p->c);
3459 width += (ww >= 0 ? ww : 1);
3462 return(width);
3467 * This returns the virtual screen width in row from index a to b (exclusive).
3469 unsigned
3470 vcellwidth_a_to_b(int row, int a, int b)
3472 CELL *pstart, *pend;
3473 VIDEO *vp;
3475 if(row < 0 || row > term.t_nrow)
3476 return 0;
3478 if(a >= b)
3479 return 0;
3481 a = MIN(MAX(0, a), term.t_ncol-1);
3482 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3484 vp = vscreen[row];
3485 pstart = &vp->v_text[a];
3486 pend = &vp->v_text[b];
3488 return(cellwidth_ptr_to_ptr(pstart, pend));
3493 * This returns the physical screen width in row from index a to b (exclusive).
3495 unsigned
3496 pcellwidth_a_to_b(int row, int a, int b)
3498 CELL *pstart, *pend;
3499 VIDEO *vp;
3501 if(row < 0 || row > term.t_nrow)
3502 return 0;
3504 if(a >= b)
3505 return 0;
3507 a = MIN(MAX(0, a), term.t_ncol-1);
3508 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3510 vp = pscreen[row];
3511 pstart = &vp->v_text[a];
3512 pend = &vp->v_text[b];
3514 return(cellwidth_ptr_to_ptr(pstart, pend));
3519 index_from_col(int row, int col)
3521 CELL *p_start, *p_end, *p_limit;
3522 int w_consumed = 0, w, done = 0;
3524 if(row < 0 || row > term.t_nrow)
3525 return 0;
3527 p_end = p_start = pscreen[row]->v_text;
3528 p_limit = p_start + term.t_ncol;
3530 if(p_start)
3531 while(!done && p_end < p_limit && p_end->c && w_consumed <= col){
3532 w = wcellwidth((UCS) p_end->c);
3533 w = (w >= 0 ? w : 1);
3534 if(w_consumed + w <= col){
3535 w_consumed += w;
3536 ++p_end;
3538 else
3539 ++done;
3542 /* MIN and MAX just to be sure */
3543 return(MIN(MAX(0, p_end - p_start), term.t_ncol-1));
3546 #ifdef _WINDOWS
3548 void
3549 pico_config_menu_items (KEYMENU *keymenu)
3551 int i;
3552 KEYMENU *k;
3553 UCS key;
3555 mswin_menuitemclear ();
3557 /* keymenu's seem to be hardcoded at 12 entries. */
3558 for (i = 0, k = keymenu; i < 12; ++i, ++k) {
3559 if (k->name != NULL && k->label != NULL &&
3560 k->menuitem != KS_NONE) {
3562 if (k->name[0] == '^')
3563 key = CTRL | k->name[1];
3564 else if (strcmp(k->name, "Ret") == 0)
3565 key = '\r';
3566 else
3567 key = k->name[0];
3569 mswin_menuitemadd (key, k->label, k->menuitem, 0);
3575 * Update the scroll range and position. (exported)
3577 * This is where curbp->b_linecnt is really managed. With out this function
3578 * to count the number of lines when needed curbp->b_linecnt will never
3579 * really be correct. BUT, this function is only compiled into the
3580 * windows version, so b_linecnt will only ever be right in the windows
3581 * version. OK for now because that is the only version that
3582 * looks at b_linecnt.
3585 update_scroll (void)
3587 long scr_pos;
3588 long scr_range;
3589 LINE *lp;
3590 static LINE *last_top_line = NULL;
3591 static long last_scroll_pos = -1;
3594 if (ComposerEditing) {
3595 /* Editing header - don't allow scroll bars. */
3596 mswin_setscrollrange (0, 0);
3597 return(0);
3602 * Count the number of lines in the current bufer. Done when:
3604 * when told to recount: curbp->b_linecnt == -1
3605 * when the top line changed: curwp->w_linep != last_top_line
3606 * when we don't know the scroll pos: last_scroll_pos == -1
3608 * The first line in the list is a "place holder" line and is not
3609 * counted. The list is circular, when we return the to place
3610 * holder we have reached the end.
3612 if(curbp->b_linecnt == -1 || curwp->w_linep != last_top_line
3613 || last_scroll_pos == -1) {
3614 scr_range = 0;
3615 scr_pos = 0;
3616 for (lp = lforw (curbp->b_linep); lp != curbp->b_linep;
3617 lp = lforw (lp)) {
3618 if (lp == curwp->w_linep)
3619 scr_pos = scr_range;
3621 ++scr_range;
3624 curbp->b_linecnt = scr_range;
3625 last_scroll_pos = scr_pos;
3626 last_top_line = curwp->w_linep;
3630 * Set new scroll range and position.
3632 mswin_setscrollrange (curwp->w_ntrows - 2, curbp->b_linecnt - 1);
3633 mswin_setscrollpos (last_scroll_pos);
3634 return (0);
3636 #endif /* _WINDOWS */