/* AbiSource Application Framework
 * Copyright (C) 1998 AbiSource, Inc.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */

#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <limits.h>
#include "zmouse.h"

#include "ut_types.h"
#include "ut_string_class.h"
#include "ut_assert.h"
#include "ut_debugmsg.h"
#include "xap_ViewListener.h"
#include "xap_Win32App.h"
#include "xap_Win32Frame.h"
#include "ev_Win32Keyboard.h"
#include "ev_Win32Mouse.h"
#include "ev_Win32Menu.h"
#include "ev_Win32Toolbar.h"
#include "ev_EditMethod.h"
#include "xav_View.h"
#include "xad_Document.h"
#include "ie_impGraphic.h"
#include "fv_View.h"

#ifdef _MSC_VER
#pragma warning(disable: 4355)	// 'this' used in base member initializer list
#endif

// TODO Fix the following header file. It seems to be incomplete
// TODO #include <ap_EditMethods.h>
// TODO In the mean time, define the needed function by hand
extern XAP_Dialog_MessageBox::tAnswer s_CouldNotLoadFileMessage(XAP_Frame * pFrame, const char * pNewFile, UT_Error errorCode);

/*****************************************************************/

bool XAP_Win32Frame::RegisterClass(XAP_Win32App* app)
{
	WNDCLASSEX wndclass;

	// register class for the frame window
	wndclass.cbSize			= sizeof(wndclass);
	wndclass.style			= CS_DBLCLKS;
	wndclass.lpfnWndProc	= XAP_Win32Frame::_FrameWndProc;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= app->getInstance();
	wndclass.hIcon			= app->getIcon();
	wndclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground	= (HBRUSH)(COLOR_BTNFACE+1);
	wndclass.lpszMenuName	= NULL;
	wndclass.lpszClassName	= app->getApplicationName();
	wndclass.hIconSm		= app->getSmallIcon();

	ATOM a = RegisterClassEx(&wndclass);
	UT_ASSERT(a);

	return true;
}

/*****************************************************************/

XAP_Win32Frame::XAP_Win32Frame(XAP_Win32App * app)
:	XAP_Frame(app),
	m_pWin32App(app),
	m_pWin32Menu(0),
	m_pWin32Popup(0),
	m_iBarHeight(0),
	m_iStatusBarHeight(0),
	m_hwndFrame(0),
	m_hwndRebar(0),
	m_hwndContainer(0),
	m_hwndStatusBar(0),
	m_dialogFactory(this, app),
	m_mouseWheelMessage(0),
	m_iSizeWidth(0),
	m_iSizeHeight(0)
{
}

// TODO when cloning a new frame from an existing one
// TODO should we also clone any frame-persistent
// TODO dialog data ??

XAP_Win32Frame::XAP_Win32Frame(XAP_Win32Frame * f)
:	XAP_Frame(f),
	m_pWin32App(f->m_pWin32App),
	m_pWin32Menu(0),
	m_pWin32Popup(0),
	m_iBarHeight(0),
	m_iStatusBarHeight(0),
	m_hwndFrame(0),
	m_hwndRebar(0),
	m_hwndContainer(0),
	m_hwndStatusBar(0),
	m_dialogFactory(this, f->m_pWin32App),
	m_mouseWheelMessage(0),
	m_iSizeWidth(0),
	m_iSizeHeight(0)
{
}

XAP_Win32Frame::~XAP_Win32Frame(void)
{
	// only delete the things we created...
	
	DELETEP(m_pWin32Menu);
	DELETEP(m_pWin32Popup);
}

