1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: display.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2018 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"
34 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
39 #include "../pith/charconv/filesys.h"
40 #include "../pith/charconv/utf8.h"
43 void vtmove(int, int);
46 void updateline(int, CELL
*, CELL
*, short *, int);
47 void updatelinecolor(int, CELL
*, CELL
*, short *, int);
49 void mlputi(int, int);
50 void pprints(int, int);
51 void mlputli(long, int);
52 void showCompTitle(void);
54 int dumbroot(int, int);
55 int dumblroot(long, int);
56 unsigned cellwidth_ptr_to_ptr(CELL
*pstart
, CELL
*pend
);
57 unsigned vcellwidth_a_to_b(int row
, int a
, int b
);
58 int window_signature_block(WINDOW
*wp
);
60 void pico_config_menu_items (KEYMENU
*);
61 int update_scroll (void);
66 * Standard pico keymenus...
68 static KEYMENU menu_pico
[] = {
69 {"^G", N_("Get Help"), KS_SCREENHELP
}, {"^O", N_("WriteOut"), KS_SAVEFILE
},
70 {"^R", N_("Read File"), KS_READFILE
}, {"^Y", N_("Prev Pg"), KS_PREVPAGE
},
71 {"^K", N_("Cut Text"), KS_NONE
}, {"^C", N_("Cur Pos"), KS_CURPOSITION
},
72 {"^X", N_("Exit"), KS_EXIT
}, {"^J", N_("Justify"), KS_JUSTIFY
},
73 {"^W", N_("Where is"), KS_WHEREIS
}, {"^V", N_("Next Pg"), KS_NEXTPAGE
},
74 {"^U", NULL
, KS_NONE
},
76 {"^T", N_("To Spell"), KS_SPELLCHK
}
78 {"^D", N_("Del Char"), KS_NONE
}
84 static KEYMENU menu_compose
[] = {
85 {"^G", N_("Get Help"), KS_SCREENHELP
}, {"^X", NULL
, KS_SEND
},
86 {"^R", N_("Read File"), KS_READFILE
}, {"^Y", N_("Prev Pg"), KS_PREVPAGE
},
87 {"^K", N_("Cut Text"), KS_NONE
}, {"^O", N_("Postpone"), KS_POSTPONE
},
88 /* TRANSLATORS: Justify is to reformat a paragraph automatically */
89 {"^C", N_("Cancel"), KS_CANCEL
}, {"^J", N_("Justify"), KS_JUSTIFY
},
90 {NULL
, NULL
, KS_NONE
}, {"^V", N_("Next Pg"), KS_NEXTPAGE
},
91 {"^U", NULL
, KS_NONE
},
93 {"^T", N_("To Spell"), KS_SPELLCHK
}
95 {"^D", N_("Del Char"), KS_NONE
}
104 * Definition's for pico's modeline
106 #define PICO_TITLE " UW PICO %s"
107 #define PICO_MOD_MSG "Modified"
108 #define PICO_NEWBUF_MSG "New Buffer"
110 #define WFDEBUG 0 /* Window flag debug. */
112 #define VFCHG 0x0001 /* Changed flag */
113 #define VFEXT 0x0002 /* extended (beyond column 80) */
114 #define VFSIG 0x0004 /* in signature block */
116 int vtrow
= 0; /* Row location of SW cursor */
117 int vtcol
= 0; /* Column location of SW cursor */
118 int vtind
= 0; /* Index into row array of SW cursor */
119 int ttrow
= FARAWAY
; /* Row location of HW cursor */
120 int ttcol
= FARAWAY
; /* Column location of HW cursor */
121 int lbound
= 0; /* leftmost column of current line
124 VIDEO
**vscreen
; /* Virtual screen. */
125 VIDEO
**pscreen
; /* Physical screen. */
129 * Initialize the data structures used by the display code. The edge vectors
130 * used to access the screens are set up. The operating system's terminal I/O
131 * channel is set up. All the other things get initialized at compile time.
132 * The original window has "WFCHG" set, so that it will get completely
133 * redrawn on the first call to "update".
146 vtterminalinfo(gmode
& MDTCAPWINS
);
150 (*term
.t_rev
)(FALSE
);
151 vscreen
= (VIDEO
**) malloc((term
.t_nrow
+1)*sizeof(VIDEO
*));
152 memset(vscreen
, 0, (term
.t_nrow
+1)*sizeof(VIDEO
*));
153 if (vscreen
== NULL
){
154 emlwrite("Allocating memory for virtual display failed.", NULL
);
158 pscreen
= (VIDEO
**) malloc((term
.t_nrow
+1)*sizeof(VIDEO
*));
159 memset(pscreen
, 0, (term
.t_nrow
+1)*sizeof(VIDEO
*));
160 if (pscreen
== NULL
){
161 free((void *)vscreen
);
162 emlwrite("Allocating memory for physical display failed.", NULL
);
167 for (i
= 0; i
<= term
.t_nrow
; ++i
) {
168 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
171 free((void *)vscreen
);
172 free((void *)pscreen
);
173 emlwrite("Allocating memory for virtual display lines failed.",
178 for(j
= 0; j
< term
.t_ncol
; j
++)
185 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
188 free((void *)vscreen
[i
]);
190 free((void *)vscreen
[i
]);
191 free((void *)pscreen
[i
]);
194 free((void *)vscreen
);
195 free((void *)pscreen
);
196 emlwrite("Allocating memory for physical display lines failed.",
201 for(j
= 0; j
< term
.t_ncol
; j
++)
213 vtterminalinfo(int termcap_wins
)
215 return((term
.t_terminalinfo
) ? (*term
.t_terminalinfo
)(termcap_wins
)
216 : (Pmaster
? 0 : TRUE
));
221 * Clean up the virtual terminal system, in anticipation for a return to the
222 * operating system. Move down to the last line and clear it out (the next
223 * system prompt will be written in the line). Shut down the channel to the
229 movecursor(term
.t_nrow
-1, 0);
231 movecursor(term
.t_nrow
, 0);
238 * Set the virtual cursor to the specified row and column on the virtual
239 * screen. There is no checking for nonsense values; this might be a good
240 * idea during the early stages.
243 vtmove(int row
, int col
)
254 * This is unused so don't worry about it.
262 * Write a character to the virtual screen. The virtual row and column are
263 * updated. If the line is too long put a "$" in the last column. This routine
264 * only puts printing characters into the virtual terminal buffers. Only
265 * column overflow is checked.
279 if (vtcol
>= term
.t_ncol
) {
281 * What's this supposed to be doing? This sets vtcol
282 * to the even multiple of 8 >= vtcol. Why are we doing that?
283 * Must make tab work correctly.
291 vtcol
= (vtcol
+ 0x07) & ~0x07;
295 * If we get to here that means that there must be characters
296 * past the right hand edge, so we want to put a $ character
297 * in the last visible character. It would be nice to replace
298 * the last visible character by a double-$ if it is double-width
299 * but we aren't doing that because we'd have to add up the widths
300 * starting at the left hand margin each time through.
302 if(vtind
> 0 && vtind
<= term
.t_ncol
)
303 vp
->v_text
[vtind
-1] = ac
;
305 else if (c
.c
== '\t') {
309 while (((vtcol
+ (vtrow
==currow
? lbound
: 0)) & 0x07) != 0 && vtcol
< term
.t_ncol
);
311 else if (ISCONTROL(c
.c
)){
314 ac
.c
= ((c
.c
& 0x7f) | 0x40);
320 * Have to worry about what happens if we skip over 0
321 * with a double-width character. There may be a better
322 * place to be setting vtind, or maybe we could make do
326 w
= wcellwidth((UCS
) c
.c
);
327 w
= (w
>= 0 ? w
: 1);
329 if(vtcol
== 0 || (vtcol
< 0 && vtcol
+ w
== 1)){
336 * Double-width character overlaps right edge.
337 * Replace it with a $.
339 if(vtcol
+ w
> term
.t_ncol
){
345 if(vtind
>= 0 && vtind
< term
.t_ncol
)
346 vp
->v_text
[vtind
++] = c
;
354 * Erase from the end of the software cursor to the end of the line on which
355 * the software cursor is located.
368 while (vtind
< term
.t_ncol
)
369 vp
->v_text
[vtind
++] = c
;
375 window_signature_block(WINDOW
*wp
)
378 int in_sig
, is_sig_start
;
382 lp
= lforw(wp
->w_bufp
->b_linep
);
385 in_sig
= lback(lp
) == wp
->w_bufp
->b_linep
? 0 : lback(lp
)->l_sig
;
387 if(llength(lp
) == 3){
388 if(lgetc(lp
, 0).c
== '-'
389 && lgetc(lp
, 1).c
== '-'
390 && lgetc(lp
, 2).c
== ' '){
397 if(is_sig_start
== 0) in_sig
= 0;
400 if(lp
->l_sig
!= in_sig
)
404 } while(lp
!= wp
->w_bufp
->b_linep
);
411 * Make sure that the display is right. This is a three part process. First,
412 * scan through all of the windows looking for dirty ones. Check the framing,
413 * and refresh the screen. Second, make sure that "currow" and "curcol" are
414 * correct for the current window. Third, make the virtual and physical
430 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
438 /* This tells our MS Windows module to not bother updating the
439 * cursor position while a massive screen update is in progress.
441 mswin_beginupdate ();
445 * BUG: setting and unsetting whole region at a time is dumb. fix this.
455 /* Look at any window with update flags set on. */
457 if(pcolors
&& (repaint
= window_signature_block(wp
))){
459 wp
->w_flag
|= WFEDIT
| WFHARD
;
461 if (wp
->w_flag
!= 0){
462 /* If not force reframe, check the framing. */
464 if ((wp
->w_flag
& WFFORCE
) == 0){
467 for (i
= 0; i
< wp
->w_ntrows
; ++i
){
468 if (lp
== wp
->w_dotp
)
471 if (lp
== wp
->w_bufp
->b_linep
)
478 /* Not acceptable, better compute a new value for the line at the
479 * top of the window. Then set the "WFHARD" flag to force full
487 if (i
>= wp
->w_ntrows
)
496 else if(TERM_OPTIMIZE
){
498 * find dotp, if its been moved just above or below the
499 * window, use scrollxxx() to facilitate quick redisplay...
501 lp
= lforw(wp
->w_dotp
);
502 if(lp
!= wp
->w_dotp
){
503 if(lp
== wp
->w_linep
&& lp
!= wp
->w_bufp
->b_linep
){
508 for(j
=0;j
< wp
->w_ntrows
; ++j
){
509 if(lp
!= wp
->w_bufp
->b_linep
)
514 if(lp
== wp
->w_dotp
&& j
== wp
->w_ntrows
)
518 j
= i
= wp
->w_ntrows
/2;
525 while (i
!= 0 && lback(lp
) != wp
->w_bufp
->b_linep
){
531 * this is supposed to speed things up by using tcap sequences
532 * to efficiently scroll the terminal screen. the thinking here
533 * is that its much faster to update pscreen[] than to actually
534 * write the stuff to the screen...
538 case 1: /* scroll text down */
539 j
= j
-i
+1; /* add one for dot line */
541 * do we scroll down the header as well? Well, only
542 * if we're not editing the header, we've backed up
543 * to the top, and the composer is not being
546 if(Pmaster
&& Pmaster
->headents
&& !ComposerEditing
547 && (lback(lp
) == wp
->w_bufp
->b_linep
)
548 && (ComposerTopLine
== COMPOSER_TOP_LINE
))
549 j
+= entry_line(1000, TRUE
); /* Never > 1000 headers */
551 scrolldown(wp
, -1, j
);
553 case 2: /* scroll text up */
554 j
= wp
->w_ntrows
- (j
-i
); /* we chose new top line! */
557 * do we scroll down the header as well? Well, only
558 * if we're not editing the header, we've backed up
559 * to the top, and the composer is not being
563 && (ComposerTopLine
!= COMPOSER_TOP_LINE
))
564 scrollup(wp
, COMPOSER_TOP_LINE
,
565 j
+entry_line(1000, TRUE
));
578 wp
->w_flag
|= WFHARD
; /* Force full. */
581 * if the line at the top of the page is the top line
582 * in the body, show the header...
584 if(Pmaster
&& Pmaster
->headents
&& !ComposerEditing
){
585 if(lback(wp
->w_linep
) == wp
->w_bufp
->b_linep
){
586 if(ComposerTopLine
== COMPOSER_TOP_LINE
){
587 i
= term
.t_nrow
- 2 - term
.t_mrow
- HeaderLen();
588 if(i
> 0 && nlforw() >= i
) { /* room for header ? */
589 if((i
= nlforw()/2) == 0 && term
.t_nrow
&1)
591 while(wp
->w_linep
!= wp
->w_bufp
->b_linep
&& i
--)
592 wp
->w_linep
= lforw(wp
->w_linep
);
600 if(ComposerTopLine
!= COMPOSER_TOP_LINE
)
601 ToggleHeader(0); /* hide it ! */
605 /* Try to use reduced update. Mode line update has its own special
606 * flag. The fast update is used if the only thing to do is within
612 if ((wp
->w_flag
& ~WFMODE
) == WFEDIT
){
613 while (lp
!= wp
->w_dotp
){
617 vscreen
[i
]->v_flag
|= (lp
->l_sig
? VFSIG
: 0)| VFCHG
;
618 /* compute physical length of line in screen */
619 vscreen
[i
]->v_length
= 0;
620 for (j
= 0; vscreen
[i
]->v_length
< term
.t_ncol
621 && j
< llength(lp
); ++j
){
624 vscreen
[i
]->v_length
|= 0x07;
625 vscreen
[i
]->v_length
++;
627 else if(ISCONTROL(c
.c
)){
628 vscreen
[i
]->v_length
+= 2;
633 w
= wcellwidth((UCS
) c
.c
);
634 vscreen
[i
]->v_length
+= (w
>= 0 ? w
: 1);
639 for (j
= 0; j
< llength(lp
); ++j
)
640 vtputc(lgetc(lp
, j
));
643 else if ((wp
->w_flag
& (WFEDIT
| WFHARD
)) != 0){
644 while (i
< wp
->w_toprow
+wp
->w_ntrows
){
645 vscreen
[i
]->v_flag
|= (lp
->l_sig
? VFSIG
: 0 )| VFCHG
;
646 /* compute physical length of line in screen */
647 vscreen
[i
]->v_length
= 0;
648 for (j
= 0; vscreen
[i
]->v_length
< term
.t_ncol
649 && j
< llength(lp
); ++j
){
652 vscreen
[i
]->v_length
|= 0x07;
653 vscreen
[i
]->v_length
++;
655 else if(ISCONTROL(c
.c
)){
656 vscreen
[i
]->v_length
+= 2;
661 w
= wcellwidth((UCS
) c
.c
);
662 vscreen
[i
]->v_length
+= (w
>= 0 ? w
: 1);
667 /* if line has been changed */
668 if (lp
!= wp
->w_bufp
->b_linep
){
669 for (j
= 0; j
< llength(lp
); ++j
)
670 vtputc(lgetc(lp
, j
));
680 if ((wp
->w_flag
&WFMODE
) != 0)
693 /* and onward to the next window */
697 /* Always recompute the row and column number of the hardware cursor. This
698 * is the only update for simple moves.
701 currow
= curwp
->w_toprow
;
703 while (lp
!= curwp
->w_dotp
){
711 while (i
< curwp
->w_doto
){
718 else if(ISCONTROL(c
.c
)){
724 w
= wcellwidth((UCS
) c
.c
);
725 curcol
+= (w
>= 0 ? w
: 1);
729 if (curcol
>= term
.t_ncol
) { /* extended line. */
730 /* flag we are extended and changed */
731 vscreen
[currow
]->v_flag
|= VFEXT
| VFCHG
;
732 updext(); /* and output extended line */
734 lbound
= 0; /* not extended line */
736 /* make sure no lines need to be de-extended because the cursor is
746 while (i
< wp
->w_toprow
+ wp
->w_ntrows
) {
747 if (vscreen
[i
]->v_flag
& VFEXT
) {
748 /* always flag extended lines as changed */
749 vscreen
[i
]->v_flag
|= VFCHG
;
750 if ((wp
!= curwp
) || (lp
!= wp
->w_dotp
) ||
751 (curcol
< term
.t_ncol
)) {
753 for (j
= 0; j
< llength(lp
); ++j
)
754 vtputc(lgetc(lp
, j
));
757 /* this line no longer is extended */
758 vscreen
[i
]->v_flag
&= ~VFEXT
;
764 /* and onward to the next window */
768 /* Special hacking if the screen is garbage. Clear the hardware screen,
769 * and update your copy to agree with it. Set all the virtual screen
770 * change bits, to force a full update.
773 if (sgarbf
!= FALSE
){
779 if(ComposerTopLine
!= COMPOSER_TOP_LINE
){
780 UpdateHeader(0); /* arrange things */
781 PaintHeader(COMPOSER_TOP_LINE
, TRUE
);
785 * since we're using only a portion of the screen and only
786 * one buffer, only clear enough screen for the current window
787 * which is to say the *only* window.
789 for(i
=wheadp
->w_toprow
;i
<=term
.t_nrow
; i
++){
792 vscreen
[i
]->v_flag
|= VFCHG
;
794 rv
= (*Pmaster
->showmsg
)('X' & 0x1f); /* ctrl-L */
796 picosigs(); /* restore altered handlers */
797 if(rv
) /* Did showmsg corrupt the display? */
798 PaintBody(0); /* Yes, repaint */
799 movecursor(wheadp
->w_toprow
, 0);
804 for (i
= 0; i
< term
.t_nrow
-term
.t_mrow
; i
++){
805 vscreen
[i
]->v_flag
|= VFCHG
;
807 for (j
= 0; j
< term
.t_ncol
; j
++)
815 movecursor(0, 0); /* Erase the screen. */
820 sgarbf
= FALSE
; /* Erase-page clears */
821 mpresf
= FALSE
; /* the message area. */
826 sgarbk
= TRUE
; /* fix the keyhelp as well...*/
829 /* Make sure that the physical and virtual displays agree. Unlike before,
830 * the "updateline" code is only called with a line that has been updated
838 if (term
.t_nrow
> term
.t_mrow
)
839 c
.c
= term
.t_nrow
- term
.t_mrow
;
843 for (; i
< (int)c
.c
; ++i
){
847 /* for each line that needs to be updated, or that needs its
848 reverse video status changed, call the line updater */
862 updateline(i
, &vp1
->v_text
[0], &vp2
->v_text
[0], &vp1
->v_flag
, vp1
->v_length
);
870 movecursor(term
.t_nrow
-1, 0);
872 movecursor(term
.t_nrow
, 0);
877 /* TRANSLATORS: UnJustify means undo the previous
879 menu_pico
[UNCUT_KEY
].label
= N_("UnJustify");
880 if(!(lastflag
&CFFLBF
)){
881 emlwrite(_("Can now UnJustify!"), NULL
);
882 mpresf
= FARAWAY
; /* remove this after next keystroke! */
886 menu_pico
[UNCUT_KEY
].label
= N_("UnCut Text");
894 emlwrite(_("Can now UnJustify!"), NULL
);
895 mpresf
= FARAWAY
; /* remove this after next keystroke! */
898 /* Finally, update the hardware cursor and flush out buffers. */
900 movecursor(currow
, curcol
- lbound
);
905 * Update the scroll bars. This function is where curbp->b_linecnt
906 * is really managed. See update_scroll.
914 /* updext - update the extended line which the cursor is currently
915 * on at a column greater than the terminal width. The line
916 * will be scrolled right or left to let the user see where
922 int rcursor
; /* real cursor location */
923 LINE
*lp
; /* pointer to current line */
924 int j
; /* index into line */
929 * Calculate what column the real cursor will end up in.
930 * The cursor will be in the rcursor'th column. So if we're
931 * counting columns 0 1 2 3 and rcursor is 8, then rcursor
932 * will be over cell 7.
934 * What this effectively does is to scroll the screen as we're
935 * moving to the right when the cursor first passes off the
936 * screen's right edge. It would be nice if it did the same
937 * thing coming back to the left. Instead, in order that the
938 * screen's display depends only on the curcol and not on
939 * how we got there, the screen scrolls when we pass the
940 * t_margin column. It's also kind of funky that you can't
941 * see the character under the $ but you can delete it.
943 rcursor
= ((curcol
- term
.t_ncol
) % (term
.t_ncol
- term
.t_margin
+ 1)) + term
.t_margin
;
944 lbound
= curcol
- rcursor
+ 1;
947 * Make sure lbound is set so that a double-width character does
948 * not straddle the boundary. If it does, move over one cell.
950 lp
= curwp
->w_dotp
; /* line to output */
951 for (j
=0; j
<llength(lp
) && w
< lbound
; ++j
){
952 ww
= wcellwidth((UCS
) lgetc(lp
, j
).c
);
953 w
+= (ww
>= 0 ? ww
: 1);
960 /* scan through the line outputing characters to the virtual screen
961 * once we reach the left edge
963 vtmove(currow
, -lbound
); /* start scanning offscreen */
964 for (j
=0; j
<llength(lp
); ++j
) /* until the end-of-line */
965 vtputc(lgetc(lp
, j
));
967 /* truncate the virtual line */
970 /* and put a '$' in column 1, may have to adjust curcol */
971 w
= wcellwidth((UCS
) vscreen
[currow
]->v_text
[0].c
);
972 vscreen
[currow
]->v_text
[0].c
= '$';
973 vscreen
[currow
]->v_text
[0].a
= 0;
976 * We want to put $ in the first two columns so that it
977 * takes up the right amount of space, but that means we
978 * have to scoot the real characters over one slot.
980 for (j
= term
.t_ncol
-1; j
>= 2; --j
)
981 vscreen
[currow
]->v_text
[j
] = vscreen
[currow
]->v_text
[j
-1];
983 vscreen
[currow
]->v_text
[1].c
= '$';
984 vscreen
[currow
]->v_text
[1].a
= 0;
988 /* update line color, to be executed to update lines when color is on */
990 updatelinecolor (int row
, CELL vline
[], CELL pline
[], short *flags
, int len
)
992 CELL
*cp1
, *cp2
, *cp3
, *cp4
, *cp5
, *cp6
, *cp7
;
993 int nbflag
; /* non-blanks to the right flag? */
995 int in_quote
, quote_found
= 0, level
;
996 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
997 COLOR_PAIR
*qcolor
= NULL
, *lastc
= NULL
, *pcolor
= NULL
;
998 int first
= 1, lastattr
= -1, change
= 0;
1000 if(pcolors
== NULL
){
1001 updateline(row
, vline
, pline
, flags
, len
);
1006 lastc
= pico_get_cur_color();
1008 /* set up pointers to virtual and physical lines */
1011 cp3
= &vline
[term
.t_ncol
];
1012 cp4
= &pline
[term
.t_ncol
];
1014 if(cellwidth_ptr_to_ptr(cp1
, cp3
) == cellwidth_ptr_to_ptr(cp2
, cp4
))
1015 while (cp3
!= cp1
&& cp3
[-1].c
== cp4
[-1].c
&& cp3
[-1].a
== cp4
[-1].a
) {
1018 if (cp3
[0].c
!= ' ' || cp3
[0].a
!= 0) /* Note if any nonblank */
1019 nbflag
= TRUE
; /* in right match. */
1024 if (nbflag
== FALSE
&& TERM_EOLEXIST
) { /* Erase to EOL ? */
1025 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1028 if (cp3
-cp5
<= 3) /* Use only if erase is */
1029 cp5
= cp3
; /* fewer characters. */
1032 /* go to start of line */
1038 w1
= cellwidth_ptr_to_ptr(cp1
, cp3
);
1039 w2
= cellwidth_ptr_to_ptr(cp2
, cp2
+ (cp3
-cp1
));
1041 if(w1
< w2
|| (nbflag
&& w1
!= w2
)){
1045 * Draw all of the characters starting with cp1
1046 * until we get to all spaces, then clear to the end of
1047 * line from there. Watch out we don't run over the
1048 * right hand edge, which shouldn't happen.
1050 * Set cp5 to the first of the repeating spaces.
1052 cp5
= &vline
[term
.t_ncol
];
1053 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1058 * In the !nbflag case we want spaces from cp5 on.
1059 * Setting cp3 to something different from cp5 triggers
1060 * the clear to end of line below.
1062 if(cellwidth_ptr_to_ptr(&vline
[0], cp5
) < term
.t_ncol
)
1069 * No peeol so draw all the way to the edge whether they
1070 * are spaces or not.
1073 for(w
= 0; w
< term
.t_ncol
; cp3
++){
1076 ww
= wcellwidth((UCS
) cp3
->c
);
1077 w
+= (ww
>= 0 ? ww
: 1);
1085 if(row
!= 0 && len
< term
.t_ncol
)
1090 while (cp1
!= cp5
){ /* Ordinary. */
1098 change
= lastattr
!= cp1
->a
;
1102 pico_set_colorp(pcolors
->tbcp
, PSC_NONE
);
1103 else if(row
< term
.t_nrow
- 2)
1104 pcolor
= (*flags
& VFSIG
) ? pcolors
->sbcp
: pcolors
->ntcp
;
1106 if(cp1
->c
!= '>' && cp1
->c
!= ' ')
1108 else if (in_quote
&& cp1
->c
== '>' && pcolors
!= NULL
)
1109 level
= (level
+ 1) % 3;
1111 if(level
== 0) pcolor
= pcolors
->qlcp
;
1112 else if(level
== 1) pcolor
= pcolors
->qllcp
;
1113 else if(level
== 2) pcolor
= pcolors
->qlllcp
;
1116 pcolor
= pcolors
->rtcp
; /* pcolor = proposed color */
1118 pico_set_colorp(pcolor
, PSC_NONE
);
1120 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1123 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1124 (*term
.t_putchar
)(cp1
->c
);
1126 ww
= wcellwidth((UCS
) cp1
->c
);
1127 ttcol
+= (ww
>= 0 ? ww
: 1);
1133 (void)pico_set_colorp(lastc
, PSC_NONE
);
1134 free_color_pair(&lastc
);
1137 (*term
.t_rev
)(0); /* turn off inverse anyway! */
1139 if (cp5
!= cp3
|| cleartoeol
) /* Erase. */
1142 *flags
&= ~(VFCHG
|VFSIG
); /* flag this line is changed */
1148 * Update a single line. This does not know how to use insert or delete
1149 * character sequences; we are using VT52 functionality. Update the physical
1150 * row and column variables.
1153 updateline(int row
, /* row on screen */
1154 CELL vline
[], /* what we want it to end up as */
1155 CELL pline
[], /* what it looks like now */
1159 CELL
*cp1
, *cp2
, *cp3
, *cp4
, *cp5
, *cp6
, *cp7
;
1161 int nbflag
; /* non-blanks to the right flag? */
1164 if(row
< 0 || row
> term
.t_nrow
)
1167 if((Pmaster
&& Pmaster
->colors
) || Pcolors
){
1168 updatelinecolor(row
, vline
, pline
, flags
, len
);
1172 /* set up pointers to virtual and physical lines */
1175 cp3
= &vline
[term
.t_ncol
];
1177 /* advance past any common chars at the left */
1178 while (cp1
!= cp3
&& cp1
[0].c
== cp2
[0].c
&& cp1
[0].a
== cp2
[0].a
) {
1183 /* This can still happen, even though we only call this routine on changed
1184 * lines. A hard update is always done when a line splits, a massive
1185 * change is done, or a buffer is displayed twice. This optimizes out most
1186 * of the excess updating. A lot of computes are used, but these tend to
1187 * be hard operations that do a lot of update, so I don't really care.
1189 /* if both lines are the same, no update needs to be done */
1191 *flags
&= ~VFCHG
; /* mark it clean */
1195 /* find out if there is a match on the right */
1197 cp3
= &vline
[term
.t_ncol
];
1198 cp4
= &pline
[term
.t_ncol
];
1200 if(cellwidth_ptr_to_ptr(cp1
, cp3
) == cellwidth_ptr_to_ptr(cp2
, cp4
))
1201 while (cp3
[-1].c
== cp4
[-1].c
&& cp3
[-1].a
== cp4
[-1].a
) {
1204 if (cp3
[0].c
!= ' ' || cp3
[0].a
!= 0) /* Note if any nonblank */
1205 nbflag
= TRUE
; /* in right match. */
1210 if (nbflag
== FALSE
&& TERM_EOLEXIST
) { /* Erase to EOL ? */
1211 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1214 if (cp3
-cp5
<= 3) /* Use only if erase is */
1215 cp5
= cp3
; /* fewer characters. */
1218 /* go to start of differences */
1219 movecursor(row
, cellwidth_ptr_to_ptr(&vline
[0], cp1
));
1221 if (!nbflag
) { /* use insert or del char? */
1226 &&(cp7
!=cp2
&& cp6
[0].c
==cp7
[-1].c
&& cp6
[0].a
==cp7
[-1].a
)){
1227 while (cp7
!= cp2
&& cp6
[0].c
==cp7
[-1].c
&& cp6
[0].a
==cp7
[-1].a
){
1232 if (cp7
==cp2
&& cp4
-cp2
> 3){
1235 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1236 o_insert((UCS
) cp1
->c
); /* insert the char */
1237 ww
= wcellwidth((UCS
) cp1
->c
);
1238 ttcol
+= (ww
>= 0 ? ww
: 1);
1239 display
= FALSE
; /* only do it once!! */
1242 else if(TERM_DELCHAR
&& cp3
!= cp1
&& cp7
[0].c
== cp6
[-1].c
1243 && cp7
[0].a
== cp6
[-1].a
){
1244 while (cp6
!= cp1
&& cp7
[0].c
==cp6
[-1].c
&& cp7
[0].a
==cp6
[-1].a
){
1249 if (cp6
==cp1
&& cp5
-cp6
> 3){
1252 w
= wcellwidth((UCS
) cp7
[0].c
);
1253 w
= (w
>= 0 ? w
: 1);
1254 while(w
-- > 0) /* in case double-width char */
1255 o_delete(); /* delete the char */
1256 display
= FALSE
; /* only do it once!! */
1261 if(cp1
!= cp5
&& display
){
1265 * If we need to copy characters from cp1 to cp2 and
1266 * we need to display them, then we have to worry about
1267 * the characters that we are replacing being of a different
1268 * width than the new characters, else the display may be
1271 * If the new width (w1) is less than the old width, that means
1272 * we will leave behind some old remnants if we aren't careful.
1273 * If the new width is larger than the old width, we have to
1274 * make sure we draw the characters all the way to the end
1275 * in order to get it right. Take advantage of clear to end
1276 * of line if we have it.
1278 w1
= cellwidth_ptr_to_ptr(cp1
, cp3
);
1279 w2
= cellwidth_ptr_to_ptr(cp2
, cp2
+ (cp3
-cp1
));
1281 if(w1
< w2
|| (nbflag
&& w1
!= w2
)){
1285 * Draw all of the characters starting with cp1
1286 * until we get to all spaces, then clear to the end of
1287 * line from there. Watch out we don't run over the
1288 * right hand edge, which shouldn't happen.
1290 * Set cp5 to the first of the repeating spaces.
1292 cp5
= &vline
[term
.t_ncol
];
1293 while (cp5
!= cp1
&& cp5
[-1].c
== ' ' && cp5
[-1].a
== 0)
1298 * In the !nbflag case we want spaces from cp5 on.
1299 * Setting cp3 to something different from cp5 triggers
1300 * the clear to end of line below.
1302 if(cellwidth_ptr_to_ptr(&vline
[0], cp5
) < term
.t_ncol
)
1309 * No peeol so draw all the way to the edge whether they
1310 * are spaces or not.
1313 for(w
= 0; w
< term
.t_ncol
; cp3
++){
1316 ww
= wcellwidth((UCS
) cp3
->c
);
1317 w
+= (ww
>= 0 ? ww
: 1);
1325 while (cp1
!= cp5
) { /* Ordinary. */
1329 (*term
.t_rev
)(cp1
->a
); /* set inverse for this char */
1330 (*term
.t_putchar
)(cp1
->c
);
1333 ww
= wcellwidth((UCS
) cp1
->c
);
1334 ttcol
+= (ww
>= 0 ? ww
: 1);
1339 (*term
.t_rev
)(0); /* turn off inverse anyway! */
1341 if (cp5
!= cp3
|| cleartoeol
) { /* Erase. */
1349 *flags
&= ~VFCHG
; /* flag this line is changed */
1354 * Redisplay the mode line for the window pointed to by the "wp". This is the
1355 * only routine that has any idea of how the modeline is formatted. You can
1356 * change the modeline format by hacking at this routine. Called by "update"
1357 * any time there is a dirty window.
1360 modeline(WINDOW
*wp
)
1366 menu_compose
[EXIT_KEY
].label
= (Pmaster
->headents
)
1367 ? N_("Send") :N_("Exit");
1368 menu_compose
[PSTPN_KEY
].name
= (Pmaster
->headents
)
1370 menu_compose
[PSTPN_KEY
].label
= (Pmaster
->headents
)
1371 ? N_("Postpone") : NULL
;
1372 menu_compose
[WHERE_KEY
].name
= (Pmaster
->alt_ed
) ? "^_" : "^W";
1373 menu_compose
[WHERE_KEY
].label
= (Pmaster
->alt_ed
) ? N_("Alt Edit")
1375 KS_OSDATASET(&menu_compose
[WHERE_KEY
],
1376 (Pmaster
->alt_ed
) ? KS_ALTEDITOR
: KS_WHEREIS
);
1377 menu_compose
[UNCUT_KEY
].label
= (thisflag
&CFFILL
) ? N_("UnJustify")
1379 wkeyhelp(menu_compose
);
1384 char t1
[NLINE
], t2
[NLINE
], t3
[NLINE
], tline
[NLINE
];
1385 int w1
, w2
, w3
, w1_to_2
, w2_to_3
, w3_to_r
;
1390 vscreen
[0]->v_flag
|= VFCHG
; /* Redraw next time. */
1391 vtmove(0, 0); /* Seek to right line. */
1393 snprintf(t1
, sizeof(t1
), PICO_TITLE
, version
); /* write version */
1396 if(bp
->b_fname
[0]) /* File name? */
1397 snprintf(t2
, sizeof(t2
), "File: %s", bp
->b_fname
);
1399 strncpy(t2
, PICO_NEWBUF_MSG
, sizeof(t2
));
1400 t2
[sizeof(t2
)-1] = '\0';
1403 if(bp
->b_flag
&BFCHG
){ /* "MOD" if changed. */
1404 strncpy(t3
, PICO_MOD_MSG
, sizeof(t3
));
1405 t3
[sizeof(t3
)-1] = '\0';
1410 #define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
1411 #define ALLBUTSPACE (w1+w2+w3+w3_to_r)
1413 w1
= utf8_width(t1
);
1414 w2
= utf8_width(t2
);
1415 w3
= utf8_width(t3
);
1416 w1_to_2
= w2_to_3
= 1; /* min values for separation */
1419 if(ALLOFTHEM
<= term
.t_ncol
){ /* everything fits */
1420 w1_to_2
= (term
.t_ncol
- ALLBUTSPACE
)/2;
1421 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1426 if(ALLOFTHEM
<= term
.t_ncol
)
1427 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1429 w1
= w1_to_2
= w3_to_r
= 0;
1430 if(ALLOFTHEM
<= term
.t_ncol
)
1431 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1434 snprintf(t2
, sizeof(t2
), "%s", bp
->b_fname
);
1435 w2
= utf8_width(t2
);
1438 if(ALLOFTHEM
<= term
.t_ncol
)
1439 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1442 if(bp
->b_fname
[0] && ALLOFTHEM
<= term
.t_ncol
){
1443 /* reduce size of file */
1444 w2
= term
.t_ncol
- (ALLOFTHEM
- w2
);
1445 t2
[0] = t2
[1] = t2
[2] = '.';
1446 utf8_to_width_rhs(t2
+3, bp
->b_fname
, sizeof(t2
)-3, w2
-3);
1447 w2
= utf8_width(t2
);
1450 w2
= utf8_width(t2
);
1452 if(ALLOFTHEM
<= term
.t_ncol
)
1453 w2_to_3
= term
.t_ncol
- (ALLBUTSPACE
+ w1_to_2
);
1455 w1
= w1_to_2
= w2
= w2_to_3
= w3_to_r
= 0;
1456 if(ALLOFTHEM
<= term
.t_ncol
)
1457 w2_to_3
= term
.t_ncol
- ALLBUTSPACE
;
1466 utf8_snprintf(tline
, sizeof(tline
),
1467 "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
1469 w1_to_2
, w1_to_2
, "",
1471 w2_to_3
, w2_to_3
, "",
1473 w3_to_r
, w3_to_r
, "");
1476 if(utf8_width(tline
) <= term
.t_ncol
)
1477 ucs
= utf8_to_ucs4_cpystr(tline
);
1485 while((c
.c
= CELLMASK
& *ucsp
++))
1488 fs_give((void **) &ucs
);
1496 * Send a command to the terminal to move the hardware cursor to row "row"
1497 * and column "col". The row and column arguments are origin 0. Optimize out
1498 * random calls. Update "ttrow" and "ttcol".
1501 movecursor(int row
, int col
)
1503 if (row
!=ttrow
|| col
!=ttcol
) {
1506 (*term
.t_move
)(MIN(MAX(row
,0),term
.t_nrow
), MIN(MAX(col
,0),term
.t_ncol
-1));
1512 * Erase any sense we have of the cursor's HW location...
1517 ttrow
= ttcol
= FARAWAY
;
1521 get_cursor(int *row
, int *col
)
1531 * Erase the message line. This is a special routine because the message line
1532 * is not considered to be part of the virtual screen. It always works
1533 * immediately; the terminal buffer is flushed via a call to the flusher.
1538 if (term
.t_nrow
< term
.t_mrow
)
1541 movecursor(term
.t_nrow
- term
.t_mrow
, 0);
1543 if (TERM_EOLEXIST
== TRUE
)
1546 if(ttrow
== term
.t_nrow
){
1547 while(ttcol
++ < term
.t_ncol
-1)
1548 (*term
.t_putchar
)(' ');
1551 while(ttcol
++ < term
.t_ncol
) /* track's ttcol */
1552 (*term
.t_putchar
)(' ');
1560 /* returns the chosen dictionary. If one was already chosen
1564 speller_choice(char **sp_list
, int *choice
)
1569 if(sp_list
== NULL
|| sp_list
[0] == NULL
|| sp_list
[0][0] == '\0')
1572 if(choice
&& *choice
>= 0)
1573 return sp_list
[*choice
];
1575 for(cnt
= 0; sp_list
[cnt
] != NULL
&& sp_list
[cnt
][0] != '\0'; cnt
++)
1578 if(cnt
> 10) /* only the first 10 dictionaries */
1581 if(cnt
== 1) /* only one dictionary? choose it! */
1584 if(ch_dict
> cnt
- 1) /* choose again in case something changed */
1587 if(ch_dict
< 0){ /* not a choice yet? do one now! */
1592 EXTRAKEYS menu_dictionary
[] = {
1605 for(i
= 0; i
< cnt
; i
++)
1606 menu_dictionary
[i
].label
= sp_list
[i
];
1609 menu_dictionary
[cnt
].name
= NULL
;
1612 /* write the prompt in utf8, and let internal functions translate it to ucs4 */
1613 ucs4_prompt
= utf8_to_ucs4_cpystr(_("Choose Dictionary: "));
1615 i
= mlchoose(ucs4_prompt
, menu_dictionary
);
1617 if(i
>= '0' && i
<= '9')
1620 if (i
== -2) /* user cancelled */
1624 fs_give((void **)&ucs4_prompt
);
1631 return ch_dict
>= 0 ? sp_list
[ch_dict
] : NULL
;
1634 /* just like mlreplyd, but user cannot fill a prompt */
1636 mlchoose(UCS
*prompt
, EXTRAKEYS
*extras
)
1641 int changed
= FALSE
;
1643 KEYMENU menu_choose
[12];
1644 COLOR_PAIR
*lastc
= NULL
;
1646 for(i
= 0; i
< 12; i
++){
1647 menu_choose
[i
].name
= NULL
;
1648 KS_OSDATASET(&menu_choose
[i
], KS_NONE
);
1651 menu_choose
[0].name
= "^G";
1652 menu_choose
[0].label
= N_("Get Help");
1653 KS_OSDATASET(&menu_choose
[0], KS_SCREENHELP
);
1655 menu_choose
[6].name
= "^C";
1656 menu_choose
[6].label
= N_("Cancel");
1657 KS_OSDATASET(&menu_choose
[6], KS_NONE
);
1659 for(i
= 0; i
< 10; i
++){
1661 menu_choose
[i
/ 2 + 1].name
= extras
[i
].name
;
1662 menu_choose
[i
/ 2 + 1].label
= extras
[i
].label
;
1665 menu_choose
[(i
+ 13) / 2].name
= extras
[i
].name
;
1666 menu_choose
[(i
+ 13) / 2].label
= extras
[i
].label
;
1669 wkeyhelp(menu_choose
); /* paint generic menu */
1670 sgarbk
= TRUE
; /* mark menu dirty */
1671 if(Pmaster
&& curwp
)
1672 curwp
->w_flag
|= WFMODE
;
1674 ucs4_strncpy(buf
, prompt
, NLINE
);
1675 buf
[NLINE
-1] = '\0';
1677 if(Pmaster
&& Pmaster
->colors
&& Pmaster
->colors
->prcp
1678 && pico_is_good_colorpair(Pmaster
->colors
->prcp
)){
1679 lastc
= pico_get_cur_color();
1680 (void) pico_set_colorp(Pmaster
->colors
->prcp
, PSC_NONE
);
1688 for(i
= 0; i
< 10 && extras
[i
].name
!= NULL
&& extras
[i
].key
!= c
; i
++)
1690 if(i
< 10 && extras
[i
].name
)
1693 case (CTRL
|'C') : /* Bail out! */
1695 pputs_utf8(_("Cancel"), 1);
1700 if(term
.t_mrow
== 0 && km_popped
== 0){
1701 movecursor(term
.t_nrow
-2, 0);
1705 (void) pico_set_colorp(lastc
, PSC_NONE
);
1706 free_color_pair(&lastc
);
1711 wkeyhelp(menu_choose
); /* paint generic menu */
1713 if(Pmaster
&& Pmaster
->colors
&& Pmaster
->colors
->prcp
1714 && pico_is_good_colorpair(Pmaster
->colors
->prcp
)){
1715 lastc
= pico_get_cur_color();
1716 (void) pico_set_colorp(Pmaster
->colors
->prcp
, PSC_NONE
);
1721 sgarbk
= TRUE
; /* mark menu dirty */
1725 /* else fall through */
1734 if (return_val
!= -1){ /* abort sets rv = -2, other return values are positive */
1736 (void) pico_set_colorp(lastc
, PSC_NONE
);
1737 free_color_pair(&lastc
);
1744 movecursor(term
.t_nrow
, 0);
1757 mlyesno_utf8(char *utf8prompt
, int dflt
)
1762 prompt
= utf8_to_ucs4_cpystr(utf8prompt
? utf8prompt
: "");
1764 ret
= mlyesno(prompt
, dflt
);
1767 fs_give((void **) &prompt
);
1774 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
1775 * ABORT. The ABORT status is returned if the user bumps out of the question
1776 * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
1780 mlyesno(UCS
*prompt
, int dflt
)
1783 UCS buf
[NLINE
], lbuf
[10];
1784 KEYMENU menu_yesno
[12];
1785 COLOR_PAIR
*lastc
= NULL
;
1786 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
1789 if (mswin_usedialog ())
1790 switch (mswin_yesno (prompt
)) {
1792 case 0: return (ABORT
);
1793 case 1: return (TRUE
);
1794 case 2: return (FALSE
);
1798 for(rv
= 0; rv
< 12; rv
++){
1799 menu_yesno
[rv
].name
= NULL
;
1800 KS_OSDATASET(&menu_yesno
[rv
], KS_NONE
);
1803 menu_yesno
[1].name
= "Y";
1804 menu_yesno
[1].label
= (dflt
== TRUE
) ? "[" N_("Yes") "]" : N_("Yes");
1805 menu_yesno
[6].name
= "^C";
1806 menu_yesno
[6].label
= N_("Cancel");
1807 menu_yesno
[7].name
= "N";
1808 menu_yesno
[7].label
= (dflt
== FALSE
) ? "[" N_("No") "]" : N_("No");
1809 wkeyhelp(menu_yesno
); /* paint generic menu */
1810 sgarbk
= TRUE
; /* mark menu dirty */
1811 if(Pmaster
&& curwp
)
1812 curwp
->w_flag
|= WFMODE
;
1814 ucs4_strncpy(buf
, prompt
, NLINE
);
1815 buf
[NLINE
-1] = '\0';
1816 lbuf
[0] = ' '; lbuf
[1] = '?'; lbuf
[2] = ' '; lbuf
[3] = '\0';
1817 ucs4_strncat(buf
, lbuf
, NLINE
- ucs4_strlen(buf
) - 1);
1818 buf
[NLINE
-1] = '\0';
1820 if(pcolors
&& pcolors
->prcp
1821 && pico_is_good_colorpair(pcolors
->prcp
)){
1822 lastc
= pico_get_cur_color();
1823 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
1830 case (CTRL
|'M') : /* default */
1832 pputs_utf8((dflt
) ? _("Yes") : _("No"), 1);
1840 case (CTRL
|'C') : /* Bail out! */
1842 pputs_utf8(_("ABORT"), 1);
1849 pputs_utf8(_("Yes"), 1);
1856 pputs_utf8(_("No"), 1);
1861 if(term
.t_mrow
== 0 && km_popped
== 0){
1862 movecursor(term
.t_nrow
-2, 0);
1866 (void) pico_set_colorp(lastc
, PSC_NONE
);
1867 free_color_pair(&lastc
);
1872 wkeyhelp(menu_yesno
); /* paint generic menu */
1874 if(pcolors
&& pcolors
->prcp
1875 && pico_is_good_colorpair(pcolors
->prcp
)){
1876 lastc
= pico_get_cur_color();
1877 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
1882 sgarbk
= TRUE
; /* mark menu dirty */
1886 /* else fall through */
1897 (void) pico_set_colorp(lastc
, PSC_NONE
);
1898 free_color_pair(&lastc
);
1905 movecursor(term
.t_nrow
, 0);
1918 * Write a prompt into the message line, then read back a response. Keep
1919 * track of the physical position of the cursor. If we are in a keyboard
1920 * macro throw the prompt away, and return the remembered response. This
1921 * lets macros run at full speed. The reply is always terminated by a carriage
1922 * return. Handle erase, kill, and abort keys.
1925 mlreply_utf8(char *utf8prompt
, char *utf8buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
1927 return(mlreplyd_utf8(utf8prompt
, utf8buf
, nbuf
, flg
|QDEFLT
, extras
));
1932 mlreply(UCS
*prompt
, UCS
*buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
1934 return(mlreplyd(prompt
, buf
, nbuf
, flg
|QDEFLT
, extras
));
1939 * function key mappings
1941 static UCS rfkm
[12][2] = {
1958 mlreplyd_utf8(char *utf8prompt
, char *utf8buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
1965 buf
= (UCS
*) fs_get(nbuf
* sizeof(*b
));
1966 b
= utf8_to_ucs4_cpystr(utf8buf
);
1968 ucs4_strncpy(buf
, b
, nbuf
);
1970 fs_give((void **) &b
);
1973 prompt
= utf8_to_ucs4_cpystr(utf8prompt
? utf8prompt
: "");
1975 ret
= mlreplyd(prompt
, buf
, nbuf
, flg
, extras
);
1977 utf8
= ucs4_to_utf8_cpystr(buf
);
1979 strncpy(utf8buf
, utf8
, nbuf
);
1980 utf8buf
[nbuf
-1] = '\0';
1981 fs_give((void **) &utf8
);
1985 fs_give((void **) &buf
);
1988 fs_give((void **) &prompt
);
2002 * mlreplyd - write the prompt to the message line along with a default
2003 * answer already typed in. Carriage return accepts the
2004 * default. answer returned in buf which also holds the initial
2005 * default, nbuf is its length, def set means use default value.
2006 * In order to be able to eliminate keys from a menu, EXTRAKEYS
2007 * always has size 10.
2010 mlreplyd(UCS
*prompt
, UCS
*buf
, int nbuf
, int flg
, EXTRAKEYS
*extras
)
2012 UCS c
; /* current char */
2013 UCS
*b
; /* pointer in buf */
2016 int changed
= FALSE
;
2018 KEYMENU menu_mlreply
[12];
2020 struct display_line dline
;
2021 COLOR_PAIR
*lastc
= NULL
;
2022 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
2025 if(mswin_usedialog()){
2026 MDlgButton btn_list
[12];
2027 LPTSTR free_names
[12];
2028 LPTSTR free_labels
[12];
2031 memset(&free_names
, 0, sizeof(LPTSTR
) * 12);
2032 memset(&free_labels
, 0, sizeof(LPTSTR
) * 12);
2033 memset(&btn_list
, 0, sizeof (MDlgButton
) * 12);
2035 for(i
= 0; extras
&& extras
[i
].name
!= NULL
; ++i
) {
2036 if(extras
[i
].label
[0] != '\0') {
2037 if((extras
[i
].key
& CTRL
) == CTRL
)
2038 btn_list
[j
].ch
= (extras
[i
].key
& ~CTRL
) - '@';
2040 btn_list
[j
].ch
= extras
[i
].key
;
2042 btn_list
[j
].rval
= extras
[i
].key
;
2043 free_names
[j
] = utf8_to_lptstr(extras
[i
].name
);
2044 btn_list
[j
].name
= free_names
[j
];
2045 free_labels
[j
] = utf8_to_lptstr(extras
[i
].label
);
2046 btn_list
[j
].label
= free_labels
[j
];
2051 btn_list
[j
].ch
= -1;
2053 return_val
= mswin_dialog(prompt
, buf
, nbuf
, ((flg
&QDEFLT
) > 0),
2054 FALSE
, btn_list
, NULL
, 0);
2057 return_val
= HELPCH
;
2059 for(i
= 0; i
< 12; i
++){
2061 fs_give((void **) &free_names
[i
]);
2063 fs_give((void **) &free_labels
[i
]);
2070 memset(&menu_mlreply
, 0, 12*sizeof(KEYMENU
));
2071 menu_mlreply
[0].name
= "^G";
2072 menu_mlreply
[0].label
= N_("Get Help");
2073 KS_OSDATASET(&menu_mlreply
[0], KS_SCREENHELP
);
2074 for(j
= 0, i
= 1; i
< 6; i
++){ /* insert odd extras */
2075 menu_mlreply
[i
].name
= NULL
;
2076 KS_OSDATASET(&menu_mlreply
[i
], KS_NONE
);
2081 rfkm
[2*i
][1] = extras
[j
].key
;
2082 menu_mlreply
[i
].name
= extras
[j
].name
;
2083 menu_mlreply
[i
].label
= extras
[j
].label
;
2084 KS_OSDATASET(&menu_mlreply
[i
], KS_OSDATAGET(&extras
[j
]));
2089 menu_mlreply
[6].name
= "^C";
2090 menu_mlreply
[6].label
= N_("Cancel");
2091 KS_OSDATASET(&menu_mlreply
[6], KS_NONE
);
2092 for(j
= 0, i
= 7; i
< 12; i
++){ /* insert even extras */
2093 menu_mlreply
[i
].name
= NULL
;
2094 rfkm
[2*(i
-6)+1][1] = 0;
2098 rfkm
[2*(i
-6)+1][1] = extras
[j
].key
;
2099 menu_mlreply
[i
].name
= extras
[j
].name
;
2100 menu_mlreply
[i
].label
= extras
[j
].label
;
2101 KS_OSDATASET(&menu_mlreply
[i
], KS_OSDATAGET(&extras
[j
]));
2106 /* set up what to watch for and return values */
2107 memset(extra_v
, 0, sizeof(extra_v
));
2108 for(i
= 0, j
= 0; i
< 12 && extras
&& extras
[i
].name
; i
++)
2109 extra_v
[j
++] = extras
[i
].key
;
2111 plen
= mlwrite(prompt
, NULL
); /* paint prompt */
2116 dline
.vused
= ucs4_strlen(buf
);
2117 dline
.dwid
= term
.t_ncol
- plen
;
2118 dline
.row
= term
.t_nrow
- term
.t_mrow
;
2121 dline
.dlen
= 2 * dline
.dwid
+ 100;
2123 dline
.dl
= (UCS
*) fs_get(dline
.dlen
* sizeof(UCS
));
2124 dline
.olddl
= (UCS
*) fs_get(dline
.dlen
* sizeof(UCS
));
2125 memset(dline
.dl
, 0, dline
.dlen
* sizeof(UCS
));
2126 memset(dline
.olddl
, 0, dline
.dlen
* sizeof(UCS
));
2128 dline
.movecursor
= movecursor
;
2129 dline
.writechar
= writeachar
;
2132 dline
.vlen
= nbuf
-1;
2135 b
= &buf
[(flg
& QBOBUF
) ? 0 : ucs4_strlen(buf
)];
2137 wkeyhelp(menu_mlreply
); /* paint generic menu */
2139 sgarbk
= 1; /* mark menu dirty */
2141 if(pcolors
&& pcolors
->prcp
2142 && pico_is_good_colorpair(pcolors
->prcp
)){
2143 lastc
= pico_get_cur_color();
2144 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
2151 line_paint(b
-buf
, &dline
, NULL
);
2155 mouse_in_content(KEY_MOUSE
, -1, -1, 0x5, 0);
2156 register_mfunc(mouse_in_content
,
2157 term
.t_nrow
- term
.t_mrow
, plen
,
2158 term
.t_nrow
- term
.t_mrow
, term
.t_ncol
-1);
2161 mswin_allowpaste(MSWIN_PASTE_LINE
);
2163 while((c
= GetKey()) == NODATA
)
2167 clear_mfunc(mouse_in_content
);
2170 mswin_allowpaste(MSWIN_PASTE_DISABLE
);
2173 switch(c
= normalize_cmd(c
, rfkm
, 1)){
2174 case (CTRL
|'A') : /* CTRL-A beginning */
2179 case (CTRL
|'B') : /* CTRL-B back a char */
2188 case (CTRL
|'C') : /* CTRL-C abort */
2189 pputs_utf8(_("ABORT"), 1);
2194 case (CTRL
|'E') : /* CTRL-E end of line */
2196 b
= &buf
[ucs4_strlen(buf
)];
2199 case (CTRL
|'F') : /* CTRL-F forward a char*/
2208 case (CTRL
|'G') : /* CTRL-G help */
2209 if(term
.t_mrow
== 0 && km_popped
== 0){
2210 movecursor(term
.t_nrow
-2, 0);
2212 sgarbk
= 1; /* mark menu dirty */
2216 (void) pico_set_colorp(lastc
, PSC_NONE
);
2217 free_color_pair(&lastc
);
2222 wkeyhelp(menu_mlreply
); /* paint generic menu */
2223 plen
= mlwrite(prompt
, NULL
); /* paint prompt */
2224 if(pcolors
&& pcolors
->prcp
2225 && pico_is_good_colorpair(pcolors
->prcp
)){
2226 lastc
= pico_get_cur_color();
2227 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
2236 pputs_utf8(_("HELP"), 1);
2237 return_val
= HELPCH
;
2240 case (CTRL
|'H') : /* CTRL-H backspace */
2241 case 0x7f : /* rubout */
2249 case (CTRL
|'D') : /* CTRL-D delete char */
2259 do /* blat out left char */
2261 while(b
[i
++] != '\0');
2264 case (CTRL
|'L') : /* CTRL-L redraw */
2265 return_val
= (CTRL
|'L');
2268 case (CTRL
|'K') : /* CTRL-K kill line */
2275 case F1
: /* sort of same thing */
2276 return_val
= HELPCH
;
2279 case (CTRL
|'M') : /* newline */
2280 return_val
= changed
;
2288 mouse_get_last (NULL
, &mp
);
2290 /* The clicked line have anything special on it? */
2292 case M_BUTTON_LEFT
: /* position cursor */
2293 mp
.col
-= plen
; /* normalize column */
2294 if(mp
.col
>= 0 && mp
.col
<= ucs4_strlen(buf
))
2299 case M_BUTTON_RIGHT
:
2301 mswin_allowpaste(MSWIN_PASTE_LINE
);
2302 mswin_paste_popup();
2303 mswin_allowpaste(MSWIN_PASTE_DISABLE
);
2307 case M_BUTTON_MIDDLE
: /* NO-OP for now */
2308 default: /* just ignore */
2318 /* look for match in extra_v */
2319 for(i
= 0; i
< 12; i
++)
2320 if(c
&& c
== extra_v
[i
]){
2327 if(c
& (CTRL
| FUNC
)){ /* bag ctrl_special chars */
2332 if(flg
&QNODQT
){ /* reject double quotes? */
2339 if(dline
.vused
>= nbuf
-1){
2344 do /* blat out left char */
2356 (void) pico_set_colorp(lastc
, PSC_NONE
);
2357 free_color_pair(&lastc
);
2366 movecursor(term
.t_nrow
, 0);
2373 fs_give((void **) &dline
.dl
);
2376 fs_give((void **) &dline
.olddl
);
2383 emlwwrite(char *utf8message
, EML
*eml
)
2386 emlwrite(utf8message
, eml
);
2390 emlwrite(char *utf8message
, EML
*eml
)
2394 message
= utf8_to_ucs4_cpystr(utf8message
? utf8message
: "");
2396 emlwrite_ucs4(message
, eml
);
2399 fs_give((void **) &message
);
2404 * emlwrite() - write the message string to the error half of the screen
2405 * center justified. much like mlwrite (which is still used
2406 * to paint the line for prompts and such), except it center
2410 emlwrite_ucs4(UCS
*message
, EML
*eml
)
2414 COLOR_PAIR
*lastc
= NULL
;
2415 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
2419 if(!(message
&& *message
) || term
.t_nrow
< 2)
2420 return; /* nothing to write or no space to write, bag it */
2424 width
= ucs4_str_width(message
);
2427 * next, figure out where the to move the cursor so the message
2428 * comes out centered
2430 if((ap
=ucs4_strchr(message
, '%')) != NULL
){
2435 width
+= (eml
&& eml
->c
) ? wcellwidth(eml
->c
) : 1;
2438 width
+= dumbroot(eml
? eml
->d
: 0, 10);
2441 width
+= dumblroot(eml
? eml
->l
: 0L, 10);
2444 width
+= dumbroot(eml
? eml
->d
: 0, 8);
2447 width
+= dumbroot(eml
? eml
->d
: 0, 16);
2449 case 's': /* string arg is UTF-8 */
2450 width
+= (eml
&& eml
->s
) ? utf8_width(eml
->s
) : 2;
2455 if(width
+4 <= term
.t_ncol
)
2456 movecursor(term
.t_nrow
-term
.t_mrow
, (term
.t_ncol
- (width
+ 4))/2);
2458 movecursor(term
.t_nrow
-term
.t_mrow
, 0);
2460 if(pcolors
&& pcolors
->stcp
2461 && pico_is_good_colorpair(pcolors
->stcp
)){
2462 lastc
= pico_get_cur_color();
2463 (void) pico_set_colorp(pcolors
->stcp
, PSC_NONE
);
2468 pputs_utf8("[ ", 1);
2469 while (*bufp
!= '\0' && ttcol
< term
.t_ncol
-2){
2472 else if(*bufp
== '%'){
2478 pputs_utf8("%c", 0);
2482 mlputi(eml
? eml
->d
: 0, 10);
2485 mlputli(eml
? eml
->l
: 0L, 10);
2488 mlputi(eml
? eml
->d
: 0, 16);
2491 mlputi(eml
? eml
->d
: 0, 8);
2494 pputs_utf8((eml
&& eml
->s
) ? eml
->s
: "%s", 0);
2507 pputs_utf8(" ]", 1);
2510 (void) pico_set_colorp(lastc
, PSC_NONE
);
2511 free_color_pair(&lastc
);
2523 mlwrite_utf8(char *utf8fmt
, void *arg
)
2528 fmt
= utf8_to_ucs4_cpystr(utf8fmt
? utf8fmt
: "");
2529 ret
= mlwrite(fmt
, arg
);
2531 fs_give((void **) &fmt
);
2538 * Write a message into the message line. Keep track of the physical cursor
2539 * position. A small class of printf like format items is handled. Assumes the
2540 * stack grows down; this assumption is made by the "++" in the argument scan
2541 * loop. Set the "message line" flag TRUE.
2544 mlwrite(UCS
*fmt
, void *arg
)
2549 COLOR_PAIR
*lastc
= NULL
;
2550 PCOLORS
*pcolors
= Pmaster
&& Pmaster
->colors
? Pmaster
->colors
: Pcolors
;
2553 * the idea is to only highlight if there is something to show
2556 movecursor(ttrow
, 0);
2558 if(pcolors
&& pcolors
->prcp
2559 && pico_is_good_colorpair(pcolors
->prcp
)){
2560 lastc
= pico_get_cur_color();
2561 (void) pico_set_colorp(pcolors
->prcp
, PSC_NONE
);
2568 while ((c
= *fmt
++) != 0) {
2576 mlputi(*(int *)ap
, 10);
2581 mlputi(*(int *)ap
, 8);
2586 mlputi(*(int *)ap
, 16);
2591 mlputli(*(long *)ap
, 10);
2596 pputs_utf8(*(char **)ap
, 1);
2597 ap
+= sizeof(char *);
2603 ttcol
+= (ww
>= 0 ? ww
: 1);
2609 while(ttcol
< term
.t_ncol
)
2612 movecursor(term
.t_nrow
- term
.t_mrow
, ret
);
2615 (void) pico_set_colorp(lastc
, PSC_NONE
);
2616 free_color_pair(&lastc
);
2629 * Write out an integer, in the specified radix. Update the physical cursor
2630 * position. This will not handle any negative numbers; maybe it should.
2633 mlputi(int i
, int r
)
2636 static char hexdigits
[] = "0123456789ABCDEF";
2648 pputc(hexdigits
[i
%r
], 1);
2653 * do the same except as a long integer.
2656 mlputli(long l
, int r
)
2670 pputc((int)(l
%r
)+'0', 1);
2675 unknown_command(UCS c
)
2677 char buf
[10], ch
, *s
;
2686 else if(c
& CTRL
&& c
>= (CTRL
|'@') && c
<= (CTRL
|'_')){
2687 ch
= c
- (CTRL
|'@') + '@';
2688 snprintf(s
, sizeof(buf
), "^%c", ch
);
2692 case ' ' : s
= "SPACE"; break;
2693 case '\033' : s
= "ESC"; break;
2694 case '\177' : s
= "DEL"; break;
2695 case ctrl('I') : s
= "TAB"; break;
2696 case ctrl('J') : s
= "LINEFEED"; break;
2697 case ctrl('M') : s
= "RETURN"; break;
2698 case ctrl('Q') : s
= "XON"; break;
2699 case ctrl('S') : s
= "XOFF"; break;
2700 case KEY_UP
: s
= "Up Arrow"; break;
2701 case KEY_DOWN
: s
= "Down Arrow"; break;
2702 case KEY_RIGHT
: s
= "Right Arrow"; break;
2703 case KEY_LEFT
: s
= "Left Arrow"; break;
2704 case CTRL
|KEY_UP
: s
= "Ctrl-Up Arrow"; break;
2705 case CTRL
|KEY_DOWN
: s
= "Ctrl-Down Arrow"; break;
2706 case CTRL
|KEY_RIGHT
: s
= "Ctrl-Right Arrow"; break;
2707 case CTRL
|KEY_LEFT
: s
= "Ctrl-Left Arrow"; break;
2708 case KEY_PGUP
: s
= "Prev Page"; break;
2709 case KEY_PGDN
: s
= "Next Page"; break;
2710 case KEY_HOME
: s
= "Home"; break;
2711 case KEY_END
: s
= "End"; break;
2712 case KEY_DEL
: s
= "Delete"; break; /* Not necessary DEL! */
2725 snprintf(s
, sizeof(buf
), "F%ld", (long) (c
- PF1
+ 1));
2730 utf8_put((unsigned char *) s
, (unsigned long) c
);
2736 emlwrite("Unknown Command: %s", &eml
);
2742 * scrolldown - use stuff to efficiently move blocks of text on the
2743 * display, and update the pscreen array to reflect those
2746 * wp is the window to move in
2747 * r is the row at which to begin scrolling
2748 * n is the number of lines to scrol
2751 scrolldown(WINDOW
*wp
, int r
, int n
)
2756 register VIDEO
*vp1
;
2757 register VIDEO
*vp2
;
2767 if(r
> wp
->w_toprow
)
2768 vscreen
[r
-1]->v_flag
|= VFCHG
;
2769 l
= wp
->w_toprow
+wp
->w_ntrows
-r
;
2774 for(i
=l
-n
-1; i
>= 0; i
--){
2776 vp2
= pscreen
[r
+i
+n
];
2777 memcpy(vp2
, vp1
, term
.t_ncol
* sizeof(CELL
));
2782 #endif /* TERMCAP */
2787 * scrollup - use tcap stuff to efficiently move blocks of text on the
2788 * display, and update the pscreen array to reflect those
2792 scrollup(WINDOW
*wp
, int r
, int n
)
2796 register VIDEO
*vp1
;
2797 register VIDEO
*vp2
;
2810 if(!(r
+i
+n
< wp
->w_toprow
+wp
->w_ntrows
))
2814 if(!((i
< wp
->w_ntrows
-n
)&&(r
+i
+n
< wp
->w_toprow
+wp
->w_ntrows
)))
2817 vp1
= pscreen
[r
+i
+n
];
2819 memcpy(vp2
, vp1
, term
.t_ncol
* sizeof(CELL
));
2822 pprints(wp
->w_toprow
+wp
->w_ntrows
-n
, wp
->w_toprow
+wp
->w_ntrows
-1);
2825 #endif /* TERMCAP */
2830 * print spaces in the physical screen starting from row abs(n) working in
2831 * either the positive or negative direction (depending on sign of n).
2834 pprints(int x
, int y
)
2840 for(i
= x
;i
<= y
; ++i
){
2841 for(j
= 0; j
< term
.t_ncol
; j
++){
2842 pscreen
[i
]->v_text
[j
].c
= ' ';
2843 pscreen
[i
]->v_text
[j
].a
= 0;
2848 for(i
= x
;i
>= y
; --i
){
2849 for(j
= 0; j
< term
.t_ncol
; j
++){
2850 pscreen
[i
]->v_text
[j
].c
= ' ';
2851 pscreen
[i
]->v_text
[j
].a
= 0;
2861 * doton - return the physical line number that the dot is on in the
2862 * current window, and by side effect the number of lines remaining
2865 doton(int *r
, unsigned *chs
)
2868 register LINE
*lp
= curwp
->w_linep
;
2871 assert(r
!= NULL
&& chs
!= NULL
);
2874 while(i
++ < curwp
->w_ntrows
){
2875 if(lp
== curwp
->w_dotp
)
2878 if(lp
== curwp
->w_bufp
->b_linep
){
2883 (*chs
) += llength(lp
);
2885 *r
= i
- l
- term
.t_mrow
;
2886 return(l
+curwp
->w_toprow
);
2892 * resize_pico - given new window dimensions, allocate new resources
2895 resize_pico(int row
, int col
)
2897 int old_nrow
, old_ncol
;
2901 old_nrow
= term
.t_nrow
;
2902 old_ncol
= term
.t_ncol
;
2907 if (old_ncol
== term
.t_ncol
&& old_nrow
== term
.t_nrow
)
2911 curwp
->w_toprow
= 2;
2912 curwp
->w_ntrows
= term
.t_nrow
- curwp
->w_toprow
- term
.t_mrow
;
2916 fillcol
= Pmaster
->fillcolumn
;
2917 (*Pmaster
->resize
)();
2919 else if(userfillcol
> 0)
2920 fillcol
= userfillcol
;
2922 fillcol
= term
.t_ncol
- 6; /* we control the fill column */
2925 * free unused screen space ...
2927 for(i
=term
.t_nrow
+1; i
<= old_nrow
; ++i
){
2928 free((char *) vscreen
[i
]);
2929 free((char *) pscreen
[i
]);
2933 * realloc new space for screen ...
2935 if((vscreen
=(VIDEO
**)realloc(vscreen
,(term
.t_nrow
+1)*sizeof(VIDEO
*))) == NULL
){
2942 if((pscreen
=(VIDEO
**)realloc(pscreen
,(term
.t_nrow
+1)*sizeof(VIDEO
*))) == NULL
){
2949 for (i
= 0; i
<= term
.t_nrow
; ++i
) {
2951 vp
= (VIDEO
*) realloc(vscreen
[i
], sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2953 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2959 if(old_ncol
< term
.t_ncol
){ /* don't let any garbage in */
2961 vtcol
= (i
< old_nrow
) ? old_ncol
: 0;
2966 vp
= (VIDEO
*) realloc(pscreen
[i
], sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2968 vp
= (VIDEO
*) malloc(sizeof(VIDEO
)+(term
.t_ncol
*sizeof(CELL
)));
2977 if(!ResizeBrowser()){
2978 if(Pmaster
&& Pmaster
->headents
){
2982 curwp
->w_flag
|= (WFHARD
| WFMODE
);
2983 pico_refresh(0, 1); /* redraw whole enchilada. */
2984 update(); /* do it */
2992 redraw_pico_for_callback(void)
3000 * showCompTitle - display the anchor line passed in from pine
3007 extern UCS
*pico_anchor
;
3008 COLOR_PAIR
*lastc
= NULL
;
3010 if((bufp
= pico_anchor
) == NULL
)
3013 movecursor(COMPOSER_TITLE_LINE
, 0);
3014 if (Pmaster
->colors
&& Pmaster
->colors
->tbcp
&&
3015 pico_is_good_colorpair(Pmaster
->colors
->tbcp
)){
3016 lastc
= pico_get_cur_color();
3017 (void)pico_set_colorp(Pmaster
->colors
->tbcp
, PSC_NONE
);
3022 while (ttcol
< term
.t_ncol
)
3029 (void)pico_set_colorp(lastc
, PSC_NONE
);
3030 free_color_pair(&lastc
);
3035 movecursor(COMPOSER_TITLE_LINE
+ 1, 0);
3043 * zotdisplay - blast malloc'd space created for display maps
3050 for (i
= 0; i
<= term
.t_nrow
; ++i
){ /* free screens */
3051 free((char *) vscreen
[i
]);
3052 free((char *) pscreen
[i
]);
3055 free((char *) vscreen
);
3056 free((char *) pscreen
);
3062 * nlforw() - returns the number of lines from the top to the dot
3068 register LINE
*lp
= curwp
->w_linep
;
3070 while(lp
!= curwp
->w_dotp
){
3080 * pputc - output the given char, keep track of it on the physical screen
3081 * array, and keep track of the cursor
3084 pputc(UCS c
, /* char to write */
3085 int a
) /* and its attribute */
3087 int ind
, width
, printable_ascii
= 0;
3090 * This is necessary but not sufficient to allow us to draw. Note that
3091 * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
3092 * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
3094 if((ttcol
>= 0 && ttcol
< term
.t_ncol
) && (ttrow
>= 0 && ttrow
<= term
.t_nrow
)){
3097 * Width is the number of screen columns a character will occupy.
3099 if(c
< 0x80 && isprint(c
)){
3104 width
= wcellwidth(c
);
3107 width
= 1; /* will be a '?' */
3109 if(ttcol
+ width
<= term
.t_ncol
){ /* it fits */
3111 * Some terminals scroll when you write in the lower right corner
3112 * of the screen, so don't write there.
3114 if(!(ttrow
== term
.t_nrow
&& ttcol
+width
== term
.t_ncol
)){
3115 (*term
.t_putchar
)(c
); /* write it */
3116 ind
= index_from_col(ttrow
, ttcol
);
3117 pscreen
[ttrow
]->v_text
[ind
].c
= c
; /* keep track of it */
3118 pscreen
[ttrow
]->v_text
[ind
].a
= a
; /* keep track of it */
3123 * Character overlaps right edge of screen. Hopefully the higher
3124 * layers will prevent this but we're making sure.
3126 * We may want to do something like writing a space character
3127 * into the cells that are on the screen. We'll see.
3131 ttcol
= MIN(term
.t_ncol
, ttcol
+width
);
3137 * pputs - print a string and keep track of the cursor
3140 pputs(UCS
*s
, /* string to write */
3141 int a
) /* and its attribute */
3149 pputs_utf8(char *s
, int a
)
3154 ucsstr
= utf8_to_ucs4_cpystr(s
);
3157 fs_give((void **) &ucsstr
);
3164 * peeol - physical screen array erase to end of the line. remember to
3170 int i
, width
= 0, ww
;
3173 if(ttrow
< 0 || ttrow
> term
.t_nrow
)
3180 * Don't clear if we think we are sitting past the last column,
3181 * that erases the last column if we just wrote it.
3183 if(ttcol
< term
.t_ncol
)
3187 * Because the characters are variable width it's a little tricky
3188 * to erase the rest of the line. What we do is add up the
3189 * widths of the characters until we reach ttcol
3190 * then set the rest to the space character.
3192 for(i
= 0; i
< term
.t_ncol
&& width
< ttcol
; i
++){
3193 ww
= wcellwidth((UCS
) pscreen
[ttrow
]->v_text
[i
].c
);
3194 width
+= (ww
>= 0 ? ww
: 1);
3197 while(i
< term
.t_ncol
)
3198 pscreen
[ttrow
]->v_text
[i
++] = cl
;
3203 * pscr - return the character cell on the physical screen map on the
3204 * given line, l, and offset, o.
3209 if((l
>= 0 && l
<= term
.t_nrow
) && (o
>= 0 && o
< term
.t_ncol
))
3210 return(&(pscreen
[l
]->v_text
[o
]));
3217 * pclear() - clear the physical screen from row x through row y (inclusive)
3218 * row is zero origin, min row = 0 max row = t_nrow
3219 * Clear whole screen -- pclear(0, term.t_nrow)
3220 * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
3221 * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
3224 pclear(int x
, int y
)
3228 x
= MIN(MAX(0, x
), term
.t_nrow
);
3229 y
= MIN(MAX(0, y
), term
.t_nrow
);
3231 for(i
=x
; i
<= y
; i
++){
3239 * dumbroot - just get close
3242 dumbroot(int x
, int b
)
3247 return(dumbroot(x
/b
, b
) + 1);
3252 * dumblroot - just get close
3255 dumblroot(long x
, int b
)
3260 return(dumblroot(x
/b
, b
) + 1);
3265 * pinsertc - use optimized insert, fixing physical screen map.
3266 * returns true if char written, false otherwise
3274 if(ttrow
< 0 || ttrow
> term
.t_nrow
)
3277 if(o_insert((UCS
) c
.c
)){ /* if we've got it, use it! */
3278 p
= pscreen
[ttrow
]->v_text
; /* then clean up physical screen */
3280 ind
= index_from_col(ttrow
, ttcol
);
3282 for(i
= term
.t_ncol
-1; i
> ind
; i
--)
3283 p
[i
] = p
[i
-1]; /* shift right */
3285 p
[ind
] = c
; /* insert new char */
3287 ww
= wcellwidth((UCS
) c
.c
);
3288 ttcol
+= (ww
>= 0 ? ww
: 1);
3298 * pdel - use optimized delete to rub out the current char and
3299 * fix the physical screen array.
3300 * returns true if optimized the delete, false otherwise
3308 if(ttrow
< 0 || ttrow
> term
.t_nrow
)
3311 if(TERM_DELCHAR
){ /* if we've got it, use it! */
3312 p
= pscreen
[ttrow
]->v_text
;
3313 ind
= index_from_col(ttrow
, ttcol
);
3317 w
= wcellwidth((UCS
) p
[ind
].c
);
3318 w
= (w
>= 0 ? w
: 1);
3321 for(i
= 0; i
< w
; i
++){
3322 (*term
.t_putchar
)('\b'); /* move left a char */
3323 o_delete(); /* and delete it */
3326 /* then clean up physical screen */
3327 for(i
=ind
; i
< term
.t_ncol
-1; i
++)
3343 * wstripe - write out the given string at the given location, and reverse
3344 * video on flagged characters. Does the same thing as pine's
3347 * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
3348 * only if you want to put the tildes before multi-byte chars.
3351 wstripe(int line
, int column
, char *utf8pmt
, int key
)
3357 COLOR_PAIR
*lastc
= NULL
;
3358 COLOR_PAIR
*kncp
= NULL
;
3359 COLOR_PAIR
*klcp
= NULL
;
3361 if(line
< 0 || line
> term
.t_nrow
)
3364 if (Pmaster
&& Pmaster
->colors
){
3365 if(pico_is_good_colorpair(Pmaster
->colors
->klcp
))
3366 klcp
= Pmaster
->colors
->klcp
;
3368 if(klcp
&& pico_is_good_colorpair(Pmaster
->colors
->kncp
))
3369 kncp
= Pmaster
->colors
->kncp
;
3372 klcp
= Pcolors
->klcp
;
3373 kncp
= Pcolors
->kncp
;
3376 lastc
= pico_get_cur_color();
3377 ucs4pmt
= utf8_to_ucs4_cpystr(utf8pmt
);
3378 l
= ucs4_strlen(ucs4pmt
);
3380 if(i
>= term
.t_ncol
|| col
>= term
.t_ncol
|| j
>= l
)
3381 return; /* equal strings */
3383 if(ucs4pmt
[j
] == (UCS
) key
)
3386 if (pscr(line
, i
) == NULL
)
3389 if(pscr(line
, i
)->c
!= ucs4pmt
[j
]){
3390 if(j
>= 1 && ucs4pmt
[j
-1] == (UCS
) key
)
3395 ww
= wcellwidth((UCS
) pscr(line
, i
)->c
);
3396 col
+= (ww
>= 0 ? ww
: 1);
3401 movecursor(line
, column
+col
);
3402 if(klcp
) (void)pico_set_colorp(klcp
, PSC_NONE
);
3405 if(*u
== (UCS
) key
){
3408 (void)pico_set_colorp(kncp
, PSC_NONE
);
3410 (void)(*term
.t_rev
)(1);
3414 (void)pico_set_colorp(klcp
, PSC_NONE
);
3416 (void)(*term
.t_rev
)(0);
3422 while(*++u
!= '\0');
3425 fs_give((void **) &ucs4pmt
);
3429 (void)pico_set_colorp(lastc
, PSC_NONE
);
3430 free_color_pair(&lastc
);
3438 * wkeyhelp - paint list of possible commands on the bottom
3439 * of the display (yet another pine clone)
3440 * NOTE: function key mode is handled here since all the labels
3443 * The KEYMENU definitions have names and labels defined as UTF-8 strings,
3444 * and wstripe expects UTF-8.
3447 wkeyhelp(KEYMENU
*keymenu
)
3449 char *obufp
, *p
, fkey
[4];
3450 char linebuf
[2*NLINE
]; /* "2" is for space for invert tokens */
3451 int row
, slot
, tspace
, adjusted_tspace
, nspace
[6], index
, n
;
3457 pico_config_menu_items (keymenu
);
3460 if(term
.t_mrow
== 0)
3467 * Calculate amount of space for the names column by column...
3469 for(index
= 0; index
< 6; index
++)
3470 if(!(gmode
&MDFKEY
)){
3471 nspace
[index
] = (keymenu
[index
].name
)
3472 ? utf8_width(keymenu
[index
].name
) : 0;
3473 if(keymenu
[index
+6].name
3474 && (n
= utf8_width(keymenu
[index
+6].name
)) > nspace
[index
])
3480 nspace
[index
] = (index
< 4) ? 3 : 4;
3482 tspace
= term
.t_ncol
/6; /* total space for each item */
3485 * Avoid writing in bottom right corner so we won't scroll screens that
3486 * scroll when you do that. The way this is setup, we won't do that
3487 * unless the number of columns is evenly divisible by 6.
3489 adjusted_tspace
= (6 * tspace
== term
.t_ncol
) ? tspace
- 1 : tspace
;
3492 for(row
= 0; row
<= 1; row
++){
3494 obufp
= &linebuf
[0];
3495 for(slot
= 0; slot
< 6; slot
++){
3496 if(keymenu
[index
].name
&& keymenu
[index
].label
){
3498 char this_label
[200], tmp_label
[200];
3500 if(keymenu
[index
].label
[0] == '[' && keymenu
[index
].label
[(l
=strlen(keymenu
[index
].label
))-1] == ']' && l
> 2){
3501 strncpy(tmp_label
, &keymenu
[index
].label
[1], MIN(sizeof(tmp_label
),l
-2));
3502 tmp_label
[MIN(sizeof(tmp_label
)-1,l
-2)] = '\0';
3503 snprintf(this_label
, sizeof(this_label
), "[%s]", _(tmp_label
));
3506 strncpy(this_label
, _(keymenu
[index
].label
), sizeof(this_label
));
3508 this_label
[sizeof(this_label
)-1] = '\0';
3512 snprintf(fkey
, sizeof(fkey
), "F%d", (2 * slot
) + row
+ 1);
3515 p
= keymenu
[index
].name
;
3517 snprintf(nbuf
, sizeof(nbuf
), "%.*s %s", nspace
[slot
], p
, this_label
);
3519 (gmode
&MDFKEY
) ? F1
+ (2 * slot
) + row
:
3520 (keymenu
[index
].name
[0] == '^')
3521 ? (CTRL
| keymenu
[index
].name
[1])
3522 : (keymenu
[index
].name
[0] == 'S'
3523 && !strcmp(keymenu
[index
].name
, "Spc"))
3525 : keymenu
[index
].name
[0],
3527 term
.t_nrow
- 1 + row
, (slot
* tspace
),
3529 (Pmaster
&& Pmaster
->colors
)
3530 ? Pmaster
->colors
->kncp
: NULL
,
3531 (Pmaster
&& Pmaster
->colors
)
3532 ? Pmaster
->colors
->klcp
: NULL
);
3536 while(p
&& *p
&& n
--){
3537 *obufp
++ = '~'; /* insert "invert" token */
3545 n
= ((slot
== 5 && row
== 1) ? adjusted_tspace
3546 : tspace
) - nspace
[slot
];
3547 while(p
&& *p
&& n
-- > 0)
3554 n
= (slot
== 5 && row
== 1) ? adjusted_tspace
: tspace
;
3559 register_key(index
, NODATA
, "", NULL
, 0, 0, 0, NULL
, NULL
);
3567 wstripe(term
.t_nrow
- 1 + row
, 0, linebuf
, '~');
3573 * This returns the screen width between pstart (inclusive) and
3574 * pend (exclusive) where the pointers point into an array of CELLs.
3577 cellwidth_ptr_to_ptr(CELL
*pstart
, CELL
*pend
)
3584 for(p
= pstart
; p
< pend
; p
++){
3585 ww
= wcellwidth((UCS
) p
->c
);
3586 width
+= (ww
>= 0 ? ww
: 1);
3594 * This returns the virtual screen width in row from index a to b (exclusive).
3597 vcellwidth_a_to_b(int row
, int a
, int b
)
3599 CELL
*pstart
, *pend
;
3602 if(row
< 0 || row
> term
.t_nrow
)
3608 a
= MIN(MAX(0, a
), term
.t_ncol
-1);
3609 b
= MIN(MAX(0, a
), term
.t_ncol
); /* b is past where we stop */
3612 pstart
= &vp
->v_text
[a
];
3613 pend
= &vp
->v_text
[b
];
3615 return(cellwidth_ptr_to_ptr(pstart
, pend
));
3620 * This returns the physical screen width in row from index a to b (exclusive).
3623 pcellwidth_a_to_b(int row
, int a
, int b
)
3625 CELL
*pstart
, *pend
;
3628 if(row
< 0 || row
> term
.t_nrow
)
3634 a
= MIN(MAX(0, a
), term
.t_ncol
-1);
3635 b
= MIN(MAX(0, a
), term
.t_ncol
); /* b is past where we stop */
3638 pstart
= &vp
->v_text
[a
];
3639 pend
= &vp
->v_text
[b
];
3641 return(cellwidth_ptr_to_ptr(pstart
, pend
));
3646 index_from_col(int row
, int col
)
3648 CELL
*p_start
, *p_end
, *p_limit
;
3649 int w_consumed
= 0, w
, done
= 0;
3651 if(row
< 0 || row
> term
.t_nrow
)
3654 p_end
= p_start
= pscreen
[row
]->v_text
;
3655 p_limit
= p_start
+ term
.t_ncol
;
3658 while(!done
&& p_end
< p_limit
&& p_end
->c
&& w_consumed
<= col
){
3659 w
= wcellwidth((UCS
) p_end
->c
);
3660 w
= (w
>= 0 ? w
: 1);
3661 if(w_consumed
+ w
<= col
){
3669 /* MIN and MAX just to be sure */
3670 return(MIN(MAX(0, p_end
- p_start
), term
.t_ncol
-1));
3676 pico_config_menu_items (KEYMENU
*keymenu
)
3682 mswin_menuitemclear ();
3684 /* keymenu's seem to be hardcoded at 12 entries. */
3685 for (i
= 0, k
= keymenu
; i
< 12; ++i
, ++k
) {
3686 if (k
->name
!= NULL
&& k
->label
!= NULL
&&
3687 k
->menuitem
!= KS_NONE
) {
3689 if (k
->name
[0] == '^')
3690 key
= CTRL
| k
->name
[1];
3691 else if (strcmp(k
->name
, "Ret") == 0)
3696 mswin_menuitemadd (key
, k
->label
, k
->menuitem
, 0);
3702 * Update the scroll range and position. (exported)
3704 * This is where curbp->b_linecnt is really managed. With out this function
3705 * to count the number of lines when needed curbp->b_linecnt will never
3706 * really be correct. BUT, this function is only compiled into the
3707 * windows version, so b_linecnt will only ever be right in the windows
3708 * version. OK for now because that is the only version that
3709 * looks at b_linecnt.
3712 update_scroll (void)
3717 static LINE
*last_top_line
= NULL
;
3718 static long last_scroll_pos
= -1;
3721 if (ComposerEditing
) {
3722 /* Editing header - don't allow scroll bars. */
3723 mswin_setscrollrange (0, 0);
3729 * Count the number of lines in the current bufer. Done when:
3731 * when told to recount: curbp->b_linecnt == -1
3732 * when the top line changed: curwp->w_linep != last_top_line
3733 * when we don't know the scroll pos: last_scroll_pos == -1
3735 * The first line in the list is a "place holder" line and is not
3736 * counted. The list is circular, when we return the to place
3737 * holder we have reached the end.
3739 if(curbp
->b_linecnt
== -1 || curwp
->w_linep
!= last_top_line
3740 || last_scroll_pos
== -1) {
3743 for (lp
= lforw (curbp
->b_linep
); lp
!= curbp
->b_linep
;
3745 if (lp
== curwp
->w_linep
)
3746 scr_pos
= scr_range
;
3751 curbp
->b_linecnt
= scr_range
;
3752 last_scroll_pos
= scr_pos
;
3753 last_top_line
= curwp
->w_linep
;
3757 * Set new scroll range and position.
3759 mswin_setscrollrange (curwp
->w_ntrows
- 2, curbp
->b_linecnt
- 1);
3760 mswin_setscrollpos (last_scroll_pos
);
3763 #endif /* _WINDOWS */