Add support for tab-completion when selecting by rule
[alpine.git] / pico / display.c
blob468de2cedab0da1608d8c3206d3d14be48725806
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 * Program: Display functions
19 * The functions in this file handle redisplay. There are two halves, the
20 * ones that update the virtual display screen, and the ones that make the
21 * physical display screen the same as the virtual display screen. These
22 * functions use hints that are left in the windows by the commands.
26 #include "../c-client/mail.h"
27 #include "../c-client/utf8.h"
29 #ifdef _WINDOWS
30 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
31 #undef ERROR
32 #endif
34 #include "headers.h"
35 #include "../pith/charconv/filesys.h"
36 #include "../pith/charconv/utf8.h"
39 void vtmove(int, int);
40 void vtputc(CELL);
41 void vteeol(void);
42 void updateline(int, CELL *, CELL *, short *, int);
43 void updatelinecolor(int, CELL *, CELL *, short *, int);
44 void updext(void);
45 void mlputi(int, int);
46 void pprints(int, int);
47 void mlputli(long, int);
48 void showCompTitle(void);
49 int nlforw(void);
50 int dumbroot(int, int);
51 int dumblroot(long, int);
52 unsigned cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend);
53 unsigned vcellwidth_a_to_b(int row, int a, int b);
54 int window_signature_block(WINDOW *wp);
55 #ifdef _WINDOWS
56 void pico_config_menu_items (KEYMENU *);
57 int update_scroll (void);
58 #endif /* _WINDOWS */
62 * Standard pico keymenus...
64 static KEYMENU menu_pico[] = {
65 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^O", N_("WriteOut"), KS_SAVEFILE},
66 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
67 {"^K", N_("Cut Text"), KS_NONE}, {"^C", N_("Cur Pos"), KS_CURPOSITION},
68 {"^X", N_("Exit"), KS_EXIT}, {"^J", N_("Justify"), KS_JUSTIFY},
69 {"^W", N_("Where is"), KS_WHEREIS}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
70 {"^U", NULL, KS_NONE},
71 #ifdef SPELLER
72 {"^T", N_("To Spell"), KS_SPELLCHK}
73 #else
74 {"^D", N_("Del Char"), KS_NONE}
75 #endif
77 #define UNCUT_KEY 10
80 static KEYMENU menu_compose[] = {
81 {"^G", N_("Get Help"), KS_SCREENHELP}, {"^X", NULL, KS_SEND},
82 {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
83 {"^K", N_("Cut Text"), KS_NONE}, {"^O", N_("Postpone"), KS_POSTPONE},
84 /* TRANSLATORS: Justify is to reformat a paragraph automatically */
85 {"^C", N_("Cancel"), KS_CANCEL}, {"^J", N_("Justify"), KS_JUSTIFY},
86 {NULL, NULL, KS_NONE}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
87 {"^U", NULL, KS_NONE},
88 #ifdef SPELLER
89 {"^T", N_("To Spell"), KS_SPELLCHK}
90 #else
91 {"^D", N_("Del Char"), KS_NONE}
92 #endif
94 #define EXIT_KEY 1
95 #define PSTPN_KEY 5
96 #define WHERE_KEY 8
100 * Definition's for pico's modeline
102 #define PICO_TITLE " UW PICO %s"
103 #define PICO_MOD_MSG "Modified"
104 #define PICO_NEWBUF_MSG "New Buffer"
106 #define WFDEBUG 0 /* Window flag debug. */
108 #define VFCHG 0x0001 /* Changed flag */
109 #define VFEXT 0x0002 /* extended (beyond column 80) */
110 #define VFSIG 0x0004 /* in signature block */
112 int vtrow = 0; /* Row location of SW cursor */
113 int vtcol = 0; /* Column location of SW cursor */
114 int vtind = 0; /* Index into row array of SW cursor */
115 int ttrow = FARAWAY; /* Row location of HW cursor */
116 int ttcol = FARAWAY; /* Column location of HW cursor */
117 int lbound = 0; /* leftmost column of current line
118 being displayed */
120 VIDEO **vscreen; /* Virtual screen. */
121 VIDEO **pscreen; /* Physical screen. */
125 * Initialize the data structures used by the display code. The edge vectors
126 * used to access the screens are set up. The operating system's terminal I/O
127 * channel is set up. All the other things get initialized at compile time.
128 * The original window has "WFCHG" set, so that it will get completely
129 * redrawn on the first call to "update".
132 vtinit(void)
134 int i, j;
135 VIDEO *vp;
136 CELL ac;
138 ac.c = ' ';
139 ac.a = 0;
141 if(Pmaster == NULL)
142 vtterminalinfo(gmode & MDTCAPWINS);
144 (*term.t_open)();
146 (*term.t_rev)(FALSE);
147 vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
148 memset(vscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
149 if (vscreen == NULL){
150 emlwrite("Allocating memory for virtual display failed.", NULL);
151 return(FALSE);
154 pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
155 memset(pscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
156 if (pscreen == NULL){
157 free((void *)vscreen);
158 emlwrite("Allocating memory for physical display failed.", NULL);
159 return(FALSE);
163 for (i = 0; i <= term.t_nrow; ++i) {
164 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
166 if (vp == NULL){
167 free((void *)vscreen);
168 free((void *)pscreen);
169 emlwrite("Allocating memory for virtual display lines failed.",
170 NULL);
171 return(FALSE);
173 else
174 for(j = 0; j < term.t_ncol; j++)
175 vp->v_text[j] = ac;
177 vp->v_flag = 0;
178 vp->v_length = 0;
179 vscreen[i] = vp;
181 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
183 if (vp == NULL){
184 free((void *)vscreen[i]);
185 while(--i >= 0){
186 free((void *)vscreen[i]);
187 free((void *)pscreen[i]);
190 free((void *)vscreen);
191 free((void *)pscreen);
192 emlwrite("Allocating memory for physical display lines failed.",
193 NULL);
194 return(FALSE);
196 else
197 for(j = 0; j < term.t_ncol; j++)
198 vp->v_text[j] = ac;
200 vp->v_flag = 0;
201 vp->v_length = 0;
202 pscreen[i] = vp;
205 return(TRUE);
209 vtterminalinfo(int termcap_wins)
211 return((term.t_terminalinfo) ? (*term.t_terminalinfo)(termcap_wins)
212 : (Pmaster ? 0 : TRUE));
217 * Clean up the virtual terminal system, in anticipation for a return to the
218 * operating system. Move down to the last line and clear it out (the next
219 * system prompt will be written in the line). Shut down the channel to the
220 * terminal.
222 void
223 vttidy(void)
225 movecursor(term.t_nrow-1, 0);
226 peeol();
227 movecursor(term.t_nrow, 0);
228 peeol();
229 (*term.t_close)();
234 * Set the virtual cursor to the specified row and column on the virtual
235 * screen. There is no checking for nonsense values; this might be a good
236 * idea during the early stages.
238 void
239 vtmove(int row, int col)
241 vtrow = row;
242 vtcol = col;
244 if(vtcol < 0)
245 vtind = -1;
246 else if(vtcol == 0)
247 vtind = vtcol;
248 else{
250 * This is unused so don't worry about it.
252 assert(0);
258 * Write a character to the virtual screen. The virtual row and column are
259 * updated. If the line is too long put a "$" in the last column. This routine
260 * only puts printing characters into the virtual terminal buffers. Only
261 * column overflow is checked.
263 void
264 vtputc(CELL c)
266 VIDEO *vp;
267 CELL ac;
268 int w;
270 vp = vscreen[vtrow];
271 ac.c = ' ';
272 ac.a = c.a;
273 ac.d = c.d;
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;
371 window_signature_block(WINDOW *wp)
373 LINE *lp, *llp;
374 int in_sig, is_sig_start = 0;
375 int change = 0;
377 llp = wp->w_linep;
378 lp = lforw(wp->w_bufp->b_linep);
380 do {
381 in_sig = lback(lp) == wp->w_bufp->b_linep ? 0 : lback(lp)->l_sig;
382 if(in_sig == 0){
383 if(llength(lp) == 3){
384 if(lgetc(lp, 0).c == '-'
385 && lgetc(lp, 1).c == '-'
386 && lgetc(lp, 2).c == ' '){
387 in_sig = 1;
388 is_sig_start = 1;
391 } else {
392 if(lisblank(lp))
393 if(is_sig_start == 0) in_sig = 0;
394 is_sig_start = 0;
396 if(lp->l_sig != in_sig)
397 change++;
398 lp->l_sig = in_sig;
399 lp = lforw(lp);
400 } while(lp != wp->w_bufp->b_linep);
401 wp->w_linep = llp;
402 return change;
407 * Make sure that the display is right. This is a three part process. First,
408 * scan through all of the windows looking for dirty ones. Check the framing,
409 * and refresh the screen. Second, make sure that "currow" and "curcol" are
410 * correct for the current window. Third, make the virtual and physical
411 * screens the same.
413 void
414 update(void)
416 LINE *lp;
417 WINDOW *wp;
418 VIDEO *vp1;
419 VIDEO *vp2;
420 int i;
421 int j;
422 int scroll = 0;
423 int repaint= 0;
424 CELL c;
425 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
427 #if TYPEAH
428 if (typahead())
429 return;
430 #endif
432 #ifdef _WINDOWS
433 /* This tells our MS Windows module to not bother updating the
434 * cursor position while a massive screen update is in progress.
436 mswin_beginupdate ();
437 #endif
440 * BUG: setting and unsetting whole region at a time is dumb. fix this.
442 if(curwp->w_markp){
443 unmarkbuffer();
444 markregion(1);
447 wp = wheadp;
449 while (wp != NULL){
450 /* Look at any window with update flags set on. */
452 if(pcolors && (repaint = window_signature_block(wp))){
453 sgarbf = TRUE;
454 wp->w_flag |= WFEDIT | WFHARD;
456 if (wp->w_flag != 0){
457 /* If not force reframe, check the framing. */
459 if ((wp->w_flag & WFFORCE) == 0){
460 lp = wp->w_linep;
462 for (i = 0; i < wp->w_ntrows; ++i){
463 if (lp == wp->w_dotp)
464 goto out;
466 if (lp == wp->w_bufp->b_linep)
467 break;
469 lp = lforw(lp);
473 /* Not acceptable, better compute a new value for the line at the
474 * top of the window. Then set the "WFHARD" flag to force full
475 * redraw.
477 i = wp->w_force;
479 if (i > 0){
480 --i;
482 if (i >= wp->w_ntrows)
483 i = wp->w_ntrows-1;
485 else if (i < 0){
486 i += wp->w_ntrows;
488 if (i < 0)
489 i = 0;
491 else if(TERM_OPTIMIZE){
493 * find dotp, if its been moved just above or below the
494 * window, use scrollxxx() to facilitate quick redisplay...
496 lp = lforw(wp->w_dotp);
497 if(lp != wp->w_dotp){
498 if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
499 scroll = 1;
501 else {
502 lp = wp->w_linep;
503 for(j=0;j < wp->w_ntrows; ++j){
504 if(lp != wp->w_bufp->b_linep)
505 lp = lforw(lp);
506 else
507 break;
509 if(lp == wp->w_dotp && j == wp->w_ntrows)
510 scroll = 2;
513 j = i = wp->w_ntrows/2;
515 else
516 i = wp->w_ntrows/2;
518 lp = wp->w_dotp;
520 while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
521 --i;
522 lp = lback(lp);
526 * this is supposed to speed things up by using tcap sequences
527 * to efficiently scroll the terminal screen. the thinking here
528 * is that its much faster to update pscreen[] than to actually
529 * write the stuff to the screen...
531 if(TERM_OPTIMIZE){
532 switch(scroll){
533 case 1: /* scroll text down */
534 j = j-i+1; /* add one for dot line */
536 * do we scroll down the header as well? Well, only
537 * if we're not editing the header, we've backed up
538 * to the top, and the composer is not being
539 * displayed...
541 if(Pmaster && Pmaster->headents && !ComposerEditing
542 && (lback(lp) == wp->w_bufp->b_linep)
543 && (ComposerTopLine == COMPOSER_TOP_LINE))
544 j += entry_line(1000, TRUE); /* Never > 1000 headers */
546 scrolldown(wp, -1, j);
547 break;
548 case 2: /* scroll text up */
549 j = wp->w_ntrows - (j-i); /* we chose new top line! */
550 if(Pmaster && j){
552 * do we scroll down the header as well? Well, only
553 * if we're not editing the header, we've backed up
554 * to the top, and the composer is not being
555 * displayed...
557 if(!ComposerEditing
558 && (ComposerTopLine != COMPOSER_TOP_LINE))
559 scrollup(wp, COMPOSER_TOP_LINE,
560 j+entry_line(1000, TRUE));
561 else
562 scrollup(wp, -1, j);
564 else
565 scrollup(wp, -1, j);
566 break;
567 default :
568 break;
572 wp->w_linep = lp;
573 wp->w_flag |= WFHARD; /* Force full. */
574 out:
576 * if the line at the top of the page is the top line
577 * in the body, show the header...
579 if(Pmaster && Pmaster->headents && !ComposerEditing){
580 if(lback(wp->w_linep) == wp->w_bufp->b_linep){
581 if(ComposerTopLine == COMPOSER_TOP_LINE){
582 i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
583 if(i > 0 && nlforw() >= i) { /* room for header ? */
584 if((i = nlforw()/2) == 0 && term.t_nrow&1)
585 i = 1;
586 while(wp->w_linep != wp->w_bufp->b_linep && i--)
587 wp->w_linep = lforw(wp->w_linep);
590 else
591 ToggleHeader(1);
594 else{
595 if(ComposerTopLine != COMPOSER_TOP_LINE)
596 ToggleHeader(0); /* hide it ! */
600 /* Try to use reduced update. Mode line update has its own special
601 * flag. The fast update is used if the only thing to do is within
602 * the line editing.
604 lp = wp->w_linep;
605 i = wp->w_toprow;
607 if ((wp->w_flag & ~WFMODE) == WFEDIT){
608 while (lp != wp->w_dotp){
609 ++i;
610 lp = lforw(lp);
612 vscreen[i]->v_flag |= (lp->l_sig ? VFSIG : 0)| VFCHG;
613 /* compute physical length of line in screen */
614 vscreen[i]->v_length = 0;
615 for (j = 0; vscreen[i]->v_length < term.t_ncol
616 && j < llength(lp); ++j){
617 c = lgetc(lp, j);
618 if(c.c == '\t'){
619 vscreen[i]->v_length |= 0x07;
620 vscreen[i]->v_length++;
622 else if(ISCONTROL(c.c)){
623 vscreen[i]->v_length += 2;
625 else{
626 int w;
628 w = wcellwidth((UCS) c.c);
629 vscreen[i]->v_length += (w >= 0 ? w : 1);
632 vtmove(i, 0);
634 for (j = 0; j < llength(lp); ++j)
635 vtputc(lgetc(lp, j));
636 vteeol();
638 else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
639 while (i < wp->w_toprow+wp->w_ntrows){
640 vscreen[i]->v_flag |= (lp->l_sig ? VFSIG : 0 )| VFCHG;
641 /* compute physical length of line in screen */
642 vscreen[i]->v_length = 0;
643 for (j = 0; vscreen[i]->v_length < term.t_ncol
644 && j < llength(lp); ++j){
645 c = lgetc(lp, j);
646 if(c.c == '\t'){
647 vscreen[i]->v_length |= 0x07;
648 vscreen[i]->v_length++;
650 else if(ISCONTROL(c.c)){
651 vscreen[i]->v_length += 2;
653 else{
654 int w;
656 w = wcellwidth((UCS) c.c);
657 vscreen[i]->v_length += (w >= 0 ? w : 1);
660 vtmove(i, 0);
662 /* if line has been changed */
663 if (lp != wp->w_bufp->b_linep){
664 for (j = 0; j < llength(lp); ++j)
665 vtputc(lgetc(lp, j));
667 lp = lforw(lp);
670 vteeol();
671 ++i;
674 #if ~WFDEBUG
675 if ((wp->w_flag&WFMODE) != 0)
676 modeline(wp);
678 wp->w_flag = 0;
679 wp->w_force = 0;
680 #endif
682 #if WFDEBUG
683 modeline(wp);
684 wp->w_flag = 0;
685 wp->w_force = 0;
686 #endif
688 /* and onward to the next window */
689 wp = wp->w_wndp;
692 /* Always recompute the row and column number of the hardware cursor. This
693 * is the only update for simple moves.
695 lp = curwp->w_linep;
696 currow = curwp->w_toprow;
698 while (lp != curwp->w_dotp){
699 ++currow;
700 lp = lforw(lp);
703 curcol = 0;
704 i = 0;
706 while (i < curwp->w_doto){
707 c = lgetc(lp, i++);
709 if(c.c == '\t'){
710 curcol |= 0x07;
711 ++curcol;
713 else if(ISCONTROL(c.c)){
714 curcol += 2;
716 else{
717 int w;
719 w = wcellwidth((UCS) c.c);
720 curcol += (w >= 0 ? w : 1);
724 if (curcol >= term.t_ncol) { /* extended line. */
725 /* flag we are extended and changed */
726 vscreen[currow]->v_flag |= VFEXT | VFCHG;
727 updext(); /* and output extended line */
728 } else
729 lbound = 0; /* not extended line */
731 /* make sure no lines need to be de-extended because the cursor is
732 * no longer on them
735 wp = wheadp;
737 while (wp != NULL) {
738 lp = wp->w_linep;
739 i = wp->w_toprow;
741 while (i < wp->w_toprow + wp->w_ntrows) {
742 if (vscreen[i]->v_flag & VFEXT) {
743 /* always flag extended lines as changed */
744 vscreen[i]->v_flag |= VFCHG;
745 if ((wp != curwp) || (lp != wp->w_dotp) ||
746 (curcol < term.t_ncol)) {
747 vtmove(i, 0);
748 for (j = 0; j < llength(lp); ++j)
749 vtputc(lgetc(lp, j));
750 vteeol();
752 /* this line no longer is extended */
753 vscreen[i]->v_flag &= ~VFEXT;
756 lp = lforw(lp);
757 ++i;
759 /* and onward to the next window */
760 wp = wp->w_wndp;
763 /* Special hacking if the screen is garbage. Clear the hardware screen,
764 * and update your copy to agree with it. Set all the virtual screen
765 * change bits, to force a full update.
768 if (sgarbf != FALSE){
769 if(Pmaster){
770 int rv;
772 showCompTitle();
774 if(ComposerTopLine != COMPOSER_TOP_LINE){
775 UpdateHeader(0); /* arrange things */
776 PaintHeader(COMPOSER_TOP_LINE, TRUE);
780 * since we're using only a portion of the screen and only
781 * one buffer, only clear enough screen for the current window
782 * which is to say the *only* window.
784 for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
785 movecursor(i, 0);
786 peeol();
787 vscreen[i]->v_flag |= VFCHG;
789 rv = (*Pmaster->showmsg)('X' & 0x1f); /* ctrl-L */
790 ttresize();
791 picosigs(); /* restore altered handlers */
792 if(rv) /* Did showmsg corrupt the display? */
793 PaintBody(0); /* Yes, repaint */
794 movecursor(wheadp->w_toprow, 0);
796 else{
797 c.c = ' ';
798 c.a = 0;
799 for (i = 0; i < term.t_nrow-term.t_mrow; i++){
800 vscreen[i]->v_flag |= VFCHG;
801 vp1 = pscreen[i];
802 for (j = 0; j < term.t_ncol; j++)
803 vp1->v_text[j] = c;
804 if(sgarbf == FALSE){
805 movecursor(i, 0);
806 term.t_eeol();
809 if(sgarbf != FALSE){
810 movecursor(0, 0); /* Erase the screen. */
811 (*term.t_eeop)();
815 sgarbf = FALSE; /* Erase-page clears */
816 mpresf = FALSE; /* the message area. */
818 if(Pmaster)
819 modeline(curwp);
820 else
821 sgarbk = TRUE; /* fix the keyhelp as well...*/
824 /* Make sure that the physical and virtual displays agree. Unlike before,
825 * the "updateline" code is only called with a line that has been updated
826 * for sure.
828 if(Pmaster)
829 i = curwp->w_toprow;
830 else
831 i = 0;
833 if (term.t_nrow > term.t_mrow)
834 c.c = term.t_nrow - term.t_mrow;
835 else
836 c.c = 0;
838 for (; i < (int)c.c; ++i){
840 vp1 = vscreen[i];
842 /* for each line that needs to be updated, or that needs its
843 reverse video status changed, call the line updater */
844 j = vp1->v_flag;
845 if (j & VFCHG){
847 #if TYPEAH
848 if (typahead()){
849 #ifdef _WINDOWS
850 mswin_endupdate ();
851 #endif
852 return;
854 #endif
855 vp2 = pscreen[i];
857 updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag, vp1->v_length);
861 if(Pmaster == NULL){
863 if(sgarbk != FALSE){
864 if(term.t_mrow > 0){
865 movecursor(term.t_nrow-1, 0);
866 peeol();
867 movecursor(term.t_nrow, 0);
868 peeol();
871 if(lastflag&CFFILL){
872 /* TRANSLATORS: UnJustify means undo the previous
873 Justify command. */
874 menu_pico[UNCUT_KEY].label = N_("UnJustify");
875 if(!(lastflag&CFFLBF)){
876 emlwrite(_("Can now UnJustify!"), NULL);
877 mpresf = FARAWAY; /* remove this after next keystroke! */
880 else
881 menu_pico[UNCUT_KEY].label = N_("UnCut Text");
883 wkeyhelp(menu_pico);
884 sgarbk = FALSE;
888 if(lastflag&CFFLBF){
889 emlwrite(_("Can now UnJustify!"), NULL);
890 mpresf = FARAWAY; /* remove this after next keystroke! */
893 /* Finally, update the hardware cursor and flush out buffers. */
895 movecursor(currow, curcol - lbound);
896 #ifdef _WINDOWS
897 mswin_endupdate ();
900 * Update the scroll bars. This function is where curbp->b_linecnt
901 * is really managed. See update_scroll.
903 update_scroll ();
904 #endif
905 (*term.t_flush)();
909 /* updext - update the extended line which the cursor is currently
910 * on at a column greater than the terminal width. The line
911 * will be scrolled right or left to let the user see where
912 * the cursor is
914 void
915 updext(void)
917 int rcursor; /* real cursor location */
918 LINE *lp; /* pointer to current line */
919 int j; /* index into line */
920 int w = 0;
921 int ww;
924 * Calculate what column the real cursor will end up in.
925 * The cursor will be in the rcursor'th column. So if we're
926 * counting columns 0 1 2 3 and rcursor is 8, then rcursor
927 * will be over cell 7.
929 * What this effectively does is to scroll the screen as we're
930 * moving to the right when the cursor first passes off the
931 * screen's right edge. It would be nice if it did the same
932 * thing coming back to the left. Instead, in order that the
933 * screen's display depends only on the curcol and not on
934 * how we got there, the screen scrolls when we pass the
935 * t_margin column. It's also kind of funky that you can't
936 * see the character under the $ but you can delete it.
938 rcursor = ((curcol - term.t_ncol) % (term.t_ncol - term.t_margin + 1)) + term.t_margin;
939 lbound = curcol - rcursor + 1;
942 * Make sure lbound is set so that a double-width character does
943 * not straddle the boundary. If it does, move over one cell.
945 lp = curwp->w_dotp; /* line to output */
946 for (j=0; j<llength(lp) && w < lbound; ++j){
947 ww = wcellwidth((UCS) lgetc(lp, j).c);
948 w += (ww >= 0 ? ww : 1);
951 if(w > lbound)
952 lbound = w;
955 /* scan through the line outputting characters to the virtual screen
956 * once we reach the left edge
958 vtmove(currow, -lbound); /* start scanning offscreen */
959 for (j=0; j<llength(lp); ++j) /* until the end-of-line */
960 vtputc(lgetc(lp, j));
962 /* truncate the virtual line */
963 vteeol();
965 /* and put a '$' in column 1, may have to adjust curcol */
966 w = wcellwidth((UCS) vscreen[currow]->v_text[0].c);
967 vscreen[currow]->v_text[0].c = '$';
968 vscreen[currow]->v_text[0].a = 0;
969 if(w == 2){
971 * We want to put $ in the first two columns so that it
972 * takes up the right amount of space, but that means we
973 * have to scoot the real characters over one slot.
975 for (j = term.t_ncol-1; j >= 2; --j)
976 vscreen[currow]->v_text[j] = vscreen[currow]->v_text[j-1];
978 vscreen[currow]->v_text[1].c = '$';
979 vscreen[currow]->v_text[1].a = 0;
983 /* update line color, to be executed to update lines when color is on */
984 void
985 updatelinecolor (int row, CELL vline[], CELL pline[], short *flags, int len)
987 CELL *cp1, *cp2, *cp3, *cp4, *cp5;
988 int nbflag; /* non-blanks to the right flag? */
989 int cleartoeol = 0;
990 int in_quote, level;
991 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
992 COLOR_PAIR *lastc = NULL, *pcolor = NULL;
993 int first = 1, lastattr = -1, change = 0;
995 if(pcolors == NULL){
996 updateline(row, vline, pline, flags, len);
997 return;
1000 nbflag = FALSE;
1001 lastc = pico_get_cur_color();
1003 /* set up pointers to virtual and physical lines */
1004 cp1 = &vline[0];
1005 cp2 = &pline[0];
1006 cp3 = &vline[term.t_ncol];
1007 cp4 = &pline[term.t_ncol];
1009 if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
1010 while (cp3 != cp1 && cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
1011 --cp3;
1012 --cp4;
1013 if (cp3[0].c != ' ' || cp3[0].a != 0) /* Note if any nonblank */
1014 nbflag = TRUE; /* in right match. */
1017 cp5 = cp3;
1019 if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
1020 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1021 --cp5;
1023 if (cp3-cp5 <= 3) /* Use only if erase is */
1024 cp5 = cp3; /* fewer characters. */
1027 /* go to start of line */
1028 movecursor(row, 0);
1030 if(cp1 != cp5){
1031 int w1, w2;
1033 w1 = cellwidth_ptr_to_ptr(cp1, cp3);
1034 w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
1036 if(w1 < w2 || (nbflag && w1 != w2)){
1037 if(TERM_EOLEXIST){
1038 if(nbflag){
1040 * Draw all of the characters starting with cp1
1041 * until we get to all spaces, then clear to the end of
1042 * line from there. Watch out we don't run over the
1043 * right hand edge, which shouldn't happen.
1045 * Set cp5 to the first of the repeating spaces.
1047 cp5 = &vline[term.t_ncol];
1048 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1049 --cp5;
1053 * In the !nbflag case we want spaces from cp5 on.
1054 * Setting cp3 to something different from cp5 triggers
1055 * the clear to end of line below.
1057 if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
1058 cleartoeol++;
1060 else{
1061 int w;
1064 * No peeol so draw all the way to the edge whether they
1065 * are spaces or not.
1067 cp3 = &vline[0];
1068 for(w = 0; w < term.t_ncol && cp3 < &vline[term.t_ncol]; cp3++){
1069 int ww;
1071 ww = wcellwidth((UCS) cp3->c);
1072 w += (ww >= 0 ? ww : 1);
1075 cp5 = cp3;
1080 if(row != 0 && len < term.t_ncol)
1081 cp5 = cp1 + len;
1083 in_quote = 1;
1084 level = -1;
1085 while (cp1 != cp5){ /* Ordinary. */
1086 int ww;
1088 if(lastattr < 0){
1089 lastattr = cp1->a;
1090 change = 0;
1092 else
1093 change = lastattr != cp1->a;
1094 if(first != 0){
1095 first = 0;
1096 if(row == 0)
1097 pico_set_colorp(pcolors->tbcp, PSC_NONE);
1098 else if(row < term.t_nrow - 2)
1099 pcolor = (*flags & VFSIG) ? pcolors->sbcp : pcolors->ntcp;
1101 if(cp1->c != '>' && cp1->c != ' ')
1102 in_quote = 0;
1103 else if (in_quote && cp1->c == '>' && pcolors != NULL)
1104 level = (level + 1) % 3;
1105 if(level >= 0){
1106 if(level == 0) pcolor = pcolors->qlcp;
1107 else if(level == 1) pcolor = pcolors->qllcp;
1108 else if(level == 2) pcolor = pcolors->qlllcp;
1110 if(cp1->a == 1)
1111 pcolor = pcolors->rtcp; /* pcolor = proposed color */
1112 if(change == 0)
1113 pico_set_colorp(pcolor, PSC_NONE);
1114 else
1115 (*term.t_rev)(cp1->a); /* set inverse for this char */
1117 if(change == 0)
1118 (*term.t_rev)(cp1->a); /* set inverse for this char */
1119 (*term.t_putchar)(cp1->c);
1121 ww = wcellwidth((UCS) cp1->c);
1122 ttcol += (ww >= 0 ? ww : 1);
1124 *cp2++ = *cp1++;
1127 if (lastc){
1128 (void)pico_set_colorp(lastc, PSC_NONE);
1129 free_color_pair(&lastc);
1132 (*term.t_rev)(0); /* turn off inverse anyway! */
1134 if (cp5 != cp3 || cleartoeol) /* Erase. */
1135 peeol();
1137 *flags &= ~(VFCHG|VFSIG); /* flag this line is changed */
1143 * Update a single line. This does not know how to use insert or delete
1144 * character sequences; we are using VT52 functionality. Update the physical
1145 * row and column variables.
1147 void
1148 updateline(int row, /* row on screen */
1149 CELL vline[], /* what we want it to end up as */
1150 CELL pline[], /* what it looks like now */
1151 short *flags,
1152 int len)
1154 CELL *cp1, *cp2, *cp3, *cp4, *cp5, *cp6, *cp7;
1155 int display = TRUE;
1156 int nbflag; /* non-blanks to the right flag? */
1157 int cleartoeol = 0;
1159 if(row < 0 || row > term.t_nrow)
1160 return;
1162 if((Pmaster && Pmaster->colors) || Pcolors){
1163 updatelinecolor(row, vline, pline, flags, len);
1164 return;
1167 /* set up pointers to virtual and physical lines */
1168 cp1 = &vline[0];
1169 cp2 = &pline[0];
1170 cp3 = &vline[term.t_ncol];
1172 /* advance past any common chars at the left */
1173 while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
1174 ++cp1;
1175 ++cp2;
1178 /* This can still happen, even though we only call this routine on changed
1179 * lines. A hard update is always done when a line splits, a massive
1180 * change is done, or a buffer is displayed twice. This optimizes out most
1181 * of the excess updating. A lot of computes are used, but these tend to
1182 * be hard operations that do a lot of update, so I don't really care.
1184 /* if both lines are the same, no update needs to be done */
1185 if (cp1 == cp3){
1186 *flags &= ~VFCHG; /* mark it clean */
1187 return;
1190 /* find out if there is a match on the right */
1191 nbflag = FALSE;
1192 cp3 = &vline[term.t_ncol];
1193 cp4 = &pline[term.t_ncol];
1195 if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
1196 while (cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
1197 --cp3;
1198 --cp4;
1199 if (cp3[0].c != ' ' || cp3[0].a != 0) /* Note if any nonblank */
1200 nbflag = TRUE; /* in right match. */
1203 cp5 = cp3;
1205 if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
1206 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1207 --cp5;
1209 if (cp3-cp5 <= 3) /* Use only if erase is */
1210 cp5 = cp3; /* fewer characters. */
1213 /* go to start of differences */
1214 movecursor(row, cellwidth_ptr_to_ptr(&vline[0], cp1));
1216 if (!nbflag) { /* use insert or del char? */
1217 cp6 = cp3;
1218 cp7 = cp4;
1220 if(TERM_INSCHAR
1221 &&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
1222 while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
1223 --cp7;
1224 --cp6;
1227 if (cp7==cp2 && cp4-cp2 > 3){
1228 int ww;
1230 (*term.t_rev)(cp1->a); /* set inverse for this char */
1231 o_insert((UCS) cp1->c); /* insert the char */
1232 ww = wcellwidth((UCS) cp1->c);
1233 ttcol += (ww >= 0 ? ww : 1);
1234 display = FALSE; /* only do it once!! */
1237 else if(TERM_DELCHAR && cp3 != cp1 && cp7[0].c == cp6[-1].c
1238 && cp7[0].a == cp6[-1].a){
1239 while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
1240 --cp7;
1241 --cp6;
1244 if (cp6==cp1 && cp5-cp6 > 3){
1245 int w;
1247 w = wcellwidth((UCS) cp7[0].c);
1248 w = (w >= 0 ? w : 1);
1249 while(w-- > 0) /* in case double-width char */
1250 o_delete(); /* delete the char */
1251 display = FALSE; /* only do it once!! */
1256 if(cp1 != cp5 && display){
1257 int w1, w2;
1260 * If we need to copy characters from cp1 to cp2 and
1261 * we need to display them, then we have to worry about
1262 * the characters that we are replacing being of a different
1263 * width than the new characters, else the display may be
1264 * messed up.
1266 * If the new width (w1) is less than the old width, that means
1267 * we will leave behind some old remnants if we aren't careful.
1268 * If the new width is larger than the old width, we have to
1269 * make sure we draw the characters all the way to the end
1270 * in order to get it right. Take advantage of clear to end
1271 * of line if we have it.
1273 w1 = cellwidth_ptr_to_ptr(cp1, cp3);
1274 w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
1276 if(w1 < w2 || (nbflag && w1 != w2)){
1277 if(TERM_EOLEXIST){
1278 if(nbflag){
1280 * Draw all of the characters starting with cp1
1281 * until we get to all spaces, then clear to the end of
1282 * line from there. Watch out we don't run over the
1283 * right hand edge, which shouldn't happen.
1285 * Set cp5 to the first of the repeating spaces.
1287 cp5 = &vline[term.t_ncol];
1288 while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
1289 --cp5;
1293 * In the !nbflag case we want spaces from cp5 on.
1294 * Setting cp3 to something different from cp5 triggers
1295 * the clear to end of line below.
1297 if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
1298 cleartoeol++;
1300 else{
1301 int w;
1304 * No peeol so draw all the way to the edge whether they
1305 * are spaces or not.
1307 cp3 = &vline[0];
1308 for(w = 0; w < term.t_ncol; cp3++){
1309 int ww;
1311 ww = wcellwidth((UCS) cp3->c);
1312 w += (ww >= 0 ? ww : 1);
1315 cp5 = cp3;
1320 while (cp1 != cp5) { /* Ordinary. */
1321 int ww;
1323 if(display){
1324 (*term.t_rev)(cp1->a); /* set inverse for this char */
1325 (*term.t_putchar)(cp1->c);
1328 ww = wcellwidth((UCS) cp1->c);
1329 ttcol += (ww >= 0 ? ww : 1);
1331 *cp2++ = *cp1++;
1334 (*term.t_rev)(0); /* turn off inverse anyway! */
1336 if (cp5 != cp3 || cleartoeol) { /* Erase. */
1337 if(display)
1338 peeol();
1339 else
1340 while (cp1 != cp3)
1341 *cp2++ = *cp1++;
1344 *flags &= ~VFCHG; /* flag this line is changed */
1349 * Redisplay the mode line for the window pointed to by the "wp". This is the
1350 * only routine that has any idea of how the modeline is formatted. You can
1351 * change the modeline format by hacking at this routine. Called by "update"
1352 * any time there is a dirty window.
1354 void
1355 modeline(WINDOW *wp)
1357 if(Pmaster){
1358 if(ComposerEditing)
1359 ShowPrompt();
1360 else{
1361 menu_compose[EXIT_KEY].label = (Pmaster->headents)
1362 ? N_("Send") :N_("Exit");
1363 menu_compose[PSTPN_KEY].name = (Pmaster->headents)
1364 ? "^O" : NULL;
1365 menu_compose[PSTPN_KEY].label = (Pmaster->headents)
1366 ? N_("Postpone") : NULL;
1367 menu_compose[WHERE_KEY].name = (Pmaster->alt_ed) ? "^_" : "^W";
1368 menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? N_("Alt Edit")
1369 : N_("Where is");
1370 KS_OSDATASET(&menu_compose[WHERE_KEY],
1371 (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
1372 menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? N_("UnJustify")
1373 : N_("UnCut Text");
1374 wkeyhelp(menu_compose);
1377 else{
1378 BUFFER *bp;
1379 char t1[NLINE], t2[NLINE], t3[NLINE], tline[NLINE];
1380 int w1, w2, w3, w1_to_2, w2_to_3, w3_to_r;
1381 UCS *ucs;
1383 vtmove(1, 0);
1384 vteeol();
1385 vscreen[0]->v_flag |= VFCHG; /* Redraw next time. */
1386 vtmove(0, 0); /* Seek to right line. */
1388 snprintf(t1, sizeof(t1), PICO_TITLE, version); /* write version */
1390 bp = wp->w_bufp;
1391 if(bp->b_fname[0]) /* File name? */
1392 snprintf(t2, sizeof(t2), "File: %.*s", (int) sizeof(t2) - 7, bp->b_fname);
1393 else{
1394 strncpy(t2, PICO_NEWBUF_MSG, sizeof(t2));
1395 t2[sizeof(t2)-1] = '\0';
1398 if(bp->b_flag&BFCHG){ /* "MOD" if changed. */
1399 strncpy(t3, PICO_MOD_MSG, sizeof(t3));
1400 t3[sizeof(t3)-1] = '\0';
1402 else
1403 t3[0] = '\0';
1405 #define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
1406 #define ALLBUTSPACE (w1+w2+w3+w3_to_r)
1408 w1 = utf8_width(t1);
1409 w2 = utf8_width(t2);
1410 w3 = utf8_width(t3);
1411 w1_to_2 = w2_to_3 = 1; /* min values for separation */
1412 w3_to_r = 2;
1414 if(ALLOFTHEM <= term.t_ncol){ /* everything fits */
1415 w1_to_2 = (term.t_ncol - ALLBUTSPACE)/2;
1416 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1418 else{
1419 w1 = 2;
1420 w1_to_2 = 0;
1421 if(ALLOFTHEM <= term.t_ncol)
1422 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1423 else{
1424 w1 = w1_to_2 = w3_to_r = 0;
1425 if(ALLOFTHEM <= term.t_ncol)
1426 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1427 else{
1428 if(bp->b_fname[0]){
1429 snprintf(t2, sizeof(t2), "%.*s", (int) sizeof(t2) - 1, bp->b_fname);
1430 w2 = utf8_width(t2);
1433 if(ALLOFTHEM <= term.t_ncol)
1434 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1435 else{
1436 w2 = 8;
1437 if(bp->b_fname[0] && ALLOFTHEM <= term.t_ncol){
1438 /* reduce size of file */
1439 w2 = term.t_ncol - (ALLOFTHEM - w2);
1440 t2[0] = t2[1] = t2[2] = '.';
1441 utf8_to_width_rhs(t2+3, bp->b_fname, sizeof(t2)-3, w2-3);
1442 w2 = utf8_width(t2);
1444 else
1445 w2 = utf8_width(t2);
1447 if(ALLOFTHEM <= term.t_ncol)
1448 w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
1449 else{
1450 w1 = w1_to_2 = w2 = w2_to_3 = w3_to_r = 0;
1451 if(ALLOFTHEM <= term.t_ncol)
1452 w2_to_3 = term.t_ncol - ALLBUTSPACE;
1453 else
1454 w3 = 0;
1461 utf8_snprintf(tline, sizeof(tline),
1462 "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
1463 w1, w1, t1,
1464 w1_to_2, w1_to_2, "",
1465 w2, w2, t2,
1466 w2_to_3, w2_to_3, "",
1467 w3, w3, t3,
1468 w3_to_r, w3_to_r, "");
1470 ucs = NULL;
1471 if(utf8_width(tline) <= term.t_ncol)
1472 ucs = utf8_to_ucs4_cpystr(tline);
1474 if(ucs){
1475 UCS *ucsp;
1476 CELL c;
1478 c.a = 1;
1479 ucsp = ucs;
1480 while((c.c = CELLMASK & *ucsp++))
1481 vtputc(c);
1483 fs_give((void **) &ucs);
1491 * Send a command to the terminal to move the hardware cursor to row "row"
1492 * and column "col". The row and column arguments are origin 0. Optimize out
1493 * random calls. Update "ttrow" and "ttcol".
1495 void
1496 movecursor(int row, int col)
1498 if (row!=ttrow || col!=ttcol) {
1499 ttrow = row;
1500 ttcol = col;
1501 (*term.t_move)(MIN(MAX(row,0),term.t_nrow), MIN(MAX(col,0),term.t_ncol-1));
1507 * Erase any sense we have of the cursor's HW location...
1509 void
1510 clearcursor(void)
1512 ttrow = ttcol = FARAWAY;
1515 void
1516 get_cursor(int *row, int *col)
1518 if(row)
1519 *row = ttrow;
1520 if(col)
1521 *col = ttcol;
1526 * Erase the message line. This is a special routine because the message line
1527 * is not considered to be part of the virtual screen. It always works
1528 * immediately; the terminal buffer is flushed via a call to the flusher.
1530 void
1531 mlerase(void)
1533 if (term.t_nrow < term.t_mrow)
1534 return;
1536 movecursor(term.t_nrow - term.t_mrow, 0);
1537 (*term.t_rev)(0);
1538 if (TERM_EOLEXIST == TRUE)
1539 peeol();
1540 else{
1541 if(ttrow == term.t_nrow){
1542 while(ttcol++ < term.t_ncol-1)
1543 (*term.t_putchar)(' ');
1545 else{
1546 while(ttcol++ < term.t_ncol) /* track's ttcol */
1547 (*term.t_putchar)(' ');
1551 (*term.t_flush)();
1552 mpresf = FALSE;
1555 /* returns the chosen dictionary. If one was already chosen
1556 * return that one
1558 char *
1559 speller_choice(char **sp_list, int *choice)
1561 int ch_dict = -1;
1562 int cnt;
1564 if(sp_list == NULL || sp_list[0] == NULL || sp_list[0][0] == '\0')
1565 return NULL;
1567 if(choice && *choice >= 0)
1568 return sp_list[*choice];
1570 for(cnt = 0; sp_list[cnt] != NULL && sp_list[cnt][0] != '\0'; cnt++)
1573 if(cnt > 10) /* only the first 10 dictionaries */
1574 cnt = 10;
1576 if(cnt == 1) /* only one dictionary? choose it! */
1577 ch_dict = 0;
1579 if(ch_dict > cnt - 1) /* choose again in case something changed */
1580 ch_dict = -1;
1582 if(ch_dict < 0){ /* not a choice yet? do one now! */
1583 int i;
1584 UCS *ucs4_prompt;
1585 EXTRAKEYS menu_dictionary[] = {
1586 {"0", NULL, '0'},
1587 {"1", NULL, '1'},
1588 {"2", NULL, '2'},
1589 {"3", NULL, '3'},
1590 {"4", NULL, '4'},
1591 {"5", NULL, '5'},
1592 {"6", NULL, '6'},
1593 {"7", NULL, '7'},
1594 {"8", NULL, '8'},
1595 {"9", NULL, '9'}
1598 for(i = 0; i < cnt; i++)
1599 menu_dictionary[i].label = sp_list[i];
1601 if(cnt < 10)
1602 menu_dictionary[cnt].name = NULL;
1604 /* write the prompt in utf8, and let internal functions translate it to ucs4 */
1605 ucs4_prompt = utf8_to_ucs4_cpystr(_("Choose Dictionary: "));
1607 i = mlchoose(ucs4_prompt, menu_dictionary);
1609 if(i >= '0' && i <= '9')
1610 ch_dict = i - '0';
1612 if (i == -2) /* user cancelled */
1613 ch_dict = -2;
1615 if(ucs4_prompt)
1616 fs_give((void **)&ucs4_prompt);
1618 else ch_dict = -1;
1620 if(choice)
1621 *choice = ch_dict;
1623 return ch_dict >= 0 ? sp_list[ch_dict] : NULL;
1626 /* just like mlreplyd, but user cannot fill a prompt */
1628 mlchoose(UCS *prompt, EXTRAKEYS *extras)
1630 UCS c;
1631 UCS buf[NLINE];
1632 int i;
1633 int return_val = 0;
1634 KEYMENU menu_choose[12];
1635 COLOR_PAIR *lastc = NULL;
1637 for(i = 0; i < 12; i++){
1638 menu_choose[i].name = NULL;
1639 KS_OSDATASET(&menu_choose[i], KS_NONE);
1642 menu_choose[0].name = "^G";
1643 menu_choose[0].label = N_("Get Help");
1644 KS_OSDATASET(&menu_choose[0], KS_SCREENHELP);
1646 menu_choose[6].name = "^C";
1647 menu_choose[6].label = N_("Cancel");
1648 KS_OSDATASET(&menu_choose[6], KS_NONE);
1650 for(i = 0; i < 10; i++){
1651 if((i % 2) == 0){
1652 menu_choose[i / 2 + 1].name = extras[i].name;
1653 menu_choose[i / 2 + 1].label = extras[i].label;
1655 else{
1656 menu_choose[(i + 13) / 2].name = extras[i].name;
1657 menu_choose[(i + 13) / 2].label = extras[i].label;
1660 wkeyhelp(menu_choose); /* paint generic menu */
1661 sgarbk = TRUE; /* mark menu dirty */
1662 if(Pmaster && curwp)
1663 curwp->w_flag |= WFMODE;
1665 ucs4_strncpy(buf, prompt, NLINE);
1666 buf[NLINE-1] = '\0';
1667 mlwrite(buf, NULL);
1668 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1669 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1670 lastc = pico_get_cur_color();
1671 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1673 else
1674 (*term.t_rev)(1);
1676 return_val = -1;
1677 while(1){
1678 c = GetKey();
1679 for(i = 0; i < 10 && extras[i].name != NULL && extras[i].key != c; i++)
1681 if(i < 10 && extras[i].name)
1682 return_val = c;
1683 else switch(c){
1684 case (CTRL|'C') : /* Bail out! */
1685 case F2 :
1686 pputs_utf8(_("Cancel"), 1);
1687 return_val = -2;
1688 break;
1690 case (CTRL|'G') :
1691 if(term.t_mrow == 0 && km_popped == 0){
1692 movecursor(term.t_nrow-2, 0);
1693 peeol();
1694 term.t_mrow = 2;
1695 if(lastc){
1696 (void) pico_set_colorp(lastc, PSC_NONE);
1697 free_color_pair(&lastc);
1699 else
1700 (*term.t_rev)(0);
1702 wkeyhelp(menu_choose); /* paint generic menu */
1703 mlwrite(buf, NULL);
1704 if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
1705 && pico_is_good_colorpair(Pmaster->colors->prcp)){
1706 lastc = pico_get_cur_color();
1707 (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
1709 else
1710 (*term.t_rev)(1);
1712 sgarbk = TRUE; /* mark menu dirty */
1713 km_popped++;
1714 break;
1716 /* else fall through */
1718 default:
1719 (*term.t_beep)();
1720 case NODATA :
1721 break;
1724 (*term.t_flush)();
1725 if (return_val != -1){ /* abort sets rv = -2, other return values are positive */
1726 if(lastc){
1727 (void) pico_set_colorp(lastc, PSC_NONE);
1728 free_color_pair(&lastc);
1730 else
1731 (*term.t_rev)(0);
1733 if(km_popped){
1734 term.t_mrow = 0;
1735 movecursor(term.t_nrow, 0);
1736 peeol();
1737 sgarbf = 1;
1738 km_popped = 0;
1741 return(return_val);
1748 mlyesno_utf8(char *utf8prompt, int dflt)
1750 int ret;
1751 UCS *prompt;
1753 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1755 ret = mlyesno(prompt, dflt);
1757 if(prompt)
1758 fs_give((void **) &prompt);
1760 return(ret);
1765 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
1766 * ABORT. The ABORT status is returned if the user bumps out of the question
1767 * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
1768 * is no default.
1771 mlyesno(UCS *prompt, int dflt)
1773 int rv;
1774 UCS buf[NLINE], lbuf[10];
1775 KEYMENU menu_yesno[12];
1776 COLOR_PAIR *lastc = NULL;
1777 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
1779 #ifdef _WINDOWS
1780 if (mswin_usedialog ())
1781 switch (mswin_yesno (prompt)) {
1782 default:
1783 case 0: return (ABORT);
1784 case 1: return (TRUE);
1785 case 2: return (FALSE);
1787 #endif
1789 for(rv = 0; rv < 12; rv++){
1790 menu_yesno[rv].name = NULL;
1791 KS_OSDATASET(&menu_yesno[rv], KS_NONE);
1794 menu_yesno[1].name = "Y";
1795 menu_yesno[1].label = (dflt == TRUE) ? "[" N_("Yes") "]" : N_("Yes");
1796 menu_yesno[6].name = "^C";
1797 menu_yesno[6].label = N_("Cancel");
1798 menu_yesno[7].name = "N";
1799 menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No");
1800 wkeyhelp(menu_yesno); /* paint generic menu */
1801 sgarbk = TRUE; /* mark menu dirty */
1802 if(Pmaster && curwp)
1803 curwp->w_flag |= WFMODE;
1805 ucs4_strncpy(buf, prompt, NLINE);
1806 buf[NLINE-1] = '\0';
1807 lbuf[0] = ' '; lbuf[1] = '?'; lbuf[2] = ' '; lbuf[3] = '\0';
1808 ucs4_strncat(buf, lbuf, NLINE - ucs4_strlen(buf) - 1);
1809 buf[NLINE-1] = '\0';
1810 mlwrite(buf, NULL);
1811 if(pcolors && pcolors->prcp
1812 && pico_is_good_colorpair(pcolors->prcp)){
1813 lastc = pico_get_cur_color();
1814 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
1815 } else
1816 (*term.t_rev)(1);
1818 rv = -1;
1819 while(1){
1820 switch(GetKey()){
1821 case (CTRL|'M') : /* default */
1822 if(dflt >= 0){
1823 pputs_utf8((dflt) ? _("Yes") : _("No"), 1);
1824 rv = dflt;
1826 else
1827 (*term.t_beep)();
1829 break;
1831 case (CTRL|'C') : /* Bail out! */
1832 case F2 :
1833 pputs_utf8(_("ABORT"), 1);
1834 rv = ABORT;
1835 break;
1837 case 'y' :
1838 case 'Y' :
1839 case F3 :
1840 pputs_utf8(_("Yes"), 1);
1841 rv = TRUE;
1842 break;
1844 case 'n' :
1845 case 'N' :
1846 case F4 :
1847 pputs_utf8(_("No"), 1);
1848 rv = FALSE;
1849 break;
1851 case (CTRL|'G') :
1852 if(term.t_mrow == 0 && km_popped == 0){
1853 movecursor(term.t_nrow-2, 0);
1854 peeol();
1855 term.t_mrow = 2;
1856 if(lastc){
1857 (void) pico_set_colorp(lastc, PSC_NONE);
1858 free_color_pair(&lastc);
1860 else
1861 (*term.t_rev)(0);
1863 wkeyhelp(menu_yesno); /* paint generic menu */
1864 mlwrite(buf, NULL);
1865 if(pcolors && pcolors->prcp
1866 && pico_is_good_colorpair(pcolors->prcp)){
1867 lastc = pico_get_cur_color();
1868 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
1870 else
1871 (*term.t_rev)(1);
1873 sgarbk = TRUE; /* mark menu dirty */
1874 km_popped++;
1875 break;
1877 /* else fall through */
1879 default:
1880 (*term.t_beep)();
1881 case NODATA :
1882 break;
1885 (*term.t_flush)();
1886 if(rv != -1){
1887 if(lastc){
1888 (void) pico_set_colorp(lastc, PSC_NONE);
1889 free_color_pair(&lastc);
1891 else
1892 (*term.t_rev)(0);
1894 if(km_popped){
1895 term.t_mrow = 0;
1896 movecursor(term.t_nrow, 0);
1897 peeol();
1898 sgarbf = 1;
1899 km_popped = 0;
1902 return(rv);
1909 * Write a prompt into the message line, then read back a response. Keep
1910 * track of the physical position of the cursor. If we are in a keyboard
1911 * macro throw the prompt away, and return the remembered response. This
1912 * lets macros run at full speed. The reply is always terminated by a carriage
1913 * return. Handle erase, kill, and abort keys.
1916 mlreply_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1918 return(mlreplyd_utf8(utf8prompt, utf8buf, nbuf, flg|QDEFLT, extras));
1923 mlreply(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
1925 return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
1930 * function key mappings
1932 static UCS rfkm[12][2] = {
1933 { F1, (CTRL|'G')},
1934 { F2, (CTRL|'C')},
1935 { F3, 0 },
1936 { F4, 0 },
1937 { F5, 0 },
1938 { F6, 0 },
1939 { F7, 0 },
1940 { F8, 0 },
1941 { F9, 0 },
1942 { F10, 0 },
1943 { F11, 0 },
1944 { F12, 0 }
1949 mlreplyd_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
1951 int ret;
1952 UCS *b, *buf;
1953 char *utf8;
1954 UCS *prompt;
1956 buf = (UCS *) fs_get(nbuf * sizeof(*b));
1957 b = utf8_to_ucs4_cpystr(utf8buf);
1958 if(b){
1959 ucs4_strncpy(buf, b, nbuf);
1960 buf[nbuf-1] = '\0';
1961 fs_give((void **) &b);
1964 prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
1966 ret = mlreplyd(prompt, buf, nbuf, flg, extras);
1968 utf8 = ucs4_to_utf8_cpystr(buf);
1969 if(utf8){
1970 strncpy(utf8buf, utf8, nbuf);
1971 utf8buf[nbuf-1] = '\0';
1972 fs_give((void **) &utf8);
1975 if(buf)
1976 fs_give((void **) &buf);
1978 if(prompt)
1979 fs_give((void **) &prompt);
1981 return(ret);
1985 void
1986 writeachar(UCS ucs)
1988 pputc(ucs, 0);
1993 * mlreplyd - write the prompt to the message line along with a default
1994 * answer already typed in. Carriage return accepts the
1995 * default. answer returned in buf which also holds the initial
1996 * default, nbuf is its length, def set means use default value.
1997 * In order to be able to eliminate keys from a menu, EXTRAKEYS
1998 * always has size 10.
2001 mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
2003 UCS c; /* current char */
2004 UCS *b; /* pointer in buf */
2005 int i, j;
2006 int plen;
2007 int changed = FALSE;
2008 int return_val = 0;
2009 KEYMENU menu_mlreply[12];
2010 UCS extra_v[12];
2011 struct display_line dline;
2012 COLOR_PAIR *lastc = NULL;
2013 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2015 #ifdef _WINDOWS
2016 if(mswin_usedialog()){
2017 MDlgButton btn_list[12];
2018 LPTSTR free_names[12];
2019 LPTSTR free_labels[12];
2020 int i, j;
2022 memset(&free_names, 0, sizeof(LPTSTR) * 12);
2023 memset(&free_labels, 0, sizeof(LPTSTR) * 12);
2024 memset(&btn_list, 0, sizeof (MDlgButton) * 12);
2025 j = 0;
2026 for(i = 0; extras && extras[i].name != NULL; ++i) {
2027 if(extras[i].label[0] != '\0') {
2028 if((extras[i].key & CTRL) == CTRL)
2029 btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
2030 else
2031 btn_list[j].ch = extras[i].key;
2033 btn_list[j].rval = extras[i].key;
2034 free_names[j] = utf8_to_lptstr(extras[i].name);
2035 btn_list[j].name = free_names[j];
2036 free_labels[j] = utf8_to_lptstr(extras[i].label);
2037 btn_list[j].label = free_labels[j];
2038 j++;
2042 btn_list[j].ch = -1;
2044 return_val = mswin_dialog(prompt, buf, nbuf, ((flg&QDEFLT) > 0),
2045 FALSE, btn_list, NULL, 0);
2047 if(return_val == 3)
2048 return_val = HELPCH;
2050 for(i = 0; i < 12; i++){
2051 if(free_names[i])
2052 fs_give((void **) &free_names[i]);
2053 if(free_labels[i])
2054 fs_give((void **) &free_labels[i]);
2057 return(return_val);
2059 #endif
2061 memset(&menu_mlreply, 0, 12*sizeof(KEYMENU));
2062 menu_mlreply[0].name = "^G";
2063 menu_mlreply[0].label = N_("Get Help");
2064 KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
2065 for(j = 0, i = 1; i < 6; i++){ /* insert odd extras */
2066 menu_mlreply[i].name = NULL;
2067 KS_OSDATASET(&menu_mlreply[i], KS_NONE);
2068 rfkm[2*i][1] = 0;
2069 if(extras){
2070 j = 2*(i-1);
2071 if(extras[j].name){
2072 rfkm[2*i][1] = extras[j].key;
2073 menu_mlreply[i].name = extras[j].name;
2074 menu_mlreply[i].label = extras[j].label;
2075 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
2080 menu_mlreply[6].name = "^C";
2081 menu_mlreply[6].label = N_("Cancel");
2082 KS_OSDATASET(&menu_mlreply[6], KS_NONE);
2083 for(j = 0, i = 7; i < 12; i++){ /* insert even extras */
2084 menu_mlreply[i].name = NULL;
2085 rfkm[2*(i-6)+1][1] = 0;
2086 if(extras){
2087 j = 2*(i-6) - 1;
2088 if(extras[j].name){
2089 rfkm[2*(i-6)+1][1] = extras[j].key;
2090 menu_mlreply[i].name = extras[j].name;
2091 menu_mlreply[i].label = extras[j].label;
2092 KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
2097 /* set up what to watch for and return values */
2098 memset(extra_v, 0, sizeof(extra_v));
2099 for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
2100 extra_v[j++] = extras[i].key;
2102 plen = mlwrite(prompt, NULL); /* paint prompt */
2104 if(!(flg&QDEFLT))
2105 *buf = '\0';
2107 dline.vused = ucs4_strlen(buf);
2108 dline.dwid = term.t_ncol - plen;
2109 dline.row = term.t_nrow - term.t_mrow;
2110 dline.col = plen;
2112 dline.dlen = 2 * dline.dwid + 100;
2114 dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
2115 dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
2116 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
2117 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
2119 dline.movecursor = movecursor;
2120 dline.writechar = writeachar;
2122 dline.vl = buf;
2123 dline.vlen = nbuf-1;
2124 dline.vbase = 0;
2126 b = &buf[(flg & QBOBUF) ? 0 : ucs4_strlen(buf)];
2128 wkeyhelp(menu_mlreply); /* paint generic menu */
2130 sgarbk = 1; /* mark menu dirty */
2132 if(pcolors && pcolors->prcp
2133 && pico_is_good_colorpair(pcolors->prcp)){
2134 lastc = pico_get_cur_color();
2135 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2137 else
2138 (*term.t_rev)(1);
2140 for(;;){
2142 line_paint(b-buf, &dline, NULL);
2143 (*term.t_flush)();
2145 #ifdef MOUSE
2146 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
2147 register_mfunc(mouse_in_content,
2148 term.t_nrow - term.t_mrow, plen,
2149 term.t_nrow - term.t_mrow, term.t_ncol-1);
2150 #endif
2151 #ifdef _WINDOWS
2152 mswin_allowpaste(MSWIN_PASTE_LINE);
2153 #endif
2154 while((c = GetKey()) == NODATA)
2157 #ifdef MOUSE
2158 clear_mfunc(mouse_in_content);
2159 #endif
2160 #ifdef _WINDOWS
2161 mswin_allowpaste(MSWIN_PASTE_DISABLE);
2162 #endif
2164 switch(c = normalize_cmd(c, rfkm, 1)){
2165 case (CTRL|'A') : /* CTRL-A beginning */
2166 case KEY_HOME :
2167 b = buf;
2168 continue;
2170 case (CTRL|'B') : /* CTRL-B back a char */
2171 case KEY_LEFT:
2172 if(b <= buf)
2173 (*term.t_beep)();
2174 else
2175 b--;
2177 continue;
2179 case (CTRL|'C') : /* CTRL-C abort */
2180 pputs_utf8(_("ABORT"), 1);
2181 ctrlg(FALSE, 0);
2182 return_val = ABORT;
2183 goto ret;
2185 case (CTRL|'E') : /* CTRL-E end of line */
2186 case KEY_END :
2187 b = &buf[ucs4_strlen(buf)];
2188 continue;
2190 case (CTRL|'F') : /* CTRL-F forward a char*/
2191 case KEY_RIGHT :
2192 if(*b == '\0')
2193 (*term.t_beep)();
2194 else
2195 b++;
2197 continue;
2199 case (CTRL|'G') : /* CTRL-G help */
2200 if(term.t_mrow == 0 && km_popped == 0){
2201 movecursor(term.t_nrow-2, 0);
2202 peeol();
2203 sgarbk = 1; /* mark menu dirty */
2204 km_popped++;
2205 term.t_mrow = 2;
2206 if(lastc){
2207 (void) pico_set_colorp(lastc, PSC_NONE);
2208 free_color_pair(&lastc);
2210 else
2211 (*term.t_rev)(0);
2213 wkeyhelp(menu_mlreply); /* paint generic menu */
2214 plen = mlwrite(prompt, NULL); /* paint prompt */
2215 if(pcolors && pcolors->prcp
2216 && pico_is_good_colorpair(pcolors->prcp)){
2217 lastc = pico_get_cur_color();
2218 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2220 else
2221 (*term.t_rev)(1);
2223 pputs(buf, 1);
2224 break;
2227 pputs_utf8(_("HELP"), 1);
2228 return_val = HELPCH;
2229 goto ret;
2231 case (CTRL|'H') : /* CTRL-H backspace */
2232 case 0x7f : /* rubout */
2233 if (b <= buf){
2234 (*term.t_beep)();
2235 break;
2237 else
2238 b--;
2240 case (CTRL|'D') : /* CTRL-D delete char */
2241 case KEY_DEL :
2242 if (!*b){
2243 (*term.t_beep)();
2244 break;
2247 changed=TRUE;
2248 i = 0;
2249 dline.vused--;
2250 do /* blat out left char */
2251 b[i] = b[i+1];
2252 while(b[i++] != '\0');
2253 break;
2255 case (CTRL|'L') : /* CTRL-L redraw */
2256 return_val = (CTRL|'L');
2257 goto ret;
2259 case (CTRL|'K') : /* CTRL-K kill line */
2260 changed=TRUE;
2261 buf[0] = '\0';
2262 dline.vused = 0;
2263 b = buf;
2264 break;
2266 case F1 : /* sort of same thing */
2267 return_val = HELPCH;
2268 goto ret;
2270 case (CTRL|'M') : /* newline */
2271 return_val = changed;
2272 goto ret;
2274 #ifdef MOUSE
2275 case KEY_MOUSE :
2277 MOUSEPRESS mp;
2279 mouse_get_last (NULL, &mp);
2281 /* The clicked line have anything special on it? */
2282 switch(mp.button){
2283 case M_BUTTON_LEFT : /* position cursor */
2284 mp.col -= plen; /* normalize column */
2285 if(mp.col >= 0 && mp.col <= ucs4_strlen(buf))
2286 b = buf + mp.col;
2288 break;
2290 case M_BUTTON_RIGHT :
2291 #ifdef _WINDOWS
2292 mswin_allowpaste(MSWIN_PASTE_LINE);
2293 mswin_paste_popup();
2294 mswin_allowpaste(MSWIN_PASTE_DISABLE);
2295 break;
2296 #endif
2298 case M_BUTTON_MIDDLE : /* NO-OP for now */
2299 default: /* just ignore */
2300 break;
2304 continue;
2305 #endif
2307 default :
2309 /* look for match in extra_v */
2310 for(i = 0; i < 12; i++)
2311 if(c && c == extra_v[i]){
2312 return_val = c;
2313 goto ret;
2316 changed=TRUE;
2318 if(c & (CTRL | FUNC)){ /* bag ctrl_special chars */
2319 (*term.t_beep)();
2321 else{
2322 i = ucs4_strlen(b);
2323 if(flg&QNODQT){ /* reject double quotes? */
2324 if(c == '"'){
2325 (*term.t_beep)();
2326 continue;
2330 if(dline.vused >= nbuf-1){
2331 (*term.t_beep)();
2332 continue;
2335 do /* blat out left char */
2336 b[i+1] = b[i];
2337 while(i-- > 0);
2339 dline.vused++;
2340 *b++ = c;
2345 ret:
2346 if(lastc){
2347 (void) pico_set_colorp(lastc, PSC_NONE);
2348 free_color_pair(&lastc);
2350 else
2351 (*term.t_rev)(0);
2353 (*term.t_flush)();
2355 if(km_popped){
2356 term.t_mrow = 0;
2357 movecursor(term.t_nrow, 0);
2358 peeol();
2359 sgarbf = 1;
2360 km_popped = 0;
2363 if(dline.dl)
2364 fs_give((void **) &dline.dl);
2366 if(dline.olddl)
2367 fs_give((void **) &dline.olddl);
2369 return(return_val);
2373 void
2374 emlwwrite(char *utf8message, EML *eml)
2376 (*term.t_beep)();
2377 emlwrite(utf8message, eml);
2380 void
2381 emlwrite(char *utf8message, EML *eml)
2383 UCS *message;
2385 message = utf8_to_ucs4_cpystr(utf8message ? utf8message : "");
2387 emlwrite_ucs4(message, eml);
2389 if(message)
2390 fs_give((void **) &message);
2395 * emlwrite() - write the message string to the error half of the screen
2396 * center justified. much like mlwrite (which is still used
2397 * to paint the line for prompts and such), except it center
2398 * the text.
2400 void
2401 emlwrite_ucs4(UCS *message, EML *eml)
2403 UCS *bufp, *ap;
2404 int width;
2405 COLOR_PAIR *lastc = NULL;
2406 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2408 mlerase();
2410 if(!(message && *message) || term.t_nrow < 2)
2411 return; /* nothing to write or no space to write, bag it */
2413 bufp = message;
2415 width = ucs4_str_width(message);
2418 * next, figure out where the to move the cursor so the message
2419 * comes out centered
2421 if((ap=ucs4_strchr(message, '%')) != NULL){
2422 width -= 2;
2423 switch(ap[1]){
2424 case '%':
2425 case 'c':
2426 width += (eml && eml->c) ? wcellwidth(eml->c) : 1;
2427 break;
2428 case 'd':
2429 width += dumbroot(eml ? eml->d : 0, 10);
2430 break;
2431 case 'D':
2432 width += dumblroot(eml ? eml->l : 0L, 10);
2433 break;
2434 case 'o':
2435 width += dumbroot(eml ? eml->d : 0, 8);
2436 break;
2437 case 'x':
2438 width += dumbroot(eml ? eml->d : 0, 16);
2439 break;
2440 case 's': /* string arg is UTF-8 */
2441 width += (eml && eml->s) ? utf8_width(eml->s) : 2;
2442 break;
2446 if(width+4 <= term.t_ncol)
2447 movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (width + 4))/2);
2448 else
2449 movecursor(term.t_nrow-term.t_mrow, 0);
2451 if(pcolors && pcolors->stcp
2452 && pico_is_good_colorpair(pcolors->stcp)){
2453 lastc = pico_get_cur_color();
2454 (void) pico_set_colorp(pcolors->stcp, PSC_NONE);
2456 else
2457 (*term.t_rev)(1);
2459 pputs_utf8("[ ", 1);
2460 while (*bufp != '\0' && ttcol < term.t_ncol-2){
2461 if(*bufp == '\007')
2462 (*term.t_beep)();
2463 else if(*bufp == '%'){
2464 switch(*++bufp){
2465 case 'c':
2466 if(eml && eml->c)
2467 pputc(eml->c, 0);
2468 else {
2469 pputs_utf8("%c", 0);
2471 break;
2472 case 'd':
2473 mlputi(eml ? eml->d : 0, 10);
2474 break;
2475 case 'D':
2476 mlputli(eml ? eml->l : 0L, 10);
2477 break;
2478 case 'o':
2479 mlputi(eml ? eml->d : 0, 16);
2480 break;
2481 case 'x':
2482 mlputi(eml ? eml->d : 0, 8);
2483 break;
2484 case 's':
2485 pputs_utf8((eml && eml->s) ? eml->s : "%s", 0);
2486 break;
2487 case '%':
2488 default:
2489 pputc(*bufp, 0);
2490 break;
2493 else
2494 pputc(*bufp, 0);
2495 bufp++;
2498 pputs_utf8(" ]", 1);
2500 if(lastc){
2501 (void) pico_set_colorp(lastc, PSC_NONE);
2502 free_color_pair(&lastc);
2504 else
2505 (*term.t_rev)(0);
2507 (*term.t_flush)();
2509 mpresf = TRUE;
2514 mlwrite_utf8(char *utf8fmt, void *arg)
2516 UCS *fmt;
2517 int ret;
2519 fmt = utf8_to_ucs4_cpystr(utf8fmt ? utf8fmt : "");
2520 ret = mlwrite(fmt, arg);
2521 if(fmt)
2522 fs_give((void **) &fmt);
2524 return(ret);
2529 * Write a message into the message line. Keep track of the physical cursor
2530 * position. A small class of printf like format items is handled. Assumes the
2531 * stack grows down; this assumption is made by the "++" in the argument scan
2532 * loop. Set the "message line" flag TRUE.
2535 mlwrite(UCS *fmt, void *arg)
2537 int ret, ww;
2538 UCS c;
2539 char *ap;
2540 COLOR_PAIR *lastc = NULL;
2541 PCOLORS *pcolors = Pmaster && Pmaster->colors ? Pmaster->colors : Pcolors;
2544 * the idea is to only highlight if there is something to show
2546 mlerase();
2547 movecursor(ttrow, 0);
2549 if(pcolors && pcolors->prcp
2550 && pico_is_good_colorpair(pcolors->prcp)){
2551 lastc = pico_get_cur_color();
2552 (void) pico_set_colorp(pcolors->prcp, PSC_NONE);
2554 else
2555 (*term.t_rev)(1);
2557 ap = (char *) &arg;
2559 while ((c = *fmt++) != 0) {
2560 if (c != '%') {
2561 pputc(c, 1);
2563 else {
2564 c = *fmt++;
2565 switch (c){
2566 case 'd':
2567 mlputi(*(int *)ap, 10);
2568 ap += sizeof(int);
2569 break;
2571 case 'o':
2572 mlputi(*(int *)ap, 8);
2573 ap += sizeof(int);
2574 break;
2576 case 'x':
2577 mlputi(*(int *)ap, 16);
2578 ap += sizeof(int);
2579 break;
2581 case 'D':
2582 mlputli(*(long *)ap, 10);
2583 ap += sizeof(long);
2584 break;
2586 case 's':
2587 pputs_utf8(*(char **)ap, 1);
2588 ap += sizeof(char *);
2589 break;
2591 default:
2592 pputc(c, 1);
2593 ww = wcellwidth(c);
2594 ttcol += (ww >= 0 ? ww : 1);
2599 ret = ttcol;
2600 while(ttcol < term.t_ncol)
2601 pputc(' ', 0);
2603 movecursor(term.t_nrow - term.t_mrow, ret);
2605 if(lastc){
2606 (void) pico_set_colorp(lastc, PSC_NONE);
2607 free_color_pair(&lastc);
2609 else
2610 (*term.t_rev)(0);
2612 (*term.t_flush)();
2613 mpresf = TRUE;
2615 return(ret);
2620 * Write out an integer, in the specified radix. Update the physical cursor
2621 * position. This will not handle any negative numbers; maybe it should.
2623 void
2624 mlputi(int i, int r)
2626 register int q;
2627 static char hexdigits[] = "0123456789ABCDEF";
2629 if (i < 0){
2630 i = -i;
2631 pputc('-', 1);
2634 q = i/r;
2636 if (q != 0)
2637 mlputi(q, r);
2639 pputc(hexdigits[i%r], 1);
2644 * do the same except as a long integer.
2646 void
2647 mlputli(long l, int r)
2649 register long q;
2651 if (l < 0){
2652 l = -l;
2653 pputc('-', 1);
2656 q = l/r;
2658 if (q != 0)
2659 mlputli(q, r);
2661 pputc((int)(l%r)+'0', 1);
2665 void
2666 unknown_command(UCS c)
2668 char buf[10], ch, *s;
2669 EML eml;
2671 buf[0] = '\0';
2672 s = buf;
2674 if(!c){
2675 /* fall through */
2677 else if(c & CTRL && c >= (CTRL|'@') && c <= (CTRL|'_')){
2678 ch = c - (CTRL|'@') + '@';
2679 snprintf(s, sizeof(buf), "^%c", ch);
2681 else
2682 switch(c){
2683 case ' ' : s = "SPACE"; break;
2684 case '\033' : s = "ESC"; break;
2685 case '\177' : s = "DEL"; break;
2686 case ctrl('I') : s = "TAB"; break;
2687 case ctrl('J') : s = "LINEFEED"; break;
2688 case ctrl('M') : s = "RETURN"; break;
2689 case ctrl('Q') : s = "XON"; break;
2690 case ctrl('S') : s = "XOFF"; break;
2691 case KEY_UP : s = "Up Arrow"; break;
2692 case KEY_DOWN : s = "Down Arrow"; break;
2693 case KEY_RIGHT : s = "Right Arrow"; break;
2694 case KEY_LEFT : s = "Left Arrow"; break;
2695 case CTRL|KEY_UP : s = "Ctrl-Up Arrow"; break;
2696 case CTRL|KEY_DOWN : s = "Ctrl-Down Arrow"; break;
2697 case CTRL|KEY_RIGHT : s = "Ctrl-Right Arrow"; break;
2698 case CTRL|KEY_LEFT : s = "Ctrl-Left Arrow"; break;
2699 case KEY_PGUP : s = "Prev Page"; break;
2700 case KEY_PGDN : s = "Next Page"; break;
2701 case KEY_HOME : s = "Home"; break;
2702 case KEY_END : s = "End"; break;
2703 case KEY_DEL : s = "Delete"; break; /* Not necessary DEL! */
2704 case F1 :
2705 case F2 :
2706 case F3 :
2707 case F4 :
2708 case F5 :
2709 case F6 :
2710 case F7 :
2711 case F8 :
2712 case F9 :
2713 case F10 :
2714 case F11 :
2715 case F12 :
2716 snprintf(s, sizeof(buf), "F%ld", (long) (c - PF1 + 1));
2717 break;
2719 default:
2720 if(c < CTRL)
2721 utf8_put((unsigned char *) s, (unsigned long) c);
2723 break;
2726 eml.s = s;
2727 emlwrite("Unknown Command: %s", &eml);
2728 (*term.t_beep)();
2733 * scrolldown - use stuff to efficiently move blocks of text on the
2734 * display, and update the pscreen array to reflect those
2735 * moves...
2737 * wp is the window to move in
2738 * r is the row at which to begin scrolling
2739 * n is the number of lines to scrol
2741 void
2742 scrolldown(WINDOW *wp, int r, int n)
2744 #ifdef TERMCAP
2745 register int i;
2746 register int l;
2747 register VIDEO *vp1;
2748 register VIDEO *vp2;
2750 if(!n)
2751 return;
2753 if(r < 0){
2754 r = wp->w_toprow;
2755 l = wp->w_ntrows;
2757 else{
2758 if(r > wp->w_toprow)
2759 vscreen[r-1]->v_flag |= VFCHG;
2760 l = wp->w_toprow+wp->w_ntrows-r;
2763 o_scrolldown(r, n);
2765 for(i=l-n-1; i >= 0; i--){
2766 vp1 = pscreen[r+i];
2767 vp2 = pscreen[r+i+n];
2768 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2770 pprints(r+n-1, r);
2771 ttrow = FARAWAY;
2772 ttcol = FARAWAY;
2773 #endif /* TERMCAP */
2778 * scrollup - use tcap stuff to efficiently move blocks of text on the
2779 * display, and update the pscreen array to reflect those
2780 * moves...
2782 void
2783 scrollup(WINDOW *wp, int r, int n)
2785 #ifdef TERMCAP
2786 register int i;
2787 register VIDEO *vp1;
2788 register VIDEO *vp2;
2790 if(!n)
2791 return;
2793 if(r < 0)
2794 r = wp->w_toprow;
2796 o_scrollup(r, n);
2798 i = 0;
2799 while(1){
2800 if(Pmaster){
2801 if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
2802 break;
2804 else{
2805 if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
2806 break;
2808 vp1 = pscreen[r+i+n];
2809 vp2 = pscreen[r+i];
2810 memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
2811 i++;
2813 pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
2814 ttrow = FARAWAY;
2815 ttcol = FARAWAY;
2816 #endif /* TERMCAP */
2821 * print spaces in the physical screen starting from row abs(n) working in
2822 * either the positive or negative direction (depending on sign of n).
2824 void
2825 pprints(int x, int y)
2827 register int i;
2828 register int j;
2830 if(x < y){
2831 for(i = x;i <= y; ++i){
2832 for(j = 0; j < term.t_ncol; j++){
2833 pscreen[i]->v_text[j].c = ' ';
2834 pscreen[i]->v_text[j].a = 0;
2838 else{
2839 for(i = x;i >= y; --i){
2840 for(j = 0; j < term.t_ncol; j++){
2841 pscreen[i]->v_text[j].c = ' ';
2842 pscreen[i]->v_text[j].a = 0;
2846 ttrow = y;
2847 ttcol = 0;
2852 * doton - return the physical line number that the dot is on in the
2853 * current window, and by side effect the number of lines remaining
2856 doton(int *r, unsigned *chs)
2858 register int i = 0;
2859 register LINE *lp = curwp->w_linep;
2860 int l = -1;
2862 assert(r != NULL && chs != NULL);
2864 *chs = 0;
2865 while(i++ < curwp->w_ntrows){
2866 if(lp == curwp->w_dotp)
2867 l = i-1;
2868 lp = lforw(lp);
2869 if(lp == curwp->w_bufp->b_linep){
2870 i++;
2871 break;
2873 if(l >= 0)
2874 (*chs) += llength(lp);
2876 *r = i - l - term.t_mrow;
2877 return(l+curwp->w_toprow);
2883 * resize_pico - given new window dimensions, allocate new resources
2886 resize_pico(int row, int col)
2888 int old_nrow, old_ncol;
2889 register int i;
2890 register VIDEO *vp;
2892 old_nrow = term.t_nrow;
2893 old_ncol = term.t_ncol;
2895 term.t_nrow = row;
2896 term.t_ncol = col;
2898 if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
2899 return(TRUE);
2901 if(curwp){
2902 curwp->w_toprow = 2;
2903 curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
2906 if(Pmaster){
2907 fillcol = Pmaster->fillcolumn;
2908 (*Pmaster->resize)();
2910 else if(userfillcol > 0)
2911 fillcol = userfillcol;
2912 else
2913 fillcol = term.t_ncol - 6; /* we control the fill column */
2916 * free unused screen space ...
2918 for(i=term.t_nrow+1; i <= old_nrow; ++i){
2919 free((char *) vscreen[i]);
2920 free((char *) pscreen[i]);
2924 * realloc new space for screen ...
2926 if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2927 if(Pmaster)
2928 return(-1);
2929 else
2930 exit(1);
2933 if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
2934 if(Pmaster)
2935 return(-1);
2936 else
2937 exit(1);
2940 for (i = 0; i <= term.t_nrow; ++i) {
2941 if(i <= old_nrow)
2942 vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2943 else
2944 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2946 if (vp == NULL)
2947 exit(1);
2948 vp->v_flag = VFCHG;
2949 vscreen[i] = vp;
2950 if(old_ncol < term.t_ncol){ /* don't let any garbage in */
2951 vtrow = i;
2952 vtcol = (i < old_nrow) ? old_ncol : 0;
2953 vteeol();
2956 if(i <= old_nrow)
2957 vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2958 else
2959 vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
2961 if (vp == NULL)
2962 exit(1);
2964 vp->v_flag = VFCHG;
2965 pscreen[i] = vp;
2968 if(!ResizeBrowser()){
2969 if(Pmaster && Pmaster->headents){
2970 ResizeHeader();
2972 else{
2973 curwp->w_flag |= (WFHARD | WFMODE);
2974 pico_refresh(0, 1); /* redraw whole enchilada. */
2975 update(); /* do it */
2979 return(TRUE);
2982 void
2983 redraw_pico_for_callback(void)
2985 pico_refresh(0, 1);
2986 update();
2991 * showCompTitle - display the anchor line passed in from pine
2993 void
2994 showCompTitle(void)
2996 if(Pmaster){
2997 UCS *bufp;
2998 extern UCS *pico_anchor;
2999 COLOR_PAIR *lastc = NULL;
3001 if((bufp = pico_anchor) == NULL)
3002 return;
3004 movecursor(COMPOSER_TITLE_LINE, 0);
3005 if (Pmaster->colors && Pmaster->colors->tbcp &&
3006 pico_is_good_colorpair(Pmaster->colors->tbcp)){
3007 lastc = pico_get_cur_color();
3008 (void)pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
3010 else
3011 (*term.t_rev)(1);
3013 while (ttcol < term.t_ncol)
3014 if(*bufp != '\0')
3015 pputc(*bufp++, 1);
3016 else
3017 pputc(' ', 1);
3019 if (lastc){
3020 (void)pico_set_colorp(lastc, PSC_NONE);
3021 free_color_pair(&lastc);
3023 else
3024 (*term.t_rev)(0);
3026 movecursor(COMPOSER_TITLE_LINE + 1, 0);
3027 peeol();
3034 * zotdisplay - blast malloc'd space created for display maps
3036 void
3037 zotdisplay(void)
3039 register int i;
3041 for (i = 0; i <= term.t_nrow; ++i){ /* free screens */
3042 free((char *) vscreen[i]);
3043 free((char *) pscreen[i]);
3046 free((char *) vscreen);
3047 free((char *) pscreen);
3053 * nlforw() - returns the number of lines from the top to the dot
3056 nlforw(void)
3058 register int i = 0;
3059 register LINE *lp = curwp->w_linep;
3061 while(lp != curwp->w_dotp){
3062 lp = lforw(lp);
3063 i++;
3065 return(i);
3071 * pputc - output the given char, keep track of it on the physical screen
3072 * array, and keep track of the cursor
3074 void
3075 pputc(UCS c, /* char to write */
3076 int a) /* and its attribute */
3078 int ind, width, printable_ascii = 0;
3081 * This is necessary but not sufficient to allow us to draw. Note that
3082 * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
3083 * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
3085 if((ttcol >= 0 && ttcol < term.t_ncol) && (ttrow >= 0 && ttrow <= term.t_nrow)){
3088 * Width is the number of screen columns a character will occupy.
3090 if(c < 0x80 && isprint(c)){
3091 printable_ascii++;
3092 width = 1;
3094 else
3095 width = wcellwidth(c);
3097 if(width < 0)
3098 width = 1; /* will be a '?' */
3100 if(ttcol + width <= term.t_ncol){ /* it fits */
3102 * Some terminals scroll when you write in the lower right corner
3103 * of the screen, so don't write there.
3105 if(!(ttrow == term.t_nrow && ttcol+width == term.t_ncol)){
3106 (*term.t_putchar)(c); /* write it */
3107 ind = index_from_col(ttrow, ttcol);
3108 pscreen[ttrow]->v_text[ind].c = c; /* keep track of it */
3109 pscreen[ttrow]->v_text[ind].a = a; /* keep track of it */
3112 else{
3114 * Character overlaps right edge of screen. Hopefully the higher
3115 * layers will prevent this but we're making sure.
3117 * We may want to do something like writing a space character
3118 * into the cells that are on the screen. We'll see.
3122 ttcol = MIN(term.t_ncol, ttcol+width);
3128 * pputs - print a string and keep track of the cursor
3130 void
3131 pputs(UCS *s, /* string to write */
3132 int a) /* and its attribute */
3134 while (*s != '\0')
3135 pputc(*s++, a);
3139 void
3140 pputs_utf8(char *s, int a)
3142 UCS *ucsstr = NULL;
3144 if(s && *s){
3145 ucsstr = utf8_to_ucs4_cpystr(s);
3146 if(ucsstr){
3147 pputs(ucsstr, a);
3148 fs_give((void **) &ucsstr);
3155 * peeol - physical screen array erase to end of the line. remember to
3156 * track the cursor.
3158 void
3159 peeol(void)
3161 int i, width = 0, ww;
3162 CELL cl;
3164 if(ttrow < 0 || ttrow > term.t_nrow)
3165 return;
3167 cl.c = ' ';
3168 cl.a = 0;
3171 * Don't clear if we think we are sitting past the last column,
3172 * that erases the last column if we just wrote it.
3174 if(ttcol < term.t_ncol)
3175 (*term.t_eeol)();
3178 * Because the characters are variable width it's a little tricky
3179 * to erase the rest of the line. What we do is add up the
3180 * widths of the characters until we reach ttcol
3181 * then set the rest to the space character.
3183 for(i = 0; i < term.t_ncol && width < ttcol; i++){
3184 ww = wcellwidth((UCS) pscreen[ttrow]->v_text[i].c);
3185 width += (ww >= 0 ? ww : 1);
3188 while(i < term.t_ncol)
3189 pscreen[ttrow]->v_text[i++] = cl;
3194 * pscr - return the character cell on the physical screen map on the
3195 * given line, l, and offset, o.
3197 CELL *
3198 pscr(int l, int o)
3200 if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
3201 return(&(pscreen[l]->v_text[o]));
3202 else
3203 return(NULL);
3208 * pclear() - clear the physical screen from row x through row y (inclusive)
3209 * row is zero origin, min row = 0 max row = t_nrow
3210 * Clear whole screen -- pclear(0, term.t_nrow)
3211 * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
3212 * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
3214 void
3215 pclear(int x, int y)
3217 register int i;
3219 x = MIN(MAX(0, x), term.t_nrow);
3220 y = MIN(MAX(0, y), term.t_nrow);
3222 for(i=x; i <= y; i++){
3223 movecursor(i, 0);
3224 peeol();
3230 * dumbroot - just get close
3233 dumbroot(int x, int b)
3235 if(x < b)
3236 return(1);
3237 else
3238 return(dumbroot(x/b, b) + 1);
3243 * dumblroot - just get close
3246 dumblroot(long x, int b)
3248 if(x < b)
3249 return(1);
3250 else
3251 return(dumblroot(x/b, b) + 1);
3256 * pinsertc - use optimized insert, fixing physical screen map.
3257 * returns true if char written, false otherwise
3260 pinsert(CELL c)
3262 int i, ind = 0, ww;
3263 CELL *p;
3265 if(ttrow < 0 || ttrow > term.t_nrow)
3266 return(0);
3268 if(o_insert((UCS) c.c)){ /* if we've got it, use it! */
3269 p = pscreen[ttrow]->v_text; /* then clean up physical screen */
3271 ind = index_from_col(ttrow, ttcol);
3273 for(i = term.t_ncol-1; i > ind; i--)
3274 p[i] = p[i-1]; /* shift right */
3276 p[ind] = c; /* insert new char */
3278 ww = wcellwidth((UCS) c.c);
3279 ttcol += (ww >= 0 ? ww : 1);
3281 return(1);
3284 return(0);
3289 * pdel - use optimized delete to rub out the current char and
3290 * fix the physical screen array.
3291 * returns true if optimized the delete, false otherwise
3294 pdel(void)
3296 int i, ind = 0, w;
3297 CELL *p;
3299 if(ttrow < 0 || ttrow > term.t_nrow)
3300 return(0);
3302 if(TERM_DELCHAR){ /* if we've got it, use it! */
3303 p = pscreen[ttrow]->v_text;
3304 ind = index_from_col(ttrow, ttcol);
3306 if(ind > 0){
3307 --ind;
3308 w = wcellwidth((UCS) p[ind].c);
3309 w = (w >= 0 ? w : 1);
3310 ttcol -= w;
3312 for(i = 0; i < w; i++){
3313 (*term.t_putchar)('\b'); /* move left a char */
3314 o_delete(); /* and delete it */
3317 /* then clean up physical screen */
3318 for(i=ind; i < term.t_ncol-1; i++)
3319 p[i] = p[i+1];
3321 p[i].c = ' ';
3322 p[i].a = 0;
3325 return(1);
3328 return(0);
3334 * wstripe - write out the given string at the given location, and reverse
3335 * video on flagged characters. Does the same thing as pine's
3336 * stripe.
3338 * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
3339 * only if you want to put the tildes before multi-byte chars.
3341 void
3342 wstripe(int line, int column, char *utf8pmt, int key)
3344 UCS *ucs4pmt, *u;
3345 int i = 0, col = 0;
3346 int j = 0;
3347 int l, ww;
3348 COLOR_PAIR *lastc = NULL;
3349 COLOR_PAIR *kncp = NULL;
3350 COLOR_PAIR *klcp = NULL;
3352 if(line < 0 || line > term.t_nrow)
3353 return;
3355 if (Pmaster && Pmaster->colors){
3356 if(pico_is_good_colorpair(Pmaster->colors->klcp))
3357 klcp = Pmaster->colors->klcp;
3359 if(klcp && pico_is_good_colorpair(Pmaster->colors->kncp))
3360 kncp = Pmaster->colors->kncp;
3362 else if(Pcolors){
3363 klcp = Pcolors->klcp;
3364 kncp = Pcolors->kncp;
3367 lastc = pico_get_cur_color();
3368 ucs4pmt = utf8_to_ucs4_cpystr(utf8pmt);
3369 l = ucs4_strlen(ucs4pmt);
3370 while(1){
3371 if(i >= term.t_ncol || col >= term.t_ncol || j >= l){
3372 if(lastc) free_color_pair(&lastc);
3373 if(ucs4pmt) fs_give((void **) &ucs4pmt);
3374 return; /* equal strings */
3377 if(ucs4pmt[j] == (UCS) key)
3378 j++;
3380 if (pscr(line, i) == NULL){
3381 if(lastc) free_color_pair(&lastc);
3382 if(ucs4pmt) fs_give((void **) &ucs4pmt);
3383 return;
3386 if(pscr(line, i)->c != ucs4pmt[j]){
3387 if(j >= 1 && ucs4pmt[j-1] == (UCS) key)
3388 j--;
3389 break;
3392 ww = wcellwidth((UCS) pscr(line, i)->c);
3393 col += (ww >= 0 ? ww : 1);
3394 j++;
3395 i++;
3398 movecursor(line, column+col);
3399 if(klcp) (void)pico_set_colorp(klcp, PSC_NONE);
3400 u = &ucs4pmt[j];
3402 if(*u == (UCS) key){
3403 u++;
3404 if(kncp)
3405 (void)pico_set_colorp(kncp, PSC_NONE);
3406 else
3407 (void)(*term.t_rev)(1);
3409 pputc(*u, 1);
3410 if(kncp)
3411 (void)pico_set_colorp(klcp, PSC_NONE);
3412 else
3413 (void)(*term.t_rev)(0);
3415 else{
3416 pputc(*u, 0);
3419 while(*++u != '\0');
3421 if(ucs4pmt)
3422 fs_give((void **) &ucs4pmt);
3424 peeol();
3425 if (lastc){
3426 (void)pico_set_colorp(lastc, PSC_NONE);
3427 free_color_pair(&lastc);
3429 (*term.t_flush)();
3435 * wkeyhelp - paint list of possible commands on the bottom
3436 * of the display (yet another pine clone)
3437 * NOTE: function key mode is handled here since all the labels
3438 * are the same...
3440 * The KEYMENU definitions have names and labels defined as UTF-8 strings,
3441 * and wstripe expects UTF-8.
3443 void
3444 wkeyhelp(KEYMENU *keymenu)
3446 char *obufp, *p, fkey[4];
3447 char linebuf[2*NLINE]; /* "2" is for space for invert tokens */
3448 int row, slot, tspace, adjusted_tspace, nspace[6], index, n;
3449 #ifdef MOUSE
3450 char nbuf[NLINE];
3451 #endif
3453 #ifdef _WINDOWS
3454 pico_config_menu_items (keymenu);
3455 #endif
3457 if(term.t_mrow == 0)
3458 return;
3460 if(term.t_nrow < 1)
3461 return;
3464 * Calculate amount of space for the names column by column...
3466 for(index = 0; index < 6; index++)
3467 if(!(gmode&MDFKEY)){
3468 nspace[index] = (keymenu[index].name)
3469 ? utf8_width(keymenu[index].name) : 0;
3470 if(keymenu[index+6].name
3471 && (n = utf8_width(keymenu[index+6].name)) > nspace[index])
3472 nspace[index] = n;
3474 nspace[index]++;
3476 else
3477 nspace[index] = (index < 4) ? 3 : 4;
3479 tspace = term.t_ncol/6; /* total space for each item */
3482 * Avoid writing in bottom right corner so we won't scroll screens that
3483 * scroll when you do that. The way this is setup, we won't do that
3484 * unless the number of columns is evenly divisible by 6.
3486 adjusted_tspace = (6 * tspace == term.t_ncol) ? tspace - 1 : tspace;
3488 index = 0;
3489 for(row = 0; row <= 1; row++){
3490 linebuf[0] = '\0';
3491 obufp = &linebuf[0];
3492 for(slot = 0; slot < 6; slot++){
3493 if(keymenu[index].name && keymenu[index].label){
3494 size_t l;
3495 char this_label[200], tmp_label[200];
3497 if(keymenu[index].label[0] == '[' && keymenu[index].label[(l=strlen(keymenu[index].label))-1] == ']' && l > 2){
3498 strncpy(tmp_label, &keymenu[index].label[1], MIN(sizeof(tmp_label),l-2));
3499 tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
3500 snprintf(this_label, sizeof(this_label), "[%s]", _(tmp_label));
3502 else
3503 strncpy(this_label, _(keymenu[index].label), sizeof(this_label));
3505 this_label[sizeof(this_label)-1] = '\0';
3507 if(gmode&MDFKEY){
3508 p = fkey;
3509 snprintf(fkey, sizeof(fkey), "F%d", (2 * slot) + row + 1);
3511 else
3512 p = keymenu[index].name;
3513 #ifdef MOUSE
3514 snprintf(nbuf, sizeof(nbuf), "%.*s %s", nspace[slot], p, this_label);
3515 register_key(index,
3516 (gmode&MDFKEY) ? F1 + (2 * slot) + row:
3517 (keymenu[index].name[0] == '^')
3518 ? (CTRL | keymenu[index].name[1])
3519 : (keymenu[index].name[0] == 'S'
3520 && !strcmp(keymenu[index].name, "Spc"))
3521 ? ' '
3522 : keymenu[index].name[0],
3523 nbuf, invert_label,
3524 term.t_nrow - 1 + row, (slot * tspace),
3525 strlen(nbuf),
3526 (Pmaster && Pmaster->colors)
3527 ? Pmaster->colors->kncp: NULL,
3528 (Pmaster && Pmaster->colors)
3529 ? Pmaster->colors->klcp: NULL);
3530 #endif
3532 n = nspace[slot];
3533 while(p && *p && n--){
3534 *obufp++ = '~'; /* insert "invert" token */
3535 *obufp++ = *p++;
3538 while(n-- > 0)
3539 *obufp++ = ' ';
3541 p = this_label;
3542 n = ((slot == 5 && row == 1) ? adjusted_tspace
3543 : tspace) - nspace[slot];
3544 while(p && *p && n-- > 0)
3545 *obufp++ = *p++;
3547 while(n-- > 0)
3548 *obufp++ = ' ';
3550 else{
3551 n = (slot == 5 && row == 1) ? adjusted_tspace : tspace;
3552 while(n--)
3553 *obufp++ = ' ';
3555 #ifdef MOUSE
3556 register_key(index, NODATA, "", NULL, 0, 0, 0, NULL, NULL);
3557 #endif
3560 *obufp = '\0';
3561 index++;
3564 wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
3570 * This returns the screen width between pstart (inclusive) and
3571 * pend (exclusive) where the pointers point into an array of CELLs.
3573 unsigned
3574 cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend)
3576 CELL *p;
3577 unsigned width = 0;
3578 int ww;
3580 if(pstart)
3581 for(p = pstart; p < pend; p++){
3582 ww = wcellwidth((UCS) p->c);
3583 width += (ww >= 0 ? ww : 1);
3586 return(width);
3591 * This returns the virtual screen width in row from index a to b (exclusive).
3593 unsigned
3594 vcellwidth_a_to_b(int row, int a, int b)
3596 CELL *pstart, *pend;
3597 VIDEO *vp;
3599 if(row < 0 || row > term.t_nrow)
3600 return 0;
3602 if(a >= b)
3603 return 0;
3605 a = MIN(MAX(0, a), term.t_ncol-1);
3606 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3608 vp = vscreen[row];
3609 pstart = &vp->v_text[a];
3610 pend = &vp->v_text[b];
3612 return(cellwidth_ptr_to_ptr(pstart, pend));
3617 * This returns the physical screen width in row from index a to b (exclusive).
3619 unsigned
3620 pcellwidth_a_to_b(int row, int a, int b)
3622 CELL *pstart, *pend;
3623 VIDEO *vp;
3625 if(row < 0 || row > term.t_nrow)
3626 return 0;
3628 if(a >= b)
3629 return 0;
3631 a = MIN(MAX(0, a), term.t_ncol-1);
3632 b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
3634 vp = pscreen[row];
3635 pstart = &vp->v_text[a];
3636 pend = &vp->v_text[b];
3638 return(cellwidth_ptr_to_ptr(pstart, pend));
3643 index_from_col(int row, int col)
3645 CELL *p_start, *p_end, *p_limit;
3646 int w_consumed = 0, w, done = 0;
3648 if(row < 0 || row > term.t_nrow)
3649 return 0;
3651 p_end = p_start = pscreen[row]->v_text;
3652 p_limit = p_start + term.t_ncol;
3654 if(p_start)
3655 while(!done && p_end < p_limit && p_end->c && w_consumed <= col){
3656 w = wcellwidth((UCS) p_end->c);
3657 w = (w >= 0 ? w : 1);
3658 if(w_consumed + w <= col){
3659 w_consumed += w;
3660 ++p_end;
3662 else
3663 ++done;
3666 /* MIN and MAX just to be sure */
3667 return(MIN(MAX(0, p_end - p_start), term.t_ncol-1));
3670 #ifdef _WINDOWS
3672 void
3673 pico_config_menu_items (KEYMENU *keymenu)
3675 int i;
3676 KEYMENU *k;
3677 UCS key;
3679 mswin_menuitemclear ();
3681 /* keymenu's seem to be hardcoded at 12 entries. */
3682 for (i = 0, k = keymenu; i < 12; ++i, ++k) {
3683 if (k->name != NULL && k->label != NULL &&
3684 k->menuitem != KS_NONE) {
3686 if (k->name[0] == '^')
3687 key = CTRL | k->name[1];
3688 else if (strcmp(k->name, "Ret") == 0)
3689 key = '\r';
3690 else
3691 key = k->name[0];
3693 mswin_menuitemadd (key, k->label, k->menuitem, 0);
3699 * Update the scroll range and position. (exported)
3701 * This is where curbp->b_linecnt is really managed. With out this function
3702 * to count the number of lines when needed curbp->b_linecnt will never
3703 * really be correct. BUT, this function is only compiled into the
3704 * windows version, so b_linecnt will only ever be right in the windows
3705 * version. OK for now because that is the only version that
3706 * looks at b_linecnt.
3709 update_scroll (void)
3711 long scr_pos;
3712 long scr_range;
3713 LINE *lp;
3714 static LINE *last_top_line = NULL;
3715 static long last_scroll_pos = -1;
3718 if (ComposerEditing) {
3719 /* Editing header - don't allow scroll bars. */
3720 mswin_setscrollrange (0, 0);
3721 return(0);
3726 * Count the number of lines in the current buffer. Done when:
3728 * when told to recount: curbp->b_linecnt == -1
3729 * when the top line changed: curwp->w_linep != last_top_line
3730 * when we don't know the scroll pos: last_scroll_pos == -1
3732 * The first line in the list is a "place holder" line and is not
3733 * counted. The list is circular, when we return the to place
3734 * holder we have reached the end.
3736 if(curbp->b_linecnt == -1 || curwp->w_linep != last_top_line
3737 || last_scroll_pos == -1) {
3738 scr_range = 0;
3739 scr_pos = 0;
3740 for (lp = lforw (curbp->b_linep); lp != curbp->b_linep;
3741 lp = lforw (lp)) {
3742 if (lp == curwp->w_linep)
3743 scr_pos = scr_range;
3745 ++scr_range;
3748 curbp->b_linecnt = scr_range;
3749 last_scroll_pos = scr_pos;
3750 last_top_line = curwp->w_linep;
3754 * Set new scroll range and position.
3756 mswin_setscrollrange (curwp->w_ntrows - 2, curbp->b_linecnt - 1);
3757 mswin_setscrollpos (last_scroll_pos);
3758 return (0);
3760 #endif /* _WINDOWS */