Posted in S/W 및 H/W 이야기.

방준영씨가 Wine Project 에 기여한 부분.

 

 

NetBSD 개발자 방준영씨는 뭐하고 지낼까 ??

 

 

방준영씨가 NetBSD 에 기여한 소스코드를 몇 개 공개한다.

 

 

 

옛날에 IT 개발자로 일하는 방준영씨에 대해 포스팅을 2번이나 한 적이 있었다.

이번에도 역시 방준영씨에 대한 포스팅인데 이 분은 옛날에 Wine Project 에도 참여한 적이 있다.

이번 포스팅에서는 Wine Project 에 관여한 몇 가지 부분에 대해 공개를 하겠다.

 

 

 

1번. Wine Project 번역 부분.

 

 

 



1. 소개



Wine은 Microsoft Windows 프로그램(DOS, Windows 3.x, Win32 실행 파일

포함)을 유닉스 상에서 실행할 수 있게 해 주는 프로그램입니다.  Wine은

Microsoft Windows 바이너리를 로드하고 실행하는 프로그램 로더와

Unix와 X11 API를 써서 Windows API를 구현한 라이브러리(Winelib)로

이루어져 있습니다.  이 라이브러리는 Win32 코드를 유닉스 실행 파일로

이식하는 용도로도 사용할 수 있습니다.

Wine은 자유 소프트웨어이며 GNU LGPL 하에 배포됩니다.  자세한 내용은

LICENSE 파일을 참조하십시오.





2. 간편 설치 및 실행


소스를 받아 컴파일하는 경우 Wine Installer를 이용하는 것이 좋습니다.
 Wine 소스의 최상위 디렉토리에서 다음 명령을 실행합니다:



./tools/wineinstall




프로그램을 실행할 때는 "wine 프로그램명"처럼 합니다.  더 자세한 정보

및 문제 해결 방법은 이 파일의 나머지 부분과 Wine 맨페이지를 참조하십시오.

특히 http://www.winehq.org 사이트에 방대한 정보가 집약되어 있습니다.





3. 빌드에 필요한 조건


Wine을 컴파일하고 실행하려면 다음 중 하나가 필요합니다:



  리눅스 2.0.36 또는 그 이상

  FreeBSD 5.3 또는 그 이상

  Solaris x86 2.5 또는 그 이상

  NetBSD-current

  Mac OS X 10.5 또는 그 이상





Wine을 실행하려면 커널 차원의 스레드 지원이 필요하기 때문에 현재는 여기

나열한 운영 체제만 지원합니다.

커널 스레드를 지원하는 기타 다른 운영 체제는 향후 지원할 예정입니다.




리눅스 정보:


  리눅스 2.2.x에서는 잘 실행될 것이지만 리눅스 2.0.x에서는 실행될 수도

  있고 그렇지 않을 수도 있습니다(초기 2.0.x 버전에서는 스레드 관련 문제로

  프로그램이 죽음). 가급적 2.4.x와 2.6.x 같은 최근 커널을 사용하기

  바랍니다.




FreeBSD 정보:


  FreeBSD 4.x 및 5.x에서 빌드되지만 FreeBSD 5.3 이전 버전에선 제대로


  동작하지 않습니다.


  더 자세한 정보는   
  <ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/emulators/wine/>

  에 있는 FreeBSD ports 페이지를 참조하십시오.





Solaris 정보:



  거의 대부분의 경우 Wine을 빌드하려면 GNU 툴체인(gcc, gas 등)을 사용해야

  합니다.  경고: gas를 단순히 설치하기만 하면 gcc에서 사용되지 않을 수도

  있습니다.  gas를 설치한 다음 gcc를 재컴파일하거나 cc, as, ld 명령을

  GNU 바이너리로 심볼 링크를 걸어 주는 일이 필요하다고 합니다.




NetBSD 정보:


  USER_LDT, SYSVSHM, SYSVSEM, SYSVMSG 옵션이 커널에 포함되어 있어야 합니다.




Mac OS X 정보:



  x86 머신에서 빌드하려면 Xcode 2.4 또는 그 이상이 필요합니다.




지원되는 파일 시스템:



  Wine은 대부분의 파일 시스템에서 문제없이 실행됩니다.  그렇지만 /tmp

  디렉토리가 umsdos 타입으로 마운트된 경우에는 프로그램이 실행되지

  않습니다.  Samba를 이용해서 파일을 액세스하는 경우 몇 가지 호환성

  문제가 보고되기도 했습니다.  또한 NTFS는 현재 디스크 기록시 안정성이

  보장되어 있지 않습니다.  Windows 프로그램은 대부분 디스크 어딘가에

  데이터를 기록하므로 NTFS는 가급적 사용하지 않는 것이 좋습니다.  NTFS

  파일의 경우 쓰기 가능한 위치로 복사한 다음 사용하시기 바랍니다.



  
필수 구성 요소:


  X11 개발용 헤더 파일이 설치되어 있어야 합니다(데비안에서는 xlib6g-dev,

  레드햇에서는 XFree86-devel이란 패키지명으로 되어 있음).




필수 빌드 도구:



  x86 시스템에서는 gcc 2.7.2 이상이 필요합니다.

  2.7.2.3 이전 버전은 최적화 옵션을 켜고 컴파일하면 몇몇 파일에서 문제가

  발생할 수 있는데, 대부분 헤더 파일 관리와 관련된 문제로 인한 것들입니다.

  "make" 프로그램은 당연히 필수입니다(거의 대부분 GNU make 사용).


  flex 버전 2.5 이상과 bison이 필요합니다.




선택적 라이브러리:


  포함 여부의 선택이 가능한 라이브러리는 ./configure --verbose 명령으로


  확인할 수 있습니다.






4. 컴파일하기



wineinstall을 사용하지 않는 경우 다음 명령을 써서 Wine을 빌드합니다:



./configure

make depend

make





이렇게 하면 "wine" 프로그램과 다수의 라이브러리/바이너리가 빌드됩니다.

"wine" 프로그램은 Windows 실행 파일을 로드하고 실행하는 데 사용됩니다.

"libwine" 라이브러리("Winelib")는 Windows 소스 코드를 유닉스 상에서

컴파일하고 링크하는 용도로 사용할 수 있습니다.



컴파일 설정 옵션을 보려면 ./configure --help 명령을 사용합니다.


패치 파일을 써서 새 릴리스로 업그레이드하려면 먼저 릴리스의 최상위

디렉토리(README 파일이 들어 있는 디렉토리)로 갑니다.  그런 다음

"make clean" 명령을 실행하고 아래 명령으로 소스를 패치합니다:



    bunzip2 -c patch-file | patch -p1



"patch-file"은 패치 파일의 이름입니다(wine-0.9.x.diff.bz2 등의 형태).

패치한 다음에는 "./configure", "make depend && make"를 차례로

실행하면 됩니다.






5. 설정




Wine이 일단 정상적으로 빌드된 다음에는 "make install" 명령으로

실행 파일과 맨페이지, 기타 필요한 파일을 시스템에 설치할 수 있습니다.


설치하기 전 이전 버전의 Wine을 제거해야 한다는 점을 잊지 마시기

바랍니다.  "dpkg -r wine" 또는 "rpm -e wine" 또는 "make uninstall" 등의

명령으로 이전 버전을 제거할 수 있습니다.



설정 관련 도움말은 http://www.winehq.org/의 Support 페이지에 있습니다.


라이브러리 로딩 에러("Error while loading shared libraries: libntdll.so")가

발생하는 경우 라이브러리 경로가 /etc/ld.so.conf 파일 안에 지정되어 있는지

확인한 다음 root 권한으로 ldconfig를 실행하십시오.






6. 프로그램 실행




Wine을 실행할 때 실행 파일의 전체 경로를 지정할 수도 있고 파일명만 지정할

수도 있습니다.

예를 들어 카드놀이를 실행하려면:




	wine sol		   (파일을 찾기 위해 설정 파일에 지정된
	wine sol.exe		    검색 경로를 이용)

	wine c:\\windows\\sol.exe  (DOS 파일명 문법을 이용)

	wine /usr/windows/sol.exe  (유닉스 파일명 문법을 이용)

        wine sol.exe /parameter1 -parameter2 parameter3
				   (프로그램을 호출할 때 파라미터를 넘겨줌)




Wine은 아직 완성 단계에 도달하지 못했기 때문에 프로그램이 제대로 실행되지

않을 수 있습니다.  프로그램이 죽는 경우 문제를 분석하고 수정할 수 있도록

디버그 모드로 자동 진입됩니다.  자세한 디버깅 방법은 Wine 개발자 가이드의

디버깅 섹션을 참조하시기 바랍니다.





7. 더 자세한 정보



WWW:    여러 종류의 Wine 가이드, 애플리케이션 데이터베이스, 버그 추적 등 
        http://www.winehq.org/ 에 있는 WineHQ 사이트에 가면 많은 정보를 얻을
        수 있습니다.  정보를 찾을 때 대개 이 사이트가 출발점이 됩니다.



FAQ:    Wine FAQ은 http://www.winehq.org/FAQ 에 있습니다.



Usenet: Wine 관련 주제를 논의하거나 도움을 받으려면 comp.emulators.ms-windows.wine
        그룹을 이용하기 바랍니다.



Bugs:   http://bugs.winehq.org 의 Wine Bugzilla에 버그를 보고하십시오.
        버그 보고서를 제출하기 전 같은 문제가 이미 제출된 적이 있는지
        데이터베이스 검색을 통해 확인하기 바랍니다.  
        comp.emulators.ms-windows.wine 그룹에 버그 보고서를 올려도 됩니다.



IRC:	irc.freenode.net 의 #WineHQ 채널에서 도움을 받을 수 있습니다.



GIT:    Wine 개발 트리는 현재 GIT로 관리되고 있습니다.  더 자세한 정보는
	http://www.winehq.org/site/git 를 참조하기 바랍니다.



메일링 리스트:


        Wine 사용자와 개발자를 위해 메일링 리스트가 운영되고 있습니다.

        더 자세한 정보는 http://www.winehq.org/forums 를 참조하기 바랍니다.




위키:	Wine 위키는 http://wiki.winehq.org 에 있습니다.





추가할 내용이나 수정할 버그가 있다면 다음번 릴리스에 포함될 수 있도록

wine-patches@winehq.org 메일링 리스트로 패치('diff -u' 형식으로 된)를 보내

주시기 바랍니다.



--
Alexandre Julliard

julliard@winehq.org




Translated into Korean by

Bang Jun-Young

junyoung@mogua.com


 

 

 

2번. ole32: Include wine/port.h for snprintf().

 

 

 

Wine Project 부분에서 wine/port.h 부분에 patch 를 적용한 것이다. 원래의 소스코드는 다음과 같다.

 

 

 



/*
 *  OLE32 Initialization
 *
 * Copyright 2000 Huw D M Davies for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"
#include "wine/port.h"

#include <stdarg.h>
#include <stdio.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "objbase.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(ole);

#define HIMETRIC_INCHES 2540

/***********************************************************************
 *		OleMetafilePictFromIconAndLabel (OLE32.@)
 */
HGLOBAL WINAPI OleMetafilePictFromIconAndLabel(HICON hIcon, LPOLESTR lpszLabel,
                                               LPOLESTR lpszSourceFile, UINT iIconIndex)
{
	METAFILEPICT mfp;
	HDC hdc;
	HGLOBAL hmem = NULL;
	LPVOID mfdata;
	static const char szIconOnly[] = "IconOnly";
	SIZE text_size = { 0, 0 };
	INT width;
	INT icon_width;
	INT icon_height;
	INT label_offset;
	HDC hdcScreen;
	LOGFONTW lf;
	HFONT font;

	TRACE("%p %p %s %d\n", hIcon, lpszLabel, debugstr_w(lpszSourceFile), iIconIndex);

	if( !hIcon )
		return NULL;

	if (!SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0))
		return NULL;

	font = CreateFontIndirectW(&lf);
	if (!font)
		return NULL;

	hdc = CreateMetaFileW(NULL);
	if( !hdc )
	{
		DeleteObject(font);
		return NULL;
	}

	SelectObject(hdc, font);

	ExtEscape(hdc, MFCOMMENT, sizeof(szIconOnly), szIconOnly, 0, NULL);

	icon_width = GetSystemMetrics(SM_CXICON);
	icon_height = GetSystemMetrics(SM_CYICON);
	/* FIXME: should we give the label a bit of padding here? */
	label_offset = icon_height;
	if (lpszLabel)
	{
		HFONT screen_old_font;
		/* metafile DCs don't support GetTextExtentPoint32, so size the font
		 * using the desktop window DC */
		hdcScreen = GetDC(NULL);
		screen_old_font = SelectObject(hdcScreen, font);
		GetTextExtentPoint32W(hdcScreen, lpszLabel, lstrlenW(lpszLabel), &text_size);
		SelectObject(hdcScreen, screen_old_font);
		ReleaseDC(NULL, hdcScreen);

		width = 3 * icon_width;
	}
	else
		width = icon_width;

	SetMapMode(hdc, MM_ANISOTROPIC);
	SetWindowOrgEx(hdc, 0, 0, NULL);
	SetWindowExtEx(hdc, width, label_offset + text_size.cy, NULL);

	/* draw the icon centred */
	DrawIcon(hdc, (width-icon_width) / 2, 0, hIcon);
	if(lpszLabel)
		/* draw the label centred too, if provided */
		TextOutW(hdc, (width-text_size.cx) / 2, label_offset, lpszLabel, lstrlenW(lpszLabel));

	if (lpszSourceFile)
	{
		char szIconIndex[10];
		int path_length = WideCharToMultiByte(CP_ACP,0,lpszSourceFile,-1,NULL,0,NULL,NULL);
		if (path_length > 1)
		{
			char * szPath = CoTaskMemAlloc(path_length * sizeof(CHAR));
			if (szPath)
			{
				WideCharToMultiByte(CP_ACP,0,lpszSourceFile,-1,szPath,path_length,NULL,NULL);
				ExtEscape(hdc, MFCOMMENT, path_length, szPath, 0, NULL);
				CoTaskMemFree(szPath);
			}
		}
		snprintf(szIconIndex, 10, "%u", iIconIndex);
		ExtEscape(hdc, MFCOMMENT, strlen(szIconIndex)+1, szIconIndex, 0, NULL);
	}

	mfp.mm = MM_ANISOTROPIC;
	hdcScreen = GetDC(NULL);
	mfp.xExt = MulDiv(width, HIMETRIC_INCHES, GetDeviceCaps(hdcScreen, LOGPIXELSX));
	mfp.yExt = MulDiv(label_offset + text_size.cy, HIMETRIC_INCHES, GetDeviceCaps(hdcScreen, LOGPIXELSY));
	ReleaseDC(NULL, hdcScreen);
	mfp.hMF = CloseMetaFile(hdc);
	DeleteObject(font);
	if( !mfp.hMF )
		return NULL;

	hmem = GlobalAlloc( GMEM_MOVEABLE, sizeof(mfp) );
	if( !hmem )
	{
		DeleteMetaFile(mfp.hMF);
		return NULL;
	}

	mfdata = GlobalLock( hmem );
	if( !mfdata )
	{
		GlobalFree( hmem );
		DeleteMetaFile(mfp.hMF);
		return NULL;
	}

	memcpy(mfdata,&mfp,sizeof(mfp));
	GlobalUnlock( hmem );

	TRACE("returning %p\n",hmem);

	return hmem;
}


 

 

 

3번. user32: Fix EndMenu() to match the PSDK.

 

 

 



/*
 * Menu functions
 *
 * Copyright 1993 Martin Ayotte
 * Copyright 1994 Alexandre Julliard
 * Copyright 1997 Morten Welinder
 * Copyright 2005 Maxime Belleng? * Copyright 2006 Phil Krylov
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

/*
 * Note: the style MF_MOUSESELECT is used to mark popup items that
 * have been selected, i.e. their popup menu is currently displayed.
 * This is probably not the meaning this style has in MS-Windows.
 *
 * Note 2: where there is a difference, these menu API's are according
 * the behavior of Windows 2k and Windows XP. Known differences with
 * Windows 9x/ME are documented in the comments, in case an application
 * is found to depend on the old behavior.
 * 
 * TODO:
 *    implements styles :
 *        - MNS_AUTODISMISS
 *        - MNS_DRAGDROP
 *        - MNS_MODELESS
 */

#include "config.h"
#include "wine/port.h"

#include <stdarg.h>
#include <string.h>

#define OEMRESOURCE

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winnls.h"
#include "wine/winbase16.h"
#include "wine/winuser16.h"
#include "wownt32.h"
#include "wine/server.h"
#include "wine/unicode.h"
#include "win.h"
#include "controls.h"
#include "user_private.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(menu);
WINE_DECLARE_DEBUG_CHANNEL(accel);

/* internal popup menu window messages */

#define MM_SETMENUHANDLE	(WM_USER + 0)
#define MM_GETMENUHANDLE	(WM_USER + 1)

/* Menu item structure */
typedef struct {
    /* ----------- MENUITEMINFO Stuff ----------- */
    UINT fType;			/* Item type. */
    UINT fState;		/* Item state.  */
    UINT_PTR wID;		/* Item id.  */
    HMENU hSubMenu;		/* Pop-up menu.  */
    HBITMAP hCheckBit;		/* Bitmap when checked.  */
    HBITMAP hUnCheckBit;	/* Bitmap when unchecked.  */
    LPWSTR text;		/* Item text. */
    ULONG_PTR dwItemData;	/* Application defined.  */
    LPWSTR dwTypeData;		/* depends on fMask */
    HBITMAP hbmpItem;		/* bitmap */
    /* ----------- Wine stuff ----------- */
    RECT      rect;		/* Item area (relative to menu window) */
    UINT      xTab;		/* X position of text after Tab */
    SIZE   bmpsize;             /* size needed for the HBMMENU_CALLBACK
                                 * bitmap */ 
} MENUITEM;

/* Popup menu structure */
typedef struct {
    WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
    WORD        wMagic;       /* Magic number */
    WORD	Width;        /* Width of the whole menu */
    WORD	Height;       /* Height of the whole menu */
    UINT        nItems;       /* Number of items in the menu */
    HWND        hWnd;         /* Window containing the menu */
    MENUITEM    *items;       /* Array of menu items */
    UINT        FocusedItem;  /* Currently focused item */
    HWND	hwndOwner;    /* window receiving the messages for ownerdraw */
    BOOL        bTimeToHide;  /* Request hiding when receiving a second click in the top-level menu item */
    BOOL        bScrolling;   /* Scroll arrows are active */
    UINT        nScrollPos;   /* Current scroll position */
    UINT        nTotalHeight; /* Total height of menu items inside menu */
    /* ------------ MENUINFO members ------ */
    DWORD	dwStyle;	/* Extended menu style */
    UINT	cyMax;		/* max height of the whole menu, 0 is screen height */
    HBRUSH	hbrBack;	/* brush for menu background */
    DWORD	dwContextHelpID;
    DWORD	dwMenuData;	/* application defined value */
    HMENU       hSysMenuOwner;  /* Handle to the dummy sys menu holder */
    SIZE        maxBmpSize;     /* Maximum size of the bitmap items */
} POPUPMENU, *LPPOPUPMENU;

/* internal flags for menu tracking */

#define TF_ENDMENU              0x0001
#define TF_SUSPENDPOPUP         0x0002
#define TF_SKIPREMOVE		0x0004

typedef struct
{
    UINT	trackFlags;
    HMENU	hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
    HMENU	hTopMenu;     /* initial menu */
    HWND	hOwnerWnd;    /* where notifications are sent */
    POINT	pt;
} MTRACKER;

#define MENU_MAGIC   0x554d  /* 'MU' */

#define ITEM_PREV		-1
#define ITEM_NEXT		 1

  /* Internal MENU_TrackMenu() flags */
#define TPM_INTERNAL		0xF0000000
#define TPM_ENTERIDLEEX	 	0x80000000		/* set owner window for WM_ENTERIDLE */
#define TPM_BUTTONDOWN		0x40000000		/* menu was clicked before tracking */
#define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */

  /* Space between 2 columns */
#define MENU_COL_SPACE 4

  /*  top and bottom margins for popup menus */
#define MENU_TOP_MARGIN 3
#define MENU_BOTTOM_MARGIN 2

  /* (other menu->FocusedItem values give the position of the focused item) */
#define NO_SELECTED_ITEM  0xffff

#define MENU_ITEM_TYPE(flags) \
  ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))

/* macro to test that flags do not indicate bitmap, ownerdraw or separator */
#define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
#define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))

#define IS_SYSTEM_MENU(menu)  \
	(!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))

#define MENUITEMINFO_TYPE_MASK \
		(MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
		MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
		MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
#define TYPE_MASK  (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
#define STATE_MASK (~TYPE_MASK)
#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))

#define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)

static SIZE     menucharsize;
static UINT     ODitemheight; /* default owner drawn item height */      

/* Use global popup window because there's no way 2 menus can
 * be tracked at the same time.  */
static HWND top_popup;

  /* Flag set by EndMenu() to force an exit from menu tracking */
static BOOL fEndMenu = FALSE;

static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );

DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);

/*********************************************************************
 * menu class descriptor
 */
const struct builtin_class_descr MENU_builtin_class =
{
    (LPCWSTR)POPUPMENU_CLASS_ATOM,  /* name */
    CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,  /* style */
    NULL,                          /* procA (winproc is Unicode only) */
    PopupMenuWndProc,              /* procW */
    sizeof(HMENU),                 /* extra */
    IDC_ARROW,                     /* cursor */
    (HBRUSH)(COLOR_MENU+1)         /* brush */
};


/***********************************************************************
 *           debug_print_menuitem
 *
 * Print a menuitem in readable form.
 */

#define debug_print_menuitem(pre, mp, post) \
    do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)

#define MENUOUT(text) \
  TRACE("%s%s", (count++ ? "," : ""), (text))

#define MENUFLAG(bit,text) \
  do { \
    if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
  } while (0)

static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
				    const char *postfix)
{
    static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
    "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
    "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
    "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
    TRACE("%s ", prefix);
    if (mp) {
        UINT flags = mp->fType;
        TRACE( "{ ID=0x%lx", mp->wID);
        if ( mp->hSubMenu)
            TRACE( ", Sub=%p", mp->hSubMenu);
        if (flags) {
            int count = 0;
            TRACE( ", fType=");
            MENUFLAG( MFT_SEPARATOR, "sep");
            MENUFLAG( MFT_OWNERDRAW, "own");
            MENUFLAG( MFT_BITMAP, "bit");
            MENUFLAG(MF_POPUP, "pop");
            MENUFLAG(MFT_MENUBARBREAK, "barbrk");
            MENUFLAG(MFT_MENUBREAK, "brk");
            MENUFLAG(MFT_RADIOCHECK, "radio");
            MENUFLAG(MFT_RIGHTORDER, "rorder");
            MENUFLAG(MF_SYSMENU, "sys");
            MENUFLAG(MFT_RIGHTJUSTIFY, "right");  /* same as MF_HELP */
            if (flags)
                TRACE( "+0x%x", flags);
        }
        flags = mp->fState;
        if (flags) {
            int count = 0;
            TRACE( ", State=");
            MENUFLAG(MFS_GRAYED, "grey");
            MENUFLAG(MFS_DEFAULT, "default");
            MENUFLAG(MFS_DISABLED, "dis");
            MENUFLAG(MFS_CHECKED, "check");
            MENUFLAG(MFS_HILITE, "hi");
            MENUFLAG(MF_USECHECKBITMAPS, "usebit");
            MENUFLAG(MF_MOUSESELECT, "mouse");
            if (flags)
                TRACE( "+0x%x", flags);
        }
        if (mp->hCheckBit)
            TRACE( ", Chk=%p", mp->hCheckBit);
        if (mp->hUnCheckBit)
            TRACE( ", Unc=%p", mp->hUnCheckBit);
        if (mp->text)
            TRACE( ", Text=%s", debugstr_w(mp->text));
        if (mp->dwItemData)
            TRACE( ", ItemData=0x%08lx", mp->dwItemData);
        if (mp->hbmpItem)
        {
            if( IS_MAGIC_BITMAP(mp->hbmpItem))
                TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
            else
                TRACE( ", hbitmap=%p", mp->hbmpItem);
        }
        TRACE( " }");
    } else
        TRACE( "NULL");
    TRACE(" %s\n", postfix);
}

#undef MENUOUT
#undef MENUFLAG


/***********************************************************************
 *           MENU_GetMenu
 *
 * Validate the given menu handle and returns the menu structure pointer.
 */
static POPUPMENU *MENU_GetMenu(HMENU hMenu)
{
    POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
    if (!menu || menu->wMagic != MENU_MAGIC)
    {
        WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
        menu = NULL;
    }
    return menu;
}

/***********************************************************************
 *           get_win_sys_menu
 *
 * Get the system menu of a window
 */
