/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#define INITGUID
#include <windows.h>
#ifndef _WIN32
#include <ole2ui.h>
#include <dispatch.h>
#endif
#include "xp_mem.h"
#include "prprf.h"
#include "ngprefs.h"
#include <olectl.h>
#include "winprefs/isppageo.h"
#include "winprefs/brprefid.h"
#include "winprefs/wprefid.h"
#include "nsIDefaultBrowser.h"
#include "winprefs/intlfont.h"
#include "winprefs/ibrprefs.h"
#include "winprefs/prefuiid.h"
#include "winprefs/prefui.h"

#include "prefapi.h"
#include "globals.h"

extern "C" {
#include "xpgetstr.h"
};

BOOL	g_bReloadAllWindows;
BOOL    g_bReloadChangeColor;

char *FE_GetProgramDirectory(char *buffer, int length)
{
    ::GetModuleFileName(g_hInst, buffer, length);
    char *tmp = strrchr(buffer, '\\');
    if (tmp) {
        *tmp = '\0';
    }
    return buffer;
}

//  Use this to create some COM objects, as we need to switch to the
//      directory where the .EXE sits if the current directory isn't
//      the same, as the pref COM DLLs have relative paths in the
//      registry.
HRESULT FEU_CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv)
{
    HRESULT hRetval = NULL;

    char aOrigDir[MAX_PATH + 1];
    DWORD dwCheck = GetCurrentDirectory(sizeof(aOrigDir), aOrigDir);
    PR_ASSERT(dwCheck);

    char aProgramDir[MAX_PATH + 1];
    FE_GetProgramDirectory(aProgramDir, sizeof(aProgramDir));

    BOOL bCheck = SetCurrentDirectory(aProgramDir);
    PR_ASSERT(bCheck);

    hRetval = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv);

    BOOL bRestoreCheck = SetCurrentDirectory(aOrigDir);
    PR_ASSERT(bRestoreCheck);

    return(hRetval);
}

/////////////////////////////////////////////////////////////////////////////
// Helper functions

#ifndef _WIN32
static LPVOID
CoTaskMemAlloc(ULONG cb)
{
	LPMALLOC	pMalloc;
	
	if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pMalloc))) {
		LPVOID	pv = pMalloc->Alloc(cb);

		pMalloc->Release();
		return pv;
	}

	return NULL;
}
#endif

// Convert an ANSI string to an OLE string (UNICODE string)
static LPOLESTR NEAR
AllocTaskOleString(LPCSTR lpszString)
{
	LPOLESTR	lpszResult;
	UINT		nLen;

	if (lpszString == NULL)
		return NULL;

	// Convert from ANSI to UNICODE
	nLen = lstrlen(lpszString) + 1;
	lpszResult = (LPOLESTR)CoTaskMemAlloc(nLen * sizeof(OLECHAR));
	if (lpszResult)	{
#ifdef _WIN32
		MultiByteToWideChar(CP_ACP, 0, lpszString, -1, lpszResult, nLen);
#else
		lstrcpy(lpszResult, lpszString);  // Win 16 doesn't use any UNICODE
#endif
	}

	return lpszResult;
}

#ifdef XXX

/////////////////////////////////////////////////////////////////////////////
// CEnumHelpers

// Helper class to encapsulate enumeration of the helper apps
class CEnumHelpers : public IEnumHelpers {
	public:
		CEnumbHelpers();
		
		// IUnknown methods
		STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppvObj);
		STDMETHODIMP_(ULONG) AddRef();
		STDMETHODIMP_(ULONG) Release();

		// IEnumHelpers methods
		STDMETHODIMP		 Next(NET_cdataStruct **ppcdata);
		STDMETHODIMP		 Reset();

	private:
		ULONG		m_uRef;
		XP_List	   *m_pInfoList;
};

CEnumHelpers::CEnumHelpers()
{
	m_uRef = 0;
	m_pInfoList = cinfo_MasterListPointer();
}

STDMETHODIMP
CEnumHelpers::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
	*ppvObj = NULL;

	if (riid == IID_IUnknown || riid == IID_IEnumHelpers) {
		*ppvObj = (LPVOID)this;
		AddRef();
		return NOERROR;
	}

	return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG)
CEnumHelpers::AddRef()
{
	return ++m_uRef;
}

STDMETHODIMP_(ULONG)
CEnumHelpers::Release()
{
	if (--m_uRef == 0) {
		delete this;
		return 0;
	}

	return m_uRef;
}

STDMETHODIMP
CEnumHelpers::Next(NET_cdataStruct **ppcdata)
{
	CHelperApp	*pHelperApp;

  while ((*ppcdata = (NET_cdataStruct *)XP_ListNextObject(m_pInfoList))) {
		// Ignore items that don't have a MIME type
		if (!(*ppcdata)->ci.type)
			continue;

		// Don't give the user an opportunity to change application/octet-stream
		// or proxy auto-config
		if (strcmp((*ppcdata)->ci.type, APPLICATION_OCTET_STREAM) == 0 ||
			strcmp((*ppcdata)->ci.type, APPLICATION_NS_PROXY_AUTOCONFIG) == 0) {
			continue;
		}

		// Ignore items that don't have a description
		if (!(*ppcdata)->ci.desc) {
#ifdef DEBUG
			TRACE1("PREFS: Ignoring MIME type %s\n", (*ppcdata)->ci.type);
#endif
			continue;
		}
		
		// Make sure there's a CHelperApp associated with the item
		pHelperApp = (CHelperApp *)(*ppcdata)->ci.fe_data;

		if (!pHelperApp) {
#ifdef DEBUG
			TRACE1("PREFS: Ignoring MIME type %s\n", (*ppcdata)->ci.type);
#endif
			continue;
		}

		// Ignore items that have HANDLE_UNKNOWN or HANDLE_MOREINFO as how to handle
		if (pHelperApp->how_handle == HANDLE_UNKNOWN || pHelperApp->how_handle == HANDLE_MOREINFO) {
#ifdef DEBUG
			TRACE1("PREFS: Ignoring MIME type %s\n", (*ppcdata)->ci.type);
#endif
			continue;
		}

		return NOERROR;
	}

	return ResultFromScode(S_FALSE);
}

