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 */
26 #define CATCOMP_NUMBERS /* We will need the string id */
29 /* Linked list of opened project */
31 ULONG sigbits
=0, sigrcvd
, sigport
, err_time
;
32 UBYTE clear
=TRUE
, BufTxt
[256];
33 pfnSelectFunc move_selection
;
35 /* Static string for version command */
36 const char Version
[]=SVER
;
38 /* Shared libraires we'll need to open */
39 struct IntuitionBase
*IntuitionBase
= NULL
;
40 struct GfxBase
*GfxBase
= NULL
;
41 struct Library
*KeymapBase
= NULL
;
42 struct Library
*GadToolsBase
= NULL
;
43 struct Library
*AslBase
= NULL
;
44 struct LocaleBase
*LocaleBase
= NULL
;
45 struct Library
*DiskfontBase
= NULL
;
46 struct UtilityBase
*UtilityBase
= NULL
;
47 struct Library
*IFFParseBase
= NULL
;
49 struct IntuiMessage
*msg
, msgbuf
; /* Used to collect events */
58 int main(int argc
, char *argv
[])
61 bmem
= AvailMem( MEMF_PUBLIC
);
64 ParseArgs(&args
, argc
, argv
);
66 /* Look first if Jano isn't already running */
67 if( find_janoed( &args
) ) cleanup(0, RETURN_OK
);
69 /* Some global initialization */
72 /* Optionnal libraries */
73 AslBase
= (struct Library
*) OpenLibrary("asl.library", 36);
74 LocaleBase
= (struct LocaleBase
*) OpenLibrary("locale.library", 38);
75 DiskfontBase
= (struct Library
*) OpenLibrary("diskfont.library", 0);
76 IFFParseBase
= (struct Library
*) OpenLibrary("iffparse.library",36);
78 if(LocaleBase
) InitLocale(); /* Localize the prog */
80 /* Open the required ROM libraries */
81 if( (IntuitionBase
= (struct IntuitionBase
*) OpenLibrary("intuition.library", 36)) &&
82 (GfxBase
= (struct GfxBase
*) OpenLibrary("graphics.library", 36)) &&
83 (GadToolsBase
= (struct Library
*) OpenLibrary("gadtools.library", 36)) &&
84 (KeymapBase
= (struct Library
*) OpenLibrary("keymap.library", 36)) &&
85 (UtilityBase
= (struct UtilityBase
*) OpenLibrary("utility.library", 36)) )
90 set_default_prefs(&prefs
, IntuitionBase
->ActiveScreen
);
93 load_prefs(&prefs
, NULL
); /* See if it exists a config file */
94 sigport
= create_port();
96 /* Create whether an empty project or an existing one */
97 if( ( edit
= create_projects(NULL
, args
.sa_ArgLst
, args
.sa_NbArgs
) ) )
99 /* Open the main interface */
102 /* Makes edit project visible */
104 active_project(edit
,TRUE
);
109 } else cleanup(ErrMsg(ERR_NOGUI
), RETURN_FAIL
);
111 } else cleanup(ErrMsg(ERR_BADOS
), RETURN_FAIL
);
113 /* Hope that all were well... */
114 cleanup(0, RETURN_OK
);
119 /*** Deallocate ressources properly ***/
120 void cleanup(UBYTE
*msg
, int errcode
)
127 free_diskio_alloc(); /* ASL */
128 if(args
.sa_Free
) FreeVec(args
.sa_ArgLst
);
129 if(DiskfontBase
) CloseLibrary(DiskfontBase
);
130 if(LocaleBase
) CloseLibrary((struct Library
*)LocaleBase
);
131 if(AslBase
) CloseLibrary(AslBase
);
132 if(IFFParseBase
) CloseLibrary(IFFParseBase
);
133 if(UtilityBase
) CloseLibrary((struct Library
*)UtilityBase
);
134 if(KeymapBase
) CloseLibrary(KeymapBase
);
135 if(GadToolsBase
) CloseLibrary(GadToolsBase
);
136 if(GfxBase
) CloseLibrary((struct Library
*)GfxBase
);
137 if(IntuitionBase
) CloseLibrary((struct Library
*)IntuitionBase
);
141 /* Here can be compared good programs with the others :-) */
142 amem
= AvailMem( MEMF_PUBLIC
);
143 if(amem
< bmem
) printf("Possible memory lost of %d bytes\n", bmem
-amem
);
148 /*** Handle events coming from main window: ***/
149 void dispatch_events()
151 extern ULONG sigmainwnd
, swinsig
;
153 BYTE scrolldisp
=0, state
=0, cnt
=0, mark
=0, quit
= 0;
157 /* Active collect, when pressing arrow gadgets */
158 sigrcvd
= (state
==0 ? Wait(sigbits
) : sigmainwnd
);
160 /* if(sigrcvd & SIGBREAKF_CTRL_C) break;
162 else */ if(sigrcvd
& sigport
) { handle_port(); continue; }
164 else if(sigrcvd
& swinsig
) { handle_search(); continue; }
166 /* Collect messages posted to the window port */
167 while( ( msg
= (struct IntuiMessage
*) GetMsg(Wnd
->UserPort
) ) )
169 /* Copy the entire message into the buffer */
170 CopyMemQuick(msg
, &msgbuf
, sizeof(msgbuf
));
171 ReplyMsg( (struct Message
*) msg
);
173 switch( msgbuf
.Class
)
175 case IDCMP_CLOSEWINDOW
: handle_menu(112); break;
179 if(record
== 1) reg_act_com(MAC_ACT_SHORTCUT
, msgbuf
.Code
, msgbuf
.Qualifier
);
183 case IDCMP_INTUITICKS
:
184 /* An error message which needs to be removed? */
185 if(err_time
== 0) err_time
= msgbuf
.Seconds
;
186 if(err_time
&& msgbuf
.Seconds
-err_time
>4) StopError(Wnd
);
188 case IDCMP_MOUSEBUTTONS
:
189 /* Click somewhere in the text */
190 switch( msgbuf
.Code
)
193 /* Click over the project bar ? */
194 if(msgbuf
.MouseY
< gui
.top
)
196 edit
= select_panel(edit
, msgbuf
.MouseX
);
200 click(edit
, msgbuf
.MouseX
, msgbuf
.MouseY
, FALSE
);
202 /* Shift-click to use columnar selection */
203 if( ( move_selection
= SwitchSelect(edit
, msgbuf
.Qualifier
& SHIFTKEYS
? 1:0, 1) ) )
207 if(mark
) unclick(edit
);
208 mark
=FALSE
; scrolldisp
=0; break;
214 case IDCMP_GADGETDOWN
: /* Left scroll bar */
215 if(msgbuf
.IAddress
== (APTR
) &Prop
->down
) state
=1;
216 if(msgbuf
.IAddress
== (APTR
) &Prop
->up
) state
=2;
218 case IDCMP_GADGETUP
: /* Arrows or prop gadget */
220 if(msgbuf
.IAddress
== (APTR
) Prop
)
221 scroll_disp(edit
, FALSE
), scrolldisp
=0;
223 case IDCMP_MOUSEMOVE
:
224 if(mark
) scrolldisp
=2;
226 if(Prop
->scroller
.Flags
& GFLG_SELECTED
) scrolldisp
=1;
229 { struct MenuItem
* Item
;
232 /* Multi-selection of menu entries */
233 while(msgbuf
.Code
!= MENUNULL
)
234 if( (Item
= ItemAddress( Menu
, msgbuf
.Code
)) )
236 /* stegerg: get NextSelect here in case menu action causes screen
237 to be closed/reopened in which case item becomes invalid.
238 Also assuming here that user in such case will not use
239 multiselection, ie. that nextselect will be MENUNULL.
241 If that's not the case it would mean more trouble and to protect
242 against that one would need to check if during handle_menu() the
243 screen has been closed/reopened and in that case break out of
244 the menu multiselection loop here. */
246 UWORD nextselect
= Item
->NextSelect
;
248 MenuId
= (IPTR
)GTMENUITEM_USERDATA( Item
);
249 handle_menu( MenuId
);
251 if(record
) reg_act_com(MAC_ACT_COM_MENU
, MenuId
, msgbuf
.Qualifier
);
254 msgbuf
.Code
= nextselect
;
259 /* Reduces the number of IDCMP mousemove messages to process */
260 if(scrolldisp
==1) scroll_disp(edit
, FALSE
), scrolldisp
=0;
261 if(scrolldisp
==2) { scrolldisp
=0; goto moveit
; }
263 /* User may want to auto-scroll the display using arrow gadgets */
264 if(state
&& (mark
|| (((struct Gadget
*)Prop
)[state
].Flags
& GFLG_SELECTED
))) {
265 /* Slow down animation: */
269 if(autoscroll(edit
,state
==1 ? 1:-1)==0) state
=0;
272 /* Adjust mouse position */
273 x
= (msgbuf
.MouseX
-gui
.left
) / XSIZE
;
274 y
= (msgbuf
.MouseY
-gui
.top
) / YSIZE
;
283 edit
->nbrwc
= (x
+= edit
->left_pos
);
284 y
+= (LONG
)edit
->top_line
;
285 if( x
!= edit
->ccp
.xc
|| y
!= edit
->ccp
.yc
)
286 /* Move the selected stream */
287 if( !(state
= move_selection(edit
,x
,y
)) )
288 set_cursor_line(edit
, y
, edit
->top_line
),
292 } /* endif: arrow gadget pressed or autoscroll */
296 /*** Refresh display, according to new window size ***/
297 void new_size(UBYTE Flags
)
299 inv_curs(edit
, FALSE
);
300 adjust_win(Wnd
,NbProject
>1); /* Adjust internal variables */
301 SetABPenDrMd(RP
, pen
.fg
, pen
.bg
, JAM2
);
304 edit
->left_pos
= curs_visible(edit
, edit
->top_line
);
305 edit
->xcurs
= (edit
->nbrc
- edit
->left_pos
)*XSIZE
+ gui
.left
;
306 if(Flags
& EDIT_GUI
) reshape_panel(edit
);
307 if(Flags
& EDIT_AREA
) redraw_content(edit
,edit
->show
,gui
.topcurs
,gui
.nbline
);
311 /*** Scroll display according to right prop gadget ***/
312 void scroll_disp(Project p
, BOOL adjust
)
314 ULONG pos
= ((struct PropInfo
*)((struct Gadget
*)Prop
)->SpecialInfo
)->VertPot
*
315 (p
->max_lines
- gui
.nbline
) / MAXPOT
;
317 if(p
->max_lines
>gui
.nbline
&& pos
!=p
->top_line
)
320 /* If selection mode is on, don't move cursor */
321 p
->ycurs
-=(pos
-p
->top_line
)*YSIZE
,
322 scroll_xy(p
, p
->left_pos
, pos
, adjust
);
324 /* Be sure cursor is always in the edit area */
326 scroll_xy(p
, curs_visible(p
,pos
), pos
, adjust
),
331 /*** Be sure that cursor is always visible ***/
332 LONG
curs_visible(Project p
, LONG newtop
)
334 if(p
->nbl
< newtop
) {
335 /* The cursor is above the top line */
336 p
->ycurs
= gui
.topcurs
;
339 } else if(p
->nbl
>= newtop
+gui
.nbline
) {
343 /* The cursor is below the bottom line */
344 p
->nbl
= newtop
+gui
.nbline
-1;
345 p
->ycurs
= gui
.botcurs
;
348 for(ln
=p
->the_line
, nb
=p
->nbl
; nb
--; ln
=ln
->next
);
353 /* Is between the top and bottom line */
354 p
->ycurs
= (p
->nbl
-newtop
) * YSIZE
+ gui
.topcurs
;
356 /* Jump too far to the right? */
357 newtop
=p
->left_pos
+ gui
.nbcol
- 1;
358 if(p
->nbrc
>newtop
) p
->nbrwc
=newtop
;
359 /* Too far on the left */
360 if((newtop
= p
->nbrc
<p
->left_pos
)) p
->nbrwc
=p
->left_pos
;
362 /* Adjust cursor pos */
363 newtop
= adjust_rc(p
->edited
, p
->nbrwc
, &p
->nbc
, newtop
);
364 if(p
->nbrc
!= newtop
)
365 p
->nbrc
= newtop
, draw_info(p
);
367 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
368 return center_horiz(p
);
371 /*** Center the display horizontally to show the cursor ***/
372 LONG
center_horiz(Project p
)
374 /* Return the new left position */
375 if(p
->nbrc
< p
->left_pos
)
376 if(p
->nbrc
> gui
.xstep
) return (LONG
)(p
->nbrc
- gui
.xstep
);
379 if(p
->nbrc
>= p
->left_pos
+gui
.nbcol
)
380 return (LONG
)(p
->nbrc
+ gui
.xstep
- gui
.nbcol
+ 1);
381 else return (LONG
)p
->left_pos
;
384 /*** Center the display vertically to show the cursor ***/
385 LONG
center_vert(Project p
)
387 /* Return the new top position */
388 if(p
->nbl
< p
->top_line
|| p
->nbl
>= p
->top_line
+gui
.nbline
)
390 LONG newtop
=p
->nbl
- (gui
.nbline
>>1) + 4;
391 return newtop
<0 ? 0:newtop
;
393 return (LONG
)p
->top_line
;
396 /*** Low-level text rendering at current rastport position ***/
397 void write_text(Project p
, LINE
*ln
)
400 register UBYTE
*str
,*buf
;
401 register LONG nb
, nbc
, size
= ln
->size
;
402 static LONG stsel
, ensel
;
404 /* Look if line is partially selected */
406 stsel
= (ln
->flags
& FIRSTSEL
? p
->ccp
.startsel
:0),
407 ensel
= (ln
->flags
& LASTSEL
? p
->ccp
.endsel
:0x7FFFFFFF);
408 else stsel
=ensel
=0x7FFFFFFF;
410 /* Find the first char to display */
411 for(str
=ln
->stream
, nb
=p
->left_pos
, nbc
=0; nbc
<nb
; str
++, size
--)
412 if(*str
=='\t') nbc
+=(ts
=tabstop(nbc
));
415 nbc
-=nb
; stsel
-=nb
; ensel
-=nb
;
416 if(nbc
>=0 && size
>=0)
418 /* Tricky case: line begins with an overlapping tabstop */
420 memset(BufTxt
,' ',nbc
);
421 if(stsel
<nbc
) stsel
=(stsel
<=nbc
-ts
? 0:nbc
);
422 if(ensel
<nbc
) ensel
=nbc
;
425 /* Copy the string to a temp buffer */
426 for(nb
=size
, buf
=BufTxt
+nbc
; nbc
<gui
.nbcol
&& nb
; str
++, nb
--)
428 register UBYTE ts
= tabstop(nbc
+p
->left_pos
);
431 if(stsel
<nbc
&& stsel
>nbc
-ts
) stsel
=nbc
;
432 if(ensel
<nbc
&& ensel
>nbc
-ts
) ensel
=nbc
;
433 } else *buf
++ = *str
, nbc
++;
435 /* Overlapping tabulation ? */
436 if(nbc
>=gui
.nbcol
) nbc
=gui
.nbcol
;
439 /* Optimize rendering of unselected line */
440 if(ensel
==stsel
|| stsel
>nbc
|| ensel
<0) Text(RP
,BufTxt
,nbc
);
442 buf
=BufTxt
; nb
=stsel
;
443 if(nb
> 0) Text(RP
,BufTxt
,nb
); else nb
=0;
444 if(ensel
>nbc
) ensel
=nbc
;
445 SetABPenDrMd(RP
,pen
.fgfill
,pen
.bgfill
,JAM2
);
446 Text(RP
,buf
+nb
,ensel
-nb
);
447 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
448 if(ensel
!=nbc
) Text(RP
,buf
+ensel
,nbc
-ensel
);
451 if(clear
&& gui
.right
>RP
->cp_x
) {
452 /* Clear the end of line, like ClearEOL(rp), but without overlapping borders :-( */
453 SetAPen(RP
,pen
.bg
); nb
=RP
->cp_y
-BASEL
;
454 RectFill(RP
,RP
->cp_x
,nb
,gui
.right
,nb
+YSIZE
-1);
459 /*** Delta horizontal scroll, with boundary check ***/
460 void scroll_xdelta(Project p
, LONG x
)
465 if(x
>p
->left_pos
) x
=0; else x
=p
->left_pos
-x
;
466 } else x
+=p
->left_pos
;
467 /* Refresh only if different pos */
470 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
471 scroll_xy(p
, x
, p
->top_line
, FALSE
);
472 /* Is cursor always visible? */
474 curs_visible(p
, p
->top_line
);
475 if(x
!=p
->xcurs
&& p
->ccp
.select
)
476 move_selection(p
, p
->nbrwc
, p
->nbl
);
478 SetAPen(RP
,pen
.fg
); inv_curs(p
,TRUE
);
482 /*** Delta vertical scroll, with boundary check ***/
483 void scroll_ydelta(Project p
, LONG y
)
485 LONG pos
=p
->top_line
+y
;
486 /* Clamp values to the boundary */
490 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
492 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
497 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
498 scroll_xy(p
, curs_visible(p
,pos
), pos
, TRUE
);
499 if(p
->ccp
.select
) move_selection(p
, p
->nbrwc
, p
->nbl
);
504 /*** Like previous, but with required simplifications ***/
505 BOOL
autoscroll(Project p
, WORD y
)
507 LONG pos
=p
->top_line
+y
;
508 /* Clamp values to the boundary */
510 if(pos
>(LONG
)p
->max_lines
-(LONG
)gui
.nbline
) pos
=p
->max_lines
-gui
.nbline
;
512 if(pos
>p
->max_lines
-1) pos
=p
->max_lines
-1;
518 if(!p
->ccp
.select
) inv_curs(p
,FALSE
);
519 scroll_xy(p
, p
->left_pos
, pos
, TRUE
);
521 curs_visible(p
, p
->top_line
),
528 /*** Redraw part of a display ***/
529 void redraw_content(Project p
, LINE
*ln
, WORD startpos
, WORD nb
)
531 for(SetAPen(RP
,pen
.fg
); nb
-- && ln
; startpos
+= YSIZE
,ln
= ln
->next
)
532 Move(RP
, gui
.left
, startpos
),
535 /* Empty lines visible? */
538 RectFill(RP
,gui
.left
, startpos
-BASEL
, gui
.right
, gui
.bottom
),
542 /*** Scroll to a determinate absolute position ***/
543 void scroll_xy(Project p
, LONG xp
, LONG yp
, BYTE adj
)
545 LONG skipy
= yp
-p
->top_line
,
546 skipx
= xp
-p
->left_pos
, svg
;
548 if(skipy
<0) skipy
= -skipy
;
549 if(skipx
<0) skipx
= -skipx
;
550 /* Can the process be optimized? */
551 if(skipy
< gui
.nbline
&& skipx
< gui
.nbcol
)
553 /* Yes, don't redraw whole display */
554 WORD ystart
,xstart
; LINE
*disp
= p
->show
;
556 xstart
= skipx
* XSIZE
;
557 ystart
= skipy
* YSIZE
;
559 /* 1. Scroll the display */
560 ScrollRaster(RP
, xp
<p
->left_pos
? -xstart
:xstart
, yp
<p
->top_line
? -ystart
:ystart
,
561 gui
.left
, gui
.top
, gui
.rcurs
-1, gui
.bottom
);
563 /* Only performs changes if required */
566 /* 2. Update internal variables if vertical scroll */
569 /* Scroll display down */
570 register LONG nb
=skipy
;
571 for(; nb
--; disp
=disp
->prev
);
572 p
->top_line
-= skipy
;
573 ystart
= gui
.topcurs
;
576 /* Scroll display up */
577 register LONG nb
=skipy
;
578 p
->top_line
+= skipy
;
579 ystart
= gui
.botcurs
- ystart
+ YSIZE
;
580 for(; nb
--; disp
=disp
->next
);
583 for(nb
=gui
.nbline
- skipy
; disp
&& nb
--; disp
=disp
->next
);
590 /* 3. Update variables if horizontal scroll */
596 /* Scroll display right */
600 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
603 /* Scroll display left */
604 register LONG left
=gui
.left
;
606 gui
.left
= gui
.rcurs
-xstart
;
608 redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
614 /* 4. Redraw display (needs previous changes before) */
615 if(skipy
) redraw_content(p
, disp
, ystart
, skipy
);
618 /* We've jump too far, whole redraw */
619 register LONG nb
= yp
-p
->top_line
;
620 register LINE
*disp
= p
->show
;
622 if(nb
< 0) for(; nb
++; disp
=disp
->prev
);
623 else for(; nb
--; disp
=disp
->next
);
626 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;
627 redraw_content(p
, disp
, gui
.topcurs
, gui
.nbline
);
629 /* Adjust position of the prop gadget */
633 /*** Scroll lines up, at a specified position (used for deleting) ***/
634 void scroll_up(Project p
, LINE
*ln
, WORD ystart
, LONG leftpos
)
637 /* If some of redrawn lines are still visible */
638 if( leftpos
== p
->left_pos
)
639 /* Optimize lines to refresh */
640 nbl
= p
->top_line
+ gui
.nbline
- p
->nbl
- 1,
643 /* Redraw whole content */
644 nbl
= gui
.nbline
, ystart
= gui
.topcurs
, ln
=p
->show
,
645 p
->left_pos
= leftpos
;
647 redraw_content(p
, ln
, ystart
, nbl
);
648 /* Adjust cursor position */
649 p
->xcurs
= (p
->nbrc
-p
->left_pos
) * XSIZE
+ gui
.left
;