/*

Jabber Protocol Plugin for Miranda IM
Copyright ( C ) 2002-04  Santithorn Bunchua
Copyright ( C ) 2005-12  George Hazan
Copyright ( C ) 2007-09  Maxim Mluhov
Copyright ( C ) 2007-09  Victor Pavlychko

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 "jabber.h"
#include "jabber_iq.h"
#include "jabber_privacy.h"

#include <m_icolib.h>
#include <m_genmenu.h>
#include <m_clistint.h>

#define JABBER_PL_BUSY_MSG	 "Sending request, please wait..."

BOOL CJabberProto::OnIqRequestPrivacyLists( HXML, CJabberIqInfo* pInfo )
{
	if ( pInfo->GetIqType() == JABBER_IQ_TYPE_SET ) {
		if ( !m_pDlgPrivacyLists )
		{
			m_privacyListManager.RemoveAllLists();
			QueryPrivacyLists();
		}
		else m_pDlgPrivacyLists->SetStatusText(TranslateT("Warning: privacy lists were changed on server."));

		XmlNodeIq iq( _T("result"), pInfo );
		m_ThreadInfo->send( iq );
	}
	return TRUE;
}

void CJabberProto::OnIqResultPrivacyListModify( HXML, CJabberIqInfo* pInfo )
{
	if ( !pInfo->m_pUserData )
		return;

	CPrivacyListModifyUserParam *pParam = ( CPrivacyListModifyUserParam * )pInfo->m_pUserData;

	if ( pInfo->m_nIqType != JABBER_IQ_TYPE_RESULT )
		pParam->m_bAllOk = FALSE;

	InterlockedDecrement( &pParam->m_dwCount );
	if ( !pParam->m_dwCount ) {
		TCHAR szText[ 512 ];
		if ( !pParam->m_bAllOk )
			mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Error occurred while applying changes"));
		else
			mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Privacy lists successfully saved"));
		if (m_pDlgPrivacyLists)
			m_pDlgPrivacyLists->SetStatusText( szText );
		// FIXME: enable apply button
		delete pParam;
	}
}

void CJabberProto::OnIqResultPrivacyList( HXML iqNode )
{
	if ( !iqNode )
		return;

	const TCHAR *type = xmlGetAttrValue( iqNode, _T("type"));
	if ( !type )
		return;

	if ( !_tcscmp( type, _T("result"))) {
		HXML query = xmlGetChild( iqNode , "query" );
		if ( !query )
			return;
		HXML list = xmlGetChild( query , "list" );
		if ( !list )
			return;
		TCHAR *szListName = ( TCHAR* )xmlGetAttrValue( list, _T("name"));
		if ( !szListName )
			return;
		m_privacyListManager.Lock();
		CPrivacyList* pList = m_privacyListManager.FindList( szListName );
		if ( !pList ) {
			m_privacyListManager.AddList( szListName );
			pList = m_privacyListManager.FindList( szListName );
			if ( !pList ) {
				m_privacyListManager.Unlock();
				return;
		}	}

		HXML item;
		for ( int i = 1; ( item = xmlGetNthChild( list, _T("item"), i )) != NULL; i++ ) {
			const TCHAR *itemType = xmlGetAttrValue( item, _T("type"));
			PrivacyListRuleType nItemType = Else;
			if ( itemType ) {
				if ( !_tcsicmp( itemType, _T( "jid" )))
					nItemType = Jid;
				else if ( !_tcsicmp( itemType, _T( "group" )))
					nItemType = Group;
				else if ( !_tcsicmp( itemType, _T( "subscription" )))
					nItemType = Subscription;
			}

			const TCHAR *itemValue = xmlGetAttrValue( item, _T("value"));

			const TCHAR *itemAction = xmlGetAttrValue( item, _T("action"));
			BOOL bAllow = TRUE;
			if ( itemAction && !_tcsicmp( itemAction, _T( "deny" )))
				bAllow = FALSE;

			const TCHAR *itemOrder = xmlGetAttrValue( item, _T("order"));
			DWORD dwOrder = 0;
			if ( itemOrder )
				dwOrder = _ttoi( itemOrder );

			DWORD dwPackets = 0;
			if ( xmlGetChild( item , "message" ))
				dwPackets |= JABBER_PL_RULE_TYPE_MESSAGE;
			if ( xmlGetChild( item , "presence-in" ))
				dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_IN;
			if ( xmlGetChild( item , "presence-out" ))
				dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;
			if ( xmlGetChild( item , "iq" ))
				dwPackets |= JABBER_PL_RULE_TYPE_IQ;

			pList->AddRule( nItemType, itemValue, bAllow, dwOrder, dwPackets );
		}
		pList->Reorder();
		pList->SetLoaded();
		pList->SetModified(FALSE);
		m_privacyListManager.Unlock();

		UI_SAFE_NOTIFY(m_pDlgPrivacyLists, WM_JABBER_REFRESH);
}	}

CPrivacyList* GetSelectedList(HWND hDlg)
{
	LRESULT nCurSel = SendDlgItemMessage( hDlg, IDC_LB_LISTS, LB_GETCURSEL, 0, 0 );
	if ( nCurSel == LB_ERR )
		return NULL;

	LRESULT nItemData = SendDlgItemMessage( hDlg, IDC_LB_LISTS, LB_GETITEMDATA, nCurSel, 0 );
	if ( nItemData == LB_ERR || nItemData == 0 )
		return NULL;

	return ( CPrivacyList* )nItemData;
}

CPrivacyListRule* GetSelectedRule(HWND hDlg)
{
	LRESULT nCurSel = SendDlgItemMessage( hDlg, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
	if ( nCurSel == LB_ERR)
		return NULL;

	LRESULT nItemData = SendDlgItemMessage( hDlg, IDC_PL_RULES_LIST, LB_GETITEMDATA, nCurSel, 0 );
	if ( nItemData == LB_ERR || nItemData == 0 )
		return NULL;

	return (CPrivacyListRule* )nItemData;
}

void CJabberProto::OnIqResultPrivacyListActive( HXML iqNode, CJabberIqInfo* pInfo )
{
	CPrivacyList *pList = (CPrivacyList *)pInfo->GetUserData();

	if ( m_pDlgPrivacyLists )
		EnableWindow( GetDlgItem( m_pDlgPrivacyLists->GetHwnd(), IDC_ACTIVATE ), TRUE );

	if ( !iqNode )
		return;

	const TCHAR *type = xmlGetAttrValue( iqNode, _T("type"));
	if ( !type )
		return;

	TCHAR szText[ 512 ];
	szText[0] = _T('\0');
	m_privacyListManager.Lock();
	if ( !_tcscmp( type, _T("result"))) {
		if ( pList ) {
			m_privacyListManager.SetActiveListName( pList->GetListName());
			mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Privacy list %s set as active"), pList->GetListName());
		}
		else {
			m_privacyListManager.SetActiveListName( NULL );
			mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Active privacy list successfully declined"));
		}
	}
	else mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Error occurred while setting active list"));

	m_privacyListManager.Unlock();

	if ( m_pDlgPrivacyLists )
	{
		m_pDlgPrivacyLists->SetStatusText( szText );
		RedrawWindow(GetDlgItem(m_pDlgPrivacyLists->GetHwnd(), IDC_LB_LISTS), NULL, NULL, RDW_INVALIDATE);
	}

	BuildPrivacyListsMenu( true );
}

void CJabberProto::OnIqResultPrivacyListDefault( HXML iqNode, CJabberIqInfo* pInfo )
{
	CPrivacyList *pList = (CPrivacyList *)pInfo->GetUserData();

	if ( m_pDlgPrivacyLists )
		EnableWindow( GetDlgItem( m_pDlgPrivacyLists->GetHwnd(), IDC_SET_DEFAULT ), TRUE );

	if ( !iqNode )
		return;

	const TCHAR *type = xmlGetAttrValue( iqNode, _T("type"));
	if ( !type )
		return;

	TCHAR szText[ 512 ];
	szText[0] = _T('\0');
	m_privacyListManager.Lock();
	if ( !_tcscmp( type, _T("result"))) {
		if ( pList ) {
			m_privacyListManager.SetDefaultListName( pList->GetListName());
			mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Privacy list %s set as default"), pList->GetListName());
		}
		else {
			m_privacyListManager.SetDefaultListName( NULL );
			mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Default privacy list successfully declined"));
		}
	}
	else {
		mir_sntprintf( szText, SIZEOF( szText ), TranslateT("Error occurred while setting default list"));
	}
	m_privacyListManager.Unlock();

	if ( m_pDlgPrivacyLists )
	{
		m_pDlgPrivacyLists->SetStatusText( szText );
		RedrawWindow(GetDlgItem(m_pDlgPrivacyLists->GetHwnd(), IDC_LB_LISTS), NULL, NULL, RDW_INVALIDATE);
	}
}

void CJabberProto::OnIqResultPrivacyLists( HXML iqNode, CJabberIqInfo* pInfo )
{
	if ( pInfo->m_nIqType != JABBER_IQ_TYPE_RESULT )
		return;

	HXML query = xmlGetChild( iqNode, "query" );
	if ( !query )
		return;

	if ( m_ThreadInfo )
		m_ThreadInfo->jabberServerCaps |= JABBER_CAPS_PRIVACY_LISTS;

	m_privacyListManager.Lock();
	m_privacyListManager.RemoveAllLists();

	for ( int i = 1; ; i++ ) {
		HXML list = xmlGetNthChild( query, _T("list"), i );
		if ( !list )
			break;

		const TCHAR *listName = xmlGetAttrValue( list, _T("name"));
		if ( listName ) {
			m_privacyListManager.AddList(( TCHAR* )listName);

			// Query contents only if list editior is visible!
			if ( m_pDlgPrivacyLists ) {
				int iqId = SerialNext();
				IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultPrivacyList);
				m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId ) << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS)) << XCHILD( _T("list")) << XATTR( _T("name"), listName ));
	}	}	}

	const TCHAR *szName = NULL;
	HXML node = xmlGetChild( query , "active" );
	if ( node )
		szName = xmlGetAttrValue( node, _T("name"));
	m_privacyListManager.SetActiveListName( szName );

	szName = NULL;
	node = xmlGetChild( query , "default" );
	if ( node )
		szName = xmlGetAttrValue( node, _T("name"));
	m_privacyListManager.SetDefaultListName( szName );

	m_privacyListManager.Unlock();

	UI_SAFE_NOTIFY(m_pDlgPrivacyLists, WM_JABBER_REFRESH);

	BuildPrivacyListsMenu( true );
}

/////////////////////////////////////////////////////////////////////////////////////////
// Add privacy list box
class CJabberDlgPrivacyAddList: public CJabberDlgBase
{
	typedef CJabberDlgBase CSuper;

public:
	TCHAR szLine[512];

	CJabberDlgPrivacyAddList(CJabberProto *proto, HWND hwndParent):
		CJabberDlgBase(proto, IDD_PRIVACY_ADD_LIST, hwndParent, false),
		m_txtName(this, IDC_EDIT_NAME),
		m_btnOk(this, IDOK),
		m_btnCancel(this, IDCANCEL)
	{
		m_btnOk.OnClick = Callback( this, &CJabberDlgPrivacyAddList::btnOk_OnClick );
		m_btnCancel.OnClick = Callback( this, &CJabberDlgPrivacyAddList::btnCancel_OnClick);
	}

	void btnOk_OnClick(CCtrlButton*)
	{
		GetDlgItemText(m_hwnd, IDC_EDIT_NAME, szLine, SIZEOF(szLine));
		EndDialog(m_hwnd, 1);
	}
	void btnCancel_OnClick(CCtrlButton*)
	{
		*szLine = 0;
		EndDialog(m_hwnd, 0);
	}

private:
	CCtrlEdit	m_txtName;
	CCtrlButton	m_btnOk;
	CCtrlButton	m_btnCancel;
};

/////////////////////////////////////////////////////////////////////////////////////////
// Privacy rule editor
class CJabberDlgPrivacyRule: public CJabberDlgBase
{
	typedef CJabberDlgBase CSuper;

	CCtrlButton	m_btnOk;
	CCtrlButton	m_btnCancel;
	CCtrlCombo	m_cbType;

public:
	CPrivacyListRule *m_pRule;

	CJabberDlgPrivacyRule(CJabberProto *proto, HWND hwndParent, CPrivacyListRule *pRule):
		CJabberDlgBase(proto, IDD_PRIVACY_RULE, hwndParent, false),
		m_btnOk(this, IDOK),
		m_btnCancel(this, IDCANCEL),
		m_cbType(this, IDC_COMBO_TYPE)
	{
		m_pRule = pRule;
		m_cbType.OnChange = Callback(this, &CJabberDlgPrivacyRule::cbType_OnChange);
		m_btnOk.OnClick = Callback(this, &CJabberDlgPrivacyRule::btnOk_OnClick);
		m_btnCancel.OnClick = Callback(this, &CJabberDlgPrivacyRule::btnCancel_OnClick);
	}

	virtual void OnInitDialog()
	{
		CSuper::OnInitDialog();

		m_proto->m_hwndPrivacyRule = m_hwnd;

		SendDlgItemMessage(m_hwnd, IDC_ICO_MESSAGE,     STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_msg_allow"), 0);
		SendDlgItemMessage(m_hwnd, IDC_ICO_QUERY,       STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_iq_allow"), 0);
		SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEIN,  STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_prin_allow"), 0);
		SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEOUT, STM_SETICON, (WPARAM)m_proto->LoadIconEx("pl_prout_allow"), 0);

		TCHAR* szTypes[] = { _T("JID"), _T("Group"), _T("Subscription"), _T("Any") };
		int i, nTypes[] = { Jid, Group, Subscription, Else };
		for ( i = 0; i < SIZEOF(szTypes); i++ )
		{
			LRESULT nItem = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_ADDSTRING, 0, (LPARAM)TranslateTS( szTypes[i] ));
			SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_SETITEMDATA, nItem, nTypes[i] );
			if ( m_pRule->GetType() == nTypes[i] )
				SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_SETCURSEL, nItem, 0 );
		}

		SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_RESETCONTENT, 0, 0 );
		TCHAR* szSubscriptions[] = { _T("none"), _T("from"), _T("to"), _T("both") };
		for ( i = 0; i < SIZEOF(szSubscriptions); i++ )
		{
			LRESULT nItem = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_ADDSTRING, 0, (LPARAM)TranslateTS( szSubscriptions[i] ));
			SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SETITEMDATA, nItem, (LPARAM)szSubscriptions[i] );
		}

		PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_COMBO_TYPE, CBN_SELCHANGE ), 0 );

		SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateTS( _T("Deny" )));
		SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateTS( _T("Allow" )));

		SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_SETCURSEL, m_pRule->GetAction() ? 1 : 0, 0 );

		DWORD dwPackets = m_pRule->GetPackets();
		if ( !dwPackets )
			dwPackets = JABBER_PL_RULE_TYPE_ALL;
		if ( dwPackets & JABBER_PL_RULE_TYPE_IQ )
			SendDlgItemMessage( m_hwnd, IDC_CHECK_QUERIES, BM_SETCHECK, BST_CHECKED, 0 );
		if ( dwPackets & JABBER_PL_RULE_TYPE_MESSAGE )
			SendDlgItemMessage( m_hwnd, IDC_CHECK_MESSAGES, BM_SETCHECK, BST_CHECKED, 0 );
		if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN )
			SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_IN, BM_SETCHECK, BST_CHECKED, 0 );
		if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT )
			SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_OUT, BM_SETCHECK, BST_CHECKED, 0 );

		if ( m_pRule->GetValue() && ( m_pRule->GetType() == Jid || m_pRule->GetType() == Group ))
			SetDlgItemText( m_hwnd, IDC_EDIT_VALUE, m_pRule->GetValue());
	}

	void cbType_OnChange(CCtrlData*)
	{
		if ( !m_pRule ) return;

		LRESULT nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0 );
		if ( nCurSel == CB_ERR )
			return;

		LRESULT nItemData = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETITEMDATA, nCurSel, 0 );
		switch (nItemData)
		{
			case Jid:
			{
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_SHOW );
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_HIDE );

				SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_RESETCONTENT, 0, 0 );

				HANDLE hContact = ( HANDLE ) db_find_first();
				while ( hContact != NULL )
				{
					char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 );
					if ( szProto != NULL && !strcmp( szProto, m_proto->m_szModuleName ))
					{
						DBVARIANT dbv;
						if ( !m_proto->JGetStringT( hContact, "jid", &dbv ))
						{
							SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_ADDSTRING, 0, (LPARAM)dbv.ptszVal );
							JFreeVariant( &dbv );
						}
					}
					hContact = db_find_next(hContact);
				}

				// append known chatroom jids from bookmarks
				LISTFOREACH(i, m_proto, LIST_BOOKMARK)
				{
					JABBER_LIST_ITEM *item = 0;
					if ( item = m_proto->ListGetItemPtrFromIndex( i ))
						SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_ADDSTRING, 0, (LPARAM)item->jid );
				}

				// FIXME: ugly code :)
				if ( m_pRule->GetValue())
				{
					SetDlgItemText( m_hwnd, IDC_COMBO_VALUES, m_pRule->GetValue());
					LRESULT nSelPos = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_FINDSTRINGEXACT , -1, (LPARAM)m_pRule->GetValue());
					if ( nSelPos != CB_ERR )
						SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_SETCURSEL, nSelPos, 0 );
				}
				break;
			}

			case Group:
			{
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_SHOW );
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_HIDE );

				SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_RESETCONTENT, 0, 0 );

				char buf[ 20 ];
				DBVARIANT dbv;
				for ( int i = 0; ; i++ )
				{
					mir_snprintf(buf, 20, "%d", i);
					if ( DBGetContactSettingTString(NULL, "CListGroups", buf, &dbv))
						break;

					SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_ADDSTRING, 0, (LPARAM)&dbv.ptszVal[1] );
					DBFreeVariant(&dbv);
				}

				// FIXME: ugly code :)
				if ( m_pRule->GetValue())
				{
					SetDlgItemText( m_hwnd, IDC_COMBO_VALUES, m_pRule->GetValue());
					LRESULT nSelPos = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_FINDSTRINGEXACT , -1, (LPARAM)m_pRule->GetValue());
					if ( nSelPos != CB_ERR )
						SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUES, CB_SETCURSEL, nSelPos, 0 );
				}
				break;
			}

			case Subscription:
			{
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_HIDE );
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_SHOW );

				if ( m_pRule->GetValue())
				{
					LRESULT nSelected = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SELECTSTRING, -1, (LPARAM)TranslateTS(m_pRule->GetValue()));
					if ( nSelected == CB_ERR )
						SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SETCURSEL, 0, 0 );
				}
				else SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_SETCURSEL, 0, 0 );
				break;
			}

			case Else:
			{
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUES ), SW_HIDE );
				ShowWindow( GetDlgItem( m_hwnd, IDC_COMBO_VALUE ), SW_HIDE );
				break;
			}
		}

		return;
	}

	void btnOk_OnClick(CCtrlButton *)
	{
		LRESULT nItemData = -1;
		LRESULT nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0 );
		if ( nCurSel != CB_ERR )
			nItemData = SendDlgItemMessage( m_hwnd, IDC_COMBO_TYPE, CB_GETITEMDATA, nCurSel, 0 );

		switch ( nItemData )
		{
			case Jid:
			case Group:
			{
				TCHAR szText[ 512 ];
				GetDlgItemText( m_hwnd, IDC_COMBO_VALUES, szText, SIZEOF(szText));
				m_pRule->SetValue( szText );
				break;
			}
			case Subscription:
			{
				nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_GETCURSEL, 0, 0 );
				if ( nCurSel != CB_ERR )
					m_pRule->SetValue(( TCHAR* )SendDlgItemMessage( m_hwnd, IDC_COMBO_VALUE, CB_GETITEMDATA, nCurSel, 0 ));
				else
					m_pRule->SetValue( _T( "none" ));
				break;
			}

			default:
				m_pRule->SetValue( NULL );
				break;
		}

		m_pRule->SetType( ( PrivacyListRuleType )nItemData );
		nCurSel = SendDlgItemMessage( m_hwnd, IDC_COMBO_ACTION, CB_GETCURSEL, 0, 0 );
		if ( nCurSel == CB_ERR )
			nCurSel = 1;
		m_pRule->SetAction( nCurSel ? TRUE : FALSE );

		DWORD dwPackets = 0;
		if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_MESSAGES, BM_GETCHECK, 0, 0 ))
			dwPackets |= JABBER_PL_RULE_TYPE_MESSAGE;
		if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_IN, BM_GETCHECK, 0, 0 ))
			dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_IN;
		if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_PRESENCE_OUT, BM_GETCHECK, 0, 0 ))
			dwPackets |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;
		if ( BST_CHECKED == SendDlgItemMessage( m_hwnd, IDC_CHECK_QUERIES, BM_GETCHECK, 0, 0 ))
			dwPackets |= JABBER_PL_RULE_TYPE_IQ;
		if ( !dwPackets )
			dwPackets = JABBER_PL_RULE_TYPE_ALL;

		m_pRule->SetPackets( dwPackets );

		EndDialog( m_hwnd, 1 );
	}

	void btnCancel_OnClick(CCtrlButton *)
	{
		EndDialog(m_hwnd, 0);
	}

	void OnDestroy()
	{
		g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_MESSAGE,     STM_SETICON, 0, 0));
		g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_QUERY,       STM_SETICON, 0, 0));
		g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEIN,  STM_SETICON, 0, 0));
		g_ReleaseIcon(( HICON )SendDlgItemMessage(m_hwnd, IDC_ICO_PRESENCEOUT, STM_SETICON, 0, 0));
		m_proto->m_hwndPrivacyRule = NULL;
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// Main privacy list dialog
class CJabberDlgPrivacyLists: public CJabberDlgBase
{
	typedef CJabberDlgBase CSuper;

public:
	CJabberDlgPrivacyLists(CJabberProto *proto);

protected:
	static int idSimpleControls[];
	static int idAdvancedControls[];

	void OnInitDialog();
	void OnClose();
	void OnDestroy();
	void OnProtoRefresh(WPARAM, LPARAM);
	int Resizer(UTILRESIZECONTROL *urc);

	UI_MESSAGE_MAP(CJabberDlgPrivacyLists, CSuper);
		UI_MESSAGE(WM_MEASUREITEM, OnWmMeasureItem);
		UI_MESSAGE(WM_DRAWITEM, OnWmDrawItem);
		UI_MESSAGE(WM_GETMINMAXINFO, OnWmGetMinMaxInfo);
	UI_MESSAGE_MAP_END();

	BOOL OnWmMeasureItem(UINT msg, WPARAM wParam, LPARAM lParam);
	BOOL OnWmDrawItem(UINT msg, WPARAM wParam, LPARAM lParam);
	BOOL OnWmGetMinMaxInfo(UINT msg, WPARAM wParam, LPARAM lParam);

	void btnSimple_OnClick(CCtrlButton *);
	void btnAdvanced_OnClick(CCtrlButton *);
	void btnAddJid_OnClick(CCtrlButton *);
	void btnActivate_OnClick(CCtrlButton *);
	void btnSetDefault_OnClick(CCtrlButton *);
	void lbLists_OnSelChange(CCtrlListBox *);
	void lbLists_OnDblClick(CCtrlListBox *);
	void lbRules_OnSelChange(CCtrlListBox *);
	void lbRules_OnDblClick(CCtrlListBox *);
	void btnEditRule_OnClick(CCtrlButton *);
	void btnAddRule_OnClick(CCtrlButton *);
	void btnRemoveRule_OnClick(CCtrlButton *);
	void btnUpRule_OnClick(CCtrlButton *);
	void btnDownRule_OnClick(CCtrlButton *);
	void btnAddList_OnClick(CCtrlButton *);
	void btnRemoveList_OnClick(CCtrlButton *);
	void btnApply_OnClick(CCtrlButton *);
	void clcClist_OnUpdate(CCtrlClc::TEventInfo *evt);
	void clcClist_OnOptionsChanged(CCtrlClc::TEventInfo *evt);
	void clcClist_OnClick(CCtrlClc::TEventInfo *evt);

	void OnCommand_Close(HWND hwndCtrl, WORD idCtrl, WORD idCode);

	void ShowAdvancedList(CPrivacyList *pList);
	void DrawNextRulePart(HDC hdc, COLORREF color, const TCHAR *text, RECT *rc);
	void DrawRuleAction(HDC hdc, COLORREF clLine1, COLORREF clLine2, CPrivacyListRule *pRule, RECT *rc);
	void DrawRulesList(LPDRAWITEMSTRUCT lpdis);
	void DrawLists(LPDRAWITEMSTRUCT lpdis);

	void CListResetOptions(HWND hwndList);
	void CListFilter(HWND hwndList);
	bool CListIsGroup(HANDLE hGroup);
	HANDLE CListFindGroupByName(TCHAR *name);
	void CListResetIcons(HWND hwndList, HANDLE hItem, bool hide=false);
	void CListSetupIcons(HWND hwndList, HANDLE hItem, int iSlot, DWORD dwProcess, BOOL bAction);
	HANDLE CListAddContact(HWND hwndList, TCHAR *jid);
	void CListApplyList(HWND hwndList, CPrivacyList *pList = NULL);
	DWORD CListGetPackets(HWND hwndList, HANDLE hItem, bool bAction);
	void CListBuildList(HWND hwndList, CPrivacyList *pList);

	void EnableEditorControls();
	BOOL CanExit();
	
	static LRESULT CALLBACK LstListsSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
	static LRESULT CALLBACK LstRulesSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

	struct TCLCInfo
	{
		struct TJidData
		{
			HANDLE hItem;
			TCHAR *jid;

			static int cmp(const TJidData *p1, const TJidData *p2) { return lstrcmp(p1->jid, p2->jid); }
		};

		HANDLE hItemDefault;
		HANDLE hItemSubNone;
		HANDLE hItemSubTo;
		HANDLE hItemSubFrom;
		HANDLE hItemSubBoth;

		LIST<TJidData> newJids;

		bool bChanged;

		CPrivacyList *pList;

		TCLCInfo(): newJids(1, TJidData::cmp), bChanged(false), pList(0) {}
		~TCLCInfo()
		{
			for (int i = 0; i < newJids.getCount(); ++i)
			{
				mir_free(newJids[i]->jid);
				mir_free(newJids[i]);
			}
			newJids.destroy();
		}

		void addJid(HANDLE hItem, TCHAR *jid)
		{
			TJidData *data = (TJidData *)mir_alloc(sizeof(TJidData));
			data->hItem = hItem;
			data->jid = mir_tstrdup(jid);
			newJids.insert(data);
		}

		HANDLE findJid(TCHAR *jid)
		{
			TJidData data = {0};
			data.jid = jid;
			TJidData *found = newJids.find(&data);
			return found ? found->hItem : 0;
		}
	};

	TCLCInfo clc_info;

private:
	CCtrlMButton	m_btnSimple;
	CCtrlMButton	m_btnAdvanced;
	CCtrlMButton	m_btnAddJid;
	CCtrlMButton	m_btnActivate;
	CCtrlMButton	m_btnSetDefault;
	CCtrlMButton	m_btnEditRule;
	CCtrlMButton	m_btnAddRule;
	CCtrlMButton	m_btnRemoveRule;
	CCtrlMButton	m_btnUpRule;
	CCtrlMButton	m_btnDownRule;
	CCtrlMButton	m_btnAddList;
	CCtrlMButton	m_btnRemoveList;
	CCtrlButton		m_btnApply;
	CCtrlListBox	m_lbLists;
	CCtrlListBox	m_lbRules;
	CCtrlClc		m_clcClist;
};

int CJabberDlgPrivacyLists::idSimpleControls[] =
{
	IDC_CLIST, IDC_CANVAS,
	IDC_TXT_OTHERJID, IDC_NEWJID, IDC_ADDJID,
	IDC_ICO_MESSAGE, IDC_ICO_QUERY, IDC_ICO_INPRESENCE, IDC_ICO_OUTPRESENCE,
	IDC_TXT_MESSAGE, IDC_TXT_QUERY, IDC_TXT_INPRESENCE, IDC_TXT_OUTPRESENCE,
	0
};

int CJabberDlgPrivacyLists::idAdvancedControls[] =
{
	IDC_PL_RULES_LIST,
	IDC_ADD_RULE, IDC_EDIT_RULE, IDC_REMOVE_RULE,
	IDC_UP_RULE, IDC_DOWN_RULE,
	0
};

CJabberDlgPrivacyLists::CJabberDlgPrivacyLists(CJabberProto *proto):
	CSuper(proto, IDD_PRIVACY_LISTS, NULL),
	m_btnSimple(this,     IDC_BTN_SIMPLE,   proto->LoadIconEx("group"),           LPGEN("Simple mode")),
	m_btnAdvanced(this,   IDC_BTN_ADVANCED, proto->LoadIconEx("sd_view_list"),    LPGEN("Advanced mode")),
	m_btnAddJid(this,     IDC_ADDJID,       proto->LoadIconEx("addroster"),       LPGEN("Add JID")),
	m_btnActivate(this,   IDC_ACTIVATE,     proto->LoadIconEx("pl_list_active"),  LPGEN("Activate")),
	m_btnSetDefault(this, IDC_SET_DEFAULT,  proto->LoadIconEx("pl_list_default"), LPGEN("Set default")),
	m_btnEditRule(this,   IDC_EDIT_RULE,    SKINICON_OTHER_RENAME,                LPGEN("Edit rule")),
	m_btnAddRule(this,    IDC_ADD_RULE,     SKINICON_OTHER_ADDCONTACT,            LPGEN("Add rule")),
	m_btnRemoveRule(this, IDC_REMOVE_RULE,  SKINICON_OTHER_DELETE,                LPGEN("Delete rule")),
	m_btnUpRule(this,     IDC_UP_RULE,      proto->LoadIconEx("arrow_up"),        LPGEN("Move rule up")),
	m_btnDownRule(this,   IDC_DOWN_RULE,    proto->LoadIconEx("arrow_down"),      LPGEN("Move rule down")),
	m_btnAddList(this,    IDC_ADD_LIST,     SKINICON_OTHER_ADDCONTACT,            LPGEN("Add list...")),
	m_btnRemoveList(this, IDC_REMOVE_LIST,  SKINICON_OTHER_DELETE,                LPGEN("Remove list")),
	m_btnApply(this,      IDC_APPLY),
	m_lbLists(this,       IDC_LB_LISTS),
	m_lbRules(this,       IDC_PL_RULES_LIST),
	m_clcClist(this,      IDC_CLIST)
{
	m_btnSimple.OnClick = Callback( this,     &CJabberDlgPrivacyLists::btnSimple_OnClick);
	m_btnAdvanced.OnClick = Callback( this,	&CJabberDlgPrivacyLists::btnAdvanced_OnClick);
	m_btnAddJid.OnClick = Callback( this,		&CJabberDlgPrivacyLists::btnAddJid_OnClick);
	m_btnActivate.OnClick = Callback( this,	&CJabberDlgPrivacyLists::btnActivate_OnClick);
	m_btnSetDefault.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnSetDefault_OnClick);
	m_btnEditRule.OnClick = Callback( this,	&CJabberDlgPrivacyLists::btnEditRule_OnClick);
	m_btnAddRule.OnClick = Callback( this,		&CJabberDlgPrivacyLists::btnAddRule_OnClick);
	m_btnRemoveRule.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnRemoveRule_OnClick);
	m_btnUpRule.OnClick = Callback( this,		&CJabberDlgPrivacyLists::btnUpRule_OnClick);
	m_btnDownRule.OnClick = Callback( this,	&CJabberDlgPrivacyLists::btnDownRule_OnClick);
	m_btnAddList.OnClick = Callback( this,		&CJabberDlgPrivacyLists::btnAddList_OnClick);
	m_btnRemoveList.OnClick = Callback( this, &CJabberDlgPrivacyLists::btnRemoveList_OnClick);
	m_btnApply.OnClick = Callback( this,		&CJabberDlgPrivacyLists::btnApply_OnClick);

	m_lbLists.OnSelChange = Callback(this, &CJabberDlgPrivacyLists::lbLists_OnSelChange);
	m_lbLists.OnDblClick = Callback(this, &CJabberDlgPrivacyLists::lbLists_OnDblClick);
	m_lbRules.OnSelChange = Callback(this, &CJabberDlgPrivacyLists::lbRules_OnSelChange);
	m_lbRules.OnDblClick = Callback(this, &CJabberDlgPrivacyLists::lbRules_OnDblClick);

	m_clcClist.OnNewContact =
	m_clcClist.OnListRebuilt = Callback(this, &CJabberDlgPrivacyLists::clcClist_OnUpdate);
	m_clcClist.OnOptionsChanged = Callback(this, &CJabberDlgPrivacyLists::clcClist_OnOptionsChanged);
	m_clcClist.OnClick = Callback(this, &CJabberDlgPrivacyLists::clcClist_OnClick);
}

void CJabberDlgPrivacyLists::OnInitDialog()
{
	CSuper::OnInitDialog();

	WindowSetIcon( m_hwnd, m_proto, "privacylists" );

	EnableWindow( GetDlgItem( m_hwnd, IDC_ADD_RULE ), FALSE );
	EnableWindow( GetDlgItem( m_hwnd, IDC_EDIT_RULE ), FALSE );
	EnableWindow( GetDlgItem( m_hwnd, IDC_REMOVE_RULE ), FALSE );
	EnableWindow( GetDlgItem( m_hwnd, IDC_UP_RULE ), FALSE );
	EnableWindow( GetDlgItem( m_hwnd, IDC_DOWN_RULE ), FALSE );

	m_proto->QueryPrivacyLists();

	LOGFONT lf;
	GetObject((HFONT)SendDlgItemMessage(m_hwnd, IDC_TXT_LISTS, WM_GETFONT, 0, 0), sizeof(lf), &lf);
	lf.lfWeight = FW_BOLD;
	HFONT hfnt = CreateFontIndirect(&lf);
	SendDlgItemMessage(m_hwnd, IDC_TXT_LISTS, WM_SETFONT, (WPARAM)hfnt, TRUE);
	SendDlgItemMessage(m_hwnd, IDC_TXT_RULES, WM_SETFONT, (WPARAM)hfnt, TRUE);

	SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_CLIST), GWL_STYLE,
		GetWindowLongPtr(GetDlgItem(m_hwnd, IDC_CLIST), GWL_STYLE)|CLS_HIDEEMPTYGROUPS|CLS_USEGROUPS|CLS_GREYALTERNATE);
	m_clcClist.SetExStyle(CLS_EX_DISABLEDRAGDROP|CLS_EX_TRACKSELECT);

	HIMAGELIST hIml = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),(IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,9,9);
	ImageList_AddIcon_Icolib(hIml, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_msg_allow"));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_msg_deny"));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prin_allow"));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prin_deny"));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prout_allow"));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_prout_deny"));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_iq_allow"));
	ImageList_AddIcon_Icolib(hIml, m_proto->LoadIconEx("pl_iq_deny"));
	m_clcClist.SetExtraImageList(hIml);
	m_clcClist.SetExtraColumns(4);

	m_btnSimple.MakePush();
	m_btnAdvanced.MakePush();

	CLCINFOITEM cii = {0};
	cii.cbSize = sizeof(cii);

	cii.flags = CLCIIF_GROUPFONT;
	cii.pszText = TranslateT("** Default **");
	clc_info.hItemDefault = m_clcClist.AddInfoItem(&cii);
	cii.pszText = TranslateT("** Subsription: both **");
	clc_info.hItemSubBoth = m_clcClist.AddInfoItem(&cii);
	cii.pszText = TranslateT("** Subsription: to **");
	clc_info.hItemSubTo = m_clcClist.AddInfoItem(&cii);
	cii.pszText = TranslateT("** Subsription: from **");
	clc_info.hItemSubFrom = m_clcClist.AddInfoItem(&cii);
	cii.pszText = TranslateT("** Subsription: none **");
	clc_info.hItemSubNone = m_clcClist.AddInfoItem(&cii);

	CListResetOptions(GetDlgItem(m_hwnd, IDC_CLIST));
	CListFilter(GetDlgItem(m_hwnd, IDC_CLIST));
	CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST));

	if ( DBGetContactSettingByte(NULL, m_proto->m_szModuleName, "plistsWnd_simpleMode", 1))
	{
		UIShowControls(m_hwnd, idSimpleControls, SW_SHOW);
		UIShowControls(m_hwnd, idAdvancedControls, SW_HIDE);
		CheckDlgButton(m_hwnd, IDC_BTN_SIMPLE, TRUE);
	} else
	{
		UIShowControls(m_hwnd, idSimpleControls, SW_HIDE);
		UIShowControls(m_hwnd, idAdvancedControls, SW_SHOW);
		CheckDlgButton(m_hwnd, IDC_BTN_ADVANCED, TRUE);
	}

	SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_LB_LISTS), GWLP_USERDATA,
		SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_LB_LISTS), GWLP_WNDPROC, (LONG_PTR)LstListsSubclassProc));
	SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_PL_RULES_LIST), GWLP_USERDATA,
		SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_PL_RULES_LIST), GWLP_WNDPROC, (LONG_PTR)LstRulesSubclassProc));

	SetStatusText(TranslateT("Loading..."));

	Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "plistsWnd_sz");
}

void CJabberDlgPrivacyLists::OnClose()
{
	if (CanExit()) {
		DestroyWindow(m_hwnd);
		CSuper::OnClose();
	}
	else
		m_lresult = TRUE;
}

void CJabberDlgPrivacyLists::OnDestroy()
{
	m_proto->m_pDlgPrivacyLists = NULL;

	// Wipe all data and query lists without contents
	m_proto->m_privacyListManager.RemoveAllLists();
	m_proto->QueryPrivacyLists();
	m_proto->m_privacyListManager.SetModified( FALSE );

	// Delete custom bold font
	DeleteObject((HFONT)SendDlgItemMessage(m_hwnd, IDC_TXT_LISTS, WM_GETFONT, 0, 0));

	DBWriteContactSettingByte(NULL, m_proto->m_szModuleName, "plistsWnd_simpleMode", IsDlgButtonChecked(m_hwnd, IDC_BTN_SIMPLE));

	Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "plistsWnd_sz");

	CSuper::OnDestroy();
}

void CJabberDlgPrivacyLists::OnProtoRefresh(WPARAM, LPARAM)
{
	LRESULT sel = SendDlgItemMessage(m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0);
	TCHAR *szCurrentSelectedList = NULL;
	if ( sel != LB_ERR ) {
		LRESULT len = SendDlgItemMessage(m_hwnd, IDC_LB_LISTS, LB_GETTEXTLEN, sel, 0) + 1;
		szCurrentSelectedList = (TCHAR *)mir_alloc(len * sizeof(TCHAR));
		SendDlgItemMessage(m_hwnd, IDC_LB_LISTS, LB_GETTEXT, sel, (LPARAM)szCurrentSelectedList);
	}

	SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_RESETCONTENT, 0, 0 );

	LRESULT nItemId = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_ADDSTRING, 0, (LPARAM)TranslateT( "<none>" ));
	SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETITEMDATA, nItemId, (LPARAM)NULL );

	m_proto->m_privacyListManager.Lock();
	CPrivacyList* pList = m_proto->m_privacyListManager.GetFirstList();
	while ( pList ) {
		if ( !pList->IsDeleted()) {
			nItemId = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_ADDSTRING, 0, (LPARAM)pList->GetListName());
			SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETITEMDATA, nItemId, (LPARAM)pList );
		}
		pList = pList->GetNext();
	}

	if ( !szCurrentSelectedList || ( SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SELECTSTRING, -1, (LPARAM)szCurrentSelectedList ) == LB_ERR ))
		SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETCURSEL, 0, 0 );
	if ( szCurrentSelectedList )
		mir_free( szCurrentSelectedList );

	m_proto->m_privacyListManager.Unlock();

	PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_LB_LISTS, LBN_SELCHANGE ), 0 );
	EnableEditorControls();
}

BOOL CJabberDlgPrivacyLists::OnWmMeasureItem(UINT, WPARAM, LPARAM lParam)
{
	LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
	if ((lpmis->CtlID != IDC_PL_RULES_LIST) && (lpmis->CtlID != IDC_LB_LISTS))
		return FALSE;

	TEXTMETRIC tm = {0};
	HDC hdc = GetDC(GetDlgItem(m_hwnd, lpmis->CtlID));
	GetTextMetrics(hdc, &tm);
	ReleaseDC(GetDlgItem(m_hwnd, lpmis->CtlID), hdc);

	if (lpmis->CtlID == IDC_PL_RULES_LIST)
		lpmis->itemHeight = tm.tmHeight * 2;
	else if (lpmis->CtlID == IDC_LB_LISTS)
		lpmis->itemHeight = tm.tmHeight;

	if (lpmis->itemHeight < 18) lpmis->itemHeight = 18;

	return TRUE;
}

BOOL CJabberDlgPrivacyLists::OnWmDrawItem(UINT, WPARAM, LPARAM lParam)
{
	LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;

	if (lpdis->CtlID == IDC_PL_RULES_LIST)
		DrawRulesList(lpdis);
	else if (lpdis->CtlID == IDC_LB_LISTS)
		DrawLists(lpdis);
	else if (lpdis->CtlID == IDC_CANVAS)
	{
		static struct
		{
			TCHAR *textEng;
			char *icon;
			TCHAR *text;
		} items[] =
		{
			{ _T("Message"),		"pl_msg_allow" },
			{ _T("Presence (in)"),	"pl_prin_allow" },
			{ _T("Presence (out)"),	"pl_prout_allow" },
			{ _T("Query"),			"pl_iq_allow" },
		};

		int i, totalWidth = -5; // spacing for last item
		for (i = 0; i < SIZEOF(items); ++i)
		{
			SIZE sz = {0};
			if (!items[i].text) items[i].text = TranslateTS(items[i].textEng);
			GetTextExtentPoint32(lpdis->hDC, items[i].text, lstrlen(items[i].text), &sz);
			totalWidth += sz.cx + 18 + 5; // 18 pixels for icon, 5 pixel spacing
		}

		COLORREF clText = GetSysColor(COLOR_BTNTEXT);
		RECT rc = lpdis->rcItem;
		rc.left = (rc.left + rc.right - totalWidth)/2;

		for (i = 0; i < SIZEOF(items); ++i)
		{
			DrawIconEx(lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, m_proto->LoadIconEx(items[i].icon),
				16, 16, 0, NULL, DI_NORMAL);
			rc.left += 18;
			DrawNextRulePart(lpdis->hDC, clText, items[i].text, &rc);
			rc.left += 5;
		}
	}
	else return FALSE;

	return TRUE;
}

BOOL CJabberDlgPrivacyLists::OnWmGetMinMaxInfo(UINT, WPARAM, LPARAM lParam)
{
	LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
	lpmmi->ptMinTrackSize.x = 550;
	lpmmi->ptMinTrackSize.y = 390;
	return 0;
}

void CJabberDlgPrivacyLists::ShowAdvancedList(CPrivacyList *pList)
{
	int nLbSel = SendDlgItemMessage(m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0);
	SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_RESETCONTENT, 0, 0 );

	BOOL bListEmpty = TRUE;

	CPrivacyListRule* pRule = pList->GetFirstRule();

	while ( pRule ) {
		bListEmpty = FALSE;
		TCHAR szTypeValue[ 512 ];
		switch ( pRule->GetType()) {
		case Jid:
			mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "If jabber id is '%s' then" ), pRule->GetValue());
			break;
		case Group:
			mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "If group is '%s' then" ), pRule->GetValue());
			break;
		case Subscription:
			mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "If subscription is '%s' then" ), pRule->GetValue());
			break;
		case Else:
			mir_sntprintf( szTypeValue, SIZEOF( szTypeValue ), _T( "Else"));
			break;
		}

		TCHAR szPackets[ 512 ];
		szPackets[ 0 ] = '\0';

		DWORD dwPackets = pRule->GetPackets();
		if ( !dwPackets )
			dwPackets = JABBER_PL_RULE_TYPE_ALL;
		if ( dwPackets == JABBER_PL_RULE_TYPE_ALL )
			_tcscpy( szPackets, _T("all"));
		else {
			if ( dwPackets & JABBER_PL_RULE_TYPE_MESSAGE )
				_tcscat( szPackets, _T("messages"));
			if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN ) {
				if ( _tcslen( szPackets ))
					_tcscat( szPackets, _T(", "));
				_tcscat( szPackets, _T("presence-in"));
			}
			if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT ) {
				if ( _tcslen( szPackets ))
					_tcscat( szPackets, _T(", "));
				_tcscat( szPackets, _T("presence-out"));
			}
			if ( dwPackets & JABBER_PL_RULE_TYPE_IQ ) {
				if ( _tcslen( szPackets ))
					_tcscat( szPackets, _T(", "));
				_tcscat( szPackets, _T("queries"));
		}	}

		TCHAR szListItem[ 512 ];
		mir_sntprintf( szListItem, SIZEOF( szListItem ), _T("%s %s %s"), szTypeValue, pRule->GetAction() ? _T("allow") : _T("deny"), szPackets );

		LRESULT nItemId = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_ADDSTRING, 0, (LPARAM)szListItem );
		SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETITEMDATA, nItemId, (LPARAM)pRule );

		pRule = pRule->GetNext();
	}

	EnableWindow( GetDlgItem( m_hwnd, IDC_PL_RULES_LIST ), !bListEmpty );
	if ( bListEmpty )
		SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_ADDSTRING, 0, (LPARAM)TranslateTS(_T("List has no rules, empty lists will be deleted then changes applied")));
	else
		SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nLbSel, 0 );

	PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_PL_RULES_LIST, LBN_SELCHANGE ), 0 );
}

void CJabberDlgPrivacyLists::DrawNextRulePart(HDC hdc, COLORREF color, const TCHAR *text, RECT *rc)
{
	SetTextColor(hdc, color);
	DrawText(hdc, text, lstrlen(text), rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS);

	SIZE sz;
	GetTextExtentPoint32(hdc, text, lstrlen(text), &sz);
	rc->left += sz.cx;
}

void CJabberDlgPrivacyLists::DrawRuleAction(HDC hdc, COLORREF clLine1, COLORREF, CPrivacyListRule *pRule, RECT *rc)
{
	DrawNextRulePart(hdc, clLine1, pRule->GetAction() ? TranslateT("allow ") : TranslateT("deny "), rc);
	if (!pRule->GetPackets() || (pRule->GetPackets() == JABBER_PL_RULE_TYPE_ALL))
	{
		DrawNextRulePart(hdc, clLine1, TranslateT("all."), rc);
	} else
	{
		bool needComma = false;
		int itemCount =
			((pRule->GetPackets() & JABBER_PL_RULE_TYPE_MESSAGE) ? 1 : 0) +
			((pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_IN) ? 1 : 0) +
			((pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_OUT) ? 1 : 0) +
			((pRule->GetPackets() & JABBER_PL_RULE_TYPE_IQ) ? 1 : 0);

		if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_MESSAGE)
		{
			--itemCount;
			needComma = true;
			DrawNextRulePart(hdc, clLine1, TranslateT("messages"), rc);
		}
		if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_IN)
		{
			--itemCount;
			if (needComma)
				DrawNextRulePart(hdc, clLine1, itemCount ? _T(", ") : TranslateT(" and "), rc);
			needComma = true;
			DrawNextRulePart(hdc, clLine1, TranslateT("incoming presences"), rc);
		}
		if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_PRESENCE_OUT)
		{
			--itemCount;
			if (needComma)
				DrawNextRulePart(hdc, clLine1, itemCount ? _T(", ") : TranslateT(" and "), rc);
			needComma = true;
			DrawNextRulePart(hdc, clLine1, TranslateT("outgoing presences"), rc);
		}
		if (pRule->GetPackets() & JABBER_PL_RULE_TYPE_IQ)
		{
			--itemCount;
			if (needComma)
				DrawNextRulePart(hdc, clLine1, itemCount ? _T(", ") : TranslateT(" and "), rc);
			needComma = true;
			DrawNextRulePart(hdc, clLine1, TranslateT("queries"), rc);
		}
		DrawNextRulePart(hdc, clLine1, _T("."), rc);
	}
}

void CJabberDlgPrivacyLists::DrawRulesList(LPDRAWITEMSTRUCT lpdis)
{
	if (lpdis->itemID == -1)
		return;

	CPrivacyListRule *pRule = (CPrivacyListRule *)lpdis->itemData;

	COLORREF clLine1, clLine2, clBack;
	if (lpdis->itemState & ODS_SELECTED)
	{
		FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
		clBack = GetSysColor(COLOR_HIGHLIGHT);
		clLine1 = GetSysColor(COLOR_HIGHLIGHTTEXT);
	} else
	{
		FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW));
		clBack = GetSysColor(COLOR_WINDOW);
		clLine1 = GetSysColor(COLOR_WINDOWTEXT);
	}
	clLine2 = RGB(
			GetRValue(clLine1) * 0.66 + GetRValue(clBack) * 0.34,
			GetGValue(clLine1) * 0.66 + GetGValue(clBack) * 0.34,
			GetBValue(clLine1) * 0.66 + GetBValue(clBack) * 0.34
		);

	SetBkMode(lpdis->hDC, TRANSPARENT);

	RECT rc;

	if ( !pRule ) {
		rc = lpdis->rcItem;
		rc.left += 25;

		int len = SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETTEXTLEN, lpdis->itemID, 0) + 1;
		TCHAR *str = (TCHAR *)_alloca(len * sizeof(TCHAR));
		SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETTEXT, lpdis->itemID, (LPARAM)str);
		DrawNextRulePart(lpdis->hDC, clLine1, str, &rc);
	}
	else if (pRule->GetType() == Else) {
		rc = lpdis->rcItem;
		rc.left += 25;

		DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("Else "), &rc);
		DrawRuleAction(lpdis->hDC, clLine1, clLine2, pRule, &rc);
	}
	else {
		rc = lpdis->rcItem;
		rc.bottom -= (rc.bottom - rc.top) / 2;
		rc.left += 25;

		switch ( pRule->GetType())
		{
			case Jid:
			{
				DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("If jabber id is '"), &rc);
				DrawNextRulePart(lpdis->hDC, clLine1, pRule->GetValue(), &rc);
				DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("'"), &rc);

				if (HANDLE hContact = m_proto->HContactFromJID(pRule->GetValue())) {
					TCHAR *szName = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
					if ( szName ) {
						DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (nickname: "), &rc);
						DrawNextRulePart(lpdis->hDC, clLine1, szName, &rc);
						DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(")"), &rc);
				}	}
				break;
			}

			case Group:
				DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("If group is '"), &rc);
				DrawNextRulePart(lpdis->hDC, clLine1, pRule->GetValue(), &rc);
				DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("'"), &rc);
				break;
			case Subscription:
				DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("If subscription is '"), &rc);
				DrawNextRulePart(lpdis->hDC, clLine1, pRule->GetValue(), &rc);
				DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("'"), &rc);
				break;
		}

		rc = lpdis->rcItem;
		rc.top += (rc.bottom - rc.top) / 2;
		rc.left += 25;

		DrawNextRulePart(lpdis->hDC, clLine2, TranslateT("then "), &rc);
		DrawRuleAction(lpdis->hDC, clLine1, clLine2, pRule, &rc);
	}

	DrawIconEx(lpdis->hDC, lpdis->rcItem.left+4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
		m_proto->LoadIconEx("main"), 16, 16, 0, NULL, DI_NORMAL);

	if (pRule)
	{
		DrawIconEx(lpdis->hDC, lpdis->rcItem.left+4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
			m_proto->LoadIconEx(pRule->GetAction() ? "disco_ok" : "disco_fail"),
			16, 16, 0, NULL, DI_NORMAL);
	}

	if (lpdis->itemState & ODS_FOCUS)
	{
		LRESULT sel = SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETCURSEL, 0, 0);
		if ((sel == LB_ERR) || (sel == (LRESULT)lpdis->itemID))
			DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
	}
}

void CJabberDlgPrivacyLists::DrawLists(LPDRAWITEMSTRUCT lpdis)
{
	if (lpdis->itemID == -1)
		return;

	CPrivacyList *pList = (CPrivacyList *)lpdis->itemData;

	COLORREF clLine1, clLine2, clBack;
	if (lpdis->itemState & ODS_SELECTED)
	{
		FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
		clBack = GetSysColor(COLOR_HIGHLIGHT);
		clLine1 = GetSysColor(COLOR_HIGHLIGHTTEXT);
	} else
	{
		FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW));
		clBack = GetSysColor(COLOR_WINDOW);
		clLine1 = GetSysColor(COLOR_WINDOWTEXT);
	}
	clLine2 = RGB(
			GetRValue(clLine1) * 0.66 + GetRValue(clBack) * 0.34,
			GetGValue(clLine1) * 0.66 + GetGValue(clBack) * 0.34,
			GetBValue(clLine1) * 0.66 + GetBValue(clBack) * 0.34
		);

	SetBkMode(lpdis->hDC, TRANSPARENT);

	RECT rc;
	m_proto->m_privacyListManager.Lock();
	TCHAR *szDefault = NEWTSTR_ALLOCA(m_proto->m_privacyListManager.GetDefaultListName());
	TCHAR *szActive = NEWTSTR_ALLOCA(m_proto->m_privacyListManager.GetActiveListName());
	m_proto->m_privacyListManager.Unlock();

	rc = lpdis->rcItem;
	rc.left +=3;

	bool bActive = false;
	bool bDefault = false;
	TCHAR *szName;

	if (!pList)
	{
		if (!szActive) bActive = true;
		if (!szDefault) bDefault = true;
		szName = TranslateT("<none>");
	} else
	{
		if (!lstrcmp(pList->GetListName(), szActive)) bActive = true;
		if (!lstrcmp(pList->GetListName(), szDefault)) bDefault = true;
		szName = pList->GetListName();
	}

	HFONT hfnt = NULL;
	if (bActive)
	{
		LOGFONT lf;
		GetObject(GetCurrentObject(lpdis->hDC, OBJ_FONT), sizeof(lf), &lf);
		lf.lfWeight = FW_BOLD;
		hfnt = (HFONT)SelectObject(lpdis->hDC, CreateFontIndirect(&lf));
	}

	DrawNextRulePart(lpdis->hDC, clLine1, szName, &rc);

	if (bActive && bDefault)
		DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (act., def.)"), &rc);
	else if (bActive)
		DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (active)"), &rc);
	else if (bDefault)
		DrawNextRulePart(lpdis->hDC, clLine2, TranslateT(" (default)"), &rc);

	DrawIconEx(lpdis->hDC, lpdis->rcItem.right-16-4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
		m_proto->LoadIconEx(bActive ? "pl_list_active" : "pl_list_any"),
		16, 16, 0, NULL, DI_NORMAL);

	if (bDefault)
		DrawIconEx(lpdis->hDC, lpdis->rcItem.right-16-4, (lpdis->rcItem.top+lpdis->rcItem.bottom-16)/2,
			m_proto->LoadIconEx("disco_ok"),
			16, 16, 0, NULL, DI_NORMAL);

	if (hfnt)
		DeleteObject(SelectObject(lpdis->hDC, hfnt));

	if (lpdis->itemState & ODS_FOCUS)
	{
		int sel = SendDlgItemMessage(m_hwnd, lpdis->CtlID, LB_GETCURSEL, 0, 0);
		if ((sel == LB_ERR) || (sel == (int)lpdis->itemID))
			DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
	}
}

void CJabberDlgPrivacyLists::CListResetOptions(HWND)
{
	m_clcClist.SetBkBitmap(0, NULL);
	m_clcClist.SetBkColor(GetSysColor(COLOR_WINDOW));
	m_clcClist.SetGreyoutFlags(0);
	m_clcClist.SetLeftMargin(4);
	m_clcClist.SetIndent(10);
	m_clcClist.SetHideEmptyGroups(false);
	m_clcClist.SetHideOfflineRoot(false);
	for (int i = 0; i <= FONTID_MAX; i++)
		m_clcClist.SetTextColor(i, GetSysColor(COLOR_WINDOWTEXT));
}

void CJabberDlgPrivacyLists::CListFilter(HWND)
{
	for	(HANDLE hContact = db_find_first();
			hContact;
			hContact = db_find_next(hContact))
	{
		char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
		if (!proto || lstrcmpA(proto, m_proto->m_szModuleName))
			if (HANDLE hItem = m_clcClist.FindContact(hContact))
				m_clcClist.DeleteItem(hItem);
	}
}

bool CJabberDlgPrivacyLists::CListIsGroup(HANDLE hGroup)
{
	char idstr[33];
	_i64toa((INT_PTR)hGroup-1, idstr, 10);

	DBVARIANT dbv;
	bool result = DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv) == 0;
	if ( result )
		DBFreeVariant(&dbv);

	return result;
}

HANDLE CJabberDlgPrivacyLists::CListFindGroupByName(TCHAR *name)
{
	char idstr[33];
	DBVARIANT dbv;

	HANDLE hGroup = 0;

	for (int i= 0; !hGroup; ++i)
	{
		_itoa(i, idstr, 10);

		if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
			break;

		if (!_tcscmp(dbv.ptszVal + 1, name))
			hGroup = (HANDLE)(i+1);

		DBFreeVariant(&dbv);
	}

	return hGroup;
}

void CJabberDlgPrivacyLists::CListResetIcons(HWND, HANDLE hItem, bool hide)
{
	for (int i = 0; i < 4; ++i)
		m_clcClist.SetExtraImage(hItem, i, hide ? 0xFF : 0);
}

void CJabberDlgPrivacyLists::CListSetupIcons(HWND, HANDLE hItem, int iSlot, DWORD dwProcess, BOOL bAction)
{
	if (dwProcess && !m_clcClist.GetExtraImage(hItem, iSlot))
		m_clcClist.SetExtraImage(hItem, iSlot, iSlot*2 + (bAction?1:2));
}

HANDLE CJabberDlgPrivacyLists::CListAddContact(HWND hwndList, TCHAR *jid)
{
	HANDLE hItem = 0;

	HANDLE hContact = m_proto->HContactFromJID(jid);
	if ( !hContact ) {
		hItem = clc_info.findJid(jid);
		if (!hItem)
		{
			CLCINFOITEM cii = {0};
			cii.cbSize = sizeof(cii);
			cii.pszText = jid;
			hItem = m_clcClist.AddInfoItem(&cii);
			CListResetIcons(hwndList, hItem);
			clc_info.addJid(hItem, jid);
		}
	} else
	{
		hItem = m_clcClist.FindContact(hContact);
	}

	return hItem;
}

void CJabberDlgPrivacyLists::CListApplyList(HWND hwndList, CPrivacyList *pList)
{
	clc_info.pList = pList;

	bool bHideIcons = pList ? false : true;

	CListResetIcons(hwndList, clc_info.hItemDefault, bHideIcons);
	CListResetIcons(hwndList, clc_info.hItemSubBoth, bHideIcons);
	CListResetIcons(hwndList, clc_info.hItemSubFrom, bHideIcons);
	CListResetIcons(hwndList, clc_info.hItemSubNone, bHideIcons);
	CListResetIcons(hwndList, clc_info.hItemSubTo, bHideIcons);

	// group handles start with 1 (0 is "root")
	for (int iGroup = 1; CListIsGroup((HANDLE)iGroup); ++iGroup)
	{
		HANDLE hItem = m_clcClist.FindGroup((HANDLE)iGroup);
		if (!hItem) continue;
		CListResetIcons(hwndList, hItem, bHideIcons);
	}

	for (HANDLE hContact=db_find_first(); hContact;
			hContact=db_find_next(hContact))
	{
		HANDLE hItem = m_clcClist.FindContact(hContact);
		if (!hItem) continue;
		CListResetIcons(hwndList, hItem, bHideIcons);
	}

	for (int iJid = 0; iJid < clc_info.newJids.getCount(); ++iJid)
		CListResetIcons(hwndList, clc_info.newJids[iJid]->hItem, bHideIcons);

	if (!pList)
		goto lbl_return;

	CPrivacyListRule *pRule;
	for (pRule = pList->GetFirstRule(); pRule; pRule = pRule->GetNext())
	{
		HANDLE hItem = 0;
		switch (pRule->GetType())
		{
			case Jid:
			{
				hItem = CListAddContact(hwndList, pRule->GetValue());
				break;
			}
			case Group:
			{
				HANDLE hGroup = CListFindGroupByName(pRule->GetValue());
				hItem = m_clcClist.FindGroup(hGroup);
				break;
			}
			case Subscription:
			{
				     if (!lstrcmp(pRule->GetValue(), _T("none")))	hItem = clc_info.hItemSubNone;
				else if (!lstrcmp(pRule->GetValue(), _T("from")))	hItem = clc_info.hItemSubFrom;
				else if (!lstrcmp(pRule->GetValue(), _T("to")))		hItem = clc_info.hItemSubTo;
				else if (!lstrcmp(pRule->GetValue(), _T("both")))	hItem = clc_info.hItemSubBoth;
				break;
			}
			case Else:
			{
				hItem = clc_info.hItemDefault;
				break;
			}
		}

		if (!hItem) continue;

		DWORD dwPackets = pRule->GetPackets();
		if (!dwPackets) dwPackets = JABBER_PL_RULE_TYPE_ALL;
		CListSetupIcons(hwndList, hItem, 0, dwPackets & JABBER_PL_RULE_TYPE_MESSAGE, pRule->GetAction());
		CListSetupIcons(hwndList, hItem, 1, dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN, pRule->GetAction());
		CListSetupIcons(hwndList, hItem, 2, dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT, pRule->GetAction());
		CListSetupIcons(hwndList, hItem, 3, dwPackets & JABBER_PL_RULE_TYPE_IQ, pRule->GetAction());
	}

lbl_return:
	clc_info.bChanged = false;
}

DWORD CJabberDlgPrivacyLists::CListGetPackets(HWND, HANDLE hItem, bool bAction)
{
	DWORD result = 0;

	int iIcon = 0;

	iIcon = m_clcClist.GetExtraImage(hItem, 0);
	     if ( bAction && (iIcon == 1)) result |= JABBER_PL_RULE_TYPE_MESSAGE;
	else if (!bAction && (iIcon == 2)) result |= JABBER_PL_RULE_TYPE_MESSAGE;

	iIcon = m_clcClist.GetExtraImage(hItem, 1);
	     if ( bAction && (iIcon == 3)) result |= JABBER_PL_RULE_TYPE_PRESENCE_IN;
	else if (!bAction && (iIcon == 4)) result |= JABBER_PL_RULE_TYPE_PRESENCE_IN;

	iIcon = m_clcClist.GetExtraImage(hItem, 2);
	     if ( bAction && (iIcon == 5)) result |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;
	else if (!bAction && (iIcon == 6)) result |= JABBER_PL_RULE_TYPE_PRESENCE_OUT;

	iIcon = m_clcClist.GetExtraImage(hItem, 3);
	     if ( bAction && (iIcon == 7)) result |= JABBER_PL_RULE_TYPE_IQ;
	else if (!bAction && (iIcon == 8)) result |= JABBER_PL_RULE_TYPE_IQ;

	return result;
}

void CJabberDlgPrivacyLists::CListBuildList(HWND hwndList, CPrivacyList *pList)
{
	if (!pList) return;

	if (!clc_info.bChanged) return;

	clc_info.bChanged = false;

	DWORD dwOrder = 0;
	DWORD dwPackets = 0;

	HANDLE hItem;
	TCHAR *szJid;

	pList->RemoveAllRules();

	for (int iJid = 0; iJid < clc_info.newJids.getCount(); ++iJid)
	{
		hItem = clc_info.newJids[iJid]->hItem;
		szJid = clc_info.newJids[iJid]->jid;

		if (dwPackets = CListGetPackets(hwndList, hItem, true))
			pList->AddRule(Jid, szJid, TRUE, dwOrder++, dwPackets);
		if (dwPackets = CListGetPackets(hwndList, hItem, false))
			pList->AddRule(Jid, szJid, FALSE, dwOrder++, dwPackets);
	}

	for (HANDLE hContact=db_find_first(); hContact;
			hContact=db_find_next(hContact))
	{
		hItem = m_clcClist.FindContact(hContact);

		DBVARIANT dbv = {0};
		if ( m_proto->JGetStringT(hContact, "jid", &dbv)) {
			if ( m_proto->JGetStringT(hContact, "ChatRoomID", &dbv))
				continue;
		}

		szJid = dbv.ptszVal;

		if (dwPackets = CListGetPackets(hwndList, hItem, true))
			pList->AddRule(Jid, szJid, TRUE, dwOrder++, dwPackets);
		if (dwPackets = CListGetPackets(hwndList, hItem, false))
			pList->AddRule(Jid, szJid, FALSE, dwOrder++, dwPackets);

		JFreeVariant(&dbv);
	}

	// group handles start with 1 (0 is "root")
	for (int iGroup = 1; ; ++iGroup)
	{
		char idstr[33];
		_itoa(iGroup-1, idstr, 10);
		DBVARIANT dbv = {0};
		if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
			break;

		hItem = m_clcClist.FindGroup((HANDLE)iGroup);
		szJid = dbv.ptszVal+1;

		if (dwPackets = CListGetPackets(hwndList, hItem, true))
			pList->AddRule(Group, szJid, TRUE, dwOrder++, dwPackets);
		if (dwPackets = CListGetPackets(hwndList, hItem, false))
			pList->AddRule(Group, szJid, FALSE, dwOrder++, dwPackets);

		DBFreeVariant(&dbv);
	}

	hItem = clc_info.hItemSubBoth;
	szJid = _T("both");
	if (dwPackets = CListGetPackets(hwndList, hItem, true))
		pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
	if (dwPackets = CListGetPackets(hwndList, hItem, false))
		pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);

	hItem = clc_info.hItemSubFrom;
	szJid = _T("from");
	if (dwPackets = CListGetPackets(hwndList, hItem, true))
		pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
	if (dwPackets = CListGetPackets(hwndList, hItem, false))
		pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);

	hItem = clc_info.hItemSubNone;
	szJid = _T("none");
	if (dwPackets = CListGetPackets(hwndList, hItem, true))
		pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
	if (dwPackets = CListGetPackets(hwndList, hItem, false))
		pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);

	hItem = clc_info.hItemSubTo;
	szJid = _T("to");
	if (dwPackets = CListGetPackets(hwndList, hItem, true))
		pList->AddRule(Subscription, szJid, TRUE, dwOrder++, dwPackets);
	if (dwPackets = CListGetPackets(hwndList, hItem, false))
		pList->AddRule(Subscription, szJid, FALSE, dwOrder++, dwPackets);

	hItem = clc_info.hItemDefault;
	szJid = NULL;
	if (dwPackets = CListGetPackets(hwndList, hItem, true))
		pList->AddRule(Else, szJid, TRUE, dwOrder++, dwPackets);
	if (dwPackets = CListGetPackets(hwndList, hItem, false))
		pList->AddRule(Else, szJid, FALSE, dwOrder++, dwPackets);

	pList->Reorder();
	pList->SetModified();
}

void CJabberDlgPrivacyLists::EnableEditorControls()
{
	m_proto->m_privacyListManager.Lock();
	BOOL bListsLoaded = m_proto->m_privacyListManager.IsAllListsLoaded();
	BOOL bListsModified = m_proto->m_privacyListManager.IsModified() || clc_info.bChanged;
	m_proto->m_privacyListManager.Unlock();

	int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
	int nItemCount = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCOUNT, 0, 0 );
	BOOL bSelected = nCurSel != CB_ERR;
	BOOL bListSelected = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCOUNT, 0, 0);
	bListSelected = bListSelected && (SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0) != LB_ERR);
	bListSelected = bListSelected && SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETITEMDATA, SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0), 0);

	EnableWindow( GetDlgItem( m_hwnd, IDC_TXT_OTHERJID ), bListsLoaded && bListSelected );
	EnableWindow( GetDlgItem( m_hwnd, IDC_NEWJID ), bListsLoaded && bListSelected );
	EnableWindow( GetDlgItem( m_hwnd, IDC_ADDJID ), bListsLoaded && bListSelected );

	EnableWindow( GetDlgItem( m_hwnd, IDC_ADD_RULE ), bListsLoaded && bListSelected );
	EnableWindow( GetDlgItem( m_hwnd, IDC_EDIT_RULE ), bListsLoaded && bSelected );
	EnableWindow( GetDlgItem( m_hwnd, IDC_REMOVE_RULE ), bListsLoaded && bSelected );
	EnableWindow( GetDlgItem( m_hwnd, IDC_UP_RULE ), bListsLoaded && bSelected && nCurSel != 0 );
	EnableWindow( GetDlgItem( m_hwnd, IDC_DOWN_RULE ), bListsLoaded && bSelected && nCurSel != ( nItemCount - 1 ));
	EnableWindow( GetDlgItem( m_hwnd, IDC_REMOVE_LIST ), bListsLoaded && bListSelected );
	EnableWindow( GetDlgItem( m_hwnd, IDC_APPLY ), bListsLoaded && bListsModified );

	if (bListsLoaded)
		SetStatusText( TranslateT("Ready."));
}

LRESULT CALLBACK CJabberDlgPrivacyLists::LstListsSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	WNDPROC sttOldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
	switch (msg)
	{
		case WM_KEYDOWN:
		case WM_SYSKEYDOWN:
		{
			if (wParam == VK_INSERT)
				return UIEmulateBtnClick(GetParent(hwnd), IDC_ADD_LIST);
			if (wParam == VK_DELETE)
				return UIEmulateBtnClick(GetParent(hwnd), IDC_REMOVE_LIST);
			if (wParam == VK_SPACE)
			{
				if (GetAsyncKeyState(VK_CONTROL))
					return UIEmulateBtnClick(GetParent(hwnd), IDC_SET_DEFAULT);
				return UIEmulateBtnClick(GetParent(hwnd), IDC_ACTIVATE);
			}

			break;
		}
	}
	return CallWindowProc(sttOldWndProc, hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK CJabberDlgPrivacyLists::LstRulesSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	WNDPROC sttOldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
	switch (msg)
	{
		case WM_KEYDOWN:
		case WM_SYSKEYDOWN:
		{
			if (wParam == VK_INSERT)
				return UIEmulateBtnClick(GetParent(hwnd), IDC_ADD_RULE);
			if (wParam == VK_DELETE)
				return UIEmulateBtnClick(GetParent(hwnd), IDC_REMOVE_RULE);
			if ((wParam == VK_UP) && (lParam & (1UL << 29)))
				return UIEmulateBtnClick(GetParent(hwnd), IDC_UP_RULE);
			if ((wParam == VK_DOWN) && (lParam & (1UL << 29)))
				return UIEmulateBtnClick(GetParent(hwnd), IDC_DOWN_RULE);
			if (wParam == VK_F2)
				return UIEmulateBtnClick(GetParent(hwnd), IDC_EDIT_RULE);

			break;
		}
	}
	return CallWindowProc(sttOldWndProc, hwnd, msg, wParam, lParam);
}

BOOL CJabberDlgPrivacyLists::CanExit()
{
	m_proto->m_privacyListManager.Lock();
	BOOL bModified = m_proto->m_privacyListManager.IsModified();
	m_proto->m_privacyListManager.Unlock();

	if (clc_info.bChanged)
		bModified = TRUE;

	if ( !bModified )
		return TRUE;

	if ( IDYES == MessageBox( m_hwnd, TranslateT("Privacy lists are not saved, discard any changes and exit?"), TranslateT("Are you sure?"), MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 ))
		return TRUE;

	return FALSE;
}

void CJabberDlgPrivacyLists::btnSimple_OnClick(CCtrlButton *)
{
	CheckDlgButton(m_hwnd, IDC_BTN_SIMPLE, TRUE);
	CheckDlgButton(m_hwnd, IDC_BTN_ADVANCED, FALSE);
	UIShowControls(m_hwnd, idSimpleControls, SW_SHOW);
	UIShowControls(m_hwnd, idAdvancedControls, SW_HIDE);
	CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
}

void CJabberDlgPrivacyLists::btnAdvanced_OnClick(CCtrlButton *)
{
	CheckDlgButton(m_hwnd, IDC_BTN_SIMPLE, FALSE);
	CheckDlgButton(m_hwnd, IDC_BTN_ADVANCED, TRUE);
	UIShowControls(m_hwnd, idSimpleControls, SW_HIDE);
	UIShowControls(m_hwnd, idAdvancedControls, SW_SHOW);
	CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
	PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_LB_LISTS, LBN_SELCHANGE), 0);
}

void CJabberDlgPrivacyLists::btnAddJid_OnClick(CCtrlButton *)
{
	int len = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_NEWJID))+1;
	TCHAR *buf = (TCHAR *)_alloca(sizeof(TCHAR) * len);
	GetWindowText(GetDlgItem(m_hwnd, IDC_NEWJID), buf, len);
	SetWindowText(GetDlgItem(m_hwnd, IDC_NEWJID), _T(""));
	CListAddContact(GetDlgItem(m_hwnd, IDC_CLIST), buf);
}

void CJabberDlgPrivacyLists::btnActivate_OnClick(CCtrlButton *)
{
	if ( m_proto->m_bJabberOnline )
	{
		m_proto->m_privacyListManager.Lock();
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		if ( pList && pList->IsModified()) {
			m_proto->m_privacyListManager.Unlock();
			MessageBox( m_hwnd, TranslateT("Please save list before activating"), TranslateT("First, save the list"), MB_OK | MB_ICONSTOP );
			return;
		}
		EnableWindow( GetDlgItem( m_hwnd, IDC_ACTIVATE ), FALSE );
		SetWindowLongPtr( GetDlgItem( m_hwnd, IDC_ACTIVATE ), GWLP_USERDATA, (LONG_PTR)pList );
		XmlNodeIq iq( m_proto->m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListActive, JABBER_IQ_TYPE_SET, NULL, 0, -1, pList ));
		HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS));
		HXML active = query << XCHILD( _T("active"));
		if ( pList )
			active << XATTR( _T("name"), pList->GetListName());
		m_proto->m_privacyListManager.Unlock();

		SetStatusText(TranslateT( JABBER_PL_BUSY_MSG ));

		m_proto->m_ThreadInfo->send( iq );
	}
}

void CJabberDlgPrivacyLists::btnSetDefault_OnClick(CCtrlButton *)
{
	if ( m_proto->m_bJabberOnline )
	{
		m_proto->m_privacyListManager.Lock();
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		if ( pList && pList->IsModified()) {
			m_proto->m_privacyListManager.Unlock();
			MessageBox( m_hwnd, TranslateT("Please save list before you make it the default list"), TranslateT("First, save the list"), MB_OK | MB_ICONSTOP );
			return;
		}
		EnableWindow( GetDlgItem( m_hwnd, IDC_SET_DEFAULT ), FALSE );
		SetWindowLongPtr( GetDlgItem( m_hwnd, IDC_SET_DEFAULT ), GWLP_USERDATA, (LONG_PTR)pList );

		XmlNodeIq iq( m_proto->m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListDefault, JABBER_IQ_TYPE_SET, NULL, 0, -1, pList ));
		HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS ));
		HXML defaultTag = query << XCHILD( _T("default"));
		if ( pList )
			xmlAddAttr( defaultTag, _T("name"), pList->GetListName());
		m_proto->m_privacyListManager.Unlock();

		SetStatusText(TranslateT( JABBER_PL_BUSY_MSG ));

		m_proto->m_ThreadInfo->send( iq );
	}
}

void CJabberDlgPrivacyLists::lbLists_OnSelChange(CCtrlListBox *)
{
	LRESULT nCurSel = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETCURSEL, 0, 0 );
	if ( nCurSel == LB_ERR )
		return;

	LRESULT nErr = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_GETITEMDATA, nCurSel, 0 );
	if ( nErr == LB_ERR )
		return;
	if ( nErr == 0 )
	{
		if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
		{
			CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);
			CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), NULL);
		} else
		{
			EnableWindow( GetDlgItem( m_hwnd, IDC_PL_RULES_LIST ), FALSE );
			SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_RESETCONTENT, 0, 0 );
			SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_ADDSTRING, 0, (LPARAM)TranslateTS(_T("No list selected")));
		}
		EnableEditorControls();
		return;
	}

	m_proto->m_privacyListManager.Lock();
	if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
	{
		CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);
		CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), (CPrivacyList* )nErr);
	}
	else ShowAdvancedList((CPrivacyList* )nErr);

	m_proto->m_privacyListManager.Unlock();

	EnableEditorControls();
}

void CJabberDlgPrivacyLists::lbLists_OnDblClick(CCtrlListBox *)
{
	PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_ACTIVATE, 0 ), 0 );
}

void CJabberDlgPrivacyLists::lbRules_OnSelChange(CCtrlListBox *)
{
	EnableEditorControls();
}

void CJabberDlgPrivacyLists::lbRules_OnDblClick(CCtrlListBox *)
{
	PostMessage( m_hwnd, WM_COMMAND, MAKEWPARAM( IDC_EDIT_RULE, 0 ), 0 );
}

void CJabberDlgPrivacyLists::btnEditRule_OnClick(CCtrlButton *)
{
	// FIXME: potential deadlock due to PLM lock while editing rule
	m_proto->m_privacyListManager.Lock();
	{
		CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		if ( pList && pRule ) {
			CJabberDlgPrivacyRule dlgPrivacyRule(m_proto, m_hwnd, pRule);
			int nResult = dlgPrivacyRule.DoModal();
			if ( nResult ) {
				pList->SetModified();
				PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
	}	}	}
	m_proto->m_privacyListManager.Unlock();
}

void CJabberDlgPrivacyLists::btnAddRule_OnClick(CCtrlButton *)
{
	// FIXME: potential deadlock due to PLM lock while editing rule
	m_proto->m_privacyListManager.Lock();
	{
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		if ( pList ) {
			CPrivacyListRule* pRule = new CPrivacyListRule( m_proto, Jid, _T(""), FALSE );
			CJabberDlgPrivacyRule dlgPrivacyRule(m_proto, m_hwnd, pRule);
			int nResult = dlgPrivacyRule.DoModal();
			if ( nResult ) {
				pList->AddRule(pRule);
				pList->Reorder();
				pList->SetModified();
				PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
			}
			else delete pRule;
	}	}
	m_proto->m_privacyListManager.Unlock();
}

void CJabberDlgPrivacyLists::btnRemoveRule_OnClick(CCtrlButton *)
{
	m_proto->m_privacyListManager.Lock();
	{
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );

		if ( pList && pRule ) {
			pList->RemoveRule( pRule );
			int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
			int nItemCount = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCOUNT, 0, 0 );
			SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nCurSel != nItemCount - 1 ? nCurSel : nCurSel - 1, 0 );
			pList->Reorder();
			pList->SetModified();
			PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
	}	}

	m_proto->m_privacyListManager.Unlock();
}

void CJabberDlgPrivacyLists::btnUpRule_OnClick(CCtrlButton *)
{
	m_proto->m_privacyListManager.Lock();
	{
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );
		int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );

		if ( pList && pRule && nCurSel ) {
			pRule->SetOrder( pRule->GetOrder() - 11 );
			SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nCurSel - 1, 0 );
			pList->Reorder();
			pList->SetModified();
			PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
	}	}

	m_proto->m_privacyListManager.Unlock();
}

void CJabberDlgPrivacyLists::btnDownRule_OnClick(CCtrlButton *)
{
	m_proto->m_privacyListManager.Lock();
	{
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		CPrivacyListRule* pRule = GetSelectedRule( m_hwnd );
		int nCurSel = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCURSEL, 0, 0 );
		int nItemCount = SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_GETCOUNT, 0, 0 );

		if ( pList && pRule && ( nCurSel != ( nItemCount - 1 ))) {
			pRule->SetOrder( pRule->GetOrder() + 11 );
			SendDlgItemMessage( m_hwnd, IDC_PL_RULES_LIST, LB_SETCURSEL, nCurSel + 1, 0 );
			pList->Reorder();
			pList->SetModified();
			PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
	}	}

	m_proto->m_privacyListManager.Unlock();
}

void CJabberDlgPrivacyLists::btnAddList_OnClick(CCtrlButton *)
{
	// FIXME: line length is hard coded in dialog procedure
	CJabberDlgPrivacyAddList dlgPrivacyAddList(m_proto, m_hwnd);
	int nRetVal = dlgPrivacyAddList.DoModal();
	if ( nRetVal && _tcslen( dlgPrivacyAddList.szLine )) {
		m_proto->m_privacyListManager.Lock();
		CPrivacyList* pList = m_proto->m_privacyListManager.FindList( dlgPrivacyAddList.szLine );
		if ( !pList ) {
			m_proto->m_privacyListManager.AddList( dlgPrivacyAddList.szLine );
			pList = m_proto->m_privacyListManager.FindList( dlgPrivacyAddList.szLine );
			if ( pList ) {
				pList->SetModified( TRUE );
				pList->SetLoaded( TRUE );
			}
		}
		if ( pList )
			pList->SetDeleted( FALSE );
		int nSelected = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SELECTSTRING, -1, (LPARAM)dlgPrivacyAddList.szLine );
		if ( nSelected == CB_ERR ) {
			nSelected = SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_ADDSTRING, 0, (LPARAM)dlgPrivacyAddList.szLine );
			SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETITEMDATA, nSelected, (LPARAM)pList );
			SendDlgItemMessage( m_hwnd, IDC_LB_LISTS, LB_SETCURSEL, nSelected, 0 );
		}
		m_proto->m_privacyListManager.Unlock();
		PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
	}
}

void CJabberDlgPrivacyLists::btnRemoveList_OnClick(CCtrlButton *)
{
	m_proto->m_privacyListManager.Lock();
	{
		CPrivacyList* pList = GetSelectedList(m_hwnd);
		if ( pList ) {
			TCHAR *szListName = pList->GetListName();
			if ( ( m_proto->m_privacyListManager.GetActiveListName() && !_tcscmp( szListName, m_proto->m_privacyListManager.GetActiveListName()))
				|| ( m_proto->m_privacyListManager.GetDefaultListName() && !_tcscmp( szListName, m_proto->m_privacyListManager.GetDefaultListName()))) {
				m_proto->m_privacyListManager.Unlock();
				MessageBox( m_hwnd, TranslateTS(_T("Can't remove active or default list")), TranslateTS(_T("Sorry")), MB_OK | MB_ICONSTOP );
				return;
			}
			pList->SetDeleted();
			pList->SetModified();
	}	}

	m_proto->m_privacyListManager.Unlock();
	PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
}

void CJabberDlgPrivacyLists::btnApply_OnClick(CCtrlButton *)
{
	if ( !m_proto->m_bJabberOnline ) {
		SetStatusText(TranslateT("Unable to save list because you are currently offline."));
		return;
	}

	m_proto->m_privacyListManager.Lock();
	{
		if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
			CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);

		CPrivacyListModifyUserParam *pUserData = NULL;
		CPrivacyList* pList = m_proto->m_privacyListManager.GetFirstList();
		while ( pList ) {
			if ( pList->IsModified()) {
				CPrivacyListRule* pRule = pList->GetFirstRule();
				if ( !pRule )
					pList->SetDeleted();
				if ( pList->IsDeleted()) {
					pList->RemoveAllRules();
					pRule = NULL;
				}
				pList->SetModified( FALSE );

				if ( !pUserData )
					pUserData = new CPrivacyListModifyUserParam();

				pUserData->m_dwCount++;

				XmlNodeIq iq( m_proto->m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListModify, JABBER_IQ_TYPE_SET, NULL, 0, -1, pUserData ));
				HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS ));
				HXML listTag = query << XCHILD( _T("list")) << XATTR( _T("name"), pList->GetListName());

				while ( pRule ) {
					HXML itemTag = listTag << XCHILD( _T("item"));
					switch ( pRule->GetType()) {
					case Jid:
						itemTag << XATTR( _T("type"), _T("jid"));
						break;
					case Group:
						itemTag << XATTR( _T("type"), _T("group"));
						break;
					case Subscription:
						itemTag << XATTR( _T("type"), _T("subscription"));
						break;
					}
					if ( pRule->GetType() != Else )
						itemTag << XATTR( _T("value"), pRule->GetValue());
					if ( pRule->GetAction())
						itemTag << XATTR( _T("action"), _T("allow"));
					else
						itemTag << XATTR( _T("action"), _T("deny"));
					itemTag << XATTRI( _T("order"), pRule->GetOrder());
					DWORD dwPackets = pRule->GetPackets();
					if ( dwPackets != JABBER_PL_RULE_TYPE_ALL ) {
						if ( dwPackets & JABBER_PL_RULE_TYPE_IQ )
							itemTag << XCHILD( _T("iq"));
						if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_IN )
							itemTag << XCHILD( _T("presence-in"));
						if ( dwPackets & JABBER_PL_RULE_TYPE_PRESENCE_OUT )
							itemTag << XCHILD( _T("presence-out"));
						if ( dwPackets & JABBER_PL_RULE_TYPE_MESSAGE )
							itemTag << XCHILD( _T("message"));
					}
					pRule = pRule->GetNext();
				}

				m_proto->m_ThreadInfo->send( iq );
			}
			pList = pList->GetNext();
	}	}
	m_proto->m_privacyListManager.Unlock();
	SetStatusText(TranslateT( JABBER_PL_BUSY_MSG ));
	PostMessage( m_hwnd, WM_JABBER_REFRESH, 0, 0 );
}

void CJabberDlgPrivacyLists::OnCommand_Close(HWND /*hwndCtrl*/, WORD /*idCtrl*/, WORD /*idCode*/)
{
	if (IsWindowVisible(GetDlgItem(m_hwnd, IDC_CLIST)))
		CListBuildList(GetDlgItem(m_hwnd, IDC_CLIST), clc_info.pList);

	if (CanExit())
		DestroyWindow(m_hwnd);
}

