
//ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
//	FILE	: App.cpp
//	AUTHOR	: Catalin Cocos
//__________________________________________________________________________________________________

#include "stdinc.h"
#include "app.h"
#include "resource.h"
#include "list.h"
#include "Tree.h"
#include "Process.h"

CScr2bin CnvEng;		// the file converter

HWND hWApp;				// the application main window
HWND hWToolbar;			// the toolbar
HWND hWStatusbar;		// the Statusbar
HWND hWList;			// the collection file list
HWND hWTree;			// the tree view
HWND hWListView;		// the list view
HWND hWOut;				// the output window


HWND hWFilter;			// the Filter list view
int FILTER_WIDTH;

HINSTANCE hInstance;	// the current instance
int ToolbarHeight;		// the height of the toolbar
int StatusHeight;		// the height of the status bar

char CrtCfg[MAX_PATH];

float SplitX = .3f;
float SplitY = .2f;

int nFocusWnd = 0;

int SplitDrag = FALSE, SplitMode;
enum { SD_VERTICAL = 0x01, SD_HORIZONTAL = 0x02, SD_FILTER = 0x04};
BOOL Active = TRUE;

// operating:

int OperatingMode = ID_MODE_NORMAL;
BOOL volatile Processing = FALSE;

int OperatingState = -1;
BOOL ProcessingState = TRUE;
BOOL FilterActive = FALSE;

void OnIdle()
{
	if(FilterActive != CnvEng.IsFilteringActive())
	{
		FilterActive = !FilterActive;
		HMENU hMenu = GetSubMenu(GetMenu( hWApp ), 3);
		CheckMenuItem( hMenu, ID_MODE_FILTER, FilterActive? MF_CHECKED: MF_UNCHECKED);
		SendMessage( hWToolbar, TB_CHECKBUTTON, ID_MODE_FILTER, FilterActive? TRUE: FALSE);
	}
	if(Processing != ProcessingState)
	{
		ProcessingState = Processing;
		UINT state = (Processing? MF_GRAYED: MF_ENABLED);

		HMENU hMenu = GetSubMenu(GetMenu( hWApp ), 3);
		EnableMenuItem( hMenu, ID_MODE_NORMAL, state);
		EnableMenuItem( hMenu, ID_MODE_FORCED, state);
		EnableMenuItem( hMenu, ID_MODE_CLEAN, state);
		EnableMenuItem( hMenu, ID_MODE_FILTER, state);
		hMenu = GetSubMenu(GetMenu( hWApp ), 2);
		EnableMenuItem( hMenu, ID_ACTION_PROCESS_LISTS, state);
		EnableMenuItem( hMenu, ID_ACTION_PROCESS_SELECTION, state);
		EnableMenuItem( hMenu, ID_ACTION_PROCESS_ALL, state);
		EnableMenuItem( hMenu, ID_ACTION_PROCESS_CANCEL, Processing? MF_ENABLED: MF_GRAYED);
		
		state = (Processing? FALSE: TRUE);

		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_MODE_NORMAL, state);
		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_MODE_FORCED, state);
		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_MODE_CLEAN, state);
		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_MODE_FILTER, state);
		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_ACTION_PROCESS_LISTS, state);
		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_ACTION_PROCESS_SELECTION, state);
		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_ACTION_PROCESS_ALL, state);
		SendMessage( hWToolbar, TB_ENABLEBUTTON, ID_ACTION_PROCESS_CANCEL, !state);

		
		int listCount = ListView_GetItemCount( hWFilter );
		LV_ITEM filterItem = { LVIF_STATE, 0, 0, (Processing && CnvEng.IsFilteringActive())? LSTATE_LOCKED: 0, LSTATE_LOCKED };
		for(filterItem.iItem = 0; filterItem.iItem<listCount; filterItem.iItem++)
			ListView_SetItem( hWFilter, &filterItem);
		
		if(!Processing)
		{
			SendMessage( hWToolbar, TB_CHECKBUTTON, ID_ACTION_PROCESS_LISTS, FALSE);
			SendMessage( hWToolbar, TB_CHECKBUTTON, ID_ACTION_PROCESS_SELECTION, FALSE);
			SendMessage( hWToolbar, TB_CHECKBUTTON, ID_ACTION_PROCESS_ALL, FALSE);
		}
	}

	if(OperatingState != OperatingMode)
	{
		OperatingState = OperatingMode;
		HMENU hMenu = GetSubMenu(GetMenu( hWApp ), 3);
		CheckMenuItem( hMenu, ID_MODE_NORMAL, OperatingMode==ID_MODE_NORMAL? MF_CHECKED: MF_UNCHECKED);
		CheckMenuItem( hMenu, ID_MODE_FORCED, OperatingMode==ID_MODE_FORCED? MF_CHECKED: MF_UNCHECKED);
		CheckMenuItem( hMenu, ID_MODE_CLEAN, OperatingMode==ID_MODE_CLEAN? MF_CHECKED: MF_UNCHECKED);
		SendMessage( hWToolbar, TB_CHECKBUTTON, ID_MODE_NORMAL, OperatingMode==ID_MODE_NORMAL? TRUE: FALSE);
		SendMessage( hWToolbar, TB_CHECKBUTTON, ID_MODE_FORCED, OperatingMode==ID_MODE_FORCED? TRUE: FALSE);
		SendMessage( hWToolbar, TB_CHECKBUTTON, ID_MODE_CLEAN, OperatingMode==ID_MODE_CLEAN? TRUE: FALSE);
	}
}
//ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
// configuration 
//__________________________________________________________________________________________________


