revert between 56095 -> 55830 in arch
[AROS.git] / workbench / tools / Edit / Project.c
blob454bb22e4e11ad301f22029af8f078446034b347
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 **************************************************************/
7 #include <dos/dos.h>
8 #include <dos/exall.h>
9 #include <graphics/rastport.h>
10 #include <exec/memory.h>
11 #include <libraries/asl.h>
12 #include <workbench/startup.h>
13 #include "Project.h"
14 #include "Gui.h"
15 #include "ClipLoc.h"
16 #include "DiskIO.h"
17 #include "Utility.h"
18 #include "Cursor.h"
19 #include "Jed.h"
20 #include "ProtoTypes.h"
21 #include "Print.h"
23 #define CATCOMP_NUMBERS /* We will need the string id */
24 #include "strings.h"
26 #define DEBUG 0
27 #include <aros/debug.h>
29 static Project first = NULL; /* Keep track of first created project */
30 UBYTE NbProject = 0; /* Number of opened projects */
32 #ifndef JANOPREF
33 /*** Alloc a new project ***/
34 Project new_project(Project ins, PREFS *prefs)
36 Project new;
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;
45 NbProject++;
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;
52 new->max_lines = 1;
53 new->undo.prj = new->redo.prj = (APTR) new;
54 new->redo.rbtype = 1;
55 new->protection = 0;
56 set_project_name(new, NULL);
58 else FreeVec(new),new = NULL;
60 return new;
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;
73 p->xcurs = gui.left;
74 p->ycurs = gui.topcurs;
75 p->state = 0;
76 p->savepoint = 0;
79 /*** Try to load the file contained in the project ***/
80 WORD load_in_project( Project p, STRPTR path )
82 LoadFileArgs args;
83 WORD err;
85 args.filename = 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;
93 p->eol = args.eol;
94 p->protection= args.protection &= ~FIBF_ARCHIVE;
96 else ThrowError(Wnd, ErrMsg(err));
98 return err;
101 /*** Load and create a new project from a path ***/
102 Project load_and_activate(Project ins, STRPTR name, BYTE use_prj)
104 Project new = NULL;
106 if( ( new = (use_prj ? ins : new_project(ins, &prefs)) ) )
108 if( use_prj >= 2 )
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);
114 if(new != ins ) {
115 inv_curs(ins, FALSE);
116 if(use_prj == 2) close_project(ins), FreeVec(ins);
117 goto refresh;
120 return NULL;
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);
133 return new;
136 /* Something failed, close project */
137 if(use_prj == 0) close_project(new),FreeVec(new);
138 FreeVec(name);
140 else ThrowError(Wnd, ErrMsg(ERR_NOMEM));
141 return NULL;
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;
148 Project new = NULL;
150 if( ( new = (use_prj ? ins : new_project(ins, &prefs)) ) )
152 if( use_prj >= 2 )
154 /* Multi-selection */
155 if( fr->fr_NumArgs > 0 )
157 new = create_projects(ins, fr->fr_ArgList, fr->fr_NumArgs);
158 if(new != ins ) {
159 inv_curs(ins, FALSE);
160 if(use_prj == 2) close_project(ins), FreeVec(ins);
161 goto refresh;
164 return NULL;
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);
177 return new;
180 /* Something failed, close project */
181 if(use_prj == 0) close_project(new),FreeVec(new);
182 FreeVec(name);
184 else ThrowError(Wnd, ErrMsg(ERR_NOMEM));
185 return NULL;
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);
196 inv_curs(p, FALSE);
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);
204 inv_curs(p, TRUE);
207 /*** Insert a file at current cursor position ***/
208 void insert_file( Project p )
210 LoadFileArgs args;
211 ULONG length;
212 WORD err;
214 if( ( args.filename = ask_load(Wnd, (AskArgs *)&p->path, TRUE, GetMenuText(208)) ) )
216 if( 0 == (err = read_file( &args, &length )) )
218 LONG pos[3];
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);
247 if(path != NULL)
248 p->name = (STRPTR) FilePart(
249 p->path = path);
250 else
251 p->path = NULL,
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)
265 char retval;
267 /* Ask for a name if file doesn't have one */
268 if(p->path == NULL || ask)
270 STRPTR newname;
271 if(NULL != (newname = ask_save(Wnd, (AskArgs *)&p->path, GetMenuText(105))) &&
272 0 != warn_overwrite( newname ))
273 set_project_name(p, newname);
274 else
275 /* User cancelled or there was an error */
276 return 0;
277 p->state = 0;
280 if( refresh )
282 UpdateTitle(Wnd, p), update_panel_name( p );
285 retval = save_file(p->path, p->the_line, p->eol, p->protection);
287 if (retval)
289 unset_modif_mark(p, TRUE);
292 return retval;
295 /*** Save all modified projects ***/
296 Project save_projects(Project active, char close)
298 Project cur, p; int nb = 0;
299 for(p=first; p; )
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);
307 if(close == TRUE)
309 cur = p; close_project(p);
310 p = p->next; FreeVec(cur);
311 if(active == cur)
312 active = p;
313 nb = 0;
315 else p=p->next;
317 if(NbProject > 0 && nb > 0) reshape_panel(active);
318 return 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;
330 case ERR_NOMEM:
331 /* This is too nasty to attempt something: close faulty project */
332 close_project(p); FreeVec(p); return 0;
335 prop_adj(p);
336 init_tabstop(p->tabsize);
337 UpdateTitle(Wnd, p);
338 SetABPenDrMd(RP,pen.fg,pen.bg,JAM2);
339 if(InitCurs)
340 p->ycurs = gui.topcurs,
341 p->xcurs = gui.left;
342 else
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;
350 inv_curs(p,TRUE);
351 draw_info(p);
352 return 1;
355 /*** Test if a string represent an AmigaDOS pattern ***/
356 STRPTR IsPat( STRPTR name )
358 int len = strlen( name ) * 2 + 2;
359 STRPTR tok;
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 )
365 return tok;
367 FreeVec( tok );
369 return NULL;
372 /*** Create a new project with an incomplete path ***/
373 UBYTE new_file(Project *ins, STRPTR path)
375 Project new;
376 if(NULL != (new = new_project(*ins, &prefs)))
378 UBYTE err;
379 if(RETURN_OK == (err = get_full_path(path, &new->name))) {
380 set_project_name(new, new->name); *ins = new;
381 new->state = PAGINATED;
382 return 1;
383 } else
384 ThrowError(Wnd, ErrMsg(err));
385 close_project(new);
386 FreeVec(new);
387 } else
388 ThrowError(Wnd, ErrMsg(ERR_NOMEM));
389 return 0;
392 /*** Create a set of projects (command line or WBStartup) ***/
393 Project create_projects(Project ins_after, APTR args, ULONG nb)
395 Project new;
396 STRPTR pattern;
397 APTR cwd = (APTR) CurrentDir( BNULL );
399 if(nb > 0)
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;
412 char more;
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;
422 do {
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))
428 goto stop_now;
430 } while( more );
431 stop_now: FreeDosObject(DOS_EXALLCONTROL, eac);
433 UnLock((BPTR) lock );
434 } /* else bad pattern: discard entry */
435 FreeVec( pattern );
437 /* Given file contains no pattern */
438 else if( !new_file(&new, arg->wa_Name) )
439 break;
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);
450 close_project(new);
451 FreeVec(new); new = prev;
452 } else break;
453 } else
454 newempty: new = new_project(NULL, &prefs);
455 CurrentDir((BPTR) cwd );
456 return new;
459 /*** set modification flag on this project ***/
460 void set_modif_mark(Project p)
462 p->state |= MODIFIED;
463 if( p->path )
465 update_panel_name( p );
466 UpdateTitle(Wnd, p);
469 void unset_modif_mark(Project p, char update)
471 commit_work( p );
472 p->state = 0;
473 if(update)
475 update_panel_name(p);
476 UpdateTitle(Wnd, p);
480 /*** Close one project and ressources allocated ***/
481 char close_project(Project p)
483 /* Close it, only if user knows what he does */
484 if( warn_modif(p) )
486 NbProject--;
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);
492 Destroy(&first, p);
493 return TRUE;
495 return FALSE;
498 /*** Close all projects, returning the first non-closed or NULL if none ***/
499 Project close_projects( void )
501 register Project prj;
502 while( first )
504 prj = first;
505 /* If user doesn't want to close this one */
506 if( close_project(first) ) FreeVec(prj);
507 else break;
509 /* If NULL, then we should quit */
510 return first;
512 #endif
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);
524 /* Bottom line */
525 if(rclear == 0) {
526 SetAPen(&RPT,pen.shine);
527 Move(&RPT, gui.left, gui.top-2);
528 Draw(&RPT, p->pleft, RPT.cp_y);
530 /* Right shape */
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);
535 if(rclear) {
536 SetAPen(&RPT, pen.panel);
537 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
538 Draw(&RPT, RPT.cp_x, gui.oldtop);
541 /* Upper line */
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);
556 /* Right shape */
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);
561 if(lclear && i) {
562 SetAPen(&RPT,pen.panel);
563 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
564 Draw(&RPT, RPT.cp_x, gui.oldtop);
567 /* Bottom line */
568 if( !lclear ) {
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);
576 #ifndef JANOPREF
577 /*** Search if a tab is select using mouse ***/
578 Project select_panel(Project current, WORD x)
580 Project new;
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
584 if( x < 0 )
585 for(new=first; new && ++x; new=new->next);
586 else
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);
598 /* Then content */
599 if( active_project(new, FALSE) == 0 )
600 reshape_panel(current);
601 else return new;
603 return 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)
613 active->labwid +=
614 (active->modwid=TextLength(&RPT, STR_MODIF, 1));
615 if(NbProject > 1)
617 RPT.AlgoStyle = FSF_BOLD;
618 draw_panel(active, FALSE, FALSE, TRUE);
621 #endif
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 */
628 if( NbProject > 1 )
630 WORD height;
631 /** Change top position, if not already **/
632 #ifndef JANOPREF
633 if(gui.top == gui.oldtop)
634 adjust_win(Wnd, 1), clear_brcorner();
635 #endif
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 */
648 if(lst != active)
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);
654 #ifndef JANOPREF
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();
661 #endif