/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2010 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "commonheaders.h"
#pragma hdrstop

#include "m_hotkeys.h"

#include "clc.h"
#include "genmenu.h"

#define MS_CLIST_HKSTATUS "Clist/HK/SetStatus"

#define FIRSTCUSTOMMENUITEMID	30000
#define MENU_CUSTOMITEMMAIN		0x80000000
//#define MENU_CUSTOMITEMCONTEXT	0x40000000
//#define MENU_CUSTOMITEMFRAME	0x20000000

typedef struct  {
	WORD id;
	int iconId;
	CLISTMENUITEM mi;
}
	CListIntMenuItem,*lpCListIntMenuItem;

//new menu sys
HANDLE hMainMenuObject = 0;
HANDLE hContactMenuObject = 0;
HANDLE hStatusMenuObject = 0;
int UnloadMoveToGroup(void);

int statustopos(int status);
void Proto_SetStatus(const char* szProto, unsigned status);

bool prochotkey;

HANDLE hPreBuildMainMenuEvent, hStatusModeChangeEvent, hPreBuildContactMenuEvent;

static HANDLE hAckHook;

static HMENU hMainMenu,hStatusMenu = 0;
static const int statusModeList[ MAX_STATUS_COUNT ] =
{
	ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED,
	ID_STATUS_DND, ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_ONTHEPHONE, ID_STATUS_OUTTOLUNCH
};

static const int skinIconStatusList[ MAX_STATUS_COUNT ] =
{
	SKINICON_STATUS_OFFLINE, SKINICON_STATUS_ONLINE, SKINICON_STATUS_AWAY, SKINICON_STATUS_NA, SKINICON_STATUS_OCCUPIED,
	SKINICON_STATUS_DND, SKINICON_STATUS_FREE4CHAT, SKINICON_STATUS_INVISIBLE, SKINICON_STATUS_ONTHEPHONE, SKINICON_STATUS_OUTTOLUNCH
};

static const int statusModePf2List[ MAX_STATUS_COUNT ] =
{
	0xFFFFFFFF, PF2_ONLINE, PF2_SHORTAWAY, PF2_LONGAWAY, PF2_LIGHTDND,
	PF2_HEAVYDND, PF2_FREECHAT, PF2_INVISIBLE, PF2_ONTHEPHONE, PF2_OUTTOLUNCH
};

static INT_PTR statusHotkeys[ MAX_STATUS_COUNT ];

PMO_IntMenuItem* hStatusMainMenuHandles;
int  hStatusMainMenuHandlesCnt;

typedef struct
{
	int protoindex;
	int protostatus[ MAX_STATUS_COUNT ];
	PMO_IntMenuItem menuhandle[ MAX_STATUS_COUNT ];
}
	tStatusMenuHandles,*lpStatusMenuHandles;

lpStatusMenuHandles hStatusMenuHandles;
int hStatusMenuHandlesCnt;

//mainmenu exec param(ownerdata)
typedef struct
{
	char *szServiceName;
	TCHAR *szMenuName;
	int Param1;
}
	MainMenuExecParam,*lpMainMenuExecParam;

//contactmenu exec param(ownerdata)
//also used in checkservice
typedef struct
{
	char *szServiceName;
	char *pszContactOwner;//for check proc
	int param;
}
	ContactMenuExecParam,*lpContactMenuExecParam;

typedef struct
{
	char *szProto;
	int isOnList;
	int isOnline;
}
	BuildContactParam;

typedef struct
{
	char *proto;            //This is unique protoname
	int protoindex;
	int status;

	BOOL custom;
	char *svc;
	HANDLE hMenuItem;
}
	StatusMenuExecParam,*lpStatusMenuExecParam;

typedef struct _MenuItemHandles
{
	HMENU OwnerMenu;
	int position;
}
	MenuItemData;

/////////////////////////////////////////////////////////////////////////////////////////
// service functions

void FreeMenuProtos( void )
{
	int i;

	if ( cli.menuProtos ) {
		for ( i=0; i < cli.menuProtoCount; i++ )
			if ( cli.menuProtos[i].szProto )
				mir_free(cli.menuProtos[i].szProto);

		mir_free( cli.menuProtos );
		cli.menuProtos = NULL;
	}
	cli.menuProtoCount = 0;
}

//////////////////////////////////////////////////////////////////////////

int GetAverageMode(int* pNetProtoCount = NULL)
{
	int netProtoCount = 0;
	int averageMode = 0;

	for ( int i=0; i < accounts.getCount(); i++ ) {
		PROTOACCOUNT* pa = accounts[i];
		if ( cli.pfnGetProtocolVisibility( pa->szModuleName ) == 0 )
			continue;

		netProtoCount++;

		if ( averageMode == 0 )
			averageMode = CallProtoService( pa->szModuleName, PS_GETSTATUS, 0, 0 );
		else if ( averageMode > 0 && averageMode != CallProtoService( pa->szModuleName, PS_GETSTATUS, 0, 0 )) {
			averageMode = -1;
            if (pNetProtoCount == NULL) break;
	    }	
    }

    if (pNetProtoCount) *pNetProtoCount = netProtoCount;
	return averageMode;
}

/////////////////////////////////////////////////////////////////////////////////////////
// MAIN MENU

/*
wparam=handle to the menu item returned by MS_CLIST_ADDCONTACTMENUITEM
return 0 on success.
*/

static INT_PTR RemoveMainMenuItem(WPARAM wParam, LPARAM)
{
	CallService(MO_REMOVEMENUITEM,wParam,0);
	return 0;
}

static INT_PTR BuildMainMenu(WPARAM, LPARAM)
{
	ListParam param = { 0 };
	param.MenuObjectHandle = hMainMenuObject;

	NotifyEventHooks(hPreBuildMainMenuEvent,(WPARAM)0,(LPARAM)0);

	CallService(MO_BUILDMENU,(WPARAM)hMainMenu,(LPARAM)&param);
	DrawMenuBar((HWND)CallService("CLUI/GetHwnd",(WPARAM)0,(LPARAM)0));
	return (INT_PTR)hMainMenu;
}

static INT_PTR AddMainMenuItem(WPARAM, LPARAM lParam)
{
	CLISTMENUITEM* mi = ( CLISTMENUITEM* )lParam;
	if ( mi->cbSize != sizeof( CLISTMENUITEM ))
		return NULL;

	TMO_MenuItem tmi = { 0 };
	tmi.cbSize   = sizeof(tmi);
	tmi.flags    = mi->flags;
	tmi.hIcon    = mi->hIcon;
	tmi.hotKey   = mi->hotKey;
	tmi.ptszName = mi->ptszName;
	tmi.position = mi->position;

	//pszPopupName for new system mean root level
	//pszPopupName for old system mean that exists popup
	tmi.root = ( HGENMENU )mi->pszPopupName;
	{
		lpMainMenuExecParam mmep;
		mmep = ( lpMainMenuExecParam )mir_alloc( sizeof( MainMenuExecParam ));
		if ( mmep == NULL )
			return 0;

		//we need just one parametr.
		mmep->szServiceName = mir_strdup(mi->pszService);
		mmep->Param1 = mi->popupPosition;
		mmep->szMenuName = tmi.ptszName;
		tmi.ownerdata=mmep;
	}

	PMO_IntMenuItem pimi = MO_AddNewMenuItem( hMainMenuObject, &tmi );

	char* name;
	bool needFree = false;

	if (mi->pszService)
		name = mi->pszService;
	else if (mi->flags & CMIF_UNICODE) {
		name = mir_t2a( mi->ptszName );
		needFree = true;
	}
	else
		name = mi->pszName;

	MO_SetOptionsMenuItem( pimi, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )name );
	if (needFree) mir_free(name);

	return ( INT_PTR )pimi;
}