static HMENU get_win_sys_menu( HWND hwnd )
{
    HMENU ret = 0;
    WND *win = WIN_GetPtr( hwnd );
    if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
    {
        ret = win->hSysMenu;
        WIN_ReleasePtr( win );
    }
    return ret;
}

/***********************************************************************
 *           get_menu_font
 */
static HFONT get_menu_font( BOOL bold )
{
    static HFONT hMenuFont, hMenuFontBold;

    HFONT ret = bold ? hMenuFontBold : hMenuFont;

    if (!ret)
    {
        NONCLIENTMETRICSW ncm;
        HFONT prev;

        ncm.cbSize = sizeof(NONCLIENTMETRICSW);
        SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);

        if (bold)
        {
            ncm.lfMenuFont.lfWeight += 300;
            if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
        }
        if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
        prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
                                                  ret, NULL );
        if (prev)
        {
            /* another thread beat us to it */
            DeleteObject( ret );
            ret = prev;
        }
    }
    return ret;
}

/***********************************************************************
 *           get_arrow_bitmap
 */
static HBITMAP get_arrow_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
    return arrow_bitmap;
}

/***********************************************************************
 *           get_down_arrow_bitmap
 */
static HBITMAP get_down_arrow_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
    return arrow_bitmap;
}

/***********************************************************************
 *           get_down_arrow_inactive_bitmap
 */
static HBITMAP get_down_arrow_inactive_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
    return arrow_bitmap;
}

/***********************************************************************
 *           get_up_arrow_bitmap
 */
static HBITMAP get_up_arrow_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
    return arrow_bitmap;
}

/***********************************************************************
 *           get_up_arrow_inactive_bitmap
 */
static HBITMAP get_up_arrow_inactive_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
    return arrow_bitmap;
}

/***********************************************************************
 *           MENU_CopySysPopup
 *
 * Return the default system menu.
 */
static HMENU MENU_CopySysPopup(void)
{
    static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
    HMENU hMenu = LoadMenuW(user32_module, sysmenuW);

    if( hMenu ) {
        POPUPMENU* menu = MENU_GetMenu(hMenu);
        menu->wFlags |= MF_SYSMENU | MF_POPUP;
	SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
    }
    else
	ERR("Unable to load default system menu\n" );

    TRACE("returning %p.\n", hMenu );

    return hMenu;
}


/**********************************************************************
 *           MENU_GetSysMenu
 *
 * Create a copy of the system menu. System menu in Windows is
 * a special menu bar with the single entry - system menu popup.
 * This popup is presented to the outside world as a "system menu".
 * However, the real system menu handle is sometimes seen in the
 * WM_MENUSELECT parameters (and Word 6 likes it this way).
 */
static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
{
    HMENU hMenu;

    TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
    if ((hMenu = CreateMenu()))
    {
	POPUPMENU *menu = MENU_GetMenu(hMenu);
	menu->wFlags = MF_SYSMENU;
	menu->hWnd = WIN_GetFullHandle( hWnd );
	TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);

	if (!hPopupMenu)
	    hPopupMenu = MENU_CopySysPopup();

	if (hPopupMenu)
	{
            if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
                DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);

	    InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
                         (UINT_PTR)hPopupMenu, NULL );

            menu->items[0].fType = MF_SYSMENU | MF_POPUP;
            menu->items[0].fState = 0;
            if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;

	    TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
	    return hMenu;
	}
	DestroyMenu( hMenu );
    }
    ERR("failed to load system menu!\n");
    return 0;
}


/***********************************************************************
 *           MENU_InitSysMenuPopup
 *
 * Grey the appropriate items in System menu.
 */
static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
{
    BOOL gray;

    gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
    EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
    gray = ((style & WS_MAXIMIZE) != 0);
    EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
    gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
    EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
    gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
    EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
    gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
    EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
    gray = (clsStyle & CS_NOCLOSE) != 0;

    /* The menu item must keep its state if it's disabled */
    if(gray)
	EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
}


/******************************************************************************
 *
 *   UINT  MENU_GetStartOfNextColumn(
 *     HMENU  hMenu )
 *
 *****************************************************************************/

static UINT  MENU_GetStartOfNextColumn(
    HMENU  hMenu )
{
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT i;

    if(!menu)
	return NO_SELECTED_ITEM;

    i = menu->FocusedItem + 1;
    if( i == NO_SELECTED_ITEM )
	return i;

    for( ; i < menu->nItems; ++i ) {
	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
	    return i;
    }

    return NO_SELECTED_ITEM;
}


/******************************************************************************
 *
 *   UINT  MENU_GetStartOfPrevColumn(
 *     HMENU  hMenu )
 *
 *****************************************************************************/

static UINT  MENU_GetStartOfPrevColumn(
    HMENU  hMenu )
{
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT  i;

    if( !menu )
	return NO_SELECTED_ITEM;

    if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
	return NO_SELECTED_ITEM;

    /* Find the start of the column */

    for(i = menu->FocusedItem; i != 0 &&
	 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
	--i); /* empty */

    if(i == 0)
	return NO_SELECTED_ITEM;

    for(--i; i != 0; --i) {
	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
	    break;
    }

    TRACE("ret %d.\n", i );

    return i;
}



/***********************************************************************
 *           MENU_FindItem
 *
 * Find a menu item. Return a pointer on the item, and modifies *hmenu
 * in case the item was in a sub-menu.
 */
static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
{
    POPUPMENU *menu;
    MENUITEM *fallback = NULL;
    UINT fallback_pos = 0;
    UINT i;

    if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
    if (wFlags & MF_BYPOSITION)
    {
	if (*nPos >= menu->nItems) return NULL;
	return &menu->items[*nPos];
    }
    else
    {
        MENUITEM *item = menu->items;
	for (i = 0; i < menu->nItems; i++, item++)
	{
	    if (item->fType & MF_POPUP)
	    {
		HMENU hsubmenu = item->hSubMenu;
		MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
		if (subitem)
		{
		    *hmenu = hsubmenu;
		    return subitem;
		}
		else if (item->wID == *nPos)
		{
		    /* fallback to this item if nothing else found */
		    fallback_pos = i;
		    fallback = item;
		}
	    }
	    else if (item->wID == *nPos)
	    {
		*nPos = i;
		return item;
	    }
	}
    }

    if (fallback)
        *nPos = fallback_pos;

    return fallback;
}

/***********************************************************************
 *           MENU_FindSubMenu
 *
 * Find a Sub menu. Return the position of the submenu, and modifies
 * *hmenu in case it is found in another sub-menu.
 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
 */
UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
{
    POPUPMENU *menu;
    UINT i;
    MENUITEM *item;
    if (((*hmenu)==(HMENU)0xffff) ||
            (!(menu = MENU_GetMenu(*hmenu))))
        return NO_SELECTED_ITEM;
    item = menu->items;
    for (i = 0; i < menu->nItems; i++, item++) {
        if(!(item->fType & MF_POPUP)) continue;
        if (item->hSubMenu == hSubTarget) {
            return i;
        }
        else  {
            HMENU hsubmenu = item->hSubMenu;
            UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
            if (pos != NO_SELECTED_ITEM) {
                *hmenu = hsubmenu;
                return pos;
            }
        }
    }
    return NO_SELECTED_ITEM;
}

/***********************************************************************
 *           MENU_FreeItemData
 */
static void MENU_FreeItemData( MENUITEM* item )
{
    /* delete text */
    HeapFree( GetProcessHeap(), 0, item->text );
}

/***********************************************************************
 *           MENU_AdjustMenuItemRect
 *
 * Adjust menu item rectangle according to scrolling state.
 */
static void
MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
{
    if (menu->bScrolling)
    {
        UINT arrow_bitmap_width, arrow_bitmap_height;
        BITMAP bmp;

        GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
        arrow_bitmap_width = bmp.bmWidth;
        arrow_bitmap_height = bmp.bmHeight;
        rect->top += arrow_bitmap_height - menu->nScrollPos;
        rect->bottom += arrow_bitmap_height - menu->nScrollPos;
    }
}


/***********************************************************************
 *           MENU_FindItemByCoords
 *
 * Find the item at the specified coordinates (screen coords). Does
 * not work for child windows and therefore should not be called for
 * an arbitrary system menu.
 */
static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
					POINT pt, UINT *pos )
{
    MENUITEM *item;
    UINT i;
    RECT rect;

    if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
    pt.x -= rect.left;
    pt.y -= rect.top;
    item = menu->items;
    for (i = 0; i < menu->nItems; i++, item++)
    {
        rect = item->rect;
        MENU_AdjustMenuItemRect(menu, &rect);
	if (PtInRect(&rect, pt))
	{
	    if (pos) *pos = i;
	    return item;
	}
    }
    return NULL;
}


/***********************************************************************
 *           MENU_FindItemByKey
 *
 * Find the menu item selected by a key press.
 * Return item id, -1 if none, -2 if we should close the menu.
 */
static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
                                WCHAR key, BOOL forceMenuChar )
{
    TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );

    if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);

    if (hmenu)
    {
	POPUPMENU *menu = MENU_GetMenu( hmenu );
	MENUITEM *item = menu->items;
	LRESULT menuchar;

	if( !forceMenuChar )
	{
	     UINT i;

	     for (i = 0; i < menu->nItems; i++, item++)
	     {
		if( item->text)
		{
		    WCHAR *p = item->text - 2;
		    do
		    {
		    	p = strchrW (p + 2, '&');
		    }
		    while (p != NULL && p [1] == '&');
		    if (p && (toupperW(p[1]) == toupperW(key))) return i;
		}
	     }
	}
	menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
	if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
	if (HIWORD(menuchar) == 1) return (UINT)(-2);
    }
    return (UINT)(-1);
}


/***********************************************************************
 *           MENU_GetBitmapItemSize
 *
 * Get the size of a bitmap item.
 */
static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
                                    HWND hwndOwner)
{
    BITMAP bm;
    HBITMAP bmp = lpitem->hbmpItem;

    size->cx = size->cy = 0;

    /* check if there is a magic menu item associated with this item */
    switch( (INT_PTR) bmp )
    {
    case (INT_PTR)HBMMENU_CALLBACK:
        {
            MEASUREITEMSTRUCT measItem;
            measItem.CtlType = ODT_MENU;
            measItem.CtlID = 0;
            measItem.itemID = lpitem->wID;
            measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
            measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
            measItem.itemData = lpitem->dwItemData;
            SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
            size->cx = measItem.itemWidth;
            size->cy = measItem.itemHeight;
            return;
        }
        break;
    case (INT_PTR)HBMMENU_SYSTEM:
        if (lpitem->dwItemData)
        {
            bmp = (HBITMAP)lpitem->dwItemData;
            break;
        }
        /* fall through */
    case (INT_PTR)HBMMENU_MBAR_RESTORE:
    case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
    case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
    case (INT_PTR)HBMMENU_MBAR_CLOSE:
    case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
        size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
        size->cy = size->cx;
        return;
    case (INT_PTR)HBMMENU_POPUP_CLOSE:
    case (INT_PTR)HBMMENU_POPUP_RESTORE:
    case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
    case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
        FIXME("Magic %p not implemented\n", bmp );
        return;
    }
    if (GetObjectW(bmp, sizeof(bm), &bm ))
    {
        size->cx = bm.bmWidth;
        size->cy = bm.bmHeight;
    }
}

/***********************************************************************
 *           MENU_DrawBitmapItem
 *
 * Draw a bitmap item.
 */
static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
                    HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
{
    BITMAP bm;
    DWORD rop;
    HDC hdcMem;
    HBITMAP bmp;
    int w = rect->right - rect->left;
    int h = rect->bottom - rect->top;
    int bmp_xoffset = 0;
    int left, top;
    HBITMAP hbmToDraw = lpitem->hbmpItem;
    bmp = hbmToDraw;

    /* Check if there is a magic menu item associated with this item */
    if (IS_MAGIC_BITMAP(hbmToDraw))
    {
        UINT flags = 0;
        RECT r;

	switch((INT_PTR)hbmToDraw)
        {
        case (INT_PTR)HBMMENU_SYSTEM:
            if (lpitem->dwItemData)
            {
                bmp = (HBITMAP)lpitem->dwItemData;
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
            }
            else
            {
                static HBITMAP hBmpSysMenu;

                if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
                bmp = hBmpSysMenu;
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
                /* only use right half of the bitmap */
                bmp_xoffset = bm.bmWidth / 2;
                bm.bmWidth -= bmp_xoffset;
            }
            goto got_bitmap;
        case (INT_PTR)HBMMENU_MBAR_RESTORE:
            flags = DFCS_CAPTIONRESTORE;
            break;
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
            flags = DFCS_CAPTIONMIN;
            break;
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
            flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
            break;
        case (INT_PTR)HBMMENU_MBAR_CLOSE:
            flags = DFCS_CAPTIONCLOSE;
            break;
        case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
            flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
            break;
        case (INT_PTR)HBMMENU_CALLBACK:
            {
                DRAWITEMSTRUCT drawItem;
                drawItem.CtlType = ODT_MENU;
                drawItem.CtlID = 0;
                drawItem.itemID = lpitem->wID;
                drawItem.itemAction = odaction;
                drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
                drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
                drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
                drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
                drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
                drawItem.hwndItem = (HWND)hmenu;
                drawItem.hDC = hdc;
                drawItem.itemData = lpitem->dwItemData;
                drawItem.rcItem = *rect;
                SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
                return;
            }
            break;
        case (INT_PTR)HBMMENU_POPUP_CLOSE:
        case (INT_PTR)HBMMENU_POPUP_RESTORE:
        case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
        case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
        default:
            FIXME("Magic %p not implemented\n", hbmToDraw);
            return;
        }
        r = *rect;
        InflateRect( &r, -1, -1 );
        if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
        DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
        return;
    }

    if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;

 got_bitmap:
    hdcMem = CreateCompatibleDC( hdc );
    SelectObject( hdcMem, bmp );

    /* handle fontsize > bitmap_height */
    top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
    left=rect->left;
    rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
    if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
        SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
    BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
    DeleteDC( hdcMem );
}


/***********************************************************************
 *           MENU_CalcItemSize
 *
 * Calculate the size of the menu item and store it in lpitem->rect.
 */
static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
			       INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
{
    WCHAR *p;
    UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
    UINT arrow_bitmap_width;
    BITMAP bm;
    INT itemheight;

    TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
    debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
			 (menuBar ? " (MenuBar)" : ""));

    GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
    arrow_bitmap_width = bm.bmWidth;

    /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
    if( !menucharsize.cx ) {
        menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
        /* Win95/98/ME will use menucharsize.cy here. Testing is possible
         * but it is unlikely an application will depend on that */
        ODitemheight = HIWORD( GetDialogBaseUnits());
    }

    SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );

    if (lpitem->fType & MF_OWNERDRAW)
    {
        MEASUREITEMSTRUCT mis;
        mis.CtlType    = ODT_MENU;
        mis.CtlID      = 0;
        mis.itemID     = lpitem->wID;
        mis.itemData   = lpitem->dwItemData;
        mis.itemHeight = ODitemheight;
        mis.itemWidth  = 0;
        SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
        /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
         * width of a menufont character to the width of an owner-drawn menu. 
         */
        lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
        if (menuBar) {
            /* under at least win95 you seem to be given a standard
               height for the menu and the height value is ignored */
            lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
        } else
            lpitem->rect.bottom += mis.itemHeight;

        TRACE("id=%04lx size=%dx%d\n",
                lpitem->wID, lpitem->rect.right-lpitem->rect.left,
                lpitem->rect.bottom-lpitem->rect.top);
        return;
    }

    if (lpitem->fType & MF_SEPARATOR)
    {
        lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
        if( !menuBar)
            lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
        return;
    }

    itemheight = 0;
    lpitem->xTab = 0;

    if (!menuBar) {
        if (lpitem->hbmpItem) {
            SIZE size;

            MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
            /* Keep the size of the bitmap in callback mode to be able
             * to draw it correctly */
            lpitem->bmpsize = size;
            lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
            lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
            lpitem->rect.right += size.cx + 2;
            itemheight = size.cy + 2;
        }
        if( !(lppop->dwStyle & MNS_NOCHECK))
            lpitem->rect.right += check_bitmap_width; 
        lpitem->rect.right += 4 + menucharsize.cx;
        lpitem->xTab = lpitem->rect.right;
        lpitem->rect.right += arrow_bitmap_width;
    } else if (lpitem->hbmpItem) { /* menuBar */
        SIZE size;

        MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
        lpitem->bmpsize = size;
        lpitem->rect.right  += size.cx;
        if( lpitem->text) lpitem->rect.right  += 2;
        itemheight = size.cy;
    }

    /* it must be a text item - unless it's the system menu */
    if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
        HFONT hfontOld = NULL;
        RECT rc = lpitem->rect;
        LONG txtheight, txtwidth;

	if ( lpitem->fState & MFS_DEFAULT ) {
	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
	}
        if (menuBar) {
            txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
                    DT_SINGLELINE|DT_CALCRECT); 
            lpitem->rect.right  += rc.right - rc.left;
            itemheight = max( max( itemheight, txtheight),
                    GetSystemMetrics( SM_CYMENU) - 1);
            lpitem->rect.right +=  2 * menucharsize.cx;
        } else {
            if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
                RECT tmprc = rc;
                LONG tmpheight;
                int n = (int)( p - lpitem->text);
                /* Item contains a tab (only meaningful in popup menus) */
                /* get text size before the tab */
                txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
                        DT_SINGLELINE|DT_CALCRECT);
                txtwidth = rc.right - rc.left;
                p += 1; /* advance past the Tab */
                /* get text size after the tab */
                tmpheight = DrawTextW( hdc, p, -1, &tmprc,
                        DT_SINGLELINE|DT_CALCRECT);
                lpitem->xTab += txtwidth;
                txtheight = max( txtheight, tmpheight);
                txtwidth += menucharsize.cx + /* space for the tab */
                    tmprc.right - tmprc.left; /* space for the short cut */
            } else {
                txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
                        DT_SINGLELINE|DT_CALCRECT);
                txtwidth = rc.right - rc.left;
                lpitem->xTab += txtwidth;
            }
            lpitem->rect.right  += 2 + txtwidth;
            itemheight = max( itemheight,
                    max( txtheight + 2, menucharsize.cy + 4));
        }
	if (hfontOld) SelectObject (hdc, hfontOld);
    } else if( menuBar) {
        itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
    }
    lpitem->rect.bottom += itemheight;
    TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
}


/***********************************************************************
 *           MENU_GetMaxPopupHeight
 */
static UINT
MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
{
    if (lppop->cyMax)
        return lppop->cyMax;
    return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
}


/***********************************************************************
 *           MENU_PopupMenuCalcSize
 *
 * Calculate the size of a popup menu.
 */
static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
{
    MENUITEM *lpitem;
    HDC hdc;
    int start, i;
    int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;

    lppop->Width = lppop->Height = 0;
    if (lppop->nItems == 0) return;
    hdc = GetDC( 0 );

    SelectObject( hdc, get_menu_font(FALSE));

    start = 0;
    maxX = 2 + 1;

    lppop->maxBmpSize.cx = 0;
    lppop->maxBmpSize.cy = 0;

    while (start < lppop->nItems)
    {
	lpitem = &lppop->items[start];
	orgX = maxX;
        if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
            orgX += MENU_COL_SPACE; 
	orgY = MENU_TOP_MARGIN;

	maxTab = maxTabWidth = 0;
	  /* Parse items until column break or end of menu */
	for (i = start; i < lppop->nItems; i++, lpitem++)
	{
	    if ((i != start) &&
		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;

	    MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
	    maxX = max( maxX, lpitem->rect.right );
	    orgY = lpitem->rect.bottom;
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
	    {
		maxTab = max( maxTab, lpitem->xTab );
		maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
	    }
	}

	  /* Finish the column (set all items to the largest width found) */
	maxX = max( maxX, maxTab + maxTabWidth );
	for (lpitem = &lppop->items[start]; start < i; start++, lpitem++) { lpitem->rect.right = maxX;
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
		lpitem->xTab = maxTab;

	}
	lppop->Height = max( lppop->Height, orgY );
    }

    lppop->Width  = maxX;

    /* space for 3d border */
    lppop->Height += MENU_BOTTOM_MARGIN;
    lppop->Width += 2;

    /* Adjust popup height if it exceeds maximum */
    maxHeight = MENU_GetMaxPopupHeight(lppop);
    lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
    if (lppop->Height >= maxHeight)
    {
        lppop->Height = maxHeight;
        lppop->bScrolling = TRUE;
    }
    else
    {
        lppop->bScrolling = FALSE;
    }

    ReleaseDC( 0, hdc );
}


/***********************************************************************
 *           MENU_MenuBarCalcSize
 *
 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
 * height is off by 1 pixel which causes lengthy window relocations when
 * active document window is maximized/restored.
 *
 * Calculate the size of the menu bar.
 */
static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
                                  LPPOPUPMENU lppop, HWND hwndOwner )
{
    MENUITEM *lpitem;
    int start, i, orgX, orgY, maxY, helpPos;

    if ((lprect == NULL) || (lppop == NULL)) return;
    if (lppop->nItems == 0) return;
    TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
    lppop->Width  = lprect->right - lprect->left;
    lppop->Height = 0;
    maxY = lprect->top+1;
    start = 0;
    helpPos = -1;
    lppop->maxBmpSize.cx = 0;
    lppop->maxBmpSize.cy = 0;
    while (start < lppop->nItems)
    {
	lpitem = &lppop->items[start];
	orgX = lprect->left;
	orgY = maxY;

	  /* Parse items until line break or end of menu */
	for (i = start; i < lppop->nItems; i++, lpitem++)
	{
	    if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
	    if ((i != start) &&
		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;

	    TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
	    debug_print_menuitem ("  item: ", lpitem, "");
	    MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );

	    if (lpitem->rect.right > lprect->right)
	    {
		if (i != start) break;
		else lpitem->rect.right = lprect->right;
	    }
	    maxY = max( maxY, lpitem->rect.bottom );
	    orgX = lpitem->rect.right;
	}

	  /* Finish the line (set all items to the largest height found) */
	while (start < i) lppop->items[start++].rect.bottom = maxY;
    }

    lprect->bottom = maxY;
    lppop->Height = lprect->bottom - lprect->top;

    /* Flush right all items between the MF_RIGHTJUSTIFY and */
    /* the last item (if several lines, only move the last line) */
    lpitem = &lppop->items[lppop->nItems-1];
    orgY = lpitem->rect.top;
    orgX = lprect->right;
    for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
        if ( (helpPos==-1) || (helpPos>i) )
            break;				/* done */
        if (lpitem->rect.top != orgY) break;	/* Other line */
        if (lpitem->rect.right >= orgX) break;	/* Too far right already */
        lpitem->rect.left += orgX - lpitem->rect.right;
        lpitem->rect.right = orgX;
        orgX = lpitem->rect.left;
    }
}


/***********************************************************************
 *           MENU_DrawScrollArrows
 *
 * Draw scroll arrows.
 */
static void
MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
{
    HDC hdcMem = CreateCompatibleDC(hdc);
    HBITMAP hOrigBitmap;
    UINT arrow_bitmap_width, arrow_bitmap_height;
    BITMAP bmp;
    RECT rect;

    GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
    arrow_bitmap_width = bmp.bmWidth;
    arrow_bitmap_height = bmp.bmHeight;

    
    if (lppop->nScrollPos)
        hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
    else
        hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
    rect.left = 0;
    rect.top = 0;
    rect.right = lppop->Width;
    rect.bottom = arrow_bitmap_height;
    FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
    BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
           arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
    rect.top = lppop->Height - arrow_bitmap_height;
    rect.bottom = lppop->Height;
    FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
    if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
        SelectObject(hdcMem, get_down_arrow_bitmap());
    else
        SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
    BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
           lppop->Height - arrow_bitmap_height,
           arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
    SelectObject(hdcMem, hOrigBitmap);
    DeleteDC(hdcMem);
}


/***********************************************************************
 *           draw_popup_arrow
 *
 * Draws the popup-menu arrow.
 */
static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
        UINT arrow_bitmap_height)
{
    HDC hdcMem = CreateCompatibleDC( hdc );
    HBITMAP hOrigBitmap;

    hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
    BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
            (rect.top + rect.bottom - arrow_bitmap_height) / 2,
            arrow_bitmap_width, arrow_bitmap_height,
            hdcMem, 0, 0, SRCCOPY );
    SelectObject( hdcMem, hOrigBitmap );
    DeleteDC( hdcMem );
}
/***********************************************************************
 *           MENU_DrawMenuItem
 *
 * Draw a single menu item.
 */