bool XAP_Win32Frame::initialize(const char* szKeyBindingsKey,
								const char* szKeyBindingsDefaultValue,
								const char* szMenuLayoutKey,
								const char* szMenuLayoutDefaultValue,
								const char* szMenuLabelSetKey,
								const char* szMenuLabelSetDefaultValue,
								const char* szToolbarLayoutsKey,
								const char* szToolbarLayoutsDefaultValue,
								const char* szToolbarLabelSetKey,
								const char* szToolbarLabelSetDefaultValue)
{
	bool bResult;

	// invoke our base class first.
	
	bResult = XAP_Frame::initialize(szKeyBindingsKey,
									szKeyBindingsDefaultValue,
									szMenuLayoutKey,
									szMenuLayoutDefaultValue,
									szMenuLabelSetKey,
									szMenuLabelSetDefaultValue,
									szToolbarLayoutsKey,
									szToolbarLayoutsDefaultValue,
									szToolbarLabelSetKey,
									szToolbarLabelSetDefaultValue);
	UT_ASSERT(bResult);

	// get a handle to our keyboard binding mechanism
	// and to our mouse binding mechanism.
	
	EV_EditEventMapper * pEEM = getEditEventMapper();
	UT_ASSERT(pEEM);

	m_pKeyboard = new ev_Win32Keyboard(pEEM);
	UT_ASSERT(m_pKeyboard);
	
	m_pMouse = new EV_Win32Mouse(pEEM);
	UT_ASSERT(m_pMouse);

	// TODO: Jeff, I'm currently showing in WinMain, to honor iCmdShow.
	// should we pass that via argv, to do it here for all frames?


	return true;
}

UT_sint32 XAP_Win32Frame::setInputMode(const char * szName)
{
	UT_sint32 result = XAP_Frame::setInputMode(szName);
	if (result == 1)
	{
		// if it actually changed we need to update keyboard and mouse

		EV_EditEventMapper * pEEM = getEditEventMapper();
		UT_ASSERT(pEEM);

		m_pKeyboard->setEditEventMap(pEEM);
		m_pMouse->setEditEventMap(pEEM);
	}

	return result;
}

HWND XAP_Win32Frame::getTopLevelWindow(void) const
{
	return m_hwndFrame;
}

HWND XAP_Win32Frame::getToolbarWindow(void) const
{
	return m_hwndRebar;
}

XAP_DialogFactory * XAP_Win32Frame::getDialogFactory(void)
{
	return &m_dialogFactory;
}

void XAP_Win32Frame::_createTopLevelWindow(void)
{
	RECT r;
	UT_uint32 iHeight, iWidth;
	UT_sint32 iPosX, iPosY;
	static bool firstWindow = true;	/* position only 1st window! */

	// create a top-level window for us.
	// get the default window size from preferences or something.
	// should set size for all, but position only on 1st created
	// TODO determine where to save & restore from Window flag (since
	//      we can't use the geometry flag (its some other junk about validity of pos & size)
	//      so we can properly restore Maximized/Minimized/Normal mode windows
	// For now we can let Win32App set the flag for the 1st opened window

	// get window width & height from preferences
	UT_uint32 t_flag;		// dummy variable
	if ( !(XAP_App::getApp()->getGeometry(&iPosX,&iPosY,&iWidth,&iHeight,&t_flag)) ||
           !((iWidth > 0) && (iHeight > 0)) )
	{
		UT_DEBUGMSG(("Unable to obtain saved geometry, using window defaults!\n"));
		iWidth = CW_USEDEFAULT;
		iHeight = CW_USEDEFAULT;
		iPosX = CW_USEDEFAULT;
		iPosY = CW_USEDEFAULT;
	}
	/* let Windows(R) place the Window for all but 1st one, for stairstep effect */
	if (!firstWindow)
	{
		iPosX = CW_USEDEFAULT;
		iPosY = CW_USEDEFAULT;
	}
	else firstWindow = false;

	m_hwndFrame = CreateWindow(m_pWin32App->getApplicationName(),
							   m_pWin32App->getApplicationTitleForTitleBar(),
							   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
							   iPosX, iPosY, iWidth, iHeight,
							   NULL, NULL, m_pWin32App->getInstance(), NULL);
	UT_ASSERT(m_hwndFrame);

	// bind this frame to its window
	SetWindowLong(m_hwndFrame, GWL_USERDATA,(LONG)this);

	m_mouseWheelMessage = RegisterWindowMessage(MSH_MOUSEWHEEL);

	// synthesize a menu from the info in our
	// base class and install it into the window.
	m_pWin32Menu = new EV_Win32MenuBar(m_pWin32App,
									   getEditEventMapper(),
									   m_szMenuLayoutName,
									   m_szMenuLabelSetName);
	UT_ASSERT(m_pWin32Menu);
	bool bResult = m_pWin32Menu->synthesizeMenuBar(this);
	UT_ASSERT(bResult);

	HMENU oldMenu = GetMenu(m_hwndFrame);
	if (SetMenu(m_hwndFrame, m_pWin32Menu->getMenuHandle()))
	{
		DrawMenuBar(m_hwndFrame);
		if (oldMenu)
			DestroyMenu(oldMenu);
	}

	// create a rebar container for all the toolbars
	m_hwndRebar = CreateWindowEx(0L, REBARCLASSNAME, NULL,
								 WS_VISIBLE | WS_BORDER | WS_CHILD | WS_CLIPCHILDREN |
								 WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN |
								 RBS_VARHEIGHT | RBS_BANDBORDERS,
								 0, 0, 0, 0,
								 m_hwndFrame, NULL, m_pWin32App->getInstance(), NULL);
	UT_ASSERT(m_hwndRebar);

	// create a toolbar instance for each toolbar listed in our base class.

	_createToolbars();

	// figure out how much room is left for the child
	GetClientRect(m_hwndFrame, &r);
	iHeight = r.bottom - r.top;
	iWidth = r.right - r.left;

	m_iSizeWidth = iWidth;
	m_iSizeHeight = iHeight;
	
	// force rebar to resize itself
	// TODO for some reason, we give REBAR the height of the FRAME
	// TODO and let it decide how much it actually needs....
	if( m_hwndRebar != NULL )
	{
		MoveWindow(m_hwndRebar, 0, 0, iWidth, iHeight, TRUE);

		GetClientRect(m_hwndRebar, &r);
		m_iBarHeight = r.bottom - r.top + 6;

		UT_ASSERT(iHeight > m_iBarHeight);
		iHeight -= m_iBarHeight;
	}
	else
		m_iBarHeight = 0;

	m_hwndContainer = _createDocumentWindow(m_hwndFrame, 0, m_iBarHeight, iWidth, iHeight);

	// Let the app-specific frame code create the status bar
	// if it wants to.  we will put it below the document
	// window (a peer with toolbars and the overall sunkenbox)
	// so that it will appear outside of the scrollbars.

	m_hwndStatusBar = _createStatusBarWindow(m_hwndFrame,0,m_iBarHeight+iHeight,iWidth);
	GetClientRect(m_hwndStatusBar,&r);
	m_iStatusBarHeight = r.bottom;

	// Allow drag-and drop
	DragAcceptFiles(m_hwndFrame, true);

	// we let our caller decide when to show m_hwndFrame.

	return;
}