STDMETHODIMP
CEnumHelpers::Reset()
{
	m_pInfoList = cinfo_MasterListPointer();
	return NOERROR;
}

#endif /* XXX */

/////////////////////////////////////////////////////////////////////////////
// CBrowserPrefs

class CBrowserPrefs : public IBrowserPrefs {
	public:
		CBrowserPrefs(LPCSTR lpszCurrentPage);

		// IUnknown methods
		STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppvObj);
		STDMETHODIMP_(ULONG) AddRef();
		STDMETHODIMP_(ULONG) Release();
		
		// IBrowserPrefs methods
		STDMETHODIMP GetCurrentPage(LPOLESTR *lpoleStr);

		STDMETHODIMP EnumHelpers(LPENUMHELPERS *ppenumHelpers);
		STDMETHODIMP GetHelperInfo(NET_cdataStruct *, LPHELPERINFO);
		STDMETHODIMP SetHelperInfo(NET_cdataStruct *, LPHELPERINFO);
		STDMETHODIMP NewFileType(LPCSTR lpszDescription,
								 LPCSTR lpszExtension,
								 LPCSTR lpszMimeType,
								 LPCSTR lpszOpenCmd,
								 NET_cdataStruct **ppcdata);
		STDMETHODIMP RemoveFileType(NET_cdataStruct *);
		// Initialization routine to create contained and aggregated objects
		HRESULT		 Init();

	private:
		ULONG	  	m_uRef;
		LPUNKNOWN 	m_pCategory;  // inner object supporting ISpecifyPropertyPageObjects
		LPCSTR		m_lpszCurrentPage;
};

CBrowserPrefs::CBrowserPrefs(LPCSTR lpszCurrentPage)
{
	m_uRef = 0;
	m_pCategory = NULL;
	m_lpszCurrentPage = lpszCurrentPage ? strdup(lpszCurrentPage) : NULL;
}

HRESULT
CBrowserPrefs::Init()
{
	// Create the object as part of an aggregate
	return FEU_CoCreateInstance(CLSID_BrowserPrefs, (LPUNKNOWN)this,
		CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&m_pCategory);
}

STDMETHODIMP
CBrowserPrefs::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
	*ppvObj = NULL;

	if (riid == IID_IUnknown || riid == IID_IBrowserPrefs) {
		*ppvObj = (LPVOID)this;
		AddRef();
		return NOERROR;

	} else if (riid == IID_ISpecifyPropertyPageObjects) {
		assert(m_pCategory);
		return m_pCategory->QueryInterface(riid, ppvObj);
	}

	return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG)
CBrowserPrefs::AddRef()
{
	return ++m_uRef;
}

STDMETHODIMP_(ULONG)
CBrowserPrefs::Release()
{
	if (--m_uRef == 0) {
		if (m_pCategory)
			m_pCategory->Release();
		if (m_lpszCurrentPage)
			XP_FREE((void *)m_lpszCurrentPage);
		delete this;
		return 0;
	}

	return m_uRef;
}

STDMETHODIMP
CBrowserPrefs::GetCurrentPage(LPOLESTR *lpoleStr)
{
	if (!lpoleStr)
		return ResultFromScode(E_INVALIDARG);
	
	*lpoleStr = 0;

	if (m_lpszCurrentPage) {
		*lpoleStr = AllocTaskOleString(m_lpszCurrentPage);
		
		if (!*lpoleStr)
			return ResultFromScode(E_OUTOFMEMORY);
	}

	return NOERROR;
}

STDMETHODIMP
CBrowserPrefs::EnumHelpers(LPENUMHELPERS *ppEnumHelpers)
{
#ifdef XXX
	CEnumHelpers   *pEnumHelpers = new CEnumHelpers;
	HRESULT			hres;
	
	if (!pEnumHelpers)
		return ResultFromScode(E_OUTOFMEMORY);

	pEnumHelpers->AddRef();
	hres = pEnumHelpers->QueryInterface(IID_IEnumHelpers, (LPVOID *)ppEnumHelpers);
	pEnumHelpers->Release();
	return hres;
#else
    return E_NOTIMPL;
#endif
}


// Returns the command string value (path and filename for the application plus any
// command line options) associated with the given file extension
static BOOL
GetOpenCommandForExt(LPCSTR lpszExt, LPSTR lpszCmdString, DWORD cbCmdString)
{
	char    szBuf[_MAX_PATH + 32];
	char	szFileClass[60];
	LONG	lResult;
	LONG	lcb;
#ifdef _WIN32
	DWORD	cbData;
	DWORD	dwType;
#else
	LONG	cbData;
#endif

	*lpszCmdString = '\0';
	
	// Look up the file association key which maps a file extension
	// to an application identifier (also called the file type class
	// identifier or just file class)
	PR_snprintf(szBuf, sizeof(szBuf), ".%s", lpszExt);
	lcb = sizeof(szFileClass);
	lResult = RegQueryValue(HKEY_CLASSES_ROOT, szBuf, szFileClass, &lcb);

#ifdef _WIN32
	PR_ASSERT(lResult != ERROR_MORE_DATA);
#endif
	if (lResult != ERROR_SUCCESS)
		return FALSE;

	// Get the key for shell\open\command
	HKEY	hKey;
	
#ifdef _WIN32
	PR_snprintf(szBuf, sizeof(szBuf), "%s\\shell\\open\\command", szFileClass);
	lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, szBuf, 0, KEY_QUERY_VALUE, &hKey);
