add casts to zune macros to silence some warnings
[tangerine.git] / workbench / tools / Edit / Project.c
blobb278ae5c307fc9ff16bc0385bec80e3b63c605bf
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 set_project_name(new, NULL);
52 else FreeVec(new),new = NULL;
54 return new;
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;
67 p->xcurs = gui.left;
68 p->ycurs = gui.topcurs;
69 p->state = 0;
70 p->savepoint = 0;
73 /*** Try to load the file contained in the project ***/
74 WORD load_in_project( Project p, STRPTR path )
76 LoadFileArgs args;
77 WORD err;
79 args.filename = 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;
87 p->eol = args.eol;
89 else ThrowError(Wnd, ErrMsg(err));
91 return err;
94 /*** Load and create a new project from a path ***/
95 Project load_and_activate(Project ins, STRPTR name, BYTE use_prj)
97 Project new;
98 if( ( new = (use_prj ? ins : new_project(ins, &prefs)) ) )
100 if( use_prj >= 2 )
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);
106 if(new != ins ) {
107 inv_curs(ins, FALSE);
108 if(use_prj == 2) close_project(ins), FreeVec(ins);
109 goto refresh;
112 return NULL;
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);
125 return new;
128 /* Something failed, close project */
129 if(use_prj == 0) close_project(new),FreeVec(new);
130 FreeVec(name);
132 else ThrowError(Wnd, ErrMsg(ERR_NOMEM));
133 return NULL;
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);
144 inv_curs(p, FALSE);
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);
152 inv_curs(p, TRUE);
155 /*** Insert a file at current cursor position ***/
156 void insert_file( Project p )
158 LoadFileArgs args;
159 ULONG length;
160 WORD err;
162 if( ( args.filename = ask_load(Wnd, (AskArgs *)&p->path, TRUE, GetMenuText(208)) ) )
164 if( 0 == (err = read_file( &args, &length )) )
166 LONG pos[3];
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);
195 if(path != NULL)
196 p->name = (STRPTR) FilePart(
197 p->path = path);
198 else
199 p->path = NULL,
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)
210 STRPTR newname;
211 if(NULL != (newname = ask_save(Wnd, (AskArgs *)&p->path, GetMenuText(105))) &&
212 0 != warn_overwrite( newname ))
213 set_project_name(p, newname);
214 else
215 /* User cancels or there was errors */
216 return 0;
217 p->state = 0;
219 unset_modif_mark(p, FALSE);
221 if( refresh )
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;
232 for(p=first; p; )
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);
240 if(close == TRUE)
242 cur = p; close_project(p);
243 p = p->next; FreeVec(cur);
244 if(active == cur) active = p; nb = 0;
246 else p=p->next;
248 if(NbProject > 0 && nb > 0) reshape_panel(active);
249 return 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;
261 case ERR_NOMEM:
262 /* This is too nasty to attempt something: close faulty project */
263 close_project(p); FreeVec(p); return 0;
266 prop_adj(p);
267 init_tabstop(p->tabsize);
268 SetTitle(Wnd, p->path ? p->path : p->name);
269 SetABPenDrMd(RP,pen.fg,pen.bg,JAM2);
270 if(InitCurs)
271 p->ycurs = gui.topcurs,
272 p->xcurs = gui.left;
273 else
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;
281 inv_curs(p,TRUE);
282 draw_info(p);
283 return 1;
286 /*** Test if a string represent an AmigaDOS pattern ***/
287 STRPTR IsPat( STRPTR name )
289 int len = strlen( name ) * 2 + 2;
290 STRPTR tok;
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 )
295 return tok;
296 FreeVec( tok );
298 return NULL;
301 /*** Create a new project with an incomplete path ***/
302 UBYTE new_file(Project *ins, STRPTR path)
304 Project new;
305 if(NULL != (new = new_project(*ins, &prefs)))
307 UBYTE err;
308 if(RETURN_OK == (err = get_full_path(path, &new->name))) {
309 set_project_name(new, new->name); *ins = new;
310 new->state = PAGINATED;
311 return 1;
312 } else
313 ThrowError(Wnd, ErrMsg(err));
314 close_project(new);
315 FreeVec(new);
316 } else
317 ThrowError(Wnd, ErrMsg(ERR_NOMEM));
318 return 0;
321 /*** Create a set of projects (command line or WBStartup) ***/
322 Project create_projects(Project ins_after, APTR args, ULONG nb)
324 Project new;
325 STRPTR pattern;
326 APTR cwd = (APTR) CurrentDir( NULL );
328 if(nb > 0)
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;
340 char more;
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;
349 do {
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))
354 goto stop_now;
355 } while( more );
356 stop_now: FreeDosObject(DOS_EXALLCONTROL, eac);
358 UnLock((BPTR) lock );
359 } /* else bad pattern: discard entry */
360 FreeVec( pattern );
362 /* Given file contains no pattern */
363 else if( !new_file(&new, arg->wa_Name) )
364 break;
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);
375 close_project(new);
376 FreeVec(new); new = prev;
377 } else break;
378 } else
379 newempty: new = new_project(NULL, &prefs);
380 CurrentDir((BPTR) cwd );
381 return new;
384 /*** set modification flag on this project ***/
385 void set_modif_mark(Project p)
387 p->state |= MODIFIED;
388 if( p->path )
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)
397 commit_work( p );
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);
404 p->state = 0;
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 */
412 if( warn_modif(p) )
414 NbProject--;
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);
420 Destroy(&first, p);
421 return TRUE;
423 return FALSE;
426 /*** Close all projects, returning the first non-closed or NULL if none ***/
427 Project close_projects( void )
429 register Project prj;
430 while( first )
432 prj = first;
433 /* If user doesn't want to close this one */
434 if( close_project(first) ) FreeVec(prj);
435 else break;
437 /* If NULL, then we should quit */
438 return first;
440 #endif
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);
452 /* Bottom line */
453 if(rclear == 0) {
454 SetAPen(&RPT,pen.shine);
455 Move(&RPT, gui.left, gui.top-2);
456 Draw(&RPT, p->pleft, RPT.cp_y);
458 /* Right shape */
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);
463 if(rclear) {
464 SetAPen(&RPT, pen.panel);
465 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
466 Draw(&RPT, RPT.cp_x, gui.oldtop);
469 /* Upper line */
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);
481 /* Right shape */
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);
486 if(lclear && i) {
487 SetAPen(&RPT,pen.panel);
488 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
489 Draw(&RPT, RPT.cp_x, gui.oldtop);
492 /* Bottom line */
493 if( !lclear ) {
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);
501 #ifndef JANOPREF
502 /*** Search if a tab is select using mouse ***/
503 Project select_panel(Project current, WORD x)
505 Project new;
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
509 if( x < 0 )
510 for(new=first; new && ++x; new=new->next);
511 else
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);
523 /* Then content */
524 if( active_project(new, FALSE) == 0 )
525 reshape_panel(current);
526 else return new;
528 return 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));
536 if(NbProject > 1)
538 RPT.AlgoStyle = FSF_BOLD;
539 draw_panel(activ, FALSE, FALSE, TRUE);
542 #endif
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 */
549 if( NbProject > 1 )
551 WORD height;
552 /** Change top position, if not already **/
553 #ifndef JANOPREF
554 if(gui.top == gui.oldtop)
555 adjust_win(Wnd, 1), clear_brcorner();
556 #endif
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 */
569 if(lst != activ)
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);
575 #ifndef JANOPREF
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();
582 #endif