bool XAP_Win32Frame::close()
{
	// NOTE: This may not be the proper place, but it does mean that the
	// last window closed is the one that the window state is stored from.
	WINDOWPLACEMENT wndPlacement;
	wndPlacement.length = sizeof(WINDOWPLACEMENT); // must do
	if (GetWindowPlacement(m_hwndFrame, &wndPlacement))
	{
		getApp()->setGeometry(wndPlacement.rcNormalPosition.left, 
				wndPlacement.rcNormalPosition.top, 
				wndPlacement.rcNormalPosition.right - wndPlacement.rcNormalPosition.left,
				wndPlacement.rcNormalPosition.bottom - wndPlacement.rcNormalPosition.top,
				/* flag is meant for info about the position & size info stored, not a generic flag
				 * TODO: figure out where to store this then, so we can max/min/normal again
				wndPlacement.showCmd
				*/
				PREF_FLAG_GEOMETRY_POS | PREF_FLAG_GEOMETRY_SIZE
				);
	}
	else
	{
		// if failed to get placement then invalidate stored settings
		getApp()->setGeometry(0,0,0,0,0);
	}

	// NOTE: this should only be called from the closeWindow edit method
	DestroyWindow(m_hwndFrame);

	return true;
}

bool XAP_Win32Frame::raise()
{
	BringWindowToTop(m_hwndFrame);

	return true;
}

bool XAP_Win32Frame::show()
{
	ShowWindow(m_hwndFrame, SW_SHOW);
	UpdateWindow(m_hwndFrame);

	return true;
}