int MainMenuCheckService(WPARAM, LPARAM)
{
	return 0;
}

//called with:
//wparam - ownerdata
//lparam - lparam from winproc
INT_PTR MainMenuExecService(WPARAM wParam, LPARAM lParam)
{
	lpMainMenuExecParam mmep = ( lpMainMenuExecParam )wParam;
	if ( mmep != NULL ) {
		// bug in help.c,it used wparam as parent window handle without reason.
		if ( !lstrcmpA(mmep->szServiceName,"Help/AboutCommand"))
			mmep->Param1 = 0;

		CallService(mmep->szServiceName,mmep->Param1,lParam);
	}
	return 1;
}

INT_PTR FreeOwnerDataMainMenu(WPARAM, LPARAM lParam)
{
	lpMainMenuExecParam mmep = ( lpMainMenuExecParam )lParam;
	if ( mmep != NULL ) {
		FreeAndNil(( void** )&mmep->szServiceName);
		FreeAndNil(( void** )&mmep);
	}
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
// CONTACT MENU

static INT_PTR RemoveContactMenuItem(WPARAM wParam, LPARAM)
{
	CallService(MO_REMOVEMENUITEM,wParam,0);
	return 0;
}

static INT_PTR AddContactMenuItem(WPARAM, LPARAM lParam)
{
	CLISTMENUITEM *mi=(CLISTMENUITEM*)lParam;
	if ( mi->cbSize != sizeof( CLISTMENUITEM ))
		return 0;

	TMO_MenuItem tmi = { 0 };
	tmi.cbSize = sizeof(tmi);
	tmi.flags = mi->flags;
	tmi.hIcon = mi->hIcon;
	tmi.hotKey = mi->hotKey;
	tmi.position = mi->position;
	tmi.ptszName = mi->ptszName;
	tmi.root = ( HGENMENU )mi->pszPopupName;

	if ( !( mi->flags & CMIF_ROOTHANDLE )) {
		//old system
		tmi.flags |= CMIF_ROOTHANDLE;
		tmi.root = NULL;
	}

	//owner data
	lpContactMenuExecParam cmep = ( lpContactMenuExecParam )mir_calloc(sizeof(ContactMenuExecParam));
	cmep->szServiceName = mir_strdup( mi->pszService );
	if ( mi->pszContactOwner != NULL )
		cmep->pszContactOwner = mir_strdup( mi->pszContactOwner );
	cmep->param = mi->popupPosition;
	tmi.ownerdata = cmep;

	//may be need to change how UniqueName is formed?
	PMO_IntMenuItem menuHandle = MO_AddNewMenuItem( hContactMenuObject, &tmi );
	char buf[ 256 ];
	if (mi->pszService)
		mir_snprintf( buf, SIZEOF(buf), "%s/%s", (mi->pszContactOwner) ? mi->pszContactOwner : "", (mi->pszService) ? mi->pszService : "" );
	else if (mi->ptszName)
	{
		if (tmi.flags&CMIF_UNICODE)
		{
			char * temp = mir_t2a(mi->ptszName);
			mir_snprintf( buf, SIZEOF(buf), "%s/NoService/%s", (mi->pszContactOwner) ? mi->pszContactOwner : "", temp );
			mir_free(temp);
		}
		else
			mir_snprintf( buf, SIZEOF(buf), "%s/NoService/%s", (mi->pszContactOwner) ? mi->pszContactOwner : "", mi->ptszName );
	}
	else buf[0]='\0';
	if (buf[0]) MO_SetOptionsMenuItem( menuHandle, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
	return ( INT_PTR )menuHandle;
}

static INT_PTR BuildContactMenu(WPARAM wParam, LPARAM)
{
	HANDLE hContact = ( HANDLE )wParam;
	NotifyEventHooks(hPreBuildContactMenuEvent,(WPARAM)hContact,0);

	char *szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);

	BuildContactParam bcp;
	bcp.szProto = szProto;
	bcp.isOnList = ( DBGetContactSettingByte(hContact,"CList","NotOnList",0) == 0 );
	bcp.isOnline = ( szProto != NULL && ID_STATUS_OFFLINE != DBGetContactSettingWord(hContact,szProto,"Status",ID_STATUS_OFFLINE));

	ListParam param = { 0 };
	param.MenuObjectHandle = hContactMenuObject;
	param.wParam = (WPARAM)&bcp;

	HMENU hMenu = CreatePopupMenu();
	CallService(MO_BUILDMENU,(WPARAM)hMenu,(LPARAM)&param);

	return (INT_PTR)hMenu;
}

//called with:
//wparam - ownerdata
//lparam - lparam from winproc
INT_PTR ContactMenuExecService(WPARAM wParam,LPARAM lParam)
{
	if (wParam!=0) {
		lpContactMenuExecParam cmep=(lpContactMenuExecParam)wParam;
		//call with wParam=(WPARAM)(HANDLE)hContact,lparam=popupposition
		CallService(cmep->szServiceName,lParam,cmep->param);
	}
	return 0;
}

//true - ok,false ignore
INT_PTR ContactMenuCheckService(WPARAM wParam,LPARAM)
{
	PCheckProcParam pcpp = ( PCheckProcParam )wParam;
	BuildContactParam *bcp=NULL;
	lpContactMenuExecParam cmep=NULL;
	TMO_MenuItem mi;

	if ( pcpp == NULL )
		return FALSE;

	bcp = ( BuildContactParam* )pcpp->wParam;
	if ( bcp == NULL )
		return FALSE;

	cmep = ( lpContactMenuExecParam )pcpp->MenuItemOwnerData;
	if ( cmep == NULL ) //this is root...build it
		return TRUE;

	if ( cmep->pszContactOwner != NULL ) {
		if ( bcp->szProto == NULL ) return FALSE;
		if ( strcmp( cmep->pszContactOwner, bcp->szProto )) return FALSE;
	}
	if ( MO_GetMenuItem(( WPARAM )pcpp->MenuItemHandle, ( LPARAM )&mi ) == 0 ) {
		if ( mi.flags & CMIF_HIDDEN ) return FALSE;
		if ( mi.flags & CMIF_NOTONLIST  && bcp->isOnList  ) return FALSE;
		if ( mi.flags & CMIF_NOTOFFLIST && !bcp->isOnList ) return FALSE;
		if ( mi.flags & CMIF_NOTONLINE  && bcp->isOnline  ) return FALSE;
		if ( mi.flags & CMIF_NOTOFFLINE && !bcp->isOnline ) return FALSE;
	}
	return TRUE;
}

INT_PTR FreeOwnerDataContactMenu (WPARAM, LPARAM lParam)
{
	lpContactMenuExecParam cmep = ( lpContactMenuExecParam )lParam;
	if ( cmep != NULL ) {
		FreeAndNil(( void** )&cmep->szServiceName);
		FreeAndNil(( void** )&cmep->pszContactOwner);
		FreeAndNil(( void** )&cmep);
	}
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
// STATUS MENU

BOOL FindMenuHandleByGlobalID(HMENU hMenu, PMO_IntMenuItem id, MenuItemData* itdat)
{
	int i;
	PMO_IntMenuItem pimi;
	MENUITEMINFO mii={0};
	BOOL inSub=FALSE;
	if (!itdat)
		return FALSE;

	mii.cbSize = MENUITEMINFO_V4_SIZE;
	mii.fMask = MIIM_SUBMENU | MIIM_DATA;
	for ( i = GetMenuItemCount( hMenu )-1; i >= 0; i-- ) {
		GetMenuItemInfo(hMenu,i,TRUE,&mii);
		if ( mii.fType == MFT_SEPARATOR )
			continue;
		if ( mii.hSubMenu )
			inSub = FindMenuHandleByGlobalID(mii.hSubMenu, id, itdat);
		if ( inSub )
			return inSub;

		pimi = MO_GetIntMenuItem(( HGENMENU )mii.dwItemData );
		if ( pimi != NULL ) {
			if ( pimi == id ) {
				itdat->OwnerMenu = hMenu;
				itdat->position = i;
				return TRUE;
	}	}	}

	return FALSE;
}

INT_PTR StatusMenuCheckService(WPARAM wParam, LPARAM)
{
	PCheckProcParam pcpp = ( PCheckProcParam )wParam;
	if ( !pcpp )
		return TRUE;

	PMO_IntMenuItem timi = MO_GetIntMenuItem( pcpp->MenuItemHandle );
	if ( !timi )
		return TRUE;

	StatusMenuExecParam *smep = ( StatusMenuExecParam* )pcpp->MenuItemOwnerData;
	if (smep && !smep->status && smep->custom ) 
	{
		if (wildcmp(smep->svc, "*XStatus*")) 
		{
			int XStatus = CallProtoService(smep->proto, "/GetXStatus", 0, 0);
			char buf[255];
			mir_snprintf( buf, sizeof(buf), "*XStatus%d", XStatus );

			bool check = wildcmp(smep->svc, buf);
			bool reset = wildcmp(smep->svc, "*XStatus0");

			if (check)
				timi->mi.flags |= CMIF_CHECKED;
			else
				timi->mi.flags &= ~CMIF_CHECKED;

			if ( reset || check ) 
			{
				PMO_IntMenuItem timiParent = MO_GetIntMenuItem( timi->mi.root );
				if (timiParent) 
				{
					CLISTMENUITEM mi2 = {0};
					mi2.cbSize = sizeof(mi2);
					mi2.flags = CMIM_NAME | CMIF_TCHAR;
					mi2.ptszName = timi->mi.hIcon ? timi->mi.ptszName : TranslateT("Custom status");

					timiParent = MO_GetIntMenuItem( timi->mi.root );

					MenuItemData it = {0};

					if (FindMenuHandleByGlobalID(hStatusMenu, timiParent, &it)) 
					{
						MENUITEMINFO mi ={0};
						TCHAR d[100];
						GetMenuString(it.OwnerMenu, it.position, d, SIZEOF(d), MF_BYPOSITION);

						if (!IsWinVer98Plus()) 
						{
							mi.cbSize = MENUITEMINFO_V4_SIZE;
							mi.fMask = MIIM_TYPE | MIIM_STATE;
							mi.fType = MFT_STRING;
						}
						else 
						{
							mi.cbSize = sizeof( mi );
							mi.fMask = MIIM_STRING | MIIM_STATE;
							if ( timi->iconId != -1 ) 
							{
								mi.fMask |= MIIM_BITMAP;
								if (IsWinVerVistaPlus() && isThemeActive()) {
									if (timi->hBmp == NULL)
										timi->hBmp = ConvertIconToBitmap(NULL, timi->parent->m_hMenuIcons, timi->iconId);
									mi.hbmpItem = timi->hBmp;
								}
								else
									mi.hbmpItem = HBMMENU_CALLBACK;
							}
						}

						mi.fState |= (check && !reset ? MFS_CHECKED : MFS_UNCHECKED );
						mi.dwTypeData = mi2.ptszName;
						SetMenuItemInfo(it.OwnerMenu, it.position, TRUE, &mi);
					}

					CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)timi->mi.root, (LPARAM)&mi2);
					timiParent->iconId = timi->iconId;
					if (timiParent->hBmp) DeleteObject(timiParent->hBmp);
					timiParent->hBmp = NULL;
		}	}	}
	}
	else if ( smep && smep->status && !smep->custom ) {
		int curProtoStatus = ( smep->proto ) ? CallProtoService(smep->proto,PS_GETSTATUS,0,0) : GetAverageMode();
		if ( smep->status == curProtoStatus )
			timi->mi.flags |= CMIF_CHECKED;
		else
			timi->mi.flags &= ~CMIF_CHECKED;
	}
	else if (( !smep || smep->proto ) && timi->mi.pszName ) {
		int curProtoStatus=0;
		BOOL IconNeedDestroy=FALSE;
		char* prot;
		if (smep)
			prot = smep->proto;
		else
		{
			#ifdef UNICODE
				char *prn=mir_u2a(timi->mi.ptszName);
				prot = NEWSTR_ALLOCA( prn );
				if (prn) mir_free(prn);
			#else
				prot = timi->mi.ptszName;
			#endif
		}
		if ( Proto_GetAccount( prot ) == NULL )
			return TRUE;

		if (( curProtoStatus = CallProtoService(prot,PS_GETSTATUS,0,0)) == CALLSERVICE_NOTFOUND )
			curProtoStatus = 0;

		if ( curProtoStatus >= ID_STATUS_OFFLINE && curProtoStatus < ID_STATUS_IDLE )
			timi->mi.hIcon = LoadSkinProtoIcon(prot,curProtoStatus);
		else {
			timi->mi.hIcon=(HICON)CallProtoService(prot,PS_LOADICON,PLI_PROTOCOL|PLIF_SMALL,0);
			if ( timi->mi.hIcon == (HICON)CALLSERVICE_NOTFOUND )
				timi->mi.hIcon = NULL;
			else
				IconNeedDestroy = TRUE;
		}

		if (timi->mi.hIcon) {
			timi->mi.flags |= CMIM_ICON;
			MO_ModifyMenuItem( timi, &timi->mi );
			if ( IconNeedDestroy ) {
				DestroyIcon( timi->mi.hIcon );
				timi->mi.hIcon = NULL;
			}
			else IconLib_ReleaseIcon(timi->mi.hIcon,0);
	}	}

	return TRUE;
}

