/* 
   Copyright 2001-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222

**********************************************************************

PROJECT.C handles the functiosn of the project window, as well as
saving and loading project files.  The main window routine vectors
some of the menu commands to the window proc of the project window
as well.

**********************************************************************

*/
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <richedit.h>
#include <stdio.h>

#define PROJVERS 3
#include "header.h"
#include <ctype.h>

extern HWND hwndToolEdit, hwndToolDebug, hwndToolBuild ;
extern LOGFONT EditFont ;
extern HINSTANCE hInstance ;
extern HWND hwndClient,hwndStatus,hwndFrame ;
extern char szProjectFilter[],szSourceFilter[], szTargetFilter[] ;
extern HWND hwndTab, hwndWatch ;
extern int browseInfo ;
extern char *findhist1[MAX_COMBO_HISTORY] ;
extern char *findhist2[MAX_COMBO_HISTORY] ;
extern char *replacehist[MAX_COMBO_HISTORY] ;
extern char *watchhist[MAX_COMBO_HISTORY] ;
extern char *fifhistory[MAX_COMBO_HISTORY] ;

HWND hwndProject ;
PROJLIST *projectList ;
PROJLIST *selectedProject ;
int changedProject ;
char szProjectName[256] ;

static char szProjectClassName[] = "xccProjectClass" ;
static char szProjectTitle[256] ;
static HWND treeWindow ;
static HBITMAP flyellow,flred,flgreen,flwhite,flcyan,fldll,fllib,flexe ;
static int ilyellow, ilred,ilgreen,ilwhite,ilcyan,ildll,illib,ilexe ;
static HIMAGELIST treeIml ;
static int treeViewSelected ;
static char ipathbuf[2048] ;

