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 */
20 #include "ProtoTypes.h"
22 #define DEBUG_STUFF /* Only activated if DEBUG macro is defined */
25 #define CATCOMP_NUMBERS /* We will need the string id */
28 /* Linked list of opened project */
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 */
57 int main(int argc
, char *argv
[])
60 bmem
= AvailMem( MEMF_PUBLIC
);
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 */
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)) )
87 set_default_prefs(&prefs
, IntuitionBase
->ActiveScreen
);
89 load_prefs(&prefs
, NULL
); /* See if it exists a config file */
90 sigport
= create_port();
92 /* Create whether an empty project or an existing one */
93 if( ( edit
= create_projects(NULL
, args
.sa_ArgLst
, args
.sa_NbArgs
) ) )
95 /* Open the main interface */
98 /* Makes edit project visible */
100 active_project(edit
,TRUE
);
105 } else cleanup(ErrMsg(ERR_NOGUI
), RETURN_FAIL
);
107 } else cleanup(ErrMsg(ERR_BADOS
), RETURN_FAIL
);
109 /* Hope that all were well... */
110 cleanup(0, RETURN_OK
);
115 /*** Deallocate ressources properly ***/
116 void cleanup(UBYTE
*msg
, int errcode
)
123 free_diskio_alloc(); /* ASL */
124 if(args
.sa_Free
) FreeVec(args
.sa_ArgLst
);
125 if(DiskfontBase
) CloseLibrary(DiskfontBase
);
126 if(LocaleBase
) CloseLibrary((struct Library
*)LocaleBase
);
127 if(AslBase
) CloseLibrary(AslBase
);
128 if(IFFParseBase
) CloseLibrary(IFFParseBase
);
129 if(UtilityBase
) CloseLibrary((struct Library
*)UtilityBase
);
130 if(KeymapBase
) CloseLibrary(KeymapBase
);
131 if(GadToolsBase
) CloseLibrary(GadToolsBase
);
132 if(GfxBase
) CloseLibrary((struct Library
*)GfxBase
);
133 if(IntuitionBase
) CloseLibrary((struct Library
*)IntuitionBase
);
137 /* Here can be compared good programs with the others :-) */
138 amem
= AvailMem( MEMF_PUBLIC
);
139 if(amem
< bmem
) printf("Possible memory lost of %d bytes\n", bmem
-amem
);
144 /*** Handle events coming from main window: ***/
145 void dispatch_events()
147 extern ULONG sigmainwnd
, swinsig
;
149 BYTE scrolldisp
=0, state
=0, cnt
=0, mark
=0, quit
= 0;
153 /* Active collect, when pressing arrow gadgets */
154 sigrcvd
= (state
==0 ? Wait(sigbits
) : sigmainwnd
);
156 /* if(sigrcvd & SIGBREAKF_CTRL_C) break;
158 else */ if(sigrcvd
& sigport
) { handle_port(); continue; }
160 else if(sigrcvd
& swinsig
) { handle_search(); continue; }
162 /* Collect messages posted to the window port */
163 while( ( msg
= (struct IntuiMessage
*) GetMsg(Wnd
->UserPort
) ) )
165 /* Copy the entire message into the buffer */
166 CopyMemQuick(msg
, &msgbuf
, sizeof(msgbuf
));
167 ReplyMsg( (struct Message
*) msg
);
169 switch( msgbuf
.Class
)
171 case IDCMP_CLOSEWINDOW
: handle_menu(112); break;
175 if(record
== 1) reg_act_com(MAC_ACT_SHORTCUT
, msgbuf
.Code
, msgbuf
.Qualifier
);
179 case IDCMP_INTUITICKS
:
180 /* An error message which needs to be removed? */
181 if(err_time
== 0) err_time
= msgbuf
.Seconds
;
182 if(err_time
&& msgbuf
.Seconds
-err_time
>4) StopError(Wnd
);
184 case IDCMP_MOUSEBUTTONS
:
185 /* Click somewhere in the text */
186 switch( msgbuf
.Code
)
189 /* Click over the project bar ? */
190 if(msgbuf
.MouseY
< gui
.top
)
192 edit
= select_panel(edit
, msgbuf
.MouseX
);
196 click(edit
, msgbuf
.MouseX
, msgbuf
.MouseY
, FALSE
);
198 /* Shift-click to use columnar selection */
199 if( ( move_selection
= SwitchSelect(edit
, msgbuf
.Qualifier
& SHIFTKEYS
? 1:0, 1) ) )
203 if(mark
) unclick(edit
);
204 mark
=FALSE
; scrolldisp
=0; break;
210 case IDCMP_GADGETDOWN
: /* Left scroll bar */
211 if(msgbuf
.IAddress
== (APTR
) &Prop
->down
) state
=1;
212 if(msgbuf
.IAddress
== (APTR
) &Prop
->up
) state
=2;
214 case IDCMP_GADGETUP
: /* Arrows or prop gadget */
216 if(msgbuf
.IAddress
== (APTR
) Prop
)
217 scroll_disp(edit
, FALSE
), scrolldisp
=0;
219 case IDCMP_MOUSEMOVE
:
220 if(mark
) scrolldisp
=2;
222 if(Prop
->scroller
.Flags
& GFLG_SELECTED
) scrolldisp
=1;
225 { struct MenuItem
* Item
;
228 /* Multi-selection of menu entries */
229 while(msgbuf
.Code
!= MENUNULL
)
230 if( (Item
= ItemAddress( Menu
, msgbuf
.Code
)) )
232 MenuId
= (ULONG
)GTMENUITEM_USERDATA( Item
);
233 handle_menu( MenuId
);
235 if(record
) reg_act_com(MAC_ACT_COM_MENU
, MenuId
, msgbuf
.Qualifier
);
238 msgbuf
.Code
= Item
->NextSelect
;
243 /* Reduces the number of IDCMP mousemove messages to process */
244 if(scrolldisp
==1) scroll_disp(edit
, FALSE
), scrolldisp
=0;
245 if(scrolldisp
==2) { scrolldisp
=0; goto moveit
; }
247 /* User may want to auto-scroll the display using arrow gadgets */
248 if(state
&& (mark
|| (((struct Gadget
*)Prop
)[state
].Flags
& GFLG_SELECTED
))) {
249 /* Slow down animation: */
253 if(autoscroll(edit
,state
==1 ? 1:-1)==0) state
=0;
256 /* Adjust mouse position */
257 x
= (msgbuf
.MouseX
-gui
.left
) / XSIZE
;
258 y
= (msgbuf
.MouseY
-gui
.top
) / YSIZE
;
259 if(x
< 0) x
= 0; if(x
>= gui
.nbcol
) x
= gui
.nbcol
-1;
260 if(y
< 0) y
= -1; if(y
> gui
.nbline
) y
= gui
.nbline
;
261 edit
->nbrwc
= (x
+= edit
->left_pos
);
262 y
+= (LONG
)edit
->top_line
;
263 if( x
!= edit
->ccp
.xc
|| y
!= edit
->ccp
.yc
)
264 /* Move the selected stream */
265 if( !(state
= move_selection(edit
,x
,y
)) )
266 set_cursor_line(edit
, y
, edit
->top_line
),
270 } /* endif: arrow gadget pressed or autoscroll */
274 /*** Refresh display, according to new window size ***/
275 void new_size(UBYTE Flags
)
277 inv_curs(edit
, FALSE
);
278 adjust_win(Wnd
,NbProject
>1); /* Adjust internal variables */
279 SetABPenDrMd(RP
, pen
.fg
, pen
.bg
, JAM2
);
282 edit
->left_pos
= curs_visible(edit
, edit
->top_line
);
283 edit
->xcurs
= (edit
->nbrc
- edit
->left_pos
)*XSIZE
+ gui
.left
;
284 if(Flags
& EDIT_GUI
) reshape_panel(edit
);
285 if(Flags
& EDIT_AREA
) redraw_content(edit
,edit
->show
,gui
.topcurs
,gui
.nbline
);
289 /*** Scroll display according to right prop gadget ***/
290 void scroll_disp(Project p
, BOOL adjust
)
292 ULONG pos
= ((struct PropInfo
*)((struct Gadget
*)Prop
)->SpecialInfo
)->VertPot
*
293 (p
->max_lines
- gui
.nbline
) / MAXPOT
;
295 if(p
->max_lines
>gui
.nbline
&& pos
!=p
->top_line
)
298 /* If selection mode is on, don't move cursor */
299 p
->ycurs
-=(pos
-p
->top_line
)*YSIZE
,
300 scroll_xy(p
, p
->left_pos
, pos
, adjust
);
302 /* Be sure cursor is always in the edit area */
304 scroll_xy(p
, curs_visible(p
,pos
), pos
, adjust
),
309 /*** Be sure that cursor is always visible ***/
310 LONG
curs_visible(Project p
, LONG newtop
)
312 if(p
->nbl
< newtop
) {
313 /* The cursor is above the top line */
314 p
->ycurs
= gui
.topcurs
;
317 } else if(p
->nbl
>= newtop
+gui
.nbline
) {
321 /* The cursor is below the bottom line */
322 p
->nbl
= newtop
+gui
.nbline
-1;
323 p
->ycurs
= gui
.botcurs
;
326 for(ln
=p
->the_line
, nb
=p
->nbl
; nb
--; ln
=ln
->next
);
331 /* Is between the top and bottom line */
332 p
->ycurs
= (p
->nbl
-newtop
) * YSIZE
+ gui
.topcurs
;
334 /* Jump too far to the right? */
335 newtop
=p
->left_pos
+ gui
.nbcol
- 1;
336 if(p
->nbrc
>newtop
) p
->nbrwc
=newtop
;
337 /* Too far on the left */
338 if((newtop
= p
->nbrc
<p
->left_pos
)) p
->nbrwc
=p
->left_pos
;
340 /* Adjust cursor pos */
341 newtop
= adjust_rc(p
->edited
, p
->nbrwc
, &p
->nbc
, newtop
);
342 if(p
->nbrc
!= newtop
)
343 p
->nbrc
= newtop
, draw_info(p
);
345 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
346 return center_horiz(p
);
349 /*** Center the display horizontally to show the cursor ***/
350 LONG
center_horiz(Project p
)
352 /* Return the new left position */
353 if(p
->nbrc
< p
->left_pos
)
354 if(p
->nbrc
> gui
.xstep
) return (LONG
)(p
->nbrc
- gui
.xstep
);
357 if(p
->nbrc
>= p
->left_pos
+gui
.nbcol
)
358 return (LONG
)(p
->nbrc
+ gui
.xstep
- gui
.nbcol
+ 1);
359 else return (LONG
)p
->left_pos
;
362 /*** Center the display vertically to show the cursor ***/
363 LONG
center_vert(Project p
)
365 /* Return the new top position */
366 if(p
->nbl
< p
->top_line
|| p
->nbl
>= p
->top_line
+gui
.nbline
)
368 LONG newtop
=p
->nbl
- (gui
.nbline
>>1) + 4;
369 return newtop
<0 ? 0:newtop
;
371 return (LONG
)p
->top_line
;
374 /*** Low-level text rendering at current rastport position ***/
375 void write_text(Project p
, LINE
*ln
)
378 register UBYTE
*str
,*buf
;
379 register LONG nb
, nbc
, size
= ln
->size
;
380 static LONG stsel
, ensel
;
382 /* Look if line is partially selected */
384 stsel
= (ln
->flags
& FIRSTSEL
? p
->ccp
.startsel
:0),
385 ensel
= (ln
->flags
& LASTSEL
? p
->ccp
.endsel
:0x7FFFFFFF);
386 else stsel
=ensel
=0x7FFFFFFF;
388 /* Find the first char to display */
389 for(str
=ln
->stream
, nb
=p
->left_pos
, nbc
=0; nbc
<nb
; str
++, size
--)
390 if(*str
=='\t') nbc
+=(ts
=tabstop(nbc
));
393 nbc
-=nb
; stsel
-=nb
; ensel
-=nb
;
394 if(nbc
>=0 && size
>=0)
396 /* Tricky case: line begins with an overlapping tabstop */
398 memset(BufTxt
,' ',nbc
);
399 if(stsel
<nbc
) stsel
=(stsel
<=nbc
-ts
? 0:nbc
);
400 if(ensel
<nbc
) ensel
=nbc
;
403 /* Copy the string to a temp buffer */
404 for(nb
=size
, buf
=BufTxt
+nbc
; nbc
<gui
.nbcol
&& nb
; str
++, nb
--)
406 register UBYTE ts
= tabstop(nbc
+p
->left_pos
);
409 if(stsel
<nbc
&& stsel
>nbc
-ts
) stsel
=nbc
;
410 if(ensel
<nbc
&& ensel
>nbc
-ts
) ensel
=nbc
;
411 } else *buf
++ = *str
, nbc
++;
413 /* Overlapping tabulation ? */
414 if(nbc
>=gui
.nbcol
) nbc
=gui
.nbcol
;
417 /* Optimize rendering of unselected line */
418 if(ensel
==stsel
|| stsel
>nbc
|| ensel
<0) Text(RP
,BufTxt
,nbc
);
420 buf
=BufTxt
; nb
=stsel
;
421 if(nb
> 0) Text(RP
,BufTxt
,nb
); else nb
=0;
422 if(ensel
>nbc
) ensel
=nbc
;
423 SetABPenDrMd(RP
,pen
.fgfill
,pen
.bgfill
,JAM2
);
424 Text(RP
,buf
+nb
,ensel
-nb
);
425 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
426 if(ensel
!=nbc
) Text(RP
,buf
+ensel
,nbc
-ensel
);
429 if(clear
&& gui
.right
>RP
->cp_x
) {
430 /* Clear the end of line, like ClearEOL(rp), but without overlapping borders :-( */
431 SetAPen(RP
,pen
.bg
); nb
=RP
->cp_y
-BASEL
;
432 RectFill(RP
,RP
->cp_x
,nb
,gui
.right
,nb
+YSIZE
-1);
437 /*** Delta horizontal scroll, with boundary check ***/
438 void scroll_xdelta(Project p
, LONG x
)
443 if(x
>p
->left_pos
) x
=0; else x
=p
->left_pos
-x
;
444 } else x
+=p
->left_pos
;
445 /* Refresh only if different pos */
448 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
449 scroll_xy(p
, x
, p
->top_line
, FALSE
);
450 /* Is cursor always visible? */
452 curs_visible(p
, p
->top_line
);
453 if(x
!=p
->xcurs
&& p
->ccp
.select
)
454 move_selection(p
, p
->nbrwc
, p
->nbl
);
456 SetAPen(RP
,pen
.fg
); inv_curs(p
,TRUE
);
460 /*** Delta vertical scroll, with boundary check ***/
461 void scroll_ydelta(Project p
, LONG y
)
463 LONG pos
=p
->top_line
+y
;
464 /* Clamp values to the boundary */
468 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
470 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
475 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
476 scroll_xy(p
, curs_visible(p
,pos
), pos
, TRUE
);
477 if(p
->ccp
.select
) move_selection(p
, p
->nbrwc
, p
->nbl
);
482 /*** Like previous, but with required simplifications ***/
483 BOOL
autoscroll(Project p
, WORD y
)
485 LONG pos
=p
->top_line
+y
;
486 /* Clamp values to the boundary */
488 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
490 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
496 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
497 scroll_xy(p
, p
->left_pos
, pos
, TRUE
);
499 curs_visible(p
, p
->top_line
),
506 /*** Redraw part of a display ***/
507 void redraw_content(Project p
, LINE
*ln
, WORD startpos
, WORD nb
)
509 for(SetAPen(RP
,pen
.fg
); nb
-- && ln
; startpos
+= YSIZE
,ln
= ln
->next
)
510 Move(RP
, gui
.left
, startpos
),
513 /* Empty lines visible? */
516 RectFill(RP
,gui
.left
, startpos
-BASEL
, gui
.right
, gui
.bottom
),
520 /*** Scroll to a determinate absolute position ***/
521 void scroll_xy(Project p
, LONG xp
, LONG yp
, BYTE adj
)
523 LONG skipy
= yp
-p
->top_line
,
524 skipx
= xp
-p
->left_pos
, svg
;
526 if(skipy
<0) skipy
= -skipy
;
527 if(skipx
<0) skipx
= -skipx
;
528 /* Can the process be optimized? */
529 if(skipy
< gui
.nbline
&& skipx
< gui
.nbcol
)
531 /* Yes, don't redraw whole display */
532 WORD ystart
,xstart
; LINE
*disp
= p
->show
;
534 xstart
= skipx
* XSIZE
;
535 ystart
= skipy
* YSIZE
;
537 /* 1. Scroll the display */
538 ScrollRaster(RP
, xp
<p
->left_pos
? -xstart
:xstart
, yp
<p
->top_line
? -ystart
:ystart
,
539 gui
.left
, gui
.top
, gui
.rcurs
-1, gui
.bottom
);
541 /* Only performs changes if required */
544 /* 2. Update internal variables if vertical scroll */
547 /* Scroll display down */
548 register LONG nb
=skipy
;
549 for(; nb
--; disp
=disp
->prev
);
550 p
->top_line
-= skipy
;
551 ystart
= gui
.topcurs
;
554 /* Scroll display up */
555 register LONG nb
=skipy
;
556 p
->top_line
+= skipy
;
557 ystart
= gui
.botcurs
- ystart
+ YSIZE
;
558 for(; nb
--; disp
=disp
->next
);
561 for(nb
=gui
.nbline
- skipy
; disp
&& nb
--; disp
=disp
->next
);
568 /* 3. Update variables if horizontal scroll */
574 /* Scroll display right */
578 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
581 /* Scroll display left */
582 register LONG left
=gui
.left
;
584 gui
.left
= gui
.rcurs
-xstart
;
586 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
592 /* 4. Redraw display (needs previous changes before) */
593 if(skipy
) redraw_content(p
, disp
, ystart
, skipy
);
596 /* We've jump too far, whole redraw */
597 register LONG nb
= yp
-p
->top_line
;
598 register LINE
*disp
= p
->show
;
600 if(nb
< 0) for(; nb
++; disp
=disp
->prev
);
601 else for(; nb
--; disp
=disp
->next
);
604 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;
605 redraw_content(p
, disp
, gui
.topcurs
, gui
.nbline
);
607 /* Adjust position of the prop gadget */
611 /*** Scroll lines up, at a specified position (used for deleting) ***/
612 void scroll_up(Project p
, LINE
*ln
, WORD ystart
, LONG leftpos
)
615 /* If some of redrawn lines are still visible */
616 if( leftpos
== p
->left_pos
)
617 /* Optimize lines to refresh */
618 nbl
= p
->top_line
+ gui
.nbline
- p
->nbl
- 1,
621 /* Redraw whole content */
622 nbl
= gui
.nbline
, ystart
= gui
.topcurs
, ln
=p
->show
,
623 p
->left_pos
= leftpos
;
625 redraw_content(p
, ln
, ystart
, nbl
);
626 /* Adjust cursor position */
627 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;