INT_PTR StatusMenuExecService(WPARAM wParam, LPARAM)
{
	lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )wParam;
	if ( smep != NULL ) {
		if ( smep->custom ) {
			if (smep->svc && *smep->svc)
				CallService(smep->svc, 0, (LPARAM)smep->hMenuItem);
		}
		else {
			if ( smep->status == 0 && smep->protoindex !=0 && smep->proto != NULL ) {
				PMO_IntMenuItem pimi;
				char *prot = smep->proto;
				char szHumanName[64]={0};
				PROTOACCOUNT * acc = Proto_GetAccount( smep->proto );
				int i=(DBGetContactSettingByte(NULL,prot,"LockMainStatus",0)?0:1);
				DBWriteContactSettingByte(NULL,prot,"LockMainStatus",(BYTE)i);

				CallProtoService( smep->proto, PS_GETNAME, (WPARAM)SIZEOF(szHumanName), (LPARAM)szHumanName );
				pimi = MO_GetIntMenuItem(( HGENMENU )smep->protoindex );
				PMO_IntMenuItem root = (PMO_IntMenuItem)pimi->mi.root;
				mir_free( pimi->mi.pszName );
				mir_free( root->mi.pszName );
				if ( i ) {
					TCHAR buf[256];
					pimi->mi.flags|=CMIF_CHECKED;
					if ( cli.bDisplayLocked ) {
						mir_sntprintf(buf,SIZEOF(buf),TranslateT("%s (locked)"),acc->tszAccountName);
						pimi->mi.ptszName = mir_tstrdup( buf );
						root->mi.ptszName = mir_tstrdup( buf );
					}
					else { 
						pimi->mi.ptszName = mir_tstrdup( acc->tszAccountName );
						root->mi.ptszName = mir_tstrdup( acc->tszAccountName );
					}
				}
				else {
					pimi->mi.ptszName = mir_tstrdup( acc->tszAccountName );
					root->mi.ptszName = mir_tstrdup( acc->tszAccountName );
					pimi->mi.flags &= ~CMIF_CHECKED;
				}
				if ( cli.hwndStatus )
					InvalidateRect( cli.hwndStatus, NULL, TRUE );
			}
			else if ( smep->proto != NULL ) {
				Proto_SetStatus(smep->proto, smep->status);
				NotifyEventHooks(hStatusModeChangeEvent, smep->status, (LPARAM)smep->proto);
			}
			else {
	            int MenusProtoCount = 0;

	            for( int i=0; i < accounts.getCount(); i++ )
		            if ( cli.pfnGetProtocolVisibility( accounts[i]->szModuleName ))
			            MenusProtoCount++;

	            cli.currentDesiredStatusMode = smep->status;

	            for ( int j=0; j < accounts.getCount(); j++ ) {
		            PROTOACCOUNT* pa = accounts[j];
		            if ( !Proto_IsAccountEnabled( pa ))
			            continue;
		            if ( MenusProtoCount > 1 && Proto_IsAccountLocked( pa ))
			            continue;

					Proto_SetStatus(pa->szModuleName, cli.currentDesiredStatusMode);
	            }
	            NotifyEventHooks( hStatusModeChangeEvent, cli.currentDesiredStatusMode, 0 );
	            DBWriteContactSettingWord( NULL, "CList", "Status", ( WORD )cli.currentDesiredStatusMode );
				return 1;
	}	}	}

	return 0;
}

