tools/adflib: build only host variant which is used by Sam440 target
[AROS.git] / workbench / tools / Edit / Jed.c
blobac90315e84de7a009a4ec19e6e306283e95bb7cf
1 /**********************************************************
2 ** jed.c : An simple, fast and efficient text editor **
3 ** Written by T.Pierron and C.Guillaume. **
4 ** Started on august 1998. **
5 **-------------------------------------------------------**
6 ** Special requirements: WorkBench 2.0, v36 or above **
7 **********************************************************/
9 #include <intuition/intuition.h> /* Std types */
10 #include <libraries/gadtools.h> /* Menu events */
11 #include <dos/dos.h> /* Standard error codes */
12 #include <exec/memory.h> /* Memory allocation */
13 #include "Version.h"
14 #include "Jed.h"
15 #include "DiskIO.h"
16 #include "Events.h"
17 #include "Utility.h"
18 #include "Macros.h"
19 #include "Search.h"
20 #include "ProtoTypes.h"
22 #define DEBUG_STUFF /* Only activated if DEBUG macro is defined */
23 #include "Debug.h"
25 #define CATCOMP_NUMBERS /* We will need the string id */
26 #include "strings.h"
28 /* Linked list of opened project */
29 Project edit = NULL;
30 ULONG sigbits=0, sigrcvd, sigport, err_time;
31 UBYTE clear=TRUE, BufTxt[256];
32 pfnSelectFunc move_selection;
34 /* Static string for version command */
35 const char Version[]=SVER;
37 /* Shared libraires we'll need to open */
38 struct IntuitionBase *IntuitionBase = NULL;
39 struct GfxBase *GfxBase = NULL;
40 struct Library *KeymapBase = NULL;
41 struct Library *GadToolsBase = NULL;
42 struct Library *AslBase = NULL;
43 struct LocaleBase *LocaleBase = NULL;
44 struct Library *DiskfontBase = NULL;
45 struct UtilityBase *UtilityBase = NULL;
46 struct Library *IFFParseBase = NULL;
48 struct IntuiMessage *msg, msgbuf; /* Used to collect events */
50 StartUpArgs args;
52 #if DEBUG
53 ULONG bmem, amem;
54 #endif
56 /*** MAIN LOOP ***/
57 int main(int argc, char *argv[])
59 #if DEBUG
60 bmem = AvailMem( MEMF_PUBLIC );
61 #endif
63 ParseArgs(&args, argc, argv);
65 /* Look first if Jano isn't already running */
66 if( find_janoed( &args ) ) cleanup(0, RETURN_OK);
68 /* Some global initialization */
69 init_searchtable();
71 /* Optionnal libraries */
72 AslBase = (struct Library *) OpenLibrary("asl.library", 36);
73 LocaleBase = (struct LocaleBase *) OpenLibrary("locale.library", 38);
74 DiskfontBase = (struct Library *) OpenLibrary("diskfont.library", 0);
75 IFFParseBase = (struct Library *) OpenLibrary("iffparse.library",36);
77 if(LocaleBase) InitLocale(); /* Localize the prog */
79 /* Open the required ROM libraries */
80 if( (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 36)) &&
81 (GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 36)) &&
82 (GadToolsBase = (struct Library *) OpenLibrary("gadtools.library", 36)) &&
83 (KeymapBase = (struct Library *) OpenLibrary("keymap.library", 36)) &&
84 (UtilityBase = (struct UtilityBase *) OpenLibrary("utility.library", 36)) )
86 int lock;
87 init_macros();
88 lock = LockIBase(0);
89 set_default_prefs(&prefs, IntuitionBase->ActiveScreen);
90 UnlockIBase(lock);
92 load_prefs(&prefs, NULL); /* See if it exists a config file */
93 sigport = create_port();
95 /* Create whether an empty project or an existing one */
96 if( ( edit = create_projects(NULL, args.sa_ArgLst, args.sa_NbArgs) ) )
98 /* Open the main interface */
99 if(setup() == 0)
101 /* Makes edit project visible */
102 reshape_panel(edit);
103 active_project(edit,TRUE);
104 clear_brcorner();
106 dispatch_events();
108 } else cleanup(ErrMsg(ERR_NOGUI), RETURN_FAIL);
110 } else cleanup(ErrMsg(ERR_BADOS), RETURN_FAIL);
112 /* Hope that all were well... */
113 cleanup(0, RETURN_OK);
115 return 0;
118 /*** Deallocate ressources properly ***/
119 void cleanup(UBYTE *msg, int errcode)
121 CBClose();
122 close_port();
123 CloseMainWnd(1);
124 CleanupLocale();
125 free_macros();
126 free_diskio_alloc(); /* ASL */
127 if(args.sa_Free) FreeVec(args.sa_ArgLst);
128 if(DiskfontBase) CloseLibrary(DiskfontBase);
129 if(LocaleBase) CloseLibrary((struct Library *)LocaleBase);
130 if(AslBase) CloseLibrary(AslBase);
131 if(IFFParseBase) CloseLibrary(IFFParseBase);
132 if(UtilityBase) CloseLibrary((struct Library *)UtilityBase);
133 if(KeymapBase) CloseLibrary(KeymapBase);
134 if(GadToolsBase) CloseLibrary(GadToolsBase);
135 if(GfxBase) CloseLibrary((struct Library *)GfxBase);
136 if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
137 if(msg) puts(msg);
139 #if DEBUG
140 /* Here can be compared good programs with the others :-) */
141 amem = AvailMem( MEMF_PUBLIC );
142 if(amem < bmem) printf("Possible memory lost of %d bytes\n", bmem-amem);
143 #endif
144 exit(errcode);
147 /*** Handle events coming from main window: ***/
148 void dispatch_events()
150 extern ULONG sigmainwnd, swinsig;
151 extern UBYTE record;
152 BYTE scrolldisp=0, state=0, cnt=0, mark=0, quit = 0;
154 while( quit == 0 )
156 /* Active collect, when pressing arrow gadgets */
157 sigrcvd = (state==0 ? Wait(sigbits) : sigmainwnd);
159 /* if(sigrcvd & SIGBREAKF_CTRL_C) break;
161 else */ if(sigrcvd & sigport) { handle_port(); continue; }
163 else if(sigrcvd & swinsig) { handle_search(); continue; }
165 /* Collect messages posted to the window port */
166 while( ( msg = (struct IntuiMessage *) GetMsg(Wnd->UserPort) ) )
168 /* Copy the entire message into the buffer */
169 CopyMemQuick(msg, &msgbuf, sizeof(msgbuf));
170 ReplyMsg( (struct Message *) msg );
172 switch( msgbuf.Class )
174 case IDCMP_CLOSEWINDOW: handle_menu(112); break;
175 case IDCMP_RAWKEY:
176 handle_kbd(edit);
177 if(record) {
178 if(record == 1) reg_act_com(MAC_ACT_SHORTCUT, msgbuf.Code, msgbuf.Qualifier);
179 else record &= 0x7f;
181 break;
182 case IDCMP_INTUITICKS:
183 /* An error message which needs to be removed? */
184 if(err_time == 0) err_time = msgbuf.Seconds;
185 if(err_time && msgbuf.Seconds-err_time>4) StopError(Wnd);
186 break;
187 case IDCMP_MOUSEBUTTONS:
188 /* Click somewhere in the text */
189 switch( msgbuf.Code )
191 case SELECTDOWN:
192 /* Click over the project bar ? */
193 if(msgbuf.MouseY < gui.top)
195 edit = select_panel(edit, msgbuf.MouseX);
196 break;
199 click(edit, msgbuf.MouseX, msgbuf.MouseY, FALSE);
201 /* Shift-click to use columnar selection */
202 if( ( move_selection = SwitchSelect(edit, msgbuf.Qualifier & SHIFTKEYS ? 1:0, 1) ) )
203 mark=TRUE;
204 break;
205 case SELECTUP:
206 if(mark) unclick(edit);
207 mark=FALSE; scrolldisp=0; break;
209 break;
210 case IDCMP_NEWSIZE:
211 new_size(EDIT_ALL);
212 break;
213 case IDCMP_GADGETDOWN: /* Left scroll bar */
214 if(msgbuf.IAddress == (APTR) &Prop->down) state=1;
215 if(msgbuf.IAddress == (APTR) &Prop->up) state=2;
216 break;
217 case IDCMP_GADGETUP: /* Arrows or prop gadget */
218 state=0;
219 if(msgbuf.IAddress == (APTR) Prop)
220 scroll_disp(edit, FALSE), scrolldisp=0;
221 break;
222 case IDCMP_MOUSEMOVE:
223 if(mark) scrolldisp=2;
224 else
225 if(Prop->scroller.Flags & GFLG_SELECTED) scrolldisp=1;
226 break;
227 case IDCMP_MENUPICK:
228 { struct MenuItem * Item;
229 ULONG MenuId;
231 /* Multi-selection of menu entries */
232 while(msgbuf.Code != MENUNULL)
233 if( (Item = ItemAddress( Menu, msgbuf.Code )) )
235 /* stegerg: get NextSelect here in case menu action causes screen
236 to be closed/reopened in which case item becomes invalid.
237 Also assuming here that user in such case will not use
238 multiselection, ie. that nextselect will be MENUNULL.
240 If that's not the case it would mean more trouble and to protect
241 against that one would need to check if during handle_menu() the
242 screen has been closed/reopened and in that case break out of
243 the menu multiselection loop here. */
245 UWORD nextselect = Item->NextSelect;
247 MenuId = (IPTR)GTMENUITEM_USERDATA( Item );
248 handle_menu( MenuId );
250 if(record) reg_act_com(MAC_ACT_COM_MENU, MenuId, msgbuf.Qualifier);
251 else record &= 0x7f;
253 msgbuf.Code = nextselect;
258 /* Reduces the number of IDCMP mousemove messages to process */
259 if(scrolldisp==1) scroll_disp(edit, FALSE), scrolldisp=0;
260 if(scrolldisp==2) { scrolldisp=0; goto moveit; }
262 /* User may want to auto-scroll the display using arrow gadgets */
263 if(state && (mark || (((struct Gadget *)Prop)[state].Flags & GFLG_SELECTED))) {
264 /* Slow down animation: */
265 WaitTOF(); cnt++;
266 if(cnt>1) {
267 cnt=0;
268 if(autoscroll(edit,state==1 ? 1:-1)==0) state=0;
269 else if(mark) {
270 LONG x , y; moveit:
271 /* Adjust mouse position */
272 x = (msgbuf.MouseX-gui.left) / XSIZE;
273 y = (msgbuf.MouseY-gui.top) / YSIZE;
274 if(x < 0)
275 x = 0;
276 if(x >= gui.nbcol)
277 x = gui.nbcol-1;
278 if(y < 0)
279 y = -1;
280 if(y > gui.nbline)
281 y = gui.nbline;
282 edit->nbrwc = (x += edit->left_pos);
283 y += (LONG)edit->top_line;
284 if( x != edit->ccp.xc || y != edit->ccp.yc )
285 /* Move the selected stream */
286 if( !(state = move_selection(edit,x,y)) )
287 set_cursor_line(edit, y, edit->top_line),
288 inv_curs(edit,TRUE);
291 } /* endif: arrow gadget pressed or autoscroll */
295 /*** Refresh display, according to new window size ***/
296 void new_size(UBYTE Flags)
298 inv_curs(edit, FALSE);
299 adjust_win(Wnd,NbProject>1); /* Adjust internal variables */
300 SetABPenDrMd(RP, pen.fg, pen.bg, JAM2);
301 clear_brcorner();
302 prop_adj(edit);
303 edit->left_pos = curs_visible(edit, edit->top_line);
304 edit->xcurs = (edit->nbrc - edit->left_pos)*XSIZE + gui.left;
305 if(Flags & EDIT_GUI) reshape_panel(edit);
306 if(Flags & EDIT_AREA) redraw_content(edit,edit->show,gui.topcurs,gui.nbline);
307 inv_curs(edit,TRUE);
310 /*** Scroll display according to right prop gadget ***/
311 void scroll_disp(Project p, BOOL adjust)
313 ULONG pos = ((struct PropInfo *)((struct Gadget*)Prop)->SpecialInfo)->VertPot *
314 (p->max_lines - gui.nbline) / MAXPOT;
316 if(p->max_lines>gui.nbline && pos!=p->top_line)
318 if(p->ccp.select)
319 /* If selection mode is on, don't move cursor */
320 p->ycurs-=(pos-p->top_line)*YSIZE,
321 scroll_xy(p, p->left_pos, pos, adjust);
322 else
323 /* Be sure cursor is always in the edit area */
324 inv_curs(p,FALSE),
325 scroll_xy(p, curs_visible(p,pos), pos, adjust),
326 inv_curs(p,TRUE);
330 /*** Be sure that cursor is always visible ***/
331 LONG curs_visible(Project p, LONG newtop)
333 if(p->nbl < newtop) {
334 /* The cursor is above the top line */
335 p->ycurs = gui.topcurs;
336 p->nbl = newtop;
337 goto adj_edited;
338 } else if(p->nbl >= newtop+gui.nbline) {
339 register LONG nb;
340 register LINE *ln;
342 /* The cursor is below the bottom line */
343 p->nbl = newtop+gui.nbline-1;
344 p->ycurs = gui.botcurs;
346 adj_edited:
347 for(ln=p->the_line, nb=p->nbl; nb--; ln=ln->next);
348 if(ln) p->edited=ln;
349 draw_info(p);
351 } else
352 /* Is between the top and bottom line */
353 p->ycurs = (p->nbl-newtop) * YSIZE+ gui.topcurs;
355 /* Jump too far to the right? */
356 newtop=p->left_pos + gui.nbcol - 1;
357 if(p->nbrc>newtop) p->nbrwc=newtop;
358 /* Too far on the left */
359 if((newtop = p->nbrc<p->left_pos)) p->nbrwc=p->left_pos;
361 /* Adjust cursor pos */
362 newtop = adjust_rc(p->edited, p->nbrwc, &p->nbc, newtop);
363 if(p->nbrc != newtop)
364 p->nbrc = newtop, draw_info(p);
366 p->xcurs = (p->nbrc-p->left_pos)*XSIZE + gui.left;
367 return center_horiz(p);
370 /*** Center the display horizontally to show the cursor ***/
371 LONG center_horiz(Project p)
373 /* Return the new left position */
374 if(p->nbrc < p->left_pos)
375 if(p->nbrc > gui.xstep) return (LONG)(p->nbrc - gui.xstep);
376 else return 0;
377 else
378 if(p->nbrc >= p->left_pos+gui.nbcol)
379 return (LONG)(p->nbrc + gui.xstep - gui.nbcol + 1);
380 else return (LONG)p->left_pos;
383 /*** Center the display vertically to show the cursor ***/
384 LONG center_vert(Project p)
386 /* Return the new top position */
387 if(p->nbl < p->top_line || p->nbl >= p->top_line+gui.nbline)
389 LONG newtop=p->nbl - (gui.nbline>>1) + 4;
390 return newtop<0 ? 0:newtop;
392 return (LONG)p->top_line;
395 /*** Low-level text rendering at current rastport position ***/
396 void write_text(Project p, LINE *ln)
398 static UBYTE ts;
399 register UBYTE *str,*buf;
400 register LONG nb, nbc, size = ln->size;
401 static LONG stsel, ensel;
403 /* Look if line is partially selected */
404 if(ln->flags)
405 stsel = (ln->flags & FIRSTSEL ? p->ccp.startsel:0),
406 ensel = (ln->flags & LASTSEL ? p->ccp.endsel:0x7FFFFFFF);
407 else stsel=ensel=0x7FFFFFFF;
409 /* Find the first char to display */
410 for(str=ln->stream, nb=p->left_pos, nbc=0; nbc<nb; str++, size--)
411 if(*str=='\t') nbc+=(ts=tabstop(nbc));
412 else nbc++;
414 nbc-=nb; stsel-=nb; ensel-=nb;
415 if(nbc>=0 && size>=0)
417 /* Tricky case: line begins with an overlapping tabstop */
418 if(nbc>0) {
419 memset(BufTxt,' ',nbc);
420 if(stsel<nbc) stsel=(stsel<=nbc-ts ? 0:nbc);
421 if(ensel<nbc) ensel=nbc;
424 /* Copy the string to a temp buffer */
425 for(nb=size, buf=BufTxt+nbc; nbc<gui.nbcol && nb; str++, nb--)
426 if(*str=='\t') {
427 register UBYTE ts = tabstop(nbc+p->left_pos);
428 memset(buf,' ',ts);
429 nbc+=ts; buf+=ts;
430 if(stsel<nbc && stsel>nbc-ts) stsel=nbc;
431 if(ensel<nbc && ensel>nbc-ts) ensel=nbc;
432 } else *buf++ = *str, nbc++;
434 /* Overlapping tabulation ? */
435 if(nbc>=gui.nbcol) nbc=gui.nbcol;
436 else *buf=' ',nbc++;
438 /* Optimize rendering of unselected line */
439 if(ensel==stsel || stsel>nbc || ensel<0) Text(RP,BufTxt,nbc);
440 else {
441 buf=BufTxt; nb=stsel;
442 if(nb > 0) Text(RP,BufTxt,nb); else nb=0;
443 if(ensel>nbc) ensel=nbc;
444 SetABPenDrMd(RP,pen.fgfill,pen.bgfill,JAM2);
445 Text(RP,buf+nb,ensel-nb);
446 SetABPenDrMd(RP,pen.fg,pen.bg,JAM2);
447 if(ensel!=nbc) Text(RP,buf+ensel,nbc-ensel);
450 if(clear && gui.right>RP->cp_x) {
451 /* Clear the end of line, like ClearEOL(rp), but without overlapping borders :-( */
452 SetAPen(RP,pen.bg); nb=RP->cp_y-BASEL;
453 RectFill(RP,RP->cp_x,nb,gui.right,nb+YSIZE-1);
454 SetAPen(RP,pen.fg);
458 /*** Delta horizontal scroll, with boundary check ***/
459 void scroll_xdelta(Project p, LONG x)
461 if(x<0)
463 x=-x;
464 if(x>p->left_pos) x=0; else x=p->left_pos-x;
465 } else x+=p->left_pos;
466 /* Refresh only if different pos */
467 if(x!=p->left_pos)
469 if(!p->ccp.select) inv_curs(p,FALSE);
470 scroll_xy(p, x, p->top_line, FALSE);
471 /* Is cursor always visible? */
472 x=p->xcurs;
473 curs_visible(p, p->top_line);
474 if(x!=p->xcurs && p->ccp.select)
475 move_selection(p, p->nbrwc, p->nbl);
477 SetAPen(RP,pen.fg); inv_curs(p,TRUE);
481 /*** Delta vertical scroll, with boundary check ***/
482 void scroll_ydelta(Project p, LONG y)
484 LONG pos=p->top_line+y;
485 /* Clamp values to the boundary */
487 if(pos<0) pos=0;
488 #if 0
489 if(pos>(LONG)p->max_lines-(LONG)gui.nbline) pos=p->max_lines-gui.nbline;
490 #else
491 if(pos>p->max_lines-1) pos=p->max_lines-1;
492 #endif
494 if(pos!=p->top_line)
496 if(!p->ccp.select) inv_curs(p,FALSE);
497 scroll_xy(p, curs_visible(p,pos), pos, TRUE);
498 if(p->ccp.select) move_selection(p, p->nbrwc, p->nbl);
499 inv_curs(p,TRUE);
503 /*** Like previous, but with required simplifications ***/
504 BOOL autoscroll(Project p, WORD y)
506 LONG pos=p->top_line+y;
507 /* Clamp values to the boundary */
508 #if 1
509 if(pos>(LONG)p->max_lines-(LONG)gui.nbline) pos=p->max_lines-gui.nbline;
510 #else
511 if(pos>p->max_lines-1) pos=p->max_lines-1;
512 #endif
513 if(pos<0) pos=0;
515 if(pos!=p->top_line)
517 if(!p->ccp.select) inv_curs(p,FALSE);
518 scroll_xy(p, p->left_pos, pos, TRUE);
519 if(!p->ccp.select)
520 curs_visible(p, p->top_line),
521 inv_curs(p,TRUE);
523 return TRUE;
524 } else return FALSE;
527 /*** Redraw part of a display ***/
528 void redraw_content(Project p, LINE *ln, WORD startpos, WORD nb)
530 for(SetAPen(RP,pen.fg); nb-- && ln; startpos += YSIZE,ln = ln->next)
531 Move(RP, gui.left, startpos),
532 write_text(p, ln);
534 /* Empty lines visible? */
535 if(nb >= 0)
536 SetAPen(RP,pen.bg),
537 RectFill(RP,gui.left, startpos-BASEL, gui.right, gui.bottom),
538 SetAPen(RP,pen.fg);
541 /*** Scroll to a determinate absolute position ***/
542 void scroll_xy(Project p, LONG xp, LONG yp, BYTE adj)
544 LONG skipy = yp-p->top_line,
545 skipx = xp-p->left_pos, svg;
547 if(skipy<0) skipy = -skipy;
548 if(skipx<0) skipx = -skipx;
549 /* Can the process be optimized? */
550 if(skipy < gui.nbline && skipx < gui.nbcol)
552 /* Yes, don't redraw whole display */
553 WORD ystart,xstart; LINE *disp = p->show;
555 xstart = skipx * XSIZE;
556 ystart = skipy * YSIZE;
558 /* 1. Scroll the display */
559 ScrollRaster(RP, xp<p->left_pos? -xstart:xstart, yp<p->top_line? -ystart:ystart,
560 gui.left, gui.top, gui.rcurs-1, gui.bottom);
562 /* Only performs changes if required */
563 if(skipy)
565 /* 2. Update internal variables if vertical scroll */
566 if(yp < p->top_line)
568 /* Scroll display down */
569 register LONG nb=skipy;
570 for(; nb--; disp=disp->prev);
571 p->top_line -= skipy;
572 ystart = gui.topcurs;
573 p->show = disp;
574 } else {
575 /* Scroll display up */
576 register LONG nb=skipy;
577 p->top_line += skipy;
578 ystart = gui.botcurs - ystart + YSIZE;
579 for(; nb--; disp=disp->next);
580 p->show = disp;
582 for(nb=gui.nbline - skipy; disp && nb--; disp=disp->next);
586 /* Same fight */
587 if(skipx)
589 /* 3. Update variables if horizontal scroll */
590 svg = gui.nbcol;
591 gui.nbcol = skipx;
593 if(xp < p->left_pos)
595 /* Scroll display right */
596 p->xcurs += xstart;
597 clear = FALSE;
598 p->left_pos = xp;
599 redraw_content(p, p->show, gui.topcurs, gui.nbline);
600 clear = TRUE;
601 } else {
602 /* Scroll display left */
603 register LONG left=gui.left;
604 p->xcurs -= xstart;
605 gui.left = gui.rcurs-xstart;
606 p->left_pos += svg;
607 redraw_content(p, p->show, gui.topcurs, gui.nbline);
608 gui.left = left;
609 p->left_pos = xp;
611 gui.nbcol = svg;
613 /* 4. Redraw display (needs previous changes before) */
614 if(skipy) redraw_content(p, disp, ystart, skipy);
616 } else {
617 /* We've jump too far, whole redraw */
618 register LONG nb = yp-p->top_line;
619 register LINE *disp = p->show;
620 p->top_line += nb;
621 if(nb < 0) for(; nb++; disp=disp->prev);
622 else for(; nb--; disp=disp->next);
623 p->show = disp;
624 p->left_pos = xp;
625 p->xcurs = (p->nbrc-p->left_pos) * XSIZE + gui.left;
626 redraw_content(p, disp, gui.topcurs, gui.nbline);
628 /* Adjust position of the prop gadget */
629 if(adj) prop_adj(p);
632 /*** Scroll lines up, at a specified position (used for deleting) ***/
633 void scroll_up(Project p, LINE *ln, WORD ystart, LONG leftpos)
635 register WORD nbl;
636 /* If some of redrawn lines are still visible */
637 if( leftpos == p->left_pos )
638 /* Optimize lines to refresh */
639 nbl = p->top_line + gui.nbline - p->nbl - 1,
640 ystart += YSIZE;
641 else
642 /* Redraw whole content */
643 nbl = gui.nbline, ystart = gui.topcurs, ln=p->show,
644 p->left_pos = leftpos;
646 redraw_content(p, ln, ystart, nbl);
647 /* Adjust cursor position */
648 p->xcurs = (p->nbrc-p->left_pos) * XSIZE + gui.left;