void CJabberDlgPrivacyLists::clcClist_OnUpdate(CCtrlClc::TEventInfo*)
{
	CListFilter(GetDlgItem(m_hwnd, IDC_CLIST));
	CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
}

void CJabberDlgPrivacyLists::clcClist_OnOptionsChanged(CCtrlClc::TEventInfo*)
{
	CListResetOptions(GetDlgItem(m_hwnd, IDC_CLIST));
	CListApplyList(GetDlgItem(m_hwnd, IDC_CLIST), GetSelectedList(m_hwnd));
}

void CJabberDlgPrivacyLists::clcClist_OnClick(CCtrlClc::TEventInfo *evt)
{
	HANDLE hItem;
	DWORD hitFlags;
	int iImage;

	if(evt->info->iColumn==-1) return;
	hItem = m_clcClist.HitTest(evt->info->pt.x, evt->info->pt.y, &hitFlags);
	if(hItem==NULL) return;
	if (!(hitFlags&CLCHT_ONITEMEXTRA)) return;

	iImage = m_clcClist.GetExtraImage(hItem, evt->info->iColumn);
	if (iImage != 0xFF)
	{
		if (iImage == 0)
			iImage = evt->info->iColumn * 2 + 2;
		else if (iImage == evt->info->iColumn * 2 + 2)
			iImage = evt->info->iColumn * 2 + 1;
		else
			iImage = 0;

		m_clcClist.SetExtraImage(hItem, evt->info->iColumn, iImage);

		clc_info.bChanged = true;

		EnableEditorControls();
	}
}