INT_PTR FreeOwnerDataStatusMenu(WPARAM, LPARAM lParam)
{
	lpStatusMenuExecParam smep = (lpStatusMenuExecParam)lParam;
	if ( smep != NULL ) {
		FreeAndNil(( void** )&smep->proto);
		FreeAndNil(( void** )&smep->svc);
		FreeAndNil(( void** )&smep);
	}

	return (0);
}

/////////////////////////////////////////////////////////////////////////////////////////
// Other menu functions

//wparam MenuItemHandle
static INT_PTR ModifyCustomMenuItem(WPARAM wParam,LPARAM lParam)
{
	CLISTMENUITEM *mi=(CLISTMENUITEM*)lParam;
	TMO_MenuItem tmi;

	if ( lParam == 0 )
		return -1;
	if ( mi->cbSize != sizeof( CLISTMENUITEM ))
		return 1;

	tmi.cbSize = sizeof(tmi);
	tmi.flags = mi->flags;
	tmi.hIcon = mi->hIcon;
	tmi.hotKey = mi->hotKey;
	tmi.ptszName = mi->ptszName;
	return MO_ModifyMenuItem(( PMO_IntMenuItem )wParam, &tmi );
}

INT_PTR MenuProcessCommand(WPARAM wParam,LPARAM lParam)
{
	WORD cmd = LOWORD(wParam);

	if ( HIWORD(wParam) & MPCF_MAINMENU ) {
		int hst = LOWORD( wParam );
		if ( hst >= ID_STATUS_OFFLINE && hst <= ID_STATUS_OUTTOLUNCH ) {
			int pos = statustopos( hst );
			if ( pos != -1 && hStatusMainMenuHandles != NULL )
				return MO_ProcessCommand( hStatusMainMenuHandles[ pos ], lParam );
		}	}

	if ( !( cmd >= CLISTMENUIDMIN && cmd <= CLISTMENUIDMAX   ))
		return 0; // DO NOT process ids outside from clist menu id range		v0.7.0.27+

	//process old menu sys
	if ( HIWORD(wParam) & MPCF_CONTACTMENU )
		return MO_ProcessCommandBySubMenuIdent( (int)hContactMenuObject, LOWORD(wParam), lParam );

	//unknown old menu
	return MO_ProcessCommandByMenuIdent( LOWORD(wParam), lParam );
}

BOOL FindMenuHanleByGlobalID(HMENU hMenu, PMO_IntMenuItem id, MenuItemData* itdat)
{
	int i;
	PMO_IntMenuItem pimi;
	MENUITEMINFO mii = {0};
	BOOL inSub=FALSE;

	if ( !itdat )
		return FALSE;

	mii.cbSize = MENUITEMINFO_V4_SIZE;
	mii.fMask = MIIM_SUBMENU | MIIM_DATA;
	for ( i = GetMenuItemCount( hMenu )-1; i >= 0; i-- ) {
		GetMenuItemInfo( hMenu, i, TRUE, &mii );
		if ( mii.fType == MFT_SEPARATOR )
			continue;

		if ( mii.hSubMenu )
			inSub = FindMenuHanleByGlobalID( mii.hSubMenu, id, itdat );
		if (inSub)
			return inSub;

		pimi = MO_GetIntMenuItem(( HGENMENU )mii.dwItemData);
		if ( pimi != NULL ) {
			if ( pimi == id ) {
				itdat->OwnerMenu = hMenu;
				itdat->position = i;
				return TRUE;
	}	}	}

	return FALSE;
}

static INT_PTR MenuProcessHotkey(WPARAM vKey, LPARAM)
{
    prochotkey = true;

    bool res = 
        MO_ProcessHotKeys( hStatusMenuObject, vKey ) ||
        MO_ProcessHotKeys( hMainMenuObject, vKey );

    prochotkey = false;

    return res;
}

static int MenuIconsChanged(WPARAM, LPARAM)
{
	//just rebuild menu
	RebuildMenuOrder();
	cli.pfnCluiProtocolStatusChanged(0,0);
	return 0;
}

static INT_PTR MeasureMenuItem(WPARAM, LPARAM lParam)
{
	return MO_MeasureMenuItem(( LPMEASUREITEMSTRUCT )lParam );
}

static INT_PTR DrawMenuItem(WPARAM, LPARAM lParam)
{
	return MO_DrawMenuItem(( LPDRAWITEMSTRUCT )lParam );
}