LRESULT CALLBACK AppWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
{
	static BOOL bRefresh = TRUE;

	switch(msg)
	{
    case WM_COMMAND:
		switch(LOWORD(wp))
		{
		case ID_MODE_FILTER: CnvEng.SetFiltering(!CnvEng.IsFilteringActive()); break;
		case ID_MODE_NORMAL:
		case ID_MODE_FORCED:
		case ID_MODE_CLEAN:
			OperatingState = -1;
			OperatingMode = LOWORD(wp);
			break;
		case ID_NEXT_WINDOW: nFocusWnd+=2;
		case ID_PREV_WINDOW: nFocusWnd+=4;
			nFocusWnd%=5;
			RestoreFocus();
			break;
		case ID_FILEOPEN:
			{
				bRefresh = FALSE;
				char filename[_MAX_PATH] = "";
				OPENFILENAME	ofn ={sizeof(OPENFILENAME),hWApp,hInstance,
				"Configuration files (*.s2b)\0*.s2b\0",NULL,0,0,filename,_MAX_PATH,NULL,0,NULL,"Load Configuration",
				OFN_FILEMUSTEXIST,0,NULL,NULL,0,NULL,NULL};
				if(GetOpenFileName(&ofn))
				{
					SaveConfiguration();
					strcpy(CrtCfg, filename);
					LoadConfiguration();
					Refresh();
					UpdateAppName();
				}
			}
			break;
		case ID_FILESAVEAS:
			{
				bRefresh = FALSE;
				char filename[_MAX_PATH] = "";
				OPENFILENAME	ofn ={sizeof(OPENFILENAME),hWApp,hInstance,
				"Configuration files (*.s2b)\0*.s2b\0",NULL,0,0,filename,_MAX_PATH,NULL,0,NULL,"Save Configuration As...",
				0,0,NULL,"s2b",0,NULL,NULL};
				if(GetSaveFileName(&ofn))
				{
					strcpy(CrtCfg, filename);
					SaveConfiguration();
					UpdateAppName();
				}
			}
			case ID_FILESAVE: SaveConfiguration(); break;	
			break;	
		case ID_FILE_EXIT: PostMessage( hwnd, WM_CLOSE, 0,0); break;
		case ID_EDIT_MARK:
			if(GetFocus())
				PostMessage( GetFocus(), WM_COMMAND, ID_SELECT_TOGGLE, 0);
			break;
		case ID_EDIT_RECURSE:
			break;
		case ID_EDIT_INSERT_LIST:
			{
				bRefresh = FALSE;
				char filename[_MAX_PATH] = "";
				OPENFILENAME	ofn ={sizeof(OPENFILENAME),NULL,hInstance,
				"Collection files (*.txt, *.log)\0*.txt;*.log\0",NULL,0,0,filename,_MAX_PATH,NULL,0,NULL,"Load Configuration",
				OFN_FILEMUSTEXIST,0,NULL,NULL,0,NULL,NULL};
				if(GetOpenFileName(&ofn))
					InsertCollectionFile(hWList, filename);
			}
			break;	
		case ID_EDIT_REMOVE_ALL_LISTS:	DeleteCollectionList();	break;	
		case ID_EDIT_CLEARSEL:	CleanSelection(); break;	
		case ID_EDIT_CLEARALL:	DeleteCollectionList();	CleanSelection(); break;	
		case ID_ACTION_PROCESS_LISTS: Process(PROCESS_COLLECTIONS); break;	
		case ID_ACTION_PROCESS_SELECTION: Process(PROCESS_SELECTION); break;	
		case ID_ACTION_PROCESS_ALL: Process(PROCESS_ALL); break;	
		case ID_ACTION_PROCESS_CANCEL: StopProcessing(); break;	
		default: return DefWindowProc(hwnd, msg, wp, lp);
		}
	break;
	case WM_MOUSEMOVE:
		if(SplitDrag)
		{
			RECT r;
			GetClientRect( hWApp, &r);
			if(SplitMode & SD_HORIZONTAL)
				SplitY = ((float)HIWORD(lp) - ToolbarHeight)/(r.bottom - ToolbarHeight - StatusHeight);
			if(SplitMode & SD_VERTICAL)
				SplitX = ((float)LOWORD(lp) - FILTER_WIDTH - 3)/(r.right - FILTER_WIDTH -3);
			if(SplitMode & SD_FILTER)
				FILTER_WIDTH = LOWORD(lp) - 1;
			PositionChildwindows();
		}
		else
		{
			SplitMode = 0;
			RECT r;
			GetClientRect( hWApp, &r);
			int xSplit = (int)((r.right - FILTER_WIDTH -3)*SplitX) + FILTER_WIDTH + 3;
			int ySplit = (int)((r.bottom - ToolbarHeight - StatusHeight )*SplitY) + ToolbarHeight;
			if(LOWORD(lp)-FILTER_WIDTH < 3)	SplitMode |= SD_FILTER;
			if(abs(xSplit - LOWORD(lp)) < 2) SplitMode |= SD_VERTICAL;
			if(abs(abs(ySplit - HIWORD(lp))) < 2) SplitMode |= SD_HORIZONTAL;

			switch(SplitMode)
			{
			case SD_FILTER:
			case SD_VERTICAL: SetCursor(LoadCursor(hInstance, MAKEINTRESOURCE(IDC_VERTICAL_SPLIT))); break;
			case SD_HORIZONTAL: SetCursor(LoadCursor(hInstance, MAKEINTRESOURCE(IDC_HORIZONTAL_SPLIT))); break;
			default: SetCursor(LoadCursor(hInstance, MAKEINTRESOURCE(IDC_SPLIT_ALL)));
			}
		}
		break;
	case WM_NCLBUTTONUP:
	case WM_NCMOUSEMOVE:
		pListTip->Show( NULL, NULL);
		return DefWindowProc(hwnd, msg, wp, lp);
	case WM_LBUTTONDOWN:
		{
			SplitDrag = TRUE;
			RECT r;
			GetClientRect( hWApp, &r);
			r.top += ToolbarHeight;
			r.bottom-=StatusHeight;
			if(SplitMode & SD_FILTER);// r.right -= 6;
			else r.left = FILTER_WIDTH + 4;
			MapWindowPoints( hwnd, NULL, (LPPOINT) &r, 2);
			SetCapture( hwnd );
			ClipCursor( &r );
		}
		break;
	case WM_LBUTTONUP:
			SplitDrag = FALSE;
			ReleaseCapture();
			ClipCursor( NULL );
			break;
	case WM_SIZING:
		{	// main window size control
			RECT* lprc = (LPRECT) lp;
			if(lprc->right - lprc->left < MIN_CLIENTX )
				if(wp  == WMSZ_LEFT || wp  == WMSZ_BOTTOMLEFT || wp  == WMSZ_TOPLEFT)
					 lprc->left -= MIN_CLIENTX + lprc->left - lprc->right;
				else lprc->right += MIN_CLIENTX + lprc->left - lprc->right;
			if(lprc->bottom - lprc->top < MIN_CLIENTY )
				if(wp  == WMSZ_TOP || wp  == WMSZ_TOPLEFT || wp  == WMSZ_TOPRIGHT)
					 lprc->top -= MIN_CLIENTY + lprc->top - lprc->bottom;
				else lprc->bottom += MIN_CLIENTY + lprc->top - lprc->bottom;
		}
		break;
	case WM_SIZE:
		{	// toolbar/statusbar repositioning
			SendMessage(hWStatusbar, WM_SIZE, wp, lp);
			SendMessage(hWToolbar, WM_SIZE, wp, lp);
			PositionChildwindows();
		}
		break;
	case WM_CLOSE:
		if(!Processing)
		{
			SaveConfiguration();	// save the current configuration
			DestroyWindow( hwnd );	// default processing
		}
		else MessageBox( hwnd, "You cannot close the application while processing.\n\rPlease choose the Cancel Processing command before closing the application",
			"Script Converter", MB_ICONHAND|MB_OK);
		break;
	case WM_DESTROY:
		Active = FALSE;
		PostQuitMessage(0);
		break;
    case WM_NOTIFY:
		switch(((LPNMHDR)lp)->code)
		{
		case TTN_NEEDTEXT:
			{
				LPTOOLTIPTEXT lpttt; 
				lpttt = (LPTOOLTIPTEXT) lp; 
				lpttt->hinst = hInstance; 
				switch ( lpttt->hdr.idFrom)
				{
				// set the needed tooltip text (for the toolbar)
				case ID_FILEOPEN:   lpttt->lpszText = "Open a previously saved Configuration file (Ctrl+O) "; break;
				case ID_FILESAVE:   lpttt->lpszText = "Save the current Configuration (Ctrl+S)"; break;	
				case ID_FILESAVEAS: lpttt->lpszText = "Save the current Configuration using a new name (Shft+S)"; break;	
				case ID_EDIT_INSERT_LIST: lpttt->lpszText = "Insert Collections"; break;	
				case ID_EDIT_REMOVE_ALL_LISTS: lpttt->lpszText = "Clear Collection List"; break;	
				case ID_EDIT_CLEARSEL: lpttt->lpszText = "Clear Selection"; break;	
				case ID_EDIT_CLEARALL: lpttt->lpszText = "Clear all (both collections and selection)"; break;	
				case ID_ACTION_PROCESS_LISTS: lpttt->lpszText = "Process Collection List (F7)"; break;	
				case ID_ACTION_PROCESS_SELECTION: lpttt->lpszText = "Process current selection (Shft+F7)"; break;	
				case ID_ACTION_PROCESS_ALL: lpttt->lpszText = "Process all (both collection list and selection) (Ctrl+F7)"; break;	
				case ID_ACTION_PROCESS_CANCEL: lpttt->lpszText = "Cancel Processing (Esc)"; break;	
				case ID_MODE_NORMAL: lpttt->lpszText = "Standard mode (process out-of-date files)"; break;	
				case ID_MODE_FORCED: lpttt->lpszText = "Forced mode (forced conversion of all selected files)"; break;	
				case ID_MODE_CLEAN: lpttt->lpszText = "Clean-up mode (delete the corresponding binary files)"; break;	
				case ID_MODE_FILTER: lpttt->lpszText = "Toggle Filtered Processing"; break;	
				default: return DefWindowProc(hwnd, msg, wp, lp);
				}
			}
			break;
		case TVN_SELCHANGED:
			if(Active) DisplayView( ((NM_TREEVIEW*)lp)->itemNew.hItem, (CDirInfo*)((NM_TREEVIEW*)lp)->itemNew.lParam );
			break;
		case TVN_GETDISPINFO:
			{
				TV_DISPINFO *ptvdi = (TV_DISPINFO *)lp;
				char fName[_MAX_PATH+10];
				strcpy(fName, ((CDirInfo*)ptvdi->item.lParam)->FileName());
				strcat(fName, "\\*.*");
				WIN32_FIND_DATA fd;
				ptvdi->item.cChildren = 0;
				HANDLE hSearch = FindFirstFile( fName, &fd );
				for(BOOL Found = (hSearch != INVALID_HANDLE_VALUE); Found; Found = FindNextFile( hSearch, &fd))
					if((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (fd.cFileName[0] != '.'))
					{
						ptvdi->item.cChildren = 1;
						break;
					}
				if( hSearch != INVALID_HANDLE_VALUE ) FindClose( hSearch );
			}
			break;
		case TVN_DELETEITEM:
			delete (CDirInfo*)((NM_TREEVIEW*)lp)->itemOld.lParam;
			break;
		case TVN_ITEMEXPANDED:
			{
				NM_TREEVIEW* pnmtv = (NM_TREEVIEW *) lp;
				if(pnmtv->action == TVE_COLLAPSE)
				{
					HTREEITEM hItem, hPrevItem = NULL;
					for( hItem = TreeView_GetChild(hWTree, pnmtv->itemNew.hItem); hItem; )
					{
						TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hItem, 0, ~0 };
						TreeView_GetItem( hWTree, &ti);
						if((ti.state & TVIS_EXPANDED) ||
						   (ti.state & TVIS_OVERLAYMASK) ||
						   ((ti.state & TVIS_STATEIMAGEMASK) == TI_STATE_FILES) ||
						   ((ti.state & TVIS_STATEIMAGEMASK) == TI_STATE_RECURSE)
						   ||((CDirInfo*)ti.lParam)->GetSize()) 
						{
							hItem = TreeView_GetNextSibling( hWTree, hPrevItem = hItem );
							continue;
						}
						TreeView_DeleteItem( hWTree, hItem);
						hItem =(hPrevItem?
								TreeView_GetNextSibling( hWTree, hPrevItem ):
								TreeView_GetChild(hWTree, pnmtv->itemNew.hItem));
					}
				}
			}
			break;
		case TVN_ITEMEXPANDING:
			if(((NM_TREEVIEW*)lp)->action == TVE_EXPAND)
				SetTreeItemChildren(&((NM_TREEVIEW*)lp)->itemNew);
			break;
		default:
			switch(((NMHDR*)lp)->idFrom)
			{
			case IDW_LIST:
				{
					NM_LISTVIEW* pList = (NM_LISTVIEW*) lp;
					if(IsWindowVisible(hWList) && pList->hdr.code == LVN_ITEMCHANGED && pList->uNewState & LVIS_FOCUSED )
						PostMessage( hWList, WM_USER+10, pList->iItem, 0);
				}
				break;
			default: return DefWindowProc(hwnd, msg, wp, lp);
			}
		}
		break;
	case WM_ACTIVATE:
		if(LOWORD(wp) != WA_INACTIVE )
		{
			//refresh the app;
			if( bRefresh )
			{
				Active = FALSE;
				Refresh();
				Active = TRUE;
			}
			bRefresh = TRUE;
			RestoreFocus();
		}
		break;
	default:return DefWindowProc(hwnd, msg, wp, lp);
	}
	return 0;
}
 
