2 * ========================================================================
3 * Copyright 2006-2007 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: Cursor manipulation functions
18 * The routines in this file move the cursor around on the screen. They
19 * compute a new value for the cursor, then adjust ".". The display code
20 * always updates the cursor location, so only moves between lines, or
21 * functions that adjust the top line in the window and invalidate the
26 #include "osdep/terminal.h"
30 * Move the cursor to the
31 * beginning of the current line.
42 * Move the cursor backwards by "n" characters. If "n" is less than zero call
43 * "forwchar" to actually do the move. Otherwise compute the new cursor
44 * location. Error if you try and move out of the buffer. Set the flag if the
45 * line pointer for dot changes.
48 backchar(int f
, int n
)
53 return (forwchar(f
, -n
));
56 if (curwp
->w_doto
== 0) {
57 if ((lp
=lback(curwp
->w_dotp
)) == curbp
->b_linep
){
58 if(Pmaster
&& Pmaster
->headents
)
60 * go up into editing the mail header if on
61 * the top line and the user hits the left arrow!!!
63 * if the editor returns anything except -1, the
64 * user requested something special, so let
67 return(HeaderEditor(2, 1));
73 curwp
->w_doto
= llength(lp
);
74 curwp
->w_flag
|= WFMOVE
;
83 * Move the cursor backwards by "n" characters. If "n" is less than zero call
84 * "forwchar" to actually do the move. Otherwise compute the new cursor
85 * location. Error if you try and move out of the buffer. Set the flag if the
86 * line pointer for dot changes.
88 * This routine does _not_ do the header editor checks. It is used by
89 * backword() in pico\word.c which gets stuck in a loop trying to go
90 * back if you've jumped into a header.
93 backchar_no_header_editor(int f
, int n
)
98 return (forwchar(f
, -n
));
101 if (curwp
->w_doto
== 0) {
102 if ((lp
=lback(curwp
->w_dotp
)) == curbp
->b_linep
){
107 curwp
->w_doto
= llength(lp
);
108 curwp
->w_flag
|= WFMOVE
;
117 * Move the cursor to the end of the current line. Trivial. No errors.
120 gotoeol(int f
, int n
)
122 curwp
->w_doto
= llength(curwp
->w_dotp
);
128 * Move the cursor forwwards by "n" characters. If "n" is less than zero call
129 * "backchar" to actually do the move. Otherwise compute the new cursor
130 * location, and move ".". Error if you try and move off the end of the
131 * buffer. Set the flag if the line pointer for dot changes.
134 forwchar(int f
, int n
)
137 return (backchar(f
, -n
));
140 if (curwp
->w_doto
== llength(curwp
->w_dotp
)) {
141 if (curwp
->w_dotp
== curbp
->b_linep
)
144 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
146 curwp
->w_flag
|= WFMOVE
;
157 * move to a particular line.
158 * argument (n) must be a positive integer for
159 * this to actually do anything
162 gotoline(int f
, int n
)
164 if (n
< 1) /* if a bogus argument...then leave */
167 /* first, we go to the start of the buffer */
168 curwp
->w_dotp
= lforw(curbp
->b_linep
);
170 return(forwline(f
, n
-1));
175 * Goto the beginning of the buffer. Massive adjustment of dot. This is
176 * considered to be hard motion; it really isn't if the original value of dot
177 * is the same as the new value of dot. Normally bound to "M-<".
180 gotobob(int f
, int n
)
182 curwp
->w_dotp
= lforw(curbp
->b_linep
);
184 curwp
->w_flag
|= WFHARD
;
190 * Move to the end of the buffer. Dot is always put at the end of the file
191 * (ZJ). The standard screen code does most of the hard parts of update.
195 gotoeob(int f
, int n
)
197 curwp
->w_dotp
= curbp
->b_linep
;
199 curwp
->w_flag
|= WFHARD
;
205 * Move forward by full lines. If the number of lines to move is less than
206 * zero, call the backward line function to actually do it. The last command
207 * controls how the goal column is set. Bound to "C-N". No errors are
211 forwline(int f
, int n
)
216 return (backline(f
, -n
));
218 if ((lastflag
&CFCPCN
) == 0) /* Reset goal if last */
219 curgoal
= getccol(FALSE
); /* not C-P or C-N */
223 while (n
-- && dlp
!=curbp
->b_linep
)
227 curwp
->w_doto
= getgoal(dlp
);
228 curwp
->w_flag
|= WFMOVE
;
234 * This function is like "forwline", but goes backwards. The scheme is exactly
235 * the same. Check for arguments that are less than zero and call your
236 * alternate. Figure out the new line and call "movedot" to perform the
237 * motion. No errors are possible. Bound to "C-P".
240 backline(int f
, int n
)
245 return (forwline(f
, -n
));
247 if(Pmaster
&& Pmaster
->headents
){
249 * go up into editing the mail header if on the top line
250 * and the user hits the up arrow!!!
252 if (lback(curwp
->w_dotp
) == curbp
->b_linep
)
254 * if the editor returns anything except -1 then the user
255 * has requested something special, so let pico know...
257 return(HeaderEditor(1, 1));
260 if ((lastflag
&CFCPCN
) == 0) /* Reset goal if the */
261 curgoal
= getccol(FALSE
); /* last isn't C-P, C-N */
265 while (n
-- && lback(dlp
)!=curbp
->b_linep
)
269 curwp
->w_doto
= getgoal(dlp
);
270 curwp
->w_flag
|= WFMOVE
;
276 * go back to the beginning of the current paragraph
277 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
278 * combination to delimit the beginning of a paragraph
281 gotobop(int f
, int n
)
284 UCS qstr
[NLINE
], qstr2
[NLINE
];
286 if (n
< 0) /* the other way...*/
287 return(gotoeop(f
, -n
));
289 while (n
-- > 0) { /* for each one asked for */
291 while(lisblank(curwp
->w_dotp
)
292 && lback(curwp
->w_dotp
) != curbp
->b_linep
){
293 curwp
->w_dotp
= lback(curwp
->w_dotp
);
297 /* scan line by line until we come to a line ending with
298 * a <NL><NL> or <NL><TAB> or <NL><SPACE>
300 * PLUS: if there's a quote string, a quoted-to-non-quoted
303 quoted
= quote_match(glo_quote_str
, curwp
->w_dotp
, qstr
, NLINE
);
304 qlen
= quoted
? ucs4_strlen(qstr
) : 0;
305 while(lback(curwp
->w_dotp
) != curbp
->b_linep
306 && llength(lback(curwp
->w_dotp
)) > qlen
307 && quoted
== quote_match(glo_quote_str
,
308 lback(curwp
->w_dotp
),
310 && !ucs4_strcmp(qstr
, qstr2
)
311 && lgetc(curwp
->w_dotp
, qlen
).c
!= TAB
312 && lgetc(curwp
->w_dotp
, qlen
).c
!= ' ')
313 curwp
->w_dotp
= lback(curwp
->w_dotp
);
317 if(lback(curwp
->w_dotp
) == curbp
->b_linep
)
320 curwp
->w_dotp
= lback(curwp
->w_dotp
);
325 /* leave cursor on first word in para */
327 while(ucs4_isspace(lgetc(curwp
->w_dotp
, curwp
->w_doto
).c
))
328 if(++curwp
->w_doto
>= llength(curwp
->w_dotp
)){
330 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
331 if(curwp
->w_dotp
== curbp
->b_linep
)
337 curwp
->w_flag
|= WFMOVE
; /* force screen update */
343 * go forword to the end of the current paragraph
344 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
345 * combination to delimit the beginning of a paragraph
348 gotoeop(int f
, int n
)
351 UCS qstr
[NLINE
], qstr2
[NLINE
];
353 if (n
< 0) /* the other way...*/
354 return(gotobop(f
, -n
));
356 while (n
-- > 0) { /* for each one asked for */
358 while(lisblank(curwp
->w_dotp
)){
360 if((curwp
->w_dotp
= lforw(curwp
->w_dotp
)) == curbp
->b_linep
)
364 /* scan line by line until we come to a line ending with
365 * a <NL><NL> or <NL><TAB> or <NL><SPACE>
367 * PLUS: if there's a quote string, a quoted-to-non-quoted
370 quoted
= quote_match(glo_quote_str
,
371 curwp
->w_dotp
, qstr
, NLINE
);
372 qlen
= quoted
? ucs4_strlen(qstr
) : 0;
374 while(curwp
->w_dotp
!= curbp
->b_linep
375 && llength(lforw(curwp
->w_dotp
)) > qlen
376 && (quoted
== quote_match(glo_quote_str
,
377 lforw(curwp
->w_dotp
),
379 && !ucs4_strcmp(qstr
, qstr2
))
380 && lgetc(lforw(curwp
->w_dotp
), qlen
).c
!= TAB
381 && lgetc(lforw(curwp
->w_dotp
), qlen
).c
!= ' ')
382 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
384 curwp
->w_doto
= llength(curwp
->w_dotp
);
388 if(curwp
->w_dotp
== curbp
->b_linep
)
391 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
397 curwp
->w_flag
|= WFMOVE
; /* force screen update */
398 return(curwp
->w_dotp
!= curbp
->b_linep
);
402 * This routine, given a pointer to a LINE, and the current cursor goal
403 * column, return the best choice for the offset. The offset is returned.
404 * Used by "C-N" and "C-P".
416 while (dbo
!= llength(dlp
)) {
417 c
= lgetc(dlp
, dbo
).c
;
424 else if (ISCONTROL(c
)){
431 newcol
+= (w
>= 0 ? w
: 1);
434 if (newcol
> curgoal
)
446 * Scroll the display forward (up) n lines.
449 scrollforw(int n
, int movedot
)
458 while (n
-- && lp
!=curbp
->b_linep
)
461 if (movedot
) { /* Move dot to top of page. */
466 curwp
->w_flag
|= WFHARD
;
467 if(lp
== curbp
->b_linep
)
473 * if the header is open, close it ...
475 if(Pmaster
&& Pmaster
->headents
&& ComposerTopLine
!= COMPOSER_TOP_LINE
){
476 n
-= ComposerTopLine
- COMPOSER_TOP_LINE
;
481 * scroll down from the top the same number of lines we've moved
485 scrollup(curwp
, -1, nl
-n
-1);
488 /* Requested to not move the dot. Look for the dot in the current
489 * window. loop through all lines, stop when at end of window
490 * or endof buffer. If the dot is found, it can stay where it
491 * is, otherwise we do need to move it.
494 for ( lp2
= lp
, i
= 0;
495 lp2
!= curbp
->b_linep
&& i
< curwp
->w_ntrows
;
496 lp2
= lforw(lp2
), ++i
) {
497 if (curwp
->w_dotp
== lp2
) {
503 /* Dot not found in window. Move to first line of window. */
514 * Scroll forward by a specified number of lines, or by a full page if no
515 * argument. Bound to "C-V". The "2" in the arithmetic on the window size is
516 * the overlap; this value is the default overlap value in ITS EMACS. Because
517 * this zaps the top line in the display window, we have to do a hard update.
520 forwpage(int f
, int n
)
524 n
= curwp
->w_ntrows
- 2; /* Default scroll. */
525 if (n
<= 0) /* Forget the overlap */
526 n
= 1; /* if tiny window. */
528 return (backpage(f
, -n
));
530 else /* Convert from pages */
531 n
*= curwp
->w_ntrows
; /* to lines. */
533 return (scrollforw (n
, TRUE
));
538 * Scroll back (down) number of lines.
541 scrollback(int n
, int movedot
)
543 register LINE
*lp
, *tp
;
547 if(Pmaster
&& Pmaster
->headents
){
549 * go up into editing the mail header if on the top line
550 * and the user hits the up arrow!!!
552 if (lback(curwp
->w_dotp
) == curbp
->b_linep
){
554 * if the editor returns anything except -1 then the user
555 * has requested something special, so let pico know...
557 return(HeaderEditor(1, 1));
562 * Count back the number of lines requested.
566 while (n
-- && lback(lp
)!=curbp
->b_linep
)
570 curwp
->w_flag
|= WFHARD
;
573 * scroll down from the top the same number of lines we've moved
576 * This isn't too cool, but it has to be this way so we can
577 * gracefully scroll in the message header
579 if(Pmaster
&& Pmaster
->headents
){
580 if((lback(lp
)==curbp
->b_linep
) && (ComposerTopLine
==COMPOSER_TOP_LINE
))
581 n
-= entry_line(1000, TRUE
); /* never more than 1000 headers */
582 if(nl
-n
-1 < curwp
->w_ntrows
)
584 scrolldown(curwp
, -1, nl
-n
-1);
588 scrolldown(curwp
, -1, nl
-n
-1);
590 if(Pmaster
&& Pmaster
->headents
){
592 * if we're at the top of the page, and the header is closed,
595 if((lback(lp
) == curbp
->b_linep
)
596 && (ComposerTopLine
== COMPOSER_TOP_LINE
)){
598 movecursor(ComposerTopLine
, 0);
603 * Decide if we move the dot or not. Calculation done AFTER deciding
604 * if we display the header because that will change the number of
605 * lines on the screen.
608 /* Dot gets put at top of window. */
609 curwp
->w_dotp
= curwp
->w_linep
;
613 /* Requested not to move dot, but we do need to keep in on
614 * the screen. Verify that it is still in the range of lines
615 * visible in the window. Loop from the first line to the
616 * last line, until we reach the end of the buffer or the end
617 * of the window. If we find the dot, then we don't need
620 for ( tp
= curwp
->w_linep
, i
= 0;
621 tp
!= curbp
->b_linep
&& i
< curwp
->w_ntrows
;
622 tp
= lforw(tp
), ++i
) {
623 if (curwp
->w_dotp
== tp
) {
629 /* Dot not found in window. Move to last line of window. */
630 curwp
->w_dotp
= lback (tp
);
642 * This command is like "forwpage", but it goes backwards. The "2", like
643 * above, is the overlap between the two windows. The value is from the ITS
644 * EMACS manual. Bound to "M-V". We do a hard update for exactly the same
648 backpage(int f
, int n
)
652 n
= curwp
->w_ntrows
- 2; /* Default scroll. */
653 if (n
<= 0) /* Don't blow up if the */
654 n
= 1; /* window is tiny. */
656 return (forwpage(f
, -n
));
658 else /* Convert from pages */
659 n
*= curwp
->w_ntrows
; /* to lines. */
661 return (scrollback (n
, TRUE
));
666 scrollupline(int f
, int n
)
668 return (scrollback (1, FALSE
));
673 scrolldownline(int f
, int n
)
675 return (scrollforw (1, FALSE
));
681 * Scroll to a position.
684 scrollto(int f
, int n
)
691 scrollLine
= mswin_getscrollto ();
694 * Starting at the first data line in the buffer, step forward
695 * 'scrollLine' lines to find the new top line. It is a circular
696 * list of buffers, so watch for the first line to reappear. if
697 * it does, we have some sort of internal error, abort scroll
698 * operation. Also watch for NULL, just in case.
700 lp
= lforw (curbp
->b_linep
);
701 for (i
= 0; i
< scrollLine
&& lp
!= curbp
->b_linep
&& lp
!= NULL
; ++i
)
704 if (lp
== curbp
->b_linep
|| lp
== NULL
)
705 return (FALSE
); /* Whoops! */
708 /* Set the new top line for the window and flag a redraw. */
712 curwp
->w_flag
|= WFHARD
;
714 if(Pmaster
&& Pmaster
->headents
){
716 * If we are at the top of the page and header not open, open it.
717 * If we are not at the top of the page and the header is open,
720 if((lback(lp
) == curbp
->b_linep
)
721 && (ComposerTopLine
== COMPOSER_TOP_LINE
)){
723 movecursor(ComposerTopLine
, 0);
725 else if((lback(lp
) != curbp
->b_linep
)
726 && (ComposerTopLine
!= COMPOSER_TOP_LINE
)){
738 * Set the mark in the current window to the value of "." in the window. No
739 * errors are possible. Bound to "M-.". If told to set an already set mark
743 setmark(int f
, int n
)
746 curwp
->w_markp
= curwp
->w_dotp
;
747 curwp
->w_marko
= curwp
->w_doto
;
749 emlwrite("Mark Set", NULL
);
752 /* clear inverse chars between here and dot */
754 curwp
->w_markp
= NULL
;
756 emlwrite("Mark UNset", NULL
);
760 mswin_allowcopycut(curwp
->w_markp
? kremove
: NULL
);
767 * Swap the values of "." and "mark" in the current window. This is pretty
768 * easy, because all of the hard work gets done by the standard routine
769 * that moves the mark about. The only possible error is "no mark". Bound to
773 swapmark(int f
, int n
)
775 register LINE
*odotp
;
778 if (curwp
->w_markp
== NULL
) {
780 emlwrite("No mark in this window", NULL
);
784 odotp
= curwp
->w_dotp
;
785 odoto
= curwp
->w_doto
;
786 curwp
->w_dotp
= curwp
->w_markp
;
787 curwp
->w_doto
= curwp
->w_marko
;
788 curwp
->w_markp
= odotp
;
789 curwp
->w_marko
= odoto
;
790 curwp
->w_flag
|= WFMOVE
;
796 * Set the mark in the current window to the value of "." in the window. No
797 * errors are possible. Bound to "M-.". If told to set an already set mark
801 setimark(int f
, int n
)
803 curwp
->w_imarkp
= curwp
->w_dotp
;
804 curwp
->w_imarko
= curwp
->w_doto
;
810 * Swap the values of "." and "mark" in the current window. This is pretty
811 * easy, because all of the hard work gets done by the standard routine
812 * that moves the mark about. The only possible error is "no mark". Bound to
816 swapimark(int f
, int n
)
818 register LINE
*odotp
;
821 if (curwp
->w_imarkp
== NULL
) {
823 emlwrite("Programmer botch! No mark in this window", NULL
);
827 odotp
= curwp
->w_dotp
;
828 odoto
= curwp
->w_doto
;
829 curwp
->w_dotp
= curwp
->w_imarkp
;
830 curwp
->w_doto
= curwp
->w_imarko
;
831 curwp
->w_imarkp
= odotp
;
832 curwp
->w_imarko
= odoto
;
833 curwp
->w_flag
|= WFMOVE
;
839 * If dot comes before mark, do nothing.
840 * If mark comes before dot, swap them.
843 swap_mark_and_dot_if_mark_comes_first(void)
847 if(!(curwp
&& curwp
->w_dotp
&& curwp
->w_markp
))
850 if(curwp
->w_dotp
== curwp
->w_markp
){ /* they are in the same line */
851 if(curwp
->w_doto
> curwp
->w_marko
)
858 * Search forward and backward from dot to see if mark
859 * is less than or greater than dot.
861 flp
= blp
= curwp
->w_dotp
;
862 while(flp
!= curbp
->b_linep
|| lback(blp
) != curbp
->b_linep
){
863 if(flp
!= curbp
->b_linep
){
865 if(flp
== curwp
->w_markp
) /* dot already less than mark */
869 if(lback(blp
) != curbp
->b_linep
){
871 if(blp
== curwp
->w_markp
){
883 * Handle a mouse down.
886 mousepress(int f
, int n
)
893 mouse_get_last (NULL
, &mp
);
897 i
= mp
.row
- ((Pmaster
&& Pmaster
->headents
) ? ComposerTopLine
: 2);
900 /* Clear existing region. */
904 /* Move to top of document before editing header. */
905 curwp
->w_dotp
= curwp
->w_linep
;
907 curwp
->w_flag
|= WFMOVE
;
908 update (); /* And update. */
910 return (HeaderEditor (1, 1));
914 while(i
-- && lp
!= curbp
->b_linep
)
919 curwp
->w_doto
= getgoal(lp
);
920 curwp
->w_flag
|= WFMOVE
;
931 toggle_xterm_mouse(int f
, int n
)
936 (e
=mouseexist()) ? end_mouse() : (void) init_mouse();
937 if(e
!= mouseexist()){
938 mouseexist() ? emlwrite(_("Xterm mouse tracking on!"), NULL
)
939 : emlwrite(_("Xterm mouse tracking off!"), NULL
);
942 emlwrite(_("Xterm mouse tracking still off ($DISPLAY variable set?)"), NULL
);