int RecursiveDeleteMenu(HMENU hMenu)
{
	int cnt = GetMenuItemCount(hMenu);
	for ( int i=0; i < cnt; i++ ) {
		HMENU submenu = GetSubMenu(hMenu, 0);
		if (submenu) DestroyMenu(submenu);
		DeleteMenu(hMenu, 0, MF_BYPOSITION);
	}
	return 0;
}

static INT_PTR MenuGetMain(WPARAM, LPARAM)
{
	RecursiveDeleteMenu(hMainMenu);
	BuildMainMenu(0,0);
	return (INT_PTR)hMainMenu;
}

static INT_PTR BuildStatusMenu(WPARAM, LPARAM)
{
	ListParam param = { 0 };
	param.MenuObjectHandle = hStatusMenuObject;

	RecursiveDeleteMenu(hStatusMenu);
	CallService(MO_BUILDMENU,(WPARAM)hStatusMenu,(LPARAM)&param);
	return (INT_PTR)hStatusMenu;
}

static INT_PTR SetStatusMode(WPARAM wParam, LPARAM)
{
	prochotkey = true;
	MenuProcessCommand(MAKEWPARAM(LOWORD(wParam), MPCF_MAINMENU), 0);
	prochotkey = false;
	return 0;
}

int fnGetProtocolVisibility(const char* accName)
{
	if ( accName ) {
		PROTOACCOUNT* pa = Proto_GetAccount( accName );
		return pa && pa->bIsVisible && Proto_IsAccountEnabled( pa ) && 
            pa->ppro && (pa->ppro->GetCaps( PFLAGNUM_2, 0 ) & ~pa->ppro->GetCaps( PFLAGNUM_5, 0 ));
	}

	return FALSE;
}

int fnGetProtoIndexByPos(PROTOCOLDESCRIPTOR ** proto, int protoCnt, int Pos)
{
	int p;
	char buf[10];
	DBVARIANT dbv;

	_itoa( Pos, buf, 10 );
	if ( !DBGetContactSetting( NULL, "Protocols", buf, &dbv )) {
		for ( p=0; p < protoCnt; p++ ) {
			if ( lstrcmpA( proto[p]->szName, dbv.pszVal ) == 0 ) {
				DBFreeVariant( &dbv );
				return p;
		}	}

		DBFreeVariant( &dbv );
	}

	return -1;
}

int fnGetAccountIndexByPos(int Pos)
{
	int i;
	for ( i=0; i < accounts.getCount(); i++ )
		if ( accounts[i]->iOrder == Pos )
			return i;

	return -1;
}