void MakeToolbar( HWND hwnd )
{
  TBBUTTON but[] ={//{0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0},                       // the buttons
                   {29,ID_MODE_FILTER,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0},
                   {STD_FILEOPEN,ID_FILEOPEN,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {STD_FILESAVE,ID_FILESAVE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {15,ID_FILESAVEAS,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0},
                   {20,ID_EDIT_INSERT_LIST,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0},
                   {21,ID_EDIT_REMOVE_ALL_LISTS,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {23,ID_EDIT_CLEARSEL,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {24,ID_EDIT_CLEARALL,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0},
                   {17,ID_ACTION_PROCESS_LISTS,TBSTATE_ENABLED,TBSTYLE_CHECK,0,0},
                   {18,ID_ACTION_PROCESS_SELECTION,TBSTATE_ENABLED,TBSTYLE_CHECK,0,0},
                   {19,ID_ACTION_PROCESS_ALL,TBSTATE_ENABLED,TBSTYLE_CHECK,0,0},
                   {16,ID_ACTION_PROCESS_CANCEL,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0},
                   {25,ID_MODE_NORMAL,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {26,ID_MODE_FORCED,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   {27,ID_MODE_CLEAN,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0},
                   };
  hWToolbar= CreateToolbarEx(hwnd,WS_VISIBLE|WS_CHILD|TBSTYLE_TOOLTIPS,IDW_TOOLBAR,0,  // create the toolbar
    hInstance,NULL,but,20,18,18,16,15,sizeof(TBBUTTON));
  TBADDBITMAP bit={HINST_COMMCTRL,IDB_STD_SMALL_COLOR}; // standard button bitmats
  SendMessage(hWToolbar,TB_ADDBITMAP,16,(LPARAM)&bit); // add standard bitmaps
  bit.hInst = hInstance;
  bit.nID = IDB_TOOLBAR; // our button bitmaps
  SendMessage(hWToolbar,TB_ADDBITMAP,34,(LPARAM)&bit); // add our bitmaps
}

void PositionChildwindows()
{
	RECT r;
	GetClientRect( hWApp, &r);
	r.right -= FILTER_WIDTH + 3;
	
	int xSplit = (int)(r.right*SplitX);
	int ySplit = (int)((r.bottom - ToolbarHeight - StatusHeight )*SplitY);
	MoveWindow( hWFilter, 0, ToolbarHeight, FILTER_WIDTH, r.bottom - ToolbarHeight - StatusHeight, TRUE );
	
	MoveWindow( hWList, FILTER_WIDTH +3, ToolbarHeight, xSplit- 1, ySplit - 1, TRUE );
	MoveWindow( hWOut, FILTER_WIDTH +3 + xSplit+2, ToolbarHeight, r.right - xSplit - 2, ySplit - 1, TRUE );
	MoveWindow( hWTree, FILTER_WIDTH +3, ToolbarHeight + ySplit + 2, xSplit- 1, r.bottom- ToolbarHeight - StatusHeight - ySplit - 2, TRUE );
	MoveWindow( hWListView, FILTER_WIDTH +3 + xSplit+2, ToolbarHeight + ySplit + 2, r.right - xSplit- 2, r.bottom- ToolbarHeight - StatusHeight - ySplit - 2, TRUE );
}


void SaveTreeItemChildren(HANDLE cfgFile, HTREEITEM hParent, BOOL SaveLastExpanded = TRUE)
{
	BOOL LastExpanded = FALSE;
	if(SaveLastExpanded)
	{
		LastExpanded = TRUE;
		TV_ITEM tie = {TVIF_STATE|TVIF_HANDLE, 0,0,TVIS_EXPANDED };
		for(tie.hItem = TreeView_GetChild( hWTree, hParent); tie.hItem; tie.hItem = TreeView_GetNextSibling(hWTree, tie.hItem))
		{
			TreeView_GetItem( hWTree, &tie );
			if( tie.state & TVIS_EXPANDED)
			{
				 LastExpanded = FALSE;
				 break;
			}
		}
	}
	TV_ITEM ti = {TVIF_STATE|TVIF_HANDLE|TVIF_PARAM, hParent,0,0 };
	TreeView_GetItem( hWTree, &ti );
	if(!(ti.state & TVIS_EXPANDED)) SaveLastExpanded = FALSE;
	if(((CDirInfo*)ti.lParam)->GetSize() ||
		ti.state & (TVIS_SELECTED|LVIS_FOCUSED) ||
		ti.state & TVIS_EXPANDED && LastExpanded ||
		(ti.state & TVIS_STATEIMAGEMASK) == TI_STATE_FILES ||
		(ti.state & TVIS_STATEIMAGEMASK) == TI_STATE_RECURSE )
	{
		// save this item
		CDirInfo* pDi = (CDirInfo*)ti.lParam;
		StoreFileName(cfgFile, (char*)pDi->FileName(), CrtCfg);	// the relative path to the directory
		int Files = pDi->GetSize(); 
		StoreItem( cfgFile, Files);					// the number of selected files in the directory
		for(int i = 0; i< Files; i++ )				// the name of the files
			StoreItem( cfgFile,(char*)(*pDi)[i]->FileName()); 
		if(!LastExpanded) ti.state &= ~TVIS_EXPANDED;
		StoreItem( cfgFile, ti.state );
	}
	if(SaveLastExpanded || ti.state & TVIS_OVERLAYMASK)
		//  save the children (the ones needed)
		for(HTREEITEM hChild = TreeView_GetChild( hWTree, hParent); hChild; hChild = TreeView_GetNextSibling(hWTree, hChild))
			SaveTreeItemChildren( cfgFile, hChild, SaveLastExpanded); 
}

void SaveConfiguration()
{
	HCURSOR hOldCursor = SetCursor( LoadCursor(NULL, IDC_WAIT));
	
	char* szIni = GetAppPathPlus( INIFILE ); // get configuration file name
	HANDLE iniFile = CreateFile(szIni,GENERIC_WRITE, 0,NULL,CREATE_ALWAYS,0,NULL); // open the defs file
	if( iniFile  != INVALID_HANDLE_VALUE)
	{
		StoreFileName(iniFile, CrtCfg, szIni);
		CloseHandle(iniFile);
	}
	delete[] szIni; // we don't need it anymore

	HANDLE cfgFile = CreateFile(CrtCfg,GENERIC_WRITE, 0,NULL,CREATE_ALWAYS,0,NULL); // open the defs file
	if( cfgFile != INVALID_HANDLE_VALUE)
	{
		// save the configuration
		WINDOWPLACEMENT FramePos = { sizeof(WINDOWPLACEMENT) };
		GetWindowPlacement( hWApp, &FramePos);
		StoreItem(cfgFile, FramePos);
		StoreItem(cfgFile, SplitX);
		StoreItem(cfgFile, SplitY);
		StoreItem(cfgFile, nFocusWnd);

		//save the collection list
		int listCount = ListView_GetItemCount( hWList );
		StoreItem(cfgFile, listCount);
		LV_ITEM testItem = { LVIF_STATE|LVIF_PARAM, 0, 0, LVIS_SELECTED, ~0 };
		for(testItem.iItem = 0; testItem.iItem<listCount; testItem.iItem++)
		{
			ListView_GetItem( hWList, &testItem);
			StoreItem(cfgFile, testItem.state);
			StoreFileName(cfgFile, (char*)testItem.lParam, CrtCfg);
		}

		//save the selection tree
		for(HTREEITEM hChild = TreeView_GetRoot(hWTree); hChild; hChild = TreeView_GetNextSibling(hWTree, hChild))
			SaveTreeItemChildren( cfgFile, hChild);
		StoreItem(cfgFile, "");

		StoreItem( cfgFile, OperatingMode);

		//save the filter list
		listCount = ListView_GetItemCount( hWFilter );
		StoreItem(cfgFile, listCount);
		char szFilterExt[_MAX_EXT];
		LV_ITEM filterItem = { LVIF_STATE|LVIF_TEXT, 0, 0, 0, ~0, szFilterExt, _MAX_EXT};
		for(filterItem.iItem = 0; filterItem.iItem<listCount; filterItem.iItem++)
		{
			ListView_GetItem( hWFilter, &filterItem);
			StoreItem(cfgFile, szFilterExt);
			StoreItem(cfgFile, filterItem.state);
		}

		// save filtering status
		BOOL bFiltering = CnvEng.IsFilteringActive();
		StoreItem( cfgFile, bFiltering);

		StoreItem( cfgFile, FILTER_WIDTH);
		//saving ends here
		//---------------------------------------------------
		CloseHandle(cfgFile);
	}
	SetCursor( hOldCursor );
}

void ConstructTreeItem(HTREEITEM hSibling, UINT state, char* szFolderName, CDirInfo* pDirInfo);

HTREEITEM hSelected;

BOOL LoadConfiguration()
{
	BOOL Maximized = FALSE;
	HCURSOR hOldCursor = SetCursor( LoadCursor(NULL, IDC_WAIT));
	SetWindowText( hWStatusbar, "Loading Configuration, please wait...");

	ListView_DeleteAllItems( hWFilter );
	FILTER_WIDTH = ListView_GetStringWidth( hWFilter, "WWW") +16 + 7 + GetSystemMetrics(SM_CXVSCROLL);
	
	// load the application configuration
	HANDLE cfgFile = CreateFile(CrtCfg,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); // open the defs file
	if( cfgFile != INVALID_HANDLE_VALUE)
	{
		// clear the previous configuration
		DeleteCollectionList();	// clear the collection list
		ShowWindow( hWTree, SW_HIDE );
		InitTree();				// re-initialize the tree view

		// we have a saved configuration file
		WINDOWPLACEMENT FramePos;
		LoadItem (cfgFile, FramePos);
		LoadItem (cfgFile, SplitX);
		LoadItem (cfgFile, SplitY);
		LoadItem (cfgFile, nFocusWnd);
		RestoreFocus();
		// load the collection list
		int listCount;
		LoadItem(cfgFile, listCount);
		char* pathStr, FName[_MAX_FNAME+1];
		LV_ITEM testItem = { LVIF_TEXT|LVIF_STATE|LVIF_PARAM, 0, 0, LVIS_SELECTED, ~0, FName };
		for(testItem.iItem = 0; testItem.iItem<listCount; testItem.iItem++)
		{
			LoadItem(cfgFile, testItem.state);
			LoadFileName(cfgFile, pathStr, CrtCfg);
			_splitpath( pathStr, NULL, NULL, FName, NULL);
			testItem.lParam = (LONG)pathStr;
			ListView_InsertItem( hWList, &testItem);
		}
		// load the tree
		char* szFolderName = NULL;
		LoadFileName(cfgFile, szFolderName, CrtCfg );
		while ( szFolderName )
		{
			// load the folder
			CDirInfo* pDirInfo = new CDirInfo(szFolderName);
			int Files;
			LoadItem( cfgFile, Files);					// the number of selected files in the directory
			pDirInfo->SetSize(Files);
			char* szFileName;
			for(int i = 0; i< Files; i++ )				// the name of the files
			{
				LoadItem( cfgFile,szFileName);
				pDirInfo->SetAt( i, new CFileName(szFileName));	
				delete[] szFileName;
			}
			UINT state;
			LoadItem( cfgFile, state );
			BOOL Drive = FALSE;
			if( szFolderName[strlen(szFolderName) -1] == ':')
			{
				// the directory is, in fact, a drive
				TV_ITEM it = { TVIF_HANDLE|TVIF_PARAM, 0 };
				for( it.hItem = TreeView_GetRoot( hWTree ); it.hItem; it.hItem = TreeView_GetNextSibling(hWTree, it.hItem) )
				{
					TreeView_GetItem( hWTree, &it);
					if(!strcmpi(szFolderName, ((CDirInfo*)(it.lParam))->FileName()))
					{
						Drive = TRUE;
						break;
					}
				}
			}
			WIN32_FIND_DATA fd;
			HANDLE hfind = INVALID_HANDLE_VALUE;
			if(	Drive ||
				(((hfind = FindFirstFile(szFolderName, &fd)) != INVALID_HANDLE_VALUE) &&
				(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
			{
				// construct the tree item
				ConstructTreeItem( NULL, state, szFolderName, pDirInfo);
			}
			if(hfind != INVALID_HANDLE_VALUE) FindClose( hfind );
			delete[] szFolderName;
			LoadFileName(cfgFile, szFolderName, CrtCfg );
		}

		LoadItem( cfgFile, OperatingMode);

		char* szExt;
		LV_ITEM filterItem = { LVIF_STATE|LVIF_TEXT, 0, 0, 0, ~0 };

		BOOL bFiltering = FALSE;
		CnvEng.SetFiltering( FALSE );

		if(LoadItem(cfgFile, listCount))
		{
			//load filter list
			for(int i = 0; i<listCount; i++)
			{
				LoadItem( cfgFile, szExt);
				filterItem.pszText = szExt;
				LoadItem( cfgFile, filterItem.state);
				if( CnvEng.EnableExtension( szExt, filterItem.state & LSTATE_CHECKED ) )
					ListView_InsertItem( hWFilter, &filterItem );
				delete[] szExt;
			}
			// load filter state
			LoadItem( cfgFile, bFiltering); 
			CnvEng.SetFiltering( bFiltering );
		}

		LoadItem( cfgFile, FILTER_WIDTH);
		// loading ends here
		//---------------------------------------------------------------
		TreeView_EnsureVisible( hWTree, hSelected);
		CloseHandle(cfgFile);
		SetWindowPlacement( hWApp, &FramePos);
		PositionChildwindows();	
		if(FramePos.showCmd == SW_SHOWMAXIMIZED) Maximized = TRUE;
		ShowWindow( hWTree, SW_SHOWNA );
	}

	int	listCount = CnvEng.GetExtCount();
	LV_FINDINFO fi = {LVFI_STRING};
	LV_ITEM filterItem = { LVIF_STATE|LVIF_TEXT, 0, 0, 0, LVIS_STATEIMAGEMASK };
	for( int i = 0; i< listCount; i++)
	{
		CFileExtension* pExt = CnvEng.Extension(i);
		fi.psz = pExt->Name();
		if( ListView_FindItem( hWFilter, -1, &fi ) < 0)
		{
			filterItem.state = (pExt->IsEnabled()? LSTATE_CHECKED: LSTATE_UNCHECKED);
			filterItem.pszText = (char*)fi.psz;
			ListView_InsertItem( hWFilter, &filterItem);
		}
	}

	
	
	SetCursor( hOldCursor );
	SetWindowText( hWStatusbar, "Ready");
	return Maximized;
}

void ConstructTreeItem(HTREEITEM hParent, UINT state, char* szFolderName, CDirInfo* pDirInfo)
{
	char* end = strchr(szFolderName, '\\');
	if(end) *end = 0;

	HTREEITEM hMatch = NULL;
	char FolderName[_MAX_PATH];
	TV_ITEM it = { TVIF_HANDLE|TVIF_TEXT|TVIF_PARAM, 0, 0, 0, FolderName, _MAX_PATH };
	for( it.hItem = TreeView_GetChild( hWTree, hParent ); it.hItem; it.hItem = TreeView_GetNextSibling(hWTree, it.hItem) )
	{
		TreeView_GetItem( hWTree, &it);
		if(!strcmpi(szFolderName, FolderName) ||
			(szFolderName[strlen(szFolderName) -1] == ':' &&
			!strcmpi(szFolderName, ((CDirInfo*)(it.lParam))->FileName())))
		{
			//we have a match
			hMatch = it.hItem;
			// expand the item if necessary
			if( !end )
			{
				// this is the target item
				delete (CDirInfo*) it.lParam;
				TV_ITEM set = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hMatch,
								state, ~(TVIS_EXPANDED|TVIS_EXPANDEDONCE|TVIS_EXPANDPARTIAL|TVIS_SELECTED|LVIS_FOCUSED)};
				set.lParam = (LPARAM) pDirInfo;
				TreeView_SetItem(hWTree, &set);
				if(state & TVIS_SELECTED) 
					TreeView_SelectItem(hWTree, hSelected = hMatch);
			}
			if(state & TVIS_EXPANDED)
			{
				TV_ITEM get = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hMatch, 0, ~0  };
				TreeView_GetItem(hWTree, &get);
				int Exp = TreeView_Expand (hWTree, hMatch, TVE_EXPAND );
				TreeView_GetItem(hWTree, &get);
				SetTreeItemChildren( &get );
			}
			break;
		}
	}
	if(!hMatch)
	{
		TV_ITEM get = { TVIF_HANDLE|TVIF_STATE, hParent, 0, ~0  };
		if(hParent && (hParent))
			TreeView_GetItem(hWTree, &get);

		TV_INSERTSTRUCT tis =
		{	hParent, TVI_SORT ,
			{	TVIF_CHILDREN|TVIF_SELECTEDIMAGE|TVIF_PARAM|TVIF_IMAGE|TVIF_STATE|TVIF_TEXT,
				NULL,((get.state & TVIS_STATEIMAGEMASK)>TI_STATE_FILES)? TI_STATE_LOCKED:TI_STATE_NONE,
				TVIS_STATEIMAGEMASK, szFolderName, 0, 0, 1, I_CHILDRENCALLBACK, 0
			}
		};
		// set the item's info
		if( end )
		{
			// we didn't yet reached the item - we are inserting its predecessors
			int length = strlen(pDirInfo->FileName()) - lstrlen( end + 1 ) -1;
			char* name = (char*) memcpy( new char[length +1], pDirInfo->FileName(), length );
			name[length] = 0;
			tis.item.lParam = (LPARAM) (new CDirInfo(name));
			delete[] name;
		}
		else
		{
			// this is the item
			tis.item.state = state;
			tis.item.stateMask = ~0;
			tis.item.lParam = (LPARAM) pDirInfo;
		}
		hMatch = TreeView_InsertItem(hWTree, &tis);
		if( !end && state & TVIS_SELECTED) 
			TreeView_SelectItem(hWTree, hSelected = hMatch);
	}

	if( end )
		ConstructTreeItem( hMatch, state, end+1, pDirInfo);
	else
	{
		NotifyChildren(hMatch);
		NotifyParents(hMatch);
	}
}

void UpdateAppName()
{
	char fname[2*_MAX_FNAME + 40] = APPNAME"    [";
	char drive[_MAX_PATH], dir[_MAX_PATH];
	_splitpath( CrtCfg, drive, dir, fname+ strlen(APPNAME) +5, NULL);
	strcat( fname, "]");
	SetWindowText(hWApp, fname);
	strcat(drive, dir);
	SetCurrentDirectory( drive );
}

void RestoreFocus()
{
	switch(nFocusWnd)
	{
		case FOCUS_OUT:		 SetFocus(hWOut); break;
		case FOCUS_TREE:	 SetFocus(hWTree); break;
		case FOCUS_LISTVIEW: SetFocus(hWListView); break;
		case FOCUS_FILTER:	 SetFocus(hWFilter); break;
		//case FOCUS_LIST:
		default:			SetFocus(hWList); break;
	}
}

HTREEITEM RefreshTreeItemChildren(HTREEITEM hChild)
{
	TV_ITEM it = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hChild };
	TreeView_GetItem( hWTree, &it);
	const char* name = ((CDirInfo*)(it.lParam))->FileName();
	//test the folder existence
	if( name[strlen(name)-1] != ':')
	{
		WIN32_FIND_DATA fd;
		HANDLE hfind = FindFirstFile(name, &fd);
		if((hfind == INVALID_HANDLE_VALUE) ||
		   !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
		{
			HTREEITEM hParent = TreeView_GetParent( hWTree, hChild);
			TreeView_DeleteItem( hWTree, hChild);
			if(hParent) NotifyParents(hParent);
			hChild = NULL;
		}
		if(hfind != INVALID_HANDLE_VALUE) FindClose( hfind );
	}
	HTREEITEM hTgt, hPrev = NULL;
	if(hChild)
	{
		// the folder exists, we are verifying the existence of selected files
		CDirInfo* pDi = (CDirInfo*)it.lParam;
		char fName[_MAX_PATH+10];
		BOOL ViewChanged = FALSE;
		WIN32_FIND_DATA fd;
		for(int i = pDi->GetUpperBound(); i>=0; i--)
		{
			strcpy(fName, name);
			strcat(fName,"\\");
			strcat(fName, (*pDi)[i]->FileName());
			HANDLE hSearch = FindFirstFile( fName, &fd );
			if(hSearch == INVALID_HANDLE_VALUE || fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
			{
				pDi->DeleteAt(i);
				ViewChanged = TRUE;
			}
			if(hSearch != INVALID_HANDLE_VALUE) FindClose(hSearch);
		}
		if(ViewChanged)
			NotifyParents(hChild);
		if(ViewChanged && (it.state & TVIS_SELECTED ))
			DisplayView( hChild, (CDirInfo*)it.lParam);
		if(it.state & TVIS_SELECTED)
		{
			// add the eventually inserted files
			strcpy(fName, name);
			strcat(fName, "\\*.*");
			UINT  state = ((it.state & TVIS_STATEIMAGEMASK) > TI_STATE_NONE? LSTATE_LOCKED: LSTATE_UNCHECKED);
			HANDLE hSearch = FindFirstFile( fName, &fd );
			for(BOOL Found = (hSearch != INVALID_HANDLE_VALUE); Found; Found = FindNextFile( hSearch, &fd))
				if(!(fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM )) &&
					CnvEng.IsFileKnown( fd.cFileName))
				{	
					LV_FINDINFO fi = {LVFI_STRING, fd.cFileName };
					if( ListView_FindItem( hWListView, -1, &fi) < 0 )
					{
						LV_ITEM a = {LVIF_TEXT|LVIF_STATE|LVIF_PARAM,ListView_GetItemCount(hWListView),0,
							 state,0, fd.cFileName, 0,0, (LPARAM) hChild };
						ListView_InsertItem(  hWListView, &a);
					}
				}
			if(hSearch != INVALID_HANDLE_VALUE) FindClose(hSearch);
		}
		// test the childs existence
		for(hTgt = TreeView_GetChild(hWTree, hChild); hTgt; )
			if(RefreshTreeItemChildren(hTgt))
			{
				hPrev = hTgt;
				hTgt = TreeView_GetNextSibling(hWTree, hTgt);
			}
			else hTgt = (hPrev?TreeView_GetNextSibling(hWTree, hPrev):TreeView_GetChild(hWTree, hChild));
		if(it.state & (TVIS_EXPANDED|TVIS_EXPANDEDONCE))
			SetTreeItemChildren( &it );
	}
	return hChild;
}

void Refresh()
{
	// refresh the list
	{
		HANDLE hFile;
		int maxCnt = ListView_GetItemCount( hWList );
		for( int i = maxCnt-1 ; i>=0; i-- )
		{
			LV_ITEM it = {LVIF_PARAM, i, 0};
			ListView_GetItem( hWList, &it);
			hFile = CreateFile( (char*)it.lParam,0,0,NULL,OPEN_EXISTING,0,NULL); // open the file
			if(hFile == INVALID_HANDLE_VALUE)
			{
				delete[] (char*)it.lParam;
				ListView_DeleteItem( hWList, i);
			}
			else CloseHandle(hFile);
		}
		if(IsWindowVisible(hWList))
			PostMessage( hWList, WM_USER+10, ListView_GetNextItem( hWList, -1, LVNI_FOCUSED ), 0);
	}
	// refresh the tree
	for(HTREEITEM hChild = TreeView_GetRoot(hWTree); hChild; hChild = TreeView_GetNextSibling(hWTree, hChild))
			RefreshTreeItemChildren(hChild);
}

void CleanTreeItemChildren(HTREEITEM hChild)
{
	TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hChild, 0, ~0 };
	TreeView_GetItem( hWTree, &ti);
	ti.state &= ~(TVIS_STATEIMAGEMASK|TVIS_OVERLAYMASK);
	ti.state |= TI_STATE_NONE;
	((CDirInfo*)ti.lParam)->DeleteAll();
	TreeView_SetItem( hWTree, &ti);
	if(ti.state & TVIS_SELECTED)
		DisplayView( hChild, (CDirInfo*)ti.lParam);
	HTREEITEM hNext;
	if(!(ti.state&TVIS_EXPANDED))
		while( hNext = TreeView_GetChild( hWTree, hChild)) TreeView_DeleteItem( hWTree, hNext);
	else
		for( hNext = TreeView_GetChild( hWTree, hChild); hNext; hNext = TreeView_GetNextSibling( hWTree, hNext))
			CleanTreeItemChildren(hNext);
}

void CleanSelection()
{
	for(HTREEITEM hChild = TreeView_GetRoot(hWTree); hChild; hChild = TreeView_GetNextSibling(hWTree, hChild))
		CleanTreeItemChildren(hChild);
}
