/* ========================================================================

                              Custom Status List
                              __________________

  Custom Status List plugin for Miranda-IM (www.miranda-im.org)
  Follower of Custom Status History List by HANAX
  Copyright � 2006-2008 HANAX
  Copyright � 2007-2009 jarvis

  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.

  ============================================================================

  File name      : $URL$
  Revision       : $Rev$
  Last change on : $Date$
  Last change by : $Author$

  ============================================================================  


  DESCRIPTION:

  Offers List of your Custom Statuses.

// ======================================================================== */

#include "cslist.h"
#include "strpos.h"
#include "legacy.h"

SortedList *servicesList;
int hLangpack;

extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { PLUGIN_GUUID, MIID_LAST };

PLUGININFOEX pluginInfoEx =
{
	sizeof(PLUGININFOEX),
	__PLUGIN_NAME,
	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
	__DESCRIPTION,
	__AUTHOR,
	__AUTHOREMAIL,
	__COPYRIGHT,
	__AUTHORWEB,
	UNICODE_AWARE,
	PLUGIN_GUUID
};

// ====[ MAIN ]===============================================================

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
	CSList::handle = hinstDLL;
	return TRUE;
}


// ====[ PLUGIN INFO ]========================================================

extern "C" __declspec( dllexport ) PLUGININFOEX* MirandaPluginInfoEx( DWORD mirandaVersion )
{
	CSList::dwMirandaVersion = mirandaVersion;
	return &pluginInfoEx;
}

// ====[ LOADER ]=============================================================

extern "C" __declspec( dllexport ) int Load(  )
{
	mir_getLP(&pluginInfoEx);
	servicesList = List_Create(0, 10);

	// support for ComboBoxEx
	{
		INITCOMMONCONTROLSEX icc;
		icc.dwSize = sizeof(icc);
		icc.dwICC = ICC_USEREX_CLASSES;
		InitCommonControlsEx( &icc );
	}

	// set global variables
	// we need 0.8.0.9 core which introduced accounts support for protocols
	CSList::bAccountsSupported = ( CSList::dwMirandaVersion >= PLUGIN_MAKE_VERSION( 0, 8, 0, 9 ));

	// are we running under Unicode core?
	{
		char szVer[MAX_PATH];

		CallService( MS_SYSTEM_GETVERSIONTEXT, MAX_PATH, ( LPARAM )szVer );
		_strlwr( szVer ); // make sure it is lowercase

		CSList::bUnicodeCore = ( strstr( szVer, "unicode" ) != NULL );
	}

	// TODO: Alerts with wrong combination of ANSI/Unicode Windows/core/plugin

	// create handler
	cslist = new CSList( );

	return FALSE;
}


// ====[ UNLOADER ]===========================================================

extern "C" __declspec( dllexport ) int Unload( void )
{
	for (int i = servicesList->realCount-1; i >= 0; i--)
	{
		HANDLE *hService = (HANDLE *)servicesList->items[i];
		DestroyServiceFunction(hService);
	}
	List_Destroy(servicesList);
	mir_free(servicesList);
	delete cslist;

	return FALSE;
}


// ====[ FUN ]================================================================


CSList::CSList( )
{
	this->mainWindow = NULL;
	this->initIcoLib( );
	this->hHookOnOptionsInit = HookEvent( ME_OPT_INITIALISE, this->initOptions );
	//this->hHookOnPluginsLoaded = HookEvent( ME_SYSTEM_MODULESLOADED, this->postLoading );
	this->hHookOnDBSettingChanged = HookEvent( ME_DB_CONTACT_SETTINGCHANGED, this->respondDBChange );
	this->hHookOnStatusMenuBuild = HookEvent( ME_CLIST_PREBUILDSTATUSMENU, this->createMenuItems );
}


CSList::~CSList( )
{
	// unhook events, destroy functions
	UnhookEvent( this->hHookOnStatusMenuBuild );
	UnhookEvent( this->hHookOnPluginsLoaded );
	UnhookEvent( this->hHookOnOptionsInit );
	UnhookEvent( this->hHookOnDBSettingChanged );
	DestroyServiceFunction( this->hServiceShowList );

	// clear IcoLib
	for ( int i = 0; i < SIZEOF(forms); i++ )
	{
		char iconName[64];
		mir_snprintf(iconName, SIZEOF(iconName), "%s_%s", __INTERNAL_NAME, forms[i].pszIconIcoLib);
		CallService( MS_SKIN2_REMOVEICON, 0, ( LPARAM )iconName );
	}
}


int CSList::postLoading( WPARAM wparam, LPARAM lparam )
{
	//cslist->registerHotkeys( );
	cslist->initIcoLib( ); // for < 0.7, but probably useless though :/
	return FALSE;
}


void CSList::registerHotkeys(char buf[200], TCHAR* accName, int Number)
{
	HOTKEYDESC hotkey = { 0 };
	hotkey.cbSize = sizeof(hotkey);
	hotkey.dwFlags = HKD_TCHAR;
	hotkey.pszName = buf;
	hotkey.ptszDescription = accName;
	hotkey.ptszSection = LPGENT("Custom Status List");
	hotkey.pszService = buf;
	hotkey.DefHotKey = HOTKEYCODE( HOTKEYF_CONTROL | HOTKEYF_SHIFT, '0'+Number );
	Hotkey_Register(&hotkey);
}

int CSList::initOptions( WPARAM wparam, LPARAM lparam )
{
	OPTIONSDIALOGPAGE odp = { 0 };
	odp.cbSize = sizeof(odp);
	odp.position = 955000000;
	odp.hInstance = CSList::handle;
	odp.pszTemplate = MAKEINTRESOURCEA( IDD_OPTIONS );
	odp.pszTitle = MODULENAME;
	odp.pfnDlgProc = CSOptionsProc;
	odp.pszGroup = LPGEN("Status");
	odp.flags = ODPF_BOLDGROUPS;
	Options_AddPage(wparam, &odp);
	return FALSE;
}