static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
			       UINT height, BOOL menuBar, UINT odaction )
{
    RECT rect;
    BOOL flat_menu = FALSE;
    int bkgnd;
    UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
    POPUPMENU *menu = MENU_GetMenu(hmenu);
    RECT bmprc;

    debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");

    if (!menuBar) {
        BITMAP bmp;
        GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
        arrow_bitmap_width = bmp.bmWidth;
        arrow_bitmap_height = bmp.bmHeight;
    }

    if (lpitem->fType & MF_SYSMENU)
    {
	if( !IsIconic(hwnd) )
	    NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
	return;
    }

    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
    bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
  
      /* Setup colors */

    if (lpitem->fState & MF_HILITE)
    {
        if(menuBar && !flat_menu) {
	    SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
            SetBkColor(hdc, GetSysColor(COLOR_MENU));
	} else {
	    if(lpitem->fState & MF_GRAYED)
		SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
	    else
		SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
            SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
	}
    }
    else
    {
	if (lpitem->fState & MF_GRAYED)
	    SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
	else
	    SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
	SetBkColor( hdc, GetSysColor( bkgnd ) );
    }

    TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
    rect = lpitem->rect;
    MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);

    if (lpitem->fType & MF_OWNERDRAW)
    {
        /*
        ** Experimentation under Windows reveals that an owner-drawn
        ** menu is given the rectangle which includes the space it requested
        ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
        ** and a popup-menu arrow.  This is the value of lpitem->rect.
        ** Windows will leave all drawing to the application except for
        ** the popup-menu arrow.  Windows always draws that itself, after
        ** the menu owner has finished drawing.
        */
        DRAWITEMSTRUCT dis;

        dis.CtlType   = ODT_MENU;
	dis.CtlID     = 0;
        dis.itemID    = lpitem->wID;
        dis.itemData  = lpitem->dwItemData;
        dis.itemState = 0;
        if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
        if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
        if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
        dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
        dis.hwndItem   = (HWND)hmenu;
        dis.hDC        = hdc;
        dis.rcItem     = rect;
        TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
	      "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
	      dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
	      dis.hDC, wine_dbgstr_rect( &dis.rcItem));
        SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
        /* Draw the popup-menu arrow */
        if (lpitem->fType & MF_POPUP)
            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
                    arrow_bitmap_height);
        return;
    }

    if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;

    if (lpitem->fState & MF_HILITE)
    {
        if (flat_menu)
        {
            InflateRect (&rect, -1, -1);
            FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
            InflateRect (&rect, 1, 1);
            FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
        }
        else
        {
            if(menuBar)
                DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
            else
                FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
        }
    }
    else
        FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );

    SetBkMode( hdc, TRANSPARENT );

    /* vertical separator */
    if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
    {
        HPEN oldPen;
        RECT rc = rect;

        rc.left -= MENU_COL_SPACE / 2 + 1;
        rc.top = 3;
        rc.bottom = height - 3;
        if (flat_menu)
        {
            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
            MoveToEx( hdc, rc.left, rc.top, NULL );
            LineTo( hdc, rc.left, rc.bottom );
            SelectObject( hdc, oldPen );
        }
        else
            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
    }

    /* horizontal separator */
    if (lpitem->fType & MF_SEPARATOR)
    {
        HPEN oldPen;
        RECT rc = rect;

        rc.left++;
        rc.right--;
        rc.top = ( rc.top + rc.bottom) / 2;
        if (flat_menu)
        {
            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
            MoveToEx( hdc, rc.left, rc.top, NULL );
            LineTo( hdc, rc.right, rc.top );
            SelectObject( hdc, oldPen );
        }
        else
            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
        return;
    }

	/* helper lines for debugging */
/*	FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
	SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
	MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
	LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
*/

    if (lpitem->hbmpItem) {
        /* calculate the bitmap rectangle in coordinates relative
         * to the item rectangle */
        if( menuBar) {
            if( lpitem->hbmpItem == HBMMENU_CALLBACK)
                bmprc.left = 3;
            else 
                bmprc.left = lpitem->text ? menucharsize.cx : 0;          
        } else {
            bmprc.left = 4;
            if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
                bmprc.left += GetSystemMetrics( SM_CXMENUCHECK); 
        }
        bmprc.right =  bmprc.left + lpitem->bmpsize.cx;
        if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
            bmprc.top = 0;
        else
            bmprc.top = (rect.bottom - rect.top -
                    lpitem->bmpsize.cy) / 2; 
        bmprc.bottom =  bmprc.top + lpitem->bmpsize.cy;
    }

    if (!menuBar)
    {
        HBITMAP bm;
        INT y = rect.top + rect.bottom;
        RECT rc = rect;
        int checked = FALSE;
        UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
        UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
        /* Draw the check mark
         *
         * FIXME:
         * Custom checkmark bitmaps are monochrome but not always 1bpp.
         */
        if( !(menu->dwStyle & MNS_NOCHECK)) {
            bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
                lpitem->hUnCheckBit;
            if (bm)  /* we have a custom bitmap */
            {
                HDC hdcMem = CreateCompatibleDC( hdc );

                SelectObject( hdcMem, bm );
                BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
                        check_bitmap_width, check_bitmap_height,
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
                checked = TRUE;
            }
            else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
            {
                RECT r;
                HBITMAP bm = CreateBitmap( check_bitmap_width,
                        check_bitmap_height, 1, 1, NULL );
                HDC hdcMem = CreateCompatibleDC( hdc );

                SelectObject( hdcMem, bm );
                SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
                DrawFrameControl( hdcMem, &r, DFC_MENU,
                        (lpitem->fType & MFT_RADIOCHECK) ?
                        DFCS_MENUBULLET : DFCS_MENUCHECK );
                BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
                DeleteObject( bm );
                checked = TRUE;
            }
        }
        if( lpitem->hbmpItem &&
                !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
            POINT origorg;
            /* some applications make this assumption on the DC's origin */
            SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
            MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
                    odaction, FALSE);
            SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
        }
	/* Draw the popup-menu arrow */
        if (lpitem->fType & MF_POPUP)
            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
                    arrow_bitmap_height);
	rect.left += 4;
        if( !(menu->dwStyle & MNS_NOCHECK))
            rect.left += check_bitmap_width;
	rect.right -= arrow_bitmap_width;
    }
    else if( lpitem->hbmpItem)
    {   /* Draw the bitmap */
        POINT origorg;
        
        SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
        MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
                odaction, menuBar);
        SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
    }
    /* process text if present */
    if (lpitem->text)
    {
	register int i;
	HFONT hfontOld = 0;

	UINT uFormat = (menuBar) ?
			DT_CENTER | DT_VCENTER | DT_SINGLELINE :
			DT_LEFT | DT_VCENTER | DT_SINGLELINE;

        if( !(menu->dwStyle & MNS_CHECKORBMP))
            rect.left += menu->maxBmpSize.cx;

	if ( lpitem->fState & MFS_DEFAULT )
	{
	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
	}

	if (menuBar) {
            if( lpitem->hbmpItem)
                rect.left += lpitem->bmpsize.cx;
            if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
                rect.left += menucharsize.cx;
            rect.right -= menucharsize.cx;
	}

	for (i = 0; lpitem->text[i]; i++)
	    if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
	        break;

	if(lpitem->fState & MF_GRAYED)
	{
	    if (!(lpitem->fState & MF_HILITE) )
	    {
		++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
		SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
		DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
		--rect.left; --rect.top; --rect.right; --rect.bottom;
	    }
	    SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
	}

	DrawTextW( hdc, lpitem->text, i, &rect, uFormat);

	/* paint the shortcut text */
	if (!menuBar && lpitem->text[i])  /* There's a tab or flush-right char */
	{
	    if (lpitem->text[i] == '\t')
	    {
		rect.left = lpitem->xTab;
		uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
	    }
	    else
	    {
		rect.right = lpitem->xTab;
		uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
	    }

	    if(lpitem->fState & MF_GRAYED)
	    {
		if (!(lpitem->fState & MF_HILITE) )
		{
		    ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
		    SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
		    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
		    --rect.left; --rect.top; --rect.right; --rect.bottom;
		}
		SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
	    }
	    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
	}

	if (hfontOld)
	    SelectObject (hdc, hfontOld);
    }
}


/***********************************************************************
 *           MENU_DrawPopupMenu
 *
 * Paint a popup menu.
 */
static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
{
    HBRUSH hPrevBrush = 0;
    RECT rect;

    TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);

    GetClientRect( hwnd, &rect );

    if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
        && (SelectObject( hdc, get_menu_font(FALSE))))
    {
	HPEN hPrevPen;

	Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );

	hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
	if( hPrevPen )
	{
	    POPUPMENU *menu;
	    BOOL flat_menu = FALSE;

	    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
	    if (flat_menu)
		FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
	    else
		DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);

            if( (menu = MENU_GetMenu( hmenu )))
            {
                /* draw menu items */
                if( menu->nItems)
                {
                    MENUITEM *item;
                    UINT u;

                    item = menu->items;
                    for( u = menu->nItems; u > 0; u--, item++)
                        MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
                                item, menu->Height, FALSE, ODA_DRAWENTIRE );
                }
                /* draw scroll arrows */
                if (menu->bScrolling)
                    MENU_DrawScrollArrows(menu, hdc);
            }
 	} else
	{
	    SelectObject( hdc, hPrevBrush );
	}
    }
}

/***********************************************************************
 *           MENU_DrawMenuBar
 *
 * Paint a menu bar. Returns the height of the menu bar.
 * called from [windows/nonclient.c]
 */
UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
                         BOOL suppress_draw)
{
    LPPOPUPMENU lppop;
    HFONT hfontOld = 0;
    HMENU hMenu = GetMenu(hwnd);

    lppop = MENU_GetMenu( hMenu );
    if (lppop == NULL || lprect == NULL)
    {
        return GetSystemMetrics(SM_CYMENU);
    }

    if (suppress_draw)
    {
	hfontOld = SelectObject( hDC, get_menu_font(FALSE));

	if (lppop->Height == 0)
		MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);

	lprect->bottom = lprect->top + lppop->Height;

        if (hfontOld) SelectObject( hDC, hfontOld);
	return lppop->Height;
    }
    else
        return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
}


/***********************************************************************
 *           MENU_ShowPopup
 *
 * Display a popup menu.
 */
static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
                              INT x, INT y, INT xanchor, INT yanchor )
{
    POPUPMENU *menu;
    INT width, height;
    POINT pt;
    HMONITOR monitor;
    MONITORINFO info;

    TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
          hwndOwner, hmenu, id, x, y, xanchor, yanchor);

    if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
    if (menu->FocusedItem != NO_SELECTED_ITEM)
    {
	menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
	menu->FocusedItem = NO_SELECTED_ITEM;
    }

    /* store the owner for DrawItem */
    menu->hwndOwner = hwndOwner;

    menu->nScrollPos = 0;
    MENU_PopupMenuCalcSize( menu );

    /* adjust popup menu pos so that it fits within the desktop */

    width = menu->Width + GetSystemMetrics(SM_CXBORDER);
    height = menu->Height + GetSystemMetrics(SM_CYBORDER);

    /* FIXME: should use item rect */
    pt.x = x;
    pt.y = y;
    monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
    info.cbSize = sizeof(info);
    GetMonitorInfoW( monitor, &info );
    if( x + width > info.rcWork.right)
    {
        if( xanchor && x >= width - xanchor )
            x -= width - xanchor;

        if( x + width > info.rcWork.right)
            x = info.rcWork.right - width;
    }
    if( x < info.rcWork.left ) x = info.rcWork.left; if( y + height > info.rcWork.bottom)
    {
        if( yanchor && y >= height + yanchor )
            y -= height + yanchor;

        if( y + height > info.rcWork.bottom)
            y = info.rcWork.bottom - height;
    }
    if( y < info.rcWork.top ) y = info.rcWork.top; /* NOTE: In Windows, top menu popup is not owned. */ menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
                                WS_POPUP, x, y, width, height,
                                hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
                                (LPVOID)hmenu );
    if( !menu->hWnd ) return FALSE;
    if (!top_popup) top_popup = menu->hWnd;

    /* Display the window */

    SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
                  SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
    UpdateWindow( menu->hWnd );
    return TRUE;
}


/***********************************************************************
 *           MENU_EnsureMenuItemVisible
 */
static void
MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
{
    if (lppop->bScrolling)
    {
        MENUITEM *item = &lppop->items[wIndex];
        UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
        UINT nOldPos = lppop->nScrollPos;
        RECT rc;
        UINT arrow_bitmap_height;
        BITMAP bmp;
        
        GetClientRect(lppop->hWnd, &rc);

        GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
        arrow_bitmap_height = bmp.bmHeight;

        rc.top += arrow_bitmap_height;
        rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
       
        nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
        if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
        {
            
            lppop->nScrollPos = item->rect.bottom - nMaxHeight;
            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
            MENU_DrawScrollArrows(lppop, hdc);
        }
        else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
        {
            lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
            MENU_DrawScrollArrows(lppop, hdc);
        }
    }
}


/***********************************************************************
 *           MENU_SelectItem
 */
static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
                             BOOL sendMenuSelect, HMENU topmenu )
{
    LPPOPUPMENU lppop;
    HDC hdc;

    TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);

    lppop = MENU_GetMenu( hmenu );
    if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;

    if (lppop->FocusedItem == wIndex) return;
    if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
    else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
    if (!top_popup) top_popup = lppop->hWnd;

    SelectObject( hdc, get_menu_font(FALSE));

      /* Clear previous highlighted item */
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
    {
	lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
	MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
                          lppop->Height, !(lppop->wFlags & MF_POPUP),
			  ODA_SELECT );
    }

      /* Highlight new item (if any) */
    lppop->FocusedItem = wIndex;
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
    {
        if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
            lppop->items[wIndex].fState |= MF_HILITE;
            MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
            MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
                    &lppop->items[wIndex], lppop->Height,
                    !(lppop->wFlags & MF_POPUP), ODA_SELECT );
        }
        if (sendMenuSelect)
        {
            MENUITEM *ip = &lppop->items[lppop->FocusedItem];
	    SendMessageW( hwndOwner, WM_MENUSELECT,
                     MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
                     ip->fType | ip->fState |
                     (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
        }
    }
    else if (sendMenuSelect) {
        if(topmenu){
            int pos;
            if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
                POPUPMENU *ptm = MENU_GetMenu( topmenu );
                MENUITEM *ip = &ptm->items[pos];
                SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
                         ip->fType | ip->fState |
                         (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
            }
        }
    }
    ReleaseDC( lppop->hWnd, hdc );
}


/***********************************************************************
 *           MENU_MoveSelection
 *
 * Moves currently selected item according to the offset parameter.
 * If there is no selection then it should select the last item if
 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
 */
static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
{
    INT i;
    POPUPMENU *menu;

    TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);

    menu = MENU_GetMenu( hmenu );
    if ((!menu) || (!menu->items)) return;

    if ( menu->FocusedItem != NO_SELECTED_ITEM )
    {
	if( menu->nItems == 1 ) return; else
	for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
					    ; i += offset)
	    if (!(menu->items[i].fType & MF_SEPARATOR))
	    {
		MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
		return;
	    }
    }

    for ( i = (offset > 0) ? 0 : menu->nItems - 1;
		  i >= 0 && i < menu->nItems ; i += offset)
	if (!(menu->items[i].fType & MF_SEPARATOR))
	{
	    MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
	    return;
	}
}


/**********************************************************************
 *         MENU_SetItemData
 *
 * Set an item's flags, id and text ptr. Called by InsertMenu() and
 * ModifyMenu().
 */
static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
                                LPCWSTR str )
{
    debug_print_menuitem("MENU_SetItemData from: ", item, "");
    TRACE("flags=%x str=%p\n", flags, str);

    if (IS_STRING_ITEM(flags))
    {
        LPWSTR prevText = item->text;
        if (!str)
        {
            flags |= MF_SEPARATOR;
            item->text = NULL;
        }
        else
        {
            LPWSTR text;
            /* Item beginning with a backspace is a help item */
            if (*str == '\b')
            {
                flags |= MF_HELP;
                str++;
            }
            if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
                return FALSE;
            strcpyW( text, str );
            item->text = text;
        }
        item->hbmpItem = NULL;
        HeapFree( GetProcessHeap(), 0, prevText );
    }
    else if(( flags & MFT_BITMAP)) {
        item->hbmpItem = HBITMAP_32(LOWORD(str));
        /* setting bitmap clears text */
        HeapFree( GetProcessHeap(), 0, item->text );
        item->text = NULL;
    }

    if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;

    if (flags & MF_OWNERDRAW)
        item->dwItemData = (DWORD_PTR)str;
    else
        item->dwItemData = 0;

    if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
	DestroyMenu( item->hSubMenu );   /* ModifyMenu() spec */

    if (flags & MF_POPUP)
    {
	POPUPMENU *menu = MENU_GetMenu((HMENU)id);
        if (menu) menu->wFlags |= MF_POPUP;
	else
        {
            item->wID = 0;
            item->hSubMenu = 0;
            item->fType = 0;
            item->fState = 0;
	    return FALSE;
        }
    }

    item->wID = id;
    if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;

    if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
      flags |= MF_POPUP; /* keep popup */

    item->fType = flags & TYPE_MASK;
    /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
       for ModifyMenu, but Windows accepts it */
    item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;

    /* Don't call SetRectEmpty here! */

    debug_print_menuitem("MENU_SetItemData to  : ", item, "");
    return TRUE;
}


/**********************************************************************
 *         MENU_InsertItem
 *
 * Insert (allocate) a new item into a menu.
 */
static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
{
    MENUITEM *newItems;
    POPUPMENU *menu;

    if (!(menu = MENU_GetMenu(hMenu)))
        return NULL;

    /* Find where to insert new item */

    if (flags & MF_BYPOSITION) {
        if (pos > menu->nItems)
            pos = menu->nItems;
    } else {
        if (!MENU_FindItem( &hMenu, &pos, flags ))
            pos = menu->nItems;
        else {
            if (!(menu = MENU_GetMenu( hMenu )))
                return NULL;
        }
    }

    /* Make sure that MDI system buttons stay on the right side.
     * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
     * regardless of their id.
     */
    while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
           (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
           (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D) pos--; TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION); /* Create new items array */ newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
    if (!newItems)
    {
        WARN("allocation failed\n" );
        return NULL;
    }
    if (menu->nItems > 0)
    {
	  /* Copy the old array into the new one */
	if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
	if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
					(menu->nItems-pos)*sizeof(MENUITEM) );
        HeapFree( GetProcessHeap(), 0, menu->items );
    }
    menu->items = newItems;
    menu->nItems++;
    memset( &newItems[pos], 0, sizeof(*newItems) );
    menu->Height = 0; /* force size recalculate */
    return &newItems[pos];
}


/**********************************************************************
 *         MENU_ParseResource
 *
 * Parse a standard menu resource and add items to the menu.
 * Return a pointer to the end of the resource.
 *
 * NOTE: flags is equivalent to the mtOption field
 */
static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
{
    WORD flags, id = 0;
    LPCSTR str;
    BOOL end_flag;

    do
    {
        flags = GET_WORD(res);
        end_flag = flags & MF_END;
        /* Remove MF_END because it has the same value as MF_HILITE */
        flags &= ~MF_END;
        res += sizeof(WORD);
        if (!(flags & MF_POPUP))
        {
            id = GET_WORD(res);
            res += sizeof(WORD);
        }
        str = res;
        if (!unicode) res += strlen(str) + 1;
        else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
        if (flags & MF_POPUP)
        {
            HMENU hSubMenu = CreatePopupMenu();
            if (!hSubMenu) return NULL;
            if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
                return NULL;
            if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
            else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
        }
        else  /* Not a popup */
        {
            if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
            else AppendMenuW( hMenu, flags, id,
                                *(LPCWSTR)str ? (LPCWSTR)str : NULL );
        }
    } while (!end_flag);
    return res;
}


/**********************************************************************
 *         MENUEX_ParseResource
 *
 * Parse an extended menu resource and add items to the menu.
 * Return a pointer to the end of the resource.
 */
static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
{
    WORD resinfo;
    do {
	MENUITEMINFOW mii;

	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
	mii.fType = GET_DWORD(res);
        res += sizeof(DWORD);
	mii.fState = GET_DWORD(res);
        res += sizeof(DWORD);
	mii.wID = GET_DWORD(res);
        res += sizeof(DWORD);
	resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
        res += sizeof(WORD);
	/* Align the text on a word boundary.  */
	res += (~((UINT_PTR)res - 1)) & 1;
	mii.dwTypeData = (LPWSTR) res;
	res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
	/* Align the following fields on a dword boundary.  */
	res += (~((UINT_PTR)res - 1)) & 3;

        TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
              mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));

	if (resinfo & 1) {	/* Pop-up? */
	    /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
	    res += sizeof(DWORD);
	    mii.hSubMenu = CreatePopupMenu();
	    if (!mii.hSubMenu)
		return NULL;
	    if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
		DestroyMenu(mii.hSubMenu);
                return NULL;
	    }
	    mii.fMask |= MIIM_SUBMENU;
	    mii.fType |= MF_POPUP;
        }
	else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
	{
	    WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
		mii.wID, mii.fType);
	    mii.fType |= MF_SEPARATOR;
	}
	InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
    } while (!(resinfo & MF_END));
    return res;
}


/***********************************************************************
 *           MENU_GetSubPopup
 *
 * Return the handle of the selected sub-popup menu (if any).
 */
static HMENU MENU_GetSubPopup( HMENU hmenu )
{
    POPUPMENU *menu;
    MENUITEM *item;

    menu = MENU_GetMenu( hmenu );

    if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;

    item = &menu->items[menu->FocusedItem];
    if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
        return item->hSubMenu;
    return 0;
}


/***********************************************************************
 *           MENU_HideSubPopups
 *
 * Hide the sub-popup menus of this menu.
 */
static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
                                BOOL sendMenuSelect )
{
    POPUPMENU *menu = MENU_GetMenu( hmenu );

    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);

    if (menu && top_popup)
    {
	HMENU hsubmenu;
	POPUPMENU *submenu;
	MENUITEM *item;

	if (menu->FocusedItem != NO_SELECTED_ITEM)
	{
	    item = &menu->items[menu->FocusedItem];
	    if (!(item->fType & MF_POPUP) ||
		!(item->fState & MF_MOUSESELECT)) return;
	    item->fState &= ~MF_MOUSESELECT;
	    hsubmenu = item->hSubMenu;
	} else return;

	submenu = MENU_GetMenu( hsubmenu );
	MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
	MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
        DestroyWindow( submenu->hWnd );
        submenu->hWnd = 0;
    }
}


/***********************************************************************
 *           MENU_ShowSubPopup
 *
 * Display the sub-menu of the selected item of this menu.
 * Return the handle of the submenu, or hmenu if no submenu to display.
 */
static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
                                  BOOL selectFirst, UINT wFlags )
{
    RECT rect;
    POPUPMENU *menu;
    MENUITEM *item;
    HDC hdc;

    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);

    if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;

    if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;

    item = &menu->items[menu->FocusedItem];
    if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
        return hmenu;

    /* message must be sent before using item,
       because nearly everything may be changed by the application ! */

    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
       SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
                     MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));

    item = &menu->items[menu->FocusedItem];
    rect = item->rect;

    /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
    if (!(item->fState & MF_HILITE))
    {
        if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
        else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);

        SelectObject( hdc, get_menu_font(FALSE));

        item->fState |= MF_HILITE;
        MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
	ReleaseDC( menu->hWnd, hdc );
    }
    if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
      item->rect = rect;

    item->fState |= MF_MOUSESELECT;

    if (IS_SYSTEM_MENU(menu))
    {
	MENU_InitSysMenuPopup(item->hSubMenu,
                              GetWindowLongW( menu->hWnd, GWL_STYLE ),
                              GetClassLongW( menu->hWnd, GCL_STYLE));

	NC_GetSysPopupPos( menu->hWnd, &rect );
	rect.top = rect.bottom;
	rect.right = GetSystemMetrics(SM_CXSIZE);
        rect.bottom = GetSystemMetrics(SM_CYSIZE);
    }
    else
    {
        GetWindowRect( menu->hWnd, &rect );
	if (menu->wFlags & MF_POPUP)
	{
            RECT rc = item->rect;

            MENU_AdjustMenuItemRect(menu, &rc);

	    /* The first item in the popup menu has to be at the
	       same y position as the focused menu item */
	    rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
	    rect.top += rc.top - MENU_TOP_MARGIN;
	    rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
	    rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
	                  - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
	}
	else
	{
	    rect.left += item->rect.left;
	    rect.top += item->rect.bottom;
	    rect.right = item->rect.right - item->rect.left;
	    rect.bottom = item->rect.bottom - item->rect.top;
	}
    }

    MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
		    rect.left, rect.top, rect.right, rect.bottom );
    if (selectFirst)
        MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
    return item->hSubMenu;
}



/**********************************************************************
 *         MENU_IsMenuActive
 */
