update experimental gcc 6 patch to gcc 6.1.0 release
[AROS.git] / workbench / tools / Edit / Project.c
blobcbec57d3e9d6aa05bbd617eb0243d1eab34a352d
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 <workbench/startup.h>
12 #include "Project.h"
13 #include "Gui.h"
14 #include "ClipLoc.h"
15 #include "DiskIO.h"
16 #include "Utility.h"
17 #include "Cursor.h"
18 #include "Jed.h"
19 #include "ProtoTypes.h"
20 #include "Print.h"
22 #define CATCOMP_NUMBERS /* We will need the string id */
23 #include "strings.h"
25 static Project first = NULL; /* Keep track of first created project */
26 UBYTE NbProject = 0; /* Number of opened projects */
28 #ifndef JANOPREF
29 /*** Alloc a new project ***/
30 Project new_project(Project ins, PREFS *prefs)
32 Project new;
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;
41 NbProject++;
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;
48 new->max_lines = 1;
49 new->undo.prj = new->redo.prj = (APTR) new;
50 new->redo.rbtype = 1;
51 new->protection = 0;
52 set_project_name(new, NULL);
54 else FreeVec(new),new = NULL;
56 return new;
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;
69 p->xcurs = gui.left;
70 p->ycurs = gui.topcurs;
71 p->state = 0;
72 p->savepoint = 0;
75 /*** Try to load the file contained in the project ***/
76 WORD load_in_project( Project p, STRPTR path )
78 LoadFileArgs args;
79 WORD err;
81 args.filename = 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;
89 p->eol = args.eol;
90 p->protection= args.protection &= ~FIBF_ARCHIVE;
92 else ThrowError(Wnd, ErrMsg(err));
94 return err;
97 /*** Load and create a new project from a path ***/
98 Project load_and_activate(Project ins, STRPTR name, BYTE use_prj)
100 Project new;
101 if( ( new = (use_prj ? ins : new_project(ins, &prefs)) ) )
103 if( use_prj >= 2 )
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);
109 if(new != ins ) {
110 inv_curs(ins, FALSE);
111 if(use_prj == 2) close_project(ins), FreeVec(ins);
112 goto refresh;
115 return NULL;
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);
128 return new;
131 /* Something failed, close project */
132 if(use_prj == 0) close_project(new),FreeVec(new);
133 FreeVec(name);
135 else ThrowError(Wnd, ErrMsg(ERR_NOMEM));
136 return NULL;
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);
147 inv_curs(p, FALSE);
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);
155 inv_curs(p, TRUE);
158 /*** Insert a file at current cursor position ***/
159 void insert_file( Project p )
161 LoadFileArgs args;
162 ULONG length;
163 WORD err;
165 if( ( args.filename = ask_load(Wnd, (AskArgs *)&p->path, TRUE, GetMenuText(208)) ) )
167 if( 0 == (err = read_file( &args, &length )) )
169 LONG pos[3];
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);
198 if(path != NULL)
199 p->name = (STRPTR) FilePart(
200 p->path = path);
201 else
202 p->path = NULL,
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)
216 char retval;
218 /* Ask for a name if file doesn't have one */
219 if(p->path == NULL || ask)
221 STRPTR newname;
222 if(NULL != (newname = ask_save(Wnd, (AskArgs *)&p->path, GetMenuText(105))) &&
223 0 != warn_overwrite( newname ))
224 set_project_name(p, newname);
225 else
226 /* User cancelled or there was an error */
227 return 0;
228 p->state = 0;
231 if( refresh )
233 UpdateTitle(Wnd, p), update_panel_name( p );
236 retval = save_file(p->path, p->the_line, p->eol, p->protection);
238 if (retval)
240 unset_modif_mark(p, TRUE);
243 return retval;
246 /*** Save all modified projects ***/
247 Project save_projects(Project active, char close)
249 Project cur, p; int nb = 0;
250 for(p=first; p; )
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);
258 if(close == TRUE)
260 cur = p; close_project(p);
261 p = p->next; FreeVec(cur);
262 if(active == cur)
263 active = p;
264 nb = 0;
266 else p=p->next;
268 if(NbProject > 0 && nb > 0) reshape_panel(active);
269 return 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;
281 case ERR_NOMEM:
282 /* This is too nasty to attempt something: close faulty project */
283 close_project(p); FreeVec(p); return 0;
286 prop_adj(p);
287 init_tabstop(p->tabsize);
288 UpdateTitle(Wnd, p);
289 SetABPenDrMd(RP,pen.fg,pen.bg,JAM2);
290 if(InitCurs)
291 p->ycurs = gui.topcurs,
292 p->xcurs = gui.left;
293 else
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;
301 inv_curs(p,TRUE);
302 draw_info(p);
303 return 1;
306 /*** Test if a string represent an AmigaDOS pattern ***/
307 STRPTR IsPat( STRPTR name )
309 int len = strlen( name ) * 2 + 2;
310 STRPTR tok;
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 )
315 return tok;
316 FreeVec( tok );
318 return NULL;
321 /*** Create a new project with an incomplete path ***/
322 UBYTE new_file(Project *ins, STRPTR path)
324 Project new;
325 if(NULL != (new = new_project(*ins, &prefs)))
327 UBYTE err;
328 if(RETURN_OK == (err = get_full_path(path, &new->name))) {
329 set_project_name(new, new->name); *ins = new;
330 new->state = PAGINATED;
331 return 1;
332 } else
333 ThrowError(Wnd, ErrMsg(err));
334 close_project(new);
335 FreeVec(new);
336 } else
337 ThrowError(Wnd, ErrMsg(ERR_NOMEM));
338 return 0;
341 /*** Create a set of projects (command line or WBStartup) ***/
342 Project create_projects(Project ins_after, APTR args, ULONG nb)
344 Project new;
345 STRPTR pattern;
346 APTR cwd = (APTR) CurrentDir( BNULL );
348 if(nb > 0)
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;
360 char more;
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;
369 do {
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))
374 goto stop_now;
375 } while( more );
376 stop_now: FreeDosObject(DOS_EXALLCONTROL, eac);
378 UnLock((BPTR) lock );
379 } /* else bad pattern: discard entry */
380 FreeVec( pattern );
382 /* Given file contains no pattern */
383 else if( !new_file(&new, arg->wa_Name) )
384 break;
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);
395 close_project(new);
396 FreeVec(new); new = prev;
397 } else break;
398 } else
399 newempty: new = new_project(NULL, &prefs);
400 CurrentDir((BPTR) cwd );
401 return new;
404 /*** set modification flag on this project ***/
405 void set_modif_mark(Project p)
407 p->state |= MODIFIED;
408 if( p->path )
410 update_panel_name( p );
411 UpdateTitle(Wnd, p);
414 void unset_modif_mark(Project p, char update)
416 commit_work( p );
417 p->state = 0;
418 if(update)
420 update_panel_name(p);
421 UpdateTitle(Wnd, p);
425 /*** Close one project and ressources allocated ***/
426 char close_project(Project p)
428 /* Close it, only if user knows what he does */
429 if( warn_modif(p) )
431 NbProject--;
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);
437 Destroy(&first, p);
438 return TRUE;
440 return FALSE;
443 /*** Close all projects, returning the first non-closed or NULL if none ***/
444 Project close_projects( void )
446 register Project prj;
447 while( first )
449 prj = first;
450 /* If user doesn't want to close this one */
451 if( close_project(first) ) FreeVec(prj);
452 else break;
454 /* If NULL, then we should quit */
455 return first;
457 #endif
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);
469 /* Bottom line */
470 if(rclear == 0) {
471 SetAPen(&RPT,pen.shine);
472 Move(&RPT, gui.left, gui.top-2);
473 Draw(&RPT, p->pleft, RPT.cp_y);
475 /* Right shape */
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);
480 if(rclear) {
481 SetAPen(&RPT, pen.panel);
482 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
483 Draw(&RPT, RPT.cp_x, gui.oldtop);
486 /* Upper line */
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);
501 /* Right shape */
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);
506 if(lclear && i) {
507 SetAPen(&RPT,pen.panel);
508 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
509 Draw(&RPT, RPT.cp_x, gui.oldtop);
512 /* Bottom line */
513 if( !lclear ) {
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);
521 #ifndef JANOPREF
522 /*** Search if a tab is select using mouse ***/
523 Project select_panel(Project current, WORD x)
525 Project new;
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
529 if( x < 0 )
530 for(new=first; new && ++x; new=new->next);
531 else
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);
543 /* Then content */
544 if( active_project(new, FALSE) == 0 )
545 reshape_panel(current);
546 else return new;
548 return 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)
558 active->labwid +=
559 (active->modwid=TextLength(&RPT, STR_MODIF, 1));
560 if(NbProject > 1)
562 RPT.AlgoStyle = FSF_BOLD;
563 draw_panel(active, FALSE, FALSE, TRUE);
566 #endif
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 */
573 if( NbProject > 1 )
575 WORD height;
576 /** Change top position, if not already **/
577 #ifndef JANOPREF
578 if(gui.top == gui.oldtop)
579 adjust_win(Wnd, 1), clear_brcorner();
580 #endif
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 */
593 if(lst != active)
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);
599 #ifndef JANOPREF
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();
606 #endif