int CSList::respondDBChange( WPARAM wparam, LPARAM lparam )
{
	HANDLE hContact = ( HANDLE )wparam;
	DBCONTACTWRITESETTING *cws = ( DBCONTACTWRITESETTING* )lparam;

	// if user changes his UIN or JID on any account
	if (hContact == NULL && (!lstrcmpA(cws->szSetting, "UIN") || !lstrcmpA(cws->szSetting, "jid")))
	{
		cslist->rebuildMenuItems();
		return 0;
	}

	// own setting or not a status-related setting
	else if ( hContact == NULL || lstrcmpA( cws->szSetting, "Status" ))
		return 0;

	DWORD dwEasteredUin = getDword( "EasterEggedUIN", 0x0 );
	if ( dwEasteredUin == 0x0 ) // no spyed UIN found
		return 0;

	DBVARIANT dbv = { 0 };
	DBGetContactSettingString( hContact, "Protocol", "p", &dbv );

	DWORD dwUin = DBGetContactSettingDword( hContact, dbv.pszVal, "UIN", 0x0 );

	if ( dwUin != 0x0 ) // if this is an ICQ user
		if ( dwUin == dwEasteredUin ) // if spyed UIN is equivalent to this contact
			if ( DBGetContactSettingWord( hContact, "UserOnline", "OldStatus", 0 ) == ID_STATUS_OFFLINE )
				if ( cws->value.wVal > ID_STATUS_OFFLINE )
					cslist->setStatus( IDC_CANCEL, NULL, "");

	return FALSE;
}


int CSList::createMenuItems( WPARAM wparam, LPARAM lparam )
{
	int ProtoCount = 0;

	ForAllProtocols(countProtos, &ProtoCount);

	if (ProtoCount == 0)
		return FALSE;

	ForAllProtocols(addProtoStatusMenuItem, 0);

	return FALSE;
}


void CSList::rebuildMenuItems( )
{
	CLIST_INTERFACE* pcli;
	pcli = ( CLIST_INTERFACE* )CallService( MS_CLIST_RETRIEVE_INTERFACE, 0, 0 );
	if ( pcli && pcli->version > 4 )
		pcli->pfnReloadProtoMenus( );
}


void CSList::setStatus( WORD code, StatusItem* item,  char* protoName)
{
	if ( code == IDCLOSE )
		return;

	PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)protoName);
	if (pdescr == NULL)
		return;
	int statusToSet = -1;
	if (lstrcmpA(pdescr->szProtoName, "ICQ") == 0)
	{
		ICQ_CUSTOM_STATUS ics = {0};
		ics.cbSize = sizeof(ICQ_CUSTOM_STATUS);
		ics.flags = CSSF_MASK_STATUS | CSSF_MASK_NAME | CSSF_MASK_MESSAGE | CSSF_TCHAR;

		if (code == IDC_CANCEL)
		{
			statusToSet = 0;
			ics.ptszName = _T("");
			ics.ptszMessage = _T("");
		}
		else if (code == IDOK && item != NULL)
		{
			statusToSet = item->iIcon + 1;
			ics.ptszName = variables_parsedup(item->tszTitle, NULL, NULL);
			ics.ptszMessage = variables_parsedup(item->tszMessage, NULL, NULL);
		}
		else return;

		ics.status = &statusToSet;

		char protoService[64];
		mir_snprintf(protoService, SIZEOF(protoService), "%s%s", protoName, PS_ICQ_SETCUSTOMSTATUSEX);
		CallService(protoService, 0, (LPARAM)&ics);
	}
	if (lstrcmpA(pdescr->szProtoName, "JABBER") == 0)
	{
		JABBER_CUSTOM_STATUS ics = {0};
		ics.cbSize = sizeof(ICQ_CUSTOM_STATUS);
		ics.flags = CSSF_MASK_STATUS | CSSF_MASK_NAME | CSSF_MASK_MESSAGE | CSSF_TCHAR;

		if (code == IDC_CANCEL)
		{
			statusToSet = 0;
			ics.ptszMessage = _T("");
		}
		else if (code == IDOK && item != NULL)
		{
			statusToSet = item->iIcon + 1;
			ics.ptszMessage = variables_parsedup(item->tszMessage, NULL, NULL);
		}
		else return;

		ics.status = &statusToSet;

		char protoService[64];
		mir_snprintf(protoService, SIZEOF(protoService), "%s%s", protoName, JS_SETXSTATUSEX);
		CallService(protoService, 0, (LPARAM)&ics);
	}
}

void CSList::initIcoLib( )
{
	// init icons of buttons
	TCHAR tszFile[MAX_PATH];
	GetModuleFileName( CSList::handle, tszFile, MAX_PATH );

	SKINICONDESC sid = { 0 };
	sid.cbSize = sizeof(SKINICONDESC);
	sid.ptszDefaultFile = tszFile;
	sid.flags = SIDF_ALL_TCHAR;
	sid.cx = sid.cy = 16;
	sid.ptszSection = _T(MODULENAME);

	for (int i=0; i < SIZEOF(forms); i++ ) {
		char szSettingName[64];
		mir_snprintf( szSettingName, SIZEOF(szSettingName), "%s_%s", __INTERNAL_NAME, forms[i].pszIconIcoLib );

		sid.pszName = szSettingName;
		sid.ptszDescription = forms[i].ptszDescr;
		sid.iDefaultIndex = -forms[i].iconNoIcoLib;
		forms[i].hIcoLibItem = Skin_AddIcon(&sid);
	}
}


INT_PTR CSList::showList(WPARAM wparam, LPARAM lparam, LPARAM param)
{
	cslist->mainWindow = new CSWindow((char*)param);
	ForkThread(&cslist->mainWindow->showWindow, cslist->mainWindow);
	return 0;
}


void CSList::closeList( HWND hwnd )
{
	ForkThread( &cslist->mainWindow->closeWindow, hwnd );
	delete cslist->mainWindow;
	cslist->mainWindow = NULL;
}


void CSList::ForAllProtocols( pForAllProtosFunc pFunc, void* arg )
{
	int protoCount;
	PROTOACCOUNT** pdesc;
	CallService( MS_PSEUDO_PROTO_PROTOACCSENUM, ( WPARAM )&protoCount, ( LPARAM )&pdesc );

	for ( int i = 0; i < protoCount; i++ )
	{
		if (lstrcmpA(pdesc[i]->szProtoName, "ICQ") == 0)
		{
			DWORD uin = 0;
			uin = DBGetContactSettingDword(NULL, pdesc[i]->szModuleName, "UIN", 0);
			if (uin == 0)
				continue;
		}
		else if (lstrcmpA(pdesc[i]->szProtoName, "JABBER") == 0)
		{
			DBVARIANT dbv = {0};
			DBGetContactSettingTString(NULL, pdesc[i]->szModuleName, "jid", &dbv);
			if (dbv.ptszVal == _T(""))
				continue;
		}
		else
			continue;
		pFunc(pdesc[i]->szModuleName , arg);
	}
}