int CJabberDlgPrivacyLists::Resizer(UTILRESIZECONTROL *urc)
{
	switch (urc->wId) {
	case IDC_HEADERBAR:
		return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORX_WIDTH;
	case IDC_BTN_SIMPLE:
	case IDC_BTN_ADVANCED:
		return RD_ANCHORX_RIGHT|RD_ANCHORY_TOP;
	case IDC_LB_LISTS:
		return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORY_HEIGHT;
	case IDC_PL_RULES_LIST:
	case IDC_CLIST:
		return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORY_HEIGHT|RD_ANCHORX_WIDTH;
	case IDC_NEWJID:
	case IDC_CANVAS:
		return RD_ANCHORX_LEFT|RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
	case IDC_ADD_LIST:
	case IDC_ACTIVATE:
	case IDC_REMOVE_LIST:
	case IDC_SET_DEFAULT:
	case IDC_TXT_OTHERJID:
	case IDC_ADD_RULE:
	case IDC_UP_RULE:
	case IDC_EDIT_RULE:
	case IDC_DOWN_RULE:
	case IDC_REMOVE_RULE:
		return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
	case IDC_ADDJID:
	case IDC_APPLY:
	case IDCANCEL:
		return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
	}
	return CSuper::Resizer(urc);
}