#else
	PR_snprintf(szBuf, sizeof(szBuf), "%s\\shell\\open\\command", szFileClass);
	lResult = RegOpenKey(HKEY_CLASSES_ROOT, szBuf, &hKey);
#endif
	if (lResult != ERROR_SUCCESS)
		return FALSE;

	// Get the value of the key
	cbData = sizeof(szBuf);
#ifdef _WIN32
	lResult = RegQueryValueEx(hKey, NULL, NULL, &dwType, (LPBYTE)szBuf, &cbData);
#else
	lResult = RegQueryValue(hKey, NULL, (LPSTR)szBuf, &cbData);
#endif
	RegCloseKey(hKey);
	if (lResult != ERROR_SUCCESS)
		return FALSE;

#ifdef _WIN32
	// Win32 doesn't expand automatically environment variables (for value
	// data of type REG_EXPAND_SZ). We need this for things like %SystemRoot%
	if (dwType == REG_EXPAND_SZ)
		ExpandEnvironmentStrings(szBuf, lpszCmdString, cbCmdString);
	else
		lstrcpy(lpszCmdString, szBuf);
#else
	lstrcpy(lpszCmdString, szBuf);
#endif

    return TRUE;
}

#ifdef XXX
// Returns the path of the helper application associated with the
// given pcdata
static BOOL
GetApplication(NET_cdataStruct *pcdata, LPSTR lpszApp)
{
	CHelperApp *pHelperApp = (CHelperApp *)pcdata->ci.fe_data;
	char   	   *lpszFile;
	char		szExtension[_MAX_EXT];
	LPSTR		lpszStrip;

	if (!pHelperApp)
		return FALSE;

	// How we do this depends on how it's handled
	switch (pHelperApp->how_handle) {
		case HANDLE_EXTERNAL:
			lstrcpy(lpszApp, (LPCSTR)pHelperApp->csCmd);

			// XXX - this is what the code in display.cpp does, but it's really
			// wrong...
			lpszStrip = strrchr(lpszApp, '%');

			if (lpszStrip)
				*lpszStrip = '\0';
			return TRUE;

		case HANDLE_SHELLEXECUTE:
		case HANDLE_BY_OLE:
			// Have FindExecutable() tell us what the executable name is
			wsprintf(szExtension, ".%s", pcdata->exts[0]);
			lpszFile = WH_TempFileName(xpTemporary, "M", szExtension);
			if (lpszFile) {
				BOOL	bResult = FEU_FindExecutable(lpszFile, lpszApp, TRUE);

				XP_FREE(lpszFile);
				return bResult;
			}
			return FALSE;

		default:
			ASSERT(FALSE);
			return FALSE;
	}
}

#endif /* XXX */

//Begin CRN_MIME
static BOOL
IsMimeTypeLocked(const char *pPrefix)
{
	BOOL bRet = FALSE;

	//???????Any holes here??????
	//I'm looking at just the ".type" field to see if a pref is locked. 
	char* setup_buf = PR_smprintf("%s.%s", pPrefix, "mimetype");

	bRet = PREF_PrefIsLocked(setup_buf);

	if(setup_buf && setup_buf[0])
		XP_FREEIF(setup_buf);

	return bRet;
}

#ifdef XXX
static int
GetPrefLoadAction(int how_handle)
{
	switch(how_handle) {
	case HANDLE_SAVE:
			return 1;

		case HANDLE_SHELLEXECUTE: 
			return 2;

		case HANDLE_UNKNOWN:
			return 3;

		case HANDLE_VIA_PLUGIN:
			return 4;

		default:
			return 2;
	}
}
//End CRN_MIME

#endif
STDMETHODIMP
CBrowserPrefs::GetHelperInfo(NET_cdataStruct *pcdata, LPHELPERINFO lpInfo)
{
#ifdef XXX
	CHelperApp	*pHelperApp;
    char         szApp[_MAX_PATH];
	
	if (!pcdata || !lpInfo)
		return ResultFromScode(E_INVALIDARG);

	pHelperApp = (CHelperApp *)pcdata->ci.fe_data;
	if (!pHelperApp)
		return ResultFromScode(E_UNEXPECTED);

	//Begin CRN_MIME
	if(! pHelperApp->csMimePrefPrefix.IsEmpty())
	{
		//This is a mime type associcated with a helper for a type specified thru' prefs.
		//Use the csMimePrefPrefix to generate the pref and see if it's locked.
		lpInfo->bIsLocked = IsMimeTypeLocked((LPCSTR)pHelperApp->csMimePrefPrefix);
		
	}
	else
		lpInfo->bIsLocked = FALSE;
	//End CRN_MIME

	lpInfo->nHowToHandle = pHelperApp->how_handle;
	lpInfo->szOpenCmd[0] = '\0';

	switch (pHelperApp->how_handle) {
		case HANDLE_EXTERNAL:
			lstrcpy(lpInfo->szOpenCmd, (LPCSTR)pHelperApp->csCmd);
            if (GetApplication(pcdata, szApp))
			    lpInfo->bAskBeforeOpening = theApp.m_pSpawn->PromptBeforeOpening(szApp);
			break;

		case HANDLE_SHELLEXECUTE:
		case HANDLE_BY_OLE:
			ASSERT(pcdata->num_exts > 0);
			if (pcdata->num_exts > 0) {
				GetOpenCommandForExt(pcdata->exts[0], lpInfo->szOpenCmd, sizeof(lpInfo->szOpenCmd));
                if (GetApplication(pcdata, szApp))
				    lpInfo->bAskBeforeOpening = theApp.m_pSpawn->PromptBeforeOpening(szApp);
			}
			break;

		case HANDLE_SAVE:
			if (pcdata->num_exts > 0) {
				// Even though it's marked as Save to Disk get the shell open command anyway. This
				// way if the user changes the association to launch the application they'll already
				// have it
				GetOpenCommandForExt(pcdata->exts[0], lpInfo->szOpenCmd, sizeof(lpInfo->szOpenCmd));
				lpInfo->bAskBeforeOpening = TRUE;
			}
			break;
//~~~
		case HANDLE_VIA_PLUGIN:
		case HANDLE_VIA_PLUGINAPPLET:
			if (pcdata->num_exts > 0) {
				GetOpenCommandForExt(pcdata->exts[0], lpInfo->szOpenCmd, sizeof(lpInfo->szOpenCmd));
				lpInfo->bAskBeforeOpening = FALSE;
			}
			break;
		default:
			break;
	}
	
	return NOERROR;
#else
    return E_NOTIMPL;
#endif
}

