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, fluch 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 projet ***/
207 char save_project(Project p
, char refresh
, char ask
)
209 /* Ask for a name if file doesn't have one */
210 if(p
->path
== NULL
|| ask
)
213 if(NULL
!= (newname
= ask_save(Wnd
, (AskArgs
*)&p
->path
, GetMenuText(105))) &&
214 0 != warn_overwrite( newname
))
215 set_project_name(p
, newname
);
217 /* User cancels or there was errors */
221 unset_modif_mark(p
, FALSE
);
224 SetTitle(Wnd
, p
->path
),
225 update_panel_name( p
);
227 return save_file(p
->path
, p
->the_line
, p
->eol
, p
->protection
);
230 /*** Save all modified projects ***/
231 Project
save_projects(Project active
, char close
)
233 Project cur
, p
; int nb
= 0;
236 /* Auto-save modified project */
237 if(p
->state
& MODIFIED
) {
238 if(save_project(p
, FALSE
, FALSE
) == 0) break; else nb
++;
239 if(p
== active
&& close
== 0) SetTitle(Wnd
, p
->path
);
244 cur
= p
; close_project(p
);
245 p
= p
->next
; FreeVec(cur
);
246 if(active
== cur
) active
= p
; nb
= 0;
250 if(NbProject
> 0 && nb
> 0) reshape_panel(active
);
254 /*** Makes project the active one (i.e. visible) ***/
255 char active_project(Project p
, char InitCurs
)
257 /* If file isn't loaded yet, makes it now */
258 if(p
->state
& PAGINATED
)
259 /* This call is subject to failed! */
260 switch( load_in_project(p
, p
->path
) )
262 case RETURN_OK
: p
->state
&= ~PAGINATED
; break;
264 /* This is too nasty to attempt something: close faulty project */
265 close_project(p
); FreeVec(p
); return 0;
269 init_tabstop(p
->tabsize
);
270 SetTitle(Wnd
, p
->path
? p
->path
: p
->name
);
271 SetABPenDrMd(RP
,pen
.fg
,pen
.bg
,JAM2
);
273 p
->ycurs
= gui
.topcurs
,
276 p
->left_pos
= curs_visible(p
, p
->top_line
),
277 p
->xcurs
= (p
->nbrc
-p
->left_pos
)*XSIZE
+ gui
.left
;
279 /* This can speedup a lot, text rendering */
280 if(p
->ccp
.select
!= 0) RP
->Mask
= gui
.selmask
;
281 redraw_content(p
,p
->show
,gui
.topcurs
,gui
.nbline
);
282 if(p
->ccp
.select
== 0) RP
->Mask
= gui
.txtmask
;
288 /*** Test if a string represent an AmigaDOS pattern ***/
289 STRPTR
IsPat( STRPTR name
)
291 int len
= strlen( name
) * 2 + 2;
293 /* 512 bytes is used for ExAll buffer scan */
294 if((tok
= (STRPTR
) AllocVec(len
+ 512, MEMF_CLEAR
)))
296 if( ParsePatternNoCase(name
, tok
+512, len
) > 0 )
303 /*** Create a new project with an incomplete path ***/
304 UBYTE
new_file(Project
*ins
, STRPTR path
)
307 if(NULL
!= (new = new_project(*ins
, &prefs
)))
310 if(RETURN_OK
== (err
= get_full_path(path
, &new->name
))) {
311 set_project_name(new, new->name
); *ins
= new;
312 new->state
= PAGINATED
;
315 ThrowError(Wnd
, ErrMsg(err
));
319 ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
323 /*** Create a set of projects (command line or WBStartup) ***/
324 Project
create_projects(Project ins_after
, APTR args
, ULONG nb
)
328 APTR cwd
= (APTR
) CurrentDir( NULL
);
332 register struct WBArg
*arg
= (struct WBArg
*)args
;
333 for(new = ins_after
; nb
--; arg
++ )
335 CurrentDir(arg
->wa_Lock
);
336 /* Directory scan is required with pattern */
337 if( ( pattern
= IsPat( (STRPTR
) FilePart(arg
->wa_Name
) ) ) )
339 struct ExAllControl
*eac
;
340 struct ExAllData
*ead
;
341 struct FileLock
*lock
;
343 ((STRPTR
)PathPart(arg
->wa_Name
))[0] = 0;
344 if((lock
= (void *) Lock(arg
->wa_Name
, SHARED_LOCK
)))
346 CurrentDir((BPTR
) lock
);
347 if ((eac
= (void *) AllocDosObject(DOS_EXALLCONTROL
,NULL
)))
349 eac
->eac_LastKey
= 0;
350 eac
->eac_MatchString
= pattern
+512;
352 more
= ExAll((BPTR
)lock
, (struct ExAllData
*)pattern
, 512, ED_TYPE
, eac
);
353 if( eac
->eac_Entries
== 0 ) continue;
354 for(ead
= (void *)pattern
; ead
; ead
= ead
->ed_Next
)
355 if(ead
->ed_Type
< 0 && !new_file(&new, ead
->ed_Name
))
358 stop_now
: FreeDosObject(DOS_EXALLCONTROL
, eac
);
360 UnLock((BPTR
) lock
);
361 } /* else bad pattern: discard entry */
364 /* Given file contains no pattern */
365 else if( !new_file(&new, arg
->wa_Name
) )
368 CurrentDir((BPTR
)cwd
);
369 /* Still have no file to edit (pattern with no match) */
370 if(new == NULL
) goto newempty
;
372 /* We have a bunch of projects, try to load at least one */
373 while(new != ins_after
)
374 if(RETURN_OK
!= load_in_project(new, new->path
)) {
375 Project prev
= new->prev
;
376 ThrowDOSError(Wnd
, new->path
);
378 FreeVec(new); new = prev
;
381 newempty
: new = new_project(NULL
, &prefs
);
382 CurrentDir((BPTR
) cwd
);
386 /*** set modification flag on this project ***/
387 void set_modif_mark(Project p
)
389 p
->state
|= MODIFIED
;
392 strcat(p
->name
, STR_MODIF
);
393 update_panel_name( p
);
394 SetTitle(Wnd
, p
->path
);
397 void unset_modif_mark(Project p
, char update
)
400 if( p
->path
&& (p
->state
& MODIFIED
) )
402 p
->labsize
= strlen(p
->name
) - sizeof(STR_MODIF
) + 1;
403 p
->name
[p
->labsize
] = 0;
404 if( update
) SetTitle(Wnd
, p
->path
);
407 if( update
) update_panel_name(p
);
410 /*** Close one project and ressources allocated ***/
411 char close_project(Project p
)
413 /* Close it, only if user knows what he does */
417 trash_file(p
->the_line
);
418 flush_undo_buf( &p
->undo
);
419 flush_undo_buf( &p
->redo
);
420 if(p
->path
) FreeVec(p
->path
);
421 if(p
->buffer
) FreeVec(p
->buffer
);
428 /*** Close all projects, returning the first non-closed or NULL if none ***/
429 Project
close_projects( void )
431 register Project prj
;
435 /* If user doesn't want to close this one */
436 if( close_project(first
) ) FreeVec(prj
);
439 /* If NULL, then we should quit */
444 /*** PROJECT BAR, GRAPHICAL ROUTINES ***/
446 /*** Draw one NextStep-like panel item ***/
447 void draw_panel(Project p
, BYTE rclear
, BYTE lclear
, BYTE activ
)
449 BYTE max
= (gui
.top
-gui
.oldtop
-1) >> 1, i
;
450 WORD right
= p
->pleft
+ p
->pwidth
- max
- 1;
452 WORD bgpan
= (activ
? pen
.abpan
: pen
.bgpan
);
456 SetAPen(&RPT
,pen
.shine
);
457 Move(&RPT
, gui
.left
, gui
.top
-2);
458 Draw(&RPT
, p
->pleft
, RPT
.cp_y
);
461 for(i
=0; i
<max
; i
++) {
462 Move(&RPT
, p
->pleft
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
463 Draw(&RPT
, RPT
.cp_x
, gui
.top
-2-(i
<<1)); SetAPen(&RPT
,pen
.shine
);
464 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
466 SetAPen(&RPT
, pen
.panel
);
467 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
468 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
472 Move(&RPT
, RPT
.cp_x
+1, gui
.oldtop
); SetAPen(&RPT
,pen
.shine
);
473 Draw(&RPT
, right
, gui
.oldtop
); SetAPen(&RPT
, bgpan
);
475 RectFill(&RPT
, p
->pleft
+max
, gui
.oldtop
+1, RPT
.cp_x
, gui
.top
-2);
476 SetABPenDrMd(&RPT
, pen
.fgpan
, bgpan
, JAM2
);
478 /* Text in the box */
479 Move(&RPT
,p
->pleft
+((p
->pwidth
-p
->labwid
)>>1), gui
.oldtop
+prefs
.scrfont
->tf_Baseline
+3 );
480 if(!activ
&& (p
->state
& PAGINATED
)) RPT
.AlgoStyle
|= FSF_ITALIC
;
481 Text(&RPT
,p
->name
,p
->labsize
);
484 for(i
=0,right
++; i
<max
; i
++) {
485 Move(&RPT
, right
+i
, gui
.top
-2); SetAPen(&RPT
, bgpan
);
486 Draw(&RPT
, RPT
.cp_x
, gui
.top
-((max
-i
)<<1)); SetAPen(&RPT
,pen
.shade
);
487 Draw(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
489 SetAPen(&RPT
,pen
.panel
);
490 Move(&RPT
, RPT
.cp_x
, RPT
.cp_y
-1);
491 Draw(&RPT
, RPT
.cp_x
, gui
.oldtop
);
496 SetAPen(&RPT
,pen
.shine
);
497 Move(&RPT
, RPT
.cp_x
, gui
.top
-2);
498 Draw(&RPT
, gui
.right
, RPT
.cp_y
);
500 SetBPen(&RPT
, pen
.bg
);
504 /*** Search if a tab is select using mouse ***/
505 Project
select_panel(Project current
, WORD x
)
508 /* Search for the hi-lighted panel */
509 if( x
== NEXT_PROJECT
) new=current
->next
; else
510 if( x
== PREV_PROJECT
) new=current
->prev
; else
512 for(new=first
; new && ++x
; new=new->next
);
514 for(new=first
; new; new=new->next
)
515 if(new->pleft
<= x
&& x
< new->pleft
+new->pwidth
) break;
517 /* Don't change project if it is the same */
518 if(new && new != current
)
520 /* Refresh panel tab first */
521 inv_curs(current
, FALSE
);
522 RPT
.AlgoStyle
= FS_NORMAL
; draw_panel(current
, FALSE
, FALSE
, FALSE
);
523 RPT
.AlgoStyle
= FSF_BOLD
; draw_panel(new, FALSE
, FALSE
, TRUE
);
526 if( active_project(new, FALSE
) == 0 )
527 reshape_panel(current
);
533 /*** Title of panel has changed, update it ***/
534 void update_panel_name( Project activ
)
536 /* Assuming that activ is the currently selected */
537 activ
->labwid
= TextLength(&RPT
, activ
->name
, activ
->labsize
=strlen(activ
->name
));
540 RPT
.AlgoStyle
= FSF_BOLD
;
541 draw_panel(activ
, FALSE
, FALSE
, TRUE
);
546 /*** Recompute size of each item and show the active project ***/
547 void reshape_panel(Project activ
)
549 Project lst
; UBYTE i
;
550 /* Do not show the project bar if there is only one project */
554 /** Change top position, if not already **/
556 if(gui
.top
== gui
.oldtop
)
557 adjust_win(Wnd
, 1), clear_brcorner();
560 /** Compute items size **/
561 for(lst
=first
,i
=1,height
=(gui
.top
-gui
.oldtop
-2)>>1; lst
; lst
=lst
->next
,i
++)
563 lst
->pleft
= (lst
==first
? gui
.left
: lst
->prev
->pleft
+lst
->prev
->pwidth
-height
);
564 lst
->pwidth
= (gui
.right
+1) * i
/ NbProject
- lst
->pleft
;
565 lst
->labwid
= TextLength(&RPT
, lst
->name
, lst
->labsize
);
567 /** Redraw the project bar **/
568 for(lst
=first
; lst
; lst
=lst
->next
) {
569 RPT
.AlgoStyle
= FS_NORMAL
;
570 /* Active project should be the last drawn */
572 draw_panel(lst
, lst
->prev
==activ
|| !lst
->prev
, TRUE
, FALSE
);
574 RPT
.AlgoStyle
= FSF_BOLD
;
575 draw_panel(activ
, !activ
->prev
, !activ
->next
, TRUE
);
578 else if(gui
.oldtop
!= gui
.top
) {
579 SetAPen(&RPT
, pen
.bg
);
580 RectFill(&RPT
, gui
.left
, gui
.oldtop
, gui
.right
, gui
.top
);
581 /** Remove project bar, if it lefts only one project **/
582 adjust_win(Wnd
, 0); clear_brcorner();