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"
21 #define CATCOMP_NUMBERS /* We will need the string id */
24 static Project first
= NULL
; /* Keep track of first created project */
25 UBYTE NbProject
= 0; /* Number of opened projects */
28 /*** Alloc a new project ***/
29 Project
new_project(Project ins
, PREFS
*prefs
)
32 if( ( new = (void *) AllocVec(sizeof(*new), MEMF_PUBLIC
| MEMF_CLEAR
) ) )
34 /* Always need at least one line */
35 if( ( new->show
= create_line(NULL
,0) ) )
37 InsertAfter(ins
, new);
38 /* Local preferences herited */
39 new->tabsize
= prefs
->tabsize
;
42 /* The line shown/edited */
43 if(NbProject
== 1) first
= new;
44 new->show
->prev
= new->show
->next
= NULL
;
45 new->the_line
= new->edited
= new->show
;
46 new->ccp
.xp
= (ULONG
)-1;
48 new->undo
.prj
= new->redo
.prj
= (APTR
) new;
51 set_project_name(new, NULL
);
53 else FreeVec(new),new = NULL
;
58 /*** Change content of a project ***/
59 void change_project(Project p
, LINE
*new)
61 flush_undo_buf( &p
->undo
);
62 flush_undo_buf( &p
->redo
);
63 trash_file(p
->the_line
);
64 if(p
->buffer
) FreeVec(p
->buffer
);
65 memset(&p
->nbc
, 0, 4*sizeof(ULONG
));
66 p
->edited
= p
->the_line
= p
->show
= new;
67 p
->top_line
= p
->left_pos
= 0;
69 p
->ycurs
= gui
.topcurs
;
74 /*** Try to load the file contained in the project ***/
75 WORD
load_in_project( Project p
, STRPTR path
)
82 if(RETURN_OK
== (err
= load_file( &args
)))
84 /* File successfully loaded */
85 change_project(p
, args
.lines
);
86 p
->buffer
= args
.buffer
;
87 p
->max_lines
= args
.nblines
;
89 p
->protection
= args
.protection
&= ~FIBF_ARCHIVE
;
91 else ThrowError(Wnd
, ErrMsg(err
));
96 /*** Load and create a new project from a path ***/
97 Project
load_and_activate(Project ins
, STRPTR name
, BYTE use_prj
)
100 if( ( new = (use_prj
? ins
: new_project(ins
, &prefs
)) ) )
104 /* Multi-selection: name is actually a (StartUpArgs *) */
105 if( ((StartUpArgs
*)name
)->sa_NbArgs
> 0 )
107 new = create_projects(ins
, ((StartUpArgs
*)name
)->sa_ArgLst
, ((StartUpArgs
*)name
)->sa_NbArgs
);
109 inv_curs(ins
, FALSE
);
110 if(use_prj
== 2) close_project(ins
), FreeVec(ins
);
116 else /* Load a single file into `new' */
118 inv_curs(ins
, FALSE
);
119 /* We have a correct path, try to load file */
120 if( RETURN_OK
== load_in_project(new, name
) )
122 set_project_name(new, name
);
123 /* Add a panel tab and show content of file */
124 if(use_prj
) update_panel_name(ins
);
125 else refresh
:reshape_panel(new);
126 active_project(new, TRUE
);
130 /* Something failed, close project */
131 if(use_prj
== 0) close_project(new),FreeVec(new);
134 else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
138 /*** Reload project, flush redolog, reduce fragmentation ***/
139 void reload_project( Project p
)
141 /* Save cursor position */
142 ULONG nbl
= p
->nbl
, top
= p
->top_line
;
144 /* Flush all buffers and reload file */
145 unset_modif_mark(p
, TRUE
);
147 load_in_project(p
, p
->path
);
149 /* Reset cursor position, if possible */
150 p
->nbl
= nbl
; p
->top_line
= top
; top
= center_vert( p
);
151 p
->top_line
= p
->nbl
= 0;
152 set_top_line(p
, top
, 0);
153 set_cursor_line(p
, nbl
, p
->top_line
);
157 /*** Insert a file at current cursor position ***/
158 void insert_file( Project p
)
164 if( ( args
.filename
= ask_load(Wnd
, (AskArgs
*)&p
->path
, TRUE
, GetMenuText(208)) ) )
166 if( 0 == (err
= read_file( &args
, &length
)) )
169 reg_group_by(&p
->undo
);
170 if( add_string( &p
->undo
, p
->edited
, p
->nbc
, args
.buffer
, length
, pos
) )
172 /* Just one line concerned? */
173 p
->max_lines
+= pos
[2];
174 if( pos
[1] == 0 ) REDRAW_CURLINE(p
)
176 move_cursor(p
, pos
[0], pos
[1]);
177 if( pos
[1] > 0 ) redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
178 if( p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
179 inv_curs(p
,TRUE
); prop_adj(p
);
181 else ThrowDOSError(Wnd
, args
.filename
);
183 if( args
.buffer
) FreeVec( args
.buffer
);
185 reg_group_by(&p
->undo
);
187 else ThrowError(Wnd
, ErrMsg( err
));
189 FreeVec( args
.filename
);
193 /*** Change and set internal project name variables ***/
194 void set_project_name( Project p
, STRPTR path
)
196 if(p
->path
!= NULL
) FreeVec(p
->path
);
198 p
->name
= (STRPTR
) FilePart(
202 p
->name
= ErrMsg(ERR_NONAME
);
203 p
->labsize
= strlen(p
->name
);
206 /*** Save one project ***/
207 char save_project(Project p
, char refresh
, char ask
)
211 /* Ask for a name if file doesn't have one */
212 if(p
->path
== NULL
|| ask
)
215 if(NULL
!= (newname
= ask_save(Wnd
, (AskArgs
*)&p
->path
, GetMenuText(105))) &&
216 0 != warn_overwrite( newname
))
217 set_project_name(p
, newname
);
219 /* User cancelled or there was an error */
226 update_panel_name( p
);
228 retval
= save_file(p
->path
, p
->the_line
, p
->eol
, p
->protection
);
231 unset_modif_mark(p
, FALSE
);
236 /*** Save all modified projects ***/
237 Project
save_projects(Project active
, char close
)
239 Project cur
, p
; int nb
= 0;
242 /* Auto-save modified project */
243 if(p
->state
& MODIFIED
) {
244 if(save_project(p
, FALSE
, FALSE
) == 0) break; else nb
++;
245 if(p
== active
&& close
== 0) UpdateTitle(Wnd
, p
);
250 cur
= p
; close_project(p
);
251 p
= p
->next
; FreeVec(cur
);
252 if(active
== cur
) active
= p
; nb
= 0;
256 if(NbProject
> 0 && nb
> 0) reshape_panel(active
);
260 /*** Makes project the active one (i.e. visible) ***/
261 char active_project(Project p
, char InitCurs
)
263 /* If file isn't loaded yet, makes it now */
264 if(p
->state
& PAGINATED
)
265 /* This call is subject to failed! */
266 switch( load_in_project(p
, p
->path
) )
268 case RETURN_OK
: p
->state
&= ~PAGINATED
; break;
270 /* This is too nasty to attempt something: close faulty project */
271 close_project(p
); FreeVec(p
); return 0;
275 init_tabstop(p
->tabsize
);
277 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
279 p
->ycurs
= gui
.topcurs
,
282 p
->left_pos
= curs_visible(p
, p
->top_line
),
283 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
285 /* This can speedup a lot, text rendering */
286 if(p
->ccp
.select
!= 0) RP
->Mask
= gui
.selmask
;
287 redraw_content(p
,p
->show
,gui
.topcurs
,gui
.nbline
);
288 if(p
->ccp
.select
== 0) RP
->Mask
= gui
.txtmask
;
294 /*** Test if a string represent an AmigaDOS pattern ***/
295 STRPTR
IsPat( STRPTR name
)
297 int len
= strlen( name
) * 2 + 2;
299 /* 512 bytes is used for ExAll buffer scan */
300 if((tok
= (STRPTR
) AllocVec(len
+ 512, MEMF_CLEAR
)))
302 if( ParsePatternNoCase(name
, tok
+512, len
) > 0 )
309 /*** Create a new project with an incomplete path ***/
310 UBYTE
new_file(Project
*ins
, STRPTR path
)
313 if(NULL
!= (new = new_project(*ins
, &prefs
)))
316 if(RETURN_OK
== (err
= get_full_path(path
, &new->name
))) {
317 set_project_name(new, new->name
); *ins
= new;
318 new->state
= PAGINATED
;
321 ThrowError(Wnd
, ErrMsg(err
));
325 ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
329 /*** Create a set of projects (command line or WBStartup) ***/
330 Project
create_projects(Project ins_after
, APTR args
, ULONG nb
)
334 APTR cwd
= (APTR
) CurrentDir( NULL
);
338 register struct WBArg
*arg
= (struct WBArg
*)args
;
339 for(new = ins_after
; nb
--; arg
++ )
341 CurrentDir(arg
->wa_Lock
);
342 /* Directory scan is required with pattern */
343 if( ( pattern
= IsPat( (STRPTR
) FilePart(arg
->wa_Name
) ) ) )
345 struct ExAllControl
*eac
;
346 struct ExAllData
*ead
;
347 struct FileLock
*lock
;
349 ((STRPTR
)PathPart(arg
->wa_Name
))[0] = 0;
350 if((lock
= (void *) Lock(arg
->wa_Name
, SHARED_LOCK
)))
352 CurrentDir((BPTR
) lock
);
353 if ((eac
= (void *) AllocDosObject(DOS_EXALLCONTROL
,NULL
)))
355 eac
->eac_LastKey
= 0;
356 eac
->eac_MatchString
= pattern
+512;
358 more
= ExAll((BPTR
)lock
, (struct ExAllData
*)pattern
, 512, ED_TYPE
, eac
);
359 if( eac
->eac_Entries
== 0 ) continue;
360 for(ead
= (void *)pattern
; ead
; ead
= ead
->ed_Next
)
361 if(ead
->ed_Type
< 0 && !new_file(&new, ead
->ed_Name
))
364 stop_now
: FreeDosObject(DOS_EXALLCONTROL
, eac
);
366 UnLock((BPTR
) lock
);
367 } /* else bad pattern: discard entry */
370 /* Given file contains no pattern */
371 else if( !new_file(&new, arg
->wa_Name
) )
374 CurrentDir((BPTR
)cwd
);
375 /* Still have no file to edit (pattern with no match) */
376 if(new == NULL
) goto newempty
;
378 /* We have a bunch of projects, try to load at least one */
379 while(new != ins_after
)
380 if(RETURN_OK
!= load_in_project(new, new->path
)) {
381 Project prev
= new->prev
;
382 ThrowDOSError(Wnd
, new->path
);
384 FreeVec(new); new = prev
;
387 newempty
: new = new_project(NULL
, &prefs
);
388 CurrentDir((BPTR
) cwd
);
392 /*** set modification flag on this project ***/
393 void set_modif_mark(Project p
)
395 p
->state
|= MODIFIED
;
398 update_panel_name( p
);
402 void unset_modif_mark(Project p
, char update
)
408 update_panel_name(p
);
413 /*** Close one project and ressources allocated ***/
414 char close_project(Project p
)
416 /* Close it, only if user knows what he does */
420 trash_file(p
->the_line
);
421 flush_undo_buf( &p
->undo
);
422 flush_undo_buf( &p
->redo
);
423 if(p
->path
) FreeVec(p
->path
);
424 if(p
->buffer
) FreeVec(p
->buffer
);
431 /*** Close all projects, returning the first non-closed or NULL if none ***/
432 Project
close_projects( void )
434 register Project prj
;
438 /* If user doesn't want to close this one */
439 if( close_project(first
) ) FreeVec(prj
);
442 /* If NULL, then we should quit */
447 /*** PROJECT BAR, GRAPHICAL ROUTINES ***/
449 /*** Draw one NextStep-like panel item ***/
450 void draw_panel(Project p
, BYTE rclear
, BYTE lclear
, BYTE activ
)
452 BYTE max
= (gui
.top
-gui
.oldtop
-1) >> 1, i
;
453 WORD right
= p
->pleft
+ p
->pwidth
- max
- 1;
455 WORD bgpan
= (activ
? pen
.abpan
: pen
.bgpan
);
459 SetAPen(&RPT
,pen
.shine
);
460 Move(&RPT
, gui
.left
, gui
.top
-2);
461 Draw(&RPT
, p
->pleft
, RPT
.cp_y
);
464 for(i
=0; i
<max
; i
++) {
465 Move(&RPT
, p
->pleft
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
466 Draw(&RPT
, RPT
.cp_x
, gui
.top
-2-(i
<<1)); SetAPen(&RPT
,pen
.shine
);
467 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
469 SetAPen(&RPT
, pen
.panel
);
470 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
471 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
475 Move(&RPT
, RPT
.cp_x
+1, gui
.oldtop
); SetAPen(&RPT
,pen
.shine
);
476 Draw(&RPT
, right
, gui
.oldtop
); SetAPen(&RPT
, bgpan
);
478 RectFill(&RPT
, p
->pleft
+max
, gui
.oldtop
+1, RPT
.cp_x
, gui
.top
-2);
479 SetABPenDrMd(&RPT
, pen
.fgpan
, bgpan
, JAM2
);
481 /* Text in the box */
482 Move(&RPT
,p
->pleft
+((p
->pwidth
-(p
->labwid
+p
->modwid
))>>1),
483 gui
.oldtop
+prefs
.scrfont
->tf_Baseline
+3 );
484 if(!activ
&& (p
->state
& PAGINATED
)) RPT
.AlgoStyle
|= FSF_ITALIC
;
485 Text(&RPT
,p
->name
,p
->labsize
);
486 if(p
->state
& MODIFIED
)
487 Text(&RPT
,STR_MODIF
,1);
490 for(i
=0,right
++; i
<max
; i
++) {
491 Move(&RPT
, right
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
492 Draw(&RPT
, RPT
.cp_x
, gui
.top
-((max
-i
)<<1)); SetAPen(&RPT
,pen
.shade
);
493 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
495 SetAPen(&RPT
,pen
.panel
);
496 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
497 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
502 SetAPen(&RPT
,pen
.shine
);
503 Move(&RPT
, RPT
.cp_x
, gui
.top
-2);
504 Draw(&RPT
, gui
.right
, RPT
.cp_y
);
506 SetBPen(&RPT
, pen
.bg
);
510 /*** Search if a tab is select using mouse ***/
511 Project
select_panel(Project current
, WORD x
)
514 /* Search for the hi-lighted panel */
515 if( x
== NEXT_PROJECT
) new=current
->next
; else
516 if( x
== PREV_PROJECT
) new=current
->prev
; else
518 for(new=first
; new && ++x
; new=new->next
);
520 for(new=first
; new; new=new->next
)
521 if(new->pleft
<= x
&& x
< new->pleft
+new->pwidth
) break;
523 /* Don't change project if it is the same */
524 if(new && new != current
)
526 /* Refresh panel tab first */
527 inv_curs(current
, FALSE
);
528 RPT
.AlgoStyle
= FS_NORMAL
; draw_panel(current
, FALSE
, FALSE
, FALSE
);
529 RPT
.AlgoStyle
= FSF_BOLD
; draw_panel(new, FALSE
, FALSE
, TRUE
);
532 if( active_project(new, FALSE
) == 0 )
533 reshape_panel(current
);
539 /*** Title of panel has changed, update it ***/
540 void update_panel_name( Project active
)
542 /* Assuming that active is the currently selected */
543 active
->labwid
= TextLength(&RPT
, active
->name
,
544 active
->labsize
=strlen(active
->name
));
545 if(active
->state
& MODIFIED
)
547 (active
->modwid
=TextLength(&RPT
, STR_MODIF
, 1));
550 RPT
.AlgoStyle
= FSF_BOLD
;
551 draw_panel(active
, FALSE
, FALSE
, TRUE
);
556 /*** Recompute size of each item and show the active project ***/
557 void reshape_panel(Project active
)
559 Project lst
; UBYTE i
;
560 /* Do not show the project bar if there is only one project */
564 /** Change top position, if not already **/
566 if(gui
.top
== gui
.oldtop
)
567 adjust_win(Wnd
, 1), clear_brcorner();
570 /** Compute items size **/
571 for(lst
=first
,i
=1,height
=(gui
.top
-gui
.oldtop
-2)>>1; lst
; lst
=lst
->next
,i
++)
573 lst
->pleft
= (lst
==first
? gui
.left
: lst
->prev
->pleft
+lst
->prev
->pwidth
-height
);
574 lst
->pwidth
= (gui
.right
+1) * i
/ NbProject
- lst
->pleft
;
575 lst
->labwid
= TextLength(&RPT
, lst
->name
, lst
->labsize
);
577 /** Redraw the project bar **/
578 for(lst
=first
; lst
; lst
=lst
->next
) {
579 RPT
.AlgoStyle
= FS_NORMAL
;
580 /* Active project should be the last drawn */
582 draw_panel(lst
, lst
->prev
==active
|| !lst
->prev
, TRUE
, FALSE
);
584 RPT
.AlgoStyle
= FSF_BOLD
;
585 draw_panel(active
, !active
->prev
, !active
->next
, TRUE
);
588 else if(gui
.oldtop
!= gui
.top
) {
589 SetAPen(&RPT
, pen
.bg
);
590 RectFill(&RPT
, gui
.left
, gui
.oldtop
, gui
.right
, gui
.top
);
591 /** Remove project bar, if it lefts only one project **/
592 adjust_win(Wnd
, 0); clear_brcorner();