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));
56 yline
=p
->ccp
.yc
; ln
=p
->ccp
.cline
;
57 /* top_line may changed */
58 y
=(yline
-p
->top_line
)*YSIZE
+gui
.topcurs
;
60 /* If new selection point is situated after the prece- **
61 ** eding one (ie:looking afterward in linked list) */
62 if( yn
>yline
|| (yn
==yline
&& xn
>p
->ccp
.xp
) )
63 for(; yline
<yn
; yline
++, ln
=ln
->next
, y
+=YSIZE
)
65 if(ln
->flags
&& yline
<=p
->ccp
.yp
)
66 if(yline
== p
->ccp
.yp
)
67 p
->ccp
.startsel
= (p
->ccp
.select
!= WORD_TYPE
? p
->ccp
.xp
:
68 x2pos(ln
, backward_word(ln
, find_nbc(ln
,p
->ccp
.xp
)))),
69 ln
->flags
= (p
->ccp
.select
== LINE_TYPE
? WHOLESEL
: FIRSTSEL
);
71 else ln
->flags
=WHOLESEL
;
72 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
73 Move(RP
, gui
.left
, y
),write_text(p
,ln
);
76 /* New selection point is before the previous */
77 for(; yline
>yn
; yline
--, ln
=ln
->prev
, y
-=YSIZE
)
79 if(ln
->flags
&& yline
>=p
->ccp
.yp
)
80 if(yline
== p
->ccp
.yp
)
81 p
->ccp
.endsel
= (p
->ccp
.select
!= WORD_TYPE
? p
->ccp
.xp
:
82 x2pos(ln
, forward_word(ln
, find_nbc(ln
,p
->ccp
.xp
)))),
83 ln
->flags
= (p
->ccp
.select
== LINE_TYPE
? WHOLESEL
: LASTSEL
);
85 else ln
->flags
=WHOLESEL
;
86 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
87 Move(RP
, gui
.left
, y
),write_text(p
,ln
);
90 /* Current point will be the previous */
91 p
->ccp
.cline
=ln
; p
->ccp
.yc
=yn
; p
->ccp
.xc
=xn
;
93 /* Update last-selected line */
94 { register UBYTE Set
= 0;
95 if(p
->ccp
.select
!= LINE_TYPE
)
98 ln
->flags
= FIRSTSEL
| LASTSEL
;
99 if(xn
< p
->ccp
.xp
) p
->ccp
.startsel
=xn
, p
->ccp
.endsel
=p
->ccp
.xp
;
100 else p
->ccp
.startsel
=p
->ccp
.xp
, p
->ccp
.endsel
=xn
;
104 ln
->flags
=LASTSEL
, p
->ccp
.endsel
=xn
, Set
= 2;
106 ln
->flags
=FIRSTSEL
,p
->ccp
.startsel
=xn
, Set
= 1;
107 else ln
->flags
= WHOLESEL
;
109 /* Adjust selection for word selection */
110 if(p
->ccp
.select
== WORD_TYPE
) {
111 if(Set
& 1) p
->ccp
.startsel
= x2pos(ln
, backward_word(ln
, find_nbc(ln
,p
->ccp
.startsel
)));
112 if(Set
& 2) p
->ccp
.endsel
= x2pos(ln
, forward_word (ln
, find_nbc(ln
,p
->ccp
.endsel
)));
115 /* Last line can overlap edit area */
116 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
117 Move(RP
, gui
.left
, y
),write_text(p
,ln
);
122 /*** Mark: block selection ***/
123 BYTE
move_column_selection(Project p
, LONG xn
, LONG yn
)
125 static WORD left
, nbcol
;
126 static LONG left_pos
;
128 LONG yline
; /* Nb. of line marked */
129 LINE
*ln
; /* Running pointer */
130 BYTE ret
; /* Autoscroll ? */
131 WORD y
; /* VPos of item sel */
134 /** Vertical autoscroll ? **/
135 ret
= (yn
<p
->top_line
? 2 : (yn
>=(LONG
)(p
->top_line
+gui
.nbline
) ? 1 : 0));
136 if(p
->nbrc
<p
->ccp
.startsel
&& gui
.topcurs
<= p
->ycurs
&& p
->ycurs
<= gui
.botcurs
)
137 p
->ccp
.select
=0,inv_curs(p
,0),p
->ccp
.select
=COLUMN_TYPE
;
140 /** Reduce refreshed area **/
141 left_pos
=p
->left_pos
; left
=gui
.left
; nbcol
=gui
.nbcol
;
143 gui
.left
+= XSIZE
* ( (
144 p
->left_pos
= MIN(MIN(p
->ccp
.xp
,p
->ccp
.xc
), xn
)) - left_pos
);
145 yline
= MAX(MAX(p
->ccp
.xp
,p
->ccp
.xc
), xn
);
146 gui
.nbcol
= yline
- p
->left_pos
+ tabstop(yline
);
147 if(p
->left_pos
+gui
.nbcol
> left_pos
+nbcol
)
148 gui
.nbcol
= left_pos
+nbcol
-p
->left_pos
;
157 yline
=p
->ccp
.yc
; ln
=p
->ccp
.cline
;
158 /* top_line may changed */
159 y
=(yline
-p
->top_line
)*YSIZE
+gui
.topcurs
;
161 /** Update start & end selection pos **/
163 p
->ccp
.endsel
= p
->ccp
.xp
, p
->ccp
.startsel
= xn
;
165 p
->ccp
.startsel
= p
->ccp
.xp
, p
->ccp
.endsel
= xn
;
167 /** Same fight as before: afterward or backward scan **/
170 /* Search is afterward */
171 for(; yline
<yn
; y
+=YSIZE
, yline
++, ln
=ln
->next
)
173 if(yline
< p
->ccp
.yp
) ln
->flags
=0;
174 else ln
->flags
=FIRSTSEL
| LASTSEL
;
175 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
176 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
178 /* If user has changed vertical position of selection an **
179 ** update of part of selected buffer will be required: */
180 if(xn
!= p
->ccp
.xc
) {
181 if(p
->ccp
.yp
< p
->ccp
.yc
) rdw
=6;
182 else if(p
->ccp
.yp
> yn
) rdw
=1;
185 /* Scan is backward */
186 for(; yline
>yn
; y
-=YSIZE
, yline
--, ln
=ln
->prev
)
188 if(yline
> p
->ccp
.yp
) ln
->flags
=0;
189 else ln
->flags
=FIRSTSEL
| LASTSEL
;
190 if(gui
.topcurs
<= y
&& y
<= gui
.botcurs
)
191 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
193 /* If user has changed vertical position of selection an **
194 ** update of part of selected buffer will be required: */
195 if(xn
!= p
->ccp
.xc
) {
196 if(p
->ccp
.yp
> p
->ccp
.yc
) rdw
=5;
197 else if(p
->ccp
.yp
< yn
) rdw
=2;
201 /** Current point now become the previous **/
202 ln
->flags
= FIRSTSEL
| LASTSEL
;
203 /* Last line can overlap edit area */
204 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
205 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
207 /** Update unmodified lines **/
210 register LINE
*ptr
; register WORD yc
;
211 /* Limits number of lines to redraw */
213 ptr
=p
->ccp
.cline
, yline
=p
->ccp
.yc
,
214 yc
=(yline
-p
->top_line
)*YSIZE
+gui
.topcurs
;
217 /* Reduces number of columns to redraw */
219 p
->left_pos
=p
->ccp
.xc
, gui
.nbcol
=xn
-p
->ccp
.xc
+1+tabstop(xn
);
221 p
->left_pos
=xn
, gui
.nbcol
=p
->ccp
.xc
-xn
+1+tabstop(p
->ccp
.xc
);
222 gui
.left
= left
+ XSIZE
*(p
->left_pos
-left_pos
);
224 /* Be sure lines won't erase right border of window */
225 if(p
->left_pos
+gui
.nbcol
> left_pos
+nbcol
) gui
.nbcol
= left_pos
+nbcol
-p
->left_pos
;
228 for(; yc
<=gui
.botcurs
&& yline
<=p
->ccp
.yp
; yline
++, yc
+=YSIZE
, ptr
=ptr
->next
)
229 if(yc
>=gui
.topcurs
) Move(RP
,gui
.left
,yc
),write_text(p
,ptr
);
231 for(; yc
>=gui
.topcurs
&& yline
>=p
->ccp
.yp
; yline
--, yc
-=YSIZE
, ptr
=ptr
->prev
)
232 if(yc
<=gui
.botcurs
) Move(RP
,gui
.left
,yc
),write_text(p
,ptr
);
236 p
->left_pos
=left_pos
; gui
.left
=left
; gui
.nbcol
=nbcol
;
237 p
->ccp
.xc
=xn
; p
->ccp
.cline
=ln
; p
->ccp
.yc
=yn
;
242 /*** Switch between different type of selection ***/
243 pfnSelectFunc
SwitchSelect(Project p
, BYTE mode
, BYTE force
)
245 pfnSelectFunc
new = NULL
;
246 /* If click on same point, switch selection mode */
247 if(p
->nbrc
== p
->ccp
.xp
&& p
->nbl
== p
->ccp
.yp
)
249 new = move_stream_selection
;
250 switch( p
->ccp
.select
)
253 case STREAM_TYPE
: p
->ccp
.select
=WORD_TYPE
; break;
254 case WORD_TYPE
: p
->ccp
.select
=LINE_TYPE
; break;
256 case LINE_TYPE
: new=NULL
; unmark_all(p
,TRUE
); break;
258 if(new) new(p
,p
->nbrc
,p
->nbl
);
261 else if(p
->ccp
.select
)
263 /* We don't `click' over the same point */
264 unmark_all(p
,TRUE
); if(force
) goto new_mode
;
267 /* Start a new default mode */
268 new_mode
: inv_curs(p
,FALSE
);
269 if(mode
) p
->ccp
.select
=COLUMN_TYPE
, new=move_column_selection
;
270 else p
->ccp
.select
=STREAM_TYPE
, new=move_stream_selection
;
272 click(p
,p
->xcurs
,p
->ycurs
,TRUE
);
278 /*** Cut: remove block of selected text ***/
279 void del_block(Project p
)
282 LINE
*ln
= p
->ccp
.cline
;
284 /* Look for first line to remove */
285 switch( p
->ccp
.select
)
288 if(p
->ccp
.xc
> p
->ccp
.xp
) p
->nbrwc
= p
->ccp
.xp
;
289 if(p
->ccp
.yc
> p
->ccp
.yp
) p
->nbl
= p
->ccp
.yp
, ln
=p
->ccp
.line
;
291 if( p
->ccp
.yc
> p
->ccp
.yp
|| (p
->ccp
.yc
==p
->ccp
.yp
&& p
->ccp
.xc
>p
->ccp
.xp
) )
292 p
->nbl
= p
->ccp
.yp
, ln
= p
->ccp
.line
, p
->nbrwc
=p
->ccp
.xp
;
296 /* Has the first line shown changed? */
297 if(p
->nbl
< p
->top_line
) p
->top_line
=p
->nbl
;
299 /* Delete selected lines */
300 for(nbdel
=nbrem
=0, reg_group_by(&p
->undo
); ln
; nbdel
++)
302 if( ln
->flags
!= WHOLESEL
)
304 /* Need to convert real-column into char-index */
306 rcs
= find_nbc(ln
, p
->ccp
.startsel
);
307 rce
= find_nbc(ln
, p
->ccp
.endsel
);
311 LASTSEL
: if(rcs
< rce
) rem_chars(&p
->undo
,ln
,rcs
,rce
-1); ln
->flags
=0; break;
312 case FIRSTSEL
: if(rcs
< ln
->size
) rem_chars(&p
->undo
,ln
,rcs
,ln
->size
-1); ln
->flags
=0; break;
313 case LASTSEL
: if(rce
> 0) rem_chars(&p
->undo
,ln
,0, rce
-1); ln
->flags
=0; ln
=ln
->prev
;
314 join_lines(&p
->undo
,ln
,ln
->next
); nbrem
++; break;
315 default: goto Ze_end
;
319 /* Remove the entire line */
320 register LINE
*next
= ln
->next
; ln
->flags
= 0;
321 if( del_line(&p
->undo
, &p
->the_line
, ln
) ) nbrem
++, nbdel
++;
326 Ze_end
: /* Makes some visual cleanup */
327 reg_group_by(&p
->undo
);
329 /* Prop gadget should be adjusted? */
330 p
->max_lines
-= nbrem
;
332 p
->ccp
.xp
= (ULONG
)-1;
333 if(nbrem
>=1) prop_adj(p
);
335 /* Adjust cursor's position */
336 p
->edited
= p
->the_line
;
339 set_cursor_line(p
, nbrem
, p
->top_line
);
340 if(p
->top_line
== p
->nbl
) p
->show
=p
->edited
;
342 /* What should be redrawn? */
345 nbdel
= p
->nbl
-p
->top_line
;
346 redraw_content(p
,p
->edited
,gui
.topcurs
+nbdel
*YSIZE
,gui
.nbline
-nbdel
);
348 else REDRAW_CURLINE(p
);
349 RP
->Mask
= gui
.txtmask
;
355 /*** Insert a character at the start of line ***/
356 void indent_by(Project p
, UBYTE ch
, BYTE method
)
358 # define AffectedLine(ln, prj) (ln->size > 0 && ((ln->flags & ~LASTSEL) || prj->ccp.endsel>0))
361 register LINE
*ln
; LONG y
;
362 /* Look for the first selected line */
363 if(p
->ccp
.yc
< p
->ccp
.yp
) y
=p
->ccp
.yc
,ln
=p
->ccp
.cline
;
364 else y
=p
->ccp
.yp
,ln
=p
->ccp
.line
;
365 y
= (y
- p
->top_line
) * YSIZE
+ gui
.topcurs
;
367 /* Every lines must start with the desired character */
370 register LINE
*line
= ln
;
371 for(; line
&& line
->flags
; line
=line
->next
)
372 if(AffectedLine(line
, p
) && ch
!= line
->stream
[0]) return;
376 for(reg_group_by(&p
->undo
); ln
&& ln
->flags
; ln
=ln
->next
,y
+=YSIZE
)
377 if( AffectedLine(ln
, p
) ) {
378 /* Force whole line selection */
379 ln
->flags
= WHOLESEL
;
380 /* Remove or add a character */
381 if(method
== -1) rem_chars(&p
->undo
,ln
,0,0);
382 else add_char(&p
->undo
,ln
,0,ch
);
384 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
385 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
387 reg_group_by(&p
->undo
);
388 move_selection
= move_stream_selection
;
394 /* Should we add or remove the char? */
396 if(p
->edited
->size
> 0 && p
->edited
->stream
[0]==ch
) {
397 rem_chars(&p
->undo
,p
->edited
,0,0);
404 if( add_char(&p
->undo
, p
->edited
, 0, ch
) )
408 p
->nbrwc
= x2pos(p
->edited
, p
->nbc
+=method
);
409 p
->xcurs
= gui
.left
+ XSIZE
* ((
410 p
->nbrc
= p
->nbrwc
) - p
->left_pos
);
411 redraw
: REDRAW_CURLINE(p
);
412 /* Cursor may have quit edit area */
413 if(p
->nbrc
<p
->left_pos
|| p
->nbrc
>=p
->left_pos
+gui
.nbcol
)
414 scroll_xy(p
, adjust_leftpos(p
, gui
.xstep
), p
->top_line
, FALSE
);
418 } else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
421 /*** Convert a character to small or capital letter ***/
422 static UBYTE Method
= 0;
423 UBYTE
change_casechar(UBYTE ch
)
427 case 0: ch
=ToUpper(ch
); break;
430 temp
=ch
; ch
=ToUpper(ch
); if(ch
!=temp
) break;
432 case 1: ch
=ToLower(ch
);
437 /*** Change one char or a whole selected region ***/
438 void change_case(Project p
, UBYTE method
)
444 /* Look for the first selected line */
445 if(p
->ccp
.yc
< p
->ccp
.yp
) y
=p
->ccp
.yc
,ln
=p
->ccp
.cline
;
446 else y
=p
->ccp
.yp
,ln
=p
->ccp
.line
;
447 y
= (y
- p
->top_line
) * YSIZE
+ gui
.topcurs
;
450 /* Let's change selected buffer */
451 for(; ln
&& ln
->flags
; ln
=ln
->next
,y
+=YSIZE
)
454 (ln
->flags
& FIRSTSEL
? find_nbc(ln
, p
->ccp
.startsel
) : 0),
455 (ln
->flags
& LASTSEL
? find_nbc(ln
, p
->ccp
.endsel
) : ln
->size
),
459 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
460 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
465 Method
=method
; ln
=p
->edited
;
466 replace_char(ln
,p
->nbc
,change_casechar(ln
->stream
[p
->nbc
]));
471 /*** Clears all selected lines ***/
472 void unmark_all(Project p
, BYTE wrap
)
476 if(p
->ccp
.yc
< p
->ccp
.yp
) ln
= p
->ccp
.cline
, y
= p
->ccp
.yc
;
477 else ln
= p
->ccp
.line
, y
= p
->ccp
.yp
;
479 y
= (y
- p
->top_line
) * YSIZE
+ gui
.topcurs
;
480 /* Unset just lines and redraw */
481 for(p
->ccp
.select
= 0; ln
&& ln
->flags
; ln
=ln
->next
, y
+=YSIZE
)
484 if(gui
.topcurs
<=y
&& y
<=gui
.botcurs
)
485 Move(RP
,gui
.left
,y
),write_text(p
,ln
);
488 /* Is the cursor always visible? */
490 if(p
->top_line
< p
->nbl
|| p
->nbl
>= p
->top_line
+gui
.nbline
||
491 p
->left_pos
< p
->nbrc
|| p
->nbrc
>= p
->left_pos
+gui
.nbcol
)
492 scroll_xy(p
, center_horiz(p
), center_vert(p
), TRUE
),
493 set_cursor_line(p
, p
->nbl
, p
->top_line
);
496 p
->ccp
.xp
= (ULONG
)-1;
497 RP
->Mask
= gui
.txtmask
;
500 /*** Set all lines as selected ***/
501 void mark_all(Project p
)
506 move_selection
= move_stream_selection
;
507 p
->ccp
.select
= LINE_TYPE
;
508 p
->ccp
.cline
= ln
= p
->the_line
;
510 p
->ccp
.yp
= p
->max_lines
-1;
511 RP
->Mask
= gui
.selmask
;
513 /* Set lines before those displayed */
514 for(nb
=p
->top_line
; nb
--; ln
=ln
->next
)
519 /* Set and refresh displayed lines */
520 { register LINE
*last
= ln
;
521 for(y
=gui
.topcurs
; ln
&& y
<=gui
.botcurs
; last
=ln
, ln
=ln
->next
, y
+=YSIZE
)
522 ln
->flags
=WHOLESEL
, Move(RP
,gui
.left
,y
),write_text(p
,ln
);
524 /* Set lines after those displayed */
525 for(; ln
; last
=ln
, ln
=ln
->next
)