void CSList::addProtoStatusMenuItem( char* protoName, void* arg )
{
	char buf[200];
	PROTOACCOUNT* pdescr;
	pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)protoName);
	if (pdescr == NULL)
		return;

	HANDLE hService;
	mir_snprintf(buf, SIZEOF(buf), "CSList/ShowList/%s", protoName);
	if (!ServiceExists(buf))
		hService = CreateServiceFunctionParam(buf, CSList::showList, (LPARAM)protoName);
	List_Insert(servicesList, hService, servicesList->realCount);

	CLISTMENUITEM mi = {0};
	mi.cbSize = sizeof(mi);
	mi.flags = CMIF_TCHAR;
	if (ServiceExists(MS_SKIN2_GETICON))
		mi.icolibItem = (HANDLE)LoadIconExEx("icon", IDI_CSLIST);
	else
		mi.hIcon = LoadIconExEx("icon", IDI_CSLIST);
	mi.ptszName = _T(MODULENAME);
	mi.position = 2000040000;
	mi.pszService = buf;
	mi.ptszPopupName = pdescr->tszAccountName;
	Menu_AddStatusMenuItem(&mi);

	cslist->registerHotkeys(buf, pdescr->tszAccountName, pdescr->iOrder);
}


void CSList::countProtos( char* protoName, void* arg )
{
	int* protosEnabled = (int*)arg;
	*protosEnabled = ( *protosEnabled )++;
}


void CSList::countPlusModProtos( char* protoName, void* arg )
{
	int* protosExtraEnabled = ( int* )arg;
	if ( DBGetContactSettingDword( NULL, protoName, "NonStandartXstatus", 0 ) ||
	     DBGetContactSettingDword( NULL, protoName, "NonStandardXstatus", 0 ))
		*protosExtraEnabled = ( *protosExtraEnabled )++;
}

void CSList::importCustomStatusUIStatusesFromAllProtos( char* protoName, void* arg )
{
	int* result = ( int* )arg;

	for ( int i = 1; i <= 37; i++ )
	{
		DBVARIANT dbv = { 0 };
		char bufTitle[32], bufMessage[32];
		StatusItem* si = new StatusItem( );

		si->iIcon = i - 1;

		mir_snprintf( bufTitle, 32, "XStatus%luName", i );
		DBGetContactSettingTString( NULL, protoName, bufTitle, &dbv );
		lstrcpy( si->tszTitle, dbv.ptszVal );

		mir_snprintf( bufMessage, 32, "XStatus%luMsg", i );
		DBGetContactSettingTString( NULL, protoName, bufMessage, &dbv );
		lstrcpy( si->tszMessage, dbv.ptszVal );

		if ( lstrlen( si->tszTitle ) || lstrlen( si->tszMessage ))
		{
			cslist->mainWindow->itemslist->list->add( si );
			cslist->mainWindow->bSomethingChanged = TRUE;
		}
		else
			delete si;

		if ( *result == IDYES )
		{
			DBDeleteContactSetting( NULL, protoName, bufTitle );
			DBDeleteContactSetting( NULL, protoName, bufMessage );
		}
	}
	cslist->mainWindow->listview->reinitItems( cslist->mainWindow->itemslist->list->getListHead( ));
}


CSWindow::CSWindow(char* protoName)
{
	this->protoName = protoName;
	this->handle = NULL;
	this->bExtraIcons = getByte( "AllowExtraIcons", DEFAULT_ALLOW_EXTRA_ICONS );
	this->itemslist = new CSItemsList(this->protoName);
	this->listview = NULL;
	this->addModifyDlg = NULL;
	this->bSomethingChanged = FALSE;
	this->filterString = NULL;
}


CSWindow::~CSWindow( )
{
	delete this->itemslist;
	SAFE_FREE( ( void** )&this->filterString );
}


void CSWindow::showWindow( void* arg )
{
	CSWindow* csw = ( CSWindow* )arg;

	while ( csw == NULL )
		SleepEx( 10, FALSE );

	DialogBoxParam( CSList::handle, MAKEINTRESOURCE( IDD_CSLIST ), NULL, ( DLGPROC )CSWindowProc, ( LPARAM )csw );
}


void CSWindow::closeWindow( void* arg )
{
	HWND hwnd = ( HWND )arg;
	EndDialog( hwnd, FALSE );
}


void CSWindow::initIcons()
{
	PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)protoName);
	if (pdescr == NULL)
		return;

	if (lstrcmpA(pdescr->szProtoName, "ICQ") == 0)
	{
		DWORD uin = 0;
		uin = DBGetContactSettingDword(NULL, pdescr->szModuleName, "UIN", 0);
		if (uin != 0)
		{
			this->icons = ImageList_Create( 16, 16,  IsWinVerXPPlus( ) ? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK, XSTATUS_COUNT_EXTENDED, 0 );
			if ( this->icons )
			{
				for ( int i = 0; i < ( ( this->bExtraIcons ) ? XSTATUS_COUNT_EXTENDED : XSTATUS_COUNT ); i++ )
				{
					char szTemp[MAX_PATH];
					mir_snprintf(szTemp, SIZEOF(szTemp), "%s_xstatus%d", protoName, i);
					ImageList_AddIcon( this->icons, ( HICON )CallService( MS_SKIN2_GETICON, 0, ( LPARAM )szTemp ));
				}
			}
		}
	}
	else if (lstrcmpA(pdescr->szProtoName, "JABBER") == 0)
	{
		DBVARIANT dbv = {0};
		DBGetContactSettingTString(NULL, pdescr->szModuleName, "jid", &dbv);
		if (dbv.ptszVal != _T(""))
		{
			this->icons = ImageList_Create( 16, 16,  IsWinVerXPPlus( ) ? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK, MOODS_COUNT, 0 );
			if ( this->icons )
			{
				for ( int i = 0; i < MOODS_COUNT; i++ )
				{
					char szTemp[MAX_PATH];
					mir_snprintf(szTemp, SIZEOF(szTemp), "%s_%s", protoName, moods_names[i].szTag);
					ImageList_AddIcon( this->icons, ( HICON )CallService( MS_SKIN2_GETICON, 0, ( LPARAM )szTemp ));
				}
			}
		}
	}
}


