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 /* stegerg: get NextSelect here in case menu action causes screen
233 to be closed/reopened in which case item becomes invalid.
234 Also assuming here that user in such case will not use
235 multiselection, ie. that nextselect will be MENUNULL.
237 If that's not the case it would mean more trouble and to protect
238 against that one would need to check if during handle_menu() the
239 screen has been closed/reopened and in that case break out of
240 the menu multiselection loop here. */
242 UWORD nextselect
= Item
->NextSelect
;
244 MenuId
= (ULONG
)GTMENUITEM_USERDATA( Item
);
245 handle_menu( MenuId
);
247 if(record
) reg_act_com(MAC_ACT_COM_MENU
, MenuId
, msgbuf
.Qualifier
);
250 msgbuf
.Code
= nextselect
;
255 /* Reduces the number of IDCMP mousemove messages to process */
256 if(scrolldisp
==1) scroll_disp(edit
, FALSE
), scrolldisp
=0;
257 if(scrolldisp
==2) { scrolldisp
=0; goto moveit
; }
259 /* User may want to auto-scroll the display using arrow gadgets */
260 if(state
&& (mark
|| (((struct Gadget
*)Prop
)[state
].Flags
& GFLG_SELECTED
))) {
261 /* Slow down animation: */
265 if(autoscroll(edit
,state
==1 ? 1:-1)==0) state
=0;
268 /* Adjust mouse position */
269 x
= (msgbuf
.MouseX
-gui
.left
) / XSIZE
;
270 y
= (msgbuf
.MouseY
-gui
.top
) / YSIZE
;
271 if(x
< 0) x
= 0; if(x
>= gui
.nbcol
) x
= gui
.nbcol
-1;
272 if(y
< 0) y
= -1; if(y
> gui
.nbline
) y
= gui
.nbline
;
273 edit
->nbrwc
= (x
+= edit
->left_pos
);
274 y
+= (LONG
)edit
->top_line
;
275 if( x
!= edit
->ccp
.xc
|| y
!= edit
->ccp
.yc
)
276 /* Move the selected stream */
277 if( !(state
= move_selection(edit
,x
,y
)) )
278 set_cursor_line(edit
, y
, edit
->top_line
),
282 } /* endif: arrow gadget pressed or autoscroll */
286 /*** Refresh display, according to new window size ***/
287 void new_size(UBYTE Flags
)
289 inv_curs(edit
, FALSE
);
290 adjust_win(Wnd
,NbProject
>1); /* Adjust internal variables */
291 SetABPenDrMd(RP
, pen
.fg
, pen
.bg
, JAM2
);
294 edit
->left_pos
= curs_visible(edit
, edit
->top_line
);
295 edit
->xcurs
= (edit
->nbrc
- edit
->left_pos
)*XSIZE
+ gui
.left
;
296 if(Flags
& EDIT_GUI
) reshape_panel(edit
);
297 if(Flags
& EDIT_AREA
) redraw_content(edit
,edit
->show
,gui
.topcurs
,gui
.nbline
);
301 /*** Scroll display according to right prop gadget ***/
302 void scroll_disp(Project p
, BOOL adjust
)
304 ULONG pos
= ((struct PropInfo
*)((struct Gadget
*)Prop
)->SpecialInfo
)->VertPot
*
305 (p
->max_lines
- gui
.nbline
) / MAXPOT
;
307 if(p
->max_lines
>gui
.nbline
&& pos
!=p
->top_line
)
310 /* If selection mode is on, don't move cursor */
311 p
->ycurs
-=(pos
-p
->top_line
)*YSIZE
,
312 scroll_xy(p
, p
->left_pos
, pos
, adjust
);
314 /* Be sure cursor is always in the edit area */
316 scroll_xy(p
, curs_visible(p
,pos
), pos
, adjust
),
321 /*** Be sure that cursor is always visible ***/
322 LONG
curs_visible(Project p
, LONG newtop
)
324 if(p
->nbl
< newtop
) {
325 /* The cursor is above the top line */
326 p
->ycurs
= gui
.topcurs
;
329 } else if(p
->nbl
>= newtop
+gui
.nbline
) {
333 /* The cursor is below the bottom line */
334 p
->nbl
= newtop
+gui
.nbline
-1;
335 p
->ycurs
= gui
.botcurs
;
338 for(ln
=p
->the_line
, nb
=p
->nbl
; nb
--; ln
=ln
->next
);
343 /* Is between the top and bottom line */
344 p
->ycurs
= (p
->nbl
-newtop
) * YSIZE
+ gui
.topcurs
;
346 /* Jump too far to the right? */
347 newtop
=p
->left_pos
+ gui
.nbcol
- 1;
348 if(p
->nbrc
>newtop
) p
->nbrwc
=newtop
;
349 /* Too far on the left */
350 if((newtop
= p
->nbrc
<p
->left_pos
)) p
->nbrwc
=p
->left_pos
;
352 /* Adjust cursor pos */
353 newtop
= adjust_rc(p
->edited
, p
->nbrwc
, &p
->nbc
, newtop
);
354 if(p
->nbrc
!= newtop
)
355 p
->nbrc
= newtop
, draw_info(p
);
357 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
358 return center_horiz(p
);
361 /*** Center the display horizontally to show the cursor ***/
362 LONG
center_horiz(Project p
)
364 /* Return the new left position */
365 if(p
->nbrc
< p
->left_pos
)
366 if(p
->nbrc
> gui
.xstep
) return (LONG
)(p
->nbrc
- gui
.xstep
);
369 if(p
->nbrc
>= p
->left_pos
+gui
.nbcol
)
370 return (LONG
)(p
->nbrc
+ gui
.xstep
- gui
.nbcol
+ 1);
371 else return (LONG
)p
->left_pos
;
374 /*** Center the display vertically to show the cursor ***/
375 LONG
center_vert(Project p
)
377 /* Return the new top position */
378 if(p
->nbl
< p
->top_line
|| p
->nbl
>= p
->top_line
+gui
.nbline
)
380 LONG newtop
=p
->nbl
- (gui
.nbline
>>1) + 4;
381 return newtop
<0 ? 0:newtop
;
383 return (LONG
)p
->top_line
;
386 /*** Low-level text rendering at current rastport position ***/
387 void write_text(Project p
, LINE
*ln
)
390 register UBYTE
*str
,*buf
;
391 register LONG nb
, nbc
, size
= ln
->size
;
392 static LONG stsel
, ensel
;
394 /* Look if line is partially selected */
396 stsel
= (ln
->flags
& FIRSTSEL
? p
->ccp
.startsel
:0),
397 ensel
= (ln
->flags
& LASTSEL
? p
->ccp
.endsel
:0x7FFFFFFF);
398 else stsel
=ensel
=0x7FFFFFFF;
400 /* Find the first char to display */
401 for(str
=ln
->stream
, nb
=p
->left_pos
, nbc
=0; nbc
<nb
; str
++, size
--)
402 if(*str
=='\t') nbc
+=(ts
=tabstop(nbc
));
405 nbc
-=nb
; stsel
-=nb
; ensel
-=nb
;
406 if(nbc
>=0 && size
>=0)
408 /* Tricky case: line begins with an overlapping tabstop */
410 memset(BufTxt
,' ',nbc
);
411 if(stsel
<nbc
) stsel
=(stsel
<=nbc
-ts
? 0:nbc
);
412 if(ensel
<nbc
) ensel
=nbc
;
415 /* Copy the string to a temp buffer */
416 for(nb
=size
, buf
=BufTxt
+nbc
; nbc
<gui
.nbcol
&& nb
; str
++, nb
--)
418 register UBYTE ts
= tabstop(nbc
+p
->left_pos
);
421 if(stsel
<nbc
&& stsel
>nbc
-ts
) stsel
=nbc
;
422 if(ensel
<nbc
&& ensel
>nbc
-ts
) ensel
=nbc
;
423 } else *buf
++ = *str
, nbc
++;
425 /* Overlapping tabulation ? */
426 if(nbc
>=gui
.nbcol
) nbc
=gui
.nbcol
;
429 /* Optimize rendering of unselected line */
430 if(ensel
==stsel
|| stsel
>nbc
|| ensel
<0) Text(RP
,BufTxt
,nbc
);
432 buf
=BufTxt
; nb
=stsel
;
433 if(nb
> 0) Text(RP
,BufTxt
,nb
); else nb
=0;
434 if(ensel
>nbc
) ensel
=nbc
;
435 SetABPenDrMd(RP
,pen
.fgfill
,pen
.bgfill
,JAM2
);
436 Text(RP
,buf
+nb
,ensel
-nb
);
437 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
438 if(ensel
!=nbc
) Text(RP
,buf
+ensel
,nbc
-ensel
);
441 if(clear
&& gui
.right
>RP
->cp_x
) {
442 /* Clear the end of line, like ClearEOL(rp), but without overlapping borders :-( */
443 SetAPen(RP
,pen
.bg
); nb
=RP
->cp_y
-BASEL
;
444 RectFill(RP
,RP
->cp_x
,nb
,gui
.right
,nb
+YSIZE
-1);
449 /*** Delta horizontal scroll, with boundary check ***/
450 void scroll_xdelta(Project p
, LONG x
)
455 if(x
>p
->left_pos
) x
=0; else x
=p
->left_pos
-x
;
456 } else x
+=p
->left_pos
;
457 /* Refresh only if different pos */
460 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
461 scroll_xy(p
, x
, p
->top_line
, FALSE
);
462 /* Is cursor always visible? */
464 curs_visible(p
, p
->top_line
);
465 if(x
!=p
->xcurs
&& p
->ccp
.select
)
466 move_selection(p
, p
->nbrwc
, p
->nbl
);
468 SetAPen(RP
,pen
.fg
); inv_curs(p
,TRUE
);
472 /*** Delta vertical scroll, with boundary check ***/
473 void scroll_ydelta(Project p
, LONG y
)
475 LONG pos
=p
->top_line
+y
;
476 /* Clamp values to the boundary */
480 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
482 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
487 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
488 scroll_xy(p
, curs_visible(p
,pos
), pos
, TRUE
);
489 if(p
->ccp
.select
) move_selection(p
, p
->nbrwc
, p
->nbl
);
494 /*** Like previous, but with required simplifications ***/
495 BOOL
autoscroll(Project p
, WORD y
)
497 LONG pos
=p
->top_line
+y
;
498 /* Clamp values to the boundary */
500 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
502 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
508 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
509 scroll_xy(p
, p
->left_pos
, pos
, TRUE
);
511 curs_visible(p
, p
->top_line
),
518 /*** Redraw part of a display ***/
519 void redraw_content(Project p
, LINE
*ln
, WORD startpos
, WORD nb
)
521 for(SetAPen(RP
,pen
.fg
); nb
-- && ln
; startpos
+= YSIZE
,ln
= ln
->next
)
522 Move(RP
, gui
.left
, startpos
),
525 /* Empty lines visible? */
528 RectFill(RP
,gui
.left
, startpos
-BASEL
, gui
.right
, gui
.bottom
),
532 /*** Scroll to a determinate absolute position ***/
533 void scroll_xy(Project p
, LONG xp
, LONG yp
, BYTE adj
)
535 LONG skipy
= yp
-p
->top_line
,
536 skipx
= xp
-p
->left_pos
, svg
;
538 if(skipy
<0) skipy
= -skipy
;
539 if(skipx
<0) skipx
= -skipx
;
540 /* Can the process be optimized? */
541 if(skipy
< gui
.nbline
&& skipx
< gui
.nbcol
)
543 /* Yes, don't redraw whole display */
544 WORD ystart
,xstart
; LINE
*disp
= p
->show
;
546 xstart
= skipx
* XSIZE
;
547 ystart
= skipy
* YSIZE
;
549 /* 1. Scroll the display */
550 ScrollRaster(RP
, xp
<p
->left_pos
? -xstart
:xstart
, yp
<p
->top_line
? -ystart
:ystart
,
551 gui
.left
, gui
.top
, gui
.rcurs
-1, gui
.bottom
);
553 /* Only performs changes if required */
556 /* 2. Update internal variables if vertical scroll */
559 /* Scroll display down */
560 register LONG nb
=skipy
;
561 for(; nb
--; disp
=disp
->prev
);
562 p
->top_line
-= skipy
;
563 ystart
= gui
.topcurs
;
566 /* Scroll display up */
567 register LONG nb
=skipy
;
568 p
->top_line
+= skipy
;
569 ystart
= gui
.botcurs
- ystart
+ YSIZE
;
570 for(; nb
--; disp
=disp
->next
);
573 for(nb
=gui
.nbline
- skipy
; disp
&& nb
--; disp
=disp
->next
);
580 /* 3. Update variables if horizontal scroll */
586 /* Scroll display right */
590 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
593 /* Scroll display left */
594 register LONG left
=gui
.left
;
596 gui
.left
= gui
.rcurs
-xstart
;
598 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
604 /* 4. Redraw display (needs previous changes before) */
605 if(skipy
) redraw_content(p
, disp
, ystart
, skipy
);
608 /* We've jump too far, whole redraw */
609 register LONG nb
= yp
-p
->top_line
;
610 register LINE
*disp
= p
->show
;
612 if(nb
< 0) for(; nb
++; disp
=disp
->prev
);
613 else for(; nb
--; disp
=disp
->next
);
616 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;
617 redraw_content(p
, disp
, gui
.topcurs
, gui
.nbline
);
619 /* Adjust position of the prop gadget */
623 /*** Scroll lines up, at a specified position (used for deleting) ***/
624 void scroll_up(Project p
, LINE
*ln
, WORD ystart
, LONG leftpos
)
627 /* If some of redrawn lines are still visible */
628 if( leftpos
== p
->left_pos
)
629 /* Optimize lines to refresh */
630 nbl
= p
->top_line
+ gui
.nbline
- p
->nbl
- 1,
633 /* Redraw whole content */
634 nbl
= gui
.nbline
, ystart
= gui
.topcurs
, ln
=p
->show
,
635 p
->left_pos
= leftpos
;
637 redraw_content(p
, ln
, ystart
, nbl
);
638 /* Adjust cursor position */
639 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;