1 /******************************************************
2 ** Edit.c : Implementation of 'Edit' menu commands **
3 ** (mark/cut/[un]indent/[lower|upper|toggle] case) **
4 ** © T.Pierron, C.Guillaume. **
5 ******************************************************/
7 #include <exec/types.h>
8 #include <graphics/rastport.h>
13 #include "ProtoTypes.h"
15 #define CATCOMP_NUMBERS /* Strings id for err. msg */
18 /*** Copy selected text up to `Max' chars into buffer ***/
19 LONG
copy_mark_to_buf(Project p
, UBYTE
*Buf
, LONG Max
)
21 LINE
*ln
; long s
,e
, nbc
;
22 ln
= (p
->ccp
.yc
< p
->ccp
.yp
? p
->ccp
.cline
: p
->ccp
.line
);
24 for(nbc
=0; Max
&& ln
&& ln
->flags
; ln
=ln
->next
)
26 s
= (ln
->flags
& FIRSTSEL
? find_nbc(ln
, p
->ccp
.startsel
) : 0);
27 e
= (ln
->flags
& LASTSEL
? find_nbc(ln
, p
->ccp
.endsel
) : ln
->size
);
29 e
-=s
; if(e
> Max
) e
=Max
;
30 CopyMem(ln
->stream
+s
, Buf
, e
);
31 Max
-=e
; Buf
+=e
; nbc
+=e
;
37 /*** Mark: stream selection ***/
38 BYTE
move_stream_selection(Project p
, LONG xn
, LONG yn
)
40 LONG yline
; /* Nb. of line marked */
41 LINE
*ln
; /* Running pointer */
42 BYTE ret
; /* Autoscroll ? */
43 WORD y
; /* VPos of item sel */
45 /* Vertical autoscroll ? */
46 ret
= (yn
<p
->top_line
? 2 : (yn
>=(LONG
)(p
->top_line
+gui
.nbline
) ? 1 : 0));
48 if(yn
<0) yn
=0; if(yn
>=p
->max_lines
) yn
=p
->max_lines
-1,ret
=0;
49 yline
=p
->ccp
.yc
; ln
=p
->ccp
.cline
;
50 /* top_line may changed */
51 y
=(yline
-p
->top_line
)*YSIZE
+gui
.topcurs
;
53 /* If new selection point is situated after the prece- **
54 ** eding one (ie:looking afterward in linked list) */
55 if( yn
>yline
|| (yn
==yline
&& xn
>p
->ccp
.xp
) )
56 for(; yline
<yn
; yline
++, ln
=ln
->next
, y
+=YSIZE
)
58 if(ln
->flags
&& yline
<=p
->ccp
.yp
)
59 if(yline
== p
->ccp
.yp
)
60 p
->ccp
.startsel
= (p
->ccp
.select
!= WORD_TYPE
? p
->ccp
.xp
:
61 x2pos(ln
, backward_word(ln
, find_nbc(ln
,p
->ccp
.xp
)))),
62 ln
->flags
= (p
->ccp
.select
== LINE_TYPE
? WHOLESEL
: FIRSTSEL
);
64 else ln
->flags
=WHOLESEL
;
65 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
66 Move(RP
, gui
.left
, y
),write_text(p
,ln
);
69 /* New selection point is before the previous */
70 for(; yline
>yn
; yline
--, ln
=ln
->prev
, y
-=YSIZE
)
72 if(ln
->flags
&& yline
>=p
->ccp
.yp
)
73 if(yline
== p
->ccp
.yp
)
74 p
->ccp
.endsel
= (p
->ccp
.select
!= WORD_TYPE
? p
->ccp
.xp
:
75 x2pos(ln
, forward_word(ln
, find_nbc(ln
,p
->ccp
.xp
)))),
76 ln
->flags
= (p
->ccp
.select
== LINE_TYPE
? WHOLESEL
: LASTSEL
);
78 else ln
->flags
=WHOLESEL
;
79 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
80 Move(RP
, gui
.left
, y
),write_text(p
,ln
);
83 /* Current point will be the previous */
84 p
->ccp
.cline
=ln
; p
->ccp
.yc
=yn
; p
->ccp
.xc
=xn
;
86 /* Update last-selected line */
87 { register UBYTE Set
= 0;
88 if(p
->ccp
.select
!= LINE_TYPE
)
91 ln
->flags
= FIRSTSEL
| LASTSEL
;
92 if(xn
< p
->ccp
.xp
) p
->ccp
.startsel
=xn
, p
->ccp
.endsel
=p
->ccp
.xp
;
93 else p
->ccp
.startsel
=p
->ccp
.xp
, p
->ccp
.endsel
=xn
;
97 ln
->flags
=LASTSEL
, p
->ccp
.endsel
=xn
, Set
= 2;
99 ln
->flags
=FIRSTSEL
,p
->ccp
.startsel
=xn
, Set
= 1;
100 else ln
->flags
= WHOLESEL
;
102 /* Adjust selection for word selection */
103 if(p
->ccp
.select
== WORD_TYPE
) {
104 if(Set
& 1) p
->ccp
.startsel
= x2pos(ln
, backward_word(ln
, find_nbc(ln
,p
->ccp
.startsel
)));
105 if(Set
& 2) p
->ccp
.endsel
= x2pos(ln
, forward_word (ln
, find_nbc(ln
,p
->ccp
.endsel
)));
108 /* Last line can overlap edit area */
109 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
110 Move(RP
, gui
.left
, y
),write_text(p
,ln
);
115 /*** Mark: block selection ***/
116 BYTE
move_column_selection(Project p
, LONG xn
, LONG yn
)
118 static WORD left
, nbcol
;
119 static LONG left_pos
;
121 LONG yline
; /* Nb. of line marked */
122 LINE
*ln
; /* Running pointer */
123 BYTE ret
; /* Autoscroll ? */
124 WORD y
; /* VPos of item sel */
127 /** Vertical autoscroll ? **/
128 ret
= (yn
<p
->top_line
? 2 : (yn
>=(LONG
)(p
->top_line
+gui
.nbline
) ? 1 : 0));
129 if(p
->nbrc
<p
->ccp
.startsel
&& gui
.topcurs
<= p
->ycurs
&& p
->ycurs
<= gui
.botcurs
)
130 p
->ccp
.select
=0,inv_curs(p
,0),p
->ccp
.select
=COLUMN_TYPE
;
133 /** Reduce refreshed area **/
134 left_pos
=p
->left_pos
; left
=gui
.left
; nbcol
=gui
.nbcol
;
136 gui
.left
+= XSIZE
* ( (
137 p
->left_pos
= MIN(MIN(p
->ccp
.xp
,p
->ccp
.xc
), xn
)) - left_pos
);
138 yline
= MAX(MAX(p
->ccp
.xp
,p
->ccp
.xc
), xn
);
139 gui
.nbcol
= yline
- p
->left_pos
+ tabstop(yline
);
140 if(p
->left_pos
+gui
.nbcol
> left_pos
+nbcol
)
141 gui
.nbcol
= left_pos
+nbcol
-p
->left_pos
;
143 if(yn
<0) yn
=0; if(yn
>=p
->max_lines
) yn
=p
->max_lines
-1,ret
=0;
144 yline
=p
->ccp
.yc
; ln
=p
->ccp
.cline
;
145 /* top_line may changed */
146 y
=(yline
-p
->top_line
)*YSIZE
+gui
.topcurs
;
148 /** Update start & end selection pos **/
150 p
->ccp
.endsel
= p
->ccp
.xp
, p
->ccp
.startsel
= xn
;
152 p
->ccp
.startsel
= p
->ccp
.xp
, p
->ccp
.endsel
= xn
;
154 /** Same fight as before: afterward or backward scan **/
157 /* Search is afterward */
158 for(; yline
<yn
; y
+=YSIZE
, yline
++, ln
=ln
->next
)
160 if(yline
< p
->ccp
.yp
) ln
->flags
=0;
161 else ln
->flags
=FIRSTSEL
| LASTSEL
;
162 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
163 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
165 /* If user has changed vertical position of selection an **
166 ** update of part of selected buffer will be required: */
167 if(xn
!= p
->ccp
.xc
) {
168 if(p
->ccp
.yp
< p
->ccp
.yc
) rdw
=6;
169 else if(p
->ccp
.yp
> yn
) rdw
=1;
172 /* Scan is backward */
173 for(; yline
>yn
; y
-=YSIZE
, yline
--, ln
=ln
->prev
)
175 if(yline
> p
->ccp
.yp
) ln
->flags
=0;
176 else ln
->flags
=FIRSTSEL
| LASTSEL
;
177 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
178 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
180 /* If user has changed vertical position of selection an **
181 ** update of part of selected buffer will be required: */
182 if(xn
!= p
->ccp
.xc
) {
183 if(p
->ccp
.yp
> p
->ccp
.yc
) rdw
=5;
184 else if(p
->ccp
.yp
< yn
) rdw
=2;
188 /** Current point now become the previous **/
189 ln
->flags
= FIRSTSEL
| LASTSEL
;
190 /* Last line can overlap edit area */
191 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
192 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
194 /** Update unmodified lines **/
197 register LINE
*ptr
; register WORD yc
;
198 /* Limits number of lines to redraw */
200 ptr
=p
->ccp
.cline
, yline
=p
->ccp
.yc
,
201 yc
=(yline
-p
->top_line
)*YSIZE
+gui
.topcurs
;
204 /* Reduces number of columns to redraw */
206 p
->left_pos
=p
->ccp
.xc
, gui
.nbcol
=xn
-p
->ccp
.xc
+1+tabstop(xn
);
208 p
->left_pos
=xn
, gui
.nbcol
=p
->ccp
.xc
-xn
+1+tabstop(p
->ccp
.xc
);
209 gui
.left
= left
+ XSIZE
*(p
->left_pos
-left_pos
);
211 /* Be sure lines won't erase right border of window */
212 if(p
->left_pos
+gui
.nbcol
> left_pos
+nbcol
) gui
.nbcol
= left_pos
+nbcol
-p
->left_pos
;
215 for(; yc
<=gui
.botcurs
&& yline
<=p
->ccp
.yp
; yline
++, yc
+=YSIZE
, ptr
=ptr
->next
)
216 if(yc
>=gui
.topcurs
) Move(RP
,gui
.left
,yc
),write_text(p
,ptr
);
218 for(; yc
>=gui
.topcurs
&& yline
>=p
->ccp
.yp
; yline
--, yc
-=YSIZE
, ptr
=ptr
->prev
)
219 if(yc
<=gui
.botcurs
) Move(RP
,gui
.left
,yc
),write_text(p
,ptr
);
223 p
->left_pos
=left_pos
; gui
.left
=left
; gui
.nbcol
=nbcol
;
224 p
->ccp
.xc
=xn
; p
->ccp
.cline
=ln
; p
->ccp
.yc
=yn
;
229 /*** Switch between different type of selection ***/
230 pfnSelectFunc
SwitchSelect(Project p
, BYTE mode
, BYTE force
)
232 pfnSelectFunc
new = NULL
;
233 /* If click on same point, switch selection mode */
234 if(p
->nbrc
== p
->ccp
.xp
&& p
->nbl
== p
->ccp
.yp
)
236 new = move_stream_selection
;
237 switch( p
->ccp
.select
)
240 case STREAM_TYPE
: p
->ccp
.select
=WORD_TYPE
; break;
241 case WORD_TYPE
: p
->ccp
.select
=LINE_TYPE
; break;
243 case LINE_TYPE
: new=NULL
; unmark_all(p
,TRUE
); break;
245 if(new) new(p
,p
->nbrc
,p
->nbl
);
248 else if(p
->ccp
.select
)
250 /* We don't `click' over the same point */
251 unmark_all(p
,TRUE
); if(force
) goto new_mode
;
254 /* Start a new default mode */
255 new_mode
: inv_curs(p
,FALSE
);
256 if(mode
) p
->ccp
.select
=COLUMN_TYPE
, new=move_column_selection
;
257 else p
->ccp
.select
=STREAM_TYPE
, new=move_stream_selection
;
259 click(p
,p
->xcurs
,p
->ycurs
,TRUE
);
265 /*** Cut: remove block of selected text ***/
266 void del_block(Project p
)
269 LINE
*ln
= p
->ccp
.cline
;
271 /* Look for first line to remove */
272 switch( p
->ccp
.select
)
275 if(p
->ccp
.xc
> p
->ccp
.xp
) p
->nbrwc
= p
->ccp
.xp
;
276 if(p
->ccp
.yc
> p
->ccp
.yp
) p
->nbl
= p
->ccp
.yp
, ln
=p
->ccp
.line
;
278 if( p
->ccp
.yc
> p
->ccp
.yp
|| (p
->ccp
.yc
==p
->ccp
.yp
&& p
->ccp
.xc
>p
->ccp
.xp
) )
279 p
->nbl
= p
->ccp
.yp
, ln
= p
->ccp
.line
, p
->nbrwc
=p
->ccp
.xp
;
283 /* Has the first line shown changed? */
284 if(p
->nbl
< p
->top_line
) p
->top_line
=p
->nbl
;
286 /* Delete selected lines */
287 for(nbdel
=nbrem
=0, reg_group_by(&p
->undo
); ln
; nbdel
++)
289 if( ln
->flags
!= WHOLESEL
)
291 /* Need to convert real-column into char-index */
293 rcs
= find_nbc(ln
, p
->ccp
.startsel
);
294 rce
= find_nbc(ln
, p
->ccp
.endsel
);
298 LASTSEL
: if(rcs
< rce
) rem_chars(&p
->undo
,ln
,rcs
,rce
-1); ln
->flags
=0; break;
299 case FIRSTSEL
: if(rcs
< ln
->size
) rem_chars(&p
->undo
,ln
,rcs
,ln
->size
-1); ln
->flags
=0; break;
300 case LASTSEL
: if(rce
> 0) rem_chars(&p
->undo
,ln
,0, rce
-1); ln
->flags
=0; ln
=ln
->prev
;
301 join_lines(&p
->undo
,ln
,ln
->next
); nbrem
++; break;
302 default: goto Ze_end
;
306 /* Remove the entire line */
307 register LINE
*next
= ln
->next
; ln
->flags
= 0;
308 if( del_line(&p
->undo
, &p
->the_line
, ln
) ) nbrem
++, nbdel
++;
313 Ze_end
: /* Makes some visual cleanup */
314 reg_group_by(&p
->undo
);
316 /* Prop gadget should be adjusted? */
317 p
->max_lines
-= nbrem
;
319 p
->ccp
.xp
= (ULONG
)-1;
320 if(nbrem
>=1) prop_adj(p
);
322 /* Adjust cursor's position */
323 p
->edited
= p
->the_line
;
326 set_cursor_line(p
, nbrem
, p
->top_line
);
327 if(p
->top_line
== p
->nbl
) p
->show
=p
->edited
;
329 /* What should be redrawn? */
332 nbdel
= p
->nbl
-p
->top_line
;
333 redraw_content(p
,p
->edited
,gui
.topcurs
+nbdel
*YSIZE
,gui
.nbline
-nbdel
);
335 else REDRAW_CURLINE(p
);
336 RP
->Mask
= gui
.txtmask
;
342 /*** Insert a character at the start of line ***/
343 void indent_by(Project p
, UBYTE ch
, BYTE method
)
345 # define AffectedLine(ln, prj) (ln->size > 0 && ((ln->flags & ~LASTSEL) || prj->ccp.endsel>0))
348 register LINE
*ln
; LONG y
;
349 /* Look for the first selected line */
350 if(p
->ccp
.yc
< p
->ccp
.yp
) y
=p
->ccp
.yc
,ln
=p
->ccp
.cline
;
351 else y
=p
->ccp
.yp
,ln
=p
->ccp
.line
;
352 y
= (y
- p
->top_line
) * YSIZE
+ gui
.topcurs
;
354 /* Every lines must start with the desired character */
357 register LINE
*line
= ln
;
358 for(; line
&& line
->flags
; line
=line
->next
)
359 if(AffectedLine(line
, p
) && ch
!= line
->stream
[0]) return;
363 for(reg_group_by(&p
->undo
); ln
&& ln
->flags
; ln
=ln
->next
,y
+=YSIZE
)
364 if( AffectedLine(ln
, p
) ) {
365 /* Force whole line selection */
366 ln
->flags
= WHOLESEL
;
367 /* Remove or add a character */
368 if(method
== -1) rem_chars(&p
->undo
,ln
,0,0);
369 else add_char(&p
->undo
,ln
,0,ch
);
371 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
372 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
374 reg_group_by(&p
->undo
);
375 move_selection
= move_stream_selection
;
376 if(p
->nbc
==0) goto redraw
; goto refresh
;
379 /* Should we add or remove the char? */
381 if(p
->edited
->size
> 0 && p
->edited
->stream
[0]==ch
) {
382 rem_chars(&p
->undo
,p
->edited
,0,0);
383 if(p
->nbc
==0) goto redraw
; goto refresh
;
387 if( add_char(&p
->undo
, p
->edited
, 0, ch
) )
391 p
->nbrwc
= x2pos(p
->edited
, p
->nbc
+=method
);
392 p
->xcurs
= gui
.left
+ XSIZE
* ((
393 p
->nbrc
= p
->nbrwc
) - p
->left_pos
);
394 redraw
: REDRAW_CURLINE(p
);
395 /* Cursor may have quit edit area */
396 if(p
->nbrc
<p
->left_pos
|| p
->nbrc
>=p
->left_pos
+gui
.nbcol
)
397 scroll_xy(p
, adjust_leftpos(p
, gui
.xstep
), p
->top_line
, FALSE
);
401 } else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
404 /*** Convert a character to small or capital letter ***/
405 static UBYTE Method
= 0;
406 UBYTE
change_casechar(UBYTE ch
)
410 case 0: ch
=ToUpper(ch
); break;
413 temp
=ch
; ch
=ToUpper(ch
); if(ch
!=temp
) break;
415 case 1: ch
=ToLower(ch
);
420 /*** Change one char or a whole selected region ***/
421 void change_case(Project p
, UBYTE method
)
427 /* Look for the first selected line */
428 if(p
->ccp
.yc
< p
->ccp
.yp
) y
=p
->ccp
.yc
,ln
=p
->ccp
.cline
;
429 else y
=p
->ccp
.yp
,ln
=p
->ccp
.line
;
430 y
= (y
- p
->top_line
) * YSIZE
+ gui
.topcurs
;
433 /* Let's change selected buffer */
434 for(; ln
&& ln
->flags
; ln
=ln
->next
,y
+=YSIZE
)
437 (ln
->flags
& FIRSTSEL
? find_nbc(ln
, p
->ccp
.startsel
) : 0),
438 (ln
->flags
& LASTSEL
? find_nbc(ln
, p
->ccp
.endsel
) : ln
->size
),
442 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
443 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
448 Method
=method
; ln
=p
->edited
;
449 replace_char(ln
,p
->nbc
,change_casechar(ln
->stream
[p
->nbc
]));
454 /*** Clears all selected lines ***/
455 void unmark_all(Project p
, BYTE wrap
)
459 if(p
->ccp
.yc
< p
->ccp
.yp
) ln
= p
->ccp
.cline
, y
= p
->ccp
.yc
;
460 else ln
= p
->ccp
.line
, y
= p
->ccp
.yp
;
462 y
= (y
- p
->top_line
) * YSIZE
+ gui
.topcurs
;
463 /* Unset just lines and redraw */
464 for(p
->ccp
.select
= 0; ln
&& ln
->flags
; ln
=ln
->next
, y
+=YSIZE
)
467 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
468 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
471 /* Is the cursor always visible? */
473 if(p
->top_line
< p
->nbl
|| p
->nbl
>= p
->top_line
+gui
.nbline
||
474 p
->left_pos
< p
->nbrc
|| p
->nbrc
>= p
->left_pos
+gui
.nbcol
)
475 scroll_xy(p
, center_horiz(p
), center_vert(p
), TRUE
),
476 set_cursor_line(p
, p
->nbl
, p
->top_line
);
479 p
->ccp
.xp
= (ULONG
)-1;
480 RP
->Mask
= gui
.txtmask
;
483 /*** Set all lines as selected ***/
484 void mark_all(Project p
)
489 move_selection
= move_stream_selection
;
490 p
->ccp
.select
= LINE_TYPE
;
491 p
->ccp
.cline
= ln
= p
->the_line
;
493 p
->ccp
.yp
= p
->max_lines
-1;
494 RP
->Mask
= gui
.selmask
;
496 /* Set lines before those displayed */
497 for(nb
=p
->top_line
; nb
--; ln
->flags
=WHOLESEL
, ln
=ln
->next
);
499 /* Set and refresh displayed lines */
500 { register LINE
*last
= ln
;
501 for(y
=gui
.topcurs
; ln
&& y
<=gui
.botcurs
; last
=ln
, ln
=ln
->next
, y
+=YSIZE
)
502 ln
->flags
=WHOLESEL
, Move(RP
,gui
.left
,y
),write_text(p
,ln
);
504 /* Set lines after those displayed */
505 for(; ln
; ln
->flags
=WHOLESEL
, last
=ln
, ln
=ln
->next
);