HWND MENU_IsMenuActive(void)
{
    return top_popup;
}

/***********************************************************************
 *           MENU_PtMenu
 *
 * Walks menu chain trying to find a menu pt maps to.
 */
static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
{
   POPUPMENU *menu = MENU_GetMenu( hMenu );
   UINT item = menu->FocusedItem;
   HMENU ret;

   /* try subpopup first (if any) */
   ret = (item != NO_SELECTED_ITEM &&
          (menu->items[item].fType & MF_POPUP) &&
          (menu->items[item].fState & MF_MOUSESELECT))
        ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;

   if (!ret)  /* check the current window (avoiding WM_HITTEST) */
   {
       INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
       if( menu->wFlags & MF_POPUP )
       {
           if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
       }
       else if (ht == HTSYSMENU)
           ret = get_win_sys_menu( menu->hWnd );
       else if (ht == HTMENU)
           ret = GetMenu( menu->hWnd );
   }
   return ret;
}

/***********************************************************************
 *           MENU_ExecFocusedItem
 *
 * Execute a menu item (for instance when user pressed Enter).
 * Return the wID of the executed item. Otherwise, -1 indicating
 * that no menu item was executed, -2 if a popup is shown;
 * Have to receive the flags for the TrackPopupMenu options to avoid
 * sending unwanted message.
 *
 */
static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
{
    MENUITEM *item;
    POPUPMENU *menu = MENU_GetMenu( hMenu );

    TRACE("%p hmenu=%p\n", pmt, hMenu);

    if (!menu || !menu->nItems ||
	(menu->FocusedItem == NO_SELECTED_ITEM)) return -1;

    item = &menu->items[menu->FocusedItem];

    TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);

    if (!(item->fType & MF_POPUP))
    {
	if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
	{
	    /* If TPM_RETURNCMD is set you return the id, but
	       do not send a message to the owner */
	    if(!(wFlags & TPM_RETURNCMD))
	    {
		if( menu->wFlags & MF_SYSMENU )
		    PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
				  MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
		else
		{
                    if (menu->dwStyle & MNS_NOTIFYBYPOS)
                        PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
                                      (LPARAM)hMenu);
                    else
                        PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
		}
	    }
	    return item->wID;
	}
    }
    else
    {
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
	return -2;
    }

    return -1;
}

/***********************************************************************
 *           MENU_SwitchTracking
 *
 * Helper function for menu navigation routines.
 */
static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
{
    POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );

    TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);

    if( pmt->hTopMenu != hPtMenu &&
	!((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
    {
	/* both are top level menus (system and menu-bar) */
	MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
        pmt->hTopMenu = hPtMenu;
    }
    else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
    MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
}


/***********************************************************************
 *           MENU_ButtonDown
 *
 * Return TRUE if we can go on with menu tracking.
 */
static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
{
    TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);

    if (hPtMenu)
    {
	UINT id = 0;
	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
	MENUITEM *item;

	if( IS_SYSTEM_MENU(ptmenu) )
	    item = ptmenu->items;
	else
	    item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );

	if( item )
	{
	    if( ptmenu->FocusedItem != id )
		MENU_SwitchTracking( pmt, hPtMenu, id );

	    /* If the popup menu is not already "popped" */
	    if(!(item->fState & MF_MOUSESELECT ))
	    {
		pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
	    }

	    return TRUE;
	}
	/* Else the click was on the menu bar, finish the tracking */
    }
    return FALSE;
}

/***********************************************************************
 *           MENU_ButtonUp
 *
 * Return the value of MENU_ExecFocusedItem if
 * the selected item was not a popup. Else open the popup.
 * A -1 return value indicates that we go on with menu tracking.
 *
 */
static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
{
    TRACE("%p hmenu=%p\n", pmt, hPtMenu);

    if (hPtMenu)
    {
	UINT id = 0;
	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
	MENUITEM *item;

        if( IS_SYSTEM_MENU(ptmenu) )
            item = ptmenu->items;
        else
            item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );

	if( item && (ptmenu->FocusedItem == id ))
	{
            debug_print_menuitem ("FocusedItem: ", item, "");

	    if( !(item->fType & MF_POPUP) )
	    {
	        INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
                if (executedMenuId == -1 || executedMenuId == -2) return -1;
                return executedMenuId;
	    }

	    /* If we are dealing with the top-level menu            */
	    /* and this is a click on an already "popped" item:     */
	    /* Stop the menu tracking and close the opened submenus */
	    if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
		return 0;
	}
	ptmenu->bTimeToHide = TRUE;
    }
    return -1;
}


/***********************************************************************
 *           MENU_MouseMove
 *
 * Return TRUE if we can go on with menu tracking.
 */
static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
{
    UINT id = NO_SELECTED_ITEM;
    POPUPMENU *ptmenu = NULL;

    if( hPtMenu )
    {
	ptmenu = MENU_GetMenu( hPtMenu );
        if( IS_SYSTEM_MENU(ptmenu) )
	    id = 0;
        else
            MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
    }

    if( id == NO_SELECTED_ITEM )
    {
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
			 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);

    }
    else if( ptmenu->FocusedItem != id )
    {
	    MENU_SwitchTracking( pmt, hPtMenu, id );
	    pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
    }
    return TRUE;
}


/***********************************************************************
 *           MENU_SetCapture
 */
static void MENU_SetCapture( HWND hwnd )
{
    HWND previous = 0;

    SERVER_START_REQ( set_capture_window )
    {
        req->handle = hwnd;
        req->flags  = CAPTURE_MENU;
        if (!wine_server_call_err( req ))
        {
            previous = reply->previous;
            hwnd = reply->full_handle;
        }
    }
    SERVER_END_REQ;

    if (previous && previous != hwnd)
        SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
}


/***********************************************************************
 *           MENU_DoNextMenu
 *
 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
 */
static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
{
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
    BOOL atEnd = FALSE;

    /* When skipping left, we need to do something special after the
       first menu.                                                  */
    if (vk == VK_LEFT && menu->FocusedItem == 0)
    {
        atEnd = TRUE;
    }
    /* When skipping right, for the non-system menu, we need to
       handle the last non-special menu item (ie skip any window
       icons such as MDI maximize, restore or close)             */
    else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
    {
        int i = menu->FocusedItem + 1;
        while (i < menu->nItems) {
            if ((menu->items[i].wID >= SC_SIZE &&
                 menu->items[i].wID <= SC_RESTORE)) { i++; } else break; } if (i == menu->nItems) {
            atEnd = TRUE;
        }
    }
    /* When skipping right, we need to cater for the system menu */
    else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
    {
        if (menu->FocusedItem == (menu->nItems - 1)) {
            atEnd = TRUE;
        }
    }

    if( atEnd )
    {
        MDINEXTMENU next_menu;
	HMENU hNewMenu;
	HWND  hNewWnd;
	UINT  id = 0;

        next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
        next_menu.hmenuNext = 0;
        next_menu.hwndNext = 0;
        SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );

        TRACE("%p [%p] -> %p [%p]\n",
              pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );

	if (!next_menu.hmenuNext || !next_menu.hwndNext)
	{
            DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
	    hNewWnd = pmt->hOwnerWnd;
	    if( IS_SYSTEM_MENU(menu) )
	    {
		/* switch to the menu bar */

                if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;

	        if( vk == VK_LEFT )
	        {
		    menu = MENU_GetMenu( hNewMenu );
		    id = menu->nItems - 1;

                    /* Skip backwards over any system predefined icons,
                       eg. MDI close, restore etc icons                 */
                    while ((id > 0) &&
                           (menu->items[id].wID >= SC_SIZE &&
                            menu->items[id].wID <= SC_RESTORE)) id--; } } else if (style & WS_SYSMENU ) { /* switch to the system menu */ hNewMenu = get_win_sys_menu( hNewWnd ); } else return FALSE; } else /* application returned a new menu to switch to */ { hNewMenu = next_menu.hmenuNext; hNewWnd = WIN_GetFullHandle( next_menu.hwndNext ); if( IsMenu(hNewMenu) && IsWindow(hNewWnd) ) { DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE ); if (style & WS_SYSMENU && GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu ) { /* get the real system menu */ hNewMenu = get_win_sys_menu(hNewWnd); } else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu ) { /* FIXME: Not sure what to do here; * perhaps try to track hNewMenu as a popup? */ TRACE(" -- got confused.\n"); return FALSE; } } else return FALSE; } if( hNewMenu != pmt->hTopMenu )
	{
	    MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
                    FALSE, 0 );
	    if( pmt->hCurrentMenu != pmt->hTopMenu )
		MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
	}

	if( hNewWnd != pmt->hOwnerWnd )
	{
	    pmt->hOwnerWnd = hNewWnd;
	    MENU_SetCapture( pmt->hOwnerWnd );
	}

	pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );

	return TRUE;
    }
    return FALSE;
}

/***********************************************************************
 *           MENU_SuspendPopup
 *
 * The idea is not to show the popup if the next input message is
 * going to hide it anyway.
 */
static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
{
    MSG msg;

    msg.hwnd = pmt->hOwnerWnd;

    PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
    pmt->trackFlags |= TF_SKIPREMOVE;

    switch( uMsg )
    {
	case WM_KEYDOWN:
	     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
	     if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
	     {
		 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
	         PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
	         if( msg.message == WM_KEYDOWN &&
		    (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
	         {
		     pmt->trackFlags |= TF_SUSPENDPOPUP;
		     return TRUE;
	         }
	     }
	     break;
    }

    /* failures go through this */
    pmt->trackFlags &= ~TF_SUSPENDPOPUP;
    return FALSE;
}

/***********************************************************************
 *           MENU_KeyEscape
 *
 * Handle a VK_ESCAPE key event in a menu.
 */
static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
{
    BOOL bEndMenu = TRUE;

    if (pmt->hCurrentMenu != pmt->hTopMenu)
    {
        POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);

        if (menu->wFlags & MF_POPUP)
        {
            HMENU hmenutmp, hmenuprev;

            hmenuprev = hmenutmp = pmt->hTopMenu;

            /* close topmost popup */
            while (hmenutmp != pmt->hCurrentMenu)
            {
                hmenuprev = hmenutmp;
                hmenutmp = MENU_GetSubPopup( hmenuprev );
            }

            MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
            pmt->hCurrentMenu = hmenuprev;
            bEndMenu = FALSE;
        }
    }

    return bEndMenu;
}

/***********************************************************************
 *           MENU_KeyLeft
 *
 * Handle a VK_LEFT key event in a menu.
 */
static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
{
    POPUPMENU *menu;
    HMENU hmenutmp, hmenuprev;
    UINT  prevcol;

    hmenuprev = hmenutmp = pmt->hTopMenu;
    menu = MENU_GetMenu( hmenutmp );

    /* Try to move 1 column left (if possible) */
    if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
	NO_SELECTED_ITEM ) {

	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
			 prevcol, TRUE, 0 );
	return;
    }

    /* close topmost popup */
    while (hmenutmp != pmt->hCurrentMenu)
    {
	hmenuprev = hmenutmp;
	hmenutmp = MENU_GetSubPopup( hmenuprev );
    }

    MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
    pmt->hCurrentMenu = hmenuprev;

    if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
    {
	/* move menu bar selection if no more popups are left */

	if( !MENU_DoNextMenu( pmt, VK_LEFT) )
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );

	if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
	{
	   /* A sublevel menu was displayed - display the next one
	    * unless there is another displacement coming up */

	    if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
						pmt->hTopMenu, TRUE, wFlags);
	}
    }
}


/***********************************************************************
 *           MENU_KeyRight
 *
 * Handle a VK_RIGHT key event in a menu.
 */
static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
{
    HMENU hmenutmp;
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
    UINT  nextcol;

    TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
          pmt->hCurrentMenu,
          debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
          pmt->hTopMenu, debugstr_w(menu->items[0].text) );

    if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
    {
	/* If already displaying a popup, try to display sub-popup */

	hmenutmp = pmt->hCurrentMenu;
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);

	/* if subpopup was displayed then we are done */
	if (hmenutmp != pmt->hCurrentMenu) return;
    }

    /* Check to see if there's another column */
    if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
	NO_SELECTED_ITEM ) {
	TRACE("Going to %d.\n", nextcol );
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
			 nextcol, TRUE, 0 );
	return;
    }

    if (!(menu->wFlags & MF_POPUP))	/* menu bar tracking */
    {
	if( pmt->hCurrentMenu != pmt->hTopMenu )
	{
	    MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
	    hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
	} else hmenutmp = 0;

	/* try to move to the next item */
	if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );

	if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
	    if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
						       pmt->hTopMenu, TRUE, wFlags);
    }
}

/***********************************************************************
 *           MENU_TrackMenu
 *
 * Menu tracking code.
 */
static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
                            HWND hwnd, const RECT *lprect )
{
    MSG msg;
    POPUPMENU *menu;
    BOOL fRemove;
    INT executedMenuId = -1;
    MTRACKER mt;
    BOOL enterIdleSent = FALSE;

    mt.trackFlags = 0;
    mt.hCurrentMenu = hmenu;
    mt.hTopMenu = hmenu;
    mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
    mt.pt.x = x;
    mt.pt.y = y;

    TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
          hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));

    fEndMenu = FALSE;
    if (!(menu = MENU_GetMenu( hmenu )))
    {
        WARN("Invalid menu handle %p\n", hmenu);
        SetLastError(ERROR_INVALID_MENU_HANDLE);
        return FALSE;
    }

    if (wFlags & TPM_BUTTONDOWN)
    {
	/* Get the result in order to start the tracking or not */
	fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
	fEndMenu = !fRemove;
    }

    if (wFlags & TF_ENDMENU) fEndMenu = TRUE;

    MENU_SetCapture( mt.hOwnerWnd );

    while (!fEndMenu)
    {
	menu = MENU_GetMenu( mt.hCurrentMenu );
	if (!menu) /* sometimes happens if I do a window manager close */
	    break;

	/* we have to keep the message in the queue until it's
	 * clear that menu loop is not over yet. */

        for (;;)
        {
            if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
            {
                if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
                /* remove the message from the queue */
                PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
            }
            else
            {
                if (!enterIdleSent)
                {
                    HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
                    enterIdleSent = TRUE;
                    SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
                }
                WaitMessage();
            }
        }

	/* check if EndMenu() tried to cancel us, by posting this message */
        if(msg.message == WM_CANCELMODE)
	{
	    /* we are now out of the loop */
    	    fEndMenu = TRUE;

	    /* remove the message from the queue */
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );

	    /* break out of internal loop, ala ESCAPE */
	    break;
	}

        TranslateMessage( &msg );
        mt.pt = msg.pt;

	if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
	  enterIdleSent=FALSE;

        fRemove = FALSE;
	if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST)) { /* * Use the mouse coordinates in lParam instead of those in the MSG * struct to properly handle synthetic messages. They are already * in screen coordinates. */ mt.pt.x = (short)LOWORD(msg.lParam); mt.pt.y = (short)HIWORD(msg.lParam); /* Find a menu for this mouse event */ hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt ); switch(msg.message) { /* no WM_NC... messages in captured state */ case WM_RBUTTONDBLCLK: case WM_RBUTTONDOWN: if (!(wFlags & TPM_RIGHTBUTTON)) break; /* fall through */ case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: /* If the message belongs to the menu, removes it from the queue */ /* Else, end menu tracking */ fRemove = MENU_ButtonDown( &mt, hmenu, wFlags ); fEndMenu = !fRemove; break; case WM_RBUTTONUP: if (!(wFlags & TPM_RIGHTBUTTON)) break; /* fall through */ case WM_LBUTTONUP: /* Check if a menu was selected by the mouse */ if (hmenu) { executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags); TRACE("executedMenuId %d\n", executedMenuId); /* End the loop if executedMenuId is an item ID */ /* or if the job was done (executedMenuId = 0). */ fEndMenu = fRemove = (executedMenuId != -1); } /* No menu was selected by the mouse */ /* if the function was called by TrackPopupMenu, continue with the menu tracking. If not, stop it */ else fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE); break; case WM_MOUSEMOVE: /* the selected menu item must be changed every time */ /* the mouse moves. */ if (hmenu) fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags ); } /* switch(msg.message) - mouse */ } else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST)) { fRemove = TRUE; /* Keyboard messages are always removed */ switch(msg.message) { case WM_KEYDOWN: case WM_SYSKEYDOWN: switch(msg.wParam) { case VK_MENU: fEndMenu = TRUE; break; case VK_HOME: case VK_END: MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, NO_SELECTED_ITEM, FALSE, 0 ); MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV ); break; case VK_UP: case VK_DOWN: /* If on menu bar, pull-down the menu */ menu = MENU_GetMenu( mt.hCurrentMenu ); if (!(menu->wFlags & MF_POPUP))
			mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
		    else      /* otherwise try to move selection */
			MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
                                       (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
		    break;

		case VK_LEFT:
		    MENU_KeyLeft( &mt, wFlags );
		    break;

		case VK_RIGHT:
		    MENU_KeyRight( &mt, wFlags );
		    break;

		case VK_ESCAPE:
                    fEndMenu = MENU_KeyEscape(&mt, wFlags);
		    break;

		case VK_F1:
		    {
			HELPINFO hi;
			hi.cbSize = sizeof(HELPINFO);
			hi.iContextType = HELPINFO_MENUITEM;
			if (menu->FocusedItem == NO_SELECTED_ITEM)
			    hi.iCtrlId = 0;
		        else
			    hi.iCtrlId = menu->items[menu->FocusedItem].wID;
			hi.hItemHandle = hmenu;
			hi.dwContextId = menu->dwContextHelpID;
			hi.MousePos = msg.pt;
			SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
			break;
		    }

		default:
		    break;
		}
		break;  /* WM_KEYDOWN */

	    case WM_CHAR:
	    case WM_SYSCHAR:
		{
		    UINT	pos;

		    if (msg.wParam == '\r' || msg.wParam == ' ')
		    {
                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
                        fEndMenu = (executedMenuId != -2);

			break;
		    }

		      /* Hack to avoid control chars. */
		      /* We will find a better way real soon... */
		    if (msg.wParam < 32) break; pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu, LOWORD(msg.wParam), FALSE ); if (pos == (UINT)-2) fEndMenu = TRUE; else if (pos == (UINT)-1) MessageBeep(0); else { MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos, TRUE, 0 ); executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags); fEndMenu = (executedMenuId != -2); } } break; } /* switch(msg.message) - kbd */ } else { PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); DispatchMessageW( &msg ); continue; } if (!fEndMenu) fRemove = TRUE; /* finally remove message from the queue */ if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) ) PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); else mt.trackFlags &= ~TF_SKIPREMOVE; } MENU_SetCapture(0); /* release the capture */ /* If dropdown is still painted and the close box is clicked on then the menu will be destroyed as part of the DispatchMessage above. This will then invalidate the menu handle in mt.hTopMenu. We should check for this first. */ if( IsMenu( mt.hTopMenu ) ) { menu = MENU_GetMenu( mt.hTopMenu ); if( IsWindow( mt.hOwnerWnd ) ) { MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE ); if (menu && (menu->wFlags & MF_POPUP))
	    {
                DestroyWindow( menu->hWnd );
                menu->hWnd = 0;
	    }
	    MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
	    SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
        }

        /* Reset the variable for hiding menu */
        if( menu ) menu->bTimeToHide = FALSE;
    }

    /* The return value is only used by TrackPopupMenu */
    if (!(wFlags & TPM_RETURNCMD)) return TRUE;
    if (executedMenuId == -1) executedMenuId = 0;
    return executedMenuId;
}

/***********************************************************************
 *           MENU_InitTracking
 */
static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
{
    POPUPMENU *menu;
    
    TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);

    HideCaret(0);

    /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
       SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );

    SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );

    if (!(wFlags & TPM_NONOTIFY))
    {
       SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
       /* If an app changed/recreated menu bar entries in WM_INITMENU
        * menu sizes will be recalculated once the menu created/shown.
        */
    }
    
    /* This makes the menus of applications built with Delphi work.
     * It also enables menus to be displayed in more than one window,
     * but there are some bugs left that need to be fixed in this case.
     */
    if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
    
    return TRUE;
}
/***********************************************************************
 *           MENU_ExitTracking
 */
static BOOL MENU_ExitTracking(HWND hWnd)
{
    TRACE("hwnd=%p\n", hWnd);

    SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
    ShowCaret(0);
    top_popup = 0;
    return TRUE;
}

/***********************************************************************
 *           MENU_TrackMouseMenuBar
 *
 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
 */
void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
{
    HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
    UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;

    TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));

    if (IsMenu(hMenu))
    {
	MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
	MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
	MENU_ExitTracking(hWnd);
    }
}


/***********************************************************************
 *           MENU_TrackKbdMenuBar
 *
 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
 */
void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
{
    UINT uItem = NO_SELECTED_ITEM;
    HMENU hTrackMenu;
    UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;

    TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);

    /* find window that has a menu */

    while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
        if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;

    /* check if we have to track a system menu */

    hTrackMenu = GetMenu( hwnd );
    if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
    {
        if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
        hTrackMenu = get_win_sys_menu( hwnd );
        uItem = 0;
        wParam |= HTSYSMENU; /* prevent item lookup */
    }

    if (!IsMenu( hTrackMenu )) return;

    MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );

    if( wChar && wChar != ' ' )
    {
        uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
        if ( uItem >= (UINT)(-2) )
        {
            if( uItem == (UINT)(-1) ) MessageBeep(0);
            /* schedule end of menu tracking */
            wFlags |= TF_ENDMENU;
            goto track_menu;
        }
    }

    MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );

    if (wParam & HTSYSMENU && wChar != ' ')
    {
        /* prevent sysmenu activation for managed windows on Alt down/up */
        if (GetPropA( hwnd, "__wine_x11_managed" ))
            wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
    }
    else
    {
        if( uItem == NO_SELECTED_ITEM )
            MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
        else
            PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
    }

track_menu:
    MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
    MENU_ExitTracking( hwnd );
}


/**********************************************************************
 *           TrackPopupMenu   (USER32.@)
 *
 * Like the win32 API, the function return the command ID only if the
 * flag TPM_RETURNCMD is on.
 *
 */
BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
                           INT nReserved, HWND hWnd, const RECT *lpRect )
{
    BOOL ret = FALSE;

    TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
           hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));

    MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);

    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
        SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);

    if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
        ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
    MENU_ExitTracking(hWnd);

    return ret;
}

/**********************************************************************
 *           TrackPopupMenuEx   (USER32.@)
 */
BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
                                HWND hWnd, LPTPMPARAMS lpTpm )
{
    FIXME("not fully implemented\n" );
    return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
                             lpTpm ? &lpTpm->rcExclude : NULL );
}

/***********************************************************************
 *           PopupMenuWndProc
 *
 * NOTE: Windows has totally different (and undocumented) popup wndproc.
 */
static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);

    switch(message)
    {
    case WM_CREATE:
	{
	    CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
	    SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
            return 0;
	}

    case WM_MOUSEACTIVATE:  /* We don't want to be activated */
        return MA_NOACTIVATE;

    case WM_PAINT:
	{
	    PAINTSTRUCT ps;
	    BeginPaint( hwnd, &ps );
	    MENU_DrawPopupMenu( hwnd, ps.hdc,
                                (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
	    EndPaint( hwnd, &ps );
            return 0;
	}
    case WM_ERASEBKGND:
        return 1;

    case WM_DESTROY:
        /* zero out global pointer in case resident popup window was destroyed. */
        if (hwnd == top_popup) top_popup = 0;
        break;

    case WM_SHOWWINDOW:

	if( wParam )
	{
            if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
	}
	else
            SetWindowLongPtrW( hwnd, 0, 0 );
	break;

    case MM_SETMENUHANDLE:
        SetWindowLongPtrW( hwnd, 0, wParam );
        break;

    case MM_GETMENUHANDLE:
        return GetWindowLongPtrW( hwnd, 0 );

    default:
        return DefWindowProcW( hwnd, message, wParam, lParam );
    }
    return 0;
}


/***********************************************************************
 *           MENU_GetMenuBarHeight
 *
 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
 */
UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
                              INT orgX, INT orgY )
{
    HDC hdc;
    RECT rectBar;
    LPPOPUPMENU lppop;

    TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );

    if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;

    hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
    SelectObject( hdc, get_menu_font(FALSE));
    SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
    MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
    ReleaseDC( hwnd, hdc );
    return lppop->Height;
}


/*******************************************************************
 *         ChangeMenuA    (USER32.@)
 */
BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
                             UINT id, UINT flags )
{
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
    if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
                                                 id, data );
    if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
    if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
                                                id, data );
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
    return InsertMenuA( hMenu, pos, flags, id, data );
}


/*******************************************************************
 *         ChangeMenuW    (USER32.@)
 */
BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
                             UINT id, UINT flags )
{
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
    if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
                                                 id, data );
    if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
    if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
                                                id, data );
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
    return InsertMenuW( hMenu, pos, flags, id, data );
}