void CSWindow::deinitIcons( )
{
	ImageList_Destroy( this->icons );
}


void CSWindow::initButtons( )
{
	for (int i = 0; i < SIZEOF(forms); i++)
	{
		if ( forms[i].idc < 0 )
			continue;

		SendDlgItemMessage( this->handle, forms[i].idc, BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadIconExEx( forms[i].pszIconIcoLib, forms[i].iconNoIcoLib ));
		SendDlgItemMessage( this->handle, forms[i].idc, BUTTONSETASFLATBTN, TRUE, 0 ); //maybe set as BUTTONSETDEFAULT?
		SendDlgItemMessage( this->handle, forms[i].idc, BUTTONADDTOOLTIP, ( WPARAM )TranslateTS(forms[i].ptszTitle), BATF_TCHAR );
	}
}

void CSWindow::loadWindowPosition( )
{
	RECT rect = { 0 };
	int width = GetSystemMetrics( SM_CXSCREEN );
	int height = GetSystemMetrics( SM_CYSCREEN );
	GetWindowRect( this->handle, &rect );
	int x, y, defX, defY;
	defX = x = ( width  + 1 - ( rect.right  - rect.left )) >> 1;
	defY = y = ( height + 1 - ( rect.bottom - rect.top  )) >> 1;
	if ( getByte( "RememberWindowPosition", DEFAULT_REMEMBER_WINDOW_POSITION ))
	{
		x = getWord( "PositionX", defX );
		y = getWord( "PositionY", defY );
	}
	if ( x > width || y > height || x < 0 || y < 0 )
	{
		x = defX;
		y = defY;
  }

	MoveWindow( this->handle,
	            x, y,
	            ( rect.right - rect.left ),
	            ( rect.bottom - rect.top ),
	            TRUE );
}


void CSWindow::toggleEmptyListMessage( )
{
	HWND hwnd = GetDlgItem( this->handle, IDC_NO_ITEMS );
	ShowWindow( hwnd, ( ListView_GetItemCount( this->listview->handle ) > 0 ) ? FALSE : TRUE );
	SetForegroundWindow( hwnd );
	hwnd = GetDlgItem( this->handle, IDC_ADD_SAMPLE );
	ShowWindow( hwnd, ( ListView_GetItemCount( this->listview->handle ) > 0 ) ? FALSE : TRUE );
	SetForegroundWindow( hwnd );
}


BOOL CSWindow::itemPassedFilter( ListItem< StatusItem >* li )
{
	TCHAR filter[MAX_PATH];
	GetDlgItemText( this->handle, IDC_FILTER_FIELD, filter, MAX_PATH );

	if ( lstrlen( filter ))
	{
		TCHAR title[EXTRASTATUS_TITLE_LIMIT], message[EXTRASTATUS_MESSAGE_LIMIT];
		lstrcpy( title, li->item->tszTitle ); lstrcpy( message, li->item->tszMessage );
		if ( strpos( _tcslwr( title ), _tcslwr( filter )) == -1 )
			if ( strpos( _tcslwr( message ), _tcslwr( filter )) == -1 )
				return FALSE;
	}

	return TRUE;
}


void CSWindow::toggleFilter( )
{
	HWND hFilter = GetDlgItem( this->handle, IDC_FILTER_FIELD );
	BOOL isEnabled = !IsWindowEnabled( hFilter ) ? TRUE : FALSE; // ! = + isEnabled = !isEnabled in one
	EnableWindow( hFilter, isEnabled );
	ShowWindow( hFilter, isEnabled );
	CheckDlgButton( this->handle, IDC_FILTER, isEnabled );
	SetForegroundWindow( hFilter );
	if ( isEnabled )
		SetFocus( hFilter );
	else
	{
		TCHAR filterText[255];
		GetDlgItemText(this->handle, IDC_FILTER_FIELD, filterText, SIZEOF(filterText));
		if ( lstrlen( filterText ) > 0 )
			SetDlgItemText( this->handle, IDC_FILTER_FIELD, TEXT( "" ));
	}
}


BOOL CSWindow::toggleButtons( )
{
	int selection = ListView_GetSelectedItemMacro( this->listview->handle );
	BOOL validSelection = ( selection >= 0 && ( unsigned int )selection < this->itemslist->list->getCount( )) ? TRUE : FALSE;
	BOOL filterEnabled = IsWindowVisible( GetDlgItem( this->handle, IDC_FILTER_FIELD ));
	BOOL somethingChanged = this->bSomethingChanged;

	EnableWindow( GetDlgItem( this->handle, IDC_ADD       ), !filterEnabled );
	EnableWindow( GetDlgItem( this->handle, IDC_MODIFY    ), validSelection && !filterEnabled );
	EnableWindow( GetDlgItem( this->handle, IDC_REMOVE    ), validSelection && !filterEnabled );
	EnableWindow( GetDlgItem( this->handle, IDC_FAVOURITE ), validSelection && !filterEnabled );
	EnableWindow( GetDlgItem( this->handle, IDC_UNDO      ), somethingChanged && !filterEnabled );
	EnableWindow( GetDlgItem( this->handle, IDC_IMPORT    ), !filterEnabled );
//	EnableWindow( GetDlgItem( this->handle, IDC_GLOBAL    ), !filterEnabled );
	EnableWindow( GetDlgItem( this->handle, IDOK          ), validSelection );

	return validSelection;
}


CSAMWindow::CSAMWindow( WORD action, CSWindow* parent )
{
	this->action = action;
	this->parent = parent;
	this->bChanged = FALSE;
	this->hCombo = this->hMessage = NULL;
	
	if ( this->action == IDC_ADD )
		this->item = new StatusItem( );
	else
		this->item = new StatusItem( *this->parent->itemslist->list->get( this->parent->listview->getPositionInList( )) );

}