bool XAP_Win32Frame::openURL(const char * szURL)
{
	// NOTE: could get finer control over browser window via DDE 
	// NOTE: may need to fallback to WinExec for old NSCP versions

	UT_String sURL = szURL;
	HWND hwnd = getTopLevelWindow();

	if ( "file:///\\" == sURL.substr(0,9) )
	{
	     sURL = sURL.substr(9, sURL.size() - 9);
	}
	
	// strip "file://" from URL, win32 doesn't handle them well
	if ( "file://" == sURL.substr(0, 7) )
	{
		sURL = sURL.substr(7, sURL.size() - 7);
	}
	
	for (int i=0; i<sURL.size();i++)
	  if (sURL[i]=='\\')      sURL[i]='/';
	  
	int res = (int) ShellExecute(hwnd, "open", sURL.c_str(), NULL, NULL, SW_SHOWNORMAL);

	// TODO: more specific (and localized) error messages ??
	// added more specific error messages as documented in http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp

	if (res <= 32)	// show error message if failed to launch browser to display URL
	{
		UT_String errMsg;
		switch (res)
		{
			case 2:
				{
					errMsg = "Error ("; 
					errMsg += UT_String_sprintf("%d", res);
					errMsg += ") displaying URL: The system cannot find the file specified.\n";
					errMsg += " [ ";  errMsg += sURL;  errMsg += " ] ";
					MessageBox(hwnd, errMsg.c_str(), "Error displaying URL", MB_OK|MB_ICONEXCLAMATION);
				}
				break;

			case 3:
				{
					errMsg = "Error ("; 
					errMsg += UT_String_sprintf("%d", res);
					errMsg += ") displaying URL: The system cannot find the path specified.\n";
					errMsg += " [ ";  errMsg += sURL;  errMsg += " ] ";
					MessageBox(hwnd, errMsg.c_str(), "Error displaying URL", MB_OK|MB_ICONEXCLAMATION);
				}
				break;

			case 5:
				{
					errMsg = "Error ("; 
					errMsg += UT_String_sprintf("%d", res);
					errMsg += ") displaying URL: Access is denied.\n";
					errMsg += " [ ";  errMsg += sURL;  errMsg += " ] ";
					MessageBox(hwnd, errMsg.c_str(), "Error displaying URL", MB_OK|MB_ICONEXCLAMATION);
				}
				break;

			default:
				{
					errMsg = "Error ("; 
					errMsg += UT_String_sprintf("%d", res);
					errMsg += ") displaying URL: \n";
					errMsg += " [ ";  errMsg += sURL;  errMsg += " ] ";
					MessageBox(hwnd, errMsg.c_str(), "Error displaying URL", MB_OK|MB_ICONEXCLAMATION);
				}
				break;
		} /* switch (res) */
	} /* if (res <= 32) */

	return (res>32);
}

bool XAP_Win32Frame::updateTitle()
{
	if (!XAP_Frame::updateTitle())
	{
		// no relevant change, so skip it
		return false;
	}

	UT_String sTmp = getTitle(INT_MAX);
	sTmp += " - ";
	sTmp += m_pWin32App->getApplicationTitleForTitleBar();
	
	SetWindowText(m_hwndFrame, sTmp.c_str());

	return true;
}