INT_PTR __cdecl CJabberProto::OnMenuHandlePrivacyLists( WPARAM, LPARAM )
{
	UI_SAFE_OPEN(CJabberDlgPrivacyLists, m_pDlgPrivacyLists);
	return 0;
}

void CJabberProto::QueryPrivacyLists( ThreadData *pThreadInfo )
{
	XmlNodeIq iq( m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyLists ));
	iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS));
	if ( pThreadInfo )
		pThreadInfo->send( iq );
	else if ( m_ThreadInfo )
		m_ThreadInfo->send( iq );
}

/////////////////////////////////////////////////////////////////////////////////////////
// builds privacy menu

INT_PTR __cdecl CJabberProto::menuSetPrivacyList( WPARAM, LPARAM, LPARAM iList )
{
	m_privacyListManager.Lock();
	CPrivacyList *pList = NULL;

	if (iList)
	{
		pList = m_privacyListManager.GetFirstList();
		for (int i = 1; pList && (i < iList); ++i)
			pList = pList->GetNext();
	}

	XmlNodeIq iq( m_iqManager.AddHandler( &CJabberProto::OnIqResultPrivacyListActive, JABBER_IQ_TYPE_SET, NULL, 0, -1, pList ));
	HXML query = iq << XQUERY( _T(JABBER_FEAT_PRIVACY_LISTS));
	HXML active = query << XCHILD( _T("active"));
	if ( pList )
		active << XATTR( _T("name"), pList->GetListName());
	m_privacyListManager.Unlock();

	m_ThreadInfo->send( iq );
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
// init privacy menu

void CJabberProto::BuildPrivacyMenu()
{
	CLISTMENUITEM mi = { 0 };
	mi.cbSize = sizeof(mi);
	mi.position = 200005;
	mi.pszContactOwner = m_szModuleName;
	mi.icolibItem = GetIconHandle(IDI_AGENTS);
	mi.flags = CMIF_ROOTPOPUP | CMIF_CHILDPOPUP | CMIF_ICONFROMICOLIB | CMIF_HIDDEN;
	mi.pszName = LPGEN("Privacy Lists");
	mi.hParentMenu = MO_GetProtoRootMenu( m_szModuleName );
	m_hPrivacyMenuRoot = Menu_AddProtoMenuItem(&mi);

	JCreateService( "/PrivacyLists", &CJabberProto::OnMenuHandlePrivacyLists );
	char srvFce[MAX_PATH + 64];
	mir_snprintf( srvFce, SIZEOF(srvFce), "%s/PrivacyLists", m_szModuleName );
	mi.pszService = srvFce;
	mi.position = 3000040000;
	mi.flags = CMIF_CHILDPOPUP | CMIF_TCHAR | CMIF_ICONFROMICOLIB;
	mi.icolibItem = GetIconHandle(IDI_PRIVACY_LISTS);
	mi.ptszName = LPGENT("List Editor...");
	mi.hParentMenu = m_hPrivacyMenuRoot;
	Menu_AddProtoMenuItem(&mi);
}

void CJabberProto::BuildPrivacyListsMenu( bool bDeleteOld )
{
	int i;
	if ( bDeleteOld )
		for ( i=0; i < m_hPrivacyMenuItems.getCount(); i++ )
			CallService( MO_REMOVEMENUITEM, (WPARAM)m_hPrivacyMenuItems[i], 0 );
	m_hPrivacyMenuItems.destroy();

	m_privacyListManager.Lock();

	i = 0;
	char srvFce[MAX_PATH + 64], *svcName = srvFce+strlen( m_szModuleName );

	CLISTMENUITEM mi = { 0 };
	mi.cbSize = sizeof(mi);
	mi.position = 2000040000;
	mi.flags = CMIF_CHILDPOPUP | CMIF_ICONFROMICOLIB | CMIF_TCHAR;
	mi.hParentMenu = m_hPrivacyMenuRoot;
	mi.pszService = srvFce;

	mir_snprintf( srvFce, SIZEOF(srvFce), "%s/menuPrivacy%d", m_szModuleName, i );
	if ( i > m_privacyMenuServiceAllocated ) {
		JCreateServiceParam( svcName, &CJabberProto::menuSetPrivacyList, i );
		m_privacyMenuServiceAllocated = i;
	}
	mi.position++;
	mi.icolibItem = LoadSkinnedIconHandle(
		m_privacyListManager.GetActiveListName() ?
			SKINICON_OTHER_SMALLDOT :
			SKINICON_OTHER_EMPTYBLOB);
	mi.ptszName = LPGENT("<none>");
	m_hPrivacyMenuItems.insert( Menu_AddProtoMenuItem(&mi));

	for ( CPrivacyList *pList = m_privacyListManager.GetFirstList(); pList; pList = pList->GetNext()) {
		++i;
		mir_snprintf( srvFce, SIZEOF(srvFce), "%s/menuPrivacy%d", m_szModuleName, i );

		if ( i > m_privacyMenuServiceAllocated ) {
			JCreateServiceParam( svcName, &CJabberProto::menuSetPrivacyList, i );
			m_privacyMenuServiceAllocated = i;
		}

		mi.position++;
		mi.icolibItem = LoadSkinnedIconHandle(
			lstrcmp( m_privacyListManager.GetActiveListName(), pList->GetListName()) ?
				SKINICON_OTHER_SMALLDOT :
				SKINICON_OTHER_EMPTYBLOB);
		mi.ptszName = pList->GetListName();
		m_hPrivacyMenuItems.insert( Menu_AddProtoMenuItem(&mi));
	}

	m_privacyListManager.Unlock();
}