CSAMWindow::~CSAMWindow( )
{
	if ( ! this->bChanged )
		delete this->item;
}


void CSAMWindow::exec( )
{
	DialogBoxParam( CSList::handle, MAKEINTRESOURCE( IDD_ADDMODIFY ), NULL, ( DLGPROC )CSAMWindowProc, ( LPARAM )this );
}


void CSAMWindow::setCombo()
{
	PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)this->parent->protoName);
	if (pdescr == NULL)
		return;

	if (lstrcmpA(pdescr->szProtoName, "ICQ") == 0)
	{
		DWORD uin = 0;
		uin = DBGetContactSettingDword(NULL, pdescr->szModuleName, "UIN", 0);
		if (uin != 0)
		{
			SendMessage( this->hCombo, CBEM_SETIMAGELIST, 0, ( LPARAM )this->parent->icons );
			for ( int i = 0; i < ( ( this->parent->bExtraIcons ) ? XSTATUS_COUNT_EXTENDED : XSTATUS_COUNT ); i++ )
			{
				COMBOBOXEXITEM cbi = { 0 };
				cbi.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_SELECTEDIMAGE;
				cbi.pszText = TranslateTS(xstatus_names[i].ptszTitle);
				cbi.iImage = xstatus_names[i].iId - 1;
				cbi.iItem = i;
				cbi.iSelectedImage = i;
				SendMessage( this->hCombo, CBEM_INSERTITEM, 0, ( LPARAM )&cbi );
			}
			SendMessage( this->hCombo, CB_SETCURSEL, 0, 0 ); // first 0 sets selection to top
		}
	}
	else if (lstrcmpA(pdescr->szProtoName, "JABBER") == 0)
	{
		DBVARIANT dbv = {0};
		DBGetContactSettingTString(NULL, pdescr->szModuleName, "jid", &dbv);
		if (dbv.ptszVal != _T(""))
		{
			SendMessage( this->hCombo, CBEM_SETIMAGELIST, 0, ( LPARAM )this->parent->icons );
			for ( int i = 0; i < MOODS_COUNT; i++ )
			{
				COMBOBOXEXITEM cbi = { 0 };
				cbi.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_SELECTEDIMAGE;
				cbi.pszText = TranslateTS(moods_names[i].ptszTitle);
				cbi.iImage = moods_names[i].iId - 1;
				cbi.iItem = i;
				cbi.iSelectedImage = i;
				SendMessage( this->hCombo, CBEM_INSERTITEM, 0, ( LPARAM )&cbi );
			}
			SendMessage( this->hCombo, CB_SETCURSEL, 0, 0 ); // first 0 sets selection to top
		}
	}
}

void CSAMWindow::fillDialog( )
{
	if ( this->action == IDC_ADD )
	{
        SetWindowText( this->handle, TranslateT("Add new item"));
        SetDlgItemText( this->handle, IDOK, TranslateT("Add"));
	}
	else
	{
        SetWindowText( this->handle, TranslateT("Modify item"));
        SetDlgItemText( this->handle, IDOK, TranslateT("Modify"));
	}

	SendMessage( this->hCombo, CB_SETCURSEL, this->item->iIcon, 0 );
	SetDlgItemText( this->handle, IDC_MESSAGE, this->item->tszMessage );
}


void CSAMWindow::checkFieldLimit( WORD action, WORD item )
{
	BOOL type = ( item == IDC_MESSAGE ) ? TRUE : FALSE;
	unsigned int limit = type ? EXTRASTATUS_MESSAGE_LIMIT : EXTRASTATUS_TITLE_LIMIT;

	if ( action == EN_CHANGE )
	{
		TCHAR* ptszInputText = (TCHAR*)mir_alloc((limit + 8) * sizeof(TCHAR));

		GetDlgItemText( this->handle, item, ptszInputText, limit + 8 );

		if ( _tcslen( ptszInputText ) > limit )
		{
#if ( WINVER >= 0x501 )
			TCHAR tszPopupTip[MAX_PATH];
			EDITBALLOONTIP ebt = { 0 };
			ebt.cbStruct = sizeof(ebt);
			ebt.pszTitle = TranslateT("Warning");
			mir_sntprintf(tszPopupTip, SIZEOF(tszPopupTip), TranslateT("This field doesn't accept string longer than %d characters. The string will be truncated."), limit);
			ebt.pszText = tszPopupTip;
			ebt.ttiIcon = TTI_WARNING;
			SendMessage( GetDlgItem( this->handle, item ), EM_SHOWBALLOONTIP, 0, ( LPARAM )&ebt );
#endif
			TCHAR* ptszOutputText = (TCHAR*)mir_alloc((limit + 1) * sizeof(TCHAR));
			GetDlgItemText( this->handle, item, ptszOutputText, limit + 1 );
			SetDlgItemText( this->handle, item, ptszOutputText );
			mir_free( ptszOutputText );
		}
		mir_free( ptszInputText );
	}
}


void CSAMWindow::checkItemValidity()
{
	COMBOBOXEXITEM cbi = { 0 };
	cbi.mask = CBEIF_IMAGE;
	cbi.iItem = SendDlgItemMessage( this->handle, IDC_COMBO, CB_GETCURSEL, 0, 0 );
	SendDlgItemMessage( this->handle, IDC_COMBO, CBEM_GETITEM, 0, ( LPARAM )&cbi );

	if ( this->item->iIcon != cbi.iImage )
		( this->item->iIcon = cbi.iImage ) && ( this->bChanged = TRUE );

	TCHAR tszInputMessage[EXTRASTATUS_MESSAGE_LIMIT];

	GetDlgItemText( this->handle, IDC_MESSAGE, tszInputMessage, EXTRASTATUS_MESSAGE_LIMIT );

	PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)this->parent->protoName);
	if (pdescr == NULL)
		return;

	if (lstrcmpA(pdescr->szProtoName, "ICQ") == 0)
	{
		int i = SendMessage(this->hCombo, CB_GETCURSEL, 0, 0);
		lstrcpy(this->item->tszTitle, TranslateTS(xstatus_names[i].ptszTitle));
	}
	if (lstrcmpA(pdescr->szProtoName, "JABBER") == 0)
	{
		int i = SendMessage(this->hCombo, CB_GETCURSEL, 0, 0);
		lstrcpy(this->item->tszTitle, TranslateTS(moods_names[i].ptszTitle));
	}
	if ( lstrcmp( this->item->tszMessage, tszInputMessage ))
		( lstrcpy( this->item->tszMessage, tszInputMessage )) && ( this->bChanged = TRUE );
}


