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)) )
89 set_default_prefs(&prefs
, IntuitionBase
->ActiveScreen
);
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 */
101 /* Makes edit project visible */
103 active_project(edit
,TRUE
);
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
);
118 /*** Deallocate ressources properly ***/
119 void cleanup(UBYTE
*msg
, int errcode
)
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
);
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
);
147 /*** Handle events coming from main window: ***/
148 void dispatch_events()
150 extern ULONG sigmainwnd
, swinsig
;
152 BYTE scrolldisp
=0, state
=0, cnt
=0, mark
=0, 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;
178 if(record
== 1) reg_act_com(MAC_ACT_SHORTCUT
, msgbuf
.Code
, msgbuf
.Qualifier
);
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
);
187 case IDCMP_MOUSEBUTTONS
:
188 /* Click somewhere in the text */
189 switch( msgbuf
.Code
)
192 /* Click over the project bar ? */
193 if(msgbuf
.MouseY
< gui
.top
)
195 edit
= select_panel(edit
, msgbuf
.MouseX
);
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) ) )
206 if(mark
) unclick(edit
);
207 mark
=FALSE
; scrolldisp
=0; 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;
217 case IDCMP_GADGETUP
: /* Arrows or prop gadget */
219 if(msgbuf
.IAddress
== (APTR
) Prop
)
220 scroll_disp(edit
, FALSE
), scrolldisp
=0;
222 case IDCMP_MOUSEMOVE
:
223 if(mark
) scrolldisp
=2;
225 if(Prop
->scroller
.Flags
& GFLG_SELECTED
) scrolldisp
=1;
228 { struct MenuItem
* Item
;
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
);
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: */
268 if(autoscroll(edit
,state
==1 ? 1:-1)==0) state
=0;
271 /* Adjust mouse position */
272 x
= (msgbuf
.MouseX
-gui
.left
) / XSIZE
;
273 y
= (msgbuf
.MouseY
-gui
.top
) / YSIZE
;
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
),
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
);
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
);
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
)
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
);
323 /* Be sure cursor is always in the edit area */
325 scroll_xy(p
, curs_visible(p
,pos
), pos
, adjust
),
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
;
338 } else if(p
->nbl
>= newtop
+gui
.nbline
) {
342 /* The cursor is below the bottom line */
343 p
->nbl
= newtop
+gui
.nbline
-1;
344 p
->ycurs
= gui
.botcurs
;
347 for(ln
=p
->the_line
, nb
=p
->nbl
; nb
--; ln
=ln
->next
);
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
);
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
)
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 */
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
));
414 nbc
-=nb
; stsel
-=nb
; ensel
-=nb
;
415 if(nbc
>=0 && size
>=0)
417 /* Tricky case: line begins with an overlapping tabstop */
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
--)
427 register UBYTE ts
= tabstop(nbc
+p
->left_pos
);
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
;
438 /* Optimize rendering of unselected line */
439 if(ensel
==stsel
|| stsel
>nbc
|| ensel
<0) Text(RP
,BufTxt
,nbc
);
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);
458 /*** Delta horizontal scroll, with boundary check ***/
459 void scroll_xdelta(Project p
, LONG 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 */
469 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
470 scroll_xy(p
, x
, p
->top_line
, FALSE
);
471 /* Is cursor always visible? */
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 */
489 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
491 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
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
);
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 */
509 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
511 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
517 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
518 scroll_xy(p
, p
->left_pos
, pos
, TRUE
);
520 curs_visible(p
, p
->top_line
),
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
),
534 /* Empty lines visible? */
537 RectFill(RP
,gui
.left
, startpos
-BASEL
, gui
.right
, gui
.bottom
),
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 */
565 /* 2. Update internal variables if vertical scroll */
568 /* Scroll display down */
569 register LONG nb
=skipy
;
570 for(; nb
--; disp
=disp
->prev
);
571 p
->top_line
-= skipy
;
572 ystart
= gui
.topcurs
;
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
);
582 for(nb
=gui
.nbline
- skipy
; disp
&& nb
--; disp
=disp
->next
);
589 /* 3. Update variables if horizontal scroll */
595 /* Scroll display right */
599 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
602 /* Scroll display left */
603 register LONG left
=gui
.left
;
605 gui
.left
= gui
.rcurs
-xstart
;
607 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
613 /* 4. Redraw display (needs previous changes before) */
614 if(skipy
) redraw_content(p
, disp
, ystart
, skipy
);
617 /* We've jump too far, whole redraw */
618 register LONG nb
= yp
-p
->top_line
;
619 register LINE
*disp
= p
->show
;
621 if(nb
< 0) for(; nb
++; disp
=disp
->prev
);
622 else for(; nb
--; disp
=disp
->next
);
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 */
632 /*** Scroll lines up, at a specified position (used for deleting) ***/
633 void scroll_up(Project p
, LINE
*ln
, WORD ystart
, LONG leftpos
)
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,
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
;