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

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

WNDPROC TreeIWP;
WNDPROC ViewIWP;

void SetTreeItemChildren(TV_ITEM* pItem)
{
	char fName[_MAX_PATH+10] = {0};
	CSTPArray<CDirInfo> arCrtItems;
	arCrtItems.SetGranularity(10);
	for(HTREEITEM hItem = TreeView_GetChild(hWTree, pItem->hItem); hItem; hItem = TreeView_GetNextSibling( hWTree, hItem))
	{
		TV_ITEM ti = { TVIF_HANDLE|TVIF_PARAM, hItem  };
		TreeView_GetItem( hWTree, &ti);
		BOOL NA;
		arCrtItems.SAdd((CDirInfo*)ti.lParam, NA);
	}
	strcpy(fName, ((CDirInfo*)pItem->lParam)->FileName());
	strcat(fName, "\\*.*");
	WIN32_FIND_DATA fd;
	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 != '.')
		{
			strcpy(fName, ((CDirInfo*)pItem->lParam)->FileName());
			strcat(fName, "\\");
			strcat(fName, fd.cFileName );
			CDirInfo* pDi = new CDirInfo(fName);
			if(arCrtItems.Index(pDi)<0)
			{
				TV_INSERTSTRUCT tis =
				{	pItem->hItem, TVI_SORT ,
					{	TVIF_CHILDREN|TVIF_SELECTEDIMAGE|TVIF_IMAGE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT,
						NULL,((pItem->state & TVIS_STATEIMAGEMASK)>TI_STATE_FILES)? TI_STATE_LOCKED:TI_STATE_NONE,
						TVIS_STATEIMAGEMASK, fd.cFileName, 0, 0, 1, I_CHILDRENCALLBACK, (LPARAM)pDi
					}
				};
				TreeView_InsertItem(hWTree, &tis);
			}
			else 
				delete pDi;
		}
	if( hSearch != INVALID_HANDLE_VALUE ) FindClose( hSearch );
}

LRESULT CALLBACK TreeWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
{
	switch(msg)
	{
	case WM_COMMAND:
		switch(LOWORD(wp))
		{
		case ID_SELECT_TOGGLE:
			ToggleTreeItemState(TreeView_GetSelection( hwnd ), FALSE);
			break;
		case ID_RECURSE_TOGGLE:
			ToggleTreeItemState(TreeView_GetSelection( hwnd ), TRUE);
			break;
		}
		break;
	case WM_LBUTTONDOWN:
		{
			TV_HITTESTINFO ht = {{ LOWORD(lp), HIWORD(lp)}};
			TreeView_HitTest( hwnd, &ht );
			if(ht.hItem  && ht.flags == TVHT_ONITEMSTATEICON )
			{
				HTREEITEM hItem = ht.hItem;
				ht.pt.x -= 11;
				TreeView_HitTest( hwnd, &ht );
				ToggleTreeItemState( hItem, ht.flags == TVHT_ONITEMSTATEICON);
			}
		}
		break;
	case WM_SETFOCUS: nFocusWnd = FOCUS_TREE; break;
	case WM_DESTROY:
		ClearTree();
		break;
	}
	return CallWindowProc( (int(CALLBACK*)())TreeIWP, hwnd, msg, wp, lp);
}

void NotifyParents( HTREEITEM hiItem)
{
	if(!hiItem) return;
	int Set = 0;
	
	TV_ITEM tip = { TVIF_HANDLE|TVIF_PARAM, hiItem };
	TreeView_GetItem( hWTree, &tip);
	if( ((CDirInfo*)tip.lParam)->GetSize() )
		Set = 1;
	else 
		for(HTREEITEM hItem = TreeView_GetChild(hWTree, hiItem); hItem; hItem = TreeView_GetNextSibling( hWTree, hItem))
		{
			TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hItem, 0, ~0 };
			TreeView_GetItem( hWTree, &ti);
			if((ti.state & TVIS_OVERLAYMASK) ||
			   (ti.state & TVIS_STATEIMAGEMASK) == TI_STATE_FILES ||
			   (ti.state & TVIS_STATEIMAGEMASK) == TI_STATE_RECURSE)
			{
				Set = 1;
				break;
			}
		}
	TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE, hiItem, INDEXTOOVERLAYMASK(Set), TVIS_OVERLAYMASK };
	TreeView_SetItem( hWTree, &ti);	
	NotifyParents(TreeView_GetParent(hWTree, hiItem));
}