CSListView::CSListView( HWND hwnd, CSWindow* parent )
{
	this->handle = hwnd;
	this->parent = parent;

#if (_WIN32_WINNT >= 0x501)
	LVGROUP lg = { 0 };
	lg.cbSize = sizeof(LVGROUP);
	lg.mask = LVGF_HEADER | LVGF_GROUPID;

	lg.pszHeader = TranslateT("Favourites");
	lg.iGroupId = 0;
	ListView_InsertGroup( this->handle, -1, &lg );

	lg.pszHeader = TranslateT("Regular statuses");
	lg.iGroupId = 1;
	ListView_InsertGroup( this->handle, -1, &lg );
	ListView_EnableGroupView( this->handle, TRUE );
#endif

	LVCOLUMN lvc = { 0 };
	lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
	lvc.fmt = LVCFMT_LEFT;
	lvc.cx = 0x00;
	lvc.pszText = TEXT( "" );
	lvc.cx = 0x16;
	SendMessage( this->handle, LVM_INSERTCOLUMN, 0, ( LPARAM )&lvc );
	lvc.pszText = TranslateT("Title");
	lvc.cx = 0x64;
	SendMessage( this->handle, LVM_INSERTCOLUMN, 1, ( LPARAM )&lvc );
	lvc.pszText = TranslateT("Message");
	lvc.cx = 0xa8;
	SendMessage( this->handle, LVM_INSERTCOLUMN, 2, ( LPARAM )&lvc );

//	ListView_SetExtendedListViewStyle( this->handle, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP );
#if (_WIN32_IE >= 0x0400)
	ListView_SetExtendedListViewStyleEx( this->handle, 0, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP );
#endif
	ListView_SetImageList( this->handle, this->parent->icons, LVSIL_SMALL );
}


void CSListView::addItem( StatusItem* item, int itemNumber )
{
	LVITEM lvi = { 0 };
	lvi.mask = LVIF_IMAGE | LVIF_GROUPID | LVIF_PARAM;
	lvi.cchTextMax = 256;
	lvi.iItem = ListView_GetItemCount( this->handle );
	lvi.lParam = itemNumber;
#if ( WINVER >= 0x501 )
	lvi.iGroupId = ( item->bFavourite == TRUE ) ? 0 : 1;
#endif

	// first column
	lvi.iSubItem = 0;
	lvi.iImage = item->iIcon; // use selected xicon
	SendMessage( this->handle, LVM_INSERTITEM, 0, ( LPARAM )&lvi );

	// second column
	lvi.mask = LVIF_TEXT;
	lvi.iSubItem = 1;
	lvi.pszText = item->tszTitle;
	SendMessage( this->handle, LVM_SETITEM, 0, ( LPARAM )&lvi );

	// third column
	lvi.iSubItem = 2;
	lvi.pszText = item->tszMessage;
	SendMessage( this->handle, LVM_SETITEM, 0, ( LPARAM )&lvi );
}


void CSListView::initItems( ListItem< StatusItem >* items )
{
	ListItem< StatusItem >* help = items;
	for ( int i = 0; help != NULL; help = help->next, i++ )
		if ( parent->itemPassedFilter( help ))
			addItem( help->item, i );
}


void CSListView::reinitItems( ListItem< StatusItem >* items )
{
	EnableWindow( this->handle, FALSE );
	this->removeItems( );
	this->initItems( items );
	EnableWindow( this->handle, TRUE );
}


void CSListView::removeItems( )
{
	ListView_DeleteAllItems( this->handle );
}


int CSListView::getPositionInList( )
{
	LVITEM lvi = { 0 };
	lvi.iItem = ListView_GetSelectedItemMacro( this->parent->listview->handle );
	lvi.iSubItem = 0;
	lvi.mask = LVIF_PARAM;
	ListView_GetItem( this->parent->listview->handle, &lvi );
	return lvi.lParam;
}


void CSListView::setFullFocusedSelection( int selection )
{
	ListView_SetItemState(  this->handle, -1, 0,     LVIS_SELECTED );
	ListView_EnsureVisible( this->handle, selection, FALSE );
	ListView_SetItemState(  this->handle, selection, LVIS_SELECTED, LVIS_SELECTED );
	ListView_SetItemState(  this->handle, selection, LVIS_FOCUSED , LVIS_FOCUSED  );
	SetFocus( this->handle );
}


// ====[ THREAD FORK ]========================================================

void ForkThread( pThreadFunc pFunc, void* arg )
{
	mir_forkthread( pFunc, arg );
}


// ====[ LIST MANAGEMENT ]====================================================

CSItemsList::CSItemsList(char* protoName)
{
	this->list = new List< StatusItem >( this->compareItems );
	this->loadItems(protoName);
}


CSItemsList::~CSItemsList( )
{
	delete this->list;
}


int CSItemsList::compareItems( const StatusItem* p1, const StatusItem* p2 )
{
	int favRes = 0, icoRes = 0, ttlRes = 0, msgRes = 0;

	if ( p1->bFavourite < p2->bFavourite )
	    favRes = 1;
	else if ( p1->bFavourite > p2->bFavourite )
	    favRes = -1;

	int result;
	if ( p1->iIcon > p2->iIcon )
		icoRes = 1;
	else if ( p1->iIcon < p2->iIcon )
		icoRes = -1;

	result = lstrcmp( p1->tszTitle, p2->tszTitle );
	ttlRes = result;

	result = lstrcmp( p1->tszMessage, p2->tszMessage );
	msgRes = result;

	if ( !icoRes && !ttlRes && !msgRes )
		return 0;

	if ( favRes != 0 )
		return favRes;
	if ( icoRes != 0 )
		return icoRes;
	if ( ttlRes != 0 )
		return ttlRes;
	if ( msgRes != 0 )
		return msgRes;

	return 0;
}