void RebuildMenuOrder( void )
{
	int i,j,s;
	DWORD flags;
	
	BYTE bHideStatusMenu = DBGetContactSettingByte( NULL, "CLUI", "DontHideStatusMenu", 0 ); // cool perversion, though

	//clear statusmenu
	RecursiveDeleteMenu(hStatusMenu);

	//status menu
	if ( hStatusMenuObject != 0 ) {
		CallService(MO_REMOVEMENUOBJECT,(WPARAM)hStatusMenuObject,0);
		mir_free( hStatusMainMenuHandles );
		mir_free( hStatusMenuHandles );
	}

	TMenuParam tmp = { 0 };
	tmp.cbSize = sizeof(tmp);
	tmp.ExecService = "StatusMenuExecService";
	tmp.CheckService = "StatusMenuCheckService";
	tmp.name = "StatusMenu";

	hStatusMenuObject=(HANDLE)CallService(MO_CREATENEWMENUOBJECT,(WPARAM)0,(LPARAM)&tmp);
	MO_SetOptionsMenuObject( hStatusMenuObject, OPT_MENUOBJECT_SET_FREE_SERVICE, (INT_PTR)"CLISTMENUS/FreeOwnerDataStatusMenu" );

	hStatusMainMenuHandles = ( PMO_IntMenuItem* )mir_calloc( SIZEOF(statusModeList) * sizeof( PMO_IntMenuItem* ));
	hStatusMainMenuHandlesCnt = SIZEOF(statusModeList);

	hStatusMenuHandles = ( tStatusMenuHandles* )mir_calloc(sizeof(tStatusMenuHandles)*accounts.getCount());
	hStatusMenuHandlesCnt = accounts.getCount();

	FreeMenuProtos();

	for ( s=0; s < accounts.getCount(); s++ ) {
		i = cli.pfnGetAccountIndexByPos( s );
		if ( i == -1 )
			continue;

		PROTOACCOUNT* pa = accounts[i];
		int pos = 0;
		if ( !bHideStatusMenu && !cli.pfnGetProtocolVisibility( pa->szModuleName ))
			continue;

		flags = pa->ppro->GetCaps( PFLAGNUM_2, 0 ) & ~pa->ppro->GetCaps( PFLAGNUM_5, 0 );
		int j;
		HICON ic;
		TCHAR tbuf[256];

		//adding root
		TMO_MenuItem tmi = { 0 };
		tmi.cbSize = sizeof(tmi);
		tmi.flags = CMIF_TCHAR | CMIF_ROOTHANDLE | CMIF_KEEPUNTRANSLATED;
		tmi.position = pos++;
		tmi.hIcon = ic = (HICON)CallProtoService( pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0 );

		if ( Proto_IsAccountLocked( pa ) && cli.bDisplayLocked ) {
			mir_sntprintf( tbuf, SIZEOF(tbuf), TranslateT("%s (locked)"), pa->tszAccountName );
			tmi.ptszName = tbuf;
		}
		else tmi.ptszName = pa->tszAccountName;

		{
			//owner data
			lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_calloc( sizeof( StatusMenuExecParam ));
			smep->proto = mir_strdup(pa->szModuleName);
			tmi.ownerdata = smep;
		}
		PMO_IntMenuItem rootmenu = MO_AddNewMenuItem( hStatusMenuObject, &tmi );

		memset(&tmi,0,sizeof(tmi));
		tmi.cbSize = sizeof(tmi);
		tmi.flags = CMIF_TCHAR | CMIF_ROOTHANDLE | CMIF_KEEPUNTRANSLATED;
		tmi.root = rootmenu;
		tmi.position = pos++;
		tmi.hIcon = ic;
		{
			//owner data
			lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_alloc( sizeof( StatusMenuExecParam ));
			memset( smep, 0, sizeof( *smep ));
			smep->proto = mir_strdup(pa->szModuleName);
			tmi.ownerdata = smep;
		}

		if ( Proto_IsAccountLocked( pa ))
			tmi.flags |= CMIF_CHECKED;

		if (( tmi.flags & CMIF_CHECKED ) && cli.bDisplayLocked ) {
			mir_sntprintf( tbuf, SIZEOF(tbuf), TranslateT("%s (locked)"), pa->tszAccountName );
			tmi.ptszName = tbuf;
		}
		else tmi.ptszName = pa->tszAccountName;

		PMO_IntMenuItem menuHandle = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
		((lpStatusMenuExecParam)tmi.ownerdata)->protoindex = ( int )menuHandle;
		MO_ModifyMenuItem( menuHandle, &tmi );

		cli.menuProtos=(MenuProto*)mir_realloc(cli.menuProtos, sizeof(MenuProto)*(cli.menuProtoCount+1));
		memset(&(cli.menuProtos[cli.menuProtoCount]),0,sizeof(MenuProto));
		cli.menuProtos[cli.menuProtoCount].pMenu = rootmenu;
		cli.menuProtos[cli.menuProtoCount].szProto = mir_strdup(pa->szModuleName);

		cli.menuProtoCount++;
		{
			char buf[256];
			mir_snprintf( buf, SIZEOF(buf), "RootProtocolIcon_%s", pa->szModuleName );
			MO_SetOptionsMenuItem( menuHandle, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
		}
		DestroyIcon(ic);
		pos += 500000;

		for ( j=0; j < SIZEOF(statusModeList); j++ ) {
			if ( !( flags & statusModePf2List[j] ))
				continue;

			//adding
			memset( &tmi, 0, sizeof( tmi ));
			tmi.cbSize = sizeof(tmi);
			tmi.flags = CMIF_ROOTHANDLE | CMIF_TCHAR;
			if ( statusModeList[j] == ID_STATUS_OFFLINE )
				tmi.flags |= CMIF_CHECKED;
			tmi.root = rootmenu;
			tmi.position = pos++;
			tmi.ptszName = cli.pfnGetStatusModeDescription( statusModeList[j], GSMDF_UNTRANSLATED );
			tmi.hIcon = LoadSkinProtoIcon( pa->szModuleName, statusModeList[j] );
			{
				//owner data
				lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_calloc( sizeof( StatusMenuExecParam ));
				smep->custom = FALSE;
				smep->status = statusModeList[j];
				smep->protoindex = i;
				smep->proto = mir_strdup(pa->szModuleName);
				tmi.ownerdata = smep;
			}

			hStatusMenuHandles[i].protoindex = i;
			hStatusMenuHandles[i].protostatus[j] = statusModeList[j];
			hStatusMenuHandles[i].menuhandle[j] = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
			{
				char buf[ 256 ];
				mir_snprintf(buf, SIZEOF(buf), "ProtocolIcon_%s_%s",pa->szModuleName,tmi.pszName);
				MO_SetOptionsMenuItem( hStatusMenuHandles[i].menuhandle[j], OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
			}
			IconLib_ReleaseIcon(tmi.hIcon,0);
	}	}

	NotifyEventHooks(cli.hPreBuildStatusMenuEvent, 0, 0);
	int pos = 200000;

	//add to root menu
	for ( j=0; j < SIZEOF(statusModeList); j++ ) {
		for ( i=0; i < accounts.getCount(); i++ ) {
			PROTOACCOUNT* pa = accounts[i];
			if ( !bHideStatusMenu && !cli.pfnGetProtocolVisibility( pa->szModuleName ))
				continue;

			flags = pa->ppro->GetCaps(PFLAGNUM_2, 0) & ~pa->ppro->GetCaps(PFLAGNUM_5, 0);
			if ( !( flags & statusModePf2List[j] ))
				continue;

			TMO_MenuItem tmi = { 0 };
			tmi.cbSize = sizeof( tmi );
			tmi.flags = CMIF_ROOTHANDLE | CMIF_TCHAR;
			if ( statusModeList[j] == ID_STATUS_OFFLINE )
				tmi.flags |= CMIF_CHECKED;

			tmi.hIcon = LoadSkinIcon( skinIconStatusList[j] );
			tmi.position = pos++;
			tmi.hotKey = MAKELPARAM(MOD_CONTROL,'0'+j);
			{
				//owner data
				lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_alloc( sizeof( StatusMenuExecParam ));
				smep->custom = FALSE;
				smep->status = statusModeList[j];
				smep->proto = NULL;
				smep->svc = NULL;
				tmi.ownerdata = smep;
			}
			{
				TCHAR buf[ 256 ], hotkeyName[ 100 ];
				WORD hotKey = GetHotkeyValue( statusHotkeys[j] );
				HotkeyToName( hotkeyName, SIZEOF(hotkeyName), HIBYTE(hotKey), LOBYTE(hotKey));
				mir_sntprintf( buf, SIZEOF( buf ), TranslateT("%s\t%s"),
					cli.pfnGetStatusModeDescription( statusModeList[j], 0 ), hotkeyName );
				tmi.ptszName = buf;
				tmi.hotKey = MAKELONG(HIBYTE(hotKey), LOBYTE(hotKey));
				hStatusMainMenuHandles[j] = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
			}
			{
				char buf[ 256 ];
				mir_snprintf( buf, sizeof( buf ), "Root2ProtocolIcon_%s_%s", pa->szModuleName, tmi.pszName );
				MO_SetOptionsMenuItem( hStatusMainMenuHandles[j], OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
			}
			IconLib_ReleaseIcon( tmi.hIcon, 0 );
			break;
	}	}

	BuildStatusMenu(0,0);
}

/////////////////////////////////////////////////////////////////////////////////////////

static int sttRebuildHotkeys( WPARAM, LPARAM )
{
	TMO_MenuItem tmi = { 0 };
	tmi.cbSize = sizeof( tmi );
	tmi.flags = CMIM_HOTKEY | CMIM_NAME | CMIF_TCHAR;

	for ( int j=0; j < SIZEOF(statusModeList); j++ ) {
		TCHAR buf[ 256 ], hotkeyName[ 100 ];
		WORD hotKey = GetHotkeyValue( statusHotkeys[j] );
		HotkeyToName( hotkeyName, SIZEOF(hotkeyName), HIBYTE(hotKey), LOBYTE(hotKey));
		mir_sntprintf( buf, SIZEOF( buf ), TranslateT("%s\t%s"),
			cli.pfnGetStatusModeDescription( statusModeList[j], 0 ), hotkeyName );
		tmi.ptszName = buf;
		tmi.hotKey = MAKELONG(HIBYTE(hotKey), LOBYTE(hotKey));
		MO_ModifyMenuItem( hStatusMainMenuHandles[j], &tmi );
	}

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

int statustopos(int status)
{
	int j;
	for ( j = 0; j < SIZEOF(statusModeList); j++ )
		if ( status == statusModeList[j] )
			return j;

	return -1;
}

static int MenuProtoAck(WPARAM, LPARAM lParam)
{
	int i;
	ACKDATA* ack=(ACKDATA*)lParam;
	int overallStatus;
	TMO_MenuItem tmi;

	if ( ack->type != ACKTYPE_STATUS ) return 0;
	if ( ack->result != ACKRESULT_SUCCESS ) return 0;
	if ( hStatusMainMenuHandles == NULL ) return 0;

    if ( cli.pfnGetProtocolVisibility( ack->szModule ) == 0 ) return 0;

    overallStatus = GetAverageMode();

	memset(&tmi,0,sizeof(tmi));
	tmi.cbSize=sizeof(tmi);
	if (overallStatus >= ID_STATUS_OFFLINE) {
		int pos = statustopos(cli.currentStatusMenuItem);
		if (pos==-1) pos=0;
		{   // reset all current possible checked statuses
			int pos2;
			for (pos2=0; pos2<hStatusMainMenuHandlesCnt; pos2++)
			{
				if (pos2>=0 && pos2 < hStatusMainMenuHandlesCnt)
				{
					tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE;
					MO_ModifyMenuItem( hStatusMainMenuHandles[pos2], &tmi );
		}	}	}

		cli.currentStatusMenuItem=overallStatus;
		pos = statustopos(cli.currentStatusMenuItem);
		if (pos>=0 && pos < hStatusMainMenuHandlesCnt) {
			tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE | CMIF_CHECKED;
			MO_ModifyMenuItem( hStatusMainMenuHandles[pos], &tmi );
		}
//		cli.currentDesiredStatusMode = cli.currentStatusMenuItem;
	}
	else {
		int pos = statustopos( cli.currentStatusMenuItem );
		if ( pos == -1 ) pos=0;
		if ( pos >= 0 && pos < hStatusMainMenuHandlesCnt ) {
			tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE;
			MO_ModifyMenuItem( hStatusMainMenuHandles[pos], &tmi );
		}
		//SetMenuDefaultItem(hStatusMenu,-1,FALSE);
		cli.currentStatusMenuItem=0;
	}

	for ( i=0; i < accounts.getCount(); i++ ) {
		if ( !lstrcmpA( accounts[i]->szModuleName, ack->szModule )) {
		//hProcess is previous mode, lParam is new mode
		if ((( int )ack->hProcess >= ID_STATUS_OFFLINE || ( int )ack->hProcess == 0 ) && ( int )ack->hProcess < ID_STATUS_OFFLINE + SIZEOF(statusModeList)) {
			int pos = statustopos(( int )ack->hProcess);
			if ( pos == -1 )
				pos = 0;
			for ( pos = 0; pos < SIZEOF(statusModeList); pos++ ) {
				tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE;
				MO_ModifyMenuItem( hStatusMenuHandles[i].menuhandle[pos], &tmi );
		}	}

		if ( ack->lParam >= ID_STATUS_OFFLINE && ack->lParam < ID_STATUS_OFFLINE + SIZEOF(statusModeList)) {
			int pos = statustopos(( int )ack->lParam );
			if ( pos >= 0 && pos < SIZEOF(statusModeList)) {
				tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE | CMIF_CHECKED;
				MO_ModifyMenuItem( hStatusMenuHandles[i].menuhandle[pos], &tmi );
			}	}
			break;
	}	}

	//BuildStatusMenu(0,0);
	return 0;
}

static MenuProto* FindProtocolMenu( const char* proto )
{
	for (int i=0; i < cli.menuProtoCount; i++)
		if ( cli.menuProtos[i].pMenu && !lstrcmpiA( cli.menuProtos[i].szProto, proto ))
			return &cli.menuProtos[i];

	if ( cli.menuProtoCount == 1 )
		if ( !lstrcmpiA( cli.menuProtos[0].szProto, proto ))
			return &cli.menuProtos[0];

	return NULL;
}

HGENMENU fnGetProtocolMenu( const char* proto )
{
	MenuProto* mp = FindProtocolMenu( proto );
	if ( mp )
		return mp->pMenu;

	return NULL;
}

static INT_PTR AddStatusMenuItem(WPARAM wParam,LPARAM lParam)
{
	CLISTMENUITEM *mi = ( CLISTMENUITEM* )lParam;
	if ( mi->cbSize != sizeof( CLISTMENUITEM ))
		return 0;

	PMO_IntMenuItem pRoot = NULL;
	lpStatusMenuExecParam smep = NULL;

	TMO_MenuItem tmi = { 0 };
	tmi.cbSize = sizeof(tmi);
	tmi.hIcon = mi->hIcon;
	tmi.hotKey = mi->hotKey;
	tmi.position = mi->position;
	tmi.pszName = mi->pszName;
	tmi.flags = mi->flags;
	tmi.root = mi->hParentMenu;

	// for new style menus the pszPopupName contains the root menu handle
	if ( mi->flags & CMIF_ROOTHANDLE )
		pRoot = MO_GetIntMenuItem( mi->hParentMenu );

	// for old style menus the pszPopupName really means the popup name
	else {
		MenuProto* mp = FindProtocolMenu( mi->pszContactOwner );
		if ( mp && mi->pszPopupName ) {
			if ( mp->pMenu ) {
				#if defined _UNICODE
					TCHAR* ptszName = ( mi->flags & CMIF_UNICODE ) ? mir_tstrdup(mi->ptszPopupName) : mir_a2t(mi->pszPopupName);
					pRoot = MO_RecursiveWalkMenu( mp->pMenu->submenu.first, FindRoot, ptszName );
					mir_free( ptszName );
				#else
					pRoot = MO_RecursiveWalkMenu( mp->pMenu->submenu.first, FindRoot, mi->pszPopupName );
				#endif
			}
			if ( pRoot == NULL ) {
				TMO_MenuItem tmi = { 0 };
				tmi.cbSize = sizeof(tmi);
				tmi.flags = (mi->flags & CMIF_UNICODE) | CMIF_ROOTHANDLE;
				tmi.position = 1001;
				tmi.root = mp->pMenu;
				tmi.hIcon = NULL;
				tmi.pszName = mi->pszPopupName;
				pRoot = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
			}

			tmi.flags |= CMIF_ROOTHANDLE;
			tmi.root = pRoot;
	}	}

	if (wParam) {
		int * res=(int*)wParam;
		*res = ( int )pRoot;
	}

	//owner data
	if ( mi->pszService ) {
		smep = ( lpStatusMenuExecParam )mir_calloc(sizeof(StatusMenuExecParam));
		smep->custom = TRUE;
		smep->svc=mir_strdup(mi->pszService);
		{
			char *buf=mir_strdup(mi->pszService);
			int i=0;
			while(buf[i]!='\0' && buf[i]!='/') i++;
			buf[i]='\0';
				smep->proto=mir_strdup(buf);
			mir_free(buf);
		}
		tmi.ownerdata = smep;
	}
	PMO_IntMenuItem menuHandle = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
	if ( smep )
		smep->hMenuItem = menuHandle;

	char buf[MAX_PATH+64];
	#if defined( _UNICODE )
	{
		char* p = ( pRoot ) ? mir_t2a( pRoot->mi.ptszName ) : NULL;
		mir_snprintf( buf, SIZEOF(buf), "%s/%s", ( p ) ? p : "", mi->pszService ? mi->pszService : "" );
		mir_free( p );
	}
	#else
		mir_snprintf( buf, SIZEOF(buf), "%s/%s", pRoot ? pRoot->mi.ptszName : _T(""), mi->pszService ? mi->pszService : "" );
	#endif
	MO_SetOptionsMenuItem( menuHandle, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );

	return ( INT_PTR )menuHandle;
}

static INT_PTR HotkeySetStatus(WPARAM wParam,LPARAM lParam)
{
	return SetStatusMode( lParam, 0 );
}

/////////////////////////////////////////////////////////////////////////////////////////
// PROTOCOL MENU

static INT_PTR AddProtoMenuItem(WPARAM wParam,LPARAM lParam)
{
	if ( DBGetContactSettingByte( NULL, "CList", "MoveProtoMenus", FALSE ))
		return AddStatusMenuItem( wParam, lParam );

	return AddMainMenuItem( wParam, lParam );
}

/////////////////////////////////////////////////////////////////////////////////////////

void InitCustomMenus(void)
{
	CreateServiceFunction("MainMenuExecService",MainMenuExecService);

	CreateServiceFunction("ContactMenuExecService",ContactMenuExecService);
	CreateServiceFunction("ContactMenuCheckService",ContactMenuCheckService);

	CreateServiceFunction("StatusMenuExecService",StatusMenuExecService);
	CreateServiceFunction("StatusMenuCheckService",StatusMenuCheckService);

	//free services
	CreateServiceFunction("CLISTMENUS/FreeOwnerDataMainMenu",FreeOwnerDataMainMenu);
	CreateServiceFunction("CLISTMENUS/FreeOwnerDataContactMenu",FreeOwnerDataContactMenu);
	CreateServiceFunction("CLISTMENUS/FreeOwnerDataStatusMenu",FreeOwnerDataStatusMenu);

	CreateServiceFunction(MS_CLIST_SETSTATUSMODE, SetStatusMode);

	CreateServiceFunction(MS_CLIST_ADDMAINMENUITEM,AddMainMenuItem);
	CreateServiceFunction(MS_CLIST_ADDSTATUSMENUITEM,AddStatusMenuItem);
	CreateServiceFunction(MS_CLIST_MENUGETMAIN,MenuGetMain);
	CreateServiceFunction(MS_CLIST_REMOVEMAINMENUITEM,RemoveMainMenuItem);
	CreateServiceFunction(MS_CLIST_MENUBUILDMAIN,BuildMainMenu);

	CreateServiceFunction(MS_CLIST_ADDCONTACTMENUITEM,AddContactMenuItem);
	CreateServiceFunction(MS_CLIST_MENUBUILDCONTACT,BuildContactMenu);
	CreateServiceFunction(MS_CLIST_REMOVECONTACTMENUITEM,RemoveContactMenuItem);

	CreateServiceFunction(MS_CLIST_MODIFYMENUITEM,ModifyCustomMenuItem);
	CreateServiceFunction(MS_CLIST_MENUMEASUREITEM,MeasureMenuItem);
	CreateServiceFunction(MS_CLIST_MENUDRAWITEM,DrawMenuItem);

	CreateServiceFunction(MS_CLIST_MENUGETSTATUS,BuildStatusMenu);
	CreateServiceFunction(MS_CLIST_MENUPROCESSCOMMAND,MenuProcessCommand);
	CreateServiceFunction(MS_CLIST_MENUPROCESSHOTKEY,MenuProcessHotkey);

	CreateServiceFunction(MS_CLIST_ADDPROTOMENUITEM,AddProtoMenuItem);

	hPreBuildContactMenuEvent=CreateHookableEvent(ME_CLIST_PREBUILDCONTACTMENU);
	hPreBuildMainMenuEvent=CreateHookableEvent(ME_CLIST_PREBUILDMAINMENU);
	cli.hPreBuildStatusMenuEvent=CreateHookableEvent(ME_CLIST_PREBUILDSTATUSMENU);
	hStatusModeChangeEvent = CreateHookableEvent( ME_CLIST_STATUSMODECHANGE );

	hAckHook=(HANDLE)HookEvent(ME_PROTO_ACK,MenuProtoAck);

	hMainMenu = CreatePopupMenu();
	hStatusMenu = CreatePopupMenu();

	hStatusMainMenuHandles=NULL;
	hStatusMainMenuHandlesCnt=0;

	hStatusMenuHandles=NULL;
	hStatusMenuHandlesCnt=0;

	//new menu sys
	InitGenMenu();

	//main menu
	{
		TMenuParam tmp = { 0 };
		tmp.cbSize=sizeof(tmp);
		tmp.CheckService=NULL;
		tmp.ExecService="MainMenuExecService";
		tmp.name="MainMenu";
		hMainMenuObject=(HANDLE)CallService(MO_CREATENEWMENUOBJECT,(WPARAM)0,(LPARAM)&tmp);
	}

	MO_SetOptionsMenuObject( hMainMenuObject, OPT_USERDEFINEDITEMS, TRUE );
	MO_SetOptionsMenuObject( hMainMenuObject, OPT_MENUOBJECT_SET_FREE_SERVICE, (INT_PTR)"CLISTMENUS/FreeOwnerDataMainMenu" );

	//contact menu
	{
		TMenuParam tmp = { 0 };
		tmp.cbSize=sizeof(tmp);
		tmp.CheckService="ContactMenuCheckService";
		tmp.ExecService="ContactMenuExecService";
		tmp.name="ContactMenu";
		hContactMenuObject=(HANDLE)CallService(MO_CREATENEWMENUOBJECT,(WPARAM)0,(LPARAM)&tmp);
	}

	MO_SetOptionsMenuObject( hContactMenuObject, OPT_USERDEFINEDITEMS, TRUE );
	MO_SetOptionsMenuObject( hContactMenuObject, OPT_MENUOBJECT_SET_FREE_SERVICE, (INT_PTR)"CLISTMENUS/FreeOwnerDataContactMenu" );

	// initialize hotkeys
	CreateServiceFunction(MS_CLIST_HKSTATUS, HotkeySetStatus);

	HOTKEYDESC hkd = { 0 };
	hkd.cbSize = sizeof( hkd );
	hkd.ptszSection = _T("Status");
	hkd.dwFlags = HKD_TCHAR;
	for ( int i = 0; i < SIZEOF(statusHotkeys); i++ ) {
		char szName[30];
		mir_snprintf( szName, SIZEOF(szName), "StatusHotKey_%d", i );
		hkd.pszName = szName;
		hkd.lParam = statusModeList[i];
		hkd.ptszDescription = fnGetStatusModeDescription( hkd.lParam, 0 );
		hkd.DefHotKey = HOTKEYCODE( HOTKEYF_CONTROL, '0'+i ) | HKF_MIRANDA_LOCAL;
		hkd.pszService = MS_CLIST_HKSTATUS;
		statusHotkeys[i] = CallService( MS_HOTKEY_REGISTER, 0, LPARAM( &hkd ));
	}

	HookEvent( ME_HOTKEYS_CHANGED, sttRebuildHotkeys );

   // add exit command to menu
	{
		CLISTMENUITEM mi = { 0 };
		mi.cbSize = sizeof( mi );
		mi.position = 0x7fffffff;
		mi.flags = CMIF_ICONFROMICOLIB;
		mi.pszService = "CloseAction";
		mi.pszName = LPGEN("E&xit");
		mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_EXIT );
		AddMainMenuItem( 0, ( LPARAM )&mi );
	}

	cli.currentStatusMenuItem=ID_STATUS_OFFLINE;
	cli.currentDesiredStatusMode=ID_STATUS_OFFLINE;

	if ( IsWinVer98Plus() )
		HookEvent(ME_SKIN_ICONSCHANGED, MenuIconsChanged );
}

void UninitCustomMenus(void)
{
	mir_free(hStatusMainMenuHandles);
	hStatusMainMenuHandles = NULL;

	mir_free( hStatusMenuHandles );
	hStatusMenuHandles = NULL;

	if ( hMainMenuObject   ) CallService( MO_REMOVEMENUOBJECT, (WPARAM)hMainMenuObject, 0 );
	if ( hStatusMenuObject ) CallService( MO_REMOVEMENUOBJECT, (WPARAM)hMainMenuObject, 0 );

	UnloadMoveToGroup();
	FreeMenuProtos();

	DestroyMenu(hMainMenu);
	DestroyMenu(hStatusMenu);
	UnhookEvent(hAckHook);
}
