1 /**************************************************************
2 **** Project.c: handle project related information. ****
3 **** Free software under GNU license, started on 16/2/2000 ****
4 **** © T.Pierron, C.Guillaume. ****
5 **************************************************************/
9 #include <graphics/rastport.h>
10 #include <exec/memory.h>
11 #include <libraries/asl.h>
12 #include <workbench/startup.h>
20 #include "ProtoTypes.h"
23 #define CATCOMP_NUMBERS /* We will need the string id */
27 #include <aros/debug.h>
29 static Project first
= NULL
; /* Keep track of first created project */
30 UBYTE NbProject
= 0; /* Number of opened projects */
33 /*** Alloc a new project ***/
34 Project
new_project(Project ins
, PREFS
*prefs
)
37 if( ( new = (void *) AllocVec(sizeof(*new), MEMF_PUBLIC
| MEMF_CLEAR
) ) )
39 /* Always need at least one line */
40 if( ( new->show
= create_line(NULL
,0) ) )
42 InsertAfter(ins
, new);
43 /* Local preferences herited */
44 new->tabsize
= prefs
->tabsize
;
47 /* The line shown/edited */
48 if(NbProject
== 1) first
= new;
49 new->show
->prev
= new->show
->next
= NULL
;
50 new->the_line
= new->edited
= new->show
;
51 new->ccp
.xp
= (ULONG
)-1;
53 new->undo
.prj
= new->redo
.prj
= (APTR
) new;
56 set_project_name(new, NULL
);
58 else FreeVec(new),new = NULL
;
63 /*** Change content of a project ***/
64 void change_project(Project p
, LINE
*new)
66 flush_undo_buf( &p
->undo
);
67 flush_undo_buf( &p
->redo
);
68 trash_file(p
->the_line
);
69 if(p
->buffer
) FreeVec(p
->buffer
);
70 memset(&p
->nbc
, 0, 4*sizeof(ULONG
));
71 p
->edited
= p
->the_line
= p
->show
= new;
72 p
->top_line
= p
->left_pos
= 0;
74 p
->ycurs
= gui
.topcurs
;
79 /*** Try to load the file contained in the project ***/
80 WORD
load_in_project( Project p
, STRPTR path
)
87 if(RETURN_OK
== (err
= load_file( &args
)))
89 /* File successfully loaded */
90 change_project(p
, args
.lines
);
91 p
->buffer
= args
.buffer
;
92 p
->max_lines
= args
.nblines
;
94 p
->protection
= args
.protection
&= ~FIBF_ARCHIVE
;
96 else ThrowError(Wnd
, ErrMsg(err
));
101 /*** Load and create a new project from a path ***/
102 Project
load_and_activate(Project ins
, STRPTR name
, BYTE use_prj
)
106 if( ( new = (use_prj
? ins
: new_project(ins
, &prefs
)) ) )
110 /* Multi-selection: name is actually a (StartUpArgs *) */
111 if( ((StartUpArgs
*)name
)->sa_NbArgs
> 0 )
113 new = create_projects(ins
, ((StartUpArgs
*)name
)->sa_ArgLst
, ((StartUpArgs
*)name
)->sa_NbArgs
);
115 inv_curs(ins
, FALSE
);
116 if(use_prj
== 2) close_project(ins
), FreeVec(ins
);
122 else /* Load a single file into `new' */
124 inv_curs(ins
, FALSE
);
125 /* We have a correct path, try to load file */
126 if( RETURN_OK
== load_in_project(new, name
) )
128 set_project_name(new, name
);
129 /* Add a panel tab and show content of file */
130 if(use_prj
) update_panel_name(ins
);
131 else refresh
:reshape_panel(new);
132 active_project(new, TRUE
);
136 /* Something failed, close project */
137 if(use_prj
== 0) close_project(new),FreeVec(new);
140 else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
144 /*** Load and create a new project from a path ***/
145 Project
load_and_activate_fr(Project ins
, APTR name
, BYTE use_prj
)
147 struct FileRequester
*fr
= (struct FileRequester
*) name
;
150 if( ( new = (use_prj
? ins
: new_project(ins
, &prefs
)) ) )
154 /* Multi-selection */
155 if( fr
->fr_NumArgs
> 0 )
157 new = create_projects(ins
, fr
->fr_ArgList
, fr
->fr_NumArgs
);
159 inv_curs(ins
, FALSE
);
160 if(use_prj
== 2) close_project(ins
), FreeVec(ins
);
166 else /* Load a single file into `new' */
168 inv_curs(ins
, FALSE
);
169 /* We have a correct path, try to load file */
170 if( RETURN_OK
== load_in_project(new, fr
->fr_ArgList
[0].wa_Name
) )
172 set_project_name(new, fr
->fr_ArgList
[0].wa_Name
);
173 /* Add a panel tab and show content of file */
174 if(use_prj
) update_panel_name(ins
);
175 else refresh
:reshape_panel(new);
176 active_project(new, TRUE
);
180 /* Something failed, close project */
181 if(use_prj
== 0) close_project(new),FreeVec(new);
184 else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
188 /*** Reload project, flush redolog, reduce fragmentation ***/
189 void reload_project( Project p
)
191 /* Save cursor position */
192 ULONG nbl
= p
->nbl
, top
= p
->top_line
;
194 /* Flush all buffers and reload file */
195 unset_modif_mark(p
, TRUE
);
197 load_in_project(p
, p
->path
);
199 /* Reset cursor position, if possible */
200 p
->nbl
= nbl
; p
->top_line
= top
; top
= center_vert( p
);
201 p
->top_line
= p
->nbl
= 0;
202 set_top_line(p
, top
, 0);
203 set_cursor_line(p
, nbl
, p
->top_line
);
207 /*** Insert a file at current cursor position ***/
208 void insert_file( Project p
)
214 if( ( args
.filename
= ask_load(Wnd
, (AskArgs
*)&p
->path
, TRUE
, GetMenuText(208)) ) )
216 if( 0 == (err
= read_file( &args
, &length
)) )
219 reg_group_by(&p
->undo
);
220 if( add_string( &p
->undo
, p
->edited
, p
->nbc
, args
.buffer
, length
, pos
) )
222 /* Just one line concerned? */
223 p
->max_lines
+= pos
[2];
224 if( pos
[1] == 0 ) REDRAW_CURLINE(p
)
226 move_cursor(p
, pos
[0], pos
[1]);
227 if( pos
[1] > 0 ) redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
228 if( p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
229 inv_curs(p
,TRUE
); prop_adj(p
);
231 else ThrowDOSError(Wnd
, args
.filename
);
233 if( args
.buffer
) FreeVec( args
.buffer
);
235 reg_group_by(&p
->undo
);
237 else ThrowError(Wnd
, ErrMsg( err
));
239 FreeVec( args
.filename
);
243 /*** Change and set internal project name variables ***/
244 void set_project_name( Project p
, STRPTR path
)
246 if(p
->path
!= NULL
) FreeVec(p
->path
);
248 p
->name
= (STRPTR
) FilePart(
252 p
->name
= ErrMsg(ERR_NONAME
);
253 p
->labsize
= strlen(p
->name
);
256 /*** Print one project ***/
257 char print_project(Project p
)
259 return print_file(p
->the_line
, p
->eol
);
262 /*** Save one project ***/
263 char save_project(Project p
, char refresh
, char ask
)
267 /* Ask for a name if file doesn't have one */
268 if(p
->path
== NULL
|| ask
)
271 if(NULL
!= (newname
= ask_save(Wnd
, (AskArgs
*)&p
->path
, GetMenuText(105))) &&
272 0 != warn_overwrite( newname
))
273 set_project_name(p
, newname
);
275 /* User cancelled or there was an error */
282 UpdateTitle(Wnd
, p
), update_panel_name( p
);
285 retval
= save_file(p
->path
, p
->the_line
, p
->eol
, p
->protection
);
289 unset_modif_mark(p
, TRUE
);
295 /*** Save all modified projects ***/
296 Project
save_projects(Project active
, char close
)
298 Project cur
, p
; int nb
= 0;
301 /* Auto-save modified project */
302 if(p
->state
& MODIFIED
) {
303 if(save_project(p
, FALSE
, FALSE
) == 0) break; else nb
++;
304 if(p
== active
&& close
== 0) UpdateTitle(Wnd
, p
);
309 cur
= p
; close_project(p
);
310 p
= p
->next
; FreeVec(cur
);
317 if(NbProject
> 0 && nb
> 0) reshape_panel(active
);
321 /*** Makes project the active one (i.e. visible) ***/
322 char active_project(Project p
, char InitCurs
)
324 /* If file isn't loaded yet, makes it now */
325 if(p
->state
& PAGINATED
)
326 /* This call is subject to failed! */
327 switch( load_in_project(p
, p
->path
) )
329 case RETURN_OK
: p
->state
&= ~PAGINATED
; break;
331 /* This is too nasty to attempt something: close faulty project */
332 close_project(p
); FreeVec(p
); return 0;
336 init_tabstop(p
->tabsize
);
338 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
340 p
->ycurs
= gui
.topcurs
,
343 p
->left_pos
= curs_visible(p
, p
->top_line
),
344 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
346 /* This can speedup a lot, text rendering */
347 if(p
->ccp
.select
!= 0) RP
->Mask
= gui
.selmask
;
348 redraw_content(p
,p
->show
,gui
.topcurs
,gui
.nbline
);
349 if(p
->ccp
.select
== 0) RP
->Mask
= gui
.txtmask
;
355 /*** Test if a string represent an AmigaDOS pattern ***/
356 STRPTR
IsPat( STRPTR name
)
358 int len
= strlen( name
) * 2 + 2;
360 /* 512 bytes is used for ExAll buffer scan */
361 if((tok
= (STRPTR
) AllocVec(len
+ 512, MEMF_CLEAR
)))
363 if( ParsePatternNoCase(name
, tok
+512, len
) > 0 )
372 /*** Create a new project with an incomplete path ***/
373 UBYTE
new_file(Project
*ins
, STRPTR path
)
376 if(NULL
!= (new = new_project(*ins
, &prefs
)))
379 if(RETURN_OK
== (err
= get_full_path(path
, &new->name
))) {
380 set_project_name(new, new->name
); *ins
= new;
381 new->state
= PAGINATED
;
384 ThrowError(Wnd
, ErrMsg(err
));
388 ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
392 /*** Create a set of projects (command line or WBStartup) ***/
393 Project
create_projects(Project ins_after
, APTR args
, ULONG nb
)
397 APTR cwd
= (APTR
) CurrentDir( BNULL
);
401 register struct WBArg
*arg
= (struct WBArg
*)args
;
403 for(new = ins_after
; nb
--; arg
++ )
405 CurrentDir(arg
->wa_Lock
);
406 /* Directory scan is required with pattern */
407 if( ( pattern
= IsPat( (STRPTR
) FilePart(arg
->wa_Name
) ) ) )
409 struct ExAllControl
*eac
;
410 struct ExAllData
*ead
;
411 struct FileLock
*lock
;
413 ((STRPTR
)PathPart(arg
->wa_Name
))[0] = 0;
415 if((lock
= (void *) Lock(arg
->wa_Name
, SHARED_LOCK
)))
417 CurrentDir((BPTR
) lock
);
418 if ((eac
= (void *) AllocDosObject(DOS_EXALLCONTROL
,NULL
)))
420 eac
->eac_LastKey
= 0;
421 eac
->eac_MatchString
= pattern
+512;
423 more
= ExAll((BPTR
)lock
, (struct ExAllData
*)pattern
, 512, ED_TYPE
, eac
);
424 if( eac
->eac_Entries
== 0 ) continue;
425 for(ead
= (void *)pattern
; ead
; ead
= ead
->ed_Next
)
427 if(ead
->ed_Type
< 0 && !new_file(&new, ead
->ed_Name
))
431 stop_now
: FreeDosObject(DOS_EXALLCONTROL
, eac
);
433 UnLock((BPTR
) lock
);
434 } /* else bad pattern: discard entry */
437 /* Given file contains no pattern */
438 else if( !new_file(&new, arg
->wa_Name
) )
441 CurrentDir((BPTR
)cwd
);
442 /* Still have no file to edit (pattern with no match) */
443 if(new == NULL
) goto newempty
;
445 /* We have a bunch of projects, try to load at least one */
446 while(new != ins_after
)
447 if(RETURN_OK
!= load_in_project(new, new->path
)) {
448 Project prev
= new->prev
;
449 ThrowDOSError(Wnd
, new->path
);
451 FreeVec(new); new = prev
;
454 newempty
: new = new_project(NULL
, &prefs
);
455 CurrentDir((BPTR
) cwd
);
459 /*** set modification flag on this project ***/
460 void set_modif_mark(Project p
)
462 p
->state
|= MODIFIED
;
465 update_panel_name( p
);
469 void unset_modif_mark(Project p
, char update
)
475 update_panel_name(p
);
480 /*** Close one project and ressources allocated ***/
481 char close_project(Project p
)
483 /* Close it, only if user knows what he does */
487 trash_file(p
->the_line
);
488 flush_undo_buf( &p
->undo
);
489 flush_undo_buf( &p
->redo
);
490 if(p
->path
) FreeVec(p
->path
);
491 if(p
->buffer
) FreeVec(p
->buffer
);
498 /*** Close all projects, returning the first non-closed or NULL if none ***/
499 Project
close_projects( void )
501 register Project prj
;
505 /* If user doesn't want to close this one */
506 if( close_project(first
) ) FreeVec(prj
);
509 /* If NULL, then we should quit */
514 /*** PROJECT BAR, GRAPHICAL ROUTINES ***/
516 /*** Draw one NextStep-like panel item ***/
517 void draw_panel(Project p
, BYTE rclear
, BYTE lclear
, BYTE activ
)
519 BYTE max
= (gui
.top
-gui
.oldtop
-1) >> 1, i
;
520 WORD right
= p
->pleft
+ p
->pwidth
- max
- 1;
522 WORD bgpan
= (activ
? pen
.abpan
: pen
.bgpan
);
526 SetAPen(&RPT
,pen
.shine
);
527 Move(&RPT
, gui
.left
, gui
.top
-2);
528 Draw(&RPT
, p
->pleft
, RPT
.cp_y
);
531 for(i
=0; i
<max
; i
++) {
532 Move(&RPT
, p
->pleft
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
533 Draw(&RPT
, RPT
.cp_x
, gui
.top
-2-(i
<<1)); SetAPen(&RPT
,pen
.shine
);
534 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
536 SetAPen(&RPT
, pen
.panel
);
537 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
538 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
542 Move(&RPT
, RPT
.cp_x
+1, gui
.oldtop
); SetAPen(&RPT
,pen
.shine
);
543 Draw(&RPT
, right
, gui
.oldtop
); SetAPen(&RPT
, bgpan
);
545 RectFill(&RPT
, p
->pleft
+max
, gui
.oldtop
+1, RPT
.cp_x
, gui
.top
-2);
546 SetABPenDrMd(&RPT
, pen
.fgpan
, bgpan
, JAM2
);
548 /* Text in the box */
549 Move(&RPT
,p
->pleft
+((p
->pwidth
-(p
->labwid
+p
->modwid
))>>1),
550 gui
.oldtop
+prefs
.scrfont
->tf_Baseline
+3 );
551 if(!activ
&& (p
->state
& PAGINATED
)) RPT
.AlgoStyle
|= FSF_ITALIC
;
552 Text(&RPT
,p
->name
,p
->labsize
);
553 if(p
->state
& MODIFIED
)
554 Text(&RPT
,STR_MODIF
,1);
557 for(i
=0,right
++; i
<max
; i
++) {
558 Move(&RPT
, right
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
559 Draw(&RPT
, RPT
.cp_x
, gui
.top
-((max
-i
)<<1)); SetAPen(&RPT
,pen
.shade
);
560 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
562 SetAPen(&RPT
,pen
.panel
);
563 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
564 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
569 SetAPen(&RPT
,pen
.shine
);
570 Move(&RPT
, RPT
.cp_x
, gui
.top
-2);
571 Draw(&RPT
, gui
.right
, RPT
.cp_y
);
573 SetBPen(&RPT
, pen
.bg
);
577 /*** Search if a tab is select using mouse ***/
578 Project
select_panel(Project current
, WORD x
)
581 /* Search for the hi-lighted panel */
582 if( x
== NEXT_PROJECT
) new=current
->next
; else
583 if( x
== PREV_PROJECT
) new=current
->prev
; else
585 for(new=first
; new && ++x
; new=new->next
);
587 for(new=first
; new; new=new->next
)
588 if(new->pleft
<= x
&& x
< new->pleft
+new->pwidth
) break;
590 /* Don't change project if it is the same */
591 if(new && new != current
)
593 /* Refresh panel tab first */
594 inv_curs(current
, FALSE
);
595 RPT
.AlgoStyle
= FS_NORMAL
; draw_panel(current
, FALSE
, FALSE
, FALSE
);
596 RPT
.AlgoStyle
= FSF_BOLD
; draw_panel(new, FALSE
, FALSE
, TRUE
);
599 if( active_project(new, FALSE
) == 0 )
600 reshape_panel(current
);
606 /*** Title of panel has changed, update it ***/
607 void update_panel_name( Project active
)
609 /* Assuming that active is the currently selected */
610 active
->labwid
= TextLength(&RPT
, active
->name
,
611 active
->labsize
=strlen(active
->name
));
612 if(active
->state
& MODIFIED
)
614 (active
->modwid
=TextLength(&RPT
, STR_MODIF
, 1));
617 RPT
.AlgoStyle
= FSF_BOLD
;
618 draw_panel(active
, FALSE
, FALSE
, TRUE
);
623 /*** Recompute size of each item and show the active project ***/
624 void reshape_panel(Project active
)
626 Project lst
; UBYTE i
;
627 /* Do not show the project bar if there is only one project */
631 /** Change top position, if not already **/
633 if(gui
.top
== gui
.oldtop
)
634 adjust_win(Wnd
, 1), clear_brcorner();
637 /** Compute items size **/
638 for(lst
=first
,i
=1,height
=(gui
.top
-gui
.oldtop
-2)>>1; lst
; lst
=lst
->next
,i
++)
640 lst
->pleft
= (lst
==first
? gui
.left
: lst
->prev
->pleft
+lst
->prev
->pwidth
-height
);
641 lst
->pwidth
= (gui
.right
+1) * i
/ NbProject
- lst
->pleft
;
642 lst
->labwid
= TextLength(&RPT
, lst
->name
, lst
->labsize
);
644 /** Redraw the project bar **/
645 for(lst
=first
; lst
; lst
=lst
->next
) {
646 RPT
.AlgoStyle
= FS_NORMAL
;
647 /* Active project should be the last drawn */
649 draw_panel(lst
, lst
->prev
==active
|| !lst
->prev
, TRUE
, FALSE
);
651 RPT
.AlgoStyle
= FSF_BOLD
;
652 draw_panel(active
, !active
->prev
, !active
->next
, TRUE
);
655 else if(gui
.oldtop
!= gui
.top
) {
656 SetAPen(&RPT
, pen
.bg
);
657 RectFill(&RPT
, gui
.left
, gui
.oldtop
, gui
.right
, gui
.top
);
658 /** Remove project bar, if it lefts only one project **/
659 adjust_win(Wnd
, 0); clear_brcorner();