1 /**************************************************************
2 **** Cursor.c : cursor movement managment. ****
3 **** Free software under GNU license, started on 20.3.2000 ****
4 **** © T.Pierron, C.Guillaume. ****
5 **************************************************************/
7 #include <exec/types.h>
8 #include <graphics/rastport.h>
12 #include "ProtoTypes.h"
14 #define CATCOMP_NUMBERS /* Strings id for err. msg */
18 /*** Convert num chars to real column number: ***/
19 ULONG
x2pos(LINE
*ln
, ULONG nb
)
21 register ULONG nbrc
=0;
23 for(str
=ln
->stream
; nb
; nb
--, str
++)
24 nbrc
+= (*str
=='\t' ? tabstop(nbrc
) : 1);
29 /*** Adjust column to an existing one: ***/
30 ULONG
adjust_rc(LINE
*ln
, ULONG rc
, ULONG
*c
, UBYTE atleast
)
32 register ULONG nbrc
, nbc
;
34 for(str
=ln
->stream
, nbrc
=nbc
=0; nbc
<ln
->size
; str
++, nbc
++)
36 register ULONG tmp
= (*str
=='\t' ? tabstop(nbrc
) : 1);
37 /* If atleast is TRUE be sure that cursor will be at least at column rc: */
38 if(atleast
) if(nbrc
<rc
) nbrc
+=tmp
; else break;
39 else if(nbrc
+tmp
<=rc
) nbrc
+=tmp
; else break;
44 /*** Returns the characer-index corresponding to the screen column ***/
45 ULONG
find_nbc(LINE
*ln
, ULONG nbrc
)
47 register LONG rc
, nbc
;
49 for(str
=ln
->stream
, rc
=0, nbc
=ln
->size
; nbc
&& rc
<nbrc
; str
++, nbc
--)
50 rc
+= (*str
=='\t' ? tabstop(rc
) : 1);
54 /*** Adjust leftpos according to cursor one: ***/
55 LONG
adjust_leftpos(Project p
, WORD step
)
58 if(step
>=0) x
=p
->nbrc
+step
-gui
.nbcol
+1;
63 /*** Search for next available word in the line ***/
64 LONG
forward_word(LINE
*ln
, ULONG pos
)
69 for(str
=ln
->stream
+pos
,type
=TypeChar
[*str
]; pos
<ln
->size
&& type
==TypeChar
[*str
]; pos
++,str
++);
70 /* Skip white spaces */
71 while( pos
<ln
->size
&& TypeChar
[*str
]==SPACE
) str
++;
72 return str
- ln
->stream
;
75 /*** Search for previous word in a line ***/
76 LONG
backward_word(LINE
*ln
, ULONG pos
)
80 /* Skip white spaces */
81 for(str
=ln
->stream
+ pos
; pos
>0 && TypeChar
[*str
]==SPACE
; str
--,pos
--);
83 for(type
=TypeChar
[*str
]; ; pos
--,str
--)
84 if( type
!= TypeChar
[*str
] ) { str
++; break; }
85 else if( pos
== 0 ) { break; }
86 return str
- ln
->stream
;
89 /*** Display cursor of project p ***/
90 void inv_curs(Project p
, BYTE state
)
94 /* Just draw a simple vert bart for selection */
95 WORD y
= p
->ycurs
- BASEL
;
96 RectFill(RP
,p
->xcurs
, y
, p
->xcurs
+1, y
+YSIZE
-1);
98 UBYTE thechar
, oldmask
= RP
->Mask
;
99 /* Fully highlight character */
100 RP
->Mask
= gui
.selmask
;
101 if(state
) SetABPenDrMd(RP
,pen
.fgfill
,pen
.bgfill
,JAM2
);
103 if(state
!= 0 && p
->cursmode
) {
104 /* Draw the replacement cursor */
105 SetAPen(RP
, pen
.bgfill
);
106 RectFill(RP
,p
->xcurs
, p
->ycurs
, p
->xcurs
+XSIZE
-1, p
->ycurs
-BASEL
+YSIZE
-1);
109 thechar
= (p
->nbc
< p
->edited
->size
? p
->edited
->stream
[p
->nbc
] : ' ');
110 if(thechar
== '\t') thechar
= ' ';
111 Move(RP
,p
->xcurs
,p
->ycurs
); Text(RP
,&thechar
,1);
113 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
118 /*** Set cursor to a specified position in file ***/
119 void set_cursor_line(Project p
, LONG pos
, ULONG top
)
122 /* Adjust all related variables */
124 if(pos
>= p
->max_lines
) pos
= p
->max_lines
-1;
125 if(pos
< p
->nbl
) for(ln
=p
->edited
; p
->nbl
!=pos
; ln
=ln
->prev
, p
->nbl
--);
126 else for(ln
=p
->edited
; p
->nbl
!=pos
; ln
=ln
->next
, p
->nbl
++);
128 /* Cursor position */
129 p
->nbrc
= adjust_rc(p
->edited
=ln
, p
->nbrwc
, &p
->nbc
, FALSE
);
130 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
131 p
->ycurs
= (p
->nbl
-top
)*YSIZE
+ gui
.topcurs
;
135 /*** Set top-left visible corner ***/
136 void set_top_line(Project p
, ULONG top
, ULONG left
)
138 LINE
*ln
; LONG nb
= top
-p
->top_line
;
139 if(nb
< 0) for(ln
=p
->show
; nb
++; ln
=ln
->prev
);
140 else for(ln
=p
->show
; nb
--; ln
=ln
->next
);
144 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;
145 redraw_content(p
, ln
, gui
.topcurs
, gui
.nbline
);
149 /*** Move cursor incrementally (paste) ***/
150 void move_cursor(Project p
, LONG dx
, LONG dy
)
152 LINE
*ln
; LONG nb
=dy
;
154 if(nb
< 0) for(ln
=p
->edited
; nb
++; ln
=ln
->prev
);
155 else for(ln
=p
->edited
; nb
--; ln
=ln
->next
);
158 p
->nbrwc
= p
->nbrc
= x2pos(ln
,p
->nbc
= dx
);
160 dx
= center_horiz(p
);
161 /* Don't scroll if top_line changed! (whole redraw instead) */
162 if(dx
!= p
->left_pos
&& dy
== 0)
163 scroll_xy(p
, dx
, p
->top_line
, TRUE
);
165 nb
= center_vert(p
) - p
->top_line
;
166 for(p
->top_line
+= nb
,ln
=p
->show
; nb
--; ln
=ln
->next
);
167 p
->show
= ln
; p
->left_pos
= dx
;
169 p
->ycurs
= (p
->nbl
-p
->top_line
)*YSIZE
+ gui
.topcurs
;
170 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
174 /*** Move cursor to absolute position ***/
175 void move_to_line(Project p
, ULONG nbline
, char dirtiness
)
179 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
180 /* Get the new top line */
181 { register ULONG old_nbl
= p
->nbl
;
183 newtop
= center_vert(p
);
185 } set_cursor_line(p
, nbline
, newtop
);
187 if(dirtiness
== LINE_AS_IS
)
188 scroll_xy(p
, center_horiz(p
), newtop
, TRUE
);
191 /* Some lines are modified: redraw in one pass */
192 if( newtop
== p
->top_line
) {
194 if( dirtiness
== LINES_DIRTY
)
195 scroll_up(p
, p
->edited
->next
, p
->ycurs
, center_horiz(p
)),
197 else if( p
->left_pos
!= (newtop
= center_horiz(p
)) )
198 scroll_xy(p
, newtop
, p
->top_line
, FALSE
);
200 else if( dirtiness
== LINE_DIRTY
)
201 scroll_xy(p
, center_horiz(p
), newtop
, TRUE
);
203 set_top_line(p
, newtop
, center_horiz(p
));
205 /* Move cursor or selection */
206 if(p
->ccp
.select
) move_selection(p
, p
->nbrwc
, p
->nbl
);
211 /*** Move cursor up ***/
212 void curs_up(Project p
)
215 if( ( ln
= p
->edited
->prev
) )
217 LONG newx
; BYTE scroll
=0;
220 /* Is the cursor on top of display? */
221 if(p
->ycurs
> gui
.topcurs
) p
->ycurs
-= YSIZE
;
224 p
->nbl
--; p
->edited
=ln
;
225 p
->nbrc
= adjust_rc(ln
, p
->nbrwc
, &p
->nbc
, FALSE
);
226 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
228 /* If cursor exits edit area due to horizontal **
229 ** adjustment, scroll the display accordingly: */
230 if((newx
=center_horiz(p
))!=p
->left_pos
|| scroll
)
231 scroll_xy(p
, newx
, p
->top_line
-scroll
, scroll
);
233 /* Update selection */
234 if(p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
240 /*** Move cursor down ***/
241 void curs_down(Project p
)
244 if( ( ln
= p
->edited
->next
) )
246 LONG newx
; BYTE scroll
=0;
249 /* Is the cursor at the bottom of the display? */
250 if( p
->ycurs
< gui
.botcurs
) p
->ycurs
+= YSIZE
;
253 p
->nbl
++; p
->edited
=ln
;
254 p
->nbrc
= adjust_rc(ln
, p
->nbrwc
, &p
->nbc
, FALSE
);
255 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
257 /* Minimise calls to scroll_xy */
258 if((newx
=center_horiz(p
))!=p
->left_pos
|| scroll
)
259 scroll_xy(p
, newx
, p
->top_line
+scroll
, scroll
);
261 /* Update selection */
262 if(p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
268 /*** Move cursor to the left ***/
269 void curs_left(Project p
, BYTE word
)
275 if(word
) p
->nbc
= backward_word(p
->edited
, p
->nbc
-1); else p
->nbc
--;
276 p
->nbrwc
= p
->nbrc
= x2pos(p
->edited
, p
->nbc
);
277 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;
279 /* Is it gone outside edit area? */
280 if(p
->nbrc
<p
->left_pos
)
281 scroll_xy(p
, adjust_leftpos(p
, -gui
.xstep
), p
->top_line
, FALSE
);
283 if(p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
287 } else if( ( prev
= p
->edited
->prev
) ) {
289 p
->nbrwc
= x2pos(prev
, prev
->size
);
294 /*** One pos right ***/
295 void curs_right(Project p
, BYTE word
)
297 if(p
->nbc
< p
->edited
->size
)
300 if(word
) p
->nbc
= forward_word(p
->edited
, p
->nbc
); else p
->nbc
++;
301 p
->nbrwc
= p
->nbrc
= x2pos(p
->edited
, p
->nbc
);
302 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;
304 /* Move the cursor */
305 /* Is it gone outside edit area? */
306 if(p
->nbrc
>=p
->left_pos
+gui
.nbcol
)
307 scroll_xy(p
, adjust_leftpos(p
, gui
.xstep
), p
->top_line
, FALSE
);
309 if(p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
313 } else if(p
->edited
->next
) {
314 /* jump down to next line */
320 /*** Jump the cursor far left or far right ***/
321 void jump_horiz(Project p
, BYTE dir
)
325 /* If cursor is already at rightmost position, scroll display */
326 if( p
->nbrwc
== p
->left_pos
+gui
.nbcol
-1 )
328 p
->nbrwc
+= gui
.nbcol
-1;
330 else p
->nbrwc
= p
->left_pos
+gui
.nbcol
-1;
332 /* Check if cursor is already at leftmost position */
333 if( p
->nbrwc
== p
->left_pos
)
335 p
->nbrwc
= p
->left_pos
- (gui
.nbcol
-1);
336 if( (LONG
)p
->nbrwc
< 0 ) p
->nbrwc
= 0;
338 else p
->nbrwc
= p
->left_pos
;
342 p
->nbrc
= adjust_rc(p
->edited
, p
->nbrwc
, &p
->nbc
, dir
<0);
343 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
345 LONG newleft
= center_horiz( p
);
346 if( newleft
!= p
->left_pos
)
347 scroll_xy(p
, newleft
, p
->top_line
, 0);
349 /* Move selection or cursor? */
350 if(p
->ccp
.select
) move_selection(p
, p
->nbrwc
, p
->nbl
);
355 /*** Jump cursor to an absolute position in line (ie: beginning or end) ***/
356 void horiz_pos( Project p
, ULONG newpos
)
358 if( newpos
!= p
->nbrwc
)
361 p
->nbrc
= adjust_rc(p
->edited
, p
->nbrwc
= newpos
, &p
->nbc
, FALSE
);
362 scroll_xy(p
, center_horiz(p
), p
->top_line
, FALSE
);
363 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
364 if(p
->ccp
.select
) move_selection(p
, p
->nbrwc
, p
->nbl
);
370 /*** Jump the cursor at bottom or top of display ***/
371 void jump_vert(Project p
, BYTE dir
)
373 LONG newline
, newtop
=p
->top_line
;
376 /* Move cursor to bottom of display or scroll one page if it's already here */
377 newline
=newtop
+gui
.nbline
-1;
378 if( p
->nbl
==newline
)
382 /* Want to jump after the last line? */
383 if(newline
>=p
->max_lines
) {
384 newline
=p
->max_lines
-1;
385 newtop
=(p
->top_line
+ gui
.nbline
> p
->max_lines
? p
->top_line
: newline
-gui
.nbline
+1);
386 if(newtop
<0) newtop
=0;
389 /* Same fight with reverse direction */
391 if( p
->nbl
==newline
) {
393 if(newtop
<0) newtop
=0;
397 /* Adjust display according to cursor position */
398 if(newline
!= p
->nbl
) {
400 set_cursor_line(p
, newline
, newtop
);
402 /* Set the selection flags before to scroll display */
403 if(p
->ccp
.select
) move_selection(p
, p
->nbrwc
, p
->nbl
);
404 scroll_xy(p
,center_horiz(p
), newtop
, TRUE
);
410 /*** Move cursor down/up one page ***/
411 void pg_updown(Project p
, BYTE dir
)
413 LONG newtop
= p
->top_line
, newcrs
;
417 if(newtop
+gui
.nbline
>= p
->max_lines
) newcrs
= p
->max_lines
-1;
419 newtop
+= gui
.nbline
;
420 if(newtop
+gui
.nbline
>= p
->max_lines
)
421 newtop
= p
->max_lines
- gui
.nbline
;
422 newcrs
= newtop
- p
->top_line
+ p
->nbl
;
426 if(newtop
== 0) newcrs
=0;
428 newtop
-= gui
.nbline
;
429 if(newtop
<0) newtop
=0;
430 newcrs
= newtop
- p
->top_line
+ p
->nbl
;
432 if(newcrs
!= p
->nbl
) inv_curs(p
,FALSE
),set_cursor_line(p
,newcrs
,newtop
);
433 scroll_xy(p
,center_horiz(p
),newtop
,TRUE
);
434 if(p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
440 void split_curline( Project p
)
442 if( split_line(&p
->undo
, p
->edited
, p
->nbc
, &p
->nbrwc
, prefs
.auto_indent
) )
444 register LINE
*ln
= p
->edited
;
445 /* Redraw line where cursor is */
446 Move(RP
,gui
.left
,p
->ycurs
);
448 /* A line has been added */
449 p
->max_lines
++; p
->nbrwc
= x2pos(ln
,p
->nbrwc
);
450 /* Scroll manually the display and redraw new line */
451 if(p
->ycurs
< gui
.botcurs
)
453 ScrollRaster(RP
, 0, -YSIZE
, gui
.left
,p
->ycurs
+YSIZE
-BASEL
,gui
.right
,gui
.bottom
);
454 Move(RP
,gui
.left
,p
->ycurs
+YSIZE
); ln
= ln
->next
;
458 /* Go down one line */
460 } else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
463 /*** Join two lines and strip spaces on the next ***/
464 void join_strip( Project p
)
467 if((ln
= p
->edited
->next
) != NULL
)
469 STRPTR data
; ULONG i
;
471 p
->nbc
= p
->edited
->size
;
472 for(i
=0, data
=ln
->stream
; TypeChar
[*data
] == SPACE
&& i
<ln
->size
; i
++, data
++);
474 reg_group_by(&p
->undo
);
477 /* Do not add a blank if there is already one */
478 if( p
->nbc
> 0 && TypeChar
[ p
->edited
->stream
[ p
->nbc
-1 ] ] != SPACE
)
479 add_char(&p
->undo
, p
->edited
, p
->nbc
, ' ');
480 if( insert_str(&p
->undo
, p
->edited
, p
->edited
->size
, data
, ln
->size
-i
) == 0 )
481 ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
483 /* ln can't be the first */
484 del_line(&p
->undo
, NULL
, ln
); p
->max_lines
--;
485 reg_group_by(&p
->undo
);
488 p
->nbrc
= p
->nbrwc
= x2pos(p
->edited
, p
->nbc
);
491 scroll_up(p
, p
->edited
->next
, p
->ycurs
, center_horiz(p
));
496 /*** Remove an entire line ***/
497 void amiga_k(Project p
)
499 LINE
*del
= p
->edited
;
501 if(p
->ccp
.select
) return;
503 /* In there a next line, move cursor to */
505 p
->edited
=del
->next
; p
->max_lines
--;
506 if(p
->show
==del
) p
->show
=del
->next
;
509 /* Adjust cursor position */
511 del_line(&p
->undo
, &p
->the_line
, del
);
512 p
->nbrc
= adjust_rc(p
->edited
, p
->nbrwc
, &p
->nbc
, FALSE
);
514 scroll_up(p
, p
->edited
->next
, p
->ycurs
, center_horiz(p
));
518 /*** Remove the char before the cursor ***/
519 void back_space(Project p
, BYTE word
)
523 ULONG nbc
= word
? backward_word(p
->edited
,p
->nbc
-1) : p
->nbc
-1;
524 rem_chars(&p
->undo
, p
->edited
, nbc
, p
->nbc
-1);
526 /* Set cursor position and redraws it */
529 p
->nbrwc
= p
->nbrc
= x2pos(p
->edited
, p
->nbc
=nbc
);
530 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
531 if( (nbc
= center_horiz(p
)) != p
->left_pos
)
532 scroll_xy(p
, nbc
, p
->top_line
, FALSE
);
535 } else if(p
->edited
->prev
!= NULL
) {
537 p
->edited
= p
->edited
->prev
;
538 p
->nbc
= p
->edited
->size
;
539 /* Join previous and current line */
540 if( join_lines(&p
->undo
, p
->edited
, p
->edited
->next
) )
542 /* Move cursor to the end of previous line */
543 p
->nbrwc
= p
->nbrc
= x2pos(p
->edited
, p
->nbc
);
545 p
->max_lines
--; p
->nbl
--;
546 /* Require to scroll the display? */
548 if(p
->ycurs
>gui
.topcurs
)
549 scroll_up(p
, p
->edited
->next
, p
->ycurs
-=YSIZE
, center_horiz(p
));
551 p
->top_line
--, p
->show
= p
->edited
,
552 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
556 /* Redraw the cursor: */
560 } else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
564 /*** Remove part of a line ***/
565 void cut_line(Project p
, BYTE mode
)
567 /* Is there something to do? */
568 if( (mode
==0 && p
->nbc
==0) || (mode
==1 && p
->nbc
==p
->edited
->size
)) return;
570 if( rem_chars(&p
->undo
, p
->edited
, mode
? p
->nbc
: 0, (mode
? p
->edited
->size
: p
->nbc
)-1) )
572 /* Recompute cursor position */
576 p
->nbc
=p
->nbrc
=p
->nbrwc
=0;
577 if(p
->left_pos
!=0) scroll_xy(p
, 0, p
->top_line
, FALSE
);
583 } else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
586 /*** Remove the char under the cursor ***/
587 void del(Project p
, BYTE word
)
589 if(p
->nbc
< p
->edited
->size
)
591 ULONG nbc
= word
? forward_word(p
->edited
,p
->nbc
)-1 : p
->nbc
;
592 rem_chars(&p
->undo
, p
->edited
, p
->nbc
, nbc
);
595 } else if(p
->edited
->next
) {
597 /* Join current and next line: */
598 if( join_lines(&p
->undo
, p
->edited
, p
->edited
->next
) )
601 /* Is it needed to scroll display? */
602 if(p
->ycurs
< gui
.botcurs
)
603 scroll_up(p
,p
->edited
->next
,p
->ycurs
,p
->left_pos
);
605 /* Redraw the cursor: */
609 } else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
613 /*** Move the cursor according to mouse click ***/
614 void click(Project p
, WORD x
, WORD y
, BYTE update
)
616 WORD xp
= (x
-gui
.left
) / XSIZE
,
617 yp
= (y
-gui
.top
) / YSIZE
;
620 if(!p
->ccp
.select
) inv_curs(p
, FALSE
);
622 p
->nbl
= p
->top_line
+ yp
;
623 for(ln
=p
->show
; ln
->next
&& yp
; yp
--,ln
=ln
->next
);
624 /* There was no lines, where we've clicked */
627 p
->nbrwc
= p
->nbrc
= xp
+p
->left_pos
;
629 xp
= curs_visible(p
,p
->top_line
);
631 scroll_xy(p
, xp
, p
->top_line
, FALSE
);
633 RP
->Mask
= gui
.selmask
;
635 /* Set starting selection point */
638 ln
->flags
= FIRSTSEL
| LASTSEL
;
639 p
->ccp
.xc
= p
->ccp
.xp
= p
->ccp
.startsel
= p
->ccp
.endsel
= p
->nbrc
;
640 p
->ccp
.yc
= p
->ccp
.yp
= p
->nbl
;
641 p
->ccp
.cline
= p
->ccp
.line
= ln
;
645 /*** User release mouse button ***/
646 void unclick(Project p
)
648 /* Is there something selected ? */
649 if( p
->ccp
.yp
!=p
->ccp
.yc
|| p
->ccp
.startsel
!=p
->ccp
.endsel
)
651 p
->edited
= p
->ccp
.cline
, p
->nbrwc
=p
->ccp
.xc
,
652 set_cursor_line(p
, p
->nbl
= p
->ccp
.yc
, p
->top_line
);
654 p
->edited
->flags
= 0, p
->ccp
.select
= 0,
655 inv_curs(p
, TRUE
), RP
->Mask
= gui
.txtmask
;