/*******************************************************************
 *         CheckMenuItem    (USER32.@)
 */
DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
{
    MENUITEM *item;
    DWORD ret;

    if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
    ret = item->fState & MF_CHECKED;
    if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
    else item->fState &= ~MF_CHECKED;
    return ret;
}


/**********************************************************************
 *         EnableMenuItem    (USER32.@)
 */
BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
{
    UINT    oldflags;
    MENUITEM *item;
    POPUPMENU *menu;

    TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);

    /* Get the Popupmenu to access the owner menu */
    if (!(menu = MENU_GetMenu(hMenu)))
	return (UINT)-1;

    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
	return (UINT)-1;

    oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
    item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);

    /* If the close item in the system menu change update the close button */
    if((item->wID == SC_CLOSE) && (oldflags != wFlags))
    {
	if (menu->hSysMenuOwner != 0)
	{
            RECT rc;
	    POPUPMENU* parentMenu;

	    /* Get the parent menu to access*/
	    if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
		return (UINT)-1;

            /* Refresh the frame to reflect the change */
            GetWindowRect(parentMenu->hWnd, &rc);
            MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
            rc.bottom = 0;
            RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
	}
    }

    return oldflags;
}


/*******************************************************************
 *         GetMenuStringA    (USER32.@)
 */
INT WINAPI GetMenuStringA(
	HMENU hMenu,	/* [in] menuhandle */
	UINT wItemID,	/* [in] menu item (dep. on wFlags) */
	LPSTR str,	/* [out] outbuffer. If NULL, func returns entry length*/
	INT nMaxSiz,	/* [in] length of buffer. if 0, func returns entry len*/
	UINT wFlags	/* [in] MF_ flags */
) {
    MENUITEM *item;

    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
    if (str && nMaxSiz) str[0] = '\0';
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return 0;
    }
    if (!item->text) return 0;
    if (!str || !nMaxSiz) return strlenW(item->text);
    if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
        str[nMaxSiz-1] = 0;
    TRACE("returning %s\n", debugstr_a(str));
    return strlen(str);
}


/*******************************************************************
 *         GetMenuStringW    (USER32.@)
 */
INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
                               LPWSTR str, INT nMaxSiz, UINT wFlags )
{
    MENUITEM *item;

    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
    if (str && nMaxSiz) str[0] = '\0';
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return 0;
    }
    if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
    if( !(item->text)) {
        str[0] = 0;
        return 0;
    }
    lstrcpynW( str, item->text, nMaxSiz );
    TRACE("returning %s\n", debugstr_w(str));
    return strlenW(str);
}


/**********************************************************************
 *         HiliteMenuItem    (USER32.@)
 */
BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
                                UINT wHilite )
{
    LPPOPUPMENU menu;
    TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
    if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
    if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
    if (menu->FocusedItem == wItemID) return TRUE;
    MENU_HideSubPopups( hWnd, hMenu, FALSE );
    MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
    return TRUE;
}


/**********************************************************************
 *         GetMenuState    (USER32.@)
 */
UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
{
    MENUITEM *item;
    TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
    debug_print_menuitem ("  item: ", item, "");
    if (item->fType & MF_POPUP)
    {
	POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
	if (!menu) return -1;
	else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
    }
    else
    {
	/* We used to (from way back then) mask the result to 0xff.  */
	/* I don't know why and it seems wrong as the documented */
	/* return flag MF_SEPARATOR is outside that mask.  */
	return (item->fType | item->fState);
    }
}


/**********************************************************************
 *         GetMenuItemCount    (USER32.@)
 */
INT WINAPI GetMenuItemCount( HMENU hMenu )
{
    LPPOPUPMENU	menu = MENU_GetMenu(hMenu);
    if (!menu) return -1;
    TRACE("(%p) returning %d\n", hMenu, menu->nItems );
    return menu->nItems;
}


/**********************************************************************
 *         GetMenuItemID    (USER32.@)
 */
UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
{
    MENUITEM * lpmi;

    if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
    if (lpmi->fType & MF_POPUP) return -1;
    return lpmi->wID;

}


/*******************************************************************
 *         InsertMenuW    (USER32.@)
 */
BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
                         UINT_PTR id, LPCWSTR str )
{
    MENUITEM *item;

    if (IS_STRING_ITEM(flags) && str)
        TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
              hMenu, pos, flags, id, debugstr_w(str) );
    else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
               hMenu, pos, flags, id, str );

    if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;

    if (!(MENU_SetItemData( item, flags, id, str )))
    {
        RemoveMenu( hMenu, pos, flags );
        return FALSE;
    }

    item->hCheckBit = item->hUnCheckBit = 0;
    return TRUE;
}


/*******************************************************************
 *         InsertMenuA    (USER32.@)
 */
BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
                         UINT_PTR id, LPCSTR str )
{
    BOOL ret = FALSE;

    if (IS_STRING_ITEM(flags) && str)
    {
        INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        if (newstr)
        {
            MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
            ret = InsertMenuW( hMenu, pos, flags, id, newstr );
            HeapFree( GetProcessHeap(), 0, newstr );
        }
        return ret;
    }
    else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
}


/*******************************************************************
 *         AppendMenuA    (USER32.@)
 */
BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
                         UINT_PTR id, LPCSTR data )
{
    return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
}


/*******************************************************************
 *         AppendMenuW    (USER32.@)
 */
BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
                         UINT_PTR id, LPCWSTR data )
{
    return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
}


/**********************************************************************
 *         RemoveMenu    (USER32.@)
 */
BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
{
    LPPOPUPMENU	menu;
    MENUITEM *item;

    TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
    if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
    if (!(menu = MENU_GetMenu(hMenu))) return FALSE;

      /* Remove item */

    MENU_FreeItemData( item );

    if (--menu->nItems == 0)
    {
        HeapFree( GetProcessHeap(), 0, menu->items );
        menu->items = NULL;
    }
    else
    {
	while(nPos < menu->nItems)
	{
	    *item = *(item+1);
	    item++;
	    nPos++;
	}
        menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
                                   menu->nItems * sizeof(MENUITEM) );
    }
    return TRUE;
}


/**********************************************************************
 *         DeleteMenu    (USER32.@)
 */
BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
{
    MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
    if (!item) return FALSE;
    if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
      /* nPos is now the position of the item */
    RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
    return TRUE;
}


/*******************************************************************
 *         ModifyMenuW    (USER32.@)
 */
BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
                         UINT_PTR id, LPCWSTR str )
{
    MENUITEM *item;

    if (IS_STRING_ITEM(flags))
        TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
    else
        TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );

    if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
    MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
    return MENU_SetItemData( item, flags, id, str );
}


/*******************************************************************
 *         ModifyMenuA    (USER32.@)
 */
BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
                         UINT_PTR id, LPCSTR str )
{
    BOOL ret = FALSE;

    if (IS_STRING_ITEM(flags) && str)
    {
        INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        if (newstr)
        {
            MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
            ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
            HeapFree( GetProcessHeap(), 0, newstr );
        }
        return ret;
    }
    else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
}


/**********************************************************************
 *         CreatePopupMenu    (USER32.@)
 */
HMENU WINAPI CreatePopupMenu(void)
{
    HMENU hmenu;
    POPUPMENU *menu;

    if (!(hmenu = CreateMenu())) return 0;
    menu = MENU_GetMenu( hmenu );
    menu->wFlags |= MF_POPUP;
    menu->bTimeToHide = FALSE;
    return hmenu;
}


/**********************************************************************
 *         GetMenuCheckMarkDimensions    (USER.417)
 *         GetMenuCheckMarkDimensions    (USER32.@)
 */
DWORD WINAPI GetMenuCheckMarkDimensions(void)
{
    return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
}


/**********************************************************************
 *         SetMenuItemBitmaps    (USER32.@)
 */
BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
                                    HBITMAP hNewUnCheck, HBITMAP hNewCheck)
{
    MENUITEM *item;

    if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;

    if (!hNewCheck && !hNewUnCheck)
    {
	item->fState &= ~MF_USECHECKBITMAPS;
    }
    else  /* Install new bitmaps */
    {
	item->hCheckBit = hNewCheck;
	item->hUnCheckBit = hNewUnCheck;
	item->fState |= MF_USECHECKBITMAPS;
    }
    return TRUE;
}


/**********************************************************************
 *         CreateMenu    (USER32.@)
 */
HMENU WINAPI CreateMenu(void)
{
    HMENU hMenu;
    LPPOPUPMENU menu;
    if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
    menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);

    ZeroMemory(menu, sizeof(POPUPMENU));
    menu->wMagic = MENU_MAGIC;
    menu->FocusedItem = NO_SELECTED_ITEM;
    menu->bTimeToHide = FALSE;

    TRACE("return %p\n", hMenu );

    return hMenu;
}


/**********************************************************************
 *         DestroyMenu    (USER32.@)
 */
BOOL WINAPI DestroyMenu( HMENU hMenu )
{
    LPPOPUPMENU lppop = MENU_GetMenu(hMenu);

    TRACE("(%p)\n", hMenu);


    if (!lppop) return FALSE;

    lppop->wMagic = 0;  /* Mark it as destroyed */

    /* DestroyMenu should not destroy system menu popup owner */
    if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
    {
        DestroyWindow( lppop->hWnd );
        lppop->hWnd = 0;
    }

    if (lppop->items) /* recursively destroy submenus */
    {
        int i;
        MENUITEM *item = lppop->items;
        for (i = lppop->nItems; i > 0; i--, item++)
        {
            if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
            MENU_FreeItemData( item );
        }
        HeapFree( GetProcessHeap(), 0, lppop->items );
    }
    USER_HEAP_FREE( hMenu );
    return TRUE;
}


/**********************************************************************
 *         GetSystemMenu    (USER32.@)
 */
HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
{
    WND *wndPtr = WIN_GetPtr( hWnd );
    HMENU retvalue = 0;

    if (wndPtr == WND_DESKTOP) return 0;
    if (wndPtr == WND_OTHER_PROCESS)
    {
        if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
    }
    else if (wndPtr)
    {
	if (wndPtr->hSysMenu && bRevert)
	{
            DestroyMenu(wndPtr->hSysMenu);
            wndPtr->hSysMenu = 0;
	}

	if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
	    wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );

	if( wndPtr->hSysMenu )
        {
	    POPUPMENU *menu;
	    retvalue = GetSubMenu(wndPtr->hSysMenu, 0);

	    /* Store the dummy sysmenu handle to facilitate the refresh */
	    /* of the close button if the SC_CLOSE item change */
	    menu = MENU_GetMenu(retvalue);
	    if ( menu )
	       menu->hSysMenuOwner = wndPtr->hSysMenu;
        }
        WIN_ReleasePtr( wndPtr );
    }
    return bRevert ? 0 : retvalue;
}


/*******************************************************************
 *         SetSystemMenu    (USER32.@)
 */
BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
{
    WND *wndPtr = WIN_GetPtr( hwnd );

    if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
    {
	if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
	wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
        WIN_ReleasePtr( wndPtr );
	return TRUE;
    }
    return FALSE;
}


/**********************************************************************
 *         GetMenu    (USER32.@)
 */
HMENU WINAPI GetMenu( HWND hWnd )
{
    HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
    TRACE("for %p returning %p\n", hWnd, retvalue);
    return retvalue;
}

/**********************************************************************
 *         GetMenuBarInfo    (USER32.@)
 */
BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
{
    FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
    return FALSE;
}

/**********************************************************************
 *         MENU_SetMenu
 *
 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
 * SetWindowPos call that would result if SetMenu were called directly.
 */
BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
{
    TRACE("(%p, %p);\n", hWnd, hMenu);

    if (hMenu && !IsMenu(hMenu))
    {
        WARN("hMenu %p is not a menu handle\n", hMenu);
        return FALSE;
    }
    if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
        return FALSE;

    hWnd = WIN_GetFullHandle( hWnd );
    if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */

    if (hMenu != 0)
    {
        LPPOPUPMENU lpmenu;

        if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;

        lpmenu->hWnd = hWnd;
        lpmenu->Height = 0;  /* Make sure we recalculate the size */
    }
    SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
    return TRUE;
}


/**********************************************************************
 *         SetMenu    (USER32.@)
 */
BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
{   
    if(!MENU_SetMenu(hWnd, hMenu))
        return FALSE;
 
    SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
    return TRUE;
}


/**********************************************************************
 *         GetSubMenu    (USER32.@)
 */
HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
{
    MENUITEM * lpmi;

    if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
    if (!(lpmi->fType & MF_POPUP)) return 0;
    return lpmi->hSubMenu;
}


/**********************************************************************
 *         DrawMenuBar    (USER32.@)
 */
BOOL WINAPI DrawMenuBar( HWND hWnd )
{
    LPPOPUPMENU lppop;
    HMENU hMenu = GetMenu(hWnd);

    if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
        return FALSE;
    if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;

    lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
    lppop->hwndOwner = hWnd;
    SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
    return TRUE;
}

/***********************************************************************
 *           DrawMenuBarTemp   (USER32.@)
 *
 * UNDOCUMENTED !!
 *
 * called by W98SE desk.cpl Control Panel Applet
 *
 * Not 100% sure about the param names, but close.
 */
DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
{
    LPPOPUPMENU lppop;
    UINT i,retvalue;
    HFONT hfontOld = 0;
    BOOL flat_menu = FALSE;

    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);

    if (!hMenu)
        hMenu = GetMenu(hwnd);

    if (!hFont)
        hFont = get_menu_font(FALSE);

    lppop = MENU_GetMenu( hMenu );
    if (lppop == NULL || lprect == NULL)
    {
        retvalue = GetSystemMetrics(SM_CYMENU);
        goto END;
    }

    TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);

    hfontOld = SelectObject( hDC, hFont);

    if (lppop->Height == 0)
        MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);

    lprect->bottom = lprect->top + lppop->Height;

    FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );

    SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
    MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
    LineTo( hDC, lprect->right, lprect->bottom );

    if (lppop->nItems == 0)
    {
        retvalue = GetSystemMetrics(SM_CYMENU);
        goto END;
    }

    for (i = 0; i < lppop->nItems; i++)
    {
        MENU_DrawMenuItem( hwnd, hMenu, hwnd,
                           hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
    }
    retvalue = lppop->Height;

END:
    if (hfontOld) SelectObject (hDC, hfontOld);
    return retvalue;
}

/***********************************************************************
 *           EndMenu   (USER.187)
 *           EndMenu   (USER32.@)
 */
BOOL WINAPI EndMenu(void)
{
    /* if we are in the menu code, and it is active */
    if (!fEndMenu && top_popup)
    {
	/* terminate the menu handling code */
        fEndMenu = TRUE;

	/* needs to be posted to wakeup the internal menu handler */
	/* which will now terminate the menu, in the event that */
	/* the main window was minimized, or lost focus, so we */
	/* don't end up with an orphaned menu */
        PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
    }
    return fEndMenu;
}


/***********************************************************************
 *           LookupMenuHandle   (USER.217)
 */
HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
{
    HMENU hmenu32 = HMENU_32(hmenu);
    UINT id32 = id;
    if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
    else return HMENU_16(hmenu32);
}


/**********************************************************************
 *	    LoadMenu    (USER.150)
 */
HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
{
    HRSRC16 hRsrc;
    HGLOBAL16 handle;
    HMENU16 hMenu;

    if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
    if (!name) return 0;

    instance = GetExePtr( instance );
    if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
    if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
    hMenu = LoadMenuIndirect16(LockResource16(handle));
    FreeResource16( handle );
    return hMenu;
}


/*****************************************************************
 *        LoadMenuA   (USER32.@)
 */
HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
{
    HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
    if (!hrsrc) return 0;
    return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
}


/*****************************************************************
 *        LoadMenuW   (USER32.@)
 */
HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
{
    HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
    if (!hrsrc) return 0;
    return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
}


/**********************************************************************
 *	    LoadMenuIndirect    (USER.220)
 */
HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
{
    HMENU hMenu;
    WORD version, offset;
    LPCSTR p = (LPCSTR)template;

    TRACE("(%p)\n", template );
    version = GET_WORD(p);
    p += sizeof(WORD);
    if (version)
    {
        WARN("version must be 0 for Win16\n" );
        return 0;
    }
    offset = GET_WORD(p);
    p += sizeof(WORD) + offset;
    if (!(hMenu = CreateMenu())) return 0;
    if (!MENU_ParseResource( p, hMenu, FALSE ))
    {
        DestroyMenu( hMenu );
        return 0;
    }
    return HMENU_16(hMenu);
}


/**********************************************************************
 *	    LoadMenuIndirectW    (USER32.@)
 */
HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
{
    HMENU hMenu;
    WORD version, offset;
    LPCSTR p = (LPCSTR)template;

    version = GET_WORD(p);
    p += sizeof(WORD);
    TRACE("%p, ver %d\n", template, version );
    switch (version)
    {
      case 0: /* standard format is version of 0 */
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
	if (!(hMenu = CreateMenu())) return 0;
	if (!MENU_ParseResource( p, hMenu, TRUE ))
	  {
	    DestroyMenu( hMenu );
	    return 0;
	  }
	return hMenu;
      case 1: /* extended format is version of 1 */
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
	if (!(hMenu = CreateMenu())) return 0;
	if (!MENUEX_ParseResource( p, hMenu))
	  {
	    DestroyMenu( hMenu );
	    return 0;
	  }
	return hMenu;
      default:
        ERR("version %d not supported.\n", version);
        return 0;
    }
}


/**********************************************************************
 *	    LoadMenuIndirectA    (USER32.@)
 */
HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
{
    return LoadMenuIndirectW( template );
}


/**********************************************************************
 *		IsMenu    (USER32.@)
 */
BOOL WINAPI IsMenu(HMENU hmenu)
{
    LPPOPUPMENU menu = MENU_GetMenu(hmenu);

    if (!menu)
    {
        SetLastError(ERROR_INVALID_MENU_HANDLE);
        return FALSE;
    }
    return TRUE;
}

/**********************************************************************
 *		GetMenuItemInfo_common
 */

static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
					LPMENUITEMINFOW lpmii, BOOL unicode)
{
    MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);

    debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");

    if (!menu)
	return FALSE;
    
    if( lpmii->fMask & MIIM_TYPE) {
        if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
            WARN("invalid combination of fMask bits used\n");
            /* this does not happen on Win9x/ME */
            SetLastError( ERROR_INVALID_PARAMETER);
            return FALSE;
        }
	lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
        if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
	lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
        if( lpmii->fType & MFT_BITMAP) {
	    lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
	    lpmii->cch = 0;
        } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
            /* this does not happen on Win9x/ME */
	    lpmii->dwTypeData = 0;
	    lpmii->cch = 0;
        }
    }

    /* copy the text string */
    if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
         if( !menu->text ) {
                if(lpmii->dwTypeData && lpmii->cch) {
                    lpmii->cch = 0;
                    if( unicode)
                        *((WCHAR *)lpmii->dwTypeData) = 0;
                    else
                        *((CHAR *)lpmii->dwTypeData) = 0;
                }
         } else {
            int len;
            if (unicode)
            {
                len = strlenW(menu->text);
                if(lpmii->dwTypeData && lpmii->cch)
                    lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
            }
            else
            {
                len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
                        0, NULL, NULL ) - 1;
                if(lpmii->dwTypeData && lpmii->cch)
                    if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
                            (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
                        ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
            }
            /* if we've copied a substring we return its length */
            if(lpmii->dwTypeData && lpmii->cch)
                if (lpmii->cch <= len + 1) lpmii->cch--;
                else
                    lpmii->cch = len;
            else {
                /* return length of string */
                /* not on Win9x/ME if fType & MFT_BITMAP */
                lpmii->cch = len;
            }
        }
    }

    if (lpmii->fMask & MIIM_FTYPE)
	lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;

    if (lpmii->fMask & MIIM_BITMAP)
	lpmii->hbmpItem = menu->hbmpItem;

    if (lpmii->fMask & MIIM_STATE)
	lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;

    if (lpmii->fMask & MIIM_ID)
	lpmii->wID = menu->wID;

    if (lpmii->fMask & MIIM_SUBMENU)
	lpmii->hSubMenu = menu->hSubMenu;
    else {
        /* hSubMenu is always cleared 
         * (not on Win9x/ME ) */
        lpmii->hSubMenu = 0;
    }

    if (lpmii->fMask & MIIM_CHECKMARKS) {
	lpmii->hbmpChecked = menu->hCheckBit;
	lpmii->hbmpUnchecked = menu->hUnCheckBit;
    }
    if (lpmii->fMask & MIIM_DATA)
	lpmii->dwItemData = menu->dwItemData;

  return TRUE;
}

/**********************************************************************
 *		GetMenuItemInfoA    (USER32.@)
 */
BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOA lpmii)
{
    BOOL ret;
    MENUITEMINFOA mii;
    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    mii.cbSize = sizeof( mii);
    ret = GetMenuItemInfo_common (hmenu, item, bypos,
                                    (LPMENUITEMINFOW)&mii, FALSE);
    mii.cbSize = lpmii->cbSize;
    memcpy( lpmii, &mii, mii.cbSize);
    return ret;
}

/**********************************************************************
 *		GetMenuItemInfoW    (USER32.@)
 */
BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOW lpmii)
{
    BOOL ret;
    MENUITEMINFOW mii;
    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    mii.cbSize = sizeof( mii);
    ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
    mii.cbSize = lpmii->cbSize;
    memcpy( lpmii, &mii, mii.cbSize);
    return ret;
}


/* set a menu item text from a ASCII or Unicode string */
static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
{
    if (!text)
        menu->text = NULL;
    else if (unicode)
    {
        if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
            strcpyW( menu->text, text );
    }
    else
    {
        LPCSTR str = (LPCSTR)text;
        int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
            MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
    }
}


/**********************************************************************
 *		SetMenuItemInfo_common
 */

static BOOL SetMenuItemInfo_common(MENUITEM * menu,
				       const MENUITEMINFOW *lpmii,
				       BOOL unicode)
{
    if (!menu) return FALSE;

    debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");

    if (lpmii->fMask & MIIM_TYPE ) {
        if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
            WARN("invalid combination of fMask bits used\n");
            /* this does not happen on Win9x/ME */
            SetLastError( ERROR_INVALID_PARAMETER);
            return FALSE;
        }

        /* Remove the old type bits and replace them with the new ones */
        menu->fType &= ~MENUITEMINFO_TYPE_MASK;
        menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;

        if (IS_STRING_ITEM(menu->fType)) {
	    HeapFree(GetProcessHeap(), 0, menu->text);
            set_menu_item_text( menu, lpmii->dwTypeData, unicode );
        } else if( (menu->fType) & MFT_BITMAP)
                menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
    }

    if (lpmii->fMask & MIIM_FTYPE ) {
        if(( lpmii->fType & MFT_BITMAP)) {
            SetLastError( ERROR_INVALID_PARAMETER);
            return FALSE;
        }
        menu->fType &= ~MENUITEMINFO_TYPE_MASK;
        menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
    }
    if (lpmii->fMask & MIIM_STRING ) {
        /* free the string when used */
        HeapFree(GetProcessHeap(), 0, menu->text);
        set_menu_item_text( menu, lpmii->dwTypeData, unicode );
    }

    if (lpmii->fMask & MIIM_STATE)
    {
         /* Other menu items having MFS_DEFAULT are not converted
           to normal items */
         menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
    }

    if (lpmii->fMask & MIIM_ID)
	menu->wID = lpmii->wID;

    if (lpmii->fMask & MIIM_SUBMENU) {
	menu->hSubMenu = lpmii->hSubMenu;
	if (menu->hSubMenu) {
	    POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
	    if (subMenu) {
		subMenu->wFlags |= MF_POPUP;
		menu->fType |= MF_POPUP;
	    }
	    else {
                SetLastError( ERROR_INVALID_PARAMETER);
                return FALSE;
            }
	}
	else
	    menu->fType &= ~MF_POPUP;
    }

    if (lpmii->fMask & MIIM_CHECKMARKS)
    {
	menu->hCheckBit = lpmii->hbmpChecked;
	menu->hUnCheckBit = lpmii->hbmpUnchecked;
    }
    if (lpmii->fMask & MIIM_DATA)
	menu->dwItemData = lpmii->dwItemData;

    if (lpmii->fMask & MIIM_BITMAP)
	menu->hbmpItem = lpmii->hbmpItem;

    if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
        menu->fType |= MFT_SEPARATOR;

    debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
    return TRUE;
}

/**********************************************************************
 *		SetMenuItemInfoA    (USER32.@)
 */
BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
                                 const MENUITEMINFOA *lpmii)
{
    MENUITEMINFOA mii;

    TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);

    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    if( lpmii->cbSize != sizeof( mii)) {
        mii.cbSize = sizeof( mii);
        mii.hbmpItem = NULL;
    }
    return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
				    (const MENUITEMINFOW *)&mii, FALSE);
}

