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;
50 set_project_name(new, NULL
);
52 else FreeVec(new),new = NULL
;
57 /*** Change content of a project ***/
58 void change_project(Project p
, LINE
*new)
60 flush_undo_buf( &p
->undo
);
61 flush_undo_buf( &p
->redo
);
62 trash_file(p
->the_line
);
63 if(p
->buffer
) FreeVec(p
->buffer
);
64 memset(&p
->nbc
, 0, 4*sizeof(ULONG
));
65 p
->edited
= p
->the_line
= p
->show
= new;
66 p
->top_line
= p
->left_pos
= 0;
68 p
->ycurs
= gui
.topcurs
;
73 /*** Try to load the file contained in the project ***/
74 WORD
load_in_project( Project p
, STRPTR path
)
81 if(RETURN_OK
== (err
= load_file( &args
)))
83 /* File successfully loaded */
84 change_project(p
, args
.lines
);
85 p
->buffer
= args
.buffer
;
86 p
->max_lines
= args
.nblines
;
89 else ThrowError(Wnd
, ErrMsg(err
));
94 /*** Load and create a new project from a path ***/
95 Project
load_and_activate(Project ins
, STRPTR name
, BYTE use_prj
)
98 if( ( new = (use_prj
? ins
: new_project(ins
, &prefs
)) ) )
102 /* Multi-selection: name is actually a (StartUpArgs *) */
103 if( ((StartUpArgs
*)name
)->sa_NbArgs
> 0 )
105 new = create_projects(ins
, ((StartUpArgs
*)name
)->sa_ArgLst
, ((StartUpArgs
*)name
)->sa_NbArgs
);
107 inv_curs(ins
, FALSE
);
108 if(use_prj
== 2) close_project(ins
), FreeVec(ins
);
114 else /* Load a single file into `new' */
116 inv_curs(ins
, FALSE
);
117 /* We have a correct path, try to load file */
118 if( RETURN_OK
== load_in_project(new, name
) )
120 set_project_name(new, name
);
121 /* Add a panel tab and show content of file */
122 if(use_prj
) update_panel_name(ins
);
123 else refresh
:reshape_panel(new);
124 active_project(new, TRUE
);
128 /* Something failed, close project */
129 if(use_prj
== 0) close_project(new),FreeVec(new);
132 else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
136 /*** Reload project, fluch redolog, reduce fragmentation ***/
137 void reload_project( Project p
)
139 /* Save cursor position */
140 ULONG nbl
= p
->nbl
, top
= p
->top_line
;
142 /* Flush all buffers and reload file */
143 unset_modif_mark(p
, TRUE
);
145 load_in_project(p
, p
->path
);
147 /* Reset cursor position, if possible */
148 p
->nbl
= nbl
; p
->top_line
= top
; top
= center_vert( p
);
149 p
->top_line
= p
->nbl
= 0;
150 set_top_line(p
, top
, 0);
151 set_cursor_line(p
, nbl
, p
->top_line
);
155 /*** Insert a file at current cursor position ***/
156 void insert_file( Project p
)
162 if( ( args
.filename
= ask_load(Wnd
, (AskArgs
*)&p
->path
, TRUE
, GetMenuText(208)) ) )
164 if( 0 == (err
= read_file( &args
, &length
)) )
167 reg_group_by(&p
->undo
);
168 if( add_string( &p
->undo
, p
->edited
, p
->nbc
, args
.buffer
, length
, pos
) )
170 /* Just one line concerned? */
171 p
->max_lines
+= pos
[2];
172 if( pos
[1] == 0 ) REDRAW_CURLINE(p
)
174 move_cursor(p
, pos
[0], pos
[1]);
175 if( pos
[1] > 0 ) redraw_content(p
, p
->show
, gui
.topcurs
, gui
.nbline
);
176 if( p
->ccp
.select
) move_selection(p
, p
->nbrc
, p
->nbl
);
177 inv_curs(p
,TRUE
); prop_adj(p
);
179 else ThrowDOSError(Wnd
, args
.filename
);
181 if( args
.buffer
) FreeVec( args
.buffer
);
183 reg_group_by(&p
->undo
);
185 else ThrowError(Wnd
, ErrMsg( err
));
187 FreeVec( args
.filename
);
191 /*** Change and set internal project name variables ***/
192 void set_project_name( Project p
, STRPTR path
)
194 if(p
->path
!= NULL
) FreeVec(p
->path
);
196 p
->name
= (STRPTR
) FilePart(
200 p
->name
= ErrMsg(ERR_NONAME
);
201 p
->labsize
= strlen(p
->name
);
204 /*** Save one projet ***/
205 char save_project(Project p
, char refresh
, char ask
)
207 /* Ask for a name if file doesn't have one */
208 if(p
->path
== NULL
|| ask
)
211 if(NULL
!= (newname
= ask_save(Wnd
, (AskArgs
*)&p
->path
, GetMenuText(105))) &&
212 0 != warn_overwrite( newname
))
213 set_project_name(p
, newname
);
215 /* User cancels or there was errors */
219 unset_modif_mark(p
, FALSE
);
222 SetTitle(Wnd
, p
->path
),
223 update_panel_name( p
);
225 return save_file(p
->path
, p
->the_line
, p
->eol
);
228 /*** Save all modified projects ***/
229 Project
save_projects(Project active
, char close
)
231 Project cur
, p
; int nb
= 0;
234 /* Auto-save modified project */
235 if(p
->state
& MODIFIED
) {
236 if(save_project(p
, FALSE
, FALSE
) == 0) break; else nb
++;
237 if(p
== active
&& close
== 0) SetTitle(Wnd
, p
->path
);
242 cur
= p
; close_project(p
);
243 p
= p
->next
; FreeVec(cur
);
244 if(active
== cur
) active
= p
; nb
= 0;
248 if(NbProject
> 0 && nb
> 0) reshape_panel(active
);
252 /*** Makes project the active one (i.e. visible) ***/
253 char active_project(Project p
, char InitCurs
)
255 /* If file isn't loaded yet, makes it now */
256 if(p
->state
& PAGINATED
)
257 /* This call is subject to failed! */
258 switch( load_in_project(p
, p
->path
) )
260 case RETURN_OK
: p
->state
&= ~PAGINATED
; break;
262 /* This is too nasty to attempt something: close faulty project */
263 close_project(p
); FreeVec(p
); return 0;
267 init_tabstop(p
->tabsize
);
268 SetTitle(Wnd
, p
->path
? p
->path
: p
->name
);
269 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
271 p
->ycurs
= gui
.topcurs
,
274 p
->left_pos
= curs_visible(p
, p
->top_line
),
275 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
277 /* This can speedup a lot, text rendering */
278 if(p
->ccp
.select
!= 0) RP
->Mask
= gui
.selmask
;
279 redraw_content(p
,p
->show
,gui
.topcurs
,gui
.nbline
);
280 if(p
->ccp
.select
== 0) RP
->Mask
= gui
.txtmask
;
286 /*** Test if a string represent an AmigaDOS pattern ***/
287 STRPTR
IsPat( STRPTR name
)
289 int len
= strlen( name
) * 2 + 2;
291 /* 512 bytes is used for ExAll buffer scan */
292 if((tok
= (STRPTR
) AllocVec(len
+ 512, MEMF_CLEAR
)))
294 if( ParsePatternNoCase(name
, tok
+512, len
) > 0 )
301 /*** Create a new project with an incomplete path ***/
302 UBYTE
new_file(Project
*ins
, STRPTR path
)
305 if(NULL
!= (new = new_project(*ins
, &prefs
)))
308 if(RETURN_OK
== (err
= get_full_path(path
, &new->name
))) {
309 set_project_name(new, new->name
); *ins
= new;
310 new->state
= PAGINATED
;
313 ThrowError(Wnd
, ErrMsg(err
));
317 ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
321 /*** Create a set of projects (command line or WBStartup) ***/
322 Project
create_projects(Project ins_after
, APTR args
, ULONG nb
)
326 APTR cwd
= (APTR
) CurrentDir( NULL
);
330 register struct WBArg
*arg
= (struct WBArg
*)args
;
331 for(new = ins_after
; nb
--; arg
++ )
333 CurrentDir(arg
->wa_Lock
);
334 /* Directory scan is required with pattern */
335 if( ( pattern
= IsPat( (STRPTR
) FilePart(arg
->wa_Name
) ) ) )
337 struct ExAllControl
*eac
;
338 struct ExAllData
*ead
;
339 struct FileLock
*lock
;
341 ((STRPTR
)PathPart(arg
->wa_Name
))[0] = 0;
342 if((lock
= (void *) Lock(arg
->wa_Name
, SHARED_LOCK
)))
344 CurrentDir((BPTR
) lock
);
345 if ((eac
= (void *) AllocDosObject(DOS_EXALLCONTROL
,NULL
)))
347 eac
->eac_LastKey
= 0;
348 eac
->eac_MatchString
= pattern
+512;
350 more
= ExAll((BPTR
)lock
, (struct ExAllData
*)pattern
, 512, ED_TYPE
, eac
);
351 if( eac
->eac_Entries
== 0 ) continue;
352 for(ead
= (void *)pattern
; ead
; ead
= ead
->ed_Next
)
353 if(ead
->ed_Type
< 0 && !new_file(&new, ead
->ed_Name
))
356 stop_now
: FreeDosObject(DOS_EXALLCONTROL
, eac
);
358 UnLock((BPTR
) lock
);
359 } /* else bad pattern: discard entry */
362 /* Given file contains no pattern */
363 else if( !new_file(&new, arg
->wa_Name
) )
366 CurrentDir((BPTR
)cwd
);
367 /* Still have no file to edit (pattern with no match) */
368 if(new == NULL
) goto newempty
;
370 /* We have a bunch of projects, try to load at least one */
371 while(new != ins_after
)
372 if(RETURN_OK
!= load_in_project(new, new->path
)) {
373 Project prev
= new->prev
;
374 ThrowDOSError(Wnd
, new->path
);
376 FreeVec(new); new = prev
;
379 newempty
: new = new_project(NULL
, &prefs
);
380 CurrentDir((BPTR
) cwd
);
384 /*** set modification flag on this project ***/
385 void set_modif_mark(Project p
)
387 p
->state
|= MODIFIED
;
390 strcat(p
->name
, STR_MODIF
);
391 update_panel_name( p
);
392 SetTitle(Wnd
, p
->path
);
395 void unset_modif_mark(Project p
, char update
)
398 if( p
->path
&& (p
->state
& MODIFIED
) )
400 p
->labsize
= strlen(p
->name
) - sizeof(STR_MODIF
) + 1;
401 p
->name
[p
->labsize
] = 0;
402 if( update
) SetTitle(Wnd
, p
->path
);
405 if( update
) update_panel_name(p
);
408 /*** Close one project and ressources allocated ***/
409 char close_project(Project p
)
411 /* Close it, only if user knows what he does */
415 trash_file(p
->the_line
);
416 flush_undo_buf( &p
->undo
);
417 flush_undo_buf( &p
->redo
);
418 if(p
->path
) FreeVec(p
->path
);
419 if(p
->buffer
) FreeVec(p
->buffer
);
426 /*** Close all projects, returning the first non-closed or NULL if none ***/
427 Project
close_projects( void )
429 register Project prj
;
433 /* If user doesn't want to close this one */
434 if( close_project(first
) ) FreeVec(prj
);
437 /* If NULL, then we should quit */
442 /*** PROJECT BAR, GRAPHICAL ROUTINES ***/
444 /*** Draw one NextStep-like panel item ***/
445 void draw_panel(Project p
, BYTE rclear
, BYTE lclear
, BYTE activ
)
447 BYTE max
= (gui
.top
-gui
.oldtop
-1) >> 1, i
;
448 WORD right
= p
->pleft
+ p
->pwidth
- max
- 1;
450 WORD bgpan
= (activ
? pen
.abpan
: pen
.bgpan
);
454 SetAPen(&RPT
,pen
.shine
);
455 Move(&RPT
, gui
.left
, gui
.top
-2);
456 Draw(&RPT
, p
->pleft
, RPT
.cp_y
);
459 for(i
=0; i
<max
; i
++) {
460 Move(&RPT
, p
->pleft
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
461 Draw(&RPT
, RPT
.cp_x
, gui
.top
-2-(i
<<1)); SetAPen(&RPT
,pen
.shine
);
462 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
464 SetAPen(&RPT
, pen
.panel
);
465 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
466 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
470 Move(&RPT
, RPT
.cp_x
+1, gui
.oldtop
); SetAPen(&RPT
,pen
.shine
);
471 Draw(&RPT
, right
, gui
.oldtop
); SetAPen(&RPT
, bgpan
);
473 RectFill(&RPT
, p
->pleft
+max
, gui
.oldtop
+1, RPT
.cp_x
, gui
.top
-2);
474 SetABPenDrMd(&RPT
, pen
.fgpan
, bgpan
, JAM2
);
476 /* Text in the box */
477 Move(&RPT
,p
->pleft
+((p
->pwidth
-p
->labwid
)>>1), gui
.oldtop
+prefs
.scrfont
->tf_Baseline
+3 );
478 if(!activ
&& (p
->state
& PAGINATED
)) RPT
.AlgoStyle
|= FSF_ITALIC
;
479 Text(&RPT
,p
->name
,p
->labsize
);
482 for(i
=0,right
++; i
<max
; i
++) {
483 Move(&RPT
, right
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
484 Draw(&RPT
, RPT
.cp_x
, gui
.top
-((max
-i
)<<1)); SetAPen(&RPT
,pen
.shade
);
485 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
487 SetAPen(&RPT
,pen
.panel
);
488 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
489 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
494 SetAPen(&RPT
,pen
.shine
);
495 Move(&RPT
, RPT
.cp_x
, gui
.top
-2);
496 Draw(&RPT
, gui
.right
, RPT
.cp_y
);
498 SetBPen(&RPT
, pen
.bg
);
502 /*** Search if a tab is select using mouse ***/
503 Project
select_panel(Project current
, WORD x
)
506 /* Search for the hi-lighted panel */
507 if( x
== NEXT_PROJECT
) new=current
->next
; else
508 if( x
== PREV_PROJECT
) new=current
->prev
; else
510 for(new=first
; new && ++x
; new=new->next
);
512 for(new=first
; new; new=new->next
)
513 if(new->pleft
<= x
&& x
< new->pleft
+new->pwidth
) break;
515 /* Don't change project if it is the same */
516 if(new && new != current
)
518 /* Refresh panel tab first */
519 inv_curs(current
, FALSE
);
520 RPT
.AlgoStyle
= FS_NORMAL
; draw_panel(current
, FALSE
, FALSE
, FALSE
);
521 RPT
.AlgoStyle
= FSF_BOLD
; draw_panel(new, FALSE
, FALSE
, TRUE
);
524 if( active_project(new, FALSE
) == 0 )
525 reshape_panel(current
);
531 /*** Title of panel has changed, update it ***/
532 void update_panel_name( Project activ
)
534 /* Assuming that activ is the currently selected */
535 activ
->labwid
= TextLength(&RPT
, activ
->name
, activ
->labsize
=strlen(activ
->name
));
538 RPT
.AlgoStyle
= FSF_BOLD
;
539 draw_panel(activ
, FALSE
, FALSE
, TRUE
);
544 /*** Recompute size of each item and show the active project ***/
545 void reshape_panel(Project activ
)
547 Project lst
; UBYTE i
;
548 /* Do not show the project bar if there is only one project */
552 /** Change top position, if not already **/
554 if(gui
.top
== gui
.oldtop
)
555 adjust_win(Wnd
, 1), clear_brcorner();
558 /** Compute items size **/
559 for(lst
=first
,i
=1,height
=(gui
.top
-gui
.oldtop
-2)>>1; lst
; lst
=lst
->next
,i
++)
561 lst
->pleft
= (lst
==first
? gui
.left
: lst
->prev
->pleft
+lst
->prev
->pwidth
-height
);
562 lst
->pwidth
= (gui
.right
+1) * i
/ NbProject
- lst
->pleft
;
563 lst
->labwid
= TextLength(&RPT
, lst
->name
, lst
->labsize
);
565 /** Redraw the project bar **/
566 for(lst
=first
; lst
; lst
=lst
->next
) {
567 RPT
.AlgoStyle
= FS_NORMAL
;
568 /* Active project should be the last drawn */
570 draw_panel(lst
, lst
->prev
==activ
|| !lst
->prev
, TRUE
, FALSE
);
572 RPT
.AlgoStyle
= FSF_BOLD
;
573 draw_panel(activ
, !activ
->prev
, !activ
->next
, TRUE
);
576 else if(gui
.oldtop
!= gui
.top
) {
577 SetAPen(&RPT
, pen
.bg
);
578 RectFill(&RPT
, gui
.left
, gui
.oldtop
, gui
.right
, gui
.top
);
579 /** Remove project bar, if it lefts only one project **/
580 adjust_win(Wnd
, 0); clear_brcorner();