#include "animator.h"


static char szAllPurpose [MAX_FILE_SIZE];

#ifdef __cplusplus
extern "C" {
#endif

BOOL NEAR PASCAL Child_OnCreate (HWND, LPCREATESTRUCT);
VOID NEAR PASCAL Child_OnSize (HWND, UINT, short, short);            
VOID NEAR PASCAL Child_OnSetFocus (HWND, HWND);
VOID NEAR PASCAL Child_OnMDIActivate (HWND, BOOL, HWND, HWND);
VOID NEAR PASCAL Child_OnCommand (HWND, UINT, HWND, UINT); 
VOID NEAR PASCAL Child_OnDestroy (HWND);
VOID NEAR PASCAL Child_OnClose(HWND);
BOOL NEAR PASCAL Child_OnQueryEndSession (HWND);
LONG NEAR PASCAL Child_OnDrawItem (HWND, const DRAWITEMSTRUCT FAR *);
LONG NEAR PASCAL Child_OnMeasureItem (HWND, LPMEASUREITEMSTRUCT);
VOID NEAR PASCAL Child_OnDropFiles (HWND, HDROP);

short NEAR PASCAL FigureOutWhereToInsert (HWND, LPPOINT);
BOOL NEAR PASCAL InsertIconImage (short, HWND, short, LPSTR);
BOOL NEAR PASCAL AddIconImage (short, HWND, LPSTR);
VOID NEAR PASCAL HandleSelectionState (const DRAWITEMSTRUCT FAR *);
VOID NEAR PASCAL HandleFocusState (const DRAWITEMSTRUCT FAR *);

#ifdef __cplusplus
}
#endif


//////////////////////////////////////////////////////////////////////////
//  ChildProc - Handles all of the messages for the Child animation
//  window which shows all of the icons and their file names.
//  The window procedure makes use of Message Crackers, a new
//  idea introduced in the header file WINDOWSX.H.  Using this file
//  is a good way to preserve your code for changes in upcoming versions
//  of Windows, where types and parameters change.
//////////////////////////////////////////////////////////////////////////

LRESULT _export CALLBACK ChildProc(WNDPROC_PARAMS)
{
	switch (uMsg) 
	{
		HANDLE_MSG (hWnd, WM_CREATE, Child_OnCreate);
		HANDLE_MSG (hWnd, WM_SIZE, Child_OnSize);
		HANDLE_MSG (hWnd, WM_SETFOCUS, Child_OnSetFocus);
		HANDLE_MSG (hWnd, WM_MDIACTIVATE, Child_OnMDIActivate);
		HANDLE_MSG (hWnd, WM_COMMAND, Child_OnCommand);
		HANDLE_MSG (hWnd, WM_DRAWITEM, Child_OnDrawItem);
		HANDLE_MSG (hWnd, WM_MEASUREITEM, Child_OnMeasureItem);
		HANDLE_MSG (hWnd, WM_DESTROY, Child_OnDestroy);
		HANDLE_MSG (hWnd, WM_CLOSE, Child_OnClose);
		HANDLE_MSG (hWnd, WM_QUERYENDSESSION, Child_OnQueryEndSession);
		HANDLE_MSG (hWnd, WM_DROPFILES, Child_OnDropFiles);
	}

	return DefMDIChildProc(hWnd, uMsg, wParam, lParam);
}
			


//////////////////////////////////////////////////////////////////////////
//  Child_OnCreate() - Creates the listbox and initializes the data
//  structure and window words for this window.
//////////////////////////////////////////////////////////////////////////