LRESULT CALLBACK XAP_Win32Frame::_FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	XAP_Win32Frame * f = (XAP_Win32Frame*)GetWindowLong(hwnd, GWL_USERDATA);

	if (!f)
	{
		return DefWindowProc(hwnd,iMsg,wParam,lParam);
	}
	
	AV_View * pView = NULL;

	pView = f->m_pView;

	if(iMsg == f->m_mouseWheelMessage)
	{
		wParam = MAKEWPARAM(0, (short)(int)wParam);
		return SendMessage(hwnd, WM_MOUSEWHEEL, wParam, lParam);
	}

	switch (iMsg)
	{
	case WM_EXITMENULOOP:
	case WM_SETFOCUS:
		if (pView)
		{
			pView->focusChange(AV_FOCUS_HERE);
			SetFocus(f->m_hwndContainer);
		}
		return 0;

	case WM_ENTERMENULOOP:
	case WM_KILLFOCUS:
		if (pView)
		{
			pView->focusChange(AV_FOCUS_NONE);
		}
		return 0;

	case WM_CREATE:
		return 0;

	case WM_COMMAND:
		if (f->m_pWin32Popup)
		{
			if (f->m_pWin32Popup->onCommand(pView,hwnd,wParam))
				return 0;
		}
		else if (f->m_pWin32Menu->onCommand(pView,hwnd,wParam))
		{
			return 0;
		}
		else if (HIWORD(wParam) == 0)
		{
			// after menu passes on it, give each of the toolbars a chance
			UT_uint32 nrToolbars, k;
			nrToolbars = f->m_vecToolbars.getItemCount();
			for (k=0; k < nrToolbars; k++)
			{
				EV_Win32Toolbar * t = (EV_Win32Toolbar *)f->m_vecToolbars.getNthItem(k);
				XAP_Toolbar_Id id = t->ItemIdFromWmCommand(LOWORD(wParam));
				if (t->toolbarEvent(id))
					return 0;
			}
		}
		return DefWindowProc(hwnd,iMsg,wParam,lParam);

	case WM_INITMENU:
		if (f->m_pWin32Popup)
		{
			if (f->m_pWin32Popup->onInitMenu(f,pView,hwnd,(HMENU)wParam))
				return 0;
		}
		else if (f->m_pWin32Menu->onInitMenu(f,pView,hwnd,(HMENU)wParam))
			return 0;
		return DefWindowProc(hwnd,iMsg,wParam,lParam);

	case WM_MENUSELECT:
		if (f->m_pWin32Popup)
		{
			if (f->m_pWin32Popup->onMenuSelect(f,pView,hwnd,(HMENU)lParam,wParam))
				return 0;
		}
		else if (f->m_pWin32Menu->onMenuSelect(f,pView,hwnd,(HMENU)lParam,wParam))
			return 0;
		return DefWindowProc(hwnd,iMsg,wParam,lParam);

	case WM_NOTIFY:
		switch (((LPNMHDR) lParam)->code) 
		{
		case TTN_NEEDTEXT:
			{
				UT_uint32 nrToolbars, k;
				nrToolbars = f->m_vecToolbars.getItemCount();
				for (k=0; k < nrToolbars; k++)
				{
					EV_Win32Toolbar * t = (EV_Win32Toolbar *)f->m_vecToolbars.getNthItem(k);
					if (t->getToolTip(lParam))
						break;
				}
			}
			break;

		case RBN_HEIGHTCHANGE:
			{
				RECT r;
				GetClientRect(f->m_hwndFrame, &r);
				int nWidth = r.right - r.left;
				int nHeight = r.bottom - r.top;

				GetClientRect(f->m_hwndRebar, &r);
				f->m_iBarHeight = r.bottom - r.top + 6;

				if (f->m_hwndContainer)
				{
					// leave room for the toolbars
					nHeight -= f->m_iBarHeight;

					MoveWindow(f->m_hwndContainer, 0, f->m_iBarHeight, nWidth, nHeight, TRUE);
				}
			}
			break;

		case NM_CUSTOMDRAW:
			{
				LPNMCUSTOMDRAW  pNMcd = (LPNMCUSTOMDRAW)lParam;
				UT_uint32 nrToolbars, k;
				nrToolbars = f->m_vecToolbars.getItemCount();
				for (k=0; k < nrToolbars; k++)
				{
					EV_Win32Toolbar * t = (EV_Win32Toolbar *)f->m_vecToolbars.getNthItem(k);
					if( pNMcd->hdr.hwndFrom == t->getWindow() )
					{
						if( pNMcd->dwDrawStage == CDDS_PREPAINT )
						{
							return CDRF_NOTIFYPOSTPAINT;
						}
						if( pNMcd->dwDrawStage == CDDS_POSTPAINT )
						{
							RECT  rc;
							HBRUSH	hBr = NULL;

							rc.top    = pNMcd->rc.top;
							rc.bottom = pNMcd->rc.bottom;
							hBr = GetSysColorBrush( COLOR_3DFACE );

							HWND  hWndChild = FindWindowEx( pNMcd->hdr.hwndFrom, NULL, NULL, NULL );
							while( hWndChild != NULL )
							{
								RECT   rcChild;
								POINT  pt;
								GetWindowRect( hWndChild, &rcChild );
								pt.x = rcChild.left;
								pt.y = rcChild.top;
								ScreenToClient( pNMcd->hdr.hwndFrom, &pt );
								rc.left = pt.x;
								pt.x = rcChild.right;
								pt.y = rcChild.bottom;
								ScreenToClient( pNMcd->hdr.hwndFrom, &pt );
								rc.right = pt.x;
								FillRect( pNMcd->hdc, &rc, hBr );
								hWndChild = FindWindowEx( pNMcd->hdr.hwndFrom, hWndChild, NULL, NULL );
							}

							if( hBr != NULL )
							{
								DeleteObject( hBr );
								hBr = NULL;
							}
						}
						break;
					}
				}
			}
			break;

		// Process other notifications here
		default:
			break;
		} /* switch (((LPNMHDR) lParam)->code) */
		break;

	case WM_SIZE:
		{
			int nWidth = LOWORD(lParam);
			int nHeight = HIWORD(lParam);

			if( pView && !pView->isLayoutFilling() )
			{
				f->_startViewAutoUpdater();

				if (nWidth != (int) f->m_iSizeWidth && f->m_hwndRebar != NULL)
				{
					MoveWindow(f->m_hwndRebar, 0, 0, nWidth, f->m_iBarHeight, TRUE); 
				}

				// leave room for the toolbars and the status bar
				nHeight -= f->m_iBarHeight;
				nHeight -= f->m_iStatusBarHeight;

				if (f->m_hwndContainer)
					MoveWindow(f->m_hwndContainer, 0, f->m_iBarHeight, nWidth, nHeight, TRUE);

				if (f->m_hwndStatusBar)
					MoveWindow(f->m_hwndStatusBar, 0, f->m_iBarHeight+nHeight, nWidth, f->m_iStatusBarHeight, TRUE);
				
				f->m_iSizeWidth = nWidth;
				f->m_iSizeHeight = nHeight;

				f->updateZoom();
			}

			return 0;
		}

	case WM_CLOSE:
		{
			XAP_App * pApp = f->getApp();
			UT_ASSERT(pApp);

			const EV_EditMethodContainer * pEMC = pApp->getEditMethodContainer();
			UT_ASSERT(pEMC);

			EV_EditMethod * pEM = pEMC->findEditMethodByName("closeWindowX");
			UT_ASSERT(pEM);						// make sure it's bound to something

			if (pEM)
			{
				(*pEM->getFn())(pView,NULL);
				return 0;
			}

			// let the window be destroyed
			break;
		}

	case WM_INPUTLANGCHANGE:
		{
			UT_DEBUGMSG(("Frame received input language change\n"));

			// This will remap the static tables used by all frames.
			// (see the comment in ev_Win32Keyboard.cpp.)
			ev_Win32Keyboard *pWin32Keyboard = static_cast<ev_Win32Keyboard *>(f->m_pKeyboard);
			pWin32Keyboard->remapKeyboard((HKL)lParam);

			// Do not propagate this message.
			
			return 1; //DefWindowProc(hwnd, iMsg, wParam, lParam);
		}

	case WM_MOUSEWHEEL:
		{
			return SendMessage(f->m_hwndContainer, iMsg, wParam, lParam);
		}

	case WM_SYSCOLORCHANGE:
		{
			if (f->m_hwndRebar)
			{
				SendMessage(f->m_hwndRebar,WM_SYSCOLORCHANGE,0,0);

				REBARBANDINFO rbbi = { 0 };
				rbbi.cbSize = sizeof(REBARBANDINFO);
				rbbi.fMask = RBBIM_COLORS;
				rbbi.clrFore = GetSysColor(COLOR_BTNTEXT);
				rbbi.clrBack = GetSysColor(COLOR_BTNFACE);

				UT_uint32 nrToolbars = f->m_vecToolbars.getItemCount();
				for (UT_uint32 k=0; k < nrToolbars; k++)
					SendMessage(f->m_hwndRebar, RB_SETBANDINFO,k,(LPARAM)&rbbi);
			}

			if (f->m_hwndContainer)
				SendMessage(f->m_hwndContainer,WM_SYSCOLORCHANGE,0,0);
			if (f->m_hwndStatusBar)
				SendMessage(f->m_hwndStatusBar,WM_SYSCOLORCHANGE,0,0);
			return 0;
		}

	case WM_DROPFILES:
		{
			HDROP hDrop = (HDROP) wParam; 
			// How many files were dropped?
			int count = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
			char szFileName[_MAX_PATH];
			int i,pathlength;
			for (i=0; i<count; i++)
			{
				pathlength = DragQueryFile(hDrop, i, NULL, 0);
				if (pathlength < _MAX_PATH)
				{
					DragQueryFile(hDrop, i, szFileName, _MAX_PATH);
					XAP_App * pApp = f->getApp();
					UT_ASSERT(pApp);
					FV_View* pView = (FV_View *) f->getCurrentView();
					XAP_Frame * pNewFrame = 0;
					IEGraphicFileType iegft = IEGFT_Unknown;					
					IE_ImpGraphic *pIEG;
					FG_Graphic* pFG;
					UT_Error errorCode;

					/*
						The user may be dropping every kind of file
						Check first if the file is a graphic. If it's a graphics. I we insert it 
						in the document, if not we assume that it's document 		
					*/								
					// If there is no import graphic, it's a document...
					errorCode = IE_ImpGraphic::constructImporter(szFileName, iegft, &pIEG);
					if(errorCode == UT_OK)
					{						
						errorCode = pIEG->importGraphic(szFileName, &pFG);
						if(errorCode != UT_OK || !pFG)
						{
							s_CouldNotLoadFileMessage(f, szFileName, errorCode);							
							DELETEP(pIEG);
							return 0;
						}

						DELETEP(pIEG);					

						errorCode = pView->cmdInsertGraphic(pFG, szFileName);
						if (errorCode != UT_OK)
						{
							s_CouldNotLoadFileMessage(f, szFileName, errorCode);							
							DELETEP(pFG);
							return 0;
						}
						
						DELETEP(pFG);
					  }
					else
					{	
						// Check if the current document is empty.
						if (f->isDirty() || f->getFilename() ||
							(f->getViewNumber() > 0))
						{
							pNewFrame = pApp->newFrame();
							if (pNewFrame == NULL)
							{
								f->setStatusMessage("Could not open another window");
								return 0;
							}
						}
						else
						{
							pNewFrame = f;
						}

						UT_Error error = pNewFrame->loadDocument(szFileName, IEFT_Unknown);
						if (error != UT_OK)
						{
							if (f != pNewFrame)
								pNewFrame->close();
							s_CouldNotLoadFileMessage(f, szFileName, error);
						}
						else
						{
							pNewFrame->show();
						}
					  }
				}
				else
				{
				}
			}
			DragFinish(hDrop);
		}
		return 0;

	case WM_DESTROY:
		return 0;

	} /* switch (iMsg) */

	return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