/**********************************************************************
 *		SetMenuItemInfoW    (USER32.@)
 */
BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
                                 const MENUITEMINFOW *lpmii)
{
    MENUITEMINFOW mii;

    TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);

    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    if( lpmii->cbSize != sizeof( mii)) {
        mii.cbSize = sizeof( mii);
        mii.hbmpItem = NULL;
    }
    return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
                &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
}

/**********************************************************************
 *		SetMenuDefaultItem    (USER32.@)
 *
 */
BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
{
	UINT i;
	POPUPMENU *menu;
	MENUITEM *item;

	TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);

	if (!(menu = MENU_GetMenu(hmenu))) return FALSE;

	/* reset all default-item flags */
	item = menu->items;
	for (i = 0; i < menu->nItems; i++, item++)
	{
	    item->fState &= ~MFS_DEFAULT;
	}

	/* no default item */
	if ( -1 == uItem)
	{
	    return TRUE;
	}

	item = menu->items;
	if ( bypos )
	{
	    if ( uItem >= menu->nItems ) return FALSE;
	    item[uItem].fState |= MFS_DEFAULT;
	    return TRUE;
	}
	else
	{
	    for (i = 0; i < menu->nItems; i++, item++)
	    {
		if (item->wID == uItem)
		{
		     item->fState |= MFS_DEFAULT;
		     return TRUE;
		}
	    }

	}
	return FALSE;
}

/**********************************************************************
 *		GetMenuDefaultItem    (USER32.@)
 */
UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
{
	POPUPMENU *menu;
	MENUITEM * item;
	UINT i = 0;

	TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);

	if (!(menu = MENU_GetMenu(hmenu))) return -1;

	/* find default item */
	item = menu->items;

	/* empty menu */
	if (! item) return -1;

	while ( !( item->fState & MFS_DEFAULT ) )
	{
	    i++; item++;
	    if  (i >= menu->nItems ) return -1;
	}

	/* default: don't return disabled items */
	if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;

	/* search rekursiv when needed */
	if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
	{
	    UINT ret;
	    ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
	    if ( -1 != ret ) return ret;

	    /* when item not found in submenu, return the popup item */
	}
	return ( bypos ) ? i : item->wID;

}


/**********************************************************************
 *		InsertMenuItemA    (USER32.@)
 */
BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOA *lpmii)
{
    MENUITEM *item;
    MENUITEMINFOA mii;

    TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);

    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    if( lpmii->cbSize != sizeof( mii)) {
        mii.cbSize = sizeof( mii);
        mii.hbmpItem = NULL;
    }

    item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
    return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
}


/**********************************************************************
 *		InsertMenuItemW    (USER32.@)
 */
BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOW *lpmii)
{
    MENUITEM *item;
    MENUITEMINFOW mii;

    TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);

    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    if( lpmii->cbSize != sizeof( mii)) {
        mii.cbSize = sizeof( mii);
        mii.hbmpItem = NULL;
    }

    item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
    return SetMenuItemInfo_common(item, &mii, TRUE);
}

/**********************************************************************
 *		CheckMenuRadioItem    (USER32.@)
 */

BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
				   UINT first, UINT last, UINT check,
				   UINT bypos)
{
    BOOL done = FALSE;
    UINT i;
    MENUITEM *mi_first = NULL, *mi_check;
    HMENU m_first, m_check;

    for (i = first; i <= last; i++) { UINT pos = i; if (!mi_first) { m_first = hMenu; mi_first = MENU_FindItem(&m_first, &pos, bypos); if (!mi_first) continue; mi_check = mi_first; m_check = m_first; } else { m_check = hMenu; mi_check = MENU_FindItem(&m_check, &pos, bypos); if (!mi_check) continue; } if (m_first != m_check) continue; if (mi_check->fType == MFT_SEPARATOR) continue;

        if (i == check)
        {
            mi_check->fType |= MFT_RADIOCHECK;
            mi_check->fState |= MFS_CHECKED;
            done = TRUE;
        }
        else
        {
            /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
            mi_check->fState &= ~MFS_CHECKED;
        }
    }

    return done;
}


/**********************************************************************
 *		GetMenuItemRect    (USER32.@)
 *
 *      ATTENTION: Here, the returned values in rect are the screen
 *                 coordinates of the item just like if the menu was
 *                 always on the upper left side of the application.
 *
 */
BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
				 LPRECT rect)
{
     POPUPMENU *itemMenu;
     MENUITEM *item;
     HWND referenceHwnd;

     TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);

     item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
     referenceHwnd = hwnd;

     if(!hwnd)
     {
	 itemMenu = MENU_GetMenu(hMenu);
	 if (itemMenu == NULL)
	     return FALSE;

	 if(itemMenu->hWnd == 0)
	     return FALSE;
	 referenceHwnd = itemMenu->hWnd;
     }

     if ((rect == NULL) || (item == NULL))
	 return FALSE;

     *rect = item->rect;

     MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);

     return TRUE;
}


/**********************************************************************
 *		SetMenuInfo    (USER32.@)
 *
 * FIXME
 *	MIM_APPLYTOSUBMENUS
 *	actually use the items to draw the menu
 */
BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
{
    POPUPMENU *menu;

    TRACE("(%p %p)\n", hMenu, lpmi);

    if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
    {

	if (lpmi->fMask & MIM_BACKGROUND)
	    menu->hbrBack = lpmi->hbrBack;

	if (lpmi->fMask & MIM_HELPID)
	    menu->dwContextHelpID = lpmi->dwContextHelpID;

	if (lpmi->fMask & MIM_MAXHEIGHT)
	    menu->cyMax = lpmi->cyMax;

	if (lpmi->fMask & MIM_MENUDATA)
	    menu->dwMenuData = lpmi->dwMenuData;

	if (lpmi->fMask & MIM_STYLE)
	{
	    menu->dwStyle = lpmi->dwStyle;
	    if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
	    if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
	    if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
	    if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
	}

	return TRUE;
    }
    return FALSE;
}

/**********************************************************************
 *		GetMenuInfo    (USER32.@)
 *
 *  NOTES
 *	win98/NT5.0
 *
 */
BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
{   POPUPMENU *menu;

    TRACE("(%p %p)\n", hMenu, lpmi);

    if (lpmi && (menu = MENU_GetMenu(hMenu)))
    {

	if (lpmi->fMask & MIM_BACKGROUND)
	    lpmi->hbrBack = menu->hbrBack;

	if (lpmi->fMask & MIM_HELPID)
	    lpmi->dwContextHelpID = menu->dwContextHelpID;

	if (lpmi->fMask & MIM_MAXHEIGHT)
	    lpmi->cyMax = menu->cyMax;

	if (lpmi->fMask & MIM_MENUDATA)
	    lpmi->dwMenuData = menu->dwMenuData;

	if (lpmi->fMask & MIM_STYLE)
	    lpmi->dwStyle = menu->dwStyle;

	return TRUE;
    }
    return FALSE;
}


/**********************************************************************
 *         SetMenuContextHelpId    (USER32.@)
 */
BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
{
    LPPOPUPMENU menu;

    TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);

    if ((menu = MENU_GetMenu(hMenu)))
    {
	menu->dwContextHelpID = dwContextHelpID;
	return TRUE;
    }
    return FALSE;
}


/**********************************************************************
 *         GetMenuContextHelpId    (USER32.@)
 */
DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
{
    LPPOPUPMENU menu;

    TRACE("(%p)\n", hMenu);

    if ((menu = MENU_GetMenu(hMenu)))
    {
	return menu->dwContextHelpID;
    }
    return 0;
}

/**********************************************************************
 *         MenuItemFromPoint    (USER32.@)
 */
INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
{
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT pos;

    /*FIXME: Do we have to handle hWnd here? */
    if (!menu) return -1;
    if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
    return pos;
}


/**********************************************************************
 *           translate_accelerator
 */
static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                                   BYTE fVirt, WORD key, WORD cmd )
{
    INT mask = 0;
    UINT mesg = 0;

    if (wParam != key) return FALSE;

    if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
    if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
    if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;

    if (message == WM_CHAR || message == WM_SYSCHAR)
    {
        if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
        {
            TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
            goto found;
        }
    }
    else
    {
        if(fVirt & FVIRTKEY)
        {
            TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
                          wParam, 0xff & HIWORD(lParam));

            if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
            TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
        }
        else
        {
            if (!(lParam & 0x01000000))  /* no special_key */
            {
                if ((fVirt & FALT) && (lParam & 0x20000000))
                {                              /* ^^ ALT pressed */
                    TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
                    goto found;
                }
            }
        }
    }
    return FALSE;

 found:
    if (message == WM_KEYUP || message == WM_SYSKEYUP)
        mesg = 1;
    else
    {
        HMENU hMenu, hSubMenu, hSysMenu;
        UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;

        hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
        hSysMenu = get_win_sys_menu( hWnd );

        /* find menu item and ask application to initialize it */
        /* 1. in the system menu */
        hSubMenu = hSysMenu;
        nPos = cmd;
        if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
        {
            if (GetCapture())
                mesg = 2;
            if (!IsWindowEnabled(hWnd))
                mesg = 3;
            else
            {
                SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
                if(hSubMenu != hSysMenu)
                {
                    nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
                    TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
                    SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
                }
                uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
            }
        }
        else /* 2. in the window's menu */
        {
            hSubMenu = hMenu;
            nPos = cmd;
            if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
            {
                if (GetCapture())
                    mesg = 2;
                if (!IsWindowEnabled(hWnd))
                    mesg = 3;
                else
                {
                    SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
                    if(hSubMenu != hMenu)
                    {
                        nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
                        TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
                        SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
                    }
                    uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
                }
            }
        }

        if (mesg == 0)
        {
            if (uSysStat != (UINT)-1)
            {
                if (uSysStat & (MF_DISABLED|MF_GRAYED))
                    mesg=4;
                else
                    mesg=WM_SYSCOMMAND;
            }
            else
            {
                if (uStat != (UINT)-1)
                {
                    if (IsIconic(hWnd))
                        mesg=5;
                    else
                    {
                        if (uStat & (MF_DISABLED|MF_GRAYED))
                            mesg=6;
                        else
                            mesg=WM_COMMAND;
                    }
                }
                else
                    mesg=WM_COMMAND;
            }
        }
    }

    if( mesg==WM_COMMAND )
    {
        TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
        SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
    }
    else if( mesg==WM_SYSCOMMAND )
    {
        TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
        SendMessageW(hWnd, mesg, cmd, 0x00010000L);
    }
    else
    {
        /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
         *   #0: unknown (please report!)
         *   #1: for WM_KEYUP,WM_SYSKEYUP
         *   #2: mouse is captured
         *   #3: window is disabled
         *   #4: it's a disabled system menu option
         *   #5: it's a menu option, but window is iconic
         *   #6: it's a menu option, but disabled
         */
        TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
        if(mesg==0)
            ERR_(accel)(" unknown reason - please report!\n");
    }
    return TRUE;
}

/**********************************************************************
 *      TranslateAcceleratorA     (USER32.@)
 *      TranslateAccelerator      (USER32.@)
 */
INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
{
    /* YES, Accel16! */
    LPACCEL16 lpAccelTbl;
    int i;
    WPARAM wParam;

    if (!hWnd || !msg) return 0;

    if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
    {
        WARN_(accel)("invalid accel handle=%p\n", hAccel);
        return 0;
    }

    wParam = msg->wParam;

    switch (msg->message)
    {
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
        break;

    case WM_CHAR:
    case WM_SYSCHAR:
        {
            char ch = LOWORD(wParam);
            WCHAR wch;
            MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
            wParam = MAKEWPARAM(wch, HIWORD(wParam));
        }
        break;

    default:
        return 0;
    }

    TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
                  hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
    i = 0;
    do
    {
        if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
                                   lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
            return 1;
    } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);

    return 0;
}

/**********************************************************************
 *      TranslateAcceleratorW     (USER32.@)
 */
INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
{
    /* YES, Accel16! */
    LPACCEL16 lpAccelTbl;
    int i;

    if (!hWnd || !msg) return 0;

    if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
    {
        WARN_(accel)("invalid accel handle=%p\n", hAccel);
        return 0;
    }

    switch (msg->message)
    {
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
    case WM_CHAR:
    case WM_SYSCHAR:
        break;

    default:
        return 0;
    }

    TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
                  hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
    i = 0;
    do
    {
        if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
                                   lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
            return 1;
    } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);

    return 0;
}


 

 

 

3번.configure: Add check for presence of _strdup() and strdup( )..

 

 

 



/*
 * Wine porting definitions
 *
 * Copyright 1996 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#ifndef __WINE_WINE_PORT_H
#define __WINE_WINE_PORT_H

#ifndef __WINE_CONFIG_H
# error You must include config.h to use this header
#endif

#ifdef __WINE_BASETSD_H
# error You must include port.h before all other headers
#endif

#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE  /* for pread/pwrite */
#include <fcntl.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_DIRECT_H
# include <direct.h>
#endif
#ifdef HAVE_IO_H
# include <io.h>
#endif
#ifdef HAVE_PROCESS_H
# include <process.h>
#endif
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif


/****************************************************************
 * Type definitions
 */

#if !defined(_MSC_VER) && !defined(__int64)
#  if defined(__x86_64__) || defined(_WIN64)
#    define __int64 long
#  else
#    define __int64 long long
#  endif
#endif

#ifndef HAVE_MODE_T
typedef int mode_t;
#endif
#ifndef HAVE_OFF_T
typedef long off_t;
#endif
#ifndef HAVE_PID_T
typedef int pid_t;
#endif
#ifndef HAVE_SIZE_T
typedef unsigned int size_t;
#endif
#ifndef HAVE_SSIZE_T
typedef int ssize_t;
#endif
#ifndef HAVE_FSBLKCNT_T
typedef unsigned long fsblkcnt_t;
#endif
#ifndef HAVE_FSFILCNT_T
typedef unsigned long fsfilcnt_t;
#endif

#ifndef HAVE_STRUCT_STATVFS_F_BLOCKS
struct statvfs
{
    unsigned long f_bsize;
    unsigned long f_frsize;
    fsblkcnt_t    f_blocks;
    fsblkcnt_t    f_bfree;
    fsblkcnt_t    f_bavail;
    fsfilcnt_t    f_files;
    fsfilcnt_t    f_ffree;
    fsfilcnt_t    f_favail;
    unsigned long f_fsid;
    unsigned long f_flag;
    unsigned long f_namemax;
};
#endif /* HAVE_STRUCT_STATVFS_F_BLOCKS */


/****************************************************************
 * Macro definitions
 */

#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#else
#define RTLD_LAZY    0x001
#define RTLD_NOW     0x002
#define RTLD_GLOBAL  0x100
#endif

#if !defined(HAVE_FTRUNCATE) && defined(HAVE_CHSIZE)
#define ftruncate chsize
#endif

#if !defined(HAVE_POPEN) && defined(HAVE__POPEN)
#define popen _popen
#endif

#if !defined(HAVE_PCLOSE) && defined(HAVE__PCLOSE)
#define pclose _pclose
#endif

#if !defined(HAVE_STRDUP) && defined(HAVE__STRDUP)
#define strdup _strdup
#endif

#if !defined(HAVE_SNPRINTF) && defined(HAVE__SNPRINTF)
#define snprintf _snprintf
#endif

#if !defined(HAVE_VSNPRINTF) && defined(HAVE__VSNPRINTF)
#define vsnprintf _vsnprintf
#endif

#ifndef S_ISLNK
# define S_ISLNK(mod) (0)
#endif

#ifndef S_ISSOCK
# define S_ISSOCK(mod) (0)
#endif

#ifndef S_ISDIR
# define S_ISDIR(mod) (((mod) & _S_IFMT) == _S_IFDIR)
#endif

#ifndef S_ISCHR
# define S_ISCHR(mod) (((mod) & _S_IFMT) == _S_IFCHR)
#endif

#ifndef S_ISFIFO
# define S_ISFIFO(mod) (((mod) & _S_IFMT) == _S_IFIFO)
#endif

#ifndef S_ISREG
# define S_ISREG(mod) (((mod) & _S_IFMT) == _S_IFREG)
#endif

#ifndef S_IWUSR
# define S_IWUSR 0
#endif

/* So we open files in 64 bit access mode on Linux */
#ifndef O_LARGEFILE
# define O_LARGEFILE 0
#endif

#ifndef O_NONBLOCK
# define O_NONBLOCK 0
#endif

#ifndef O_BINARY
# define O_BINARY 0
#endif

#if !defined(S_IXUSR) && defined(S_IEXEC)
# define S_IXUSR S_IEXEC
#endif
#if !defined(S_IXGRP) && defined(S_IEXEC)
# define S_IXGRP S_IEXEC
#endif
#if !defined(S_IXOTH) && defined(S_IEXEC)
# define S_IXOTH S_IEXEC
#endif


/****************************************************************
 * Constants
 */

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#ifndef M_PI_2
#define M_PI_2 1.570796326794896619
#endif


/* Macros to define assembler functions somewhat portably */

#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(__APPLE__)
# define __ASM_GLOBAL_FUNC(name,code) \
      __asm__( ".text\n\t" \
               ".align 4\n\t" \
               ".globl " __ASM_NAME(#name) "\n\t" \
               __ASM_FUNC(#name) "\n" \
               __ASM_NAME(#name) ":\n\t" \
               code \
               "\n\t.previous" );
#else  /* defined(__GNUC__) && !defined(__MINGW32__) && !defined(__APPLE__)  */
# define __ASM_GLOBAL_FUNC(name,code) \
      void __asm_dummy_##name(void) { \
          asm( ".align 4\n\t" \
               ".globl " __ASM_NAME(#name) "\n\t" \
               __ASM_FUNC(#name) "\n" \
               __ASM_NAME(#name) ":\n\t" \
               code ); \
      }
#endif  /* __GNUC__ */


/* Register functions */

#ifdef __i386__
#define DEFINE_REGS_ENTRYPOINT( name, args, pop_args ) \
    __ASM_GLOBAL_FUNC( name, \
                       "pushl %eax\n\t" \
                       "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t" \
                       ".long " __ASM_NAME("__regs_") #name "-.\n\t" \
                       ".byte " #args "," #pop_args )
/* FIXME: add support for other CPUs */
#endif  /* __i386__ */


/****************************************************************
 * Function definitions (only when using libwine_port)
 */

#ifndef NO_LIBWINE_PORT

#ifndef HAVE_FSTATVFS
int fstatvfs( int fd, struct statvfs *buf );
#endif

#ifndef HAVE_GETOPT_LONG
extern char *optarg;
extern int optind;
extern int opterr;
extern int optopt;
struct option;

#ifndef HAVE_STRUCT_OPTION_NAME
struct option
{
    const char *name;
    int has_arg;
    int *flag;
    int val;
};
#endif

extern int getopt_long (int ___argc, char *const *___argv,
                        const char *__shortopts,
                        const struct option *__longopts, int *__longind);
extern int getopt_long_only (int ___argc, char *const *___argv,
                             const char *__shortopts,
                             const struct option *__longopts, int *__longind);
#endif  /* HAVE_GETOPT_LONG */

#ifndef HAVE_FFS
int ffs( int x );
#endif

#ifndef HAVE_FUTIMES
struct timeval;
int futimes(int fd, const struct timeval *tv);
#endif

#ifndef HAVE_GETPAGESIZE
size_t getpagesize(void);
#endif  /* HAVE_GETPAGESIZE */

#ifndef HAVE_GETTID
pid_t gettid(void);
#endif /* HAVE_GETTID */

#ifndef HAVE_LSTAT
int lstat(const char *file_name, struct stat *buf);
#endif /* HAVE_LSTAT */

#ifndef HAVE_MEMMOVE
void *memmove(void *dest, const void *src, size_t len);
#endif /* !defined(HAVE_MEMMOVE) */

#ifndef HAVE_PREAD
ssize_t pread( int fd, void *buf, size_t count, off_t offset );
#endif /* HAVE_PREAD */

#ifndef HAVE_PWRITE
ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset );
#endif /* HAVE_PWRITE */

#ifndef HAVE_READLINK
int readlink( const char *path, char *buf, size_t size );
#endif /* HAVE_READLINK */

#ifndef HAVE_SIGSETJMP
# include <setjmp.h>
typedef jmp_buf sigjmp_buf;
int sigsetjmp( sigjmp_buf buf, int savesigs );
void siglongjmp( sigjmp_buf buf, int val );
#endif /* HAVE_SIGSETJMP */

#ifndef HAVE_STATVFS
int statvfs( const char *path, struct statvfs *buf );
#endif

#ifndef HAVE_STRNCASECMP
# ifndef HAVE__STRNICMP
int strncasecmp(const char *str1, const char *str2, size_t n);
# else
# define strncasecmp _strnicmp
# endif
#endif /* !defined(HAVE_STRNCASECMP) */

#ifndef HAVE_STRERROR
const char *strerror(int err);
#endif /* !defined(HAVE_STRERROR) */

#ifndef HAVE_STRCASECMP
# ifndef HAVE__STRICMP
int strcasecmp(const char *str1, const char *str2);
# else
# define strcasecmp _stricmp
# endif
#endif /* !defined(HAVE_STRCASECMP) */

#ifndef HAVE_USLEEP
int usleep (unsigned int useconds);
#endif /* !defined(HAVE_USLEEP) */

#ifdef __i386__
static inline void *memcpy_unaligned( void *dst, const void *src, size_t size )
{
    return memcpy( dst, src, size );
}
#else
extern void *memcpy_unaligned( void *dst, const void *src, size_t size );
#endif /* __i386__ */

extern int mkstemps(char *template, int suffix_len);

/* Process creation flags */
#ifndef _P_WAIT
# define _P_WAIT    0
# define _P_NOWAIT  1
# define _P_OVERLAY 2
# define _P_NOWAITO 3
# define _P_DETACH  4
#endif
#ifndef HAVE_SPAWNVP
extern int spawnvp(int mode, const char *cmdname, const char * const argv[]);
#endif

/* Interlocked functions */

#if defined(__i386__) && defined(__GNUC__)

extern inline int interlocked_cmpxchg( int *dest, int xchg, int compare );
extern inline void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare );
extern __int64 interlocked_cmpxchg64( __int64 *dest, __int64 xchg, __int64 compare );
extern inline int interlocked_xchg( int *dest, int val );
extern inline void *interlocked_xchg_ptr( void **dest, void *val );
extern inline int interlocked_xchg_add( int *dest, int incr );

extern inline int interlocked_cmpxchg( int *dest, int xchg, int compare )
{
    int ret;
    __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
                          : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
    return ret;
}

extern inline void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
{
    void *ret;
    __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
                          : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
    return ret;
}

extern inline int interlocked_xchg( int *dest, int val )
{
    int ret;
    __asm__ __volatile__( "lock; xchgl %0,(%1)"
                          : "=r" (ret) : "r" (dest), "0" (val) : "memory" );
    return ret;
}

extern inline void *interlocked_xchg_ptr( void **dest, void *val )
{
    void *ret;
    __asm__ __volatile__( "lock; xchgl %0,(%1)"
                          : "=r" (ret) : "r" (dest), "0" (val) : "memory" );
    return ret;
}

extern inline int interlocked_xchg_add( int *dest, int incr )
{
    int ret;
    __asm__ __volatile__( "lock; xaddl %0,(%1)"
                          : "=r" (ret) : "r" (dest), "0" (incr) : "memory" );
    return ret;
}

#else  /* __i386___ && __GNUC__ */

extern int interlocked_cmpxchg( int *dest, int xchg, int compare );
extern void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare );
extern __int64 interlocked_cmpxchg64( __int64 *dest, __int64 xchg, __int64 compare );
extern int interlocked_xchg( int *dest, int val );
extern void *interlocked_xchg_ptr( void **dest, void *val );
extern int interlocked_xchg_add( int *dest, int incr );

#endif  /* __i386___ && __GNUC__ */

#else /* NO_LIBWINE_PORT */

#define __WINE_NOT_PORTABLE(func) func##_is_not_portable func##_is_not_portable

#define ffs                     __WINE_NOT_PORTABLE(ffs)
#define fstatvfs                __WINE_NOT_PORTABLE(fstatvfs)
#define futimes                 __WINE_NOT_PORTABLE(futimes)
#define getopt_long             __WINE_NOT_PORTABLE(getopt_long)
#define getopt_long_only        __WINE_NOT_PORTABLE(getopt_long_only)
#define getpagesize             __WINE_NOT_PORTABLE(getpagesize)
#define interlocked_cmpxchg     __WINE_NOT_PORTABLE(interlocked_cmpxchg)
#define interlocked_cmpxchg_ptr __WINE_NOT_PORTABLE(interlocked_cmpxchg_ptr)
#define interlocked_xchg        __WINE_NOT_PORTABLE(interlocked_xchg)
#define interlocked_xchg_ptr    __WINE_NOT_PORTABLE(interlocked_xchg_ptr)
#define interlocked_xchg_add    __WINE_NOT_PORTABLE(interlocked_xchg_add)
#define lstat                   __WINE_NOT_PORTABLE(lstat)
#define memcpy_unaligned        __WINE_NOT_PORTABLE(memcpy_unaligned)
#undef memmove
#define memmove                 __WINE_NOT_PORTABLE(memmove)
#define pread                   __WINE_NOT_PORTABLE(pread)
#define pwrite                  __WINE_NOT_PORTABLE(pwrite)
#define spawnvp                 __WINE_NOT_PORTABLE(spawnvp)
#define statvfs                 __WINE_NOT_PORTABLE(statvfs)
#define strcasecmp              __WINE_NOT_PORTABLE(strcasecmp)
#define strerror                __WINE_NOT_PORTABLE(strerror)
#define strncasecmp             __WINE_NOT_PORTABLE(strncasecmp)
#define usleep                  __WINE_NOT_PORTABLE(usleep)

#endif /* NO_LIBWINE_PORT */

#endif /* !defined(__WINE_WINE_PORT_H) */


 

 

 

4번. wininet: config.h police..

 

 

 



/*
 * Wininet
 *
 * Copyright 1999 Corel Corporation
 *
 * Ulrich Czekalla
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#ifndef _WINE_INTERNET_H_
#define _WINE_INTERNET_H_

#ifndef __WINE_CONFIG_H
# error You must include config.h to use this header
#endif

#include "wine/unicode.h"

#include <time.h>
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <sys/types.h>
# include <netinet/in.h>
#endif
#ifdef HAVE_OPENSSL_SSL_H
#define DSA __ssl_DSA  /* avoid conflict with commctrl.h */
#undef FAR
# include <openssl/ssl.h>
#undef FAR
#define FAR do_not_use_this_in_wine
#undef DSA
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif

#if defined(__MINGW32__) || defined (_MSC_VER)
#include "winsock2.h"
#ifndef MSG_WAITALL
#define MSG_WAITALL 0
#endif
#else
#define closesocket close
#endif /* __MINGW32__ */

/* used for netconnection.c stuff */
typedef struct
{
    BOOL useSSL;
    int socketFD;
#ifdef HAVE_OPENSSL_SSL_H
    SSL *ssl_s;
    char *peek_msg;
    char *peek_msg_mem;
    size_t peek_len;
#endif
} WININET_NETCONNECTION;

inline static LPSTR WININET_strdup( LPCSTR str )
{
    LPSTR ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 );
    if (ret) strcpy( ret, str );
    return ret;
}

inline static LPWSTR WININET_strdupW( LPCWSTR str )
{
    LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1)*sizeof(WCHAR) );
    if (ret) strcpyW( ret, str );
    return ret;
}