STDMETHODIMP
CBrowserPrefs::SetHelperInfo(NET_cdataStruct *pcdata, LPHELPERINFO lpInfo)
{
#ifdef XXX
    fe_ChangeFileType(pcdata, lpInfo->lpszMimeType, lpInfo->nHowToHandle, lpInfo->szOpenCmd);

//Begin CRN_MIME
	//Set user pref values only for mime types setup from the pref file
	CHelperApp	*pHelperApp;
	pHelperApp = (CHelperApp *)pcdata->ci.fe_data;
	if(pHelperApp)
	{
		if(! pHelperApp->csMimePrefPrefix.IsEmpty())
		{
			char* setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "mimetype");
			PREF_SetCharPref(setup_buf, lpInfo->lpszMimeType);
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);

			setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "win_appname");
			PREF_SetCharPref(setup_buf, lpInfo->szOpenCmd);
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);

			setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "load_action");
			PREF_SetIntPref(setup_buf, GetPrefLoadAction(lpInfo->nHowToHandle));
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);
		}
	}

//End CRN_MIME

	if (lpInfo->nHowToHandle == HANDLE_EXTERNAL ||
		lpInfo->nHowToHandle == HANDLE_SHELLEXECUTE ||
		lpInfo->nHowToHandle == HANDLE_BY_OLE) {

		// Update whether we should prompt before opening the file
        char    szApp[_MAX_PATH];

        if (GetApplication(pcdata, szApp))
		    theApp.m_pSpawn->SetPromptBeforeOpening(szApp, lpInfo->bAskBeforeOpening);
	}
	return NOERROR;
#else
    return E_NOTIMPL;
#endif
}

STDMETHODIMP
CBrowserPrefs::NewFileType(LPCSTR lpszDescription,
						   LPCSTR lpszExtension,
						   LPCSTR lpszMimeType,
						   LPCSTR lpszOpenCmd,
						   NET_cdataStruct **ppcdata)
{
#ifdef XXX
	*ppcdata = fe_NewFileType(lpszDescription, lpszExtension, lpszMimeType, lpszOpenCmd);
	return *ppcdata ? NOERROR : ResultFromScode(S_FALSE);
#else
    return E_NOTIMPL;
#endif
}

STDMETHODIMP
CBrowserPrefs::RemoveFileType(NET_cdataStruct *pcdata)
{
#ifdef XXX
//Begin CRN_MIME
	//Clear user pref values only for mime types setup from the pref file
	CHelperApp	*pHelperApp;
	pHelperApp = (CHelperApp *)pcdata->ci.fe_data;
	if(pHelperApp)
	{
		if(! pHelperApp->csMimePrefPrefix.IsEmpty())
		{
			char* setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "mimetype");
			PREF_ClearUserPref(setup_buf);
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);

			setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "win_appname");
			PREF_ClearUserPref(setup_buf);
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);

			setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "load_action");
			PREF_ClearUserPref(setup_buf);
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);

			setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "extension");
			PREF_ClearUserPref(setup_buf);
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);

			setup_buf = PR_smprintf("%s.%s", (LPCSTR)pHelperApp->csMimePrefPrefix, "description");
			PREF_ClearUserPref(setup_buf);
			if(setup_buf && setup_buf[0])
				XP_FREEIF(setup_buf);
		}
	}
//End CRN_MIME

	return fe_RemoveFileType(pcdata) ? NOERROR : ResultFromScode(S_FALSE);
#else
    return E_NOTIMPL;
#endif
}

/////////////////////////////////////////////////////////////////////////////
// CAppearancePreferences

class CAppearancePrefs : public IIntlFont {
	public:
		CAppearancePrefs(int nCharsetId);

		// IUnknown methods
		STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppvObj);
		STDMETHODIMP_(ULONG) AddRef();
		STDMETHODIMP_(ULONG) Release();
		
		// IIntlFont methods
		STDMETHODIMP GetNumEncodings(LPDWORD pdwEncodings);
		STDMETHODIMP GetEncodingName(DWORD dwCharsetNum, LPOLESTR *pszName);
		STDMETHODIMP GetEncodingInfo(DWORD dwCharsetNum, LPENCODINGINFO lpInfo);
		STDMETHODIMP SetEncodingFonts(DWORD dwCharsetNum, LPENCODINGINFO lpInfo);
		STDMETHODIMP GetCurrentCharset(LPDWORD pdwCharsetNum);

		// Initialization routine to create contained and aggregated objects
		HRESULT		 Init();

	private:
		ULONG	  m_uRef;
		int		  m_nCharsetId;
		LPUNKNOWN m_pCategory;  // inner object supporting ISpecifyPropertyPageObjects
};

