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 <workbench/startup.h>
19 #include "ProtoTypes.h"
22 #define CATCOMP_NUMBERS /* We will need the string id */
25 static Project first
= NULL
; /* Keep track of first created project */
26 UBYTE NbProject
= 0; /* Number of opened projects */
29 /*** Alloc a new project ***/
30 Project
new_project(Project ins
, PREFS
*prefs
)
33 if( ( new = (void *) AllocVec(sizeof(*new), MEMF_PUBLIC
| MEMF_CLEAR
) ) )
35 /* Always need at least one line */
36 if( ( new->show
= create_line(NULL
,0) ) )
38 InsertAfter(ins
, new);
39 /* Local preferences herited */
40 new->tabsize
= prefs
->tabsize
;
43 /* The line shown/edited */
44 if(NbProject
== 1) first
= new;
45 new->show
->prev
= new->show
->next
= NULL
;
46 new->the_line
= new->edited
= new->show
;
47 new->ccp
.xp
= (ULONG
)-1;
49 new->undo
.prj
= new->redo
.prj
= (APTR
) new;
52 set_project_name(new, NULL
);
54 else FreeVec(new),new = NULL
;
59 /*** Change content of a project ***/
60 void change_project(Project p
, LINE
*new)
62 flush_undo_buf( &p
->undo
);
63 flush_undo_buf( &p
->redo
);
64 trash_file(p
->the_line
);
65 if(p
->buffer
) FreeVec(p
->buffer
);
66 memset(&p
->nbc
, 0, 4*sizeof(ULONG
));
67 p
->edited
= p
->the_line
= p
->show
= new;
68 p
->top_line
= p
->left_pos
= 0;
70 p
->ycurs
= gui
.topcurs
;
75 /*** Try to load the file contained in the project ***/
76 WORD
load_in_project( Project p
, STRPTR path
)
83 if(RETURN_OK
== (err
= load_file( &args
)))
85 /* File successfully loaded */
86 change_project(p
, args
.lines
);
87 p
->buffer
= args
.buffer
;
88 p
->max_lines
= args
.nblines
;
90 p
->protection
= args
.protection
&= ~FIBF_ARCHIVE
;
92 else ThrowError(Wnd
, ErrMsg(err
));
97 /*** Load and create a new project from a path ***/
98 Project
load_and_activate(Project ins
, STRPTR name
, BYTE use_prj
)
101 if( ( new = (use_prj
? ins
: new_project(ins
, &prefs
)) ) )
105 /* Multi-selection: name is actually a (StartUpArgs *) */
106 if( ((StartUpArgs
*)name
)->sa_NbArgs
> 0 )
108 new = create_projects(ins
, ((StartUpArgs
*)name
)->sa_ArgLst
, ((StartUpArgs
*)name
)->sa_NbArgs
);
110 inv_curs(ins
, FALSE
);
111 if(use_prj
== 2) close_project(ins
), FreeVec(ins
);
117 else /* Load a single file into `new' */
119 inv_curs(ins
, FALSE
);
120 /* We have a correct path, try to load file */
121 if( RETURN_OK
== load_in_project(new, name
) )
123 set_project_name(new, name
);
124 /* Add a panel tab and show content of file */
125 if(use_prj
) update_panel_name(ins
);
126 else refresh
:reshape_panel(new);
127 active_project(new, TRUE
);
131 /* Something failed, close project */
132 if(use_prj
== 0) close_project(new),FreeVec(new);
135 else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
139 /*** Reload project, flush redolog, reduce fragmentation ***/
140 void reload_project( Project p
)
142 /* Save cursor position */
143 ULONG nbl
= p
->nbl
, top
= p
->top_line
;
145 /* Flush all buffers and reload file */
146 unset_modif_mark(p
, TRUE
);
148 load_in_project(p
, p
->path
);
150 /* Reset cursor position, if possible */
151 p
->nbl
= nbl
; p
->top_line
= top
; top
= center_vert( p
);
152 p
->top_line
= p
->nbl
= 0;
153 set_top_line(p
, top
, 0);
154 set_cursor_line(p
, nbl
, p
->top_line
);
158 /*** Insert a file at current cursor position ***/
159 void insert_file( Project p
)
165 if( ( args
.filename
= ask_load(Wnd
, (AskArgs
*)&p
->path
, TRUE
, GetMenuText(208)) ) )
167 if( 0 == (err
= read_file( &args
, &length
)) )
170 reg_group_by(&p
->undo
);
171 if( add_string( &p
->undo
, p
->edited
, p
->nbc
, args
.buffer
, length
, pos
) )
173 /* Just one line concerned? */
174 p
->max_lines
+= pos
[2];
175 if( pos
[1] == 0 ) REDRAW_CURLINE(p
)
177 move_cursor(p
, pos
[0], pos
[1]);
178 if( pos
[1] > 0 ) redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
179 if( p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
180 inv_curs(p
,TRUE
); prop_adj(p
);
182 else ThrowDOSError(Wnd
, args
.filename
);
184 if( args
.buffer
) FreeVec( args
.buffer
);
186 reg_group_by(&p
->undo
);
188 else ThrowError(Wnd
, ErrMsg( err
));
190 FreeVec( args
.filename
);
194 /*** Change and set internal project name variables ***/
195 void set_project_name( Project p
, STRPTR path
)
197 if(p
->path
!= NULL
) FreeVec(p
->path
);
199 p
->name
= (STRPTR
) FilePart(
203 p
->name
= ErrMsg(ERR_NONAME
);
204 p
->labsize
= strlen(p
->name
);
207 /*** Print one project ***/
208 char print_project(Project p
)
210 return print_file(p
->the_line
, p
->eol
);
213 /*** Save one project ***/
214 char save_project(Project p
, char refresh
, char ask
)
218 /* Ask for a name if file doesn't have one */
219 if(p
->path
== NULL
|| ask
)
222 if(NULL
!= (newname
= ask_save(Wnd
, (AskArgs
*)&p
->path
, GetMenuText(105))) &&
223 0 != warn_overwrite( newname
))
224 set_project_name(p
, newname
);
226 /* User cancelled or there was an error */
233 UpdateTitle(Wnd
, p
), update_panel_name( p
);
236 retval
= save_file(p
->path
, p
->the_line
, p
->eol
, p
->protection
);
240 unset_modif_mark(p
, TRUE
);
246 /*** Save all modified projects ***/
247 Project
save_projects(Project active
, char close
)
249 Project cur
, p
; int nb
= 0;
252 /* Auto-save modified project */
253 if(p
->state
& MODIFIED
) {
254 if(save_project(p
, FALSE
, FALSE
) == 0) break; else nb
++;
255 if(p
== active
&& close
== 0) UpdateTitle(Wnd
, p
);
260 cur
= p
; close_project(p
);
261 p
= p
->next
; FreeVec(cur
);
268 if(NbProject
> 0 && nb
> 0) reshape_panel(active
);
272 /*** Makes project the active one (i.e. visible) ***/
273 char active_project(Project p
, char InitCurs
)
275 /* If file isn't loaded yet, makes it now */
276 if(p
->state
& PAGINATED
)
277 /* This call is subject to failed! */
278 switch( load_in_project(p
, p
->path
) )
280 case RETURN_OK
: p
->state
&= ~PAGINATED
; break;
282 /* This is too nasty to attempt something: close faulty project */
283 close_project(p
); FreeVec(p
); return 0;
287 init_tabstop(p
->tabsize
);
289 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
291 p
->ycurs
= gui
.topcurs
,
294 p
->left_pos
= curs_visible(p
, p
->top_line
),
295 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
297 /* This can speedup a lot, text rendering */
298 if(p
->ccp
.select
!= 0) RP
->Mask
= gui
.selmask
;
299 redraw_content(p
,p
->show
,gui
.topcurs
,gui
.nbline
);
300 if(p
->ccp
.select
== 0) RP
->Mask
= gui
.txtmask
;
306 /*** Test if a string represent an AmigaDOS pattern ***/
307 STRPTR
IsPat( STRPTR name
)
309 int len
= strlen( name
) * 2 + 2;
311 /* 512 bytes is used for ExAll buffer scan */
312 if((tok
= (STRPTR
) AllocVec(len
+ 512, MEMF_CLEAR
)))
314 if( ParsePatternNoCase(name
, tok
+512, len
) > 0 )
321 /*** Create a new project with an incomplete path ***/
322 UBYTE
new_file(Project
*ins
, STRPTR path
)
325 if(NULL
!= (new = new_project(*ins
, &prefs
)))
328 if(RETURN_OK
== (err
= get_full_path(path
, &new->name
))) {
329 set_project_name(new, new->name
); *ins
= new;
330 new->state
= PAGINATED
;
333 ThrowError(Wnd
, ErrMsg(err
));
337 ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
341 /*** Create a set of projects (command line or WBStartup) ***/
342 Project
create_projects(Project ins_after
, APTR args
, ULONG nb
)
346 APTR cwd
= (APTR
) CurrentDir( BNULL
);
350 register struct WBArg
*arg
= (struct WBArg
*)args
;
351 for(new = ins_after
; nb
--; arg
++ )
353 CurrentDir(arg
->wa_Lock
);
354 /* Directory scan is required with pattern */
355 if( ( pattern
= IsPat( (STRPTR
) FilePart(arg
->wa_Name
) ) ) )
357 struct ExAllControl
*eac
;
358 struct ExAllData
*ead
;
359 struct FileLock
*lock
;
361 ((STRPTR
)PathPart(arg
->wa_Name
))[0] = 0;
362 if((lock
= (void *) Lock(arg
->wa_Name
, SHARED_LOCK
)))
364 CurrentDir((BPTR
) lock
);
365 if ((eac
= (void *) AllocDosObject(DOS_EXALLCONTROL
,NULL
)))
367 eac
->eac_LastKey
= 0;
368 eac
->eac_MatchString
= pattern
+512;
370 more
= ExAll((BPTR
)lock
, (struct ExAllData
*)pattern
, 512, ED_TYPE
, eac
);
371 if( eac
->eac_Entries
== 0 ) continue;
372 for(ead
= (void *)pattern
; ead
; ead
= ead
->ed_Next
)
373 if(ead
->ed_Type
< 0 && !new_file(&new, ead
->ed_Name
))
376 stop_now
: FreeDosObject(DOS_EXALLCONTROL
, eac
);
378 UnLock((BPTR
) lock
);
379 } /* else bad pattern: discard entry */
382 /* Given file contains no pattern */
383 else if( !new_file(&new, arg
->wa_Name
) )
386 CurrentDir((BPTR
)cwd
);
387 /* Still have no file to edit (pattern with no match) */
388 if(new == NULL
) goto newempty
;
390 /* We have a bunch of projects, try to load at least one */
391 while(new != ins_after
)
392 if(RETURN_OK
!= load_in_project(new, new->path
)) {
393 Project prev
= new->prev
;
394 ThrowDOSError(Wnd
, new->path
);
396 FreeVec(new); new = prev
;
399 newempty
: new = new_project(NULL
, &prefs
);
400 CurrentDir((BPTR
) cwd
);
404 /*** set modification flag on this project ***/
405 void set_modif_mark(Project p
)
407 p
->state
|= MODIFIED
;
410 update_panel_name( p
);
414 void unset_modif_mark(Project p
, char update
)
420 update_panel_name(p
);
425 /*** Close one project and ressources allocated ***/
426 char close_project(Project p
)
428 /* Close it, only if user knows what he does */
432 trash_file(p
->the_line
);
433 flush_undo_buf( &p
->undo
);
434 flush_undo_buf( &p
->redo
);
435 if(p
->path
) FreeVec(p
->path
);
436 if(p
->buffer
) FreeVec(p
->buffer
);
443 /*** Close all projects, returning the first non-closed or NULL if none ***/
444 Project
close_projects( void )
446 register Project prj
;
450 /* If user doesn't want to close this one */
451 if( close_project(first
) ) FreeVec(prj
);
454 /* If NULL, then we should quit */
459 /*** PROJECT BAR, GRAPHICAL ROUTINES ***/
461 /*** Draw one NextStep-like panel item ***/
462 void draw_panel(Project p
, BYTE rclear
, BYTE lclear
, BYTE activ
)
464 BYTE max
= (gui
.top
-gui
.oldtop
-1) >> 1, i
;
465 WORD right
= p
->pleft
+ p
->pwidth
- max
- 1;
467 WORD bgpan
= (activ
? pen
.abpan
: pen
.bgpan
);
471 SetAPen(&RPT
,pen
.shine
);
472 Move(&RPT
, gui
.left
, gui
.top
-2);
473 Draw(&RPT
, p
->pleft
, RPT
.cp_y
);
476 for(i
=0; i
<max
; i
++) {
477 Move(&RPT
, p
->pleft
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
478 Draw(&RPT
, RPT
.cp_x
, gui
.top
-2-(i
<<1)); SetAPen(&RPT
,pen
.shine
);
479 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
481 SetAPen(&RPT
, pen
.panel
);
482 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
483 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
487 Move(&RPT
, RPT
.cp_x
+1, gui
.oldtop
); SetAPen(&RPT
,pen
.shine
);
488 Draw(&RPT
, right
, gui
.oldtop
); SetAPen(&RPT
, bgpan
);
490 RectFill(&RPT
, p
->pleft
+max
, gui
.oldtop
+1, RPT
.cp_x
, gui
.top
-2);
491 SetABPenDrMd(&RPT
, pen
.fgpan
, bgpan
, JAM2
);
493 /* Text in the box */
494 Move(&RPT
,p
->pleft
+((p
->pwidth
-(p
->labwid
+p
->modwid
))>>1),
495 gui
.oldtop
+prefs
.scrfont
->tf_Baseline
+3 );
496 if(!activ
&& (p
->state
& PAGINATED
)) RPT
.AlgoStyle
|= FSF_ITALIC
;
497 Text(&RPT
,p
->name
,p
->labsize
);
498 if(p
->state
& MODIFIED
)
499 Text(&RPT
,STR_MODIF
,1);
502 for(i
=0,right
++; i
<max
; i
++) {
503 Move(&RPT
, right
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
504 Draw(&RPT
, RPT
.cp_x
, gui
.top
-((max
-i
)<<1)); SetAPen(&RPT
,pen
.shade
);
505 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
507 SetAPen(&RPT
,pen
.panel
);
508 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
509 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
514 SetAPen(&RPT
,pen
.shine
);
515 Move(&RPT
, RPT
.cp_x
, gui
.top
-2);
516 Draw(&RPT
, gui
.right
, RPT
.cp_y
);
518 SetBPen(&RPT
, pen
.bg
);
522 /*** Search if a tab is select using mouse ***/
523 Project
select_panel(Project current
, WORD x
)
526 /* Search for the hi-lighted panel */
527 if( x
== NEXT_PROJECT
) new=current
->next
; else
528 if( x
== PREV_PROJECT
) new=current
->prev
; else
530 for(new=first
; new && ++x
; new=new->next
);
532 for(new=first
; new; new=new->next
)
533 if(new->pleft
<= x
&& x
< new->pleft
+new->pwidth
) break;
535 /* Don't change project if it is the same */
536 if(new && new != current
)
538 /* Refresh panel tab first */
539 inv_curs(current
, FALSE
);
540 RPT
.AlgoStyle
= FS_NORMAL
; draw_panel(current
, FALSE
, FALSE
, FALSE
);
541 RPT
.AlgoStyle
= FSF_BOLD
; draw_panel(new, FALSE
, FALSE
, TRUE
);
544 if( active_project(new, FALSE
) == 0 )
545 reshape_panel(current
);
551 /*** Title of panel has changed, update it ***/
552 void update_panel_name( Project active
)
554 /* Assuming that active is the currently selected */
555 active
->labwid
= TextLength(&RPT
, active
->name
,
556 active
->labsize
=strlen(active
->name
));
557 if(active
->state
& MODIFIED
)
559 (active
->modwid
=TextLength(&RPT
, STR_MODIF
, 1));
562 RPT
.AlgoStyle
= FSF_BOLD
;
563 draw_panel(active
, FALSE
, FALSE
, TRUE
);
568 /*** Recompute size of each item and show the active project ***/
569 void reshape_panel(Project active
)
571 Project lst
; UBYTE i
;
572 /* Do not show the project bar if there is only one project */
576 /** Change top position, if not already **/
578 if(gui
.top
== gui
.oldtop
)
579 adjust_win(Wnd
, 1), clear_brcorner();
582 /** Compute items size **/
583 for(lst
=first
,i
=1,height
=(gui
.top
-gui
.oldtop
-2)>>1; lst
; lst
=lst
->next
,i
++)
585 lst
->pleft
= (lst
==first
? gui
.left
: lst
->prev
->pleft
+lst
->prev
->pwidth
-height
);
586 lst
->pwidth
= (gui
.right
+1) * i
/ NbProject
- lst
->pleft
;
587 lst
->labwid
= TextLength(&RPT
, lst
->name
, lst
->labsize
);
589 /** Redraw the project bar **/
590 for(lst
=first
; lst
; lst
=lst
->next
) {
591 RPT
.AlgoStyle
= FS_NORMAL
;
592 /* Active project should be the last drawn */
594 draw_panel(lst
, lst
->prev
==active
|| !lst
->prev
, TRUE
, FALSE
);
596 RPT
.AlgoStyle
= FSF_BOLD
;
597 draw_panel(active
, !active
->prev
, !active
->next
, TRUE
);
600 else if(gui
.oldtop
!= gui
.top
) {
601 SetAPen(&RPT
, pen
.bg
);
602 RectFill(&RPT
, gui
.left
, gui
.oldtop
, gui
.right
, gui
.top
);
603 /** Remove project bar, if it lefts only one project **/
604 adjust_win(Wnd
, 0); clear_brcorner();