BOOL NEAR PASCAL Child_OnCreate (HWND hwnd, LPCREATESTRUCT lpCS)
{
	short i,j;
	HWND  hwndList = CreateWindow ("LISTBOX","",
									 WS_CHILD|WS_VISIBLE|
									 LBS_NOINTEGRALHEIGHT|
									 LBS_OWNERDRAWFIXED|LBS_HASSTRINGS|
									 LBS_NOTIFY|WS_VSCROLL|WS_BORDER,
									 0,0,0,0,hwnd,
									 IDD_LISTBOX, _hInst, NULL);
	if (!IsWindow (hwndList)) 
	{
		return FALSE;
	}

	SendMessage (hwndList, WM_SETFONT, GetStockFont(ANSI_VAR_FONT),0L);
	DragAcceptFiles (hwnd, TRUE);

	i = (short)((LPMDICREATESTRUCT)lpCS->lpCreateParams)->lParam;
	
	SET_HWNDLIST (hwnd, hwndList);   // next three are window words.   
	SET_WINDOWNUM (hwnd, i);
	SET_ISDIRTY (hwnd, FALSE);   
	SET_EXELOADED (i, FALSE);        // rest are in data structure.
	SET_AUTOANIMATE (i, FALSE);
	SZFILENAME(i)[0] = '\0';
	SZEXELINK(i)[0] = '\0';
	SET_HWNDANIM(i, hwnd);  
	SET_INDEX (i, 0);
	SET_NUMICONS (i, 0);
	SET_TIMEINT (i, 100);   
	

	for (j=0; j<(short)MAXICONS ; j++)
	{
		HICONS(i)[j] = NULL;
	}

	UPDATE_STATBAR;

	return TRUE;
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnSize() - Moves listbox accordingly, and sets column width.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL Child_OnSize (HWND hwnd, UINT wSizeType, short cx, short cy)            
{
	
	if (LB_ERR==(WORD)SendMessage(HWNDLIST(hwnd),LB_SETCOLUMNWIDTH,cx-2,0L))
	{
		MESSAGE (IDS_ListBoxSize);
	}

	MoveWindow(HWNDLIST(hwnd), 0, 0, cx, cy, TRUE);

	DefMDIChildProc(hwnd, WM_SIZE, (WPARAM)wSizeType, MAKELPARAM(cx,cy));
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnDropFiles() - Handles the Drag-n-Drop protocol that File
//  Manager in Windows 3.1 makes available to us.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL Child_OnDropFiles (HWND hwnd, HDROP hDrop)
{
	short  sChild   = WINDOWNUM (hwnd);
	HWND   hwndList = HWNDLIST (hwnd);
	short  sNumDrop = (short)DragQueryFile (hDrop,(WORD)-1,NULL,0);

	if (ISANIMATING(sChild))
	{
		MESSAGE (IDS_NoDropOnWhileAnimate);
		return;
	}

	if (sNumDrop > 0)
	{
		short i;
		short sInsert;
		POINT pt;
		char   szDropFile [MAX_FILE_SIZE];    

		// get the point of up-click.  If up-click over an existing
		// listbox entry, insert the files there.  Else, append.

		DragQueryPoint (hDrop, (LPPOINT)&pt);

		sInsert = FigureOutWhereToInsert (hwndList, (LPPOINT)&pt);
		
		for (i=0 ; i<(short)sNumDrop ; i++)
		{
			DragQueryFile (hDrop, i, (LPSTR)szDropFile, MAX_FILE_SIZE);

			if (sInsert < 0)
			{
				AddIconImage (sChild, hwndList, (LPSTR)szDropFile);
			}
			else
			{
				InsertIconImage (sChild, hwndList, sInsert, (LPSTR)szDropFile);
			}    
						
			SET_ISDIRTY (hwnd, TRUE);
		}
	}
}

		

//////////////////////////////////////////////////////////////////////////
//  Child_OnSetFocus() - Sets focus to the listbox.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL Child_OnSetFocus (HWND hwnd, HWND hwndPrev)
{
	SetFocus(HWNDLIST(hwnd));
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnMDIActivate() - Sets up the menu accordingly, and updates
//  the status bar, since the new active child may have a totally
//  different state.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL Child_OnMDIActivate (HWND hwnd, BOOL bActive, 
	HWND hwndActive, HWND hwndDeActive)
{
	SendMessage(_hwndClient, WM_MDISETMENU, (WPARAM)0,
		bActive ? MAKELPARAM(_hmenuChild, _hmenuChildWindow)
				: MAKELPARAM(_hmenuMain, _hmenuMainWindow));

	DrawMenuBar(_hwndFrame);

	UPDATE_STATBAR;

	if (bActive)
	{
		SetFocus (hwnd);
	}
	
	DefMDIChildProc(hwnd, WM_MDIACTIVATE, (WPARAM)bActive, 
		MAKELPARAM(hwndActive,hwndDeActive));
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnCommand() - Handles all of the command processing specific
//  to an animation.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL Child_OnCommand (HWND hwnd, UINT uMsg, 
		HWND hChild, UINT uExtra) 
{
	short sChild = WINDOWNUM(hwnd);

	switch (uMsg) 
	{
		case IDM_SETTINGS:
		{
			if (DialogBox (_hInst, SETTINGSDLG, hwnd, _lpfnSettings))
			{
				UPDATE_STATBAR;
				SET_ISDIRTY (hwnd, TRUE);

				if (AUTOANIMATE (sChild))
				{
					PostMessage (hwnd, WM_COMMAND, IDM_GO, 0L);
				}
			}
			break;
		}

		case IDM_GO:
		{            
			if (!NUMICONS(sChild))
			{
				MESSAGE (IDS_NoIcons);
				break;
			}

			if (!TIMEINT(sChild) || !SZEXELINK(sChild)[0])
			{
				SendMessage (_hwndFrame, WM_COMMAND, IDM_SETTINGS, 0L);
			}

			if (SZEXELINK(sChild) && TIMEINT(sChild))
			{
				// Free old icon handles before creating new ones.
				// 
				DeletePreviousHandles (hwnd);
				SetupIconHandles (hwnd);
				SET_INDEX (sChild, 0);
				SET_ISANIMATING (sChild, TRUE);
			}

			break;
		}

		case IDM_STOP:
		{
			SET_ISANIMATING (sChild, FALSE);

			if (IsWindow (HWNDTARGET(sChild)))
			{
				SetClassWord (HWNDTARGET(sChild), GCW_HICON, 
					HPREVICON(sChild));

				InvalidateAll (HWNDTARGET(sChild),HPREVICON(sChild));
			}

			break ;
		}       
		 
		case IDM_SAVE:
		case IDM_SAVEAS:
		{
			SaveIconsToFile (hwnd, uMsg);
			SET_ISDIRTY (hwnd, FALSE);
			break;
		}

		case IDM_ADDICON:
		case IDM_INSERTICON:
		{
			char szAddFile [MAX_FILE_SIZE];

			getcwd (szAddFile, MAX_FILE_SIZE);

			if (ShowCommonDialog(hwnd,_lpszIconFilter,
				(LPSTR)szAddFile, (LPSTR)"Add or Insert Icon...\0",
				(LPSTR)"*.ICO", FALSE))
			{
				HWND hwndList = HWNDLIST (hwnd);
				
				if (!IsWindow (hwndList)) 
				{
					break;
				}

				if (uMsg == IDM_INSERTICON)
				{
					short sSel = (short)ListBox_GetCurSel (hwndList);

					if (sSel == LB_ERR)
					{
						MESSAGE (IDS_PleaseSelect);
					}
					else
					{
						InsertIconImage (sChild, hwndList, sSel, 
												(LPSTR)szAddFile);
					}
				}
				else
				{
					AddIconImage (sChild, hwndList, (LPSTR)szAddFile);
				}
				
				SET_ISDIRTY (hwnd, TRUE);
			}

			break;
		}

		case IDM_DELETEICON:
		{
			DeleteCurSel (hwnd);           
			SET_ISDIRTY (hwnd, TRUE);
			break;
		}
		default:
			return;
	}

	UPDATE_STATBAR;
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnDestroy() - frees all memory handles used for icons, and
//  resets the animStruct for this window to all zeros.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL Child_OnDestroy (HWND hWnd)
{   
	int i;
	short sChild = WINDOWNUM(hWnd);
 
	if (ISANIMATING(sChild)) 
	{
		SET_ISANIMATING (sChild, FALSE);
		
		if (IsWindow (HWNDTARGET(sChild)))
		{
			SetClassWord (HWNDTARGET(sChild), GCW_HICON, HPREVICON(sChild));
			InvalidateAll (HWNDTARGET(sChild),HPREVICON(sChild));
		}
	}

	DestroyWindow(HWNDLIST(hWnd));

	for (i=0; i<(short)NUMICONS(sChild) ; i++)
	{
		GlobalFree (HICONS(sChild)[i]);
		HICONS(sChild)[i] = NULL;  // reset the structure to all nulls
	}

	_fmemset ((LPANIMSTRUCT)&_animStruct[sChild], 0x00, sizeof(ANIMSTRUCT));            

	_lPageFlags &= 0xFFFFFFFFL ^ (1 << sChild);

	DefMDIChildProc(hWnd, WM_DESTROY, (WPARAM)0, (LPARAM)0L);
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnClose() -  Checks with QueryEndSession to see if it is ok
//  with the user to close, then closes, by calling Destroy.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL
Child_OnClose (HWND hwnd)
{
	if (SendMessage(hwnd, WM_QUERYENDSESSION, 0, 0L))
	{
		MDI_Destroy (_hwndClient, hwnd);  
	}
}



//////////////////////////////////////////////////////////////////////////
//  Asks whether or not to save changes, if changes need to be saved.
//  If everything is cool, or the user presses NO, we say its OK to 
//  close, as FrameProc()'s BroadcastProc() is the one calling this.
//  This child also calls it when a WM_CLOSE message comes its way.
//////////////////////////////////////////////////////////////////////////

BOOL NEAR PASCAL Child_OnQueryEndSession (HWND hWnd)
{
	char szBigString [192];
	char szWindowText [MAX_FILE_SIZE];
	char szFmtString [36];

	if (ISDIRTY(hWnd))
	{
		short sChild = WINDOWNUM(hWnd);
		UINT uReply;

		GetWindowText (hWnd, (LPSTR)szWindowText, MAX_FILE_SIZE);

		LoadStr(IDS_QueryEnd, szFmtString);    
		wsprintf ((LPSTR)szBigString,
				  (LPSTR)szFmtString,
				  (LPSTR)szWindowText);

		uReply = MessageBox (hWnd, szBigString, _szAppName, MB_YESNOCANCEL);

		if (uReply == IDYES)
		{
			SendMessage (hWnd, WM_COMMAND, (WPARAM)(SZFILENAME(sChild)[0] ? 
				IDM_SAVE : IDM_SAVEAS), (LPARAM)0L);
			return TRUE;
		}    
		else if (uReply == IDCANCEL)
		{
			return FALSE;
		}
	}

	return TRUE;
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnDrawItem() - Does the meat of the drawing for each item
//  in the listbox, and calls the HandleFocusState() and HandleSelection()
//  functions as necessary.
//////////////////////////////////////////////////////////////////////////

LONG NEAR PASCAL Child_OnDrawItem (HWND hwnd, const DRAWITEMSTRUCT FAR * lp)
{
	short sChild = WINDOWNUM(hwnd);

	if ((lp->CtlType == ODT_LISTBOX) && (lp->CtlID == IDD_LISTBOX))
	{
		if (NUMICONS(sChild) == 0) 
		{
			return 0L;
		}

		if (lp->itemAction & ODA_DRAWENTIRE)
		{
			DrawIcon (lp->hDC,                                        
					 (lp->rcItem.right - lp->rcItem.left - ICON_DX)/2, 
					  lp->rcItem.top+(PADDING/2),   
					  HICONS(sChild)[lp->itemID]);                     

			HandleSelectionState(lp);
		}

		// If a list box item was just selected or unselected,
		// call function (which could check if ODS_SELECTED bit is set)
		// and draw item in selected or unselected state.

		if (lp->itemAction & ODA_SELECT)
		{
			HandleSelectionState(lp);
		}

		return 1L;
	}
	return 0L;
}



//////////////////////////////////////////////////////////////////////////
//  Child_OnMeasureItem() - tells Windows how to size the listbox.
//////////////////////////////////////////////////////////////////////////

LONG NEAR PASCAL Child_OnMeasureItem (HWND hwnd, LPMEASUREITEMSTRUCT lpmis)
{   
	lpmis->itemHeight = GetANSITextHeight()+ICON_DY+PADDING;
	return 0L;
}




//////////////////////////////////////////////////////////////////////////
//  HandleSelectionState() - Handles the drawing of the listbox with
//  current selection, and draws an un-selected one.  The item being
//  selected and the one being un-selected are dealt with here.
//////////////////////////////////////////////////////////////////////////

VOID NEAR PASCAL HandleSelectionState(const DRAWITEMSTRUCT FAR * lp)
{   
	BOOL fOn = lp->itemState & ODS_SELECTED;
	short sIndent;
	short sDownSpace;
	UINT oldBkMode;
	COLORREF oldTextColor;
	DWORD dwExtent;
	RECT rc;
	HBRUSH hBrush;

	CopyRect (&rc, &lp->rcItem);

	ListBox_GetText (lp->hwndItem, lp->itemID, (LPSTR)szAllPurpose);
	dwExtent = GetTextExtent (lp->hDC, (LPSTR)szAllPurpose, 
		lstrlen((LPSTR)szAllPurpose));

	sIndent = ((short)(rc.right-rc.left-LOWORD(dwExtent)) / 2);
	sDownSpace = (short)(rc.bottom-rc.top-HIWORD(dwExtent));

	/* Setup formatting rectangle */
	rc.left  += sIndent;
	rc.right -= sIndent;
	rc.top   += sDownSpace;

	/* Use the title bar colors for highlighting, so we
	   set the text color and bk color to those colors,
	   draw the text, then reset the colors back to original
	   values */

	oldBkMode = SetBkMode (lp->hDC, TRANSPARENT);
		
	oldTextColor = SetTextColor (lp->hDC, 
		Color(fOn ? COLOR_CAPTIONTEXT : COLOR_WINDOWTEXT));

	hBrush = CreateSolidBrush(Color (fOn ? COLOR_ACTIVECAPTION :
										   COLOR_WINDOW));
	FillRect (lp->hDC, &rc, hBrush);
	DeleteBrush (hBrush);

	ExtTextOut (lp->hDC, rc.left, rc.top,
		ETO_CLIPPED,
		(LPRECT)&rc,
		(LPSTR)szAllPurpose,
		lstrlen ((LPSTR)szAllPurpose),
		NULL);

	SetBkMode (lp->hDC, oldBkMode);
	SetTextColor (lp->hDC, oldTextColor);

}
	





//////////////////////////////////////////////////////////////////////////
// FigureOutWhereToInsert() - Given the listbox and a point somewhere
// in the listbox when the user up-clicked the LBUTTON, we find out
// where, then return an index if they up-clicked over an item.  If not,
// we return telling them to append to end of list.
//////////////////////////////////////////////////////////////////////////

short NEAR PASCAL FigureOutWhereToInsert (HWND hwndList, LPPOINT lppt)
{
	short  sCount = (short)ListBox_GetCount (hwndList);
	short  i;
	RECT   rc;

	for (i=0 ; i<sCount ; i++)
	{
		ListBox_GetItemRect (hwndList, i, (LPRECT)&rc);

		if (!PtInRect ((LPRECT)&rc, *lppt)) continue;
		return i;
	}

	return (short)(-1);
}



//////////////////////////////////////////////////////////////////////////
// InsertIconImage() - inserts an icon into a specific position in the
// list.
//////////////////////////////////////////////////////////////////////////

BOOL NEAR PASCAL InsertIconImage (short sChild, HWND hwndList,
	short sInsert, LPSTR lpszFile)
{
	short sNumIcons = NUMICONS (sChild);
	HICON hNewIcon;
	short i;

	if (sNumIcons >= MAXICONS)
	{
		MESSAGE (IDS_TooManyIcons);
		return FALSE;
	}

	hNewIcon = ExtractIcon (_hInst, lpszFile, 0);

	if (hNewIcon == NULL) return FALSE;

	if (sNumIcons == 0)
	{
		HICONS(sChild)[sNumIcons] = hNewIcon;
	}
	else
	{
		for (i=sNumIcons ; i>=0 ; i--)
		{   
			HICONS(sChild)[i] = HICONS(sChild)[i-1];
			if ( (i-1) == sInsert)
			{
				HICONS(sChild)[i-1] = hNewIcon;
				break;
			}
		}
	}

	sNumIcons++;

	SET_NUMICONS (sChild, sNumIcons);

	if (LB_ERR == ListBox_InsertString(hwndList,sInsert,lpszFile))
	{
		MESSAGE(IDS_AddFileError);
		return FALSE;
	}
	
	ListBox_SetCurSel (hwndList, sInsert);

	return TRUE;
}   



//////////////////////////////////////////////////////////////////////////
//  AddIconImage() - Adds an icon to the end of the list.
//////////////////////////////////////////////////////////////////////////

BOOL NEAR PASCAL AddIconImage (short sChild, HWND hwndList, 
	LPSTR lpszFile)
{
	HICON hNewIcon;
	short sNumIcons = NUMICONS(sChild);

	if (sNumIcons >= MAXICONS)
	{
		MESSAGE (IDS_TooManyIcons);
		return FALSE;
	}

	hNewIcon = ExtractIcon (_hInst, lpszFile, 0);

	if (hNewIcon == NULL) return FALSE;

	HICONS(sChild)[sNumIcons] = hNewIcon;
	sNumIcons++;
	SET_NUMICONS (sChild, sNumIcons);
	
	if (LB_ERR == ListBox_AddString(hwndList,lpszFile))
	{
		MESSAGE(IDS_AddFileError);
		return FALSE;
	}
	
	ListBox_SetCurSel (hwndList, ListBox_GetCount(hwndList)-1);

	return TRUE;
}