CAppearancePrefs::CAppearancePrefs(int nCharsetId)
{
	m_uRef = 0;
	m_nCharsetId = nCharsetId;
	m_pCategory = NULL;
}

HRESULT
CAppearancePrefs::Init()
{
	// Create the object as part of an aggregate
	return FEU_CoCreateInstance(CLSID_AppearancePrefs, (LPUNKNOWN)this,
		CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&m_pCategory);
}

STDMETHODIMP
CAppearancePrefs::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
	*ppvObj = NULL;

	if (riid == IID_IUnknown || riid == IID_IIntlFont) {
		*ppvObj = (LPVOID)this;
		AddRef();
		return NOERROR;

	} else if (riid == IID_ISpecifyPropertyPageObjects) {
		assert(m_pCategory);
		return m_pCategory->QueryInterface(riid, ppvObj);
	}

	return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG)
CAppearancePrefs::AddRef()
{
	return ++m_uRef;
}

STDMETHODIMP_(ULONG)
CAppearancePrefs::Release()
{
	if (--m_uRef == 0) {
		if (m_pCategory)
			m_pCategory->Release();
		delete this;
		return 0;
	}

	return m_uRef;
}

STDMETHODIMP
CAppearancePrefs::GetNumEncodings(LPDWORD pdwEncodings)
{
	if (!pdwEncodings)
		return ResultFromScode(E_INVALIDARG);

#ifdef XXX	
	*pdwEncodings = (DWORD)MAXLANGNUM;
#else
	*pdwEncodings = (DWORD)0;
#endif
	return NOERROR;
}

STDMETHODIMP
CAppearancePrefs::GetEncodingName(DWORD dwCharsetNum, LPOLESTR *lpoleName)
{
#ifdef XXX
	if (dwCharsetNum >= (DWORD)MAXLANGNUM || !lpoleName)
		return ResultFromScode(E_INVALIDARG);

	LPCSTR	lpszName = theApp.m_pIntlFont->GetEncodingName((int)dwCharsetNum);

	if (lpszName) {
		*lpoleName = AllocTaskOleString(lpszName);
		return *lpoleName ? NOERROR : ResultFromScode(E_OUTOFMEMORY);
	}
#endif
	
	return ResultFromScode(E_UNEXPECTED);
}

STDMETHODIMP
CAppearancePrefs::GetEncodingInfo(DWORD dwCharsetNum, LPENCODINGINFO lpInfo)
{
#ifdef XXX
	EncodingInfo	*lpEncoding;

	if (dwCharsetNum >= (DWORD)MAXLANGNUM || !lpInfo)
		return ResultFromScode(E_INVALIDARG);
	
	lpEncoding = theApp.m_pIntlFont->GetEncodingInfo((int)dwCharsetNum);
	lpInfo->bIgnorePitch = ( CIntlWin::FontSelectIgnorePitch(lpEncoding->iCSID) );
	lstrcpy(lpInfo->szVariableWidthFont, lpEncoding->szPropName);
	lpInfo->nVariableWidthSize = lpEncoding->iPropSize;
	lstrcpy(lpInfo->szFixedWidthFont, lpEncoding->szFixName);
	lpInfo->nFixedWidthSize = lpEncoding->iFixSize;
#endif
	return NOERROR;
}

STDMETHODIMP
CAppearancePrefs::SetEncodingFonts(DWORD dwCharsetNum, LPENCODINGINFO lpInfo)
{	
#ifdef XXX	
    EncodingInfo   *lpEncoding;
	BOOL			bFixedWidthChanged;
	BOOL			bVariableWidthChanged;

	if (dwCharsetNum >= (DWORD)MAXLANGNUM || !lpInfo)
		return ResultFromScode(E_INVALIDARG);

	lpEncoding = theApp.m_pIntlFont->GetEncodingInfo((int)dwCharsetNum);

	// See if anything has changed. We don't want to reset everything
	// if nothing has changed
	bFixedWidthChanged = lstrcmp(lpInfo->szFixedWidthFont, lpEncoding->szFixName) != 0 ||
		lpInfo->nFixedWidthSize != lpEncoding->iFixSize;
	bVariableWidthChanged = lstrcmp(lpInfo->szVariableWidthFont, lpEncoding->szPropName) != 0 ||
		lpInfo->nVariableWidthSize != lpEncoding->iPropSize;

	if (bFixedWidthChanged || bVariableWidthChanged) {
		lstrcpy(lpEncoding->szFixName, lpInfo->szFixedWidthFont);
		lpEncoding->iFixSize = lpInfo->nFixedWidthSize;
		lstrcpy(lpEncoding->szPropName, lpInfo->szVariableWidthFont);
		lpEncoding->iPropSize = lpInfo->nVariableWidthSize;
    	
		// Reset the assorted font caches...
		CDCCX::ClearAllFontCaches();
#ifdef MOZ_NGLAYOUT
  XP_ASSERT(0);
#else
		CVirtualFontFontCache::Reset();
#endif /* MOZ_NGLAYOUT */
		theApp.m_pIntlFont->WriteToIniFile();

		// Indicate we need to reload all of the windows
		g_bReloadAllWindows = TRUE;
	}
#endif	
	return NOERROR;
}

STDMETHODIMP
CAppearancePrefs::GetCurrentCharset(LPDWORD pdwCharsetNum)
{
	if (!pdwCharsetNum)
		return ResultFromScode(E_INVALIDARG);

	*pdwCharsetNum = (DWORD)m_nCharsetId;
	return NOERROR;
}

typedef HRESULT (*DllServerFunction)(void);