typedef struct tagTVKEYDOWN {
    NMHDR hdr;
    WORD wVKey;
    UINT flags;
} NMTVKEYDOWN, FAR *LPNMTVKEYDOWN;
void FindModuleName(char *out, char *in) 
{
   PROJLIST *l = projectList ;
	int i = 0 ;
	while (l) {
		int j = 0;
		FILELIST *m = l->files ;
		while (m) {
         if (!xstricmpz(in,m->name)) {
            strcpy(out,in) ;
            return ;
         }
         if (!xstricmpz(in,m->title)) {
            strcpy(out,m->name) ;
            return ;
         }
			m = m->next ;
		}
		l = l->next ;
	}
}                
void abspath(char *name)
{
  char projname[256],*p,*nname = name ;
   if (!szProjectName[0])
      return name ;
  if (name[0] == 0)
   return ;
   if (name[1] == ':')
      return ;
   strcpy(projname,szProjectName) ;
   p = strrchr(projname,'\\') ;
   if (!p)
      return ;
   p-- ;
   if (!strstr(name,"..\\")) {
      if (name[0] != '\\') {
         strcpy(p+2,name) ;
         strcpy(nname,projname) ;
      }
      return ;
   }
   while (!strncmp(name,"..\\",3)) {
      while (p> projname && *p-- != '\\') ;
      name += 3 ;
   }
   *++p = '\\' ;
   p++ ;
   strcpy(p,name) ;
   strcpy(nname,projname) ;
}
char *relpath(char *name)
{
   static char projname[256],localname[256] ;
   char *p = localname, *q = projname,*r,*s;
   if (!szProjectName[0])
      return name ;
   if (toupper(name[0]) != toupper(szProjectName[0]))
      return name ;
   
   strcpy(localname,name) ;
   strcpy(projname,szProjectName) ;
   r= strrchr(localname,'\\') ;
   if (r)
      *r++ = 0 ; // r has the point to the file name
   s = strrchr(projname,'\\') ;
   if (!s)
      return name ;
   if (*s)
      *s = 0 ;


   while (*p && *q && toupper(*p) == toupper(*q)) 
      p++,q++ ;
   if (!(*p | *q)) 
      return r ;
   else if (*(p-1) == '\\' && *(p-2) == ':')
      return name ;
   else {
      int count = *q != 0 ;
      while (*p && p > localname && *p != '\\')
         p-- ;
      while (*q && (q = strchr(q+1,'\\')))
         count++ ;
      projname[0] = 0 ;
      while (count--)
         strcat(projname,"..\\") ;
      if (*p) {
         strcat(projname,p+1) ;
         strcat(projname,"\\") ;
      }
      strcat(projname,r) ;
      return projname ;
   }
}
void absincludepath(char *name)
{
   
   char *p = ipathbuf,*dest1 = name, *dest2=name ;
   strcpy(ipathbuf,name) ;
   name[0] = 0 ;
   do {
      char *q ;
      if (*p == ';')
         p++ ;
      q = p ;
      dest1 = dest2 ;
      p = strchr(p,';') ;
      if (!p)
         p = ipathbuf + strlen(ipathbuf) ;
      while (q != p)
         *dest2++ = *q++ ;
      *dest2 = 0 ;
      abspath(dest1) ;
      dest2 = dest1 + strlen(dest1) ;
      if (*p)
         *dest2++=';' ;
    } while (*p) ;
}
char * relincludepath(char *name)
{
   char *p = name, *dest1 = ipathbuf, *dest2 = ipathbuf ;
   ipathbuf[0] = 0 ;
   do {
      char *q ;
      if (*p == ';')
         p++ ;
      q = p ;
      dest1 = dest2 ;
      p = strchr(p,';') ;
      if (!p)
         p = name + strlen(name) ;
      while (q != p)
         *dest2++ = *q++ ;
      *dest2 = 0 ;
      strcpy(dest1,relpath(dest1)) ;
      dest2 = dest1 + strlen(dest1) ;
      if (*p)
         *dest2++=';' ;
    } while (*p) ;
    return ipathbuf ;
}
int imageof(char *name)
{
	name = strrchr(name,'.') ;
   if (!name)
      return ilyellow ;
	if (!xstricmpz(name,".asm"))
		return ilred ;
	if (!xstricmpz(name,".c") || !xstricmpz(name,".cpp"))
		return ilcyan ;
	if (!xstricmpz(name,".rc"))
		return ilgreen ;
	if (!xstricmpz(name,".h"))
		return ilyellow ;
	if (!xstricmpz(name,".exe"))
		return ilexe ;
	if (!xstricmpz(name,".lib"))
		return illib ;
	if (!xstricmpz(name,".dll"))
		return ildll ;

	return ilwhite ;
}
HTREEITEM TVInitInsert(HWND hTree, HTREEITEM hParent, HTREEITEM after, char * title, int toexpand)
{
	HTREEITEM rv ;
	TV_INSERTSTRUCT t;
	memset(&t,0,sizeof(t)) ;
	t.hParent =hParent ;
	t.hInsertAfter = after ;
	t.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
	t.item.hItem = 0;	
	t.item.pszText = title ;
	t.item.cchTextMax = strlen(title) ;
	t.item.iImage = t.item.iSelectedImage = imageof(title) ;
	rv = TreeView_InsertItem(hTree,&t) ;
	if (toexpand)
		TreeView_Expand(treeWindow,hParent,	TVE_EXPAND) ;
   return rv ;
}
void AddFileInternal(PROJLIST *p, int selected, char *name, char *title)
{
			FILELIST *x =p->files ;
			while (x) {
            if (!xstricmp(x->name,name))
					return ;
				x = x->next ;
			}
			x = malloc(sizeof(FILELIST)) ;
			memset(x,0,sizeof(*x)) ;
			if (x) {
				FILELIST **y = &p->files ;
				int i;
				HTREEITEM item = TVI_FIRST ;
            while (*y) {
               if (strcmp((*y)->name,name) > 0)
                  break ;
               item = (*y)->treeHandle ;
               y = &(*y)->next ;
            }
#ifdef XXXXX
				for (i=0; i < selected; i++) {
					item = (*y)->treeHandle ;
					y = &(*y)->next ;
				}
#endif
				x->next = (*y) ;
				
				*y = x ;
            strcpy(x->name,name) ;
            strcpy(x->title,title) ;
				x->treeHandle = TVInitInsert(treeWindow,p->treeHandle,item,x->title,TRUE) ;
			}
}
void ProjectAddFile(PROJLIST *p, int selected)
{
	if (hwndProject) {
		OPENFILENAME ofn ;
      if (OpenFileDialog(&ofn,0,hwndProject,FALSE,TRUE,szSourceFilter,"FILEDIR","Select Source Files")) {
         char *q = ofn.lpstrFile ;
         char path[256] ;
         char filename[256],*r ;
         changedProject = TRUE ;
         strcpy(path,q) ;
         q = q + strlen(q) + 1 ;
         if (*q) {
            while (*q) {
               sprintf(filename,"%s\\%s",path,q) ;
               AddFileInternal(p,selected++,filename,q) ;
               q = q + strlen(q) + 1 ;
            }
         } else {
            r = strrchr(path,'\\') ;
            if (!r)
               r = path ;
            else
               r++ ;
            AddFileInternal(p,selected,path,r) ;
         }
		}
	}
}
void ProjectAddTarget(PROJLIST *p)
{
	if (hwndProject) {
		OPENFILENAME ofn ;
      if (OpenFileDialog(&ofn,0,hwndProject,TRUE,FALSE,szTargetFilter,"FILEDIR","Select Target")) {

			PROJLIST *x =projectList ;
			while (x) {
				if (!xstricmp(x->name,ofn.lpstrFile))
					return ;
				x = x->next ;
			}
			x = malloc(sizeof(PROJLIST)) ;
			memset(x,0,sizeof(*x)) ;
			if (x) {
            int rootproj = FALSE ;
            changedProject = TRUE ;
            if (!strchr(ofn.lpstrFile,'.')) {
               strcat(ofn.lpstrFile,".exe") ;
               strcat(ofn.lpstrFileTitle,".exe") ;
            }
				strcpy(x->name,ofn.lpstrFile) ;
				strcpy(x->title,ofn.lpstrFileTitle) ;
				if (!projectList) {
               rootproj = TRUE ;
					projectList = x ;
					x->treeHandle = TVInitInsert(treeWindow,TVI_ROOT,TVI_FIRST,ofn.lpstrFileTitle,FALSE) ;
				} else {
					if (!p) {
						p = projectList ;
						while (p->next)
							p = p->next ;
					}
					x->treeHandle = TVInitInsert(treeWindow,TVI_ROOT,p->treeHandle,ofn.lpstrFileTitle,FALSE) ;
					p->next = x ;
				}
            if (lstrlen(x->name) > 4)
               if (!xstricmpz(".dll",x->name + lstrlen(x->name) - 4) )
                  x->buildType = BT_DLL ;
               else if (!xstricmpz(".lib",x->name + lstrlen(x->name) - 4) )
                  x->buildType = BT_LIBRARY ;
               else {
                  x->buildType = BT_CONSOLE ;
                  x->buildFlags = BF_BREAKWINMAIN | BF_DEBUGTOOLTIPS    
                        | BF_DEBUGINFO ;
                  if (rootproj)
                     x->buildFlags |= BF_CHOSENPROJECT ;
               }
            x->dbgview = 1 << DID_WATCHWND ;
			}
		}
	}
}
void DeleteDepends(DEPENDSLIST *l)
{
	while (l) {
		DEPENDSLIST *n = l->next ;
		if (l->treeHandle)
			TreeView_DeleteItem(treeWindow,l->treeHandle) ;
		free(l) ;
		l = n ;
	}
}
void DeleteFiles(FILELIST *l)
{
	while (l) {
		FILELIST *n = l->next ;
		DeleteDepends(l->depends) ;
		if (l->treeHandle)
			TreeView_DeleteItem(treeWindow,l->treeHandle) ;
		free(l) ;
		l = n ;
	}
}
void ProjectDeleteFile(PROJLIST *p, int selected)
{
	if (hwndProject) {
		int i ;
		FILELIST **y = &p->files,*x ; 
		if (*y) {
			for (i=1; i < selected; i++)
				y = &(*y)->next ;
			x = *y ;
			*y = (*y)->next ;
         changedProject = TRUE ;
			DeleteDepends(x->depends) ;
			TreeView_DeleteItem(treeWindow,x->treeHandle) ;
			free(x) ;
		}
	}
}
void ProjectDeleteTarget(PROJLIST *proj)
{
	if (hwndProject) {
		int i;
		PROJLIST **y = &projectList,*x ; 
		while (*y && *y != proj) 
			y= &(*y)->next ;
		x = *y ;
		*y = (*y)->next ;
		DeleteFiles(x->files) ;
      changedProject = TRUE ;
		TreeView_DeleteItem(treeWindow,x->treeHandle) ;
		free(x) ;

	}
}
void ProjectFreeList (void ) 
{
	
	while (projectList) {
		PROJLIST *temp = projectList->next ;
		ProjectDeleteTarget(projectList) ;
		free(projectList) ;
		projectList = temp ;
	}
   changedProject = FALSE ;
}
void ProjectNewList(PROJLIST *list)
{
	projectList = list ;
}
PROJLIST * LoadErr(char *name, PROJLIST *list, FILE *in)
{
	projectList = list ;
	ProjectFreeList() ;
	ExtendedMessageBox("Load error",0,"File %s is the wrong format",name) ;
	return 0 ;
}
int ReadString(char *buf, FILE *in, char *compare)
{
	int l ;
	if (feof(in))
		return 1 ;
	fgets(buf,256,in) ;
	l = strlen(buf) ;
	if (buf[l-1] == '\n') 
		buf[--l] = 0 ;
	if (compare)
		return xstricmp(buf,compare) ;
	return 0 ;
}
void SaveDocks(FILE *out)
{
   int i;
   CCW_params **p;
   CCD_params **d;
   int len ;
   len = dmgrGetInfo(&p,&d) ;
   fprintf(out,"<DOCKWIN>\n") ;
   for (i=0; i < len; i++) {
      fprintf(out,"<DOCK %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d>\n",
         p[i]->id,
         d[i]->flags, d[i]->flexparams,
         d[i]->rowindex, d[i]->colindex,
         d[i]->hidden, d[i]->hiddenwidth,
         d[i]->oldsize.left, d[i]->oldsize.top, d[i]->oldsize.right, d[i]->oldsize.bottom,
         d[i]->position.left, d[i]->position.top, d[i]->position.right, d[i]->position.bottom,
         d[i]->lastposition.left, d[i]->lastposition.top, d[i]->lastposition.right, d[i]->lastposition.bottom) ;
   }
   fprintf(out,"</DOCKWIN>\n") ;
}
int RestoreDocks(FILE *in)
{
   char buf[256] ;
   CCD_params d[20] ;
   int ids[20] ;
   int count=0 ;
   while (count < 20) {
      if (ReadString(buf,in,0))
         return 1 ;
      if (!xstricmp(buf,"</DOCKWIN"))
         break ;
      if (xstricmp(buf,"<DOCK"))
         return 1 ;
      sscanf(buf+6,"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d>\n",
         &ids[count],
         &d[count].flags, &d[count].flexparams,
         &d[count].rowindex, &d[count].colindex,
         &d[count].hidden, &d[count].hiddenwidth,
         &d[count].oldsize.left, &d[count].oldsize.top, &d[count].oldsize.right, &d[count].oldsize.bottom,
         &d[count].position.left, &d[count].position.top, &d[count].position.right, &d[count].position.bottom,
         &d[count].lastposition.left, &d[count].lastposition.top, &d[count].lastposition.right, &d[count].lastposition.bottom) ;
      count++ ;
   }
   dmgrSetInfo(ids,&d[0],count) ;
   return 0 ;
}
void onehistsave(FILE *out, char **hist)
{
   int i ;
   fprintf(out,"<BLOCK>\n") ;
   for (i=0; i < MAX_COMBO_HISTORY; i++)
      if (hist[i])
         fprintf(out,"%s\n",hist[i]) ;
   fprintf(out,"</BLOCK>\n") ;
}
int SaveHistory(FILE *out)
{
   fprintf(out,"<HISTORY>\n") ;
   onehistsave(out,findhist1) ;
   onehistsave(out,findhist2) ;
   onehistsave(out,replacehist) ;
   onehistsave(out,watchhist) ;
   onehistsave(out,fifhistory) ;
   fprintf(out,"</HISTORY>\n") ;
}
int onehistrestore(FILE *in, char **hist)
{
   int count = 0 ;
   char buf[256] ;
   for (count = 0; count < MAX_COMBO_HISTORY; count++)
      if (hist[count]) {
         free(hist[count]) ;
         hist[count] = 0 ;
      }
   count = 0 ;
   if (ReadString(buf,in,"<BLOCK>"))
      return 1 ;
   while (1) {
      if (ReadString(buf,in,0))
         return 1 ;
      if (!xstricmp(buf,"</BLOCK"))
         return 0 ;
      if (count < MAX_COMBO_HISTORY)
         hist[count++] = strdup(buf) ;
   }
}
int RestoreHistory(FILE *in)
{
   char buf[256] ;
   if (ReadString(buf,in,"<HISTORY>"))
      return 1 ;
   if (onehistrestore(in,findhist1))
      return 1 ;
   if (onehistrestore(in,findhist2))
      return 1 ;
   if (onehistrestore(in,replacehist))
      return 1 ;
   if (onehistrestore(in,watchhist))
      return 1 ;
   if (onehistrestore(in,fifhistory))
      return 1 ;
   if (ReadString(buf,in,"</HISTORY>"))
      return 1 ;
   return 0 ;
}
void SaveFont (FILE *out)
{
   int i,j ;
   fprintf(out,"<FONT>\n") ;
   for (i=0; i < sizeof(LOGFONT); i+=16) {
      int k = sizeof(LOGFONT) - i ;
      if (k > 16) k = 16 ;
      for (j=0; j < k; j++)
         fprintf(out,"%X ",*(((unsigned char *)&EditFont) + i + j));
      fprintf(out,"\n") ;
   }
   fprintf(out,"</FONT>\n") ;
}
int RestoreFont(FILE *in)
{
   int i,j ;
   char buf[256] ;
   if (ReadString(buf,in,"<FONT>"))
      return 1 ;
   for (i=0; i < sizeof(LOGFONT); i+=16) {
      int k = sizeof(LOGFONT) - i,n=0,n1,s ;
      if (k > 16) k = 16 ;
      if (ReadString(buf,in ,0))
         return 1 ;
      for (j=0; j < k; j++) {
         sscanf(buf+n,"%X %n",&s,&n1) ;
         n += n1 ;
         *(((unsigned char *)&EditFont) + i + j) = s ;
      }
   }
   if (ReadString(buf,in,"</FONT>"))
      return 1 ;
   ApplyFontSettings(&EditFont) ;
   return 0 ;
}
PROJLIST *ProjectLoadList(char *name, RECT *rect)
{
	PROJLIST *plist=0,**pl=&plist ;
	FILE *in = fopen(name,"r") ;
   RECT wrect ;
	char buf[256] ;
   int temp,projectVersion ;
	if (!in) {
		ExtendedMessageBox("Error",0,"File Not Found") ;
		return 0;
	}
   if (ReadString(buf,in,"<PROJECT"))
		return LoadErr(name,plist,in) ;
   if (buf[8] == '>')
      projectVersion = 1 ;
   else
      sscanf(buf+9,"%d",&projectVersion) ;
	if (ReadString(buf,in,"<TARGETS>"))
		return LoadErr(name,plist,in) ;
	while (TRUE) {
		if (ReadString(buf,in,"<TARGET "))
			break ;
      *pl = calloc(sizeof(*plist),1) ;
		if (pl) {
			char *p = buf + 8;
			int i = 0 ;
			FILELIST *list=0,**l=&list,*lastlist = 0 ;
			memset(*pl,0,sizeof(*plist)) ;
			while (*p && *p != ';')
				(*pl)->name[i++] = *p++ ;
         if (projectVersion >= 2)
            abspath((*pl)->name) ;
			if (*p) 
				p++ ;
         i = 0 ;
			while (*p && *p != '>')
				(*pl)->title[i++] = *p++ ;	
			if (ReadString(buf,in,"<PATHS"))
            return LoadErr(name,plist,in) ;
         p = strrchr(buf,';') ;
         if (!p)
            p = strrchr(buf,'>') ;
         if (p)
            *p = 0 ;
         strcpy((*pl)->includePath,buf+7) ;
         if (projectVersion >= 2)
            absincludepath((*pl)->includePath) ;
			if (*p) 
            p+=2 ;
         if (p++) {
            i = 0 ;
            while (*p && *p != '>')
               (*pl)->outputPath[i++] = *p++ ;  
            if (projectVersion >= 2)
               abspath((*pl)->outputPath) ;
         }
			if (ReadString(buf,in,"<BUILD "))
            return LoadErr(name,plist,in) ;
         sscanf(buf+7,"%d %x",&(*pl)->buildType, &(*pl)->buildFlags) ;
         if (projectVersion >= 3) {
           if (ReadString(buf,in,"<DEBUG "))
              return LoadErr(name,plist,in) ;
           sscanf(buf+7,"%x",&(*pl)->dbgview) ;
         } else
            (*pl)->dbgview = 1 << DID_WATCHWND ;
         if (projectVersion >= 2) {
           if (ReadString(buf,in,"<OPTS "))
              return LoadErr(name,plist,in) ;
           sscanf(buf+6,"\"%[^\"]\" \"%[^\"]\" \"%[^\"]\" \"%[^\"]\" ",
              (*pl)->compileopts, (*pl)->assembleopts, (*pl)->linkopts, (*pl)->libopts) ;
         }
         if (ReadString(buf,in,0))
            return LoadErr(name,plist,in) ;
         if (buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = 0 ;
         strcpy((*pl)->cmdline,buf) ;

			while (TRUE) {
				if (ReadString(buf,in,0))
					return LoadErr(name,plist,in) ;
				if (buf[0] == '<')
					if (!xstricmp(buf,"<DEPENDS>")) {
						DEPENDSLIST *dlist=0,**dl =&dlist ;
						if (!list)
							return LoadErr(name,plist,in) ;
						while (TRUE) {
							if (ReadString(buf,in,0))
								return LoadErr(name,plist,in) ;
							if (buf[0] == '<')
								break ;
							*dl = malloc(sizeof(*dlist)) ;
							if (*dl) {
								char *p = buf ;
								int i = 0 ;
								memset(*dl,0,sizeof(*dlist)) ;
								while (*p && *p != ';')
									(*dl)->name[i++] = *p++ ;
                        if (projectVersion >= 2)
                           abspath((*dl)->name) ;
								if (*p) 
									p++ ;
								i = 0;
								while (*p && *p != '>')
									(*dl)->title[i++] = *p++ ;	
								dl = &(*dl)->next ;
							}
						}
						if (xstricmp(buf,"</DEPENDS>"))
							return LoadErr(name,plist,in) ;
						lastlist->depends = dlist ;
						continue ;
					} else
						break ;
				*l = malloc(sizeof(*list)) ;
				if (*l) {
					char *p = buf ;
					int i = 0 ;
					memset(*l,0,sizeof(*list)) ;
					while (*p && *p != ';')
						(*l)->name[i++] = *p++ ;
               if (projectVersion >= 2)
                  abspath((*l)->name) ;
					if (*p) 
						p++ ;
					i = 0;
					while (*p)
						(*l)->title[i++] = *p++ ;	
					lastlist = *l ;
					l = &(*l)->next ;
				}
			}
			if (xstricmp(buf,"</TARGET>"))
				return LoadErr(name,plist,in) ;
			(*pl)->files = list ;
			pl = &(*pl)->next ;
		}
	}
	if (xstricmp(buf,"</TARGETS>"))
		return LoadErr(name,plist,in) ;
   /* backsupport for old docking */
   if (projectVersion == 1) {
      if (ReadString(buf,in,"<PROJWIN"))
         return LoadErr(name,plist,in) ;
//      if (ReadString(buf,in,"<WATCHWIN"))
//         return LoadErr(name,plist,in) ;
   } else { // projectVersion >= 2
      if (projectVersion >= 3) {
         if (ReadString(buf,in,"<TOOLBARS"))
            return LoadErr(name,plist,in) ;
         if (!RestoreToolBar(in,hwndToolEdit))
            return LoadErr(name,plist,in) ;
         if (!RestoreToolBar(in,hwndToolBuild))
            return LoadErr(name,plist,in) ;
         if (!RestoreToolBar(in,hwndToolDebug))
            return LoadErr(name,plist,in) ;
         if (ReadString(buf,in,"</TOOLBARS"))
            return LoadErr(name,plist,in) ;
      } 
      if (RestoreDocks(in))
         return LoadErr(name,plist,in) ;
      if (ReadString(buf,in,"<BROWSE"))
         return LoadErr(name,plist,in) ;
      sscanf(buf+8,"%d",&browseInfo) ;
      if (RestoreHistory(in))
         return LoadErr(name,plist,in) ;
      if (RestoreTags(in))
         return LoadErr(name,plist,in) ;
      if (RestoreFont(in))
         return LoadErr(name,plist,in) ;
   }
	CloseAll() ;
   if (RestoreWindows(in, projectVersion))
		return LoadErr(name,plist,in) ;
	if (ReadString(buf,in,"</PROJECT>"))
		return LoadErr(name,plist,in) ;
	fclose(in) ;
		
	return plist ;
}
void ProjectSaveList(int annun)
{
	OPENFILENAME ofn ;
	PROJLIST *list = projectList ;
	FILE *out; 
   RECT r ;
   POINT p ;
   if (!projectList)
      return ;
   if (annun && changedProject)
      if (ExtendedMessageBox("Changed Project",MB_YESNO,"Project settings have changed.  Do you wish to save the project?") != IDYES)
         return ;
   changedProject = FALSE ;
	if (!szProjectTitle[0]) {
      if (!SaveFileDialog(&ofn,0,hwndProject,TRUE,szProjectFilter,"ProjDir","Save Project As")) 
			return ;
      strcpy(szProjectName,ofn.lpstrFile) ;
		out = fopen(ofn.lpstrFile,"w") ;
	} else
		out = fopen(szProjectName,"w") ;
   fprintf(out,"<PROJECT %d>\n<TARGETS>\n",PROJVERS) ;
	while (list) {
		FILELIST *list1 = list->files ;
      char buf[256];
      buf[0] = 0 ;
      strcpy(buf,relpath(list->outputPath)) ;
      fprintf(out,"<TARGET %s;%s>\n",relpath(list->name),list->title) ;
      fprintf(out,"<PATHS %s;%s>\n",relincludepath(list->includePath), buf) ;
      fprintf(out,"<BUILD %d %x>\n",list->buildType,list->buildFlags) ;
      fprintf(out,"<DEBUG %x>\n",list->dbgview,list) ;
      fprintf(out,"<OPTS \"%s\" \"%s\" \"%s\" \"%s\">\n",list->compileopts,
               list->assembleopts, list->linkopts, list->libopts) ;
      fprintf(out,"%s\n",list->cmdline) ;
		while (list1) {
         fprintf(out,"%s;%s\n",relpath(list1->name),list1->title) ;
			if (list1->depends) {
				DEPENDSLIST *d = list1->depends ;
				fprintf(out,"<DEPENDS>\n") ;
				while (d) {
               fprintf(out,"%s;%s\n",relpath(d->name),d->title) ;
					d = d->next ;
				}
				fprintf(out,"</DEPENDS>\n") ;
			}
			list1 = list1->next ;
		}
		fprintf(out,"</TARGET>\n") ;
		list = list->next ;
	}
	fprintf(out,"</TARGETS>\n") ;
   fprintf(out,"<TOOLBARS>\n") ;
   SaveToolBar(out,hwndToolEdit) ;
   SaveToolBar(out,hwndToolBuild) ;
   SaveToolBar(out,hwndToolDebug) ;
   fprintf(out,"</TOOLBARS>\n") ;
   SaveDocks(out) ;
   fprintf(out,"<BROWSE %d>\n",browseInfo) ;
   SaveHistory(out) ;
   SaveTags(out) ;
   SaveFont(out) ;
	SaveWindows(out) ;
	fprintf(out,"</PROJECT>\n") ;
   fclose(out) ;
}
static void InitItems(HWND hTree, PROJLIST *projlist)
{
	PROJLIST *l = projlist ;
	int i = 0 ;
	HTREEITEM h = TVI_FIRST ;
   TreeView_DeleteAllItems(hTree) ;
	while (l) {
		int j = 0;
		FILELIST *m = l->files ;
		HTREEITEM k = TVI_FIRST ;
		h = l->treeHandle = TVInitInsert(hTree,TVI_ROOT,h,l->title,FALSE) ;
		while (m) {
			DEPENDSLIST *n = m->depends ;
			HTREEITEM t = TVI_FIRST ;
			k = m->treeHandle = TVInitInsert(hTree,l->treeHandle,k,m->title,FALSE) ;
			while (n) {
				t = n->treeHandle = TVInitInsert(hTree,m->treeHandle,t,n->title,FALSE) ;
				n = n->next ;
			}
			m = m->next ;
		}
		l = l->next ;
	}
}                
int InsideRect(RECT *r, POINT *q)
{
	if (r->left <=q->x && r->top <= q->y)
      if (r->right > q->x && r->bottom > q->y) {
			return TRUE ;
      }
	return FALSE ;
}    
void FindItemByWind(HWND hwnd, PROJLIST **list, int *sel)
{
	PROJLIST *l =projectList ;
	FILELIST *m ;
	DWINFO *info = (DWINFO *)GetWindowLong(hwnd,0) ;
	*list = 0 ;
	if (!info)
		return ;
	while (l) {
		int i = 1 ;
		m = l->files ;
		while (m) {
			if (!xstricmpz(m->name,info->dwName)) {
				*list = l ;
				*sel = i ;
				return ;
			}
			i++ ;
			m = m->next ;
		}
		l = l->next ;
	}
}
void FindItemByPoint(HWND hwnd, PROJLIST **list, int *pos, int *depend)
{
	POINT q ;
	PROJLIST *l = *list = projectList ;
	GetCursorPos(&q) ;
	ScreenToClient(hwnd,&q) ;
	*pos = -1 ;
	*depend = 0 ;
	if (!l)
		return ;
	while (l) {
		int i = 0 ;
		RECT r ;
		FILELIST *m = l->files ;
		if (TreeView_GetItemRect(treeWindow,l->treeHandle,&r,0)) {
			if (InsideRect(&r,&q)) {
				*pos = i ;
				*list = l ;
				return ;
			}
		}
		while (m) {
			int j = 0 ;
			DEPENDSLIST *n = m->depends ;
			++i ;
			if (TreeView_GetItemRect(treeWindow,m->treeHandle,&r,0)) {
				if (InsideRect(&r,&q)) {
					*pos = i ;
					*list = l ;
					return ;
				}
			}
			while (n) {
				j++ ;
				if (TreeView_GetItemRect(treeWindow,n->treeHandle,&r,0)) {
					if (InsideRect(&r,&q)) {
						*pos = i ;
						*list = l ;
						*depend = j ;
						return ;
					}
				}
				n = n->next ;
			}	
			m = m->next ;
		}	
		l = l->next ;
	}

}
void FindItemBySelected(HWND hwnd, PROJLIST **list, int *pos, int *depend)
{
	PROJLIST *l = *list = projectList ;
	*pos = -1 ;
	*depend = 0 ;
	if (!l)
		return ;
	while (l) {
		int i = 0 ;
		RECT r ;
		FILELIST *m = l->files ;
    if (treeViewSelected == l->treeHandle) {
      *pos = i ;
      *list = l ;
      return ;
    }
		while (m) {
			int j = 0 ;
			DEPENDSLIST *n = m->depends ;
			++i ;
      if (treeViewSelected == m->treeHandle) {
        *pos = i ;
        *list = l ;
        return ;
      }
			while (n) {
				j++ ;
        if (treeViewSelected == n->treeHandle) {
          *pos = i ;
          *depend = j ;
          *list = l ;
          return ;
        }
				n = n->next ;
			}	
			m = m->next ;
		}	
		l = l->next ;
	}

}
PROJLIST *ProjectFindSelectedEXE(void)
{
	PROJLIST *l = projectList ;
   if (!hwndProject || !l)
		return 0 ;
   if (!l->next) {
      selectedProject = l ;
      return l ;
   }
   /* Did they select a project in the project window? */
	while (l) {
		FILELIST *m = l->files ;
      selectedProject = l ;
      if (l->buildFlags & BF_CHOSENPROJECT)
         return l ;
		l = l->next ;
	}
   /* no look for a selection */
   l = projectList ;
	if (treeViewSelected == 0)
		return 0 ;
	while (l) {
		FILELIST *m = l->files ;
      selectedProject = l ;
		if (treeViewSelected == l->treeHandle)
         return l ;
		while (m) {
			FILELIST *n = l->files ;
			if (treeViewSelected == m->treeHandle)
            return l ;
			while (n) {
				if (treeViewSelected == n->treeHandle)
               return l ;
				n = n->next ;
			}
			m = m->next ;
		}
		l = l->next ;
	}
	return 0 ;
}
void CollapseExpand(PROJLIST *selectedProj, int selectedItem, int flag)
{
   if (selectedItem != -1) {
      int i ;
      if (selectedItem == 0)
         TreeView_Expand(treeWindow, selectedProj->treeHandle, flag) ;
      else {
         FILELIST *m = selectedProj->files ;
         for (i=1; i < selectedItem; i++)
            m = m->next ;
         TreeView_Expand(treeWindow, m->treeHandle, flag) ;
      }
   }
}   
LRESULT  CALLBACK _export ProjectProc( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
	static int selectedItem,selectedHeader ;
	int i;
   RECT rs,*rt;
	static PROJLIST *selectedProj ;
	NM_TREEVIEW *nm ;
	FILELIST *list ;
	DWINFO info ;
	HMENU menu,popup ;
	POINT pos ;
   char buf[256] ;
   OPENFILENAME ofn ;
   LPNMTVKEYDOWN key ;
	switch(iMessage) {
		case WM_SYSCOMMAND :
			if (wParam == SC_CLOSE)
				SendMessage(hwnd,WM_CLOSE,0,0) ;
			break ;
      case WM_LBUTTONDOWN:
      case WM_RBUTTONDOWN:
         SetFocus(hwnd) ;
         break ;
		case WM_NOTIFY:
			nm = (NM_TREEVIEW *)lParam ;
			switch (nm->hdr.code) {
            case TVN_KEYDOWN:
               key = (LPNMTVKEYDOWN)lParam ;
               switch(key->wVKey) {
                  case VK_INSERT:
                     if (GetKeyState(VK_CONTROL) & 0x80000000)
                        CollapseExpand(selectedProj, selectedItem, TVE_EXPAND) ;
                     break ;
                  case VK_DELETE:
                     if (GetKeyState(VK_CONTROL) & 0x80000000)
                        CollapseExpand(selectedProj, selectedItem, TVE_COLLAPSE) ;
                     break ;
                  case VK_RETURN:
                     if (selectedItem && selectedItem != -1 )
                        SendMessage(hwnd,WM_COMMAND,IDM_OPENFILES,0) ;
                     break ;
               }
               break ;
            case NM_DBLCLK:
               FindItemByPoint(hwnd,&selectedProj,&selectedItem,&selectedHeader) ;
               PostMessage(hwnd,WM_COMMAND,IDM_OPENFILES,0) ;
               return 0 ;
				case NM_RCLICK:
                 FindItemByPoint(hwnd,&selectedProj,&selectedItem,&selectedHeader) ;
                 menu  = LoadMenu( hInstance, "PROJECTMENU" ) ;
                 popup = GetSubMenu( menu, 0 );
                  if (!selectedItem) {
							EnableMenuItem(menu,IDM_OPENFILES,MF_GRAYED) ;
                     EnableMenuItem(menu,IDM_COMPILEVIAPROJ,MF_GRAYED) ;
						} else if (!selectedHeader && selectedItem != -1) {
							EnableMenuItem(menu,IDM_PROJECTPROPERTIES, MF_GRAYED) ;
							EnableMenuItem(menu,IDM_CALCDEPENDS,MF_GRAYED) ;
							EnableMenuItem(menu,IDM_ADDTARGET,MF_GRAYED) ;
							EnableMenuItem(menu,IDM_REMOVETARGET,MF_GRAYED) ;
						} else {
							EnableMenuItem(menu,IDM_CALCDEPENDS,MF_GRAYED) ;
							if (selectedItem != -1)
								EnableMenuItem(menu,IDM_ADDTARGET,MF_GRAYED) ;
							else
								EnableMenuItem(menu,IDM_OPENFILES,MF_GRAYED) ;
							EnableMenuItem(menu,IDM_REMOVETARGET,MF_GRAYED) ;
							EnableMenuItem(menu,IDM_ADDFILES,MF_GRAYED) ;
							EnableMenuItem(menu,IDM_REMOVEFILES,MF_GRAYED) ;
                     EnableMenuItem(menu,IDM_COMPILEVIAPROJ,MF_GRAYED) ;
							EnableMenuItem(menu,IDM_PROJECTPROPERTIES, MF_GRAYED) ;
						}
  					GetCursorPos( &pos );
  					TrackPopupMenuEx( popup, TPM_BOTTOMALIGN | TPM_LEFTBUTTON,
								pos.x, pos.y, hwnd, NULL );
  					DestroyMenu( menu );
					break ;
				case TVN_SELCHANGED:
					nm = (NM_TREEVIEW *)lParam ;
					treeViewSelected = nm->itemNew.hItem ;
          FindItemBySelected(hwnd,&selectedProj,&selectedItem,&selectedHeader) ;
					break ;
			}	
			break ;
		case WM_COMMAND:
			switch(LOWORD(wParam)) {
				case IDM_CALCDEPENDS:
					CalcDepends(treeWindow,projectList) ;
					break ;
				case IDM_ADDFILES:
					ProjectAddFile(selectedProj,selectedItem) ;
					break ;
				case IDM_REMOVEFILES:
					ProjectDeleteFile(selectedProj,selectedItem) ;
					break ;
				case IDM_ADDTARGET:
					ProjectAddTarget(selectedProj) ;
					break ;
				case IDM_REMOVETARGET:
					ProjectDeleteTarget(selectedProj) ;
					break ;
            case IDM_COMPILEVIAPROJ:
					Compiler(selectedProj,selectedItem) ;
					break ;
            case IDM_GENMAKE:
               if (projectList && projectList->files) {
                  if (SaveFileDialog(&ofn,"makefile",hwnd,FALSE,0,"ProjDir","Save Makefile As"))
                     genMakeFile(projectList,ofn.lpstrFile) ;
                  ExtendedMessageBox("Makegen ok",0,"Makefile generated") ;
               } else
                  ExtendedMessageBox("Makegen Error",0,"You need a project with targets and source files to generate a make file") ;
               break ;
				case IDM_BUILDALL :
               SaveDrawAll() ;
					markProjects(0) ;
					CalcRebuilds() ;
					markProjects(1) ;
					Maker() ;
               CheckEditWindowChanged() ;
					break ;
				case IDM_MAKE:
               SaveDrawAll() ;
					markProjects(0) ;
					CalcRebuilds() ;
					Maker() ;
               CheckEditWindowChanged() ;
					break ;
				case IDM_PROJECTPROPERTIES:
					TargetProperties(hInstance,hwnd, selectedProj) ;
					break ;
				case IDM_OPENFILES:
doopen:
					selectedItem-- ;
					list = selectedProj->files ;
					for (i=0; i < selectedItem ; i++)
						if (list)
							list = list->next ;
					if (selectedHeader) {
						DEPENDSLIST *llist = list->depends ;
						for (i=1; i < selectedHeader; i++)
							if (llist)
								llist = llist->next ;
						if (llist) {
							strcpy(info.dwName,llist->name) ;
							strcpy(info.dwTitle,llist->title) ;
							info.dwLineNo = -1 ;
							CreateDrawWindow(&info) ;
						}
							
					} else if (list) {
						strcpy(info.dwName,list->name) ;
						strcpy(info.dwTitle,list->title) ;
						info.dwLineNo = -1 ;
						CreateDrawWindow(&info) ;
					}
					break;
				case IDM_CLOSE:
					SendMessage(hwnd,WM_CLOSE,0,0) ;
					break ;
				default:
					return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
			}
			break ;
		case WM_SETFOCUS:
			SendMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0) ;
			break ;
		case WM_CREATE:
			hwndProject = hwnd ;
			SetWindowText(hwnd,szProjectTitle) ;
			GetClientRect(hwnd,&rs) ;

			treeViewSelected = 0 ;
			flyellow = LoadBitmap(hInstance,"ID_FLYELLOW") ;
			flred = LoadBitmap(hInstance,"ID_FLRED") ;
			flcyan = LoadBitmap(hInstance,"ID_FLCYAN") ;
			flgreen = LoadBitmap(hInstance,"ID_FLGREEN") ;
			flwhite = LoadBitmap(hInstance,"ID_FLWHITE") ;
			fllib = LoadBitmap(hInstance,"ID_FLLIB") ;
			fldll = LoadBitmap(hInstance,"ID_FLDLL") ;
			flexe = LoadBitmap(hInstance,"ID_FLEXE") ;
			treeIml = ImageList_Create(16,16,FALSE,8,0) ;
			ilyellow = ImageList_Add(treeIml, flyellow, 0) ;
			ilred = ImageList_Add(treeIml, flred, 0) ;
			ilcyan = ImageList_Add(treeIml, flcyan, 0) ;
			ilgreen = ImageList_Add(treeIml, flgreen, 0) ;
			ilwhite = ImageList_Add(treeIml, flwhite, 0) ;
			illib = ImageList_Add(treeIml, fllib, 0) ;
			ildll = ImageList_Add(treeIml, fldll, 0) ;
			ilexe = ImageList_Add(treeIml, flexe, 0) ;
			DeleteObject(flyellow) ;
			DeleteObject(flred) ;
			DeleteObject(flcyan) ;
			DeleteObject(flgreen) ;
			DeleteObject(flwhite) ;
			DeleteObject(fllib) ;
			DeleteObject(fldll) ;
			DeleteObject(flexe) ;
			treeWindow = CreateWindowEx(0,WC_TREEVIEW,"",
               WS_VISIBLE | WS_CHILD | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | WS_BORDER,
					0,0,rs.right,rs.bottom,hwnd, (HMENU) ID_TREEVIEW, hInstance, NULL) ;
			TreeView_SetImageList(treeWindow,treeIml,TVSIL_NORMAL) ;
			InitItems(treeWindow, projectList) ;
         selectedProj = 0 ;
         selectedItem = selectedHeader = 0 ;
			return 0 ;
		case WM_CLOSE:
			break ;
		case WM_DESTROY:
         ProjectSaveList(TRUE) ;
			DestroyWindow(treeWindow) ;
			hwndProject = 0 ;
			ProjectFreeList() ;
			break ;
		case WM_SIZE:       
			MoveWindow(treeWindow,0,0,LOWORD(lParam),HIWORD(lParam),TRUE) ;
			break ;
		default: 
			break ;
	}
	return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
}
void RegisterProjectWindow(void)
{
		WNDCLASS wc ;
      memset(&wc,0,sizeof(wc)) ;
      wc.style = CS_DBLCLKS ;
		wc.lpfnWndProc = &ProjectProc ;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = sizeof(void *) ;
		wc.hInstance = hInstance ;
		wc.hIcon = LoadIcon(0,IDI_APPLICATION) ;
		wc.hCursor = LoadCursor(0,IDC_ARROW) ;
		wc.hbrBackground = GetStockObject(WHITE_BRUSH) ;
		wc.lpszMenuName = 0 ;
		wc.lpszClassName = szProjectClassName ;
		RegisterClass(&wc) ;
}
void NewProject (char *name)
{
	PROJLIST *list = 0 ;
   RECT rect ;
   char buf[256] ;
   if (projectList)
      ProjectSaveList(TRUE) ;
   strcpy(szProjectName,name) ;
   strcpy(szProjectTitle,name) ;
   if (!strrchr(szProjectName,'.')) {
      strcat(szProjectName,".prj") ;
      strcat(szProjectTitle,".prj") ;
   }
   if (!(list = ProjectLoadList(szProjectName, &rect)))
      return ;
   ProjectFreeList() ;
   ProjectNewList(list );
   sprintf(buf,"%s - CC386 IDE", szProjectTitle) ;
   SetWindowText(hwndFrame,buf) ;
   InitItems(treeWindow,projectList) ;
   SetWindowText(hwndProject,szProjectTitle) ;
}
void setProjectMRU(void)
{
   DWINFO info ;
   if (szProjectName[0]) {
      strcpy(info.dwName,szProjectName) ;
      strcpy(info.dwTitle, szProjectTitle) ;
      InsertMRU(&info, 1) ;
      MRUToMenu(1) ;
   }
}
void IndirectProjectWindow(DWINFO *info)
{
	PROJLIST *list = 0 ;
   RECT rect ;
   char buf[256] ;
   if (projectList)
      ProjectSaveList(TRUE) ;
   strcpy(szProjectTitle, info->dwTitle) ;
   strcpy(szProjectName, info->dwName) ;
   if (!(list = ProjectLoadList(szProjectName, &rect)))
      return ;
   ProjectFreeList() ;
   ProjectNewList(list );
   sprintf(buf,"%s - CC386 IDE", szProjectTitle) ;
   SetWindowText(hwndFrame,buf) ;
   changedProject = FALSE ;
   InitItems(treeWindow,projectList) ;
   SetWindowText(hwndProject,szProjectTitle) ;
   setProjectMRU() ;
}
void CreateProjectWindow(int new, HWND parent)
{
	MDICREATESTRUCT mc ;
	PROJLIST *list = 0 ;
	OPENFILENAME ofn ;
   RECT rect ;
   if (projectList)
      ProjectSaveList(TRUE) ;
	if (!new) {
      if (!(OpenFileDialog(&ofn,0,hwndClient,TRUE,FALSE,szProjectFilter,"ProjDir","Open Project")))
			return ;
		strcpy(szProjectTitle, ofn.lpstrFileTitle) ;
      strcpy(szProjectName, ofn.lpstrFile) ;
      if (!(list = ProjectLoadList(szProjectName, &rect)))
			return ;
	} else {
      
      if (new == 2) {
         if (!SaveFileDialog(&ofn,0,hwndProject,TRUE,szProjectFilter,"ProjDir","New Project")) 
            return ;
         strcpy(szProjectTitle, ofn.lpstrFileTitle) ;
         strcpy(szProjectName, ofn.lpstrFile) ;

      } else {
         szProjectTitle[0] = 0 ;
            szProjectName[0] = 0 ;
      }
	}
	if (hwndProject) {
      char buf[256] ;
		ProjectFreeList() ;
		ProjectNewList(list );
      changedProject = FALSE ;
      InitItems(treeWindow,projectList) ;
		SetWindowText(hwndProject,szProjectTitle) ;
      sprintf(buf,"%s - CC386 IDE", szProjectTitle) ;
      SetWindowText(hwndFrame,buf) ;
      setProjectMRU() ;
	} else {
      ProjectNewList(list );
      changedProject = FALSE ;
      GetTabRect(&rect) ;
      hwndProject = CreateWindow( szProjectClassName, szProjectTitle,
            WS_VISIBLE | WS_CHILD,
            rect.left,                               
            rect.top,
            rect.right-rect.left,
            rect.bottom-rect.top, parent, 0 ,hInstance, 0) ;
	}
}