inline static LPWSTR WININET_strdup_AtoW( LPCSTR str )
{
    int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0);
    LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
    if (ret)
        MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len);
    return ret;
}

inline static LPSTR WININET_strdup_WtoA( LPCWSTR str )
{
    int len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
    LPSTR ret = HeapAlloc( GetProcessHeap(), 0, len );
    if (ret)
        WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL);
    return ret;
}

inline static void WININET_find_data_WtoA(LPWIN32_FIND_DATAW dataW, LPWIN32_FIND_DATAA dataA)
{
    dataA->dwFileAttributes = dataW->dwFileAttributes;
    dataA->ftCreationTime   = dataW->ftCreationTime;
    dataA->ftLastAccessTime = dataW->ftLastAccessTime;
    dataA->ftLastWriteTime  = dataW->ftLastWriteTime;
    dataA->nFileSizeHigh    = dataW->nFileSizeHigh;
    dataA->nFileSizeLow     = dataW->nFileSizeLow;
    dataA->dwReserved0      = dataW->dwReserved0;
    dataA->dwReserved1      = dataW->dwReserved1;
    WideCharToMultiByte(CP_ACP, 0, dataW->cFileName, -1, 
        dataA->cFileName, sizeof(dataA->cFileName),
        NULL, NULL);
    WideCharToMultiByte(CP_ACP, 0, dataW->cAlternateFileName, -1, 
        dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName),
        NULL, NULL);
}

typedef enum
{
    WH_HINIT = INTERNET_HANDLE_TYPE_INTERNET,
    WH_HFTPSESSION = INTERNET_HANDLE_TYPE_CONNECT_FTP,
    WH_HGOPHERSESSION = INTERNET_HANDLE_TYPE_CONNECT_GOPHER,
    WH_HHTTPSESSION = INTERNET_HANDLE_TYPE_CONNECT_HTTP,
    WH_HFILE = INTERNET_HANDLE_TYPE_FTP_FILE,
    WH_HFTPFINDNEXT = INTERNET_HANDLE_TYPE_FTP_FIND,
    WH_HHTTPREQ = INTERNET_HANDLE_TYPE_HTTP_REQUEST,
} WH_TYPE;

#define INET_OPENURL 0x0001
#define INET_CALLBACKW 0x0002

struct _WININETHANDLEHEADER;
typedef struct _WININETHANDLEHEADER WININETHANDLEHEADER, *LPWININETHANDLEHEADER;

typedef void (*WININET_object_destructor)( LPWININETHANDLEHEADER );

struct _WININETHANDLEHEADER
{
    WH_TYPE htype;
    HINTERNET hInternet;
    DWORD  dwFlags;
    DWORD  dwContext;
    DWORD  dwError;
    DWORD  dwInternalFlags;
    DWORD  dwRefCount;
    WININET_object_destructor destroy;
    INTERNET_STATUS_CALLBACK lpfnStatusCB;
};


typedef struct
{
    WININETHANDLEHEADER hdr;
    LPWSTR  lpszAgent;
    LPWSTR  lpszProxy;
    LPWSTR  lpszProxyBypass;
    LPWSTR  lpszProxyUsername;
    LPWSTR  lpszProxyPassword;
    DWORD   dwAccessType;
} WININETAPPINFOW, *LPWININETAPPINFOW;


typedef struct
{
    WININETHANDLEHEADER hdr;
    WININETAPPINFOW *lpAppInfo;
    LPWSTR  lpszHostName; /* the final destination of the request */
    LPWSTR  lpszServerName; /* the name of the server we directly connect to */
    LPWSTR  lpszUserName;
    INTERNET_PORT nHostPort; /* the final destination port of the request */
    INTERNET_PORT nServerPort; /* the port of the server we directly connect to */
    struct sockaddr_in socketAddress;
} WININETHTTPSESSIONW, *LPWININETHTTPSESSIONW;

#define HDR_ISREQUEST		0x0001
#define HDR_COMMADELIMITED	0x0002
#define HDR_SEMIDELIMITED	0x0004

typedef struct
{
    LPWSTR lpszField;
    LPWSTR lpszValue;
    WORD wFlags;
    WORD wCount;
} HTTPHEADERW, *LPHTTPHEADERW;


typedef struct
{
    WININETHANDLEHEADER hdr;
    WININETHTTPSESSIONW *lpHttpSession;
    LPWSTR lpszPath;
    LPWSTR lpszVerb;
    LPWSTR lpszRawHeaders;
    WININET_NETCONNECTION netConnection;
    LPWSTR lpszVersion;
    LPWSTR lpszStatusText;
    DWORD dwContentLength; /* total number of bytes to be read */
    DWORD dwContentRead; /* bytes of the content read so far */
    HTTPHEADERW *pCustHeaders;
    DWORD nCustHeaders;
} WININETHTTPREQW, *LPWININETHTTPREQW;


struct _WININETFTPSESSIONW;

typedef struct
{
    WININETHANDLEHEADER hdr;
    struct _WININETFTPSESSIONW *lpFtpSession;
    BOOL session_deleted;
    int nDataSocket;
} WININETFTPFILE, *LPWININETFTPFILE;


typedef struct _WININETFTPSESSIONW
{
    WININETHANDLEHEADER hdr;
    WININETAPPINFOW *lpAppInfo;
    int sndSocket;
    int lstnSocket;
    int pasvSocket; /* data socket connected by us in case of passive FTP */
    LPWININETFTPFILE download_in_progress;
    struct sockaddr_in socketAddress;
    struct sockaddr_in lstnSocketAddress;
    LPWSTR  lpszPassword;
    LPWSTR  lpszUserName;
} WININETFTPSESSIONW, *LPWININETFTPSESSIONW;


typedef struct
{
    BOOL bIsDirectory;
    LPWSTR lpszName;
    DWORD nSize;
    struct tm tmLastModified;
    unsigned short permissions;
} FILEPROPERTIESW, *LPFILEPROPERTIESW;


typedef struct
{
    WININETHANDLEHEADER hdr;
    WININETFTPSESSIONW *lpFtpSession;
    DWORD index;
    DWORD size;
    LPFILEPROPERTIESW lpafp;
} WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;

struct WORKREQ_FTPPUTFILEW
{
    LPWSTR lpszLocalFile;
    LPWSTR lpszNewRemoteFile;
    DWORD  dwFlags;
    DWORD  dwContext;
};

struct WORKREQ_FTPSETCURRENTDIRECTORYW
{
    LPWSTR lpszDirectory;
};

struct WORKREQ_FTPCREATEDIRECTORYW
{
    LPWSTR lpszDirectory;
};

struct WORKREQ_FTPFINDFIRSTFILEW
{
    LPWSTR lpszSearchFile;
    LPWIN32_FIND_DATAW lpFindFileData;
    DWORD  dwFlags;
    DWORD  dwContext;
};

struct WORKREQ_FTPGETCURRENTDIRECTORYW
{
    LPWSTR lpszDirectory;
    DWORD *lpdwDirectory;
};

struct WORKREQ_FTPOPENFILEW
{
    LPWSTR lpszFilename;
    DWORD  dwAccess;
    DWORD  dwFlags;
    DWORD  dwContext;
};

struct WORKREQ_FTPGETFILEW
{
    LPWSTR lpszRemoteFile;
    LPWSTR lpszNewFile;
    BOOL   fFailIfExists;
    DWORD  dwLocalFlagsAttribute;
    DWORD  dwFlags;
    DWORD  dwContext;
};

struct WORKREQ_FTPDELETEFILEW
{
    LPWSTR lpszFilename;
};

struct WORKREQ_FTPREMOVEDIRECTORYW
{
    LPWSTR lpszDirectory;
};

struct WORKREQ_FTPRENAMEFILEW
{
    LPWSTR lpszSrcFile;
    LPWSTR lpszDestFile;
};

struct WORKREQ_FTPFINDNEXTW
{
    LPWIN32_FIND_DATAW lpFindFileData;
};

struct WORKREQ_HTTPSENDREQUESTW
{
    LPWSTR lpszHeader;
    DWORD  dwHeaderLength;
    LPVOID lpOptional;
    DWORD  dwOptionalLength;
    DWORD  dwContentLength;
    BOOL   bEndRequest;
};

struct WORKREQ_SENDCALLBACK
{
    DWORD     dwContext;
    DWORD     dwInternetStatus;
    LPVOID    lpvStatusInfo;
    DWORD     dwStatusInfoLength;
};

struct WORKREQ_INTERNETOPENURLW
{
    HINTERNET hInternet;
    LPWSTR     lpszUrl;
    LPWSTR     lpszHeaders;
    DWORD     dwHeadersLength;
    DWORD     dwFlags;
    DWORD     dwContext;
};

struct WORKREQ_INTERNETREADFILEEXA
{
    LPINTERNET_BUFFERSA lpBuffersOut;
};

typedef struct WORKREQ
{
    void (*asyncproc)(struct WORKREQ*);
    WININETHANDLEHEADER *hdr;

    union {
        struct WORKREQ_FTPPUTFILEW              FtpPutFileW;
        struct WORKREQ_FTPSETCURRENTDIRECTORYW  FtpSetCurrentDirectoryW;
        struct WORKREQ_FTPCREATEDIRECTORYW      FtpCreateDirectoryW;
        struct WORKREQ_FTPFINDFIRSTFILEW        FtpFindFirstFileW;
        struct WORKREQ_FTPGETCURRENTDIRECTORYW  FtpGetCurrentDirectoryW;
        struct WORKREQ_FTPOPENFILEW             FtpOpenFileW;
        struct WORKREQ_FTPGETFILEW              FtpGetFileW;
        struct WORKREQ_FTPDELETEFILEW           FtpDeleteFileW;
        struct WORKREQ_FTPREMOVEDIRECTORYW      FtpRemoveDirectoryW;
        struct WORKREQ_FTPRENAMEFILEW           FtpRenameFileW;
        struct WORKREQ_FTPFINDNEXTW             FtpFindNextW;
        struct WORKREQ_HTTPSENDREQUESTW         HttpSendRequestW;
        struct WORKREQ_SENDCALLBACK             SendCallback;
	struct WORKREQ_INTERNETOPENURLW         InternetOpenUrlW;
        struct WORKREQ_INTERNETREADFILEEXA      InternetReadFileExA;
    } u;

} WORKREQUEST, *LPWORKREQUEST;

HINTERNET WININET_AllocHandle( LPWININETHANDLEHEADER info );
LPWININETHANDLEHEADER WININET_GetObject( HINTERNET hinternet );
LPWININETHANDLEHEADER WININET_AddRef( LPWININETHANDLEHEADER info );
BOOL WININET_Release( LPWININETHANDLEHEADER info );
BOOL WININET_FreeHandle( HINTERNET hinternet );

time_t ConvertTimeString(LPCWSTR asctime);

HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
	INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
	LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
	DWORD dwInternalFlags);

HINTERNET HTTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
	INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
	LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
	DWORD dwInternalFlags);

BOOL GetAddress(LPCWSTR lpszServerName, INTERNET_PORT nServerPort,
	struct sockaddr_in *psa);

void INTERNET_SetLastError(DWORD dwError);
DWORD INTERNET_GetLastError(void);
BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest);
LPSTR INTERNET_GetResponseBuffer(void);
LPSTR INTERNET_GetNextLine(INT nSocket, LPDWORD dwLen);
BOOL INTERNET_ReadFile(LPWININETHANDLEHEADER lpwh, LPVOID lpBuffer,
                       DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead,
                       BOOL bWait, BOOL bSendCompletionStatus);

BOOLAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
    LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext);
BOOLAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
BOOLAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
INTERNETAPI HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
    LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext);
BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData);
BOOLAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
	LPDWORD lpdwCurrentDirectory);
BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
BOOL FTP_FtpRenameFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest);
BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName);
HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName,
	DWORD fdwAccess, DWORD dwFlags, DWORD dwContext);
BOOLAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
	BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
	DWORD dwContext);

BOOLAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
	DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
	DWORD dwContentLength, BOOL bEndRequest);
INTERNETAPI HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
	LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
	LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
	DWORD dwFlags, DWORD dwContext);
BOOL HTTP_FinishedReading(LPWININETHTTPREQW lpwhr);

VOID SendAsyncCallback(LPWININETHANDLEHEADER hdr, DWORD dwContext,
                       DWORD dwInternetStatus, LPVOID lpvStatusInfo,
                       DWORD dwStatusInfoLength);

VOID INTERNET_SendCallback(LPWININETHANDLEHEADER hdr, DWORD dwContext,
                           DWORD dwInternetStatus, LPVOID lpvStatusInfo,
                           DWORD dwStatusInfoLength);

LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW lpwhr, LPCWSTR header);

BOOL NETCON_connected(WININET_NETCONNECTION *connection);
BOOL NETCON_init(WININET_NETCONNECTION *connnection, BOOL useSSL);
BOOL NETCON_create(WININET_NETCONNECTION *connection, int domain,
	      int type, int protocol);
BOOL NETCON_close(WININET_NETCONNECTION *connection);
BOOL NETCON_connect(WININET_NETCONNECTION *connection, const struct sockaddr *serv_addr,
		    unsigned int addrlen);
BOOL NETCON_secure_connect(WININET_NETCONNECTION *connection, LPCWSTR hostname);
BOOL NETCON_send(WININET_NETCONNECTION *connection, const void *msg, size_t len, int flags,
		int *sent /* out */);
BOOL NETCON_recv(WININET_NETCONNECTION *connection, void *buf, size_t len, int flags,
		int *recvd /* out */);
BOOL NETCON_getNextLine(WININET_NETCONNECTION *connection, LPSTR lpszBuffer, LPDWORD dwBuffer);
LPCVOID NETCON_GetCert(WININET_NETCONNECTION *connection);
BOOL NETCON_set_timeout(WININET_NETCONNECTION *connection, BOOL send, int value);

extern void URLCacheContainers_CreateDefaults(void);
extern void URLCacheContainers_DeleteAll(void);

#define MAX_REPLY_LEN	 	0x5B4

/* Used for debugging - maybe need to be shared in the Wine debugging code ? */
typedef struct
{
    DWORD val;
    const char* name;
} wininet_flag_info;

#endif /* _WINE_INTERNET_H_ */


 

 

 

5번. comdlg32: Remove redundant headers.

 

 

 



/*
 * COMMDLG - File Dialogs
 *
 * Copyright 1994 Martin Ayotte
 * Copyright 1996 Albrecht Kleine
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wingdi.h"
#include "winuser.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "cderr.h"
#include "winreg.h"
#include "winternl.h"
#include "commdlg.h"
#include "shlwapi.h"

WINE_DEFAULT_DEBUG_CHANNEL(commdlg);

#include "cdlg.h"
#include "filedlg31.h"

#define BUFFILE 512
#define BUFFILEALLOC 512 * sizeof(WCHAR)

static const WCHAR FILE_star[] = {'*','.','*', 0};
static const WCHAR FILE_bslash[] = {'\\', 0};
static const WCHAR FILE_specc[] = {'%','c',':', 0};
static const int fldrHeight = 16;
static const int fldrWidth = 20;

static HICON hFolder = 0;
static HICON hFolder2 = 0;
static HICON hFloppy = 0;
static HICON hHDisk = 0;
static HICON hCDRom = 0;
static HICON hNet = 0;

/***********************************************************************
 * 				FD31_Init			[internal]
 */
BOOL FD31_Init(void)
{
    static BOOL initialized = 0;

    if (!initialized) {
        hFolder  = LoadImageA( COMDLG32_hInstance, "FOLDER", IMAGE_ICON, 16, 16, LR_SHARED );
        hFolder2 = LoadImageA( COMDLG32_hInstance, "FOLDER2", IMAGE_ICON, 16, 16, LR_SHARED );
        hFloppy  = LoadImageA( COMDLG32_hInstance, "FLOPPY", IMAGE_ICON, 16, 16, LR_SHARED );
        hHDisk   = LoadImageA( COMDLG32_hInstance, "HDISK", IMAGE_ICON, 16, 16, LR_SHARED );
        hCDRom   = LoadImageA( COMDLG32_hInstance, "CDROM", IMAGE_ICON, 16, 16, LR_SHARED );
        hNet     = LoadImageA( COMDLG32_hInstance, "NETWORK", IMAGE_ICON, 16, 16, LR_SHARED );
	if (hFolder == 0 || hFolder2 == 0 || hFloppy == 0 ||
	    hHDisk == 0 || hCDRom == 0 || hNet == 0)
	{
	    ERR("Error loading icons !\n");
	    return FALSE;
	}
	initialized = TRUE;
    }
    return TRUE;
}

/***********************************************************************
 *                              FD31_StripEditControl        [internal]
 * Strip pathnames off the contents of the edit control.
 */
static void FD31_StripEditControl(HWND hwnd)
{
    WCHAR temp[BUFFILE], *cp;

    GetDlgItemTextW( hwnd, edt1, temp, sizeof(temp)/sizeof(WCHAR));
    cp = strrchrW(temp, '\\');
    if (cp != NULL) {
	strcpyW(temp, cp+1);
    }
    cp = strrchrW(temp, ':');
    if (cp != NULL) {
	strcpyW(temp, cp+1);
    }
    /* FIXME: shouldn't we do something with the result here? 😉 */
}

/***********************************************************************
 *                              FD31_CallWindowProc          [internal]
 *
 *      Call the appropriate hook
 */
BOOL FD31_CallWindowProc(PFD31_DATA lfs, UINT wMsg, WPARAM wParam,
                         LPARAM lParam)
{
    return lfs->callbacks->CWP(lfs, wMsg, wParam, lParam);
}

/***********************************************************************
 * 				FD31_ScanDir                 [internal]
 */
static BOOL FD31_ScanDir(HWND hWnd, LPWSTR newPath)
{
    WCHAR		buffer[BUFFILE];
    HWND 		hdlg, hdlgDir;
    LRESULT             lRet = TRUE;
    HCURSOR             hCursorWait, oldCursor;

    TRACE("Trying to change to %s\n", debugstr_w(newPath));
    if  ( newPath[0] && !SetCurrentDirectoryW( newPath ))
        return FALSE;
    lstrcpynW(buffer, newPath, sizeof(buffer)/sizeof(WCHAR));

    /* get the list of spec files */
    GetDlgItemTextW(hWnd, edt1, buffer, sizeof(buffer)/sizeof(WCHAR));

    hCursorWait = LoadCursorA(0, (LPSTR)IDC_WAIT);
    oldCursor = SetCursor(hCursorWait);

    /* list of files */
    if ((hdlg = GetDlgItem(hWnd, lst1)) != 0) {
        WCHAR*	scptr; /* ptr on semi-colon */
	WCHAR*	filter = buffer;

	TRACE("Using filter %s\n", debugstr_w(filter));
	SendMessageW(hdlg, LB_RESETCONTENT, 0, 0);
	while (filter) {
	    scptr = strchrW(filter, ';');
	    if (scptr)	*scptr = 0;
            while (*filter == ' ') filter++;
	    TRACE("Using file spec %s\n", debugstr_w(filter));
	    if (SendMessageW(hdlg, LB_DIR, 0, (LPARAM)filter) == LB_ERR)
	        return FALSE;
	    if (scptr) *scptr = ';';
	        filter = (scptr) ? (scptr + 1) : 0;
	 }
    }

    /* list of directories */
    strcpyW(buffer, FILE_star);

    if ((hdlgDir = GetDlgItem(hWnd, lst2)) != 0) {
        lRet = DlgDirListW(hWnd, buffer, lst2, stc1, DDL_EXCLUSIVE | DDL_DIRECTORY);
    }
    SetCursor(oldCursor);
    return lRet;
}

/***********************************************************************
 * 				FD31_GetFileType		[internal]
 */

static LPWSTR FD31_GetFileType(LPWSTR cfptr, LPWSTR fptr, WORD index)
{
  int n, i;
  i = 0;
  if (cfptr)
    for ( ;(n = lstrlenW(cfptr)) != 0; i++)
      {
	cfptr += n + 1;
	if (i == index)
	  return cfptr;
	cfptr += lstrlenW(cfptr) + 1;
      }
  if (fptr)
    for ( ;(n = lstrlenW(fptr)) != 0; i++)
      {
	fptr += n + 1;
	if (i == index)
	  return fptr;
	fptr += lstrlenW(fptr) + 1;
    }
  return (LPWSTR) FILE_star; /* FIXME */
}

/***********************************************************************
 *                              FD31_WMDrawItem              [internal]
 */
LONG FD31_WMDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam,
       int savedlg, LPDRAWITEMSTRUCT lpdis)
{
    WCHAR *str;
    HICON hIcon;
    COLORREF oldText = 0, oldBk = 0;

    if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst1)
    {
        if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC))) return FALSE;
	SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
                      (LPARAM)str);

	if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
	{
	    oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
	    oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	if (savedlg)
	    SetTextColor(lpdis->hDC,GetSysColor(COLOR_GRAYTEXT) );

	ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + 1,
                  lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
                  &(lpdis->rcItem), str, lstrlenW(str), NULL);

	if (lpdis->itemState & ODS_SELECTED)
	    DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );

	if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
	{
	    SetBkColor( lpdis->hDC, oldBk );
	    SetTextColor( lpdis->hDC, oldText );
	}
        HeapFree(GetProcessHeap(), 0, str);
	return TRUE;
    }

    if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst2)
    {
        if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
            return FALSE;
	SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
                      (LPARAM)str);

	if (lpdis->itemState & ODS_SELECTED)
	{
	    oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
	    oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
                  lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
                  &(lpdis->rcItem), str, lstrlenW(str), NULL);

	if (lpdis->itemState & ODS_SELECTED)
	    DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );

	if (lpdis->itemState & ODS_SELECTED)
	{
	    SetBkColor( lpdis->hDC, oldBk );
	    SetTextColor( lpdis->hDC, oldText );
	}
	DrawIcon(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hFolder);
        HeapFree(GetProcessHeap(), 0, str);
	return TRUE;
    }
    if (lpdis->CtlType == ODT_COMBOBOX && lpdis->CtlID == cmb2)
    {
        char root[] = "a:";
        if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
            return FALSE;
	SendMessageW(lpdis->hwndItem, CB_GETLBTEXT, lpdis->itemID,
                      (LPARAM)str);
        root[0] += str[2] - 'a';
        switch(GetDriveTypeA(root))
        {
        case DRIVE_REMOVABLE: hIcon = hFloppy; break;
        case DRIVE_CDROM:     hIcon = hCDRom; break;
        case DRIVE_REMOTE:    hIcon = hNet; break;
        case DRIVE_FIXED:
        default:           hIcon = hHDisk; break;
        }
	if (lpdis->itemState & ODS_SELECTED)
	{
	    oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
	    oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
                  lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
                  &(lpdis->rcItem), str, lstrlenW(str), NULL);

	if (lpdis->itemState & ODS_SELECTED)
	{
	    SetBkColor( lpdis->hDC, oldBk );
	    SetTextColor( lpdis->hDC, oldText );
	}
	DrawIcon(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hIcon);
        HeapFree(GetProcessHeap(), 0, str);
	return TRUE;
    }
    return FALSE;
}