void NotifyChildren( HTREEITEM hiItem, HTREEITEM hParent )
{
	if(hParent)
	{
		TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE, hParent, 0, TVIS_STATEIMAGEMASK|TVIS_OVERLAYMASK };
		TreeView_GetItem( hWTree, &ti);
		ti.state &= TVIS_STATEIMAGEMASK;
		if(ti.state > TI_STATE_FILES )
		{
			ti.state = TI_STATE_LOCKED;
			TV_ITEM tip = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hiItem , 0 ,~0 };
			TreeView_GetItem( hWTree, &tip);
			((CDirInfo*)tip.lParam)->DeleteAll();

		}
		else
			ti.state = TI_STATE_NONE;
		ti.hItem = hiItem;
		TreeView_SetItem( hWTree, &ti);
	}
	for(HTREEITEM hItem = TreeView_GetChild(hWTree, hiItem); hItem; hItem = TreeView_GetNextSibling( hWTree, hItem))
		NotifyChildren( hItem, hiItem);
}

void UpdateView( UINT state, CDirInfo* pDi)
{
	UINT st = (state == TI_STATE_NONE? LSTATE_UNCHECKED:LSTATE_LOCKED);
	LV_ITEM iIt = { LVIF_STATE, 0, 0, st, LVIS_STATEIMAGEMASK };
	for( iIt.iItem = ListView_GetItemCount(hWListView); iIt.iItem>=0; iIt.iItem-- )
		ListView_SetItem(hWListView, &iIt);
}

void ToggleTreeItemState(HTREEITEM hItem, BOOL RecurseState )
{
	if(!hItem) return;
	BOOL RecurseStateChanged = FALSE;
	TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hItem, 0, TVIS_STATEIMAGEMASK };
	TreeView_GetItem( hWTree, &ti);
	BOOL Selected = ti.state & TVIS_SELECTED;
	ti.state &= TVIS_STATEIMAGEMASK;
	if( ( ti.state  == TI_STATE_LOCKED) || 
		(RecurseState && (ti.state  == TI_STATE_NONE))) return;
	switch( ti.state  )
	{
	case TI_STATE_NONE:	ti.state = TI_STATE_FILES;	break;
	case TI_STATE_FILES:
		ti.state = ((RecurseStateChanged = RecurseState)? TI_STATE_RECURSE: TI_STATE_NONE);
		break;
	case TI_STATE_RECURSE:
		RecurseStateChanged = TRUE;
		ti.state = (RecurseState? TI_STATE_FILES: TI_STATE_NONE);
		break;
	}
	if(ti.state > TI_STATE_NONE) ((CDirInfo*)ti.lParam)->DeleteAll();
	//if(RecurseStateChanged) ti.stateMask |= TVIS_OVERLAYMASK;
	TreeView_SetItem( hWTree, &ti);
	if(Selected) UpdateView( ti.state & TVIS_STATEIMAGEMASK, (CDirInfo*)ti.lParam);
	if(RecurseStateChanged) NotifyChildren(hItem);
//	NotifyParents(TreeView_GetParent(hWTree, hItem));
	NotifyParents(hItem );
}


void ClearTree()
{
	TreeView_DeleteAllItems ( hWTree);
}

void InitTree()
{
	ClearTree();
	// create the drive items
	char buffer[1001], DriveName[_MAX_PATH + 10];
	CDirInfo * pInfo;
	if(GetLogicalDriveStrings(1000, buffer))
		for(char* name = buffer; *name; name += strlen(name) + 2)
			if(GetDriveType(name) == DRIVE_FIXED)
			{
				GetVolumeInformation( name, DriveName, _MAX_PATH, NULL, NULL, NULL, NULL, NULL ); 
				name[strlen(name)-1] = 0;
				if(*DriveName) CharLower(DriveName+1); 
				strcat( DriveName, " (");
				strcat( DriveName, name);
				strcat( DriveName, ")");
				pInfo = new CDirInfo(name);
				TV_INSERTSTRUCT tis =
				{	NULL , TVI_LAST ,
					{	TVIF_CHILDREN|TVIF_SELECTEDIMAGE|TVIF_IMAGE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT,
						NULL,TI_STATE_NONE, TVIS_STATEIMAGEMASK,
						DriveName, 0, 3, 3, I_CHILDRENCALLBACK, (LPARAM)pInfo
					}
				};
				TreeView_InsertItem(hWTree, &tis);
			}
			else name[strlen(name)-1] = 0;
}


//------------------------------------ View -----------------------------------

