added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / workbench / tools / Edit / Project.c
blobd4771202e6e92fcce93af07d3f926d4d2ca7613d
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, 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);
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 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)
212 STRPTR newname;
213 if(NULL != (newname = ask_save(Wnd, (AskArgs *)&p->path, GetMenuText(105))) &&
214 0 != warn_overwrite( newname ))
215 set_project_name(p, newname);
216 else
217 /* User cancels or there was errors */
218 return 0;
219 p->state = 0;
221 unset_modif_mark(p, FALSE);
223 if( refresh )
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;
234 for(p=first; p; )
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);
242 if(close == TRUE)
244 cur = p; close_project(p);
245 p = p->next; FreeVec(cur);
246 if(active == cur) active = p; nb = 0;
248 else p=p->next;
250 if(NbProject > 0 && nb > 0) reshape_panel(active);
251 return 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;
263 case ERR_NOMEM:
264 /* This is too nasty to attempt something: close faulty project */
265 close_project(p); FreeVec(p); return 0;
268 prop_adj(p);
269 init_tabstop(p->tabsize);
270 SetTitle(Wnd, p->path ? p->path : p->name);
271 SetABPenDrMd(RP,pen.fg,pen.bg,JAM2);
272 if(InitCurs)
273 p->ycurs = gui.topcurs,
274 p->xcurs = gui.left;
275 else
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;
283 inv_curs(p,TRUE);
284 draw_info(p);
285 return 1;
288 /*** Test if a string represent an AmigaDOS pattern ***/
289 STRPTR IsPat( STRPTR name )
291 int len = strlen( name ) * 2 + 2;
292 STRPTR tok;
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 )
297 return tok;
298 FreeVec( tok );
300 return NULL;
303 /*** Create a new project with an incomplete path ***/
304 UBYTE new_file(Project *ins, STRPTR path)
306 Project new;
307 if(NULL != (new = new_project(*ins, &prefs)))
309 UBYTE err;
310 if(RETURN_OK == (err = get_full_path(path, &new->name))) {
311 set_project_name(new, new->name); *ins = new;
312 new->state = PAGINATED;
313 return 1;
314 } else
315 ThrowError(Wnd, ErrMsg(err));
316 close_project(new);
317 FreeVec(new);
318 } else
319 ThrowError(Wnd, ErrMsg(ERR_NOMEM));
320 return 0;
323 /*** Create a set of projects (command line or WBStartup) ***/
324 Project create_projects(Project ins_after, APTR args, ULONG nb)
326 Project new;
327 STRPTR pattern;
328 APTR cwd = (APTR) CurrentDir( NULL );
330 if(nb > 0)
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;
342 char more;
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;
351 do {
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))
356 goto stop_now;
357 } while( more );
358 stop_now: FreeDosObject(DOS_EXALLCONTROL, eac);
360 UnLock((BPTR) lock );
361 } /* else bad pattern: discard entry */
362 FreeVec( pattern );
364 /* Given file contains no pattern */
365 else if( !new_file(&new, arg->wa_Name) )
366 break;
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);
377 close_project(new);
378 FreeVec(new); new = prev;
379 } else break;
380 } else
381 newempty: new = new_project(NULL, &prefs);
382 CurrentDir((BPTR) cwd );
383 return new;
386 /*** set modification flag on this project ***/
387 void set_modif_mark(Project p)
389 p->state |= MODIFIED;
390 if( p->path )
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)
399 commit_work( p );
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);
406 p->state = 0;
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 */
414 if( warn_modif(p) )
416 NbProject--;
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);
422 Destroy(&first, p);
423 return TRUE;
425 return FALSE;
428 /*** Close all projects, returning the first non-closed or NULL if none ***/
429 Project close_projects( void )
431 register Project prj;
432 while( first )
434 prj = first;
435 /* If user doesn't want to close this one */
436 if( close_project(first) ) FreeVec(prj);
437 else break;
439 /* If NULL, then we should quit */
440 return first;
442 #endif
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);
454 /* Bottom line */
455 if(rclear == 0) {
456 SetAPen(&RPT,pen.shine);
457 Move(&RPT, gui.left, gui.top-2);
458 Draw(&RPT, p->pleft, RPT.cp_y);
460 /* Right shape */
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);
465 if(rclear) {
466 SetAPen(&RPT, pen.panel);
467 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
468 Draw(&RPT, RPT.cp_x, gui.oldtop);
471 /* Upper line */
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);
483 /* Right shape */
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);
488 if(lclear && i) {
489 SetAPen(&RPT,pen.panel);
490 Move(&RPT, RPT.cp_x, RPT.cp_y-1);
491 Draw(&RPT, RPT.cp_x, gui.oldtop);
494 /* Bottom line */
495 if( !lclear ) {
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);
503 #ifndef JANOPREF
504 /*** Search if a tab is select using mouse ***/
505 Project select_panel(Project current, WORD x)
507 Project new;
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
511 if( x < 0 )
512 for(new=first; new && ++x; new=new->next);
513 else
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);
525 /* Then content */
526 if( active_project(new, FALSE) == 0 )
527 reshape_panel(current);
528 else return new;
530 return 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));
538 if(NbProject > 1)
540 RPT.AlgoStyle = FSF_BOLD;
541 draw_panel(activ, FALSE, FALSE, TRUE);
544 #endif
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 */
551 if( NbProject > 1 )
553 WORD height;
554 /** Change top position, if not already **/
555 #ifndef JANOPREF
556 if(gui.top == gui.oldtop)
557 adjust_win(Wnd, 1), clear_brcorner();
558 #endif
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 */
571 if(lst != activ)
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);
577 #ifndef JANOPREF
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();
584 #endif