* clear out some warnings by gcc 9.3.1.
[alpine.git] / pico / display.c
blob705dae109eeef3b13066e20b455d7be32770af88
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-2020 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 *, int);
47 void updatelinecolor(int, CELL *, CELL *, short *, int);
48 void updext(void);
49 void mlputi(int, int);
50 void pprints(int, int);
51 void mlputli(long, int);
52 void showCompTitle(void);
53 int nlforw(void);
54 int dumbroot(int, int);
55 int dumblroot(long, int);
56 unsigned cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend);
57 unsigned vcellwidth_a_to_b(int row, int a, int b);
58 int window_signature_block(WINDOW *wp);
59 #ifdef _WINDOWS
60 void pico_config_menu_items (KEYMENU *);
61 int update_scroll (void);
62 #endif /* _WINDOWS */
66 * Standard pico keymenus...
68 static KEYMENU menu_pico[] = {
69 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^O", N_("WriteOut"), KS_SAVEFILE},
70 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
71 {"^K", N_("Cut Text"), KS_NONE}, {"^C", N_("Cur Pos"), KS_CURPOSITION},
72 {"^X", N_("Exit"), KS_EXIT}, {"^J", N_("Justify"), KS_JUSTIFY},
73 {"^W", N_("Where is"), KS_WHEREIS}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
74 {"^U", NULL, KS_NONE},
75 #ifdef SPELLER
76 {"^T", N_("To Spell"), KS_SPELLCHK}
77 #else
78 {"^D", N_("Del Char"), KS_NONE}
79 #endif
81 #define UNCUT_KEY 10
84 static KEYMENU menu_compose[] = {
85 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^X", NULL, KS_SEND},
86 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
87 {"^K", N_("Cut Text"), KS_NONE}, {"^O", N_("Postpone"), KS_POSTPONE},
88 /* TRANSLATORS: Justify is to reformat a paragraph automatically */
89 {"^C", N_("Cancel"), KS_CANCEL}, {"^J", N_("Justify"), KS_JUSTIFY},
90 {NULL, NULL, KS_NONE}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
91 {"^U", NULL, KS_NONE},
92 #ifdef SPELLER
93 {"^T", N_("To Spell"), KS_SPELLCHK}
94 #else
95 {"^D", N_("Del Char"), KS_NONE}
96 #endif
98 #define EXIT_KEY 1
99 #define PSTPN_KEY 5
100 #define WHERE_KEY 8
104 * Definition's for pico's modeline
106 #define PICO_TITLE " UW PICO %s"
107 #define PICO_MOD_MSG "Modified"
108 #define PICO_NEWBUF_MSG "New Buffer"
110 #define WFDEBUG 0 /* Window flag debug. */
112 #define VFCHG 0x0001 /* Changed flag */
113 #define VFEXT 0x0002 /* extended (beyond column 80) */
114 #define VFSIG 0x0004 /* in signature block */
116 int vtrow = 0; /* Row location of SW cursor */
117 int vtcol = 0; /* Column location of SW cursor */
118 int vtind = 0; /* Index into row array of SW cursor */
119 int ttrow = FARAWAY; /* Row location of HW cursor */
120 int ttcol = FARAWAY; /* Column location of HW cursor */
121 int lbound = 0; /* leftmost column of current line
122 being displayed */
124 VIDEO **vscreen; /* Virtual screen. */
125 VIDEO **pscreen; /* Physical screen. */
129 * Initialize the data structures used by the display code. The edge vectors
130 * used to access the screens are set up. The operating system's terminal I/O
131 * channel is set up. All the other things get initialized at compile time.
132 * The original window has "WFCHG" set, so that it will get completely
133 * redrawn on the first call to "update".
136 vtinit(void)
138 int i, j;
139 VIDEO *vp;
140 CELL ac;
142 ac.c = ' ';
143 ac.a = 0;
145 if(Pmaster == NULL)
146 vtterminalinfo(gmode & MDTCAPWINS);
148 (*term.t_open)();
150 (*term.t_rev)(FALSE);
151 vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
152 memset(vscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
153 if (vscreen == NULL){
154 emlwrite("Allocating memory for virtual display failed.", NULL);
155 return(FALSE);
158 pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
159 memset(pscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
160 if (pscreen == NULL){
161 free((void *)vscreen);
162 emlwrite("Allocating memory for physical display failed.", NULL);
163 return(FALSE);
167 for (i = 0; i <= term.t_nrow; ++i) {
168 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
170 if (vp == NULL){
171 free((void *)vscreen);
172 free((void *)pscreen);
173 emlwrite("Allocating memory for virtual display lines failed.",
174 NULL);
175 return(FALSE);
177 else
178 for(j = 0; j < term.t_ncol; j++)
179 vp->v_text[j] = ac;
181 vp->v_flag = 0;
182 vp->v_length = 0;
183 vscreen[i] = vp;
185 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
187 if (vp == NULL){
188 free((void *)vscreen[i]);
189 while(--i >= 0){
190 free((void *)vscreen[i]);
191 free((void *)pscreen[i]);
194 free((void *)vscreen);
195 free((void *)pscreen);
196 emlwrite("Allocating memory for physical display lines failed.",
197 NULL);
198 return(FALSE);
200 else
201 for(j = 0; j < term.t_ncol; j++)
202 vp->v_text[j] = ac;
204 vp->v_flag = 0;
205 vp->v_length = 0;
206 pscreen[i] = vp;
209 return(TRUE);
213 vtterminalinfo(int termcap_wins)
215 return((term.t_terminalinfo) ? (*term.t_terminalinfo)(termcap_wins)
216 : (Pmaster ? 0 : TRUE));
221 * Clean up the virtual terminal system, in anticipation for a return to the
222 * operating system. Move down to the last line and clear it out (the next
223 * system prompt will be written in the line). Shut down the channel to the
224 * terminal.
226 void
227 vttidy(void)
229 movecursor(term.t_nrow-1, 0);
230 peeol();
231 movecursor(term.t_nrow, 0);
232 peeol();
233 (*term.t_close)();
238 * Set the virtual cursor to the specified row and column on the virtual
239 * screen. There is no checking for nonsense values; this might be a good
240 * idea during the early stages.
242 void
243 vtmove(int row, int col)
245 vtrow = row;
246 vtcol = col;
248 if(vtcol < 0)
249 vtind = -1;
250 else if(vtcol == 0)
251 vtind = vtcol;
252 else{
254 * This is unused so don't worry about it.
256 assert(0);
262 * Write a character to the virtual screen. The virtual row and column are
263 * updated. If the line is too long put a "$" in the last column. This routine
264 * only puts printing characters into the virtual terminal buffers. Only
265 * column overflow is checked.
267 void
268 vtputc(CELL c)
270 VIDEO *vp;
271 CELL ac;
272 int w;
274 vp = vscreen[vtrow];
275 ac.c = ' ';
276 ac.a = c.a;
277 ac.d = c.d;
279 if (vtcol >= term.t_ncol) {
281 * What's this supposed to be doing? This sets vtcol
282 * to the even multiple of 8 >= vtcol. Why are we doing that?
283 * Must make tab work correctly.
284 * 24 -> 24
285 * 25 -> 32
286 * ...
287 * 31 -> 32
288 * 32 -> 32
289 * 33 -> 40
291 vtcol = (vtcol + 0x07) & ~0x07;
292 ac.c = '$';
295 * If we get to here that means that there must be characters
296 * past the right hand edge, so we want to put a $ character
297 * in the last visible character. It would be nice to replace
298 * the last visible character by a double-$ if it is double-width
299 * but we aren't doing that because we'd have to add up the widths
300 * starting at the left hand margin each time through.
302 if(vtind > 0 && vtind <= term.t_ncol)
303 vp->v_text[vtind-1] = ac;
305 else if (c.c == '\t') {
306 do {
307 vtputc(ac);
309 while (((vtcol + (vtrow==currow ? lbound : 0)) & 0x07) != 0 && vtcol < term.t_ncol);
311 else if (ISCONTROL(c.c)){
312 ac.c = '^';
313 vtputc(ac);
314 ac.c = ((c.c & 0x7f) | 0x40);
315 vtputc(ac);
317 else{
320 * Have to worry about what happens if we skip over 0
321 * with a double-width character. There may be a better
322 * place to be setting vtind, or maybe we could make do
323 * without it.
326 w = wcellwidth((UCS) c.c);
327 w = (w >= 0 ? w : 1);
329 if(vtcol == 0 || (vtcol < 0 && vtcol + w == 1)){
330 vtind = 0;
331 if(vtcol < 0)
332 vtcol = 0;
336 * Double-width character overlaps right edge.
337 * Replace it with a $.
339 if(vtcol + w > term.t_ncol){
340 ac.c = '$';
341 c = ac;
342 w = 1;
345 if(vtind >= 0 && vtind < term.t_ncol)
346 vp->v_text[vtind++] = c;
348 vtcol += w;
354 * Erase from the end of the software cursor to the end of the line on which
355 * the software cursor is located.
357 void
358 vteeol(void)
360 register VIDEO *vp;
361 CELL c;
363 c.c = ' ';
364 c.a = 0;
365 vp = vscreen[vtrow];
367 if(vtind >= 0)
368 while (vtind < term.t_ncol)
369 vp->v_text[vtind++] = c;
371 vtcol = term.t_ncol;
375 window_signature_block(WINDOW *wp)
377 LINE *lp, *llp;
378 int in_sig, is_sig_start;
379 int change = 0;
381 llp = wp->w_linep;
382 lp = lforw(wp->w_bufp->b_linep);
384 do {
385 in_sig = lback(lp) == wp->w_bufp->b_linep ? 0 : lback(lp)->l_sig;
386 if(in_sig == 0){
387 if(llength(lp) == 3){
388 if(lgetc(lp, 0).c == '-'
389 && lgetc(lp, 1).c == '-'
390 && lgetc(lp, 2).c == ' '){
391 in_sig = 1;
392 is_sig_start = 1;
395 } else {
396 if(lisblank(lp))
397 if(is_sig_start == 0) in_sig = 0;
398 is_sig_start = 0;
400 if(lp->l_sig != in_sig)
401 change++;
402 lp->l_sig = in_sig;
403 lp = lforw(lp);
404 } while(lp != wp->w_bufp->b_linep);
405 wp->w_linep = llp;
406 return change;
411 * Make sure that the display is right. This is a three part process. First,
412 * scan through all of the windows looking for dirty ones. Check the framing,
413 * and refresh the screen. Second, make sure that "currow" and "curcol" are
414 * correct for the current window. Third, make the virtual and physical
415 * screens the same.
417 void
418 update(void)
420 LINE *lp;
421 WINDOW *wp;
422 VIDEO *vp1;
423 VIDEO *vp2;
424 int i;
425 int j;
426 int scroll = 0;
427 int repaint= 0;
428 int quoted;
429 CELL c;
430 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
432 #if TYPEAH
433 if (typahead())
434 return;
435 #endif
437 #ifdef _WINDOWS
438 /* This tells our MS Windows module to not bother updating the
439 * cursor position while a massive screen update is in progress.
441 mswin_beginupdate ();
442 #endif
445 * BUG: setting and unsetting whole region at a time is dumb. fix this.
447 if(curwp->w_markp){
448 unmarkbuffer();
449 markregion(1);
452 wp = wheadp;
454 while (wp != NULL){
455 /* Look at any window with update flags set on. */
457 if(pcolors && (repaint = window_signature_block(wp))){
458 sgarbf = TRUE;
459 wp->w_flag |= WFEDIT | WFHARD;
461 if (wp->w_flag != 0){
462 /* If not force reframe, check the framing. */
464 if ((wp->w_flag & WFFORCE) == 0){
465 lp = wp->w_linep;
467 for (i = 0; i < wp->w_ntrows; ++i){
468 if (lp == wp->w_dotp)
469 goto out;
471 if (lp == wp->w_bufp->b_linep)
472 break;
474 lp = lforw(lp);
478 /* Not acceptable, better compute a new value for the line at the
479 * top of the window. Then set the "WFHARD" flag to force full
480 * redraw.
482 i = wp->w_force;
484 if (i > 0){
485 --i;
487 if (i >= wp->w_ntrows)
488 i = wp->w_ntrows-1;
490 else if (i < 0){
491 i += wp->w_ntrows;
493 if (i < 0)
494 i = 0;
496 else if(TERM_OPTIMIZE){
498 * find dotp, if its been moved just above or below the
499 * window, use scrollxxx() to facilitate quick redisplay...
501 lp = lforw(wp->w_dotp);
502 if(lp != wp->w_dotp){
503 if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
504 scroll = 1;
506 else {
507 lp = wp->w_linep;
508 for(j=0;j < wp->w_ntrows; ++j){
509 if(lp != wp->w_bufp->b_linep)
510 lp = lforw(lp);
511 else
512 break;
514 if(lp == wp->w_dotp && j == wp->w_ntrows)
515 scroll = 2;
518 j = i = wp->w_ntrows/2;
520 else
521 i = wp->w_ntrows/2;
523 lp = wp->w_dotp;
525 while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
526 --i;
527 lp = lback(lp);
531 * this is supposed to speed things up by using tcap sequences
532 * to efficiently scroll the terminal screen. the thinking here
533 * is that its much faster to update pscreen[] than to actually
534 * write the stuff to the screen...
536 if(TERM_OPTIMIZE){
537 switch(scroll){
538 case 1: /* scroll text down */
539 j = j-i+1; /* add one for dot line */
541 * do we scroll down the header as well? Well, only
542 * if we're not editing the header, we've backed up
543 * to the top, and the composer is not being
544 * displayed...
546 if(Pmaster && Pmaster->headents && !ComposerEditing
547 && (lback(lp) == wp->w_bufp->b_linep)
548 && (ComposerTopLine == COMPOSER_TOP_LINE))
549 j += entry_line(1000, TRUE); /* Never > 1000 headers */
551 scrolldown(wp, -1, j);
552 break;
553 case 2: /* scroll text up */
554 j = wp->w_ntrows - (j-i); /* we chose new top line! */
555 if(Pmaster && j){
557 * do we scroll down the header as well? Well, only
558 * if we're not editing the header, we've backed up
559 * to the top, and the composer is not being
560 * displayed...
562 if(!ComposerEditing
563 && (ComposerTopLine != COMPOSER_TOP_LINE))
564 scrollup(wp, COMPOSER_TOP_LINE,
565 j+entry_line(1000, TRUE));
566 else
567 scrollup(wp, -1, j);
569 else
570 scrollup(wp, -1, j);
571 break;
572 default :
573 break;
577 wp->w_linep = lp;
578 wp->w_flag |= WFHARD; /* Force full. */
579 out:
581 * if the line at the top of the page is the top line
582 * in the body, show the header...
584 if(Pmaster && Pmaster->headents && !ComposerEditing){
585 if(lback(wp->w_linep) == wp->w_bufp->b_linep){
586 if(ComposerTopLine == COMPOSER_TOP_LINE){
587 i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
588 if(i > 0 && nlforw() >= i) { /* room for header ? */
589 if((i = nlforw()/2) == 0 && term.t_nrow&1)
590 i = 1;
591 while(wp->w_linep != wp->w_bufp->b_linep && i--)
592 wp->w_linep = lforw(wp->w_linep);
595 else
596 ToggleHeader(1);
599 else{
600 if(ComposerTopLine != COMPOSER_TOP_LINE)
601 ToggleHeader(0); /* hide it ! */
605 /* Try to use reduced update. Mode line update has its own special
606 * flag. The fast update is used if the only thing to do is within
607 * the line editing.
609 lp = wp->w_linep;
610 i = wp->w_toprow;
612 if ((wp->w_flag & ~WFMODE) == WFEDIT){
613 while (lp != wp->w_dotp){
614 ++i;
615 lp = lforw(lp);
617 vscreen[i]->v_flag |= (lp->l_sig ? VFSIG : 0)| VFCHG;
618 /* compute physical length of line in screen */
619 vscreen[i]->v_length = 0;
620 for (j = 0; vscreen[i]->v_length < term.t_ncol
621 && j < llength(lp); ++j){
622 c = lgetc(lp, j);
623 if(c.c == '\t'){
624 vscreen[i]->v_length |= 0x07;
625 vscreen[i]->v_length++;
627 else if(ISCONTROL(c.c)){
628 vscreen[i]->v_length += 2;
630 else{
631 int w;
633 w = wcellwidth((UCS) c.c);
634 vscreen[i]->v_length += (w >= 0 ? w : 1);
637 vtmove(i, 0);
639 for (j = 0; j < llength(lp); ++j)
640 vtputc(lgetc(lp, j));
641 vteeol();
643 else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
644 while (i < wp->w_toprow+wp->w_ntrows){
645 vscreen[i]->v_flag |= (lp->l_sig ? VFSIG : 0 )| VFCHG;
646 /* compute physical length of line in screen */
647 vscreen[i]->v_length = 0;
648 for (j = 0; vscreen[i]->v_length < term.t_ncol
649 && j < llength(lp); ++j){
650 c = lgetc(lp, j);
651 if(c.c == '\t'){
652 vscreen[i]->v_length |= 0x07;
653 vscreen[i]->v_length++;
655 else if(ISCONTROL(c.c)){
656 vscreen[i]->v_length += 2;
658 else{
659 int w;
661 w = wcellwidth((UCS) c.c);
662 vscreen[i]->v_length += (w >= 0 ? w : 1);
665 vtmove(i, 0);
667 /* if line has been changed */
668 if (lp != wp->w_bufp->b_linep){
669 for (j = 0; j < llength(lp); ++j)
670 vtputc(lgetc(lp, j));
672 lp = lforw(lp);
675 vteeol();
676 ++i;
679 #if ~WFDEBUG
680 if ((wp->w_flag&WFMODE) != 0)
681 modeline(wp);
683 wp->w_flag = 0;
684 wp->w_force = 0;
685 #endif
687 #if WFDEBUG
688 modeline(wp);
689 wp->w_flag = 0;
690 wp->w_force = 0;
691 #endif
693 /* and onward to the next window */
694 wp = wp->w_wndp;
697 /* Always recompute the row and column number of the hardware cursor. This
698 * is the only update for simple moves.
700 lp = curwp->w_linep;
701 currow = curwp->w_toprow;
703 while (lp != curwp->w_dotp){
704 ++currow;
705 lp = lforw(lp);
708 curcol = 0;
709 i = 0;
711 while (i < curwp->w_doto){
712 c = lgetc(lp, i++);
714 if(c.c == '\t'){
715 curcol |= 0x07;
716 ++curcol;
718 else if(ISCONTROL(c.c)){
719 curcol += 2;
721 else{
722 int w;
724 w = wcellwidth((UCS) c.c);
725 curcol += (w >= 0 ? w : 1);
729 if (curcol >= term.t_ncol) { /* extended line. */
730 /* flag we are extended and changed */
731 vscreen[currow]->v_flag |= VFEXT | VFCHG;
732 updext(); /* and output extended line */
733 } else
734 lbound = 0; /* not extended line */
736 /* make sure no lines need to be de-extended because the cursor is
737 * no longer on them
740 wp = wheadp;
742 while (wp != NULL) {
743 lp = wp->w_linep;
744 i = wp->w_toprow;
746 while (i < wp->w_toprow + wp->w_ntrows) {
747 if (vscreen[i]->v_flag & VFEXT) {
748 /* always flag extended lines as changed */
749 vscreen[i]->v_flag |= VFCHG;
750 if ((wp != curwp) || (lp != wp->w_dotp) ||
751 (curcol < term.t_ncol)) {
752 vtmove(i, 0);
753 for (j = 0; j < llength(lp); ++j)
754 vtputc(lgetc(lp, j));
755 vteeol();
757 /* this line no longer is extended */
758 vscreen[i]->v_flag &= ~VFEXT;
761 lp = lforw(lp);
762 ++i;
764 /* and onward to the next window */
765 wp = wp->w_wndp;
768 /* Special hacking if the screen is garbage. Clear the hardware screen,
769 * and update your copy to agree with it. Set all the virtual screen
770 * change bits, to force a full update.
773 if (sgarbf != FALSE){
774 if(Pmaster){
775 int rv;
777 showCompTitle();
779 if(ComposerTopLine != COMPOSER_TOP_LINE){
780 UpdateHeader(0); /* arrange things */
781 PaintHeader(COMPOSER_TOP_LINE, TRUE);
785 * since we're using only a portion of the screen and only
786 * one buffer, only clear enough screen for the current window
787 * which is to say the *only* window.
789 for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
790 movecursor(i, 0);
791 peeol();
792 vscreen[i]->v_flag |= VFCHG;
794 rv = (*Pmaster->showmsg)('X' & 0x1f); /* ctrl-L */
795 ttresize();
796 picosigs(); /* restore altered handlers */
797 if(rv) /* Did showmsg corrupt the display? */
798 PaintBody(0); /* Yes, repaint */
799 movecursor(wheadp->w_toprow, 0);
801 else{
802 c.c = ' ';
803 c.a = 0;
804 for (i = 0; i < term.t_nrow-term.t_mrow; i++){
805 vscreen[i]->v_flag |= VFCHG;
806 vp1 = pscreen[i];
807 for (j = 0; j < term.t_ncol; j++)
808 vp1->v_text[j] = c;
809 if(sgarbf == FALSE){
810 movecursor(i, 0);
811 term.t_eeol();
814 if(sgarbf != FALSE){
815 movecursor(0, 0); /* Erase the screen. */
816 (*term.t_eeop)();
820 sgarbf = FALSE; /* Erase-page clears */
821 mpresf = FALSE; /* the message area. */
823 if(Pmaster)
824 modeline(curwp);
825 else
826 sgarbk = TRUE; /* fix the keyhelp as well...*/
829 /* Make sure that the physical and virtual displays agree. Unlike before,
830 * the "updateline" code is only called with a line that has been updated
831 * for sure.
833 if(Pmaster)
834 i = curwp->w_toprow;
835 else
836 i = 0;
838 if (term.t_nrow > term.t_mrow)
839 c.c = term.t_nrow - term.t_mrow;
840 else
841 c.c = 0;
843 for (; i < (int)c.c; ++i){
845 vp1 = vscreen[i];
847 /* for each line that needs to be updated, or that needs its
848 reverse video status changed, call the line updater */
849 j = vp1->v_flag;
850 if (j & VFCHG){
852 #if TYPEAH
853 if (typahead()){
854 #ifdef _WINDOWS
855 mswin_endupdate ();
856 #endif
857 return;
859 #endif
860 vp2 = pscreen[i];
862 updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag, vp1->v_length);
866 if(Pmaster == NULL){
868 if(sgarbk != FALSE){
869 if(term.t_mrow > 0){
870 movecursor(term.t_nrow-1, 0);
871 peeol();
872 movecursor(term.t_nrow, 0);
873 peeol();
876 if(lastflag&CFFILL){
877 /* TRANSLATORS: UnJustify means undo the previous
878 Justify command. */
879 menu_pico[UNCUT_KEY].label = N_("UnJustify");
880 if(!(lastflag&CFFLBF)){
881 emlwrite(_("Can now UnJustify!"), NULL);
882 mpresf = FARAWAY; /* remove this after next keystroke! */
885 else
886 menu_pico[UNCUT_KEY].label = N_("UnCut Text");
888 wkeyhelp(menu_pico);
889 sgarbk = FALSE;
893 if(lastflag&CFFLBF){
894 emlwrite(_("Can now UnJustify!"), NULL);
895 mpresf = FARAWAY; /* remove this after next keystroke! */
898 /* Finally, update the hardware cursor and flush out buffers. */
900 movecursor(currow, curcol - lbound);
901 #ifdef _WINDOWS
902 mswin_endupdate ();
905 * Update the scroll bars. This function is where curbp->b_linecnt
906 * is really managed. See update_scroll.
908 update_scroll ();
909 #endif
910 (*term.t_flush)();
914 /* updext - update the extended line which the cursor is currently
915 * on at a column greater than the terminal width. The line
916 * will be scrolled right or left to let the user see where
917 * the cursor is
919 void
920 updext(void)
922 int rcursor; /* real cursor location */
923 LINE *lp; /* pointer to current line */
924 int j; /* index into line */
925 int w = 0;
926 int ww;
929 * Calculate what column the real cursor will end up in.
930 * The cursor will be in the rcursor'th column. So if we're
931 * counting columns 0 1 2 3 and rcursor is 8, then rcursor
932 * will be over cell 7.
934 * What this effectively does is to scroll the screen as we're
935 * moving to the right when the cursor first passes off the
936 * screen's right edge. It would be nice if it did the same
937 * thing coming back to the left. Instead, in order that the
938 * screen's display depends only on the curcol and not on
939 * how we got there, the screen scrolls when we pass the
940 * t_margin column. It's also kind of funky that you can't
941 * see the character under the $ but you can delete it.
943 rcursor = ((curcol - term.t_ncol) % (term.t_ncol - term.t_margin + 1)) + term.t_margin;
944 lbound = curcol - rcursor + 1;
947 * Make sure lbound is set so that a double-width character does
948 * not straddle the boundary. If it does, move over one cell.
950 lp = curwp->w_dotp; /* line to output */
951 for (j=0; j<llength(lp) && w < lbound; ++j){
952 ww = wcellwidth((UCS) lgetc(lp, j).c);
953 w += (ww >= 0 ? ww : 1);
956 if(w > lbound)
957 lbound = w;
960 /* scan through the line outputting characters to the virtual screen
961 * once we reach the left edge
963 vtmove(currow, -lbound); /* start scanning offscreen */
964 for (j=0; j<llength(lp); ++j) /* until the end-of-line */
965 vtputc(lgetc(lp, j));
967 /* truncate the virtual line */
968 vteeol();
970 /* and put a '$' in column 1, may have to adjust curcol */
971 w = wcellwidth((UCS) vscreen[currow]->v_text[0].c);
972 vscreen[currow]->v_text[0].c = '$';
973 vscreen[currow]->v_text[0].a = 0;
974 if(w == 2){
976 * We want to put $ in the first two columns so that it
977 * takes up the right amount of space, but that means we
978 * have to scoot the real characters over one slot.
980 for (j = term.t_ncol-1; j >= 2; --j)
981 vscreen[currow]->v_text[j] = vscreen[currow]->v_text[j-1];
983 vscreen[currow]->v_text[1].c = '$';
984 vscreen[currow]->v_text[1].a = 0;
988 /* update line color, to be executed to update lines when color is on */
989 void
990 updatelinecolor (int row, CELL vline[], CELL pline[], short *flags, int len)
992 CELL *cp1, *cp2, *cp3, *cp4, *cp5, *cp6, *cp7;
993 int nbflag; /* non-blanks to the right flag? */
994 int cleartoeol = 0;
995 int in_quote, quote_found = 0, level;
996 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
997 COLOR_PAIR *qcolor = NULL, *lastc = NULL, *pcolor = NULL;
998 int first = 1, lastattr = -1, change = 0;
1000 if(pcolors == NULL){
1001 updateline(row, vline, pline, flags, len);
1002 return;
1005 nbflag = FALSE;
1006 lastc = pico_get_cur_color();
1008 /* set up pointers to virtual and physical lines */
1009 cp1 = &vline[0];
1010 cp2 = &pline[0];
1011 cp3 = &vline[term.t_ncol];
1012 cp4 = &pline[term.t_ncol];
1014 if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
1015 while (cp3 != cp1 && cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
1016 --cp3;
1017 --cp4;
1018 if (cp3[0].c != ' ' || cp3[0].a != 0) /* Note if any nonblank */
1019 nbflag = TRUE; /* in right match. */
1022 cp5 = cp3;
1024 if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
1025 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1026 --cp5;
1028 if (cp3-cp5 <= 3) /* Use only if erase is */
1029 cp5 = cp3; /* fewer characters. */
1032 /* go to start of line */
1033 movecursor(row, 0);
1035 if(cp1 != cp5){
1036 int w1, w2;
1038 w1 = cellwidth_ptr_to_ptr(cp1, cp3);
1039 w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
1041 if(w1 < w2 || (nbflag && w1 != w2)){
1042 if(TERM_EOLEXIST){
1043 if(nbflag){
1045 * Draw all of the characters starting with cp1
1046 * until we get to all spaces, then clear to the end of
1047 * line from there. Watch out we don't run over the
1048 * right hand edge, which shouldn't happen.
1050 * Set cp5 to the first of the repeating spaces.
1052 cp5 = &vline[term.t_ncol];
1053 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1054 --cp5;
1058 * In the !nbflag case we want spaces from cp5 on.
1059 * Setting cp3 to something different from cp5 triggers
1060 * the clear to end of line below.
1062 if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
1063 cleartoeol++;
1065 else{
1066 int w;
1069 * No peeol so draw all the way to the edge whether they
1070 * are spaces or not.
1072 cp3 = &vline[0];
1073 for(w = 0; w < term.t_ncol; cp3++){
1074 int ww;
1076 ww = wcellwidth((UCS) cp3->c);
1077 w += (ww >= 0 ? ww : 1);
1080 cp5 = cp3;
1085 if(row != 0 && len < term.t_ncol)
1086 cp5 = cp1 + len;
1088 in_quote = 1;
1089 level = -1;
1090 while (cp1 != cp5){ /* Ordinary. */
1091 int ww;
1093 if(lastattr < 0){
1094 lastattr = cp1->a;
1095 change = 0;
1097 else
1098 change = lastattr != cp1->a;
1099 if(first != 0){
1100 first = 0;
1101 if(row == 0)
1102 pico_set_colorp(pcolors->tbcp, PSC_NONE);
1103 else if(row < term.t_nrow - 2)
1104 pcolor = (*flags & VFSIG) ? pcolors->sbcp : pcolors->ntcp;
1106 if(cp1->c != '>' && cp1->c != ' ')
1107 in_quote = 0;
1108 else if (in_quote && cp1->c == '>' && pcolors != NULL)
1109 level = (level + 1) % 3;
1110 if(level >= 0){
1111 if(level == 0) pcolor = pcolors->qlcp;
1112 else if(level == 1) pcolor = pcolors->qllcp;
1113 else if(level == 2) pcolor = pcolors->qlllcp;
1115 if(cp1->a == 1)
1116 pcolor = pcolors->rtcp; /* pcolor = proposed color */
1117 if(change == 0)
1118 pico_set_colorp(pcolor, PSC_NONE);
1119 else
1120 (*term.t_rev)(cp1->a); /* set inverse for this char */
1122 if(change == 0)
1123 (*term.t_rev)(cp1->a); /* set inverse for this char */
1124 (*term.t_putchar)(cp1->c);
1126 ww = wcellwidth((UCS) cp1->c);
1127 ttcol += (ww >= 0 ? ww : 1);
1129 *cp2++ = *cp1++;
1132 if (lastc){
1133 (void)pico_set_colorp(lastc, PSC_NONE);
1134 free_color_pair(&lastc);
1137 (*term.t_rev)(0); /* turn off inverse anyway! */
1139 if (cp5 != cp3 || cleartoeol) /* Erase. */
1140 peeol();
1142 *flags &= ~(VFCHG|VFSIG); /* flag this line is changed */
1148 * Update a single line. This does not know how to use insert or delete
1149 * character sequences; we are using VT52 functionality. Update the physical
1150 * row and column variables.
1152 void
1153 updateline(int row, /* row on screen */
1154 CELL vline[], /* what we want it to end up as */
1155 CELL pline[], /* what it looks like now */
1156 short *flags,
1157 int len)
1159 CELL *cp1, *cp2, *cp3, *cp4, *cp5, *cp6, *cp7;
1160 int display = TRUE;
1161 int nbflag; /* non-blanks to the right flag? */
1162 int cleartoeol = 0;
1164 if(row < 0 || row > term.t_nrow)
1165 return;
1167 if((Pmaster && Pmaster->colors) || Pcolors){
1168 updatelinecolor(row, vline, pline, flags, len);
1169 return;
1172 /* set up pointers to virtual and physical lines */
1173 cp1 = &vline[0];
1174 cp2 = &pline[0];
1175 cp3 = &vline[term.t_ncol];
1177 /* advance past any common chars at the left */
1178 while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
1179 ++cp1;
1180 ++cp2;
1183 /* This can still happen, even though we only call this routine on changed
1184 * lines. A hard update is always done when a line splits, a massive
1185 * change is done, or a buffer is displayed twice. This optimizes out most
1186 * of the excess updating. A lot of computes are used, but these tend to
1187 * be hard operations that do a lot of update, so I don't really care.
1189 /* if both lines are the same, no update needs to be done */
1190 if (cp1 == cp3){
1191 *flags &= ~VFCHG; /* mark it clean */
1192 return;
1195 /* find out if there is a match on the right */
1196 nbflag = FALSE;
1197 cp3 = &vline[term.t_ncol];
1198 cp4 = &pline[term.t_ncol];
1200 if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
1201 while (cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
1202 --cp3;
1203 --cp4;
1204 if (cp3[0].c != ' ' || cp3[0].a != 0) /* Note if any nonblank */
1205 nbflag = TRUE; /* in right match. */
1208 cp5 = cp3;
1210 if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
1211 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1212 --cp5;
1214 if (cp3-cp5 <= 3) /* Use only if erase is */
1215 cp5 = cp3; /* fewer characters. */
1218 /* go to start of differences */
1219 movecursor(row, cellwidth_ptr_to_ptr(&vline[0], cp1));
1221 if (!nbflag) { /* use insert or del char? */
1222 cp6 = cp3;
1223 cp7 = cp4;
1225 if(TERM_INSCHAR
1226 &&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
1227 while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
1228 --cp7;
1229 --cp6;
1232 if (cp7==cp2 && cp4-cp2 > 3){
1233 int ww;
1235 (*term.t_rev)(cp1->a); /* set inverse for this char */
1236 o_insert((UCS) cp1->c); /* insert the char */
1237 ww = wcellwidth((UCS) cp1->c);
1238 ttcol += (ww >= 0 ? ww : 1);
1239 display = FALSE; /* only do it once!! */
1242 else if(TERM_DELCHAR && cp3 != cp1 && cp7[0].c == cp6[-1].c
1243 && cp7[0].a == cp6[-1].a){
1244 while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
1245 --cp7;
1246 --cp6;
1249 if (cp6==cp1 && cp5-cp6 > 3){
1250 int w;
1252 w = wcellwidth((UCS) cp7[0].c);
1253 w = (w >= 0 ? w : 1);
1254 while(w-- > 0) /* in case double-width char */
1255 o_delete(); /* delete the char */
1256 display = FALSE; /* only do it once!! */
1261 if(cp1 != cp5 && display){
1262 int w1, w2;
1265 * If we need to copy characters from cp1 to cp2 and
1266 * we need to display them, then we have to worry about
1267 * the characters that we are replacing being of a different
1268 * width than the new characters, else the display may be
1269 * messed up.
1271 * If the new width (w1) is less than the old width, that means
1272 * we will leave behind some old remnants if we aren't careful.
1273 * If the new width is larger than the old width, we have to
1274 * make sure we draw the characters all the way to the end
1275 * in order to get it right. Take advantage of clear to end
1276 * of line if we have it.
1278 w1 = cellwidth_ptr_to_ptr(cp1, cp3);
1279 w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
1281 if(w1 < w2 || (nbflag && w1 != w2)){
1282 if(TERM_EOLEXIST){
1283 if(nbflag){
1285 * Draw all of the characters starting with cp1
1286 * until we get to all spaces, then clear to the end of
1287 * line from there. Watch out we don't run over the
1288 * right hand edge, which shouldn't happen.
1290 * Set cp5 to the first of the repeating spaces.
1292 cp5 = &vline[term.t_ncol];
1293 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1294 --cp5;
1298 * In the !nbflag case we want spaces from cp5 on.
1299 * Setting cp3 to something different from cp5 triggers
1300 * the clear to end of line below.
1302 if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
1303 cleartoeol++;
1305 else{
1306 int w;
1309 * No peeol so draw all the way to the edge whether they
1310 * are spaces or not.
1312 cp3 = &vline[0];
1313 for(w = 0; w < term.t_ncol; cp3++){
1314 int ww;
1316 ww = wcellwidth((UCS) cp3->c);
1317 w += (ww >= 0 ? ww : 1);
1320 cp5 = cp3;
1325 while (cp1 != cp5) { /* Ordinary. */
1326 int ww;
1328 if(display){
1329 (*term.t_rev)(cp1->a); /* set inverse for this char */
1330 (*term.t_putchar)(cp1->c);
1333 ww = wcellwidth((UCS) cp1->c);
1334 ttcol += (ww >= 0 ? ww : 1);
1336 *cp2++ = *cp1++;
1339 (*term.t_rev)(0); /* turn off inverse anyway! */
1341 if (cp5 != cp3 || cleartoeol) { /* Erase. */
1342 if(display)
1343 peeol();
1344 else
1345 while (cp1 != cp3)
1346 *cp2++ = *cp1++;
1349 *flags &= ~VFCHG; /* flag this line is changed */
1354 * Redisplay the mode line for the window pointed to by the "wp". This is the
1355 * only routine that has any idea of how the modeline is formatted. You can
1356 * change the modeline format by hacking at this routine. Called by "update"
1357 * any time there is a dirty window.
1359 void
1360 modeline(WINDOW *wp)
1362 if(Pmaster){
1363 if(ComposerEditing)
1364 ShowPrompt();
1365 else{
1366 menu_compose[EXIT_KEY].label = (Pmaster->headents)
1367 ? N_("Send") :N_("Exit");
1368 menu_compose[PSTPN_KEY].name = (Pmaster->headents)
1369 ? "^O" : NULL;
1370 menu_compose[PSTPN_KEY].label = (Pmaster->headents)
1371 ? N_("Postpone") : NULL;
1372 menu_compose[WHERE_KEY].name = (Pmaster->alt_ed) ? "^_" : "^W";
1373 menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? N_("Alt Edit")
1374 : N_("Where is");
1375 KS_OSDATASET(&menu_compose[WHERE_KEY],
1376 (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
1377 menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? N_("UnJustify")
1378 : N_("UnCut Text");
1379 wkeyhelp(menu_compose);
1382 else{
1383 BUFFER *bp;
1384 char t1[NLINE], t2[NLINE], t3[NLINE], tline[NLINE];
1385 int w1, w2, w3, w1_to_2, w2_to_3, w3_to_r;
1386 UCS *ucs;
1388 vtmove(1, 0);
1389 vteeol();
1390 vscreen[0]->v_flag |= VFCHG; /* Redraw next time. */
1391 vtmove(0, 0); /* Seek to right line. */
1393 snprintf(t1, sizeof(t1), PICO_TITLE, version); /* write version */
1395 bp = wp->w_bufp;
1396 if(bp->b_fname[0]) /* File name? */
1397 snprintf(t2, sizeof(t2), "File: %s", bp->b_fname);
1398 else{
1399 strncpy(t2, PICO_NEWBUF_MSG, sizeof(t2));
1400 t2[sizeof(t2)-1] = '\0';
1403 if(bp->b_flag&BFCHG){ /* "MOD" if changed. */
1404 strncpy(t3, PICO_MOD_MSG, sizeof(t3));
1405 t3[sizeof(t3)-1] = '\0';
1407 else
1408 t3[0] = '\0';
1410 #define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
1411 #define ALLBUTSPACE (w1+w2+w3+w3_to_r)
1413 w1 = utf8_width(t1);
1414 w2 = utf8_width(t2);
1415 w3 = utf8_width(t3);
1416 w1_to_2 = w2_to_3 = 1; /* min values for separation */
1417 w3_to_r = 2;
1419 if(ALLOFTHEM <= term.t_ncol){ /* everything fits */
1420 w1_to_2 = (term.t_ncol - ALLBUTSPACE)/2;
1421 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1423 else{
1424 w1 = 2;
1425 w1_to_2 = 0;
1426 if(ALLOFTHEM <= term.t_ncol)
1427 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1428 else{
1429 w1 = w1_to_2 = w3_to_r = 0;
1430 if(ALLOFTHEM <= term.t_ncol)
1431 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1432 else{
1433 if(bp->b_fname[0]){
1434 snprintf(t2, sizeof(t2), "%s", bp->b_fname);
1435 w2 = utf8_width(t2);
1438 if(ALLOFTHEM <= term.t_ncol)
1439 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1440 else{
1441 w2 = 8;
1442 if(bp->b_fname[0] && ALLOFTHEM <= term.t_ncol){
1443 /* reduce size of file */
1444 w2 = term.t_ncol - (ALLOFTHEM - w2);
1445 t2[0] = t2[1] = t2[2] = '.';
1446 utf8_to_width_rhs(t2+3, bp->b_fname, sizeof(t2)-3, w2-3);
1447 w2 = utf8_width(t2);
1449 else
1450 w2 = utf8_width(t2);
1452 if(ALLOFTHEM <= term.t_ncol)
1453 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1454 else{
1455 w1 = w1_to_2 = w2 = w2_to_3 = w3_to_r = 0;
1456 if(ALLOFTHEM <= term.t_ncol)
1457 w2_to_3 = term.t_ncol - ALLBUTSPACE;
1458 else
1459 w3 = 0;
1466 utf8_snprintf(tline, sizeof(tline),
1467 "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
1468 w1, w1, t1,
1469 w1_to_2, w1_to_2, "",
1470 w2, w2, t2,
1471 w2_to_3, w2_to_3, "",
1472 w3, w3, t3,
1473 w3_to_r, w3_to_r, "");
1475 ucs = NULL;
1476 if(utf8_width(tline) <= term.t_ncol)
1477 ucs = utf8_to_ucs4_cpystr(tline);
1479 if(ucs){
1480 UCS *ucsp;
1481 CELL c;
1483 c.a = 1;
1484 ucsp = ucs;
1485 while((c.c = CELLMASK & *ucsp++))
1486 vtputc(c);
1488 fs_give((void **) &ucs);
1496 * Send a command to the terminal to move the hardware cursor to row "row"
1497 * and column "col". The row and column arguments are origin 0. Optimize out
1498 * random calls. Update "ttrow" and "ttcol".
1500 void
1501 movecursor(int row, int col)
1503 if (row!=ttrow || col!=ttcol) {
1504 ttrow = row;
1505 ttcol = col;
1506 (*term.t_move)(MIN(MAX(row,0),term.t_nrow), MIN(MAX(col,0),term.t_ncol-1));
1512 * Erase any sense we have of the cursor's HW location...
1514 void
1515 clearcursor(void)
1517 ttrow = ttcol = FARAWAY;
1520 void
1521 get_cursor(int *row, int *col)
1523 if(row)
1524 *row = ttrow;
1525 if(col)
1526 *col = ttcol;
1531 * Erase the message line. This is a special routine because the message line
1532 * is not considered to be part of the virtual screen. It always works
1533 * immediately; the terminal buffer is flushed via a call to the flusher.
1535 void
1536 mlerase(void)
1538 if (term.t_nrow < term.t_mrow)
1539 return;
1541 movecursor(term.t_nrow - term.t_mrow, 0);
1542 (*term.t_rev)(0);
1543 if (TERM_EOLEXIST == TRUE)
1544 peeol();
1545 else{
1546 if(ttrow == term.t_nrow){
1547 while(ttcol++ < term.t_ncol-1)
1548 (*term.t_putchar)(' ');
1550 else{
1551 while(ttcol++ < term.t_ncol) /* track's ttcol */
1552 (*term.t_putchar)(' ');
1556 (*term.t_flush)();
1557 mpresf = FALSE;
1560 /* returns the chosen dictionary. If one was already chosen
1561 * return that one
1563 char *
1564 speller_choice(char **sp_list, int *choice)
1566 int ch_dict = -1;
1567 int cnt;
1569 if(sp_list == NULL || sp_list[0] == NULL || sp_list[0][0] == '\0')
1570 return NULL;
1572 if(choice && *choice >= 0)
1573 return sp_list[*choice];
1575 for(cnt = 0; sp_list[cnt] != NULL && sp_list[cnt][0] != '\0'; cnt++)
1578 if(cnt > 10) /* only the first 10 dictionaries */
1579 cnt = 10;
1581 if(cnt == 1) /* only one dictionary? choose it! */
1582 ch_dict = 0;
1584 if(ch_dict > cnt - 1) /* choose again in case something changed */
1585 ch_dict = -1;
1587 if(ch_dict < 0){ /* not a choice yet? do one now! */
1588 UCS buf[128];
1589 int i;
1590 char *utf8_prompt;
1591 UCS *ucs4_prompt;
1592 EXTRAKEYS menu_dictionary[] = {
1593 {"0", NULL, '0'},
1594 {"1", NULL, '1'},
1595 {"2", NULL, '2'},
1596 {"3", NULL, '3'},
1597 {"4", NULL, '4'},
1598 {"5", NULL, '5'},
1599 {"6", NULL, '6'},
1600 {"7", NULL, '7'},
1601 {"8", NULL, '8'},
1602 {"9", NULL, '9'}
1605 for(i = 0; i < cnt; i++)
1606 menu_dictionary[i].label = sp_list[i];
1608 if(cnt < 10)
1609 menu_dictionary[cnt].name = NULL;
1611 buf[0] = '\0';
1612 /* write the prompt in utf8, and let internal functions translate it to ucs4 */
1613 ucs4_prompt = utf8_to_ucs4_cpystr(_("Choose Dictionary: "));
1615 i = mlchoose(ucs4_prompt, menu_dictionary);
1617 if(i >= '0' && i <= '9')
1618 ch_dict = i - '0';
1620 if (i == -2) /* user cancelled */
1621 ch_dict = -2;
1623 if(ucs4_prompt)
1624 fs_give((void **)&ucs4_prompt);
1626 else ch_dict = -1;
1628 if(choice)
1629 *choice = ch_dict;
1631 return ch_dict >= 0 ? sp_list[ch_dict] : NULL;
1634 /* just like mlreplyd, but user cannot fill a prompt */
1636 mlchoose(UCS *prompt, EXTRAKEYS *extras)
1638 UCS c;
1639 UCS buf[NLINE];
1640 int i;
1641 int changed = FALSE;
1642 int return_val = 0;
1643 KEYMENU menu_choose[12];
1644 COLOR_PAIR *lastc = NULL;
1646 for(i = 0; i < 12; i++){
1647 menu_choose[i].name = NULL;
1648 KS_OSDATASET(&menu_choose[i], KS_NONE);
1651 menu_choose[0].name = "^G";
1652 menu_choose[0].label = N_("Get Help");
1653 KS_OSDATASET(&menu_choose[0], KS_SCREENHELP);
1655 menu_choose[6].name = "^C";
1656 menu_choose[6].label = N_("Cancel");
1657 KS_OSDATASET(&menu_choose[6], KS_NONE);
1659 for(i = 0; i < 10; i++){
1660 if((i % 2) == 0){
1661 menu_choose[i / 2 + 1].name = extras[i].name;
1662 menu_choose[i / 2 + 1].label = extras[i].label;
1664 else{
1665 menu_choose[(i + 13) / 2].name = extras[i].name;
1666 menu_choose[(i + 13) / 2].label = extras[i].label;
1669 wkeyhelp(menu_choose); /* paint generic menu */
1670 sgarbk = TRUE; /* mark menu dirty */
1671 if(Pmaster && curwp)
1672 curwp->w_flag |= WFMODE;
1674 ucs4_strncpy(buf, prompt, NLINE);
1675 buf[NLINE-1] = '\0';
1676 mlwrite(buf, NULL);
1677 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1678 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1679 lastc = pico_get_cur_color();
1680 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1682 else
1683 (*term.t_rev)(1);
1685 return_val = -1;
1686 while(1){
1687 c = GetKey();
1688 for(i = 0; i < 10 && extras[i].name != NULL && extras[i].key != c; i++)
1690 if(i < 10 && extras[i].name)
1691 return_val = c;
1692 else switch(c){
1693 case (CTRL|'C') : /* Bail out! */
1694 case F2 :
1695 pputs_utf8(_("Cancel"), 1);
1696 return_val = -2;
1697 break;
1699 case (CTRL|'G') :
1700 if(term.t_mrow == 0 && km_popped == 0){
1701 movecursor(term.t_nrow-2, 0);
1702 peeol();
1703 term.t_mrow = 2;
1704 if(lastc){
1705 (void) pico_set_colorp(lastc, PSC_NONE);
1706 free_color_pair(&lastc);
1708 else
1709 (*term.t_rev)(0);
1711 wkeyhelp(menu_choose); /* paint generic menu */
1712 mlwrite(buf, NULL);
1713 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1714 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1715 lastc = pico_get_cur_color();
1716 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1718 else
1719 (*term.t_rev)(1);
1721 sgarbk = TRUE; /* mark menu dirty */
1722 km_popped++;
1723 break;
1725 /* else fall through */
1727 default:
1728 (*term.t_beep)();
1729 case NODATA :
1730 break;
1733 (*term.t_flush)();
1734 if (return_val != -1){ /* abort sets rv = -2, other return values are positive */
1735 if(lastc){
1736 (void) pico_set_colorp(lastc, PSC_NONE);
1737 free_color_pair(&lastc);
1739 else
1740 (*term.t_rev)(0);
1742 if(km_popped){
1743 term.t_mrow = 0;
1744 movecursor(term.t_nrow, 0);
1745 peeol();
1746 sgarbf = 1;
1747 km_popped = 0;
1750 return(return_val);
1757 mlyesno_utf8(char *utf8prompt, int dflt)
1759 int ret;
1760 UCS *prompt;
1762 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1764 ret = mlyesno(prompt, dflt);
1766 if(prompt)
1767 fs_give((void **) &prompt);
1769 return(ret);
1774 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
1775 * ABORT. The ABORT status is returned if the user bumps out of the question
1776 * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
1777 * is no default.
1780 mlyesno(UCS *prompt, int dflt)
1782 int rv;
1783 UCS buf[NLINE], lbuf[10];
1784 KEYMENU menu_yesno[12];
1785 COLOR_PAIR *lastc = NULL;
1786 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
1788 #ifdef _WINDOWS
1789 if (mswin_usedialog ())
1790 switch (mswin_yesno (prompt)) {
1791 default:
1792 case 0: return (ABORT);
1793 case 1: return (TRUE);
1794 case 2: return (FALSE);
1796 #endif
1798 for(rv = 0; rv < 12; rv++){
1799 menu_yesno[rv].name = NULL;
1800 KS_OSDATASET(&menu_yesno[rv], KS_NONE);
1803 menu_yesno[1].name = "Y";
1804 menu_yesno[1].label = (dflt == TRUE) ? "[" N_("Yes") "]" : N_("Yes");
1805 menu_yesno[6].name = "^C";
1806 menu_yesno[6].label = N_("Cancel");
1807 menu_yesno[7].name = "N";
1808 menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No");
1809 wkeyhelp(menu_yesno); /* paint generic menu */
1810 sgarbk = TRUE; /* mark menu dirty */
1811 if(Pmaster && curwp)
1812 curwp->w_flag |= WFMODE;
1814 ucs4_strncpy(buf, prompt, NLINE);
1815 buf[NLINE-1] = '\0';
1816 lbuf[0] = ' '; lbuf[1] = '?'; lbuf[2] = ' '; lbuf[3] = '\0';
1817 ucs4_strncat(buf, lbuf, NLINE - ucs4_strlen(buf) - 1);
1818 buf[NLINE-1] = '\0';
1819 mlwrite(buf, NULL);
1820 if(pcolors && pcolors->prcp
1821 && pico_is_good_colorpair(pcolors->prcp)){
1822 lastc = pico_get_cur_color();
1823 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
1824 } else
1825 (*term.t_rev)(1);
1827 rv = -1;
1828 while(1){
1829 switch(GetKey()){
1830 case (CTRL|'M') : /* default */
1831 if(dflt >= 0){
1832 pputs_utf8((dflt) ? _("Yes") : _("No"), 1);
1833 rv = dflt;
1835 else
1836 (*term.t_beep)();
1838 break;
1840 case (CTRL|'C') : /* Bail out! */
1841 case F2 :
1842 pputs_utf8(_("ABORT"), 1);
1843 rv = ABORT;
1844 break;
1846 case 'y' :
1847 case 'Y' :
1848 case F3 :
1849 pputs_utf8(_("Yes"), 1);
1850 rv = TRUE;
1851 break;
1853 case 'n' :
1854 case 'N' :
1855 case F4 :
1856 pputs_utf8(_("No"), 1);
1857 rv = FALSE;
1858 break;
1860 case (CTRL|'G') :
1861 if(term.t_mrow == 0 && km_popped == 0){
1862 movecursor(term.t_nrow-2, 0);
1863 peeol();
1864 term.t_mrow = 2;
1865 if(lastc){
1866 (void) pico_set_colorp(lastc, PSC_NONE);
1867 free_color_pair(&lastc);
1869 else
1870 (*term.t_rev)(0);
1872 wkeyhelp(menu_yesno); /* paint generic menu */
1873 mlwrite(buf, NULL);
1874 if(pcolors && pcolors->prcp
1875 && pico_is_good_colorpair(pcolors->prcp)){
1876 lastc = pico_get_cur_color();
1877 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
1879 else
1880 (*term.t_rev)(1);
1882 sgarbk = TRUE; /* mark menu dirty */
1883 km_popped++;
1884 break;
1886 /* else fall through */
1888 default:
1889 (*term.t_beep)();
1890 case NODATA :
1891 break;
1894 (*term.t_flush)();
1895 if(rv != -1){
1896 if(lastc){
1897 (void) pico_set_colorp(lastc, PSC_NONE);
1898 free_color_pair(&lastc);
1900 else
1901 (*term.t_rev)(0);
1903 if(km_popped){
1904 term.t_mrow = 0;
1905 movecursor(term.t_nrow, 0);
1906 peeol();
1907 sgarbf = 1;
1908 km_popped = 0;
1911 return(rv);
1918 * Write a prompt into the message line, then read back a response. Keep
1919 * track of the physical position of the cursor. If we are in a keyboard
1920 * macro throw the prompt away, and return the remembered response. This
1921 * lets macros run at full speed. The reply is always terminated by a carriage
1922 * return. Handle erase, kill, and abort keys.
1925 mlreply_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1927 return(mlreplyd_utf8(utf8prompt, utf8buf, nbuf, flg|QDEFLT, extras));
1932 mlreply(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
1934 return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
1939 * function key mappings
1941 static UCS rfkm[12][2] = {
1942 { F1, (CTRL|'G')},
1943 { F2, (CTRL|'C')},
1944 { F3, 0 },
1945 { F4, 0 },
1946 { F5, 0 },
1947 { F6, 0 },
1948 { F7, 0 },
1949 { F8, 0 },
1950 { F9, 0 },
1951 { F10, 0 },
1952 { F11, 0 },
1953 { F12, 0 }
1958 mlreplyd_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1960 int ret;
1961 UCS *b, *buf;
1962 char *utf8;
1963 UCS *prompt;
1965 buf = (UCS *) fs_get(nbuf * sizeof(*b));
1966 b = utf8_to_ucs4_cpystr(utf8buf);
1967 if(b){
1968 ucs4_strncpy(buf, b, nbuf);
1969 buf[nbuf-1] = '\0';
1970 fs_give((void **) &b);
1973 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1975 ret = mlreplyd(prompt, buf, nbuf, flg, extras);
1977 utf8 = ucs4_to_utf8_cpystr(buf);
1978 if(utf8){
1979 strncpy(utf8buf, utf8, nbuf);
1980 utf8buf[nbuf-1] = '\0';
1981 fs_give((void **) &utf8);
1984 if(buf)
1985 fs_give((void **) &buf);
1987 if(prompt)
1988 fs_give((void **) &prompt);
1990 return(ret);
1994 void
1995 writeachar(UCS ucs)
1997 pputc(ucs, 0);
2002 * mlreplyd - write the prompt to the message line along with a default
2003 * answer already typed in. Carriage return accepts the
2004 * default. answer returned in buf which also holds the initial
2005 * default, nbuf is its length, def set means use default value.
2006 * In order to be able to eliminate keys from a menu, EXTRAKEYS
2007 * always has size 10.
2010 mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
2012 UCS c; /* current char */
2013 UCS *b; /* pointer in buf */
2014 int i, j;
2015 int plen;
2016 int changed = FALSE;
2017 int return_val = 0;
2018 KEYMENU menu_mlreply[12];
2019 UCS extra_v[12];
2020 struct display_line dline;
2021 COLOR_PAIR *lastc = NULL;
2022 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2024 #ifdef _WINDOWS
2025 if(mswin_usedialog()){
2026 MDlgButton btn_list[12];
2027 LPTSTR free_names[12];
2028 LPTSTR free_labels[12];
2029 int i, j;
2031 memset(&free_names, 0, sizeof(LPTSTR) * 12);
2032 memset(&free_labels, 0, sizeof(LPTSTR) * 12);
2033 memset(&btn_list, 0, sizeof (MDlgButton) * 12);
2034 j = 0;
2035 for(i = 0; extras && extras[i].name != NULL; ++i) {
2036 if(extras[i].label[0] != '\0') {
2037 if((extras[i].key & CTRL) == CTRL)
2038 btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
2039 else
2040 btn_list[j].ch = extras[i].key;
2042 btn_list[j].rval = extras[i].key;
2043 free_names[j] = utf8_to_lptstr(extras[i].name);
2044 btn_list[j].name = free_names[j];
2045 free_labels[j] = utf8_to_lptstr(extras[i].label);
2046 btn_list[j].label = free_labels[j];
2047 j++;
2051 btn_list[j].ch = -1;
2053 return_val = mswin_dialog(prompt, buf, nbuf, ((flg&QDEFLT) > 0),
2054 FALSE, btn_list, NULL, 0);
2056 if(return_val == 3)
2057 return_val = HELPCH;
2059 for(i = 0; i < 12; i++){
2060 if(free_names[i])
2061 fs_give((void **) &free_names[i]);
2062 if(free_labels[i])
2063 fs_give((void **) &free_labels[i]);
2066 return(return_val);
2068 #endif
2070 memset(&menu_mlreply, 0, 12*sizeof(KEYMENU));
2071 menu_mlreply[0].name = "^G";
2072 menu_mlreply[0].label = N_("Get Help");
2073 KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
2074 for(j = 0, i = 1; i < 6; i++){ /* insert odd extras */
2075 menu_mlreply[i].name = NULL;
2076 KS_OSDATASET(&menu_mlreply[i], KS_NONE);
2077 rfkm[2*i][1] = 0;
2078 if(extras){
2079 j = 2*(i-1);
2080 if(extras[j].name){
2081 rfkm[2*i][1] = extras[j].key;
2082 menu_mlreply[i].name = extras[j].name;
2083 menu_mlreply[i].label = extras[j].label;
2084 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
2089 menu_mlreply[6].name = "^C";
2090 menu_mlreply[6].label = N_("Cancel");
2091 KS_OSDATASET(&menu_mlreply[6], KS_NONE);
2092 for(j = 0, i = 7; i < 12; i++){ /* insert even extras */
2093 menu_mlreply[i].name = NULL;
2094 rfkm[2*(i-6)+1][1] = 0;
2095 if(extras){
2096 j = 2*(i-6) - 1;
2097 if(extras[j].name){
2098 rfkm[2*(i-6)+1][1] = extras[j].key;
2099 menu_mlreply[i].name = extras[j].name;
2100 menu_mlreply[i].label = extras[j].label;
2101 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
2106 /* set up what to watch for and return values */
2107 memset(extra_v, 0, sizeof(extra_v));
2108 for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
2109 extra_v[j++] = extras[i].key;
2111 plen = mlwrite(prompt, NULL); /* paint prompt */
2113 if(!(flg&QDEFLT))
2114 *buf = '\0';
2116 dline.vused = ucs4_strlen(buf);
2117 dline.dwid = term.t_ncol - plen;
2118 dline.row = term.t_nrow - term.t_mrow;
2119 dline.col = plen;
2121 dline.dlen = 2 * dline.dwid + 100;
2123 dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
2124 dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
2125 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
2126 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
2128 dline.movecursor = movecursor;
2129 dline.writechar = writeachar;
2131 dline.vl = buf;
2132 dline.vlen = nbuf-1;
2133 dline.vbase = 0;
2135 b = &buf[(flg & QBOBUF) ? 0 : ucs4_strlen(buf)];
2137 wkeyhelp(menu_mlreply); /* paint generic menu */
2139 sgarbk = 1; /* mark menu dirty */
2141 if(pcolors && pcolors->prcp
2142 && pico_is_good_colorpair(pcolors->prcp)){
2143 lastc = pico_get_cur_color();
2144 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2146 else
2147 (*term.t_rev)(1);
2149 for(;;){
2151 line_paint(b-buf, &dline, NULL);
2152 (*term.t_flush)();
2154 #ifdef MOUSE
2155 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
2156 register_mfunc(mouse_in_content,
2157 term.t_nrow - term.t_mrow, plen,
2158 term.t_nrow - term.t_mrow, term.t_ncol-1);
2159 #endif
2160 #ifdef _WINDOWS
2161 mswin_allowpaste(MSWIN_PASTE_LINE);
2162 #endif
2163 while((c = GetKey()) == NODATA)
2166 #ifdef MOUSE
2167 clear_mfunc(mouse_in_content);
2168 #endif
2169 #ifdef _WINDOWS
2170 mswin_allowpaste(MSWIN_PASTE_DISABLE);
2171 #endif
2173 switch(c = normalize_cmd(c, rfkm, 1)){
2174 case (CTRL|'A') : /* CTRL-A beginning */
2175 case KEY_HOME :
2176 b = buf;
2177 continue;
2179 case (CTRL|'B') : /* CTRL-B back a char */
2180 case KEY_LEFT:
2181 if(b <= buf)
2182 (*term.t_beep)();
2183 else
2184 b--;
2186 continue;
2188 case (CTRL|'C') : /* CTRL-C abort */
2189 pputs_utf8(_("ABORT"), 1);
2190 ctrlg(FALSE, 0);
2191 return_val = ABORT;
2192 goto ret;
2194 case (CTRL|'E') : /* CTRL-E end of line */
2195 case KEY_END :
2196 b = &buf[ucs4_strlen(buf)];
2197 continue;
2199 case (CTRL|'F') : /* CTRL-F forward a char*/
2200 case KEY_RIGHT :
2201 if(*b == '\0')
2202 (*term.t_beep)();
2203 else
2204 b++;
2206 continue;
2208 case (CTRL|'G') : /* CTRL-G help */
2209 if(term.t_mrow == 0 && km_popped == 0){
2210 movecursor(term.t_nrow-2, 0);
2211 peeol();
2212 sgarbk = 1; /* mark menu dirty */
2213 km_popped++;
2214 term.t_mrow = 2;
2215 if(lastc){
2216 (void) pico_set_colorp(lastc, PSC_NONE);
2217 free_color_pair(&lastc);
2219 else
2220 (*term.t_rev)(0);
2222 wkeyhelp(menu_mlreply); /* paint generic menu */
2223 plen = mlwrite(prompt, NULL); /* paint prompt */
2224 if(pcolors && pcolors->prcp
2225 && pico_is_good_colorpair(pcolors->prcp)){
2226 lastc = pico_get_cur_color();
2227 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2229 else
2230 (*term.t_rev)(1);
2232 pputs(buf, 1);
2233 break;
2236 pputs_utf8(_("HELP"), 1);
2237 return_val = HELPCH;
2238 goto ret;
2240 case (CTRL|'H') : /* CTRL-H backspace */
2241 case 0x7f : /* rubout */
2242 if (b <= buf){
2243 (*term.t_beep)();
2244 break;
2246 else
2247 b--;
2249 case (CTRL|'D') : /* CTRL-D delete char */
2250 case KEY_DEL :
2251 if (!*b){
2252 (*term.t_beep)();
2253 break;
2256 changed=TRUE;
2257 i = 0;
2258 dline.vused--;
2259 do /* blat out left char */
2260 b[i] = b[i+1];
2261 while(b[i++] != '\0');
2262 break;
2264 case (CTRL|'L') : /* CTRL-L redraw */
2265 return_val = (CTRL|'L');
2266 goto ret;
2268 case (CTRL|'K') : /* CTRL-K kill line */
2269 changed=TRUE;
2270 buf[0] = '\0';
2271 dline.vused = 0;
2272 b = buf;
2273 break;
2275 case F1 : /* sort of same thing */
2276 return_val = HELPCH;
2277 goto ret;
2279 case (CTRL|'M') : /* newline */
2280 return_val = changed;
2281 goto ret;
2283 #ifdef MOUSE
2284 case KEY_MOUSE :
2286 MOUSEPRESS mp;
2288 mouse_get_last (NULL, &mp);
2290 /* The clicked line have anything special on it? */
2291 switch(mp.button){
2292 case M_BUTTON_LEFT : /* position cursor */
2293 mp.col -= plen; /* normalize column */
2294 if(mp.col >= 0 && mp.col <= ucs4_strlen(buf))
2295 b = buf + mp.col;
2297 break;
2299 case M_BUTTON_RIGHT :
2300 #ifdef _WINDOWS
2301 mswin_allowpaste(MSWIN_PASTE_LINE);
2302 mswin_paste_popup();
2303 mswin_allowpaste(MSWIN_PASTE_DISABLE);
2304 break;
2305 #endif
2307 case M_BUTTON_MIDDLE : /* NO-OP for now */
2308 default: /* just ignore */
2309 break;
2313 continue;
2314 #endif
2316 default :
2318 /* look for match in extra_v */
2319 for(i = 0; i < 12; i++)
2320 if(c && c == extra_v[i]){
2321 return_val = c;
2322 goto ret;
2325 changed=TRUE;
2327 if(c & (CTRL | FUNC)){ /* bag ctrl_special chars */
2328 (*term.t_beep)();
2330 else{
2331 i = ucs4_strlen(b);
2332 if(flg&QNODQT){ /* reject double quotes? */
2333 if(c == '"'){
2334 (*term.t_beep)();
2335 continue;
2339 if(dline.vused >= nbuf-1){
2340 (*term.t_beep)();
2341 continue;
2344 do /* blat out left char */
2345 b[i+1] = b[i];
2346 while(i-- > 0);
2348 dline.vused++;
2349 *b++ = c;
2354 ret:
2355 if(lastc){
2356 (void) pico_set_colorp(lastc, PSC_NONE);
2357 free_color_pair(&lastc);
2359 else
2360 (*term.t_rev)(0);
2362 (*term.t_flush)();
2364 if(km_popped){
2365 term.t_mrow = 0;
2366 movecursor(term.t_nrow, 0);
2367 peeol();
2368 sgarbf = 1;
2369 km_popped = 0;
2372 if(dline.dl)
2373 fs_give((void **) &dline.dl);
2375 if(dline.olddl)
2376 fs_give((void **) &dline.olddl);
2378 return(return_val);
2382 void
2383 emlwwrite(char *utf8message, EML *eml)
2385 (*term.t_beep)();
2386 emlwrite(utf8message, eml);
2389 void
2390 emlwrite(char *utf8message, EML *eml)
2392 UCS *message;
2394 message = utf8_to_ucs4_cpystr(utf8message ? utf8message : "");
2396 emlwrite_ucs4(message, eml);
2398 if(message)
2399 fs_give((void **) &message);
2404 * emlwrite() - write the message string to the error half of the screen
2405 * center justified. much like mlwrite (which is still used
2406 * to paint the line for prompts and such), except it center
2407 * the text.
2409 void
2410 emlwrite_ucs4(UCS *message, EML *eml)
2412 UCS *bufp, *ap;
2413 int width;
2414 COLOR_PAIR *lastc = NULL;
2415 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2417 mlerase();
2419 if(!(message && *message) || term.t_nrow < 2)
2420 return; /* nothing to write or no space to write, bag it */
2422 bufp = message;
2424 width = ucs4_str_width(message);
2427 * next, figure out where the to move the cursor so the message
2428 * comes out centered
2430 if((ap=ucs4_strchr(message, '%')) != NULL){
2431 width -= 2;
2432 switch(ap[1]){
2433 case '%':
2434 case 'c':
2435 width += (eml && eml->c) ? wcellwidth(eml->c) : 1;
2436 break;
2437 case 'd':
2438 width += dumbroot(eml ? eml->d : 0, 10);
2439 break;
2440 case 'D':
2441 width += dumblroot(eml ? eml->l : 0L, 10);
2442 break;
2443 case 'o':
2444 width += dumbroot(eml ? eml->d : 0, 8);
2445 break;
2446 case 'x':
2447 width += dumbroot(eml ? eml->d : 0, 16);
2448 break;
2449 case 's': /* string arg is UTF-8 */
2450 width += (eml && eml->s) ? utf8_width(eml->s) : 2;
2451 break;
2455 if(width+4 <= term.t_ncol)
2456 movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (width + 4))/2);
2457 else
2458 movecursor(term.t_nrow-term.t_mrow, 0);
2460 if(pcolors && pcolors->stcp
2461 && pico_is_good_colorpair(pcolors->stcp)){
2462 lastc = pico_get_cur_color();
2463 (void) pico_set_colorp(pcolors->stcp, PSC_NONE);
2465 else
2466 (*term.t_rev)(1);
2468 pputs_utf8("[ ", 1);
2469 while (*bufp != '\0' && ttcol < term.t_ncol-2){
2470 if(*bufp == '\007')
2471 (*term.t_beep)();
2472 else if(*bufp == '%'){
2473 switch(*++bufp){
2474 case 'c':
2475 if(eml && eml->c)
2476 pputc(eml->c, 0);
2477 else {
2478 pputs_utf8("%c", 0);
2480 break;
2481 case 'd':
2482 mlputi(eml ? eml->d : 0, 10);
2483 break;
2484 case 'D':
2485 mlputli(eml ? eml->l : 0L, 10);
2486 break;
2487 case 'o':
2488 mlputi(eml ? eml->d : 0, 16);
2489 break;
2490 case 'x':
2491 mlputi(eml ? eml->d : 0, 8);
2492 break;
2493 case 's':
2494 pputs_utf8((eml && eml->s) ? eml->s : "%s", 0);
2495 break;
2496 case '%':
2497 default:
2498 pputc(*bufp, 0);
2499 break;
2502 else
2503 pputc(*bufp, 0);
2504 bufp++;
2507 pputs_utf8(" ]", 1);
2509 if(lastc){
2510 (void) pico_set_colorp(lastc, PSC_NONE);
2511 free_color_pair(&lastc);
2513 else
2514 (*term.t_rev)(0);
2516 (*term.t_flush)();
2518 mpresf = TRUE;
2523 mlwrite_utf8(char *utf8fmt, void *arg)
2525 UCS *fmt;
2526 int ret;
2528 fmt = utf8_to_ucs4_cpystr(utf8fmt ? utf8fmt : "");
2529 ret = mlwrite(fmt, arg);
2530 if(fmt)
2531 fs_give((void **) &fmt);
2533 return(ret);
2538 * Write a message into the message line. Keep track of the physical cursor
2539 * position. A small class of printf like format items is handled. Assumes the
2540 * stack grows down; this assumption is made by the "++" in the argument scan
2541 * loop. Set the "message line" flag TRUE.
2544 mlwrite(UCS *fmt, void *arg)
2546 int ret, ww;
2547 UCS c;
2548 char *ap;
2549 COLOR_PAIR *lastc = NULL;
2550 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2553 * the idea is to only highlight if there is something to show
2555 mlerase();
2556 movecursor(ttrow, 0);
2558 if(pcolors && pcolors->prcp
2559 && pico_is_good_colorpair(pcolors->prcp)){
2560 lastc = pico_get_cur_color();
2561 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2563 else
2564 (*term.t_rev)(1);
2566 ap = (char *) &arg;
2568 while ((c = *fmt++) != 0) {
2569 if (c != '%') {
2570 pputc(c, 1);
2572 else {
2573 c = *fmt++;
2574 switch (c){
2575 case 'd':
2576 mlputi(*(int *)ap, 10);
2577 ap += sizeof(int);
2578 break;
2580 case 'o':
2581 mlputi(*(int *)ap, 8);
2582 ap += sizeof(int);
2583 break;
2585 case 'x':
2586 mlputi(*(int *)ap, 16);
2587 ap += sizeof(int);
2588 break;
2590 case 'D':
2591 mlputli(*(long *)ap, 10);
2592 ap += sizeof(long);
2593 break;
2595 case 's':
2596 pputs_utf8(*(char **)ap, 1);
2597 ap += sizeof(char *);
2598 break;
2600 default:
2601 pputc(c, 1);
2602 ww = wcellwidth(c);
2603 ttcol += (ww >= 0 ? ww : 1);
2608 ret = ttcol;
2609 while(ttcol < term.t_ncol)
2610 pputc(' ', 0);
2612 movecursor(term.t_nrow - term.t_mrow, ret);
2614 if(lastc){
2615 (void) pico_set_colorp(lastc, PSC_NONE);
2616 free_color_pair(&lastc);
2618 else
2619 (*term.t_rev)(0);
2621 (*term.t_flush)();
2622 mpresf = TRUE;
2624 return(ret);
2629 * Write out an integer, in the specified radix. Update the physical cursor
2630 * position. This will not handle any negative numbers; maybe it should.
2632 void
2633 mlputi(int i, int r)
2635 register int q;
2636 static char hexdigits[] = "0123456789ABCDEF";
2638 if (i < 0){
2639 i = -i;
2640 pputc('-', 1);
2643 q = i/r;
2645 if (q != 0)
2646 mlputi(q, r);
2648 pputc(hexdigits[i%r], 1);
2653 * do the same except as a long integer.
2655 void
2656 mlputli(long l, int r)
2658 register long q;
2660 if (l < 0){
2661 l = -l;
2662 pputc('-', 1);
2665 q = l/r;
2667 if (q != 0)
2668 mlputli(q, r);
2670 pputc((int)(l%r)+'0', 1);
2674 void
2675 unknown_command(UCS c)
2677 char buf[10], ch, *s;
2678 EML eml;
2680 buf[0] = '\0';
2681 s = buf;
2683 if(!c){
2684 /* fall through */
2686 else if(c & CTRL && c >= (CTRL|'@') && c <= (CTRL|'_')){
2687 ch = c - (CTRL|'@') + '@';
2688 snprintf(s, sizeof(buf), "^%c", ch);
2690 else
2691 switch(c){
2692 case ' ' : s = "SPACE"; break;
2693 case '\033' : s = "ESC"; break;
2694 case '\177' : s = "DEL"; break;
2695 case ctrl('I') : s = "TAB"; break;
2696 case ctrl('J') : s = "LINEFEED"; break;
2697 case ctrl('M') : s = "RETURN"; break;
2698 case ctrl('Q') : s = "XON"; break;
2699 case ctrl('S') : s = "XOFF"; break;
2700 case KEY_UP : s = "Up Arrow"; break;
2701 case KEY_DOWN : s = "Down Arrow"; break;
2702 case KEY_RIGHT : s = "Right Arrow"; break;
2703 case KEY_LEFT : s = "Left Arrow"; break;
2704 case CTRL|KEY_UP : s = "Ctrl-Up Arrow"; break;
2705 case CTRL|KEY_DOWN : s = "Ctrl-Down Arrow"; break;
2706 case CTRL|KEY_RIGHT : s = "Ctrl-Right Arrow"; break;
2707 case CTRL|KEY_LEFT : s = "Ctrl-Left Arrow"; break;
2708 case KEY_PGUP : s = "Prev Page"; break;
2709 case KEY_PGDN : s = "Next Page"; break;
2710 case KEY_HOME : s = "Home"; break;
2711 case KEY_END : s = "End"; break;
2712 case KEY_DEL : s = "Delete"; break; /* Not necessary DEL! */
2713 case F1 :
2714 case F2 :
2715 case F3 :
2716 case F4 :
2717 case F5 :
2718 case F6 :
2719 case F7 :
2720 case F8 :
2721 case F9 :
2722 case F10 :
2723 case F11 :
2724 case F12 :
2725 snprintf(s, sizeof(buf), "F%ld", (long) (c - PF1 + 1));
2726 break;
2728 default:
2729 if(c < CTRL)
2730 utf8_put((unsigned char *) s, (unsigned long) c);
2732 break;
2735 eml.s = s;
2736 emlwrite("Unknown Command: %s", &eml);
2737 (*term.t_beep)();
2742 * scrolldown - use stuff to efficiently move blocks of text on the
2743 * display, and update the pscreen array to reflect those
2744 * moves...
2746 * wp is the window to move in
2747 * r is the row at which to begin scrolling
2748 * n is the number of lines to scrol
2750 void
2751 scrolldown(WINDOW *wp, int r, int n)
2753 #ifdef TERMCAP
2754 register int i;
2755 register int l;
2756 register VIDEO *vp1;
2757 register VIDEO *vp2;
2759 if(!n)
2760 return;
2762 if(r < 0){
2763 r = wp->w_toprow;
2764 l = wp->w_ntrows;
2766 else{
2767 if(r > wp->w_toprow)
2768 vscreen[r-1]->v_flag |= VFCHG;
2769 l = wp->w_toprow+wp->w_ntrows-r;
2772 o_scrolldown(r, n);
2774 for(i=l-n-1; i >= 0; i--){
2775 vp1 = pscreen[r+i];
2776 vp2 = pscreen[r+i+n];
2777 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2779 pprints(r+n-1, r);
2780 ttrow = FARAWAY;
2781 ttcol = FARAWAY;
2782 #endif /* TERMCAP */
2787 * scrollup - use tcap stuff to efficiently move blocks of text on the
2788 * display, and update the pscreen array to reflect those
2789 * moves...
2791 void
2792 scrollup(WINDOW *wp, int r, int n)
2794 #ifdef TERMCAP
2795 register int i;
2796 register VIDEO *vp1;
2797 register VIDEO *vp2;
2799 if(!n)
2800 return;
2802 if(r < 0)
2803 r = wp->w_toprow;
2805 o_scrollup(r, n);
2807 i = 0;
2808 while(1){
2809 if(Pmaster){
2810 if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
2811 break;
2813 else{
2814 if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
2815 break;
2817 vp1 = pscreen[r+i+n];
2818 vp2 = pscreen[r+i];
2819 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2820 i++;
2822 pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
2823 ttrow = FARAWAY;
2824 ttcol = FARAWAY;
2825 #endif /* TERMCAP */
2830 * print spaces in the physical screen starting from row abs(n) working in
2831 * either the positive or negative direction (depending on sign of n).
2833 void
2834 pprints(int x, int y)
2836 register int i;
2837 register int j;
2839 if(x < y){
2840 for(i = x;i <= y; ++i){
2841 for(j = 0; j < term.t_ncol; j++){
2842 pscreen[i]->v_text[j].c = ' ';
2843 pscreen[i]->v_text[j].a = 0;
2847 else{
2848 for(i = x;i >= y; --i){
2849 for(j = 0; j < term.t_ncol; j++){
2850 pscreen[i]->v_text[j].c = ' ';
2851 pscreen[i]->v_text[j].a = 0;
2855 ttrow = y;
2856 ttcol = 0;
2861 * doton - return the physical line number that the dot is on in the
2862 * current window, and by side effect the number of lines remaining
2865 doton(int *r, unsigned *chs)
2867 register int i = 0;
2868 register LINE *lp = curwp->w_linep;
2869 int l = -1;
2871 assert(r != NULL && chs != NULL);
2873 *chs = 0;
2874 while(i++ < curwp->w_ntrows){
2875 if(lp == curwp->w_dotp)
2876 l = i-1;
2877 lp = lforw(lp);
2878 if(lp == curwp->w_bufp->b_linep){
2879 i++;
2880 break;
2882 if(l >= 0)
2883 (*chs) += llength(lp);
2885 *r = i - l - term.t_mrow;
2886 return(l+curwp->w_toprow);
2892 * resize_pico - given new window dimensions, allocate new resources
2895 resize_pico(int row, int col)
2897 int old_nrow, old_ncol;
2898 register int i;
2899 register VIDEO *vp;
2901 old_nrow = term.t_nrow;
2902 old_ncol = term.t_ncol;
2904 term.t_nrow = row;
2905 term.t_ncol = col;
2907 if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
2908 return(TRUE);
2910 if(curwp){
2911 curwp->w_toprow = 2;
2912 curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
2915 if(Pmaster){
2916 fillcol = Pmaster->fillcolumn;
2917 (*Pmaster->resize)();
2919 else if(userfillcol > 0)
2920 fillcol = userfillcol;
2921 else
2922 fillcol = term.t_ncol - 6; /* we control the fill column */
2925 * free unused screen space ...
2927 for(i=term.t_nrow+1; i <= old_nrow; ++i){
2928 free((char *) vscreen[i]);
2929 free((char *) pscreen[i]);
2933 * realloc new space for screen ...
2935 if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2936 if(Pmaster)
2937 return(-1);
2938 else
2939 exit(1);
2942 if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2943 if(Pmaster)
2944 return(-1);
2945 else
2946 exit(1);
2949 for (i = 0; i <= term.t_nrow; ++i) {
2950 if(i <= old_nrow)
2951 vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2952 else
2953 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2955 if (vp == NULL)
2956 exit(1);
2957 vp->v_flag = VFCHG;
2958 vscreen[i] = vp;
2959 if(old_ncol < term.t_ncol){ /* don't let any garbage in */
2960 vtrow = i;
2961 vtcol = (i < old_nrow) ? old_ncol : 0;
2962 vteeol();
2965 if(i <= old_nrow)
2966 vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2967 else
2968 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2970 if (vp == NULL)
2971 exit(1);
2973 vp->v_flag = VFCHG;
2974 pscreen[i] = vp;
2977 if(!ResizeBrowser()){
2978 if(Pmaster && Pmaster->headents){
2979 ResizeHeader();
2981 else{
2982 curwp->w_flag |= (WFHARD | WFMODE);
2983 pico_refresh(0, 1); /* redraw whole enchilada. */
2984 update(); /* do it */
2988 return(TRUE);
2991 void
2992 redraw_pico_for_callback(void)
2994 pico_refresh(0, 1);
2995 update();
3000 * showCompTitle - display the anchor line passed in from pine
3002 void
3003 showCompTitle(void)
3005 if(Pmaster){
3006 UCS *bufp;
3007 extern UCS *pico_anchor;
3008 COLOR_PAIR *lastc = NULL;
3010 if((bufp = pico_anchor) == NULL)
3011 return;
3013 movecursor(COMPOSER_TITLE_LINE, 0);
3014 if (Pmaster->colors && Pmaster->colors->tbcp &&
3015 pico_is_good_colorpair(Pmaster->colors->tbcp)){
3016 lastc = pico_get_cur_color();
3017 (void)pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
3019 else
3020 (*term.t_rev)(1);
3022 while (ttcol < term.t_ncol)
3023 if(*bufp != '\0')
3024 pputc(*bufp++, 1);
3025 else
3026 pputc(' ', 1);
3028 if (lastc){
3029 (void)pico_set_colorp(lastc, PSC_NONE);
3030 free_color_pair(&lastc);
3032 else
3033 (*term.t_rev)(0);
3035 movecursor(COMPOSER_TITLE_LINE + 1, 0);
3036 peeol();
3043 * zotdisplay - blast malloc'd space created for display maps
3045 void
3046 zotdisplay(void)
3048 register int i;
3050 for (i = 0; i <= term.t_nrow; ++i){ /* free screens */
3051 free((char *) vscreen[i]);
3052 free((char *) pscreen[i]);
3055 free((char *) vscreen);
3056 free((char *) pscreen);
3062 * nlforw() - returns the number of lines from the top to the dot
3065 nlforw(void)
3067 register int i = 0;
3068 register LINE *lp = curwp->w_linep;
3070 while(lp != curwp->w_dotp){
3071 lp = lforw(lp);
3072 i++;
3074 return(i);
3080 * pputc - output the given char, keep track of it on the physical screen
3081 * array, and keep track of the cursor
3083 void
3084 pputc(UCS c, /* char to write */
3085 int a) /* and its attribute */
3087 int ind, width, printable_ascii = 0;
3090 * This is necessary but not sufficient to allow us to draw. Note that
3091 * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
3092 * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
3094 if((ttcol >= 0 && ttcol < term.t_ncol) && (ttrow >= 0 && ttrow <= term.t_nrow)){
3097 * Width is the number of screen columns a character will occupy.
3099 if(c < 0x80 && isprint(c)){
3100 printable_ascii++;
3101 width = 1;
3103 else
3104 width = wcellwidth(c);
3106 if(width < 0)
3107 width = 1; /* will be a '?' */
3109 if(ttcol + width <= term.t_ncol){ /* it fits */
3111 * Some terminals scroll when you write in the lower right corner
3112 * of the screen, so don't write there.
3114 if(!(ttrow == term.t_nrow && ttcol+width == term.t_ncol)){
3115 (*term.t_putchar)(c); /* write it */
3116 ind = index_from_col(ttrow, ttcol);
3117 pscreen[ttrow]->v_text[ind].c = c; /* keep track of it */
3118 pscreen[ttrow]->v_text[ind].a = a; /* keep track of it */
3121 else{
3123 * Character overlaps right edge of screen. Hopefully the higher
3124 * layers will prevent this but we're making sure.
3126 * We may want to do something like writing a space character
3127 * into the cells that are on the screen. We'll see.
3131 ttcol = MIN(term.t_ncol, ttcol+width);
3137 * pputs - print a string and keep track of the cursor
3139 void
3140 pputs(UCS *s, /* string to write */
3141 int a) /* and its attribute */
3143 while (*s != '\0')
3144 pputc(*s++, a);
3148 void
3149 pputs_utf8(char *s, int a)
3151 UCS *ucsstr = NULL;
3153 if(s && *s){
3154 ucsstr = utf8_to_ucs4_cpystr(s);
3155 if(ucsstr){
3156 pputs(ucsstr, a);
3157 fs_give((void **) &ucsstr);
3164 * peeol - physical screen array erase to end of the line. remember to
3165 * track the cursor.
3167 void
3168 peeol(void)
3170 int i, width = 0, ww;
3171 CELL cl;
3173 if(ttrow < 0 || ttrow > term.t_nrow)
3174 return;
3176 cl.c = ' ';
3177 cl.a = 0;
3180 * Don't clear if we think we are sitting past the last column,
3181 * that erases the last column if we just wrote it.
3183 if(ttcol < term.t_ncol)
3184 (*term.t_eeol)();
3187 * Because the characters are variable width it's a little tricky
3188 * to erase the rest of the line. What we do is add up the
3189 * widths of the characters until we reach ttcol
3190 * then set the rest to the space character.
3192 for(i = 0; i < term.t_ncol && width < ttcol; i++){
3193 ww = wcellwidth((UCS) pscreen[ttrow]->v_text[i].c);
3194 width += (ww >= 0 ? ww : 1);
3197 while(i < term.t_ncol)
3198 pscreen[ttrow]->v_text[i++] = cl;
3203 * pscr - return the character cell on the physical screen map on the
3204 * given line, l, and offset, o.
3206 CELL *
3207 pscr(int l, int o)
3209 if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
3210 return(&(pscreen[l]->v_text[o]));
3211 else
3212 return(NULL);
3217 * pclear() - clear the physical screen from row x through row y (inclusive)
3218 * row is zero origin, min row = 0 max row = t_nrow
3219 * Clear whole screen -- pclear(0, term.t_nrow)
3220 * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
3221 * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
3223 void
3224 pclear(int x, int y)
3226 register int i;
3228 x = MIN(MAX(0, x), term.t_nrow);
3229 y = MIN(MAX(0, y), term.t_nrow);
3231 for(i=x; i <= y; i++){
3232 movecursor(i, 0);
3233 peeol();
3239 * dumbroot - just get close
3242 dumbroot(int x, int b)
3244 if(x < b)
3245 return(1);
3246 else
3247 return(dumbroot(x/b, b) + 1);
3252 * dumblroot - just get close
3255 dumblroot(long x, int b)
3257 if(x < b)
3258 return(1);
3259 else
3260 return(dumblroot(x/b, b) + 1);
3265 * pinsertc - use optimized insert, fixing physical screen map.
3266 * returns true if char written, false otherwise
3269 pinsert(CELL c)
3271 int i, ind = 0, ww;
3272 CELL *p;
3274 if(ttrow < 0 || ttrow > term.t_nrow)
3275 return(0);
3277 if(o_insert((UCS) c.c)){ /* if we've got it, use it! */
3278 p = pscreen[ttrow]->v_text; /* then clean up physical screen */
3280 ind = index_from_col(ttrow, ttcol);
3282 for(i = term.t_ncol-1; i > ind; i--)
3283 p[i] = p[i-1]; /* shift right */
3285 p[ind] = c; /* insert new char */
3287 ww = wcellwidth((UCS) c.c);
3288 ttcol += (ww >= 0 ? ww : 1);
3290 return(1);
3293 return(0);
3298 * pdel - use optimized delete to rub out the current char and
3299 * fix the physical screen array.
3300 * returns true if optimized the delete, false otherwise
3303 pdel(void)
3305 int i, ind = 0, w;
3306 CELL *p;
3308 if(ttrow < 0 || ttrow > term.t_nrow)
3309 return(0);
3311 if(TERM_DELCHAR){ /* if we've got it, use it! */
3312 p = pscreen[ttrow]->v_text;
3313 ind = index_from_col(ttrow, ttcol);
3315 if(ind > 0){
3316 --ind;
3317 w = wcellwidth((UCS) p[ind].c);
3318 w = (w >= 0 ? w : 1);
3319 ttcol -= w;
3321 for(i = 0; i < w; i++){
3322 (*term.t_putchar)('\b'); /* move left a char */
3323 o_delete(); /* and delete it */
3326 /* then clean up physical screen */
3327 for(i=ind; i < term.t_ncol-1; i++)
3328 p[i] = p[i+1];
3330 p[i].c = ' ';
3331 p[i].a = 0;
3334 return(1);
3337 return(0);
3343 * wstripe - write out the given string at the given location, and reverse
3344 * video on flagged characters. Does the same thing as pine's
3345 * stripe.
3347 * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
3348 * only if you want to put the tildes before multi-byte chars.
3350 void
3351 wstripe(int line, int column, char *utf8pmt, int key)
3353 UCS *ucs4pmt, *u;
3354 int i = 0, col = 0;
3355 int j = 0;
3356 int l, ww;
3357 COLOR_PAIR *lastc = NULL;
3358 COLOR_PAIR *kncp = NULL;
3359 COLOR_PAIR *klcp = NULL;
3361 if(line < 0 || line > term.t_nrow)
3362 return;
3364 if (Pmaster && Pmaster->colors){
3365 if(pico_is_good_colorpair(Pmaster->colors->klcp))
3366 klcp = Pmaster->colors->klcp;
3368 if(klcp && pico_is_good_colorpair(Pmaster->colors->kncp))
3369 kncp = Pmaster->colors->kncp;
3371 else if(Pcolors){
3372 klcp = Pcolors->klcp;
3373 kncp = Pcolors->kncp;
3376 lastc = pico_get_cur_color();
3377 ucs4pmt = utf8_to_ucs4_cpystr(utf8pmt);
3378 l = ucs4_strlen(ucs4pmt);
3379 while(1){
3380 if(i >= term.t_ncol || col >= term.t_ncol || j >= l){
3381 if(lastc) free_color_pair(&lastc);
3382 if(ucs4pmt) fs_give((void **) &ucs4pmt);
3383 return; /* equal strings */
3386 if(ucs4pmt[j] == (UCS) key)
3387 j++;
3389 if (pscr(line, i) == NULL){
3390 if(lastc) free_color_pair(&lastc);
3391 if(ucs4pmt) fs_give((void **) &ucs4pmt);
3392 return;
3395 if(pscr(line, i)->c != ucs4pmt[j]){
3396 if(j >= 1 && ucs4pmt[j-1] == (UCS) key)
3397 j--;
3398 break;
3401 ww = wcellwidth((UCS) pscr(line, i)->c);
3402 col += (ww >= 0 ? ww : 1);
3403 j++;
3404 i++;
3407 movecursor(line, column+col);
3408 if(klcp) (void)pico_set_colorp(klcp, PSC_NONE);
3409 u = &ucs4pmt[j];
3411 if(*u == (UCS) key){
3412 u++;
3413 if(kncp)
3414 (void)pico_set_colorp(kncp, PSC_NONE);
3415 else
3416 (void)(*term.t_rev)(1);
3418 pputc(*u, 1);
3419 if(kncp)
3420 (void)pico_set_colorp(klcp, PSC_NONE);
3421 else
3422 (void)(*term.t_rev)(0);
3424 else{
3425 pputc(*u, 0);
3428 while(*++u != '\0');
3430 if(ucs4pmt)
3431 fs_give((void **) &ucs4pmt);
3433 peeol();
3434 if (lastc){
3435 (void)pico_set_colorp(lastc, PSC_NONE);
3436 free_color_pair(&lastc);
3438 (*term.t_flush)();
3444 * wkeyhelp - paint list of possible commands on the bottom
3445 * of the display (yet another pine clone)
3446 * NOTE: function key mode is handled here since all the labels
3447 * are the same...
3449 * The KEYMENU definitions have names and labels defined as UTF-8 strings,
3450 * and wstripe expects UTF-8.
3452 void
3453 wkeyhelp(KEYMENU *keymenu)
3455 char *obufp, *p, fkey[4];
3456 char linebuf[2*NLINE]; /* "2" is for space for invert tokens */
3457 int row, slot, tspace, adjusted_tspace, nspace[6], index, n;
3458 #ifdef MOUSE
3459 char nbuf[NLINE];
3460 #endif
3462 #ifdef _WINDOWS
3463 pico_config_menu_items (keymenu);
3464 #endif
3466 if(term.t_mrow == 0)
3467 return;
3469 if(term.t_nrow < 1)
3470 return;
3473 * Calculate amount of space for the names column by column...
3475 for(index = 0; index < 6; index++)
3476 if(!(gmode&MDFKEY)){
3477 nspace[index] = (keymenu[index].name)
3478 ? utf8_width(keymenu[index].name) : 0;
3479 if(keymenu[index+6].name
3480 && (n = utf8_width(keymenu[index+6].name)) > nspace[index])
3481 nspace[index] = n;
3483 nspace[index]++;
3485 else
3486 nspace[index] = (index < 4) ? 3 : 4;
3488 tspace = term.t_ncol/6; /* total space for each item */
3491 * Avoid writing in bottom right corner so we won't scroll screens that
3492 * scroll when you do that. The way this is setup, we won't do that
3493 * unless the number of columns is evenly divisible by 6.
3495 adjusted_tspace = (6 * tspace == term.t_ncol) ? tspace - 1 : tspace;
3497 index = 0;
3498 for(row = 0; row <= 1; row++){
3499 linebuf[0] = '\0';
3500 obufp = &linebuf[0];
3501 for(slot = 0; slot < 6; slot++){
3502 if(keymenu[index].name && keymenu[index].label){
3503 size_t l;
3504 char this_label[200], tmp_label[200];
3506 if(keymenu[index].label[0] == '[' && keymenu[index].label[(l=strlen(keymenu[index].label))-1] == ']' && l > 2){
3507 strncpy(tmp_label, &keymenu[index].label[1], MIN(sizeof(tmp_label),l-2));
3508 tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
3509 snprintf(this_label, sizeof(this_label), "[%s]", _(tmp_label));
3511 else
3512 strncpy(this_label, _(keymenu[index].label), sizeof(this_label));
3514 this_label[sizeof(this_label)-1] = '\0';
3516 if(gmode&MDFKEY){
3517 p = fkey;
3518 snprintf(fkey, sizeof(fkey), "F%d", (2 * slot) + row + 1);
3520 else
3521 p = keymenu[index].name;
3522 #ifdef MOUSE
3523 snprintf(nbuf, sizeof(nbuf), "%.*s %s", nspace[slot], p, this_label);
3524 register_key(index,
3525 (gmode&MDFKEY) ? F1 + (2 * slot) + row:
3526 (keymenu[index].name[0] == '^')
3527 ? (CTRL | keymenu[index].name[1])
3528 : (keymenu[index].name[0] == 'S'
3529 && !strcmp(keymenu[index].name, "Spc"))
3530 ? ' '
3531 : keymenu[index].name[0],
3532 nbuf, invert_label,
3533 term.t_nrow - 1 + row, (slot * tspace),
3534 strlen(nbuf),
3535 (Pmaster && Pmaster->colors)
3536 ? Pmaster->colors->kncp: NULL,
3537 (Pmaster && Pmaster->colors)
3538 ? Pmaster->colors->klcp: NULL);
3539 #endif
3541 n = nspace[slot];
3542 while(p && *p && n--){
3543 *obufp++ = '~'; /* insert "invert" token */
3544 *obufp++ = *p++;
3547 while(n-- > 0)
3548 *obufp++ = ' ';
3550 p = this_label;
3551 n = ((slot == 5 && row == 1) ? adjusted_tspace
3552 : tspace) - nspace[slot];
3553 while(p && *p && n-- > 0)
3554 *obufp++ = *p++;
3556 while(n-- > 0)
3557 *obufp++ = ' ';
3559 else{
3560 n = (slot == 5 && row == 1) ? adjusted_tspace : tspace;
3561 while(n--)
3562 *obufp++ = ' ';
3564 #ifdef MOUSE
3565 register_key(index, NODATA, "", NULL, 0, 0, 0, NULL, NULL);
3566 #endif
3569 *obufp = '\0';
3570 index++;
3573 wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
3579 * This returns the screen width between pstart (inclusive) and
3580 * pend (exclusive) where the pointers point into an array of CELLs.
3582 unsigned
3583 cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend)
3585 CELL *p;
3586 unsigned width = 0;
3587 int ww;
3589 if(pstart)
3590 for(p = pstart; p < pend; p++){
3591 ww = wcellwidth((UCS) p->c);
3592 width += (ww >= 0 ? ww : 1);
3595 return(width);
3600 * This returns the virtual screen width in row from index a to b (exclusive).
3602 unsigned
3603 vcellwidth_a_to_b(int row, int a, int b)
3605 CELL *pstart, *pend;
3606 VIDEO *vp;
3608 if(row < 0 || row > term.t_nrow)
3609 return 0;
3611 if(a >= b)
3612 return 0;
3614 a = MIN(MAX(0, a), term.t_ncol-1);
3615 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3617 vp = vscreen[row];
3618 pstart = &vp->v_text[a];
3619 pend = &vp->v_text[b];
3621 return(cellwidth_ptr_to_ptr(pstart, pend));
3626 * This returns the physical screen width in row from index a to b (exclusive).
3628 unsigned
3629 pcellwidth_a_to_b(int row, int a, int b)
3631 CELL *pstart, *pend;
3632 VIDEO *vp;
3634 if(row < 0 || row > term.t_nrow)
3635 return 0;
3637 if(a >= b)
3638 return 0;
3640 a = MIN(MAX(0, a), term.t_ncol-1);
3641 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3643 vp = pscreen[row];
3644 pstart = &vp->v_text[a];
3645 pend = &vp->v_text[b];
3647 return(cellwidth_ptr_to_ptr(pstart, pend));
3652 index_from_col(int row, int col)
3654 CELL *p_start, *p_end, *p_limit;
3655 int w_consumed = 0, w, done = 0;
3657 if(row < 0 || row > term.t_nrow)
3658 return 0;
3660 p_end = p_start = pscreen[row]->v_text;
3661 p_limit = p_start + term.t_ncol;
3663 if(p_start)
3664 while(!done && p_end < p_limit && p_end->c && w_consumed <= col){
3665 w = wcellwidth((UCS) p_end->c);
3666 w = (w >= 0 ? w : 1);
3667 if(w_consumed + w <= col){
3668 w_consumed += w;
3669 ++p_end;
3671 else
3672 ++done;
3675 /* MIN and MAX just to be sure */
3676 return(MIN(MAX(0, p_end - p_start), term.t_ncol-1));
3679 #ifdef _WINDOWS
3681 void
3682 pico_config_menu_items (KEYMENU *keymenu)
3684 int i;
3685 KEYMENU *k;
3686 UCS key;
3688 mswin_menuitemclear ();
3690 /* keymenu's seem to be hardcoded at 12 entries. */
3691 for (i = 0, k = keymenu; i < 12; ++i, ++k) {
3692 if (k->name != NULL && k->label != NULL &&
3693 k->menuitem != KS_NONE) {
3695 if (k->name[0] == '^')
3696 key = CTRL | k->name[1];
3697 else if (strcmp(k->name, "Ret") == 0)
3698 key = '\r';
3699 else
3700 key = k->name[0];
3702 mswin_menuitemadd (key, k->label, k->menuitem, 0);
3708 * Update the scroll range and position. (exported)
3710 * This is where curbp->b_linecnt is really managed. With out this function
3711 * to count the number of lines when needed curbp->b_linecnt will never
3712 * really be correct. BUT, this function is only compiled into the
3713 * windows version, so b_linecnt will only ever be right in the windows
3714 * version. OK for now because that is the only version that
3715 * looks at b_linecnt.
3718 update_scroll (void)
3720 long scr_pos;
3721 long scr_range;
3722 LINE *lp;
3723 static LINE *last_top_line = NULL;
3724 static long last_scroll_pos = -1;
3727 if (ComposerEditing) {
3728 /* Editing header - don't allow scroll bars. */
3729 mswin_setscrollrange (0, 0);
3730 return(0);
3735 * Count the number of lines in the current buffer. Done when:
3737 * when told to recount: curbp->b_linecnt == -1
3738 * when the top line changed: curwp->w_linep != last_top_line
3739 * when we don't know the scroll pos: last_scroll_pos == -1
3741 * The first line in the list is a "place holder" line and is not
3742 * counted. The list is circular, when we return the to place
3743 * holder we have reached the end.
3745 if(curbp->b_linecnt == -1 || curwp->w_linep != last_top_line
3746 || last_scroll_pos == -1) {
3747 scr_range = 0;
3748 scr_pos = 0;
3749 for (lp = lforw (curbp->b_linep); lp != curbp->b_linep;
3750 lp = lforw (lp)) {
3751 if (lp == curwp->w_linep)
3752 scr_pos = scr_range;
3754 ++scr_range;
3757 curbp->b_linecnt = scr_range;
3758 last_scroll_pos = scr_pos;
3759 last_top_line = curwp->w_linep;
3763 * Set new scroll range and position.
3765 mswin_setscrollrange (curwp->w_ntrows - 2, curbp->b_linecnt - 1);
3766 mswin_setscrollpos (last_scroll_pos);
3767 return (0);
3769 #endif /* _WINDOWS */