Updated PCI IDs to latest snapshot.
[tangerine.git] / workbench / tools / Edit / Project.c
blob74e401c08e9b2ba139a9a3609f8b3bcbe74f3707
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"
21 #define CATCOMP_NUMBERS /* We will need the string id */
22 #include "strings.h"
24 static Project first = NULL; /* Keep track of first created project */
25 UBYTE NbProject = 0; /* Number of opened projects */
27 #ifndef JANOPREF
28 /*** Alloc a new project ***/
29 Project new_project(Project ins, PREFS *prefs)
31 Project new;
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;
40 NbProject++;
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;
47 new->max_lines = 1;
48 new->undo.prj = new->redo.prj = (APTR) new;
49 new->redo.rbtype = 1;
50 new->protection = 0;
51 set_project_name(new, NULL);
53 else FreeVec(new),new = NULL;
55 return new;
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;
68 p->xcurs = gui.left;
69 p->ycurs = gui.topcurs;
70 p->state = 0;
71 p->savepoint = 0;
74 /*** Try to load the file contained in the project ***/
75 WORD load_in_project( Project p, STRPTR path )
77 LoadFileArgs args;
78 WORD err;
80 args.filename = 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;
88 p->eol = args.eol;
89 p->protection= args.protection &= ~FIBF_ARCHIVE;
91 else ThrowError(Wnd, ErrMsg(err));
93 return err;
96 /*** Load and create a new project from a path ***/
97 Project load_and_activate(Project ins, STRPTR name, BYTE use_prj)
99 Project new;
100 if( ( new = (use_prj ? ins : new_project(ins, &prefs)) ) )
102 if( use_prj >= 2 )
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);
108 if(new != ins ) {
109 inv_curs(ins, FALSE);
110 if(use_prj == 2) close_project(ins), FreeVec(ins);
111 goto refresh;
114 return NULL;
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);
127 return new;
130 /* Something failed, close project */
131 if(use_prj == 0) close_project(new),FreeVec(new);
132 FreeVec(name);
134 else ThrowError(Wnd, ErrMsg(ERR_NOMEM));
135 return NULL;
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);
146 inv_curs(p, FALSE);
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);
154 inv_curs(p, TRUE);
157 /*** Insert a file at current cursor position ***/
158 void insert_file( Project p )
160 LoadFileArgs args;
161 ULONG length;
162 WORD err;
164 if( ( args.filename = ask_load(Wnd, (AskArgs *)&p->path, TRUE, GetMenuText(208)) ) )
166 if( 0 == (err = read_file( &args, &length )) )
168 LONG pos[3];
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);
197 if(path != NULL)
198 p->name = (STRPTR) FilePart(
199 p->path = path);
200 else
201 p->path = NULL,
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)
209 char retval;
211 /* Ask for a name if file doesn't have one */
212 if(p->path == NULL || ask)
214 STRPTR newname;
215 if(NULL != (newname = ask_save(Wnd, (AskArgs *)&p->path, GetMenuText(105))) &&
216 0 != warn_overwrite( newname ))
217 set_project_name(p, newname);
218 else
219 /* User cancelled or there was an error */
220 return 0;
221 p->state = 0;
224 if( refresh )
225 UpdateTitle(Wnd, p),
226 update_panel_name( p );
228 retval = save_file(p->path, p->the_line, p->eol, p->protection);
230 if (retval)
231 unset_modif_mark(p, FALSE);
233 return retval;
236 /*** Save all modified projects ***/
237 Project save_projects(Project active, char close)
239 Project cur, p; int nb = 0;
240 for(p=first; p; )
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);
248 if(close == TRUE)
250 cur = p; close_project(p);
251 p = p->next; FreeVec(cur);
252 if(active == cur) active = p; nb = 0;
254 else p=p->next;
256 if(NbProject > 0 && nb > 0) reshape_panel(active);
257 return 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;
269 case ERR_NOMEM:
270 /* This is too nasty to attempt something: close faulty project */
271 close_project(p); FreeVec(p); return 0;
274 prop_adj(p);
275 init_tabstop(p->tabsize);
276 UpdateTitle(Wnd, p);
277 SetABPenDrMd(RP,pen.fg,pen.bg,JAM2);
278 if(InitCurs)
279 p->ycurs = gui.topcurs,
280 p->xcurs = gui.left;
281 else
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;
289 inv_curs(p,TRUE);
290 draw_info(p);
291 return 1;
294 /*** Test if a string represent an AmigaDOS pattern ***/
295 STRPTR IsPat( STRPTR name )
297 int len = strlen( name ) * 2 + 2;
298 STRPTR tok;
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 )
303 return tok;
304 FreeVec( tok );
306 return NULL;
309 /*** Create a new project with an incomplete path ***/
310 UBYTE new_file(Project *ins, STRPTR path)
312 Project new;
313 if(NULL != (new = new_project(*ins, &prefs)))
315 UBYTE err;
316 if(RETURN_OK == (err = get_full_path(path, &new->name))) {
317 set_project_name(new, new->name); *ins = new;
318 new->state = PAGINATED;
319 return 1;
320 } else
321 ThrowError(Wnd, ErrMsg(err));
322 close_project(new);
323 FreeVec(new);
324 } else
325 ThrowError(Wnd, ErrMsg(ERR_NOMEM));
326 return 0;
329 /*** Create a set of projects (command line or WBStartup) ***/
330 Project create_projects(Project ins_after, APTR args, ULONG nb)
332 Project new;
333 STRPTR pattern;
334 APTR cwd = (APTR) CurrentDir( NULL );
336 if(nb > 0)
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;
348 char more;
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;
357 do {
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))
362 goto stop_now;
363 } while( more );
364 stop_now: FreeDosObject(DOS_EXALLCONTROL, eac);
366 UnLock((BPTR) lock );
367 } /* else bad pattern: discard entry */
368 FreeVec( pattern );
370 /* Given file contains no pattern */
371 else if( !new_file(&new, arg->wa_Name) )
372 break;
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);
383 close_project(new);
384 FreeVec(new); new = prev;
385 } else break;
386 } else
387 newempty: new = new_project(NULL, &prefs);
388 CurrentDir((BPTR) cwd );
389 return new;
392 /*** set modification flag on this project ***/
393 void set_modif_mark(Project p)
395 p->state |= MODIFIED;
396 if( p->path )
398 update_panel_name( p );
399 UpdateTitle(Wnd, p);
402 void unset_modif_mark(Project p, char update)
404 commit_work( p );
405 p->state = 0;
406 if(update)
408 update_panel_name(p);
409 UpdateTitle(Wnd, p);
413 /*** Close one project and ressources allocated ***/
414 char close_project(Project p)
416 /* Close it, only if user knows what he does */
417 if( warn_modif(p) )
419 NbProject--;
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);
425 Destroy(&first, p);
426 return TRUE;
428 return FALSE;
431 /*** Close all projects, returning the first non-closed or NULL if none ***/
432 Project close_projects( void )
434 register Project prj;
435 while( first )
437 prj = first;
438 /* If user doesn't want to close this one */
439 if( close_project(first) ) FreeVec(prj);
440 else break;
442 /* If NULL, then we should quit */
443 return first;
445 #endif
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);
457 /* Bottom line */
458 if(rclear == 0) {
459 SetAPen(&RPT,pen.shine);
460 Move(&RPT, gui.left, gui.top-2);
461 Draw(&RPT, p->pleft, RPT.cp_y);
463 /* Right shape */
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);
468 if(rclear) {
469 SetAPen(&RPT, pen.panel);
470 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
471 Draw(&RPT, RPT.cp_x, gui.oldtop);
474 /* Upper line */
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);
489 /* Right shape */
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);
494 if(lclear && i) {
495 SetAPen(&RPT,pen.panel);
496 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
497 Draw(&RPT, RPT.cp_x, gui.oldtop);
500 /* Bottom line */
501 if( !lclear ) {
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);
509 #ifndef JANOPREF
510 /*** Search if a tab is select using mouse ***/
511 Project select_panel(Project current, WORD x)
513 Project new;
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
517 if( x < 0 )
518 for(new=first; new && ++x; new=new->next);
519 else
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);
531 /* Then content */
532 if( active_project(new, FALSE) == 0 )
533 reshape_panel(current);
534 else return new;
536 return 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)
546 active->labwid +=
547 (active->modwid=TextLength(&RPT, STR_MODIF, 1));
548 if(NbProject > 1)
550 RPT.AlgoStyle = FSF_BOLD;
551 draw_panel(active, FALSE, FALSE, TRUE);
554 #endif
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 */
561 if( NbProject > 1 )
563 WORD height;
564 /** Change top position, if not already **/
565 #ifndef JANOPREF
566 if(gui.top == gui.oldtop)
567 adjust_win(Wnd, 1), clear_brcorner();
568 #endif
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 */
581 if(lst != active)
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);
587 #ifndef JANOPREF
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();
594 #endif