* Update to version 2.19.5
[alpine.git] / pico / display.c
blob792783d57a726e49bda58c2862f731ec25decc1d
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-2014 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
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 #ifdef _WINDOWS
58 void pico_config_menu_items (KEYMENU *);
59 int update_scroll (void);
60 #endif /* _WINDOWS */
64 * Standard pico keymenus...
66 static KEYMENU menu_pico[] = {
67 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^O", N_("WriteOut"), KS_SAVEFILE},
68 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
69 {"^K", N_("Cut Text"), KS_NONE}, {"^C", N_("Cur Pos"), KS_CURPOSITION},
70 {"^X", N_("Exit"), KS_EXIT}, {"^J", N_("Justify"), KS_JUSTIFY},
71 {"^W", N_("Where is"), KS_WHEREIS}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
72 {"^U", NULL, KS_NONE},
73 #ifdef SPELLER
74 {"^T", N_("To Spell"), KS_SPELLCHK}
75 #else
76 {"^D", N_("Del Char"), KS_NONE}
77 #endif
79 #define UNCUT_KEY 10
82 static KEYMENU menu_compose[] = {
83 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^X", NULL, KS_SEND},
84 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
85 {"^K", N_("Cut Text"), KS_NONE}, {"^O", N_("Postpone"), KS_POSTPONE},
86 /* TRANSLATORS: Justify is to reformat a paragraph automatically */
87 {"^C", N_("Cancel"), KS_CANCEL}, {"^J", N_("Justify"), KS_JUSTIFY},
88 {NULL, NULL, KS_NONE}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
89 {"^U", NULL, KS_NONE},
90 #ifdef SPELLER
91 {"^T", N_("To Spell"), KS_SPELLCHK}
92 #else
93 {"^D", N_("Del Char"), KS_NONE}
94 #endif
96 #define EXIT_KEY 1
97 #define PSTPN_KEY 5
98 #define WHERE_KEY 8
102 * Definition's for pico's modeline
104 #define PICO_TITLE " UW PICO %s"
105 #define PICO_MOD_MSG "Modified"
106 #define PICO_NEWBUF_MSG "New Buffer"
108 #define WFDEBUG 0 /* Window flag debug. */
110 #define VFCHG 0x0001 /* Changed flag */
111 #define VFEXT 0x0002 /* extended (beyond column 80) */
112 #define VFREV 0x0004 /* reverse video status */
113 #define VFREQ 0x0008 /* reverse video request */
115 int vtrow = 0; /* Row location of SW cursor */
116 int vtcol = 0; /* Column location of SW cursor */
117 int vtind = 0; /* Index into row array of SW cursor */
118 int ttrow = FARAWAY; /* Row location of HW cursor */
119 int ttcol = FARAWAY; /* Column location of HW cursor */
120 int lbound = 0; /* leftmost column of current line
121 being displayed */
123 VIDEO **vscreen; /* Virtual screen. */
124 VIDEO **pscreen; /* Physical screen. */
128 * Initialize the data structures used by the display code. The edge vectors
129 * used to access the screens are set up. The operating system's terminal I/O
130 * channel is set up. All the other things get initialized at compile time.
131 * The original window has "WFCHG" set, so that it will get completely
132 * redrawn on the first call to "update".
135 vtinit(void)
137 int i, j;
138 VIDEO *vp;
139 CELL ac;
141 ac.c = ' ';
142 ac.a = 0;
144 if(Pmaster == NULL)
145 vtterminalinfo(gmode & MDTCAPWINS);
147 (*term.t_open)();
149 (*term.t_rev)(FALSE);
150 vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
151 memset(vscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
152 if (vscreen == NULL){
153 emlwrite("Allocating memory for virtual display failed.", NULL);
154 return(FALSE);
157 pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
158 memset(pscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
159 if (pscreen == NULL){
160 free((void *)vscreen);
161 emlwrite("Allocating memory for physical display failed.", NULL);
162 return(FALSE);
166 for (i = 0; i <= term.t_nrow; ++i) {
167 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
169 if (vp == NULL){
170 free((void *)vscreen);
171 free((void *)pscreen);
172 emlwrite("Allocating memory for virtual display lines failed.",
173 NULL);
174 return(FALSE);
176 else
177 for(j = 0; j < term.t_ncol; j++)
178 vp->v_text[j] = ac;
180 vp->v_flag = 0;
181 vscreen[i] = vp;
183 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
185 if (vp == NULL){
186 free((void *)vscreen[i]);
187 while(--i >= 0){
188 free((void *)vscreen[i]);
189 free((void *)pscreen[i]);
192 free((void *)vscreen);
193 free((void *)pscreen);
194 emlwrite("Allocating memory for physical display lines failed.",
195 NULL);
196 return(FALSE);
198 else
199 for(j = 0; j < term.t_ncol; j++)
200 vp->v_text[j] = ac;
202 vp->v_flag = 0;
203 pscreen[i] = vp;
206 return(TRUE);
210 vtterminalinfo(int termcap_wins)
212 return((term.t_terminalinfo) ? (*term.t_terminalinfo)(termcap_wins)
213 : (Pmaster ? 0 : TRUE));
218 * Clean up the virtual terminal system, in anticipation for a return to the
219 * operating system. Move down to the last line and clear it out (the next
220 * system prompt will be written in the line). Shut down the channel to the
221 * terminal.
223 void
224 vttidy(void)
226 movecursor(term.t_nrow-1, 0);
227 peeol();
228 movecursor(term.t_nrow, 0);
229 peeol();
230 (*term.t_close)();
235 * Set the virtual cursor to the specified row and column on the virtual
236 * screen. There is no checking for nonsense values; this might be a good
237 * idea during the early stages.
239 void
240 vtmove(int row, int col)
242 vtrow = row;
243 vtcol = col;
245 if(vtcol < 0)
246 vtind = -1;
247 else if(vtcol == 0)
248 vtind = vtcol;
249 else{
251 * This is unused so don't worry about it.
253 assert(0);
259 * Write a character to the virtual screen. The virtual row and column are
260 * updated. If the line is too long put a "$" in the last column. This routine
261 * only puts printing characters into the virtual terminal buffers. Only
262 * column overflow is checked.
264 void
265 vtputc(CELL c)
267 VIDEO *vp;
268 CELL ac;
269 int w;
271 vp = vscreen[vtrow];
272 ac.c = ' ';
273 ac.a = c.a;
275 if (vtcol >= term.t_ncol) {
277 * What's this supposed to be doing? This sets vtcol
278 * to the even multiple of 8 >= vtcol. Why are we doing that?
279 * Must make tab work correctly.
280 * 24 -> 24
281 * 25 -> 32
282 * ...
283 * 31 -> 32
284 * 32 -> 32
285 * 33 -> 40
287 vtcol = (vtcol + 0x07) & ~0x07;
288 ac.c = '$';
291 * If we get to here that means that there must be characters
292 * past the right hand edge, so we want to put a $ character
293 * in the last visible character. It would be nice to replace
294 * the last visible character by a double-$ if it is double-width
295 * but we aren't doing that because we'd have to add up the widths
296 * starting at the left hand margin each time through.
298 if(vtind > 0 && vtind <= term.t_ncol)
299 vp->v_text[vtind-1] = ac;
301 else if (c.c == '\t') {
302 do {
303 vtputc(ac);
305 while (((vtcol + (vtrow==currow ? lbound : 0)) & 0x07) != 0 && vtcol < term.t_ncol);
307 else if (ISCONTROL(c.c)){
308 ac.c = '^';
309 vtputc(ac);
310 ac.c = ((c.c & 0x7f) | 0x40);
311 vtputc(ac);
313 else{
316 * Have to worry about what happens if we skip over 0
317 * with a double-width character. There may be a better
318 * place to be setting vtind, or maybe we could make do
319 * without it.
322 w = wcellwidth((UCS) c.c);
323 w = (w >= 0 ? w : 1);
325 if(vtcol == 0 || (vtcol < 0 && vtcol + w == 1)){
326 vtind = 0;
327 if(vtcol < 0)
328 vtcol = 0;
332 * Double-width character overlaps right edge.
333 * Replace it with a $.
335 if(vtcol + w > term.t_ncol){
336 ac.c = '$';
337 c = ac;
338 w = 1;
341 if(vtind >= 0 && vtind < term.t_ncol)
342 vp->v_text[vtind++] = c;
344 vtcol += w;
350 * Erase from the end of the software cursor to the end of the line on which
351 * the software cursor is located.
353 void
354 vteeol(void)
356 register VIDEO *vp;
357 CELL c;
359 c.c = ' ';
360 c.a = 0;
361 vp = vscreen[vtrow];
363 if(vtind >= 0)
364 while (vtind < term.t_ncol)
365 vp->v_text[vtind++] = c;
367 vtcol = term.t_ncol;
372 * Make sure that the display is right. This is a three part process. First,
373 * scan through all of the windows looking for dirty ones. Check the framing,
374 * and refresh the screen. Second, make sure that "currow" and "curcol" are
375 * correct for the current window. Third, make the virtual and physical
376 * screens the same.
378 void
379 update(void)
381 LINE *lp;
382 WINDOW *wp;
383 VIDEO *vp1;
384 VIDEO *vp2;
385 int i;
386 int j;
387 int scroll = 0;
388 CELL c;
390 #if TYPEAH
391 if (typahead())
392 return;
393 #endif
395 #ifdef _WINDOWS
396 /* This tells our MS Windows module to not bother updating the
397 * cursor position while a massive screen update is in progress.
399 mswin_beginupdate ();
400 #endif
403 * BUG: setting and unsetting whole region at a time is dumb. fix this.
405 if(curwp->w_markp){
406 unmarkbuffer();
407 markregion(1);
410 wp = wheadp;
412 while (wp != NULL){
413 /* Look at any window with update flags set on. */
415 if (wp->w_flag != 0){
416 /* If not force reframe, check the framing. */
418 if ((wp->w_flag & WFFORCE) == 0){
419 lp = wp->w_linep;
421 for (i = 0; i < wp->w_ntrows; ++i){
422 if (lp == wp->w_dotp)
423 goto out;
425 if (lp == wp->w_bufp->b_linep)
426 break;
428 lp = lforw(lp);
432 /* Not acceptable, better compute a new value for the line at the
433 * top of the window. Then set the "WFHARD" flag to force full
434 * redraw.
436 i = wp->w_force;
438 if (i > 0){
439 --i;
441 if (i >= wp->w_ntrows)
442 i = wp->w_ntrows-1;
444 else if (i < 0){
445 i += wp->w_ntrows;
447 if (i < 0)
448 i = 0;
450 else if(TERM_OPTIMIZE){
452 * find dotp, if its been moved just above or below the
453 * window, use scrollxxx() to facilitate quick redisplay...
455 lp = lforw(wp->w_dotp);
456 if(lp != wp->w_dotp){
457 if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
458 scroll = 1;
460 else {
461 lp = wp->w_linep;
462 for(j=0;j < wp->w_ntrows; ++j){
463 if(lp != wp->w_bufp->b_linep)
464 lp = lforw(lp);
465 else
466 break;
468 if(lp == wp->w_dotp && j == wp->w_ntrows)
469 scroll = 2;
472 j = i = wp->w_ntrows/2;
474 else
475 i = wp->w_ntrows/2;
477 lp = wp->w_dotp;
479 while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
480 --i;
481 lp = lback(lp);
485 * this is supposed to speed things up by using tcap sequences
486 * to efficiently scroll the terminal screen. the thinking here
487 * is that its much faster to update pscreen[] than to actually
488 * write the stuff to the screen...
490 if(TERM_OPTIMIZE){
491 switch(scroll){
492 case 1: /* scroll text down */
493 j = j-i+1; /* add one for dot line */
495 * do we scroll down the header as well? Well, only
496 * if we're not editing the header, we've backed up
497 * to the top, and the composer is not being
498 * displayed...
500 if(Pmaster && Pmaster->headents && !ComposerEditing
501 && (lback(lp) == wp->w_bufp->b_linep)
502 && (ComposerTopLine == COMPOSER_TOP_LINE))
503 j += entry_line(1000, TRUE); /* Never > 1000 headers */
505 scrolldown(wp, -1, j);
506 break;
507 case 2: /* scroll text up */
508 j = wp->w_ntrows - (j-i); /* we chose new top line! */
509 if(Pmaster && j){
511 * do we scroll down the header as well? Well, only
512 * if we're not editing the header, we've backed up
513 * to the top, and the composer is not being
514 * displayed...
516 if(!ComposerEditing
517 && (ComposerTopLine != COMPOSER_TOP_LINE))
518 scrollup(wp, COMPOSER_TOP_LINE,
519 j+entry_line(1000, TRUE));
520 else
521 scrollup(wp, -1, j);
523 else
524 scrollup(wp, -1, j);
525 break;
526 default :
527 break;
531 wp->w_linep = lp;
532 wp->w_flag |= WFHARD; /* Force full. */
533 out:
535 * if the line at the top of the page is the top line
536 * in the body, show the header...
538 if(Pmaster && Pmaster->headents && !ComposerEditing){
539 if(lback(wp->w_linep) == wp->w_bufp->b_linep){
540 if(ComposerTopLine == COMPOSER_TOP_LINE){
541 i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
542 if(i > 0 && nlforw() >= i) { /* room for header ? */
543 if((i = nlforw()/2) == 0 && term.t_nrow&1)
544 i = 1;
545 while(wp->w_linep != wp->w_bufp->b_linep && i--)
546 wp->w_linep = lforw(wp->w_linep);
549 else
550 ToggleHeader(1);
553 else{
554 if(ComposerTopLine != COMPOSER_TOP_LINE)
555 ToggleHeader(0); /* hide it ! */
559 /* Try to use reduced update. Mode line update has its own special
560 * flag. The fast update is used if the only thing to do is within
561 * the line editing.
563 lp = wp->w_linep;
564 i = wp->w_toprow;
566 if ((wp->w_flag & ~WFMODE) == WFEDIT){
567 while (lp != wp->w_dotp){
568 ++i;
569 lp = lforw(lp);
572 vscreen[i]->v_flag |= VFCHG;
573 vtmove(i, 0);
575 for (j = 0; j < llength(lp); ++j)
576 vtputc(lgetc(lp, j));
578 vteeol();
580 else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
581 while (i < wp->w_toprow+wp->w_ntrows){
582 vscreen[i]->v_flag |= VFCHG;
583 vtmove(i, 0);
585 /* if line has been changed */
586 if (lp != wp->w_bufp->b_linep){
587 for (j = 0; j < llength(lp); ++j)
588 vtputc(lgetc(lp, j));
590 lp = lforw(lp);
593 vteeol();
594 ++i;
597 #if ~WFDEBUG
598 if ((wp->w_flag&WFMODE) != 0)
599 modeline(wp);
601 wp->w_flag = 0;
602 wp->w_force = 0;
603 #endif
605 #if WFDEBUG
606 modeline(wp);
607 wp->w_flag = 0;
608 wp->w_force = 0;
609 #endif
611 /* and onward to the next window */
612 wp = wp->w_wndp;
615 /* Always recompute the row and column number of the hardware cursor. This
616 * is the only update for simple moves.
618 lp = curwp->w_linep;
619 currow = curwp->w_toprow;
621 while (lp != curwp->w_dotp){
622 ++currow;
623 lp = lforw(lp);
626 curcol = 0;
627 i = 0;
629 while (i < curwp->w_doto){
630 c = lgetc(lp, i++);
632 if(c.c == '\t'){
633 curcol |= 0x07;
634 ++curcol;
636 else if(ISCONTROL(c.c)){
637 curcol += 2;
639 else{
640 int w;
642 w = wcellwidth((UCS) c.c);
643 curcol += (w >= 0 ? w : 1);
647 if (curcol >= term.t_ncol) { /* extended line. */
648 /* flag we are extended and changed */
649 vscreen[currow]->v_flag |= VFEXT | VFCHG;
650 updext(); /* and output extended line */
651 } else
652 lbound = 0; /* not extended line */
654 /* make sure no lines need to be de-extended because the cursor is
655 * no longer on them
658 wp = wheadp;
660 while (wp != NULL) {
661 lp = wp->w_linep;
662 i = wp->w_toprow;
664 while (i < wp->w_toprow + wp->w_ntrows) {
665 if (vscreen[i]->v_flag & VFEXT) {
666 /* always flag extended lines as changed */
667 vscreen[i]->v_flag |= VFCHG;
668 if ((wp != curwp) || (lp != wp->w_dotp) ||
669 (curcol < term.t_ncol)) {
670 vtmove(i, 0);
671 for (j = 0; j < llength(lp); ++j)
672 vtputc(lgetc(lp, j));
673 vteeol();
675 /* this line no longer is extended */
676 vscreen[i]->v_flag &= ~VFEXT;
679 lp = lforw(lp);
680 ++i;
682 /* and onward to the next window */
683 wp = wp->w_wndp;
686 /* Special hacking if the screen is garbage. Clear the hardware screen,
687 * and update your copy to agree with it. Set all the virtual screen
688 * change bits, to force a full update.
691 if (sgarbf != FALSE){
692 if(Pmaster){
693 int rv;
695 showCompTitle();
697 if(ComposerTopLine != COMPOSER_TOP_LINE){
698 UpdateHeader(0); /* arrange things */
699 PaintHeader(COMPOSER_TOP_LINE, TRUE);
703 * since we're using only a portion of the screen and only
704 * one buffer, only clear enough screen for the current window
705 * which is to say the *only* window.
707 for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
708 movecursor(i, 0);
709 peeol();
710 vscreen[i]->v_flag |= VFCHG;
712 rv = (*Pmaster->showmsg)('X' & 0x1f); /* ctrl-L */
713 ttresize();
714 picosigs(); /* restore altered handlers */
715 if(rv) /* Did showmsg corrupt the display? */
716 PaintBody(0); /* Yes, repaint */
717 movecursor(wheadp->w_toprow, 0);
719 else{
720 for (i = 0; i < term.t_nrow-term.t_mrow; ++i){
721 vscreen[i]->v_flag |= VFCHG;
722 vp1 = pscreen[i];
723 c.c = ' ';
724 c.a = 0;
725 for (j = 0; j < term.t_ncol; ++j)
726 vp1->v_text[j] = c;
729 movecursor(0, 0); /* Erase the screen. */
730 (*term.t_eeop)();
734 sgarbf = FALSE; /* Erase-page clears */
735 mpresf = FALSE; /* the message area. */
737 if(Pmaster)
738 modeline(curwp);
739 else
740 sgarbk = TRUE; /* fix the keyhelp as well...*/
743 /* Make sure that the physical and virtual displays agree. Unlike before,
744 * the "updateline" code is only called with a line that has been updated
745 * for sure.
747 if(Pmaster)
748 i = curwp->w_toprow;
749 else
750 i = 0;
752 if (term.t_nrow > term.t_mrow)
753 c.c = term.t_nrow - term.t_mrow;
754 else
755 c.c = 0;
757 for (; i < (int)c.c; ++i){
759 vp1 = vscreen[i];
761 /* for each line that needs to be updated, or that needs its
762 reverse video status changed, call the line updater */
763 j = vp1->v_flag;
764 if (j & VFCHG){
766 #if TYPEAH
767 if (typahead()){
768 #ifdef _WINDOWS
769 mswin_endupdate ();
770 #endif
771 return;
773 #endif
774 vp2 = pscreen[i];
776 updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
780 if(Pmaster == NULL){
782 if(sgarbk != FALSE){
783 if(term.t_mrow > 0){
784 movecursor(term.t_nrow-1, 0);
785 peeol();
786 movecursor(term.t_nrow, 0);
787 peeol();
790 if(lastflag&CFFILL){
791 /* TRANSLATORS: UnJustify means undo the previous
792 Justify command. */
793 menu_pico[UNCUT_KEY].label = N_("UnJustify");
794 if(!(lastflag&CFFLBF)){
795 emlwrite(_("Can now UnJustify!"), NULL);
796 mpresf = FARAWAY; /* remove this after next keystroke! */
799 else
800 menu_pico[UNCUT_KEY].label = N_("UnCut Text");
802 wkeyhelp(menu_pico);
803 sgarbk = FALSE;
807 if(lastflag&CFFLBF){
808 emlwrite(_("Can now UnJustify!"), NULL);
809 mpresf = FARAWAY; /* remove this after next keystroke! */
812 /* Finally, update the hardware cursor and flush out buffers. */
814 movecursor(currow, curcol - lbound);
815 #ifdef _WINDOWS
816 mswin_endupdate ();
819 * Update the scroll bars. This function is where curbp->b_linecnt
820 * is really managed. See update_scroll.
822 update_scroll ();
823 #endif
824 (*term.t_flush)();
828 /* updext - update the extended line which the cursor is currently
829 * on at a column greater than the terminal width. The line
830 * will be scrolled right or left to let the user see where
831 * the cursor is
833 void
834 updext(void)
836 int rcursor; /* real cursor location */
837 LINE *lp; /* pointer to current line */
838 int j; /* index into line */
839 int w = 0;
840 int ww;
843 * Calculate what column the real cursor will end up in.
844 * The cursor will be in the rcursor'th column. So if we're
845 * counting columns 0 1 2 3 and rcursor is 8, then rcursor
846 * will be over cell 7.
848 * What this effectively does is to scroll the screen as we're
849 * moving to the right when the cursor first passes off the
850 * screen's right edge. It would be nice if it did the same
851 * thing coming back to the left. Instead, in order that the
852 * screen's display depends only on the curcol and not on
853 * how we got there, the screen scrolls when we pass the
854 * t_margin column. It's also kind of funky that you can't
855 * see the character under the $ but you can delete it.
857 rcursor = ((curcol - term.t_ncol) % (term.t_ncol - term.t_margin + 1)) + term.t_margin;
858 lbound = curcol - rcursor + 1;
861 * Make sure lbound is set so that a double-width character does
862 * not straddle the boundary. If it does, move over one cell.
864 lp = curwp->w_dotp; /* line to output */
865 for (j=0; j<llength(lp) && w < lbound; ++j){
866 ww = wcellwidth((UCS) lgetc(lp, j).c);
867 w += (ww >= 0 ? ww : 1);
870 if(w > lbound)
871 lbound = w;
874 /* scan through the line outputing characters to the virtual screen
875 * once we reach the left edge
877 vtmove(currow, -lbound); /* start scanning offscreen */
878 for (j=0; j<llength(lp); ++j) /* until the end-of-line */
879 vtputc(lgetc(lp, j));
881 /* truncate the virtual line */
882 vteeol();
884 /* and put a '$' in column 1, may have to adjust curcol */
885 w = wcellwidth((UCS) vscreen[currow]->v_text[0].c);
886 vscreen[currow]->v_text[0].c = '$';
887 vscreen[currow]->v_text[0].a = 0;
888 if(w == 2){
890 * We want to put $ in the first two columns so that it
891 * takes up the right amount of space, but that means we
892 * have to scoot the real characters over one slot.
894 for (j = term.t_ncol-1; j >= 2; --j)
895 vscreen[currow]->v_text[j] = vscreen[currow]->v_text[j-1];
897 vscreen[currow]->v_text[1].c = '$';
898 vscreen[currow]->v_text[1].a = 0;
904 * Update a single line. This does not know how to use insert or delete
905 * character sequences; we are using VT52 functionality. Update the physical
906 * row and column variables.
908 void
909 updateline(int row, /* row on screen */
910 CELL vline[], /* what we want it to end up as */
911 CELL pline[], /* what it looks like now */
912 short *flags)
914 CELL *cp1, *cp2, *cp3, *cp4, *cp5, *cp6, *cp7;
915 int display = TRUE;
916 int nbflag; /* non-blanks to the right flag? */
917 int cleartoeol = 0;
919 if(row < 0 || row > term.t_nrow)
920 return;
922 /* set up pointers to virtual and physical lines */
923 cp1 = &vline[0];
924 cp2 = &pline[0];
925 cp3 = &vline[term.t_ncol];
927 /* advance past any common chars at the left */
928 while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
929 ++cp1;
930 ++cp2;
933 /* This can still happen, even though we only call this routine on changed
934 * lines. A hard update is always done when a line splits, a massive
935 * change is done, or a buffer is displayed twice. This optimizes out most
936 * of the excess updating. A lot of computes are used, but these tend to
937 * be hard operations that do a lot of update, so I don't really care.
939 /* if both lines are the same, no update needs to be done */
940 if (cp1 == cp3){
941 *flags &= ~VFCHG; /* mark it clean */
942 return;
945 /* find out if there is a match on the right */
946 nbflag = FALSE;
947 cp3 = &vline[term.t_ncol];
948 cp4 = &pline[term.t_ncol];
950 if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
951 while (cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
952 --cp3;
953 --cp4;
954 if (cp3[0].c != ' ' || cp3[0].a != 0) /* Note if any nonblank */
955 nbflag = TRUE; /* in right match. */
958 cp5 = cp3;
960 if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
961 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
962 --cp5;
964 if (cp3-cp5 <= 3) /* Use only if erase is */
965 cp5 = cp3; /* fewer characters. */
968 /* go to start of differences */
969 movecursor(row, cellwidth_ptr_to_ptr(&vline[0], cp1));
971 if (!nbflag) { /* use insert or del char? */
972 cp6 = cp3;
973 cp7 = cp4;
975 if(TERM_INSCHAR
976 &&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
977 while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
978 --cp7;
979 --cp6;
982 if (cp7==cp2 && cp4-cp2 > 3){
983 int ww;
985 (*term.t_rev)(cp1->a); /* set inverse for this char */
986 o_insert((UCS) cp1->c); /* insert the char */
987 ww = wcellwidth((UCS) cp1->c);
988 ttcol += (ww >= 0 ? ww : 1);
989 display = FALSE; /* only do it once!! */
992 else if(TERM_DELCHAR && cp3 != cp1 && cp7[0].c == cp6[-1].c
993 && cp7[0].a == cp6[-1].a){
994 while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
995 --cp7;
996 --cp6;
999 if (cp6==cp1 && cp5-cp6 > 3){
1000 int w;
1002 w = wcellwidth((UCS) cp7[0].c);
1003 w = (w >= 0 ? w : 1);
1004 while(w-- > 0) /* in case double-width char */
1005 o_delete(); /* delete the char */
1006 display = FALSE; /* only do it once!! */
1011 if(cp1 != cp5 && display){
1012 int w1, w2;
1015 * If we need to copy characters from cp1 to cp2 and
1016 * we need to display them, then we have to worry about
1017 * the characters that we are replacing being of a different
1018 * width than the new characters, else the display may be
1019 * messed up.
1021 * If the new width (w1) is less than the old width, that means
1022 * we will leave behind some old remnants if we aren't careful.
1023 * If the new width is larger than the old width, we have to
1024 * make sure we draw the characters all the way to the end
1025 * in order to get it right. Take advantage of clear to end
1026 * of line if we have it.
1028 w1 = cellwidth_ptr_to_ptr(cp1, cp3);
1029 w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
1031 if(w1 < w2 || (nbflag && w1 != w2)){
1032 if(TERM_EOLEXIST){
1033 if(nbflag){
1035 * Draw all of the characters starting with cp1
1036 * until we get to all spaces, then clear to the end of
1037 * line from there. Watch out we don't run over the
1038 * right hand edge, which shouldn't happen.
1040 * Set cp5 to the first of the repeating spaces.
1042 cp5 = &vline[term.t_ncol];
1043 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1044 --cp5;
1048 * In the !nbflag case we want spaces from cp5 on.
1049 * Setting cp3 to something different from cp5 triggers
1050 * the clear to end of line below.
1052 if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
1053 cleartoeol++;
1055 else{
1056 int w;
1059 * No peeol so draw all the way to the edge whether they
1060 * are spaces or not.
1062 cp3 = &vline[0];
1063 for(w = 0; w < term.t_ncol; cp3++){
1064 int ww;
1066 ww = wcellwidth((UCS) cp3->c);
1067 w += (ww >= 0 ? ww : 1);
1070 cp5 = cp3;
1075 while (cp1 != cp5) { /* Ordinary. */
1076 int ww;
1078 if(display){
1079 (*term.t_rev)(cp1->a); /* set inverse for this char */
1080 (*term.t_putchar)(cp1->c);
1083 ww = wcellwidth((UCS) cp1->c);
1084 ttcol += (ww >= 0 ? ww : 1);
1086 *cp2++ = *cp1++;
1089 (*term.t_rev)(0); /* turn off inverse anyway! */
1091 if (cp5 != cp3 || cleartoeol) { /* Erase. */
1092 if(display)
1093 peeol();
1094 else
1095 while (cp1 != cp3)
1096 *cp2++ = *cp1++;
1099 *flags &= ~VFCHG; /* flag this line is changed */
1104 * Redisplay the mode line for the window pointed to by the "wp". This is the
1105 * only routine that has any idea of how the modeline is formatted. You can
1106 * change the modeline format by hacking at this routine. Called by "update"
1107 * any time there is a dirty window.
1109 void
1110 modeline(WINDOW *wp)
1112 if(Pmaster){
1113 if(ComposerEditing)
1114 ShowPrompt();
1115 else{
1116 menu_compose[EXIT_KEY].label = (Pmaster->headents)
1117 ? N_("Send") :N_("Exit");
1118 menu_compose[PSTPN_KEY].name = (Pmaster->headents)
1119 ? "^O" : NULL;
1120 menu_compose[PSTPN_KEY].label = (Pmaster->headents)
1121 ? N_("Postpone") : NULL;
1122 menu_compose[WHERE_KEY].name = (Pmaster->alt_ed) ? "^_" : "^W";
1123 menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? N_("Alt Edit")
1124 : N_("Where is");
1125 KS_OSDATASET(&menu_compose[WHERE_KEY],
1126 (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
1127 menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? N_("UnJustify")
1128 : N_("UnCut Text");
1129 wkeyhelp(menu_compose);
1132 else{
1133 BUFFER *bp;
1134 char t1[NLINE], t2[NLINE], t3[NLINE], tline[NLINE];
1135 int w1, w2, w3, w1_to_2, w2_to_3, w3_to_r;
1136 UCS *ucs;
1138 vtmove(1, 0);
1139 vteeol();
1140 vscreen[0]->v_flag |= VFCHG; /* Redraw next time. */
1141 vtmove(0, 0); /* Seek to right line. */
1143 snprintf(t1, sizeof(t1), PICO_TITLE, version); /* write version */
1145 bp = wp->w_bufp;
1146 if(bp->b_fname[0]) /* File name? */
1147 snprintf(t2, sizeof(t2), "File: %s", bp->b_fname);
1148 else{
1149 strncpy(t2, PICO_NEWBUF_MSG, sizeof(t2));
1150 t2[sizeof(t2)-1] = '\0';
1153 if(bp->b_flag&BFCHG){ /* "MOD" if changed. */
1154 strncpy(t3, PICO_MOD_MSG, sizeof(t3));
1155 t3[sizeof(t3)-1] = '\0';
1157 else
1158 t3[0] = '\0';
1160 #define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
1161 #define ALLBUTSPACE (w1+w2+w3+w3_to_r)
1163 w1 = utf8_width(t1);
1164 w2 = utf8_width(t2);
1165 w3 = utf8_width(t3);
1166 w1_to_2 = w2_to_3 = 1; /* min values for separation */
1167 w3_to_r = 2;
1169 if(ALLOFTHEM <= term.t_ncol){ /* everything fits */
1170 w1_to_2 = (term.t_ncol - ALLBUTSPACE)/2;
1171 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1173 else{
1174 w1 = 2;
1175 w1_to_2 = 0;
1176 if(ALLOFTHEM <= term.t_ncol)
1177 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1178 else{
1179 w1 = w1_to_2 = w3_to_r = 0;
1180 if(ALLOFTHEM <= term.t_ncol)
1181 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1182 else{
1183 if(bp->b_fname[0]){
1184 snprintf(t2, sizeof(t2), "%s", bp->b_fname);
1185 w2 = utf8_width(t2);
1188 if(ALLOFTHEM <= term.t_ncol)
1189 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1190 else{
1191 w2 = 8;
1192 if(bp->b_fname[0] && ALLOFTHEM <= term.t_ncol){
1193 /* reduce size of file */
1194 w2 = term.t_ncol - (ALLOFTHEM - w2);
1195 t2[0] = t2[1] = t2[2] = '.';
1196 utf8_to_width_rhs(t2+3, bp->b_fname, sizeof(t2)-3, w2-3);
1197 w2 = utf8_width(t2);
1199 else
1200 w2 = utf8_width(t2);
1202 if(ALLOFTHEM <= term.t_ncol)
1203 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1204 else{
1205 w1 = w1_to_2 = w2 = w2_to_3 = w3_to_r = 0;
1206 if(ALLOFTHEM <= term.t_ncol)
1207 w2_to_3 = term.t_ncol - ALLBUTSPACE;
1208 else
1209 w3 = 0;
1216 utf8_snprintf(tline, sizeof(tline),
1217 "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
1218 w1, w1, t1,
1219 w1_to_2, w1_to_2, "",
1220 w2, w2, t2,
1221 w2_to_3, w2_to_3, "",
1222 w3, w3, t3,
1223 w3_to_r, w3_to_r, "");
1225 ucs = NULL;
1226 if(utf8_width(tline) <= term.t_ncol)
1227 ucs = utf8_to_ucs4_cpystr(tline);
1229 if(ucs){
1230 UCS *ucsp;
1231 CELL c;
1233 c.a = 1;
1234 ucsp = ucs;
1235 while((c.c = CELLMASK & *ucsp++))
1236 vtputc(c);
1238 fs_give((void **) &ucs);
1246 * Send a command to the terminal to move the hardware cursor to row "row"
1247 * and column "col". The row and column arguments are origin 0. Optimize out
1248 * random calls. Update "ttrow" and "ttcol".
1250 void
1251 movecursor(int row, int col)
1253 if (row!=ttrow || col!=ttcol) {
1254 ttrow = row;
1255 ttcol = col;
1256 (*term.t_move)(MIN(MAX(row,0),term.t_nrow), MIN(MAX(col,0),term.t_ncol-1));
1262 * Erase any sense we have of the cursor's HW location...
1264 void
1265 clearcursor(void)
1267 ttrow = ttcol = FARAWAY;
1270 void
1271 get_cursor(int *row, int *col)
1273 if(row)
1274 *row = ttrow;
1275 if(col)
1276 *col = ttcol;
1281 * Erase the message line. This is a special routine because the message line
1282 * is not considered to be part of the virtual screen. It always works
1283 * immediately; the terminal buffer is flushed via a call to the flusher.
1285 void
1286 mlerase(void)
1288 if (term.t_nrow < term.t_mrow)
1289 return;
1291 movecursor(term.t_nrow - term.t_mrow, 0);
1292 (*term.t_rev)(0);
1293 if (TERM_EOLEXIST == TRUE)
1294 peeol();
1295 else{
1296 if(ttrow == term.t_nrow){
1297 while(ttcol++ < term.t_ncol-1)
1298 (*term.t_putchar)(' ');
1300 else{
1301 while(ttcol++ < term.t_ncol) /* track's ttcol */
1302 (*term.t_putchar)(' ');
1306 (*term.t_flush)();
1307 mpresf = FALSE;
1312 mlyesno_utf8(char *utf8prompt, int dflt)
1314 int ret;
1315 UCS *prompt;
1317 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1319 ret = mlyesno(prompt, dflt);
1321 if(prompt)
1322 fs_give((void **) &prompt);
1324 return(ret);
1329 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
1330 * ABORT. The ABORT status is returned if the user bumps out of the question
1331 * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
1332 * is no default.
1335 mlyesno(UCS *prompt, int dflt)
1337 int rv;
1338 UCS buf[NLINE], lbuf[10];
1339 KEYMENU menu_yesno[12];
1340 COLOR_PAIR *lastc = NULL;
1342 #ifdef _WINDOWS
1343 if (mswin_usedialog ())
1344 switch (mswin_yesno (prompt)) {
1345 default:
1346 case 0: return (ABORT);
1347 case 1: return (TRUE);
1348 case 2: return (FALSE);
1350 #endif
1352 for(rv = 0; rv < 12; rv++){
1353 menu_yesno[rv].name = NULL;
1354 KS_OSDATASET(&menu_yesno[rv], KS_NONE);
1357 menu_yesno[1].name = "Y";
1358 menu_yesno[1].label = (dflt == TRUE) ? "[" N_("Yes") "]" : N_("Yes");
1359 menu_yesno[6].name = "^C";
1360 menu_yesno[6].label = N_("Cancel");
1361 menu_yesno[7].name = "N";
1362 menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No");
1363 wkeyhelp(menu_yesno); /* paint generic menu */
1364 sgarbk = TRUE; /* mark menu dirty */
1365 if(Pmaster && curwp)
1366 curwp->w_flag |= WFMODE;
1368 ucs4_strncpy(buf, prompt, NLINE);
1369 buf[NLINE-1] = '\0';
1370 lbuf[0] = ' '; lbuf[1] = '?'; lbuf[2] = ' '; lbuf[3] = '\0';
1371 ucs4_strncat(buf, lbuf, NLINE - ucs4_strlen(buf) - 1);
1372 buf[NLINE-1] = '\0';
1373 mlwrite(buf, NULL);
1374 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1375 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1376 lastc = pico_get_cur_color();
1377 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1379 else
1380 (*term.t_rev)(1);
1382 rv = -1;
1383 while(1){
1384 switch(GetKey()){
1385 case (CTRL|'M') : /* default */
1386 if(dflt >= 0){
1387 pputs_utf8((dflt) ? _("Yes") : _("No"), 1);
1388 rv = dflt;
1390 else
1391 (*term.t_beep)();
1393 break;
1395 case (CTRL|'C') : /* Bail out! */
1396 case F2 :
1397 pputs_utf8(_("ABORT"), 1);
1398 rv = ABORT;
1399 break;
1401 case 'y' :
1402 case 'Y' :
1403 case F3 :
1404 pputs_utf8(_("Yes"), 1);
1405 rv = TRUE;
1406 break;
1408 case 'n' :
1409 case 'N' :
1410 case F4 :
1411 pputs_utf8(_("No"), 1);
1412 rv = FALSE;
1413 break;
1415 case (CTRL|'G') :
1416 if(term.t_mrow == 0 && km_popped == 0){
1417 movecursor(term.t_nrow-2, 0);
1418 peeol();
1419 term.t_mrow = 2;
1420 if(lastc){
1421 (void) pico_set_colorp(lastc, PSC_NONE);
1422 free_color_pair(&lastc);
1424 else
1425 (*term.t_rev)(0);
1427 wkeyhelp(menu_yesno); /* paint generic menu */
1428 mlwrite(buf, NULL);
1429 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1430 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1431 lastc = pico_get_cur_color();
1432 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1434 else
1435 (*term.t_rev)(1);
1437 sgarbk = TRUE; /* mark menu dirty */
1438 km_popped++;
1439 break;
1441 /* else fall through */
1443 default:
1444 (*term.t_beep)();
1445 case NODATA :
1446 break;
1449 (*term.t_flush)();
1450 if(rv != -1){
1451 if(lastc){
1452 (void) pico_set_colorp(lastc, PSC_NONE);
1453 free_color_pair(&lastc);
1455 else
1456 (*term.t_rev)(0);
1458 if(km_popped){
1459 term.t_mrow = 0;
1460 movecursor(term.t_nrow, 0);
1461 peeol();
1462 sgarbf = 1;
1463 km_popped = 0;
1466 return(rv);
1473 * Write a prompt into the message line, then read back a response. Keep
1474 * track of the physical position of the cursor. If we are in a keyboard
1475 * macro throw the prompt away, and return the remembered response. This
1476 * lets macros run at full speed. The reply is always terminated by a carriage
1477 * return. Handle erase, kill, and abort keys.
1480 mlreply_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1482 return(mlreplyd_utf8(utf8prompt, utf8buf, nbuf, flg|QDEFLT, extras));
1487 mlreply(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
1489 return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
1494 * function key mappings
1496 static UCS rfkm[12][2] = {
1497 { F1, (CTRL|'G')},
1498 { F2, (CTRL|'C')},
1499 { F3, 0 },
1500 { F4, 0 },
1501 { F5, 0 },
1502 { F6, 0 },
1503 { F7, 0 },
1504 { F8, 0 },
1505 { F9, 0 },
1506 { F10, 0 },
1507 { F11, 0 },
1508 { F12, 0 }
1513 mlreplyd_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1515 int ret;
1516 UCS *b, *buf;
1517 char *utf8;
1518 UCS *prompt;
1520 buf = (UCS *) fs_get(nbuf * sizeof(*b));
1521 b = utf8_to_ucs4_cpystr(utf8buf);
1522 if(b){
1523 ucs4_strncpy(buf, b, nbuf);
1524 buf[nbuf-1] = '\0';
1525 fs_give((void **) &b);
1528 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1530 ret = mlreplyd(prompt, buf, nbuf, flg, extras);
1532 utf8 = ucs4_to_utf8_cpystr(buf);
1533 if(utf8){
1534 strncpy(utf8buf, utf8, nbuf);
1535 utf8buf[nbuf-1] = '\0';
1536 fs_give((void **) &utf8);
1539 if(buf)
1540 fs_give((void **) &buf);
1542 if(prompt)
1543 fs_give((void **) &prompt);
1545 return(ret);
1549 void
1550 writeachar(UCS ucs)
1552 pputc(ucs, 0);
1557 * mlreplyd - write the prompt to the message line along with a default
1558 * answer already typed in. Carriage return accepts the
1559 * default. answer returned in buf which also holds the initial
1560 * default, nbuf is its length, def set means use default value.
1563 mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
1565 UCS c; /* current char */
1566 UCS *b; /* pointer in buf */
1567 int i, j;
1568 int plen;
1569 int changed = FALSE;
1570 int return_val = 0;
1571 KEYMENU menu_mlreply[12];
1572 UCS extra_v[12];
1573 struct display_line dline;
1574 COLOR_PAIR *lastc = NULL;
1576 #ifdef _WINDOWS
1577 if(mswin_usedialog()){
1578 MDlgButton btn_list[12];
1579 LPTSTR free_names[12];
1580 LPTSTR free_labels[12];
1581 int i, j;
1583 memset(&free_names, 0, sizeof(LPTSTR) * 12);
1584 memset(&free_labels, 0, sizeof(LPTSTR) * 12);
1585 memset(&btn_list, 0, sizeof (MDlgButton) * 12);
1586 j = 0;
1587 for(i = 0; extras && extras[i].name != NULL; ++i) {
1588 if(extras[i].label[0] != '\0') {
1589 if((extras[i].key & CTRL) == CTRL)
1590 btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
1591 else
1592 btn_list[j].ch = extras[i].key;
1594 btn_list[j].rval = extras[i].key;
1595 free_names[j] = utf8_to_lptstr(extras[i].name);
1596 btn_list[j].name = free_names[j];
1597 free_labels[j] = utf8_to_lptstr(extras[i].label);
1598 btn_list[j].label = free_labels[j];
1599 j++;
1603 btn_list[j].ch = -1;
1605 return_val = mswin_dialog(prompt, buf, nbuf, ((flg&QDEFLT) > 0),
1606 FALSE, btn_list, NULL, 0);
1608 if(return_val == 3)
1609 return_val = HELPCH;
1611 for(i = 0; i < 12; i++){
1612 if(free_names[i])
1613 fs_give((void **) &free_names[i]);
1614 if(free_labels[i])
1615 fs_give((void **) &free_labels[i]);
1618 return(return_val);
1620 #endif
1622 menu_mlreply[0].name = "^G";
1623 menu_mlreply[0].label = N_("Get Help");
1624 KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
1625 for(j = 0, i = 1; i < 6; i++){ /* insert odd extras */
1626 menu_mlreply[i].name = NULL;
1627 KS_OSDATASET(&menu_mlreply[i], KS_NONE);
1628 rfkm[2*i][1] = 0;
1629 if(extras){
1630 for(; extras[j].name && j != 2*(i-1); j++)
1633 if(extras[j].name){
1634 rfkm[2*i][1] = extras[j].key;
1635 menu_mlreply[i].name = extras[j].name;
1636 menu_mlreply[i].label = extras[j].label;
1637 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
1642 menu_mlreply[6].name = "^C";
1643 menu_mlreply[6].label = N_("Cancel");
1644 KS_OSDATASET(&menu_mlreply[6], KS_NONE);
1645 for(j = 0, i = 7; i < 12; i++){ /* insert even extras */
1646 menu_mlreply[i].name = NULL;
1647 rfkm[2*(i-6)+1][1] = 0;
1648 if(extras){
1649 for(; extras[j].name && j != (2*(i-6)) - 1; j++)
1652 if(extras[j].name){
1653 rfkm[2*(i-6)+1][1] = extras[j].key;
1654 menu_mlreply[i].name = extras[j].name;
1655 menu_mlreply[i].label = extras[j].label;
1656 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
1661 /* set up what to watch for and return values */
1662 memset(extra_v, 0, sizeof(extra_v));
1663 for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
1664 extra_v[j++] = extras[i].key;
1666 plen = mlwrite(prompt, NULL); /* paint prompt */
1668 if(!(flg&QDEFLT))
1669 *buf = '\0';
1671 dline.vused = ucs4_strlen(buf);
1672 dline.dwid = term.t_ncol - plen;
1673 dline.row = term.t_nrow - term.t_mrow;
1674 dline.col = plen;
1676 dline.dlen = 2 * dline.dwid + 100;
1678 dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
1679 dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
1680 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
1681 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
1683 dline.movecursor = movecursor;
1684 dline.writechar = writeachar;
1686 dline.vl = buf;
1687 dline.vlen = nbuf-1;
1688 dline.vbase = 0;
1690 b = &buf[(flg & QBOBUF) ? 0 : ucs4_strlen(buf)];
1692 wkeyhelp(menu_mlreply); /* paint generic menu */
1694 sgarbk = 1; /* mark menu dirty */
1696 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1697 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1698 lastc = pico_get_cur_color();
1699 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1701 else
1702 (*term.t_rev)(1);
1704 for(;;){
1706 line_paint(b-buf, &dline, NULL);
1707 (*term.t_flush)();
1709 #ifdef MOUSE
1710 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
1711 register_mfunc(mouse_in_content,
1712 term.t_nrow - term.t_mrow, plen,
1713 term.t_nrow - term.t_mrow, term.t_ncol-1);
1714 #endif
1715 #ifdef _WINDOWS
1716 mswin_allowpaste(MSWIN_PASTE_LINE);
1717 #endif
1718 while((c = GetKey()) == NODATA)
1721 #ifdef MOUSE
1722 clear_mfunc(mouse_in_content);
1723 #endif
1724 #ifdef _WINDOWS
1725 mswin_allowpaste(MSWIN_PASTE_DISABLE);
1726 #endif
1728 switch(c = normalize_cmd(c, rfkm, 1)){
1729 case (CTRL|'A') : /* CTRL-A beginning */
1730 case KEY_HOME :
1731 b = buf;
1732 continue;
1734 case (CTRL|'B') : /* CTRL-B back a char */
1735 case KEY_LEFT:
1736 if(b <= buf)
1737 (*term.t_beep)();
1738 else
1739 b--;
1741 continue;
1743 case (CTRL|'C') : /* CTRL-C abort */
1744 pputs_utf8(_("ABORT"), 1);
1745 ctrlg(FALSE, 0);
1746 return_val = ABORT;
1747 goto ret;
1749 case (CTRL|'E') : /* CTRL-E end of line */
1750 case KEY_END :
1751 b = &buf[ucs4_strlen(buf)];
1752 continue;
1754 case (CTRL|'F') : /* CTRL-F forward a char*/
1755 case KEY_RIGHT :
1756 if(*b == '\0')
1757 (*term.t_beep)();
1758 else
1759 b++;
1761 continue;
1763 case (CTRL|'G') : /* CTRL-G help */
1764 if(term.t_mrow == 0 && km_popped == 0){
1765 movecursor(term.t_nrow-2, 0);
1766 peeol();
1767 sgarbk = 1; /* mark menu dirty */
1768 km_popped++;
1769 term.t_mrow = 2;
1770 if(lastc){
1771 (void) pico_set_colorp(lastc, PSC_NONE);
1772 free_color_pair(&lastc);
1774 else
1775 (*term.t_rev)(0);
1777 wkeyhelp(menu_mlreply); /* paint generic menu */
1778 plen = mlwrite(prompt, NULL); /* paint prompt */
1779 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1780 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1781 lastc = pico_get_cur_color();
1782 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1784 else
1785 (*term.t_rev)(1);
1787 pputs(buf, 1);
1788 break;
1791 pputs_utf8(_("HELP"), 1);
1792 return_val = HELPCH;
1793 goto ret;
1795 case (CTRL|'H') : /* CTRL-H backspace */
1796 case 0x7f : /* rubout */
1797 if (b <= buf){
1798 (*term.t_beep)();
1799 break;
1801 else
1802 b--;
1804 case (CTRL|'D') : /* CTRL-D delete char */
1805 case KEY_DEL :
1806 if (!*b){
1807 (*term.t_beep)();
1808 break;
1811 changed=TRUE;
1812 i = 0;
1813 dline.vused--;
1814 do /* blat out left char */
1815 b[i] = b[i+1];
1816 while(b[i++] != '\0');
1817 break;
1819 case (CTRL|'L') : /* CTRL-L redraw */
1820 return_val = (CTRL|'L');
1821 goto ret;
1823 case (CTRL|'K') : /* CTRL-K kill line */
1824 changed=TRUE;
1825 buf[0] = '\0';
1826 dline.vused = 0;
1827 b = buf;
1828 break;
1830 case F1 : /* sort of same thing */
1831 return_val = HELPCH;
1832 goto ret;
1834 case (CTRL|'M') : /* newline */
1835 return_val = changed;
1836 goto ret;
1838 #ifdef MOUSE
1839 case KEY_MOUSE :
1841 MOUSEPRESS mp;
1843 mouse_get_last (NULL, &mp);
1845 /* The clicked line have anything special on it? */
1846 switch(mp.button){
1847 case M_BUTTON_LEFT : /* position cursor */
1848 mp.col -= plen; /* normalize column */
1849 if(mp.col >= 0 && mp.col <= ucs4_strlen(buf))
1850 b = buf + mp.col;
1852 break;
1854 case M_BUTTON_RIGHT :
1855 #ifdef _WINDOWS
1856 mswin_allowpaste(MSWIN_PASTE_LINE);
1857 mswin_paste_popup();
1858 mswin_allowpaste(MSWIN_PASTE_DISABLE);
1859 break;
1860 #endif
1862 case M_BUTTON_MIDDLE : /* NO-OP for now */
1863 default: /* just ignore */
1864 break;
1868 continue;
1869 #endif
1871 default :
1873 /* look for match in extra_v */
1874 for(i = 0; i < 12; i++)
1875 if(c && c == extra_v[i]){
1876 return_val = c;
1877 goto ret;
1880 changed=TRUE;
1882 if(c & (CTRL | FUNC)){ /* bag ctrl_special chars */
1883 (*term.t_beep)();
1885 else{
1886 i = ucs4_strlen(b);
1887 if(flg&QNODQT){ /* reject double quotes? */
1888 if(c == '"'){
1889 (*term.t_beep)();
1890 continue;
1894 if(dline.vused >= nbuf-1){
1895 (*term.t_beep)();
1896 continue;
1899 do /* blat out left char */
1900 b[i+1] = b[i];
1901 while(i-- > 0);
1903 dline.vused++;
1904 *b++ = c;
1909 ret:
1910 if(lastc){
1911 (void) pico_set_colorp(lastc, PSC_NONE);
1912 free_color_pair(&lastc);
1914 else
1915 (*term.t_rev)(0);
1917 (*term.t_flush)();
1919 if(km_popped){
1920 term.t_mrow = 0;
1921 movecursor(term.t_nrow, 0);
1922 peeol();
1923 sgarbf = 1;
1924 km_popped = 0;
1927 if(dline.dl)
1928 fs_give((void **) &dline.dl);
1930 if(dline.olddl)
1931 fs_give((void **) &dline.olddl);
1933 return(return_val);
1937 void
1938 emlwrite(char *utf8message, EML *eml)
1940 UCS *message;
1942 message = utf8_to_ucs4_cpystr(utf8message ? utf8message : "");
1944 emlwrite_ucs4(message, eml);
1946 if(message)
1947 fs_give((void **) &message);
1952 * emlwrite() - write the message string to the error half of the screen
1953 * center justified. much like mlwrite (which is still used
1954 * to paint the line for prompts and such), except it center
1955 * the text.
1957 void
1958 emlwrite_ucs4(UCS *message, EML *eml)
1960 UCS *bufp, *ap;
1961 int width;
1962 COLOR_PAIR *lastc = NULL;
1964 mlerase();
1966 if(!(message && *message) || term.t_nrow < 2)
1967 return; /* nothing to write or no space to write, bag it */
1969 bufp = message;
1971 width = ucs4_str_width(message);
1974 * next, figure out where the to move the cursor so the message
1975 * comes out centered
1977 if((ap=ucs4_strchr(message, '%')) != NULL){
1978 width -= 2;
1979 switch(ap[1]){
1980 case '%':
1981 case 'c':
1982 width += (eml && eml->c) ? wcellwidth(eml->c) : 1;
1983 break;
1984 case 'd':
1985 width += dumbroot(eml ? eml->d : 0, 10);
1986 break;
1987 case 'D':
1988 width += dumblroot(eml ? eml->l : 0L, 10);
1989 break;
1990 case 'o':
1991 width += dumbroot(eml ? eml->d : 0, 8);
1992 break;
1993 case 'x':
1994 width += dumbroot(eml ? eml->d : 0, 16);
1995 break;
1996 case 's': /* string arg is UTF-8 */
1997 width += (eml && eml->s) ? utf8_width(eml->s) : 2;
1998 break;
2002 if(width+4 <= term.t_ncol)
2003 movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (width + 4))/2);
2004 else
2005 movecursor(term.t_nrow-term.t_mrow, 0);
2007 if(Pmaster && Pmaster->colors && Pmaster->colors->stcp
2008 && pico_is_good_colorpair(Pmaster->colors->stcp)){
2009 lastc = pico_get_cur_color();
2010 (void) pico_set_colorp(Pmaster->colors->stcp, PSC_NONE);
2012 else
2013 (*term.t_rev)(1);
2015 pputs_utf8("[ ", 1);
2016 while (*bufp != '\0' && ttcol < term.t_ncol-2){
2017 if(*bufp == '\007')
2018 (*term.t_beep)();
2019 else if(*bufp == '%'){
2020 switch(*++bufp){
2021 case 'c':
2022 if(eml && eml->c)
2023 pputc(eml->c, 0);
2024 else {
2025 pputs_utf8("%c", 0);
2027 break;
2028 case 'd':
2029 mlputi(eml ? eml->d : 0, 10);
2030 break;
2031 case 'D':
2032 mlputli(eml ? eml->l : 0L, 10);
2033 break;
2034 case 'o':
2035 mlputi(eml ? eml->d : 0, 16);
2036 break;
2037 case 'x':
2038 mlputi(eml ? eml->d : 0, 8);
2039 break;
2040 case 's':
2041 pputs_utf8((eml && eml->s) ? eml->s : "%s", 0);
2042 break;
2043 case '%':
2044 default:
2045 pputc(*bufp, 0);
2046 break;
2049 else
2050 pputc(*bufp, 0);
2051 bufp++;
2054 pputs_utf8(" ]", 1);
2056 if(lastc){
2057 (void) pico_set_colorp(lastc, PSC_NONE);
2058 free_color_pair(&lastc);
2060 else
2061 (*term.t_rev)(0);
2063 (*term.t_flush)();
2065 mpresf = TRUE;
2070 mlwrite_utf8(char *utf8fmt, void *arg)
2072 UCS *fmt;
2073 int ret;
2075 fmt = utf8_to_ucs4_cpystr(utf8fmt ? utf8fmt : "");
2076 ret = mlwrite(fmt, arg);
2077 if(fmt)
2078 fs_give((void **) &fmt);
2080 return(ret);
2085 * Write a message into the message line. Keep track of the physical cursor
2086 * position. A small class of printf like format items is handled. Assumes the
2087 * stack grows down; this assumption is made by the "++" in the argument scan
2088 * loop. Set the "message line" flag TRUE.
2091 mlwrite(UCS *fmt, void *arg)
2093 int ret, ww;
2094 UCS c;
2095 char *ap;
2096 COLOR_PAIR *lastc = NULL;
2099 * the idea is to only highlight if there is something to show
2101 mlerase();
2102 movecursor(ttrow, 0);
2104 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
2105 && pico_is_good_colorpair(Pmaster->colors->prcp)){
2106 lastc = pico_get_cur_color();
2107 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
2109 else
2110 (*term.t_rev)(1);
2112 ap = (char *) &arg;
2114 while ((c = *fmt++) != 0) {
2115 if (c != '%') {
2116 pputc(c, 1);
2118 else {
2119 c = *fmt++;
2120 switch (c){
2121 case 'd':
2122 mlputi(*(int *)ap, 10);
2123 ap += sizeof(int);
2124 break;
2126 case 'o':
2127 mlputi(*(int *)ap, 8);
2128 ap += sizeof(int);
2129 break;
2131 case 'x':
2132 mlputi(*(int *)ap, 16);
2133 ap += sizeof(int);
2134 break;
2136 case 'D':
2137 mlputli(*(long *)ap, 10);
2138 ap += sizeof(long);
2139 break;
2141 case 's':
2142 pputs_utf8(*(char **)ap, 1);
2143 ap += sizeof(char *);
2144 break;
2146 default:
2147 pputc(c, 1);
2148 ww = wcellwidth(c);
2149 ttcol += (ww >= 0 ? ww : 1);
2154 ret = ttcol;
2155 while(ttcol < term.t_ncol)
2156 pputc(' ', 0);
2158 movecursor(term.t_nrow - term.t_mrow, ret);
2160 if(lastc){
2161 (void) pico_set_colorp(lastc, PSC_NONE);
2162 free_color_pair(&lastc);
2164 else
2165 (*term.t_rev)(0);
2167 (*term.t_flush)();
2168 mpresf = TRUE;
2170 return(ret);
2175 * Write out an integer, in the specified radix. Update the physical cursor
2176 * position. This will not handle any negative numbers; maybe it should.
2178 void
2179 mlputi(int i, int r)
2181 register int q;
2182 static char hexdigits[] = "0123456789ABCDEF";
2184 if (i < 0){
2185 i = -i;
2186 pputc('-', 1);
2189 q = i/r;
2191 if (q != 0)
2192 mlputi(q, r);
2194 pputc(hexdigits[i%r], 1);
2199 * do the same except as a long integer.
2201 void
2202 mlputli(long l, int r)
2204 register long q;
2206 if (l < 0){
2207 l = -l;
2208 pputc('-', 1);
2211 q = l/r;
2213 if (q != 0)
2214 mlputli(q, r);
2216 pputc((int)(l%r)+'0', 1);
2220 void
2221 unknown_command(UCS c)
2223 char buf[10], ch, *s;
2224 EML eml;
2226 buf[0] = '\0';
2227 s = buf;
2229 if(!c){
2230 /* fall through */
2232 else if(c & CTRL && c >= (CTRL|'@') && c <= (CTRL|'_')){
2233 ch = c - (CTRL|'@') + '@';
2234 snprintf(s, sizeof(buf), "^%c", ch);
2236 else
2237 switch(c){
2238 case ' ' : s = "SPACE"; break;
2239 case '\033' : s = "ESC"; break;
2240 case '\177' : s = "DEL"; break;
2241 case ctrl('I') : s = "TAB"; break;
2242 case ctrl('J') : s = "LINEFEED"; break;
2243 case ctrl('M') : s = "RETURN"; break;
2244 case ctrl('Q') : s = "XON"; break;
2245 case ctrl('S') : s = "XOFF"; break;
2246 case KEY_UP : s = "Up Arrow"; break;
2247 case KEY_DOWN : s = "Down Arrow"; break;
2248 case KEY_RIGHT : s = "Right Arrow"; break;
2249 case KEY_LEFT : s = "Left Arrow"; break;
2250 case CTRL|KEY_UP : s = "Ctrl-Up Arrow"; break;
2251 case CTRL|KEY_DOWN : s = "Ctrl-Down Arrow"; break;
2252 case CTRL|KEY_RIGHT : s = "Ctrl-Right Arrow"; break;
2253 case CTRL|KEY_LEFT : s = "Ctrl-Left Arrow"; break;
2254 case KEY_PGUP : s = "Prev Page"; break;
2255 case KEY_PGDN : s = "Next Page"; break;
2256 case KEY_HOME : s = "Home"; break;
2257 case KEY_END : s = "End"; break;
2258 case KEY_DEL : s = "Delete"; break; /* Not necessary DEL! */
2259 case F1 :
2260 case F2 :
2261 case F3 :
2262 case F4 :
2263 case F5 :
2264 case F6 :
2265 case F7 :
2266 case F8 :
2267 case F9 :
2268 case F10 :
2269 case F11 :
2270 case F12 :
2271 snprintf(s, sizeof(buf), "F%ld", (long) (c - PF1 + 1));
2272 break;
2274 default:
2275 if(c < CTRL)
2276 utf8_put((unsigned char *) s, (unsigned long) c);
2278 break;
2281 eml.s = s;
2282 emlwrite("Unknown Command: %s", &eml);
2283 (*term.t_beep)();
2288 * scrolldown - use stuff to efficiently move blocks of text on the
2289 * display, and update the pscreen array to reflect those
2290 * moves...
2292 * wp is the window to move in
2293 * r is the row at which to begin scrolling
2294 * n is the number of lines to scrol
2296 void
2297 scrolldown(WINDOW *wp, int r, int n)
2299 #ifdef TERMCAP
2300 register int i;
2301 register int l;
2302 register VIDEO *vp1;
2303 register VIDEO *vp2;
2305 if(!n)
2306 return;
2308 if(r < 0){
2309 r = wp->w_toprow;
2310 l = wp->w_ntrows;
2312 else{
2313 if(r > wp->w_toprow)
2314 vscreen[r-1]->v_flag |= VFCHG;
2315 l = wp->w_toprow+wp->w_ntrows-r;
2318 o_scrolldown(r, n);
2320 for(i=l-n-1; i >= 0; i--){
2321 vp1 = pscreen[r+i];
2322 vp2 = pscreen[r+i+n];
2323 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2325 pprints(r+n-1, r);
2326 ttrow = FARAWAY;
2327 ttcol = FARAWAY;
2328 #endif /* TERMCAP */
2333 * scrollup - use tcap stuff to efficiently move blocks of text on the
2334 * display, and update the pscreen array to reflect those
2335 * moves...
2337 void
2338 scrollup(WINDOW *wp, int r, int n)
2340 #ifdef TERMCAP
2341 register int i;
2342 register VIDEO *vp1;
2343 register VIDEO *vp2;
2345 if(!n)
2346 return;
2348 if(r < 0)
2349 r = wp->w_toprow;
2351 o_scrollup(r, n);
2353 i = 0;
2354 while(1){
2355 if(Pmaster){
2356 if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
2357 break;
2359 else{
2360 if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
2361 break;
2363 vp1 = pscreen[r+i+n];
2364 vp2 = pscreen[r+i];
2365 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2366 i++;
2368 pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
2369 ttrow = FARAWAY;
2370 ttcol = FARAWAY;
2371 #endif /* TERMCAP */
2376 * print spaces in the physical screen starting from row abs(n) working in
2377 * either the positive or negative direction (depending on sign of n).
2379 void
2380 pprints(int x, int y)
2382 register int i;
2383 register int j;
2385 if(x < y){
2386 for(i = x;i <= y; ++i){
2387 for(j = 0; j < term.t_ncol; j++){
2388 pscreen[i]->v_text[j].c = ' ';
2389 pscreen[i]->v_text[j].a = 0;
2393 else{
2394 for(i = x;i >= y; --i){
2395 for(j = 0; j < term.t_ncol; j++){
2396 pscreen[i]->v_text[j].c = ' ';
2397 pscreen[i]->v_text[j].a = 0;
2401 ttrow = y;
2402 ttcol = 0;
2407 * doton - return the physical line number that the dot is on in the
2408 * current window, and by side effect the number of lines remaining
2411 doton(int *r, unsigned *chs)
2413 register int i = 0;
2414 register LINE *lp = curwp->w_linep;
2415 int l = -1;
2417 assert(r != NULL && chs != NULL);
2419 *chs = 0;
2420 while(i++ < curwp->w_ntrows){
2421 if(lp == curwp->w_dotp)
2422 l = i-1;
2423 lp = lforw(lp);
2424 if(lp == curwp->w_bufp->b_linep){
2425 i++;
2426 break;
2428 if(l >= 0)
2429 (*chs) += llength(lp);
2431 *r = i - l - term.t_mrow;
2432 return(l+curwp->w_toprow);
2438 * resize_pico - given new window dimensions, allocate new resources
2441 resize_pico(int row, int col)
2443 int old_nrow, old_ncol;
2444 register int i;
2445 register VIDEO *vp;
2447 old_nrow = term.t_nrow;
2448 old_ncol = term.t_ncol;
2450 term.t_nrow = row;
2451 term.t_ncol = col;
2453 if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
2454 return(TRUE);
2456 if(curwp){
2457 curwp->w_toprow = 2;
2458 curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
2461 if(Pmaster){
2462 fillcol = Pmaster->fillcolumn;
2463 (*Pmaster->resize)();
2465 else if(userfillcol > 0)
2466 fillcol = userfillcol;
2467 else
2468 fillcol = term.t_ncol - 6; /* we control the fill column */
2471 * free unused screen space ...
2473 for(i=term.t_nrow+1; i <= old_nrow; ++i){
2474 free((char *) vscreen[i]);
2475 free((char *) pscreen[i]);
2479 * realloc new space for screen ...
2481 if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2482 if(Pmaster)
2483 return(-1);
2484 else
2485 exit(1);
2488 if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2489 if(Pmaster)
2490 return(-1);
2491 else
2492 exit(1);
2495 for (i = 0; i <= term.t_nrow; ++i) {
2496 if(i <= old_nrow)
2497 vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2498 else
2499 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2501 if (vp == NULL)
2502 exit(1);
2503 vp->v_flag = VFCHG;
2504 vscreen[i] = vp;
2505 if(old_ncol < term.t_ncol){ /* don't let any garbage in */
2506 vtrow = i;
2507 vtcol = (i < old_nrow) ? old_ncol : 0;
2508 vteeol();
2511 if(i <= old_nrow)
2512 vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2513 else
2514 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2516 if (vp == NULL)
2517 exit(1);
2519 vp->v_flag = VFCHG;
2520 pscreen[i] = vp;
2523 if(!ResizeBrowser()){
2524 if(Pmaster && Pmaster->headents){
2525 ResizeHeader();
2527 else{
2528 curwp->w_flag |= (WFHARD | WFMODE);
2529 pico_refresh(0, 1); /* redraw whole enchilada. */
2530 update(); /* do it */
2534 return(TRUE);
2537 void
2538 redraw_pico_for_callback(void)
2540 pico_refresh(0, 1);
2541 update();
2546 * showCompTitle - display the anchor line passed in from pine
2548 void
2549 showCompTitle(void)
2551 if(Pmaster){
2552 UCS *bufp;
2553 extern UCS *pico_anchor;
2554 COLOR_PAIR *lastc = NULL;
2556 if((bufp = pico_anchor) == NULL)
2557 return;
2559 movecursor(COMPOSER_TITLE_LINE, 0);
2560 if (Pmaster->colors && Pmaster->colors->tbcp &&
2561 pico_is_good_colorpair(Pmaster->colors->tbcp)){
2562 lastc = pico_get_cur_color();
2563 (void)pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2565 else
2566 (*term.t_rev)(1);
2568 while (ttcol < term.t_ncol)
2569 if(*bufp != '\0')
2570 pputc(*bufp++, 1);
2571 else
2572 pputc(' ', 1);
2574 if (lastc){
2575 (void)pico_set_colorp(lastc, PSC_NONE);
2576 free_color_pair(&lastc);
2578 else
2579 (*term.t_rev)(0);
2581 movecursor(COMPOSER_TITLE_LINE + 1, 0);
2582 peeol();
2589 * zotdisplay - blast malloc'd space created for display maps
2591 void
2592 zotdisplay(void)
2594 register int i;
2596 for (i = 0; i <= term.t_nrow; ++i){ /* free screens */
2597 free((char *) vscreen[i]);
2598 free((char *) pscreen[i]);
2601 free((char *) vscreen);
2602 free((char *) pscreen);
2608 * nlforw() - returns the number of lines from the top to the dot
2611 nlforw(void)
2613 register int i = 0;
2614 register LINE *lp = curwp->w_linep;
2616 while(lp != curwp->w_dotp){
2617 lp = lforw(lp);
2618 i++;
2620 return(i);
2626 * pputc - output the given char, keep track of it on the physical screen
2627 * array, and keep track of the cursor
2629 void
2630 pputc(UCS c, /* char to write */
2631 int a) /* and its attribute */
2633 int ind, width, printable_ascii = 0;
2636 * This is necessary but not sufficient to allow us to draw. Note that
2637 * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
2638 * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
2640 if((ttcol >= 0 && ttcol < term.t_ncol) && (ttrow >= 0 && ttrow <= term.t_nrow)){
2643 * Width is the number of screen columns a character will occupy.
2645 if(c < 0x80 && isprint(c)){
2646 printable_ascii++;
2647 width = 1;
2649 else
2650 width = wcellwidth(c);
2652 if(width < 0)
2653 width = 1; /* will be a '?' */
2655 if(ttcol + width <= term.t_ncol){ /* it fits */
2657 * Some terminals scroll when you write in the lower right corner
2658 * of the screen, so don't write there.
2660 if(!(ttrow == term.t_nrow && ttcol+width == term.t_ncol)){
2661 (*term.t_putchar)(c); /* write it */
2662 ind = index_from_col(ttrow, ttcol);
2663 pscreen[ttrow]->v_text[ind].c = c; /* keep track of it */
2664 pscreen[ttrow]->v_text[ind].a = a; /* keep track of it */
2667 else{
2669 * Character overlaps right edge of screen. Hopefully the higher
2670 * layers will prevent this but we're making sure.
2672 * We may want to do something like writing a space character
2673 * into the cells that are on the screen. We'll see.
2677 ttcol = MIN(term.t_ncol, ttcol+width);
2683 * pputs - print a string and keep track of the cursor
2685 void
2686 pputs(UCS *s, /* string to write */
2687 int a) /* and its attribute */
2689 while (*s != '\0')
2690 pputc(*s++, a);
2694 void
2695 pputs_utf8(char *s, int a)
2697 UCS *ucsstr = NULL;
2699 if(s && *s){
2700 ucsstr = utf8_to_ucs4_cpystr(s);
2701 if(ucsstr){
2702 pputs(ucsstr, a);
2703 fs_give((void **) &ucsstr);
2710 * peeol - physical screen array erase to end of the line. remember to
2711 * track the cursor.
2713 void
2714 peeol(void)
2716 int i, width = 0, ww;
2717 CELL cl;
2719 if(ttrow < 0 || ttrow > term.t_nrow)
2720 return;
2722 cl.c = ' ';
2723 cl.a = 0;
2726 * Don't clear if we think we are sitting past the last column,
2727 * that erases the last column if we just wrote it.
2729 if(ttcol < term.t_ncol)
2730 (*term.t_eeol)();
2733 * Because the characters are variable width it's a little tricky
2734 * to erase the rest of the line. What we do is add up the
2735 * widths of the characters until we reach ttcol
2736 * then set the rest to the space character.
2738 for(i = 0; i < term.t_ncol && width < ttcol; i++){
2739 ww = wcellwidth((UCS) pscreen[ttrow]->v_text[i].c);
2740 width += (ww >= 0 ? ww : 1);
2743 while(i < term.t_ncol)
2744 pscreen[ttrow]->v_text[i++] = cl;
2749 * pscr - return the character cell on the physical screen map on the
2750 * given line, l, and offset, o.
2752 CELL *
2753 pscr(int l, int o)
2755 if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
2756 return(&(pscreen[l]->v_text[o]));
2757 else
2758 return(NULL);
2763 * pclear() - clear the physical screen from row x through row y (inclusive)
2764 * row is zero origin, min row = 0 max row = t_nrow
2765 * Clear whole screen -- pclear(0, term.t_nrow)
2766 * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
2767 * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
2769 void
2770 pclear(int x, int y)
2772 register int i;
2774 x = MIN(MAX(0, x), term.t_nrow);
2775 y = MIN(MAX(0, y), term.t_nrow);
2777 for(i=x; i <= y; i++){
2778 movecursor(i, 0);
2779 peeol();
2785 * dumbroot - just get close
2788 dumbroot(int x, int b)
2790 if(x < b)
2791 return(1);
2792 else
2793 return(dumbroot(x/b, b) + 1);
2798 * dumblroot - just get close
2801 dumblroot(long x, int b)
2803 if(x < b)
2804 return(1);
2805 else
2806 return(dumblroot(x/b, b) + 1);
2811 * pinsertc - use optimized insert, fixing physical screen map.
2812 * returns true if char written, false otherwise
2815 pinsert(CELL c)
2817 int i, ind = 0, ww;
2818 CELL *p;
2820 if(ttrow < 0 || ttrow > term.t_nrow)
2821 return(0);
2823 if(o_insert((UCS) c.c)){ /* if we've got it, use it! */
2824 p = pscreen[ttrow]->v_text; /* then clean up physical screen */
2826 ind = index_from_col(ttrow, ttcol);
2828 for(i = term.t_ncol-1; i > ind; i--)
2829 p[i] = p[i-1]; /* shift right */
2831 p[ind] = c; /* insert new char */
2833 ww = wcellwidth((UCS) c.c);
2834 ttcol += (ww >= 0 ? ww : 1);
2836 return(1);
2839 return(0);
2844 * pdel - use optimized delete to rub out the current char and
2845 * fix the physical screen array.
2846 * returns true if optimized the delete, false otherwise
2849 pdel(void)
2851 int i, ind = 0, w;
2852 CELL *p;
2854 if(ttrow < 0 || ttrow > term.t_nrow)
2855 return(0);
2857 if(TERM_DELCHAR){ /* if we've got it, use it! */
2858 p = pscreen[ttrow]->v_text;
2859 ind = index_from_col(ttrow, ttcol);
2861 if(ind > 0){
2862 --ind;
2863 w = wcellwidth((UCS) p[ind].c);
2864 w = (w >= 0 ? w : 1);
2865 ttcol -= w;
2867 for(i = 0; i < w; i++){
2868 (*term.t_putchar)('\b'); /* move left a char */
2869 o_delete(); /* and delete it */
2872 /* then clean up physical screen */
2873 for(i=ind; i < term.t_ncol-1; i++)
2874 p[i] = p[i+1];
2876 p[i].c = ' ';
2877 p[i].a = 0;
2880 return(1);
2883 return(0);
2889 * wstripe - write out the given string at the given location, and reverse
2890 * video on flagged characters. Does the same thing as pine's
2891 * stripe.
2893 * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
2894 * only if you want to put the tildes before multi-byte chars.
2896 void
2897 wstripe(int line, int column, char *utf8pmt, int key)
2899 UCS *ucs4pmt, *u;
2900 int i = 0, col = 0;
2901 int j = 0;
2902 int l, ww;
2903 COLOR_PAIR *lastc = NULL;
2904 COLOR_PAIR *kncp = NULL;
2905 COLOR_PAIR *klcp = NULL;
2907 if(line < 0 || line > term.t_nrow)
2908 return;
2910 if (Pmaster && Pmaster->colors){
2911 if(pico_is_good_colorpair(Pmaster->colors->klcp))
2912 klcp = Pmaster->colors->klcp;
2914 if(klcp && pico_is_good_colorpair(Pmaster->colors->kncp))
2915 kncp = Pmaster->colors->kncp;
2917 lastc = pico_get_cur_color();
2920 ucs4pmt = utf8_to_ucs4_cpystr(utf8pmt);
2921 l = ucs4_strlen(ucs4pmt);
2922 while(1){
2923 if(i >= term.t_ncol || col >= term.t_ncol || j >= l)
2924 return; /* equal strings */
2926 if(ucs4pmt[j] == (UCS) key)
2927 j++;
2929 if (pscr(line, i) == NULL)
2930 return;
2932 if(pscr(line, i)->c != ucs4pmt[j]){
2933 if(j >= 1 && ucs4pmt[j-1] == (UCS) key)
2934 j--;
2935 break;
2938 ww = wcellwidth((UCS) pscr(line, i)->c);
2939 col += (ww >= 0 ? ww : 1);
2940 j++;
2941 i++;
2944 movecursor(line, column+col);
2945 if(klcp) (void)pico_set_colorp(klcp, PSC_NONE);
2946 u = &ucs4pmt[j];
2948 if(*u == (UCS) key){
2949 u++;
2950 if(kncp)
2951 (void)pico_set_colorp(kncp, PSC_NONE);
2952 else
2953 (void)(*term.t_rev)(1);
2955 pputc(*u, 1);
2956 if(kncp)
2957 (void)pico_set_colorp(klcp, PSC_NONE);
2958 else
2959 (void)(*term.t_rev)(0);
2961 else{
2962 pputc(*u, 0);
2965 while(*++u != '\0');
2967 if(ucs4pmt)
2968 fs_give((void **) &ucs4pmt);
2970 peeol();
2971 if (lastc){
2972 (void)pico_set_colorp(lastc, PSC_NONE);
2973 free_color_pair(&lastc);
2975 (*term.t_flush)();
2981 * wkeyhelp - paint list of possible commands on the bottom
2982 * of the display (yet another pine clone)
2983 * NOTE: function key mode is handled here since all the labels
2984 * are the same...
2986 * The KEYMENU definitions have names and labels defined as UTF-8 strings,
2987 * and wstripe expects UTF-8.
2989 void
2990 wkeyhelp(KEYMENU *keymenu)
2992 char *obufp, *p, fkey[4];
2993 char linebuf[2*NLINE]; /* "2" is for space for invert tokens */
2994 int row, slot, tspace, adjusted_tspace, nspace[6], index, n;
2995 #ifdef MOUSE
2996 char nbuf[NLINE];
2997 #endif
2999 #ifdef _WINDOWS
3000 pico_config_menu_items (keymenu);
3001 #endif
3003 if(term.t_mrow == 0)
3004 return;
3006 if(term.t_nrow < 1)
3007 return;
3010 * Calculate amount of space for the names column by column...
3012 for(index = 0; index < 6; index++)
3013 if(!(gmode&MDFKEY)){
3014 nspace[index] = (keymenu[index].name)
3015 ? utf8_width(keymenu[index].name) : 0;
3016 if(keymenu[index+6].name
3017 && (n = utf8_width(keymenu[index+6].name)) > nspace[index])
3018 nspace[index] = n;
3020 nspace[index]++;
3022 else
3023 nspace[index] = (index < 4) ? 3 : 4;
3025 tspace = term.t_ncol/6; /* total space for each item */
3028 * Avoid writing in bottom right corner so we won't scroll screens that
3029 * scroll when you do that. The way this is setup, we won't do that
3030 * unless the number of columns is evenly divisible by 6.
3032 adjusted_tspace = (6 * tspace == term.t_ncol) ? tspace - 1 : tspace;
3034 index = 0;
3035 for(row = 0; row <= 1; row++){
3036 linebuf[0] = '\0';
3037 obufp = &linebuf[0];
3038 for(slot = 0; slot < 6; slot++){
3039 if(keymenu[index].name && keymenu[index].label){
3040 size_t l;
3041 char this_label[200], tmp_label[200];
3043 if(keymenu[index].label[0] == '[' && keymenu[index].label[(l=strlen(keymenu[index].label))-1] == ']' && l > 2){
3044 strncpy(tmp_label, &keymenu[index].label[1], MIN(sizeof(tmp_label),l-2));
3045 tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
3046 snprintf(this_label, sizeof(this_label), "[%s]", _(tmp_label));
3048 else
3049 strncpy(this_label, _(keymenu[index].label), sizeof(this_label));
3051 this_label[sizeof(this_label)-1] = '\0';
3053 if(gmode&MDFKEY){
3054 p = fkey;
3055 snprintf(fkey, sizeof(fkey), "F%d", (2 * slot) + row + 1);
3057 else
3058 p = keymenu[index].name;
3059 #ifdef MOUSE
3060 snprintf(nbuf, sizeof(nbuf), "%.*s %s", nspace[slot], p, this_label);
3061 register_key(index,
3062 (gmode&MDFKEY) ? F1 + (2 * slot) + row:
3063 (keymenu[index].name[0] == '^')
3064 ? (CTRL | keymenu[index].name[1])
3065 : (keymenu[index].name[0] == 'S'
3066 && !strcmp(keymenu[index].name, "Spc"))
3067 ? ' '
3068 : keymenu[index].name[0],
3069 nbuf, invert_label,
3070 term.t_nrow - 1 + row, (slot * tspace),
3071 strlen(nbuf),
3072 (Pmaster && Pmaster->colors)
3073 ? Pmaster->colors->kncp: NULL,
3074 (Pmaster && Pmaster->colors)
3075 ? Pmaster->colors->klcp: NULL);
3076 #endif
3078 n = nspace[slot];
3079 while(p && *p && n--){
3080 *obufp++ = '~'; /* insert "invert" token */
3081 *obufp++ = *p++;
3084 while(n-- > 0)
3085 *obufp++ = ' ';
3087 p = this_label;
3088 n = ((slot == 5 && row == 1) ? adjusted_tspace
3089 : tspace) - nspace[slot];
3090 while(p && *p && n-- > 0)
3091 *obufp++ = *p++;
3093 while(n-- > 0)
3094 *obufp++ = ' ';
3096 else{
3097 n = (slot == 5 && row == 1) ? adjusted_tspace : tspace;
3098 while(n--)
3099 *obufp++ = ' ';
3101 #ifdef MOUSE
3102 register_key(index, NODATA, "", NULL, 0, 0, 0, NULL, NULL);
3103 #endif
3106 *obufp = '\0';
3107 index++;
3110 wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
3116 * This returns the screen width between pstart (inclusive) and
3117 * pend (exclusive) where the pointers point into an array of CELLs.
3119 unsigned
3120 cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend)
3122 CELL *p;
3123 unsigned width = 0;
3124 int ww;
3126 if(pstart)
3127 for(p = pstart; p < pend; p++){
3128 ww = wcellwidth((UCS) p->c);
3129 width += (ww >= 0 ? ww : 1);
3132 return(width);
3137 * This returns the virtual screen width in row from index a to b (exclusive).
3139 unsigned
3140 vcellwidth_a_to_b(int row, int a, int b)
3142 CELL *pstart, *pend;
3143 VIDEO *vp;
3145 if(row < 0 || row > term.t_nrow)
3146 return 0;
3148 if(a >= b)
3149 return 0;
3151 a = MIN(MAX(0, a), term.t_ncol-1);
3152 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3154 vp = vscreen[row];
3155 pstart = &vp->v_text[a];
3156 pend = &vp->v_text[b];
3158 return(cellwidth_ptr_to_ptr(pstart, pend));
3163 * This returns the physical screen width in row from index a to b (exclusive).
3165 unsigned
3166 pcellwidth_a_to_b(int row, int a, int b)
3168 CELL *pstart, *pend;
3169 VIDEO *vp;
3171 if(row < 0 || row > term.t_nrow)
3172 return 0;
3174 if(a >= b)
3175 return 0;
3177 a = MIN(MAX(0, a), term.t_ncol-1);
3178 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3180 vp = pscreen[row];
3181 pstart = &vp->v_text[a];
3182 pend = &vp->v_text[b];
3184 return(cellwidth_ptr_to_ptr(pstart, pend));
3189 index_from_col(int row, int col)
3191 CELL *p_start, *p_end, *p_limit;
3192 int w_consumed = 0, w, done = 0;
3194 if(row < 0 || row > term.t_nrow)
3195 return 0;
3197 p_end = p_start = pscreen[row]->v_text;
3198 p_limit = p_start + term.t_ncol;
3200 if(p_start)
3201 while(!done && p_end < p_limit && p_end->c && w_consumed <= col){
3202 w = wcellwidth((UCS) p_end->c);
3203 w = (w >= 0 ? w : 1);
3204 if(w_consumed + w <= col){
3205 w_consumed += w;
3206 ++p_end;
3208 else
3209 ++done;
3212 /* MIN and MAX just to be sure */
3213 return(MIN(MAX(0, p_end - p_start), term.t_ncol-1));
3216 #ifdef _WINDOWS
3218 void
3219 pico_config_menu_items (KEYMENU *keymenu)
3221 int i;
3222 KEYMENU *k;
3223 UCS key;
3225 mswin_menuitemclear ();
3227 /* keymenu's seem to be hardcoded at 12 entries. */
3228 for (i = 0, k = keymenu; i < 12; ++i, ++k) {
3229 if (k->name != NULL && k->label != NULL &&
3230 k->menuitem != KS_NONE) {
3232 if (k->name[0] == '^')
3233 key = CTRL | k->name[1];
3234 else if (strcmp(k->name, "Ret") == 0)
3235 key = '\r';
3236 else
3237 key = k->name[0];
3239 mswin_menuitemadd (key, k->label, k->menuitem, 0);
3245 * Update the scroll range and position. (exported)
3247 * This is where curbp->b_linecnt is really managed. With out this function
3248 * to count the number of lines when needed curbp->b_linecnt will never
3249 * really be correct. BUT, this function is only compiled into the
3250 * windows version, so b_linecnt will only ever be right in the windows
3251 * version. OK for now because that is the only version that
3252 * looks at b_linecnt.
3255 update_scroll (void)
3257 long scr_pos;
3258 long scr_range;
3259 LINE *lp;
3260 static LINE *last_top_line = NULL;
3261 static long last_scroll_pos = -1;
3264 if (ComposerEditing) {
3265 /* Editing header - don't allow scroll bars. */
3266 mswin_setscrollrange (0, 0);
3267 return(0);
3272 * Count the number of lines in the current bufer. Done when:
3274 * when told to recount: curbp->b_linecnt == -1
3275 * when the top line changed: curwp->w_linep != last_top_line
3276 * when we don't know the scroll pos: last_scroll_pos == -1
3278 * The first line in the list is a "place holder" line and is not
3279 * counted. The list is circular, when we return the to place
3280 * holder we have reached the end.
3282 if(curbp->b_linecnt == -1 || curwp->w_linep != last_top_line
3283 || last_scroll_pos == -1) {
3284 scr_range = 0;
3285 scr_pos = 0;
3286 for (lp = lforw (curbp->b_linep); lp != curbp->b_linep;
3287 lp = lforw (lp)) {
3288 if (lp == curwp->w_linep)
3289 scr_pos = scr_range;
3291 ++scr_range;
3294 curbp->b_linecnt = scr_range;
3295 last_scroll_pos = scr_pos;
3296 last_top_line = curwp->w_linep;
3300 * Set new scroll range and position.
3302 mswin_setscrollrange (curwp->w_ntrows - 2, curbp->b_linecnt - 1);
3303 mswin_setscrollpos (last_scroll_pos);
3304 return (0);
3306 #endif /* _WINDOWS */