/***********************************************************************
 *                              FD31_UpdateResult            [internal]
 *      update the displayed file name (with path)
 */
static void FD31_UpdateResult(PFD31_DATA lfs, WCHAR *tmpstr)
{
    int lenstr2;
    LPOPENFILENAMEW ofnW = lfs->ofnW;
    WCHAR tmpstr2[BUFFILE];
    WCHAR *p;

    TRACE("%s\n", debugstr_w(tmpstr));
    if(ofnW->Flags & OFN_NOVALIDATE)
        tmpstr2[0] = '\0';
    else
        GetCurrentDirectoryW(BUFFILE, tmpstr2);
    lenstr2 = strlenW(tmpstr2);
    if (lenstr2 > 3)
        tmpstr2[lenstr2++]='\\';
    lstrcpynW(tmpstr2+lenstr2, tmpstr, BUFFILE-lenstr2);
    if (ofnW->lpstrFile)
        lstrcpynW(ofnW->lpstrFile, tmpstr2, ofnW->nMaxFile);

    /* set filename offset */
    p = PathFindFileNameW(ofnW->lpstrFile);
    ofnW->nFileOffset = (p - ofnW->lpstrFile);

    /* set extension offset */
    p = PathFindExtensionW(ofnW->lpstrFile);
    ofnW->nFileExtension = (*p) ? (p - ofnW->lpstrFile) + 1 : 0;

    TRACE("file %s, file offset %d, ext offset %d\n",
          debugstr_w(ofnW->lpstrFile), ofnW->nFileOffset, ofnW->nFileExtension);

    /* update the real client structures if any */
    lfs->callbacks->UpdateResult(lfs);
}

/***********************************************************************
 *                              FD31_UpdateFileTitle         [internal]
 *      update the displayed file name (without path)
 */
static void FD31_UpdateFileTitle(PFD31_DATA lfs)
{
  LONG lRet;
  LPOPENFILENAMEW ofnW = lfs->ofnW;
  if (ofnW->lpstrFileTitle != NULL)
  {
    lRet = SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
    SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETTEXT, lRet,
                             (LPARAM)ofnW->lpstrFileTitle );
    lfs->callbacks->UpdateFileTitle(lfs);
  }
}

/***********************************************************************
 *                              FD31_DirListDblClick         [internal]
 */
static LRESULT FD31_DirListDblClick( PFD31_DATA lfs )
{
  LONG lRet;
  HWND hWnd = lfs->hwnd;
  LPWSTR pstr;
  WCHAR tmpstr[BUFFILE];

  /* get the raw string (with brackets) */
  lRet = SendDlgItemMessageW(hWnd, lst2, LB_GETCURSEL, 0, 0);
  if (lRet == LB_ERR) return TRUE;
  pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
  SendDlgItemMessageW(hWnd, lst2, LB_GETTEXT, lRet,
		     (LPARAM)pstr);
  strcpyW( tmpstr, pstr );
  HeapFree(GetProcessHeap(), 0, pstr);
  /* get the selected directory in tmpstr */
  if (tmpstr[0] == '[')
    {
      tmpstr[lstrlenW(tmpstr) - 1] = 0;
      strcpyW(tmpstr,tmpstr+1);
    }
  strcatW(tmpstr, FILE_bslash);

  FD31_ScanDir(hWnd, tmpstr);
  /* notify the app */
  if (lfs->hook)
    {
      if (FD31_CallWindowProc(lfs, lfs->lbselchstring, lst2,
              MAKELONG(lRet,CD_LBSELCHANGE)))
        return TRUE;
    }
  return TRUE;
}

/***********************************************************************
 *                              FD31_FileListSelect         [internal]
 *    called when a new item is picked in the file list
 */
static LRESULT FD31_FileListSelect( PFD31_DATA lfs )
{
    LONG lRet;
    HWND hWnd = lfs->hwnd;
    LPWSTR pstr;

    lRet = lfs->callbacks->SendLbGetCurSel(lfs);
    if (lRet == LB_ERR)
        return TRUE;

    /* set the edit control to the choosen file */
    if ((pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
    {
        SendDlgItemMessageW(hWnd, lst1, LB_GETTEXT, lRet,
                       (LPARAM)pstr);
        SetDlgItemTextW( hWnd, edt1, pstr );
        HeapFree(GetProcessHeap(), 0, pstr);
    }
    if (lfs->hook)
    {
        FD31_CallWindowProc(lfs, lfs->lbselchstring, lst1,
                           MAKELONG(lRet,CD_LBSELCHANGE));
    }
    /* FIXME: for OFN_ALLOWMULTISELECT we need CD_LBSELSUB, CD_SELADD,
           CD_LBSELNOITEMS */
    return TRUE;
}

/***********************************************************************
 *                              FD31_TestPath      [internal]
 *      before accepting the file name, test if it includes wild cards
 *      tries to scan the directory and returns TRUE if no error.
 */
static LRESULT FD31_TestPath( PFD31_DATA lfs, LPWSTR path )
{
    HWND hWnd = lfs->hwnd;
    LPWSTR pBeginFileName, pstr2;
    WCHAR tmpstr2[BUFFILE];

    pBeginFileName = strrchrW(path, '\\');
    if (pBeginFileName == NULL)
	pBeginFileName = strrchrW(path, ':');

    if (strchrW(path,'*') != NULL || strchrW(path,'?') != NULL)
    {
        /* edit control contains wildcards */
        if (pBeginFileName != NULL)
        {
	    lstrcpynW(tmpstr2, pBeginFileName + 1, BUFFILE);
	    *(pBeginFileName + 1) = 0;
	}
	else
	{
	    strcpyW(tmpstr2, path);
            if(!(lfs->ofnW->Flags & OFN_NOVALIDATE))
                *path = 0;
        }

        TRACE("path=%s, tmpstr2=%s\n", debugstr_w(path), debugstr_w(tmpstr2));
        SetDlgItemTextW( hWnd, edt1, tmpstr2 );
        FD31_ScanDir(hWnd, path);
        return (lfs->ofnW->Flags & OFN_NOVALIDATE) ? TRUE : FALSE;
    }

    /* no wildcards, we might have a directory or a filename */
    /* try appending a wildcard and reading the directory */

    pstr2 = path + lstrlenW(path);
    if (pBeginFileName == NULL || *(pBeginFileName + 1) != 0)
        strcatW(path, FILE_bslash);

    /* if ScanDir succeeds, we have changed the directory */
    if (FD31_ScanDir(hWnd, path))
        return FALSE; /* and path is not a valid file name */

    /* if not, this must be a filename */

    *pstr2 = 0; /* remove the wildcard added before */

    if (pBeginFileName != NULL)
    {
        /* strip off the pathname */
        *pBeginFileName = 0;
        SetDlgItemTextW( hWnd, edt1, pBeginFileName + 1 );

        lstrcpynW(tmpstr2, pBeginFileName + 1, sizeof(tmpstr2)/sizeof(WCHAR) );
        /* Should we MessageBox() if this fails? */
        if (!FD31_ScanDir(hWnd, path))
        {
            return FALSE;
        }
        strcpyW(path, tmpstr2);
    }
    else
        SetDlgItemTextW( hWnd, edt1, path );
    return TRUE;
}

/***********************************************************************
 *                              FD31_Validate               [internal]
 *   called on: click Ok button, Enter in edit, DoubleClick in file list
 */
static LRESULT FD31_Validate( PFD31_DATA lfs, LPWSTR path, UINT control, INT itemIndex,
                                 BOOL internalUse )
{
    LONG lRet;
    HWND hWnd = lfs->hwnd;
    OPENFILENAMEW ofnsav;
    LPOPENFILENAMEW ofnW = lfs->ofnW;
    WCHAR filename[BUFFILE];

    ofnsav = *ofnW; /* for later restoring */

    /* get current file name */
    if (path)
        lstrcpynW(filename, path, sizeof(filename)/sizeof(WCHAR));
    else
        GetDlgItemTextW( hWnd, edt1, filename, sizeof(filename)/sizeof(WCHAR));

    TRACE("got filename = %s\n", debugstr_w(filename));
    /* if we did not click in file list to get there */
    if (control != lst1)
    {
        if (!FD31_TestPath( lfs, filename) )
           return FALSE;
    }
    FD31_UpdateResult(lfs, filename);

    if (internalUse)
    { /* called internally after a change in a combo */
        if (lfs->hook)
        {
             FD31_CallWindowProc(lfs, lfs->lbselchstring, control,
                             MAKELONG(itemIndex,CD_LBSELCHANGE));
        }
        return TRUE;
    }

    FD31_UpdateFileTitle(lfs);
    if (lfs->hook)
    {
        lRet = (BOOL)FD31_CallWindowProc(lfs, lfs->fileokstring,
                  0, lfs->lParam );
        if (lRet)
        {
            *ofnW = ofnsav; /* restore old state */
            return FALSE;
        }
    }
    if ((ofnW->Flags & OFN_ALLOWMULTISELECT) && (ofnW->Flags & OFN_EXPLORER))
    {
        if (ofnW->lpstrFile)
        {
            LPWSTR str = (LPWSTR)ofnW->lpstrFile;
            LPWSTR ptr = strrchrW(str, '\\');
	    str[lstrlenW(str) + 1] = '\0';
	    *ptr = 0;
        }
    }
    return TRUE;
}

/***********************************************************************
 *                              FD31_DiskChange             [internal]
 *    called when a new item is picked in the disk selection combo
 */
static LRESULT FD31_DiskChange( PFD31_DATA lfs )
{
    LONG lRet;
    HWND hWnd = lfs->hwnd;
    LPWSTR pstr;
    WCHAR diskname[BUFFILE];

    FD31_StripEditControl(hWnd);
    lRet = SendDlgItemMessageW(hWnd, cmb2, CB_GETCURSEL, 0, 0L);
    if (lRet == LB_ERR)
        return 0;
    pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
    SendDlgItemMessageW(hWnd, cmb2, CB_GETLBTEXT, lRet,
                         (LPARAM)pstr);
    wsprintfW(diskname, FILE_specc, pstr[2]);
    HeapFree(GetProcessHeap(), 0, pstr);

    return FD31_Validate( lfs, diskname, cmb2, lRet, TRUE );
}

/***********************************************************************
 *                              FD31_FileTypeChange         [internal]
 *    called when a new item is picked in the file type combo
 */
static LRESULT FD31_FileTypeChange( PFD31_DATA lfs )
{
    LONG lRet;
    LPWSTR pstr;

    lRet = SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETCURSEL, 0, 0);
    if (lRet == LB_ERR)
        return TRUE;
    pstr = (LPWSTR)SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETITEMDATA, lRet, 0);
    TRACE("Selected filter : %s\n", debugstr_w(pstr));
    SetDlgItemTextW( lfs->hwnd, edt1, pstr );

    return FD31_Validate( lfs, NULL, cmb1, lRet, TRUE );
}

/***********************************************************************
 *                              FD31_WMCommand               [internal]
 */
LRESULT FD31_WMCommand(HWND hWnd, LPARAM lParam, UINT notification,
       UINT control, PFD31_DATA lfs )
{
    switch (control)
    {
        case lst1: /* file list */
        FD31_StripEditControl(hWnd);
        if (notification == LBN_DBLCLK)
        {
            return SendMessageW(hWnd, WM_COMMAND, IDOK, 0);
        }
        else if (notification == LBN_SELCHANGE)
            return FD31_FileListSelect( lfs );
        break;

        case lst2: /* directory list */
        FD31_StripEditControl(hWnd);
        if (notification == LBN_DBLCLK)
            return FD31_DirListDblClick( lfs );
        break;

        case cmb1: /* file type drop list */
        if (notification == CBN_SELCHANGE)
            return FD31_FileTypeChange( lfs );
        break;

        case chx1:
        break;

        case pshHelp:
        break;

        case cmb2: /* disk dropdown combo */
        if (notification == CBN_SELCHANGE)
            return FD31_DiskChange( lfs );
        break;

        case IDOK:
        TRACE("OK pressed\n");
        if (FD31_Validate( lfs, NULL, control, 0, FALSE ))
            EndDialog(hWnd, TRUE);
        return TRUE;

        case IDCANCEL:
        EndDialog(hWnd, FALSE);
        return TRUE;

        case IDABORT: /* can be sent by the hook procedure */
        EndDialog(hWnd, TRUE);
        return TRUE;
    }
    return FALSE;
}

/************************************************************************
 *                              FD31_MapStringPairsToW       [internal]
 *      map string pairs to Unicode
 */
static LPWSTR FD31_MapStringPairsToW(LPCSTR strA, UINT size)
{
    LPCSTR s;
    LPWSTR x;
    unsigned int n, len;

    s = strA;
    while (*s)
        s = s+strlen(s)+1;
    s++;
    n = s + 1 - strA; /* Don't forget the other \0 */
    if (n < size) n = size; len = MultiByteToWideChar( CP_ACP, 0, strA, n, NULL, 0 ); x = HeapAlloc(GetProcessHeap(),0, len * sizeof(WCHAR)); MultiByteToWideChar( CP_ACP, 0, strA, n, x, len ); return x; } /************************************************************************ * FD31_DupToW [internal] * duplicates an Ansi string to unicode, with a buffer size */ static LPWSTR FD31_DupToW(LPCSTR str, DWORD size) { LPWSTR strW = NULL; if (str && (size > 0))
    {
        strW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
        if (strW) MultiByteToWideChar( CP_ACP, 0, str, -1, strW, size );
    }
    return strW;
}

/************************************************************************
 *                              FD31_MapOfnStructA          [internal]
 *      map a 32 bits Ansi structure to an Unicode one
 */
void FD31_MapOfnStructA(const LPOPENFILENAMEA ofnA, LPOPENFILENAMEW ofnW, BOOL open)
{
    UNICODE_STRING usBuffer;

    ofnW->lStructSize = sizeof(OPENFILENAMEW);
    ofnW->hwndOwner = ofnA->hwndOwner;
    ofnW->hInstance = ofnA->hInstance;
    if (ofnA->lpstrFilter)
        ofnW->lpstrFilter = FD31_MapStringPairsToW(ofnA->lpstrFilter, 0);

    if ((ofnA->lpstrCustomFilter) && (*(ofnA->lpstrCustomFilter)))
        ofnW->lpstrCustomFilter = FD31_MapStringPairsToW(ofnA->lpstrCustomFilter, ofnA->nMaxCustFilter);
    ofnW->nMaxCustFilter = ofnA->nMaxCustFilter;
    ofnW->nFilterIndex = ofnA->nFilterIndex;
    ofnW->nMaxFile = ofnA->nMaxFile;
    ofnW->lpstrFile = FD31_DupToW(ofnA->lpstrFile, ofnW->nMaxFile);
    ofnW->nMaxFileTitle = ofnA->nMaxFileTitle;
    ofnW->lpstrFileTitle = FD31_DupToW(ofnA->lpstrFileTitle, ofnW->nMaxFileTitle);
    if (ofnA->lpstrInitialDir)
    {
        RtlCreateUnicodeStringFromAsciiz (&usBuffer,ofnA->lpstrInitialDir);
        ofnW->lpstrInitialDir = usBuffer.Buffer;
    }
    if (ofnA->lpstrTitle) {
        RtlCreateUnicodeStringFromAsciiz (&usBuffer, ofnA->lpstrTitle);
        ofnW->lpstrTitle = usBuffer.Buffer;
    } else {
        WCHAR buf[16];
        int len;
        LoadStringW(COMDLG32_hInstance, open ? IDS_OPEN_FILE : IDS_SAVE_AS,
                    buf, sizeof(buf)/sizeof(WCHAR));
        len = lstrlenW(buf)+1;
        ofnW->lpstrTitle = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
        memcpy((void*)ofnW->lpstrTitle, buf, len*sizeof(WCHAR));
    }
    ofnW->Flags = ofnA->Flags;
    ofnW->nFileOffset = ofnA->nFileOffset;
    ofnW->nFileExtension = ofnA->nFileExtension;
    ofnW->lpstrDefExt = FD31_DupToW(ofnA->lpstrDefExt, 3);
    if ((ofnA->Flags & OFN_ENABLETEMPLATE) && (ofnA->lpTemplateName))
    {
        if (HIWORD(ofnA->lpTemplateName))
        {
            RtlCreateUnicodeStringFromAsciiz (&usBuffer,ofnA->lpTemplateName);
            ofnW->lpTemplateName = usBuffer.Buffer;
        }
        else /* numbered resource */
            ofnW->lpTemplateName = (LPWSTR) ofnA->lpTemplateName;
    }
}


/************************************************************************
 *                              FD31_FreeOfnW          [internal]
 *      Undo all allocations done by FD31_MapOfnStructA
 */
void FD31_FreeOfnW(LPOPENFILENAMEW ofnW)
{
   HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrFilter);
   HeapFree(GetProcessHeap(), 0, ofnW->lpstrCustomFilter);
   HeapFree(GetProcessHeap(), 0, ofnW->lpstrFile);
   HeapFree(GetProcessHeap(), 0, ofnW->lpstrFileTitle);
   HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrInitialDir);
   HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrTitle);
   if (HIWORD(ofnW->lpTemplateName))
       HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpTemplateName);
}

/************************************************************************
 *                              FD31_DestroyPrivate            [internal]
 *      destroys the private object
 */
void FD31_DestroyPrivate(PFD31_DATA lfs)
{
    HWND hwnd;
    if (!lfs) return;
    hwnd = lfs->hwnd;
    TRACE("destroying private allocation %p\n", lfs);
    lfs->callbacks->Destroy(lfs);
    HeapFree(GetProcessHeap(), 0, lfs);
    RemovePropA(hwnd, FD31_OFN_PROP);
}

/************************************************************************
 *                              FD31_AllocPrivate            [internal]
 *      allocate a private object to hold 32 bits Unicode
 *      structure that will be used throughtout the calls, while
 *      keeping available the original structures and a few variables
 *      On entry : type = dialog procedure type (16,32A,32W)
 *                 dlgType = dialog type (open or save)
 */
PFD31_DATA FD31_AllocPrivate(LPARAM lParam, UINT dlgType,
                             PFD31_CALLBACKS callbacks, DWORD data)
{
    PFD31_DATA lfs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FD31_DATA));

    TRACE("alloc private buf %p\n", lfs);
    if (!lfs) return NULL;
    lfs->hook = FALSE;
    lfs->lParam = lParam;
    lfs->open = (dlgType == OPEN_DIALOG);
    lfs->callbacks = callbacks;
    if (! lfs->callbacks->Init(lParam, lfs, data))
    {
        FD31_DestroyPrivate(lfs);
        return NULL;
    }
    lfs->lbselchstring = RegisterWindowMessageA(LBSELCHSTRINGA);
    lfs->fileokstring = RegisterWindowMessageA(FILEOKSTRINGA);

    return lfs;
}

/***********************************************************************
 *                              FD31_WMInitDialog            [internal]
 */

LONG FD31_WMInitDialog(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  int i, n;
  WCHAR tmpstr[BUFFILE];
  LPWSTR pstr, old_pstr;
  LPOPENFILENAMEW ofn;
  PFD31_DATA lfs = (PFD31_DATA) lParam;

  if (!lfs) return FALSE;
  SetPropA(hWnd, FD31_OFN_PROP, (HANDLE)lfs);
  lfs->hwnd = hWnd;
  ofn = lfs->ofnW;

  TRACE("flags=%lx initialdir=%s\n", ofn->Flags, debugstr_w(ofn->lpstrInitialDir));

  SetWindowTextW( hWnd, ofn->lpstrTitle );
  /* read custom filter information */
  if (ofn->lpstrCustomFilter)
    {
      pstr = ofn->lpstrCustomFilter;
      n = 0;
      TRACE("lpstrCustomFilter = %p\n", pstr);
      while(*pstr)
	{
	  old_pstr = pstr;
          i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
                                   (LPARAM)(ofn->lpstrCustomFilter) + n );
          n += lstrlenW(pstr) + 1;
	  pstr += lstrlenW(pstr) + 1;
	  TRACE("add str=%s associated to %s\n",
                debugstr_w(old_pstr), debugstr_w(pstr));
          SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
          n += lstrlenW(pstr) + 1;
	  pstr += lstrlenW(pstr) + 1;
	}
    }
  /* read filter information */
  if (ofn->lpstrFilter) {
	pstr = (LPWSTR) ofn->lpstrFilter;
	n = 0;
	while(*pstr) {
	  old_pstr = pstr;
	  i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
				       (LPARAM)(ofn->lpstrFilter + n) );
	  n += lstrlenW(pstr) + 1;
	  pstr += lstrlenW(pstr) + 1;
	  TRACE("add str=%s associated to %s\n",
                debugstr_w(old_pstr), debugstr_w(pstr));
	  SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
	  n += lstrlenW(pstr) + 1;
	  pstr += lstrlenW(pstr) + 1;
	}
  }
  /* set default filter */
  if (ofn->nFilterIndex == 0 && ofn->lpstrCustomFilter == NULL)
  	ofn->nFilterIndex = 1;
  SendDlgItemMessageW(hWnd, cmb1, CB_SETCURSEL, ofn->nFilterIndex - 1, 0);
  lstrcpynW(tmpstr, FD31_GetFileType(ofn->lpstrCustomFilter,
	     (LPWSTR)ofn->lpstrFilter, ofn->nFilterIndex - 1),BUFFILE);
  TRACE("nFilterIndex = %ld, SetText of edt1 to %s\n",
  			ofn->nFilterIndex, debugstr_w(tmpstr));
  SetDlgItemTextW( hWnd, edt1, tmpstr );
  /* get drive list */
  *tmpstr = 0;
  DlgDirListComboBoxW(hWnd, tmpstr, cmb2, 0, DDL_DRIVES | DDL_EXCLUSIVE);
  /* read initial directory */
  /* FIXME: Note that this is now very version-specific (See MSDN description of
   * the OPENFILENAME structure).  For example under 2000/XP any path in the
   * lpstrFile overrides the lpstrInitialDir, but not under 95/98/ME
   */
  if (ofn->lpstrInitialDir != NULL)
    {
      int len;
      lstrcpynW(tmpstr, ofn->lpstrInitialDir, 511);
      len = lstrlenW(tmpstr);
      if (len > 0 && tmpstr[len-1] != '\\'  && tmpstr[len-1] != ':') {
        tmpstr[len]='\\';
        tmpstr[len+1]='\0';
      }
    }
  else
    *tmpstr = 0;
  if (!FD31_ScanDir(hWnd, tmpstr)) {
    *tmpstr = 0;
    if (!FD31_ScanDir(hWnd, tmpstr))
      WARN("Couldn't read initial directory %s!\n", debugstr_w(tmpstr));
  }
  /* select current drive in combo 2, omit missing drives */
  {
      char dir[MAX_PATH];
      char str[4] = "a:\\";
      GetCurrentDirectoryA( sizeof(dir), dir );
      for(i = 0, n = -1; i < 26; i++) { str[0] = 'a' + i; if (GetDriveTypeA(str) > DRIVE_NO_ROOT_DIR) n++;
          if (toupper(str[0]) == toupper(dir[0])) break;
      }
  }
  SendDlgItemMessageW(hWnd, cmb2, CB_SETCURSEL, n, 0);
  if (!(ofn->Flags & OFN_SHOWHELP))
    ShowWindow(GetDlgItem(hWnd, pshHelp), SW_HIDE);
  if (ofn->Flags & OFN_HIDEREADONLY)
    ShowWindow(GetDlgItem(hWnd, chx1), SW_HIDE);
  if (lfs->hook)
      return (BOOL) FD31_CallWindowProc(lfs, WM_INITDIALOG, wParam, lfs->lParam);
  return TRUE;
}

int FD31_GetFldrHeight(void)
{
  return fldrHeight;
}


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Advertisements

글쓴이:

저는 맑은강물 입니다.