1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: basic.c 831 2007-11-27 01:04:19Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2007 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: Cursor manipulation functions
22 * The routines in this file move the cursor around on the screen. They
23 * compute a new value for the cursor, then adjust ".". The display code
24 * always updates the cursor location, so only moves between lines, or
25 * functions that adjust the top line in the window and invalidate the
30 #include "osdep/terminal.h"
34 * Move the cursor to the
35 * beginning of the current line.
46 * Move the cursor backwards by "n" characters. If "n" is less than zero call
47 * "forwchar" to actually do the move. Otherwise compute the new cursor
48 * location. Error if you try and move out of the buffer. Set the flag if the
49 * line pointer for dot changes.
52 backchar(int f
, int n
)
57 return (forwchar(f
, -n
));
60 if (curwp
->w_doto
== 0) {
61 if ((lp
=lback(curwp
->w_dotp
)) == curbp
->b_linep
){
62 if(Pmaster
&& Pmaster
->headents
)
64 * go up into editing the mail header if on
65 * the top line and the user hits the left arrow!!!
67 * if the editor returns anything except -1, the
68 * user requested something special, so let
71 return(HeaderEditor(2, 1));
77 curwp
->w_doto
= llength(lp
);
78 curwp
->w_flag
|= WFMOVE
;
87 * Move the cursor backwards by "n" characters. If "n" is less than zero call
88 * "forwchar" to actually do the move. Otherwise compute the new cursor
89 * location. Error if you try and move out of the buffer. Set the flag if the
90 * line pointer for dot changes.
92 * This routine does _not_ do the header editor checks. It is used by
93 * backword() in pico\word.c which gets stuck in a loop trying to go
94 * back if you've jumped into a header.
97 backchar_no_header_editor(int f
, int n
)
102 return (forwchar(f
, -n
));
105 if (curwp
->w_doto
== 0) {
106 if ((lp
=lback(curwp
->w_dotp
)) == curbp
->b_linep
){
111 curwp
->w_doto
= llength(lp
);
112 curwp
->w_flag
|= WFMOVE
;
121 * Move the cursor to the end of the current line. Trivial. No errors.
124 gotoeol(int f
, int n
)
126 curwp
->w_doto
= llength(curwp
->w_dotp
);
132 * Move the cursor forwwards by "n" characters. If "n" is less than zero call
133 * "backchar" to actually do the move. Otherwise compute the new cursor
134 * location, and move ".". Error if you try and move off the end of the
135 * buffer. Set the flag if the line pointer for dot changes.
138 forwchar(int f
, int n
)
141 return (backchar(f
, -n
));
144 if (curwp
->w_doto
== llength(curwp
->w_dotp
)) {
145 if (curwp
->w_dotp
== curbp
->b_linep
)
148 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
150 curwp
->w_flag
|= WFMOVE
;
161 * move to a particular line.
162 * argument (n) must be a positive integer for
163 * this to actually do anything
166 gotoline(int f
, int n
)
168 if (n
< 1) /* if a bogus argument...then leave */
171 /* first, we go to the start of the buffer */
172 curwp
->w_dotp
= lforw(curbp
->b_linep
);
174 return(forwline(f
, n
-1));
179 * Goto the beginning of the buffer. Massive adjustment of dot. This is
180 * considered to be hard motion; it really isn't if the original value of dot
181 * is the same as the new value of dot. Normally bound to "M-<".
184 gotobob(int f
, int n
)
186 curwp
->w_dotp
= lforw(curbp
->b_linep
);
188 curwp
->w_flag
|= WFHARD
;
194 * Move to the end of the buffer. Dot is always put at the end of the file
195 * (ZJ). The standard screen code does most of the hard parts of update.
199 gotoeob(int f
, int n
)
201 curwp
->w_dotp
= curbp
->b_linep
;
203 curwp
->w_flag
|= WFHARD
;
209 * Move forward by full lines. If the number of lines to move is less than
210 * zero, call the backward line function to actually do it. The last command
211 * controls how the goal column is set. Bound to "C-N". No errors are
215 forwline(int f
, int n
)
220 return (backline(f
, -n
));
222 if ((lastflag
&CFCPCN
) == 0) /* Reset goal if last */
223 curgoal
= getccol(FALSE
); /* not C-P or C-N */
227 while (n
-- && dlp
!=curbp
->b_linep
)
231 curwp
->w_doto
= getgoal(dlp
);
232 curwp
->w_flag
|= WFMOVE
;
238 * This function is like "forwline", but goes backwards. The scheme is exactly
239 * the same. Check for arguments that are less than zero and call your
240 * alternate. Figure out the new line and call "movedot" to perform the
241 * motion. No errors are possible. Bound to "C-P".
244 backline(int f
, int n
)
249 return (forwline(f
, -n
));
251 if(Pmaster
&& Pmaster
->headents
){
253 * go up into editing the mail header if on the top line
254 * and the user hits the up arrow!!!
256 if (lback(curwp
->w_dotp
) == curbp
->b_linep
)
258 * if the editor returns anything except -1 then the user
259 * has requested something special, so let pico know...
261 return(HeaderEditor(1, 1));
264 if ((lastflag
&CFCPCN
) == 0) /* Reset goal if the */
265 curgoal
= getccol(FALSE
); /* last isn't C-P, C-N */
269 while (n
-- && lback(dlp
)!=curbp
->b_linep
)
273 curwp
->w_doto
= getgoal(dlp
);
274 curwp
->w_flag
|= WFMOVE
;
280 * go back to the begining of the current paragraph
281 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
282 * combination to delimit the begining of a paragraph
285 gotobop(int f
, int n
)
288 UCS qstr
[NLINE
], qstr2
[NLINE
];
290 if (n
< 0) /* the other way...*/
291 return(gotoeop(f
, -n
));
293 while (n
-- > 0) { /* for each one asked for */
295 while(lisblank(curwp
->w_dotp
)
296 && lback(curwp
->w_dotp
) != curbp
->b_linep
){
297 curwp
->w_dotp
= lback(curwp
->w_dotp
);
301 /* scan line by line until we come to a line ending with
302 * a <NL><NL> or <NL><TAB> or <NL><SPACE>
304 * PLUS: if there's a quote string, a quoted-to-non-quoted
307 quoted
= quote_match(glo_quote_str
, curwp
->w_dotp
, qstr
, NLINE
);
308 qlen
= quoted
? ucs4_strlen(qstr
) : 0;
309 while(lback(curwp
->w_dotp
) != curbp
->b_linep
310 && llength(lback(curwp
->w_dotp
)) > qlen
311 && quoted
== quote_match(glo_quote_str
,
312 lback(curwp
->w_dotp
),
314 && !ucs4_strcmp(qstr
, qstr2
)
315 && lgetc(curwp
->w_dotp
, qlen
).c
!= TAB
316 && lgetc(curwp
->w_dotp
, qlen
).c
!= ' ')
317 curwp
->w_dotp
= lback(curwp
->w_dotp
);
321 if(lback(curwp
->w_dotp
) == curbp
->b_linep
)
324 curwp
->w_dotp
= lback(curwp
->w_dotp
);
329 /* leave cursor on first word in para */
331 while(ucs4_isspace(lgetc(curwp
->w_dotp
, curwp
->w_doto
).c
))
332 if(++curwp
->w_doto
>= llength(curwp
->w_dotp
)){
334 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
335 if(curwp
->w_dotp
== curbp
->b_linep
)
341 curwp
->w_flag
|= WFMOVE
; /* force screen update */
347 * go forword to the end of the current paragraph
348 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
349 * combination to delimit the begining of a paragraph
352 gotoeop(int f
, int n
)
355 UCS qstr
[NLINE
], qstr2
[NLINE
];
357 if (n
< 0) /* the other way...*/
358 return(gotobop(f
, -n
));
360 while (n
-- > 0) { /* for each one asked for */
362 while(lisblank(curwp
->w_dotp
)){
364 if((curwp
->w_dotp
= lforw(curwp
->w_dotp
)) == curbp
->b_linep
)
368 /* scan line by line until we come to a line ending with
369 * a <NL><NL> or <NL><TAB> or <NL><SPACE>
371 * PLUS: if there's a quote string, a quoted-to-non-quoted
374 quoted
= quote_match(glo_quote_str
,
375 curwp
->w_dotp
, qstr
, NLINE
);
376 qlen
= quoted
? ucs4_strlen(qstr
) : 0;
378 while(curwp
->w_dotp
!= curbp
->b_linep
379 && llength(lforw(curwp
->w_dotp
)) > qlen
380 && (quoted
== quote_match(glo_quote_str
,
381 lforw(curwp
->w_dotp
),
383 && !ucs4_strcmp(qstr
, qstr2
))
384 && lgetc(lforw(curwp
->w_dotp
), qlen
).c
!= TAB
385 && lgetc(lforw(curwp
->w_dotp
), qlen
).c
!= ' ')
386 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
388 curwp
->w_doto
= llength(curwp
->w_dotp
);
392 if(curwp
->w_dotp
== curbp
->b_linep
)
395 curwp
->w_dotp
= lforw(curwp
->w_dotp
);
401 curwp
->w_flag
|= WFMOVE
; /* force screen update */
402 return(curwp
->w_dotp
!= curbp
->b_linep
);
406 * This routine, given a pointer to a LINE, and the current cursor goal
407 * column, return the best choice for the offset. The offset is returned.
408 * Used by "C-N" and "C-P".
420 while (dbo
!= llength(dlp
)) {
421 c
= lgetc(dlp
, dbo
).c
;
428 else if (ISCONTROL(c
)){
435 newcol
+= (w
>= 0 ? w
: 1);
438 if (newcol
> curgoal
)
450 * Scroll the display forward (up) n lines.
453 scrollforw(int n
, int movedot
)
462 while (n
-- && lp
!=curbp
->b_linep
)
465 if (movedot
) { /* Move dot to top of page. */
470 curwp
->w_flag
|= WFHARD
;
471 if(lp
== curbp
->b_linep
)
477 * if the header is open, close it ...
479 if(Pmaster
&& Pmaster
->headents
&& ComposerTopLine
!= COMPOSER_TOP_LINE
){
480 n
-= ComposerTopLine
- COMPOSER_TOP_LINE
;
485 * scroll down from the top the same number of lines we've moved
489 scrollup(curwp
, -1, nl
-n
-1);
492 /* Requested to not move the dot. Look for the dot in the current
493 * window. loop through all lines, stop when at end of window
494 * or endof buffer. If the dot is found, it can stay where it
495 * is, otherwise we do need to move it.
498 for ( lp2
= lp
, i
= 0;
499 lp2
!= curbp
->b_linep
&& i
< curwp
->w_ntrows
;
500 lp2
= lforw(lp2
), ++i
) {
501 if (curwp
->w_dotp
== lp2
) {
507 /* Dot not found in window. Move to first line of window. */
518 * Scroll forward by a specified number of lines, or by a full page if no
519 * argument. Bound to "C-V". The "2" in the arithmetic on the window size is
520 * the overlap; this value is the default overlap value in ITS EMACS. Because
521 * this zaps the top line in the display window, we have to do a hard update.
524 forwpage(int f
, int n
)
528 n
= curwp
->w_ntrows
- 2; /* Default scroll. */
529 if (n
<= 0) /* Forget the overlap */
530 n
= 1; /* if tiny window. */
532 return (backpage(f
, -n
));
534 else /* Convert from pages */
535 n
*= curwp
->w_ntrows
; /* to lines. */
537 return (scrollforw (n
, TRUE
));
542 * Scroll back (down) number of lines.
545 scrollback(int n
, int movedot
)
547 register LINE
*lp
, *tp
;
551 if(Pmaster
&& Pmaster
->headents
){
553 * go up into editing the mail header if on the top line
554 * and the user hits the up arrow!!!
556 if (lback(curwp
->w_dotp
) == curbp
->b_linep
){
558 * if the editor returns anything except -1 then the user
559 * has requested something special, so let pico know...
561 return(HeaderEditor(1, 1));
566 * Count back the number of lines requested.
570 while (n
-- && lback(lp
)!=curbp
->b_linep
)
574 curwp
->w_flag
|= WFHARD
;
577 * scroll down from the top the same number of lines we've moved
580 * This isn't too cool, but it has to be this way so we can
581 * gracefully scroll in the message header
583 if(Pmaster
&& Pmaster
->headents
){
584 if((lback(lp
)==curbp
->b_linep
) && (ComposerTopLine
==COMPOSER_TOP_LINE
))
585 n
-= entry_line(1000, TRUE
); /* never more than 1000 headers */
586 if(nl
-n
-1 < curwp
->w_ntrows
)
588 scrolldown(curwp
, -1, nl
-n
-1);
592 scrolldown(curwp
, -1, nl
-n
-1);
594 if(Pmaster
&& Pmaster
->headents
){
596 * if we're at the top of the page, and the header is closed,
599 if((lback(lp
) == curbp
->b_linep
)
600 && (ComposerTopLine
== COMPOSER_TOP_LINE
)){
602 movecursor(ComposerTopLine
, 0);
607 * Decide if we move the dot or not. Calculation done AFTER deciding
608 * if we display the header because that will change the number of
609 * lines on the screen.
612 /* Dot gets put at top of window. */
613 curwp
->w_dotp
= curwp
->w_linep
;
617 /* Requested not to move dot, but we do need to keep in on
618 * the screen. Verify that it is still in the range of lines
619 * visable in the window. Loop from the first line to the
620 * last line, until we reach the end of the buffer or the end
621 * of the window. If we find the dot, then we don't need
624 for ( tp
= curwp
->w_linep
, i
= 0;
625 tp
!= curbp
->b_linep
&& i
< curwp
->w_ntrows
;
626 tp
= lforw(tp
), ++i
) {
627 if (curwp
->w_dotp
== tp
) {
633 /* Dot not found in window. Move to last line of window. */
634 curwp
->w_dotp
= lback (tp
);
646 * This command is like "forwpage", but it goes backwards. The "2", like
647 * above, is the overlap between the two windows. The value is from the ITS
648 * EMACS manual. Bound to "M-V". We do a hard update for exactly the same
652 backpage(int f
, int n
)
656 n
= curwp
->w_ntrows
- 2; /* Default scroll. */
657 if (n
<= 0) /* Don't blow up if the */
658 n
= 1; /* window is tiny. */
660 return (forwpage(f
, -n
));
662 else /* Convert from pages */
663 n
*= curwp
->w_ntrows
; /* to lines. */
665 return (scrollback (n
, TRUE
));
670 scrollupline(int f
, int n
)
672 return (scrollback (1, FALSE
));
677 scrolldownline(int f
, int n
)
679 return (scrollforw (1, FALSE
));
685 * Scroll to a position.
688 scrollto(int f
, int n
)
695 scrollLine
= mswin_getscrollto ();
698 * Starting at the first data line in the buffer, step forward
699 * 'scrollLine' lines to find the new top line. It is a circular
700 * list of buffers, so watch for the first line to reappear. if
701 * it does, we have some sort of internal error, abort scroll
702 * operation. Also watch for NULL, just in case.
704 lp
= lforw (curbp
->b_linep
);
705 for (i
= 0; i
< scrollLine
&& lp
!= curbp
->b_linep
&& lp
!= NULL
; ++i
)
708 if (lp
== curbp
->b_linep
|| lp
== NULL
)
709 return (FALSE
); /* Whoops! */
712 /* Set the new top line for the window and flag a redraw. */
716 curwp
->w_flag
|= WFHARD
;
718 if(Pmaster
&& Pmaster
->headents
){
720 * If we are at the top of the page and header not open, open it.
721 * If we are not at the top of the page and the header is open,
724 if((lback(lp
) == curbp
->b_linep
)
725 && (ComposerTopLine
== COMPOSER_TOP_LINE
)){
727 movecursor(ComposerTopLine
, 0);
729 else if((lback(lp
) != curbp
->b_linep
)
730 && (ComposerTopLine
!= COMPOSER_TOP_LINE
)){
742 * Set the mark in the current window to the value of "." in the window. No
743 * errors are possible. Bound to "M-.". If told to set an already set mark
747 setmark(int f
, int n
)
750 curwp
->w_markp
= curwp
->w_dotp
;
751 curwp
->w_marko
= curwp
->w_doto
;
753 emlwrite("Mark Set", NULL
);
756 /* clear inverse chars between here and dot */
758 curwp
->w_markp
= NULL
;
760 emlwrite("Mark UNset", NULL
);
764 mswin_allowcopycut(curwp
->w_markp
? kremove
: NULL
);
771 * Swap the values of "." and "mark" in the current window. This is pretty
772 * easy, bacause all of the hard work gets done by the standard routine
773 * that moves the mark about. The only possible error is "no mark". Bound to
777 swapmark(int f
, int n
)
779 register LINE
*odotp
;
782 if (curwp
->w_markp
== NULL
) {
784 emlwrite("No mark in this window", NULL
);
788 odotp
= curwp
->w_dotp
;
789 odoto
= curwp
->w_doto
;
790 curwp
->w_dotp
= curwp
->w_markp
;
791 curwp
->w_doto
= curwp
->w_marko
;
792 curwp
->w_markp
= odotp
;
793 curwp
->w_marko
= odoto
;
794 curwp
->w_flag
|= WFMOVE
;
800 * Set the mark in the current window to the value of "." in the window. No
801 * errors are possible. Bound to "M-.". If told to set an already set mark
805 setimark(int f
, int n
)
807 curwp
->w_imarkp
= curwp
->w_dotp
;
808 curwp
->w_imarko
= curwp
->w_doto
;
814 * Swap the values of "." and "mark" in the current window. This is pretty
815 * easy, bacause all of the hard work gets done by the standard routine
816 * that moves the mark about. The only possible error is "no mark". Bound to
820 swapimark(int f
, int n
)
822 register LINE
*odotp
;
825 if (curwp
->w_imarkp
== NULL
) {
827 emlwrite("Programmer botch! No mark in this window", NULL
);
831 odotp
= curwp
->w_dotp
;
832 odoto
= curwp
->w_doto
;
833 curwp
->w_dotp
= curwp
->w_imarkp
;
834 curwp
->w_doto
= curwp
->w_imarko
;
835 curwp
->w_imarkp
= odotp
;
836 curwp
->w_imarko
= odoto
;
837 curwp
->w_flag
|= WFMOVE
;
843 * If dot comes before mark, do nothing.
844 * If mark comes before dot, swap them.
847 swap_mark_and_dot_if_mark_comes_first(void)
851 if(!(curwp
&& curwp
->w_dotp
&& curwp
->w_markp
))
854 if(curwp
->w_dotp
== curwp
->w_markp
){ /* they are in the same line */
855 if(curwp
->w_doto
> curwp
->w_marko
)
862 * Search forward and backward from dot to see if mark
863 * is less than or greater than dot.
865 flp
= blp
= curwp
->w_dotp
;
866 while(flp
!= curbp
->b_linep
|| lback(blp
) != curbp
->b_linep
){
867 if(flp
!= curbp
->b_linep
){
869 if(flp
== curwp
->w_markp
) /* dot already less than mark */
873 if(lback(blp
) != curbp
->b_linep
){
875 if(blp
== curwp
->w_markp
){
887 * Handle a mouse down.
890 mousepress(int f
, int n
)
897 mouse_get_last (NULL
, &mp
);
901 i
= mp
.row
- ((Pmaster
&& Pmaster
->headents
) ? ComposerTopLine
: 2);
904 /* Clear existing region. */
908 /* Move to top of document before editing header. */
909 curwp
->w_dotp
= curwp
->w_linep
;
911 curwp
->w_flag
|= WFMOVE
;
912 update (); /* And update. */
914 return (HeaderEditor (1, 1));
918 while(i
-- && lp
!= curbp
->b_linep
)
923 curwp
->w_doto
= getgoal(lp
);
924 curwp
->w_flag
|= WFMOVE
;
935 toggle_xterm_mouse(int f
, int n
)
940 (e
=mouseexist()) ? end_mouse() : (void) init_mouse();
941 if(e
!= mouseexist()){
942 mouseexist() ? emlwrite(_("Xterm mouse tracking on!"), NULL
)
943 : emlwrite(_("Xterm mouse tracking off!"), NULL
);
946 emlwrite(_("Xterm mouse tracking still off ($DISPLAY variable set?)"), NULL
);