void DisplayView( HTREEITEM hItem, CDirInfo* dirInfo )
{
	ListView_DeleteAllItems(hWListView);
	char fName[_MAX_PATH+10];
	strcpy(fName, dirInfo->FileName());
	strcat(fName, "\\*.*");

	TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE, hItem, 0, TVIS_STATEIMAGEMASK };
	TreeView_GetItem( hWTree, &ti);
	ti.state &= TVIS_STATEIMAGEMASK;

	UINT  state = (ti.state > TI_STATE_NONE? LSTATE_LOCKED: LSTATE_UNCHECKED);
	
	WIN32_FIND_DATA fd;
	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_ITEM a = {LVIF_TEXT|LVIF_STATE|LVIF_PARAM,ListView_GetItemCount(hWListView),0,
						 state,0, fd.cFileName, 0,0, (LPARAM) hItem };
			ListView_InsertItem(  hWListView, &a);
		}
	if( hSearch != INVALID_HANDLE_VALUE )
	{
		FindClose( hSearch );
		LV_FINDINFO fi = {LVFI_STRING};
		LV_ITEM tItem = { LVIF_STATE, 0, 0, LSTATE_CHECKED|LVIS_SELECTED, ~0 };
		for(int i = dirInfo->GetUpperBound(); i>=0; i--)
		{
			fi.psz = (*dirInfo)[i]->FileName();
			if((tItem.iItem = ListView_FindItem( hWListView, -1, &fi)) < 0)
				dirInfo->DeleteAt(i);
			else
				ListView_SetItem(hWListView, &tItem);
		}
		if(ListView_GetItemCount(hWListView))
		{
			int idx = ListView_GetNextItem( hWListView, -1, LVNI_SELECTED );
			LV_ITEM iItem = { LVIF_STATE, max(idx, 0), 0, LVIS_FOCUSED, LVIS_FOCUSED };
			ListView_SetItem(hWListView, &iItem);
		}
	}
	else dirInfo->DeleteAll();
}

void SetFile( LV_ITEM* ptestItem, CDirInfo* pDi )
{
	ListView_SetItem( hWListView, ptestItem);

	char name[_MAX_FNAME];
	LV_ITEM testItem = { LVIF_TEXT, ptestItem->iItem, 0, 0, 0, name, _MAX_FNAME};
	ListView_GetItem( hWListView, &testItem);
	CFileName* pName = new CFileName(name);
	
	if((ptestItem->state & LVIS_STATEIMAGEMASK) == LSTATE_CHECKED)
	{
		BOOL NA;
		pDi->SAdd( pName, NA, FALSE);
		if( NA ) delete pName;
	}
	else
	{
		int idx = pDi->Index( pName );
		if( idx>=0 ) pDi-> DeleteAt( idx );
		delete pName;
	}
}

void SetViewItemState( int idx )
{
	if( idx<0) return;
	LV_ITEM testItem = { LVIF_STATE| LVIF_PARAM, idx, 0, 0, ~LVIS_FOCUSED };
	ListView_GetItem( hWListView, &testItem);
	if( testItem.state & LSTATE_LOCKED) return;
	
	testItem.state ^= (LSTATE_UNCHECKED|LSTATE_CHECKED);
	
	HTREEITEM hti = (HTREEITEM)testItem.lParam;
	TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hti, 0, ~0 };
	TreeView_GetItem( hWTree, &ti);
	CDirInfo* pDi = (CDirInfo*) ti.lParam;

	if(testItem.state & LVIS_SELECTED)
	{
		int maxCnt = ListView_GetSelectedCount( hWListView );
		int i = 0;
		for( testItem.iItem = -1; i<maxCnt; i++ )
		{
			testItem.iItem = ListView_GetNextItem( hWListView, testItem.iItem, LVNI_SELECTED );
			SetFile( &testItem, pDi );			
		}
	}
	else SetFile( &testItem, pDi );
	NotifyParents(hti);
}

LRESULT CALLBACK ViewWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
{
	switch(msg)
	{
	case WM_SETFOCUS: nFocusWnd = FOCUS_LISTVIEW; break;
	case WM_COMMAND:
		switch(LOWORD(wp))
		{
		case ID_SELECT_TOGGLE:
			SetViewItemState( ListView_GetNextItem( hwnd, -1, LVNI_FOCUSED ) );
			return 0;
		case ID_SELECT_ALL:
			{
				LV_ITEM testItem = { LVIF_STATE, 0, 0, LVIS_SELECTED, LVIS_SELECTED };
				for(testItem.iItem = ListView_GetItemCount( hwnd )-1; testItem.iItem>=0; testItem.iItem--)
					ListView_SetItem( hwnd, &testItem);
				return 0;
			}
		}
		break;
	case WM_LBUTTONDOWN:
		{
			LV_HITTESTINFO ht = {{ LOWORD(lp), HIWORD(lp)}};
			ListView_HitTest( hwnd, &ht);
			if(ht.iItem >=0 && ht.flags == LVHT_ONITEMSTATEICON)
			{
				SetFocus( hwnd );
				SetViewItemState( ht.iItem );
				return 0;
			}
		}
		break;
	}
	return CallWindowProc( (int(CALLBACK*)())ViewIWP, hwnd, msg, wp, lp);
}