void CSItemsList::loadItems(char* protoName)
{
	char dbSetting[32];
	mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_ItemsCount", protoName);
	unsigned int itemsCount = getWord(dbSetting, DEFAULT_ITEMS_COUNT);

	for (unsigned int i = 1; i <= itemsCount; i++)
	{
		StatusItem* item = new StatusItem();
		DBVARIANT dbv = {DBVT_TCHAR};
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dIcon", protoName, i);
		item->iIcon = getByte(dbSetting, DEFAULT_ITEM_ICON);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dTitle", protoName, i);
		getTString(dbSetting, &dbv);
		lstrcpy(item->tszTitle, dbv.ptszVal);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dMessage", protoName, i);
		getTString(dbSetting, &dbv);
		lstrcpy(item->tszMessage, dbv.ptszVal);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dFavourite", protoName, i);
		item->bFavourite = (BOOL)getByte(dbSetting, DEFAULT_ITEM_IS_FAVOURITE);
		this->list->add(item);
	}
}


void CSItemsList::saveItems(char* protoName)
{
	unsigned int i;
	char dbSetting[32];
	mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_ItemsCount", protoName);
	unsigned int oldItemsCount = getWord(dbSetting, DEFAULT_ITEMS_COUNT);

	for (i = 1; i <= this->list->getCount(); i++)
	{
		StatusItem* item = this->list->get( i - 1 );
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dIcon", protoName, i);
		setByte(dbSetting, item->iIcon);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dTitle", protoName, i);
		setTString(dbSetting, item->tszTitle);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dMessage", protoName, i);
		setTString(dbSetting, item->tszMessage);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dFavourite", protoName, i);
		setByte(dbSetting, item->bFavourite);
	}

	mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_ItemsCount", protoName);
	setWord(dbSetting, this->list->getCount());

	for ( ; i <= oldItemsCount; i++)
	{
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dIcon", protoName, i);
		deleteSetting(dbSetting);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dTitle", protoName, i);
		deleteSetting(dbSetting);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dMessage", protoName, i);
		deleteSetting(dbSetting);
		mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dFavourite", protoName, i);
		deleteSetting(dbSetting);
	}
}


// ====[ PROCEDURES ]=========================================================

INT_PTR CALLBACK CSWindowProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
	CSWindow* csw = ( CSWindow* )GetWindowLongPtr( hwnd, GWLP_USERDATA );

	switch ( message )
	{

	case WM_INITDIALOG:
		csw = ( CSWindow* )lparam;
		SetWindowLongPtr( hwnd, GWLP_USERDATA, lparam );
		csw->handle = hwnd;
		csw->initIcons();
		csw->initButtons( );
		csw->listview = new CSListView( GetDlgItem( hwnd, IDC_CSLIST ), csw );
		csw->listview->initItems( csw->itemslist->list->getListHead( ));
		csw->toggleButtons( );
		csw->toggleEmptyListMessage( );
		csw->loadWindowPosition( );
		SetWindowText(hwnd, TranslateT(MODULENAME));
		TranslateDialogDefault(hwnd);
		return TRUE;

	case WM_COMMAND:
		switch ( LOWORD( wparam ))
		{
		case IDC_MODIFY:
		case IDC_ADD:
			csw->addModifyDlg = new CSAMWindow( LOWORD( wparam ), csw );
			csw->addModifyDlg->exec( );
			if ( csw->addModifyDlg->bChanged )
			{
				if ( LOWORD( wparam ) == IDC_MODIFY )
					csw->itemslist->list->remove( csw->listview->getPositionInList( ));

				int selection = csw->itemslist->list->add( csw->addModifyDlg->item );
				csw->bSomethingChanged = TRUE;
				csw->listview->reinitItems( csw->itemslist->list->getListHead( ));
				csw->listview->setFullFocusedSelection( selection );
				csw->toggleButtons( );
				csw->toggleEmptyListMessage( );
			}
			delete csw->addModifyDlg;
			break;

		case IDC_REMOVE:
			if ( getByte( "ConfirmDeletion", DEFAULT_PLUGIN_CONFIRM_ITEMS_DELETION ))
				if ( MessageBox( hwnd, TranslateT("Do you really want to delete selected item?"), TranslateT(MODULENAME), MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION ) == IDNO )
					break;
			csw->itemslist->list->remove( csw->listview->getPositionInList( ));
			csw->bSomethingChanged = TRUE;
			csw->listview->reinitItems( csw->itemslist->list->getListHead( ));
			csw->toggleButtons( );
			csw->toggleEmptyListMessage( );
			break;

		case IDC_FAVOURITE:
			{
				int selection = csw->listview->getPositionInList( );
				StatusItem* f = new StatusItem( *csw->itemslist->list->get( selection ));
				f->bFavourite = ! f->bFavourite;
				csw->itemslist->list->remove( selection );
				selection = csw->itemslist->list->add( f );
				csw->bSomethingChanged = TRUE;
				csw->listview->reinitItems( csw->itemslist->list->getListHead( ));
				csw->listview->setFullFocusedSelection( selection );
				csw->toggleButtons( );
			}
			break;

		case IDC_UNDO:
			{
			csw->itemslist->list->destroy( );
			csw->itemslist->loadItems(csw->protoName);
			csw->bSomethingChanged = FALSE;
			csw->listview->reinitItems( csw->itemslist->list->getListHead( ));
			csw->toggleButtons( );
			csw->toggleEmptyListMessage( );
			break;
			}

		case IDC_IMPORT:
			{
				int result = getByte( "DeleteAfterImport", DEFAULT_PLUGIN_DELETE_AFTER_IMPORT );
				if ( result == TRUE )
					result = IDYES;
				else
				{
					result = MessageBox( hwnd, TranslateT("Do you want old database entries to be deleted after Import?"),
					                     TranslateT(MODULENAME), MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONQUESTION );
					if ( result == IDCANCEL ) break;
				}

				CSList::ForAllProtocols( CSList::importCustomStatusUIStatusesFromAllProtos, ( void* )&result );
				csw->bSomethingChanged = TRUE;
				csw->toggleButtons( );
				csw->toggleEmptyListMessage( );
			}
			break;

		case IDC_FILTER:
			csw->toggleFilter( );
			csw->toggleButtons( );
			break;

		case IDC_FILTER_FIELD:
			if ( HIWORD( wparam ) == EN_CHANGE )
				csw->listview->reinitItems( csw->itemslist->list->getListHead( ));
			break;

		case IDCLOSE:    // close and save, no custom status 
        case IDCANCEL:   // close and save, no custom status 
        case IDC_CANCEL:   // close and save, cancel custom status
        case IDOK:       // close and save, set selected custom status
			if ( LOWORD( wparam ) == IDOK && csw->toggleButtons( ))
				cslist->setStatus(IDOK, csw->itemslist->list->get(csw->listview->getPositionInList()), csw->protoName);
			if ( LOWORD( wparam ) == IDC_CANCEL )
				cslist->setStatus(IDC_CANCEL, NULL, csw->protoName);
			if ( csw->bSomethingChanged )
				if (csw->itemslist!=NULL)
					csw->itemslist->saveItems(csw->protoName);
			csw->saveWindowPosition( csw->handle );
			delete csw->listview;
			csw->deinitIcons( );
			cslist->closeList( hwnd );
			break;

		}
		return FALSE;

	case WM_NOTIFY:
		if ( wparam == IDC_CSLIST )
		{
			NMHDR* pnmh = ( NMHDR* )lparam;
			switch ( pnmh->code )
			{

			case NM_DBLCLK:
				PostMessage( hwnd, WM_COMMAND, IDOK, 0L );
				break;

			case LVN_ITEMCHANGED:
			case NM_CLICK:
				csw->toggleButtons( );
				break;

			}
		}
		return FALSE;

	case WM_CTLCOLORSTATIC:
		SetTextColor( ( HDC )wparam, RGB( 174, 174, 174 ));
		if ( ( ( HWND )lparam == GetDlgItem( hwnd, IDC_NO_ITEMS )) ||
		     ( ( HWND )lparam == GetDlgItem( hwnd, IDC_ADD_SAMPLE )) )
			return ( BOOL )GetStockObject( WHITE_BRUSH );
		return FALSE;
	}
	return FALSE;
}


