//	************************************************************
//
//	System-Click Notification Icon
//	Win95 32-bit Shell Extension
//		James P. Ketrenos
//
//	************************************************************
#include <afx.h>
#include <shlobj.h>	//	SHAddToRecentDocs()
//	NOTE:	I had to comment out line 139 in OAIDL.H in order to get it to compile with shlobj.h.
//			The line commented out redefined BSTR

#include "Resource.h"
#include "Global.h"
#include "IconWnd.h"



//	************************************************************
//	CIconWnd Class Implementation
//
//	Purpose:
//	*	Respond to Notification Button actions.
//	*	Respond to WM_WININICHANGE messages to keep tool-tip 
//		up to date.
//	*	Keep the system INI and registry up to date with all
//		the latest values as we change them.
//	************************************************************

BEGIN_MESSAGE_MAP(CIconWnd, CFrameWnd)
	ON_COMMAND(IDC_FULLDRAG, OnFullDrag)
	ON_COMMAND(IDC_ANIMATED, OnAnimated)
	ON_COMMAND(IDC_CLRDOCS, OnClearDocs)
	ON_COMMAND(IDC_ABOUT, OnAbout)
	ON_COMMAND(IDC_APPHELP, OnHelp)
	ON_COMMAND(IDCLOSE, OnClose)
	ON_MESSAGE(SC_WM_NOTIFYICON, OnNotifyIcon)
	ON_WM_DESTROY()
	ON_WM_TIMER()
	ON_WM_WININICHANGE()
END_MESSAGE_MAP()



//	************************************************************
//	Message-Mapped Functions
//	************************************************************



//	*******************************
//	CIconWnd::OnWinIniChange()
//
//	Purpose:
//	*	Respond to the system WM_WININICHANGE message
//	*	If the [Windows] section has been altered, re-fetch
//		our settings from the system and update our variables.
//	*	Update the Notification Icon accordingly
//	*******************************
void	CIconWnd::OnWinIniChange(LPCTSTR lpszSection)
{
CString	String	= "WINDOWS";
	
	if (!String.CompareNoCase(lpszSection))
	{
		m_Fetch();

		m_UpdateIconTip();
		
		//	Check to see if Windows Extension #1 is installed
		m_bExtension1	= SystemParametersInfo(SPI_GETWINDOWSEXTENSION, 1, 0, 0);
	}
	
}



//	*******************************
//	CIconWnd::OnAbout()
//
//	Purpose:
//	*	Check to see if our About window is already up.
//	+	If so, make the current one the foreground active window
//	+	If not, create the About window and display it.
//	*******************************
void	CIconWnd::OnAbout()
{
CWnd*	pWnd	= FindWindow(NULL, "About System-Click . . .");

	if (!pWnd)
	{
	CDialog	About(IDD_ABOUT, this);

		About.DoModal();
	}
	else
		pWnd->SetForegroundWindow();
}



//	*******************************
//	CIconWnd::OnHelp()
//
//	Purpose:
//	*	Check to see if our Help window is already up.
//	+	If so, make the current one the foreground active window
//	+	If not, create the Help window and display it.
//	*******************************
void	CIconWnd::OnHelp()
{
CWnd*	pWnd	= FindWindow(NULL, "System-Click Help");

	if (!pWnd)
	{
	CDialog	Help(IDD_HELP, this);

		Help.DoModal();
	}
	else
		pWnd->SetForegroundWindow();
}


//	*******************************
//	CIconWnd::OnClearDocs()
//
//	Purpose:
//	*	Clears the Recent Document list in the Start Menus
//	*******************************
void	CIconWnd::OnClearDocs()
{
	//	Clears the document list
	::SHAddToRecentDocs(NULL, NULL);//SHARD_PATH, NULL); //SHARD_PIDL, NULL);
}