/*****************************************************************/

bool XAP_Win32Frame::runModalContextMenu(AV_View * pView, const char * szMenuName,
											UT_sint32 x, UT_sint32 y)
{
	bool bResult = false;

	UT_ASSERT(!m_pWin32Popup);

	m_pWin32Popup = new EV_Win32MenuPopup(m_pWin32App,szMenuName,m_szMenuLabelSetName);
	if (m_pWin32Popup && m_pWin32Popup->synthesizeMenuPopup(this))
	{
		UT_DEBUGMSG(("ContextMenu: %s at [%d,%d]\n",szMenuName,x,y));

		translateDocumentToScreen(x,y);

		TrackPopupMenu(m_pWin32Popup->getMenuHandle(),
					   TPM_CENTERALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
					   x,y,0,m_hwndFrame,NULL);

		// the popup steals our capture, so we need to reset our counter.
		EV_Win32Mouse *pWin32Mouse = static_cast<EV_Win32Mouse *>(m_pMouse);
		pWin32Mouse->reset();
	}

	DELETEP(m_pWin32Popup);
	return bResult;
}

EV_Toolbar * XAP_Win32Frame::_newToolbar(XAP_App*		app,
										 XAP_Frame*		frame,
										 const char*	szLayout,
										 const char*	szLanguage)
{
	EV_Win32Toolbar *result = new EV_Win32Toolbar(static_cast<XAP_Win32App *>(app), 
												  static_cast<XAP_Win32Frame *>(frame), 
												  szLayout, szLanguage);
	// for now, position each one manually
	// TODO: put 'em all in a rebar instead
	HWND hwndBar = result->getWindow();
	
	RECT rcClient;
	GetClientRect(hwndBar, &rcClient);
	const UT_uint32 iHeight = rcClient.bottom - rcClient.top;
	
	m_iBarHeight += iHeight;

	return result;
}

EV_Menu* XAP_Win32Frame::getMainMenu()
{
	return m_pWin32Menu;
}

void XAP_Win32Frame::nullUpdate() const
{
	MSG msg;
	for( int i = 0 ; i < 10 ; i++ )
	{
		if( PeekMessage( &msg, (HWND) NULL, 0, 0, PM_REMOVE) )
		{
	        DispatchMessage(&msg); 
    	} 
	}
}

void XAP_Win32Frame::setCursor(GR_Graphics::Cursor c)
{
	// TODO - currently does nothing
}