INT_PTR CALLBACK CSAMWindowProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
	CSAMWindow* csamw = ( CSAMWindow* )GetWindowLongPtr( hwnd, GWLP_USERDATA );

	switch ( message )
	{

		case WM_INITDIALOG:
			{
				csamw = ( CSAMWindow* )lparam;
				SetWindowLongPtr( hwnd, GWLP_USERDATA, lparam );
				csamw->handle = hwnd;
				EnableWindow( csamw->parent->handle, FALSE );
				csamw->hCombo = GetDlgItem( hwnd, IDC_COMBO );
				csamw->hMessage = GetDlgItem( hwnd, IDC_MESSAGE );
				csamw->setCombo();
				csamw->fillDialog();
				TranslateDialogDefault(hwnd);
			}
			break;

		case WM_COMMAND:
			switch ( LOWORD( wparam ))
			{
			case IDC_MESSAGE:
				csamw->checkFieldLimit( HIWORD( wparam ), LOWORD( wparam ));
				break;
			case IDOK:
				csamw->checkItemValidity( );
			case IDCANCEL:
				EnableWindow( csamw->parent->handle, TRUE );
				EndDialog( hwnd, LOWORD( wparam ));
				break;

			}
			break;

	}
	return FALSE;
}


INT_PTR CALLBACK CSOptionsProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
	switch ( message )
	{

	case WM_INITDIALOG:
		{
			CheckDlgButton( hwnd, IDC_CONFIRM_DELETION,
							getByte( "ConfirmDeletion", DEFAULT_PLUGIN_CONFIRM_ITEMS_DELETION ) ?
							BST_CHECKED : BST_UNCHECKED );

			CheckDlgButton( hwnd, IDC_DELETE_AFTER_IMPORT,
							getByte( "DeleteAfterImport", DEFAULT_PLUGIN_DELETE_AFTER_IMPORT ) ?
							BST_CHECKED : BST_UNCHECKED );

			CheckDlgButton( hwnd, IDC_REMEMBER_POSITION,
			                getByte( "RememberWindowPosition", DEFAULT_REMEMBER_WINDOW_POSITION ) ?
			                BST_CHECKED : BST_UNCHECKED );

			unsigned int protosExtraEnabled = 1;

			CSList::ForAllProtocols( CSList::countPlusModProtos, ( void* )&protosExtraEnabled );

			if ( protosExtraEnabled ) // some protocol(s) allow(s) use of extra icons
			{
				EnableWindow( GetDlgItem( hwnd, IDC_ALLOW_EXTRA_ICONS ), TRUE );
				CheckDlgButton( hwnd, IDC_ALLOW_EXTRA_ICONS, getByte( "AllowExtraIcons",
				                DEFAULT_ALLOW_EXTRA_ICONS ));
			}

			TranslateDialogDefault(hwnd);
		}
		return TRUE;

	case WM_NOTIFY:
		switch ( ( ( LPNMHDR )lparam )->code )
		{

		case PSN_APPLY:
			{
				setByte( "ConfirmDeletion", IsDlgButtonChecked( hwnd, IDC_CONFIRM_DELETION ) ? 1 : 0 );
				setByte( "DeleteAfterImport", IsDlgButtonChecked( hwnd, IDC_DELETE_AFTER_IMPORT ) ? 1 : 0 );
				setByte( "AllowExtraIcons", IsDlgButtonChecked( hwnd, IDC_ALLOW_EXTRA_ICONS ) ? 1 : 0 );
				setByte( "RememberWindowPosition", IsDlgButtonChecked( hwnd, IDC_REMEMBER_POSITION ) ? 1 : 0 );

				cslist->rebuildMenuItems( );
			}
			break;

		}
		return TRUE;

	}
	return FALSE;
}


// ====[ HELPERS ]============================================================

HICON LoadIconExEx( const char* IcoLibName, int NonIcoLibIcon )
{
	if ( ServiceExists( MS_SKIN2_GETICON )) {
		char szSettingName[64];
		mir_snprintf(szSettingName, SIZEOF(szSettingName), "%s_%s", __INTERNAL_NAME, IcoLibName);
		return ( HICON )CallService( MS_SKIN2_GETICON, 0, ( LPARAM )szSettingName );
	}
	else
		return ( HICON )LoadImage( cslist->handle, MAKEINTRESOURCE( NonIcoLibIcon ), IMAGE_ICON, 0, 0, 0 );
}