//	*******************************
//	CIconWnd::OnAnimated()
//
//	Purpose:
//	*	Toggle the Animated Window flag
//	*	Update the Notification Icon Tool-Tip to reflect current state
//	*	Update the Win95 system parameters
//	*******************************
void	CIconWnd::OnAnimated()
{
ANIMATIONINFO	Animated;

	m_bAnimated	= (m_bAnimated ? FALSE : TRUE);

	m_UpdateIconTip();
	
	//	Update the system parameter, save the setting in the Windows INI file, and let
	//	all top level windows know we have changed a system parameter.
	Animated.cbSize			= sizeof(ANIMATIONINFO);
	Animated.iMinAnimate	= m_bAnimated;

	SystemParametersInfo(
		SPI_SETANIMATION, 
		0, 
		&Animated, 
		SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
}



//	*******************************
//	CIconWnd::OnFullDrag()
//
//	Purpose:
//	*	Toggle the Full Dragging flag
//	*	Update the Notification Icon Tool-Tip to reflect current state
//	*	Update the Win95 system parameters
//	*******************************
void	CIconWnd::OnFullDrag()
{
	m_bFullDrag	= (m_bFullDrag ? FALSE : TRUE);

	m_UpdateIconTip();
	
	//	Update the system parameter, save the setting in the Windows INI file, and let
	//	all top level windows know we have changed a system parameter.
	SystemParametersInfo(
		SPI_SETDRAGFULLWINDOWS, 
		m_bFullDrag, 
		0, 
		SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
}



//	************************************************************
//	General Class Functions
//	************************************************************



//	*******************************
//	CIconWnd::CIconWnd()
//
//	Purpose:
//	*	Place keeper
//	*******************************
CIconWnd::CIconWnd()
{
}



//	*******************************
//	CIconWnd::~CIconWnd()
//
//	Purpose:
//	*	Place keeper
//	*******************************
CIconWnd::~CIconWnd()
{
}



//	*******************************
//	CIconWnd::m_UpdateIconTip()
//
//	Purpose:
//	*	Updates the Notification Icon Tool-Tip to refelct
//		current variable states.
//	*******************************
void	CIconWnd::m_UpdateIconTip()
{
NOTIFYICONDATA	nidSysClk;

	nidSysClk.cbSize	= sizeof(NOTIFYICONDATA);
	nidSysClk.hWnd		= AfxGetApp()->m_pMainWnd->GetSafeHwnd();
	nidSysClk.uID		= IDI_SYSCLK;
	nidSysClk.uFlags	= NIF_TIP;
	wsprintf(
		nidSysClk.szTip, 
		"FD: %s  AW: %s", 
		m_bFullDrag ? "ON" : "OFF",
		m_bAnimated ? "ON" : "OFF");

	Shell_NotifyIcon(NIM_MODIFY, &nidSysClk);
}
		


//	*******************************
//	CIconWnd::m_Fetch()
//
//	Purpose:
//	*	Fetch system settings from Win95
//	*******************************
void	CIconWnd::m_Fetch()
{
ANIMATIONINFO	Animated;

	SystemParametersInfo(
		SPI_GETDRAGFULLWINDOWS, 
		0, 
		&m_bFullDrag,
		0);

	Animated.cbSize			= sizeof(ANIMATIONINFO);
	SystemParametersInfo(
		SPI_GETANIMATION, 
		0, 
		&Animated, 
		0);
	m_bAnimated	= Animated.iMinAnimate ? TRUE : FALSE;
}



//	*******************************
//	CIconWnd::OnTimer()
//
//	Purpose:
//	*	Called .5 seconds after the Notification Icon is activated
//	*	Provide functionality for Notification Icon
//	*	Keeps stray mouse button clicks from dismissing menu before
//		it has displayed.
//	*******************************
void	CIconWnd::OnTimer(UINT nIDEvent)
{
	if (nIDEvent == IDT_CLOSE)
	{
		SendMessage(WM_CLOSE);
	}

	if (nIDEvent == IDT_POPUP)
	{
	CMenu	hPopup;
	POINT	Point;
	HWND	hOrigActive;

		//	Kill the timer
		KillTimer(m_Timer);
		m_Timer	= 0;

		//	Create our menu
		hPopup.CreatePopupMenu();
		
		if (m_bExtension1)	//	Requires Extension1 Pack to be installed
			hPopup.AppendMenu(MF_STRING, IDC_FULLDRAG, "&Full Drag");	

		//	Standard Win95 functionality
		hPopup.AppendMenu(MF_STRING, IDC_ANIMATED, "&Animated Windows");
		hPopup.AppendMenu(MF_STRING, IDC_CLRDOCS, "&Clear Recent Doc List");

		//	Generic program menu items
		hPopup.AppendMenu(MF_SEPARATOR);
		hPopup.AppendMenu(MF_STRING, IDC_ABOUT, "A&bout . . .");
		hPopup.AppendMenu(MF_STRING, IDC_APPHELP, "&Help");
		//hPopup.AppendMenu(MF_STRING, IDCLOSE, "E&xit");
		
		//	Set the initial checks correctly
		hPopup.CheckMenuItem(IDC_ANIMATED, MF_BYCOMMAND | (m_bAnimated ? MF_CHECKED : MF_UNCHECKED));
		
		if (m_bExtension1)
			hPopup.CheckMenuItem(IDC_FULLDRAG, MF_BYCOMMAND | (m_bFullDrag ? MF_CHECKED : MF_UNCHECKED));

		//	Get the currently active window, set our window to active, pop up the menu, and
		//	then return the system state to the way it was.
			
		hOrigActive	= ::GetForegroundWindow();
		SetForegroundWindow();
		::GetCursorPos(&Point);
		hPopup.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, Point.x, Point.y, this, NULL);
		::SetForegroundWindow(hOrigActive);

		//	Destroy our menu to insure our resources are freed
		hPopup.DestroyMenu();

		return;
	}
}



//	*******************************
//	CIconWnd::OnNotifyIcon()
//	
//	Purpose:
//	*	Notification Icon callback function
//	*	If the user left clicks on the icon, pop up the toggle button window.
//	*	If the user double-clicks on the icon, toggle the current mode.
//
//	NOTE:	Because of the magic in Win95, it is better not to do any actual
//			processing immediately after we encounter our message from the
//			Notification Icon.  
//			
//			It is better to trigger a Timer and respond to the WM_TIMER message.  
//			This gives the system enough time to flush out the stray mouse 
//			messages before our code actually takes place.
//
//			It appears as if the OS gives our program the mouse messages first, and 
//			then hands them to the Taskbar after we take them.  If we do not wait
//			and immediately do a TrackPopupMenu(), the stray mouse clicks may
//			dismiss this menu.  Likewise, if we close the application immediately,
//			the mouse clicks that triggered our app to shutdown will be fed to
//			the task bar and may cause adverse effects.
//	*******************************
LONG	CIconWnd::OnNotifyIcon(WPARAM wParam, LPARAM lParam)
{
UINT	uID;
UINT	uMsg;
BOOL	bFDOriginal	= m_bFullDrag;
BOOL	bAWOriginal	= m_bAnimated;

	uID		= (UINT)wParam;
	uMsg	= (UINT)lParam;

	if ((uMsg == WM_RBUTTONDBLCLK) && (uID == IDI_SYSCLK))
	{
		m_Timer	= SetTimer(IDT_CLOSE, 500, NULL);
		
		return	0L;
	}
	
	if ((uMsg == WM_LBUTTONDOWN) && (uID == IDI_SYSCLK))
	{
		m_Timer	= SetTimer(IDT_POPUP, 500, NULL);

		return	0L;
	}
		
	return 0L;
}



//	*******************************
//	CIconWnd::m_InitIcon()
//
//	Purpose:
//	*	Fetch the Full-Drag option from our INI file
//	*	Initialize the Notification Icon
//	*	Set the SystemParameterInfo() setting for the Full Dragging
//		accordingly.
//	Returns:
//	TRUE	Success
//	FALSE	Failure, either from Shell_NotifyIcon
//	*******************************
BOOL	CIconWnd::m_InitIcon()
{
NOTIFYICONDATA	nidSysClk;
BOOL			bRetVal;
HICON			hIcon;
ANIMATIONINFO	Animated;

	//	Fetch entries from our INI file [defaults = FALSE]
	//	NOTE:	We don't do this since all our settings are saved in the
	//			windows INI and in the registry.
	//m_bFullDrag	= AfxGetApp()->GetProfileInt("General", "Full Drag", 0);
	//m_bAnimated	= AfxGetApp()->GetProfileInt("General", "Animated", 0);

	//	Fetch current values from the System settings
	m_Fetch();
	
	//	Latch the Notification Icon into the Notification Area
	hIcon		= AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDI_SYSCLK));
	
	nidSysClk.cbSize			= sizeof(NOTIFYICONDATA);
	nidSysClk.hWnd				= GetSafeHwnd();
	nidSysClk.uID				= IDI_SYSCLK;
	nidSysClk.uFlags			= NIF_MESSAGE | NIF_ICON | NIF_TIP;
	nidSysClk.uCallbackMessage	= SC_WM_NOTIFYICON;
	nidSysClk.hIcon				= hIcon; 
	wsprintf(
		nidSysClk.szTip, 
		"FD: %s  AW: %s", 
		m_bFullDrag ? "ON" : "OFF",
		m_bAnimated ? "ON" : "OFF");
	
	bRetVal	= Shell_NotifyIcon(NIM_ADD, &nidSysClk);

	if (hIcon)
		DestroyIcon(hIcon);

	//	Update the system parameter, save the setting in the Windows INI file, and let
	//	all top level windows know we have changed a system parameter.
	SystemParametersInfo(
		SPI_SETDRAGFULLWINDOWS, 
		m_bFullDrag, 
		0, 
		SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);

	Animated.cbSize			= sizeof(ANIMATIONINFO);
	Animated.iMinAnimate	= m_bAnimated;

	SystemParametersInfo(
		SPI_SETANIMATION, 
		0, 
		&Animated, 
		SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
			   
	return	bRetVal;
}



//	*******************************
//	CIconWnd::OnDestroy()
//
//	Purpose:
//	*	Delete the notification icon from the Notification area.
//	*	Save the System-Click setting to our INI setting.
//
//	*******************************
void	CIconWnd::OnDestroy()
{
NOTIFYICONDATA	nidSysClk;

	//	Verify that we do not have a timer allocated
	if (m_Timer)
	{
		KillTimer(m_Timer);
		m_Timer	= 0;
	}
	
	//	Remove the Notification Icon from the Notification area
	nidSysClk.cbSize	= sizeof(NOTIFYICONDATA);
	nidSysClk.hWnd		= GetSafeHwnd();
	nidSysClk.uID		= IDI_SYSCLK;
	
	Shell_NotifyIcon(NIM_DELETE, &nidSysClk);

	//	Save our setting to the INI file
	//	NOTE:	We don't do this since all our settings are saved in the
	//			windows INI and in the registry.
	//AfxGetApp()->WriteProfileInt("General", "SysClk", m_bFullDrag);
	//AfxGetApp()->WriteProfileInt("General", "Animated", m_bAnimated);
	
	CWnd::OnDestroy();
}