static BOOL
RegisterCLSIDForDll(char * dllName)
{
	BOOL 	bRetval = FALSE;
	char    szLib[_MAX_PATH + 32];
	FE_GetProgramDirectory( szLib, _MAX_PATH + 32 );
	if ( *szLib ) {
		strcat( szLib, dllName );

		if(szLib)  {
			HINSTANCE hLibInstance = hLibInstance = LoadLibrary(szLib);
#ifdef WIN32
			if(hLibInstance)
#else
			if(hLibInstance > (HINSTANCE)HINSTANCE_ERROR)
#endif
			{
				FARPROC RegistryFunc = NULL;

				RegistryFunc = ::GetProcAddress(hLibInstance, "DllRegisterServer");
							
				if(RegistryFunc)   {
					HRESULT hResult = (RegistryFunc)();

					if(GetScode(hResult) == S_OK)   {
						bRetval = TRUE;
					}

					RegistryFunc = NULL;
				}
				else    {
					/* If the DLL doesn't have those functions then it just doesn't support
					 * self-registration. We don't consider that to be an error
					 *
					 * We should consider checking for the "OleSelfRegister" string in the
					 * StringFileInfo section of the version information resource. If the DLL
					 * has "OleSelfRegister" but doesn't have the self-registration functions
					 * then that would be an error
					 */
					bRetval = TRUE;
				}

				FreeLibrary(hLibInstance);
				hLibInstance = NULL;
			}
		}
	}
			
	return bRetval;
}

#ifdef MOZ_MAIL_NEWS
static BOOL
CreateMailNewsCategory(MWContext *pContext, LPSPECIFYPROPERTYPAGEOBJECTS *pCategory)
{
	BOOL	bResult = FALSE;

	// Create the property page providers
	CMailNewsPreferences *pMailNews = new CMailNewsPreferences(pContext);
	pMailNews->AddRef();

	// Initialize the mail news object. This allows it to load any objects that are
	// contained or aggregated
	if (SUCCEEDED(pMailNews->Init())) {
		// Get the interface pointer for ISpecifyPropertyPageObjects
		if (SUCCEEDED(pMailNews->QueryInterface(IID_ISpecifyPropertyPageObjects, (LPVOID *)pCategory)))
			bResult = TRUE;
	}
	else {
		// Try to register the dll and initialize again
#ifdef _WIN32
		bResult = RegisterCLSIDForDll("mnpref32.dll");
#else
		bResult = RegisterCLSIDForDll("mnpref16.dll");
#endif
		if (bResult) {
			if (SUCCEEDED(pMailNews->Init())) {
				// Get the interface pointer for ISpecifyPropertyPageObjects
				if (SUCCEEDED(pMailNews->QueryInterface(IID_ISpecifyPropertyPageObjects, (LPVOID *)pCategory)))
					bResult = TRUE;
			}
			else
				bResult = FALSE;
		}
	}
	
	// We're all done with the object
	pMailNews->Release();
	return bResult;
}
#endif /* MOZ_MAIL_NEWS */

#ifdef MOZ_LOC_INDEP
/////////////////////////////////////////////////////////////////////////////
// CLIPreference

class CLIPreference : public ILIPrefs {
	public:
		CLIPreference();

		// IUnknown methods
		STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppvObj);
		STDMETHODIMP_(ULONG) AddRef();
		STDMETHODIMP_(ULONG) Release();
		
		// Initialization routine to create contained and aggregated objects
		HRESULT		 Init();

	private:

		ULONG	  m_uRef;
		LPUNKNOWN m_pCategory;  // inner object supporting ISpecifyPropertyPageObjects
};

/////////////////////////////////////////////////////////////////////////////
// CLIPreference

CLIPreference::CLIPreference()
{
	m_uRef = 0;
	m_pCategory = NULL;
}

HRESULT
CLIPreference::Init()
{
	// Create the object as part of an aggregate
	return FEU_CoCreateInstance(CLSID_LIPrefs, (LPUNKNOWN)this,
		CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&m_pCategory);
}

STDMETHODIMP
CLIPreference::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
	*ppvObj = NULL;

	if (riid == IID_IUnknown || riid == IID_ILIlogin) {
		*ppvObj = (LPVOID)this;
		AddRef();
		return NOERROR;

	} else if (riid == IID_ISpecifyPropertyPageObjects) {
		assert(m_pCategory);
		return m_pCategory->QueryInterface(riid, ppvObj);
	}

	return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG)
CLIPreference::AddRef()
{
	return ++m_uRef;
}

STDMETHODIMP_(ULONG)
CLIPreference::Release()
{
	if (--m_uRef == 0) {
		if (m_pCategory)
			m_pCategory->Release();
		delete this;
		return 0;
	}

	return m_uRef;
}
#endif /* MOZ_LOC_INDEP */

#ifdef MOZ_SMARTUPDATE
/////////////////////////////////////////////////////////////////////////////
// CSmartUpdatePreference

class CSmartUpdatePrefs : public ISmartUpdatePrefs {
	public:
		CSmartUpdatePrefs();

		// IUnknown methods
		STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppvObj);
		STDMETHODIMP_(ULONG) AddRef();
		STDMETHODIMP_(ULONG) Release();
		
        // ISmartUpdatePrefs methods
        STDMETHODIMP_(LONG) Uninstall(char* regPackageName);
        STDMETHODIMP_(LONG) EnumUninstall(void** context, char* packageName,
                                    LONG len1, char*regPackageName, LONG len2);

	private:

		ULONG	  m_uRef;
};

/////////////////////////////////////////////////////////////////////////////
// CSmartUpdatePrefs
CSmartUpdatePrefs::CSmartUpdatePrefs()
{
	m_uRef = 0;
}

