revert between 56095 -> 55830 in arch
[AROS.git] / workbench / tools / Edit / Edit.c
blobb037fc2b0b2cb93c669d1b5d6009705a3dac8ed2
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>
9 #include "Jed.h"
10 #include "ClipLoc.h"
11 #include "Utility.h"
12 #include "Edit.h"
13 #include "ProtoTypes.h"
15 #define CATCOMP_NUMBERS /* Strings id for err. msg */
16 #include "strings.h"
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);
28 if(s>=e) continue;
29 e-=s; if(e > Max) e=Max;
30 CopyMem(ln->stream+s, Buf, e);
31 Max-=e; Buf+=e; nbc+=e;
33 *Buf=0;
34 return nbc;
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)
49 yn=0;
50 if(yn>=p->max_lines)
52 yn=p->max_lines-1;
53 ret=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);
70 else ln->flags=0;
71 else ln->flags=WHOLESEL;
72 if(gui.topcurs <= y && y <= gui.botcurs)
73 Move(RP, gui.left, y),write_text(p,ln);
75 else
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);
84 else ln->flags=0;
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)
96 if(yn == p->ccp.yp)
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;
101 Set = 3;
102 } else
103 if( yn > p->ccp.yp )
104 ln->flags=LASTSEL, p->ccp.endsel=xn, Set = 2;
105 else
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);
119 return ret;
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;
127 extern BYTE clear;
128 LONG yline; /* Nb. of line marked */
129 LINE *ln; /* Running pointer */
130 BYTE ret; /* Autoscroll ? */
131 WORD y; /* VPos of item sel */
132 BYTE rdw = 0;
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;
138 clear=FALSE;
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;
150 if(yn<0)
151 yn=0;
152 if(yn>=p->max_lines)
154 yn=p->max_lines-1;
155 ret=0;
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 **/
162 if( xn < p->ccp.xp )
163 p->ccp.endsel = p->ccp.xp, p->ccp.startsel = xn;
164 else
165 p->ccp.startsel = p->ccp.xp, p->ccp.endsel = xn;
167 /** Same fight as before: afterward or backward scan **/
168 if( yn >= yline )
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;
184 } else {
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 **/
208 if(rdw)
210 register LINE *ptr; register WORD yc;
211 /* Limits number of lines to redraw */
212 if(rdw&4)
213 ptr=p->ccp.cline, yline=p->ccp.yc,
214 yc=(yline-p->top_line)*YSIZE+gui.topcurs;
215 else ptr=ln, yc=y;
217 /* Reduces number of columns to redraw */
218 if(p->ccp.xc < xn)
219 p->left_pos=p->ccp.xc, gui.nbcol=xn-p->ccp.xc+1+tabstop(xn);
220 else
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;
227 if(rdw&1) {
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);
230 } else {
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;
238 clear=TRUE;
239 return ret;
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 )
252 case 0:
253 case STREAM_TYPE: p->ccp.select=WORD_TYPE; break;
254 case WORD_TYPE: p->ccp.select=LINE_TYPE; break;
255 case COLUMN_TYPE:
256 case LINE_TYPE: new=NULL; unmark_all(p,TRUE); break;
258 if(new) new(p,p->nbrc,p->nbl);
259 inv_curs(p,TRUE);
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;
266 else {
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);
273 inv_curs(p,TRUE);
275 return new;
278 /*** Cut: remove block of selected text ***/
279 void del_block(Project p)
281 ULONG nbdel, nbrem;
282 LINE *ln = p->ccp.cline;
284 /* Look for first line to remove */
285 switch( p->ccp.select )
287 case COLUMN_TYPE:
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;
290 default:
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;
293 break;
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 */
305 ULONG rcs, rce;
306 rcs = find_nbc(ln, p->ccp.startsel);
307 rce = find_nbc(ln, p->ccp.endsel);
308 switch( ln->flags )
310 case FIRSTSEL |
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;
317 ln=ln->next;
318 } else {
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++;
322 ln = next;
326 Ze_end: /* Makes some visual cleanup */
327 reg_group_by(&p->undo);
329 /* Prop gadget should be adjusted? */
330 p->max_lines -= nbrem;
331 p->ccp.select = 0;
332 p->ccp.xp = (ULONG)-1;
333 if(nbrem>=1) prop_adj(p);
335 /* Adjust cursor's position */
336 p->edited = p->the_line;
337 nbrem = p->nbl;
338 p->nbl = 0;
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? */
343 if(nbdel>1)
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;
351 inv_curs(p,TRUE);
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))
359 if( p->ccp.select )
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 */
368 if( method == -1 )
370 register LINE *line = ln;
371 for(; line && line->flags; line=line->next)
372 if(AffectedLine(line, p) && ch != line->stream[0]) return;
375 /* Do it! */
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);
383 /* Redraw */
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;
389 if(p->nbc==0)
390 goto redraw;
391 goto refresh;
394 /* Should we add or remove the char? */
395 if(method == -1) {
396 if(p->edited->size > 0 && p->edited->stream[0]==ch) {
397 rem_chars(&p->undo,p->edited,0,0);
398 if(p->nbc==0)
399 goto redraw;
400 goto refresh;
401 } else return;
404 if( add_char(&p->undo, p->edited, 0, ch) )
406 refresh:
407 inv_curs(p, FALSE);
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);
416 inv_curs(p,TRUE);
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)
425 switch(Method)
427 case 0: ch=ToUpper(ch); break;
428 case 2:
429 { UBYTE temp;
430 temp=ch; ch=ToUpper(ch); if(ch!=temp) break;
432 case 1: ch=ToLower(ch);
434 return ch;
437 /*** Change one char or a whole selected region ***/
438 void change_case(Project p, UBYTE method)
440 LINE *ln;
441 if( p->ccp.select )
443 LONG y;
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;
449 Method=method;
450 /* Let's change selected buffer */
451 for(; ln && ln->flags; ln=ln->next,y+=YSIZE)
453 replace_chars(ln,
454 (ln->flags & FIRSTSEL ? find_nbc(ln, p->ccp.startsel) : 0),
455 (ln->flags & LASTSEL ? find_nbc(ln, p->ccp.endsel) : ln->size),
456 change_casechar
459 if(gui.topcurs<=y && y<=gui.botcurs)
460 Move(RP,gui.left,y),write_text(p,ln);
462 inv_curs(p,TRUE);
463 } else {
465 Method=method; ln=p->edited;
466 replace_char(ln,p->nbc,change_casechar(ln->stream[p->nbc]));
467 curs_right(p,FALSE);
471 /*** Clears all selected lines ***/
472 void unmark_all(Project p, BYTE wrap)
474 LINE *ln;
475 LONG y;
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)
483 ln->flags=0;
484 if(gui.topcurs<=y && y<=gui.botcurs)
485 Move(RP,gui.left,y),write_text(p,ln);
488 /* Is the cursor always visible? */
489 if( wrap ) {
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);
494 inv_curs(p,TRUE);
496 p->ccp.xp = (ULONG)-1;
497 RP->Mask = gui.txtmask;
500 /*** Set all lines as selected ***/
501 void mark_all(Project p)
503 LONG nb, y;
504 LINE *ln;
506 move_selection = move_stream_selection;
507 p->ccp.select = LINE_TYPE;
508 p->ccp.cline = ln = p->the_line;
509 p->ccp.yc = 0;
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)
516 ln->flags=WHOLESEL;
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)
527 ln->flags=WHOLESEL;
529 p->ccp.line = last;
531 inv_curs(p,TRUE);