STDMETHODIMP
CSmartUpdatePrefs::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
	*ppvObj = NULL;

	if (riid == IID_IUnknown || riid == IID_ISmartUpdatePrefs) {
		*ppvObj = (LPVOID)this;
		AddRef();
		return NOERROR;

	} 

	return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG)
CSmartUpdatePrefs::AddRef()
{
	return ++m_uRef;
}

STDMETHODIMP_(ULONG)
CSmartUpdatePrefs::Release()
{
	if (--m_uRef == 0) {
		delete this;
		return 0;
	}

	return m_uRef;
}

STDMETHODIMP_(LONG)
CSmartUpdatePrefs::Uninstall(char* regPackageName)
{
	return SU_Uninstall(regPackageName);
}

STDMETHODIMP_(LONG)
CSmartUpdatePrefs::EnumUninstall(void** context, char* packageName,
                                 LONG len1, char*regPackageName, LONG len2)
{
	return SU_EnumUninstall(context, packageName,len1, regPackageName,len2);
}

#endif /* MOZ_SMARTUPDATE */


/////////////////////////////////////////////////////////////////////////////
// CAdvancedPrefs

class CAdvancedPrefs : public IAdvancedPrefs {
	public:
		CAdvancedPrefs();

		// IUnknown methods
		STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppvObj);
		STDMETHODIMP_(ULONG) AddRef();
		STDMETHODIMP_(ULONG) Release();
		
       	// Initialization routine to create contained and aggregated objects
		HRESULT		 Init();

	private:

		ULONG	  m_uRef;
		LPUNKNOWN m_pCategory;  // inner object supporting ISpecifyPropertyPageObjects
#ifdef MOZ_SMARTUPDATE
        CSmartUpdatePrefs *m_pSmartUpdatePrefs;
#endif /* MOZ_SMARTUPDATE */
};

/////////////////////////////////////////////////////////////////////////////
// CAdvancedPrefs
CAdvancedPrefs::CAdvancedPrefs()
{
	m_uRef = 0;
	m_pCategory = NULL;
#ifdef MOZ_SMARTUPDATE
    m_pSmartUpdatePrefs = new CSmartUpdatePrefs;
    m_pSmartUpdatePrefs->AddRef();
#endif /* MOZ_SMARTUPDATE */
 }

HRESULT
CAdvancedPrefs::Init()
{
   	// Create the object as part of an aggregate
  	return FEU_CoCreateInstance(CLSID_AdvancedPrefs, (LPUNKNOWN)this,
		CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&m_pCategory);
}

STDMETHODIMP
CAdvancedPrefs::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
	*ppvObj = NULL;

	if (riid == IID_IUnknown || riid == IID_IAdvancedPrefs) {
		*ppvObj = (LPVOID)this;
		AddRef();
		return NOERROR;
#ifdef MOZ_SMARTUPDATE
	} else if (riid == IID_ISmartUpdatePrefs) {
		assert(m_pSmartUpdatePrefs);
		return m_pSmartUpdatePrefs->QueryInterface(riid, ppvObj);
#endif /* MOZ_SMARTUPDATE */
	} else if (riid == IID_ISpecifyPropertyPageObjects) {
		assert(m_pCategory);
		return m_pCategory->QueryInterface(riid, ppvObj);
	}

	return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG)
CAdvancedPrefs::AddRef()
{
	return ++m_uRef;
}

STDMETHODIMP_(ULONG)
CAdvancedPrefs::Release()
{
	if (--m_uRef == 0) {
		if (m_pCategory)
			m_pCategory->Release();
#ifdef MOZ_SMARTUPDATE
        if (m_pSmartUpdatePrefs)
			m_pSmartUpdatePrefs->Release();
#endif /* MOZ_SMARTUPDATE */
		delete this;
		return 0;
	}

	return m_uRef;
}

void wfe_ReloadAllWindows()
{
    g_bReloadChangeColor = FALSE;
    g_bReloadAllWindows = FALSE;
}

static BOOL
CreateAppearancesCategory(HWND pFrame, LPSPECIFYPROPERTYPAGEOBJECTS *pCategory)
{
    int                 nCharsetId = 0;
	CAppearancePrefs   *pAppearance = new CAppearancePrefs(nCharsetId);
	BOOL		   		bResult = FALSE;
	
	pAppearance->AddRef();

	// Initialize the appearances object. This allows it to load any objects that are
	// contained or aggregated
	if (SUCCEEDED(pAppearance->Init())) {
		// Get the interface pointer for ISpecifyPropertyPageObjects
		if (SUCCEEDED(pAppearance->QueryInterface(IID_ISpecifyPropertyPageObjects, (LPVOID *)pCategory)))
			bResult = TRUE;
	}
	else {
		// Try to register the dll and initialize again
#ifdef _WIN32
		bResult = RegisterCLSIDForDll("brpref32.dll");
#else
		bResult = RegisterCLSIDForDll("brpref16.dll");
#endif
		if (bResult) {
			if (SUCCEEDED(pAppearance->Init())) {
				// Get the interface pointer for ISpecifyPropertyPageObjects
				if (SUCCEEDED(pAppearance->QueryInterface(IID_ISpecifyPropertyPageObjects, (LPVOID *)pCategory)))
					bResult = TRUE;
			}
			else
				bResult = FALSE;
		}
	}

	// We're all done with the object
	pAppearance->Release();
	return bResult;
}

static BOOL
CreateBrowserCategory(LPSPECIFYPROPERTYPAGEOBJECTS *pCategory)
{
	CBrowserPrefs *pBrowser;
	BOOL		   bResult = FALSE;

	// If this is the browser then use the URL for the current page
	pBrowser = new CBrowserPrefs(NULL);
	pBrowser->AddRef();
	
	// Initialize the browser pref object. This allows it to load any objects that are
	// contained or aggregated
	if (SUCCEEDED(pBrowser->Init())) {
		// Get the interface pointer for ISpecifyPropertyPageObjects
		if (SUCCEEDED(pBrowser->QueryInterface(IID_ISpecifyPropertyPageObjects, (LPVOID *)pCategory)))
			bResult = TRUE;
	}
	else {
		// Try to register the dll and initialize again
#ifdef _WIN32
		bResult = RegisterCLSIDForDll("brpref32.dll");
#else
		bResult = RegisterCLSIDForDll("brpref16.dll");
#endif
		if (bResult) {
			if (SUCCEEDED(pBrowser->Init())) {
				// Get the interface pointer for ISpecifyPropertyPageObjects
				if (SUCCEEDED(pBrowser->QueryInterface(IID_ISpecifyPropertyPageObjects, (LPVOID *)pCategory)))
					bResult = TRUE;
			}
			else
				bResult = FALSE;
		}
	}

	// We're all done with the object
	pBrowser->Release();
	return bResult;
}

static BOOL
CreateAdvancedCategory(LPSPECIFYPROPERTYPAGEOBJECTS *pCategory)
{
	CAdvancedPrefs *pAdvanced;
	BOOL		   bResult = FALSE;

	
	pAdvanced = new CAdvancedPrefs();
	pAdvanced->AddRef();
	
	// Initialize the browser pref object. This allows it to load any objects that are
	// contained or aggregated
	if (SUCCEEDED(pAdvanced->Init())) {
		// Get the interface pointer for ISpecifyPropertyPageObjects
		if (SUCCEEDED(pAdvanced->QueryInterface(IID_ISpecifyPropertyPageObjects, (LPVOID *)pCategory)))
			bResult = TRUE;
	} 
	
	// We're all done with the object
	pAdvanced->Release();
	return bResult;
}

typedef HRESULT (STDAPICALLTYPE *PFNPREFS)(HWND, int, int, LPCSTR, ULONG, LPSPECIFYPROPERTYPAGEOBJECTS *, ULONG, NETHELPFUNC);

// Callback for prefs to display a NetHelp window
void CALLBACK
#ifndef _WIN32
__export
#endif
NetHelpWrapper(LPCSTR lpszHelpTopic)
{
}

extern "C" void
DisplayPreferences(HWND pFrame)
{
	HINSTANCE					 	hPrefDll;
	LPSPECIFYPROPERTYPAGEOBJECTS	categories[7];
	PFNPREFS					 	pfnCreatePropertyFrame;
	ULONG						 	nCategories = 0;
	ULONG						 	nInitialCategory;
    ULONG                           nDesktopCategory = -1;
	static HWND		                pCurrentFrame = NULL;

	// We only allow one pref UI window up at a time
	if (pCurrentFrame) {
		HWND	hwnd = pCurrentFrame;

#ifdef _WIN32
		::SetForegroundWindow(hwnd);
#else
		::BringWindowToTop(hwnd);
#endif
		return;
	}

	// Load the preferences UI DLL
#ifdef _WIN32
	hPrefDll = LoadLibrary("prefui32.dll");
	if (hPrefDll) {
#else
	hPrefDll = LoadLibrary("prefui16.dll");
	if ((UINT)hPrefDll > 32) {
#endif

		// Get the address of the routine to create the property frame
		pfnCreatePropertyFrame = (PFNPREFS)GetProcAddress(hPrefDll, "NS_CreatePropertyFrame");
		if (!pfnCreatePropertyFrame) {
#ifdef _DEBUG
            const char * const pText = "Unable to get proc address for NS_CreatePropertyFrame";
            ::MessageBox( pFrame, pText, NULL, MB_OK | MB_ICONEXCLAMATION);
#endif
			FreeLibrary(hPrefDll);
			return;
		}
		
		// Remember that this window is displaying the pref UI
		pCurrentFrame = pFrame;

		// Appearances category
		if (CreateAppearancesCategory(pFrame, &categories[nCategories]))
			nCategories++;

		// Browser category
		if (CreateBrowserCategory(&categories[nCategories]))
			nCategories++;

		// Advanced category
        if (CreateAdvancedCategory(&categories[nCategories])) {
			nCategories++;
		}
             
        // Make sure we have at least one category
		if (nCategories == 0) {
            ::MessageBox(pFrame, "Can't Load Prefs", NULL, MB_OK | MB_ICONEXCLAMATION);
			FreeLibrary(hPrefDll);
			return;
		}
		
        // Hack to indicate whether we need to reload the windows because something
		// like a color or a font changed
		//
		// We do have code that observes the XP prefs, but if more than one preference
		// changes we'll get several callbacks (we don't have a way to do batch changes)
		// and we'll reload the windows multiple times which is very sloppy...
		g_bReloadAllWindows = FALSE;
		g_bReloadChangeColor = FALSE;

		// Figure out what the initial category we display should be
        nInitialCategory = 1;
		
		// Display the preferences
		pfnCreatePropertyFrame(pFrame, -1, -1, "Preferences", nCategories, categories,
            nInitialCategory, NetHelpWrapper);
		pCurrentFrame = NULL;

		// Release the interface pointers for the preference categories
		for (ULONG i = 0; i < nCategories; i++)
			categories[i]->Release();

		// Unload the library
		FreeLibrary(hPrefDll);

		// See if we need to reload the windows
		if (g_bReloadAllWindows) {
			wfe_ReloadAllWindows();
			g_bReloadAllWindows = FALSE;
		}

        // Save out the preferences
		PREF_SavePrefFile();

	} else {
#ifdef _DEBUG
		MessageBox(NULL, "Unable to load NSPREFUI.DLL", "Navigator",
			MB_OK | MB_ICONEXCLAMATION);
#endif
	}
}

